Nonlinear and Tone Processors¶
These processors cover memoryless saturation, circuit clipper primitives, two-transistor fuzz, tape, and speaker behavior. Pick the smallest class that owns the behavior you need instead of treating every coloration task as one generic distortion.
| Processor | Header | Role |
|---|---|---|
Saturator |
<pulp/signal/saturator.hpp> |
General waveshaping with drive, bias, tone, alias policy, mix, and trim |
DiodeClipper, FeedbackClipper, ToneStack |
<pulp/signal/distortion.hpp> |
Circuit-oriented clipping and surrounding pre/post tone stages |
FuzzPair |
<pulp/signal/fuzz_pair.hpp> |
Coupled two-stage fuzz with device, source-loading, starvation, and seeded drift controls |
TapeMachine |
<pulp/signal/tape_machine.hpp> |
Stereo record/playback chain with archetype, speed, EQ, bias, age, crosstalk, companding, and print-through |
SpeakerModel |
<pulp/signal/speaker_cabinet.hpp> |
Driver, cabinet, breakup, compression, microphone geometry, and diffraction |
General saturation¶
#include <pulp/signal/saturator.hpp>
pulp::signal::Saturator sat;
sat.prepare(sample_rate);
sat.set_shape(pulp::signal::SaturatorShape::tanh_soft);
sat.set_drive_db(12.0);
sat.set_alias_policy(pulp::signal::SaturatorAliasPolicy::oversample_2x);
sat.set_mix(1.0);
for (int i = 0; i < num_frames; ++i)
mono[i] = sat.process(mono[i]);
Use shaped(x) to plot or test the selected memoryless transfer without running
filter or oversampling state. worst_case_gain() and latency_samples() are the
corresponding bounds for host integration.
Choosing an alias policy:
| Policy | Wet-path delay | Top-octave cost | When |
|---|---|---|---|
adaa (default) |
0.5 sample | two-tap mean: −3.0 dB at a quarter of the rate | Low latency, moderate drive |
adaa2 |
1 sample (reported, dry path matched) | three-tap mean: −9.5 dB at a quarter of the rate, null at a third | Content below about a sixth of the rate, where its worst-case aliasing measures at least 12 dB below adaa's and under -50 dBc (tanh at +18 dB drive); higher up its roll-off costs more than it saves |
oversample_2x |
64 samples (linear phase) | flat | Phase coherence with another linear-phase oversampled stage |
off |
0 | flat | Measurement and the CPU floor |
adaa_linear_magnitude(order, f) returns the ADAA stencils' small-signal
response so a plot or test can show the roll-off without rendering audio, and
group_delay_samples() reports the half sample that the integer
latency_samples() rounds away. For hard drive on wide-band material, an
OversamplerT with Kind::polyphase_iir around a cheap curve such as
FastMath::tanh_minimax reaches a lower alias floor than either ADAA order for
less CPU on Apple Silicon; ADAA's advantages are its sub-sample latency and its
linear phase.
Circuit and system boundaries¶
DiodeClipperowns a shunt diode node with resistance and capacitance.voltage(),resistive_residual(), andlast_iteration_count()expose the solver rather than forcing tests to infer it from audio.FeedbackClipperowns the feedback-amplifier topology. Itslinear_gain()is useful for gain staging before the nonlinear solve.ToneStackdeliberately separatesprocess_pre()andprocess_post()so a graph, plot, or test can observe the two stages independently.FuzzPairincludes its required oversampling. Keepset_oversampling_enabled(false)confined to measurement tests.TapeMachineis stereo and may allocate duringprepare()or while loading derived FIR state. Set archetype, speed, EQ, and long-lived topology outside the sample loop. Its inspection methods expose the actual record/playback EQ and gap filter.SpeakerModelis a complete mono cabinet/microphone chain. Re-runprepare()when the sample rate changes, then reportlatency_samples()from the owning plugin.
The complete advanced DSP API lists every control, processing overload, and inspection method.