From 0fb8b19ab5d9893f4a165f65ac6c7e03ac2b7c5f Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Mon, 20 Feb 2023 09:53:20 +0000 Subject: [PATCH 1/6] Working on further tests --- src/test/TestStretcher.cpp | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/test/TestStretcher.cpp b/src/test/TestStretcher.cpp index 29fe542..bacbcfc 100644 --- a/src/test/TestStretcher.cpp +++ b/src/test/TestStretcher.cpp @@ -679,4 +679,52 @@ BOOST_AUTO_TEST_CASE(impulses_2x_5up_offline_finer) */ } +BOOST_AUTO_TEST_CASE(final_realtime_faster) +{ + int n = 10000; + float freq = 440.f; + int rate = 44100; + int blocksize = 700; + RubberBandStretcher stretcher + (rate, 1, + RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime); + + stretcher.setTimeRatio(2.0); + + int excess = 10000; + vector in(n, 0.f), out(n * 2 + excess, 0.f); + + for (int i = n - 100; i < n; ++i) { + in[i] = sinf(float(i) * freq * M_PI * 2.f / float(rate)); + } + float *inp = in.data(), *outp = out.data(); + + stretcher.setMaxProcessSize(blocksize); + BOOST_TEST(stretcher.available() == 0); + + int incount = 0, outcount = 0; + while (incount < n) { + + int inbs = std::min(blocksize, n - incount); + BOOST_TEST(inbs > 0); + + bool final = (incount + inbs >= n); + float *in = inp + incount; + stretcher.process(&in, inbs, final); + + int avail = stretcher.available(); + BOOST_TEST(avail >= 0); + BOOST_TEST(outcount + avail < n + excess); + + float *out = outp + outcount; + size_t got = stretcher.retrieve(&out, avail); + BOOST_TEST(got == size_t(avail)); + BOOST_TEST(stretcher.available() == 0); + + incount += inbs; + outcount += got; + } +} + BOOST_AUTO_TEST_SUITE_END() From afd1b1f86c0c66341a084c58d253841b69ef858f Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Mon, 20 Feb 2023 15:25:27 +0000 Subject: [PATCH 2/6] Ensure we consume when called with final=true and no samples - this was the default behaviour in R2 and also initially in R3 but changed when resample-before modes were added --- src/finer/R3Stretcher.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/finer/R3Stretcher.cpp b/src/finer/R3Stretcher.cpp index 36da6b0..e56f3d2 100644 --- a/src/finer/R3Stretcher.cpp +++ b/src/finer/R3Stretcher.cpp @@ -679,7 +679,13 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) int channels = m_parameters.channels; int inputIx = 0; - while (inputIx < int(samples)) { + if (samples == 0 && final) { + + m_log.log(2, "process: no samples but final specified, consuming"); + + consume(); + + } else while (inputIx < int(samples)) { int remaining = int(samples) - inputIx; int ws = m_channelData[0]->inbuf->getWriteSpace(); From 1368ba87eeb748644e1d208cd17b904a60b1e111 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Mon, 20 Feb 2023 15:26:12 +0000 Subject: [PATCH 3/6] Fix latency calculations in resample-before modes. This affects realtime use in the non-recommended realtime pitch modes (i.e. those other than HighConsistency) --- src/faster/R2Stretcher.cpp | 23 ++++- src/finer/R3Stretcher.cpp | 49 ++++++++-- src/test/TestStretcher.cpp | 179 +++++++++++++++++++++++++++++++------ 3 files changed, 217 insertions(+), 34 deletions(-) diff --git a/src/faster/R2Stretcher.cpp b/src/faster/R2Stretcher.cpp index bbf423c..996d195 100644 --- a/src/faster/R2Stretcher.cpp +++ b/src/faster/R2Stretcher.cpp @@ -667,6 +667,7 @@ R2Stretcher::configure() size_t rbs = lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale)); if (rbs < m_increment * 16) rbs = m_increment * 16; + if (rbs < m_aWindowSize * 2) rbs = m_aWindowSize * 2; m_channelData[c]->setResampleBufSize(rbs); } } @@ -823,14 +824,28 @@ size_t R2Stretcher::getPreferredStartPad() const { if (!m_realtime) return 0; - return m_aWindowSize/2; + + size_t pad = m_aWindowSize / 2; + + if (resampleBeforeStretching()) { + return size_t(ceil(pad * m_pitchScale)); + } else { + return pad; + } } size_t R2Stretcher::getStartDelay() const { if (!m_realtime) return 0; - return lrint((m_aWindowSize/2) / m_pitchScale); + + size_t pad = m_aWindowSize / 2; + + if (resampleBeforeStretching()) { + return pad; + } else { + return size_t(ceil(pad / m_pitchScale)); + } } void @@ -1202,6 +1217,10 @@ R2Stretcher::getSamplesRequired() const } } } + + if (resampleBeforeStretching() && m_pitchScale > 1.0) { + reqd = size_t(ceil(double(reqd) * m_pitchScale)); + } return reqd; } diff --git a/src/finer/R3Stretcher.cpp b/src/finer/R3Stretcher.cpp index e56f3d2..8382b1e 100644 --- a/src/finer/R3Stretcher.cpp +++ b/src/finer/R3Stretcher.cpp @@ -495,7 +495,14 @@ R3Stretcher::getPreferredStartPad() const if (!isRealTime()) { return 0; } else { - return getWindowSourceSize() / 2; + bool resamplingBefore = false; + areWeResampling(&resamplingBefore, nullptr); + size_t pad = getWindowSourceSize() / 2; + if (resamplingBefore) { + return size_t(ceil(pad * m_pitchScale)); + } else { + return pad; + } } } @@ -505,8 +512,14 @@ R3Stretcher::getStartDelay() const if (!isRealTime()) { return 0; } else { - double factor = 0.5 / m_pitchScale; - return size_t(ceil(getWindowSourceSize() * factor)); + bool resamplingBefore = false; + areWeResampling(&resamplingBefore, nullptr); + size_t delay = getWindowSourceSize() / 2; + if (resamplingBefore) { + return delay; + } else { + return size_t(ceil(delay / m_pitchScale)); + } } } @@ -580,8 +593,24 @@ R3Stretcher::getSamplesRequired() const { if (available() != 0) return 0; int rs = m_channelData[0]->inbuf->getReadSpace(); + + m_log.log(2, "getSamplesRequired: read space and window source size", rs, getWindowSourceSize()); + if (rs < getWindowSourceSize()) { - return getWindowSourceSize() - rs; + + int req = getWindowSourceSize() - rs; + + bool resamplingBefore = false; + areWeResampling(&resamplingBefore, nullptr); + + if (!resamplingBefore) { + return req; + } else { + int adjusted = int(ceil(double(req) * m_pitchScale)); + m_log.log(2, "getSamplesRequired: resamplingBefore is true, req and adjusted", req, adjusted); + return adjusted; + } + } else { return 0; } @@ -733,6 +762,8 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) 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 (m_channelData.at(c)->resampled.data(), @@ -741,8 +772,11 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) } else { int toWrite = std::min(ws, remaining); + + m_log.log(2, "process: resamplingBefore is false, writing to inbuf from supplied data, former read space and samples being added", m_channelData[0]->inbuf->getReadSpace(), toWrite); + for (int c = 0; c < m_parameters.channels; ++c) { - m_channelData[c]->inbuf->write (input[c] + inputIx, toWrite); + m_channelData[c]->inbuf->write(input[c] + inputIx, toWrite); } inputIx += toWrite; } @@ -834,6 +868,8 @@ 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) { @@ -846,6 +882,9 @@ R3Stretcher::consume() // the map iterators int readSpace = cd0->inbuf->getReadSpace(); + + m_log.log(2, "consume: read space and window source size", readSpace, getWindowSourceSize()); + if (readSpace < getWindowSourceSize()) { if (m_mode == ProcessMode::Finished) { if (readSpace == 0) { diff --git a/src/test/TestStretcher.cpp b/src/test/TestStretcher.cpp index bacbcfc..3bd96aa 100644 --- a/src/test/TestStretcher.cpp +++ b/src/test/TestStretcher.cpp @@ -35,6 +35,8 @@ using namespace RubberBand; using std::vector; +using std::cerr; +using std::endl; namespace tt = boost::test_tools; @@ -241,11 +243,14 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, // This test simulates block-by-block realtime processing with // latency compensation, and checks that the output is all in the // expected place - - RubberBandStretcher stretcher(rate, 1, options, timeRatio, pitchScale); + RubberBandStretcher stretcher(rate, 1, options, timeRatio, pitchScale); stretcher.setMaxProcessSize(bs); + if (printDebug) { + stretcher.setDebugLevel(2); + } + // 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 // start, via increments of 0.1, to 1.0 at the end @@ -294,37 +299,35 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, BOOST_TEST(required > 0); // because available == 0 int toProcess = std::min(required, n - inOffset); float *source = in.data() + inOffset; - stretcher.process(&source, toProcess, toProcess < required); + bool final = (toProcess < required); + stretcher.process(&source, toProcess, final); inOffset += toProcess; - if (options & RubberBandStretcher::OptionEngineFiner) { - //!!! Faster engine sometimes does return - // available == 0 here - I'm not sure why, need to - // look into this. The process still completes fine - BOOST_TEST(stretcher.available() > 0); - } + BOOST_TEST(stretcher.available() > 0); continue; + } else if (toSkip > 0) { // available > 0 && toSkip > 0 + float *target = out.data() + outOffset; + int toRetrieve = std::min(toSkip, available); + int retrieved = stretcher.retrieve(&target, toRetrieve); + BOOST_TEST(retrieved == toRetrieve); + toSkip -= retrieved; + } else { // available > 0 float *target = out.data() + outOffset; int toRetrieve = std::min(needed - obtained, available); int retrieved = stretcher.retrieve(&target, toRetrieve); BOOST_TEST(retrieved == toRetrieve); - int advance = retrieved; - if (toSkip > 0) { - int skipping = std::min(advance, toSkip); - advance -= skipping; - toSkip -= skipping; - } - obtained += advance; - outOffset += advance; + obtained += retrieved; + outOffset += retrieved; } } } if (printDebug) { - std::cout << "sample\tV" << std::endl; + // The initial # is to allow grep on the test output + std::cout << "#sample\tV" << std::endl; for (int i = 0; i < nOut; ++i) { - std::cout << i << "\t" << out[i] << std::endl; + std::cout << "#" << i << "\t" << out[i] << std::endl; } } @@ -335,6 +338,8 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, for (int chunk = 0; chunk < 20; ++chunk) { +// cerr << "chunk " << chunk << " of 20" << endl; + int i0 = (nOut * chunk) / 20; int i1 = (nOut * (chunk + 1)) / 20; @@ -350,7 +355,9 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, int expectedCrossings = int(round((freq * pitchScale * double(i1 - i0)) / rate)); -// std::cout << chunk << std::endl; + bool highSpeedPitch = + ! ((options & RubberBandStretcher::OptionPitchHighQuality) || + (options & RubberBandStretcher::OptionPitchHighConsistency)); // The check here has to depend on whether we are in Finer or // Faster mode. In Finer mode, we expect to be generally exact @@ -361,12 +368,13 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, int slack = 0; if (options & RubberBandStretcher::OptionEngineFiner) { - if (chunk == 0 || chunk == 19) { - slack = (timeRatio < 1.0 ? 10 : 1); + if (chunk == 0 || chunk == 19 || highSpeedPitch) { + slack = 1; } } else { + slack = 1; if (chunk == 0) { - slack = (timeRatio < 1.0 ? 10 : 2); + slack = (timeRatio < 1.0 ? 3 : 2); } else if (chunk == 19) { // all bets are off, practically slack = expectedCrossings / 2; @@ -374,7 +382,7 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, slack = 1; } } - + BOOST_TEST(positiveCrossings <= expectedCrossings + slack); BOOST_TEST(positiveCrossings >= expectedCrossings - slack); @@ -387,7 +395,16 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, rms = sqrt(rms / double(i1 - i0)); double expected = (chunk/2 + 1) * 0.05 * sqrt(2.0); - BOOST_TEST(rms - expected < 0.01); + + double maxOver = 0.01; + double maxUnder = 0.1; + + if (!(options & RubberBandStretcher::OptionEngineFiner)) { + maxUnder = 0.2; + } + + BOOST_TEST(rms - expected < maxOver); + BOOST_TEST(expected - rms < maxUnder); } } @@ -431,6 +448,24 @@ BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_finer) false); } +BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_finer_hqpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighQuality, + 4.0, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_finer_hcpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 4.0, 1.5, + false); +} + BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_faster) { sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | @@ -439,6 +474,24 @@ BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_faster) false); } +BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_faster_hqpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighQuality, + 4.0, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_faster_hcpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 4.0, 1.5, + false); +} + BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_finer) { sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | @@ -447,6 +500,24 @@ BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_finer) false); } +BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_finer_hqpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighQuality, + 0.5, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_finer_hcpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 0.5, 1.5, + false); +} + BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_faster) { sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | @@ -455,6 +526,24 @@ BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_faster) false); } +BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_faster_hqpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighQuality, + 0.5, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_faster_hcpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 0.5, 1.5, + false); +} + BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_finer) { sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | @@ -463,6 +552,24 @@ BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_finer) false); } +BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_finer_hqpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighQuality, + 8.0, 0.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_finer_hcpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 8.0, 0.5, + false); +} + BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_faster) { sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | @@ -471,6 +578,24 @@ BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_faster) false); } +BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_faster_hqpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighQuality, + 8.0, 0.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_faster_hcpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 8.0, 0.5, + false); +} + BOOST_AUTO_TEST_CASE(impulses_2x_offline_faster) { int n = 10000; @@ -678,7 +803,7 @@ BOOST_AUTO_TEST_CASE(impulses_2x_5up_offline_finer) } */ } - +/* BOOST_AUTO_TEST_CASE(final_realtime_faster) { int n = 10000; @@ -726,5 +851,5 @@ BOOST_AUTO_TEST_CASE(final_realtime_faster) outcount += got; } } - +*/ BOOST_AUTO_TEST_SUITE_END() From d5dd0e573a42422c605a4bf6221ece217d41744b Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Mon, 20 Feb 2023 17:46:10 +0000 Subject: [PATCH 4/6] Further timing tests --- src/test/TestStretcher.cpp | 444 ++++++++++++++++++++++++++++--------- 1 file changed, 338 insertions(+), 106 deletions(-) diff --git a/src/test/TestStretcher.cpp b/src/test/TestStretcher.cpp index 3bd96aa..a389f60 100644 --- a/src/test/TestStretcher.cpp +++ b/src/test/TestStretcher.cpp @@ -229,40 +229,13 @@ BOOST_AUTO_TEST_CASE(sinusoid_2x_offline_finer) BOOST_TEST(rms < 0.1); } -static void sinusoid_realtime(RubberBandStretcher::Options options, - double timeRatio, - double pitchScale, - bool printDebug) +static vector process_realtime(RubberBandStretcher &stretcher, + const vector &in, + int nOut, + int bs, + bool printDebug) { - int n = (timeRatio < 1.0 ? 80000 : 40000); - int nOut = int(ceil(n * timeRatio)); - float freq = 441.f; - int rate = 44100; - int bs = 512; - - // This test simulates block-by-block realtime processing with - // latency compensation, and checks that the output is all in the - // expected place - - RubberBandStretcher stretcher(rate, 1, options, timeRatio, pitchScale); - stretcher.setMaxProcessSize(bs); - - if (printDebug) { - stretcher.setDebugLevel(2); - } - - // 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 - // start, via increments of 0.1, to 1.0 at the end - - vector in(n); - for (int i = 0; i < n; ++i) { - float amplitude = float((i / (n/10)) + 1) / 10.f; - float sample = amplitude * - sinf(float(i) * freq * M_PI * 2.f / float(rate)); - in[i] = sample; - } - + int n = in.size(); vector out(nOut, 0.f); // Prime the start @@ -298,7 +271,7 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, int required = stretcher.getSamplesRequired(); BOOST_TEST(required > 0); // because available == 0 int toProcess = std::min(required, n - inOffset); - float *source = in.data() + inOffset; + const float *const source = in.data() + inOffset; bool final = (toProcess < required); stretcher.process(&source, toProcess, final); inOffset += toProcess; @@ -330,6 +303,45 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, std::cout << "#" << i << "\t" << out[i] << std::endl; } } + + return out; +} + +static void sinusoid_realtime(RubberBandStretcher::Options options, + double timeRatio, + double pitchScale, + bool printDebug) +{ + int n = (timeRatio < 1.0 ? 80000 : 40000); + int nOut = int(ceil(n * timeRatio)); + float freq = 441.f; + int rate = 44100; + int bs = 512; + + // This test simulates block-by-block realtime processing with + // latency compensation, and checks that the output is all in the + // expected place + + RubberBandStretcher stretcher(rate, 1, options, timeRatio, pitchScale); + stretcher.setMaxProcessSize(bs); + + if (printDebug) { + stretcher.setDebugLevel(2); + } + + // 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 + // start, via increments of 0.1, to 1.0 at the end + + vector in(n); + for (int i = 0; i < n; ++i) { + float amplitude = float((i / (n/10)) + 1) / 10.f; + float sample = amplitude * + sinf(float(i) * freq * M_PI * 2.f / float(rate)); + in[i] = sample; + } + + vector out = process_realtime(stretcher, in, nOut, bs, 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 @@ -368,7 +380,9 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, int slack = 0; if (options & RubberBandStretcher::OptionEngineFiner) { - if (chunk == 0 || chunk == 19 || highSpeedPitch) { + if (options & RubberBandStretcher::OptionWindowShort) { + slack = 2; + } else if (chunk == 0 || chunk == 19 || highSpeedPitch) { slack = 1; } } else { @@ -399,7 +413,8 @@ static void sinusoid_realtime(RubberBandStretcher::Options options, double maxOver = 0.01; double maxUnder = 0.1; - if (!(options & RubberBandStretcher::OptionEngineFiner)) { + if (!(options & RubberBandStretcher::OptionEngineFiner) || + (options & RubberBandStretcher::OptionWindowShort)) { maxUnder = 0.2; } @@ -416,14 +431,6 @@ BOOST_AUTO_TEST_CASE(sinusoid_slow_samepitch_realtime_finer) false); } -BOOST_AUTO_TEST_CASE(sinusoid_slow_samepitch_realtime_faster) -{ - sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | - RubberBandStretcher::OptionProcessRealTime, - 8.0, 1.0, - false); -} - BOOST_AUTO_TEST_CASE(sinusoid_fast_samepitch_realtime_finer) { sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | @@ -432,14 +439,6 @@ BOOST_AUTO_TEST_CASE(sinusoid_fast_samepitch_realtime_finer) false); } -BOOST_AUTO_TEST_CASE(sinusoid_fast_samepitch_realtime_faster) -{ - sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | - RubberBandStretcher::OptionProcessRealTime, - 0.5, 1.0, - false); -} - BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_finer) { sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | @@ -466,32 +465,6 @@ BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_finer_hcpitch) false); } -BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_faster) -{ - sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | - RubberBandStretcher::OptionProcessRealTime, - 4.0, 1.5, - false); -} - -BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_faster_hqpitch) -{ - sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | - RubberBandStretcher::OptionProcessRealTime | - RubberBandStretcher::OptionPitchHighQuality, - 4.0, 1.5, - false); -} - -BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_faster_hcpitch) -{ - sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | - RubberBandStretcher::OptionProcessRealTime | - RubberBandStretcher::OptionPitchHighConsistency, - 4.0, 1.5, - false); -} - BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_finer) { sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | @@ -518,32 +491,6 @@ BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_finer_hcpitch) false); } -BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_faster) -{ - sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | - RubberBandStretcher::OptionProcessRealTime, - 0.5, 1.5, - false); -} - -BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_faster_hqpitch) -{ - sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | - RubberBandStretcher::OptionProcessRealTime | - RubberBandStretcher::OptionPitchHighQuality, - 0.5, 1.5, - false); -} - -BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_faster_hcpitch) -{ - sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | - RubberBandStretcher::OptionProcessRealTime | - RubberBandStretcher::OptionPitchHighConsistency, - 0.5, 1.5, - false); -} - BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_finer) { sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | @@ -570,6 +517,149 @@ BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_finer_hcpitch) false); } +BOOST_AUTO_TEST_CASE(sinusoid_slow_samepitch_realtime_finer_short) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionWindowShort, + 8.0, 1.0, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_fast_samepitch_realtime_finer_short) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionWindowShort, + 0.5, 1.0, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_finer_short) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionWindowShort, + 4.0, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_finer_short_hcpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionWindowShort | + RubberBandStretcher::OptionPitchHighConsistency, + 4.0, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_finer_short) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionWindowShort, + 0.5, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_finer_hcpitch_short) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionWindowShort | + RubberBandStretcher::OptionPitchHighConsistency, + 0.5, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_finer_short) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionWindowShort, + 8.0, 0.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_finer_short_hcpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionWindowShort | + RubberBandStretcher::OptionPitchHighConsistency, + 8.0, 0.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_samepitch_realtime_faster) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime, + 8.0, 1.0, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_fast_samepitch_realtime_faster) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime, + 0.5, 1.0, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_faster) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime, + 4.0, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_faster_hqpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighQuality, + 4.0, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_slow_higher_realtime_faster_hcpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 4.0, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_faster) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime, + 0.5, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_faster_hqpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighQuality, + 0.5, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(sinusoid_fast_higher_realtime_faster_hcpitch) +{ + sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 0.5, 1.5, + false); +} + BOOST_AUTO_TEST_CASE(sinusoid_slow_lower_realtime_faster) { sinusoid_realtime(RubberBandStretcher::OptionEngineFaster | @@ -803,7 +893,142 @@ BOOST_AUTO_TEST_CASE(impulses_2x_5up_offline_finer) } */ } + +static void impulses_realtime(RubberBandStretcher::Options options, + double timeRatio, + double pitchScale, + bool printDebug) +{ + int n = 10000; + int nOut = int(ceil(n * timeRatio)); + int rate = 48000; + int bs = 1024; + + RubberBandStretcher stretcher(rate, 1, options, timeRatio, pitchScale); + + if (printDebug) { + stretcher.setDebugLevel(2); + } + + vector in(n, 0.f); + + in[100] = 1.f; + in[101] = -1.f; + + in[5000] = 1.f; + in[5001] = -1.f; + + in[9900] = 1.f; + in[9901] = -1.f; + + vector out = process_realtime(stretcher, in, nOut, bs, printDebug); + + int peak0 = -1, peak1 = -1, peak2 = -1; + float max; + + max = -2.f; + for (int i = 0; i < nOut/4; ++i) { + if (out[i] > max) { max = out[i]; peak0 = i; } + } + + max = -2.f; + for (int i = nOut/4; i < (nOut*3)/4; ++i) { + if (out[i] > max) { max = out[i]; peak1 = i; } + } + + max = -2.f; + for (int i = (nOut*3)/4; i < nOut; ++i) { + if (out[i] > max) { max = out[i]; peak2 = i; } + } + + // These limits aren't alarming, but it be worth tightening them + // and and taking a look at the waveforms + + BOOST_TEST(peak0 < int(ceil(200 * timeRatio))); + BOOST_TEST(peak0 > int(ceil(50 * timeRatio))); + + BOOST_TEST(peak1 < int(ceil(5070 * timeRatio))); + BOOST_TEST(peak1 > int(ceil(4840 * timeRatio))); + + BOOST_TEST(peak2 < int(ceil(9970 * timeRatio))); + BOOST_TEST(peak2 > int(ceil(9770 * timeRatio))); + /* + std::cout << "ms\tV" << std::endl; + for (int i = 0; i < n*2; ++i) { + std::cout << i << "\t" << out[i] << std::endl; + } +*/ +} + +BOOST_AUTO_TEST_CASE(impulses_slow_samepitch_realtime_finer) +{ + impulses_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime, + 8.0, 1.0, + false); +} + +BOOST_AUTO_TEST_CASE(impulses_fast_samepitch_realtime_finer) +{ + impulses_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime, + 0.5, 1.0, + false); +} + +BOOST_AUTO_TEST_CASE(impulses_slow_higher_realtime_finer) +{ + impulses_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime, + 4.0, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(impulses_slow_higher_realtime_finer_hqpitch) +{ + impulses_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighQuality, + 4.0, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(impulses_slow_higher_realtime_finer_hcpitch) +{ + impulses_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 4.0, 1.5, + false); +} + +BOOST_AUTO_TEST_CASE(impulses_slow_lower_realtime_finer) +{ + impulses_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime, + 8.0, 0.5, + false); +} + +BOOST_AUTO_TEST_CASE(impulses_slow_lower_realtime_finer_hqpitch) +{ + impulses_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighQuality, + 8.0, 0.5, + false); +} + +BOOST_AUTO_TEST_CASE(impulses_slow_lower_realtime_finer_hcpitch) +{ + impulses_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 8.0, 0.5, + false); +} + BOOST_AUTO_TEST_CASE(final_realtime_faster) { int n = 10000; @@ -845,11 +1070,18 @@ BOOST_AUTO_TEST_CASE(final_realtime_faster) float *out = outp + outcount; size_t got = stretcher.retrieve(&out, avail); BOOST_TEST(got == size_t(avail)); - BOOST_TEST(stretcher.available() == 0); incount += inbs; outcount += got; + + cerr << "outcount = " << outcount << ", n = " << n << endl; + + if (outcount >= n * 2) { + BOOST_TEST(stretcher.available() == -1); + } else { + BOOST_TEST(stretcher.available() == 0); + } } } -*/ + BOOST_AUTO_TEST_SUITE_END() From 4b4c50b479c60334bc902777e8ebd4bda2c6a475 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Tue, 21 Feb 2023 10:28:57 +0000 Subject: [PATCH 5/6] Tests for handling of final flag --- meson.build | 2 + src/finer/R3Stretcher.cpp | 1 + src/test/TestStretcher.cpp | 130 +++++++++++++++++++++++++++++-------- 3 files changed, 107 insertions(+), 26 deletions(-) diff --git a/meson.build b/meson.build index a624141..b294699 100644 --- a/meson.build +++ b/meson.build @@ -821,6 +821,8 @@ if have_boost_unit_test unit_tests, args: [ '--run_test=TestVectorOpsComplex', general_test_args ]) test('SignalBits', unit_tests, args: [ '--run_test=TestSignalBits', general_test_args ]) + test('Stretcher', + unit_tests, args: [ '--run_test=TestStretcher', general_test_args ]) else target_summary += { 'Unit tests': false } message('Not building unit tests: boost_unit_test_framework dependency not found') diff --git a/src/finer/R3Stretcher.cpp b/src/finer/R3Stretcher.cpp index 8382b1e..c76d980 100644 --- a/src/finer/R3Stretcher.cpp +++ b/src/finer/R3Stretcher.cpp @@ -697,6 +697,7 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final) // 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; diff --git a/src/test/TestStretcher.cpp b/src/test/TestStretcher.cpp index a389f60..398bb65 100644 --- a/src/test/TestStretcher.cpp +++ b/src/test/TestStretcher.cpp @@ -1029,59 +1029,137 @@ BOOST_AUTO_TEST_CASE(impulses_slow_lower_realtime_finer_hcpitch) false); } -BOOST_AUTO_TEST_CASE(final_realtime_faster) +static void final_realtime(RubberBandStretcher::Options options, + double timeRatio, + double pitchScale, + bool finalAfterFinishing, + bool printDebug) { int n = 10000; float freq = 440.f; int rate = 44100; int blocksize = 700; - RubberBandStretcher stretcher - (rate, 1, - RubberBandStretcher::OptionEngineFaster | - RubberBandStretcher::OptionProcessRealTime); + RubberBandStretcher stretcher(rate, 1, options); - stretcher.setTimeRatio(2.0); - - int excess = 10000; - vector in(n, 0.f), out(n * 2 + excess, 0.f); - - for (int i = n - 100; i < n; ++i) { - in[i] = sinf(float(i) * freq * M_PI * 2.f / float(rate)); + if (printDebug) { + stretcher.setDebugLevel(2); } + + stretcher.setTimeRatio(timeRatio); + stretcher.setPitchScale(pitchScale); + + int nOut = int(ceil(n * timeRatio)); + int excess = std::max(nOut, n); + vector in(n, 0.f), out(nOut + excess, 0.f); + + for (int i = 0; i < 100; ++i) { + in[n - 101 + i] = sinf(float(i) * freq * M_PI * 2.f / float(rate)); + } + + // Prime the start + { + float *source = out.data(); // just reuse out because it's silent + stretcher.process(&source, stretcher.getPreferredStartPad(), false); + } + float *inp = in.data(), *outp = out.data(); stretcher.setMaxProcessSize(blocksize); BOOST_TEST(stretcher.available() == 0); + int toSkip = stretcher.getStartDelay(); + int incount = 0, outcount = 0; - while (incount < n) { + while (true) { int inbs = std::min(blocksize, n - incount); - BOOST_TEST(inbs > 0); - bool final = (incount + inbs >= n); + bool final; + if (finalAfterFinishing) { + BOOST_TEST(inbs >= 0); + final = (inbs == 0); + } else { + BOOST_TEST(inbs > 0); + final = (incount + inbs >= n); + } + float *in = inp + incount; stretcher.process(&in, inbs, final); + incount += inbs; int avail = stretcher.available(); BOOST_TEST(avail >= 0); - BOOST_TEST(outcount + avail < n + excess); + BOOST_TEST(outcount + avail < nOut + excess); float *out = outp + outcount; - size_t got = stretcher.retrieve(&out, avail); - BOOST_TEST(got == size_t(avail)); - incount += inbs; - outcount += got; - - cerr << "outcount = " << outcount << ", n = " << n << endl; - - if (outcount >= n * 2) { - BOOST_TEST(stretcher.available() == -1); + if (toSkip > 0) { + int skipHere = std::min(toSkip, avail); + size_t got = stretcher.retrieve(&out, skipHere); + BOOST_TEST(got == size_t(skipHere)); + toSkip -= got; +// cerr << "got = " << got << ", toSkip now = " << toSkip << ", n = " << n << endl; } else { - BOOST_TEST(stretcher.available() == 0); + size_t got = stretcher.retrieve(&out, avail); + BOOST_TEST(got == size_t(avail)); + outcount += got; +// cerr << "got = " << got << ", outcount = " << outcount << ", n = " << n << endl; + if (final) { + BOOST_TEST(stretcher.available() == -1); + } else { + BOOST_TEST(stretcher.available() == 0); + } + } + + if (final) break; + } + + if (printDebug) { + // The initial # is to allow grep on the test output + std::cout << "#sample\tV" << std::endl; + for (int i = 0; i < outcount; ++i) { + std::cout << "#" << i << "\t" << out[i] << std::endl; } } + } +BOOST_AUTO_TEST_CASE(final_slow_samepitch_realtime_finer) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime, + 8.0, 1.0, + false, + false); +} + +BOOST_AUTO_TEST_CASE(final_slow_samepitch_realtime_finer_after) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime, + 8.0, 1.0, + true, + false); +} + +BOOST_AUTO_TEST_CASE(final_fast_samepitch_realtime_finer) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime, + 0.2, 1.0, + false, + false); +} + +BOOST_AUTO_TEST_CASE(final_fast_samepitch_realtime_finer_after) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime, + 0.2, 1.0, + true, + false); +} + + + BOOST_AUTO_TEST_SUITE_END() From a4299b0ec684edfee30d4e7a8e5804bc148ca0fa Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Tue, 21 Feb 2023 11:30:04 +0000 Subject: [PATCH 6/6] Expand tests --- src/test/TestStretcher.cpp | 100 +++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/src/test/TestStretcher.cpp b/src/test/TestStretcher.cpp index 398bb65..9e63d2a 100644 --- a/src/test/TestStretcher.cpp +++ b/src/test/TestStretcher.cpp @@ -1091,6 +1091,8 @@ static void final_realtime(RubberBandStretcher::Options options, BOOST_TEST(avail >= 0); BOOST_TEST(outcount + avail < nOut + excess); +// cerr << "in = " << inbs << ", incount now = " << incount << ", avail = " << avail << endl; + float *out = outp + outcount; if (toSkip > 0) { @@ -1099,7 +1101,10 @@ static void final_realtime(RubberBandStretcher::Options options, BOOST_TEST(got == size_t(skipHere)); toSkip -= got; // cerr << "got = " << got << ", toSkip now = " << toSkip << ", n = " << n << endl; - } else { + } + + avail = stretcher.available(); + if (toSkip == 0 && avail > 0) { size_t got = stretcher.retrieve(&out, avail); BOOST_TEST(got == size_t(avail)); outcount += got; @@ -1114,6 +1119,8 @@ static void final_realtime(RubberBandStretcher::Options options, if (final) break; } + BOOST_TEST(outcount >= nOut); + if (printDebug) { // The initial # is to allow grep on the test output std::cout << "#sample\tV" << std::endl; @@ -1127,7 +1134,8 @@ static void final_realtime(RubberBandStretcher::Options options, BOOST_AUTO_TEST_CASE(final_slow_samepitch_realtime_finer) { final_realtime(RubberBandStretcher::OptionEngineFiner | - RubberBandStretcher::OptionProcessRealTime, + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, 8.0, 1.0, false, false); @@ -1136,7 +1144,8 @@ BOOST_AUTO_TEST_CASE(final_slow_samepitch_realtime_finer) BOOST_AUTO_TEST_CASE(final_slow_samepitch_realtime_finer_after) { final_realtime(RubberBandStretcher::OptionEngineFiner | - RubberBandStretcher::OptionProcessRealTime, + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, 8.0, 1.0, true, false); @@ -1145,7 +1154,8 @@ BOOST_AUTO_TEST_CASE(final_slow_samepitch_realtime_finer_after) BOOST_AUTO_TEST_CASE(final_fast_samepitch_realtime_finer) { final_realtime(RubberBandStretcher::OptionEngineFiner | - RubberBandStretcher::OptionProcessRealTime, + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, 0.2, 1.0, false, false); @@ -1154,12 +1164,92 @@ BOOST_AUTO_TEST_CASE(final_fast_samepitch_realtime_finer) BOOST_AUTO_TEST_CASE(final_fast_samepitch_realtime_finer_after) { final_realtime(RubberBandStretcher::OptionEngineFiner | - RubberBandStretcher::OptionProcessRealTime, + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, 0.2, 1.0, true, false); } +BOOST_AUTO_TEST_CASE(final_slow_higher_realtime_finer) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 8.0, 1.5, + false, + false); +} + +BOOST_AUTO_TEST_CASE(final_slow_higher_realtime_finer_after) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 8.0, 1.5, + true, + false); +} + +BOOST_AUTO_TEST_CASE(final_fast_higher_realtime_finer) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 0.2, 1.5, + false, + false); +} + +BOOST_AUTO_TEST_CASE(final_fast_higher_realtime_finer_after) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 0.2, 1.5, + true, + false); +} + +BOOST_AUTO_TEST_CASE(final_slow_lower_realtime_finer) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 8.0, 0.5, + false, + false); +} + +BOOST_AUTO_TEST_CASE(final_slow_lower_realtime_finer_after) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 8.0, 0.5, + true, + false); +} + +BOOST_AUTO_TEST_CASE(final_fast_lower_realtime_finer) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 0.2, 0.5, + false, + false); +} + +BOOST_AUTO_TEST_CASE(final_fast_lower_realtime_finer_after) +{ + final_realtime(RubberBandStretcher::OptionEngineFiner | + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency, + 0.2, 0.5, + true, + false); +} BOOST_AUTO_TEST_SUITE_END()