.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
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"

View File

@ -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<class Outputs>
void flush(Outputs &&outputs, int outputSamples) {
int plainOutput = std::min<int>(outputSamples, int(stft.blockSamples()));
int foldedBackOutput = std::min<int>(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<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);
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<Inputs> offsetInput{inputs, seekLength};
process(offsetInput, inputSamples - seekLength, outputs, outputIndex);
struct Zeros {
struct Channel {
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);
OffsetIO<Outputs> offsetOutput{outputs, outputIndex};
flush(offsetOutput, outputSamples - outputIndex, playbackRate);
return true;
}