Skip to content

Realtime Engine ​

Where StreamAnalyzer listens, the RealtimeEngine plays.

It is the transport-and-playback side of libsonare's realtime tools. It handles tempo, clip scheduling, a metronome, automation, lane mixing, capture, and meter/scope snapshots for the UI.

Use it when you are building a playback engine rather than just a live analyzer. If your app needs a playhead, clips, MIDI, or a mixer that runs while audio plays, this is the right layer. For the API, see Realtime Engine; the AudioWorklet bridge is in Realtime and Streaming.

Transport: the playhead and tempo ​

Transport is the playback clock — play/stop/seek, the current position, loop points, and the tempo that everything else is timed against. A clip scheduled "at bar 5" or an automation point "two beats in" only has meaning relative to the transport, so the transport is the shared timeline the whole engine reads from. After a loop wrap, seek or stop, the engine resends the controller, program and pitch-bend values that clips set before the new position, so playback resumes in the right state.

The engine keeps three positions side by side: a render-frame count that only moves forward, the timeline sample of the playhead, and the playhead's musical position in PPQ. The figure below follows all three through a stop, a tempo change and a loop wrap.

Three clocks over the same eight seconds
stoppedplaytempo 120 → 80 BPMloop endrender frame0384knever jumps; runs while stoppedtimeline sample0240kwraps to loop startPPQ (quarter notes)048slower tempo: beats spread outbeats048k96k144k192k240k288k336k384krender frames at 48 kHz
  • render frame
  • timeline sample
  • PPQ (quarter notes)
A 48 kHz engine stays stopped for 1 s, then plays a loop over PPQ 0–8 whose second bar is at 80 BPM instead of 120. Render frames count straight through the stop, the tempo change and the wrap. The timeline sample stands still while stopped and folds back at the loop end, as a seek would move it. PPQ follows the timeline, so after the tempo change each beat spans more samples.

Clips and scheduling ​

The engine plays clips — buffers of audio or sequences of MIDI events placed on the timeline at scheduled positions. Scheduling means deciding when a clip starts and stops relative to the transport, so playback can be assembled from many pieces rather than one continuous stream. This is how an arrangement, a backing track, or a rhythm-game chart is played back.

ENGINE · LANE MIXERIDLE
The engine lane mixer — faders and mutes inside the playback engine

Three MIDI clips loop through the realtime engine: each track occupies a lane with its own channel strip, and the lanes sum into a master strip carrying the engine’s true-peak limiter. The faders call the strip setters and the mutes call setSoloMute — every band below is the engine’s separate solo previews through the master chain, re-rendered through renderOffline as you move the controls.

Lead fader
0 dB
Bass fader
3 dB
Drums fader
-8 dB
Mute lead
Mute bass
Mute drums

The demo shows the same idea at a small scale: several lanes play together, the mixer combines them, and meters reflect the live output.

Metronome ​

A metronome generates click events locked to the transport's tempo and meter — useful for monitoring, recording, and lining up performance. Because it is derived from the same transport clock, it stays in sync with clips and automation automatically.

Telemetry: watching a realtime graph ​

You cannot inspect a realtime audio callback with a debugger without breaking timing. Instead the engine emits telemetry — periodic snapshots of meters and state — that your UI reads on a normal (non-audio) thread. This keeps the audio path untouched while still letting you draw level meters, a goniometer (a scope that plots the stereo phase relationship between left and right), or transport state.

How libsonare models the engine

RealtimeEngine works with EngineController: it owns the transport, schedules clips through ClipPlayer/ClipSchedule over ClipAudioBuffer storage, generates clicks with Metronome (MetronomeConfig → MetronomeEvent), and plays automation back on the mixer graph. Metering is reported through a MeterTelemetryTap producing MeterTelemetryRecords and a general Telemetry channel (TelemetryType/TelemetryErrorCode) that a UI reads off the audio thread. The whole engine is real-time-safe and runs the same offline (bounce) and inside an AudioWorklet, including freeze and capture paths.

Two read-outs size what a host has to wait for: tailSamples is a conservative upper bound, in samples, on the engine's longest audible tail, and graphLatencySamplesQ8 is its processing latency as a fixed-point count: divide by 256 to get samples (33,280 is 130 samples). Before an offline bounce or freeze the engine primes itself without rendering, so a second bounce equals the first; the steps are described under Offline render, bounce, and freeze.

Related: Realtime Engine, Realtime and Streaming, Streaming Analysis, Realtime Safety, Automation and Metering