首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Javascript: MIDI Sequencer应用程序中快速定时器的setTimeOut替代品

Javascript: MIDI Sequencer应用程序中快速定时器的setTimeOut替代品
EN

Stack Overflow用户
提问于 2018-10-27 00:21:11
回答 4查看 1.2K关注 0票数 3

我正在开发一个包含序列器的Javascript音乐应用程序。对于那些不熟悉的人,MIDI sequencers的工作原理是这样的:有一种叫做PPQ的东西:pulses per quarter note。每个脉冲被称为“滴答”脉冲()。它描绘了每个季度音符的“细分”,比如分辨率。因此,Sequencer一次一个节拍地“播放”音轨中的事件: Play Tick1、wait Tick Duration、Play tick2、Tick Duration等等。

现在,假设我们有一个带有PPQ=96 (标准)的120的BPM(每分钟跳数)。这意味着每个季度音符持续时间为500ms,每个滴答持续时间为5.20833ms。

我们在Javascript中有哪些计时器替代方案?

1)我们有旧的setTimeOut。它有几个问题:等待时间为4ms。(https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Minimum_delay_and_timeout_nesting)它也会受到抖动/时间变化的影响。它并不精确,而且要求很高,因为回调堆叠在偶数循环中。

2)有一个涉及使用requestAnimationFrame().的setTimeOut/setInterval替代方案它非常精确,CPU效率也很高。但是,可以设置的最小时间约为16.7ms (典型的60FPS监视器中一帧的持续时间)

还有没有别的选择?要精确地每2-5ms安排一次事件?

注意:在循环中完成的函数,playEventsAtTick()一点也不苛刻,所以它不会比Tick Duration花更多的时间来执行。

谢谢!丹尼·布洛

EN

回答 4

Stack Overflow用户

发布于 2018-10-27 00:28:49

为了在做这类事情时保持理智,你需要在一个专门的线程上做音频处理。更好的是,使用Web Audio API,让那些长期考虑这些问题的人来做样本准确性的艰苦工作。

也可以查看Web MIDI (仅限chrome)。

票数 1
EN

Stack Overflow用户

发布于 2018-10-27 00:56:57

感谢nvioli。我知道Web Audio API。然而,我不认为这能在这里有所帮助。我没有直接触发音频:我在曲目中存储了MIDI事件(或者说只是“事件”)。这些事件在任何时刻都会发生。因此,Sequencer需要循环每个节拍持续时间,以扫描在特定节拍播放的内容。

致敬,丹尼·布洛

票数 0
EN

Stack Overflow用户

发布于 2018-10-27 02:15:16

在单独的线程中,例如web worker,您可以创建无限循环。在这个循环中,你需要做的就是计算两个节拍之间的时间。在时间有效后,您可以向主进程发送一条消息,以执行一些视觉效果、播放声音或任何您想要执行的操作。

这是一个Working example

代码语言:javascript
复制
class MyWorker {

  constructor() {
    // Keeps the loop running
    this.run = true
    // Beats per minute
    this.bpm = 120
    // Time last beat was called
    this.lastLoopTime = this.milliseconds
  }

  get milliseconds() {
    return new Date().getTime()
  }

  start() {
    while (this.run) {
      // Get the current time
      let now = this.milliseconds
      // Get the elapsed time between now and the last beat
      let updateLength = now - this.lastLoopTime
      // If not enough time has passed restart from the beginning of the loop
      if (updateLength < (1000 * 60) / this.bpm) continue;
      // Enough time has passed update the last time
      this.lastLoopTime = now

      // Do any processing that you would like here

      // Send a message back to the main thread
      postMessage({ msg: 'beat', time: now })

    }
  }

}

new MyWorker().start()

接下来,我们可以创建索引页面,它将运行worker,并在每次从worker返回消息时闪现一个正方形。

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
  <head>
    <script>
      // Start the worker
      var myWorker = new Worker('worker.js')
      // Listen for messages from the worker
      myWorker.onmessage = function (e) {
        var msg = e.data
        switch (msg.msg) {
          // If the message is a `beat` message, flash the square
          case 'beat':
            let div = document.querySelector('div')
            div.classList.add('red')
            setTimeout(() => div.classList.remove('red'), 100)
            break;
        }
      }
    </script>
    <style>
      div { width: 100px; height: 100px; border: solid 1px; }
      .red { background: red; }
    </style>
  </head>
  <body>
    <div></div>
  </body>
</html>
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53012768

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档