.flush() processes new output (zero-valued input) for longer lengths

This commit is contained in:
Geraint 2025-08-11 14:54:32 +01:00
parent 3e71aec5f7
commit 2724daacaf
2 changed files with 41 additions and 43 deletions

View File

@ -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 # Uses input files from: https://signalsmith-audio.co.uk/code/stretch/inputs.zip
examples: out/stretch examples: out/stretch
mkdir -p out/examples mkdir -p out/examples
inputs/run-all.sh out/examples/u2- out/stretch --semitones=2 --exact inputs/run-all.sh out/examples/u2- out/stretch --semitones=2
inputs/run-all.sh out/examples/d2- out/stretch --semitones=-2 --exact inputs/run-all.sh out/examples/d2- out/stretch --semitones=-2
inputs/run-all.sh out/examples/u4- out/stretch --semitones=4 --exact inputs/run-all.sh out/examples/u4- out/stretch --semitones=4
inputs/run-all.sh out/examples/d4- out/stretch --semitones=-4 --exact inputs/run-all.sh out/examples/d4- out/stretch --semitones=-4
inputs/run-all.sh out/examples/u8- out/stretch --semitones=8 --exact inputs/run-all.sh out/examples/u8- out/stretch --semitones=8
inputs/run-all.sh out/examples/d8- out/stretch --semitones=-8 --exact inputs/run-all.sh out/examples/d8- out/stretch --semitones=-8
inputs/run-all.sh out/examples/u16- out/stretch --semitones=16 --exact inputs/run-all.sh out/examples/u16- out/stretch --semitones=16
inputs/run-all.sh out/examples/d16- out/stretch --semitones=-16 --exact inputs/run-all.sh out/examples/d16- out/stretch --semitones=-16
inputs/run-all.sh out/examples/t_8- out/stretch --time=0.8 --exact 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 --exact 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 --exact inputs/run-all.sh out/examples/t1_5- out/stretch --time=1.5
inputs/run-all.sh out/examples/t2- out/stretch --time=2 --exact inputs/run-all.sh out/examples/t2- out/stretch --time=2
inputs/run-all.sh out/examples/t4- out/stretch --time=4 --exact inputs/run-all.sh out/examples/t4- out/stretch --time=4
TEST_WAV ?= "inputs/voice.wav" TEST_WAV ?= "inputs/voice.wav"

View File

@ -415,27 +415,40 @@ struct SignalsmithStretch {
#endif #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<class Outputs> template<class Outputs>
void flush(Outputs &&outputs, int outputSamples) { void flush(Outputs &&outputs, int outputSamples, Sample playbackRate=0) {
int plainOutput = std::min<int>(outputSamples, int(stft.blockSamples())); struct Zeros {
int foldedBackOutput = std::min<int>(outputSamples, int(stft.blockSamples()) - plainOutput); 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<int>(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<int>(tailSamples, outputSamples - tailSamples);
stft.finishOutput(1); stft.finishOutput(1);
for (int c = 0; c < channels; ++c) { for (int c = 0; c < channels; ++c) {
tmpBuffer.resize(plainOutput); tmpBuffer.resize(tailSamples);
stft.readOutput(c, plainOutput, tmpBuffer.data()); stft.readOutput(c, tailSamples, tmpBuffer.data());
auto &&outputChannel = outputs[c]; auto &&outputChannel = outputs[c];
for (int i = 0; i < plainOutput; ++i) { for (int i = 0; i < tailSamples; ++i) {
outputChannel[i] = tmpBuffer[i]; outputChannel[outputBlock + i] = tmpBuffer[i];
} }
tmpBuffer.resize(foldedBackOutput); tmpBuffer.resize(reflectSamples);
stft.readOutput(c, plainOutput, foldedBackOutput, tmpBuffer.data()); stft.readOutput(c, reflectSamples, tailSamples, tmpBuffer.data());
for (int i = 0; i < foldedBackOutput; ++i) { for (int i = 0; i < reflectSamples; ++i) {
outputChannel[outputSamples - 1 - i] -= tmpBuffer[i]; outputChannel[outputBlock + tailSamples - 1 - i] -= tmpBuffer[i];
} }
} }
stft.reset(0.1f); stft.reset(0.1f);
// Reset the phase-vocoder stuff, so the next block gets a fresh start // Reset the phase-vocoder stuff, so the next block gets a fresh start
for (int c = 0; c < channels; ++c) { for (int c = 0; c < channels; ++c) {
auto channelBands = bandsForChannel(c); auto channelBands = bandsForChannel(c);
@ -467,23 +480,8 @@ struct SignalsmithStretch {
OffsetIO<Inputs> offsetInput{inputs, seekLength}; OffsetIO<Inputs> offsetInput{inputs, seekLength};
process(offsetInput, inputSamples - seekLength, outputs, outputIndex); process(offsetInput, inputSamples - seekLength, outputs, outputIndex);
struct Zeros { OffsetIO<Outputs> offsetOutput{outputs, outputIndex};
struct Channel { flush(offsetOutput, outputSamples - outputIndex, playbackRate);
Sample operator[](int) {
return 0;
}
};
Channel operator[](int) {
return {};
}
} zeros;
OffsetIO<Outputs> oo{outputs, outputIndex};
int outputIndex2 = outputSamples - stft.defaultInterval();
int outputBlock = outputIndex2 - outputIndex;
process(zeros, outputBlock/playbackRate, oo, outputBlock);
OffsetIO<Outputs> offsetOutput{outputs, outputIndex2};
flush(offsetOutput, outputSamples - outputIndex);
return true; return true;
} }