Javascript 最小化窗口时的AudioContext计时问题

Javascript 最小化窗口时的AudioContext计时问题,javascript,audio,xmlhttprequest,web-audio-api,audiocontext,Javascript,Audio,Xmlhttprequest,Web Audio Api,Audiocontext,我按照中的说明创建了一个Javascript节拍器。它使用Web音频API,并以精确计时为核心 我的版本,可在,是一个非常简化的版本的原始,由克里斯威尔逊和可用。为了让我的工作,因为它使用的是一个实际的音频文件,而不是通过振荡器合成声音,所以你需要下载plunker并将其放在根文件夹中(它是节拍器的“滴答声”,但你可以使用任何你想要的声音) 它就像一个魔咒——如果不是因为用户将窗口最小化,否则非常精确的节拍器会立即开始严重打嗝。我真的不明白这里有什么问题 Javascript var conte

我按照中的说明创建了一个Javascript节拍器。它使用Web音频API,并以精确计时为核心

我的版本,可在,是一个非常简化的版本的原始,由克里斯威尔逊和可用。为了让我的工作,因为它使用的是一个实际的音频文件,而不是通过振荡器合成声音,所以你需要下载plunker并将其放在根文件夹中(它是节拍器的“滴答声”,但你可以使用任何你想要的声音)

它就像一个魔咒——如果不是因为用户将窗口最小化,否则非常精确的节拍器会立即开始严重打嗝。我真的不明白这里有什么问题

Javascript

var context, request, buffer;
var tempo = 120;
var tickTime;

function ticking() {
    var source = context.createBufferSource();
    source.buffer = buffer;
    source.connect(context.destination);
    source.start(tickTime);
}

function scheduler() {
    while (tickTime < context.currentTime + 0.1) {  //while there are notes to schedule, play the last scheduled note and advance the pointer
        ticking();
        tickTime += 60 / tempo;
    }
}

function loadTick() {
    request = new XMLHttpRequest();                 //Asynchronous http request (you'll need a local server) 
    request.open('GET', 'tick.wav', true);          //You need to download the file @ http://s000.tinyupload.com/index.php?file_id=89415137224761217947
    request.responseType = 'arraybuffer';
    request.onload = function () {
        context.decodeAudioData(request.response, function (theBuffer) {
            buffer = theBuffer;
        });
    };
    request.send();
}

function start() {
    tickTime = context.currentTime;
    scheduleTimer = setInterval(function () {
        scheduler();
   }, 25);
}

window.onload = function () {
    window.AudioContext = window.AudioContext || window.webkitAudioContext;
    context = new AudioContext();
    loadTick();
    start();
};
var上下文、请求、缓冲区;
var-tempo=120;
时间;
函数滴答(){
var source=context.createBufferSource();
source.buffer=缓冲区;
source.connect(context.destination);
source.start(开始时间);
}
函数调度程序(){
while(tickTime是的,这是因为当窗口失去焦点时,浏览器将setTimeout和setInterval限制为每秒一次。(这样做是为了避免由于开发人员使用setTimeout/setInterval进行可视化动画,并且在选项卡失去焦点时不暂停动画而导致CPU/电源消耗。)

有两种解决方法:

1) 将“向前看”(在您的示例中为0.1秒)增加到大于1秒,如1.1s。不幸的是,这意味着你不能在没有超过一秒延迟的情况下改变事情(比如停止播放,或者改变节奏);因此,您可能只希望在窗口上触发模糊事件时增加该值,并在触发窗口焦点事件时将其更改回0.1。仍然不理想

2) 绕过节流阀。:)事实证明,您可以做到这一点,因为在Web Workers中setTimeout/setInterval是不受限制的!(这种方法最初是由我在上一篇文章的评论线程中的某个人提出的。)我在中为metronome代码实现了这种方法:看看js/metronome.js和js/metronomeworker.js。工作者基本上只是维护计时器,并将消息封送回主线程;特别是看看它是如何开始的。您可以修改该代码片段并按原样使用metronomeworker.js来修复此问题