first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
# loudness-meter
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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<LoudnessResult>`.
- `analyzeStream(doc, opts?) => AsyncGenerator<LoudnessResult>`.
- `analyzeBlock(block, ctx) => Partial<LoudnessResult>`.
- `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.