first commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user