Diagnostics
Diagnostics are observations, not a second processing pipeline. Record the smallest allocation-free facts on the realtime producer, then interpret and retain history on a control or message thread. Keep route state, input pressure, transport delivery, and analyzer history as separate domains.
Poll callback health
Section titled “Poll callback health”IPerformanceMonitor::getMetrics() returns an atomic snapshot of CPU usage, latency, underrun count, active clips, total samples processed, and uptime. Queries are intended to be safe from any thread and are designed for low overhead; a UI or message thread can poll them periodically. Peak CPU and callback timing histograms are separate monitor views and can be reset from a non-realtime owner according to the monitor’s lifetime rules.
RealtimeDiagnostics provides the smaller callback-side counter set: callback count, samples processed, underrun count, over-budget counts, and the last buffer/sample-rate pair. recordCallback() and reportUnderrun() perform relaxed atomic work without heap allocation, locks, file I/O, or callback ownership changes. Exactly one realtime producer may record or report at a time. Reset only after that producer has stopped.
Neither interface owns an analyzer timeline. The performance monitor explicitly leaves audio-level metering to routing, and callback diagnostics are bounded counters rather than a waveform, FFT, or UI history.
Source: performance_monitor.h, performance_monitor.h, and realtime_diagnostics.h.
Drain fixed-capacity transport telemetry
Section titled “Drain fixed-capacity transport telemetry”Use getRealtimeTelemetry() for the transport’s fixed-capacity single-producer/single-consumer bridge. The audio callback calls beginRealtimeBlock(), reports source/cache underruns, and publishes due fixed-size snapshots. The message-thread consumer calls tryRead() and owns all work after the read.
The bridge retains one snapshot after its default decimation cadence of eight callbacks and has capacity for 64 snapshots. A full ring drops the new snapshot and increments its drop count; sequence gaps identify dropped capture attempts. Poll pendingSnapshotCount() and droppedSnapshotCount() alongside the snapshots so a quiet UI does not hide pressure.
A snapshot contains canonical post-block sample position, realtime diagnostics, active voice count, group/output meters, and routing-meter availability. It is intentionally presentation-neutral: build meter histories, smoothing, FFTs, and view models after tryRead() on the message thread. Do not retain the transport-owned bridge past controller destruction, and do not add a second consumer.
Source: realtime_telemetry.h, realtime_telemetry.h, and realtime_telemetry.h.
Diagnose transport delivery and capacity
Section titled “Diagnose transport delivery and capacity”Transport event delivery has its own audio-to-control SPSC queue. processCallbacks() drains it from exactly one control/message thread and translates events into host callbacks away from the audio thread. Poll callback-delivery telemetry separately: attempted, posted, dropped, and snapshot-watermark sequences are cumulative and do not reset when callbacks are drained. Saturation and sequence gaps are evidence of delivery pressure, not proof that the transport stopped rendering.
Control-to-audio command ingress is a different bounded system. Poll getCommandIngressTelemetry() for attempted, admitted, slot-unavailable, publication-contention, preparation-rejected, and processed counts. Queue refusal is distinct from preparation failure. The counters are independent while activity is in flight and saturate at their defined limits, so interpret them after quiescence when comparing partitions.
Active-voice and tagged-start snapshots provide bounded reconciliation when event delivery is incomplete. They describe the current published state and retained settlement window; they are not an unbounded event log.
Source: transport_controller.h, transport_controller.h, and transport_controller.h.
Diagnose route and input failures
Section titled “Diagnose route and input failures”Backend telemetry is control-thread data. AudioIoTelemetry reports cumulative input render failures, route outcome, input FIFO overruns/underruns, input/output conversion failures, and an optional platform error code. A terminal route outcome means rendering stopped and requires explicit host-selected reinitialization; it does not authorize automatic endpoint substitution.
For capture, IAudioInputStream::capture() is the audio-side producer operation and is all-or-nothing for a requested frame block: a return of zero means the block was dropped and the overflow counter records pressure. A background consumer calls drain() and checks framesPending() and overflowCount(). Allocate the ring before starting capture; never make the callback write a file or wait for a consumer.
Separate these observations in diagnostics:
- route availability, negotiation, permission, and conversion failures;
- input FIFO overflow or underrun caused by producer/consumer pressure;
- transport source underruns that render silence;
- callback budget counters and CPU/latency snapshots;
- routing meter values and any host-owned analyzer history.
Source: audio_driver.h, audio_driver.h, and audio_input.h.