This is the code used to produce the audio demos for the ADC22 presentation: Four Ways To Write A Pitch-Shifter. It aimes for simplicity rather than performance.
The ideas from this code are used in the Signalsmith Stretch library.
All the different stretching methods are packed into one file. It definitely could be tidier, and I hope to get to that soon - in the meantime, do get in contact if you have any questions about it.
Each class uses a similar streaming-style API. You should be able to instantiate them simply:
HybridPhaseStretch stretch;
Configure them with channel-count and block sizes:
double blockMs = 120;
int blockSamples = int(sampleRate*0.001*blockMs);
stretch.configure(channels, blockSamples, blockSamples/4);
Then process blocks:
stretch.setTimeFactor(timeFactor);
stretch.setFreqFactor(freqFactor);
// For `outputBufferSize` output samples, how many input samples should we have?
int inputBufferSize = processor.samplesForOutput(outputBufferSize);
processor.process(inputBufferSize, inputSamples, outputBuffers, outputBufferSize);
processBlocks(stretch, timeFactor);
If you're not performing a time-stretch, then inputBufferSize
will always match outputBufferSize
.
It depends on my DSP library for a couple of things (to get delay-buffers, FFT and Kaiser windows).
The main logic is in shift-stretch.h
.
If you use make
, it compiles main.cpp
which produces a command-line interface, with built-in help. (I wouldn't recommend focusing on that code - the main stuff is in shift-stretch.h
).
It's currently written to use a Modified Real FFT, which has a half-bin offset (so we have N complex bins/bands, instead of N-1 complex and 2 real ones). I'd like to change this in future because it's probably more confusing than necessary.
It should be easy to write adapt this to a more typical Real FFT - the main difference should be in bandToFreq()
, and perhaps handling the fact that the 0Hz and Nyquist bands will be real.