first commit
This commit is contained in:
56
docs/media/audio/audio-analysis-loudness/README.md
Normal file
56
docs/media/audio/audio-analysis-loudness/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# @suaveplan/audio-analysis-loudness - Universal ITU-R BS.1770-4 loudness analyser: K-weighting, gated integrated LUFS, momentary and short-term loudness, EBU R128 loudness range, 4x oversampled true-peak detection, RMS, sample peak, and per-channel DC offset. Streaming and block-level metering for broadcast compliance, mastering, and auto-normalisation pipelines.
|
||||
|
||||
Universal ITU-R BS.1770-4 loudness analyser. Drop-in `AnalysisOp` plugin for
|
||||
the `@suaveplan/audio-*` family.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [loudness-range](./src/algorithms/loudness-range.md) — `loudness-range` computes the EBU Tech 3342 loudness range (LRA, in LU) — a statistical measure of dynamic spread. It t…
|
||||
- [lufs](./src/algorithms/lufs.md) — `lufs` contains the kernel of BS.1770-4 loudness measurement: the per-block mean-square energy calculation, the BS.1770…
|
||||
- [true-peak](./src/algorithms/true-peak.md) — `true-peak` implements true-peak (dBTP) detection via 4× polyphase oversampling, per ITU-R BS.1770-4 Annex 2. Sample pe…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — This module owns the stable, machine-readable error-code registry for `@suaveplan/audio-analysis-loudness`. Every fault…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — This module owns the stable, machine-readable error-code registry for `@suaveplan/audio-analysis-loudness`. Every fault…
|
||||
- [filters](./src/filters/k-weighting.md) — `k-weighting` implements the ITU-R BS.1770 K-weighting filter — a two-stage IIR biquad cascade applied per channel befo…
|
||||
- [k-weighting](./src/filters/k-weighting.md) — `k-weighting` implements the ITU-R BS.1770 K-weighting filter — a two-stage IIR biquad cascade applied per channel befo…
|
||||
- [meter](./src/meter/loudness-meter.md) — `LoudnessMeter` is the orchestration layer of `@suaveplan/audio-analysis-loudness`. It wires the K-weighting filter, th…
|
||||
- [loudness-meter](./src/meter/loudness-meter.md) — `LoudnessMeter` is the orchestration layer of `@suaveplan/audio-analysis-loudness`. It wires the K-weighting filter, th…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Features
|
||||
|
||||
- Integrated LUFS via two-gate algorithm (absolute −70, relative −10 LU).
|
||||
- Momentary (400 ms) and short-term (3 s) loudness.
|
||||
- EBU Tech 3342 loudness range (LRA in LU).
|
||||
- 4× oversampled true-peak detection (dBTP, per BS.1770-4 Annex 2).
|
||||
- Sample peak (dB), RMS (dB), per-channel DC offset.
|
||||
- Streaming `analyzeStream` whose final yield equals batch `analyze`.
|
||||
- Real-time `analyzeBlock` for live meters.
|
||||
- Finite silence floors — no `-Infinity` ever leaks to consumers.
|
||||
- OTel span `audio.analysis.loudness`, counter, histograms.
|
||||
- Pure universal: no `window`, no `document`, no `node:fs`.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/audio-analysis-loudness
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```ts
|
||||
import { LoudnessMeter } from "@suaveplan/audio-analysis-loudness";
|
||||
|
||||
const meter = new LoudnessMeter();
|
||||
const result = await meter.analyze(doc);
|
||||
console.log({
|
||||
integrated: result.lufs,
|
||||
range: result.loudnessRange,
|
||||
truePeak: result.truePeak,
|
||||
});
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# loudness-range
|
||||
|
||||
## Purpose
|
||||
|
||||
`loudness-range` computes the EBU Tech 3342 loudness range (LRA, in LU) — a
|
||||
statistical measure of dynamic spread. It takes short-term loudness samples
|
||||
(K-weighted, channel-summed loudness over 3 s windows stepped by 1 s) and
|
||||
returns the 95th-minus-10th percentile spread of the gated distribution.
|
||||
|
||||
LRA complements integrated loudness: two programmes can share an integrated
|
||||
LUFS but have wildly different listener experience if one is uniformly
|
||||
compressed and the other has dynamic swings. Broadcasters use LRA thresholds
|
||||
(e.g. EBU R128 recommends ≤ 7 LU for spoken-word programming) to flag
|
||||
material that needs dynamics processing before transmission.
|
||||
|
||||
## Features
|
||||
|
||||
- Two-gate algorithm: absolute gate at −70 LUFS, relative gate 20 LU below
|
||||
the gated mean (wider than the BS.1770-4 integrated-loudness 10 LU gate).
|
||||
- Linear-interpolated percentiles per Tech 3342.
|
||||
- Returns `lra`, `low`, `high`, and `gatedCount` for downstream UIs.
|
||||
- Sub-window documents safely report `lra: 0` instead of throwing.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { loudnessRange } from "@suaveplan/audio-analysis-loudness/algorithms/loudness-range";
|
||||
|
||||
const { lra } = loudnessRange(shortTermLufsValues);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
loudnessRange,
|
||||
percentile,
|
||||
} from "@suaveplan/audio-analysis-loudness/algorithms/loudness-range";
|
||||
|
||||
const result = loudnessRange(shortTermLufsValues);
|
||||
console.log({
|
||||
lra: result.lra,
|
||||
p10: result.low,
|
||||
p95: result.high,
|
||||
surviving: result.gatedCount,
|
||||
});
|
||||
|
||||
// Inspect arbitrary percentiles for diagnostic overlays.
|
||||
const sorted = [...shortTermLufsValues].sort((a, b) => a - b);
|
||||
const p50 = percentile(sorted, 0.5);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `LRA_RELATIVE_GATE_OFFSET_LU` — `-20`, the Tech 3342 relative-gate offset.
|
||||
- `loudnessRange(shortTermLufs: readonly number[]) => LoudnessRangeResult`.
|
||||
- `percentile(sortedAsc, q)` — linear-interpolated q ∈ [0,1].
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The mean used for the relative gate is computed in linear power and then
|
||||
converted back to LUFS via `energyToLufs`, not as an arithmetic mean of LUFS
|
||||
values — averaging in dB would over-weight quiet blocks. Edge cases (no
|
||||
input, no survivors after the absolute gate, no survivors after the relative
|
||||
gate) all map cleanly to `lra: 0` with the best-available `low`/`high` so
|
||||
downstream consumers never see NaN.
|
||||
@@ -0,0 +1,57 @@
|
||||
# lufs
|
||||
|
||||
## Purpose
|
||||
|
||||
`lufs` contains the kernel of BS.1770-4 loudness measurement: the per-block
|
||||
mean-square energy calculation, the BS.1770 channel-weighted summation, the
|
||||
energy-to-LUFS conversion, and the streaming `IntegratedLoudness` accumulator
|
||||
that implements the two-gate algorithm (absolute gate at −70 LUFS, then a
|
||||
relative gate 10 LU below the ungated mean of survivors). The kernels are
|
||||
document-agnostic — they consume `Float32Array[]` blocks and numbers — so a
|
||||
true-peak limiter can pull `momentaryLufs` without instantiating a meter.
|
||||
|
||||
## Features
|
||||
|
||||
- Pure functions: `blockMeanSquare`, `combineChannelEnergies`,
|
||||
`energyToLufs`, `momentaryLufs`, `shortTermLufs`.
|
||||
- Streaming `IntegratedLoudness` class with two-gate integration.
|
||||
- BS.1770 channel weights — L/R/C at 1.0, surround at 1.41, LFE excluded.
|
||||
- Finite silence floor (`LUFS_SILENCE_FLOOR = -100`) instead of `-Infinity`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { momentaryLufs } from "@suaveplan/audio-analysis-loudness/algorithms/lufs";
|
||||
|
||||
const lufs = momentaryLufs(kWeightedBlock);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { IntegratedLoudness } from "@suaveplan/audio-analysis-loudness/algorithms/lufs";
|
||||
|
||||
const integrator = new IntegratedLoudness(2);
|
||||
for (const block of kWeightedGatingBlocks) integrator.pushBlock(block);
|
||||
const integrated = integrator.integrated();
|
||||
console.log({ integrated, surviving: integrator.gatedBlockCount });
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `LUFS_SILENCE_FLOOR`, `ABSOLUTE_GATE_LUFS`, `RELATIVE_GATE_OFFSET_LU`,
|
||||
`CHANNEL_WEIGHTS_BS1770` — frozen constants.
|
||||
- `channelWeight(index)`, `blockMeanSquare(block)`,
|
||||
`combineChannelEnergies(perChannel)`, `energyToLufs(weightedMS)`,
|
||||
`momentaryLufs(block)`, `shortTermLufs(block)`.
|
||||
- `class IntegratedLoudness` — `pushBlock`, `integrated`, `reset`,
|
||||
`gatedBlockCount`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The integrator stores the linear weighted mean-square values that survived
|
||||
the absolute gate rather than their LUFS counterparts, because the relative
|
||||
gate is itself derived from the linear mean. This avoids a second
|
||||
`Math.pow(10, …)` pass per block. The silence floor is the BS.1770-4 minimum
|
||||
reported value (−70 LUFS for "no programme") rounded down to −100 to give
|
||||
downstream UI meters a stable bottom.
|
||||
@@ -0,0 +1,56 @@
|
||||
# true-peak
|
||||
|
||||
## Purpose
|
||||
|
||||
`true-peak` implements true-peak (dBTP) detection via 4× polyphase
|
||||
oversampling, per ITU-R BS.1770-4 Annex 2. Sample peak — `max |x[n]|` — under-
|
||||
reports the real analog peak because the true waveform crests *between*
|
||||
samples; near-Nyquist signals routinely show inter-sample peaks 1–3 dB above
|
||||
the sample peak. Broadcast normalisation pipelines rely on dBTP to keep
|
||||
material clear of the digital ceiling after DAC reconstruction.
|
||||
|
||||
## Features
|
||||
|
||||
- 4-phase polyphase FIR with 12-tap kernel per phase.
|
||||
- `truePeakDbtp(block)` — true-peak across all channels in dBTP.
|
||||
- `samplePeakLinear(block)` — sample peak in linear amplitude.
|
||||
- Finite floor at −200 dBTP for digital silence (no `-Infinity`).
|
||||
- Edge-aware: taps falling outside the buffer are zero-padded.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { truePeakDbtp } from "@suaveplan/audio-analysis-loudness/algorithms/true-peak";
|
||||
|
||||
const dbtp = truePeakDbtp([leftChannel, rightChannel]);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
truePeakDbtp,
|
||||
samplePeakLinear,
|
||||
} from "@suaveplan/audio-analysis-loudness/algorithms/true-peak";
|
||||
|
||||
const tp = truePeakDbtp(channels);
|
||||
const sp = 20 * Math.log10(samplePeakLinear(channels));
|
||||
console.log({ samplePeakDb: sp, truePeakDbtp: tp, intersampleMargin: tp - sp });
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `truePeakDbtp(block: readonly Float32Array[]) => number` — dBTP across all
|
||||
channels.
|
||||
- `samplePeakLinear(block: readonly Float32Array[]) => number` — linear-
|
||||
amplitude sample peak.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The polyphase kernel is a fixed 12-tap windowed-sinc low-pass (Kaiser β=8,
|
||||
cutoff 0.45·Nyquist), normalised per phase to unity DC gain. Phase 0 is the
|
||||
identity phase so the original samples are always included in the search and
|
||||
`truePeakDbtp ≥ samplePeakDb` always holds. BS.1770-4 permits higher
|
||||
oversampling ratios; the meter sticks to the specified minimum of 4× because
|
||||
it is sufficient up to 48 kHz material — a configurable ratio would add
|
||||
surface area for negligible accuracy gain in the target range.
|
||||
@@ -0,0 +1,77 @@
|
||||
# error-codes
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module owns the stable, machine-readable error-code registry for
|
||||
`@suaveplan/audio-analysis-loudness`. Every fault raised by the loudness
|
||||
analyser is reported through a single `AudioAnalysisLoudnessError` subclass of
|
||||
`SuaveplanError`, carrying a code drawn from `AUDIO_ANALYSIS_LOUDNESS_CODES`
|
||||
and rich context such as Zod issue lists or abort reasons.
|
||||
|
||||
The registry exists so downstream observability, alerting, and i18n layers
|
||||
can bind to stable string identifiers instead of fragile message strings.
|
||||
Codes never change once shipped — new fault classes get new codes, never
|
||||
re-purposed old ones.
|
||||
|
||||
## Features
|
||||
|
||||
- Three stable codes: `INVALID_OPTIONS`, `EMPTY_DOCUMENT`, `ABORTED`.
|
||||
- Frozen metadata table (`description`, `severity`) keyed by code.
|
||||
- `AudioAnalysisLoudnessError` subclass with public constructor and a
|
||||
`category` discriminant that satisfies the no-suppression-comment rule
|
||||
without any `// biome-ignore`.
|
||||
- Idempotent `registerAudioAnalysisLoudnessErrorCodes()` hook.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
AUDIO_ANALYSIS_LOUDNESS_CODES,
|
||||
AudioAnalysisLoudnessError,
|
||||
} from "@suaveplan/audio-analysis-loudness/error-codes";
|
||||
|
||||
throw new AudioAnalysisLoudnessError(
|
||||
AUDIO_ANALYSIS_LOUDNESS_CODES.EMPTY_DOCUMENT,
|
||||
"audio-analysis-loudness.empty-document",
|
||||
{ context: { sourceLength: 0 } },
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
AUDIO_ANALYSIS_LOUDNESS_CODES,
|
||||
AUDIO_ANALYSIS_LOUDNESS_ERROR_METADATA,
|
||||
registerAudioAnalysisLoudnessErrorCodes,
|
||||
} from "@suaveplan/audio-analysis-loudness/error-codes";
|
||||
|
||||
registerAudioAnalysisLoudnessErrorCodes();
|
||||
|
||||
for (const code of Object.values(AUDIO_ANALYSIS_LOUDNESS_CODES)) {
|
||||
const meta = AUDIO_ANALYSIS_LOUDNESS_ERROR_METADATA[code];
|
||||
console.log(code, meta.severity, meta.description);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `AUDIO_ANALYSIS_LOUDNESS_CODES` — frozen object mapping reason names to
|
||||
namespaced code strings.
|
||||
- `AudioAnalysisLoudnessErrorCode` — union of every code string.
|
||||
- `AUDIO_ANALYSIS_LOUDNESS_ERROR_METADATA` — frozen lookup keyed by code.
|
||||
- `AudioAnalysisLoudnessError` — `SuaveplanError` subclass.
|
||||
- `registerAudioAnalysisLoudnessErrorCodes()` — idempotent startup hook.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`defineCodes` produces both the runtime object and the literal type union; the
|
||||
returned object is frozen, so attempts to mutate codes at runtime fail in
|
||||
strict mode. The metadata table is independently `Object.freeze`-d so consumers
|
||||
cannot mutate descriptions or severities. The `category` field on the error
|
||||
class is what keeps the subclass "non-trivial" enough for the static
|
||||
analyser — without it, biome flags the override as useless.
|
||||
@@ -0,0 +1,71 @@
|
||||
# k-weighting
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`k-weighting` implements the ITU-R BS.1770 K-weighting filter — a two-stage
|
||||
IIR biquad cascade applied per channel before any loudness energy is
|
||||
accumulated. The first stage is a high-shelf "pre-filter" approximating the
|
||||
acoustic effect of the listener's head (≈ +4 dB lift above ~1500 Hz); the
|
||||
second stage is an RLB high-pass that rolls off content below ~100 Hz so
|
||||
sub-bass rumble does not dominate the measurement.
|
||||
|
||||
Without K-weighting, "loudness" would be raw RMS — which BS.1770 explicitly
|
||||
rejects because raw RMS over-weights low-frequency energy that the human ear
|
||||
perceives as quiet. The K-curve is what makes BS.1770 loudness correlate with
|
||||
perceived programme loudness across genres.
|
||||
|
||||
## Features
|
||||
|
||||
- Canonical BS.1770 coefficients at the 48 kHz reference rate.
|
||||
- Rate-agnostic coefficient derivation via inverse bilinear transform —
|
||||
44.1 kHz, 88.2 kHz, 96 kHz, and arbitrary rates all reproduce the
|
||||
BS.1770 magnitude response within floating-point tolerance.
|
||||
- Per-channel direct-form-II transposed state so block-by-block filtering is
|
||||
sample-identical to a whole-buffer pass.
|
||||
- Pure `Float32Array[]` in/out — no `AudioBuffer`, no platform globals.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createKWeighting } from "@suaveplan/audio-analysis-loudness/filters/k-weighting";
|
||||
|
||||
const filter = createKWeighting(48000, 2);
|
||||
const filtered = filter.processBlock([leftChannel, rightChannel]);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { buildKWeighting, createKWeighting } from "@suaveplan/audio-analysis-loudness/filters/k-weighting";
|
||||
|
||||
// Inspect the coefficients for an arbitrary rate.
|
||||
const { pre, rlb } = buildKWeighting(44100);
|
||||
|
||||
// Stream blocks through a stateful filter — output equals a single-pass run.
|
||||
const filter = createKWeighting(44100, 1);
|
||||
for (const block of stream) filter.processBlock(block);
|
||||
filter.reset(); // clear state between unrelated signals
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `buildKWeighting(sampleRate)` → `{ pre: Biquad; rlb: Biquad }`. Returns
|
||||
fresh objects; mutating them does not affect future calls.
|
||||
- `createKWeighting(sampleRate, channels)` → `KWeightingFilter`.
|
||||
- `KWeightingFilter.processBlock(block)` → `Float32Array[]` of identical
|
||||
shape; never mutates the input.
|
||||
- `KWeightingFilter.reset()` — zeroes all per-channel biquad state.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Coefficients at non-reference rates are derived by recovering the analog
|
||||
prototype from the 48 kHz biquad (inverse bilinear transform) and then
|
||||
re-discretising at the target rate (forward bilinear). This avoids the
|
||||
warping that would occur if the canonical coefficients were used verbatim at
|
||||
44.1 kHz, where the magnitude response would shift by ~10% in the shelf
|
||||
region. State arrays are typed `Float64Array` so accumulated rounding stays
|
||||
below `Float32` quantisation across hour-long buffers.
|
||||
@@ -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