# loudness-meter _(no submodules)_ ## Purpose `LoudnessMeter` is the orchestration layer of `@suaveplan/audio-analysis-loudness`. It wires the K-weighting filter, the gated-integration engine, the short-term LUFS accumulator (for loudness range), the 4× oversampled true-peak detector, and the RMS / sample-peak / DC-offset accumulators together behind three entry points — full-document `analyze`, streaming `analyzeStream`, and the real-time `analyzeBlock`. It is a sidecar processor (plugin slot 3) and never participates in the render pipeline. The class also threads OTel spans, the run counter, and per-block histograms through every entry point, validates all options at the Zod boundary, and propagates `AbortSignal` cancellation to mid-stream block boundaries. ## Features - ITU-R BS.1770-4 integrated LUFS via two-gate (absolute + relative). - EBU Tech 3342 loudness range (LRA in LU). - 4× oversampled true-peak in dBTP (per BS.1770-4 Annex 2). - Sample peak in dB, RMS in dB, per-channel DC offset. - Streaming `analyzeStream` whose final yield equals batch `analyze`. - Real-time `analyzeBlock` for live meters — synchronous, allocation-light. - Finite silence floors (no `-Infinity` ever leaks to consumers). - `AbortSignal` propagation at gating-block boundaries. - OTel span, counter, histograms — inert without an exporter. ## Usage: Basic ```ts import { LoudnessMeter } from "@suaveplan/audio-analysis-loudness"; const meter = new LoudnessMeter(); const result = await meter.analyze(doc); console.log(result.lufs, result.truePeak, result.loudnessRange); ``` ## Usage: Advanced ```ts import { LoudnessMeter } from "@suaveplan/audio-analysis-loudness"; const meter = new LoudnessMeter({ blockSize: 400, overlap: 0.75, targetLufs: -23 }); // Streaming integrated loudness — final yield equals batch analyze. const ctrl = new AbortController(); for await (const partial of meter.analyzeStream(doc, { signal: ctrl.signal })) { ui.updateLoudness(partial.lufs); } // Real-time block meter — call on every audio callback. const partial = meter.analyzeBlock([left, right], { sampleRate: 48000, channelCount: 2, }); console.log(partial.lufs, partial.peak, partial.rms); ``` ## API Reference - `class LoudnessMeter` - `readonly name: "loudness-meter"`, `readonly description: string`, `readonly version: string`. - `constructor(options?: LoudnessMeterOptions)`. - `analyze(doc, opts?) => Promise`. - `analyzeStream(doc, opts?) => AsyncGenerator`. - `analyzeBlock(block, ctx) => Partial`. - `LoudnessMeterOptions` — `blockSize`, `overlap`, `targetLufs`, `signal`. - `LoudnessResult` extends `AnalysisResult` with `lufs`, `loudnessRange`, `truePeak`, `rms`, `peak`, `dcOffset[]`. ## Implementation Notes `analyze` and `analyzeStream` share one `GatingEngine`. The streaming path yields the engine's running state after every gating block; the batch path runs the same engine to completion. The invariant is that the last `analyzeStream` yield equals the `analyze` result for the same document. `analyzeBlock` keeps a per-instance scratch K-weighting filter so live-meter callers do not allocate on every audio callback. The scratch filter is re-built lazily when `(sampleRate, channelCount)` changes.