Skip to content

Clock & Alignment

MIDI leaves resonon for gear that has its own timing: a DAW, a drum machine, a CV interface, a synth with its own latency. This page explains how resonon shares its clock with that gear, and how it times what it sends so it lands with the audio.

A session either sends clock and is the tempo’s source, or follows a clock from elsewhere. Doing both would mean two sources for one tempo, so turning one on turns the other off.

Sending clock takes two switches: set_midi_clock_send(true) for the session, and send_clock = true on each port that should receive it. Ports don’t receive clock unless they ask for it, so adding a port to a rig never starts driving a device that was meant to run on its own clock.

Which devices take clock is a fact about the rig, not about the piece, so it is written in the port’s entry in ~/.resonon/preferences.toml, beside the port, rather than in code (The Machine and the Piece).

A port that starts receiving clock while the music plays — because clock was turned on, or because the port opted in — is sent the current position and Continue, so it picks up where resonon is rather than from the top. A SEEK while playing does the same for every port: Stop, the new position, and Continue, where the play head lands. The ticks that follow sit on the same grid as the notes. Gear that divides clock down to an analogue pulse needs exactly that to land on the beat. A port that stops receiving clock is sent Stop, rather than left waiting for ticks that no longer come.

MIDI clock is 24 ticks per quarter note, and every tick arrives with some timing noise. A follower estimates the tempo from all of them, with a filter that measures the port’s own noise: a clean source is followed quickly and precisely, a jittery one quietly. A tempo jump — a DAW switching tempo — is recognised as a jump and taken within a few ticks. A nudge or a ramp is followed within a bar or two.

Start restarts from the top, Stop pauses and Continue resumes. A song position sent before Continue is honoured exactly, to the sixteenth, as Live does. As MIDI asks of a receiver, playback starts on the first tick after Start or Continue, at the moment that tick was stamped, not when resonon got round to reading it. The first notes can sound a few milliseconds late, but the beat they play on is the source’s.

From then on, every tick measures how far the play head is from the source. That difference is not corrected by moving the play head, which would be heard as a jump. Instead the play head runs up to half a percent faster or slower until it is back on the source’s beat. Half a percent can’t be heard, even on a tempo-synced delay. A buffer’s worth of drift, say after a dropout, is gone in two or three seconds. Ticks that arrive late or never arrive are filtered out before they reach the measurement.

A SEEK while following is yours: resonon keeps it, lines it up with the nearest tick, and doesn’t pull it back to where the source thinks the song is. midi_clock_report() shows the measured phase error and how hard the play head is being steered.

Audio takes time to reach your speakers: the buffer, the converter, and the delay of any plugin with lookahead. A note sent the moment it is scheduled would sound early against the mix. So every note, CC value and clock tick is timestamped for the moment the audio it belongs with reaches the converter, plugin delay included. Inserting a lookahead limiter does not shift your hardware against the mix.

That leaves what differs from device to device: one synth speaks a little later than another, and some gear settles on clock differently than on notes. Those corrections belong to the device, so they sit in its port entry, as trim_ms and clock_trim_ms, and stay with the port for as long as the entry does.

The automatic part can be turned off with set_midi_latency("manual"), leaving only the trims. Notes you play through a track never get the automatic part (Monitoring & Thru).