AudioContext перестает работать после использования ровно 50 раз - PullRequest
0 голосов
/ 06 мая 2020

Привет, я пытаюсь сделать ударную установку, для этого я использую AudioContext API. Моя проблема в том, что когда я использую ровно 50 раз, он перестает работать. Единственное, что я обнаружил, чтобы заставить его работать, - это закрыть предыдущий использованный AudioContext, дело в том, что он издает "щелкающий" звук из-за внезапной остановки звука. Есть идеи, что делать? Вот над чем я работаю, чтобы не останавливаться на 50 использований:

let i = 0;
let audioContext;
let volumeControl;

// Credit to MDN for AudioContext and StereoPannerNode tutorial.
function playSound(src, volume, pitch, stereo) {
    if (audioContext != null) {
        //volumeControl.gain.value = 0;
        audioContext.close();
    }

    console.log(i++);

    audioContext = new  AudioContext();
    const stereoControl = new StereoPannerNode(audioContext);

    volumeControl = audioContext.createGain();
    volumeControl.gain.value = volume;
    stereoControl.pan.value = stereo;

    const source = audioContext.createBufferSource();

    const request = new XMLHttpRequest();
    request.open('GET', src, true);
    request.responseType = 'arraybuffer';

    request.onload = function() {
        const audioData = request.response;

        audioContext.decodeAudioData(audioData, function(buffer) {
            source.buffer = buffer;
            source.playbackRate.value = pitch;
            source.connect(volumeControl).connect(stereoControl).connect(audioContext.destination);
        });

    };

    request.send();
    source.play = source.start;
    source.play();
}

1 Ответ

0 голосов
/ 06 мая 2020

Не создавайте звуковой контекст для каждого звука, а создайте один для своей страницы и добавьте к нему узлы. Что-то вроде этого ...

const audioContext = new AudioContext();
function playSound(src, volume, pitch, stereo) {
  const stereoControl = audioContext.createStereoPanner();
  const volumeControl = audioContext.createGain();
  volumeControl.gain.value = volume;
  stereoControl.pan.value = stereo;
  const source = audioContext.createBufferSource();
  const request = new XMLHttpRequest();
  request.open("GET", src, true);
  request.responseType = "arraybuffer";

  request.onload = function() {
    const audioData = request.response;

    audioContext.decodeAudioData(audioData, function(buffer) {
      source.buffer = buffer;
      source.playbackRate.value = pitch;
      source
        .connect(volumeControl)
        .connect(stereoControl)
        .connect(audioContext.destination);
      source.start();
    });
  };

  request.send();
}

Кроме того, для барабанной установки вам нужно либо предварительно загрузить все сэмплы, либо, по крайней мере, кэшировать декодированные аудио буферы и не выполнять запрос каждый раз для них:

const cache = {};
const audioContext = new AudioContext();

function loadSound(src) {
  if (cache[src]) {
    // Already cached
    return Promise.resolve(cache[src]);
  }
  return new Promise(resolve => {
    const request = new XMLHttpRequest();
    request.open("GET", src, true);
    request.responseType = "arraybuffer";
    request.onload = function() {
      const audioData = request.response;
      audioContext.decodeAudioData(audioData, function(buffer) {
        cache[src] = buffer;
        resolve(buffer);
      });
    };
    request.send();
  });
}

function playSound(src, volume, pitch, stereo) {
  loadSound(src).then(buffer => {
    const stereoControl = audioContext.createStereoPanner();
    const volumeControl = audioContext.createGain();
    volumeControl.gain.value = volume;
    stereoControl.pan.value = stereo;
    const source = audioContext.createBufferSource();
    source.buffer = buffer;
    source.playbackRate.value = pitch;
    source
      .connect(volumeControl)
      .connect(stereoControl)
      .connect(audioContext.destination);
    source.start();
  });
}

...