0013 — Latency telemetry is measured in the audio engine
Status: Accepted (built — 2026-07-29 note below) Date: 2026-06-29
Context
A live operator needs to know the exact latency each step and each plugin adds, in real time — to manage delay compensation, to align the PA against the stage, to spot a plugin that just blew the monitor-path budget. Latency could be estimated in the UI from plugin nameplate figures, but that is a guess: the real numbers depend on the live graph — the quantum, the sample rate, each plugin's reported latency port, and how many async links a path crosses.
The audio engine is the only component that owns the graph and can read those numbers directly.
Decision
Measure latency in the audio engine, at its source, and stream it out — do not estimate it elsewhere.
- Per plugin — read the LV2
lv2:latency-designated output port where the plugin reports it (mod-host exposes the port value). Where a plugin does not report, mark it0/unknown — do not guess. - Per step/node — PipeWire node processing latency, plus the base buffering latency
quantum / sampleRateper link. - Per path — sum input → inserts → bus → output in milliseconds, with the per-element breakdown retained.
- Health — xrun/underrun counter, current quantum and sample rate.
The engine exposes getLatencyReport(); the server polls it a few times a second and
broadcasts telemetry.*; the UI shows a per-channel total badge that expands to the
per-plugin/per-step breakdown, with the mains/monitor path shown prominently and a live
xrun counter.
Consequences
- The numbers are real, not nameplate estimates, because they come from the live graph.
- It is the natural place for delay compensation to live too: the engine that measures the
per-path latency is the one that inserts
delaynodes where parallel paths reconverge. - Telemetry is a low-rate, separate stream (a few Hz), kept off the per-control message path — meters and latency do not bloat every fader frame.
- It depends on plugins honestly designating their latency port; unknown is surfaced as unknown, which is the honest failure mode.
- Verifying real PipeWire/plugin latency values needs the actual mixing host; this is a documented test gate, not something CI can assert without hardware.
Note — 2026-07-29: built
getLatencyReport() is a real contract (the LatencyReportSource interface in
packages/core/src/telemetry.ts) with a real implementation in the engine
(SoftwareMixer.getLatencyReport in packages/audio-engine/src/software-mixer.ts), and
the periodic report is polled by TelemetryBroadcaster
(packages/server/src/telemetry.ts) and served at GET /telemetry/latency?watch=1.
Measurement stays in the engine, as decided.