如何创建和正确调度 AudioBufferSource 节点

How to create and properly schedule AudioBufferSource nodes?

本文关键字:调度 AudioBufferSource 节点 何创建 创建      更新时间:2023-09-26

我正在尝试使用Javascript开发节拍器。我按照本文中的说明创建了一个调度程序,该调度程序每隔一段时间就会被调用并调度"ticks",这些"tick"存在于一个名为 ticksInQueue 的数组中。我希望"滴答声"是节拍器的实际声音,而不是振荡器产生的频率。

振荡器

function scheduler() {
    while (nextTickTime < audioContext.currentTime + lookahead) { // while there are notes that will need to play before the next interval
      ticksInQueue.push({tick: currentTick, time: nextTickTime}); // push the note on the queue, even if we're not playing.
      //create an oscillator
      var osc = audioContext.createOscillator();
      osc.connect(audioContext.destination);
      osc.frequency.value = 440.0;
      osc.start(nextTickTime);
      osc.stop(nextTickTime + 0.1);
      nextTickTime += 60.0 / tempo;  // Add beat length to last beat time
      currentTick = currentTick + 1; // Advance the beat number, wrap to zero
      if (currentTick === 5) {
         currentTick = 0;
      }
   }
}

按照有关如何创建AudioBufferSource节点的视频教程,我编写了这段在加载窗口时执行的代码。

audioContext = new AudioContext();
request = new XMLHttpRequest();
request.open("GET", "tick.mp3", true);
request.responseType = "arraybuffer";
function onDecoded(buffer) {
    bufferSource = audioContext.createBufferSource();
    bufferSource.buffer = buffer;
    bufferSource.connect(audioContext.destination);
}
request.onload = function () {
    // audio data is in request.response
    audioContext.decodeAudioData(request.response, onDecoded);
};
request.send();

但是,如果我用缓冲区替换振荡器,如下所示,控制台会告诉我" Uncaught InvalidStateError: Failed to execute 'start' on 'AudioBufferSourceNode': cannot call start more than once. "

音频缓冲源

function scheduler() {
    while (nextTickTime < audioContext.currentTime + lookahead) {
      ticksInQueue.push({tick: currentTick, time: nextTickTime});
      //Changed code
      bufferSource.start(nextTickTime);
      bufferSource.stop(nextTickTime + 0.1);
      nextTickTime += 60.0 / tempo;  // Add beat length to last beat time
      currentTick = currentTick + 1; // Advance the beat number, wrap to zero
      if (currentTick === 5) {
         currentTick = 0;
      }
   }
}

是的,你需要为每个"tick"创建一个 BufferSource。 (它们共享缓冲区,但缓冲区源是一次性节点。

所以,像这样:

var the_buffer=null;
function onDecoded(buffer) {
    the_buffer=buffer;
}
function playTick() {
    if (the_buffer) {  // make sure it's been decoded
        var bufferSource = audioContext.createBufferSource();
        bufferSource.buffer = the_buffer;
        bufferSource.connect(audioContext.destination);
        bufferSource.start(0);
        // bufferSource will get auto-garbage-collected when it's done playing.
    }
}

现在,将调度器中的振荡器代码替换为对 playTick() 的调用;