Javascript:MIDI Sequencer应用程序中快速定时器设置超时的替代方案

Javascript:MIDI Sequencer应用程序中快速定时器设置超时的替代方案,javascript,timer,settimeout,midi,requestanimationframe,Javascript,Timer,Settimeout,Midi,Requestanimationframe,我正在开发一个包含音序器的Javascript音乐应用程序。对于那些不熟悉的人,MIDI音序器的工作原理与此类似:有一种叫做PPQ:每四分之一音符的脉冲数。每个脉冲称为“滴答声”。它描述了每个季度纸币的“细分”,如分辨率。所以音序器一次播放一个节拍:播放节拍1,等待节拍持续时间,播放节拍2,节拍持续时间,等等 现在,假设我们有一个BPM(每分钟心跳次数)为120,并且PPQ=96(标准)。这意味着每个季度票据的持续时间为500毫秒,每个滴答声的持续时间为5.20833毫秒 我们在Javascri

我正在开发一个包含音序器的Javascript音乐应用程序。对于那些不熟悉的人,MIDI音序器的工作原理与此类似:有一种叫做PPQ每四分之一音符的脉冲数。每个脉冲称为“滴答声”。它描述了每个季度纸币的“细分”,如分辨率。所以音序器一次播放一个节拍:播放节拍1,等待节拍持续时间,播放节拍2,节拍持续时间,等等

现在,假设我们有一个BPM(每分钟心跳次数)为120,并且PPQ=96(标准)。这意味着每个季度票据的持续时间为500毫秒,每个滴答声的持续时间为5.20833毫秒

我们在Javascript中有哪些计时器选项?

1) 我们有旧的设置超时。它有几个问题:最小等待时间是4ms。() 它也会受到抖动/时间变化的影响。这是不精确的,而且要求很高,因为回访是在均匀循环中堆积的

2) 除了使用requestAnimationFrame()之外,还有一种方法可以替代setTimeOut/setInterval。它非常精确,CPU效率高。但是,可以设置的最短时间约为16.7ms(典型60FPS监视器中的帧持续时间)

还有其他选择吗?要精确地安排每2-5毫秒一次的活动?

注意:在循环中完成的函数,playEventsAtTick()一点要求都不高,因此执行时间不会超过Tick Duration

谢谢!
Danny Bullo

为了在做这种事情时保持理智,你需要在一个专门的线程上进行音频处理。更好的办法是,使用这个工具,让那些长期以来一直在思考这些问题的人来努力提高样本的准确性


另外请查看(仅限chrome)。

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

问候,,
Danny Bullo

在一个单独的线程(如a)中,可以创建一个无止境的循环。在这个循环中,您所需要做的就是计算两次跳动之间的时间。在时间有效后,您可以向主进程发送消息,执行一些视觉效果、播放声音或任何您想执行的操作

这是一个
类MyWorker{
构造函数(){
//保持循环运行
this.run=true
//每分钟拍数
此值为0.bpm=120
//最后一拍的时间
this.lastLoopTime=this.ms
}
获取毫秒(){
返回新日期().getTime()
}
开始(){
while(this.run){
//获取当前时间
让我们现在=this.ms
//获取从现在到最后一拍之间经过的时间
让updateLength=now-this.lastLoopTime
//如果时间不够,请从循环开始重新启动
如果(updateLength<(1000*60)/this.bpm)继续;
//上次更新已过足够的时间
this.lastLoopTime=现在
//在这里进行任何您想要的处理
//将消息发送回主线程
postMessage({msg:'beat',time:now})
}
}
}
新建MyWorker().start()
接下来,我们可以创建索引页面,该页面将运行worker,并在每次从worker返回消息时闪烁一个正方形

<!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>

//启动工人
var myWorker=new Worker('Worker.js')
//侦听来自工作人员的消息
myWorker.onmessage=函数(e){
var msg=e.data
开关(msg.msg){
//如果消息是“beat”消息,则闪烁方块
“节拍”一案:
设div=document.querySelector('div')
div.classList.add('red')
setTimeout(()=>div.classList.remove('red'),100)
打破
}
}
div{宽度:100px;高度:100px;边框:实心1px;}
.red{背景:red;}

离开我的草坪:你建议的方法不完全有效。假设我向web worker添加了一个方法来停止Sequencer

stop() {
    this.run = false;
}
问题是方法myWorker.onmessage=function(e){…}从未被触发。我怀疑这是因为Web Worker线程“太忙”了,没有尽头的循环。有办法解决吗

此外,在播放时,它可以工作……但CPU的速度大大提高了。。。。。唯一可能的解决方案是Sleep()方法,但是真正的Sleep在Javascript中不存在


感谢

在工作流程中使用无休止的
while
循环,只需向工作人员发送消息即可。您是否找到了准确且CPU效率高的解决方案?因此,这不是一个论坛。请仅对实际答案使用“发布答案”按钮。若要评论或要求澄清,请在答案下方留下评论。非常感谢离开我的草坪!事实上,我确实在另一个Web Worker中安装了时钟生成器。这就是设置超时并在每次滴答声时发送信号的过程。我会试试你的建议。不确定它将如何执行一个无休止的循环困难…(当sequencer正在播放时)上面是我在plunker上使用的简化版本,plunker示例处理
bpm
ppq
,并将其发送到主线程。worker运行非常平稳,但是浏览器渲染不能始终保持,这是一个浏览器问题,而不是循环问题。嗯……在Web worker中,我可以像你一样进行无休止的循环,这表明
stop() {
    this.run = false;
}