* Fix failure to remember that we have constructed an interpolator

window already (#25).  Also avoid using alloca for substantial buffers
* Lose FFT::getFloatTimeBuffer and getDoubleTimeBuffer -- it's too
unclear when it's safe to use them and it's safer to control sizes externally.
In RB with smoothing on, these buffers were incorrectly being used for window-si
zed calculations (larger than FFT-sized).
* Fix some incorrect buffer resize sizes
* Build fixes for OS/X
This commit is contained in:
Chris Cannam
2011-03-19 12:41:38 +00:00
parent 0b8c1bd90b
commit c45acda473
15 changed files with 133 additions and 205 deletions

View File

@@ -4,10 +4,29 @@ Rubber Band
An audio time-stretching and pitch-shifting library and utility program.
Copyright 2007-2010 Chris Cannam, chris.cannam@breakfastquay.com.
Copyright 2007-2011 Chris Cannam, chris.cannam@breakfastquay.com.
Distributed under the GNU General Public License.
Contents
========
1. About Rubber Band
- Attractive features
- Limitations
2. Compiling Rubber Band
3. Using the Rubber Band utility
4. Using the Rubber Band library
About Rubber Band
-----------------
Rubber Band is a library and utility program that permits you to
change the tempo and pitch of an audio recording independently of one
another.
@@ -90,11 +109,15 @@ Limitations
Compiling Rubber Band
---------------------
Rubber Band is supplied with build scripts that have been tested on
Linux platforms. It is also possible to build Rubber Band on other
platforms, including both POSIX platforms such as OS/X and non-POSIX
platforms such as Win32. There are some example Makefiles in the misc
directory.
Rubber Band Library is supplied with a configure script for Linux and
other systems with pkg-config, and a separate Makefile for basic OS/X
builds without pkg-config. It's also possible to build the Rubber
Band Library GPL edition for Windows using MinGW, though you'll have
to hack your own Makefile for that.
Using configure
~~~~~~~~~~~~~~~
To build Rubber Band you will also need libsndfile, libsamplerate,
FFTW3, the Vamp plugin SDK, the LADSPA plugin header, the pthread
@@ -113,6 +136,18 @@ to compile, and optionally
to install.
Simple build for OS/X
~~~~~~~~~~~~~~~~~~~~~
To build just the library (but not the command-line utility, Vamp
plugin or LADSPA plugin) for OS/X, run
$ make -f build/Makefile.osx
You will need libsamplerate and libfftw3 installed, but no other
non-system dependencies.
Using the Rubber Band utility
-----------------------------

View File

@@ -3,8 +3,12 @@
# Does not build the Vamp plugin, LADSPA plugin, or command-line utility.
CXX := g++
CXXFLAGS := -DHAVE_FFTW3 -DFFTW_DOUBLE_ONLY -DNO_THREAD_CHECKS -DNO_TIMING -DNDEBUG -mmacosx-version-min=10.4 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -O3 -arch i386 -arch ppc -msse -msse2 -ffast-math -ftree-vectorize -I../include -I/usr/local/include -Irubberband -I. -Isrc
LDFLAGS := -mmacosx-version-min=10.4 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -arch ppc -L../lib -L/usr/local/lib
CXXFLAGS := -DHAVE_FFTW3 -DFFTW_DOUBLE_ONLY -DNO_THREAD_CHECKS -DNO_TIMING -DNDEBUG -O3 -arch i386 -arch x86_64 -msse -msse2 -ffast-math -ftree-vectorize -I../include -I/usr/local/include -Irubberband -I. -Isrc
LDFLAGS := -arch i386 -arch x86_64 -L../lib -L/usr/local/lib
# CXX := g++-4.0
# CXXFLAGS := -DHAVE_FFTW3 -DFFTW_DOUBLE_ONLY -DNO_THREAD_CHECKS -DNO_TIMING -DNDEBUG -mmacosx-version-min=10.4 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -O3 -arch i386 -msse -msse2 -ffast-math -ftree-vectorize -I../include -I/usr/local/include -Irubberband -I. -Isrc
# LDFLAGS := -mmacosx-version-min=10.4 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386 -L../lib -L/usr/local/lib
LIBRARY_LIBS := -lsamplerate -lfftw3 -lpthread -lm

View File

@@ -26,13 +26,9 @@
#include "system/sysutils.h"
#ifdef __MSVC__
#include "getopt/getopt.h"
#else
#include <getopt.h>
#include <unistd.h>
#include <sys/time.h>
#endif
#include "base/Profiler.h"
@@ -43,9 +39,6 @@ using namespace RubberBand;
using RubberBand::gettimeofday;
#endif
#ifdef __MSVC__
using RubberBand::usleep;
#endif
double tempo_convert(const char *str)
{

View File

@@ -70,9 +70,10 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set<size_t> &sizes,
unwrappedPhase = allocate_and_zero<process_t>(realSize);
envelope = allocate_and_zero<process_t>(realSize);
freqPeak = new size_t[realSize];
freqPeak = allocate_and_zero<size_t>(realSize);
fltbuf = allocate_and_zero<float>(maxSize);
dblbuf = allocate_and_zero<process_t>(maxSize);
accumulator = allocate_and_zero<float>(maxSize);
windowAccumulator = allocate_and_zero<float>(maxSize);
@@ -90,31 +91,12 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set<size_t> &sizes,
}
fft = ffts[initialFftSize];
if (sizeof(process_t) == sizeof(double)) {
dblbuf = (process_t *)fft->getDoubleTimeBuffer();
} else {
dblbuf = (process_t *)fft->getFloatTimeBuffer();
}
resampler = 0;
resamplebuf = 0;
resamplebufSize = 0;
reset();
for (size_t i = 0; i < realSize; ++i) {
freqPeak[i] = 0;
}
for (size_t i = 0; i < initialFftSize; ++i) {
dblbuf[i] = 0.0;
}
for (size_t i = 0; i < maxSize; ++i) {
accumulator[i] = 0.f;
windowAccumulator[i] = 0.f;
}
// Avoid dividing opening sample (which will be discarded anyway) by zero
windowAccumulator[0] = 1.f;
}
@@ -127,6 +109,7 @@ RubberBandStretcher::Impl::ChannelData::setSizes(size_t windowSize,
size_t maxSize = std::max(windowSize, fftSize);
size_t realSize = maxSize / 2 + 1;
size_t oldMax = inbuf->getSize();
size_t oldReal = oldMax / 2 + 1;
if (oldMax >= maxSize) {
@@ -149,12 +132,7 @@ RubberBandStretcher::Impl::ChannelData::setSizes(size_t windowSize,
fft = ffts[fftSize];
if (sizeof(process_t) == sizeof(double)) {
dblbuf = (process_t *)fft->getDoubleTimeBuffer();
} else {
dblbuf = (process_t *)fft->getFloatTimeBuffer();
}
v_zero(fltbuf, maxSize);
v_zero(dblbuf, maxSize);
v_zero(mag, realSize);
@@ -180,34 +158,25 @@ RubberBandStretcher::Impl::ChannelData::setSizes(size_t windowSize,
// We don't want to preserve data in these arrays
mag = reallocate_and_zero<process_t>(mag, oldMax, realSize);
phase = reallocate_and_zero<process_t>(phase, oldMax, realSize);
prevPhase = reallocate_and_zero<process_t>(prevPhase, oldMax, realSize);
prevError = reallocate_and_zero<process_t>(prevError, oldMax, realSize);
unwrappedPhase = reallocate_and_zero<process_t>(unwrappedPhase, oldMax, realSize);
envelope = reallocate_and_zero<process_t>(envelope, oldMax, realSize);
mag = reallocate_and_zero(mag, oldReal, realSize);
phase = reallocate_and_zero(phase, oldReal, realSize);
prevPhase = reallocate_and_zero(prevPhase, oldReal, realSize);
prevError = reallocate_and_zero(prevError, oldReal, realSize);
unwrappedPhase = reallocate_and_zero(unwrappedPhase, oldReal, realSize);
envelope = reallocate_and_zero(envelope, oldReal, realSize);
freqPeak = reallocate_and_zero(freqPeak, oldReal, realSize);
fltbuf = reallocate_and_zero(fltbuf, oldMax, maxSize);
dblbuf = reallocate_and_zero(dblbuf, oldMax, maxSize);
delete[] freqPeak;
freqPeak = new size_t[realSize];
deallocate(fltbuf);
fltbuf = allocate_and_zero<float>(maxSize);
interpolator = reallocate_and_zero<float>(interpolator, oldMax, maxSize);
// But we do want to preserve data in these
float *newAcc = allocate_and_zero<float>(maxSize);
accumulator = reallocate_and_zero_extension
(accumulator, oldMax, maxSize);
v_copy(newAcc, accumulator, oldMax);
deallocate(accumulator);
accumulator = newAcc;
newAcc = allocate_and_zero<float>(maxSize);
v_copy(newAcc, windowAccumulator, oldMax);
deallocate(windowAccumulator);
windowAccumulator = newAcc;
windowAccumulator = reallocate_and_zero_extension
(windowAccumulator, oldMax, maxSize);
interpolatorScale = 0;
@@ -223,14 +192,6 @@ RubberBandStretcher::Impl::ChannelData::setSizes(size_t windowSize,
}
fft = ffts[fftSize];
if (sizeof(process_t) == sizeof(double)) {
dblbuf = (process_t *)fft->getDoubleTimeBuffer();
} else {
dblbuf = (process_t *)fft->getFloatTimeBuffer();
}
v_zero(dblbuf, fftSize);
}
void
@@ -273,7 +234,7 @@ RubberBandStretcher::Impl::ChannelData::~ChannelData()
deallocate(prevError);
deallocate(unwrappedPhase);
deallocate(envelope);
delete[] freqPeak;
deallocate(freqPeak);
deallocate(accumulator);
deallocate(windowAccumulator);
deallocate(fltbuf);

View File

@@ -783,8 +783,10 @@ RubberBandStretcher::Impl::reconfigure()
new Resampler(Resampler::FastestTolerable, 1, m_sWindowSize,
m_debugLevel);
m_channelData[c]->setResampleBufSize
(lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale)));
size_t rbs =
lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale));
if (rbs < m_increment * 16) rbs = m_increment * 16;
m_channelData[c]->setResampleBufSize(rbs);
}
}

