From 4b4c50b479c60334bc902777e8ebd4bda2c6a475 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Tue, 21 Feb 2023 10:28:57 +0000 Subject: [PATCH] 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()