Skip to content

Recording & Latency

Recording is where timing stops being abstract. You play along to sound that has already left the speakers, and what you play reaches the machine later still. This page explains how resonon keeps input and output on one timeline, and how it places a take where it was performed.

The piece names channels, the machine names devices

Section titled “The piece names channels, the machine names devices”

A track takes its input as channel numbers — .set_input(0), .set_input(2, 3) — counting from 0, the way Output() does. The interface those channels belong to is [audio] input in ~/.resonon/preferences.toml, never in the piece. Channel numbers are what make a project portable: the same script runs on another rig, which may have a different interface but still has a channel 0. The device name is a fact about your machine (see The Machine and the Piece).

The input device is opened at startup, before any of your code runs, so a rig configured for an interface monitors through its converters from the first moment rather than through the built-in microphone until something asks.

On macOS resonon runs playback and capture through one CoreAudio device, on one clock — one IO callback, and no buffer between what your microphone hears and what the mix renders against it.

Name two different devices and it still does. [audio] input and [audio] output on separate interfaces are joined into a private aggregate device, built for the session and destroyed with it: the output is the clock, and macOS drift-corrects the input against it. Recording from the built-in microphone while playing out of the built-in speakers — two CoreAudio devices on every Mac — is an ordinary rig.

A pair costs two things one interface does not. The block size is whatever both accept, so [audio] buffer_size is bounded by the stricter of the two. And they have to share a sample rate.

A device has one nominal rate, and no amount of clock syncing changes that. So a session runs at a single sample rate, and resonon picks one that both directions can run at: an interface whose microphone runs only at 44.1 kHz while its outputs also do 48 opens the session at 44.1.

Writing [audio] sample_rate makes that decision yours instead, and a rate you asked for is never overridden to accommodate a microphone. If the input can’t join it, the input is refused, with the rates it does run at — playback is unaffected. You never choose a rate for your microphone; you choose one for the session, and the input follows.

A recorded clip is a reference into the audio that was captured, not a copy of it — the same thing a clip is in a DAW. It names the recording, the frame it starts at and how long it runs, so trimming a clip’s head costs nothing and throws nothing away.

Every pass you record is kept. Collecting takes hands them back one pass at a time, oldest first — the order they were performed, and the order an arrangement would show them in.

A take recorded while playing back arrives late by the output latency plus the input latency. Resonon measures both from the device’s own timestamps and corrects for them automatically. Plugin delay compensation is part of it: a lookahead limiter on the master pushes the mix you play along to further back, and a take recorded against it is corrected by that much more — the same term MIDI output is aligned by.

The correction moves where the take begins. It never deletes audio: the recording behind a clip holds every frame that was captured, and the clip starts at the corrected frame. If the correction is a hair too large, an attack transient stays recoverable rather than gone.

It is also what places the take. A clip’s position is where it was performed — the moment the audio reached the machine, plus the correction — so a take doesn’t have to be lined up by hand against what it was played over. That position is rarely on a cycle line, which is why an arrangement places clips off the grid.

Some interfaces report a latency that is not the one they have. [audio] recording_offset_ms is the residual correction for that — a property of the interface, so it lives in preferences, not in the piece. A number beyond ±500 ms is refused rather than clamped, because a number that large is a typo, not a rig.

While a track monitors its input, resonon bypasses its plugin-delay-compensation alignment delay. The input still runs through the track’s effects, but the artificial delay that lines it up with higher-latency tracks is skipped — so you hear yourself promptly even when other tracks host heavy lookahead plugins. What you hear then is the two converters and your buffer, and nothing else.

If the machine stalls long enough that captured audio can’t reach the recorder, the missing frames are held open as silence rather than deleted, so the take keeps its length and everything after the gap stays where it belongs. A stall longer than the input buffer can represent (~200 ms) is beyond that, and is reported as input_record_align_lost.