Dynamics Processors¶
Pulp provides complementary compressor designs and reusable dynamics
utilities. They share an explicit
prepare()/reset() lifecycle, expose their gain computer for meters and curve
editors, and report latency without requiring an audio probe. Use the float
aliases shown below; every class also has a 64 double-precision alias.
<pulp/signal/dynamics_contract.hpp> supplies the common vocabulary around
those intentionally different processors:
EnvelopeFollowerbuilds on the establishedBallisticsFilter. It accepts raw linear-amplitude samples, supports peak and RMS modes, and reports linear or dBFS envelopes. Attack and release are exact 10-to-90-percent times in the smoothed-state domain: amplitude for peak mode and mean-square power for RMS mode. The olderBallisticsFiltername retains its historical nominal 2.2 exponent so existing renders remain bit-identical.StereoEnvelopeFollowercomposes two followers and links them in the detector-magnitude domain. Link zero is dual mono; link one follows the louder channel on both sides without opposite-polarity cancellation.GainReductionis the shared meter contract. Itsdb()value is always a non-negative attenuation magnitude;signed_db()andlinear_gain()expose the equivalent gain-domain values. Positive infinite attenuation represents a complete mute and maps to zero linear gain.
| Processor | Header | Choose it for |
|---|---|---|
FeedforwardCompressor |
<pulp/signal/feedforward_compressor.hpp> |
Transparent peak/RMS compression, stereo linking, lookahead, and auto makeup |
VcaCompressor |
<pulp/signal/vca_compressor.hpp> |
Program-dependent RMS behavior, OverEasy-style knee, and infinity-plus curves |
DiodeBridgeCompressor |
<pulp/signal/diode_bridge_compressor.hpp> |
Feedback dynamics with diode-bridge and transformer character |
FetCompressor |
<pulp/signal/fet_compressor.hpp> |
Fixed-threshold input-drive workflow, ratio buttons, fast attack, and transformer output |
Expander |
<pulp/signal/expander.hpp> |
Downward or upward expansion with a bounded soft-knee curve and prepared peak/RMS detection |
Minimal insert¶
#include <pulp/signal/feedforward_compressor.hpp>
pulp::signal::FeedforwardCompressor compressor;
// Processor::prepare(), never the audio callback.
compressor.prepare(sample_rate, 10.0);
compressor.set_threshold_db(-18.0);
compressor.set_ratio(4.0);
compressor.set_attack_ms(8.0);
compressor.set_release_ms(120.0);
compressor.set_lookahead_ms(2.0);
// Processor::process().
compressor.process_block_stereo(left, right, num_frames);
const int latency = compressor.latency_samples(); // Return from your Processor.
prepare() sizes lookahead or oversampling state. Keep it off the audio thread.
The setters and processing calls are allocation-free after preparation. Call
reset() on transport discontinuities; do not call prepare() merely to clear
history.
Expander processes stereo sample pairs and makes its detector policy explicit
with DynamicsStereoLink::{independent,peak_linked}. Its configure() call
validates the complete configuration transactionally, while
gain_computer_db() exposes the exact bounded knee/range law used after the
detector. Attack and release use the shared envelope follower's exact
10-to-90-percent convention. set_bypassed(true) returns each finite input
sample exactly while detector history continues advancing, so leaving bypass
does not resume from stale state.
Choosing a topology¶
- Start with
FeedforwardCompressorwhen the requirement is level control rather than audible character. Select peak or RMS detection explicitly and usestatic_curve_db()to draw the same curve the processor applies. - Use
VcaCompressorwhen one time control and an attack/release ratio are part of the interaction model.gain_computer_unclamped_db()exposes where the ceiling floor begins to affect the curve. - Use
DiodeBridgeCompressorwhen the feedback topology is intentional. Thecharactermacro drives both bridge and transformer color;set_adaa()is a measurement switch, not an ordinary user control. - Use
FetCompressorfor the fixed-threshold, input-drive convention. The nominal ratio and measured closed-loop ratio are deliberately separate quantities; expose the nominal ratio to users and the measured accessors to analysis or visualization code.
All gain-reduction meters are valid after processing a sample. Use
gain_reduction() when combining processor types: it returns the canonical
non-negative GainReduction value. The older gain_reduction_db() accessors
retain each circuit lineage's established sign and remain available for
compatibility and lineage-specific analysis.
Verification¶
Test the memoryless curve separately from detector timing, then test the full
processor over the sample-rate/block-size matrix. Lookahead must be reflected in
the owning Processor::latency_samples(). For audible comparison, render both
paths offline and use the audio quality lab; do not open a live device merely to
check a curve or envelope.