void Downsampler::processBlock(AudioSampleBuffer& buffer, int startSample, int numSamples)
{
processBuffer.makeCopyOf(buffer);
resample(processBuffer, inputSampleRate, targetSampleRate); //introduce distortion
resample(processBuffer, targetSampleRate, inputSampleRate); //go back to host Fs
buffer.clear();
for(int channel = 0; channel < buffer.getNumChannels(); channel++)
{
buffer.copyFrom(channel, 0, processBuffer, channel, 0, processBuffer.getNumSamples());
}
}
void Downsampler::resample(AudioSampleBuffer& buffer, double fromSampleRate, double toSampleRate)
{
if(fromSampleRate == toSampleRate)
{
return;
}
int upsampleFactor, downsampleFactor; //upsample factor, downsample factor
int gcd = calcGCD(toSampleRate, fromSampleRate);
upsampleFactor = toSampleRate / gcd;
downsampleFactor = fromSampleRate / gcd;
upsample(buffer, upsampleFactor);
interpolate(upsampledBuffer, fromSampleRate, fromSampleRate/2.0);
downsample(buffer, downsampleFactor);
}
void Downsampler::upsample(AudioSampleBuffer& bufferToUpsampleFrom, int upsampleFactor)
{
upsampledBuffer.setSize(bufferToUpsampleFrom.getNumChannels(), upsampleFactor*bufferToUpsampleFrom.getNumSamples());
upsampledBuffer.clear();
for(int srcBuffIndex=0; srcBuffIndex < bufferToUpsampleFrom.getNumSamples(); srcBuffIndex++)
{
int upsamBuffIndex = srcBuffIndex * upsampleFactor;
for(int channels = 0; channels < upsampledBuffer.getNumChannels(); channels++)
{
upsampledBuffer.setSample(channels, upsamBuffIndex, bufferToUpsampleFrom.getSample(channels, srcBuffIndex));
}
}
}
void Downsampler::interpolate(AudioSampleBuffer& buffer, double sampleRate, double cutoffFreq)
{
IIRCoefficients interpolationCoefficients = IIRCoefficients::makeLowPass(sampleRate, cutoffFreq);
int numSamples = buffer.getNumSamples();
leftFilter.setCoefficients(interpolationCoefficients);
float* bufferPointer = buffer.getWritePointer(0);
leftFilter.processSamples(bufferPointer, numSamples);
rightFilter.setCoefficients(interpolationCoefficients);
bufferPointer = buffer.getWritePointer(1);
rightFilter.processSamples(bufferPointer, numSamples);
}
void Downsampler::downsample(AudioSampleBuffer& bufferToDownsampleTo, int downsampleFactor)
{
int newBufferSize = upsampledBuffer.getNumSamples()/downsampleFactor;
bufferToDownsampleTo.setSize(upsampledBuffer.getNumChannels(), newBufferSize);
for(int i = 0; i < newBufferSize; i++)
{
int retrieveIndex = i*downsampleFactor;
for(int channel = 0; channel < bufferToDownsampleTo.getNumChannels(); channel++)
{
bufferToDownsampleTo.setSample(channel, i, upsampledBuffer.getSample(channel, retrieveIndex));
}
}
}
int Downsampler::calcGCD(int x, int y)
{
return y == 0 ? x : calcGCD(y, x % y);
}
Above is my updated code for the Downsampler effect. This code audibly seems to work for a targetSampleRate that’s an integer ratio of the inputSampleRate, but produces crackly garbage with non-ratio targetSampleRates.
Also, there’s another problem: the second time through, newBufferSize in downsample sometimes comes out a few samples below the size of the buffer originally passed to processBlock. It’s for this reason that I’m using copyFrom rather than makeCopyOf, but the extra zeros from not fully filling the block introduces unwanted frequencies to the signal.
Any ideas on what needs to be fixed? Also, any recommendations for some spaces specifically focused on audio/music DSP where I might find further help? Thanks!