Tests for handling of final flag

This commit is contained in:
Chris Cannam
2023-02-21 10:28:57 +00:00
parent d5dd0e573a
commit 4b4c50b479
3 changed files with 107 additions and 26 deletions

View File

@@ -821,6 +821,8 @@ if have_boost_unit_test
unit_tests, args: [ '--run_test=TestVectorOpsComplex', general_test_args ]) unit_tests, args: [ '--run_test=TestVectorOpsComplex', general_test_args ])
test('SignalBits', test('SignalBits',
unit_tests, args: [ '--run_test=TestSignalBits', general_test_args ]) unit_tests, args: [ '--run_test=TestSignalBits', general_test_args ])
test('Stretcher',
unit_tests, args: [ '--run_test=TestStretcher', general_test_args ])
else else
target_summary += { 'Unit tests': false } target_summary += { 'Unit tests': false }
message('Not building unit tests: boost_unit_test_framework dependency not found') message('Not building unit tests: boost_unit_test_framework dependency not found')

View File

@@ -697,6 +697,7 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final)
// haven't yet delivered all the samples" because the // haven't yet delivered all the samples" because the
// distinction is meaningless internally - it only affects // distinction is meaningless internally - it only affects
// whether available() finds any samples in the buffer // whether available() finds any samples in the buffer
m_log.log(1, "final is set, entering Finished mode");
m_mode = ProcessMode::Finished; m_mode = ProcessMode::Finished;
} else { } else {
m_mode = ProcessMode::Processing; m_mode = ProcessMode::Processing;

View File

@@ -1029,59 +1029,137 @@ BOOST_AUTO_TEST_CASE(impulses_slow_lower_realtime_finer_hcpitch)
false); 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; int n = 10000;
float freq = 440.f; float freq = 440.f;
int rate = 44100; int rate = 44100;
int blocksize = 700; int blocksize = 700;
RubberBandStretcher stretcher RubberBandStretcher stretcher(rate, 1, options);
(rate, 1,
RubberBandStretcher::OptionEngineFaster |
RubberBandStretcher::OptionProcessRealTime);
stretcher.setTimeRatio(2.0); if (printDebug) {
stretcher.setDebugLevel(2);
int excess = 10000;
vector<float> 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));
} }
stretcher.setTimeRatio(timeRatio);
stretcher.setPitchScale(pitchScale);
int nOut = int(ceil(n * timeRatio));
int excess = std::max(nOut, n);
vector<float> 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(); float *inp = in.data(), *outp = out.data();
stretcher.setMaxProcessSize(blocksize); stretcher.setMaxProcessSize(blocksize);
BOOST_TEST(stretcher.available() == 0); BOOST_TEST(stretcher.available() == 0);
int toSkip = stretcher.getStartDelay();
int incount = 0, outcount = 0; int incount = 0, outcount = 0;
while (incount < n) { while (true) {
int inbs = std::min(blocksize, n - incount); 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; float *in = inp + incount;
stretcher.process(&in, inbs, final); stretcher.process(&in, inbs, final);
incount += inbs;
int avail = stretcher.available(); int avail = stretcher.available();
BOOST_TEST(avail >= 0); BOOST_TEST(avail >= 0);
BOOST_TEST(outcount + avail < n + excess); BOOST_TEST(outcount + avail < nOut + excess);
float *out = outp + outcount; float *out = outp + outcount;
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 {
size_t got = stretcher.retrieve(&out, avail); size_t got = stretcher.retrieve(&out, avail);
BOOST_TEST(got == size_t(avail)); BOOST_TEST(got == size_t(avail));
incount += inbs;
outcount += got; outcount += got;
// cerr << "got = " << got << ", outcount = " << outcount << ", n = " << n << endl;
cerr << "outcount = " << outcount << ", n = " << n << endl; if (final) {
if (outcount >= n * 2) {
BOOST_TEST(stretcher.available() == -1); BOOST_TEST(stretcher.available() == -1);
} else { } else {
BOOST_TEST(stretcher.available() == 0); 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() BOOST_AUTO_TEST_SUITE_END()