From 173c7212f1ee5a6e0637924e819e8a6485d12720 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 1 Jun 2023 10:51:09 +0100 Subject: [PATCH 1/8] Ensure tests actually exercise large input buffers --- src/test/TestStretcher.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/test/TestStretcher.cpp b/src/test/TestStretcher.cpp index 719759d..a2568c7 100644 --- a/src/test/TestStretcher.cpp +++ b/src/test/TestStretcher.cpp @@ -270,6 +270,11 @@ static vector process_realtime(RubberBandStretcher &stretcher, } else if (available == 0) { // need to provide more input int required = stretcher.getSamplesRequired(); BOOST_TEST(required > 0); // because available == 0 + if (required < bs) { + // Because we sometimes want to explicitly test + // passing large blocks to process + required = bs; + } int toProcess = std::min(required, n - inOffset); const float *const source = in.data() + inOffset; bool final = (toProcess < required); @@ -324,11 +329,12 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, // expected place RubberBandStretcher stretcher(rate, 1, options, timeRatio, pitchScale); - stretcher.setMaxProcessSize(bs); if (printDebug) { stretcher.setDebugLevel(2); } + + stretcher.setMaxProcessSize(bs); // The input signal is a fixed frequency sinusoid that steps up in // amplitude every 1/10 of the total duration - from 0.1 at the @@ -665,15 +671,24 @@ BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_faster) { sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | RubberBandStretcher::OptionProcessRealTime, - 8.0, 1.5, + 8.0, 0.5, 80000); } -BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_finer) +BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_finer_stretch) { sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | RubberBandStretcher::OptionProcessRealTime, - 8.0, 1.5, + 2.0, 1.0, + 80000, + true); +} + +BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_finer_shift) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime, + 1.0, 0.5, 80000); } From af6759f74bfdf8a21f9ff052d3ff02addddf03a8 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 1 Jun 2023 10:51:20 +0100 Subject: [PATCH 2/8] Work on proper sizing of internal buffers --- src/finer/R3Stretcher.cpp | 120 +++++++++++++++++++++++++------------- src/finer/R3Stretcher.h | 10 ++-- 2 files changed, 84 insertions(+), 46 deletions(-) diff --git a/src/finer/R3Stretcher.cpp b/src/finer/R3Stretcher.cpp index 8918af3..6979f90 100644 --- a/src/finer/R3Stretcher.cpp +++ b/src/finer/R3Stretcher.cpp @@ -105,16 +105,19 @@ R3Stretcher::initialise() int inRingBufferSize = getWindowSourceSize() * 16; int outRingBufferSize = getWindowSourceSize() * 16; + int hopBufferSize = + 2 * std::max(m_limits.maxInhop, m_limits.maxPreferredOuthop); + m_channelData.clear(); for (int c = 0; c < m_parameters.channels; ++c) { m_channelData.push_back(std::make_shared (segmenterParameters, classifierParameters, - m_guideConfiguration.longestFftSize, getWindowSourceSize(), inRingBufferSize, - outRingBufferSize)); + outRingBufferSize, + hopBufferSize)); for (int b = 0; b < m_guideConfiguration.fftBandLimitCount; ++b) { const auto &band = m_guideConfiguration.fftBandLimits[b]; int fftSize = band.fftSize; @@ -636,17 +639,30 @@ R3Stretcher::getSamplesRequired() const void R3Stretcher::setMaxProcessSize(size_t n) { - size_t oldSize = m_channelData[0]->inbuf->getSize(); - size_t newSize = getWindowSourceSize() + n; + size_t oldInSize = m_channelData[0]->inbuf->getSize(); + size_t newInSize = getWindowSourceSize() + n; - if (newSize > oldSize) { - m_log.log(1, "setMaxProcessSize: resizing from and to", oldSize, newSize); + size_t oldOutSize = m_channelData[0]->outbuf->getSize(); + size_t newOutSize = newInSize; + + if (newInSize > oldInSize) { + m_log.log(1, "setMaxProcessSize: resizing inbuf from and to", oldInSize, newInSize); for (int c = 0; c < m_parameters.channels; ++c) { m_channelData[c]->inbuf = std::unique_ptr> - (m_channelData[c]->inbuf->resized(newSize)); + (m_channelData[c]->inbuf->resized(newInSize)); } } else { - m_log.log(1, "setMaxProcessSize: nothing to be done, newSize <= oldSize", newSize, oldSize); + m_log.log(1, "setMaxProcessSize: nothing to be done at inbuf, newInSize <= oldInSize", newInSize, oldInSize); + } + + if (newOutSize > oldOutSize) { + m_log.log(1, "setMaxProcessSize: resizing outbuf from and to", oldOutSize, newOutSize); + for (int c = 0; c < m_parameters.channels; ++c) { + m_channelData[c]->outbuf = std::unique_ptr> + (m_channelData[c]->outbuf->resized(newOutSize)); + } + } else { + m_log.log(1, "setMaxProcessSize: nothing to be done at outbuf, newOutSize <= oldOutSize", newOutSize, oldOutSize); } } @@ -709,17 +725,6 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) } } - if (final) { - // We don't distinguish between Finished and "draining, but - // haven't yet delivered all the samples" because the - // distinction is meaningless internally - it only affects - // whether available() finds any samples in the buffer - m_log.log(1, "final is set, entering Finished mode"); - m_mode = ProcessMode::Finished; - } else { - m_mode = ProcessMode::Processing; - } - bool resamplingBefore = false; areWeResampling(&resamplingBefore, nullptr); @@ -730,7 +735,7 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) m_log.log(2, "process: no samples but final specified, consuming"); - consume(); + consume(true); } else while (inputIx < int(samples)) { @@ -738,7 +743,7 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) int ws = m_channelData[0]->inbuf->getWriteSpace(); if (ws == 0) { - consume(); + consume(false); ws = m_channelData[0]->inbuf->getWriteSpace(); } @@ -772,13 +777,16 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) prepareInput(input, inputIx, resampleInput); + bool finalHop = (final && + inputIx + resampleInput >= int(samples)); + int resampleOutput = m_resampler->resample (m_channelAssembly.resampled.data(), maxResampleOutput, m_channelAssembly.input.data(), resampleInput, 1.0 / m_pitchScale, - final); + finalHop); inputIx += resampleInput; @@ -804,7 +812,18 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) inputIx += toWrite; } - consume(); + consume(final && inputIx >= int(samples)); + } + + if (final) { + // We don't distinguish between Finished and "draining, but + // haven't yet delivered all the samples" because the + // distinction is meaningless internally - it only affects + // whether available() finds any samples in the buffer + m_log.log(1, "final is set, entering Finished mode"); + m_mode = ProcessMode::Finished; + } else { + m_mode = ProcessMode::Processing; } } @@ -826,6 +845,8 @@ R3Stretcher::retrieve(float *const *output, size_t samples) const int got = samples; + m_log.log(2, "retrieve: requested, outbuf has", samples, m_channelData[0]->outbuf->getReadSpace()); + for (int c = 0; c < m_parameters.channels; ++c) { int gotHere = m_channelData[c]->outbuf->read(output[c], got); if (gotHere < got) { @@ -847,6 +868,8 @@ R3Stretcher::retrieve(float *const *output, size_t samples) const } } + m_log.log(2, "retrieve: returning, outbuf now has", got, m_channelData[0]->outbuf->getReadSpace()); + return got; } @@ -874,7 +897,7 @@ R3Stretcher::prepareInput(const float *const *input, int ix, int n) } void -R3Stretcher::consume() +R3Stretcher::consume(bool final) { Profiler profiler("R3Stretcher::consume"); @@ -927,23 +950,23 @@ R3Stretcher::consume() auto &cd0 = m_channelData.at(0); m_log.log(2, "consume: write space and outhop", cd0->outbuf->getWriteSpace(), outhop); - - while (cd0->outbuf->getWriteSpace() >= outhop) { + + // NB our ChannelData, ScaleData, and ChannelScaleData maps + // contain shared_ptrs; whenever we retain one of them in a + // variable, we do so by reference to avoid copying the shared_ptr + // (as that is not realtime safe). Same goes for the map iterators + + while (true) { Profiler profiler("R3Stretcher::consume/loop"); - - // NB our ChannelData, ScaleData, and ChannelScaleData maps - // contain shared_ptrs; whenever we retain one of them in a - // variable, we do so by reference to avoid copying the - // shared_ptr (as that is not realtime safe). Same goes for - // the map iterators int readSpace = cd0->inbuf->getReadSpace(); + int writeSpace = cd0->outbuf->getWriteSpace(); - m_log.log(2, "consume: read space and window source size", readSpace, getWindowSourceSize()); + m_log.log(2, "consume: read space and write space", readSpace, writeSpace); if (readSpace < getWindowSourceSize()) { - if (m_mode == ProcessMode::Finished) { + if (final) { if (readSpace == 0) { int fill = cd0->scales.at(longest)->accumulatorFill; if (fill == 0) { @@ -957,7 +980,20 @@ R3Stretcher::consume() break; } } - + + while (writeSpace < outhop) { + m_log.log(0, "R3Stretcher::process: WARNING: Forced to increase output buffer size. Using smaller process blocks or an artificially larger value for setMaxProcessSize may avoid this"); + size_t oldSize = m_channelData[0]->outbuf->getSize(); + size_t newSize = oldSize * 2; + m_log.log(0, "R3Stretcher::process: old and new sizes", oldSize, newSize); + for (int c = 0; c < m_parameters.channels; ++c) { + auto newBuf = m_channelData[c]->outbuf->resized(newSize); + m_channelData[c]->outbuf = + std::unique_ptr>(newBuf); + } + writeSpace = m_channelData[0]->outbuf->getWriteSpace(); + } + // Analysis for (int c = 0; c < channels; ++c) { @@ -1009,9 +1045,9 @@ R3Stretcher::consume() m_channelAssembly.resampled[c] = cd->resampled.data(); } - bool final = (m_mode == ProcessMode::Finished && - readSpace < inhop && - cd0->scales.at(longest)->accumulatorFill <= outhop); + bool finalHop = (final && + readSpace < inhop && + cd0->scales.at(longest)->accumulatorFill <= outhop); resampledCount = m_resampler->resample (m_channelAssembly.resampled.data(), @@ -1019,7 +1055,7 @@ R3Stretcher::consume() m_channelAssembly.mixdown.data(), outhop, 1.0 / m_pitchScale, - final); + finalHop); } // Emit @@ -1041,8 +1077,8 @@ R3Stretcher::consume() int advanceCount = inhop; if (advanceCount > readSpace) { - // This should happen only when draining (Finished) - if (m_mode != ProcessMode::Finished) { + // This should happen only when draining + if (!final) { m_log.log(0, "R3Stretcher: WARNING: readSpace < inhop when processing is not yet finished", readSpace, inhop); } advanceCount = readSpace; @@ -1074,6 +1110,8 @@ R3Stretcher::consume() m_prevInhop = inhop; m_prevOuthop = outhop; } + + m_log.log(2, "consume: write space reduced to", cd0->outbuf->getWriteSpace()); } void diff --git a/src/finer/R3Stretcher.h b/src/finer/R3Stretcher.h index 9b79552..7cdd74b 100644 --- a/src/finer/R3Stretcher.h +++ b/src/finer/R3Stretcher.h @@ -228,10 +228,10 @@ protected: std::unique_ptr formant; ChannelData(BinSegmenter::Parameters segmenterParameters, BinClassifier::Parameters classifierParameters, - int /*!!! longestFftSize */, int windowSourceSize, int inRingBufferSize, - int outRingBufferSize) : + int outRingBufferSize, + int hopBufferSize) : scales(), windowSource(windowSourceSize, 0.0), readahead(segmenterParameters.fftSize), @@ -243,8 +243,8 @@ protected: BinClassifier::Classification::Residual), segmenter(new BinSegmenter(segmenterParameters)), segmentation(), prevSegmentation(), nextSegmentation(), - mixdown(inRingBufferSize, 0.f), - resampled(outRingBufferSize, 0.f), + mixdown(hopBufferSize, 0.f), + resampled(hopBufferSize, 0.f), inbuf(new RingBuffer(inRingBufferSize)), outbuf(new RingBuffer(outRingBufferSize)), formant(new FormantData(segmenterParameters.fftSize)) { } @@ -359,7 +359,7 @@ protected: void initialise(); void prepareInput(const float *const *input, int ix, int n); - void consume(); + void consume(bool final); void createResampler(); void calculateHop(); void updateRatioFromMap(); From b1cd0913e2f4e79ee5e78d99748fcd1377693081 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 1 Jun 2023 14:09:39 +0100 Subject: [PATCH 3/8] Significant further work on internal buffer sizes --- rubberband/RubberBandStretcher.h | 7 ++ src/finer/R3Stretcher.cpp | 171 ++++++++++++++++++------------- src/finer/R3Stretcher.h | 6 +- src/test/TestStretcher.cpp | 7 +- 4 files changed, 116 insertions(+), 75 deletions(-) diff --git a/rubberband/RubberBandStretcher.h b/rubberband/RubberBandStretcher.h index 12610ce..2227bee 100644 --- a/rubberband/RubberBandStretcher.h +++ b/rubberband/RubberBandStretcher.h @@ -803,6 +803,13 @@ public: * study() (to which you may pass any number of samples at a time, * and from which there is no output). * + * Despite the existence of this call and its use of a size_t + * argument, there is an internal limit to the maximum process + * buffer size that can be requested. This is currently 524288 (or + * 2^19). The Rubber Band API is essentially block-based and is + * not designed to process an entire signal within a single + * process cycle. + * * Note that the value of "samples" refers to the number of audio * sample frames, which may be multi-channel, not the number of * individual samples. (For example, one second of stereo audio diff --git a/src/finer/R3Stretcher.cpp b/src/finer/R3Stretcher.cpp index 6979f90..735eba0 100644 --- a/src/finer/R3Stretcher.cpp +++ b/src/finer/R3Stretcher.cpp @@ -637,32 +637,58 @@ R3Stretcher::getSamplesRequired() const } void -R3Stretcher::setMaxProcessSize(size_t n) +R3Stretcher::setMaxProcessSize(size_t requested) { - size_t oldInSize = m_channelData[0]->inbuf->getSize(); - size_t newInSize = getWindowSourceSize() + n; - - size_t oldOutSize = m_channelData[0]->outbuf->getSize(); - size_t newOutSize = newInSize; - - if (newInSize > oldInSize) { - m_log.log(1, "setMaxProcessSize: resizing inbuf from and to", oldInSize, newInSize); - for (int c = 0; c < m_parameters.channels; ++c) { - m_channelData[c]->inbuf = std::unique_ptr> - (m_channelData[c]->inbuf->resized(newInSize)); - } + m_log.log(2, "R3Stretcher::setMaxProcessSize", requested); + + int n = m_limits.overallMaxProcessSize; + if (requested > size_t(n)) { + m_log.log(0, "R3Stretcher::setMaxProcessSize: request exceeds overall limit", requested, n); } else { - m_log.log(1, "setMaxProcessSize: nothing to be done at inbuf, newInSize <= oldInSize", newInSize, oldInSize); + n = int(requested); } - if (newOutSize > oldOutSize) { - m_log.log(1, "setMaxProcessSize: resizing outbuf from and to", oldOutSize, newOutSize); - for (int c = 0; c < m_parameters.channels; ++c) { - m_channelData[c]->outbuf = std::unique_ptr> - (m_channelData[c]->outbuf->resized(newOutSize)); - } - } else { - m_log.log(1, "setMaxProcessSize: nothing to be done at outbuf, newOutSize <= oldOutSize", newOutSize, oldOutSize); + ensureInbuf(n * 2, false); + ensureOutbuf(n * 8, false); +} + +void +R3Stretcher::ensureInbuf(int required, bool warn) +{ + int ws = m_channelData[0]->inbuf->getWriteSpace(); + if (required < ws) { + return; + } + if (warn) { + m_log.log(0, "R3Stretcher::ensureInbuf: WARNING: Forced to increase input buffer size. Either setMaxProcessSize was not properly called, process is being called repeatedly without retrieve, or an internal error has led to an incorrect resampler output calculation. Samples to write and space available", required, ws); + } + size_t oldSize = m_channelData[0]->inbuf->getSize(); + size_t newSize = oldSize - ws + required; + if (newSize < oldSize * 2) newSize = oldSize * 2; + m_log.log(warn ? 0 : 2, "R3Stretcher::ensureInbuf: old and new sizes", oldSize, newSize); + for (int c = 0; c < m_parameters.channels; ++c) { + auto newBuf = m_channelData[c]->inbuf->resized(newSize); + m_channelData[c]->inbuf = std::unique_ptr>(newBuf); + } +} + +void +R3Stretcher::ensureOutbuf(int required, bool warn) +{ + int ws = m_channelData[0]->outbuf->getWriteSpace(); + if (required < ws) { + return; + } + if (warn) { + m_log.log(0, "R3Stretcher::ensureOutbuf: WARNING: Forced to increase output buffer size. Using smaller process blocks or an artificially larger value for setMaxProcessSize may avoid this. Samples to write and space available", required, ws); + } + size_t oldSize = m_channelData[0]->outbuf->getSize(); + size_t newSize = oldSize - ws + required; + if (newSize < oldSize * 2) newSize = oldSize * 2; + m_log.log(warn ? 0 : 2, "R3Stretcher::ensureOutbuf: old and new sizes", oldSize, newSize); + for (int c = 0; c < m_parameters.channels; ++c) { + auto newBuf = m_channelData[c]->outbuf->resized(newSize); + m_channelData[c]->outbuf = std::unique_ptr>(newBuf); } } @@ -676,6 +702,13 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) return; } + int n = m_limits.overallMaxProcessSize; + if (samples > size_t(n)) { + m_log.log(0, "R3Stretcher::process: request exceeds overall limit", samples, n); + } else { + n = int(samples); + } + if (!isRealTime()) { if (m_mode == ProcessMode::Studying) { @@ -714,8 +747,12 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) // when the ratio changes. int pad = getWindowSourceSize() / 2; m_log.log(1, "offline mode: prefilling with", pad); + ensureInbuf(pad); for (int c = 0; c < m_parameters.channels; ++c) { - m_channelData[c]->inbuf->zero(pad); + int zeroed = m_channelData[c]->inbuf->zero(pad); + if (zeroed != pad) { + m_log.log(0, "R3Stretcher: WARNING: too few padding samples written", zeroed, pad); + } } // NB by the time we skip this later we may have resampled @@ -731,35 +768,22 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) int channels = m_parameters.channels; int inputIx = 0; - if (samples == 0 && final) { + if (n == 0 && final) { m_log.log(2, "process: no samples but final specified, consuming"); consume(true); - } else while (inputIx < int(samples)) { + } else while (inputIx < n) { + + int remaining = n - inputIx; - int remaining = int(samples) - inputIx; int ws = m_channelData[0]->inbuf->getWriteSpace(); - if (ws == 0) { consume(false); - ws = m_channelData[0]->inbuf->getWriteSpace(); - } - - if (ws == 0) { - m_log.log(0, "R3Stretcher::process: WARNING: Forced to increase input buffer size. Either setMaxProcessSize was not properly called, process is being called repeatedly without retrieve, or an internal error has led to an incorrect resampler output calculation. Samples to write", remaining); - size_t oldSize = m_channelData[0]->inbuf->getSize(); - size_t newSize = oldSize + remaining; - if (newSize < oldSize * 2) newSize = oldSize * 2; - m_log.log(0, "R3Stretcher::process: old and new sizes", oldSize, newSize); - for (int c = 0; c < m_parameters.channels; ++c) { - auto newBuf = m_channelData[c]->inbuf->resized(newSize); - m_channelData[c]->inbuf = - std::unique_ptr>(newBuf); - } - continue; } + ensureInbuf(remaining); + ws = m_channelData[0]->inbuf->getWriteSpace(); if (resamplingBefore) { @@ -775,10 +799,11 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) int resampleInput = std::min(remaining, maxResampleInput); if (resampleInput == 0) resampleInput = 1; + m_log.log(2, "R3Stretcher::process: resamplingBefore is true, resampleInput and maxResampleOutput", resampleInput, maxResampleOutput); + prepareInput(input, inputIx, resampleInput); - bool finalHop = (final && - inputIx + resampleInput >= int(samples)); + bool finalHop = (final && inputIx + resampleInput >= n); int resampleOutput = m_resampler->resample (m_channelAssembly.resampled.data(), @@ -789,13 +814,16 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) finalHop); inputIx += resampleInput; - - m_log.log(2, "process: resamplingBefore is true, writing to inbuf from resampled data, former read space and samples being added", m_channelData[0]->inbuf->getReadSpace(), resampleOutput); - + for (int c = 0; c < m_parameters.channels; ++c) { - m_channelData[c]->inbuf->write + + int written = m_channelData[c]->inbuf->write (m_channelData.at(c)->resampled.data(), resampleOutput); + + if (written != resampleOutput) { + m_log.log(0, "R3Stretcher: WARNING: too few samples written to input buffer from resampler", written, resampleOutput); + } } } else { @@ -806,13 +834,19 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) prepareInput(input, inputIx, toWrite); for (int c = 0; c < m_parameters.channels; ++c) { - m_channelData[c]->inbuf->write + + int written = m_channelData[c]->inbuf->write (m_channelAssembly.input[c], toWrite); + + if (written != toWrite) { + m_log.log(0, "R3Stretcher: WARNING: too few samples written to input buffer", written, toWrite); + } } + inputIx += toWrite; } - consume(final && inputIx >= int(samples)); + consume(final && inputIx >= n); } if (final) { @@ -961,9 +995,7 @@ R3Stretcher::consume(bool final) Profiler profiler("R3Stretcher::consume/loop"); int readSpace = cd0->inbuf->getReadSpace(); - int writeSpace = cd0->outbuf->getWriteSpace(); - - m_log.log(2, "consume: read space and write space", readSpace, writeSpace); + m_log.log(2, "consume: read space", readSpace); if (readSpace < getWindowSourceSize()) { if (final) { @@ -980,19 +1012,8 @@ R3Stretcher::consume(bool final) break; } } - - while (writeSpace < outhop) { - m_log.log(0, "R3Stretcher::process: WARNING: Forced to increase output buffer size. Using smaller process blocks or an artificially larger value for setMaxProcessSize may avoid this"); - size_t oldSize = m_channelData[0]->outbuf->getSize(); - size_t newSize = oldSize * 2; - m_log.log(0, "R3Stretcher::process: old and new sizes", oldSize, newSize); - for (int c = 0; c < m_parameters.channels; ++c) { - auto newBuf = m_channelData[c]->outbuf->resized(newSize); - m_channelData[c]->outbuf = - std::unique_ptr>(newBuf); - } - writeSpace = m_channelData[0]->outbuf->getWriteSpace(); - } + + ensureOutbuf(outhop); // Analysis @@ -1086,12 +1107,19 @@ R3Stretcher::consume(bool final) for (int c = 0; c < channels; ++c) { auto &cd = m_channelData.at(c); + int written = 0; if (resamplingAfter) { - cd->outbuf->write(cd->resampled.data(), writeCount); + written = cd->outbuf->write(cd->resampled.data(), writeCount); } else { - cd->outbuf->write(cd->mixdown.data(), writeCount); + written = cd->outbuf->write(cd->mixdown.data(), writeCount); + } + if (written != writeCount) { + m_log.log(0, "R3Stretcher: WARNING: too few samples written to output buffer", written, writeCount); + } + int skipped = cd->inbuf->skip(advanceCount); + if (skipped != advanceCount) { + m_log.log(0, "R3Stretcher: WARNING: too few samples advanced", skipped, advanceCount); } - cd->inbuf->skip(advanceCount); } m_consumedInputDuration += advanceCount; @@ -1101,7 +1129,10 @@ R3Stretcher::consume(bool final) int rs = cd0->outbuf->getReadSpace(); int toSkip = std::min(m_startSkip, rs); for (int c = 0; c < channels; ++c) { - m_channelData.at(c)->outbuf->skip(toSkip); + int skipped = m_channelData.at(c)->outbuf->skip(toSkip); + if (skipped != toSkip) { + m_log.log(0, "R3Stretcher: WARNING: too few samples skipped at output", skipped, toSkip); + } } m_startSkip -= toSkip; m_totalOutputDuration = rs - toSkip; diff --git a/src/finer/R3Stretcher.h b/src/finer/R3Stretcher.h index 7cdd74b..0f97a28 100644 --- a/src/finer/R3Stretcher.h +++ b/src/finer/R3Stretcher.h @@ -110,13 +110,15 @@ protected: int minInhop; int maxInhopWithReadahead; int maxInhop; + int overallMaxProcessSize; Limits(RubberBandStretcher::Options options, double rate) : // commented values are results when rate = 44100 or 48000 minPreferredOuthop(roundUpDiv(rate, 512)), // 128 maxPreferredOuthop(roundUpDiv(rate, 128)), // 512 minInhop(1), maxInhopWithReadahead(roundUpDiv(rate, 64)), // 1024 - maxInhop(roundUpDiv(rate, 32)) { // 2048 + maxInhop(roundUpDiv(rate, 32)), // 2048 + overallMaxProcessSize(524288) { if (options & RubberBandStretcher::OptionWindowShort) { // See note in calculateHop minPreferredOuthop = roundUpDiv(rate, 256); // 256 @@ -361,6 +363,8 @@ protected: void prepareInput(const float *const *input, int ix, int n); void consume(bool final); void createResampler(); + void ensureInbuf(int, bool warn = true); + void ensureOutbuf(int, bool warn = true); void calculateHop(); void updateRatioFromMap(); void analyseChannel(int channel, int inhop, int prevInhop, int prevOuthop); diff --git a/src/test/TestStretcher.cpp b/src/test/TestStretcher.cpp index a2568c7..1817af7 100644 --- a/src/test/TestStretcher.cpp +++ b/src/test/TestStretcher.cpp @@ -680,8 +680,7 @@ BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_finer_stretch) sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | RubberBandStretcher::OptionProcessRealTime, 2.0, 1.0, - 80000, - true); + 80000); } BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_finer_shift) @@ -1394,8 +1393,8 @@ static void with_resets(RubberBandStretcher::Options options, for (int i = 0; i < nActual; ++i) { BOOST_TEST(out[i] == outRef[i]); if (out[i] != outRef[i]) { - std::cerr << "Failure at index " << i << " in run " - << run << std::endl; + std::cerr << "Failure at index " << i << " of " + << nActual << " in run " << run << std::endl; break; } } From fe1aafea282392818301597043b597c89e946c03 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Wed, 7 Jun 2023 11:16:07 +0100 Subject: [PATCH 4/8] Further work on end state with long process sizes in R2. This passes tests but is not the minimal necessary change I think - review. --- src/faster/R2Stretcher.cpp | 11 +++++ src/faster/StretcherProcess.cpp | 79 ++++++++++++++++++++++++++------- src/test/TestStretcher.cpp | 74 +++++++++++++++++++++++++----- 3 files changed, 135 insertions(+), 29 deletions(-) diff --git a/src/faster/R2Stretcher.cpp b/src/faster/R2Stretcher.cpp index 4c44bc8..07c7193 100644 --- a/src/faster/R2Stretcher.cpp +++ b/src/faster/R2Stretcher.cpp @@ -307,7 +307,10 @@ R2Stretcher::setExpectedInputDuration(size_t samples) void R2Stretcher::setMaxProcessSize(size_t samples) { + m_log.log(2, "R2Stretcher::setMaxProcessSize", samples); if (samples <= m_maxProcessSize) return; + + m_log.log(2, "R2Stretcher::setMaxProcessSize: increasing from, to", m_maxProcessSize, samples); m_maxProcessSize = samples; reconfigure(); @@ -1232,6 +1235,8 @@ R2Stretcher::process(const float *const *input, size_t samples, bool final) { Profiler profiler("R2Stretcher::process"); + m_log.log(2, "process entering, samples and final", samples, final); + if (m_mode == Finished) { m_log.log(0, "R2Stretcher::process: Cannot process again after final chunk"); return; @@ -1294,10 +1299,16 @@ R2Stretcher::process(const float *const *input, size_t samples, bool final) consumed[c], samples - consumed[c], final); + if (c == 0) { + m_log.log(2, "consumed channel 0, consumed and samples now", consumed[c], samples); + } if (consumed[c] < samples) { allConsumed = false; } else { if (final) { + if (c == 0) { + m_log.log(2, "final is true, setting input size", m_channelData[c]->inCount); + } m_channelData[c]->inputSize = m_channelData[c]->inCount; } } diff --git a/src/faster/StretcherProcess.cpp b/src/faster/StretcherProcess.cpp index 63e6988..c9eb096 100644 --- a/src/faster/StretcherProcess.cpp +++ b/src/faster/StretcherProcess.cpp @@ -224,6 +224,7 @@ R2Stretcher::consumeChannel(size_t c, if (writable < toWrite) { if (resampling) { + m_log.log(1, "consumeChannel: resampler produced too much output, cannot use", toWrite, writable); return 0; } toWrite = writable; @@ -323,6 +324,8 @@ R2Stretcher::processOneChunk() { Profiler profiler("R2Stretcher::processOneChunk"); + m_log.log(2, "R2Stretcher::processOneChunk"); + // Process a single chunk for all channels, provided there is // enough data on each channel for at least one chunk. This is // able to calculate increments as it goes along. @@ -335,6 +338,7 @@ R2Stretcher::processOneChunk() return false; } ChannelData &cd = *m_channelData[c]; + m_log.log(2, "read space and draining", cd.inbuf->getReadSpace(), cd.draining); if (!cd.draining) { size_t ready = cd.inbuf->getReadSpace(); assert(ready >= m_aWindowSize || cd.inputSize >= 0); @@ -356,6 +360,7 @@ R2Stretcher::processOneChunk() m_channelData[c]->chunkCount++; } + m_log.log(2, "R2Stretcher::processOneChunk returning", last); return last; } @@ -395,7 +400,9 @@ R2Stretcher::testInbufReadSpace(size_t c) m_log.log(2, "read space = 0, giving up"); return false; } else if (rs < m_aWindowSize/2) { - m_log.log(2, "setting draining true with read space", rs); + m_log.log(2, "setting draining true with read space and window size", rs, m_aWindowSize); + m_log.log(2, "outbuf read space is", cd.outbuf->getReadSpace()); + m_log.log(2, "accumulator fill is", cd.accumulatorFill); cd.draining = true; } } @@ -454,6 +461,11 @@ R2Stretcher::processChunkForChannel(size_t c, if (cd.draining) { m_log.log(2, "draining: accumulator fill and shift increment", cd.accumulatorFill, shiftIncrement); + m_log.log(2, "outbuf read space is", cd.outbuf->getReadSpace()); + if (cd.accumulatorFill == 0) { + m_log.log(2, "draining: accumulator empty"); + return true; + } if (shiftIncrement == 0) { m_log.log(0, "WARNING: draining: shiftIncrement == 0, can't handle that in this context: setting to", m_increment); shiftIncrement = m_increment; @@ -494,6 +506,7 @@ R2Stretcher::processChunkForChannel(size_t c, } writeChunk(c, shiftIncrement, last); + m_log.log(2, "processChunkForChannel: accumulatorFill now; returning", cd.accumulatorFill, last); return last; } @@ -1069,7 +1082,7 @@ R2Stretcher::writeChunk(size_t channel, size_t shiftIncrement, bool last) si, 1.0 / m_pitchScale, last); - + #if defined(STRETCHER_IMPL_RESAMPLER_MUTEX_REQUIRED) if (m_threaded) { m_resamplerMutex.unlock(); @@ -1095,10 +1108,12 @@ R2Stretcher::writeChunk(size_t channel, size_t shiftIncrement, bool last) } else { cd.accumulatorFill = 0; if (cd.draining) { - m_log.log(2, "processChunks: setting outputComplete to true"); + m_log.log(2, "writeChunk: setting outputComplete to true"); cd.outputComplete = true; } } + + m_log.log(2, "writeChunk: accumulatorFill now", cd.accumulatorFill); } void @@ -1134,7 +1149,7 @@ R2Stretcher::writeOutput(RingBuffer &to, } } - m_log.log(3, "writing", qty); + m_log.log(2, "writing", qty); size_t written = to.write(from, qty); @@ -1143,6 +1158,8 @@ R2Stretcher::writeOutput(RingBuffer &to, } outCount += written; + + m_log.log(2, "written and new outCount", written, outCount); return; } @@ -1168,6 +1185,8 @@ R2Stretcher::available() const { Profiler profiler("R2Stretcher::available"); + m_log.log(2, "R2Stretcher::available"); + #ifndef NO_THREADING if (m_threaded) { MutexLocker locker(&m_threadSetMutex); @@ -1180,14 +1199,24 @@ R2Stretcher::available() const #ifndef NO_THREADING if (!m_threaded) { #endif - for (size_t c = 0; c < m_channels; ++c) { - if (m_channelData[c]->inputSize >= 0) { - if (m_channelData[c]->inbuf->getReadSpace() > 0) { - m_log.log(2, "calling processChunks from available, channel" , c); - //!!! do we ever actually do this? if so, this method should not be const - // ^^^ yes, we do sometimes -- e.g. when fed a very short file - bool any = false, last = false; - ((R2Stretcher *)this)->processChunks(c, any, last); + if (m_channelData[0]->inputSize >= 0) { + //!!! do we ever actually do this? if so, this method should not be const + // ^^^ yes, we do sometimes -- e.g. when fed a very short file + if (m_realtime) { + while (m_channelData[0]->inbuf->getReadSpace() > 0 || + (m_channelData[0]->accumulatorFill > 0 && m_channelData[0]->draining)) { + m_log.log(2, "calling processOneChunk from available"); + if (((R2Stretcher *)this)->processOneChunk()) { + break; + } + } + } else { + for (size_t c = 0; c < m_channels; ++c) { + if (m_channelData[c]->inbuf->getReadSpace() > 0) { + m_log.log(2, "calling processChunks from available, channel" , c); + bool any = false, last = false; + ((R2Stretcher *)this)->processChunks(c, any, last); + } } } } @@ -1202,17 +1231,29 @@ R2Stretcher::available() const for (size_t i = 0; i < m_channels; ++i) { size_t availIn = m_channelData[i]->inbuf->getReadSpace(); size_t availOut = m_channelData[i]->outbuf->getReadSpace(); - m_log.log(3, "available in and out", availIn, availOut); + m_log.log(2, "available in and out", availIn, availOut); if (i == 0 || availOut < min) min = availOut; if (!m_channelData[i]->outputComplete) consumed = false; if (m_channelData[i]->resampler) haveResamplers = true; } - if (min == 0 && consumed) return -1; - if (m_pitchScale == 1.0) return min; + if (min == 0 && consumed) { + m_log.log(2, "R2Stretcher::available: end of stream"); + return -1; + } + if (m_pitchScale == 1.0) { + m_log.log(2, "R2Stretcher::available (not shifting): returning", min); + return min; + } - if (haveResamplers) return min; // resampling has already happened - return int(floor(min / m_pitchScale)); + int rv; + if (haveResamplers) { + rv = min; // resampling has already happened + } else { + rv = int(floor(min / m_pitchScale)); + } + m_log.log(2, "R2Stretcher::available (shifting): returning", rv); + return rv; } size_t @@ -1220,6 +1261,8 @@ R2Stretcher::retrieve(float *const *output, size_t samples) const { Profiler profiler("R2Stretcher::retrieve"); + m_log.log(2, "R2Stretcher::retrieve", samples); + size_t got = samples; for (size_t c = 0; c < m_channels; ++c) { @@ -1244,6 +1287,8 @@ R2Stretcher::retrieve(float *const *output, size_t samples) const } } + m_log.log(2, "R2Stretcher::retrieve returning", got); + return got; } diff --git a/src/test/TestStretcher.cpp b/src/test/TestStretcher.cpp index 1817af7..8213dbe 100644 --- a/src/test/TestStretcher.cpp +++ b/src/test/TestStretcher.cpp @@ -233,6 +233,7 @@ static vector process_realtime(RubberBandStretcher &stretcher, const vector &in, int nOut, int bs, + bool roundUpProcessSize, bool printDebug) { int n = in.size(); @@ -270,14 +271,20 @@ static vector process_realtime(RubberBandStretcher &stretcher, } else if (available == 0) { // need to provide more input int required = stretcher.getSamplesRequired(); BOOST_TEST(required > 0); // because available == 0 - if (required < bs) { - // Because we sometimes want to explicitly test - // passing large blocks to process - required = bs; + int toProcess = required; + if (roundUpProcessSize) { + // We sometimes want to explicitly test passing + // large blocks to process, longer than + // getSamplesRequired indicates + toProcess = std::max(required, bs); + } + bool final = false; + if (toProcess >= n - inOffset) { + toProcess = n - inOffset; + final = true; } - int toProcess = std::min(required, n - inOffset); const float *const source = in.data() + inOffset; - bool final = (toProcess < required); +// cerr << "toProcess = " << toProcess << ", inOffset = " << inOffset << ", n = " << n << ", required = " << required << ", outOffset = " << outOffset << ", obtained = " << obtained << ", bs = " << bs << ", final = " << final << endl; stretcher.process(&source, toProcess, final); inOffset += toProcess; BOOST_TEST(stretcher.available() > 0); @@ -316,6 +323,7 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, double timeRatio, double pitchScale, int bs = 512, + bool roundUpProcessSize = false, bool printDebug = false) { int n = (timeRatio < 1.0 ? 80000 : 40000); @@ -348,7 +356,8 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, in[i] = sample; } - vector out = process_realtime(stretcher, in, nOut, bs, printDebug); + vector out = process_realtime(stretcher, in, nOut, bs, + roundUpProcessSize, printDebug); // Step through the output signal in chunk of 1/20 of its duration // (i.e. a rather arbitrary two per expected 0.1 increment in @@ -671,8 +680,48 @@ BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_faster) { sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | RubberBandStretcher::OptionProcessRealTime, - 8.0, 0.5, - 80000); + 4.0, 0.5, + 80000, true); +} + +BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_faster_stretch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime, + 2.0, 1.0, + 80000, true); +} + +BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_faster_shrink) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime, + 0.8, 1.0, + 80000, true); +} + +BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_faster_higher) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime, + 1.0, 2.0, + 80000, true); +} + +BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_faster_lower) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime, + 1.0, 0.5, + 80000, true); +} + +BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_finer) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime, + 4.0, 0.5, + 80000, true); } BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_finer_stretch) @@ -680,7 +729,7 @@ BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_finer_stretch) sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | RubberBandStretcher::OptionProcessRealTime, 2.0, 1.0, - 80000); + 80000, true); } BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_finer_shift) @@ -688,7 +737,7 @@ BOOST_AUTO_TEST_CASE(sinusoid_realtime_long_blocksize_finer_shift) sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | RubberBandStretcher::OptionProcessRealTime, 1.0, 0.5, - 80000); + 80000, true); } BOOST_AUTO_TEST_CASE(impulses_2x_offline_faster) @@ -926,7 +975,8 @@ static void impulses_realtime(RubberBandStretcher::Options options, in[9900] = 1.f; in[9901] = -1.f; - vector out = process_realtime(stretcher, in, nOut, bs, printDebug); + vector out = process_realtime(stretcher, in, nOut, bs, + false, printDebug); int peak0 = -1, peak1 = -1, peak2 = -1; float max; From 74395b9976b397f0d29bea91c86fbd438c08e1f4 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Wed, 7 Jun 2023 14:37:29 +0100 Subject: [PATCH 5/8] Fix oversized final block tests for libsamplerate as well --- src/faster/StretcherProcess.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/faster/StretcherProcess.cpp b/src/faster/StretcherProcess.cpp index c9eb096..c77ffb7 100644 --- a/src/faster/StretcherProcess.cpp +++ b/src/faster/StretcherProcess.cpp @@ -175,8 +175,10 @@ R2Stretcher::consumeChannel(size_t c, Profiler profiler2("R2Stretcher::resample"); toWrite = int(ceil(samples / m_pitchScale)); + bool shortened = false; if (writable < toWrite) { samples = int(floor(writable * m_pitchScale)); + shortened = true; if (samples == 0) return 0; } @@ -213,7 +215,7 @@ R2Stretcher::consumeChannel(size_t c, &input, samples, 1.0 / m_pitchScale, - final); + final && !shortened); #if defined(STRETCHER_IMPL_RESAMPLER_MUTEX_REQUIRED) if (m_threaded) { From 73f7f7bb0aa4573dad2bebd4580500b57fd00765 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Wed, 7 Jun 2023 17:01:47 +0100 Subject: [PATCH 6/8] Fix handling of oversized process buffers in mid-side mode --- src/finer/R3Stretcher.cpp | 8 ++++++++ src/finer/R3Stretcher.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/finer/R3Stretcher.cpp b/src/finer/R3Stretcher.cpp index 735eba0..32fb8c1 100644 --- a/src/finer/R3Stretcher.cpp +++ b/src/finer/R3Stretcher.cpp @@ -669,6 +669,9 @@ R3Stretcher::ensureInbuf(int required, bool warn) for (int c = 0; c < m_parameters.channels; ++c) { auto newBuf = m_channelData[c]->inbuf->resized(newSize); m_channelData[c]->inbuf = std::unique_ptr>(newBuf); + // mixdown is used for mid-side mixing as well as the single + // hop output mix, so it needs to be enough to match the inbuf + m_channelData[c]->mixdown.resize(newSize, 0.f); } } @@ -913,6 +916,11 @@ R3Stretcher::prepareInput(const float *const *input, int ix, int n) if (useMidSide()) { auto &c0 = m_channelData.at(0)->mixdown; auto &c1 = m_channelData.at(1)->mixdown; + int bufsize = c0.size(); + if (n > bufsize) { + m_log.log(0, "R3Stretcher::prepareInput: WARNING: called with size greater than mixdown buffer length", n, bufsize); + n = bufsize; + } for (int i = 0; i < n; ++i) { float l = input[0][i + ix]; float r = input[1][i + ix]; diff --git a/src/finer/R3Stretcher.h b/src/finer/R3Stretcher.h index 0f97a28..cea3742 100644 --- a/src/finer/R3Stretcher.h +++ b/src/finer/R3Stretcher.h @@ -245,7 +245,7 @@ protected: BinClassifier::Classification::Residual), segmenter(new BinSegmenter(segmenterParameters)), segmentation(), prevSegmentation(), nextSegmentation(), - mixdown(hopBufferSize, 0.f), + mixdown(inRingBufferSize, 0.f), resampled(hopBufferSize, 0.f), inbuf(new RingBuffer(inRingBufferSize)), outbuf(new RingBuffer(outRingBufferSize)), From d97551834fd20daf36f4716f3ff69bdfd39f64f8 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 8 Jun 2023 09:40:11 +0100 Subject: [PATCH 7/8] Make some debug lower priority --- src/faster/R2Stretcher.cpp | 8 ++++---- src/faster/StretcherProcess.cpp | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/faster/R2Stretcher.cpp b/src/faster/R2Stretcher.cpp index 07c7193..1ad1eec 100644 --- a/src/faster/R2Stretcher.cpp +++ b/src/faster/R2Stretcher.cpp @@ -1235,7 +1235,7 @@ R2Stretcher::process(const float *const *input, size_t samples, bool final) { Profiler profiler("R2Stretcher::process"); - m_log.log(2, "process entering, samples and final", samples, final); + m_log.log(3, "process entering, samples and final", samples, final); if (m_mode == Finished) { m_log.log(0, "R2Stretcher::process: Cannot process again after final chunk"); @@ -1300,7 +1300,7 @@ R2Stretcher::process(const float *const *input, size_t samples, bool final) samples - consumed[c], final); if (c == 0) { - m_log.log(2, "consumed channel 0, consumed and samples now", consumed[c], samples); + m_log.log(3, "consumed channel 0, consumed and samples now", consumed[c], samples); } if (consumed[c] < samples) { allConsumed = false; @@ -1343,10 +1343,10 @@ R2Stretcher::process(const float *const *input, size_t samples, bool final) } #endif - m_log.log(2, "process looping"); + m_log.log(3, "process looping"); } - m_log.log(2, "process returning"); + m_log.log(3, "process returning"); if (final) m_mode = Finished; } diff --git a/src/faster/StretcherProcess.cpp b/src/faster/StretcherProcess.cpp index c77ffb7..2e664e6 100644 --- a/src/faster/StretcherProcess.cpp +++ b/src/faster/StretcherProcess.cpp @@ -326,7 +326,7 @@ R2Stretcher::processOneChunk() { Profiler profiler("R2Stretcher::processOneChunk"); - m_log.log(2, "R2Stretcher::processOneChunk"); + m_log.log(3, "R2Stretcher::processOneChunk"); // Process a single chunk for all channels, provided there is // enough data on each channel for at least one chunk. This is @@ -340,7 +340,7 @@ R2Stretcher::processOneChunk() return false; } ChannelData &cd = *m_channelData[c]; - m_log.log(2, "read space and draining", cd.inbuf->getReadSpace(), cd.draining); + m_log.log(3, "read space and draining", cd.inbuf->getReadSpace(), cd.draining); if (!cd.draining) { size_t ready = cd.inbuf->getReadSpace(); assert(ready >= m_aWindowSize || cd.inputSize >= 0); @@ -362,7 +362,7 @@ R2Stretcher::processOneChunk() m_channelData[c]->chunkCount++; } - m_log.log(2, "R2Stretcher::processOneChunk returning", last); + m_log.log(3, "R2Stretcher::processOneChunk returning", last); return last; } @@ -508,7 +508,7 @@ R2Stretcher::processChunkForChannel(size_t c, } writeChunk(c, shiftIncrement, last); - m_log.log(2, "processChunkForChannel: accumulatorFill now; returning", cd.accumulatorFill, last); + m_log.log(3, "processChunkForChannel: accumulatorFill now; returning", cd.accumulatorFill, last); return last; } @@ -1115,7 +1115,7 @@ R2Stretcher::writeChunk(size_t channel, size_t shiftIncrement, bool last) } } - m_log.log(2, "writeChunk: accumulatorFill now", cd.accumulatorFill); + m_log.log(3, "writeChunk: accumulatorFill now", cd.accumulatorFill); } void @@ -1151,7 +1151,7 @@ R2Stretcher::writeOutput(RingBuffer &to, } } - m_log.log(2, "writing", qty); + m_log.log(3, "writing", qty); size_t written = to.write(from, qty); @@ -1161,7 +1161,7 @@ R2Stretcher::writeOutput(RingBuffer &to, outCount += written; - m_log.log(2, "written and new outCount", written, outCount); + m_log.log(3, "written and new outCount", written, outCount); return; } @@ -1187,7 +1187,7 @@ R2Stretcher::available() const { Profiler profiler("R2Stretcher::available"); - m_log.log(2, "R2Stretcher::available"); + m_log.log(3, "R2Stretcher::available"); #ifndef NO_THREADING if (m_threaded) { @@ -1233,7 +1233,7 @@ R2Stretcher::available() const for (size_t i = 0; i < m_channels; ++i) { size_t availIn = m_channelData[i]->inbuf->getReadSpace(); size_t availOut = m_channelData[i]->outbuf->getReadSpace(); - m_log.log(2, "available in and out", availIn, availOut); + m_log.log(3, "available in and out", availIn, availOut); if (i == 0 || availOut < min) min = availOut; if (!m_channelData[i]->outputComplete) consumed = false; if (m_channelData[i]->resampler) haveResamplers = true; @@ -1244,7 +1244,7 @@ R2Stretcher::available() const return -1; } if (m_pitchScale == 1.0) { - m_log.log(2, "R2Stretcher::available (not shifting): returning", min); + m_log.log(3, "R2Stretcher::available (not shifting): returning", min); return min; } @@ -1254,7 +1254,7 @@ R2Stretcher::available() const } else { rv = int(floor(min / m_pitchScale)); } - m_log.log(2, "R2Stretcher::available (shifting): returning", rv); + m_log.log(3, "R2Stretcher::available (shifting): returning", rv); return rv; } @@ -1263,7 +1263,7 @@ R2Stretcher::retrieve(float *const *output, size_t samples) const { Profiler profiler("R2Stretcher::retrieve"); - m_log.log(2, "R2Stretcher::retrieve", samples); + m_log.log(3, "R2Stretcher::retrieve", samples); size_t got = samples; @@ -1289,7 +1289,7 @@ R2Stretcher::retrieve(float *const *output, size_t samples) const } } - m_log.log(2, "R2Stretcher::retrieve returning", got); + m_log.log(3, "R2Stretcher::retrieve returning", got); return got; } From 2949996cf605565880138e503670057a68723775 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 8 Jun 2023 09:44:09 +0100 Subject: [PATCH 8/8] Remove redundant test (processOneChunk checks this and returns true if accumulator empty when draining) --- src/faster/StretcherProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/faster/StretcherProcess.cpp b/src/faster/StretcherProcess.cpp index 2e664e6..1974e30 100644 --- a/src/faster/StretcherProcess.cpp +++ b/src/faster/StretcherProcess.cpp @@ -1206,7 +1206,7 @@ R2Stretcher::available() const // ^^^ yes, we do sometimes -- e.g. when fed a very short file if (m_realtime) { while (m_channelData[0]->inbuf->getReadSpace() > 0 || - (m_channelData[0]->accumulatorFill > 0 && m_channelData[0]->draining)) { + m_channelData[0]->draining) { m_log.log(2, "calling processOneChunk from available"); if (((R2Stretcher *)this)->processOneChunk()) { break;