diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2008-07-10 11:30:19 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2008-07-10 11:30:19 +0000 |
commit | ce601905c010ea8fc1ac75536be2a31459c1b9cc (patch) | |
tree | 6be6c533d16d2111e64186c9f9746e8b59010b2b /libs/rubberband | |
parent | 94f3ead75ec0214b08a73e315b6b5ce406595b48 (diff) |
update to rubberband 1.2
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@3576 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/rubberband')
49 files changed, 4067 insertions, 952 deletions
diff --git a/libs/rubberband/rubberband/RubberBandStretcher.h b/libs/rubberband/rubberband/RubberBandStretcher.h index 94f1e88e2d..ff12bafe8a 100644 --- a/libs/rubberband/rubberband/RubberBandStretcher.h +++ b/libs/rubberband/rubberband/RubberBandStretcher.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -14,18 +14,44 @@ #ifndef _RUBBERBANDSTRETCHER_H_ #define _RUBBERBANDSTRETCHER_H_ - -#include "TimeStretcher.h" + +#define RUBBERBAND_VERSION "1.2.0-gpl" +#define RUBBERBAND_API_MAJOR_VERSION 2 +#define RUBBERBAND_API_MINOR_VERSION 0 #include <vector> +/** + * @mainpage RubberBand + * + * The Rubber Band API is contained in the single class + * RubberBand::RubberBandStretcher. + * + * Threading notes for real-time applications: + * + * Multiple instances of RubberBandStretcher may be created and used + * in separate threads concurrently. However, for any single instance + * of RubberBandStretcher, you may not call process() more than once + * concurrently, and you may not change the time or pitch ratio while + * a process() call is being executed (if the stretcher was created in + * "real-time mode"; in "offline mode" you can't change the ratios + * during use anyway). + * + * So you can run process() in its own thread if you like, but if you + * want to change ratios dynamically from a different thread, you will + * need some form of mutex in your code. Changing the time or pitch + * ratio is real-time safe except in extreme circumstances, so for + * most applications that may change these dynamically it probably + * makes most sense to do so from the same thread as calls process(), + * even if that is a real-time thread. + */ + namespace RubberBand { -class RubberBandStretcher : public TimeStretcher +class RubberBandStretcher { public: - /** * Processing options for the timestretcher. The preferred * options should normally be set in the constructor, as a bitwise @@ -102,21 +128,15 @@ public: * during non-transient segments. These options may be changed at * any time. * - * \li \c OptionPhaseAdaptive - Lock the adjustments of phase - * for frequencies close to peak frequencies to those of the - * peak, but reduce the degree of locking as the stretch ratio - * gets longer. This, the default setting, should give a good - * balance between clarity and smoothness in most situations. + * \li \c OptionPhaseLaminar - Adjust phases when stretching in + * such a way as to try to retain the continuity of phase + * relationships between adjacent frequency bins whose phases + * are behaving in similar ways. This, the default setting, + * should give good results in most situations. * - * \li \c OptionPhasePeakLocked - Lock the adjustments of phase - * for frequencies close to peak frequencies to those of the - * peak. This should give a clear result in situations with - * relatively low stretch ratios, but a relatively metallic - * sound at longer stretches. - * - * \li \c OptionPhaseIndependent - Do not lock phase adjustments - * to peak frequencies. This usually results in a softer, - * phasier sound. + * \li \c OptionPhaseIndependent - Adjust the phase in each + * frequency bin independently from its neighbours. This + * usually results in a slightly softer, phasier sound. * * 5. Flags prefixed \c OptionThreading control the threading * model of the stretcher. These options may not be changed after @@ -151,34 +171,79 @@ public: * \li \c OptionWindowLong - Use a longer window. This is * likely to result in a smoother sound at the expense of * clarity and timing. + * + * 7. Flags prefixed \c OptionFormant control the handling of + * formant shape (spectral envelope) when pitch-shifting. These + * options may be changed at any time. + * + * \li \c OptionFormantShifted - Apply no special formant + * processing. The spectral envelope will be pitch shifted as + * normal. + * + * \li \c OptionFormantPreserved - Preserve the spectral + * envelope of the unshifted signal. This permits shifting the + * note frequency without so substantially affecting the + * perceived pitch profile of the voice or instrument. + * + * 8. Flags prefixed \c OptionPitch control the method used for + * pitch shifting. These options may be changed at any time. + * They are only effective in realtime mode; in offline mode, the + * pitch-shift method is fixed. + * + * \li \c OptionPitchHighSpeed - Use a method with a CPU cost + * that is relatively moderate and predictable. This may + * sound less clear than OptionPitchHighQuality, especially + * for large pitch shifts. + + * \li \c OptionPitchHighQuality - Use the highest quality + * method for pitch shifting. This method has a CPU cost + * approximately proportional to the required frequency shift. + + * \li \c OptionPitchHighConsistency - Use the method that gives + * greatest consistency when used to create small variations in + * pitch around the 1.0-ratio level. Unlike the previous two + * options, this avoids discontinuities when moving across the + * 1.0 pitch scale in real-time; it also consumes more CPU than + * the others in the case where the pitch scale is exactly 1.0. */ - typedef int Options; - static const int OptionProcessOffline = 0x00000000; - static const int OptionProcessRealTime = 0x00000001; + enum Option { + + OptionProcessOffline = 0x00000000, + OptionProcessRealTime = 0x00000001, - static const int OptionStretchElastic = 0x00000000; - static const int OptionStretchPrecise = 0x00000010; + OptionStretchElastic = 0x00000000, + OptionStretchPrecise = 0x00000010, - static const int OptionTransientsCrisp = 0x00000000; - static const int OptionTransientsMixed = 0x00000100; - static const int OptionTransientsSmooth = 0x00000200; + OptionTransientsCrisp = 0x00000000, + OptionTransientsMixed = 0x00000100, + OptionTransientsSmooth = 0x00000200, - static const int OptionPhaseAdaptive = 0x00000000; - static const int OptionPhasePeakLocked = 0x00001000; - static const int OptionPhaseIndependent = 0x00002000; + OptionPhaseLaminar = 0x00000000, + OptionPhaseIndependent = 0x00002000, - static const int OptionThreadingAuto = 0x00000000; - static const int OptionThreadingNever = 0x00010000; - static const int OptionThreadingAlways = 0x00020000; + OptionThreadingAuto = 0x00000000, + OptionThreadingNever = 0x00010000, + OptionThreadingAlways = 0x00020000, - static const int OptionWindowStandard = 0x00000000; - static const int OptionWindowShort = 0x00100000; - static const int OptionWindowLong = 0x00200000; + OptionWindowStandard = 0x00000000, + OptionWindowShort = 0x00100000, + OptionWindowLong = 0x00200000, + + OptionFormantShifted = 0x00000000, + OptionFormantPreserved = 0x01000000, + + OptionPitchHighSpeed = 0x00000000, + OptionPitchHighQuality = 0x02000000, + OptionPitchHighConsistency = 0x04000000 + }; + + typedef int Options; - static const int DefaultOptions = 0x00000000; - static const int PercussiveOptions = OptionWindowShort | \ - OptionPhaseIndependent; + enum PresetOption { + DefaultOptions = 0x00000000, + PercussiveOptions = 0x00102000 + }; /** * Construct a time and pitch stretcher object to run at the given @@ -193,14 +258,14 @@ public: Options options = DefaultOptions, double initialTimeRatio = 1.0, double initialPitchScale = 1.0); - virtual ~RubberBandStretcher(); + ~RubberBandStretcher(); /** * Reset the stretcher's internal buffers. The stretcher should * subsequently behave as if it had just been constructed * (although retaining the current time and pitch ratio). */ - virtual void reset(); + void reset(); /** * Set the time ratio for the stretcher. This is the ratio of @@ -223,7 +288,7 @@ public: * mechanism to ensure that setTimeRatio and process() cannot be * run at once (there is no internal mutex for this purpose). */ - virtual void setTimeRatio(double ratio); + void setTimeRatio(double ratio); /** * Set the pitch scaling ratio for the stretcher. This is the @@ -250,19 +315,19 @@ public: * mechanism to ensure that setPitchScale and process() cannot be * run at once (there is no internal mutex for this purpose). */ - virtual void setPitchScale(double scale); + void setPitchScale(double scale); /** * Return the last time ratio value that was set (either on * construction or with setTimeRatio()). */ - virtual double getTimeRatio() const; + double getTimeRatio() const; /** * Return the last pitch scaling ratio value that was set (either * on construction or with setPitchScale()). */ - virtual double getPitchScale() const; + double getPitchScale() const; /** * Return the processing latency of the stretcher. This is the @@ -273,7 +338,7 @@ public: * In RealTime mode, the latency may depend on the time and pitch * ratio and other options. */ - virtual size_t getLatency() const; + size_t getLatency() const; /** * Change an OptionTransients configuration setting. This may be @@ -281,7 +346,7 @@ public: * Offline mode (for which the transients option is fixed on * construction). */ - virtual void setTransientsOption(Options options); + void setTransientsOption(Options options); /** * Change an OptionPhase configuration setting. This may be @@ -291,7 +356,25 @@ public: * may not take effect immediately if processing is already under * way when this function is called. */ - virtual void setPhaseOption(Options options); + void setPhaseOption(Options options); + + /** + * Change an OptionFormant configuration setting. This may be + * called at any time in any mode. + * + * Note that if running multi-threaded in Offline mode, the change + * may not take effect immediately if processing is already under + * way when this function is called. + */ + void setFormantOption(Options options); + + /** + * Change an OptionPitch configuration setting. This may be + * called at any time in RealTime mode. It may not be called in + * Offline mode (for which the transients option is fixed on + * construction). + */ + void setPitchOption(Options options); /** * Tell the stretcher exactly how many input samples it will @@ -300,7 +383,7 @@ public: * exactly correct. In RealTime mode no such guarantee is * possible and this value is ignored. */ - virtual void setExpectedInputDuration(size_t samples); + void setExpectedInputDuration(size_t samples); /** * Ask the stretcher how many audio sample frames should be @@ -314,7 +397,7 @@ public: * study() (to which you may pass any number of samples at a time, * and from which there is no output). */ - virtual size_t getSamplesRequired() const; + size_t getSamplesRequired() const; /** * Tell the stretcher the maximum number of sample frames that you @@ -331,7 +414,7 @@ public: * study() (to which you may pass any number of samples at a time, * and from which there is no output). */ - virtual void setMaxProcessSize(size_t samples); + void setMaxProcessSize(size_t samples); /** * Provide a block of "samples" sample frames for the stretcher to @@ -350,7 +433,7 @@ public: * Set "final" to true if this is the last block of data that will * be provided to study() before the first process() call. */ - virtual void study(const float *const *input, size_t samples, bool final); + void study(const float *const *input, size_t samples, bool final); /** * Provide a block of "samples" sample frames for processing. @@ -358,7 +441,7 @@ public: * * Set "final" to true if this is the last block of input data. */ - virtual void process(const float *const *input, size_t samples, bool final); + void process(const float *const *input, size_t samples, bool final); /** * Ask the stretcher how many audio sample frames of output data @@ -373,7 +456,7 @@ public: * This function returns -1 if all data has been fully processed * and all output read, and the stretch process is now finished. */ - virtual int available() const; + int available() const; /** * Obtain some processed output data from the stretcher. Up to @@ -382,22 +465,91 @@ public: * The return value is the actual number of sample frames * retrieved. */ - virtual size_t retrieve(float *const *output, size_t samples) const; + size_t retrieve(float *const *output, size_t samples) const; - virtual float getFrequencyCutoff(int n) const; - virtual void setFrequencyCutoff(int n, float f); - - virtual size_t getInputIncrement() const; - virtual std::vector<int> getOutputIncrements() const; //!!! document particular meaning in RT mode - virtual std::vector<float> getPhaseResetCurve() const; //!!! document particular meaning in RT mode - virtual std::vector<int> getExactTimePoints() const; //!!! meaningless in RT mode + /** + * Return the value of internal frequency cutoff value n. + * + * This function is not for general use. + */ + float getFrequencyCutoff(int n) const; - virtual size_t getChannelCount() const; + /** + * Set the value of internal frequency cutoff n to f Hz. + * + * This function is not for general use. + */ + void setFrequencyCutoff(int n, float f); - virtual void calculateStretch(); + /** + * Retrieve the value of the internal input block increment value. + * + * This function is provided for diagnostic purposes only. + */ + size_t getInputIncrement() const; + + /** + * In offline mode, retrieve the sequence of internal block + * increments for output, for the entire audio data, provided the + * stretch profile has been calculated. In realtime mode, + * retrieve any output increments that have accumulated since the + * last call to getOutputIncrements, to a limit of 16. + * + * This function is provided for diagnostic purposes only. + */ + std::vector<int> getOutputIncrements() const; - virtual void setDebugLevel(int level); + /** + * In offline mode, retrieve the sequence of internal phase reset + * detection function values, for the entire audio data, provided + * the stretch profile has been calculated. In realtime mode, + * retrieve any phase reset points that have accumulated since the + * last call to getPhaseResetCurve, to a limit of 16. + * + * This function is provided for diagnostic purposes only. + */ + std::vector<float> getPhaseResetCurve() const; + /** + * In offline mode, retrieve the sequence of internal frames for + * which exact timing has been sought, for the entire audio data, + * provided the stretch profile has been calculated. In realtime + * mode, return an empty sequence. + * + * This function is provided for diagnostic purposes only. + */ + std::vector<int> getExactTimePoints() const; + + /** + * Return the number of channels this stretcher was constructed + * with. + */ + size_t getChannelCount() const; + + /** + * Force the stretcher to calculate a stretch profile. Normally + * this happens automatically for the first process() call in + * offline mode. + * + * This function is provided for diagnostic purposes only. + */ + void calculateStretch(); + + /** + * Set the level of debug output. The value may be from 0 (errors + * only) to 3 (very verbose, with audible ticks in the output at + * phase reset points). The default is whatever has been set + * using setDefaultDebugLevel, or 0 if that function has not been + * called. + */ + void setDebugLevel(int level); + + /** + * Set the default level of debug output for subsequently + * constructed stretchers. + * + * @see setDebugLevel + */ static void setDefaultDebugLevel(int level); protected: diff --git a/libs/rubberband/rubberband/rubberband-c.h b/libs/rubberband/rubberband/rubberband-c.h new file mode 100644 index 0000000000..78fd129684 --- /dev/null +++ b/libs/rubberband/rubberband/rubberband-c.h @@ -0,0 +1,121 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band + An audio time-stretching and pitch-shifting library. + Copyright 2007-2008 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RUBBERBAND_C_API_H_ +#define _RUBBERBAND_C_API_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define RUBBERBAND_VERSION "1.2.0-gpl" +#define RUBBERBAND_API_MAJOR_VERSION 2 +#define RUBBERBAND_API_MINOR_VERSION 0 + +/** + * This is a C-linkage interface to the Rubber Band time stretcher. + * + * This is a wrapper interface: the primary interface is in C++ and is + * defined and documented in RubberBandStretcher.h. The library + * itself is implemented in C++, and requires C++ standard library + * support even when using the C-linkage API. + * + * Please see RubberBandStretcher.h for documentation. + * + * If you are writing to the C++ API, do not include this header. + */ + +enum RubberBandOption { + + RubberBandOptionProcessOffline = 0x00000000, + RubberBandOptionProcessRealTime = 0x00000001, + + RubberBandOptionStretchElastic = 0x00000000, + RubberBandOptionStretchPrecise = 0x00000010, + + RubberBandOptionTransientsCrisp = 0x00000000, + RubberBandOptionTransientsMixed = 0x00000100, + RubberBandOptionTransientsSmooth = 0x00000200, + + RubberBandOptionPhaseLaminar = 0x00000000, + RubberBandOptionPhaseIndependent = 0x00002000, + + RubberBandOptionThreadingAuto = 0x00000000, + RubberBandOptionThreadingNever = 0x00010000, + RubberBandOptionThreadingAlways = 0x00020000, + + RubberBandOptionWindowStandard = 0x00000000, + RubberBandOptionWindowShort = 0x00100000, + RubberBandOptionWindowLong = 0x00200000, + + RubberBandOptionFormantShifted = 0x00000000, + RubberBandOptionFormantPreserved = 0x01000000, + + RubberBandOptionPitchHighQuality = 0x00000000, + RubberBandOptionPitchHighSpeed = 0x02000000, + RubberBandOptionPitchHighConsistency = 0x04000000 +}; + +typedef int RubberBandOptions; + +struct RubberBandState_; +typedef struct RubberBandState_ *RubberBandState; + +extern RubberBandState rubberband_new(unsigned int sampleRate, + unsigned int channels, + RubberBandOptions options, + double initialTimeRatio, + double initialPitchScale); + +extern void rubberband_delete(RubberBandState); + +extern void rubberband_reset(RubberBandState); + +extern void rubberband_set_time_ratio(RubberBandState, double ratio); +extern void rubberband_set_pitch_scale(RubberBandState, double scale); + +extern double rubberband_get_time_ratio(const RubberBandState); +extern double rubberband_get_pitch_scale(const RubberBandState); + +extern unsigned int rubberband_get_latency(const RubberBandState); + +extern void rubberband_set_transients_option(RubberBandState, RubberBandOptions options); +extern void rubberband_set_phase_option(RubberBandState, RubberBandOptions options); +extern void rubberband_set_formant_option(RubberBandState, RubberBandOptions options); +extern void rubberband_set_pitch_option(RubberBandState, RubberBandOptions options); + +extern void rubberband_set_expected_input_duration(RubberBandState, unsigned int samples); + +extern unsigned int rubberband_get_samples_required(const RubberBandState); + +extern void rubberband_set_max_process_size(RubberBandState, unsigned int samples); + +extern void rubberband_study(RubberBandState, const float *const *input, unsigned int samples, int final); +extern void rubberband_process(RubberBandState, const float *const *input, unsigned int samples, int final); + +extern int rubberband_available(const RubberBandState); +extern unsigned int rubberband_retrieve(const RubberBandState, float *const *output, unsigned int samples); + +extern unsigned int rubberband_get_channel_count(const RubberBandState); + +extern void rubberband_calculate_stretch(RubberBandState); + +extern void rubberband_set_debug_level(RubberBandState, int level); +extern void rubberband_set_default_debug_level(int level); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/rubberband/src/AudioCurve.cpp b/libs/rubberband/src/AudioCurve.cpp index c18d134b0d..118caf4bdc 100644 --- a/libs/rubberband/src/AudioCurve.cpp +++ b/libs/rubberband/src/AudioCurve.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -14,6 +14,9 @@ #include "AudioCurve.h" +#include <iostream> +using namespace std; + namespace RubberBand { @@ -27,5 +30,15 @@ AudioCurve::~AudioCurve() { } +float +AudioCurve::process(const double *R__ mag, size_t increment) +{ + cerr << "WARNING: Using inefficient AudioCurve::process(double)" << endl; + float *tmp = new float[m_windowSize]; + for (int i = 0; i < int(m_windowSize); ++i) tmp[i] = float(mag[i]); + float df = process(tmp, increment); + delete[] tmp; + return df; +} } diff --git a/libs/rubberband/src/AudioCurve.h b/libs/rubberband/src/AudioCurve.h index e7a57c52a2..7896308013 100644 --- a/libs/rubberband/src/AudioCurve.h +++ b/libs/rubberband/src/AudioCurve.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -17,6 +17,8 @@ #include <sys/types.h> +#include "sysutils.h" + namespace RubberBand { @@ -28,7 +30,8 @@ public: virtual void setWindowSize(size_t newSize) = 0; - virtual float process(float *mag, size_t increment) = 0; + virtual float process(const float *R__ mag, size_t increment) = 0; + virtual float process(const double *R__ mag, size_t increment); virtual void reset() = 0; protected: diff --git a/libs/rubberband/src/ConstantAudioCurve.cpp b/libs/rubberband/src/ConstantAudioCurve.cpp index 85c2c67072..3263c53c65 100644 --- a/libs/rubberband/src/ConstantAudioCurve.cpp +++ b/libs/rubberband/src/ConstantAudioCurve.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -38,7 +38,7 @@ ConstantAudioCurve::setWindowSize(size_t newSize) } float -ConstantAudioCurve::process(float *, size_t) +ConstantAudioCurve::process(const float *R__, size_t) { return 1.f; } diff --git a/libs/rubberband/src/ConstantAudioCurve.h b/libs/rubberband/src/ConstantAudioCurve.h index 87a4f7526c..d73cabe943 100644 --- a/libs/rubberband/src/ConstantAudioCurve.h +++ b/libs/rubberband/src/ConstantAudioCurve.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -28,7 +28,7 @@ public: virtual void setWindowSize(size_t newSize); - virtual float process(float *mag, size_t increment); + virtual float process(const float *R__ mag, size_t increment); virtual void reset(); }; diff --git a/libs/rubberband/src/FFT.cpp b/libs/rubberband/src/FFT.cpp index 1177d1dde4..5a655efc55 100644 --- a/libs/rubberband/src/FFT.cpp +++ b/libs/rubberband/src/FFT.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -14,9 +14,27 @@ #include "FFT.h" #include "Thread.h" +#include "Profiler.h" +//#define FFT_MEASUREMENT 1 +#define HAVE_FFTW3 // for Ardour + +#ifdef HAVE_FFTW3 #include <fftw3.h> +#endif + +#ifdef USE_KISSFFT +#include "bsd-3rdparty/kissfft/kiss_fftr.h" +#endif + +#ifndef HAVE_FFTW3 +#ifndef USE_KISSFFT +#ifndef USE_BUILTIN_FFT +#error No FFT implementation selected! +#endif +#endif +#endif #include <cmath> #include <iostream> @@ -35,26 +53,30 @@ public: virtual void initFloat() = 0; virtual void initDouble() = 0; - virtual void forward(double *realIn, double *realOut, double *imagOut) = 0; - virtual void forwardPolar(double *realIn, double *magOut, double *phaseOut) = 0; - virtual void forwardMagnitude(double *realIn, double *magOut) = 0; + virtual void forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) = 0; + virtual void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) = 0; + virtual void forwardMagnitude(const double *R__ realIn, double *R__ magOut) = 0; - virtual void forward(float *realIn, float *realOut, float *imagOut) = 0; - virtual void forwardPolar(float *realIn, float *magOut, float *phaseOut) = 0; - virtual void forwardMagnitude(float *realIn, float *magOut) = 0; + virtual void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) = 0; + virtual void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) = 0; + virtual void forwardMagnitude(const float *R__ realIn, float *R__ magOut) = 0; - virtual void inverse(double *realIn, double *imagIn, double *realOut) = 0; - virtual void inversePolar(double *magIn, double *phaseIn, double *realOut) = 0; + virtual void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) = 0; + virtual void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) = 0; + virtual void inverseCepstral(const double *R__ magIn, double *R__ cepOut) = 0; - virtual void inverse(float *realIn, float *imagIn, float *realOut) = 0; - virtual void inversePolar(float *magIn, float *phaseIn, float *realOut) = 0; + virtual void inverse(const float *R__ realIn, const float *R__ imagIn, 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 { +#ifdef HAVE_FFTW3 // Define FFTW_DOUBLE_ONLY to make all uses of FFTW functions be // double-precision (so "float" FFTs are calculated by casting to @@ -73,13 +95,14 @@ public: //#define FFTW_DOUBLE_ONLY 1 //#define FFTW_FLOAT_ONLY 1 -#ifdef FFTW_DOUBLE_ONLY -#ifdef FFTW_FLOAT_ONLY -#error Building for FFTW-DOUBLE BOTH +#if defined(FFTW_DOUBLE_ONLY) && defined(FFTW_FLOAT_ONLY) // Can't meaningfully define both #undef FFTW_DOUBLE_ONLY #undef FFTW_FLOAT_ONLY -#else /* !FFTW_FLOAT_ONLY */ +#endif + +#ifdef FFTW_DOUBLE_ONLY +#define fft_float_type double #define fftwf_complex fftw_complex #define fftwf_plan fftw_plan #define fftwf_plan_dft_r2c_1d fftw_plan_dft_r2c_1d @@ -92,10 +115,12 @@ public: #define sqrtf sqrt #define cosf cos #define sinf sin -#endif /* !FFTW_FLOAT_ONLY */ -#endif +#else +#define fft_float_type float +#endif /* FFTW_DOUBLE_ONLY */ #ifdef FFTW_FLOAT_ONLY +#define fft_double_type float #define fftw_complex fftwf_complex #define fftw_plan fftwf_plan #define fftw_plan_dft_r2c_1d fftwf_plan_dft_r2c_1d @@ -107,13 +132,15 @@ public: #define atan2 atan2f #define sqrt sqrtf #define cos cosf -#define sif sinf +#define sin sinf +#else +#define fft_double_type double #endif /* FFTW_FLOAT_ONLY */ class D_FFTW : public FFTImpl { public: - D_FFTW(unsigned int size) : m_fplanf(0) + D_FFTW(int size) : m_fplanf(0) #ifdef FFTW_DOUBLE_ONLY , m_frb(0) #endif @@ -131,7 +158,9 @@ public: m_extantMutex.lock(); if (m_extantf > 0 && --m_extantf == 0) save = true; m_extantMutex.unlock(); +#ifndef FFTW_DOUBLE_ONLY if (save) saveWisdom('f'); +#endif fftwf_destroy_plan(m_fplanf); fftwf_destroy_plan(m_fplani); fftwf_free(m_fbuf); @@ -145,7 +174,9 @@ public: m_extantMutex.lock(); if (m_extantd > 0 && --m_extantd == 0) save = true; m_extantMutex.unlock(); +#ifndef FFTW_FLOAT_ONLY if (save) saveWisdom('d'); +#endif fftw_destroy_plan(m_dplanf); fftw_destroy_plan(m_dplani); fftw_free(m_dbuf); @@ -164,11 +195,10 @@ public: m_extantMutex.unlock(); #ifdef FFTW_DOUBLE_ONLY if (load) loadWisdom('d'); - m_fbuf = (double *)fftw_malloc(m_size * sizeof(double)); #else if (load) loadWisdom('f'); - m_fbuf = (float *)fftwf_malloc(m_size * sizeof(float)); #endif + m_fbuf = (fft_float_type *)fftw_malloc(m_size * sizeof(fft_float_type)); m_fpacked = (fftwf_complex *)fftw_malloc ((m_size/2 + 1) * sizeof(fftwf_complex)); m_fplanf = fftwf_plan_dft_r2c_1d @@ -185,11 +215,10 @@ public: m_extantMutex.unlock(); #ifdef FFTW_FLOAT_ONLY if (load) loadWisdom('f'); - m_dbuf = (float *)fftwf_malloc(m_size * sizeof(float)); #else if (load) loadWisdom('d'); - m_dbuf = (double *)fftw_malloc(m_size * sizeof(double)); #endif + m_dbuf = (fft_double_type *)fftw_malloc(m_size * sizeof(fft_double_type)); m_dpacked = (fftw_complex *)fftw_malloc ((m_size/2 + 1) * sizeof(fftw_complex)); m_dplanf = fftw_plan_dft_r2c_1d @@ -252,175 +281,279 @@ public: fclose(f); } - void packFloat(float *re, float *im) { - for (unsigned int i = 0; i <= m_size/2; ++i) { - m_fpacked[i][0] = re[i]; - m_fpacked[i][1] = im[i]; + void packFloat(const float *R__ re, const float *R__ im) { + const int hs = m_size/2; + fftwf_complex *const R__ fpacked = m_fpacked; + for (int i = 0; i <= hs; ++i) { + fpacked[i][0] = re[i]; } + if (im) { + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = im[i]; + } + } else { + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = 0.f; + } + } } - void packDouble(double *re, double *im) { - for (unsigned int i = 0; i <= m_size/2; ++i) { - m_dpacked[i][0] = re[i]; - m_dpacked[i][1] = im[i]; + void packDouble(const double *R__ re, const double *R__ im) { + const int hs = m_size/2; + fftw_complex *const R__ dpacked = m_dpacked; + for (int i = 0; i <= hs; ++i) { + dpacked[i][0] = re[i]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = im[i]; + } + } else { + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = 0.0; + } } } - void unpackFloat(float *re, float *im) { - for (unsigned int i = 0; i <= m_size/2; ++i) { + void unpackFloat(float *R__ re, float *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { re[i] = m_fpacked[i][0]; - im[i] = m_fpacked[i][1]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + im[i] = m_fpacked[i][1]; + } } } - void unpackDouble(double *re, double *im) { - for (unsigned int i = 0; i <= m_size/2; ++i) { + void unpackDouble(double *R__ re, double *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { re[i] = m_dpacked[i][0]; - im[i] = m_dpacked[i][1]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + im[i] = m_dpacked[i][1]; + } } } - void forward(double *realIn, double *realOut, double *imagOut) { + void forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { if (!m_dplanf) initDouble(); + const int sz = m_size; + fft_double_type *const R__ dbuf = m_dbuf; #ifndef FFTW_FLOAT_ONLY - if (realIn != m_dbuf) + if (realIn != dbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_dbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; } fftw_execute(m_dplanf); unpackDouble(realOut, imagOut); } - void forwardPolar(double *realIn, double *magOut, double *phaseOut) { + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { if (!m_dplanf) initDouble(); + fft_double_type *const R__ dbuf = m_dbuf; + const int sz = m_size; #ifndef FFTW_FLOAT_ONLY - if (realIn != m_dbuf) + if (realIn != dbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_dbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; } fftw_execute(m_dplanf); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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 (unsigned int i = 0; i <= m_size/2; ++i) { + for (int i = 0; i <= hs; ++i) { phaseOut[i] = atan2(m_dpacked[i][1], m_dpacked[i][0]); } } - void forwardMagnitude(double *realIn, double *magOut) { + void forwardMagnitude(const double *R__ realIn, double *R__ magOut) { if (!m_dplanf) initDouble(); + fft_double_type *const R__ dbuf = m_dbuf; + const int sz = m_size; #ifndef FFTW_FLOAT_ONLY if (realIn != m_dbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_dbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; } fftw_execute(m_dplanf); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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]); } } - void forward(float *realIn, float *realOut, float *imagOut) { + void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { if (!m_fplanf) initFloat(); + fft_float_type *const R__ fbuf = m_fbuf; + const int sz = m_size; #ifndef FFTW_DOUBLE_ONLY - if (realIn != m_fbuf) + if (realIn != fbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_fbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; } fftwf_execute(m_fplanf); unpackFloat(realOut, imagOut); } - void forwardPolar(float *realIn, float *magOut, float *phaseOut) { + void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { if (!m_fplanf) initFloat(); + fft_float_type *const R__ fbuf = m_fbuf; + const int sz = m_size; #ifndef FFTW_DOUBLE_ONLY - if (realIn != m_fbuf) + if (realIn != fbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_fbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; } fftwf_execute(m_fplanf); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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 (unsigned int i = 0; i <= m_size/2; ++i) { - phaseOut[i] = atan2f(m_fpacked[i][1], m_fpacked[i][0]) ; + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2f(m_fpacked[i][1], m_fpacked[i][0]) ; } } - void forwardMagnitude(float *realIn, float *magOut) { + void forwardMagnitude(const float *R__ realIn, float *R__ magOut) { if (!m_fplanf) initFloat(); + fft_float_type *const R__ fbuf = m_fbuf; + const int sz = m_size; #ifndef FFTW_DOUBLE_ONLY - if (realIn != m_fbuf) + if (realIn != fbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_fbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; } fftwf_execute(m_fplanf); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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]); } } - void inverse(double *realIn, double *imagIn, double *realOut) { + void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { if (!m_dplanf) initDouble(); packDouble(realIn, imagIn); fftw_execute(m_dplani); + const int sz = m_size; + fft_double_type *const R__ dbuf = m_dbuf; +#ifndef FFTW_FLOAT_ONLY + if (realOut != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + realOut[i] = dbuf[i]; + } + } + + void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { + if (!m_dplanf) initDouble(); + const int hs = m_size/2; + fftw_complex *const R__ dpacked = m_dpacked; + for (int i = 0; i <= hs; ++i) { + dpacked[i][0] = magIn[i] * cos(phaseIn[i]); + } + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = magIn[i] * sin(phaseIn[i]); + } + fftw_execute(m_dplani); + const int sz = m_size; + fft_double_type *const R__ dbuf = m_dbuf; #ifndef FFTW_FLOAT_ONLY - if (realOut != m_dbuf) + if (realOut != dbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - realOut[i] = m_dbuf[i]; + for (int i = 0; i < sz; ++i) { + realOut[i] = dbuf[i]; } } - void inversePolar(double *magIn, double *phaseIn, double *realOut) { + void inverseCepstral(const double *R__ magIn, double *R__ cepOut) { if (!m_dplanf) initDouble(); - for (unsigned int i = 0; i <= m_size/2; ++i) { - m_dpacked[i][0] = magIn[i] * cos(phaseIn[i]); - m_dpacked[i][1] = magIn[i] * sin(phaseIn[i]); + fft_double_type *const R__ dbuf = m_dbuf; + fftw_complex *const R__ dpacked = m_dpacked; + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + dpacked[i][0] = log(magIn[i] + 0.000001); + } + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = 0.0; } fftw_execute(m_dplani); + const int sz = m_size; #ifndef FFTW_FLOAT_ONLY - if (realOut != m_dbuf) + if (cepOut != dbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - realOut[i] = m_dbuf[i]; + for (int i = 0; i < sz; ++i) { + cepOut[i] = dbuf[i]; } } - void inverse(float *realIn, float *imagIn, float *realOut) { + void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) { if (!m_fplanf) initFloat(); packFloat(realIn, imagIn); fftwf_execute(m_fplani); + const int sz = m_size; + fft_float_type *const R__ fbuf = m_fbuf; +#ifndef FFTW_DOUBLE_ONLY + if (realOut != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + realOut[i] = fbuf[i]; + } + } + + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) { + if (!m_fplanf) initFloat(); + const int hs = m_size/2; + fftwf_complex *const R__ fpacked = m_fpacked; + for (int i = 0; i <= hs; ++i) { + fpacked[i][0] = magIn[i] * cosf(phaseIn[i]); + } + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = magIn[i] * sinf(phaseIn[i]); + } + fftwf_execute(m_fplani); + const int sz = m_size; + fft_float_type *const R__ fbuf = m_fbuf; #ifndef FFTW_DOUBLE_ONLY - if (realOut != m_fbuf) + if (realOut != fbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - realOut[i] = m_fbuf[i]; + for (int i = 0; i < sz; ++i) { + realOut[i] = fbuf[i]; } } - void inversePolar(float *magIn, float *phaseIn, float *realOut) { + void inverseCepstral(const float *R__ magIn, float *R__ cepOut) { if (!m_fplanf) initFloat(); - for (unsigned int i = 0; i <= m_size/2; ++i) { - m_fpacked[i][0] = magIn[i] * cosf(phaseIn[i]); - m_fpacked[i][1] = magIn[i] * sinf(phaseIn[i]); + const int hs = m_size/2; + fftwf_complex *const R__ fpacked = m_fpacked; + for (int i = 0; i <= hs; ++i) { + fpacked[i][0] = logf(magIn[i] + 0.000001f); + } + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = 0.f; } fftwf_execute(m_fplani); + const int sz = m_size; + fft_float_type *const R__ fbuf = m_fbuf; #ifndef FFTW_DOUBLE_ONLY - if (realOut != m_fbuf) + if (cepOut != fbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - realOut[i] = m_fbuf[i]; + for (int i = 0; i < sz; ++i) { + cepOut[i] = fbuf[i]; } } @@ -462,27 +595,278 @@ private: #else double *m_dbuf; #endif - fftw_complex *m_dpacked; - unsigned int m_size; - static unsigned int m_extantf; - static unsigned int m_extantd; + fftw_complex * m_dpacked; + const int m_size; + static int m_extantf; + static int m_extantd; static Mutex m_extantMutex; }; -unsigned int +int D_FFTW::m_extantf = 0; -unsigned int +int D_FFTW::m_extantd = 0; Mutex D_FFTW::m_extantMutex; +#endif /* HAVE_FFTW3 */ + +#ifdef USE_KISSFFT + +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) + { +#ifdef FIXED_POINT +#error KISSFFT is not configured for float values +#endif + if (sizeof(kiss_fft_scalar) != sizeof(float)) { + std::cerr << "ERROR: KISSFFT is not configured for float values" + << std::endl; + } + + m_fbuf = new kiss_fft_scalar[m_size + 2]; + m_fpacked = new kiss_fft_cpx[m_size + 2]; + m_fplanf = kiss_fftr_alloc(m_size, 0, NULL, NULL); + m_fplani = kiss_fftr_alloc(m_size, 1, NULL, NULL); + } + + ~D_KISSFFT() { + kiss_fftr_free(m_fplanf); + kiss_fftr_free(m_fplani); + kiss_fft_cleanup(); + + delete[] m_fbuf; + delete[] m_fpacked; + + if (m_frb) delete[] m_frb; + if (m_drb) delete[] m_drb; + } + + void initFloat() { } + void initDouble() { } + + void packFloat(const float *R__ re, const float *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = re[i]; + m_fpacked[i].i = im[i]; + } + } + + void unpackFloat(float *R__ re, float *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + re[i] = m_fpacked[i].r; + im[i] = m_fpacked[i].i; + } + } + + void packDouble(const double *R__ re, const double *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = float(re[i]); + m_fpacked[i].i = float(im[i]); + } + } + + void unpackDouble(double *R__ re, double *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + re[i] = double(m_fpacked[i].r); + im[i] = double(m_fpacked[i].i); + } + } + + void forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { + + for (int i = 0; i < m_size; ++i) { + m_fbuf[i] = float(realIn[i]); + } + + kiss_fftr(m_fplanf, m_fbuf, m_fpacked); + unpackDouble(realOut, imagOut); + } + + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { + + for (int i = 0; i < m_size; ++i) { + m_fbuf[i] = float(realIn[i]); + } + + kiss_fftr(m_fplanf, m_fbuf, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(double(m_fpacked[i].r) * double(m_fpacked[i].r) + + double(m_fpacked[i].i) * double(m_fpacked[i].i)); + } + + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2(double(m_fpacked[i].i), double(m_fpacked[i].r)); + } + } + + void forwardMagnitude(const double *R__ realIn, double *R__ magOut) { + + for (int i = 0; i < m_size; ++i) { + m_fbuf[i] = float(realIn[i]); + } + + kiss_fftr(m_fplanf, m_fbuf, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(double(m_fpacked[i].r) * double(m_fpacked[i].r) + + double(m_fpacked[i].i) * double(m_fpacked[i].i)); + } + } + + void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { + + kiss_fftr(m_fplanf, realIn, m_fpacked); + unpackFloat(realOut, imagOut); + } + + void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { + + kiss_fftr(m_fplanf, realIn, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrtf(m_fpacked[i].r * m_fpacked[i].r + + m_fpacked[i].i * m_fpacked[i].i); + } + + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2f(m_fpacked[i].i, m_fpacked[i].r); + } + } + + void forwardMagnitude(const float *R__ realIn, float *R__ magOut) { + + kiss_fftr(m_fplanf, realIn, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrtf(m_fpacked[i].r * m_fpacked[i].r + + m_fpacked[i].i * m_fpacked[i].i); + } + } + + void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { + + packDouble(realIn, imagIn); + + kiss_fftri(m_fplani, m_fpacked, m_fbuf); + + for (int i = 0; i < m_size; ++i) { + realOut[i] = m_fbuf[i]; + } + } + + void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = float(magIn[i] * cos(phaseIn[i])); + m_fpacked[i].i = float(magIn[i] * sin(phaseIn[i])); + } + + kiss_fftri(m_fplani, m_fpacked, m_fbuf); + + for (int i = 0; i < m_size; ++i) { + realOut[i] = m_fbuf[i]; + } + } + + void inverseCepstral(const double *R__ magIn, double *R__ cepOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = float(log(magIn[i] + 0.000001)); + m_fpacked[i].i = 0.0f; + } + + kiss_fftri(m_fplani, m_fpacked, m_fbuf); + + for (int i = 0; i < m_size; ++i) { + cepOut[i] = m_fbuf[i]; + } + } + + void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) { + + packFloat(realIn, imagIn); + kiss_fftri(m_fplani, m_fpacked, realOut); + } + + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = magIn[i] * cosf(phaseIn[i]); + m_fpacked[i].i = magIn[i] * sinf(phaseIn[i]); + } + + kiss_fftri(m_fplani, m_fpacked, realOut); + } + + void inverseCepstral(const float *R__ magIn, float *R__ cepOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = logf(magIn[i] + 0.000001f); + m_fpacked[i].i = 0.0f; + } + + 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; + kiss_fft_cpx *m_fpacked; +}; + +#endif /* USE_KISSFFT */ + +#ifdef USE_BUILTIN_FFT class D_Cross : public FFTImpl { public: - D_Cross(unsigned 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_frb(0), m_drb(0) { m_a = new double[size]; m_b = new double[size]; @@ -491,8 +875,8 @@ public: m_table = new int[m_size]; - unsigned int bits; - unsigned int i, j, k, m; + int bits; + int i, j, k, m; for (i = 0; ; ++i) { if (m_size & (1 << i)) { @@ -527,53 +911,64 @@ public: void initFloat() { } void initDouble() { } - void forward(double *realIn, double *realOut, double *imagOut) { + void forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { basefft(false, realIn, 0, m_c, m_d); - for (size_t i = 0; i <= m_size/2; ++i) realOut[i] = m_c[i]; - for (size_t i = 0; i <= m_size/2; ++i) imagOut[i] = m_d[i]; + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) realOut[i] = m_c[i]; + if (imagOut) { + for (int i = 0; i <= hs; ++i) imagOut[i] = m_d[i]; + } } - void forwardPolar(double *realIn, double *magOut, double *phaseOut) { + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { basefft(false, realIn, 0, m_c, m_d); - for (unsigned int i = 0; i <= m_size/2; ++i) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]); phaseOut[i] = atan2(m_d[i], m_c[i]) ; } } - void forwardMagnitude(double *realIn, double *magOut) { + void forwardMagnitude(const double *R__ realIn, double *R__ magOut) { basefft(false, realIn, 0, m_c, m_d); - for (unsigned int i = 0; i <= m_size/2; ++i) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]); } } - void forward(float *realIn, float *realOut, float *imagOut) { - for (size_t i = 0; i < m_size; ++i) m_a[i] = realIn[i]; + void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { + for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i]; basefft(false, m_a, 0, m_c, m_d); - for (size_t i = 0; i <= m_size/2; ++i) realOut[i] = m_c[i]; - for (size_t i = 0; i <= m_size/2; ++i) imagOut[i] = m_d[i]; + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) realOut[i] = m_c[i]; + if (imagOut) { + for (int i = 0; i <= hs; ++i) imagOut[i] = m_d[i]; + } } - void forwardPolar(float *realIn, float *magOut, float *phaseOut) { - for (size_t i = 0; i < m_size; ++i) m_a[i] = realIn[i]; + void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { + for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i]; basefft(false, m_a, 0, m_c, m_d); - for (unsigned int i = 0; i <= m_size/2; ++i) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]); phaseOut[i] = atan2(m_d[i], m_c[i]) ; } } - void forwardMagnitude(float *realIn, float *magOut) { - for (size_t i = 0; i < m_size; ++i) m_a[i] = realIn[i]; + void forwardMagnitude(const float *R__ realIn, float *R__ magOut) { + for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i]; basefft(false, m_a, 0, m_c, m_d); - for (unsigned int i = 0; i <= m_size/2; ++i) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]); } } - void inverse(double *realIn, double *imagIn, double *realOut) { - for (unsigned int i = 0; i <= m_size/2; ++i) { + void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { double real = realIn[i]; double imag = imagIn[i]; m_a[i] = real; @@ -586,8 +981,9 @@ public: basefft(true, m_a, m_b, realOut, m_d); } - void inversePolar(double *magIn, double *phaseIn, double *realOut) { - for (unsigned int i = 0; i <= m_size/2; ++i) { + void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { double real = magIn[i] * cos(phaseIn[i]); double imag = magIn[i] * sin(phaseIn[i]); m_a[i] = real; @@ -600,8 +996,23 @@ public: basefft(true, m_a, m_b, realOut, m_d); } - void inverse(float *realIn, float *imagIn, float *realOut) { - for (unsigned int i = 0; i <= m_size/2; ++i) { + void inverseCepstral(const double *R__ magIn, double *R__ cepOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + double real = log(magIn[i] + 0.000001); + m_a[i] = real; + m_b[i] = 0.0; + if (i > 0) { + m_a[m_size-i] = real; + m_b[m_size-i] = 0.0; + } + } + basefft(true, m_a, m_b, cepOut, m_d); + } + + void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { float real = realIn[i]; float imag = imagIn[i]; m_a[i] = real; @@ -612,11 +1023,12 @@ public: } } basefft(true, m_a, m_b, m_c, m_d); - for (unsigned int i = 0; i < m_size; ++i) realOut[i] = m_c[i]; + for (int i = 0; i < m_size; ++i) realOut[i] = m_c[i]; } - void inversePolar(float *magIn, float *phaseIn, float *realOut) { - for (unsigned int i = 0; i <= m_size/2; ++i) { + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { float real = magIn[i] * cosf(phaseIn[i]); float imag = magIn[i] * sinf(phaseIn[i]); m_a[i] = real; @@ -627,7 +1039,22 @@ public: } } basefft(true, m_a, m_b, m_c, m_d); - for (unsigned int i = 0; i < m_size; ++i) realOut[i] = m_c[i]; + for (int i = 0; i < m_size; ++i) realOut[i] = m_c[i]; + } + + void inverseCepstral(const float *R__ magIn, float *R__ cepOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + float real = logf(magIn[i] + 0.000001); + m_a[i] = real; + m_b[i] = 0.0; + if (i > 0) { + m_a[m_size-i] = real; + m_b[m_size-i] = 0.0; + } + } + basefft(true, m_a, m_b, m_c, m_d); + for (int i = 0; i < m_size; ++i) cepOut[i] = m_c[i]; } float *getFloatTimeBuffer() { @@ -641,7 +1068,7 @@ public: } private: - unsigned int m_size; + const int m_size; int *m_table; float *m_frb; double *m_drb; @@ -649,32 +1076,36 @@ private: double *m_b; double *m_c; double *m_d; - void basefft(bool inverse, double *ri, double *ii, double *ro, double *io); + void basefft(bool inverse, const double *R__ ri, const double *R__ ii, double *R__ ro, double *R__ io); }; void -D_Cross::basefft(bool inverse, double *ri, double *ii, double *ro, double *io) +D_Cross::basefft(bool inverse, const double *R__ ri, const double *R__ ii, double *R__ ro, double *R__ io) { if (!ri || !ro || !io) return; - unsigned int i, j, k, m; - unsigned int blockSize, blockEnd; + int i, j, k, m; + int blockSize, blockEnd; double tr, ti; double angle = 2.0 * M_PI; if (inverse) angle = -angle; - const unsigned int n = m_size; + const int n = m_size; if (ii) { for (i = 0; i < n; ++i) { ro[m_table[i]] = ri[i]; + } + for (i = 0; i < n; ++i) { io[m_table[i]] = ii[i]; } } else { for (i = 0; i < n; ++i) { ro[m_table[i]] = ri[i]; + } + for (i = 0; i < n; ++i) { io[m_table[i]] = 0.0; } } @@ -738,34 +1169,87 @@ D_Cross::basefft(bool inverse, double *ri, double *ii, double *ro, double *io) */ } +#endif /* USE_BUILTIN_FFT */ + +} /* end namespace FFTs */ + int FFT::m_method = -1; -FFT::FFT(unsigned int size) +FFT::FFT(int size, int debugLevel) { - if (size < 2) throw InvalidSize; - if (size & (size-1)) throw InvalidSize; + if ((size < 2) || + (size & (size-1))) { + std::cerr << "FFT::FFT(" << size << "): power-of-two sizes only supported, minimum size 2" << std::endl; + throw InvalidSize; + } if (m_method == -1) { + m_method = 3; +#ifdef USE_KISSFFT + m_method = 2; +#endif +#ifdef HAVE_FFTW3 m_method = 1; +#endif } switch (m_method) { case 0: - d = new D_Cross(size); + std::cerr << "FFT::FFT(" << size << "): WARNING: Selected implemention not available" << std::endl; +#ifdef USE_BUILTIN_FFT + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif break; case 1: -// std::cerr << "FFT::FFT(" << size << "): using FFTW3 implementation" -// << std::endl; - d = new D_FFTW(size); +#ifdef HAVE_FFTW3 + if (debugLevel > 0) { + std::cerr << "FFT::FFT(" << size << "): using FFTW3 implementation" + << std::endl; + } + d = new FFTs::D_FFTW(size); +#else + std::cerr << "FFT::FFT(" << size << "): WARNING: Selected implemention not available" << std::endl; +#ifdef USE_BUILTIN_FFT + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif +#endif + break; + + case 2: +#ifdef USE_KISSFFT + if (debugLevel > 0) { + std::cerr << "FFT::FFT(" << size << "): using KISSFFT implementation" + << std::endl; + } + d = new FFTs::D_KISSFFT(size); +#else + std::cerr << "FFT::FFT(" << size << "): WARNING: Selected implemention not available" << std::endl; +#ifdef USE_BUILTIN_FFT + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif +#endif break; default: - std::cerr << "FFT::FFT(" << size << "): WARNING: using slow built-in implementation" - << std::endl; - d = new D_Cross(size); +#ifdef USE_BUILTIN_FFT + std::cerr << "FFT::FFT(" << size << "): WARNING: using slow built-in implementation" << std::endl; + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif break; } } @@ -776,66 +1260,78 @@ FFT::~FFT() } void -FFT::forward(double *realIn, double *realOut, double *imagOut) +FFT::forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { d->forward(realIn, realOut, imagOut); } void -FFT::forwardPolar(double *realIn, double *magOut, double *phaseOut) +FFT::forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { d->forwardPolar(realIn, magOut, phaseOut); } void -FFT::forwardMagnitude(double *realIn, double *magOut) +FFT::forwardMagnitude(const double *R__ realIn, double *R__ magOut) { d->forwardMagnitude(realIn, magOut); } void -FFT::forward(float *realIn, float *realOut, float *imagOut) +FFT::forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { d->forward(realIn, realOut, imagOut); } void -FFT::forwardPolar(float *realIn, float *magOut, float *phaseOut) +FFT::forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { d->forwardPolar(realIn, magOut, phaseOut); } void -FFT::forwardMagnitude(float *realIn, float *magOut) +FFT::forwardMagnitude(const float *R__ realIn, float *R__ magOut) { d->forwardMagnitude(realIn, magOut); } void -FFT::inverse(double *realIn, double *imagIn, double *realOut) +FFT::inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { d->inverse(realIn, imagIn, realOut); } void -FFT::inversePolar(double *magIn, double *phaseIn, double *realOut) +FFT::inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { d->inversePolar(magIn, phaseIn, realOut); } void -FFT::inverse(float *realIn, float *imagIn, float *realOut) +FFT::inverseCepstral(const double *R__ magIn, double *R__ cepOut) +{ + d->inverseCepstral(magIn, cepOut); +} + +void +FFT::inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) { d->inverse(realIn, imagIn, realOut); } void -FFT::inversePolar(float *magIn, float *phaseIn, float *realOut) +FFT::inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) { d->inversePolar(magIn, phaseIn, realOut); } void +FFT::inverseCepstral(const float *R__ magIn, float *R__ cepOut) +{ + d->inverseCepstral(magIn, cepOut); +} + +void FFT::initFloat() { d->initFloat(); diff --git a/libs/rubberband/src/FFT.h b/libs/rubberband/src/FFT.h index 65c185de98..b31d925d36 100644 --- a/libs/rubberband/src/FFT.h +++ b/libs/rubberband/src/FFT.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -15,6 +15,8 @@ #ifndef _RUBBERBAND_FFT_H_ #define _RUBBERBAND_FFT_H_ +#include "sysutils.h" + namespace RubberBand { class FFTImpl; @@ -36,22 +38,24 @@ class FFT public: enum Exception { InvalidSize }; - FFT(unsigned int size); // may throw InvalidSize + FFT(int size, int debugLevel = 0); // may throw InvalidSize ~FFT(); - void forward(double *realIn, double *realOut, double *imagOut); - void forwardPolar(double *realIn, double *magOut, double *phaseOut); - void forwardMagnitude(double *realIn, double *magOut); + void forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut); + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut); + void forwardMagnitude(const double *R__ realIn, double *R__ magOut); - void forward(float *realIn, float *realOut, float *imagOut); - void forwardPolar(float *realIn, float *magOut, float *phaseOut); - void forwardMagnitude(float *realIn, float *magOut); + void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut); + void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut); + void forwardMagnitude(const float *R__ realIn, float *R__ magOut); - void inverse(double *realIn, double *imagIn, double *realOut); - void inversePolar(double *magIn, double *phaseIn, double *realOut); + void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut); + void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut); + void inverseCepstral(const double *R__ magIn, double *R__ cepOut); - void inverse(float *realIn, float *imagIn, float *realOut); - void inversePolar(float *magIn, float *phaseIn, float *realOut); + void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut); + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut); + void inverseCepstral(const float *R__ magIn, float *R__ cepOut); // Calling one or both of these is optional -- if neither is // called, the first call to a forward or inverse method will call diff --git a/libs/rubberband/src/HighFrequencyAudioCurve.cpp b/libs/rubberband/src/HighFrequencyAudioCurve.cpp index 1bc439944d..987cf76a66 100644 --- a/libs/rubberband/src/HighFrequencyAudioCurve.cpp +++ b/libs/rubberband/src/HighFrequencyAudioCurve.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -38,12 +38,14 @@ HighFrequencyAudioCurve::setWindowSize(size_t newSize) } float -HighFrequencyAudioCurve::process(float *mag, size_t increment) +HighFrequencyAudioCurve::process(const float *R__ mag, size_t increment) { float result = 0.0; - for (size_t n = 0; n <= m_windowSize / 2; ++n) { - result += mag[n] * n; + const int sz = m_windowSize / 2; + + for (int n = 0; n <= sz; ++n) { + result = result + mag[n] * n; } return result; diff --git a/libs/rubberband/src/HighFrequencyAudioCurve.h b/libs/rubberband/src/HighFrequencyAudioCurve.h index e891afa930..d42513f9b7 100644 --- a/libs/rubberband/src/HighFrequencyAudioCurve.h +++ b/libs/rubberband/src/HighFrequencyAudioCurve.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -30,7 +30,7 @@ public: virtual void setWindowSize(size_t newSize); - virtual float process(float *mag, size_t increment); + virtual float process(const float *R__ mag, size_t increment); virtual void reset(); }; diff --git a/libs/rubberband/src/PercussiveAudioCurve.cpp b/libs/rubberband/src/PercussiveAudioCurve.cpp index 98c65086d5..f8925961f3 100644 --- a/libs/rubberband/src/PercussiveAudioCurve.cpp +++ b/libs/rubberband/src/PercussiveAudioCurve.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -14,15 +14,18 @@ #include "PercussiveAudioCurve.h" +#include "Profiler.h" + #include <cmath> + namespace RubberBand { PercussiveAudioCurve::PercussiveAudioCurve(size_t sampleRate, size_t windowSize) : AudioCurve(sampleRate, windowSize) { - m_prevMag = new double[m_windowSize/2 + 1]; + m_prevMag = new float[m_windowSize/2 + 1]; for (size_t i = 0; i <= m_windowSize/2; ++i) { m_prevMag[i] = 0.f; @@ -45,29 +48,60 @@ PercussiveAudioCurve::reset() void PercussiveAudioCurve::setWindowSize(size_t newSize) { - delete[] m_prevMag; m_windowSize = newSize; - - m_prevMag = new double[m_windowSize/2 + 1]; + + delete[] m_prevMag; + m_prevMag = new float[m_windowSize/2 + 1]; reset(); } float -PercussiveAudioCurve::process(float *mag, size_t increment) +PercussiveAudioCurve::process(const float *R__ mag, size_t increment) +{ + static float threshold = powf(10.f, 0.15f); // 3dB rise in square of magnitude + static float zeroThresh = powf(10.f, -8); + + size_t count = 0; + size_t nonZeroCount = 0; + + const int sz = m_windowSize / 2; + + for (int n = 1; n <= sz; ++n) { + bool above = ((mag[n] / m_prevMag[n]) >= threshold); + if (above) ++count; + if (mag[n] > zeroThresh) ++nonZeroCount; + } + + for (int n = 1; n <= sz; ++n) { + m_prevMag[n] = mag[n]; + } + + if (nonZeroCount == 0) return 0; + else return float(count) / float(nonZeroCount); +} + +float +PercussiveAudioCurve::process(const double *R__ mag, size_t increment) { - static float threshold = pow(10, 0.3); - static float zeroThresh = pow(10, -16); + Profiler profiler("PercussiveAudioCurve::process"); + + static double threshold = pow(10.0, 0.15); // 3dB rise in square of magnitude + static double zeroThresh = pow(10.0, -8); size_t count = 0; size_t nonZeroCount = 0; - for (size_t n = 1; n <= m_windowSize / 2; ++n) { - float sqrmag = mag[n] * mag[n]; - bool above = ((sqrmag / m_prevMag[n]) >= threshold); + const int sz = m_windowSize / 2; + + for (int n = 1; n <= sz; ++n) { + bool above = ((mag[n] / m_prevMag[n]) >= threshold); if (above) ++count; - if (sqrmag > zeroThresh) ++nonZeroCount; - m_prevMag[n] = sqrmag; + if (mag[n] > zeroThresh) ++nonZeroCount; + } + + for (int n = 1; n <= sz; ++n) { + m_prevMag[n] = mag[n]; } if (nonZeroCount == 0) return 0; diff --git a/libs/rubberband/src/PercussiveAudioCurve.h b/libs/rubberband/src/PercussiveAudioCurve.h index 1d23a5068d..29c4fb7fd9 100644 --- a/libs/rubberband/src/PercussiveAudioCurve.h +++ b/libs/rubberband/src/PercussiveAudioCurve.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -29,11 +29,12 @@ public: virtual void setWindowSize(size_t newSize); - virtual float process(float *mag, size_t increment); + virtual float process(const float *R__ mag, size_t increment); + virtual float process(const double *R__ mag, size_t increment); virtual void reset(); protected: - double *m_prevMag; + float *R__ m_prevMag; }; } diff --git a/libs/rubberband/src/Profiler.cpp b/libs/rubberband/src/Profiler.cpp new file mode 100644 index 0000000000..df148d48e3 --- /dev/null +++ b/libs/rubberband/src/Profiler.cpp @@ -0,0 +1,176 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band + An audio time-stretching and pitch-shifting library. + Copyright 2007-2008 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "Profiler.h" + +#include <algorithm> +#include <set> +#include <string> +#include <map> + +#include <cstdio> + +namespace RubberBand { + +#ifndef NO_TIMING + +Profiler::ProfileMap +Profiler::m_profiles; + +Profiler::WorstCallMap +Profiler::m_worstCalls; + +void +Profiler::add(const char *id, float ms) +{ + ProfileMap::iterator pmi = m_profiles.find(id); + if (pmi != m_profiles.end()) { + ++pmi->second.first; + pmi->second.second += ms; + } else { + m_profiles[id] = TimePair(1, ms); + } + + WorstCallMap::iterator wci = m_worstCalls.find(id); + if (wci != m_worstCalls.end()) { + if (ms > wci->second) wci->second = ms; + } else { + m_worstCalls[id] = ms; + } +} + +void +Profiler::dump() +{ +#ifdef PROFILE_CLOCKS + fprintf(stderr, "Profiling points [CPU time]:\n"); +#else + fprintf(stderr, "Profiling points [Wall time]:\n"); +#endif + + fprintf(stderr, "\nBy name:\n"); + + typedef std::set<const char *, std::less<std::string> > StringSet; + + StringSet profileNames; + for (ProfileMap::const_iterator i = m_profiles.begin(); + i != m_profiles.end(); ++i) { + profileNames.insert(i->first); + } + + for (StringSet::const_iterator i = profileNames.begin(); + i != profileNames.end(); ++i) { + + ProfileMap::const_iterator j = m_profiles.find(*i); + if (j == m_profiles.end()) continue; + + const TimePair &pp(j->second); + fprintf(stderr, "%s(%d):\n", *i, pp.first); + fprintf(stderr, "\tReal: \t%f ms \t[%f ms total]\n", + (pp.second / pp.first), + (pp.second)); + + WorstCallMap::const_iterator k = m_worstCalls.find(*i); + if (k == m_worstCalls.end()) continue; + + fprintf(stderr, "\tWorst:\t%f ms/call\n", k->second); + } + + typedef std::multimap<float, const char *> TimeRMap; + typedef std::multimap<int, const char *> IntRMap; + TimeRMap totmap, avgmap, worstmap; + IntRMap ncallmap; + + for (ProfileMap::const_iterator i = m_profiles.begin(); + i != m_profiles.end(); ++i) { + totmap.insert(TimeRMap::value_type(i->second.second, i->first)); + avgmap.insert(TimeRMap::value_type(i->second.second / + i->second.first, i->first)); + ncallmap.insert(IntRMap::value_type(i->second.first, i->first)); + } + + for (WorstCallMap::const_iterator i = m_worstCalls.begin(); + i != m_worstCalls.end(); ++i) { + worstmap.insert(TimeRMap::value_type(i->second, i->first)); + } + + fprintf(stderr, "\nBy total:\n"); + for (TimeRMap::const_iterator i = totmap.end(); i != totmap.begin(); ) { + --i; + fprintf(stderr, "%-40s %f ms\n", i->second, i->first); + } + + fprintf(stderr, "\nBy average:\n"); + for (TimeRMap::const_iterator i = avgmap.end(); i != avgmap.begin(); ) { + --i; + fprintf(stderr, "%-40s %f ms\n", i->second, i->first); + } + + fprintf(stderr, "\nBy worst case:\n"); + for (TimeRMap::const_iterator i = worstmap.end(); i != worstmap.begin(); ) { + --i; + fprintf(stderr, "%-40s %f ms\n", i->second, i->first); + } + + fprintf(stderr, "\nBy number of calls:\n"); + for (IntRMap::const_iterator i = ncallmap.end(); i != ncallmap.begin(); ) { + --i; + fprintf(stderr, "%-40s %d\n", i->second, i->first); + } +} + +Profiler::Profiler(const char* c) : + m_c(c), + m_ended(false) +{ +#ifdef PROFILE_CLOCKS + m_start = clock(); +#else + (void)gettimeofday(&m_start, 0); +#endif +} + +Profiler::~Profiler() +{ + if (!m_ended) end(); +} + +void +Profiler::end() +{ +#ifdef PROFILE_CLOCKS + clock_t end = clock(); + clock_t elapsed = end - m_start; + float ms = float((double(elapsed) / double(CLOCKS_PER_SEC)) * 1000.0); +#else + struct timeval tv; + (void)gettimeofday(&tv, 0); + + tv.tv_sec -= m_start.tv_sec; + if (tv.tv_usec < m_start.tv_usec) { + tv.tv_usec += 1000000; + tv.tv_sec -= 1; + } + tv.tv_usec -= m_start.tv_usec; + float ms = float((double(tv.tv_sec) + (double(tv.tv_usec) / 1000000.0)) * 1000.0); +#endif + + add(m_c, ms); + + m_ended = true; +} + +#endif + +} diff --git a/libs/rubberband/src/Profiler.h b/libs/rubberband/src/Profiler.h new file mode 100644 index 0000000000..616a553ecb --- /dev/null +++ b/libs/rubberband/src/Profiler.h @@ -0,0 +1,91 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band + An audio time-stretching and pitch-shifting library. + Copyright 2007-2008 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _PROFILER_H_ +#define _PROFILER_H_ + +#define NO_TIMING 1 + +//#define WANT_TIMING 1 +//#define PROFILE_CLOCKS 1 + +#ifdef NDEBUG +#ifndef WANT_TIMING +#define NO_TIMING 1 +#endif +#endif + +#ifndef NO_TIMING +#ifdef PROFILE_CLOCKS +#include <time.h> +#else +#include "sysutils.h" +#ifndef _WIN32 +#include <sys/time.h> +#endif +#endif +#endif + +#include <map> + +namespace RubberBand { + +#ifndef NO_TIMING + +class Profiler +{ +public: + Profiler(const char *name); + ~Profiler(); + + void end(); // same action as dtor + + static void dump(); + +protected: + const char* m_c; +#ifdef PROFILE_CLOCKS + clock_t m_start; +#else + struct timeval m_start; +#endif + bool m_showOnDestruct; + bool m_ended; + + typedef std::pair<int, float> TimePair; + typedef std::map<const char *, TimePair> ProfileMap; + typedef std::map<const char *, float> WorstCallMap; + static ProfileMap m_profiles; + static WorstCallMap m_worstCalls; + static void add(const char *, float); +}; + +#else + +class Profiler +{ +public: + Profiler(const char *) { } + ~Profiler() { } + + void update() const { } + void end() { } + static void dump() { } +}; + +#endif + +} + +#endif diff --git a/libs/rubberband/src/Resampler.cpp b/libs/rubberband/src/Resampler.cpp index 2196856abd..296537f085 100644 --- a/libs/rubberband/src/Resampler.cpp +++ b/libs/rubberband/src/Resampler.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -14,6 +14,8 @@ #include "Resampler.h" +#include "Profiler.h" + #include <cstdlib> #include <cmath> @@ -22,16 +24,40 @@ #include <samplerate.h> + + namespace RubberBand { -class Resampler::D +class ResamplerImpl +{ +public: + virtual ~ResamplerImpl() { } + + virtual int resample(const float *const R__ *const R__ in, + float *const R__ *const R__ out, + int incount, + float ratio, + bool final) = 0; + + virtual void reset() = 0; +}; + +namespace Resamplers { + + + +class D_SRC : public ResamplerImpl { public: - D(Quality quality, size_t channels, size_t maxBufferSize); - ~D(); + D_SRC(Resampler::Quality quality, int channels, int maxBufferSize, + int m_debugLevel); + ~D_SRC(); - size_t resample(float **in, float **out, - size_t incount, float ratio, bool final); + int resample(const float *const R__ *const R__ in, + float *const R__ *const R__ out, + int incount, + float ratio, + bool final); void reset(); @@ -39,40 +65,52 @@ protected: SRC_STATE *m_src; float *m_iin; float *m_iout; - size_t m_channels; - size_t m_iinsize; - size_t m_ioutsize; + float m_lastRatio; + int m_channels; + int m_iinsize; + int m_ioutsize; + int m_debugLevel; }; -Resampler::D::D(Quality quality, size_t channels, size_t maxBufferSize) : +D_SRC::D_SRC(Resampler::Quality quality, int channels, int maxBufferSize, + int debugLevel) : m_src(0), m_iin(0), m_iout(0), + m_lastRatio(1.f), m_channels(channels), m_iinsize(0), - m_ioutsize(0) + m_ioutsize(0), + m_debugLevel(debugLevel) { -// std::cerr << "Resampler::Resampler: using libsamplerate implementation" -// << std::endl; + if (m_debugLevel > 0) { + std::cerr << "Resampler::Resampler: using libsamplerate implementation" + << std::endl; + } int err = 0; - m_src = src_new(quality == Best ? SRC_SINC_BEST_QUALITY : - quality == Fastest ? SRC_LINEAR : + m_src = src_new(quality == Resampler::Best ? SRC_SINC_BEST_QUALITY : + quality == Resampler::Fastest ? SRC_LINEAR : SRC_SINC_FASTEST, channels, &err); - //!!! check err, throw + if (err) { + std::cerr << "Resampler::Resampler: failed to create libsamplerate resampler: " + << src_strerror(err) << std::endl; + throw Resampler::ImplementationError; //!!! of course, need to catch this! + } if (maxBufferSize > 0 && m_channels > 1) { - //!!! alignment? m_iinsize = maxBufferSize * m_channels; m_ioutsize = maxBufferSize * m_channels * 2; - m_iin = (float *)malloc(m_iinsize * sizeof(float)); - m_iout = (float *)malloc(m_ioutsize * sizeof(float)); + m_iin = allocFloat(m_iinsize); + m_iout = allocFloat(m_ioutsize); } + + reset(); } -Resampler::D::~D() +D_SRC::~D_SRC() { src_delete(m_src); if (m_iinsize > 0) { @@ -83,28 +121,29 @@ Resampler::D::~D() } } -size_t -Resampler::D::resample(float **in, float **out, size_t incount, float ratio, - bool final) +int +D_SRC::resample(const float *const R__ *const R__ in, + float *const R__ *const R__ out, + int incount, + float ratio, + bool final) { SRC_DATA data; - size_t outcount = lrintf(ceilf(incount * ratio)); + int outcount = lrintf(ceilf(incount * ratio)); if (m_channels == 1) { - data.data_in = *in; + data.data_in = const_cast<float *>(*in); //!!!??? data.data_out = *out; } else { if (incount * m_channels > m_iinsize) { - m_iinsize = incount * m_channels; - m_iin = (float *)realloc(m_iin, m_iinsize * sizeof(float)); + m_iin = allocFloat(m_iin, m_iinsize); } if (outcount * m_channels > m_ioutsize) { - m_ioutsize = outcount * m_channels; - m_iout = (float *)realloc(m_iout, m_ioutsize * sizeof(float)); + m_iout = allocFloat(m_iout, m_ioutsize); } - for (size_t i = 0; i < incount; ++i) { - for (size_t c = 0; c < m_channels; ++c) { + for (int i = 0; i < incount; ++i) { + for (int c = 0; c < m_channels; ++c) { m_iin[i * m_channels + c] = in[c][i]; } } @@ -120,51 +159,101 @@ Resampler::D::resample(float **in, float **out, size_t incount, float ratio, int err = 0; err = src_process(m_src, &data); - //!!! check err, respond appropriately + if (err) { + std::cerr << "Resampler::process: libsamplerate error: " + << src_strerror(err) << std::endl; + throw Resampler::ImplementationError; //!!! of course, need to catch this! + } if (m_channels > 1) { for (int i = 0; i < data.output_frames_gen; ++i) { - for (size_t c = 0; c < m_channels; ++c) { + for (int c = 0; c < m_channels; ++c) { out[c][i] = m_iout[i * m_channels + c]; } } } + m_lastRatio = ratio; + return data.output_frames_gen; } void -Resampler::D::reset() +D_SRC::reset() { src_reset(m_src); } -} // end namespace -namespace RubberBand { +} /* end namespace Resamplers */ -Resampler::Resampler(Quality quality, size_t channels, size_t maxBufferSize) +Resampler::Resampler(Resampler::Quality quality, int channels, + int maxBufferSize, int debugLevel) { - m_d = new D(quality, channels, maxBufferSize); + m_method = -1; + + switch (quality) { + + case Resampler::Best: + m_method = 1; + break; + + case Resampler::FastestTolerable: + m_method = 1; + break; + + case Resampler::Fastest: + m_method = 1; + break; + } + + if (m_method == -1) { + std::cerr << "Resampler::Resampler(" << quality << ", " << channels + << ", " << maxBufferSize << "): No implementation available!" + << std::endl; + abort(); + } + + switch (m_method) { + case 0: + std::cerr << "Resampler::Resampler(" << quality << ", " << channels + << ", " << maxBufferSize << "): No implementation available!" + << std::endl; + abort(); + break; + + case 1: + d = new Resamplers::D_SRC(quality, channels, maxBufferSize, debugLevel); + break; + + case 2: + std::cerr << "Resampler::Resampler(" << quality << ", " << channels + << ", " << maxBufferSize << "): No implementation available!" + << std::endl; + abort(); + break; + } } Resampler::~Resampler() { - delete m_d; + delete d; } -size_t -Resampler::resample(float **in, float **out, - size_t incount, float ratio, bool final) +int +Resampler::resample(const float *const R__ *const R__ in, + float *const R__ *const R__ out, + int incount, float ratio, bool final) { - return m_d->resample(in, out, incount, ratio, final); + Profiler profiler("Resampler::resample"); + return d->resample(in, out, incount, ratio, final); } void Resampler::reset() { - m_d->reset(); + d->reset(); } } diff --git a/libs/rubberband/src/Resampler.h b/libs/rubberband/src/Resampler.h index bc07c585da..3c4af40e8e 100644 --- a/libs/rubberband/src/Resampler.h +++ b/libs/rubberband/src/Resampler.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -17,12 +17,17 @@ #include <sys/types.h> +#include "sysutils.h" + namespace RubberBand { +class ResamplerImpl; + class Resampler { public: enum Quality { Best, FastestTolerable, Fastest }; + enum Exception { ImplementationError }; /** * Construct a resampler with the given quality level and channel @@ -30,17 +35,21 @@ public: * that may be passed to the resample function before the * resampler needs to reallocate its internal buffers. */ - Resampler(Quality quality, size_t channels, size_t maxBufferSize = 0); + Resampler(Quality quality, int channels, int maxBufferSize = 0, + int debugLevel = 0); ~Resampler(); - size_t resample(float **in, float **out, - size_t incount, float ratio, bool final = false); + int resample(const float *const R__ *const R__ in, + float *const R__ *const R__ out, + int incount, + float ratio, + bool final = false); void reset(); protected: - class D; - D *m_d; + ResamplerImpl *d; + int m_method; }; } diff --git a/libs/rubberband/src/RingBuffer.h b/libs/rubberband/src/RingBuffer.h index 60004e3fd8..07312169a6 100644 --- a/libs/rubberband/src/RingBuffer.h +++ b/libs/rubberband/src/RingBuffer.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -18,11 +18,15 @@ #include <cstring> #include <sys/types.h> +#include <cstring> + #ifndef _WIN32 #include <sys/mman.h> #endif #include "Scavenger.h" +#include "Profiler.h" + //#define DEBUG_RINGBUFFER 1 @@ -58,7 +62,7 @@ public: * power of two, this means n should ideally be some power of two * minus one. */ - RingBuffer(size_t n); + RingBuffer(int n); virtual ~RingBuffer(); @@ -66,7 +70,7 @@ public: * Return the total capacity of the ring buffer in samples. * (This is the argument n passed to the constructor.) */ - size_t getSize() const; + int getSize() const; /** * Resize the ring buffer. This also empties it; use resized() @@ -74,7 +78,7 @@ public: * new, larger buffer; the old buffer is scavenged after a seemly * delay. Should be called from the write thread. */ - void resize(size_t newSize); + void resize(int newSize); /** * Return a new ring buffer (allocated with "new" -- called must @@ -84,7 +88,7 @@ public: * or inconsistent. If this buffer's data will not fit in the new * size, the contents are undefined. */ - RingBuffer<T, N> *resized(size_t newSize, int R = 0) const; + RingBuffer<T, N> *resized(int newSize, int R = 0) const; /** * Lock the ring buffer into physical memory. Returns true @@ -102,19 +106,19 @@ public: * Return the amount of data available for reading by reader R, in * samples. */ - size_t getReadSpace(int R = 0) const; + int getReadSpace(int R = 0) const; /** * Return the amount of space available for writing, in samples. */ - size_t getWriteSpace() const; + int getWriteSpace() const; /** * Read n samples from the buffer, for reader R. If fewer than n * are available, the remainder will be zeroed out. Returns the * number of samples actually read. */ - size_t read(T *destination, size_t n, int R = 0); + int read(T *R__ destination, int n, int R = 0); /** * Read n samples from the buffer, for reader R, adding them to @@ -122,7 +126,7 @@ public: * will be left alone. Returns the number of samples actually * read. */ - size_t readAdding(T *destination, size_t n, int R = 0); + int readAdding(T *R__ destination, int n, int R = 0); /** * Read one sample from the buffer, for reader R. If no sample is @@ -140,7 +144,7 @@ public: * n are available, the remainder will be zeroed out. Returns the * number of samples actually read. */ - size_t peek(T *destination, size_t n, int R = 0) const; + int peek(T *R__ destination, int n, int R = 0) const; /** * Read one sample from the buffer, if available, without @@ -156,27 +160,27 @@ public: * samples). Returns the number of samples actually available for * discarding. */ - size_t skip(size_t n, int R = 0); + int skip(int n, int R = 0); /** * Write n samples to the buffer. If insufficient space is * available, not all samples may actually be written. Returns * the number of samples actually written. */ - size_t write(const T *source, size_t n); + int write(const T *source, int n); /** * Write n zero-value samples to the buffer. If insufficient * space is available, not all zeros may actually be written. * Returns the number of zeroes actually written. */ - size_t zero(size_t n); + int zero(int n); protected: - T *m_buffer; - volatile size_t m_writer; - volatile size_t m_readers[N]; - size_t m_size; + T *R__ m_buffer; + volatile int m_writer; + volatile int m_readers[N]; + int m_size; bool m_mlocked; static Scavenger<ScavengerArrayWrapper<T> > m_scavenger; @@ -190,7 +194,7 @@ template <typename T, int N> Scavenger<ScavengerArrayWrapper<T> > RingBuffer<T, N>::m_scavenger; template <typename T, int N> -RingBuffer<T, N>::RingBuffer(size_t n) : +RingBuffer<T, N>::RingBuffer(int n) : m_buffer(new T[n + 1]), m_writer(0), m_size(n + 1), @@ -221,7 +225,7 @@ RingBuffer<T, N>::~RingBuffer() } template <typename T, int N> -size_t +int RingBuffer<T, N>::getSize() const { #ifdef DEBUG_RINGBUFFER @@ -233,7 +237,7 @@ RingBuffer<T, N>::getSize() const template <typename T, int N> void -RingBuffer<T, N>::resize(size_t newSize) +RingBuffer<T, N>::resize(int newSize) { #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T," << N << ">[" << this << "]::resize(" << newSize << ")" << std::endl; @@ -260,12 +264,12 @@ RingBuffer<T, N>::resize(size_t newSize) template <typename T, int N> RingBuffer<T, N> * -RingBuffer<T, N>::resized(size_t newSize, int R) const +RingBuffer<T, N>::resized(int newSize, int R) const { RingBuffer<T, N> *newBuffer = new RingBuffer<T, N>(newSize); - size_t w = m_writer; - size_t r = m_readers[R]; + int w = m_writer; + int r = m_readers[R]; while (r != w) { T value = m_buffer[r]; @@ -298,12 +302,12 @@ RingBuffer<T, N>::reset() } template <typename T, int N> -size_t +int RingBuffer<T, N>::getReadSpace(int R) const { - size_t writer = m_writer; - size_t reader = m_readers[R]; - size_t space; + int writer = m_writer; + int reader = m_readers[R]; + int space; #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): reader " << reader << ", writer " << writer << std::endl; @@ -321,20 +325,20 @@ RingBuffer<T, N>::getReadSpace(int R) const } template <typename T, int N> -size_t +int RingBuffer<T, N>::getWriteSpace() const { - size_t space = 0; + int space = 0; for (int i = 0; i < N; ++i) { - size_t writer = m_writer; - size_t reader = m_readers[i]; - size_t here = (reader + m_size - writer - 1); + int writer = m_writer; + int reader = m_readers[i]; + int here = (reader + m_size - writer - 1); if (here >= m_size) here -= m_size; if (i == 0 || here < space) space = here; } #ifdef DEBUG_RINGBUFFER - size_t rs(getReadSpace()), rp(m_readers[0]); + int rs(getReadSpace()), rp(m_readers[0]); std::cerr << "RingBuffer: write space " << space << ", read space " << rs << ", total " << (space + rs) << ", m_size " << m_size << std::endl; @@ -349,39 +353,44 @@ RingBuffer<T, N>::getWriteSpace() const } template <typename T, int N> -size_t -RingBuffer<T, N>::read(T *destination, size_t n, int R) +int +RingBuffer<T, N>::read(T *R__ destination, int n, int R) { + Profiler profiler("RingBuffer::read"); + #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl; #endif - size_t available = getReadSpace(R); + int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only " << available << " samples available" << std::endl; #endif - for (size_t i = available; i < n; ++i) { + for (int i = available; i < n; ++i) { destination[i] = 0; } n = available; } if (n == 0) return n; - size_t reader = m_readers[R]; - size_t here = m_size - reader; + int reader = m_readers[R]; + int here = m_size - reader; + T *const R__ bufbase = m_buffer + reader; if (here >= n) { - for (size_t i = 0; i < n; ++i) { - destination[i] = (m_buffer + reader)[i]; + for (int i = 0; i < n; ++i) { + destination[i] = bufbase[i]; } } else { - for (size_t i = 0; i < here; ++i) { - destination[i] = (m_buffer + reader)[i]; + for (int i = 0; i < here; ++i) { + destination[i] = bufbase[i]; } - for (size_t i = 0; i < (n - here); ++i) { - destination[i + here] = m_buffer[i]; + T *const R__ destbase = destination + here; + const int nh = n - here; + for (int i = 0; i < nh; ++i) { + destbase[i] = m_buffer[i]; } } @@ -397,14 +406,16 @@ RingBuffer<T, N>::read(T *destination, size_t n, int R) } template <typename T, int N> -size_t -RingBuffer<T, N>::readAdding(T *destination, size_t n, int R) +int +RingBuffer<T, N>::readAdding(T *R__ destination, int n, int R) { + Profiler profiler("RingBuffer::readAdding"); + #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl; #endif - size_t available = getReadSpace(R); + int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only " << available << " samples available" @@ -414,19 +425,22 @@ RingBuffer<T, N>::readAdding(T *destination, size_t n, int R) } if (n == 0) return n; - size_t reader = m_readers[R]; - size_t here = m_size - reader; + int reader = m_readers[R]; + int here = m_size - reader; + const T *const R__ bufbase = m_buffer + reader; if (here >= n) { - for (size_t i = 0; i < n; ++i) { - destination[i] += (m_buffer + reader)[i]; + for (int i = 0; i < n; ++i) { + destination[i] += bufbase[i]; } } else { - for (size_t i = 0; i < here; ++i) { - destination[i] += (m_buffer + reader)[i]; + for (int i = 0; i < here; ++i) { + destination[i] += bufbase[i]; } - for (size_t i = 0; i < (n - here); ++i) { - destination[i + here] += m_buffer[i]; + T *const R__ destbase = destination + here; + const int nh = n - here; + for (int i = 0; i < nh; ++i) { + destbase[i] += m_buffer[i]; } } @@ -451,7 +465,7 @@ RingBuffer<T, N>::readOne(int R) #endif return 0; } - size_t reader = m_readers[R]; + int reader = m_readers[R]; T value = m_buffer[reader]; if (++reader == m_size) reader = 0; m_readers[R] = reader; @@ -459,14 +473,16 @@ RingBuffer<T, N>::readOne(int R) } template <typename T, int N> -size_t -RingBuffer<T, N>::peek(T *destination, size_t n, int R) const +int +RingBuffer<T, N>::peek(T *R__ destination, int n, int R) const { + Profiler profiler("RingBuffer::peek"); + #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl; #endif - size_t available = getReadSpace(R); + int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only " << available << " samples available" @@ -477,19 +493,22 @@ RingBuffer<T, N>::peek(T *destination, size_t n, int R) const } if (n == 0) return n; - size_t reader = m_readers[R]; - size_t here = m_size - reader; + int reader = m_readers[R]; + int here = m_size - reader; + const T *const R__ bufbase = m_buffer + reader; if (here >= n) { - for (size_t i = 0; i < n; ++i) { - destination[i] = (m_buffer + reader)[i]; + for (int i = 0; i < n; ++i) { + destination[i] = bufbase[i]; } } else { - for (size_t i = 0; i < here; ++i) { - destination[i] = (m_buffer + reader)[i]; + for (int i = 0; i < here; ++i) { + destination[i] = bufbase[i]; } - for (size_t i = 0; i < (n - here); ++i) { - destination[i + here] = m_buffer[i]; + T *const R__ destbase = destination + here; + const int nh = n - here; + for (int i = 0; i < nh; ++i) { + destbase[i] = m_buffer[i]; } } @@ -520,14 +539,14 @@ RingBuffer<T, N>::peekOne(int R) const } template <typename T, int N> -size_t -RingBuffer<T, N>::skip(size_t n, int R) +int +RingBuffer<T, N>::skip(int n, int R) { #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T," << N << ">[" << this << "]::skip(" << n << ", " << R << ")" << std::endl; #endif - size_t available = getReadSpace(R); + int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only " << available << " samples available" @@ -537,7 +556,7 @@ RingBuffer<T, N>::skip(size_t n, int R) } if (n == 0) return n; - size_t reader = m_readers[R]; + int reader = m_readers[R]; reader += n; while (reader >= m_size) reader -= m_size; m_readers[R] = reader; @@ -545,14 +564,16 @@ RingBuffer<T, N>::skip(size_t n, int R) } template <typename T, int N> -size_t -RingBuffer<T, N>::write(const T *source, size_t n) +int +RingBuffer<T, N>::write(const T *source, int n) { + Profiler profiler("RingBuffer::write"); + #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write(" << n << ")" << std::endl; #endif - size_t available = getWriteSpace(); + int available = getWriteSpace(); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only room for " << available << " samples" @@ -562,18 +583,23 @@ RingBuffer<T, N>::write(const T *source, size_t n) } if (n == 0) return n; - size_t writer = m_writer; - size_t here = m_size - writer; + int writer = m_writer; + int here = m_size - writer; + T *const R__ bufbase = m_buffer + writer; + if (here >= n) { - for (size_t i = 0; i < n; ++i) { - (m_buffer + writer)[i] = source[i]; + for (int i = 0; i < n; ++i) { + bufbase[i] = source[i]; } } else { - for (size_t i = 0; i < here; ++i) { - (m_buffer + writer)[i] = source[i]; + for (int i = 0; i < here; ++i) { + bufbase[i] = source[i]; } - for (size_t i = 0; i < (n - here); ++i) { - m_buffer[i] = (source + here)[i]; + const int nh = n - here; + const T *const R__ srcbase = source + here; + T *const R__ buf = m_buffer; + for (int i = 0; i < nh; ++i) { + buf[i] = srcbase[i]; } } @@ -589,14 +615,16 @@ RingBuffer<T, N>::write(const T *source, size_t n) } template <typename T, int N> -size_t -RingBuffer<T, N>::zero(size_t n) +int +RingBuffer<T, N>::zero(int n) { + Profiler profiler("RingBuffer::zero"); + #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T," << N << ">[" << this << "]::zero(" << n << ")" << std::endl; #endif - size_t available = getWriteSpace(); + int available = getWriteSpace(); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only room for " << available << " samples" @@ -606,17 +634,20 @@ RingBuffer<T, N>::zero(size_t n) } if (n == 0) return n; - size_t writer = m_writer; - size_t here = m_size - writer; + int writer = m_writer; + int here = m_size - writer; + T *const R__ bufbase = m_buffer + writer; + if (here >= n) { - for (size_t i = 0; i < n; ++i) { - (m_buffer + writer)[i] = 0; + for (int i = 0; i < n; ++i) { + bufbase[i] = 0; } } else { - for (size_t i = 0; i < here; ++i) { - (m_buffer + writer)[i] = 0; + for (int i = 0; i < here; ++i) { + bufbase[i] = 0; } - for (size_t i = 0; i < (n - here); ++i) { + const int nh = n - here; + for (int i = 0; i < nh; ++i) { m_buffer[i] = 0; } } @@ -634,4 +665,6 @@ RingBuffer<T, N>::zero(size_t n) } +//#include "RingBuffer.cpp" + #endif // _RINGBUFFER_H_ diff --git a/libs/rubberband/src/RubberBandStretcher.cpp b/libs/rubberband/src/RubberBandStretcher.cpp index 9a401b4e43..7e249c6633 100644 --- a/libs/rubberband/src/RubberBandStretcher.cpp +++ b/libs/rubberband/src/RubberBandStretcher.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -22,8 +22,7 @@ RubberBandStretcher::RubberBandStretcher(size_t sampleRate, Options options, double initialTimeRatio, double initialPitchScale) : - TimeStretcher(sampleRate, channels), - m_d(new Impl(this, sampleRate, channels, options, + m_d(new Impl(sampleRate, channels, options, initialTimeRatio, initialPitchScale)) { } @@ -82,6 +81,18 @@ RubberBandStretcher::setPhaseOption(Options options) } void +RubberBandStretcher::setFormantOption(Options options) +{ + m_d->setFormantOption(options); +} + +void +RubberBandStretcher::setPitchOption(Options options) +{ + m_d->setPitchOption(options); +} + +void RubberBandStretcher::setExpectedInputDuration(size_t samples) { m_d->setExpectedInputDuration(samples); diff --git a/libs/rubberband/src/Scavenger.h b/libs/rubberband/src/Scavenger.h index 54af5dab0c..d1b6ca9ffa 100644 --- a/libs/rubberband/src/Scavenger.h +++ b/libs/rubberband/src/Scavenger.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -17,9 +17,12 @@ #include <vector> #include <list> -#include <sys/time.h> #include <iostream> +#ifndef WIN32 +#include <sys/time.h> +#endif + #include "Thread.h" #include "sysutils.h" diff --git a/libs/rubberband/src/SilentAudioCurve.cpp b/libs/rubberband/src/SilentAudioCurve.cpp new file mode 100644 index 0000000000..b44564671c --- /dev/null +++ b/libs/rubberband/src/SilentAudioCurve.cpp @@ -0,0 +1,69 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band + An audio time-stretching and pitch-shifting library. + Copyright 2007-2008 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "SilentAudioCurve.h" + +#include <cmath> + +namespace RubberBand +{ + +SilentAudioCurve::SilentAudioCurve(size_t sampleRate, size_t windowSize) : + AudioCurve(sampleRate, windowSize) +{ +} + +SilentAudioCurve::~SilentAudioCurve() +{ +} + +void +SilentAudioCurve::reset() +{ +} + +void +SilentAudioCurve::setWindowSize(size_t newSize) +{ + m_windowSize = newSize; +} + +float +SilentAudioCurve::process(const float *R__ mag, size_t) +{ + const int hs = m_windowSize / 2; + static float threshold = powf(10.f, -6); + + for (int i = 0; i <= hs; ++i) { + if (mag[i] > threshold) return 0.f; + } + + return 1.f; +} + +float +SilentAudioCurve::process(const double *R__ mag, size_t) +{ + const int hs = m_windowSize / 2; + static double threshold = pow(10.0, -6); + + for (int i = 0; i <= hs; ++i) { + if (mag[i] > threshold) return 0.f; + } + + return 1.f; +} + +} + diff --git a/libs/rubberband/src/SilentAudioCurve.h b/libs/rubberband/src/SilentAudioCurve.h new file mode 100644 index 0000000000..ec7009a871 --- /dev/null +++ b/libs/rubberband/src/SilentAudioCurve.h @@ -0,0 +1,38 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band + An audio time-stretching and pitch-shifting library. + Copyright 2007-2008 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _SILENT_AUDIO_CURVE_H_ +#define _SILENT_AUDIO_CURVE_H_ + +#include "AudioCurve.h" + +namespace RubberBand +{ + +class SilentAudioCurve : public AudioCurve +{ +public: + SilentAudioCurve(size_t sampleRate, size_t windowSize); + virtual ~SilentAudioCurve(); + + virtual void setWindowSize(size_t newSize); + + virtual float process(const float *R__ mag, size_t increment); + virtual float process(const double *R__ mag, size_t increment); + virtual void reset(); +}; + +} + +#endif diff --git a/libs/rubberband/src/SpectralDifferenceAudioCurve.cpp b/libs/rubberband/src/SpectralDifferenceAudioCurve.cpp index fe26e3e357..0deec53c87 100644 --- a/libs/rubberband/src/SpectralDifferenceAudioCurve.cpp +++ b/libs/rubberband/src/SpectralDifferenceAudioCurve.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -20,7 +20,7 @@ namespace RubberBand SpectralDifferenceAudioCurve::SpectralDifferenceAudioCurve(size_t sampleRate, size_t windowSize) : AudioCurve(sampleRate, windowSize) { - m_prevMag = new double[m_windowSize/2 + 1]; + m_prevMag = new float[m_windowSize/2 + 1]; for (size_t i = 0; i <= m_windowSize/2; ++i) { m_prevMag[i] = 0.f; @@ -43,11 +43,16 @@ SpectralDifferenceAudioCurve::reset() void SpectralDifferenceAudioCurve::setWindowSize(size_t newSize) { + delete[] m_prevMag; m_windowSize = newSize; + + m_prevMag = new float[m_windowSize/2 + 1]; + + reset(); } float -SpectralDifferenceAudioCurve::process(float *mag, size_t increment) +SpectralDifferenceAudioCurve::process(const float *R__ mag, size_t increment) { float result = 0.0; diff --git a/libs/rubberband/src/SpectralDifferenceAudioCurve.h b/libs/rubberband/src/SpectralDifferenceAudioCurve.h index c6f4484d43..6ab0af9c02 100644 --- a/libs/rubberband/src/SpectralDifferenceAudioCurve.h +++ b/libs/rubberband/src/SpectralDifferenceAudioCurve.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -30,11 +30,11 @@ public: virtual void setWindowSize(size_t newSize); - virtual float process(float *mag, size_t increment); + virtual float process(const float *R__ mag, size_t increment); virtual void reset(); protected: - double *m_prevMag; + float *R__ m_prevMag; }; } diff --git a/libs/rubberband/src/StretchCalculator.cpp b/libs/rubberband/src/StretchCalculator.cpp index a4c8236a53..61dc611033 100644 --- a/libs/rubberband/src/StretchCalculator.cpp +++ b/libs/rubberband/src/StretchCalculator.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -20,6 +20,9 @@ #include <deque> #include <set> #include <cassert> +#include <algorithm> + +#include "sysutils.h" namespace RubberBand { @@ -163,9 +166,11 @@ StretchCalculator::calculate(double ratio, size_t inputDuration, int StretchCalculator::calculateSingle(double ratio, - size_t inputDurationSoFar, - float df) + float df, + size_t increment) { + if (increment == 0) increment = m_increment; + bool isTransient = false; // We want to ensure, as close as possible, that the phase reset @@ -177,10 +182,10 @@ StretchCalculator::calculateSingle(double ratio, // from the ratio directly. For the moment we're happy if it // works well in common situations. - float transientThreshold = 0.35; - if (ratio > 1) transientThreshold = 0.25; + float transientThreshold = 0.35f; + if (ratio > 1) transientThreshold = 0.25f; - if (m_useHardPeaks && df > m_prevDf * 1.1 && df > transientThreshold) { + if (m_useHardPeaks && df > m_prevDf * 1.1f && df > transientThreshold) { isTransient = true; } @@ -191,39 +196,41 @@ StretchCalculator::calculateSingle(double ratio, m_prevDf = df; + bool ratioChanged = (ratio != m_prevRatio); + m_prevRatio = ratio; + if (isTransient && m_transientAmnesty == 0) { if (m_debugLevel > 1) { - std::cerr << "StretchCalculator::calculateSingle: transient found at " - << inputDurationSoFar << std::endl; + std::cerr << "StretchCalculator::calculateSingle: transient" + << std::endl; } - m_divergence += m_increment - (m_increment * ratio); + m_divergence += increment - (increment * ratio); // as in offline mode, 0.05 sec approx min between transients m_transientAmnesty = - lrint(ceil(double(m_sampleRate) / (20 * double(m_increment)))); + lrint(ceil(double(m_sampleRate) / (20 * double(increment)))); - m_recovery = m_divergence / ((m_sampleRate / 10.0) / m_increment); - return -m_increment; + m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment); + return -int(increment); } - if (m_prevRatio != ratio) { - m_recovery = m_divergence / ((m_sampleRate / 10.0) / m_increment); - m_prevRatio = ratio; + if (ratioChanged) { + m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment); } if (m_transientAmnesty > 0) --m_transientAmnesty; - int incr = lrint(m_increment * ratio - m_recovery); + int incr = lrint(increment * ratio - m_recovery); if (m_debugLevel > 2 || (m_debugLevel > 1 && m_divergence != 0)) { std::cerr << "divergence = " << m_divergence << ", recovery = " << m_recovery << ", incr = " << incr << ", "; } - if (incr < lrint((m_increment * ratio) / 2)) { - incr = lrint((m_increment * ratio) / 2); - } else if (incr > lrint(m_increment * ratio * 2)) { - incr = lrint(m_increment * ratio * 2); + if (incr < lrint((increment * ratio) / 2)) { + incr = lrint((increment * ratio) / 2); + } else if (incr > lrint(increment * ratio * 2)) { + incr = lrint(increment * ratio * 2); } - double divdiff = (m_increment * ratio) - incr; + double divdiff = (increment * ratio) - incr; if (m_debugLevel > 2 || (m_debugLevel > 1 && m_divergence != 0)) { std::cerr << "divdiff = " << divdiff << std::endl; @@ -233,7 +240,7 @@ StretchCalculator::calculateSingle(double ratio, m_divergence -= divdiff; if ((prevDivergence < 0 && m_divergence > 0) || (prevDivergence > 0 && m_divergence < 0)) { - m_recovery = m_divergence / ((m_sampleRate / 10.0) / m_increment); + m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment); } return incr; diff --git a/libs/rubberband/src/StretchCalculator.h b/libs/rubberband/src/StretchCalculator.h index f6c3544b2d..e79c8e3c1e 100644 --- a/libs/rubberband/src/StretchCalculator.h +++ b/libs/rubberband/src/StretchCalculator.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -45,9 +45,12 @@ public: * phase-lock audio curve. State is retained between calls in the * StretchCalculator object; call reset() to reset it. This uses * a less sophisticated method than the offline calculate(). + * + * If increment is non-zero, use it for the input increment for + * this block in preference to m_increment. */ - virtual int calculateSingle(double ratio, size_t inputDurationSoFar, - float curveValue); + virtual int calculateSingle(double ratio, float curveValue, + size_t increment = 0); void setUseHardPeaks(bool use) { m_useHardPeaks = use; } diff --git a/libs/rubberband/src/StretcherChannelData.cpp b/libs/rubberband/src/StretcherChannelData.cpp index ecbb9a6b88..8378975cbd 100644 --- a/libs/rubberband/src/StretcherChannelData.cpp +++ b/libs/rubberband/src/StretcherChannelData.cpp @@ -1,22 +1,39 @@ /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ +/* + Rubber Band + An audio time-stretching and pitch-shifting library. + Copyright 2007-2008 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + #include "StretcherChannelData.h" #include "Resampler.h" + namespace RubberBand { - + RubberBandStretcher::Impl::ChannelData::ChannelData(size_t windowSize, - size_t outbufSize) + int overSample, + size_t outbufSize) : + oversample(overSample) { std::set<size_t> s; construct(s, windowSize, outbufSize); } RubberBandStretcher::Impl::ChannelData::ChannelData(const std::set<size_t> &windowSizes, + int overSample, size_t initialWindowSize, - size_t outbufSize) + size_t outbufSize) : + oversample(overSample) { construct(windowSizes, initialWindowSize, outbufSize); } @@ -37,7 +54,8 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set<size_t> &window if (initialWindowSize > maxSize) maxSize = initialWindowSize; } - size_t realSize = maxSize/2 + 1; // size of the real "half" of freq data + // max size of the real "half" of freq data + size_t realSize = (maxSize * oversample)/2 + 1; // std::cerr << "ChannelData::construct([" << windowSizes.size() << "], " << maxSize << ", " << outbufSize << ")" << std::endl; @@ -46,24 +64,27 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set<size_t> &window inbuf = new RingBuffer<float>(maxSize); outbuf = new RingBuffer<float>(outbufSize); - mag = new double[realSize]; - phase = new double[realSize]; - prevPhase = new double[realSize]; - unwrappedPhase = new double[realSize]; + mag = allocDouble(realSize); + phase = allocDouble(realSize); + prevPhase = allocDouble(realSize); + prevError = allocDouble(realSize); + unwrappedPhase = allocDouble(realSize); + envelope = allocDouble(realSize); + freqPeak = new size_t[realSize]; - accumulator = new float[maxSize]; - windowAccumulator = new float[maxSize]; + fltbuf = allocFloat(maxSize); - fltbuf = new float[maxSize]; + accumulator = allocFloat(maxSize); + windowAccumulator = allocFloat(maxSize); for (std::set<size_t>::const_iterator i = windowSizes.begin(); i != windowSizes.end(); ++i) { - ffts[*i] = new FFT(*i); + ffts[*i] = new FFT(*i * oversample); ffts[*i]->initDouble(); } if (windowSizes.find(initialWindowSize) == windowSizes.end()) { - ffts[initialWindowSize] = new FFT(initialWindowSize); + ffts[initialWindowSize] = new FFT(initialWindowSize * oversample); ffts[initialWindowSize]->initDouble(); } fft = ffts[initialWindowSize]; @@ -77,29 +98,19 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set<size_t> &window reset(); for (size_t i = 0; i < realSize; ++i) { - mag[i] = 0.0; - phase[i] = 0.0; - prevPhase[i] = 0.0; - unwrappedPhase[i] = 0.0; freqPeak[i] = 0; } - for (size_t i = 0; i < initialWindowSize; ++i) { + for (size_t i = 0; i < initialWindowSize * oversample; ++i) { dblbuf[i] = 0.0; } - - for (size_t i = 0; i < maxSize; ++i) { - accumulator[i] = 0.f; - windowAccumulator[i] = 0.f; - fltbuf[i] = 0.0; - } } void RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) { size_t oldSize = inbuf->getSize(); - size_t realSize = windowSize/2 + 1; + size_t realSize = (windowSize * oversample) / 2 + 1; // std::cerr << "ChannelData::setWindowSize(" << windowSize << ") [from " << oldSize << "]" << std::endl; @@ -114,7 +125,7 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) if (ffts.find(windowSize) == ffts.end()) { //!!! this also requires a lock, but it shouldn't occur in //RT mode with proper initialisation - ffts[windowSize] = new FFT(windowSize); + ffts[windowSize] = new FFT(windowSize * oversample); ffts[windowSize]->initDouble(); } @@ -122,7 +133,7 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) dblbuf = fft->getDoubleTimeBuffer(); - for (size_t i = 0; i < windowSize; ++i) { + for (size_t i = 0; i < windowSize * oversample; ++i) { dblbuf[i] = 0.0; } @@ -130,6 +141,7 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) mag[i] = 0.0; phase[i] = 0.0; prevPhase[i] = 0.0; + prevError[i] = 0.0; unwrappedPhase[i] = 0.0; freqPeak[i] = 0; } @@ -150,54 +162,46 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) // We don't want to preserve data in these arrays - delete[] mag; - delete[] phase; - delete[] prevPhase; - delete[] unwrappedPhase; - delete[] freqPeak; + mag = allocDouble(mag, realSize); + phase = allocDouble(phase, realSize); + prevPhase = allocDouble(prevPhase, realSize); + prevError = allocDouble(prevError, realSize); + unwrappedPhase = allocDouble(unwrappedPhase, realSize); + envelope = allocDouble(envelope, realSize); - mag = new double[realSize]; - phase = new double[realSize]; - prevPhase = new double[realSize]; - unwrappedPhase = new double[realSize]; + delete[] freqPeak; freqPeak = new size_t[realSize]; - delete[] fltbuf; - fltbuf = new float[windowSize]; + fltbuf = allocFloat(fltbuf, windowSize); // But we do want to preserve data in these - float *newAcc = new float[windowSize]; + float *newAcc = allocFloat(windowSize); + for (size_t i = 0; i < oldSize; ++i) newAcc[i] = accumulator[i]; - delete[] accumulator; + + freeFloat(accumulator); accumulator = newAcc; - newAcc = new float[windowSize]; + newAcc = allocFloat(windowSize); + for (size_t i = 0; i < oldSize; ++i) newAcc[i] = windowAccumulator[i]; - delete[] windowAccumulator; + + freeFloat(windowAccumulator); windowAccumulator = newAcc; //!!! and resampler? for (size_t i = 0; i < realSize; ++i) { - mag[i] = 0.0; - phase[i] = 0.0; - prevPhase[i] = 0.0; - unwrappedPhase[i] = 0.0; freqPeak[i] = 0; } for (size_t i = 0; i < windowSize; ++i) { - fltbuf[i] = 0.0; - } - - for (size_t i = oldSize; i < windowSize; ++i) { - accumulator[i] = 0.f; - windowAccumulator[i] = 0.f; + fltbuf[i] = 0.f; } if (ffts.find(windowSize) == ffts.end()) { - ffts[windowSize] = new FFT(windowSize); + ffts[windowSize] = new FFT(windowSize * oversample); ffts[windowSize]->initDouble(); } @@ -205,7 +209,7 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) dblbuf = fft->getDoubleTimeBuffer(); - for (size_t i = 0; i < windowSize; ++i) { + for (size_t i = 0; i < windowSize * oversample; ++i) { dblbuf[i] = 0.0; } } @@ -228,21 +232,32 @@ RubberBandStretcher::Impl::ChannelData::setOutbufSize(size_t outbufSize) } } +void +RubberBandStretcher::Impl::ChannelData::setResampleBufSize(size_t sz) +{ + resamplebuf = allocFloat(resamplebuf, sz); + resamplebufSize = sz; +} + RubberBandStretcher::Impl::ChannelData::~ChannelData() { delete resampler; - delete[] resamplebuf; + + freeFloat(resamplebuf); delete inbuf; delete outbuf; - delete[] mag; - delete[] phase; - delete[] prevPhase; - delete[] unwrappedPhase; + + freeDouble(mag); + freeDouble(phase); + freeDouble(prevPhase); + freeDouble(prevError); + freeDouble(unwrappedPhase); + freeDouble(envelope); delete[] freqPeak; - delete[] accumulator; - delete[] windowAccumulator; - delete[] fltbuf; + freeFloat(accumulator); + freeFloat(windowAccumulator); + freeFloat(fltbuf); for (std::map<size_t, FFT *>::iterator i = ffts.begin(); i != ffts.end(); ++i) { @@ -264,6 +279,7 @@ RubberBandStretcher::Impl::ChannelData::reset() inCount = 0; inputSize = -1; outCount = 0; + unchanged = true; draining = false; outputComplete = false; } diff --git a/libs/rubberband/src/StretcherChannelData.h b/libs/rubberband/src/StretcherChannelData.h index ff110d14b6..b56a6e07dc 100644 --- a/libs/rubberband/src/StretcherChannelData.h +++ b/libs/rubberband/src/StretcherChannelData.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -19,6 +19,8 @@ #include <set> +//#define EXPERIMENT 1 + namespace RubberBand { @@ -39,7 +41,7 @@ public: * the pitch scale factor and any maximum processing block * size specified by the user of the code. */ - ChannelData(size_t windowSize, size_t outbufSize); + ChannelData(size_t windowSize, int overSample, size_t outbufSize); /** * Construct a ChannelData structure that can process at @@ -54,7 +56,7 @@ public: * called subsequently. */ ChannelData(const std::set<size_t> &windowSizes, - size_t initialWindowSize, size_t outbufSize); + int overSample, size_t initialWindowSize, size_t outbufSize); ~ChannelData(); /** @@ -76,6 +78,12 @@ public: */ void setOutbufSize(size_t outbufSize); + /** + * Set the resampler buffer size. Default if not called is no + * buffer allocated at all. + */ + void setResampleBufSize(size_t resamplebufSize); + RingBuffer<float> *inbuf; RingBuffer<float> *outbuf; @@ -83,8 +91,10 @@ public: double *phase; double *prevPhase; + double *prevError; double *unwrappedPhase; + size_t *freqPeak; float *accumulator; @@ -93,6 +103,8 @@ public: float *fltbuf; double *dblbuf; // owned by FFT object, only used for time domain FFT i/o + double *envelope; // for cepstral formant shift + bool unchanged; size_t prevIncrement; // only used in RT mode @@ -111,6 +123,8 @@ public: float *resamplebuf; size_t resamplebufSize; + int oversample; + private: void construct(const std::set<size_t> &windowSizes, size_t initialWindowSize, size_t outbufSize); diff --git a/libs/rubberband/src/StretcherImpl.cpp b/libs/rubberband/src/StretcherImpl.cpp index 30bc529bc8..126b001b83 100644 --- a/libs/rubberband/src/StretcherImpl.cpp +++ b/libs/rubberband/src/StretcherImpl.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -16,10 +16,12 @@ #include "PercussiveAudioCurve.h" #include "HighFrequencyAudioCurve.h" #include "SpectralDifferenceAudioCurve.h" +#include "SilentAudioCurve.h" #include "ConstantAudioCurve.h" #include "StretchCalculator.h" #include "StretcherChannelData.h" #include "Resampler.h" +#include "Profiler.h" #include <cassert> #include <cmath> @@ -34,6 +36,7 @@ using std::set; using std::max; using std::min; + namespace RubberBand { const size_t @@ -46,13 +49,13 @@ int RubberBandStretcher::Impl::m_defaultDebugLevel = 0; -RubberBandStretcher::Impl::Impl(RubberBandStretcher *stretcher, - size_t sampleRate, + +RubberBandStretcher::Impl::Impl(size_t sampleRate, size_t channels, Options options, double initialTimeRatio, double initialPitchScale) : - m_stretcher(stretcher), + m_sampleRate(sampleRate), m_channels(channels), m_timeRatio(initialTimeRatio), m_pitchScale(initialPitchScale), @@ -70,23 +73,26 @@ RubberBandStretcher::Impl::Impl(RubberBandStretcher *stretcher, m_studyFFT(0), m_spaceAvailable("space"), m_inputDuration(0), + m_silentHistory(0), m_lastProcessOutputIncrements(16), m_lastProcessPhaseResetDf(16), m_phaseResetAudioCurve(0), m_stretchAudioCurve(0), + m_silentAudioCurve(0), m_stretchCalculator(0), m_freq0(600), m_freq1(1200), m_freq2(12000), m_baseWindowSize(m_defaultWindowSize) { + if (m_debugLevel > 0) { - cerr << "RubberBandStretcher::Impl::Impl: rate = " << m_stretcher->m_sampleRate << ", options = " << options << endl; + cerr << "RubberBandStretcher::Impl::Impl: rate = " << m_sampleRate << ", options = " << options << endl; } // Window size will vary according to the audio sample rate, but // we don't let it drop below the 48k default - m_rateMultiple = float(m_stretcher->m_sampleRate) / 48000.f; + m_rateMultiple = float(m_sampleRate) / 48000.f; if (m_rateMultiple < 1.f) m_rateMultiple = 1.f; m_baseWindowSize = roundUp(int(m_defaultWindowSize * m_rateMultiple)); @@ -160,6 +166,7 @@ RubberBandStretcher::Impl::~Impl() delete m_phaseResetAudioCurve; delete m_stretchAudioCurve; + delete m_silentAudioCurve; delete m_stretchCalculator; delete m_studyFFT; @@ -193,7 +200,9 @@ RubberBandStretcher::Impl::reset() m_mode = JustCreated; if (m_phaseResetAudioCurve) m_phaseResetAudioCurve->reset(); if (m_stretchAudioCurve) m_stretchAudioCurve->reset(); + if (m_silentAudioCurve) m_silentAudioCurve->reset(); m_inputDuration = 0; + m_silentHistory = 0; if (m_threaded) m_threadSetMutex.unlock(); @@ -227,9 +236,25 @@ RubberBandStretcher::Impl::setPitchScale(double fs) } if (fs == m_pitchScale) return; + + bool was1 = (m_pitchScale == 1.f); + bool rbs = resampleBeforeStretching(); + m_pitchScale = fs; reconfigure(); + + if (!(m_options & OptionPitchHighConsistency) && + (was1 || resampleBeforeStretching() != rbs) && + m_pitchScale != 1.f) { + + // resampling mode has changed + for (int c = 0; c < int(m_channels); ++c) { + if (m_channelData[c]->resampler) { + m_channelData[c]->resampler->reset(); + } + } + } } double @@ -321,31 +346,62 @@ RubberBandStretcher::Impl::calculateSizes() if (m_realtime) { - // use a fixed input increment - - inputIncrement = roundUp(int(m_defaultIncrement * m_rateMultiple)); - if (r < 1) { + + bool rsb = (m_pitchScale < 1.0 && !resampleBeforeStretching()); + float windowIncrRatio = 4.5; + if (r == 1.0) windowIncrRatio = 4; + else if (rsb) windowIncrRatio = 4.5; + else windowIncrRatio = 6; + + inputIncrement = int(windowSize / windowIncrRatio); outputIncrement = int(floor(inputIncrement * r)); - if (outputIncrement < 1) { - outputIncrement = 1; - inputIncrement = roundUp(lrint(ceil(outputIncrement / r))); - windowSize = inputIncrement * 4; + + // Very long stretch or very low pitch shift + if (outputIncrement < m_defaultIncrement / 4) { + if (outputIncrement < 1) outputIncrement = 1; + while (outputIncrement < m_defaultIncrement / 4 && + windowSize < m_baseWindowSize * 4) { + outputIncrement *= 2; + inputIncrement = lrint(ceil(outputIncrement / r)); + windowSize = roundUp(lrint(ceil(inputIncrement * windowIncrRatio))); + } } + } else { - outputIncrement = int(ceil(inputIncrement * r)); - while (outputIncrement > 1024 && inputIncrement > 1) { - inputIncrement /= 2; - outputIncrement = lrint(ceil(inputIncrement * r)); + + bool rsb = (m_pitchScale > 1.0 && resampleBeforeStretching()); + float windowIncrRatio = 4.5; + if (r == 1.0) windowIncrRatio = 4; + else if (rsb) windowIncrRatio = 4.5; + else windowIncrRatio = 6; + + outputIncrement = int(windowSize / windowIncrRatio); + inputIncrement = int(outputIncrement / r); + while (outputIncrement > 1024 * m_rateMultiple && + inputIncrement > 1) { + outputIncrement /= 2; + inputIncrement = int(outputIncrement / r); + } + size_t minwin = roundUp(lrint(outputIncrement * windowIncrRatio)); + if (windowSize < minwin) windowSize = minwin; + + if (rsb) { +// cerr << "adjusting window size from " << windowSize; + size_t newWindowSize = roundUp(lrint(windowSize / m_pitchScale)); + if (newWindowSize < 512) newWindowSize = 512; + size_t div = windowSize / newWindowSize; + if (inputIncrement > div && outputIncrement > div) { + inputIncrement /= div; + outputIncrement /= div; + windowSize /= div; + } +// cerr << " to " << windowSize << " (inputIncrement = " << inputIncrement << ", outputIncrement = " << outputIncrement << ")" << endl; } - windowSize = std::max(windowSize, roundUp(outputIncrement * 6)); - if (r > 5) while (windowSize < 8192) windowSize *= 2; } } else { - // use a variable increment - if (r < 1) { inputIncrement = windowSize / 4; while (inputIncrement >= 512) inputIncrement /= 2; @@ -365,7 +421,7 @@ RubberBandStretcher::Impl::calculateSizes() windowSize = std::max(windowSize, roundUp(outputIncrement * 6)); if (r > 5) while (windowSize < 8192) windowSize *= 2; } - } + } if (m_expectedInputDuration > 0) { while (inputIncrement * 4 > m_expectedInputDuration && @@ -450,8 +506,9 @@ RubberBandStretcher::Impl::configure() set<size_t> windowSizes; if (m_realtime) { windowSizes.insert(m_baseWindowSize); + windowSizes.insert(m_baseWindowSize / 2); windowSizes.insert(m_baseWindowSize * 2); - windowSizes.insert(m_baseWindowSize * 4); +// windowSizes.insert(m_baseWindowSize * 4); } windowSizes.insert(m_windowSize); @@ -479,24 +536,27 @@ RubberBandStretcher::Impl::configure() for (size_t c = 0; c < m_channels; ++c) { m_channelData.push_back - (new ChannelData(windowSizes, m_windowSize, m_outbufSize)); + (new ChannelData(windowSizes, 1, m_windowSize, m_outbufSize)); } } if (!m_realtime && windowSizeChanged) { delete m_studyFFT; - m_studyFFT = new FFT(m_windowSize); + m_studyFFT = new FFT(m_windowSize, m_debugLevel); m_studyFFT->initFloat(); } - if (m_pitchScale != 1.0 || m_realtime) { + if (m_pitchScale != 1.0 || + (m_options & OptionPitchHighConsistency) || + m_realtime) { for (size_t c = 0; c < m_channels; ++c) { if (m_channelData[c]->resampler) continue; m_channelData[c]->resampler = - new Resampler(Resampler::FastestTolerable, 1, 4096 * 16); + new Resampler(Resampler::FastestTolerable, 1, 4096 * 16, + m_debugLevel); // rbs is the amount of buffer space we think we'll need // for resampling; but allocate a sensible amount in case @@ -504,32 +564,36 @@ RubberBandStretcher::Impl::configure() size_t rbs = lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale)); if (rbs < m_increment * 16) rbs = m_increment * 16; - m_channelData[c]->resamplebufSize = rbs; - m_channelData[c]->resamplebuf = new float[rbs]; + m_channelData[c]->setResampleBufSize(rbs); } } + // stretchAudioCurve is unused in RT mode; phaseResetAudioCurve, + // silentAudioCurve and stretchCalculator however are used in all + // modes + delete m_phaseResetAudioCurve; - m_phaseResetAudioCurve = new PercussiveAudioCurve(m_stretcher->m_sampleRate, - m_windowSize); + m_phaseResetAudioCurve = new PercussiveAudioCurve + (m_sampleRate, m_windowSize); - // stretchAudioCurve unused in RT mode; phaseResetAudioCurve and - // stretchCalculator however are used in all modes + delete m_silentAudioCurve; + m_silentAudioCurve = new SilentAudioCurve + (m_sampleRate, m_windowSize); if (!m_realtime) { delete m_stretchAudioCurve; if (!(m_options & OptionStretchPrecise)) { m_stretchAudioCurve = new SpectralDifferenceAudioCurve - (m_stretcher->m_sampleRate, m_windowSize); + (m_sampleRate, m_windowSize); } else { m_stretchAudioCurve = new ConstantAudioCurve - (m_stretcher->m_sampleRate, m_windowSize); + (m_sampleRate, m_windowSize); } } delete m_stretchCalculator; m_stretchCalculator = new StretchCalculator - (m_stretcher->m_sampleRate, m_increment, + (m_sampleRate, m_increment, !(m_options & OptionTransientsSmooth)); m_stretchCalculator->setDebugLevel(m_debugLevel); @@ -565,6 +629,7 @@ RubberBandStretcher::Impl::reconfigure() calculateStretch(); m_phaseResetDf.clear(); m_stretchDf.clear(); + m_silence.clear(); m_inputDuration = 0; } configure(); @@ -609,12 +674,11 @@ RubberBandStretcher::Impl::reconfigure() std::cerr << "WARNING: reconfigure(): resampler construction required in RT mode" << std::endl; m_channelData[c]->resampler = - new Resampler(Resampler::FastestTolerable, 1, m_windowSize); + new Resampler(Resampler::FastestTolerable, 1, m_windowSize, + m_debugLevel); - m_channelData[c]->resamplebufSize = - lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale)); - m_channelData[c]->resamplebuf = - new float[m_channelData[c]->resamplebufSize]; + m_channelData[c]->setResampleBufSize + (lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale))); } } @@ -637,9 +701,9 @@ RubberBandStretcher::Impl::setTransientsOption(Options options) cerr << "RubberBandStretcher::Impl::setTransientsOption: Not permissible in non-realtime mode" << endl; return; } - m_options &= ~(OptionTransientsMixed | - OptionTransientsSmooth | - OptionTransientsCrisp); + int mask = (OptionTransientsMixed | OptionTransientsSmooth | OptionTransientsCrisp); + m_options &= ~mask; + options &= mask; m_options |= options; m_stretchCalculator->setUseHardPeaks @@ -649,15 +713,46 @@ RubberBandStretcher::Impl::setTransientsOption(Options options) void RubberBandStretcher::Impl::setPhaseOption(Options options) { - m_options &= ~(OptionPhaseAdaptive | - OptionPhasePeakLocked | - OptionPhaseIndependent); + int mask = (OptionPhaseLaminar | OptionPhaseIndependent); + m_options &= ~mask; + options &= mask; m_options |= options; } void +RubberBandStretcher::Impl::setFormantOption(Options options) +{ + int mask = (OptionFormantShifted | OptionFormantPreserved); + m_options &= ~mask; + options &= mask; + m_options |= options; +} + +void +RubberBandStretcher::Impl::setPitchOption(Options options) +{ + if (!m_realtime) { + cerr << "RubberBandStretcher::Impl::setPitchOption: Pitch option is not used in non-RT mode" << endl; + return; + } + + Options prior = m_options; + + int mask = (OptionPitchHighQuality | + OptionPitchHighSpeed | + OptionPitchHighConsistency); + m_options &= ~mask; + options &= mask; + m_options |= options; + + if (prior != m_options) reconfigure(); +} + +void RubberBandStretcher::Impl::study(const float *const *input, size_t samples, bool final) { + Profiler profiler("RubberBandStretcher::Impl::study"); + if (m_realtime) { if (m_debugLevel > 1) { cerr << "RubberBandStretcher::Impl::study: Not meaningful in realtime mode" << endl; @@ -715,8 +810,8 @@ RubberBandStretcher::Impl::study(const float *const *input, size_t samples, bool consumed += writable; } - while ((inbuf.getReadSpace() >= m_windowSize) || - (final && (inbuf.getReadSpace() >= m_windowSize/2))) { + while ((inbuf.getReadSpace() >= int(m_windowSize)) || + (final && (inbuf.getReadSpace() >= int(m_windowSize/2)))) { // We know we have at least m_windowSize samples available // in m_inbuf. We need to peek m_windowSize of them for @@ -744,6 +839,13 @@ RubberBandStretcher::Impl::study(const float *const *input, size_t samples, bool df = m_stretchAudioCurve->process(cd.fltbuf, m_increment); m_stretchDf.push_back(df); + df = m_silentAudioCurve->process(cd.fltbuf, m_increment); + bool silent = (df > 0.f); + if (silent && m_debugLevel > 1) { + cerr << "silence found at " << m_inputDuration << endl; + } + m_silence.push_back(silent); + // cout << df << endl; // We have augmented the input by m_windowSize/2 so @@ -817,12 +919,28 @@ RubberBandStretcher::Impl::getExactTimePoints() const void RubberBandStretcher::Impl::calculateStretch() { + Profiler profiler("RubberBandStretcher::Impl::calculateStretch"); + std::vector<int> increments = m_stretchCalculator->calculate (getEffectiveRatio(), m_inputDuration, m_phaseResetDf, m_stretchDf); + int history = 0; + for (size_t i = 0; i < increments.size(); ++i) { + if (i >= m_silence.size()) break; + if (m_silence[i]) ++history; + else history = 0; + if (history >= int(m_windowSize / m_increment) && increments[i] >= 0) { + increments[i] = -increments[i]; + if (m_debugLevel > 1) { + std::cerr << "phase reset on silence (silent history == " + << history << ")" << std::endl; + } + } + } + if (m_outputIncrements.empty()) m_outputIncrements = increments; else { for (size_t i = 0; i < increments.size(); ++i) { @@ -843,6 +961,8 @@ RubberBandStretcher::Impl::setDebugLevel(int level) size_t RubberBandStretcher::Impl::getSamplesRequired() const { + Profiler profiler("RubberBandStretcher::Impl::getSamplesRequired"); + size_t reqd = 0; for (size_t c = 0; c < m_channels; ++c) { @@ -878,6 +998,8 @@ RubberBandStretcher::Impl::getSamplesRequired() const void RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bool final) { + Profiler profiler("RubberBandStretcher::Impl::process"); + if (m_mode == Finished) { cerr << "RubberBandStretcher::Impl::process: Cannot process again after final chunk" << endl; return; @@ -913,15 +1035,13 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo bool allConsumed = false; - map<size_t, size_t> consumed; + size_t *consumed = (size_t *)alloca(m_channels * sizeof(size_t)); for (size_t c = 0; c < m_channels; ++c) { consumed[c] = 0; } while (!allConsumed) { -// cerr << "process looping" << endl; - //#ifndef NO_THREADING // if (m_threaded) { // pthread_mutex_lock(&m_inputProcessedMutex); @@ -935,10 +1055,12 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo // have actually been processed. allConsumed = true; + for (size_t c = 0; c < m_channels; ++c) { consumed[c] += consumeChannel(c, input[c] + consumed[c], - samples - consumed[c]); + samples - consumed[c], + final); if (consumed[c] < samples) { allConsumed = false; // cerr << "process: waiting on input consumption for channel " << c << endl; @@ -981,6 +1103,9 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo } */ } + +// if (!allConsumed) cerr << "process looping" << endl; + } // cerr << "process returning" << endl; @@ -988,36 +1113,6 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo if (final) m_mode = Finished; } -size_t -RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input, size_t samples) -{ - size_t consumed = 0; - - ChannelData &cd = *m_channelData[c]; - RingBuffer<float> &inbuf = *cd.inbuf; - - while (consumed < samples) { - - size_t writable = inbuf.getWriteSpace(); - -// cerr << "channel " << c << ": samples remaining = " << samples - consumed << ", writable space = " << writable << endl; - - writable = min(writable, samples - consumed); - - if (writable == 0) { - // warn -// cerr << "WARNING: writable == 0 for ch " << c << " (consumed = " << consumed << ", samples = " << samples << ")" << endl; - return consumed; - } else { - inbuf.write(input + consumed, writable); - consumed += writable; - cd.inCount += writable; - } - } - - return samples; -} - } diff --git a/libs/rubberband/src/StretcherImpl.h b/libs/rubberband/src/StretcherImpl.h index 0dec4aa245..996c61b7ef 100644 --- a/libs/rubberband/src/StretcherImpl.h +++ b/libs/rubberband/src/StretcherImpl.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -34,8 +34,7 @@ class StretchCalculator; class RubberBandStretcher::Impl { public: - Impl(RubberBandStretcher *stretcher, - size_t sampleRate, size_t channels, Options options, + Impl(size_t sampleRate, size_t channels, Options options, double initialTimeRatio, double initialPitchScale); ~Impl(); @@ -50,6 +49,8 @@ public: void setTransientsOption(Options); void setPhaseOption(Options); + void setFormantOption(Options); + void setPitchOption(Options); void setExpectedInputDuration(size_t samples); void setMaxProcessSize(size_t samples); @@ -83,10 +84,11 @@ public: static void setDefaultDebugLevel(int level) { m_defaultDebugLevel = level; } protected: - RubberBandStretcher *m_stretcher; + size_t m_sampleRate; size_t m_channels; - size_t consumeChannel(size_t channel, const float *input, size_t samples); + size_t consumeChannel(size_t channel, const float *input, + size_t samples, bool final); void processChunks(size_t channel, bool &any, bool &last); bool processOneChunk(); // across all channels, for real time use bool processChunkForChannel(size_t channel, size_t phaseIncrement, @@ -98,6 +100,7 @@ protected: size_t &shiftIncrement, bool &phaseReset); void analyseChunk(size_t channel); void modifyChunk(size_t channel, size_t outputIncrement, bool phaseReset); + void formantShiftChunk(size_t channel); void synthesiseChunk(size_t channel); void writeChunk(size_t channel, size_t shiftIncrement, bool last); @@ -109,6 +112,8 @@ protected: size_t roundUp(size_t value); // to next power of two + bool resampleBeforeStretching() const; + double m_timeRatio; double m_pitchScale; @@ -161,6 +166,8 @@ protected: size_t m_inputDuration; std::vector<float> m_phaseResetDf; std::vector<float> m_stretchDf; + std::vector<bool> m_silence; + int m_silentHistory; class ChannelData; std::vector<ChannelData *> m_channelData; @@ -172,6 +179,7 @@ protected: AudioCurve *m_phaseResetAudioCurve; AudioCurve *m_stretchAudioCurve; + AudioCurve *m_silentAudioCurve; StretchCalculator *m_stretchCalculator; float m_freq0; diff --git a/libs/rubberband/src/StretcherProcess.cpp b/libs/rubberband/src/StretcherProcess.cpp index 3ea4bee53e..7c3f1e781f 100644 --- a/libs/rubberband/src/StretcherProcess.cpp +++ b/libs/rubberband/src/StretcherProcess.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -19,11 +19,14 @@ #include "StretchCalculator.h" #include "StretcherChannelData.h" #include "Resampler.h" +#include "Profiler.h" #include <cassert> #include <cmath> #include <set> #include <map> +#include <deque> + using std::cerr; using std::endl; @@ -97,9 +100,84 @@ RubberBandStretcher::Impl::ProcessThread::abandon() m_abandoning = true; } +bool +RubberBandStretcher::Impl::resampleBeforeStretching() const +{ + // We can't resample before stretching in offline mode, because + // the stretch calculation is based on doing it the other way + // around. It would take more work (and testing) to enable this. + if (!m_realtime) return false; + + if (m_options & OptionPitchHighQuality) { + return (m_pitchScale < 1.0); // better sound + } else if (m_options & OptionPitchHighConsistency) { + return false; + } else { + return (m_pitchScale > 1.0); // better performance + } +} + +size_t +RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input, + size_t samples, bool final) +{ + Profiler profiler("RubberBandStretcher::Impl::consumeChannel"); + + ChannelData &cd = *m_channelData[c]; + RingBuffer<float> &inbuf = *cd.inbuf; + + size_t toWrite = samples; + size_t writable = inbuf.getWriteSpace(); + + bool resampling = resampleBeforeStretching(); + + if (resampling) { + + toWrite = int(ceil(samples / m_pitchScale)); + if (writable < toWrite) { + samples = int(floor(writable * m_pitchScale)); + if (samples == 0) return 0; + } + + size_t reqSize = int(ceil(samples / m_pitchScale)); + if (reqSize > cd.resamplebufSize) { + cerr << "WARNING: RubberBandStretcher::Impl::consumeChannel: resizing resampler buffer from " + << cd.resamplebufSize << " to " << reqSize << endl; + cd.setResampleBufSize(reqSize); + } + + + toWrite = cd.resampler->resample(&input, + &cd.resamplebuf, + samples, + 1.0 / m_pitchScale, + final); + + } + + if (writable < toWrite) { + if (resampling) { + return 0; + } + toWrite = writable; + } + + if (resampling) { + inbuf.write(cd.resamplebuf, toWrite); + cd.inCount += samples; + return samples; + } else { + inbuf.write(input, toWrite); + cd.inCount += toWrite; + return toWrite; + } +} + void RubberBandStretcher::Impl::processChunks(size_t c, bool &any, bool &last) { + Profiler profiler("RubberBandStretcher::Impl::processChunks"); + // Process as many chunks as there are available on the input // buffer for channel c. This requires that the increments have // already been calculated. @@ -140,6 +218,8 @@ RubberBandStretcher::Impl::processChunks(size_t c, bool &any, bool &last) bool RubberBandStretcher::Impl::processOneChunk() { + Profiler profiler("RubberBandStretcher::Impl::processOneChunk"); + // Process a single chunk for all channels, provided there is // enough data on each channel for at least one chunk. This is // able to calculate increments as it goes along. @@ -173,6 +253,8 @@ RubberBandStretcher::Impl::processOneChunk() bool RubberBandStretcher::Impl::testInbufReadSpace(size_t c) { + Profiler profiler("RubberBandStretcher::Impl::testInbufReadSpace"); + ChannelData &cd = *m_channelData[c]; RingBuffer<float> &inbuf = *cd.inbuf; @@ -223,6 +305,8 @@ RubberBandStretcher::Impl::processChunkForChannel(size_t c, size_t shiftIncrement, bool phaseReset) { + Profiler profiler("RubberBandStretcher::Impl::processChunkForChannel"); + // Process a single chunk on a single channel. This assumes // enough input data is available; caller must have tested this // using e.g. testInbufReadSpace first. Return true if this is @@ -249,7 +333,7 @@ RubberBandStretcher::Impl::processChunkForChannel(size_t c, // We need to peek m_windowSize samples for processing, and // then skip m_increment to advance the read pointer. - + modifyChunk(c, phaseIncrement, phaseReset); synthesiseChunk(c); // reads from cd.mag, cd.phase @@ -284,7 +368,9 @@ RubberBandStretcher::Impl::processChunkForChannel(size_t c, } if (m_threaded) { - size_t required = shiftIncrement; + + int required = shiftIncrement; + if (m_pitchScale != 1.0) { required = int(required / m_pitchScale) + 1; } @@ -313,6 +399,8 @@ RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn, size_t &shiftIncrementRtn, bool &phaseReset) { + Profiler profiler("RubberBandStretcher::Impl::calculateIncrements"); + // cerr << "calculateIncrements" << endl; // Calculate the next upcoming phase and shift increment, on the @@ -342,6 +430,8 @@ RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn, } } + const int hs = m_windowSize/2 + 1; + // Normally we would mix down the time-domain signal and apply a // single FFT, or else mix down the Cartesian form of the // frequency-domain signal. Both of those would be inefficient @@ -352,22 +442,33 @@ RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn, // phases to cancel each other, and broadband effects will still // be apparent. - for (size_t i = 0; i <= m_windowSize/2; ++i) { - cd.fltbuf[i] = 0.0; - } + float df = 0.f; + bool silent = false; - for (size_t c = 0; c < m_channels; ++c) { - for (size_t i = 0; i <= m_windowSize/2; ++i) { - cd.fltbuf[i] += m_channelData[c]->mag[i]; + if (m_channels == 1) { + + df = m_phaseResetAudioCurve->process(cd.mag, m_increment); + silent = (m_silentAudioCurve->process(cd.mag, m_increment) > 0.f); + + } else { + + double *tmp = (double *)alloca(hs * sizeof(double)); + + for (int i = 0; i < hs; ++i) { + tmp[i] = 0.0; + } + for (size_t c = 0; c < m_channels; ++c) { + for (int i = 0; i < hs; ++i) { + tmp[i] += m_channelData[c]->mag[i]; + } } - } - float df = m_phaseResetAudioCurve->process(cd.fltbuf, m_increment); + df = m_phaseResetAudioCurve->process(tmp, m_increment); + silent = (m_silentAudioCurve->process(tmp, m_increment) > 0.f); + } int incr = m_stretchCalculator->calculateSingle - (getEffectiveRatio(), - m_inputDuration, //!!! no, totally wrong... fortunately it doesn't matter atm - df); + (getEffectiveRatio(), df, m_increment); m_lastProcessPhaseResetDf.write(&df, 1); m_lastProcessOutputIncrements.write(&incr, 1); @@ -399,6 +500,17 @@ RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn, } cd.prevIncrement = shiftIncrementRtn; + + if (silent) ++m_silentHistory; + else m_silentHistory = 0; + + if (m_silentHistory >= int(m_windowSize / m_increment) && !phaseReset) { + phaseReset = true; + if (m_debugLevel > 1) { + cerr << "calculateIncrements: phase reset on silence (silent history == " + << m_silentHistory << ")" << endl; + } + } } bool @@ -407,6 +519,8 @@ RubberBandStretcher::Impl::getIncrements(size_t channel, size_t &shiftIncrementRtn, bool &phaseReset) { + Profiler profiler("RubberBandStretcher::Impl::getIncrements"); + if (channel >= m_channels) { phaseIncrementRtn = m_increment; shiftIncrementRtn = m_increment; @@ -478,241 +592,366 @@ RubberBandStretcher::Impl::getIncrements(size_t channel, void RubberBandStretcher::Impl::analyseChunk(size_t channel) { - size_t i; + Profiler profiler("RubberBandStretcher::Impl::analyseChunk"); + + int i; ChannelData &cd = *m_channelData[channel]; + double *const R__ dblbuf = cd.dblbuf; + float *const R__ fltbuf = cd.fltbuf; + + int sz = m_windowSize; + int hs = m_windowSize/2; + // cd.fltbuf is known to contain m_windowSize samples - m_window->cut(cd.fltbuf); + m_window->cut(fltbuf); - for (i = 0; i < m_windowSize/2; ++i) { - cd.dblbuf[i] = cd.fltbuf[i + m_windowSize/2]; - cd.dblbuf[i + m_windowSize/2] = cd.fltbuf[i]; + if (cd.oversample > 1) { + + int bufsiz = sz * cd.oversample; + int offset = (bufsiz - sz) / 2; + + // eek + + for (i = 0; i < offset; ++i) { + dblbuf[i] = 0.0; + } + for (i = 0; i < offset; ++i) { + dblbuf[bufsiz - i - 1] = 0.0; + } + for (i = 0; i < sz; ++i) { + dblbuf[offset + i] = fltbuf[i]; + } + for (i = 0; i < bufsiz / 2; ++i) { + double tmp = dblbuf[i]; + dblbuf[i] = dblbuf[i + bufsiz/2]; + dblbuf[i + bufsiz/2] = tmp; + } + } else { + for (i = 0; i < hs; ++i) { + dblbuf[i] = fltbuf[i + hs]; + dblbuf[i + hs] = fltbuf[i]; + } } - cd.fft->forwardPolar(cd.dblbuf, cd.mag, cd.phase); + cd.fft->forwardPolar(dblbuf, cd.mag, cd.phase); } -double mod(double x, double y) { return x - (y * floor(x / y)); } -double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; } +static inline double mod(double x, double y) { return x - (y * floor(x / y)); } +static inline double princarg(double a) { return mod(a + M_PI, -2.0 * M_PI) + M_PI; } void -RubberBandStretcher::Impl::modifyChunk(size_t channel, size_t outputIncrement, +RubberBandStretcher::Impl::modifyChunk(size_t channel, + size_t outputIncrement, bool phaseReset) { + Profiler profiler("RubberBandStretcher::Impl::modifyChunk"); + ChannelData &cd = *m_channelData[channel]; if (phaseReset && m_debugLevel > 1) { cerr << "phase reset: leaving phases unmodified" << endl; } - size_t count = m_windowSize/2; - size_t pfp = 0; - double rate = m_stretcher->m_sampleRate; + const double rate = m_sampleRate; + const int sz = m_windowSize; + const int count = (sz * cd.oversample) / 2; + + bool unchanged = cd.unchanged && (outputIncrement == m_increment); + bool fullReset = phaseReset; + bool laminar = !(m_options & OptionPhaseIndependent); + bool bandlimited = (m_options & OptionTransientsMixed); + int bandlow = lrint((150 * sz * cd.oversample) / rate); + int bandhigh = lrint((1000 * sz * cd.oversample) / rate); + + float freq0 = m_freq0; + float freq1 = m_freq1; + float freq2 = m_freq2; + + if (laminar) { + float r = getEffectiveRatio(); + if (r > 1) { + float rf0 = 600 + (600 * ((r-1)*(r-1)*(r-1)*2)); + float f1ratio = freq1 / freq0; + float f2ratio = freq2 / freq0; + freq0 = std::max(freq0, rf0); + freq1 = freq0 * f1ratio; + freq2 = freq0 * f2ratio; + } + } + + int limit0 = lrint((freq0 * sz * cd.oversample) / rate); + int limit1 = lrint((freq1 * sz * cd.oversample) / rate); + int limit2 = lrint((freq2 * sz * cd.oversample) / rate); + + if (limit1 < limit0) limit1 = limit0; + if (limit2 < limit1) limit2 = limit1; + + double prevInstability = 0.0; + bool prevDirection = false; - if (!(m_options & OptionPhaseIndependent)) { + double distance = 0.0; + const double maxdist = 8.0; - cd.freqPeak[0] = 0; + const int lookback = 1; - float freq0 = m_freq0; - float freq1 = m_freq1; - float freq2 = m_freq2; + double distacc = 0.0; - // As the stretch ratio increases, so the frequency thresholds - // for phase lamination should increase. Beyond a ratio of - // about 1.5, the threshold should be about 1200Hz; beyond a - // ratio of 2, we probably want no lamination to happen at all - // by default. This calculation aims for more or less that. - // We only do this if the phase option is OptionPhaseAdaptive - // (the default), i.e. not Independent or PeakLocked. + for (int i = count; i >= 0; i -= lookback) { - if (!(m_options & OptionPhasePeakLocked)) { - float r = getEffectiveRatio(); - if (r > 1) { - float rf0 = 600 + (600 * ((r-1)*(r-1)*(r-1)*2)); - float f1ratio = freq1 / freq0; - float f2ratio = freq2 / freq0; - freq0 = std::max(freq0, rf0); - freq1 = freq0 * f1ratio; - freq2 = freq0 * f2ratio; + bool resetThis = phaseReset; + + if (bandlimited) { + if (resetThis) { + if (i > bandlow && i < bandhigh) { + resetThis = false; + fullReset = false; + } } } - size_t limit0 = lrint((freq0 * m_windowSize) / rate); - size_t limit1 = lrint((freq1 * m_windowSize) / rate); - size_t limit2 = lrint((freq2 * m_windowSize) / rate); + double p = cd.phase[i]; + double perr = 0.0; + double outphase = p; - size_t range = 0; + double mi = maxdist; + if (i <= limit0) mi = 0.0; + else if (i <= limit1) mi = 1.0; + else if (i <= limit2) mi = 3.0; - if (limit1 < limit0) limit1 = limit0; - if (limit2 < limit1) limit2 = limit1; - -// cerr << "limit0 = " << limit0 << " limit1 = " << limit1 << " limit2 = " << limit2 << endl; + if (!resetThis) { - int peakCount = 0; + double omega = (2 * M_PI * m_increment * i) / (sz * cd.oversample); - for (size_t i = 0; i <= count; ++i) { + double pp = cd.prevPhase[i]; + double ep = pp + omega; + perr = princarg(p - ep); - double mag = cd.mag[i]; - bool isPeak = true; + double instability = fabs(perr - cd.prevError[i]); + bool direction = (perr > cd.prevError[i]); - for (size_t j = 1; j <= range; ++j) { + bool inherit = false; - if (mag < cd.mag[i-j]) { - isPeak = false; - break; + if (laminar) { + if (distance >= mi) { + inherit = false; + } else if (bandlimited && (i == bandhigh || i == bandlow)) { + inherit = false; + } else if (instability > prevInstability && + direction == prevDirection) { + inherit = true; } + } - if (mag < cd.mag[i+j]) { - isPeak = false; - break; - } - } + double advance = outputIncrement * ((omega + perr) / m_increment); + + if (inherit) { + double inherited = + cd.unwrappedPhase[i + lookback] - cd.prevPhase[i + lookback]; + advance = ((advance * distance) + + (inherited * (maxdist - distance))) + / maxdist; + outphase = p + advance; + distacc += distance; + distance += 1.0; + } else { + outphase = cd.unwrappedPhase[i] + advance; + distance = 0.0; + } - if (isPeak) { + prevInstability = instability; + prevDirection = direction; - // i is a peak bin. + } else { + distance = 0.0; + } - // The previous peak bin was at pfp; make freqPeak entries - // from pfp to half-way between pfp and i point at pfp, and - // those from the half-way mark to i point at i. - - size_t halfway = (pfp + i) / 2; - if (halfway == pfp) halfway = pfp + 1; + cd.prevError[i] = perr; + cd.prevPhase[i] = p; + cd.phase[i] = outphase; + cd.unwrappedPhase[i] = outphase; + } - for (size_t j = pfp + 1; j < halfway; ++j) { - cd.freqPeak[j] = pfp; - } - for (size_t j = halfway; j <= i; ++j) { - cd.freqPeak[j] = i; - } + if (m_debugLevel > 1) { + cerr << "mean inheritance distance = " << distacc / count << endl; + } - pfp = i; + if (fullReset) unchanged = true; + cd.unchanged = unchanged; - ++peakCount; - } + if (unchanged && m_debugLevel > 1) { + cerr << "frame unchanged on channel " << channel << endl; + } +} - if (i == limit0) range = 1; - if (i == limit1) range = 2; - if (i >= limit2) { - range = 3; - if (i + range + 1 > count) range = count - i; - } - } -// cerr << "peakCount = " << peakCount << endl; - - cd.freqPeak[count-1] = count-1; - cd.freqPeak[count] = count; - } +void +RubberBandStretcher::Impl::formantShiftChunk(size_t channel) +{ + Profiler profiler("RubberBandStretcher::Impl::formantShiftChunk"); - double peakInPhase = 0.0; - double peakOutPhase = 0.0; - size_t p, pp; + ChannelData &cd = *m_channelData[channel]; - for (size_t i = 0; i <= count; ++i) { - - if (m_options & OptionPhaseIndependent) { - p = i; - pp = i-1; - } else { - p = cd.freqPeak[i]; - pp = cd.freqPeak[i-1]; - } + double *const R__ mag = cd.mag; + double *const R__ envelope = cd.envelope; + double *const R__ dblbuf = cd.dblbuf; - bool resetThis = phaseReset; - - if (m_options & OptionTransientsMixed) { - size_t low = lrint((150 * m_windowSize) / rate); - size_t high = lrint((1000 * m_windowSize) / rate); - if (resetThis) { - if (i > low && i < high) resetThis = false; - } - } + const int sz = m_windowSize; + const int hs = m_windowSize/2; + const double denom = sz; - if (!resetThis) { + + cd.fft->inverseCepstral(mag, dblbuf); - if (i == 0 || p != pp) { - - double omega = (2 * M_PI * m_increment * p) / m_windowSize; - double expectedPhase = cd.prevPhase[p] + omega; - double phaseError = princarg(cd.phase[p] - expectedPhase); - double phaseIncrement = (omega + phaseError) / m_increment; - - double unwrappedPhase = cd.unwrappedPhase[p] + - outputIncrement * phaseIncrement; + for (int i = 0; i < sz; ++i) { + dblbuf[i] /= denom; + } - cd.prevPhase[p] = cd.phase[p]; - cd.phase[p] = unwrappedPhase; - cd.unwrappedPhase[p] = unwrappedPhase; + const int cutoff = m_sampleRate / 700; - peakInPhase = cd.prevPhase[p]; - peakOutPhase = unwrappedPhase; - } +// cerr <<"cutoff = "<< cutoff << ", m_sampleRate/cutoff = " << m_sampleRate/cutoff << endl; - if (i != p) { + dblbuf[0] /= 2; + dblbuf[cutoff-1] /= 2; - double diffToPeak = peakInPhase - cd.phase[i]; - double unwrappedPhase = peakOutPhase - diffToPeak; - - cd.prevPhase[i] = cd.phase[i]; - cd.phase[i] = unwrappedPhase; - cd.unwrappedPhase[i] = unwrappedPhase; - } + for (int i = cutoff; i < sz; ++i) { + dblbuf[i] = 0.0; + } - } else { - cd.prevPhase[i] = cd.phase[i]; - cd.unwrappedPhase[i] = cd.phase[i]; + cd.fft->forward(dblbuf, envelope, 0); + + + for (int i = 0; i <= hs; ++i) { + envelope[i] = exp(envelope[i]); + } + for (int i = 0; i <= hs; ++i) { + mag[i] /= envelope[i]; + } + + if (m_pitchScale > 1.0) { + // scaling up, we want a new envelope that is lower by the pitch factor + for (int target = 0; target <= hs; ++target) { + int source = lrint(target * m_pitchScale); + if (source > int(m_windowSize)) { + envelope[target] = 0.0; + } else { + envelope[target] = envelope[source]; + } + } + } else { + // scaling down, we want a new envelope that is higher by the pitch factor + for (int target = hs; target > 0; ) { + --target; + int source = lrint(target * m_pitchScale); + envelope[target] = envelope[source]; } } + + for (int i = 0; i <= hs; ++i) { + mag[i] *= envelope[i]; + } + + cd.unchanged = false; } void RubberBandStretcher::Impl::synthesiseChunk(size_t channel) { - ChannelData &cd = *m_channelData[channel]; + Profiler profiler("RubberBandStretcher::Impl::synthesiseChunk"); - cd.fft->inversePolar(cd.mag, cd.phase, cd.dblbuf); - for (size_t i = 0; i < m_windowSize/2; ++i) { - cd.fltbuf[i] = cd.dblbuf[i + m_windowSize/2]; - cd.fltbuf[i + m_windowSize/2] = cd.dblbuf[i]; + if ((m_options & OptionFormantPreserved) && + (m_pitchScale != 1.0)) { + formantShiftChunk(channel); } - // our ffts produced unscaled results - for (size_t i = 0; i < m_windowSize; ++i) { - cd.fltbuf[i] = cd.fltbuf[i] / m_windowSize; + ChannelData &cd = *m_channelData[channel]; + + double *const R__ dblbuf = cd.dblbuf; + float *const R__ fltbuf = cd.fltbuf; + float *const R__ accumulator = cd.accumulator; + float *const R__ windowAccumulator = cd.windowAccumulator; + + int sz = m_windowSize; + int hs = m_windowSize/2; + int i; + + + if (!cd.unchanged) { + + cd.fft->inversePolar(cd.mag, cd.phase, cd.dblbuf); + + if (cd.oversample > 1) { + + int bufsiz = sz * cd.oversample; + int hbs = hs * cd.oversample; + int offset = (bufsiz - sz) / 2; + + for (i = 0; i < hbs; ++i) { + double tmp = dblbuf[i]; + dblbuf[i] = dblbuf[i + hbs]; + dblbuf[i + hbs] = tmp; + } + for (i = 0; i < sz; ++i) { + fltbuf[i] = float(dblbuf[i + offset]); + } + } else { + for (i = 0; i < hs; ++i) { + fltbuf[i] = float(dblbuf[i + hs]); + } + for (i = 0; i < hs; ++i) { + fltbuf[i + hs] = float(dblbuf[i]); + } + } + + float denom = float(sz * cd.oversample); + + // our ffts produced unscaled results + for (i = 0; i < sz; ++i) { + fltbuf[i] = fltbuf[i] / denom; + } } - m_window->cut(cd.fltbuf); + m_window->cut(fltbuf); - for (size_t i = 0; i < m_windowSize; ++i) { - cd.accumulator[i] += cd.fltbuf[i]; + for (i = 0; i < sz; ++i) { + accumulator[i] += fltbuf[i]; } cd.accumulatorFill = m_windowSize; - float fixed = m_window->getArea() * 1.5; + float fixed = m_window->getArea() * 1.5f; - for (size_t i = 0; i < m_windowSize; ++i) { + for (i = 0; i < sz; ++i) { float val = m_window->getValue(i); - cd.windowAccumulator[i] += val * fixed; + windowAccumulator[i] += val * fixed; } } void RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, bool last) { + Profiler profiler("RubberBandStretcher::Impl::writeChunk"); + ChannelData &cd = *m_channelData[channel]; + + float *const R__ accumulator = cd.accumulator; + float *const R__ windowAccumulator = cd.windowAccumulator; + + const int sz = m_windowSize; + const int si = shiftIncrement; + int i; + if (m_debugLevel > 2) { cerr << "writeChunk(" << channel << ", " << shiftIncrement << ", " << last << ")" << endl; } - for (unsigned int i = 0; i < shiftIncrement; ++i) { - if (cd.windowAccumulator[i] > 0.f) { - cd.accumulator[i] /= cd.windowAccumulator[i]; + for (i = 0; i < si; ++i) { + if (windowAccumulator[i] > 0.f) { + accumulator[i] /= windowAccumulator[i]; } } @@ -723,9 +962,13 @@ RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, boo theoreticalOut = lrint(cd.inputSize * m_timeRatio); } - if (m_pitchScale != 1.0 && cd.resampler) { + bool resampledAlready = resampleBeforeStretching(); + + if (!resampledAlready && + (m_pitchScale != 1.0 || m_options & OptionPitchHighConsistency) && + cd.resampler) { - size_t reqSize = int(ceil(shiftIncrement / m_pitchScale)); + size_t reqSize = int(ceil(si / m_pitchScale)); if (reqSize > cd.resamplebufSize) { // This shouldn't normally happen -- the buffer is // supposed to be initialised with enough space in the @@ -734,15 +977,13 @@ RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, boo // calculator has gone mad, or something. cerr << "WARNING: RubberBandStretcher::Impl::writeChunk: resizing resampler buffer from " << cd.resamplebufSize << " to " << reqSize << endl; - cd.resamplebufSize = reqSize; - if (cd.resamplebuf) delete[] cd.resamplebuf; - cd.resamplebuf = new float[cd.resamplebufSize]; + cd.setResampleBufSize(reqSize); } size_t outframes = cd.resampler->resample(&cd.accumulator, &cd.resamplebuf, - shiftIncrement, + si, 1.0 / m_pitchScale, last); @@ -751,28 +992,28 @@ RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, boo outframes, cd.outCount, theoreticalOut); } else { - writeOutput(*cd.outbuf, cd.accumulator, - shiftIncrement, cd.outCount, theoreticalOut); + writeOutput(*cd.outbuf, accumulator, + si, cd.outCount, theoreticalOut); } - for (size_t i = 0; i < m_windowSize - shiftIncrement; ++i) { - cd.accumulator[i] = cd.accumulator[i + shiftIncrement]; + for (i = 0; i < sz - si; ++i) { + accumulator[i] = accumulator[i + si]; } - for (size_t i = m_windowSize - shiftIncrement; i < m_windowSize; ++i) { - cd.accumulator[i] = 0.0f; + for (i = sz - si; i < sz; ++i) { + accumulator[i] = 0.0f; } - for (size_t i = 0; i < m_windowSize - shiftIncrement; ++i) { - cd.windowAccumulator[i] = cd.windowAccumulator[i + shiftIncrement]; + for (i = 0; i < sz - si; ++i) { + windowAccumulator[i] = windowAccumulator[i + si]; } - for (size_t i = m_windowSize - shiftIncrement; i < m_windowSize; ++i) { - cd.windowAccumulator[i] = 0.0f; + for (i = sz - si; i < sz; ++i) { + windowAccumulator[i] = 0.0f; } - if (cd.accumulatorFill > shiftIncrement) { - cd.accumulatorFill -= shiftIncrement; + if (int(cd.accumulatorFill) > si) { + cd.accumulatorFill -= si; } else { cd.accumulatorFill = 0; if (cd.draining) { @@ -787,6 +1028,8 @@ RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, boo void RubberBandStretcher::Impl::writeOutput(RingBuffer<float> &to, float *from, size_t qty, size_t &outCount, size_t theoreticalOut) { + Profiler profiler("RubberBandStretcher::Impl::writeOutput"); + // In non-RT mode, we don't want to write the first startSkip // samples, because the first chunk is centred on the start of the // output. In RT mode we didn't apply any pre-padding in @@ -859,6 +1102,8 @@ RubberBandStretcher::Impl::writeOutput(RingBuffer<float> &to, float *from, size_ int RubberBandStretcher::Impl::available() const { + Profiler profiler("RubberBandStretcher::Impl::available"); + if (m_threaded) { MutexLocker locker(&m_threadSetMutex); if (m_channelData.empty()) return 0; @@ -906,6 +1151,8 @@ RubberBandStretcher::Impl::available() const size_t RubberBandStretcher::Impl::retrieve(float *const *output, size_t samples) const { + Profiler profiler("RubberBandStretcher::Impl::retrieve"); + size_t got = samples; for (size_t c = 0; c < m_channels; ++c) { diff --git a/libs/rubberband/src/Thread.cpp b/libs/rubberband/src/Thread.cpp index 8b5e074932..98f6e3b445 100644 --- a/libs/rubberband/src/Thread.cpp +++ b/libs/rubberband/src/Thread.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -16,14 +16,11 @@ #include <cstdlib> #include <iostream> +#include <cstdlib> #include <sys/time.h> #include <time.h> -//#define DEBUG_THREAD 1 -//#define DEBUG_MUTEX 1 -//#define DEBUG_CONDITION 1 - using std::cerr; using std::endl; using std::string; @@ -108,8 +105,11 @@ Thread::staticRun(LPVOID arg) return 0; } -Mutex::Mutex() : - m_locked(false) +Mutex::Mutex() +#ifndef NO_THREAD_CHECKS + : + m_lockedBy(-1) +#endif { m_mutex = CreateMutex(NULL, FALSE, NULL); #ifdef DEBUG_MUTEX @@ -128,50 +128,71 @@ Mutex::~Mutex() void Mutex::lock() { - if (m_locked) { +#ifndef NO_THREAD_CHECKS + DWORD tid = GetCurrentThreadId(); + if (m_lockedBy == tid) { cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl; } +#endif #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Want to lock mutex " << &m_mutex << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl; #endif WaitForSingleObject(m_mutex, INFINITE); - m_locked = true; +#ifndef NO_THREAD_CHECKS + m_lockedBy = tid; +#endif #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Locked mutex " << &m_mutex << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl; #endif } void Mutex::unlock() { +#ifndef NO_THREAD_CHECKS + DWORD tid = GetCurrentThreadId(); + if (m_lockedBy != tid) { + cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl; + return; + } +#endif #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Unlocking mutex " << &m_mutex << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl; +#endif +#ifndef NO_THREAD_CHECKS + m_lockedBy = -1; #endif - m_locked = false; ReleaseMutex(m_mutex); } bool Mutex::trylock() { +#ifndef NO_THREAD_CHECKS + DWORD tid = GetCurrentThreadId(); +#endif DWORD result = WaitForSingleObject(m_mutex, 0); if (result == WAIT_TIMEOUT || result == WAIT_FAILED) { #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Mutex " << &m_mutex << " unavailable" << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl; #endif return false; } else { - m_locked = true; +#ifndef NO_THREAD_CHECKS + m_lockedBy = tid; +#endif #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; #endif return true; } } Condition::Condition(string name) : - m_name(name), m_locked(false) +#ifdef DEBUG_CONDITION + , m_name(name) +#endif { m_mutex = CreateMutex(NULL, FALSE, NULL); m_condition = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -344,8 +365,12 @@ Thread::staticRun(void *arg) return 0; } -Mutex::Mutex() : +Mutex::Mutex() +#ifndef NO_THREAD_CHECKS + : + m_lockedBy(0), m_locked(false) +#endif { pthread_mutex_init(&m_mutex, 0); #ifdef DEBUG_MUTEX @@ -364,49 +389,75 @@ Mutex::~Mutex() void Mutex::lock() { - if (m_locked) { +#ifndef NO_THREAD_CHECKS + pthread_t tid = pthread_self(); + if (m_locked && m_lockedBy == tid) { cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl; } +#endif #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Want to lock mutex " << &m_mutex << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl; #endif pthread_mutex_lock(&m_mutex); +#ifndef NO_THREAD_CHECKS + m_lockedBy = tid; m_locked = true; +#endif #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Locked mutex " << &m_mutex << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl; #endif } void Mutex::unlock() { +#ifndef NO_THREAD_CHECKS + pthread_t tid = pthread_self(); + if (!m_locked) { + cerr << "ERROR: Mutex " << &m_mutex << " not locked in unlock" << endl; + return; + } else if (m_lockedBy != tid) { + cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl; + return; + } +#endif #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Unlocking mutex " << &m_mutex << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl; #endif +#ifndef NO_THREAD_CHECKS m_locked = false; +#endif pthread_mutex_unlock(&m_mutex); } bool Mutex::trylock() { +#ifndef NO_THREAD_CHECKS + pthread_t tid = pthread_self(); +#endif if (pthread_mutex_trylock(&m_mutex)) { #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Mutex " << &m_mutex << " unavailable" << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl; #endif return false; } else { +#ifndef NO_THREAD_CHECKS + m_lockedBy = tid; m_locked = true; +#endif #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; #endif return true; } } Condition::Condition(string name) : - m_locked(false), - m_name(name) + m_locked(false) +#ifdef DEBUG_CONDITION + , m_name(name) +#endif { pthread_mutex_init(&m_mutex, 0); pthread_cond_init(&m_condition, 0); diff --git a/libs/rubberband/src/Thread.h b/libs/rubberband/src/Thread.h index dc37f6dd45..061469297e 100644 --- a/libs/rubberband/src/Thread.h +++ b/libs/rubberband/src/Thread.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -23,6 +23,10 @@ #include <string> +//#define DEBUG_THREAD 1 +//#define DEBUG_MUTEX 1 +//#define DEBUG_CONDITION 1 + namespace RubberBand { @@ -73,11 +77,16 @@ public: private: #ifdef _WIN32 HANDLE m_mutex; - bool m_locked; +#ifndef NO_THREAD_CHECKS + DWORD m_lockedBy; +#endif #else pthread_mutex_t m_mutex; +#ifndef NO_THREAD_CHECKS + pthread_t m_lockedBy; bool m_locked; #endif +#endif }; class MutexLocker @@ -113,15 +122,17 @@ public: void signal(); private: + #ifdef _WIN32 HANDLE m_mutex; - bool m_locked; HANDLE m_condition; - std::string m_name; + bool m_locked; #else pthread_mutex_t m_mutex; - bool m_locked; pthread_cond_t m_condition; + bool m_locked; +#endif +#ifdef DEBUG_CONDITION std::string m_name; #endif }; diff --git a/libs/rubberband/src/Window.cpp b/libs/rubberband/src/Window.cpp new file mode 100644 index 0000000000..106faa7b62 --- /dev/null +++ b/libs/rubberband/src/Window.cpp @@ -0,0 +1,17 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band + An audio time-stretching and pitch-shifting library. + Copyright 2007-2008 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "Window.h" + + diff --git a/libs/rubberband/src/Window.h b/libs/rubberband/src/Window.h index 28a3a0446b..b4427fb415 100644 --- a/libs/rubberband/src/Window.h +++ b/libs/rubberband/src/Window.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -18,8 +18,11 @@ #include <cmath> #include <cstdlib> #include <iostream> +#include <cstdlib> #include <map> +#include "sysutils.h" + namespace RubberBand { enum WindowType { @@ -41,7 +44,7 @@ public: /** * Construct a windower of the given type. */ - Window(WindowType type, size_t size) : m_type(type), m_size(size) { encache(); } + Window(WindowType type, int size) : m_type(type), m_size(size) { encache(); } Window(const Window &w) : m_type(w.m_type), m_size(w.m_size) { encache(); } Window &operator=(const Window &w) { if (&w == this) return *this; @@ -52,21 +55,34 @@ public: } virtual ~Window() { delete[] m_cache; } - void cut(T *src) const { cut(src, src); } - void cut(T *src, T *dst) const { - for (size_t i = 0; i < m_size; ++i) dst[i] = src[i] * m_cache[i]; + void cut(T *R__ src) const + { + const int sz = m_size; + for (int i = 0; i < sz; ++i) { + src[i] *= m_cache[i]; + } + } + + void cut(T *R__ src, T *dst) const { + const int sz = m_size; + for (int i = 0; i < sz; ++i) { + dst[i] = src[i]; + } + for (int i = 0; i < sz; ++i) { + dst[i] *= m_cache[i]; + } } T getArea() { return m_area; } - T getValue(size_t i) { return m_cache[i]; } + T getValue(int i) { return m_cache[i]; } WindowType getType() const { return m_type; } - size_t getSize() const { return m_size; } + int getSize() const { return m_size; } protected: WindowType m_type; - size_t m_size; - T *m_cache; + int m_size; + T *R__ m_cache; T m_area; void encache(); diff --git a/libs/rubberband/src/bsd-3rdparty/float_cast/float_cast.h b/libs/rubberband/src/bsd-3rdparty/float_cast/float_cast.h new file mode 100644 index 0000000000..1ba0e03bdc --- /dev/null +++ b/libs/rubberband/src/bsd-3rdparty/float_cast/float_cast.h @@ -0,0 +1,73 @@ +/* +** Copyright (C) 2001 Erik de Castro Lopo <erikd AT mega-nerd DOT com> +** +** Permission to use, copy, modify, distribute, and sell this file for any +** purpose is hereby granted without fee, provided that the above copyright +** and this permission notice appear in all copies. No representations are +** made about the suitability of this software for any purpose. It is +** provided "as is" without express or implied warranty. +*/ + +/* Version 1.1 */ + + +/*============================================================================ +** On Intel Pentium processors (especially PIII and probably P4), converting +** from float to int is very slow. To meet the C specs, the code produced by +** most C compilers targeting Pentium needs to change the FPU rounding mode +** before the float to int conversion is performed. +** +** Changing the FPU rounding mode causes the FPU pipeline to be flushed. It +** is this flushing of the pipeline which is so slow. +** +** Fortunately the ISO C99 specifications define the functions lrint, lrintf, +** llrint and llrintf which fix this problem as a side effect. +** +** On Unix-like systems, the configure process should have detected the +** presence of these functions. If they weren't found we have to replace them +** here with a standard C cast. +*/ + +/* +** The C99 prototypes for lrint and lrintf are as follows: +** +** long int lrintf (float x) ; +** long int lrint (double x) ; +*/ + +#if (defined (WIN32) || defined (_WIN32)) + + #include <math.h> + + /* Win32 doesn't seem to have these functions. + ** Therefore implement inline versions of these functions here. + */ + + __inline long int + lrint (double flt) + { int intgr; + + _asm + { fld flt + fistp intgr + } ; + + return intgr ; + } + + __inline long int + lrintf (float flt) + { int intgr; + + _asm + { fld flt + fistp intgr + } ; + + return intgr ; + } + +#endif + + + diff --git a/libs/rubberband/src/bsd-3rdparty/getopt/getopt.c b/libs/rubberband/src/bsd-3rdparty/getopt/getopt.c new file mode 100644 index 0000000000..ce9abb3cce --- /dev/null +++ b/libs/rubberband/src/bsd-3rdparty/getopt/getopt.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':' && optopt != BADCH) + (void)fprintf(stderr, "%s: illegal option -- %c\n", + "progname", optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + "progname", optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} diff --git a/libs/rubberband/src/bsd-3rdparty/getopt/getopt.h b/libs/rubberband/src/bsd-3rdparty/getopt/getopt.h new file mode 100644 index 0000000000..d95d6cf8f8 --- /dev/null +++ b/libs/rubberband/src/bsd-3rdparty/getopt/getopt.h @@ -0,0 +1,110 @@ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ +/* $FreeBSD: src/include/getopt.h,v 1.1 2002/09/29 04:14:30 eric Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +#ifdef _WIN32 +/* from <sys/cdefs.h> */ +# ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +# else +# define __BEGIN_DECLS +# define __END_DECLS +# endif +# define __P(args) args +#endif + +/*#ifndef _WIN32 +#include <sys/cdefs.h> +#include <unistd.h> +#endif*/ + +#ifdef _WIN32 +# if !defined(GETOPT_API) +# define GETOPT_API __declspec(dllimport) +# endif +#endif + +/* + * Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions + */ +#if !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE) +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; +}; + +__BEGIN_DECLS +GETOPT_API int getopt_long __P((int, char * const *, const char *, + const struct option *, int *)); +__END_DECLS +#endif + +#ifdef _WIN32 +/* These are global getopt variables */ +__BEGIN_DECLS + +GETOPT_API extern int opterr, /* if error message should be printed */ + optind, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +GETOPT_API extern char* optarg; /* argument associated with option */ + +/* Original getopt */ +GETOPT_API int getopt __P((int, char * const *, const char *)); + +__END_DECLS +#endif + +#endif /* !_GETOPT_H_ */ diff --git a/libs/rubberband/src/bsd-3rdparty/getopt/getopt_long.c b/libs/rubberband/src/bsd-3rdparty/getopt/getopt_long.c new file mode 100644 index 0000000000..1f92449a06 --- /dev/null +++ b/libs/rubberband/src/bsd-3rdparty/getopt/getopt_long.c @@ -0,0 +1,547 @@ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ +/* $FreeBSD: src/lib/libc/stdlib/getopt_long.c,v 1.2 2002/10/16 22:18:42 alfred Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include <stdlib.h> +#include <string.h> + +#ifdef _WIN32 + +/* Windows needs warnx(). We change the definition though: + * 1. (another) global is defined, opterrmsg, which holds the error message + * 2. errors are always printed out on stderr w/o the program name + * Note that opterrmsg always gets set no matter what opterr is set to. The + * error message will not be printed if opterr is 0 as usual. + */ + +#include "getopt.h" +#include <stdio.h> +#include <stdarg.h> + +GETOPT_API extern char opterrmsg[128]; +char opterrmsg[128]; /* last error message is stored here */ + +static void warnx(int print_error, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (fmt != NULL) + _vsnprintf(opterrmsg, 128, fmt, ap); + else + opterrmsg[0]='\0'; + va_end(ap); + if (print_error) { + fprintf(stderr, opterrmsg); + fprintf(stderr, "\n"); + } +} + +#endif /*_WIN32*/ + +/* not part of the original file */ +#ifndef _DIAGASSERT +#define _DIAGASSERT(X) +#endif + +#if HAVE_CONFIG_H && !HAVE_GETOPT_LONG && !HAVE_DECL_OPTIND +#define REPLACE_GETOPT +#endif + +#ifdef REPLACE_GETOPT +#ifdef __weak_alias +__weak_alias(getopt,_getopt) +#endif +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#elif HAVE_CONFIG_H && !HAVE_DECL_OPTRESET +static int optreset; +#endif + +#ifdef __weak_alias +__weak_alias(getopt_long,_getopt_long) +#endif + +#if !HAVE_GETOPT_LONG +#define IGNORE_FIRST (*options == '-' || *options == '+') +#define PRINT_ERROR ((opterr) && ((*options != ':') \ + || (IGNORE_FIRST && options[1] != ':'))) +#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL) +#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST) +/* XXX: GNU ignores PC if *options == '-' */ +#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-') + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((IGNORE_FIRST && options[1] == ':') \ + || (*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static int getopt_internal(int, char * const *, const char *); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(a, b) + int a; + int b; +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return b; +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(panonopt_start, panonopt_end, opt_end, nargv) + int panonopt_start; + int panonopt_end; + int opt_end; + char * const *nargv; +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + _DIAGASSERT(nargv != NULL); + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + * Returns -2 if -- is found (can be long option or end of options marker). + */ +static int +getopt_internal(nargc, nargv, options) + int nargc; + char * const *nargv; + const char *options; +{ + char *oli; /* option letter list index */ + int optchar; + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + + optarg = NULL; + + /* + * XXX Some programs (like rsyncd) expect to be able to + * XXX re-initialize optind to 0 and have getopt_long(3) + * XXX properly function again. Work around this braindamage. + */ + if (optind == 0) + optind = 1; + + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return -1; + } + if ((*(place = nargv[optind]) != '-') + || (place[1] == '\0')) { /* found non-option */ + place = EMSG; + if (IN_ORDER) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return INORDER; + } + if (!PERMUTE) { + /* + * if no permutation wanted, stop parsing + * at first non-option + */ + return -1; + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + if (place[1] && *++place == '-') { /* found "--" */ + place++; + return -2; + } + } + if ((optchar = (int)*place++) == (int)':' || + (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) { + /* option letter unknown or ':' */ + if (!*place) + ++optind; +#ifndef _WIN32 + if (PRINT_ERROR) + warnx(illoptchar, optchar); +#else + warnx(PRINT_ERROR, illoptchar, optchar); +#endif + optopt = optchar; + return BADCH; + } + if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ + /* XXX: what if no long options provided (called by getopt)? */ + if (*place) + return -2; + + if (++optind >= nargc) { /* no arg */ + place = EMSG; +#ifndef _WIN32 + if (PRINT_ERROR) + warnx(recargchar, optchar); +#else + warnx(PRINT_ERROR, recargchar, optchar); +#endif + optopt = optchar; + return BADARG; + } else /* white space */ + place = nargv[optind]; + /* + * Handle -W arg the same as --arg (which causes getopt to + * stop parsing). + */ + return -2; + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + /* XXX: disable test for :: if PC? (GNU doesn't) */ + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; +#ifndef _WIN32 + if (PRINT_ERROR) + warnx(recargchar, optchar); +#else + warnx(PRINT_ERROR, recargchar, optchar); +#endif + optopt = optchar; + return BADARG; + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return optchar; +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the real getopt] + */ +int +getopt(nargc, nargv, options) + int nargc; + char * const *nargv; + const char *options; +{ + int retval; + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + + if ((retval = getopt_internal(nargc, nargv, options)) == -2) { + ++optind; + /* + * We found an option (--), so if we skipped non-options, + * we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, optind, + nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + retval = -1; + } + return retval; +} +#endif + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(nargc, nargv, options, long_options, idx) + int nargc; + char * const *nargv; + const char *options; + const struct option *long_options; + int *idx; +{ + int retval; + + _DIAGASSERT(nargv != NULL); + _DIAGASSERT(options != NULL); + _DIAGASSERT(long_options != NULL); + /* idx may be NULL */ + + if ((retval = getopt_internal(nargc, nargv, options)) == -2) { + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match; + + current_argv = place; + match = -1; + + optind++; + place = EMSG; + + if (*current_argv == '\0') { /* found "--" */ + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return -1; + } + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == + (unsigned)current_argv_len) { + /* exact match */ + match = i; + break; + } + if (match == -1) /* partial match */ + match = i; + else { + /* ambiguous abbreviation */ +#ifndef _WIN32 + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); +#else + warnx(PRINT_ERROR, ambig, (int)current_argv_len, + current_argv); +#endif + optopt = 0; + return BADCH; + } + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { +#ifndef _WIN32 + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); +#else + warnx(PRINT_ERROR, noarg, (int)current_argv_len, + current_argv); +#endif + /* + * XXX: GNU sets optopt to val regardless of + * flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return BADARG; + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use + * next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' + * indicates no error should be generated + */ +#ifndef _WIN32 + if (PRINT_ERROR) + warnx(recargstring, current_argv); +#else + warnx(PRINT_ERROR, recargstring, current_argv); +#endif + /* + * XXX: GNU sets optopt to val regardless + * of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return BADARG; + } + } else { /* unknown option */ +#ifndef _WIN32 + if (PRINT_ERROR) + warnx(illoptstring, current_argv); +#else + warnx(PRINT_ERROR, illoptstring, current_argv); +#endif + optopt = 0; + return BADCH; + } + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + retval = 0; + } else + retval = long_options[match].val; + if (idx) + *idx = match; + } + return retval; +} +#endif /* !GETOPT_LONG */ diff --git a/libs/rubberband/src/bsd-3rdparty/getopt/unistd.h b/libs/rubberband/src/bsd-3rdparty/getopt/unistd.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/libs/rubberband/src/bsd-3rdparty/getopt/unistd.h diff --git a/libs/rubberband/src/ladspa/RubberBandPitchShifter.cpp b/libs/rubberband/src/ladspa/RubberBandPitchShifter.cpp index c0b2813c79..6839124921 100644 --- a/libs/rubberband/src/ladspa/RubberBandPitchShifter.cpp +++ b/libs/rubberband/src/ladspa/RubberBandPitchShifter.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,14 +21,21 @@ using namespace RubberBand; +using std::cout; +using std::cerr; +using std::endl; +using std::min; + const char *const RubberBandPitchShifter::portNamesMono[PortCountMono] = { - "_latency", + "latency", "Cents", "Semitones", "Octaves", "Crispness", + "Formant Preserving", + "Faster", "Input", "Output" }; @@ -36,11 +43,13 @@ RubberBandPitchShifter::portNamesMono[PortCountMono] = const char *const RubberBandPitchShifter::portNamesStereo[PortCountStereo] = { - "_latency", + "latency", "Cents", "Semitones", "Octaves", "Crispness", + "Formant Preserving", + "Faster", "Input L", "Output L", "Input R", @@ -55,6 +64,8 @@ RubberBandPitchShifter::portsMono[PortCountMono] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO }; @@ -67,6 +78,8 @@ RubberBandPitchShifter::portsStereo[PortCountStereo] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, @@ -76,26 +89,36 @@ RubberBandPitchShifter::portsStereo[PortCountStereo] = const LADSPA_PortRangeHint RubberBandPitchShifter::hintsMono[PortCountMono] = { - { 0, 0, 0 }, - { LADSPA_HINT_DEFAULT_0 | + { 0, 0, 0 }, // latency + { LADSPA_HINT_DEFAULT_0 | // cents LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, -100.0, 100.0 }, - { LADSPA_HINT_DEFAULT_0 | + { LADSPA_HINT_DEFAULT_0 | // semitones LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_INTEGER, -12.0, 12.0 }, - { LADSPA_HINT_DEFAULT_0 | + { LADSPA_HINT_DEFAULT_0 | // octaves LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_INTEGER, - -4.0, 4.0 }, - { LADSPA_HINT_DEFAULT_MAXIMUM | + -3.0, 3.0 }, + { LADSPA_HINT_DEFAULT_MAXIMUM | // crispness LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_INTEGER, 0.0, 3.0 }, + { LADSPA_HINT_DEFAULT_0 | // formant preserving + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_TOGGLED, + 0.0, 1.0 }, + { LADSPA_HINT_DEFAULT_0 | // fast + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_TOGGLED, + 0.0, 1.0 }, { 0, 0, 0 }, { 0, 0, 0 } }; @@ -103,26 +126,36 @@ RubberBandPitchShifter::hintsMono[PortCountMono] = const LADSPA_PortRangeHint RubberBandPitchShifter::hintsStereo[PortCountStereo] = { - { 0, 0, 0 }, - { LADSPA_HINT_DEFAULT_0 | + { 0, 0, 0 }, // latency + { LADSPA_HINT_DEFAULT_0 | // cents LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, -100.0, 100.0 }, - { LADSPA_HINT_DEFAULT_0 | + { LADSPA_HINT_DEFAULT_0 | // semitones LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_INTEGER, -12.0, 12.0 }, - { LADSPA_HINT_DEFAULT_0 | + { LADSPA_HINT_DEFAULT_0 | // octaves LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_INTEGER, - -4.0, 4.0 }, - { LADSPA_HINT_DEFAULT_MAXIMUM | + -3.0, 3.0 }, + { LADSPA_HINT_DEFAULT_MAXIMUM | // crispness LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_INTEGER, 0.0, 3.0 }, + { LADSPA_HINT_DEFAULT_0 | // formant preserving + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_TOGGLED, + 0.0, 1.0 }, + { LADSPA_HINT_DEFAULT_0 | // fast + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_TOGGLED, + 0.0, 1.0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, @@ -139,7 +172,7 @@ RubberBandPitchShifter::ladspaDescriptorMono = "rubberband-pitchshifter-mono", // Label properties, "Rubber Band Mono Pitch Shifter", // Name - "Chris Cannam", + "Breakfast Quay", "GPL", PortCountMono, portsMono, @@ -163,7 +196,7 @@ RubberBandPitchShifter::ladspaDescriptorStereo = "rubberband-pitchshifter-stereo", // Label properties, "Rubber Band Stereo Pitch Shifter", // Name - "Chris Cannam", + "Breakfast Quay", "GPL", PortCountStereo, portsStereo, @@ -194,25 +227,37 @@ RubberBandPitchShifter::RubberBandPitchShifter(int sampleRate, size_t channels) m_semitones(0), m_octaves(0), m_crispness(0), + m_formant(0), + m_fast(0), m_ratio(1.0), m_prevRatio(1.0), m_currentCrispness(-1), - m_extraLatency(8192), //!!! this should be at least the maximum possible displacement from linear at input rates, divided by the pitch scale factor. It could be very large + m_currentFormant(false), + m_currentFast(false), + m_blockSize(1024), + m_reserve(1024), + m_minfill(0), m_stretcher(new RubberBandStretcher (sampleRate, channels, - RubberBandStretcher::OptionProcessRealTime)), + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionPitchHighConsistency)), m_sampleRate(sampleRate), m_channels(channels) { for (size_t c = 0; c < m_channels; ++c) { + m_input[c] = 0; m_output[c] = 0; - //!!! size must be at least max process size plus m_extraLatency: - m_outputBuffer[c] = new RingBuffer<float>(8092); //!!! - m_outputBuffer[c]->zero(m_extraLatency); - //!!! size must be at least max process size: - m_scratch[c] = new float[16384];//!!! + + int bufsize = m_blockSize + m_reserve + 8192; + + m_outputBuffer[c] = new RingBuffer<float>(bufsize); + + m_scratch[c] = new float[bufsize]; + for (int i = 0; i < bufsize; ++i) m_scratch[c][i] = 0.f; } + + activateImpl(); } RubberBandPitchShifter::~RubberBandPitchShifter() @@ -247,23 +292,59 @@ RubberBandPitchShifter::connectPort(LADSPA_Handle handle, &shifter->m_semitones, &shifter->m_octaves, &shifter->m_crispness, - &shifter->m_input[0], + &shifter->m_formant, + &shifter->m_fast, + &shifter->m_input[0], &shifter->m_output[0], &shifter->m_input[1], &shifter->m_output[1] }; + if (shifter->m_channels == 1) { + if (port >= PortCountMono) return; + } else { + if (port >= PortCountStereo) return; + } + *ports[port] = (float *)location; + + if (shifter->m_latency) { + *(shifter->m_latency) = + float(shifter->m_stretcher->getLatency() + shifter->m_reserve); + } } void RubberBandPitchShifter::activate(LADSPA_Handle handle) { RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle; - shifter->updateRatio(); - shifter->m_prevRatio = shifter->m_ratio; - shifter->m_stretcher->reset(); - shifter->m_stretcher->setPitchScale(shifter->m_ratio); + shifter->activateImpl(); +} + +void +RubberBandPitchShifter::activateImpl() +{ + updateRatio(); + m_prevRatio = m_ratio; + m_stretcher->reset(); + m_stretcher->setPitchScale(m_ratio); + + for (size_t c = 0; c < m_channels; ++c) { + m_outputBuffer[c]->reset(); + m_outputBuffer[c]->zero(m_reserve); + } + + m_minfill = 0; + + // prime stretcher +// for (int i = 0; i < 8; ++i) { +// int reqd = m_stretcher->getSamplesRequired(); +// m_stretcher->process(m_scratch, reqd, false); +// int avail = m_stretcher->available(); +// if (avail > 0) { +// m_stretcher->retrieve(m_scratch, avail); +// } +// } } void @@ -276,9 +357,9 @@ RubberBandPitchShifter::run(LADSPA_Handle handle, unsigned long samples) void RubberBandPitchShifter::updateRatio() { - double oct = *m_octaves; - oct += *m_semitones / 12; - oct += *m_cents / 1200; + double oct = (m_octaves ? *m_octaves : 0.0); + oct += (m_semitones ? *m_semitones : 0.0) / 12; + oct += (m_cents ? *m_cents : 0.0) / 1200; m_ratio = pow(2.0, oct); } @@ -298,15 +379,15 @@ RubberBandPitchShifter::updateCrispness() s->setTransientsOption(RubberBandStretcher::OptionTransientsSmooth); break; case 1: - s->setPhaseOption(RubberBandStretcher::OptionPhaseAdaptive); + s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar); s->setTransientsOption(RubberBandStretcher::OptionTransientsSmooth); break; case 2: - s->setPhaseOption(RubberBandStretcher::OptionPhaseAdaptive); + s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar); s->setTransientsOption(RubberBandStretcher::OptionTransientsMixed); break; case 3: - s->setPhaseOption(RubberBandStretcher::OptionPhaseAdaptive); + s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar); s->setTransientsOption(RubberBandStretcher::OptionTransientsCrisp); break; } @@ -315,9 +396,65 @@ RubberBandPitchShifter::updateCrispness() } void +RubberBandPitchShifter::updateFormant() +{ + if (!m_formant) return; + + bool f = (*m_formant > 0.5f); + if (f == m_currentFormant) return; + + RubberBandStretcher *s = m_stretcher; + + s->setFormantOption(f ? + RubberBandStretcher::OptionFormantPreserved : + RubberBandStretcher::OptionFormantShifted); + + m_currentFormant = f; +} + +void +RubberBandPitchShifter::updateFast() +{ + if (!m_fast) return; + + bool f = (*m_fast > 0.5f); + if (f == m_currentFast) return; + + RubberBandStretcher *s = m_stretcher; + + s->setPitchOption(f ? + RubberBandStretcher::OptionPitchHighSpeed : + RubberBandStretcher::OptionPitchHighConsistency); + + m_currentFast = f; +} + +void RubberBandPitchShifter::runImpl(unsigned long insamples) { -// std::cerr << "RubberBandPitchShifter::runImpl(" << insamples << ")" << std::endl; + unsigned long offset = 0; + + // We have to break up the input into chunks like this because + // insamples could be arbitrarily large and our output buffer is + // of limited size + + while (offset < insamples) { + + unsigned long block = (unsigned long)m_blockSize; + if (block + offset > insamples) block = insamples - offset; + + runImpl(block, offset); + + offset += block; + } +} + +void +RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset) +{ +// cerr << "RubberBandPitchShifter::runImpl(" << insamples << ")" << endl; + +// static int incount = 0, outcount = 0; updateRatio(); if (m_ratio != m_prevRatio) { @@ -326,71 +463,80 @@ RubberBandPitchShifter::runImpl(unsigned long insamples) } if (m_latency) { - *m_latency = m_stretcher->getLatency() + m_extraLatency; -// std::cerr << "latency = " << *m_latency << std::endl; + *m_latency = float(m_stretcher->getLatency() + m_reserve); +// cerr << "latency = " << *m_latency << endl; } updateCrispness(); + updateFormant(); + updateFast(); - int samples = insamples; + const int samples = insamples; int processed = 0; size_t outTotal = 0; float *ptrs[2]; - // We have to break up the input into chunks like this because - // insamples could be arbitrarily large + int rs = m_outputBuffer[0]->getReadSpace(); + if (rs < int(m_minfill)) { +// cerr << "temporary expansion (have " << rs << ", want " << m_reserve << ")" << endl; + m_stretcher->setTimeRatio(1.1); // fill up temporarily + } else if (rs > 8192) { +// cerr << "temporary reduction (have " << rs << ", want " << m_reserve << ")" << endl; + m_stretcher->setTimeRatio(0.9); // reduce temporarily + } else { + m_stretcher->setTimeRatio(1.0); + } while (processed < samples) { - //!!! size_t: + // never feed more than the minimum necessary number of + // samples at a time; ensures nothing will overflow internally + // and we don't need to call setMaxProcessSize + int toCauseProcessing = m_stretcher->getSamplesRequired(); -// std::cout << "to-cause: " << toCauseProcessing << ", remain = " << samples - processed; - int inchunk = std::min(samples - processed, toCauseProcessing); + int inchunk = min(samples - processed, toCauseProcessing); for (size_t c = 0; c < m_channels; ++c) { - ptrs[c] = &(m_input[c][processed]); + ptrs[c] = &(m_input[c][offset + processed]); } m_stretcher->process(ptrs, inchunk, false); processed += inchunk; int avail = m_stretcher->available(); int writable = m_outputBuffer[0]->getWriteSpace(); - int outchunk = std::min(avail, writable); + int outchunk = min(avail, writable); size_t actual = m_stretcher->retrieve(m_scratch, outchunk); outTotal += actual; -// std::cout << ", avail: " << avail << ", outchunk = " << outchunk; -// if (actual != outchunk) std::cout << " (" << actual << ")"; -// std::cout << std::endl; +// incount += inchunk; +// outcount += actual; + +// cout << "avail: " << avail << ", outchunk = " << outchunk; +// if (actual != outchunk) cout << " (" << actual << ")"; +// cout << endl; outchunk = actual; for (size_t c = 0; c < m_channels; ++c) { if (int(m_outputBuffer[c]->getWriteSpace()) < outchunk) { - std::cerr << "RubberBandPitchShifter::runImpl: buffer overrun: chunk = " << outchunk << ", space = " << m_outputBuffer[c]->getWriteSpace() << std::endl; + cerr << "RubberBandPitchShifter::runImpl: buffer overrun: chunk = " << outchunk << ", space = " << m_outputBuffer[c]->getWriteSpace() << endl; } m_outputBuffer[c]->write(m_scratch[c], outchunk); } } - -// std::cout << "processed = " << processed << " in, " << outTotal << " out" << ", fill = " << m_outputBuffer[0]->getReadSpace() << " of " << m_outputBuffer[0]->getSize() << std::endl; for (size_t c = 0; c < m_channels; ++c) { - int avail = m_outputBuffer[c]->getReadSpace(); -// std::cout << "avail: " << avail << std::endl; - if (avail < samples && c == 0) { - std::cerr << "RubberBandPitchShifter::runImpl: buffer underrun: required = " << samples << ", available = " << avail << std::endl; + int toRead = m_outputBuffer[c]->getReadSpace(); + if (toRead < samples && c == 0) { + cerr << "RubberBandPitchShifter::runImpl: buffer underrun: required = " << samples << ", available = " << toRead << endl; } - int chunk = std::min(avail, samples); -// std::cout << "out chunk: " << chunk << std::endl; - m_outputBuffer[c]->read(m_output[c], chunk); + int chunk = min(toRead, samples); + m_outputBuffer[c]->read(&(m_output[c][offset]), chunk); } - static int minr = -1; - int avail = m_outputBuffer[0]->getReadSpace(); - if (minr == -1 || (avail >= 0 && avail < minr)) { - std::cerr << "RubberBandPitchShifter::runImpl: new min remaining " << avail << " from " << minr << std::endl; - minr = avail; + if (m_minfill == 0) { + m_minfill = m_outputBuffer[0]->getReadSpace(); +// cerr << "minfill = " << m_minfill << endl; } } diff --git a/libs/rubberband/src/ladspa/RubberBandPitchShifter.h b/libs/rubberband/src/ladspa/RubberBandPitchShifter.h index 3adfb61bc1..f2f351bff6 100644 --- a/libs/rubberband/src/ladspa/RubberBandPitchShifter.h +++ b/libs/rubberband/src/ladspa/RubberBandPitchShifter.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -15,7 +15,7 @@ #ifndef _RUBBERBAND_PITCH_SHIFTER_H_ #define _RUBBERBAND_PITCH_SHIFTER_H_ -#include "ladspa.h" +#include <ladspa.h> #include "RingBuffer.h" @@ -38,11 +38,13 @@ protected: SemitonesPort = 2, CentsPort = 3, CrispnessPort = 4, - InputPort1 = 5, - OutputPort1 = 6, + FormantPort = 5, + FastPort = 6, + InputPort1 = 7, + OutputPort1 = 8, PortCountMono = OutputPort1 + 1, - InputPort2 = 7, - OutputPort2 = 8, + InputPort2 = 9, + OutputPort2 = 10, PortCountStereo = OutputPort2 + 1 }; @@ -66,9 +68,13 @@ protected: static void deactivate(LADSPA_Handle); static void cleanup(LADSPA_Handle); + void activateImpl(); void runImpl(unsigned long); + void runImpl(unsigned long, unsigned long offset); void updateRatio(); void updateCrispness(); + void updateFormant(); + void updateFast(); float *m_input[2]; float *m_output[2]; @@ -77,11 +83,17 @@ protected: float *m_semitones; float *m_octaves; float *m_crispness; + float *m_formant; + float *m_fast; double m_ratio; double m_prevRatio; int m_currentCrispness; + bool m_currentFormant; + bool m_currentFast; - size_t m_extraLatency; + size_t m_blockSize; + size_t m_reserve; + size_t m_minfill; RubberBand::RubberBandStretcher *m_stretcher; RubberBand::RingBuffer<float> *m_outputBuffer[2]; diff --git a/libs/rubberband/src/ladspa/libmain.cpp b/libs/rubberband/src/ladspa/libmain.cpp index afc7ac0709..d949e81898 100644 --- a/libs/rubberband/src/ladspa/libmain.cpp +++ b/libs/rubberband/src/ladspa/libmain.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/libs/rubberband/src/main.cpp b/libs/rubberband/src/main.cpp index 2f8b386d51..370ced2c7d 100644 --- a/libs/rubberband/src/main.cpp +++ b/libs/rubberband/src/main.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -18,15 +18,19 @@ #include <iostream> #include <sndfile.h> #include <cmath> -#include <cstdlib> -#include <sys/time.h> #include <time.h> +#include <cstdlib> +#include <cstring> #include "sysutils.h" +#ifdef __MSVC__ +#include "bsd-3rdparty/getopt/getopt.h" +#else #include <getopt.h> +#include <sys/time.h> +#endif -// for import and export of FFTW wisdom -#include <fftw3.h> +#include "Profiler.h" using namespace std; using namespace RubberBand; @@ -36,23 +40,48 @@ using RubberBand::gettimeofday; using RubberBand::usleep; #endif +double tempo_convert(const char *str) +{ + char *d = strchr((char *)str, ':'); + + if (!d || !*d) { + double m = atof(str); + if (m != 0.0) return 1.0 / m; + else return 1.0; + } + + char *a = strdup(str); + char *b = strdup(d+1); + a[d-str] = '\0'; + double m = atof(a); + double n = atof(b); + free(a); + free(b); + if (n != 0.0 && m != 0.0) return m / n; + else return 1.0; +} + int main(int argc, char **argv) { int c; double ratio = 1.0; - double pitchshift = 1.0; + double duration = 0.0; + double pitchshift = 0.0; double frequencyshift = 1.0; int debug = 0; bool realtime = false; bool precise = false; int threading = 0; - bool peaklock = true; + bool lamination = true; bool longwin = false; bool shortwin = false; - bool softening = true; + bool hqpitch = false; + bool formant = false; + bool crispchanged = false; int crispness = -1; bool help = false; + bool version = false; bool quiet = false; bool haveRatio = false; @@ -63,17 +92,15 @@ int main(int argc, char **argv) Transients } transients = Transients; - float fthresh0 = -1.f; - float fthresh1 = -1.f; - float fthresh2 = -1.f; - while (1) { int optionIndex = 0; static struct option longOpts[] = { { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, { "time", 1, 0, 't' }, { "tempo", 1, 0, 'T' }, + { "duration", 1, 0, 'D' }, { "pitch", 1, 0, 'p' }, { "frequency", 1, 0, 'f' }, { "crisp", 1, 0, 'c' }, @@ -81,75 +108,81 @@ int main(int argc, char **argv) { "debug", 1, 0, 'd' }, { "realtime", 0, 0, 'R' }, { "precise", 0, 0, 'P' }, + { "formant", 0, 0, 'F' }, { "no-threads", 0, 0, '0' }, { "no-transients", 0, 0, '1' }, - { "no-peaklock", 0, 0, '2' }, + { "no-lamination", 0, 0, '2' }, { "window-long", 0, 0, '3' }, { "window-short", 0, 0, '4' }, - { "thresh0", 1, 0, '5' }, - { "thresh1", 1, 0, '6' }, - { "thresh2", 1, 0, '7' }, { "bl-transients", 0, 0, '8' }, - { "no-softening", 0, 0, '9' }, + { "pitch-hq", 0, 0, '%' }, { "threads", 0, 0, '@' }, { "quiet", 0, 0, 'q' }, { 0, 0, 0 } }; - c = getopt_long(argc, argv, "t:p:d:RPc:f:qh", longOpts, &optionIndex); + c = getopt_long(argc, argv, "t:p:d:RPFc:f:T:D:qhV", longOpts, &optionIndex); if (c == -1) break; switch (c) { case 'h': help = true; break; + case 'V': version = true; break; case 't': ratio *= atof(optarg); haveRatio = true; break; - case 'T': { double m = atof(optarg); if (m != 0.0) ratio /= m; }; haveRatio = true; break; + case 'T': ratio *= tempo_convert(optarg); haveRatio = true; break; + case 'D': duration = atof(optarg); haveRatio = true; break; case 'p': pitchshift = atof(optarg); haveRatio = true; break; case 'f': frequencyshift = atof(optarg); haveRatio = true; break; case 'd': debug = atoi(optarg); break; case 'R': realtime = true; break; case 'P': precise = true; break; + case 'F': formant = true; break; case '0': threading = 1; break; case '@': threading = 2; break; - case '1': transients = NoTransients; break; - case '2': peaklock = false; break; - case '3': longwin = true; break; - case '4': shortwin = true; break; - case '5': fthresh0 = atof(optarg); break; - case '6': fthresh1 = atof(optarg); break; - case '7': fthresh2 = atof(optarg); break; - case '8': transients = BandLimitedTransients; break; - case '9': softening = false; break; + case '1': transients = NoTransients; crispchanged = true; break; + case '2': lamination = false; crispchanged = true; break; + case '3': longwin = true; crispchanged = true; break; + case '4': shortwin = true; crispchanged = true; break; + case '8': transients = BandLimitedTransients; crispchanged = true; break; + case '%': hqpitch = true; break; case 'c': crispness = atoi(optarg); break; case 'q': quiet = true; break; default: help = true; break; } } + if (version) { + cerr << RUBBERBAND_VERSION << endl; + return 0; + } + if (help || !haveRatio || optind + 2 != argc) { cerr << endl; cerr << "Rubber Band" << endl; cerr << "An audio time-stretching and pitch-shifting library and utility program." << endl; - cerr << "Copyright 2007 Chris Cannam. Distributed under the GNU General Public License." << endl; + cerr << "Copyright 2008 Chris Cannam. Distributed under the GNU General Public License." << endl; cerr << endl; cerr << " Usage: " << argv[0] << " [options] <infile.wav> <outfile.wav>" << endl; cerr << endl; cerr << "You must specify at least one of the following time and pitch ratio options." << endl; cerr << endl; cerr << " -t<X>, --time <X> Stretch to X times original duration, or" << endl; - cerr << " -T<X>, --tempo <X> Change tempo by multiple X (equivalent to --time 1/X)" << endl; + cerr << " -T<X>, --tempo <X> Change tempo by multiple X (same as --time 1/X), or" << endl; + cerr << " -T<X>, --tempo <X>:<Y> Change tempo from X to Y (same as --time X/Y), or" << endl; + cerr << " -D<X>, --duration <X> Stretch or squash to make output file X seconds long" << endl; cerr << endl; cerr << " -p<X>, --pitch <X> Raise pitch by X semitones, or" << endl; cerr << " -f<X>, --frequency <X> Change frequency by multiple X" << endl; cerr << endl; - cerr << "The following option provides a simple way to adjust the sound. See below" << endl; + cerr << "The following options provide a simple way to adjust the sound. See below" << endl; cerr << "for more details." << endl; cerr << endl; cerr << " -c<N>, --crisp <N> Crispness (N = 0,1,2,3,4,5); default 4 (see below)" << endl; + cerr << " -F, --formant Enable formant preservation when pitch shifting" << endl; cerr << endl; cerr << "The remaining options fine-tune the processing mode and stretch algorithm." << endl; cerr << "These are mostly included for test purposes; the default settings and standard" << endl; cerr << "crispness parameter are intended to provide the best sounding set of options" << endl; - cerr << "for most situations." << endl; + cerr << "for most situations. The default is to use none of these options." << endl; cerr << endl; cerr << " -P, --precise Aim for minimal time distortion (implied by -R)" << endl; cerr << " -R, --realtime Select realtime mode (implies -P --no-threads)" << endl; @@ -157,37 +190,42 @@ int main(int argc, char **argv) cerr << " --threads Assume multi-CPU even if only one CPU is identified" << endl; cerr << " --no-transients Disable phase resynchronisation at transients" << endl; cerr << " --bl-transients Band-limit phase resync to extreme frequencies" << endl; - cerr << " --no-peaklock Disable phase locking to peak frequencies" << endl; - cerr << " --no-softening Disable large-ratio softening of phase locking" << endl; + cerr << " --no-lamination Disable phase lamination" << endl; cerr << " --window-long Use longer processing window (actual size may vary)" << endl; cerr << " --window-short Use shorter processing window" << endl; - cerr << " --thresh<N> <F> Set internal freq threshold N (N = 0,1,2) to F Hz" << endl; + cerr << " --pitch-hq In RT mode, use a slower, higher quality pitch shift" << endl; cerr << endl; cerr << " -d<N>, --debug <N> Select debug level (N = 0,1,2,3); default 0, full 3" << endl; cerr << " (N.B. debug level 3 includes audible ticks in output)" << endl; cerr << " -q, --quiet Suppress progress output" << endl; cerr << endl; + cerr << " -V, --version Show version number and exit" << endl; cerr << " -h, --help Show this help" << endl; cerr << endl; cerr << "\"Crispness\" levels:" << endl; - cerr << " -c 0 equivalent to --no-transients --no-peaklock --window-long" << endl; - cerr << " -c 1 equivalent to --no-transients --no-peaklock" << endl; + cerr << " -c 0 equivalent to --no-transients --no-lamination --window-long" << endl; + cerr << " -c 1 equivalent to --no-transients --no-lamination" << endl; cerr << " -c 2 equivalent to --no-transients" << endl; cerr << " -c 3 equivalent to --bl-transients" << endl; cerr << " -c 4 default processing options" << endl; - cerr << " -c 5 equivalent to --no-peaklock --window-short (may be suitable for drums)" << endl; + cerr << " -c 5 equivalent to --no-lamination --window-short (may be good for drums)" << endl; cerr << endl; return 2; } + if (crispness >= 0 && crispchanged) { + cerr << "WARNING: Both crispness option and transients, lamination or window options" << endl; + cerr << " provided -- crispness will override these other options" << endl; + } + switch (crispness) { case -1: crispness = 4; break; - case 0: transients = NoTransients; peaklock = false; longwin = true; shortwin = false; break; - case 1: transients = NoTransients; peaklock = false; longwin = false; shortwin = false; break; - case 2: transients = NoTransients; peaklock = true; longwin = false; shortwin = false; break; - case 3: transients = BandLimitedTransients; peaklock = true; longwin = false; shortwin = false; break; - case 4: transients = Transients; peaklock = true; longwin = false; shortwin = false; break; - case 5: transients = Transients; peaklock = false; longwin = false; shortwin = true; break; + case 0: transients = NoTransients; lamination = false; longwin = true; shortwin = false; break; + case 1: transients = NoTransients; lamination = false; longwin = false; shortwin = false; break; + case 2: transients = NoTransients; lamination = true; longwin = false; shortwin = false; break; + case 3: transients = BandLimitedTransients; lamination = true; longwin = false; shortwin = false; break; + case 4: transients = Transients; lamination = true; longwin = false; shortwin = false; break; + case 5: transients = Transients; lamination = false; longwin = false; shortwin = true; break; }; if (!quiet) { @@ -205,7 +243,7 @@ int main(int argc, char **argv) char *fileName = strdup(argv[optind++]); char *fileNameOut = strdup(argv[optind++]); - + SNDFILE *sndfile; SNDFILE *sndfileOut; SF_INFO sfinfo; @@ -219,6 +257,15 @@ int main(int argc, char **argv) return 1; } + if (duration != 0.0) { + if (sfinfo.frames == 0 || sfinfo.samplerate == 0) { + cerr << "ERROR: File lacks frame count or sample rate in header, cannot use --duration" << endl; + return 1; + } + double induration = double(sfinfo.frames) / double(sfinfo.samplerate); + if (induration != 0.0) ratio = duration / induration; + } + sfinfoOut.channels = sfinfo.channels; sfinfoOut.format = sfinfo.format; sfinfoOut.frames = int(sfinfo.frames * ratio + 0.1); @@ -228,8 +275,8 @@ int main(int argc, char **argv) sndfileOut = sf_open(fileNameOut, SFM_WRITE, &sfinfoOut) ; if (!sndfileOut) { - cerr << "ERROR: Failed to open output file \"" << fileName << "\" for writing: " - << sf_strerror(sndfile) << endl; + cerr << "ERROR: Failed to open output file \"" << fileNameOut << "\" for writing: " + << sf_strerror(sndfileOut) << endl; return 1; } @@ -239,10 +286,11 @@ int main(int argc, char **argv) RubberBandStretcher::Options options = 0; if (realtime) options |= RubberBandStretcher::OptionProcessRealTime; if (precise) options |= RubberBandStretcher::OptionStretchPrecise; - if (!peaklock) options |= RubberBandStretcher::OptionPhaseIndependent; - if (!softening) options |= RubberBandStretcher::OptionPhasePeakLocked; + if (!lamination) options |= RubberBandStretcher::OptionPhaseIndependent; if (longwin) options |= RubberBandStretcher::OptionWindowLong; if (shortwin) options |= RubberBandStretcher::OptionWindowShort; + if (formant) options |= RubberBandStretcher::OptionFormantPreserved; + if (hqpitch) options |= RubberBandStretcher::OptionPitchHighQuality; switch (threading) { case 0: @@ -268,10 +316,13 @@ int main(int argc, char **argv) break; } - if (pitchshift != 1.0) { + if (pitchshift != 0.0) { frequencyshift *= pow(2.0, pitchshift / 12); } + cerr << "Using time ratio " << ratio; + cerr << " and frequency ratio " << frequencyshift << endl; + #ifdef _WIN32 RubberBand:: #endif @@ -471,6 +522,8 @@ int main(int argc, char **argv) cerr << "elapsed time: " << sec << " sec, in frames/sec: " << countIn/sec << ", out frames/sec: " << countOut/sec << endl; } + Profiler::dump(); + return 0; } diff --git a/libs/rubberband/src/rubberband-c.cpp b/libs/rubberband/src/rubberband-c.cpp new file mode 100644 index 0000000000..7bdd701ddf --- /dev/null +++ b/libs/rubberband/src/rubberband-c.cpp @@ -0,0 +1,146 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band + An audio time-stretching and pitch-shifting library. + Copyright 2007-2008 Chris Cannam. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "rubberband-c.h" +#include "RubberBandStretcher.h" + +struct RubberBandState_ +{ + RubberBand::RubberBandStretcher *m_s; +}; + +RubberBandState rubberband_new(unsigned int sampleRate, + unsigned int channels, + RubberBandOptions options, + double initialTimeRatio, + double initialPitchScale) +{ + RubberBandState_ *state = new RubberBandState_(); + state->m_s = new RubberBand::RubberBandStretcher + (sampleRate, channels, options, + initialTimeRatio, initialPitchScale); + return state; +} + +void rubberband_delete(RubberBandState state) +{ + delete state->m_s; + delete state; +} + +void rubberband_reset(RubberBandState state) +{ + state->m_s->reset(); +} + +void rubberband_set_time_ratio(RubberBandState state, double ratio) +{ + state->m_s->setTimeRatio(ratio); +} + +void rubberband_set_pitch_scale(RubberBandState state, double scale) +{ + state->m_s->setPitchScale(scale); +} + +double rubberband_get_time_ratio(const RubberBandState state) +{ + return state->m_s->getTimeRatio(); +} + +double rubberband_get_pitch_scale(const RubberBandState state) +{ + return state->m_s->getPitchScale(); +} + +unsigned int rubberband_get_latency(const RubberBandState state) +{ + return state->m_s->getLatency(); +} + +void rubberband_set_transients_option(RubberBandState state, RubberBandOptions options) +{ + state->m_s->setTransientsOption(options); +} + +void rubberband_set_phase_option(RubberBandState state, RubberBandOptions options) +{ + state->m_s->setPhaseOption(options); +} + +void rubberband_set_formant_option(RubberBandState state, RubberBandOptions options) +{ + state->m_s->setFormantOption(options); +} + +void rubberband_set_pitch_option(RubberBandState state, RubberBandOptions options) +{ + state->m_s->setPitchOption(options); +} + +void rubberband_set_expected_input_duration(RubberBandState state, unsigned int samples) +{ + state->m_s->setExpectedInputDuration(samples); +} + +unsigned int rubberband_get_samples_required(const RubberBandState state) +{ + return state->m_s->getSamplesRequired(); +} + +void rubberband_set_max_process_size(RubberBandState state, unsigned int samples) +{ + state->m_s->setMaxProcessSize(samples); +} + +void rubberband_study(RubberBandState state, const float *const *input, unsigned int samples, int final) +{ + state->m_s->study(input, samples, final != 0); +} + +void rubberband_process(RubberBandState state, const float *const *input, unsigned int samples, int final) +{ + state->m_s->process(input, samples, final != 0); +} + +int rubberband_available(const RubberBandState state) +{ + return state->m_s->available(); +} + +unsigned int rubberband_retrieve(const RubberBandState state, float *const *output, unsigned int samples) +{ + return state->m_s->retrieve(output, samples); +} + +unsigned int rubberband_get_channel_count(const RubberBandState state) +{ + return state->m_s->getChannelCount(); +} + +void rubberband_calculate_stretch(RubberBandState state) +{ + state->m_s->calculateStretch(); +} + +void rubberband_set_debug_level(RubberBandState state, int level) +{ + state->m_s->setDebugLevel(level); +} + +void rubberband_set_default_debug_level(int level) +{ + RubberBand::RubberBandStretcher::setDefaultDebugLevel(level); +} + diff --git a/libs/rubberband/src/sysutils.cpp b/libs/rubberband/src/sysutils.cpp index fc4a17b3e7..b71949c83a 100644 --- a/libs/rubberband/src/sysutils.cpp +++ b/libs/rubberband/src/sysutils.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -27,6 +27,7 @@ #include <iostream> + namespace RubberBand { bool @@ -81,7 +82,7 @@ system_is_multiprocessor() #ifdef _WIN32 -void gettimeofday(struct timeval *tv, void *tz) +int gettimeofday(struct timeval *tv, void *tz) { union { long long ns100; @@ -91,6 +92,7 @@ void gettimeofday(struct timeval *tv, void *tz) ::GetSystemTimeAsFileTime(&now.ft); tv->tv_usec = (long)((now.ns100 / 10LL) % 1000000LL); tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 10000000LL); + return 0; } void usleep(unsigned long usec) @@ -100,6 +102,52 @@ void usleep(unsigned long usec) #endif + +float *allocFloat(float *ptr, int count) +{ + if (ptr) free((void *)ptr); + void *allocated; +#ifndef _WIN32 + if (!posix_memalign(&allocated, 16, count * sizeof(float))) +#endif + allocated = malloc(count * sizeof(float)); + for (int i = 0; i < count; ++i) ((float *)allocated)[i] = 0.f; + return (float *)allocated; +} + +float *allocFloat(int count) +{ + return allocFloat(0, count); +} + +void freeFloat(float *ptr) +{ + if (ptr) free(ptr); +} + +double *allocDouble(double *ptr, int count) +{ + if (ptr) free((void *)ptr); + void *allocated; +#ifndef _WIN32 + if (!posix_memalign(&allocated, 16, count * sizeof(double))) +#endif + allocated = malloc(count * sizeof(double)); + for (int i = 0; i < count; ++i) ((double *)allocated)[i] = 0.f; + return (double *)allocated; +} + +double *allocDouble(int count) +{ + return allocDouble(0, count); +} + +void freeDouble(double *ptr) +{ + if (ptr) free(ptr); +} + + } diff --git a/libs/rubberband/src/sysutils.h b/libs/rubberband/src/sysutils.h index b9dd23e79c..a529afde0d 100644 --- a/libs/rubberband/src/sysutils.h +++ b/libs/rubberband/src/sysutils.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -15,16 +15,48 @@ #ifndef _RUBBERBAND_SYSINFO_H_ #define _RUBBERBAND_SYSINFO_H_ +#ifdef __MSVC__ +#include "bsd-3rdparty/float_cast/float_cast.h" +#define R__ __restrict +#endif + +#ifdef __GNUC__ +#define R__ __restrict__ +#endif + +#ifndef R__ +#define R__ +#endif + +#ifdef __MINGW32__ +#include <malloc.h> +#endif + +#ifdef __MSVC__ +#define alloca _alloca +#endif + namespace RubberBand { extern bool system_is_multiprocessor(); #ifdef _WIN32 + struct timeval { long tv_sec; long tv_usec; }; -void gettimeofday(struct timeval *p, void *tz); +int gettimeofday(struct timeval *p, void *tz); + void usleep(unsigned long); + #endif +extern float *allocFloat(int); +extern float *allocFloat(float *, int); +extern void freeFloat(float *); + +extern double *allocDouble(int); +extern double *allocDouble(double *, int); +extern void freeDouble(double *); + } #endif diff --git a/libs/rubberband/src/vamp/RubberBandVampPlugin.cpp b/libs/rubberband/src/vamp/RubberBandVampPlugin.cpp index 1e9227fac0..feb5bfa6bb 100644 --- a/libs/rubberband/src/vamp/RubberBandVampPlugin.cpp +++ b/libs/rubberband/src/vamp/RubberBandVampPlugin.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -15,6 +15,7 @@ #include "RubberBandVampPlugin.h" #include "StretchCalculator.h" +#include "sysutils.h" #include <cmath> @@ -123,7 +124,7 @@ RubberBandVampPlugin::getDescription() const string RubberBandVampPlugin::getMaker() const { - return "Rubber Band"; ///!!! + return "Breakfast Quay"; } int @@ -159,7 +160,7 @@ RubberBandVampPlugin::getOutputDescriptors() const d.isQuantized = true; d.quantizeStep = 1.0; d.sampleType = OutputDescriptor::VariableSampleRate; - d.sampleRate = rate; + d.sampleRate = float(rate); m_d->m_incrementsOutput = list.size(); list.push_back(d); @@ -182,7 +183,7 @@ RubberBandVampPlugin::getOutputDescriptors() const d.name = "Phase Reset Detection Function"; d.description = "Curve whose peaks are used to identify transients for phase reset points"; d.unit = ""; - d.sampleRate = rate; + d.sampleRate = float(rate); m_d->m_phaseResetDfOutput = list.size(); list.push_back(d); @@ -326,11 +327,11 @@ RubberBandVampPlugin::getParameter(std::string id) const { if (id == "timeratio") return m_d->m_timeRatio * 100.f; if (id == "pitchratio") return m_d->m_pitchRatio * 100.f; - if (id == "mode") return m_d->m_realtime ? 1 : 0; - if (id == "stretchtype") return m_d->m_elasticTiming ? 0 : 1; - if (id == "transientmode") return m_d->m_transientMode; - if (id == "phasemode") return m_d->m_phaseIndependent ? 1 : 0; - if (id == "windowmode") return m_d->m_windowLength; + if (id == "mode") return m_d->m_realtime ? 1.f : 0.f; + if (id == "stretchtype") return m_d->m_elasticTiming ? 0.f : 1.f; + if (id == "transientmode") return float(m_d->m_transientMode); + if (id == "phasemode") return m_d->m_phaseIndependent ? 1.f : 0.f; + if (id == "windowmode") return float(m_d->m_windowLength); return 0.f; } @@ -378,7 +379,7 @@ RubberBandVampPlugin::initialise(size_t channels, size_t stepSize, size_t blockS if (m_d->m_phaseIndependent) options |= RubberBand::RubberBandStretcher::OptionPhaseIndependent; - else options |= RubberBand::RubberBandStretcher::OptionPhasePeakLocked; + else options |= RubberBand::RubberBandStretcher::OptionPhaseLaminar; if (m_d->m_windowLength == 0) options |= RubberBand::RubberBandStretcher::OptionWindowStandard; @@ -565,12 +566,12 @@ RubberBandVampPlugin::Impl::createFeatures(size_t inputIncrement, Feature feature; feature.hasTimestamp = true; feature.timestamp = t; - feature.values.push_back(oi); + feature.values.push_back(float(oi)); feature.label = Vamp::RealTime::frame2RealTime(oi, rate).toText(); features[m_incrementsOutput].push_back(feature); feature.values.clear(); - feature.values.push_back(actual); + feature.values.push_back(float(actual)); feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText(); features[m_aggregateIncrementsOutput].push_back(feature); @@ -594,7 +595,7 @@ RubberBandVampPlugin::Impl::createFeatures(size_t inputIncrement, if (i < phaseResetDf.size()) { feature.values.clear(); feature.values.push_back(phaseResetDf[i]); - sprintf(buf, "%d", baseCount + i); + sprintf(buf, "%d", int(baseCount + i)); feature.label = buf; features[m_phaseResetDfOutput].push_back(feature); } @@ -626,7 +627,7 @@ RubberBandVampPlugin::Impl::createFeatures(size_t inputIncrement, feature.timestamp = t; feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText(); feature.values.clear(); - feature.values.push_back(actual); + feature.values.push_back(float(actual)); features[m_aggregateIncrementsOutput].push_back(feature); float linear = ((baseCount + outputIncrements.size()) diff --git a/libs/rubberband/src/vamp/RubberBandVampPlugin.h b/libs/rubberband/src/vamp/RubberBandVampPlugin.h index f850a282da..f062e35eea 100644 --- a/libs/rubberband/src/vamp/RubberBandVampPlugin.h +++ b/libs/rubberband/src/vamp/RubberBandVampPlugin.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as diff --git a/libs/rubberband/src/vamp/libmain.cpp b/libs/rubberband/src/vamp/libmain.cpp index a535c2008c..1b4185130d 100644 --- a/libs/rubberband/src/vamp/libmain.cpp +++ b/libs/rubberband/src/vamp/libmain.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + Copyright 2007-2008 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as |