diff --git a/cmd/Makefile b/cmd/Makefile index 30fdfb2..6887a65 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -11,19 +11,19 @@ out/stretch: main.cpp ../signalsmith-stretch.h util/*.h util/*.hxx # Uses input files from: https://signalsmith-audio.co.uk/code/stretch/inputs.zip examples: out/stretch mkdir -p out/examples - inputs/run-all.sh out/examples/u2- out/stretch --semitones=2 --exact - inputs/run-all.sh out/examples/d2- out/stretch --semitones=-2 --exact - inputs/run-all.sh out/examples/u4- out/stretch --semitones=4 --exact - inputs/run-all.sh out/examples/d4- out/stretch --semitones=-4 --exact - inputs/run-all.sh out/examples/u8- out/stretch --semitones=8 --exact - inputs/run-all.sh out/examples/d8- out/stretch --semitones=-8 --exact - inputs/run-all.sh out/examples/u16- out/stretch --semitones=16 --exact - inputs/run-all.sh out/examples/d16- out/stretch --semitones=-16 --exact - inputs/run-all.sh out/examples/t_8- out/stretch --time=0.8 --exact - inputs/run-all.sh out/examples/t1_2- out/stretch --time=1.2 --exact - inputs/run-all.sh out/examples/t1_5- out/stretch --time=1.5 --exact - inputs/run-all.sh out/examples/t2- out/stretch --time=2 --exact - inputs/run-all.sh out/examples/t4- out/stretch --time=4 --exact + inputs/run-all.sh out/examples/u2- out/stretch --semitones=2 + inputs/run-all.sh out/examples/d2- out/stretch --semitones=-2 + inputs/run-all.sh out/examples/u4- out/stretch --semitones=4 + inputs/run-all.sh out/examples/d4- out/stretch --semitones=-4 + inputs/run-all.sh out/examples/u8- out/stretch --semitones=8 + inputs/run-all.sh out/examples/d8- out/stretch --semitones=-8 + inputs/run-all.sh out/examples/u16- out/stretch --semitones=16 + inputs/run-all.sh out/examples/d16- out/stretch --semitones=-16 + inputs/run-all.sh out/examples/t_8- out/stretch --time=0.8 + inputs/run-all.sh out/examples/t1_2- out/stretch --time=1.2 + inputs/run-all.sh out/examples/t1_5- out/stretch --time=1.5 + inputs/run-all.sh out/examples/t2- out/stretch --time=2 + inputs/run-all.sh out/examples/t4- out/stretch --time=4 TEST_WAV ?= "inputs/voice.wav" diff --git a/signalsmith-stretch.h b/signalsmith-stretch.h index c4620fb..c7fd4e0 100644 --- a/signalsmith-stretch.h +++ b/signalsmith-stretch.h @@ -415,27 +415,40 @@ struct SignalsmithStretch { #endif } - // Read the remaining output, providing no further input. `outputSamples` should ideally be at least `.outputLatency()` + // Read the remaining output, providing no further input. If `outputSamples` is more than one interval, it will compute additional blocks assuming a zero-valued input template - void flush(Outputs &&outputs, int outputSamples) { - int plainOutput = std::min(outputSamples, int(stft.blockSamples())); - int foldedBackOutput = std::min(outputSamples, int(stft.blockSamples()) - plainOutput); + void flush(Outputs &&outputs, int outputSamples, Sample playbackRate=0) { + struct Zeros { + struct Channel { + Sample operator[](int) { + return 0; + } + }; + Channel operator[](int) { + return {}; + } + } zeros; + // If we're asked for more than an interval of extra output, then zero-pad the input + int outputBlock = std::max(0, outputSamples - stft.defaultInterval()); + if (outputBlock > 0) process(zeros, outputBlock*playbackRate, outputs, outputBlock); + + int tailSamples = outputSamples - outputBlock; // at most one interval + int reflectSamples = std::min(tailSamples, outputSamples - tailSamples); stft.finishOutput(1); for (int c = 0; c < channels; ++c) { - tmpBuffer.resize(plainOutput); - stft.readOutput(c, plainOutput, tmpBuffer.data()); + tmpBuffer.resize(tailSamples); + stft.readOutput(c, tailSamples, tmpBuffer.data()); auto &&outputChannel = outputs[c]; - for (int i = 0; i < plainOutput; ++i) { - outputChannel[i] = tmpBuffer[i]; + for (int i = 0; i < tailSamples; ++i) { + outputChannel[outputBlock + i] = tmpBuffer[i]; } - tmpBuffer.resize(foldedBackOutput); - stft.readOutput(c, plainOutput, foldedBackOutput, tmpBuffer.data()); - for (int i = 0; i < foldedBackOutput; ++i) { - outputChannel[outputSamples - 1 - i] -= tmpBuffer[i]; + tmpBuffer.resize(reflectSamples); + stft.readOutput(c, reflectSamples, tailSamples, tmpBuffer.data()); + for (int i = 0; i < reflectSamples; ++i) { + outputChannel[outputBlock + tailSamples - 1 - i] -= tmpBuffer[i]; } } stft.reset(0.1f); - // Reset the phase-vocoder stuff, so the next block gets a fresh start for (int c = 0; c < channels; ++c) { auto channelBands = bandsForChannel(c); @@ -467,23 +480,8 @@ struct SignalsmithStretch { OffsetIO offsetInput{inputs, seekLength}; process(offsetInput, inputSamples - seekLength, outputs, outputIndex); - struct Zeros { - struct Channel { - Sample operator[](int) { - return 0; - } - }; - Channel operator[](int) { - return {}; - } - } zeros; - OffsetIO oo{outputs, outputIndex}; - int outputIndex2 = outputSamples - stft.defaultInterval(); - int outputBlock = outputIndex2 - outputIndex; - process(zeros, outputBlock/playbackRate, oo, outputBlock); - - OffsetIO offsetOutput{outputs, outputIndex2}; - flush(offsetOutput, outputSamples - outputIndex); + OffsetIO offsetOutput{outputs, outputIndex}; + flush(offsetOutput, outputSamples - outputIndex, playbackRate); return true; }