Just experimenting with JUCE’s interpolators.
My idea was to create a class that takes an AudioBuffer and up-samples or down-samples it to another AudioBuffer. Input sample rate and output sample rate are specified.
As a very first step sanity check I’m generating a sine wave at 440Hz, with an overall sample rate of 96000Hz. I convert it to the host DAW sample rate, in this case 44100Hz. I’m using Span spectrum analyser with the floor set at -180dB so I can see everything that’s going on.
This is what I’m seeing:
If the input and output sample rates are the same, I see a perfect frequency spectrum. It also works for other combos. But not for 96000 → 44100. I suspect it maybe be due to fractional buffer size calculation.
Here’s my quick knock-up class for reference:
class AudioResampler
{
public:
AudioResampler(double inputSampleRate, double outputSampleRate)
: inputRate(inputSampleRate), outputRate(outputSampleRate), ratio(inputSampleRate / outputSampleRate)
{
// Initialize one interpolator per channel
for (int i = 0; i < 2; ++i)
interpolators.add(new juce::WindowedSincInterpolator());
}
int getNumInputSamplesNeeded(const int numOutputSamples) const
{
const int baseNeeded = static_cast<int>(std::ceil(numOutputSamples * ratio));
return baseNeeded;
}
// Get the fixed latency
int getLatencyInSamples() const
{
return 16; // Fixed latency of WindowedSincInterpolator
}
// Process input buffer to output buffer
void process(AudioBuffer<float> &inputBuffer,
AudioBuffer<float> &outputBuffer)
{
const int numChannels = jmin(inputBuffer.getNumChannels(),
outputBuffer.getNumChannels());
auto numOutputSamples = outputBuffer.getNumSamples();
// Process each channel
for (int channel = 0; channel < numChannels; ++channel)
{
float *outData = outputBuffer.getWritePointer(channel);
const float *inData = inputBuffer.getReadPointer(channel);
// Let the interpolator handle the resampling
interpolators[channel]->process(ratio,
inData,
outData,
numOutputSamples);
}
}
// Reset the interpolators' internal state
void reset()
{
for (auto *interp : interpolators)
interp->reset();
}
// Get current sample rates
double getInputSampleRate() const { return inputRate; }
double getOutputSampleRate() const { return outputRate; }
double getRatio() const { return ratio; }
private:
const double inputRate;
const double outputRate;
const double ratio;
OwnedArray<WindowedSincInterpolator> interpolators;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioResampler)
};
I use getNumInputSamplesNeeded() to figure out how many samples to generate in the input buffer - in this case for the 440Hz sine wave.
The documentation for WindowedSincInterpolator says the number of input samples “…must contain at least (speedRatio * numOutputSamplesToProduce) samples.”
I’m using std::ceil in getNumInputSamplesNeeded(), but I’ve tried floor and round too - no luck.
Aliasing isn’t a factor - it’s a 440Hz sine wave.
Anyone here with experience of using this class that can see what I’m missing?

