Ok, I’m at a complete loss as to how to get Rubberband working. Two problems:
setTimeRatiodoes nothing to the output. Setting this to any value has no effect.setPitchScaleonly resamples. I.e. if set to 2, there is an octave shift upwards but the duration of halved.
I’ve tried using a circular buffer for input/output, reading/writing directly to and from vectors, arrays, juce::AudioBuffer, etc. The sample values are not exactly the same but are extremely close which makes me think it is a function of the input being processed by whatever FFT process and being resynthesized.
I’m attempting to do this all offline so I don’t have to deal with latency since time-stretching is inherently non-realtime.
A toned-down example with lots of experiments removed:
void PitchShifter::processOffline (AudioBuffer<float>* buffer, const String& writePath)
{
int samples = buffer->getNumSamples();
size_t sampsToProcess = 0;
size_t numProcessed = 0;
int numAvailable = 0;
size_t writeOffset = 0;
size_t processedInBlock = 0;
bool saved = false;
size_t blockSize = 512;
juce::AudioBuffer<float> output (buffer->getNumChannels(), buffer->getNumSamples()*2);
float* inPtrs[buffer->getNumChannels()]; // <- these are in/outs of RubberBand
float* outPtrs[output.getNumChannels()]; // <- these are in/outs of RubberBand
shifter->reset();
shifter->setMaxProcessSize(blockSize);
output.clear(); // make sure things are clear
/*
I don't know why the time scaling isn't working and why the pitch is not
independent of time! Note that when you set pitchscale to 2, the duration
also halves. WTF.
*/
shifter->setPitchScale (pitchScale);
shifter->setTimeRatio (timeRatio);
samplesRequired = static_cast<int> (shifter->getSamplesRequired());
// do the whole file
while (numProcessed < samples)
{
// do each block of blocksize samples (512)
while (processedInBlock < blockSize && numProcessed < samples)
{
// update the pointers
for (size_t c = 0; c < buffer->getNumChannels(); ++c)
{
inPtrs[c] = (float* const) buffer->getReadPointer (static_cast<int> (c), numProcessed);
outPtrs[c] = (float* const) output.getWritePointer (static_cast<int> (c), writeOffset);
}
sampsToProcess = std::min (blockSize, std::min (shifter->getSamplesRequired(), samples - numProcessed)); // pass the minumum required
// sampsToProcess = std::min (blockSize, samples - numProcessed);
if (numProcessed + sampsToProcess <= samples)
{
shifter->process (inPtrs, sampsToProcess, false);
}
else
{
shifter->process (inPtrs, sampsToProcess, true);
}
// return as many samples as we can
numAvailable = shifter->available();
if (numAvailable > 0)
{
writeOffset += shifter->retrieve (outPtrs, numAvailable);;
}
numProcessed += sampsToProcess;
processedInBlock += sampsToProcess;
numAvailable = 0; // just in case
}
processedInBlock = 0;
while (shifter->available() > 0)
{
numAvailable = shifter->available();
writeOffset += shifter->retrieve (outPtrs, numAvailable);
}
}
saved = saveBufferToFile (writePath, output, fs); // function that lives elsewhere
}
This simply ends up writing the input buffer to the output buffer when shifter->setPitchRatio(1) and shifter->setTimeScale(x) where x is literally any number.
Any help or pointing out glaring errors is appreciated.
