- Published on
How to implement oversampling in JUCE?
- Authors

- Name
- Desi Ilieva
Introduction
Oversampling is one of those things you don’t skip once you start writing nonlinear audio effects — like distortion, saturation, or waveshaping. These kinds of processors generate harmonics above the Nyquist frequency (half the sample rate), which fold back into the audible range as aliasing.
To fight that, we oversample — meaning we temporarily increase the sample rate, process at that higher rate, and then downsample back to the original rate.
Check this article for more in depth information about oversampling: What is Oversampling?
When to implement oversampling in our plugin?
You don’t need oversampling everywhere — only where nonlinearities exist.
Here are a few situations where you should use it:
Distortion / saturation (anything that clips or folds the waveform)
Waveshapers
Bitcrushers (sometimes)
Compressors or limiters with fast attack/release that distort under extreme conditions
And you usually don’t need it for:
EQs
Delays
Reverbs
Filters that stay linear
Basically, if your plugin changes the shape of the waveform in a nonlinear way, think oversampling.
JUCE's oversampling class
JUCE makes oversampling now easier with the dsp::Oversampling class, from JUCE's dsp module, which handles both the upsampling and downsampling filters internally
Here's the constructor of the Oversampling class:
dsp::Oversampling(
size_t numChannels,
size_t factor,
dsp::Oversampling<float>::FilterType type,
bool isMaxQuality = true,
bool shouldUseIntegerLatency = false
);
Parameters breakdown:
numChannels: Number of channels (mono = 1, stereo = 2)
factor: how much to oversample (2x, 4x, 8x, etc.)
FilterType: Either filterHalfBandFIREquiripple or filterHalfBandPolyphaseIIR
isMaxQuality: Whether to use higher-quality filters (slightly more CPU)
shouldUseIntegerLatency: Whether to round latency to an integer number of samples (useful for plugin delay compensation)
Step by step implementation
Let's assume we have a distortion plugin to which we want to implement oversampling.
First we need to declare an oversampling object in our PluginProcessor.h
juce::dsp::Oversampling<float> oversampling {
2, // number of channels
4, // oversampling factor (4x), we can make it a variable later so the user be able to choose the factor from UI
juce::dsp::Oversampling<float>::filterHalfBandFIREquiripple, // when we write :: we will see the proposed filters
true, // max quality
false // integer latency off
};
Then natually we will need to prepare it in PrepareToPlay function in PluginProcessor.cpp :
oversampling.reset();
oversampling.initProcessing(static_cast<juce::uint32>(samplesPerBlock));
- note: we do static cast to unsigned: (size_t)/(juce::uint32) for samplesPerBlock just to explicitly say it's positive and never negative
Audio in digital systems it's not literally processed sample by sample, but via a pack of samples by pack of samples. This pack is called the buffer size. When we process audio via the dsp module we wrap each buffer in blocks.
juce::dsp::AudioBlock<float> block (buffer);
Once we have wrapped our buffers in blocks, we can start our oversampling process.
First we do upsampling and save the upsampled block into new variable.
// Upsample
auto oversampledBlock = oversampling.processSamplesUp(block);
Then let's assume we have a simple distortion, from the juce::dsp::WaveShaper<Type, Function> template.
juce::dsp::WaveShaper<float, std::function<float(float)>> distortion;
This struct template have few functions that will be useful now:
- prepare (const ProcessSpec &) - to use in prepareToPlay
distortion.prepare(spec);
distortion.functionToUse = [this](float x)
{
return saturationFunction(x);
};
- process (const ProcessContext & context) -to process the oversampledBlock supplied in the processing context.
juce::dsp::ProcessContextReplacing<float> distortionContext(oversampledBlock);
distortion.process(distortionContext);
- note: is a good idea to process antialias filter in the oversampled block, because removing the aliases at higher sample rate, avoid bad digital artifacts.
After we can do our downsampling and return to our original sample rate.
// Downsample
oversampling.processSamplesDown(block);
```
# Summary
Oversampling is essential when fighting aliasing in nonlinear plugins.
With dsp::Oversampling, JUCE takes care of the main work like filtering and up/down sampling.
Rule of thumb:
- Use 2x or 4x oversampling for subtle nonlinear effects
- Go 8x or higher for heavy distortion
- Always A/B test CPU usage vs quality