b862f5a971
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