# `limiter` _(no submodules)_ ## Purpose `LimiterPlugin` is the universal brick-wall limiter. Its core contract is unconditional: for any bounded input, no output sample's absolute value exceeds the linear conversion of the `ceiling` parameter. The plugin pairs an instantaneous (zero-lookahead) attack with a one-pole exponential release, so transients are clamped on the sample they occur — no lookahead buffer, no group delay, no latency to report. The plugin extends `AbstractAudioFxPlugin`, so it runs identically across browser / server / worklet renderers. State is a single linked gain value plus a frame-peak reduction metric for telemetry. `reset()` snaps the gain back to unity in place. ## Features - Three parameters: `ceiling` (dB, -60..0), `release` (ms, 1..1000), and `gainReductionDb` (telemetry). - Linked-stereo brick-wall: gain is shared across all input channels per sample, so a transient on one channel attenuates every channel identically — the stereo image stays phase-coherent. - Zero-lookahead attack: when `|peak| > ceilingLin` at sample `i`, the gain at sample `i` is set to `ceilingLin / |peak|` *before* writing the output, so the ceiling is a hard contract verifiable by audit. - One-pole release: `gain += releaseCoef · (1 − gain)` per sample with `releaseCoef = 1 − exp(-1/(τ · sr))`. - `gainReductionDb` is updated once per block with the worst-case in-block reduction so meters get a useful peak reading without per-sample state. - `getGainReductionDb()` accessor mirrors the parameter for low-friction metering. ## Usage: Basic Example ```ts import { LimiterPlugin } from "@suaveplan/audio-fx-plugin-limiter"; const limiter = new LimiterPlugin({ initialCeilingDb: -1, initialReleaseMs: 50, }); chain.add(limiter); ``` ## Usage: Advanced Example ```ts import { LimiterPlugin } from "@suaveplan/audio-fx-plugin-limiter"; // Master-bus limiter feeding a UI meter from the live gain reduction. const limiter = new LimiterPlugin({ initialCeilingDb: -0.3, initialReleaseMs: 80, }); await limiter.setup( { sampleRate: 48_000, channels: 2, blockSize: 512 }, ctx ); setInterval(() => { const gr = limiter.getGainReductionDb(); if (gr > 6) { console.warn(`Master limiter pulling ${gr.toFixed(2)} dB`); } }, 100); ``` ## API Reference - `LimiterPlugin` (class extends `AbstractAudioFxPlugin`) - `new LimiterPlugin(options?: LimiterPluginOptions)` — throws `AUDIO_FX.INVALID_OPTIONS` on malformed options. - `getGainReductionDb(): number` — last block's peak gain reduction (positive number, in dB). - Inherited parameter accessors for `ceiling`, `release`, `gainReductionDb`. ## Implementation Notes The limiter is intentionally a true brick-wall, not a "soft" or "transparent" limiter. We do not interpolate around the threshold or apply a knee. The reason: many use sites layer this plugin downstream of an output-stage compressor that already provides knee shaping, and rely on the limiter for guaranteed bound semantics. Adding a knee would weaken the contract that the test in `output peak never exceeds the configured ceiling` enforces. There is no lookahead buffer. That keeps the plugin latency-free (`latencySamples: 0`, inherited default) and makes the bound testable by static inspection of the inner loop: at sample `i`, if `peak > ceilingLin` then `gain := ceilingLin / peak` *before* `output[ch][i] = input[ch][i] * gain`. The multiplication therefore can produce at most `|input[ch][i]| * (ceilingLin / |peak|) ≤ ceilingLin`. The downside is that an unusually fast transient (single-sample spike at a very low source sample rate) can produce visible distortion on its leading edge — for those cases a future `LookaheadLimiterPlugin` can sit on a separate subpath without breaking this contract. `gainReductionDb` is reported as a per-block peak rather than a running average. Meters typically expect peak; if a future surface wants RMS, it can compute that from the gain-reduction stream by subscribing to a parameter automation channel. The release coefficient is recomputed every block from the current `release` parameter so automation works without introducing zipper noise — the one-pole step is small per sample even when the parameter changes mid-block.