3

我按照本文中的说明创建了一个 Javascript 节拍器。它利用 Web Audio 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();
};
4

1 回答 1

3

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

有两种方法可以解决这个问题:

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

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

于 2016-03-25T17:46:48.743 回答