窗口最小化时的AudioContext定时问题

4
我按照这篇文章中的指示制作了一个Javascript节拍器。它使用Web音频API,并且核心是audioContext.currentTime,用于精确计时。

我的版本可以在这个Plunker上找到,它是原版的极简化版本,由Chris Wilson制作,原版在这里。为了让我的版本工作正常,因为它使用实际的音频文件而不是通过振荡器合成声音,你需要下载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();
};
1个回答

2
是的,这是因为浏览器在窗口失去焦点时会将setTimeout和setInterval限制为每秒一次。 (这是为了规避由于开发人员在使用setTimeout / setInterval进行视觉动画时未暂停动画而导致的CPU /电源耗尽。)
有两种方法可以解决这个问题:
1)将“向前看”(在您的示例中为0.1秒)增加到大于一秒钟,例如1.1秒。不幸的是,这意味着您不能更改事物(例如停止播放或更改速度)而没有超过一秒钟的延迟;因此,您可能只想在窗口上的模糊事件触发时增加该值,并在窗口焦点事件触发时将其改回0.1。仍然不理想。
2)规避限制。 :)原来你可以做到这一点,因为setTimeout / setInterval在Web Workers中不受限制!(这种方法最初是由我在http://www.html5rocks.com/en/tutorials/audio/scheduling/#disqus_thread的原始文章评论线程中建议的。)我在https://github.com/cwilso/metronome的节拍器代码中实现了这一点:查看js / metronome.js和js / metronomeworker.js。 Worker基本上只维护计时器,并跨主线程传递消息;请查看https://github.com/cwilso/metronome/blob/master/js/metronome.js#L153,特别是要看看它是如何启动的。您可以修改该代码片段并使用metronomeworker.js原样修复此问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接