@maruaudio/dsp/dynamics/stereo-de-esser lowers a high shelf when sibilant energy rises. Each channel runs an RBJ high-pass at the frequency (Q 0.7071); the larger of the two magnitudes drives one peak envelope (0.5 ms attack, 60 ms release), so both channels move together. Every 16 samples the reduction clamp(20·log10(envelope) − threshold, 0, range) is applied as negative gain on an RBJ high shelf at 0.8 × the frequency (Q 0.8), when it moved by more than 0.05 dB or reached 0. Filters are @maru/filters-biquad states.
const state = initStereoDeEsser(48000)
configureStereoDeEsser(state, 6000, 10, -30, false)
stereoDeEsserTick(state, left, right) // state.outLeft, state.outRightParameters
configureStereoDeEsser(state, frequencyHz, rangeDb, thresholdDb, listen); a non-finite value takes the default, then clamps. Filters recompute only when the frequency changes.
| Argument | Range the code applies | Default | Unit |
|---|---|---|---|
frequencyHz | 2000 to 16000, then the biquad's 0.45 × rate ceiling | 6000 | Hz |
rangeDb | 0 to 24 | 10 | dB |
thresholdDb | −60 to 0 | −30 | dBFS |
listen | true outputs the detector high-pass instead | false | flag |
Lifecycle
initStereoDeEsser(sampleRate) (rate bounded to 8 to 192 kHz, NaN gives 48 kHz) on the control thread → configureStereoDeEsser → stereoDeEsserTick per frame → resetStereoDeEsser, which clears every filter, the envelope and the clock and returns the shelf to 0 dB. Configure, tick and reset allocate nothing.
When to use it
Reducing sibilance on a vocal or spoken-word track; Listen exposes the detector.
Limits
- Stereo, linked, no internal mix, lookahead or alignment delay. The 16-sample control clock persists across host blocks and reads only past samples.
- The shelf keeps running in Listen, so switching back does not revive stale state. NaN and infinite input are read as 0.