View File

@@ -886,7 +886,7 @@ RubberBandStretcher::Impl::synthesiseChunk(size_t channel,
const int hs = fsz / 2;
const int wsz = m_sWindowSize;
if (!cd.unchanged) {
cd.fft->inversePolar(cd.mag, cd.phase, cd.dblbuf);
@@ -910,22 +910,27 @@ RubberBandStretcher::Impl::synthesiseChunk(size_t channel,
}
if (wsz > fsz) {
float *tmp = (float *)alloca(wsz * sizeof(float));
int p = shiftIncrement * 2;
if (cd.interpolatorScale != p) {
SincWindow<float>::write(cd.interpolator, wsz, p);
cd.interpolatorScale = p;
}
v_multiply(fltbuf, cd.interpolator, wsz);
v_copy(tmp, cd.interpolator, wsz);
m_swindow->cut(tmp);
v_add(windowAccumulator, tmp, wsz);
} else {
m_swindow->add(windowAccumulator, m_awindow->getArea() * 1.5f);
}
m_swindow->cut(fltbuf);
v_add(accumulator, fltbuf, wsz);
cd.accumulatorFill = wsz;
if (wsz > fsz) {
// reuse fltbuf to calculate interpolating window shape for
// window accumulator
v_copy(fltbuf, cd.interpolator, wsz);
m_swindow->cut(fltbuf);
v_add(windowAccumulator, fltbuf, wsz);
} else {
m_swindow->add(windowAccumulator, m_awindow->getArea() * 1.5f);
}
}
void

View File

@@ -43,6 +43,9 @@ namespace RubberBand {
* -- it's just a quick hack for use with things like plugins.
*/
//!!! should review this, it's not really thread safe owing to lack of
//!!! atomic updates
template <typename T>
class Scavenger
{

View File

@@ -28,6 +28,7 @@ CompoundAudioCurve::CompoundAudioCurve(Parameters parameters) :
m_hf(parameters),
m_hfFilter(new MovingMedian<double>(19, 85)),
m_hfDerivFilter(new MovingMedian<double>(19, 90)),
m_type(CompoundDetector),
m_lastHf(0.0),
m_lastResult(0.0),
m_risingCount(0)

View File

@@ -35,7 +35,7 @@ public:
CompoundDetector,
SoftDetector
};
virtual void setType(Type);
virtual void setType(Type); // default is CompoundDetector
virtual void setFftSize(int newSize);

View File

@@ -17,8 +17,9 @@
#include "base/Profiler.h"
#include "system/Allocators.h"
#include "system/VectorOps.h"
#include "system/VectorOpsComplex.h"
//#define FFT_MEASUREMENT 1
#define FFT_MEASUREMENT 1
#ifdef HAVE_FFTW3
@@ -77,9 +78,6 @@ public:
virtual void inverseInterleaved(const float *R__ complexIn, float *R__ realOut) = 0;
virtual void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) = 0;
virtual void inverseCepstral(const float *R__ magIn, float *R__ cepOut) = 0;
virtual float *getFloatTimeBuffer() = 0;
virtual double *getDoubleTimeBuffer() = 0;
};
namespace FFTs {
@@ -152,15 +150,8 @@ namespace FFTs {
class D_FFTW : public FFTImpl
{
public:
D_FFTW(int size) : m_fplanf(0)
#ifdef FFTW_DOUBLE_ONLY
, m_frb(0)
#endif
, m_dplanf(0)
#ifdef FFTW_FLOAT_ONLY
, m_drb(0)
#endif
, m_size(size)
D_FFTW(int size) :
m_fplanf(0), m_dplanf(0), m_size(size)
{
}
@@ -176,9 +167,6 @@ public:
fftwf_destroy_plan(m_fplani);
fftwf_free(m_fbuf);
fftwf_free(m_fpacked);
#ifdef FFTW_DOUBLE_ONLY
if (m_frb) fftw_free(m_frb);
#endif
m_commonMutex.unlock();
}
if (m_dplanf) {
@@ -192,9 +180,6 @@ public:
fftw_destroy_plan(m_dplani);
fftw_free(m_dbuf);
fftw_free(m_dpacked);
#ifdef FFTW_FLOAT_ONLY
if (m_drb) fftwf_free(m_drb);
#endif
m_commonMutex.unlock();
}
}
@@ -390,14 +375,8 @@ public:
dbuf[i] = realIn[i];
}
fftw_execute(m_dplanf);
const int hs = m_size/2;
for (int i = 0; i <= hs; ++i) {
magOut[i] = sqrt(m_dpacked[i][0] * m_dpacked[i][0] +
m_dpacked[i][1] * m_dpacked[i][1]);
}
for (int i = 0; i <= hs; ++i) {
phaseOut[i] = atan2(m_dpacked[i][1], m_dpacked[i][0]);
}
v_cartesian_interleaved_to_polar(magOut, phaseOut,
(double *)m_dpacked, m_size/2+1);
}
void forwardMagnitude(const double *R__ realIn, double *R__ magOut) {
@@ -457,14 +436,8 @@ public:
fbuf[i] = realIn[i];
}
fftwf_execute(m_fplanf);
const int hs = m_size/2;
for (int i = 0; i <= hs; ++i) {
magOut[i] = sqrtf(m_fpacked[i][0] * m_fpacked[i][0] +
m_fpacked[i][1] * m_fpacked[i][1]);
}
for (int i = 0; i <= hs; ++i) {
phaseOut[i] = atan2f(m_fpacked[i][1], m_fpacked[i][0]) ;
}
v_cartesian_interleaved_to_polar(magOut, phaseOut,
(float *)m_fpacked, m_size/2+1);
}
void forwardMagnitude(const float *R__ realIn, float *R__ magOut) {
@@ -625,31 +598,10 @@ public:
}
}
float *getFloatTimeBuffer() {
initFloat();
#ifdef FFTW_DOUBLE_ONLY
if (!m_frb) m_frb = (float *)fftw_malloc(m_size * sizeof(float));
return m_frb;
#else
return m_fbuf;
#endif
}
double *getDoubleTimeBuffer() {
initDouble();
#ifdef FFTW_FLOAT_ONLY
if (!m_drb) m_drb = (double *)fftwf_malloc(m_size * sizeof(double));
return m_drb;
#else
return m_dbuf;
#endif
}
private:
fftwf_plan m_fplanf;
fftwf_plan m_fplani;
#ifdef FFTW_DOUBLE_ONLY
float *m_frb;
double *m_fbuf;
#else
float *m_fbuf;
@@ -659,11 +611,10 @@ private:
fftw_plan m_dplani;
#ifdef FFTW_FLOAT_ONLY
float *m_dbuf;
double *m_drb;
#else
double *m_dbuf;
#endif
fftw_complex * m_dpacked;
fftw_complex *m_dpacked;
const int m_size;
static int m_extantf;
static int m_extantd;
@@ -688,8 +639,6 @@ class D_KISSFFT : public FFTImpl
public:
D_KISSFFT(int size) :
m_size(size),
m_frb(0),
m_drb(0),
m_fplanf(0),
m_fplani(0)
{
@@ -714,9 +663,6 @@ public:
delete[] m_fbuf;
delete[] m_fpacked;
if (m_frb) delete[] m_frb;
if (m_drb) delete[] m_drb;
}
void initFloat() { }
@@ -956,21 +902,9 @@ public:
kiss_fftri(m_fplani, m_fpacked, cepOut);
}
float *getFloatTimeBuffer() {
if (!m_frb) m_frb = new float[m_size];
return m_frb;
}
double *getDoubleTimeBuffer() {
if (!m_drb) m_drb = new double[m_size];
return m_drb;
}
private:
const int m_size;
float* m_frb;
double* m_drb;
kiss_fftr_cfg m_fplanf;
kiss_fftr_cfg m_fplani;
kiss_fft_scalar *m_fbuf;
@@ -984,7 +918,7 @@ private:
class D_Cross : public FFTImpl
{
public:
D_Cross(int size) : m_size(size), m_table(0), m_frb(0), m_drb(0) {
D_Cross(int size) : m_size(size), m_table(0) {
m_a = new double[size];
m_b = new double[size];
@@ -1022,8 +956,6 @@ public:
delete[] m_b;
delete[] m_c;
delete[] m_d;
delete[] m_frb;
delete[] m_drb;
}
void initFloat() { }
@@ -1221,21 +1153,9 @@ public:
for (int i = 0; i < m_size; ++i) cepOut[i] = m_c[i];
}
float *getFloatTimeBuffer() {
if (!m_frb) m_frb = new float[m_size];
return m_frb;
}
double *getDoubleTimeBuffer() {
if (!m_drb) m_drb = new double[m_size];
return m_drb;
}
private:
const int m_size;
int *m_table;
float *m_frb;
double *m_drb;
double *m_a;
double *m_b;
double *m_c;
@@ -1561,18 +1481,6 @@ FFT::initDouble()
d->initDouble();
}
float *
FFT::getFloatTimeBuffer()
{
return d->getFloatTimeBuffer();
}
double *
FFT::getDoubleTimeBuffer()
{
return d->getDoubleTimeBuffer();
}
void
FFT::tune()

