V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
starvedcat
V2EX  ›  问与答

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

  •  
  •   starvedcat · 2016-11-14 11:48:09 +08:00 · 1607 次点击
    这是一个创建于 2960 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    以下是例子:

    <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 多……

    第 1 条附言  ·  2016-11-15 01:37:47 +08:00

    用这个可解:

    oscillator.onended = function () {
        console.log("actual note time:   ", Date.now());
    };
    
    14 条回复    2016-11-15 04:59:30 +08:00
    starvedcat
        1
    starvedcat  
    OP
       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
        2
    starvedcat  
    OP
       2016-11-14 12:03:22 +08:00
    上面 2 也应该是必定准确的。

    我的想法就是:这个键盘敲击的时间,仿佛和这个节拍音的时间有一个固定的差值。而每次刷新网页,这个差值都在变化……
    starvedcat
        3
    starvedcat  
    OP
       2016-11-14 12:06:30 +08:00
    对了键盘是机械键盘巡检速率 1000Hz ……
    starvedcat
        4
    starvedcat  
    OP
       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
        5
    ctsed  
       2016-11-14 14:09:38 +08:00   ❤️ 1
    requestAnimationFrame
    zzNucker
        6
    zzNucker  
       2016-11-14 14:13:52 +08:00
    有网址吗
    starvedcat
        7
    starvedcat  
    OP
       2016-11-14 14:26:13 +08:00
    @ctsed 没用到 canvas
    starvedcat
        8
    starvedcat  
    OP
       2016-11-14 14:26:27 +08:00
    @zzNucker 上面的网页复制下来就行了。。。。。。。。。
    otakustay
        9
    otakustay  
       2016-11-14 14:27:44 +08:00   ❤️ 1
    @starvedcat raf 和 canvas 没关系,用的话基本能控制在 16ms 左右的精度,算是比较可以的了
    murmur
        10
    murmur  
       2016-11-14 14:32:36 +08:00
    击拍定速还是音游? 校正输入延迟么?
    xxxyyy
        11
    xxxyyy  
       2016-11-14 15:26:41 +08:00   ❤️ 1
    你的 sound time 应该使用实际播放时的时间,比如:
    oscillator.onended = function () {
    console.log("sound start time:", Date.now() - 500);
    };
    cst4you
        12
    cst4you  
       2016-11-14 19:51:08 +08:00
    setInterval 1 毫秒
    starvedcat
        13
    starvedcat  
    OP
       2016-11-15 01:33:10 +08:00
    感谢 ls 诸位!
    andy12530
        14
    andy12530  
       2016-11-15 04:59:30 +08:00 via iPhone   ❤️ 1
    performance api 能精确一些。还有一个可以取到 ns
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2933 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 13:30 · PVG 21:30 · LAX 05:30 · JFK 08:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.