怎样让 js 中的时间更加精确?

2016-11-14 11:48:09 +08:00
 starvedcat

我想做这样一个功能:网页播放有规律的节拍声音,用户按照这个节拍敲击键盘;我需要比较“节拍声音播放的时刻”和“用户敲击键盘的时刻”

以下是例子:

<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/keymaster/1.6.1/keymaster.min.js"></script>
<script src="https://tonejs.github.io/CDN/latest/Tone.min.js"></script>
<script>
    var contextCreatedTime = Date.now();
    var context = Tone.context;

    // 从第 3 秒开始,每隔 0.5 秒发出声音,共 8 次
    for (var i = 0; i < 8; i++) {
        var oscillator = context.createOscillator();
        oscillator.connect(context.destination);
        oscillator.frequency.value = 220;

        var soundStartTime = 3 + i * 0.5;
        oscillator.start(soundStartTime);
        oscillator.stop(soundStartTime + 0.05);

        // 将后 4 次发出声音的时刻输出到 console
        if (i > 3) {
            console.log("sound time:    " + (contextCreatedTime + Math.floor(soundStartTime * 1000)));
        }
    }

    // 记录键盘“ k ”键的敲击时刻,输出到 console
    key('k', function () {
        console.log("keyboard time: " + Date.now());
    });
</script>
</body>
</html>

具体操作是这样:

这个网页从载入完成之后的第 3 秒开始,会播放 8 个短促音,它们之间的间隔是 0.5 秒。其中,前 4 个音的作用是提示用户节奏,用户需要踩着后 4 个音的节拍敲击“ k ”键

这后 4 个音的输出时间,以及用户踩着后 4 个音的节拍敲击“ k ”键的时间,都会被输出到 console

但是,通过比较它们之间的时间差,我发现这个时间并不够准确, 比如说,有时候, 4 个节拍音比 4 个按键音的时间要大 40 (毫秒),有时候要大 7 80 ,有时候误差在 10 以内,有时候又会差 100 多……

1599 次点击
所在节点    问与答
14 条回复
starvedcat
2016-11-14 11:59:33 +08:00
它们之间的差值可能是这样的:[35, 46, 42, 50],也可能是[107, 95, 88, 102],也可能是[7, 4, -2, 11]这样。我每次都是以相同的节奏感去敲击键盘的,也试了相当多次了,可以排除人为因素。

这里牵扯到三个东西的时刻,理论上他们应该属于同一时刻:
1. 人为设置的发声时间,即“ contextCreatedTime + Math.floor(soundStartTime * 1000)”。这个就是算式,必定准确,关键看另外两者是不是符合它
2. 实际发出声音的时间,即, web audio api 是否按照 1 中设置的时间准时发声了?姑且认为这个 API 也是准确的
3. 用户敲击键盘的时间。

但是令我非常不理解的是,为什么输出按键的时刻,反而是要小于节拍音的时刻呢?照道理说,如果按键在监听事件的过程中耗费的时间从而导致误差的话,那应该是会大于节拍音才对啊?
starvedcat
2016-11-14 12:03:22 +08:00
上面 2 也应该是必定准确的。

我的想法就是:这个键盘敲击的时间,仿佛和这个节拍音的时间有一个固定的差值。而每次刷新网页,这个差值都在变化……
starvedcat
2016-11-14 12:06:30 +08:00
对了键盘是机械键盘巡检速率 1000Hz ……
starvedcat
2016-11-14 12:10:58 +08:00
而且我发现,加载的 js 越多,这个误差就越大。上面这个最小化的例子,误差就几十,说实话还可以接受,真正项目中,误差都快上 200 了……比如这样:
sound time: 1479096471944
sound time: 1479096472444
sound time: 1479096472944
sound time: 1479096473444

keyboard time: 1479096471779
keyboard time: 1479096472260
keyboard time: 1479096472750
keyboard time: 1479096473264

差值都在 180 左右
ctsed
2016-11-14 14:09:38 +08:00
requestAnimationFrame
zzNucker
2016-11-14 14:13:52 +08:00
有网址吗
starvedcat
2016-11-14 14:26:13 +08:00
@ctsed 没用到 canvas
starvedcat
2016-11-14 14:26:27 +08:00
@zzNucker 上面的网页复制下来就行了。。。。。。。。。
otakustay
2016-11-14 14:27:44 +08:00
@starvedcat raf 和 canvas 没关系,用的话基本能控制在 16ms 左右的精度,算是比较可以的了
murmur
2016-11-14 14:32:36 +08:00
击拍定速还是音游? 校正输入延迟么?
xxxyyy
2016-11-14 15:26:41 +08:00
你的 sound time 应该使用实际播放时的时间,比如:
oscillator.onended = function () {
console.log("sound start time:", Date.now() - 500);
};
cst4you
2016-11-14 19:51:08 +08:00
setInterval 1 毫秒
starvedcat
2016-11-15 01:33:10 +08:00
感谢 ls 诸位!
andy12530
2016-11-15 04:59:30 +08:00
performance api 能精确一些。还有一个可以取到 ns

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/320339

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX