JavaScript: альтернатива setTimeOut для таймера FAST в приложении MIDI Sequencer

1

Я работаю над Javascript Music App, который включает в себя секвенсор. Для тех, кто не знаком, MIDI-секвенсоры работают примерно так: есть что-то, называемое PPQ: импульсы за четверть ноты. Каждый импульс называется "Tick". В нем показано, как могут быть "подразделения" на четверть ноты, например, разрешение. Итак, секвенсоры "играют" события, которые находятся в треках по одному тику за раз: играйте Tick1, ждите Tick Duration, Play tick2, Tick Duration и т.д.

Теперь скажем, что у нас есть BPM (Beats per Min) 120 с PPQ= 96 (стандарт). Это означает, что каждая продолжительность четвертной ноты составляет 500 мс, а каждая продолжительность тика - 5,20833 мс.

Какие альтернативы Timer у нас есть в Javascript?

1) У нас есть старый setTimeOut. У этого есть несколько проблем: мин. время ожидания - 4 мс. (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Minimum_delay_and_timeout_nesting) Он также зависит от JITTER/time Variations. Он не является точным и требует, поскольку обратные вызовы сложены в четном цикле.

2) Существует альтернатива setTimeOut/setInterval, которая включает использование requestAnimationFrame(). Это ОЧЕНЬ точный и эффективный процессор. Однако минимальное время, которое может быть установлено, составляет около 16,7 мс (продолжительность кадра в типичном мониторе 60FPS)

Есть ли другая альтернатива? Чтобы точно спланировать событие каждые 2-5 мс?

Примечание: функция, выполняемая в конце цикла, playEventsAtTick() НЕ требует вообще, поэтому никогда не потребуется больше времени для выполнения, чем Tick Duration.

Спасибо! Дэнни Булло

  • 0
    использовать бесконечное во while цикла в процессе работы, и просто отправлять сообщения / от работника.
Теги:
timer
settimeout
requestanimationframe
midi

4 ответа

1

Чтобы поддерживать какое-либо здравомыслие в этом, вы захотите сделать обработку звука на посвященный поток. А еще лучше, использовать Web Audio API, и пусть люди, которые думают об этих проблемах в течение длительного времени делать тяжелую работу образца-точность.

Также проверьте веб-MIDI (только хром).

0

Выйдите из моей лужайки: предложенный вами подход не полностью работает. Позвольте сказать, что я добавляю к веб-рабочему метод STOP Sequencer:

stop() {
    this.run = false;
}

Проблема в том, что метод myWorker.onmessage = function (e) {...} никогда не запускается. Я подозреваю, что это связано с тем, что поток веб-рабочих "TOO BUSY" с бесконечным циклом. любой способ решить это?

Кроме того, во время игры он работает..... но процессор значительно повышается..... Единственным возможным решением может быть метод Sleep(), но Real SLEEP, который не существует в Javascript...

Спасибо

0

В отдельном потоке, таком как веб-рабочий, вы можете создать бесконечный цикл. В этом цикле все, что вам нужно сделать, это вычислить время между ударами. По истечении времени вы можете отправить сообщение в основной процесс, сделать некоторые визуальные эффекты, воспроизвести звук или что бы вы хотели сделать.

Вот рабочий пример

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()

Затем мы можем создать индексную страницу, в которой будет выполняться рабочий, и каждый раз, когда сообщение возвращается от рабочего, будет мигать квадрат.

<!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
    Большое спасибо Получить с моей лужайки! На самом деле, у меня действительно есть ГЕНЕРАТОР ЧАСОВ в другом веб-работнике. Это процесс, который имел setTimeOut и отправлял сигнал на каждом тике. Попробую ваше предложение. Не уверен, как он будет работать, чтобы иметь бесконечный жесткий цикл ... (пока секвенсор играет)
  • 0
    Выше приведен упрощенный вариант того, что я положил на plunker, пример plunker обрабатывает как bpm и ppq и отправляет это в основной поток. Работник работает довольно гладко, однако рендеринг браузера не всегда может идти в ногу, что является проблемой браузера, а не проблемой цикла.
Показать ещё 2 комментария
0

Спасибо nvioli. Я знаю API веб-аудио. Однако я не думаю, что это может помочь здесь. Я напрямую не запускаю AUDIO: у меня есть MIDI-события (или пусть просто "СОБЫТИЯ"), хранящиеся в TRACKS. И эти события происходят в любом TICK. Таким образом, Sequencer должен зацикливать каждую Tick Duration для сканирования того, что нужно играть на этом конкретном тике.

С уважением, Дэнни Булло

  • 2
    ТАК не форум. Пожалуйста, используйте кнопку Разместить ответ только для актуальных ответов. Чтобы критиковать или запросить разъяснения, оставьте комментарий под этим ответом.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню