使用Web Audio API安排样本播放

时间:2018-10-27 13:43:18

标签: javascript web-audio web-audio-api

我正试图制造一个鼓采样机,它使用样本而不是振荡器,只是为了学习。但是我在安排前两个节拍的节奏时遇到麻烦。发生的情况是前两个节拍不同步,而其余两个节拍似乎按照预期的节奏演奏。播放振荡器而不是样本时,不会发生此问题。

我读了《两个时钟的故事》和我能找到的所有相关教程,但是它们都与振荡器一起工作,而振荡器似乎没有出现这个问题。以下只是我尝试实现代码的方法之一-我尝试了OOP和各种功能编程版本,但所有这些都出现了问题。

在此示例中,我制作了名为playSound()和playKick()的函数。 playSound()会触发振荡器音符,而playKick()函数会触发踢音采样。尝试在scheduler()函数中在两者之间切换,以了解问题如何发生。

let audioContext = new (window.AudioContext || window.webkitAudioContext)();
var nextNotetime = audioContext.currentTime;
var startBtn = document.getElementById("startBtn");
var stopBtn = document.getElementById("stopBtn");
var timerID;
let kickBuffer;
loadKick('sounds/kick.wav');

function loadKick(url) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);
  xhr.responseType = 'arraybuffer';
  xhr.onload = function() {
    audioContext.decodeAudioData(xhr.response, decoded => {
      kickBuffer = decoded;
    });
  }
  xhr.send();
}

function playKick(time) {
  let source = audioContext.createBufferSource();
  source.connect(audioContext.destination);
  source.buffer = kickBuffer;
  source.start(time);
}

function playSound(time) {

  var osc = audioContext.createOscillator();
  osc.connect(audioContext.destination);
  osc.frequency.value = 200;
  osc.start(time);
  osc.stop(time + 0.1);

};

function scheduler() {
    while(nextNotetime < audioContext.currentTime + 0.1) {
        // switch between playSound and playKick to hear the problem
        nextNotetime += 0.5;
        playSound(nextNotetime);
        // playKick(nextNotetime);
    }
   timerID = window.setTimeout(scheduler, 0);
}

startBtn.addEventListener('click', function() {
    scheduler();
  }, false);

stopBtn.addEventListener('click', function() {
    clearTimeout(timerID);
  }, false);

if(audioContext.state === 'suspended'){
  audioContext.resume();
};

您可以看到,在文件加载后就立即预加载了缓冲区,这不是原因的根源。任何有关如何解决此问题的建议将不胜感激。

1 个答案:

答案 0 :(得分:0)

正在解决。

"use strict";



var audioContext = new AudioContext(),
    futureTickTime = audioContext.currentTime,
    counter = 1,
    tempo = 120,
    secondsPerBeat = 60 / tempo,
    counterTimeValue = (secondsPerBeat / 4),
    timerID = undefined,
    isPlaying = false;


//_____________________________________________BEGIN load sound samples
let kickBuffer;
loadKick('sounds/kick.mp3');

function loadKick(url) {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'arraybuffer';
    xhr.onload = function() {
        audioContext.decodeAudioData(xhr.response, function(decoded) {
            kickBuffer = decoded;
        });
    }
    xhr.send();
}

function playKick(time) {
    let source = audioContext.createBufferSource();
    source.connect(audioContext.destination);
    source.buffer = kickBuffer;
    source.start(audioContext.currentTime + time);
}


//_____________________________________________END load sound samples


function scheduleSound(time) {
    playKick(time)

}


function playTick() {
    console.log(counter);
    secondsPerBeat = 60 / tempo;
    counterTimeValue = (secondsPerBeat / 1);
    counter += 1;
    futureTickTime += counterTimeValue;
}


function scheduler() {
    if (futureTickTime < audioContext.currentTime + 0.1) {
        scheduleSound(futureTickTime - audioContext.currentTime);

        playTick();
    }

    timerID = window.setTimeout(scheduler, 0);
}


function play() {
    isPlaying = !isPlaying;

    if (isPlaying) {
        counter = 1;
        futureTickTime = audioContext.currentTime;
        scheduler();
    } else {
        window.clearTimeout(timerID);
    }
}



var playStop = document.getElementsByClassName("play-stop-button")[0];

playStop.addEventListener("click",function(){
    play();
})