diff --git a/Doxyfile b/Doxyfile index 4bbbc66..649b309 100644 --- a/Doxyfile +++ b/Doxyfile @@ -25,13 +25,13 @@ DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. -PROJECT_NAME = "Rubber Band" +PROJECT_NAME = "Rubber Band Library" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 1.6 +PROJECT_NUMBER = 1.7 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/configure.ac b/configure.ac index 95bb045..1ff8688 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT(RubberBand, 1.6, cannam@all-day-breakfast.com) +AC_INIT(RubberBand, 1.7, cannam@all-day-breakfast.com) AC_CONFIG_SRCDIR(src/StretcherImpl.h) AC_PROG_CXX diff --git a/main/main.cpp b/main/main.cpp index 4661300..c977423 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -79,6 +79,7 @@ int main(int argc, char **argv) bool smoothing = false; bool hqpitch = false; bool formant = false; + bool together = false; bool crispchanged = false; int crispness = -1; bool help = false; @@ -122,6 +123,7 @@ int main(int argc, char **argv) { "no-threads", 0, 0, '0' }, { "no-transients", 0, 0, '1' }, { "no-lamination", 0, 0, '2' }, + { "centre-focus", 0, 0, '7' }, { "window-long", 0, 0, '3' }, { "window-short", 0, 0, '4' }, { "bl-transients", 0, 0, '8' }, @@ -161,6 +163,7 @@ int main(int argc, char **argv) case '4': shortwin = true; crispchanged = true; break; case '5': detector = PercussiveDetector; crispchanged = true; break; case '6': detector = SoftDetector; crispchanged = true; break; + case '7': together = true; break; case '8': transients = BandLimitedTransients; crispchanged = true; break; case '9': smoothing = true; crispchanged = true; break; case '%': hqpitch = true; break; @@ -226,6 +229,8 @@ int main(int argc, char **argv) cerr << " --detector-perc Use percussive transient detector (as in pre-1.5)" << endl; cerr << " --detector-soft Use soft transient detector" << endl; cerr << " --pitch-hq In RT mode, use a slower, higher quality pitch shift" << endl; + cerr << " --centre-focus Preserve focus of centre material in stereo" << endl; + cerr << " (at a cost in width and individual channel quality)" << endl; cerr << endl; cerr << " -d, --debug 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; @@ -304,7 +309,9 @@ int main(int argc, char **argv) while (i < line.length() && line[i] == ' ') ++i; size_t target = atoi(line.substr(i).c_str()); mapping[source] = target; - cerr << "adding mapping from " << source << " to " << target << endl; + if (debug > 0) { + cerr << "adding mapping from " << source << " to " << target << endl; + } ++lineno; } ifile.close(); @@ -365,6 +372,7 @@ int main(int argc, char **argv) if (smoothing) options |= RubberBandStretcher::OptionSmoothingOn; if (formant) options |= RubberBandStretcher::OptionFormantPreserved; if (hqpitch) options |= RubberBandStretcher::OptionPitchHighQuality; + if (together) options |= RubberBandStretcher::OptionChannelsTogether; switch (threading) { case 0: @@ -498,6 +506,10 @@ int main(int argc, char **argv) bool final = (frame + ibs >= sfinfo.frames); + if (debug > 2) { + cerr << "count = " << count << ", ibs = " << ibs << ", frame = " << frame << ", frames = " << sfinfo.frames << ", final = " << final << endl; + } + ts.process(ibuf, count, final); int avail = ts.available(); diff --git a/rubberband/RubberBandStretcher.h b/rubberband/RubberBandStretcher.h index 1b99c00..a1d75a7 100644 --- a/rubberband/RubberBandStretcher.h +++ b/rubberband/RubberBandStretcher.h @@ -15,9 +15,9 @@ #ifndef _RUBBERBANDSTRETCHER_H_ #define _RUBBERBANDSTRETCHER_H_ -#define RUBBERBAND_VERSION "1.6-gpl" +#define RUBBERBAND_VERSION "1.7-gpl" #define RUBBERBAND_API_MAJOR_VERSION 2 -#define RUBBERBAND_API_MINOR_VERSION 4 +#define RUBBERBAND_API_MINOR_VERSION 5 #include #include @@ -110,7 +110,9 @@ public: * percussive event). This, the default setting, usually * results in a clear-sounding output; but it is not always * consistent, and may cause interruptions in stable sounds - * present at the same time as transient events. + * present at the same time as transient events. The + * OptionDetector flags (below) can be used to tune this to some + * extent. * * \li \c OptionTransientsMixed - Reset component phases at the * peak of each transient, outside a frequency range typical of @@ -132,7 +134,7 @@ public: * * \li \c OptionDetectorCompound - Use a general-purpose * transient detector which is likely to be good for most - * situations. + * situations. This is the default. * * \li \c OptionDetectorPercussive - Detect percussive * transients. Note that this was the default and only option @@ -166,7 +168,8 @@ public: * determine its own threading model. Usually this means using * one processing thread per audio channel in offline mode if * the stretcher is able to determine that more than one CPU is - * available, and one thread only in realtime mode. + * available, and one thread only in realtime mode. This is the + * defafult. * * \li \c OptionThreadingNever - Never use more than one thread. * @@ -197,11 +200,13 @@ public: * not be changed after construction. * * \li \c OptionSmoothingOff - Do not use time-domain smoothing. + * This is the default. * - * \li \c OptionSmoothingOn - Use time-domain smoothing. - * This will result in a softer sound, but it may be - * appropriate for longer stretches of some instruments and - * can mix well with OptionWindowShort. + * \li \c OptionSmoothingOn - Use time-domain smoothing. This + * will result in a softer sound with some audible artifacts + * around sharp transients, but it may be appropriate for longer + * stretches of some instruments and can mix well with + * OptionWindowShort. * * 9. Flags prefixed \c OptionFormant control the handling of * formant shape (spectral envelope) when pitch-shifting. These @@ -209,7 +214,7 @@ public: * * \li \c OptionFormantShifted - Apply no special formant * processing. The spectral envelope will be pitch shifted as - * normal. + * normal. This is the default. * * \li \c OptionFormantPreserved - Preserve the spectral * envelope of the unshifted signal. This permits shifting the @@ -224,7 +229,7 @@ public: * \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. + * for large pitch shifts. This is the default. * \li \c OptionPitchHighQuality - Use the highest quality * method for pitch shifting. This method has a CPU cost @@ -236,6 +241,26 @@ public: * 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. + * + * 11. Flags prefixed \c OptionChannels control the method used for + * processing two-channel audio. These options may not be changed + * after construction. + * + * \li \c OptionChannelsApart - Each channel is processed + * individually, though timing is synchronised and phases are + * synchronised at transients (depending on the OptionTransients + * setting). This gives the highest quality for the individual + * channels but a relative lack of stereo focus and unrealistic + * increase in "width". This is the default. + * + * \li \c OptionChannelsTogether - The first two channels (where + * two or more are present) are considered to be a stereo pair + * and are processed in mid-side format; mid and side are + * processed individually, with timing synchronised and phases + * synchronised at transients (depending on the OptionTransients + * setting). This usually leads to better focus in the centre + * but a loss of stereo space and width. Any channels beyond + * the first two are processed individually. */ enum Option { @@ -273,7 +298,12 @@ public: OptionPitchHighSpeed = 0x00000000, OptionPitchHighQuality = 0x02000000, - OptionPitchHighConsistency = 0x04000000 + OptionPitchHighConsistency = 0x04000000, + + OptionChannelsApart = 0x00000000, + OptionChannelsTogether = 0x10000000, + + // n.b. Options is int, so we must stop before 0x80000000 }; typedef int Options; @@ -431,27 +461,21 @@ public: */ void setExpectedInputDuration(size_t samples); - /** - * Ask the stretcher how many audio sample frames should be - * provided as input in order to ensure that some more output - * becomes available. Normal usage consists of querying this - * function, providing that number of samples to process(), - * reading the output using available() and retrieve(), and then - * repeating. - * - * Note that this value is only relevant to process(), not to - * study() (to which you may pass any number of samples at a time, - * and from which there is no output). - */ - size_t getSamplesRequired() const; - /** * Tell the stretcher the maximum number of sample frames that you - * will ever be passing in to a single process() call. If you - * don't call this function, the stretcher will assume that you - * never pass in more samples than getSamplesRequired() suggested - * you should. You should not pass in more samples than that - * unless you have called setMaxProcessSize first. + * will ever be passing in to a single process() call. If you + * don't call this, the stretcher will assume that you are calling + * getSamplesRequired() at each cycle and are never passing more + * samples than are suggested by that function. + * + * If your application has some external constraint that means you + * prefer a fixed block size, then your normal mode of operation + * would be to provide that block size to this function; to loop + * calling process() with that size of block; after each call to + * process(), test whether output has been generated by calling + * available(); and, if so, call retrieve() to obtain it. See + * getSamplesRequired() for a more suitable operating mode for + * applications without such external constraints. * * This function may not be called after the first call to study() * or process(). @@ -462,6 +486,26 @@ public: */ void setMaxProcessSize(size_t samples); + /** + * Ask the stretcher how many audio sample frames should be + * provided as input in order to ensure that some more output + * becomes available. + * + * If your application has no particular constraint on processing + * block size and you are able to provide any block size as input + * for each cycle, then your normal mode of operation would be to + * loop querying this function; providing that number of samples + * to process(); and reading the output using available() and + * retrieve(). See setMaxProcessSize() for a more suitable + * operating mode for applications that do have external block + * size constraints. + * + * Note that this value is only relevant to process(), not to + * study() (to which you may pass any number of samples at a time, + * and from which there is no output). + */ + size_t getSamplesRequired() const; + /** * Provide a set of mappings from "before" to "after" sample * numbers so as to enforce a particular stretch profile. The diff --git a/rubberband/rubberband-c.h b/rubberband/rubberband-c.h index 54a7a0a..6b74783 100644 --- a/rubberband/rubberband-c.h +++ b/rubberband/rubberband-c.h @@ -19,9 +19,9 @@ extern "C" { #endif -#define RUBBERBAND_VERSION "1.6-gpl" +#define RUBBERBAND_VERSION "1.7-gpl" #define RUBBERBAND_API_MAJOR_VERSION 2 -#define RUBBERBAND_API_MINOR_VERSION 4 +#define RUBBERBAND_API_MINOR_VERSION 5 /** * This is a C-linkage interface to the Rubber Band time stretcher. @@ -71,7 +71,10 @@ enum RubberBandOption { RubberBandOptionPitchHighQuality = 0x00000000, RubberBandOptionPitchHighSpeed = 0x02000000, - RubberBandOptionPitchHighConsistency = 0x04000000 + RubberBandOptionPitchHighConsistency = 0x04000000, + + RubberBandOptionChannelsApart = 0x00000000, + RubberBandOptionChannelsTogether = 0x10000000, }; typedef int RubberBandOptions; diff --git a/src/RubberBandStretcher.cpp b/src/RubberBandStretcher.cpp index a7c78a9..0c24f47 100644 --- a/src/RubberBandStretcher.cpp +++ b/src/RubberBandStretcher.cpp @@ -14,6 +14,8 @@ #include "StretcherImpl.h" +using namespace std; + namespace RubberBand { @@ -111,7 +113,7 @@ RubberBandStretcher::setMaxProcessSize(size_t samples) } void -RubberBandStretcher::setKeyFrameMap(const std::map &mapping) +RubberBandStretcher::setKeyFrameMap(const map &mapping) { m_d->setKeyFrameMap(mapping); } @@ -166,19 +168,19 @@ RubberBandStretcher::getInputIncrement() const return m_d->getInputIncrement(); } -std::vector +vector RubberBandStretcher::getOutputIncrements() const { return m_d->getOutputIncrements(); } -std::vector +vector RubberBandStretcher::getPhaseResetCurve() const { return m_d->getPhaseResetCurve(); } -std::vector +vector RubberBandStretcher::getExactTimePoints() const { return m_d->getExactTimePoints(); diff --git a/src/StretcherChannelData.cpp b/src/StretcherChannelData.cpp index 927f983..4ccc6fa 100644 --- a/src/StretcherChannelData.cpp +++ b/src/StretcherChannelData.cpp @@ -43,9 +43,11 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set &sizes, size_t initialFftSize, size_t outbufSize) { - size_t maxSize = initialWindowSize; + size_t maxSize = initialWindowSize * 2; if (initialFftSize > maxSize) maxSize = initialFftSize; +// std::cerr << "ChannelData::construct: initialWindowSize = " << initialWindowSize << ", initialFftSize = " << initialFftSize << ", outbufSize = " << outbufSize << std::endl; + // std::set is ordered by value std::set::const_iterator i = sizes.end(); if (i != sizes.begin()) { @@ -56,7 +58,7 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set &sizes, // max possible size of the real "half" of freq data size_t realSize = maxSize / 2 + 1; -// std::cerr << "ChannelData::construct([" << windowSizes.size() << "], " << maxSize << ", " << outbufSize << ")" << std::endl; +// std::cerr << "ChannelData::construct([" << sizes.size() << "], " << maxSize << ", " << realSize << ", " << outbufSize << ")" << std::endl; if (outbufSize < maxSize) outbufSize = maxSize; @@ -106,7 +108,9 @@ void RubberBandStretcher::Impl::ChannelData::setSizes(size_t windowSize, size_t fftSize) { - size_t maxSize = std::max(windowSize, fftSize); +// std::cerr << "ChannelData::setSizes: windowSize = " << windowSize << ", fftSize = " << fftSize << std::endl; + + size_t maxSize = 2 * std::max(windowSize, fftSize); size_t realSize = maxSize / 2 + 1; size_t oldMax = inbuf->getSize(); size_t oldReal = oldMax / 2 + 1; diff --git a/src/StretcherImpl.cpp b/src/StretcherImpl.cpp index 046db01..01d8461 100644 --- a/src/StretcherImpl.cpp +++ b/src/StretcherImpl.cpp @@ -699,6 +699,9 @@ RubberBandStretcher::Impl::configure() // want gaps when the ratio changes. if (!m_realtime) { + if (m_debugLevel > 1) { + cerr << "Not real time mode: prefilling" << endl; + } for (size_t c = 0; c < m_channels; ++c) { m_channelData[c]->reset(); m_channelData[c]->inbuf->zero(m_aWindowSize/2); @@ -953,8 +956,9 @@ RubberBandStretcher::Impl::study(const float *const *input, size_t samples, bool // cd.accumulator is not otherwise used during studying, // so we can use it as a temporary buffer here - size_t got = inbuf.peek(cd.accumulator, m_aWindowSize); - assert(final || got == m_aWindowSize); + size_t ready = inbuf.getReadSpace(); + assert(final || ready >= m_aWindowSize); + inbuf.peek(cd.accumulator, std::min(ready, m_aWindowSize)); if (m_aWindowSize == m_fftSize) { @@ -1149,8 +1153,22 @@ RubberBandStretcher::Impl::getSamplesRequired() const ChannelData &cd = *m_channelData[c]; RingBuffer &inbuf = *cd.inbuf; + RingBuffer &outbuf = *cd.outbuf; size_t rs = inbuf.getReadSpace(); + size_t ws = outbuf.getReadSpace(); + + if (m_debugLevel > 2) { + cerr << "getSamplesRequired: ws = " << ws << ", rs = " << rs << ", m_aWindowSize = " << m_aWindowSize << endl; + } + + // We should never return zero in non-threaded modes if + // available() would also return zero, i.e. if ws == 0. If we + // do that, nothing will ever happen again! We need to demand + // at least one increment (i.e. a nominal amount) to feed the + // engine. + + if (ws == 0 && reqd == 0) reqd = m_increment; // See notes in testInbufReadSpace @@ -1186,13 +1204,22 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo if (m_mode == JustCreated || m_mode == Studying) { if (m_mode == Studying) { + calculateStretch(); + + if (!m_realtime) { + // See note in configure() above. Of course, we should + // never enter Studying unless we are non-RT anyway + if (m_debugLevel > 1) { + cerr << "Not real time mode: prefilling" << endl; + } + for (size_t c = 0; c < m_channels; ++c) { + m_channelData[c]->reset(); + m_channelData[c]->inbuf->zero(m_aWindowSize/2); + } + } } - for (size_t c = 0; c < m_channels; ++c) { - m_channelData[c]->reset(); - m_channelData[c]->inbuf->zero(m_aWindowSize/2); - } if (m_threaded) { MutexLocker locker(&m_threadSetMutex); @@ -1236,7 +1263,8 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo for (size_t c = 0; c < m_channels; ++c) { consumed[c] += consumeChannel(c, - input[c] + consumed[c], + input, + consumed[c], samples - consumed[c], final); if (consumed[c] < samples) { @@ -1284,11 +1312,14 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo */ } -// if (!allConsumed) cerr << "process looping" << endl; - + if (m_debugLevel > 2) { + if (!allConsumed) cerr << "process looping" << endl; + } + } + + if (m_debugLevel > 2) { + cerr << "process returning" << endl; } - -// cerr << "process returning" << endl; if (final) m_mode = Finished; } diff --git a/src/StretcherImpl.h b/src/StretcherImpl.h index 59ac21d..2cae410 100644 --- a/src/StretcherImpl.h +++ b/src/StretcherImpl.h @@ -23,6 +23,7 @@ #include "dsp/CompoundAudioCurve.h" #include "base/RingBuffer.h" +#include "base/Scavenger.h" #include "system/Thread.h" #include "system/sysutils.h" @@ -96,8 +97,10 @@ protected: size_t m_sampleRate; size_t m_channels; - size_t consumeChannel(size_t channel, const float *input, - size_t samples, bool final); + void prepareChannelMS(size_t channel, const float *const *inputs, + size_t offset, size_t samples, float *prepared); + size_t consumeChannel(size_t channel, const float *const *inputs, + size_t offset, 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, diff --git a/src/StretcherProcess.cpp b/src/StretcherProcess.cpp index 2504ccb..32a9708 100644 --- a/src/StretcherProcess.cpp +++ b/src/StretcherProcess.cpp @@ -132,10 +132,33 @@ RubberBandStretcher::Impl::resampleBeforeStretching() const return (m_pitchScale > 1.0); // better performance } } + +void +RubberBandStretcher::Impl::prepareChannelMS(size_t c, + const float *const *inputs, + size_t offset, + size_t samples, + float *prepared) +{ + for (size_t i = 0; i < samples; ++i) { + float left = inputs[0][i + offset]; + float right = inputs[1][i + offset]; + float mid = (left + right) / 2; + float side = (left - right) / 2; + if (c == 0) { + prepared[i] = mid; + } else { + prepared[i] = side; + } + } +} size_t -RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input, - size_t samples, bool final) +RubberBandStretcher::Impl::consumeChannel(size_t c, + const float *const *inputs, + size_t offset, + size_t samples, + bool final) { Profiler profiler("RubberBandStretcher::Impl::consumeChannel"); @@ -147,6 +170,13 @@ RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input, bool resampling = resampleBeforeStretching(); + float *ms = 0; + const float *input = 0; + + bool useMidSide = ((m_options & OptionChannelsTogether) && + (m_channels >= 2) && + (c < 2)); + if (resampling) { toWrite = int(ceil(samples / m_pitchScale)); @@ -163,6 +193,14 @@ RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input, } + if (useMidSide) { + ms = (float *)alloca(samples * sizeof(float)); + prepareChannelMS(c, inputs, offset, samples, ms); + input = ms; + } else { + input = inputs[c] + offset; + } + toWrite = cd.resampler->resample(&input, &cd.resamplebuf, samples, @@ -179,10 +217,21 @@ RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input, } if (resampling) { + inbuf.write(cd.resamplebuf, toWrite); cd.inCount += samples; return samples; + } else { + + if (useMidSide) { + ms = (float *)alloca(toWrite * sizeof(float)); + prepareChannelMS(c, inputs, offset, toWrite, ms); + input = ms; + } else { + input = inputs[c] + offset; + } + inbuf.write(input, toWrite); cd.inCount += toWrite; return toWrite; @@ -208,15 +257,18 @@ RubberBandStretcher::Impl::processChunks(size_t c, bool &any, bool &last) while (!last) { if (!testInbufReadSpace(c)) { -// cerr << "not enough input" << endl; + if (m_debugLevel > 2) { + cerr << "processChunks: out of input" << endl; + } break; } any = true; if (!cd.draining) { - size_t got = cd.inbuf->peek(cd.fltbuf, m_aWindowSize); - assert(got == m_aWindowSize || cd.inputSize >= 0); + size_t ready = cd.inbuf->getReadSpace(); + assert(ready >= m_aWindowSize || cd.inputSize >= 0); + cd.inbuf->peek(cd.fltbuf, std::min(ready, m_aWindowSize)); cd.inbuf->skip(m_increment); } @@ -267,11 +319,17 @@ RubberBandStretcher::Impl::processOneChunk() // This is the normal process method in RT mode. for (size_t c = 0; c < m_channels; ++c) { - if (!testInbufReadSpace(c)) return false; + if (!testInbufReadSpace(c)) { + if (m_debugLevel > 2) { + cerr << "processOneChunk: out of input" << endl; + } + return false; + } ChannelData &cd = *m_channelData[c]; if (!cd.draining) { - size_t got = cd.inbuf->peek(cd.fltbuf, m_aWindowSize); - assert(got == m_aWindowSize || cd.inputSize >= 0); + size_t ready = cd.inbuf->getReadSpace(); + assert(ready >= m_aWindowSize || cd.inputSize >= 0); + cd.inbuf->peek(cd.fltbuf, std::min(ready, m_aWindowSize)); cd.inbuf->skip(m_increment); analyseChunk(c); } @@ -314,9 +372,11 @@ RubberBandStretcher::Impl::testInbufReadSpace(size_t c) // we know there is more input to come. if (!m_threaded) { -// cerr << "WARNING: RubberBandStretcher: read space < chunk size (" -// << inbuf.getReadSpace() << " < " << m_windowSize -// << ") when not all input written, on processChunks for channel " << c << endl; + if (m_debugLevel > 1) { + cerr << "WARNING: RubberBandStretcher: read space < chunk size (" + << inbuf.getReadSpace() << " < " << m_aWindowSize + << ") when not all input written, on processChunks for channel " << c << endl; + } } return false; } @@ -520,8 +580,12 @@ RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn, int incr = m_stretchCalculator->calculateSingle (getEffectiveRatio(), df, m_increment); - m_lastProcessPhaseResetDf.write(&df, 1); - m_lastProcessOutputIncrements.write(&incr, 1); + if (m_lastProcessPhaseResetDf.getWriteSpace() > 0) { + m_lastProcessPhaseResetDf.write(&df, 1); + } + if (m_lastProcessOutputIncrements.getWriteSpace() > 0) { + m_lastProcessOutputIncrements.write(&incr, 1); + } if (incr < 0) { phaseReset = true; @@ -1103,7 +1167,9 @@ RubberBandStretcher::Impl::available() const if (m_channelData[c]->inputSize >= 0) { // cerr << "available: m_done true" << endl; if (m_channelData[c]->inbuf->getReadSpace() > 0) { -// cerr << "calling processChunks(" << c << ") from available" << endl; + if (m_debugLevel > 1) { + cerr << "calling processChunks(" << c << ") from available" << endl; + } //!!! do we ever actually do this? if so, this method should not be const // ^^^ yes, we do sometimes -- e.g. when fed a very short file bool any = false, last = false; @@ -1154,6 +1220,17 @@ RubberBandStretcher::Impl::retrieve(float *const *output, size_t samples) const } } + if ((m_options & OptionChannelsTogether) && (m_channels >= 2)) { + for (size_t i = 0; i < got; ++i) { + float mid = output[0][i]; + float side = output[1][i]; + float left = mid + side; + float right = mid - side; + output[0][i] = left; + output[1][i] = right; + } + } + return got; } diff --git a/src/base/RingBuffer.h b/src/base/RingBuffer.h index 87f9641..faea993 100644 --- a/src/base/RingBuffer.h +++ b/src/base/RingBuffer.h @@ -17,25 +17,24 @@ #include -#include "Scavenger.h" - //#define DEBUG_RINGBUFFER 1 #include "system/sysutils.h" #include "system/Allocators.h" -#ifdef DEBUG_RINGBUFFER #include -#endif namespace RubberBand { /** - * RingBuffer implements a lock-free ring buffer for one writer and N - * readers, that is to be used to store a sample type T. + * RingBuffer implements a lock-free ring buffer for one writer and + * one reader, that is to be used to store a sample type T. + * + * RingBuffer is thread-safe provided only one thread writes and only + * one thread reads. */ -template +template class RingBuffer { public: @@ -58,14 +57,6 @@ public: */ int getSize() const; - /** - * Resize the ring buffer. This also empties it; use resized() - * below if you do not want this to happen. Actually swaps in a - * new, larger buffer; the old buffer is scavenged after a seemly - * delay. Should be called from the write thread. - */ - void resize(int newSize); - /** * Return a new ring buffer (allocated with "new" -- called must * delete when no longer needed) of the given size, containing the @@ -74,7 +65,7 @@ public: * or inconsistent. If this buffer's data will not fit in the new * size, the contents are undefined. */ - RingBuffer *resized(int newSize, int R = 0) const; + RingBuffer *resized(int newSize) const; /** * Lock the ring buffer into physical memory. Returns true @@ -92,7 +83,7 @@ public: * Return the amount of data available for reading by reader R, in * samples. */ - int getReadSpace(int R = 0) const; + int getReadSpace() const; /** * Return the amount of space available for writing, in samples. @@ -106,18 +97,23 @@ public: * * This is a template function, taking an argument S for the target * sample type, which is permitted to differ from T if the two - * types are compatible for assignment. + * types are compatible for arithmetic operations. */ template - int read(S *const R__ destination, int n, int R = 0); + int read(S *const R__ destination, int n); /** * Read n samples from the buffer, for reader R, adding them to * the destination. If fewer than n are available, the remainder * will be left alone. Returns the number of samples actually * read. + * + * This is a template function, taking an argument S for the target + * sample type, which is permitted to differ from T if the two + * types are compatible for arithmetic operations. */ - int readAdding(T *const R__ destination, int n, int R = 0); + template + int readAdding(S *const R__ destination, int n); /** * Read one sample from the buffer, for reader R. If no sample is @@ -126,7 +122,7 @@ public: * may be good enough if you don't want to allocate a buffer to * read into. */ - T readOne(int R = 0); + T readOne(); /** * Read n samples from the buffer, if available, for reader R, @@ -135,7 +131,7 @@ public: * n are available, the remainder will be zeroed out. Returns the * number of samples actually read. */ - int peek(T *const R__ destination, int n, int R = 0) const; + int peek(T *const R__ destination, int n) const; /** * Read one sample from the buffer, if available, without @@ -143,7 +139,7 @@ public: * skip() will be necessary to empty the buffer. Returns zero if * no sample was available. */ - T peekOne(int R = 0) const; + T peekOne() const; /** * Pretend to read n samples from the buffer, for reader R, @@ -151,7 +147,7 @@ public: * samples). Returns the number of samples actually available for * discarding. */ - int skip(int n, int R = 0); + int skip(int n); /** * Write n samples to the buffer. If insufficient space is @@ -173,101 +169,78 @@ public: int zero(int n); protected: - T *R__ m_buffer; - volatile int m_writer; - volatile int m_readers[N]; - int m_size; - bool m_mlocked; + T *const R__ m_buffer; + int m_writer; + int m_reader; + const int m_size; + bool m_mlocked; - static Scavenger > m_scavenger; + int readSpaceFor(int w, int r) const { + int space; + if (w > r) space = w - r; + else if (w < r) space = (w + m_size) - r; + else space = 0; + return space; + } + + int writeSpaceFor(int w, int r) const { + int space = (r + m_size - w - 1); + if (space >= m_size) space -= m_size; + return space; + } private: RingBuffer(const RingBuffer &); // not provided RingBuffer &operator=(const RingBuffer &); // not provided }; -template -Scavenger > RingBuffer::m_scavenger; - -template -RingBuffer::RingBuffer(int n) : +template +RingBuffer::RingBuffer(int n) : + m_buffer(allocate(n + 1)), m_writer(0), m_size(n + 1), m_mlocked(false) { - m_buffer = allocate(n + 1); - #ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::RingBuffer(" << n << ")" << std::endl; + std::cerr << "RingBuffer[" << this << "]::RingBuffer(" << n << ")" << std::endl; #endif - for (int i = 0; i < N; ++i) m_readers[i] = 0; - - m_scavenger.scavenge(); + m_reader = 0; } -template -RingBuffer::~RingBuffer() +template +RingBuffer::~RingBuffer() { #ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::~RingBuffer" << std::endl; + std::cerr << "RingBuffer[" << this << "]::~RingBuffer" << std::endl; #endif if (m_mlocked) { MUNLOCK((void *)m_buffer, m_size * sizeof(T)); } - deallocate(m_buffer); - - m_scavenger.scavenge(); + deallocate(m_buffer); } -template +template int -RingBuffer::getSize() const +RingBuffer::getSize() const { #ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::getSize(): " << m_size-1 << std::endl; + std::cerr << "RingBuffer[" << this << "]::getSize(): " << m_size-1 << std::endl; #endif return m_size - 1; } -template -void -RingBuffer::resize(int newSize) +template +RingBuffer * +RingBuffer::resized(int newSize) const { -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::resize(" << newSize << ")" << std::endl; -#endif - - m_scavenger.scavenge(); - - if (m_mlocked) { - MUNLOCK((void *)m_buffer, m_size * sizeof(T)); - } - - m_scavenger.claim(new ScavengerArrayWrapper(m_buffer)); - - reset(); - m_buffer = allocate(newSize + 1); - m_size = newSize + 1; - - if (m_mlocked) { - if (MLOCK((void *)m_buffer, m_size * sizeof(T))) { - m_mlocked = false; - } - } -} - -template -RingBuffer * -RingBuffer::resized(int newSize, int R) const -{ - RingBuffer *newBuffer = new RingBuffer(newSize); + RingBuffer *newBuffer = new RingBuffer(newSize); int w = m_writer; - int r = m_readers[R]; + int r = m_reader; while (r != w) { T value = m_buffer[r]; @@ -278,143 +251,95 @@ RingBuffer::resized(int newSize, int R) const return newBuffer; } -template +template bool -RingBuffer::mlock() +RingBuffer::mlock() { if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false; m_mlocked = true; return true; } -template +template void -RingBuffer::reset() +RingBuffer::reset() { #ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::reset" << std::endl; + std::cerr << "RingBuffer[" << this << "]::reset" << std::endl; #endif m_writer = 0; - for (int i = 0; i < N; ++i) m_readers[i] = 0; + m_reader = 0; } -template +template int -RingBuffer::getReadSpace(int R) const +RingBuffer::getReadSpace() const { - int writer = m_writer; - int reader = m_readers[R]; - int space; - -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::getReadSpace(" << R << "): reader " << reader << ", writer " << writer << std::endl; -#endif - - if (writer > reader) space = writer - reader; - else if (writer < reader) space = (writer + m_size) - reader; - else space = 0; - -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::getReadSpace(" << R << "): " << space << std::endl; -#endif - - return space; + return readSpaceFor(m_writer, m_reader); } -template +template int -RingBuffer::getWriteSpace() const +RingBuffer::getWriteSpace() const { - int space = 0; - for (int i = 0; i < N; ++i) { - 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 - 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; - std::cerr << "RingBuffer: reader " << rp << ", writer " << m_writer << std::endl; -#endif - -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::getWriteSpace(): " << space << std::endl; -#endif - - return space; + return writeSpaceFor(m_writer, m_reader); } -template +template template int -RingBuffer::read(S *const R__ destination, int n, int R) +RingBuffer::read(S *const R__ destination, int n) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl; -#endif + int w = m_writer; + int r = m_reader; - int available = getReadSpace(R); + int available = readSpaceFor(w, r); if (n > available) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only " << available << " samples available" - << std::endl; -#endif - for (int i = available; i < n; ++i) { - destination[i] = S(0); - } + std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only " + << available << " available" << std::endl; + v_zero(destination + available, n - available); n = available; } if (n == 0) return n; - int reader = m_readers[R]; - int here = m_size - reader; - T *const R__ bufbase = m_buffer + reader; + int here = m_size - r; + T *const R__ bufbase = m_buffer + r; if (here >= n) { - v_convert(destination, bufbase, n); + v_convert(destination, bufbase, n); } else { - v_convert(destination, bufbase, here); - v_convert(destination + here, m_buffer, n - here); + v_convert(destination, bufbase, here); + v_convert(destination + here, m_buffer, n - here); } - reader += n; - while (reader >= m_size) reader -= m_size; - m_readers[R] = reader; + r += n; + while (r >= m_size) r -= m_size; -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl; -#endif + MBARRIER(); + m_reader = r; return n; } -template +template +template int -RingBuffer::readAdding(T *const R__ destination, int n, int R) +RingBuffer::readAdding(S *const R__ destination, int n) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl; -#endif + int w = m_writer; + int r = m_reader; - int available = getReadSpace(R); + int available = readSpaceFor(w, r); if (n > available) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only " << available << " samples available" - << std::endl; -#endif + std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only " + << available << " available" << std::endl; n = available; } if (n == 0) return n; - int reader = m_readers[R]; - int here = m_size - reader; - const T *const R__ bufbase = m_buffer + reader; + int here = m_size - r; + T *const R__ bufbase = m_buffer + r; if (here >= n) { v_add(destination, bufbase, n); @@ -423,56 +348,55 @@ RingBuffer::readAdding(T *const R__ destination, int n, int R) v_add(destination + here, m_buffer, n - here); } - reader += n; - while (reader >= m_size) reader -= m_size; - m_readers[R] = reader; + r += n; + while (r >= m_size) r -= m_size; + + MBARRIER(); + m_reader = r; + return n; } -template +template T -RingBuffer::readOne(int R) +RingBuffer::readOne() { -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::readOne(" << R << ")" << std::endl; -#endif + int w = m_writer; + int r = m_reader; - if (m_writer == m_readers[R]) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: No sample available" + if (w == r) { + std::cerr << "WARNING: RingBuffer::readOne: no sample available" << std::endl; -#endif return 0; } - int reader = m_readers[R]; - T value = m_buffer[reader]; - if (++reader == m_size) reader = 0; - m_readers[R] = reader; + + T value = m_buffer[r]; + if (++r == m_size) r = 0; + + MBARRIER(); + m_reader = r; + return value; } -template +template int -RingBuffer::peek(T *const R__ destination, int n, int R) const +RingBuffer::peek(T *const R__ destination, int n) const { -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl; -#endif + int w = m_writer; + int r = m_reader; - int available = getReadSpace(R); + int available = readSpaceFor(w, r); if (n > available) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only " << available << " samples available" - << std::endl; -#endif + std::cerr << "WARNING: RingBuffer::peek: " << n << " requested, only " + << available << " available" << std::endl; memset(destination + available, 0, (n - available) * sizeof(T)); n = available; } if (n == 0) return n; - int reader = m_readers[R]; - int here = m_size - reader; - const T *const R__ bufbase = m_buffer + reader; + int here = m_size - r; + const T *const R__ bufbase = m_buffer + r; if (here >= n) { v_copy(destination, bufbase, n); @@ -481,79 +405,68 @@ RingBuffer::peek(T *const R__ destination, int n, int R) const v_copy(destination + here, m_buffer, n - here); } -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::peek: read " << n << std::endl; -#endif - return n; } -template +template T -RingBuffer::peekOne(int R) const +RingBuffer::peekOne() const { -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::peek(" << R << ")" << std::endl; -#endif + int w = m_writer; + int r = m_reader; - if (m_writer == m_readers[R]) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: No sample available" + if (w == r) { + std::cerr << "WARNING: RingBuffer::peekOne: no sample available" << std::endl; -#endif return 0; } - T value = m_buffer[m_readers[R]]; + + T value = m_buffer[r]; return value; } -template +template int -RingBuffer::skip(int n, int R) +RingBuffer::skip(int n) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::skip(" << n << ", " << R << ")" << std::endl; -#endif + int w = m_writer; + int r = m_reader; - int available = getReadSpace(R); + int available = readSpaceFor(w, r); if (n > available) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only " << available << " samples available" - << std::endl; -#endif + std::cerr << "WARNING: RingBuffer::skip: " << n << " requested, only " + << available << " available" << std::endl; n = available; } if (n == 0) return n; - int reader = m_readers[R]; - reader += n; - while (reader >= m_size) reader -= m_size; - m_readers[R] = reader; + r += n; + while (r >= m_size) r -= m_size; + + // No memory barrier required, because we didn't read any data + m_reader = r; + return n; } -template +template template int -RingBuffer::write(const S *const R__ source, int n) +RingBuffer::write(const S *const R__ source, int n) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::write(" << n << ")" << std::endl; -#endif + int w = m_writer; + int r = m_reader; - int available = getWriteSpace(); + int available = writeSpaceFor(w, r); if (n > available) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only room for " << available << " samples" - << std::endl; -#endif + std::cerr << "WARNING: RingBuffer::write: " << n + << " requested, only room for " << available << std::endl; n = available; } if (n == 0) return n; - int writer = m_writer; - int here = m_size - writer; - T *const R__ bufbase = m_buffer + writer; + int here = m_size - w; + T *const R__ bufbase = m_buffer + w; if (here >= n) { v_convert(bufbase, source, n); @@ -562,38 +475,32 @@ RingBuffer::write(const S *const R__ source, int n) v_convert(m_buffer, source + here, n - here); } - writer += n; - while (writer >= m_size) writer -= m_size; - m_writer = writer; + w += n; + while (w >= m_size) w -= m_size; -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl; -#endif + MBARRIER(); + m_writer = w; return n; } -template +template int -RingBuffer::zero(int n) +RingBuffer::zero(int n) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "RingBuffer[" << this << "]::zero(" << n << ")" << std::endl; -#endif + int w = m_writer; + int r = m_reader; - int available = getWriteSpace(); + int available = writeSpaceFor(w, r); if (n > available) { -#ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only room for " << available << " samples" - << std::endl; -#endif + std::cerr << "WARNING: RingBuffer::zero: " << n + << " requested, only room for " << available << std::endl; n = available; } if (n == 0) return n; - int writer = m_writer; - int here = m_size - writer; - T *const R__ bufbase = m_buffer + writer; + int here = m_size - w; + T *const R__ bufbase = m_buffer + w; if (here >= n) { v_zero(bufbase, n); @@ -602,13 +509,11 @@ RingBuffer::zero(int n) v_zero(m_buffer, n - here); } - writer += n; - while (writer >= m_size) writer -= m_size; - m_writer = writer; + w += n; + while (w >= m_size) w -= m_size; -#ifdef DEBUG_RINGBUFFER - std::cerr << "writer -> " << m_writer << std::endl; -#endif + MBARRIER(); + m_writer = w; return n; } diff --git a/src/dsp/AudioCurveCalculator.h b/src/dsp/AudioCurveCalculator.h index 8d8b66e..2b2bc75 100644 --- a/src/dsp/AudioCurveCalculator.h +++ b/src/dsp/AudioCurveCalculator.h @@ -23,6 +23,23 @@ namespace RubberBand { +/** + * AudioCurveCalculator turns a sequence of audio "columns" -- + * short-time spectrum magnitude blocks -- into a sequence of numbers + * representing some quality of the input such as power or likelihood + * of an onset occurring. + * + * These are typically low-level building-blocks: AudioCurveCalculator + * is a simple causal interface in which each input column corresponds + * to exactly one output value which is returned immediately. They + * have far less power (because of the causal interface and + * magnitude-only input) and flexibility (because of the limited + * return types) than for example the Vamp plugin interface. + * + * AudioCurveCalculator implementations typically remember the history + * of their processing data, and the caller must call reset() before + * resynchronising to an unrelated piece of input audio. + */ class AudioCurveCalculator { public: @@ -56,11 +73,36 @@ public: // given instance + /** + * Process the given magnitude spectrum block and return the curve + * value for it. The mag input contains (fftSize/2 + 1) values + * corresponding to the magnitudes of the complex FFT output bins + * for a windowed input of size fftSize. The hop (expressed in + * time-domain audio samples) from the previous to the current + * input block is given by increment. + */ virtual float processFloat(const float *R__ mag, int increment) = 0; + + /** + * Process the given magnitude spectrum block and return the curve + * value for it. The mag input contains (fftSize/2 + 1) values + * corresponding to the magnitudes of the complex FFT output bins + * for a windowed input of size fftSize. The hop (expressed in + * time-domain audio samples) from the previous to the current + * input block is given by increment. + */ virtual double processDouble(const double *R__ mag, int increment) = 0; + /** + * Reset the calculator, forgetting the history of the audio input + * so far. + */ virtual void reset() = 0; + /** + * If the output of this calculator has a known unit, return it as + * text. For example, "Hz" or "V". + */ virtual const char *getUnit() const { return ""; } protected: diff --git a/src/dsp/FFT.cpp b/src/dsp/FFT.cpp index 6f4b0df..e4b69d1 100644 --- a/src/dsp/FFT.cpp +++ b/src/dsp/FFT.cpp @@ -19,7 +19,7 @@ #include "system/VectorOps.h" #include "system/VectorOpsComplex.h" -#define FFT_MEASUREMENT 1 +//#define FFT_MEASUREMENT 1 #ifdef HAVE_FFTW3 diff --git a/src/dsp/Resampler.cpp b/src/dsp/Resampler.cpp index ef9f160..b0232b7 100644 --- a/src/dsp/Resampler.cpp +++ b/src/dsp/Resampler.cpp @@ -126,7 +126,7 @@ D_SRC::D_SRC(Resampler::Quality quality, int channels, int maxBufferSize, 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! + throw Resampler::ImplementationError; } if (maxBufferSize > 0 && m_channels > 1) { @@ -184,7 +184,7 @@ D_SRC::resample(const float *const R__ *const R__ in, if (err) { std::cerr << "Resampler::process: libsamplerate error: " << src_strerror(err) << std::endl; - throw Resampler::ImplementationError; //!!! of course, need to catch this! + throw Resampler::ImplementationError; } if (m_channels > 1) { @@ -220,7 +220,7 @@ D_SRC::resampleInterleaved(const float *const R__ in, if (err) { std::cerr << "Resampler::process: libsamplerate error: " << src_strerror(err) << std::endl; - throw Resampler::ImplementationError; //!!! of course, need to catch this! + throw Resampler::ImplementationError; } m_lastRatio = ratio; diff --git a/src/getopt/getopt.h b/src/getopt/getopt.h index d95d6cf..2cd3191 100644 --- a/src/getopt/getopt.h +++ b/src/getopt/getopt.h @@ -107,4 +107,4 @@ GETOPT_API int getopt __P((int, char * const *, const char *)); __END_DECLS #endif -#endif /* !_GETOPT_H_ */ +#endif diff --git a/src/system/Allocators.h b/src/system/Allocators.h index e186692..659ef2e 100644 --- a/src/system/Allocators.h +++ b/src/system/Allocators.h @@ -34,6 +34,7 @@ #include #endif + namespace RubberBand { template @@ -44,13 +45,15 @@ T *allocate(size_t count) if (posix_memalign(&ptr, 16, count * sizeof(T))) { ptr = malloc(count * sizeof(T)); } -#else +#else // Note that malloc always aligns to 16 byte boundaries on OS/X, // so we don't need posix_memalign there (which is fortunate, // since it doesn't exist) ptr = malloc(count * sizeof(T)); -#endif - if (!ptr) throw(std::bad_alloc()); +#endif + if (!ptr) { + throw(std::bad_alloc()); + } return (T *)ptr; } @@ -74,13 +77,7 @@ void deallocate(T *ptr) template T *reallocate(T *ptr, size_t oldcount, size_t count) { - T *newptr = 0; - try { - newptr = allocate(count); - } catch (std::bad_alloc) { - if (ptr) deallocate(ptr); - throw; - } + T *newptr = allocate(count); if (oldcount && ptr) { v_copy(newptr, ptr, oldcount < count ? oldcount : count); } @@ -141,13 +138,7 @@ T **reallocate_channels(T **ptr, size_t oldchannels, size_t oldcount, size_t channels, size_t count) { - T **newptr = 0; - try { - newptr = allocate_channels(channels, count); - } catch (std::bad_alloc) { - if (ptr) deallocate_channels(ptr, channels); - throw; - } + T **newptr = allocate_channels(channels, count); if (oldcount && ptr) { v_copy_channels(newptr, ptr, channels, oldcount < count ? oldcount : count); } @@ -160,13 +151,7 @@ T **reallocate_and_zero_extend_channels(T **ptr, size_t oldchannels, size_t oldcount, size_t channels, size_t count) { - T **newptr = 0; - try { - newptr = allocate_and_zero_channels(channels, count); - } catch (std::bad_alloc) { - if (ptr) deallocate_channels(ptr, channels); - throw; - } + T **newptr = allocate_and_zero_channels(channels, count); if (oldcount && ptr) { v_copy_channels(newptr, ptr, channels, oldcount < count ? oldcount : count); } diff --git a/src/system/VectorOps.h b/src/system/VectorOps.h index 8d6404b..0de89d8 100644 --- a/src/system/VectorOps.h +++ b/src/system/VectorOps.h @@ -83,6 +83,7 @@ inline void v_copy_channels(T *const R__ *const R__ dst, } } +// src and dst alias by definition, so not restricted template inline void v_move(T *const dst, const T *const src, @@ -139,6 +140,16 @@ inline void v_add(T *const R__ dst, } } +template +inline void v_add(T *const R__ dst, + const T value, + const int count) +{ + for (int i = 0; i < count; ++i) { + dst[i] += value; + } +} + template inline void v_add_channels(T *const R__ *const R__ dst, @@ -241,6 +252,17 @@ inline void v_multiply_and_add(T *const R__ dst, } +template +inline T v_sum(const T *const R__ src, + const int count) +{ + T result = T(); + for (int i = 0; i < count; ++i) { + result += src[i]; + } + return result; +} + template inline void v_log(T *const R__ dst, const int count) diff --git a/src/system/sysutils.cpp b/src/system/sysutils.cpp index d011b06..9dbec38 100644 --- a/src/system/sysutils.cpp +++ b/src/system/sysutils.cpp @@ -23,6 +23,8 @@ #include #ifdef __APPLE__ #include +#include +#include #else /* !__APPLE__, !_WIN32 */ #include #include @@ -53,17 +55,17 @@ system_get_platform_tag() #else /* !_WIN32 */ #ifdef __APPLE__ return "osx"; -#else /* !__APPLE__ */ +#else #ifdef __LINUX__ if (sizeof(long) == 8) { return "linux64"; } else { return "linux"; } -#else /* !__LINUX__ */ +#else return "posix"; -#endif /* !__LINUX__ */ -#endif /* !__APPLE__ */ +#endif +#endif #endif /* !_WIN32 */ } @@ -146,6 +148,27 @@ void gettimeofday(struct timeval *tv, void *tz) tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 10000000LL); } +void clock_gettime(int, struct timespec *ts) +{ + static LARGE_INTEGER cps; + static bool haveCps = false; + + if (!haveCps) { + QueryPerformanceFrequency(&cps); + haveCps = true; + } + + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + + //!!! check this + ts->tv_sec = counter.QuadPart / cps.QuadPart; + double sub = counter.QuadPart % cps.QuadPart; + sub = sub / cps.QuadPart; + sub = sub * 1000000000.; + ts->tv_nsec = long(sub) ; +} + void usleep(unsigned long usec) { ::Sleep(usec == 0 ? 0 : usec < 1000 ? 1 : usec / 1000); @@ -153,6 +176,20 @@ void usleep(unsigned long usec) #endif +#ifdef __APPLE__ + +void clock_gettime(int, struct timespec *ts) +{ + uint64_t t = mach_absolute_time(); + static mach_timebase_info_data_t sTimebaseInfo; + if (sTimebaseInfo.denom == 0) (void)mach_timebase_info(&sTimebaseInfo); + uint64_t n = t * sTimebaseInfo.numer / sTimebaseInfo.denom; + ts->tv_sec = n / 1000000000; + ts->tv_nsec = n % 1000000000; +} + +#endif + void system_specific_initialise() { } @@ -163,7 +200,7 @@ void system_specific_application_initialise() ProcessStatus -GetProcessStatus(int pid) +system_get_process_status(int pid) { #ifdef _WIN32 HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); @@ -186,6 +223,24 @@ GetProcessStatus(int pid) #endif } +#ifdef _WIN32 +void system_memorybarrier() +{ + MemoryBarrier(); +} +#else /* !_WIN32 */ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) +// Not required +#else +#include +void system_memorybarrier() +{ + pthread_mutex_t dummy = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&dummy); + pthread_mutex_unlock(&dummy); +} +#endif +#endif } diff --git a/src/system/sysutils.h b/src/system/sysutils.h index 24fc0c2..f72bbd7 100644 --- a/src/system/sysutils.h +++ b/src/system/sysutils.h @@ -42,17 +42,28 @@ extern bool system_is_multiprocessor(); extern void system_specific_initialise(); extern void system_specific_application_initialise(); +enum ProcessStatus { ProcessRunning, ProcessNotRunning, UnknownProcessStatus }; +extern ProcessStatus system_get_process_status(int pid); + +#ifdef __APPLE__ +struct timespec { long tv_sec; long tv_nsec; }; +void clock_gettime(int clk_id, struct timespec *p); +#define CLOCK_MONOTONIC 1 +#endif + #ifdef _WIN32 struct timeval { long tv_sec; long tv_usec; }; void gettimeofday(struct timeval *p, void *tz); +struct timespec { long tv_sec; long tv_nsec; }; +// always uses GetPerformanceCounter, does not check whether it's valid or not: +void clock_gettime(int clk_id, struct timespec *p); +#define CLOCK_MONOTONIC 1 + #endif -enum ProcessStatus { ProcessRunning, ProcessNotRunning, UnknownProcessStatus }; -extern ProcessStatus GetProcessStatus(int pid); - inline double mod(double x, double y) { return x - (y * floor(x / y)); } inline float modf(float x, float y) { return x - (y * float(floor(x / y))); } @@ -73,6 +84,11 @@ inline float princargf(float a) { return modf(a + (float)M_PI, -2.f * (float)M_P #define MUNLOCK(a,b) 1 #define MUNLOCK_SAMPLEBLOCK(a) 1 +namespace RubberBand { +extern void system_memorybarrier(); +} +#define MBARRIER() RubberBand::system_memorybarrier() + #define DLOPEN(a,b) LoadLibrary((a).toStdWString().c_str()) #define DLSYM(a,b) GetProcAddress((HINSTANCE)(a),(b)) #define DLCLOSE(a) FreeLibrary((HINSTANCE)(a)) @@ -88,6 +104,20 @@ inline float princargf(float a) { return modf(a + (float)M_PI, -2.f * (float)M_P #define MUNLOCK(a,b) (::munlock((char *)(a),(b)) ? (::perror("munlock failed"), 0) : 0) #define MUNLOCK_SAMPLEBLOCK(a) do { if (!(a).empty()) { const float &b = *(a).begin(); MUNLOCK(&b, (a).capacity() * sizeof(float)); } } while(0); +#ifdef __APPLE__ +#include +#define MBARRIER() OSMemoryBarrier() +#else +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) +#define MBARRIER() __sync_synchronize() +#else +namespace RubberBand { +extern void system_memorybarrier(); +} +#define MBARRIER() ::RubberBand::system_memorybarrier() +#endif +#endif + #define DLOPEN(a,b) dlopen((a).toStdString().c_str(),(b)) #define DLSYM(a,b) dlsym((a),(b)) #define DLCLOSE(a) dlclose((a))