Goal
Ship one shared render-thread telemetry layer that publishes lock-free scalars, and build two synergistic features on top of it: (1) a Live HUD — input/output level, sample-peak + a CLIP indicator, an "AI working hard" confidence signal, and added latency in ms; and (2) an integrated LUFS loudness meter (ITU-R BS.1770 K-weighting) with an optional target-loudness normalization stage (−14 / −16 LUFS) bounded by a true-peak-style ceiling that extends the existing Limiter.
Architecture
A new pure value type LoudnessMeter (Core/AudioProcessing) implements real ITU-R BS.1770 K-weighting (high-shelf head stage + RLB high-pass stage, published 48 kHz biquad coefficients) → mean-square → gated integration, plus sample-peak tracking — headless-testable, validated at multiple frequencies. Render/DSP threads publish metering via plain lock-free scalar snapshots on AudioModel (atomic 32-bit loads/stores on arm64, no locks). The LoudnessMeter value is never read cross-thread: mutated only on the render thread, which copies plain getters into scalar snapshots (tMomentaryLUFS, tIntegratedLUFS, tLoudnessSamplePeak) the UI timer reads. A ~25 Hz timer snapshots scalars into @Published properties for SwiftUI. The HUD reuses MeterView; loudness normalization adds a pre-limiter auto-gain stage in VoiceChain, gated by a persisted mv.* toggle + target.
Plan
docs/plans/2026-06-15-metering-and-loudness.md (Codex-reviewed, approved)
Goal
Ship one shared render-thread telemetry layer that publishes lock-free scalars, and build two synergistic features on top of it: (1) a Live HUD — input/output level, sample-peak + a CLIP indicator, an "AI working hard" confidence signal, and added latency in ms; and (2) an integrated LUFS loudness meter (ITU-R BS.1770 K-weighting) with an optional target-loudness normalization stage (−14 / −16 LUFS) bounded by a true-peak-style ceiling that extends the existing
Limiter.Architecture
A new pure value type
LoudnessMeter(Core/AudioProcessing) implements real ITU-R BS.1770 K-weighting (high-shelf head stage + RLB high-pass stage, published 48 kHz biquad coefficients) → mean-square → gated integration, plus sample-peak tracking — headless-testable, validated at multiple frequencies. Render/DSP threads publish metering via plain lock-free scalar snapshots onAudioModel(atomic 32-bit loads/stores on arm64, no locks). TheLoudnessMetervalue is never read cross-thread: mutated only on the render thread, which copies plain getters into scalar snapshots (tMomentaryLUFS,tIntegratedLUFS,tLoudnessSamplePeak) the UI timer reads. A ~25 Hz timer snapshots scalars into@Publishedproperties for SwiftUI. The HUD reusesMeterView; loudness normalization adds a pre-limiter auto-gain stage inVoiceChain, gated by a persistedmv.*toggle + target.Plan
docs/plans/2026-06-15-metering-and-loudness.md(Codex-reviewed, approved)