The polyphase evaluation is PHASES*TAPS_PER_PHASE multiply-adds per channel
(96 for stereo) on every sample, unconditionally, and measured as 42 ns/sample
- 17% of the whole chain, and the largest single item in it. It is also the
only part that is pure throughput rather than a serial dependency chain, which
makes it the one worth attacking.
It is skippable. The interpolator is linear, so over its FIR support
|true peak| <= peak_gain * max|history|, peak_gain = max_phase sum |c|
and when that bound sits under the ceiling the true peak provably cannot reach
it. The limiter's gain target would be 1.0 for that sample either way, so
feeding the sample peak instead cannot change the outcome - a genuinely loud
neighbour still gets caught by its own evaluation.
- oversampler: split max_true_peak into push (cheap, must run every sample or
the FIR history develops holes) and evaluate (the expensive half, skippable).
Expose peak_gain and SUPPORT_LEN so a caller can compute the bound.
- limiter: track the sample peak over the FIR support with the SlidingMax
already used for the look-ahead window, and skip evaluate when bounded under.
Measured, 48 kHz stereo:
limiter, quiet 0.1 (skips) 66 -> 31 ns/sample
limiter, hot 0.95 (always evaluates) 66 -> 72 ns/sample (no real change)
FULL CHAIN 232 -> 195 ns/sample
So this pays off on material that is not pinned to the ceiling and costs
nothing when it is. On a maximizer the hot case is common, so treat the win as
situational rather than a flat 16%.
evaluate_never_exceeds_peak_gain_bound asserts the bound directly against
adversarial input (full-scale square waves, impulses), since the whole
optimisation rests on it, and also asserts it is not vacuously loose.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LU1NRL7fAaZTtFMTTPn2b1
Found by reading the audio-thread code, not by profiling - no profiler is
set up in this project yet. All four are algorithmic or arithmetic wins,
none change the DSP result; the existing tests still pass.
- oversampler: max_true_peak recomputed its circular-history tap index
(including a % TAPS_PER_PHASE) inside the phase x channel loop, though
the index depends only on pos/k - 8x redundant. Hoisted to a per-sample
precomputed index array. This runs unconditionally every sample (it
feeds the limiter's true-peak detector, not gated by metering), so it
was the highest-value fix.
- limiter: the sliding-window max rescanned the entire look-ahead ring
(~73 samples at 48kHz/1.5ms) every sample - O(window) per sample.
Replaced with an O(1)-amortized monotonic-deque sliding max, backed by
preallocated ring arrays and RT-safe. Covered by a new test against a
naive brute-force reference. Also precomputed the constant true-peak
margin gain in prepare() instead of a powf every sample.
- compressor: replaced per-sample log10/powf(10, x/20) with the
equivalent ln/exp forms, mirroring nih-plug's own gain_to_db_fast /
db_to_gain_fast. Called 4x/sample (3 bands + 'All').
- both: replaced % capacity ring-index math (a real integer division,
since capacity is not a power of two) with a conditional-subtract
wrap_add helper. Left the oversampler's % TAPS_PER_PHASE alone - that
divisor is a compile-time constant LLVM already turns into a multiply.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LU1NRL7fAaZTtFMTTPn2b1
Upgrades the brickwall limiter from sample-peak to true-peak (inter-sample).
- src/dsp/oversampler.rs: 4x polyphase windowed-sinc (4 phases x 12 taps, Blackman,
each phase normalized to unity DC). Detection-only: max_true_peak() returns the
inter-sample max magnitude and discards the upsampled samples; the audio path is
untouched. Built in prepare(), no realtime allocation. Cost ~ one base-rate FIR
per channel; its small group delay is absorbed by the limiter look-ahead, so no
added reported latency.
- src/dsp/limiter.rs: detector peak = max(sample_peak, oversampler.max_true_peak());
targets a 0.3 dB margin under the ceiling to cover the 4x detection residual.
- 16 unit tests (2 new: detects ~3 dB fs/4 inter-sample overshoot; preserves DC
amplitude). README/docs updated: Stage 4 complete.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>