Fix latency calculations in resample-before modes. This affects realtime use in the non-recommended realtime pitch modes (i.e. those other than HighConsistency)

This commit is contained in:
Chris Cannam
2023-02-20 15:26:12 +00:00
parent afd1b1f86c
commit 1368ba87ee
3 changed files with 217 additions and 34 deletions

View File

@@ -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
@@ -1203,6 +1218,10 @@ R2Stretcher::getSamplesRequired() const
}
}
if (resampleBeforeStretching() && m_pitchScale > 1.0) {
reqd = size_t(ceil(double(reqd) * m_pitchScale));
}
return reqd;
}

View File

@@ -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,6 +772,9 @@ 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);
}
@@ -835,6 +869,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) {
Profiler profiler("R3Stretcher::consume/loop");
@@ -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) {

View File

@@ -35,6 +35,8 @@
using namespace RubberBand;
using std::vector;
using std::cerr;
using std::endl;
namespace tt = boost::test_tools;
@@ -243,9 +245,12 @@ static void sinusoid_realtime(RubberBandStretcher::Options options,
// 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
@@ -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);
}
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;
@@ -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()