From 263e0cbf70077dabe093d4fe79c09f56ab1ecb36 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 7 Jul 2022 10:35:01 +0100 Subject: [PATCH] Ensure output accumulator is properly drained at finish. We rely on the calculated output count (in offline mode) to truncate properly. --- main/main.cpp | 2 +- src/finer/R3Stretcher.cpp | 51 +++++++++++++++++++++++++-------------- src/finer/R3Stretcher.h | 7 ++++-- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/main/main.cpp b/main/main.cpp index 8f615c6..4f004ae 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -988,7 +988,7 @@ int main(int argc, char **argv) cerr << "in: " << countIn << ", out: " << countOut << ", ratio: " << float(countOut)/float(countIn) << ", ideal output: " << lrint(countIn * ratio) - << ", error: " << abs(lrint(countIn * ratio) - int(countOut)) + << ", error: " << int(countOut) - lrint(countIn * ratio) << endl; #ifdef _WIN32 diff --git a/src/finer/R3Stretcher.cpp b/src/finer/R3Stretcher.cpp index a57d6c3..4ac918a 100644 --- a/src/finer/R3Stretcher.cpp +++ b/src/finer/R3Stretcher.cpp @@ -635,6 +635,11 @@ R3Stretcher::consume() longest, longest, true); + + if (outhop < 1) { + m_log.log(0, "R3Stretcher::consume: WARNING: outhop calculated as", outhop); + outhop = 1; + } // Now inhop is the distance by which the input stream will be // advanced after our current frame has been read, and outhop is @@ -655,8 +660,10 @@ R3Stretcher::consume() if (outhop != m_prevOuthop) { m_log.log(2, "change in outhop", double(m_prevOuthop), double(outhop)); } + + auto &cd0 = m_channelData.at(0); - while (m_channelData.at(0)->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 @@ -664,13 +671,19 @@ R3Stretcher::consume() // shared_ptr (as that is not realtime safe). Same goes for // the map iterators - int readSpace = m_channelData.at(0)->inbuf->getReadSpace(); + int readSpace = cd0->inbuf->getReadSpace(); if (readSpace < longest) { if (m_mode == ProcessMode::Finished) { if (readSpace == 0) { - break; + int fill = cd0->scales.at(longest)->accumulatorFill; + if (fill == 0) { + break; + } else { + m_log.log(1, "finished reading input, but samples remaining in output accumulator", fill); + } } } else { + // await more input break; } } @@ -712,17 +725,7 @@ R3Stretcher::consume() // Resynthesis for (int c = 0; c < channels; ++c) { - synthesiseChannel(c, outhop); - } - - // We now have outhop samples at the start of the mixdown - // buffer, but they aren't necessarily all valid, because we - // might have had fewer than outhop at the start of inbuf - // (when finished and draining) - - int validCount = outhop; - if (readSpace < outhop) { - validCount = readSpace; + synthesiseChannel(c, outhop, readSpace == 0); } // Resample @@ -747,14 +750,14 @@ R3Stretcher::consume() (m_channelAssembly.resampled.data(), m_channelData[0]->resampled.size(), m_channelAssembly.mixdown.data(), - validCount, + outhop, 1.0 / m_pitchScale, m_mode == ProcessMode::Finished && readSpace < inhop); } // Emit - int writeCount = validCount; + int writeCount = outhop; if (resampling) { writeCount = resampledCount; } @@ -792,7 +795,7 @@ R3Stretcher::consume() m_totalOutputDuration += writeCount; if (m_startSkip > 0) { - int rs = m_channelData.at(0)->outbuf->getReadSpace(); + 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); @@ -1146,7 +1149,7 @@ R3Stretcher::adjustPreKick(int c) } void -R3Stretcher::synthesiseChannel(int c, int outhop) +R3Stretcher::synthesiseChannel(int c, int outhop, bool draining) { int longest = m_guideConfiguration.longestFftSize; @@ -1229,6 +1232,18 @@ R3Stretcher::synthesiseChannel(int c, int outhop) int n = scale->accumulator.size() - outhop; v_move(accptr, accptr + outhop, n); v_zero(accptr + n, outhop); + + if (draining) { + if (scale->accumulatorFill > outhop) { + auto newFill = scale->accumulatorFill - outhop; + m_log.log(2, "draining: reducing accumulatorFill from, to", scale->accumulatorFill, newFill); + scale->accumulatorFill = newFill; + } else { + scale->accumulatorFill = 0; + } + } else { + scale->accumulatorFill = scale->accumulator.size(); + } } } diff --git a/src/finer/R3Stretcher.h b/src/finer/R3Stretcher.h index b8d7346..098f617 100644 --- a/src/finer/R3Stretcher.h +++ b/src/finer/R3Stretcher.h @@ -129,6 +129,7 @@ protected: FixedVector prevMag; FixedVector pendingKick; FixedVector accumulator; + int accumulatorFill; ChannelScaleData(int _fftSize, int _longestFftSize) : fftSize(_fftSize), @@ -141,12 +142,14 @@ protected: advancedPhase(bufSize, 0.f), prevMag(bufSize, 0.f), pendingKick(bufSize, 0.f), - accumulator(_longestFftSize, 0.f) + accumulator(_longestFftSize, 0.f), + accumulatorFill(0) { } void reset() { v_zero(prevMag.data(), prevMag.size()); v_zero(accumulator.data(), accumulator.size()); + accumulatorFill = 0; } private: @@ -322,7 +325,7 @@ protected: void analyseFormant(int channel); void adjustFormant(int channel); void adjustPreKick(int channel); - void synthesiseChannel(int channel, int outhop); + void synthesiseChannel(int channel, int outhop, bool draining); struct ToPolarSpec { int magFromBin;