Danny Bullo
Danny Bullo

Reputation: 75

Schedule Web MIDI Events with precision using WAAClock lib

I have read and implemented in the past something similar to what Chris Wilson described in his article "A Tale of Two Clocks": https://web.dev/audio-scheduling/

I recently found WAAClock which in theory implements the same principle: https://github.com/sebpiq/WAAClock

I'm working on a MIDI Web App and I want to send MIDI Clock Messages, which requires precise scheduling. I wrote this post in WebMidiJS forum (an amazing lib I'm using in my Project):

https://github.com/djipco/webmidi/discussions/333

Essentially this is my code:

const PULSES_PER_QUARTER_NOTE = 24;
const BPM_ONE_SECOND = 60;

let context = new AudioContext();
let clock = new WAAClock(context);

const calculateClockDelay = bpm => BPM_ONE_SECOND  / bpm /  PULSES_PER_QUARTER_NOTE; 

const startMidiClock = bpm => {
    clock.start();

    clock.callbackAtTime(function () {
        WebMidi.outputs.forEach(outputPort => outputPort.sendClock({}));
    }, 0).repeat(calculateClockDelay(bpm));`
}

const stopMidiClock = () =>  clock.stop();

As described in that posts, I CANNOT get the event to happen with high precision. I see the BPM Meter to slightly DRIFT. I tried sending MIDI Clock from a DAW and the timing is perfect.

I'm using ZERO as tolerance in the clock.callbackAtTime function.

Thanks a lot! Danny Bullo

Upvotes: 1

Views: 403

Answers (1)

cwilso
cwilso

Reputation: 13908

Eh. I'm not deeply familiar with that library - but at a quick glance, I am deeply suspicious that it can do what it claims to. From its code, this snippet makes me VERY suspicious:

  this._clockNode.onaudioprocess = function () {
    setTimeout(function() { self._tick() }, 0)
  }

If I understand it, this is trying to use the scriptprocessornode to get a high-stability, low-latency clock. That's not, unfortunately, something that scriptprocessornode can do. You COULD do something closer to this with audioworklets, except you wouldn't be able to call back out to the main thread to fire the MIDI calls.

I'm not sure you've fully grasped how to apply the TOTC approach to MIDI - but the key is that Web MIDI has a scheduler built in, too: instead of trying to call your Javascript code and outputPort.sendClock() at PRECISELY the right time, you need to schedule ahead a call with (essentially) outputPort.sendClock( time ) - that is, some small amount of time before the clock message needs to be sent, you need to call the MIDIOutput.send() with the timestamp parameter set - a schedule time for precisely when it needs to be sent.

This is gone over in more detail (for audio, though, not MIDI) in https://web.dev/audio-scheduling/#obtaining-rock-solid-timing-by-looking-ahead.

Upvotes: 3

Related Questions