* Add centre-focus option (mid/side processing)

* Simplify RingBuffer and add explicit memory locks
* Fix hang with certain unfortunate combinations of parameters
* Bump version to 1.7
This commit is contained in:
Chris Cannam
2011-11-25 11:11:59 +00:00
parent 9c52352c24
commit c26dc1dc88
19 changed files with 588 additions and 373 deletions

View File

@@ -25,13 +25,13 @@ DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded # The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project. # 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. # The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or # This could be handy for archiving the generated documentation or
# if some version control system is used. # 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) # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put. # base path where the generated documentation will be put.

View File

@@ -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_CONFIG_SRCDIR(src/StretcherImpl.h)
AC_PROG_CXX AC_PROG_CXX

View File

@@ -79,6 +79,7 @@ int main(int argc, char **argv)
bool smoothing = false; bool smoothing = false;
bool hqpitch = false; bool hqpitch = false;
bool formant = false; bool formant = false;
bool together = false;
bool crispchanged = false; bool crispchanged = false;
int crispness = -1; int crispness = -1;
bool help = false; bool help = false;
@@ -122,6 +123,7 @@ int main(int argc, char **argv)
{ "no-threads", 0, 0, '0' }, { "no-threads", 0, 0, '0' },
{ "no-transients", 0, 0, '1' }, { "no-transients", 0, 0, '1' },
{ "no-lamination", 0, 0, '2' }, { "no-lamination", 0, 0, '2' },
{ "centre-focus", 0, 0, '7' },
{ "window-long", 0, 0, '3' }, { "window-long", 0, 0, '3' },
{ "window-short", 0, 0, '4' }, { "window-short", 0, 0, '4' },
{ "bl-transients", 0, 0, '8' }, { "bl-transients", 0, 0, '8' },
@@ -161,6 +163,7 @@ int main(int argc, char **argv)
case '4': shortwin = true; crispchanged = true; break; case '4': shortwin = true; crispchanged = true; break;
case '5': detector = PercussiveDetector; crispchanged = true; break; case '5': detector = PercussiveDetector; crispchanged = true; break;
case '6': detector = SoftDetector; crispchanged = true; break; case '6': detector = SoftDetector; crispchanged = true; break;
case '7': together = true; break;
case '8': transients = BandLimitedTransients; crispchanged = true; break; case '8': transients = BandLimitedTransients; crispchanged = true; break;
case '9': smoothing = true; crispchanged = true; break; case '9': smoothing = true; crispchanged = true; break;
case '%': hqpitch = 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-perc Use percussive transient detector (as in pre-1.5)" << endl;
cerr << " --detector-soft Use soft transient detector" << endl; cerr << " --detector-soft Use soft transient detector" << endl;
cerr << " --pitch-hq In RT mode, use a slower, higher quality pitch shift" << 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 << endl;
cerr << " -d<N>, --debug <N> Select debug level (N = 0,1,2,3); default 0, full 3" << 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 << " (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; while (i < line.length() && line[i] == ' ') ++i;
size_t target = atoi(line.substr(i).c_str()); size_t target = atoi(line.substr(i).c_str());
mapping[source] = target; mapping[source] = target;
if (debug > 0) {
cerr << "adding mapping from " << source << " to " << target << endl; cerr << "adding mapping from " << source << " to " << target << endl;
}
++lineno; ++lineno;
} }
ifile.close(); ifile.close();
@@ -365,6 +372,7 @@ int main(int argc, char **argv)
if (smoothing) options |= RubberBandStretcher::OptionSmoothingOn; if (smoothing) options |= RubberBandStretcher::OptionSmoothingOn;
if (formant) options |= RubberBandStretcher::OptionFormantPreserved; if (formant) options |= RubberBandStretcher::OptionFormantPreserved;
if (hqpitch) options |= RubberBandStretcher::OptionPitchHighQuality; if (hqpitch) options |= RubberBandStretcher::OptionPitchHighQuality;
if (together) options |= RubberBandStretcher::OptionChannelsTogether;
switch (threading) { switch (threading) {
case 0: case 0:
@@ -498,6 +506,10 @@ int main(int argc, char **argv)
bool final = (frame + ibs >= sfinfo.frames); 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); ts.process(ibuf, count, final);
int avail = ts.available(); int avail = ts.available();

View File

@@ -15,9 +15,9 @@
#ifndef _RUBBERBANDSTRETCHER_H_ #ifndef _RUBBERBANDSTRETCHER_H_
#define _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_MAJOR_VERSION 2
#define RUBBERBAND_API_MINOR_VERSION 4 #define RUBBERBAND_API_MINOR_VERSION 5
#include <vector> #include <vector>
#include <map> #include <map>
@@ -110,7 +110,9 @@ public:
* percussive event). This, the default setting, usually * percussive event). This, the default setting, usually
* results in a clear-sounding output; but it is not always * results in a clear-sounding output; but it is not always
* consistent, and may cause interruptions in stable sounds * 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 * \li \c OptionTransientsMixed - Reset component phases at the
* peak of each transient, outside a frequency range typical of * peak of each transient, outside a frequency range typical of
@@ -132,7 +134,7 @@ public:
* *
* \li \c OptionDetectorCompound - Use a general-purpose * \li \c OptionDetectorCompound - Use a general-purpose
* transient detector which is likely to be good for most * transient detector which is likely to be good for most
* situations. * situations. This is the default.
* *
* \li \c OptionDetectorPercussive - Detect percussive * \li \c OptionDetectorPercussive - Detect percussive
* transients. Note that this was the default and only option * transients. Note that this was the default and only option
@@ -166,7 +168,8 @@ public:
* determine its own threading model. Usually this means using * determine its own threading model. Usually this means using
* one processing thread per audio channel in offline mode if * one processing thread per audio channel in offline mode if
* the stretcher is able to determine that more than one CPU is * 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. * \li \c OptionThreadingNever - Never use more than one thread.
* *
@@ -197,11 +200,13 @@ public:
* not be changed after construction. * not be changed after construction.
* *
* \li \c OptionSmoothingOff - Do not use time-domain smoothing. * \li \c OptionSmoothingOff - Do not use time-domain smoothing.
* This is the default.
* *
* \li \c OptionSmoothingOn - Use time-domain smoothing. * \li \c OptionSmoothingOn - Use time-domain smoothing. This
* This will result in a softer sound, but it may be * will result in a softer sound with some audible artifacts
* appropriate for longer stretches of some instruments and * around sharp transients, but it may be appropriate for longer
* can mix well with OptionWindowShort. * stretches of some instruments and can mix well with
* OptionWindowShort.
* *
* 9. Flags prefixed \c OptionFormant control the handling of * 9. Flags prefixed \c OptionFormant control the handling of
* formant shape (spectral envelope) when pitch-shifting. These * formant shape (spectral envelope) when pitch-shifting. These
@@ -209,7 +214,7 @@ public:
* *
* \li \c OptionFormantShifted - Apply no special formant * \li \c OptionFormantShifted - Apply no special formant
* processing. The spectral envelope will be pitch shifted as * processing. The spectral envelope will be pitch shifted as
* normal. * normal. This is the default.
* *
* \li \c OptionFormantPreserved - Preserve the spectral * \li \c OptionFormantPreserved - Preserve the spectral
* envelope of the unshifted signal. This permits shifting the * envelope of the unshifted signal. This permits shifting the
@@ -224,7 +229,7 @@ public:
* \li \c OptionPitchHighSpeed - Use a method with a CPU cost * \li \c OptionPitchHighSpeed - Use a method with a CPU cost
* that is relatively moderate and predictable. This may * that is relatively moderate and predictable. This may
* sound less clear than OptionPitchHighQuality, especially * 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 * \li \c OptionPitchHighQuality - Use the highest quality
* method for pitch shifting. This method has a CPU cost * method for pitch shifting. This method has a CPU cost
@@ -236,6 +241,26 @@ public:
* options, this avoids discontinuities when moving across the * options, this avoids discontinuities when moving across the
* 1.0 pitch scale in real-time; it also consumes more CPU than * 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. * 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 { enum Option {
@@ -273,7 +298,12 @@ public:
OptionPitchHighSpeed = 0x00000000, OptionPitchHighSpeed = 0x00000000,
OptionPitchHighQuality = 0x02000000, OptionPitchHighQuality = 0x02000000,
OptionPitchHighConsistency = 0x04000000 OptionPitchHighConsistency = 0x04000000,
OptionChannelsApart = 0x00000000,
OptionChannelsTogether = 0x10000000,
// n.b. Options is int, so we must stop before 0x80000000
}; };
typedef int Options; typedef int Options;
@@ -431,27 +461,21 @@ public:
*/ */
void setExpectedInputDuration(size_t samples); 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 * Tell the stretcher the maximum number of sample frames that you
* will ever be passing in to a single process() call. If you * will ever be passing in to a single process() call. If you
* don't call this function, the stretcher will assume that you * don't call this, the stretcher will assume that you are calling
* never pass in more samples than getSamplesRequired() suggested * getSamplesRequired() at each cycle and are never passing more
* you should. You should not pass in more samples than that * samples than are suggested by that function.
* unless you have called setMaxProcessSize first. *
* 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() * This function may not be called after the first call to study()
* or process(). * or process().
@@ -462,6 +486,26 @@ public:
*/ */
void setMaxProcessSize(size_t samples); 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 * Provide a set of mappings from "before" to "after" sample
* numbers so as to enforce a particular stretch profile. The * numbers so as to enforce a particular stretch profile. The