View File

@@ -73,9 +73,6 @@ public:
void initFloat();
void initDouble();
float *getFloatTimeBuffer();
double *getDoubleTimeBuffer();
static void tune();
protected:

View File

@@ -43,7 +43,7 @@ PercussiveAudioCurve::reset()
void
PercussiveAudioCurve::setFftSize(int newSize)
{
m_prevMag = reallocate(m_prevMag, m_fftSize, newSize);
m_prevMag = reallocate(m_prevMag, m_fftSize/2 + 1, newSize/2 + 1);
AudioCurveCalculator::setFftSize(newSize);
reset();
}

View File

@@ -506,6 +506,14 @@ Resampler::Resampler(Resampler::Quality quality, int channels,
#endif
break;
}
if (!d) {
std::cerr << "Resampler::Resampler(" << quality << ", " << channels
<< ", " << maxBufferSize
<< "): Internal error: No implementation selected"
<< std::endl;
abort();
}
}
Resampler::~Resampler()

View File

@@ -69,7 +69,8 @@ void deallocate(T *ptr)
if (ptr) free((void *)ptr);
}
/// Reallocate preserving contents but leaving additional memory uninitialised
template <typename T>
T *reallocate(T *ptr, size_t oldcount, size_t count)
{
@@ -86,7 +87,8 @@ T *reallocate(T *ptr, size_t oldcount, size_t count)
if (ptr) deallocate<T>(ptr);
return newptr;
}
/// Reallocate, zeroing all contents
template <typename T>
T *reallocate_and_zero(T *ptr, size_t oldcount, size_t count)
{
@@ -94,6 +96,15 @@ T *reallocate_and_zero(T *ptr, size_t oldcount, size_t count)
v_zero(ptr, count);
return ptr;
}
/// Reallocate preserving contents and zeroing any additional memory
template <typename T>
T *reallocate_and_zero_extension(T *ptr, size_t oldcount, size_t count)
{
ptr = reallocate(ptr, oldcount, count);
if (count > oldcount) v_zero(ptr + oldcount, count - oldcount);
return ptr;
}
template <typename T>
T **allocate_channels(size_t channels, size_t count)

View File

@@ -99,26 +99,26 @@ private:
Mutex *m_mutex;
};
/**
The Condition class bundles a condition variable and mutex.
To wait on a condition, call lock(), test the termination condition
if desired, then wait(). The condition will be unlocked during the
wait and re-locked when wait() returns (which will happen when the
condition is signalled or the timer times out).
To signal a condition, call signal(). If the condition is signalled
between lock() and wait(), the signal may be missed by the waiting
thread. To avoid this, the signalling thread should also lock the
condition before calling signal() and unlock it afterwards.
*/
class Condition
{
public:
Condition(std::string name);
~Condition();
// The Condition class bundles a condition variable and mutex.
// To wait on a condition, call lock(), test the termination
// condition if desired, then wait(). The condition will be
// unlocked during the wait and re-locked when wait() returns
// (which will happen when the condition is signalled or the timer
// times out).
// To signal a condition, call signal(). If the condition is
// signalled between lock() and wait(), the signal may be missed
// by the waiting thread. To avoid this, the signalling thread
// should also lock the condition before calling signal() and
// unlock it afterwards.
void lock();
void unlock();
void wait(int us = 0);