Skip to content

Commit

Permalink
Don't trust RubberBand::getSamplesRequired().
Browse files Browse the repository at this point in the history
In librubberband 1.3.x (packaged in Ubuntu up through Quantal) getSamplesRequired() can return 0 even when no samples are available. This leads to an infinite loop.
  • Loading branch information
rryan committed Jun 22, 2013
1 parent 58e2506 commit adb034b
Showing 1 changed file with 30 additions and 7 deletions.
37 changes: 30 additions & 7 deletions src/engine/enginebufferscalerubberband.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@

using RubberBand::RubberBandStretcher;

EngineBufferScaleRubberBand::EngineBufferScaleRubberBand(ReadAheadManager* pReadAheadManager)
// This is the default increment from RubberBand 1.8.1.
static size_t kRubberBandBlockSize = 256;

EngineBufferScaleRubberBand::EngineBufferScaleRubberBand(
ReadAheadManager* pReadAheadManager)
: m_bBackwards(false),
m_buffer_back(SampleUtil::alloc(MAX_BUFFER_LEN)),
m_pRubberBand(NULL),
m_pReadAheadManager(pReadAheadManager) {
qDebug() << "RubberBand version" << RUBBERBAND_VERSION;

m_retrieve_buffer[0] = SampleUtil::alloc(MAX_BUFFER_LEN);
m_retrieve_buffer[1] = SampleUtil::alloc(MAX_BUFFER_LEN);

Expand Down Expand Up @@ -48,6 +54,7 @@ void EngineBufferScaleRubberBand::initializeRubberBand(int iSampleRate) {
m_pRubberBand = new RubberBandStretcher(
iSampleRate, 2,
RubberBandStretcher::OptionProcessRealTime);
m_pRubberBand->setMaxProcessSize(kRubberBandBlockSize);
}

void EngineBufferScaleRubberBand::setScaleParameters(double* rate_adjust,
Expand Down Expand Up @@ -107,11 +114,12 @@ void EngineBufferScaleRubberBand::clear() {
m_pRubberBand->reset();
}

size_t EngineBufferScaleRubberBand::retrieveAndDeinterleave(CSAMPLE* pBuffer, size_t frames) {
size_t EngineBufferScaleRubberBand::retrieveAndDeinterleave(CSAMPLE* pBuffer,
size_t frames) {
size_t frames_available = m_pRubberBand->available();
size_t frames_to_read = math_min(frames_available, frames);
size_t received_frames = m_pRubberBand->retrieve((float* const*)m_retrieve_buffer,
frames_to_read);
size_t received_frames = m_pRubberBand->retrieve(
(float* const*)m_retrieve_buffer, frames_to_read);

for (size_t i = 0; i < received_frames; ++i) {
pBuffer[i*2] = m_retrieve_buffer[0][i];
Expand All @@ -129,7 +137,8 @@ void EngineBufferScaleRubberBand::deinterleaveAndProcess(
m_retrieve_buffer[1][i] = pBuffer[i*2+1];
}

m_pRubberBand->process((const float* const*)m_retrieve_buffer, frames, flush);
m_pRubberBand->process((const float* const*)m_retrieve_buffer,
frames, flush);
}


Expand Down Expand Up @@ -167,14 +176,28 @@ CSAMPLE* EngineBufferScaleRubberBand::getScaled(unsigned long buf_size) {
read += received_frames * iNumChannels;

if (break_out_after_retrieve_and_reset_rubberband) {
//qDebug() << "break_out_after_retrieve_and_reset_rubberband";
// If we break out early then we have flushed RubberBand and need to
// reset it.
m_pRubberBand->reset();
break;
}

if (remaining_frames > 0) {
unsigned long iLenFramesRequired = m_pRubberBand->getSamplesRequired();
size_t iLenFramesRequired = m_pRubberBand->getSamplesRequired();
if (iLenFramesRequired == 0) {
// 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
// zero. If it is, then we submit a fixed block size of
// kRubberBandBlockSize.
int available = m_pRubberBand->available();
if (available == 0) {
iLenFramesRequired = kRubberBandBlockSize;
}
}
//qDebug() << "iLenFramesRequired" << iLenFramesRequired;

if (remaining_frames > 0 && iLenFramesRequired > 0) {
unsigned long iAvailSamples = m_pReadAheadManager
->getNextSamples(
// The value doesn't matter here. All that matters is we
Expand Down

0 comments on commit adb034b

Please sign in to comment.