View File

@@ -19,9 +19,9 @@
extern "C" { extern "C" {
#endif #endif
#define RUBBERBAND_VERSION "1.6-gpl" #define RUBBERBAND_VERSION "1.7-gpl"
#define RUBBERBAND_API_MAJOR_VERSION 2 #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. * This is a C-linkage interface to the Rubber Band time stretcher.
@@ -71,7 +71,10 @@ enum RubberBandOption {
RubberBandOptionPitchHighQuality = 0x00000000, RubberBandOptionPitchHighQuality = 0x00000000,
RubberBandOptionPitchHighSpeed = 0x02000000, RubberBandOptionPitchHighSpeed = 0x02000000,
RubberBandOptionPitchHighConsistency = 0x04000000 RubberBandOptionPitchHighConsistency = 0x04000000,
RubberBandOptionChannelsApart = 0x00000000,
RubberBandOptionChannelsTogether = 0x10000000,
}; };
typedef int RubberBandOptions; typedef int RubberBandOptions;

View File

@@ -14,6 +14,8 @@
#include "StretcherImpl.h" #include "StretcherImpl.h"
using namespace std;
namespace RubberBand { namespace RubberBand {
@@ -111,7 +113,7 @@ RubberBandStretcher::setMaxProcessSize(size_t samples)
} }
void void
RubberBandStretcher::setKeyFrameMap(const std::map<size_t, size_t> &mapping) RubberBandStretcher::setKeyFrameMap(const map<size_t, size_t> &mapping)
{ {
m_d->setKeyFrameMap(mapping); m_d->setKeyFrameMap(mapping);
} }
@@ -166,19 +168,19 @@ RubberBandStretcher::getInputIncrement() const
return m_d->getInputIncrement(); return m_d->getInputIncrement();
} }
std::vector<int> vector<int>
RubberBandStretcher::getOutputIncrements() const RubberBandStretcher::getOutputIncrements() const
{ {
return m_d->getOutputIncrements(); return m_d->getOutputIncrements();
} }
std::vector<float> vector<float>
RubberBandStretcher::getPhaseResetCurve() const RubberBandStretcher::getPhaseResetCurve() const
{ {
return m_d->getPhaseResetCurve(); return m_d->getPhaseResetCurve();
} }
std::vector<int> vector<int>
RubberBandStretcher::getExactTimePoints() const RubberBandStretcher::getExactTimePoints() const
{ {
return m_d->getExactTimePoints(); return m_d->getExactTimePoints();

View File

@@ -43,9 +43,11 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set<size_t> &sizes,
size_t initialFftSize, size_t initialFftSize,
size_t outbufSize) size_t outbufSize)
{ {
size_t maxSize = initialWindowSize; size_t maxSize = initialWindowSize * 2;
if (initialFftSize > maxSize) maxSize = initialFftSize; 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 is ordered by value
std::set<size_t>::const_iterator i = sizes.end(); std::set<size_t>::const_iterator i = sizes.end();
if (i != sizes.begin()) { if (i != sizes.begin()) {
@@ -56,7 +58,7 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set<size_t> &sizes,
// max possible size of the real "half" of freq data // max possible size of the real "half" of freq data
size_t realSize = maxSize / 2 + 1; 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; if (outbufSize < maxSize) outbufSize = maxSize;
@@ -106,7 +108,9 @@ void
RubberBandStretcher::Impl::ChannelData::setSizes(size_t windowSize, RubberBandStretcher::Impl::ChannelData::setSizes(size_t windowSize,
size_t fftSize) 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 realSize = maxSize / 2 + 1;
size_t oldMax = inbuf->getSize(); size_t oldMax = inbuf->getSize();
size_t oldReal = oldMax / 2 + 1; size_t oldReal = oldMax / 2 + 1;

View File

@@ -699,6 +699,9 @@ RubberBandStretcher::Impl::configure()
// want gaps when the ratio changes. // want gaps when the ratio changes.
if (!m_realtime) { if (!m_realtime) {
if (m_debugLevel > 1) {
cerr << "Not real time mode: prefilling" << endl;
}
for (size_t c = 0; c < m_channels; ++c) { for (size_t c = 0; c < m_channels; ++c) {
m_channelData[c]->reset(); m_channelData[c]->reset();
m_channelData[c]->inbuf->zero(m_aWindowSize/2); 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, // cd.accumulator is not otherwise used during studying,
// so we can use it as a temporary buffer here // so we can use it as a temporary buffer here
size_t got = inbuf.peek(cd.accumulator, m_aWindowSize); size_t ready = inbuf.getReadSpace();
assert(final || got == m_aWindowSize); assert(final || ready >= m_aWindowSize);
inbuf.peek(cd.accumulator, std::min(ready, m_aWindowSize));
if (m_aWindowSize == m_fftSize) { if (m_aWindowSize == m_fftSize) {
@@ -1149,8 +1153,22 @@ RubberBandStretcher::Impl::getSamplesRequired() const
ChannelData &cd = *m_channelData[c]; ChannelData &cd = *m_channelData[c];
RingBuffer<float> &inbuf = *cd.inbuf; RingBuffer<float> &inbuf = *cd.inbuf;
RingBuffer<float> &outbuf = *cd.outbuf;
size_t rs = inbuf.getReadSpace(); 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 // 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 == JustCreated || m_mode == Studying) {
if (m_mode == Studying) { if (m_mode == Studying) {
calculateStretch();
}
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) { for (size_t c = 0; c < m_channels; ++c) {
m_channelData[c]->reset(); m_channelData[c]->reset();
m_channelData[c]->inbuf->zero(m_aWindowSize/2); m_channelData[c]->inbuf->zero(m_aWindowSize/2);
} }
}
}
if (m_threaded) { if (m_threaded) {
MutexLocker locker(&m_threadSetMutex); 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) { for (size_t c = 0; c < m_channels; ++c) {
consumed[c] += consumeChannel(c, consumed[c] += consumeChannel(c,
input[c] + consumed[c], input,
consumed[c],
samples - consumed[c], samples - consumed[c],
final); final);
if (consumed[c] < samples) { 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;
}
} }
// cerr << "process returning" << endl; if (m_debugLevel > 2) {
cerr << "process returning" << endl;
}
if (final) m_mode = Finished; if (final) m_mode = Finished;
} }

View File

@@ -23,6 +23,7 @@
#include "dsp/CompoundAudioCurve.h" #include "dsp/CompoundAudioCurve.h"
#include "base/RingBuffer.h" #include "base/RingBuffer.h"
#include "base/Scavenger.h"
#include "system/Thread.h" #include "system/Thread.h"
#include "system/sysutils.h" #include "system/sysutils.h"
@@ -96,8 +97,10 @@ protected:
size_t m_sampleRate; size_t m_sampleRate;
size_t m_channels; size_t m_channels;
size_t consumeChannel(size_t channel, const float *input, void prepareChannelMS(size_t channel, const float *const *inputs,
size_t samples, bool final); 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); void processChunks(size_t channel, bool &any, bool &last);
bool processOneChunk(); // across all channels, for real time use bool processOneChunk(); // across all channels, for real time use
bool processChunkForChannel(size_t channel, size_t phaseIncrement, bool processChunkForChannel(size_t channel, size_t phaseIncrement,

View File

@@ -133,9 +133,32 @@ RubberBandStretcher::Impl::resampleBeforeStretching() const
} }
} }
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 size_t
RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input, RubberBandStretcher::Impl::consumeChannel(size_t c,
size_t samples, bool final) const float *const *inputs,
size_t offset,
size_t samples,
bool final)
{ {
Profiler profiler("RubberBandStretcher::Impl::consumeChannel"); Profiler profiler("RubberBandStretcher::Impl::consumeChannel");
@@ -147,6 +170,13 @@ RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input,
bool resampling = resampleBeforeStretching(); bool resampling = resampleBeforeStretching();
float *ms = 0;
const float *input = 0;
bool useMidSide = ((m_options & OptionChannelsTogether) &&
(m_channels >= 2) &&
(c < 2));
if (resampling) { if (resampling) {
toWrite = int(ceil(samples / m_pitchScale)); 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, toWrite = cd.resampler->resample(&input,
&cd.resamplebuf, &cd.resamplebuf,
samples, samples,
@@ -179,10 +217,21 @@ RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input,
} }
if (resampling) { if (resampling) {
inbuf.write(cd.resamplebuf, toWrite); inbuf.write(cd.resamplebuf, toWrite);
cd.inCount += samples; cd.inCount += samples;
return samples; return samples;
} else { } 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); inbuf.write(input, toWrite);
cd.inCount += toWrite; cd.inCount += toWrite;
return toWrite; return toWrite;
@@ -208,15 +257,18 @@ RubberBandStretcher::Impl::processChunks(size_t c, bool &any, bool &last)
while (!last) { while (!last) {
if (!testInbufReadSpace(c)) { if (!testInbufReadSpace(c)) {
// cerr << "not enough input" << endl; if (m_debugLevel > 2) {
cerr << "processChunks: out of input" << endl;
}
break; break;
} }
any = true; any = true;
if (!cd.draining) { if (!cd.draining) {
size_t got = cd.inbuf->peek(cd.fltbuf, m_aWindowSize); size_t ready = cd.inbuf->getReadSpace();
assert(got == m_aWindowSize || cd.inputSize >= 0); assert(ready >= m_aWindowSize || cd.inputSize >= 0);
cd.inbuf->peek(cd.fltbuf, std::min(ready, m_aWindowSize));
cd.inbuf->skip(m_increment); cd.inbuf->skip(m_increment);
} }
@@ -267,11 +319,17 @@ RubberBandStretcher::Impl::processOneChunk()
// This is the normal process method in RT mode. // This is the normal process method in RT mode.
for (size_t c = 0; c < m_channels; ++c) { 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]; ChannelData &cd = *m_channelData[c];
if (!cd.draining) { if (!cd.draining) {
size_t got = cd.inbuf->peek(cd.fltbuf, m_aWindowSize); size_t ready = cd.inbuf->getReadSpace();
assert(got == m_aWindowSize || cd.inputSize >= 0); assert(ready >= m_aWindowSize || cd.inputSize >= 0);
cd.inbuf->peek(cd.fltbuf, std::min(ready, m_aWindowSize));
cd.inbuf->skip(m_increment); cd.inbuf->skip(m_increment);
analyseChunk(c); analyseChunk(c);
} }
@@ -314,9 +372,11 @@ RubberBandStretcher::Impl::testInbufReadSpace(size_t c)
// we know there is more input to come. // we know there is more input to come.
if (!m_threaded) { if (!m_threaded) {
// cerr << "WARNING: RubberBandStretcher: read space < chunk size (" if (m_debugLevel > 1) {
// << inbuf.getReadSpace() << " < " << m_windowSize cerr << "WARNING: RubberBandStretcher: read space < chunk size ("
// << ") when not all input written, on processChunks for channel " << c << endl; << inbuf.getReadSpace() << " < " << m_aWindowSize
<< ") when not all input written, on processChunks for channel " << c << endl;
}
} }
return false; return false;
} }
@@ -520,8 +580,12 @@ RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn,
int incr = m_stretchCalculator->calculateSingle int incr = m_stretchCalculator->calculateSingle
(getEffectiveRatio(), df, m_increment); (getEffectiveRatio(), df, m_increment);
if (m_lastProcessPhaseResetDf.getWriteSpace() > 0) {
m_lastProcessPhaseResetDf.write(&df, 1); m_lastProcessPhaseResetDf.write(&df, 1);
}
if (m_lastProcessOutputIncrements.getWriteSpace() > 0) {
m_lastProcessOutputIncrements.write(&incr, 1); m_lastProcessOutputIncrements.write(&incr, 1);
}
if (incr < 0) { if (incr < 0) {
phaseReset = true; phaseReset = true;
@@ -1103,7 +1167,9 @@ RubberBandStretcher::Impl::available() const
if (m_channelData[c]->inputSize >= 0) { if (m_channelData[c]->inputSize >= 0) {
// cerr << "available: m_done true" << endl; // cerr << "available: m_done true" << endl;
if (m_channelData[c]->inbuf->getReadSpace() > 0) { 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 //!!! 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 // ^^^ yes, we do sometimes -- e.g. when fed a very short file
bool any = false, last = false; 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; return got;
} }

View File

@@ -17,25 +17,24 @@
#include <sys/types.h> #include <sys/types.h>
#include "Scavenger.h"
//#define DEBUG_RINGBUFFER 1 //#define DEBUG_RINGBUFFER 1
#include "system/sysutils.h" #include "system/sysutils.h"
#include "system/Allocators.h" #include "system/Allocators.h"
#ifdef DEBUG_RINGBUFFER
#include <iostream> #include <iostream>
#endif
namespace RubberBand { namespace RubberBand {
/** /**
* RingBuffer implements a lock-free ring buffer for one writer and N * RingBuffer implements a lock-free ring buffer for one writer and
* readers, that is to be used to store a sample type T. * 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 <typename T, int N = 1> template <typename T>
class RingBuffer class RingBuffer
{ {
public: public:
@@ -58,14 +57,6 @@ public:
*/ */
int getSize() const; 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 * Return a new ring buffer (allocated with "new" -- called must
* delete when no longer needed) of the given size, containing the * 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 * or inconsistent. If this buffer's data will not fit in the new
* size, the contents are undefined. * size, the contents are undefined.
*/ */
RingBuffer<T, N> *resized(int newSize, int R = 0) const; RingBuffer<T> *resized(int newSize) const;
/** /**
* Lock the ring buffer into physical memory. Returns true * 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 * Return the amount of data available for reading by reader R, in
* samples. * samples.
*/ */
int getReadSpace(int R = 0) const; int getReadSpace() const;
/** /**
* Return the amount of space available for writing, in samples. * 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 * This is a template function, taking an argument S for the target
* sample type, which is permitted to differ from T if the two * sample type, which is permitted to differ from T if the two
* types are compatible for assignment. * types are compatible for arithmetic operations.
*/ */
template <typename S> template <typename S>
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 * Read n samples from the buffer, for reader R, adding them to
* the destination. If fewer than n are available, the remainder * the destination. If fewer than n are available, the remainder
* will be left alone. Returns the number of samples actually * will be left alone. Returns the number of samples actually
* read. * 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 <typename S>
int readAdding(S *const R__ destination, int n);
/** /**
* Read one sample from the buffer, for reader R. If no sample is * 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 * may be good enough if you don't want to allocate a buffer to
* read into. * read into.
*/ */
T readOne(int R = 0); T readOne();
/** /**
* Read n samples from the buffer, if available, for reader R, * 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 * n are available, the remainder will be zeroed out. Returns the
* number of samples actually read. * 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 * 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 * skip() will be necessary to empty the buffer. Returns zero if
* no sample was available. * no sample was available.
*/ */
T peekOne(int R = 0) const; T peekOne() const;
/** /**
* Pretend to read n samples from the buffer, for reader R, * 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 * samples). Returns the number of samples actually available for
* discarding. * discarding.
*/ */
int skip(int n, int R = 0); int skip(int n);
/** /**
* Write n samples to the buffer. If insufficient space is * Write n samples to the buffer. If insufficient space is
@@ -173,101 +169,78 @@ public:
int zero(int n); int zero(int n);
protected: protected:
T *R__ m_buffer; T *const R__ m_buffer;
volatile int m_writer; int m_writer;
volatile int m_readers[N]; int m_reader;
int m_size; const int m_size;
bool m_mlocked; bool m_mlocked;
static Scavenger<ScavengerArrayWrapper<T> > 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: private:
RingBuffer(const RingBuffer &); // not provided RingBuffer(const RingBuffer &); // not provided
RingBuffer &operator=(const RingBuffer &); // not provided RingBuffer &operator=(const RingBuffer &); // not provided
}; };
template <typename T, int N> template <typename T>
Scavenger<ScavengerArrayWrapper<T> > RingBuffer<T, N>::m_scavenger; RingBuffer<T>::RingBuffer(int n) :
m_buffer(allocate<T>(n + 1)),
template <typename T, int N>
RingBuffer<T, N>::RingBuffer(int n) :
m_writer(0), m_writer(0),
m_size(n + 1), m_size(n + 1),
m_mlocked(false) m_mlocked(false)
{ {
m_buffer = allocate<T>(n + 1);
#ifdef DEBUG_RINGBUFFER #ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl; std::cerr << "RingBuffer<T>[" << this << "]::RingBuffer(" << n << ")" << std::endl;
#endif #endif
for (int i = 0; i < N; ++i) m_readers[i] = 0; m_reader = 0;
m_scavenger.scavenge();
} }
template <typename T, int N> template <typename T>
RingBuffer<T, N>::~RingBuffer() RingBuffer<T>::~RingBuffer()
{ {
#ifdef DEBUG_RINGBUFFER #ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl; std::cerr << "RingBuffer<T>[" << this << "]::~RingBuffer" << std::endl;
#endif #endif
if (m_mlocked) { if (m_mlocked) {
MUNLOCK((void *)m_buffer, m_size * sizeof(T)); MUNLOCK((void *)m_buffer, m_size * sizeof(T));
} }
deallocate<T>(m_buffer); deallocate(m_buffer);
m_scavenger.scavenge();
} }
template <typename T, int N> template <typename T>
int int
RingBuffer<T, N>::getSize() const RingBuffer<T>::getSize() const
{ {
#ifdef DEBUG_RINGBUFFER #ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getSize(): " << m_size-1 << std::endl; std::cerr << "RingBuffer<T>[" << this << "]::getSize(): " << m_size-1 << std::endl;
#endif #endif
return m_size - 1; return m_size - 1;
} }
template <typename T, int N> template <typename T>
void RingBuffer<T> *
RingBuffer<T, N>::resize(int newSize) RingBuffer<T>::resized(int newSize) const
{ {
#ifdef DEBUG_RINGBUFFER RingBuffer<T> *newBuffer = new RingBuffer<T>(newSize);
std::cerr << "RingBuffer<T," << N << ">[" << 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<T>(m_buffer));
reset();
m_buffer = allocate<T>(newSize + 1);
m_size = newSize + 1;
if (m_mlocked) {
if (MLOCK((void *)m_buffer, m_size * sizeof(T))) {
m_mlocked = false;
}
}
}
template <typename T, int N>
RingBuffer<T, N> *
RingBuffer<T, N>::resized(int newSize, int R) const
{
RingBuffer<T, N> *newBuffer = new RingBuffer<T, N>(newSize);
int w = m_writer; int w = m_writer;
int r = m_readers[R]; int r = m_reader;
while (r != w) { while (r != w) {
T value = m_buffer[r]; T value = m_buffer[r];
@@ -278,143 +251,95 @@ RingBuffer<T, N>::resized(int newSize, int R) const
return newBuffer; return newBuffer;
} }
template <typename T, int N> template <typename T>
bool bool
RingBuffer<T, N>::mlock() RingBuffer<T>::mlock()
{ {
if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false; if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false;
m_mlocked = true; m_mlocked = true;
return true; return true;
} }
template <typename T, int N> template <typename T>
void void
RingBuffer<T, N>::reset() RingBuffer<T>::reset()
{ {
#ifdef DEBUG_RINGBUFFER #ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl; std::cerr << "RingBuffer<T>[" << this << "]::reset" << std::endl;
#endif #endif
m_writer = 0; m_writer = 0;
for (int i = 0; i < N; ++i) m_readers[i] = 0; m_reader = 0;
} }
template <typename T, int N> template <typename T>
int int
RingBuffer<T, N>::getReadSpace(int R) const RingBuffer<T>::getReadSpace() const
{ {
int writer = m_writer; return readSpaceFor(m_writer, m_reader);
int reader = m_readers[R];
int space;
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << 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<T," << N << ">[" << this << "]::getReadSpace(" << R << "): " << space << std::endl;
#endif
return space;
} }
template <typename T, int N> template <typename T>
int int
RingBuffer<T, N>::getWriteSpace() const RingBuffer<T>::getWriteSpace() const
{ {
int space = 0; return writeSpaceFor(m_writer, m_reader);
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<T," << N << ">[" << this << "]::getWriteSpace(): " << space << std::endl;
#endif
return space;
} }
template <typename T, int N> template <typename T>
template <typename S> template <typename S>
int int
RingBuffer<T, N>::read(S *const R__ destination, int n, int R) RingBuffer<T>::read(S *const R__ destination, int n)
{ {
#ifdef DEBUG_RINGBUFFER int w = m_writer;
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl; int r = m_reader;
#endif
int available = getReadSpace(R); int available = readSpaceFor(w, r);
if (n > available) { if (n > available) {
#ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only "
std::cerr << "WARNING: Only " << available << " samples available" << available << " available" << std::endl;
<< std::endl; v_zero(destination + available, n - available);
#endif
for (int i = available; i < n; ++i) {
destination[i] = S(0);
}
n = available; n = available;
} }
if (n == 0) return n; if (n == 0) return n;
int reader = m_readers[R]; int here = m_size - r;
int here = m_size - reader; T *const R__ bufbase = m_buffer + r;
T *const R__ bufbase = m_buffer + reader;
if (here >= n) { if (here >= n) {
v_convert<T, S>(destination, bufbase, n); v_convert(destination, bufbase, n);
} else { } else {
v_convert<T, S>(destination, bufbase, here); v_convert(destination, bufbase, here);
v_convert<T, S>(destination + here, m_buffer, n - here); v_convert(destination + here, m_buffer, n - here);
} }
reader += n; r += n;
while (reader >= m_size) reader -= m_size; while (r >= m_size) r -= m_size;
m_readers[R] = reader;
#ifdef DEBUG_RINGBUFFER MBARRIER();
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl; m_reader = r;
#endif
return n; return n;
} }
template <typename T, int N> template <typename T>
template <typename S>
int int
RingBuffer<T, N>::readAdding(T *const R__ destination, int n, int R) RingBuffer<T>::readAdding(S *const R__ destination, int n)
{ {
#ifdef DEBUG_RINGBUFFER int w = m_writer;
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl; int r = m_reader;
#endif
int available = getReadSpace(R); int available = readSpaceFor(w, r);
if (n > available) { if (n > available) {
#ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only "
std::cerr << "WARNING: Only " << available << " samples available" << available << " available" << std::endl;
<< std::endl;
#endif
n = available; n = available;
} }
if (n == 0) return n; if (n == 0) return n;
int reader = m_readers[R]; int here = m_size - r;
int here = m_size - reader; T *const R__ bufbase = m_buffer + r;
const T *const R__ bufbase = m_buffer + reader;
if (here >= n) { if (here >= n) {
v_add(destination, bufbase, n); v_add(destination, bufbase, n);
@@ -423,56 +348,55 @@ RingBuffer<T, N>::readAdding(T *const R__ destination, int n, int R)
v_add(destination + here, m_buffer, n - here); v_add(destination + here, m_buffer, n - here);
} }
reader += n; r += n;
while (reader >= m_size) reader -= m_size; while (r >= m_size) r -= m_size;
m_readers[R] = reader;
MBARRIER();
m_reader = r;
return n; return n;
} }
template <typename T, int N> template <typename T>
T T
RingBuffer<T, N>::readOne(int R) RingBuffer<T>::readOne()
{ {
#ifdef DEBUG_RINGBUFFER int w = m_writer;
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readOne(" << R << ")" << std::endl; int r = m_reader;
#endif
if (m_writer == m_readers[R]) { if (w == r) {
#ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: RingBuffer::readOne: no sample available"
std::cerr << "WARNING: No sample available"
<< std::endl; << std::endl;
#endif
return 0; return 0;
} }
int reader = m_readers[R];
T value = m_buffer[reader]; T value = m_buffer[r];
if (++reader == m_size) reader = 0; if (++r == m_size) r = 0;
m_readers[R] = reader;
MBARRIER();
m_reader = r;
return value; return value;
} }
template <typename T, int N> template <typename T>
int int
RingBuffer<T, N>::peek(T *const R__ destination, int n, int R) const RingBuffer<T>::peek(T *const R__ destination, int n) const
{ {
#ifdef DEBUG_RINGBUFFER int w = m_writer;
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl; int r = m_reader;
#endif
int available = getReadSpace(R); int available = readSpaceFor(w, r);
if (n > available) { if (n > available) {
#ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: RingBuffer::peek: " << n << " requested, only "
std::cerr << "WARNING: Only " << available << " samples available" << available << " available" << std::endl;
<< std::endl;
#endif
memset(destination + available, 0, (n - available) * sizeof(T)); memset(destination + available, 0, (n - available) * sizeof(T));
n = available; n = available;
} }
if (n == 0) return n; if (n == 0) return n;
int reader = m_readers[R]; int here = m_size - r;
int here = m_size - reader; const T *const R__ bufbase = m_buffer + r;
const T *const R__ bufbase = m_buffer + reader;
if (here >= n) { if (here >= n) {
v_copy(destination, bufbase, n); v_copy(destination, bufbase, n);
@@ -481,79 +405,68 @@ RingBuffer<T, N>::peek(T *const R__ destination, int n, int R) const
v_copy(destination + here, m_buffer, n - here); v_copy(destination + here, m_buffer, n - here);
} }
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek: read " << n << std::endl;
#endif
return n; return n;
} }
template <typename T, int N> template <typename T>
T T
RingBuffer<T, N>::peekOne(int R) const RingBuffer<T>::peekOne() const
{ {
#ifdef DEBUG_RINGBUFFER int w = m_writer;
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(" << R << ")" << std::endl; int r = m_reader;
#endif
if (m_writer == m_readers[R]) { if (w == r) {
#ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: RingBuffer::peekOne: no sample available"
std::cerr << "WARNING: No sample available"
<< std::endl; << std::endl;
#endif
return 0; return 0;
} }
T value = m_buffer[m_readers[R]];
T value = m_buffer[r];
return value; return value;
} }
template <typename T, int N> template <typename T>
int int
RingBuffer<T, N>::skip(int n, int R) RingBuffer<T>::skip(int n)
{ {
#ifdef DEBUG_RINGBUFFER int w = m_writer;
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::skip(" << n << ", " << R << ")" << std::endl; int r = m_reader;
#endif
int available = getReadSpace(R); int available = readSpaceFor(w, r);
if (n > available) { if (n > available) {
#ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: RingBuffer::skip: " << n << " requested, only "
std::cerr << "WARNING: Only " << available << " samples available" << available << " available" << std::endl;
<< std::endl;
#endif
n = available; n = available;
} }
if (n == 0) return n; if (n == 0) return n;
int reader = m_readers[R]; r += n;
reader += n; while (r >= m_size) r -= m_size;
while (reader >= m_size) reader -= m_size;
m_readers[R] = reader; // No memory barrier required, because we didn't read any data
m_reader = r;
return n; return n;
} }
template <typename T, int N> template <typename T>
template <typename S> template <typename S>
int int
RingBuffer<T, N>::write(const S *const R__ source, int n) RingBuffer<T>::write(const S *const R__ source, int n)
{ {
#ifdef DEBUG_RINGBUFFER int w = m_writer;
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write(" << n << ")" << std::endl; int r = m_reader;
#endif
int available = getWriteSpace(); int available = writeSpaceFor(w, r);
if (n > available) { if (n > available) {
#ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: RingBuffer::write: " << n
std::cerr << "WARNING: Only room for " << available << " samples" << " requested, only room for " << available << std::endl;
<< std::endl;
#endif
n = available; n = available;
} }
if (n == 0) return n; if (n == 0) return n;
int writer = m_writer; int here = m_size - w;
int here = m_size - writer; T *const R__ bufbase = m_buffer + w;
T *const R__ bufbase = m_buffer + writer;
if (here >= n) { if (here >= n) {
v_convert<S, T>(bufbase, source, n); v_convert<S, T>(bufbase, source, n);
@@ -562,38 +475,32 @@ RingBuffer<T, N>::write(const S *const R__ source, int n)
v_convert<S, T>(m_buffer, source + here, n - here); v_convert<S, T>(m_buffer, source + here, n - here);
} }
writer += n; w += n;
while (writer >= m_size) writer -= m_size; while (w >= m_size) w -= m_size;
m_writer = writer;
#ifdef DEBUG_RINGBUFFER MBARRIER();
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl; m_writer = w;
#endif
return n; return n;
} }
template <typename T, int N> template <typename T>
int int
RingBuffer<T, N>::zero(int n) RingBuffer<T>::zero(int n)
{ {
#ifdef DEBUG_RINGBUFFER int w = m_writer;
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::zero(" << n << ")" << std::endl; int r = m_reader;
#endif
int available = getWriteSpace(); int available = writeSpaceFor(w, r);
if (n > available) { if (n > available) {
#ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: RingBuffer::zero: " << n
std::cerr << "WARNING: Only room for " << available << " samples" << " requested, only room for " << available << std::endl;
<< std::endl;
#endif
n = available; n = available;
} }
if (n == 0) return n; if (n == 0) return n;
int writer = m_writer; int here = m_size - w;
int here = m_size - writer; T *const R__ bufbase = m_buffer + w;
T *const R__ bufbase = m_buffer + writer;
if (here >= n) { if (here >= n) {
v_zero(bufbase, n); v_zero(bufbase, n);
@@ -602,13 +509,11 @@ RingBuffer<T, N>::zero(int n)
v_zero(m_buffer, n - here); v_zero(m_buffer, n - here);
} }
writer += n; w += n;
while (writer >= m_size) writer -= m_size; while (w >= m_size) w -= m_size;
m_writer = writer;
#ifdef DEBUG_RINGBUFFER MBARRIER();
std::cerr << "writer -> " << m_writer << std::endl; m_writer = w;
#endif
return n; return n;
} }

View File

@@ -23,6 +23,23 @@
namespace RubberBand 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 class AudioCurveCalculator
{ {
public: public:
@@ -56,11 +73,36 @@ public:
// given instance // 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; 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; 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; 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 ""; } virtual const char *getUnit() const { return ""; }
protected: protected:

View File

@@ -19,7 +19,7 @@
#include "system/VectorOps.h" #include "system/VectorOps.h"
#include "system/VectorOpsComplex.h" #include "system/VectorOpsComplex.h"
#define FFT_MEASUREMENT 1 //#define FFT_MEASUREMENT 1
#ifdef HAVE_FFTW3 #ifdef HAVE_FFTW3

View File

@@ -126,7 +126,7 @@ D_SRC::D_SRC(Resampler::Quality quality, int channels, int maxBufferSize,
if (err) { if (err) {
std::cerr << "Resampler::Resampler: failed to create libsamplerate resampler: " std::cerr << "Resampler::Resampler: failed to create libsamplerate resampler: "
<< src_strerror(err) << std::endl; << src_strerror(err) << std::endl;
throw Resampler::ImplementationError; //!!! of course, need to catch this! throw Resampler::ImplementationError;
} }
if (maxBufferSize > 0 && m_channels > 1) { if (maxBufferSize > 0 && m_channels > 1) {
@@ -184,7 +184,7 @@ D_SRC::resample(const float *const R__ *const R__ in,
if (err) { if (err) {
std::cerr << "Resampler::process: libsamplerate error: " std::cerr << "Resampler::process: libsamplerate error: "
<< src_strerror(err) << std::endl; << src_strerror(err) << std::endl;
throw Resampler::ImplementationError; //!!! of course, need to catch this! throw Resampler::ImplementationError;
} }
if (m_channels > 1) { if (m_channels > 1) {
@@ -220,7 +220,7 @@ D_SRC::resampleInterleaved(const float *const R__ in,
if (err) { if (err) {
std::cerr << "Resampler::process: libsamplerate error: " std::cerr << "Resampler::process: libsamplerate error: "
<< src_strerror(err) << std::endl; << src_strerror(err) << std::endl;
throw Resampler::ImplementationError; //!!! of course, need to catch this! throw Resampler::ImplementationError;
} }
m_lastRatio = ratio; m_lastRatio = ratio;

View File

@@ -107,4 +107,4 @@ GETOPT_API int getopt __P((int, char * const *, const char *));
__END_DECLS __END_DECLS
#endif #endif
#endif /* !_GETOPT_H_ */ #endif

View File

@@ -34,6 +34,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#endif #endif
namespace RubberBand { namespace RubberBand {
template <typename T> template <typename T>
@@ -50,7 +51,9 @@ T *allocate(size_t count)
// since it doesn't exist) // since it doesn't exist)
ptr = malloc(count * sizeof(T)); ptr = malloc(count * sizeof(T));
#endif #endif
if (!ptr) throw(std::bad_alloc()); if (!ptr) {
throw(std::bad_alloc());
}
return (T *)ptr; return (T *)ptr;
} }
@@ -74,13 +77,7 @@ void deallocate(T *ptr)
template <typename T> template <typename T>
T *reallocate(T *ptr, size_t oldcount, size_t count) T *reallocate(T *ptr, size_t oldcount, size_t count)
{ {
T *newptr = 0; T *newptr = allocate<T>(count);
try {
newptr = allocate<T>(count);
} catch (std::bad_alloc) {
if (ptr) deallocate<T>(ptr);
throw;
}
if (oldcount && ptr) { if (oldcount && ptr) {
v_copy(newptr, ptr, oldcount < count ? oldcount : count); 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 oldchannels, size_t oldcount,
size_t channels, size_t count) size_t channels, size_t count)
{ {
T **newptr = 0; T **newptr = allocate_channels<T>(channels, count);
try {
newptr = allocate_channels<T>(channels, count);
} catch (std::bad_alloc) {
if (ptr) deallocate_channels<T>(ptr, channels);
throw;
}
if (oldcount && ptr) { if (oldcount && ptr) {
v_copy_channels(newptr, ptr, channels, oldcount < count ? oldcount : count); 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 oldchannels, size_t oldcount,
size_t channels, size_t count) size_t channels, size_t count)
{ {
T **newptr = 0; T **newptr = allocate_and_zero_channels<T>(channels, count);
try {
newptr = allocate_and_zero_channels<T>(channels, count);
} catch (std::bad_alloc) {
if (ptr) deallocate_channels<T>(ptr, channels);
throw;
}
if (oldcount && ptr) { if (oldcount && ptr) {
v_copy_channels(newptr, ptr, channels, oldcount < count ? oldcount : count); v_copy_channels(newptr, ptr, channels, oldcount < count ? oldcount : count);
} }

View File

@@ -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<typename T> template<typename T>
inline void v_move(T *const dst, inline void v_move(T *const dst,
const T *const src, const T *const src,
@@ -139,6 +140,16 @@ inline void v_add(T *const R__ dst,
} }
} }
template<typename T>
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<typename T> template<typename T>
inline void v_add_channels(T *const R__ *const R__ dst, 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<typename T>
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<typename T> template<typename T>
inline void v_log(T *const R__ dst, inline void v_log(T *const R__ dst,
const int count) const int count)

View File

@@ -23,6 +23,8 @@
#include <unistd.h> #include <unistd.h>
#ifdef __APPLE__ #ifdef __APPLE__
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#else /* !__APPLE__, !_WIN32 */ #else /* !__APPLE__, !_WIN32 */
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -53,17 +55,17 @@ system_get_platform_tag()
#else /* !_WIN32 */ #else /* !_WIN32 */
#ifdef __APPLE__ #ifdef __APPLE__
return "osx"; return "osx";
#else /* !__APPLE__ */ #else
#ifdef __LINUX__ #ifdef __LINUX__
if (sizeof(long) == 8) { if (sizeof(long) == 8) {
return "linux64"; return "linux64";
} else { } else {
return "linux"; return "linux";
} }
#else /* !__LINUX__ */ #else
return "posix"; return "posix";
#endif /* !__LINUX__ */ #endif
#endif /* !__APPLE__ */ #endif
#endif /* !_WIN32 */ #endif /* !_WIN32 */
} }
@@ -146,6 +148,27 @@ void gettimeofday(struct timeval *tv, void *tz)
tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 10000000LL); 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) void usleep(unsigned long usec)
{ {
::Sleep(usec == 0 ? 0 : usec < 1000 ? 1 : usec / 1000); ::Sleep(usec == 0 ? 0 : usec < 1000 ? 1 : usec / 1000);
@@ -153,6 +176,20 @@ void usleep(unsigned long usec)
#endif #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() void system_specific_initialise()
{ {
} }
@@ -163,7 +200,7 @@ void system_specific_application_initialise()
ProcessStatus ProcessStatus
GetProcessStatus(int pid) system_get_process_status(int pid)
{ {
#ifdef _WIN32 #ifdef _WIN32
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
@@ -186,6 +223,24 @@ GetProcessStatus(int pid)
#endif #endif
} }
#ifdef _WIN32
void system_memorybarrier()
{
MemoryBarrier();
}
#else /* !_WIN32 */
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
// Not required
#else
#include <pthread.h>
void system_memorybarrier()
{
pthread_mutex_t dummy = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&dummy);
pthread_mutex_unlock(&dummy);
}
#endif
#endif
} }

View File

@@ -42,17 +42,28 @@ extern bool system_is_multiprocessor();
extern void system_specific_initialise(); extern void system_specific_initialise();
extern void system_specific_application_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 #ifdef _WIN32
struct timeval { long tv_sec; long tv_usec; }; struct timeval { long tv_sec; long tv_usec; };
void gettimeofday(struct timeval *p, void *tz); 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 #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 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))); } 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(a,b) 1
#define MUNLOCK_SAMPLEBLOCK(a) 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 DLOPEN(a,b) LoadLibrary((a).toStdWString().c_str())
#define DLSYM(a,b) GetProcAddress((HINSTANCE)(a),(b)) #define DLSYM(a,b) GetProcAddress((HINSTANCE)(a),(b))
#define DLCLOSE(a) FreeLibrary((HINSTANCE)(a)) #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(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); #define MUNLOCK_SAMPLEBLOCK(a) do { if (!(a).empty()) { const float &b = *(a).begin(); MUNLOCK(&b, (a).capacity() * sizeof(float)); } } while(0);
#ifdef __APPLE__
#include <libkern/OSAtomic.h>
#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 DLOPEN(a,b) dlopen((a).toStdString().c_str(),(b))
#define DLSYM(a,b) dlsym((a),(b)) #define DLSYM(a,b) dlsym((a),(b))
#define DLCLOSE(a) dlclose((a)) #define DLCLOSE(a) dlclose((a))