|
DSPark 1.8.0
Header-only C++20 DSP for real-time and offline audio
|
High-performance brickwall lookahead limiter. More...
#include <Limiter.h>

Public Member Functions | |
| ~Limiter ()=default | |
| void | prepare (double sampleRate, int numChannels=2, double initialLookaheadMs=-1.0) |
| Allocates memory and prepares the limiter for processing. | |
| void | prepare (const AudioSpec &spec) |
| Prepares from AudioSpec (unified API). | |
| void | processBlock (AudioBufferView< T > buffer) noexcept |
| Processes an AudioBufferView in-place. | |
| T | processSample (T input, int channel) noexcept |
| Processes a single sample of one channel. | |
| void | reset () noexcept |
| Resets the internal state (delays, gain reduction). RT-Safe. | |
| void | setCeiling (T dB) noexcept |
| Sets the absolute output ceiling. | |
| void | setRelease (T ms) noexcept |
| Sets the base release time. | |
| void | setLookahead (T ms) noexcept |
| Sets the lookahead time dynamically. | |
| void | setTruePeak (bool enabled) noexcept |
| Enables 4x oversampled ISP true-peak detection. RT-Safe. The attack ramp shortens by the estimator's support (11 samples) so the reduction covers every sample a true-peak reading is formed from. | |
| void | setAdaptiveRelease (bool enabled) noexcept |
| Enables program-dependent adaptive release. RT-Safe. | |
| void | setHold (T ms) noexcept |
| Sets how long the reduction is held after the last peak that asked for it, before the release starts. RT-Safe. | |
| T | getHold () const noexcept |
| void | setSafetyClip (bool enabled) noexcept |
| Enables the post-limiter soft-knee safety clipper. RT-Safe. | |
| bool | isTruePeakEnabled () const noexcept |
| bool | isAdaptiveReleaseEnabled () const noexcept |
| bool | isSafetyClipEnabled () const noexcept |
| T | getLookahead () const noexcept |
| int | getLatency () const noexcept |
| Reports the processing latency (the lookahead) in samples. | |
| T | getGainReductionDb () const noexcept |
| Current gain reduction in dB (metering). | |
| std::vector< uint8_t > | getState () const |
| Serializes the parameter state (setup/UI threads; allocates). | |
| bool | setState (const uint8_t *data, size_t size) |
| Restores parameters from a blob (tolerant; rejects foreign ids). | |
Protected Member Functions | |
| int | lookaheadSamplesFor (T ms) const noexcept |
| void | applyLookaheadTarget () noexcept |
| void | configureGainComputer () noexcept |
| DSPARK_NOINLINE void | resumBoxes () noexcept |
| DSPARK_NOINLINE void | restartHoldBlock (int64_t firstFrame) noexcept |
| void | resetGainComputer () noexcept |
| void | advanceGain (T peak, T ceiling, bool adaptive, T relMs) noexcept |
| void | syncParameters () noexcept |
| void | updateHoldSamples () noexcept |
| void | updateReleaseCoefficient () noexcept |
| T | applySafetyClipper (T out, T clipCeil) const noexcept |
Protected Attributes | |
| TruePeakDetector< T, kMaxChannels > | truePeak_ |
| bool | prepared_ = false |
| double | sampleRate_ = 48000.0 |
| T | invSampleRate_ = T(1.0 / 48000.0) |
| int | numChannels_ = 2 |
| int | lookaheadSamples_ = 96 |
| Audio-side lookahead (consumed from lookaheadMs_). | |
| int | maxLimitSamples_ = 96000 |
| Adaptive-release duration cap (2 s). | |
| std::atomic< T > | ceilingDb_ { T(-0.3) } |
| std::atomic< T > | releaseMs_ { T(100) } |
| std::atomic< T > | holdMs_ { T(10) } |
| std::atomic< T > | lookaheadMs_ { T(2) } |
| std::atomic< bool > | lookaheadDirty_ { false } |
| std::atomic< bool > | truePeakEnabled_ { false } |
| std::atomic< bool > | adaptiveRelease_ { false } |
| std::atomic< bool > | safetyClipEnabled_ { false } |
| SmoothedValue< T > | ceilingSmooth_ |
| T | lastCeilingDb_ = T(-0.3) |
| Change detector for the ceiling pow skip. | |
| T | releaseCoeff_ = T(0) |
| T | lastReleaseMs_ = T(-1) |
| T | lastHoldMs_ = T(-1) |
| int | holdSamples_ = 0 |
| Hold length in samples. | |
| int | holdLeft_ = 0 |
| Hold samples left before the release may act. | |
| T | currentGain_ = T(1) |
| std::atomic< T > | publishedGain_ { T(1) } |
| int | limitingDuration_ = 0 |
| T | lookaheadCurrent_ = T(96) |
| Smoothed read offset (glides on live changes). | |
| int | histSize_ = 0 |
| int | histMask_ = 0 |
| std::vector< double > | eHist_ |
| Released envelope history. | |
| std::vector< double > | avgHist_ |
| First moving-average output history. | |
| std::vector< double > | rHist_ |
| Required-gain history. | |
| std::vector< double > | sufMin_ |
| Suffix minima of the last complete hold block (+1 empty). | |
| int | blockPos_ = 0 |
| Position inside the current hold block. | |
| double | prefixMin_ = 1.0 |
| Running minimum of the current hold block. | |
| int64_t | frame_ = 0 |
| double | envelope_ = 1.0 |
| double | sum1_ = 1.0 |
| double | sum2_ = 1.0 |
| int | boxA_ = 1 |
| int | boxB_ = 1 |
| double | invBoxA_ = 1.0 |
| double | invBoxB_ = 1.0 |
| int | holdLen_ = 97 |
| int | sinceResum_ = 0 |
| T | pendingPeak_ = T(0) |
| Other channels' peaks for the next processSample() frame. | |
| T | chunkPeak_ [kChunk] = {} |
| T | chunkCeiling_ [kChunk] = {} |
| T | chunkGain_ [kChunk] = {} |
| int | chunkLook_ [kChunk] = {} |
| T | sampleCeiling_ = T(0.96605) |
| int | sampleLookNow_ = 96 |
| std::vector< RingBuffer< T > > | delayLines_ |
Static Protected Attributes | |
| static constexpr int | kMaxChannels = 16 |
| static constexpr double | kMaxLookaheadMs = 10.0 |
| static constexpr T | kSafetyClipCeiling = T(0.96605) |
| -0.3 dBFS | |
| static constexpr int | kResumPeriod = 1 << 16 |
| static constexpr int | kChunk = 128 |
High-performance brickwall lookahead limiter.
| T | Sample type (float or double). |
|
default |
|
inlineprotectednoexcept |
|
inlineprotectednoexcept |
|
inlineprotectednoexcept |
|
inlineprotectednoexcept |
|
inlinenoexcept |
Current gain reduction in dB (metering).
Safe from any thread: it loads a published atomic word, so off the audio thread it may be up to one block behind. It used to read currentGain_ directly – a plain word the audio thread writes every sample, read from another thread, which is a data race and not merely an approximate number.
|
inlinenoexcept |
|
inlinenoexcept |
Reports the processing latency (the lookahead) in samples.
Derived from the published lookahead parameter, so it is correct immediately after setLookahead() from any thread (the audio thread may consume the change one block later; the glide covers the transition).
|
inlinenoexcept |
|
inline |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlineprotectednoexcept |
Lookahead in samples for a given ms parameter (shared by the audio-side recompute and getLatency() so both always agree exactly). The product is capped in double before the int cast (out-of-range casts are UB).
|
inline |
|
inline |
Allocates memory and prepares the limiter for processing.
Invalid arguments are release-safe: a non-positive or non-finite sample rate makes this call a no-op (the previous state, if any, is kept). Configured parameters (ceiling, release, lookahead, toggles) survive re-preparation.
| sampleRate | Sample rate in Hz (must be > 0). |
| numChannels | Number of channels to process (clamped to [1, 16]). |
| initialLookaheadMs | Optional lookahead override in ms; any non-positive or non-finite value (the default) keeps the current lookahead parameter. |
|
inlinenoexcept |
Processes an AudioBufferView in-place.
RT-Safe: Yes. Lock-free and allocation-free.
The gain envelope is linked: the loudest channel drives the reduction applied to all channels. Channels beyond the prepared count pass through untouched (and undelayed).
| buffer | Audio buffer view. |
|
inlinenoexcept |
Processes a single sample of one channel.
The shared state (ceiling smoother, lookahead glide and gain computer) advances on channel 0, so call channel 0 first within each sample frame. For mono streams (channel 0 only) this path is bit-identical to processBlock(). Peaks of the other channels enter the gain computer one frame late; for properly linked multi-channel limiting prefer processBlock().
|
inlinenoexcept |
|
inlineprotectednoexcept |
|
inlineprotectednoexcept |
Sliding minimum (van Herk / Gil-Werman, streaming form): the frames are cut into blocks of holdLen_. Once a block is complete its suffix minima are stored; the window ending at a frame of the next block is then the minimum of one stored suffix and the running prefix of that block. Branch-free per frame, one backward pass per block (amortised O(1)). This stores the suffix minima of the block starting at firstFrame and starts a new block at the following frame.
|
inlineprotectednoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
Sets how long the reduction is held after the last peak that asked for it, before the release starts. RT-Safe.
Without a hold the gain recovers between the crests of a low tone and dips again at the next one, and that ripple is distortion: a 50 Hz sine driven 6 dB over the ceiling (5 ms lookahead, 50 ms release) measures -56.8 dB THD+N with no hold and -149.6 dB with the default 10 ms, and 40 Hz goes from -47.4 to -149.7 dB (30 Hz: -38.4 to -84.7); the 60 Hz + 7 kHz intermodulation products fall from -62.4 to -74.2 dB. The price is loudness after isolated peaks: 0.21 dB of integrated loudness on a dense mix driven 12 dB over the ceiling.
| ms | Hold in milliseconds, clamped to [0, 50] (default 10; 0 is the previous release-only behaviour). Non-finite values are ignored. |
|
inlinenoexcept |
Sets the lookahead time dynamically.
| ms | Lookahead in milliseconds (clamped to 0.5 to 10.0 ms). |
|
inlinenoexcept |
|
inlinenoexcept |
Enables the post-limiter soft-knee safety clipper. RT-Safe.
Softens the region above -0.3 dBFS up to the ceiling; it only has an effect when the ceiling is set above -0.3 dBFS (below that, the brickwall clamp already keeps the output under the clipper threshold).
|
inline |
|
inlinenoexcept |
|
inlineprotectednoexcept |
|
inlineprotectednoexcept |
|
inlineprotectednoexcept |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
staticconstexprprotected |
|
staticconstexprprotected |
|
staticconstexprprotected |
|
staticconstexprprotected |
|
staticconstexprprotected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
Other channels' peaks for the next processSample() frame.
|
protected |
|
protected |
|
protected |
Cross-thread metering readout of currentGain_: published once per processBlock() and once per processSample() call. processSample() is the per-sample entry point, so there the store does run once per sample – what it never does is sit inside processBlock()'s own sample loop.
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |
|
protected |