Designing Analog-Modeled Saturation and Wave-Shaping Algorithms for VST3 Plugins
Pure digital audio can sound sterile and harsh because it lacks the gentle harmonic coloration and natural peak compression of vintage hardware. Creating authentic analog-style saturation requires non-linear transfer curves coupled with rigorous anti-aliasing filter stages.
1. The Mathematics of Hyperbolic Tangent Soft-Clipping
The hyperbolic tangent function, tanh(x), is the quintessential mathematical model for analog triode tube distortion. For small inputs, tanh(x) ≈ x, producing completely linear output. As the signal approaches unity amplitude, the transfer curve smoothly saturates, compressing peaks and introducing warm odd harmonics:
inline float processTanhSaturation(float inputSample, float driveGain) {
float driven = inputSample * driveGain;
// Approximating tanh for CPU efficiency:
// Pade approximant or direct fast-math intrinsic
return std::tanh(driven);
}
2. The Aliasing Menace: Why Oversampling is Mandatory
Non-linear wave-shaping generates harmonic components that extend far beyond the original signal bandwidth. According to the Nyquist-Shannon sampling theorem, any generated harmonic frequency exceeding half the sample rate ($f_s / 2$) reflects backwards into the audible spectrum as dissonant inharmonic distortion, known as aliasing.
To eliminate aliasing, the input signal must be upsampled (typically 4x or 8x) using a steep linear-phase polyphase FIR filter before saturation, and subsequently filtered and decimated back to the original host sample rate.
// JUCE DSP Oversampling Block
juce::dsp::Oversampling<float> oversamplingEngine { 2, 2, juce::dsp::Oversampling<float>::filterHalfBandPolyphaseIIR };
void prepare(const juce::dsp::ProcessSpec& spec) {
oversamplingEngine.initProcessing(spec.maximumBlockSize);
}
void process(juce::AudioBuffer<float>& buffer) {
juce::dsp::AudioBlock<float> block(buffer);
juce::dsp::AudioBlock<float> oversampledBlock = oversamplingEngine.processSamplesUp(block);
// Apply non-linear wave-shaping at 4x sample rate
for (size_t ch = 0; ch < oversampledBlock.getNumChannels(); ++ch) {
auto* data = oversampledBlock.getChannelPointer(ch);
for (size_t i = 0; i < oversampledBlock.getNumSamples(); ++i) {
data[i] = std::tanh(data[i] * 2.5f);
}
}
oversamplingEngine.processSamplesDown(block);
}
3. Summary
Analog warmth is not magic; it is the deliberate application of non-linear mathematics combined with high-order oversampling to preserve high-frequency clarity.