Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rubberband V3 stretcher #4853

Merged
merged 3 commits into from
Jul 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 46 additions & 20 deletions src/engine/bufferscalers/enginebufferscalerubberband.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ using RubberBand::RubberBandStretcher;

namespace {

// TODO (XXX): this should be removed. It is only needed to work around
// a Rubberband 1.3 bug.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we ifdef it based on the rubberband version?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was only able to verify that this is not present the newer rubberband version. So I didn't want to introduce regressions based on speculation.

// This is the default increment from RubberBand 1.8.1.
size_t kRubberBandBlockSize = 256;

#define RUBBERBANDV3 (RUBBERBAND_API_MAJOR_VERSION >= 2 && RUBBERBAND_API_MINOR_VERSION >= 7)

} // namespace

EngineBufferScaleRubberBand::EngineBufferScaleRubberBand(
Expand Down Expand Up @@ -53,14 +57,16 @@ void EngineBufferScaleRubberBand::setScaleParameters(double base_rate,
//
// References:
// https://bugs.launchpad.net/ubuntu/+bug/1263233
// https://bitbucket.org/breakfastquay/rubberband/issue/4/sigfpe-zero-division-with-high-time-ratios
constexpr double kMinSeekSpeed = 1.0 / 128.0;
// https://todo.sr.ht/~breakfastquay/rubberband/5

double speed_abs = fabs(*pTempoRatio);
if (speed_abs < kMinSeekSpeed) {
// Let the caller know we ignored their speed.
speed_abs = *pTempoRatio = 0;
if (runningEngineVersion() == 2) {
constexpr double kMinSeekSpeed = 1.0 / 128.0;
if (speed_abs < kMinSeekSpeed) {
// Let the caller know we ignored their speed.
speed_abs = *pTempoRatio = 0;
}
}

// RubberBand handles checking for whether the change in pitchScale is a
// no-op.
double pitchScale = fabs(base_rate * *pPitchRatio);
Expand All @@ -80,21 +86,22 @@ void EngineBufferScaleRubberBand::setScaleParameters(double base_rate,
m_pRubberBand->setTimeRatio(1.0 / timeRatioInverse);
}

if (m_pRubberBand->getInputIncrement() == 0) {
qWarning() << "EngineBufferScaleRubberBand inputIncrement is 0."
<< "On RubberBand <=1.8.1 a SIGFPE is imminent despite"
<< "our workaround. Taking evasive action."
<< "Please report this message to [email protected].";

// This is much slower than the minimum seek speed workaround above.
while (m_pRubberBand->getInputIncrement() == 0) {
timeRatioInverse += 0.001;
m_pRubberBand->setTimeRatio(1.0 / timeRatioInverse);
if (runningEngineVersion() == 2) {
if (m_pRubberBand->getInputIncrement() == 0) {
qWarning() << "EngineBufferScaleRubberBand inputIncrement is 0."
<< "On RubberBand <=1.8.1 a SIGFPE is imminent despite"
<< "our workaround. Taking evasive action."
<< "Please file an issue on https://github.com/mixxxdj/mixxx/issues";

// This is much slower than the minimum seek speed workaround above.
while (m_pRubberBand->getInputIncrement() == 0) {
timeRatioInverse += 0.001;
m_pRubberBand->setTimeRatio(1.0 / timeRatioInverse);
}
speed_abs = timeRatioInverse / base_rate;
*pTempoRatio = m_bBackwards ? -speed_abs : speed_abs;
}
speed_abs = timeRatioInverse / base_rate;
*pTempoRatio = m_bBackwards ? -speed_abs : speed_abs;
}

// Used by other methods so we need to keep them up to date.
m_dBaseRate = base_rate;
m_dTempoRatio = speed_abs;
Expand All @@ -109,10 +116,18 @@ void EngineBufferScaleRubberBand::onSampleRateChanged() {
m_pRubberBand.reset();
return;
}
RubberBandStretcher::Options rubberbandOptions = RubberBandStretcher::OptionProcessRealTime;
#if RUBBERBANDV3
// TODO make this a runtime option
rubberbandOptions |= RubberBandStretcher::OptionEngineFiner;
#endif

m_pRubberBand = std::make_unique<RubberBandStretcher>(
getOutputSignal().getSampleRate(),
getOutputSignal().getChannelCount(),
RubberBandStretcher::OptionProcessRealTime);
rubberbandOptions);
// TODO (XXX): we should always be able to provide rubberband as
// many samples as it wants. So remove this.
m_pRubberBand->setMaxProcessSize(kRubberBandBlockSize);
// Setting the time ratio to a very high value will cause RubberBand
// to preallocate buffers large enough to (almost certainly)
Expand Down Expand Up @@ -190,6 +205,9 @@ double EngineBufferScaleRubberBand::scaleBuffer(

size_t iLenFramesRequired = m_pRubberBand->getSamplesRequired();
if (iLenFramesRequired == 0) {
// TODO (XXX): Rubberband 1.3 is not being packaged anymore.
// Remove this workaround.
//
// rubberband 1.3 (packaged up through Ubuntu Quantal) has a bug
// where it can report 0 samples needed forever which leads us to an
// infinite loop. To work around this, we check if available() is
Expand Down Expand Up @@ -245,3 +263,11 @@ double EngineBufferScaleRubberBand::scaleBuffer(

return framesRead;
}

int EngineBufferScaleRubberBand::runningEngineVersion() {
#if RUBBERBANDV3
return m_pRubberBand->getEngineVersion();
#else
return 2;
#endif
}
2 changes: 2 additions & 0 deletions src/engine/bufferscalers/enginebufferscalerubberband.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class EngineBufferScaleRubberBand : public EngineBufferScale {
// Reset RubberBand library with new audio signal
void onSampleRateChanged() override;

int runningEngineVersion();

void deinterleaveAndProcess(const CSAMPLE* pBuffer, SINT frames, bool flush);
SINT retrieveAndDeinterleave(CSAMPLE* pBuffer, SINT frames);

Expand Down