我按照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();
};
答案 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解决这个问题。