window 最小化时的 AudioContext 计时问题
AudioContext timing issues when window gets minimized
我按照 this article and created a Javascript metronome. It makes use of the Web Audio API and has audioContext.currentTime
中的说明在其核心进行了精确计时。
我的版本,可在 this plunker, is a very simplified version of the original, made by Chris Wilson and available here. In order for mine to work, since it uses an actual audio file and doesn't synthesize sounds through an oscillator, you need to download the plunker and this audio file 获得,将其放在根文件夹中(它是节拍器 'tick' 声音,但您可以使用任何您想要的声音)。
它就像一个魅力 - 如果不是因为如果用户最小化 window,否则非常准确的节拍器会立即开始打嗝并且非常糟糕。我真的不明白这里有什么问题。
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();
};
是的,这是因为当 window 失去焦点时,浏览器将 setTimeout 和 setInterval 限制为每秒一次。 (这样做是为了避免 CPU/power 由于开发人员使用 setTimeout/setInterval 进行视觉动画而造成的流失,并且在选项卡失去焦点时不暂停动画。)
有两种解决方法:
1) 将 "look-ahead"(在您的示例中为 0.1 秒)增加到大于一秒 - 例如 1.1 秒。不幸的是,这意味着您无法在更改延迟超过一秒的情况下更改内容(例如停止播放或更改速度);所以您可能只想在 window 上触发模糊事件时增加该值,并在 window 焦点事件触发时将其更改回 0.1。还是不理想。
2) 规避节流。 :) 事实证明你可以这样做,因为 setTimeout/setInterval 在 Web Workers 中没有被限制! (这种方法最初是由某人在我在 http://www.html5rocks.com/en/tutorials/audio/scheduling/#disqus_thread.) I implemented this for the metronome code in https://github.com/cwilso/metronome: take a look at the js/metronome.js and js/metronomeworker.js. The Worker basically just maintains the timer, and marshals a message back across to the main thread; take a look at https://github.com/cwilso/metronome/blob/master/js/metronome.js#L153 的原始文章的评论线程中提出的,特别是为了了解它是如何开始的。您可以修改该代码片段并按原样使用 metronomeworker.js解决这个问题。
我按照 this article and created a Javascript metronome. It makes use of the Web Audio API and has audioContext.currentTime
中的说明在其核心进行了精确计时。
我的版本,可在 this plunker, is a very simplified version of the original, made by Chris Wilson and available here. In order for mine to work, since it uses an actual audio file and doesn't synthesize sounds through an oscillator, you need to download the plunker and this audio file 获得,将其放在根文件夹中(它是节拍器 'tick' 声音,但您可以使用任何您想要的声音)。
它就像一个魅力 - 如果不是因为如果用户最小化 window,否则非常准确的节拍器会立即开始打嗝并且非常糟糕。我真的不明白这里有什么问题。
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();
};
是的,这是因为当 window 失去焦点时,浏览器将 setTimeout 和 setInterval 限制为每秒一次。 (这样做是为了避免 CPU/power 由于开发人员使用 setTimeout/setInterval 进行视觉动画而造成的流失,并且在选项卡失去焦点时不暂停动画。)
有两种解决方法:
1) 将 "look-ahead"(在您的示例中为 0.1 秒)增加到大于一秒 - 例如 1.1 秒。不幸的是,这意味着您无法在更改延迟超过一秒的情况下更改内容(例如停止播放或更改速度);所以您可能只想在 window 上触发模糊事件时增加该值,并在 window 焦点事件触发时将其更改回 0.1。还是不理想。
2) 规避节流。 :) 事实证明你可以这样做,因为 setTimeout/setInterval 在 Web Workers 中没有被限制! (这种方法最初是由某人在我在 http://www.html5rocks.com/en/tutorials/audio/scheduling/#disqus_thread.) I implemented this for the metronome code in https://github.com/cwilso/metronome: take a look at the js/metronome.js and js/metronomeworker.js. The Worker basically just maintains the timer, and marshals a message back across to the main thread; take a look at https://github.com/cwilso/metronome/blob/master/js/metronome.js#L153 的原始文章的评论线程中提出的,特别是为了了解它是如何开始的。您可以修改该代码片段并按原样使用 metronomeworker.js解决这个问题。