From a7f9c47a00690b4bed470f581ca5cd823c0952fb Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Tue, 28 Jun 2022 11:47:30 +0100 Subject: [PATCH] Document engine; add getEngineVersion() --- rubberband/RubberBandStretcher.h | 43 +++++++++++++++++ src/RubberBandStretcher.cpp | 12 +++++ src/test/TestStretcher.cpp | 81 +++++++++++++++++++++++++++++++- 3 files changed, 135 insertions(+), 1 deletion(-) diff --git a/rubberband/RubberBandStretcher.h b/rubberband/RubberBandStretcher.h index b806e48..ebd894b 100644 --- a/rubberband/RubberBandStretcher.h +++ b/rubberband/RubberBandStretcher.h @@ -111,6 +111,36 @@ public: * non-real-time operation on seekable files: Offline; real-time * or streaming operation: RealTime. * + * 2. Flags prefixed \c OptionEngine select the core Rubber Band + * processing engine to be used. These options may not be changed + * after construction. + * + * \li \c OptionEngineFaster - Use the Rubber Band Library R2 + * (Faster) engine. This is the engine implemented in Rubber + * Band Library v1.x and v2.x, and it remains the default for + * backward compatibility. It uses substantially less CPU than + * the R3 engine and there are still many situations in which it + * is likely to be the more appropriate choice. + * + * \li \c OptionEngineFiner - Use the Rubber Band Library R3 + * (Finer) engine. This engine was added in Rubber Band Library + * v3.0. It produces higher-quality results than the R2 engine + * for most material, especially complex mixes, vocals and other + * sounds that have soft onsets and smooth pitch changes, and + * music with substantial bass content. However, it uses much + * more CPU power than the R2 engine. + * + * Important note: Consider calling getEngineVersion() after + * construction to make sure the engine you requested is + * active. That's not because engine selection can fail, but + * because Rubber Band Library ignores any unknown options + * supplied on construction - so a program that requests the R3 + * engine but ends up linked against an older version of the + * library (prior to v3.0) will silently use the R2 engine + * instead. Calling the v3.0 function getEngineVersion() will + * ensure a link failure in this situation instead, and supply a + * reassuring run-time check. + * * 3. Flags prefixed \c OptionTransients control the component * frequency phase-reset mechanism that may be used at transient * points to provide clarity and realism to percussion and other @@ -402,6 +432,15 @@ public: */ void reset(); + /** + * Return the active internal engine version, according to the \c + * OptionEngine flag supplied on construction. This will return 2 + * for the R2 (Faster) engine or 3 for the R3 (Finer) engine. + * + * This function was added in Rubber Band Library v3.0. + */ + int getEngineVersion() const; + /** * Set the time ratio for the stretcher. This is the ratio of * stretched to unstretched duration -- not tempo. For example, a @@ -477,6 +516,8 @@ public: * * This function is supported only in the R3 (OptionEngineFiner) * engine. It has no effect in R2 (OptionEngineFaster). + * + * This function was added in Rubber Band Library v3.0. */ void setFormantScale(double scale); @@ -499,6 +540,8 @@ public: * * This function is supported only in the R3 (OptionEngineFiner) * engine. It always returns 0.0 in R2 (OptionEngineFaster). + * + * This function was added in Rubber Band Library v3.0. */ double getFormantScale() const; diff --git a/src/RubberBandStretcher.cpp b/src/RubberBandStretcher.cpp index 575ae53..c17e3ba 100644 --- a/src/RubberBandStretcher.cpp +++ b/src/RubberBandStretcher.cpp @@ -96,6 +96,12 @@ public: delete m_r3; } + int getEngineVersion() const + { + if (m_r3) return 3; + else return 2; + } + void reset() { if (m_r2) m_r2->reset(); @@ -359,6 +365,12 @@ RubberBandStretcher::reset() m_d->reset(); } +int +RubberBandStretcher::getEngineVersion() const +{ + return m_d->getEngineVersion(); +} + RTENTRY__ void RubberBandStretcher::setTimeRatio(double ratio) diff --git a/src/test/TestStretcher.cpp b/src/test/TestStretcher.cpp index 4d5f26e..ad3f9a8 100644 --- a/src/test/TestStretcher.cpp +++ b/src/test/TestStretcher.cpp @@ -36,6 +36,14 @@ namespace tt = boost::test_tools; BOOST_AUTO_TEST_SUITE(TestStretcher) +BOOST_AUTO_TEST_CASE(engine_version) +{ + RubberBandStretcher s2(44100, 1, RubberBandStretcher::OptionEngineFaster); + BOOST_TEST(s2.getEngineVersion() == 2); + RubberBandStretcher s3(44100, 1, RubberBandStretcher::OptionEngineFiner); + BOOST_TEST(s3.getEngineVersion() == 3); +} + BOOST_AUTO_TEST_CASE(sinusoid_unchanged_single_offline_faster) { int n = 10000; @@ -204,11 +212,82 @@ BOOST_AUTO_TEST_CASE(impulses_2_offline_faster) BOOST_TEST(peak0 == 0); BOOST_TEST(peak1 == n - 1); BOOST_TEST(peak2 == n*2 - 2); - +/* 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_2_offline_finer) +{ + int n = 10000; + float freq = 440.f; + int rate = 44100; + RubberBandStretcher stretcher + (rate, 1, RubberBandStretcher::OptionEngineFiner, 2.0, 1.0); + + vector in(n, 0.f), out(n * 2, 0.f); + + in[0] = 1.f; + in[1] = -1.f; + + in[4999] = 1.f; + in[5000] = -1.f; + + in[9998] = 1.f; + in[9999] = -1.f; + + float *inp = in.data(), *outp = out.data(); + + stretcher.setMaxProcessSize(n); + stretcher.setExpectedInputDuration(n); + BOOST_TEST(stretcher.available() == 0); + + stretcher.study(&inp, n, true); + BOOST_TEST(stretcher.available() == 0); + + stretcher.process(&inp, n, true); + BOOST_TEST(stretcher.available() == n * 2); + + BOOST_TEST(stretcher.getLatency() == 0); // offline mode + + size_t got = stretcher.retrieve(&outp, n * 2); + BOOST_TEST(got == n * 2); + BOOST_TEST(stretcher.available() == -1); + + float max; + int peak0, peak1, peak2; + + for (int i = 0, max = -2.f; i < n/2; ++i) { + if (out[i] > max) { + max = out[i]; + peak0 = i; + } + } + for (int i = n/2, max = -2.f; i < (n*3)/2; ++i) { + if (out[i] > max) { + max = out[i]; + peak1 = i; + } + } + for (int i = (n*3)/2, max = -2.f; i < n*2; ++i) { + if (out[i] > max) { + max = out[i]; + peak2 = i; + } + } + + BOOST_TEST(peak0 == 0); + BOOST_TEST(peak1 == n - 1); + BOOST_TEST(peak2 == n*2 - 2); + +// std::cout << "ms\tV" << std::endl; +// for (int i = 0; i < n*2; ++i) { +// std::cout << i << "\t" << out[i] << std::endl; +// } + } #endif