窗口最小化时,AudioContext计时问题

时间:2016-03-24 23:58:11

标签: javascript audio xmlhttprequest web-audio audiocontext

我按照this article中的说明创建了一个Javascript节拍器。它利用了Web Audio API,其核心是audioContext.currentTime,用于精确计时。

我的版本,this plunker,是原版的简化版本,由Chris Wilson制作,可用here。为了使我的工作,因为它使用实际的音频文件,而不是通过振荡器合成声音,你需要下载plunker和this audio file,将它放在根文件夹(它是一个节拍器'tick'声音,但你可以使用你想要的任何声音。

它的作用就像一个魅力 - 如果不是因为如果用户最小化窗口,那么非常精确的节拍器会立即开始打嗝。我真的不明白这是什么问题。

的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 个答案:

答案 0 :(得分:3)

是的,这是因为当窗口失去焦点时,浏览器会将setTimeout和setInterval限制为每秒一次。 (这样做是为了避免由于开发人员使用setTimeout / setInterval进行可视化动画而导致CPU /功率消耗,并且当选项卡失去焦点时不会暂停动画。)

有两种解决方法:

1)将“预见”(在你的例子中,0.1秒)增加到大于一秒 - 比如1.1s。不幸的是,这意味着你不能改变一些东西(比如停止播放或改变节奏)而不会在变化中超过一秒钟的延迟;所以你可能只想在窗口上触发blur事件时增加该值,并在窗口焦点事件触发时将其更改回0.1。仍然不理想。

2)规避节流。 :)事实证明你可以这样做,因为在Web Workers中不会限制setTimeout / setInterval! (这种方法最初由我在http://www.html5rocks.com/en/tutorials/audio/scheduling/#disqus_thread的原始文章的评论主题中提出。)我为https://github.com/cwilso/metronome中的节拍器代码实现了这一点:看看js / metronome.js和js /metronomeworker.js。工作者基本上只维护计时器,并将消息编组回主线程;特别要看看https://github.com/cwilso/metronome/blob/master/js/metronome.js#L153,看看它是如何开始的。你可以修改那段代码并按原样使用metronomeworker.js解决这个问题。