diff --git a/README.txt b/README.txt index 4729a58..26d6fc6 100644 --- a/README.txt +++ b/README.txt @@ -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 ----------------------------- diff --git a/misc/Makefile.osx b/build/Makefile.osx similarity index 92% rename from misc/Makefile.osx rename to build/Makefile.osx index 64ea4f6..8d15dbc 100644 --- a/misc/Makefile.osx +++ b/build/Makefile.osx @@ -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 diff --git a/main/main.cpp b/main/main.cpp index 589c987..4661300 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -26,13 +26,9 @@ #include "system/sysutils.h" -#ifdef __MSVC__ -#include "getopt/getopt.h" -#else #include #include #include -#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) { diff --git a/src/StretcherChannelData.cpp b/src/StretcherChannelData.cpp index b9d09ea..927f983 100644 --- a/src/StretcherChannelData.cpp +++ b/src/StretcherChannelData.cpp @@ -70,9 +70,10 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set &sizes, unwrappedPhase = allocate_and_zero(realSize); envelope = allocate_and_zero(realSize); - freqPeak = new size_t[realSize]; + freqPeak = allocate_and_zero(realSize); fltbuf = allocate_and_zero(maxSize); + dblbuf = allocate_and_zero(maxSize); accumulator = allocate_and_zero(maxSize); windowAccumulator = allocate_and_zero(maxSize); @@ -90,31 +91,12 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set &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(mag, oldMax, realSize); - phase = reallocate_and_zero(phase, oldMax, realSize); - prevPhase = reallocate_and_zero(prevPhase, oldMax, realSize); - prevError = reallocate_and_zero(prevError, oldMax, realSize); - unwrappedPhase = reallocate_and_zero(unwrappedPhase, oldMax, realSize); - envelope = reallocate_and_zero(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(maxSize); + interpolator = reallocate_and_zero(interpolator, oldMax, maxSize); // But we do want to preserve data in these - float *newAcc = allocate_and_zero(maxSize); + accumulator = reallocate_and_zero_extension + (accumulator, oldMax, maxSize); - v_copy(newAcc, accumulator, oldMax); - - deallocate(accumulator); - accumulator = newAcc; - - newAcc = allocate_and_zero(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); diff --git a/src/StretcherImpl.cpp b/src/StretcherImpl.cpp index ed1561f..046db01 100644 --- a/src/StretcherImpl.cpp +++ b/src/StretcherImpl.cpp @@ -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); } } diff --git a/src/StretcherProcess.cpp b/src/StretcherProcess.cpp index 8c84a01..2504ccb 100644 --- a/src/StretcherProcess.cpp +++ b/src/StretcherProcess.cpp @@ -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::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 diff --git a/src/base/Scavenger.h b/src/base/Scavenger.h index cfebe32..e1be933 100644 --- a/src/base/Scavenger.h +++ b/src/base/Scavenger.h @@ -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 class Scavenger { diff --git a/src/dsp/CompoundAudioCurve.cpp b/src/dsp/CompoundAudioCurve.cpp index 3840885..fe7bb3b 100644 --- a/src/dsp/CompoundAudioCurve.cpp +++ b/src/dsp/CompoundAudioCurve.cpp @@ -28,6 +28,7 @@ CompoundAudioCurve::CompoundAudioCurve(Parameters parameters) : m_hf(parameters), m_hfFilter(new MovingMedian(19, 85)), m_hfDerivFilter(new MovingMedian(19, 90)), + m_type(CompoundDetector), m_lastHf(0.0), m_lastResult(0.0), m_risingCount(0) diff --git a/src/dsp/CompoundAudioCurve.h b/src/dsp/CompoundAudioCurve.h index 5cbadf4..d3b2b55 100644 --- a/src/dsp/CompoundAudioCurve.h +++ b/src/dsp/CompoundAudioCurve.h @@ -35,7 +35,7 @@ public: CompoundDetector, SoftDetector }; - virtual void setType(Type); + virtual void setType(Type); // default is CompoundDetector virtual void setFftSize(int newSize); diff --git a/src/dsp/FFT.cpp b/src/dsp/FFT.cpp index 0c4647d..6f4b0df 100644 --- a/src/dsp/FFT.cpp +++ b/src/dsp/FFT.cpp @@ -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() diff --git a/src/dsp/FFT.h b/src/dsp/FFT.h index 4acbbc5..ebd6453 100644 --- a/src/dsp/FFT.h +++ b/src/dsp/FFT.h @@ -73,9 +73,6 @@ public: void initFloat(); void initDouble(); - float *getFloatTimeBuffer(); - double *getDoubleTimeBuffer(); - static void tune(); protected: diff --git a/src/dsp/PercussiveAudioCurve.cpp b/src/dsp/PercussiveAudioCurve.cpp index c36fc52..eb82f38 100644 --- a/src/dsp/PercussiveAudioCurve.cpp +++ b/src/dsp/PercussiveAudioCurve.cpp @@ -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(); } diff --git a/src/dsp/Resampler.cpp b/src/dsp/Resampler.cpp index e8b761b..ef9f160 100644 --- a/src/dsp/Resampler.cpp +++ b/src/dsp/Resampler.cpp @@ -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() diff --git a/src/system/Allocators.h b/src/system/Allocators.h index 04eb9f1..e186692 100644 --- a/src/system/Allocators.h +++ b/src/system/Allocators.h @@ -69,7 +69,8 @@ void deallocate(T *ptr) if (ptr) free((void *)ptr); } - + +/// Reallocate preserving contents but leaving additional memory uninitialised template 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(ptr); return newptr; } - + +/// Reallocate, zeroing all contents template 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 +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 T **allocate_channels(size_t channels, size_t count) diff --git a/src/system/Thread.h b/src/system/Thread.h index 410ba64..e976a95 100644 --- a/src/system/Thread.h +++ b/src/system/Thread.h @@ -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);