midi_clock.h
MIDI clock: tempo and transport from the wire.
#include <sonore/midi_clock.h>- 1type
- Guides: MIDI and MPE
On this page
Design notes
Why this exists
A plugin inside a DAW is told the tempo: the host fills in a transport structure and every wrapper here passes it through. A plugin driven from a MIDI cable is told nothing. It receives a single byte, 0xF8, twenty-four times per quarter note, and whatever it wants to know about tempo it has to work out from how fast they arrive.
That is the case this covers: a hardware sequencer, a drum machine, another computer, or the standalone build being clocked by any of them.
Estimating a tempo from arrival times
The naive reading (one interval, converted directly) is unusable. MIDI clock is jittery by construction: it travels over a serial link at 31250 baud, it queues behind whatever else is being sent, and USB adds its own millisecond of scheduling. A tempo read from single intervals jumps by several BPM continuously, and a delay time computed from it warbles.
So the estimate is smoothed, and the amount of smoothing is the entire design decision here:
- Too little, and the tempo audibly wanders while the source is steady.
- Too much, and a real tempo change takes seconds to follow, which is worse: the sequencer has already moved and the plugin has not.
A one-pole over the last couple of dozen clocks lands close to a quarter note of averaging: enough that jitter disappears, short enough that a deliberate change arrives within a beat.
What a one-pole cannot do is tell a glitch from a change. One dropped byte doubles an interval and one duplicate splits it, and at 120 BPM those read 60 and 240 -- well inside any sane tempo range, and each pulled the estimate by several BPM. So a reading more than a quarter away from the current tempo is held back: a glitch happens once, a real change repeats, and four consistent readings in a row are believed outright.
Not a replacement for host transport
When a host provides a transport, believe the host. It knows the tempo exactly, it knows the bar position, and it is not guessing from arrival times. This is for when there is nothing better.
Types 1
Tempo and transport, derived from the realtime bytes in a MIDI stream and the Song Position Pointer that goes with them.
Call process() once per block with that block's MIDI and its length. Nothing allocates and nothing blocks; this is safe on the audio thread.