* 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:
4
Doxyfile
4
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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<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;
|
||||
@@ -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;
|
||||
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();
|
||||
|
||||
@@ -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 <vector>
|
||||
#include <map>
|
||||
@@ -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.
|
||||
* 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<size_t, size_t> &mapping)
|
||||
RubberBandStretcher::setKeyFrameMap(const map<size_t, size_t> &mapping)
|
||||
{
|
||||
m_d->setKeyFrameMap(mapping);
|
||||
}
|
||||
@@ -166,19 +168,19 @@ RubberBandStretcher::getInputIncrement() const
|
||||
return m_d->getInputIncrement();
|
||||
}
|
||||
|
||||
std::vector<int>
|
||||
vector<int>
|
||||
RubberBandStretcher::getOutputIncrements() const
|
||||
{
|
||||
return m_d->getOutputIncrements();
|
||||
}
|
||||
|
||||
std::vector<float>
|
||||
vector<float>
|
||||
RubberBandStretcher::getPhaseResetCurve() const
|
||||
{
|
||||
return m_d->getPhaseResetCurve();
|
||||
}
|
||||
|
||||
std::vector<int>
|
||||
vector<int>
|
||||
RubberBandStretcher::getExactTimePoints() const
|
||||
{
|
||||
return m_d->getExactTimePoints();
|
||||
|
||||
@@ -43,9 +43,11 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set<size_t> &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<size_t>::const_iterator i = sizes.end();
|
||||
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
|
||||
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;
|
||||
|
||||
@@ -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<float> &inbuf = *cd.inbuf;
|
||||
RingBuffer<float> &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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// cerr << "process returning" << endl;
|
||||
if (m_debugLevel > 2) {
|
||||
cerr << "process returning" << endl;
|
||||
}
|
||||
|
||||
if (final) m_mode = Finished;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,25 +17,24 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "Scavenger.h"
|
||||
|
||||
//#define DEBUG_RINGBUFFER 1
|
||||
|
||||
#include "system/sysutils.h"
|
||||
#include "system/Allocators.h"
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
#include <iostream>
|
||||
#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 <typename T, int N = 1>
|
||||
template <typename T>
|
||||
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<T, N> *resized(int newSize, int R = 0) const;
|
||||
RingBuffer<T> *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 <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
|
||||
* 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 <typename S>
|
||||
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;
|
||||
T *const R__ m_buffer;
|
||||
int m_writer;
|
||||
int m_reader;
|
||||
const int m_size;
|
||||
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:
|
||||
RingBuffer(const RingBuffer &); // not provided
|
||||
RingBuffer &operator=(const RingBuffer &); // not provided
|
||||
};
|
||||
|
||||
template <typename T, int N>
|
||||
Scavenger<ScavengerArrayWrapper<T> > RingBuffer<T, N>::m_scavenger;
|
||||
|
||||
template <typename T, int N>
|
||||
RingBuffer<T, N>::RingBuffer(int n) :
|
||||
template <typename T>
|
||||
RingBuffer<T>::RingBuffer(int n) :
|
||||
m_buffer(allocate<T>(n + 1)),
|
||||
m_writer(0),
|
||||
m_size(n + 1),
|
||||
m_mlocked(false)
|
||||
{
|
||||
m_buffer = allocate<T>(n + 1);
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl;
|
||||
std::cerr << "RingBuffer<T>[" << this << "]::RingBuffer(" << n << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < N; ++i) m_readers[i] = 0;
|
||||
|
||||
m_scavenger.scavenge();
|
||||
m_reader = 0;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
RingBuffer<T, N>::~RingBuffer()
|
||||
template <typename T>
|
||||
RingBuffer<T>::~RingBuffer()
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl;
|
||||
std::cerr << "RingBuffer<T>[" << this << "]::~RingBuffer" << std::endl;
|
||||
#endif
|
||||
|
||||
if (m_mlocked) {
|
||||
MUNLOCK((void *)m_buffer, m_size * sizeof(T));
|
||||
}
|
||||
|
||||
deallocate<T>(m_buffer);
|
||||
|
||||
m_scavenger.scavenge();
|
||||
deallocate(m_buffer);
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
template <typename T>
|
||||
int
|
||||
RingBuffer<T, N>::getSize() const
|
||||
RingBuffer<T>::getSize() const
|
||||
{
|
||||
#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
|
||||
|
||||
return m_size - 1;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
void
|
||||
RingBuffer<T, N>::resize(int newSize)
|
||||
template <typename T>
|
||||
RingBuffer<T> *
|
||||
RingBuffer<T>::resized(int newSize) const
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
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);
|
||||
RingBuffer<T> *newBuffer = new RingBuffer<T>(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<T, N>::resized(int newSize, int R) const
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
template <typename T>
|
||||
bool
|
||||
RingBuffer<T, N>::mlock()
|
||||
RingBuffer<T>::mlock()
|
||||
{
|
||||
if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false;
|
||||
m_mlocked = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
template <typename T>
|
||||
void
|
||||
RingBuffer<T, N>::reset()
|
||||
RingBuffer<T>::reset()
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl;
|
||||
std::cerr << "RingBuffer<T>[" << this << "]::reset" << std::endl;
|
||||
#endif
|
||||
|
||||
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
|
||||
RingBuffer<T, N>::getReadSpace(int R) const
|
||||
RingBuffer<T>::getReadSpace() const
|
||||
{
|
||||
int writer = m_writer;
|
||||
int reader = m_readers[R];
|
||||
int space;
|
||||
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): reader " << reader << ", writer " << writer << std::endl;
|
||||
#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;
|
||||
return readSpaceFor(m_writer, m_reader);
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
template <typename T>
|
||||
int
|
||||
RingBuffer<T, N>::getWriteSpace() const
|
||||
RingBuffer<T>::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<T," << N << ">[" << this << "]::getWriteSpace(): " << space << std::endl;
|
||||
#endif
|
||||
|
||||
return space;
|
||||
return writeSpaceFor(m_writer, m_reader);
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
template <typename T>
|
||||
template <typename S>
|
||||
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
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << 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<T, S>(destination, bufbase, n);
|
||||
v_convert(destination, bufbase, n);
|
||||
} else {
|
||||
v_convert<T, S>(destination, bufbase, here);
|
||||
v_convert<T, S>(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<T," << N << ">[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl;
|
||||
#endif
|
||||
MBARRIER();
|
||||
m_reader = r;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
template <typename T>
|
||||
template <typename S>
|
||||
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
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << 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<T, N>::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 <typename T, int N>
|
||||
template <typename T>
|
||||
T
|
||||
RingBuffer<T, N>::readOne(int R)
|
||||
RingBuffer<T>::readOne()
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << 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 <typename T, int N>
|
||||
template <typename T>
|
||||
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
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << 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<T, N>::peek(T *const R__ destination, int n, int R) const
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
template <typename T>
|
||||
T
|
||||
RingBuffer<T, N>::peekOne(int R) const
|
||||
RingBuffer<T>::peekOne() const
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << 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 <typename T, int N>
|
||||
template <typename T>
|
||||
int
|
||||
RingBuffer<T, N>::skip(int n, int R)
|
||||
RingBuffer<T>::skip(int n)
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << 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 <typename T, int N>
|
||||
template <typename T>
|
||||
template <typename S>
|
||||
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
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << 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<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);
|
||||
}
|
||||
|
||||
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<T," << N << ">[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl;
|
||||
#endif
|
||||
MBARRIER();
|
||||
m_writer = w;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
template <typename T>
|
||||
int
|
||||
RingBuffer<T, N>::zero(int n)
|
||||
RingBuffer<T>::zero(int n)
|
||||
{
|
||||
#ifdef DEBUG_RINGBUFFER
|
||||
std::cerr << "RingBuffer<T," << N << ">[" << 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<T, N>::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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "system/VectorOps.h"
|
||||
#include "system/VectorOpsComplex.h"
|
||||
|
||||
#define FFT_MEASUREMENT 1
|
||||
//#define FFT_MEASUREMENT 1
|
||||
|
||||
|
||||
#ifdef HAVE_FFTW3
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -107,4 +107,4 @@ GETOPT_API int getopt __P((int, char * const *, const char *));
|
||||
__END_DECLS
|
||||
#endif
|
||||
|
||||
#endif /* !_GETOPT_H_ */
|
||||
#endif
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace RubberBand {
|
||||
|
||||
template <typename T>
|
||||
@@ -50,7 +51,9 @@ T *allocate(size_t count)
|
||||
// since it doesn't exist)
|
||||
ptr = malloc(count * sizeof(T));
|
||||
#endif
|
||||
if (!ptr) throw(std::bad_alloc());
|
||||
if (!ptr) {
|
||||
throw(std::bad_alloc());
|
||||
}
|
||||
return (T *)ptr;
|
||||
}
|
||||
|
||||
@@ -74,13 +77,7 @@ void deallocate(T *ptr)
|
||||
template <typename T>
|
||||
T *reallocate(T *ptr, size_t oldcount, size_t count)
|
||||
{
|
||||
T *newptr = 0;
|
||||
try {
|
||||
newptr = allocate<T>(count);
|
||||
} catch (std::bad_alloc) {
|
||||
if (ptr) deallocate<T>(ptr);
|
||||
throw;
|
||||
}
|
||||
T *newptr = allocate<T>(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<T>(channels, count);
|
||||
} catch (std::bad_alloc) {
|
||||
if (ptr) deallocate_channels<T>(ptr, channels);
|
||||
throw;
|
||||
}
|
||||
T **newptr = allocate_channels<T>(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<T>(channels, count);
|
||||
} catch (std::bad_alloc) {
|
||||
if (ptr) deallocate_channels<T>(ptr, channels);
|
||||
throw;
|
||||
}
|
||||
T **newptr = allocate_and_zero_channels<T>(channels, count);
|
||||
if (oldcount && ptr) {
|
||||
v_copy_channels(newptr, ptr, channels, oldcount < count ? oldcount : count);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
inline void v_move(T *const dst,
|
||||
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>
|
||||
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>
|
||||
inline void v_log(T *const R__ dst,
|
||||
const int count)
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <unistd.h>
|
||||
#ifdef __APPLE__
|
||||
#include <sys/sysctl.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#else /* !__APPLE__, !_WIN32 */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -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 <pthread.h>
|
||||
void system_memorybarrier()
|
||||
{
|
||||
pthread_mutex_t dummy = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_lock(&dummy);
|
||||
pthread_mutex_unlock(&dummy);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <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 DLSYM(a,b) dlsym((a),(b))
|
||||
#define DLCLOSE(a) dlclose((a))
|
||||
|
||||
Reference in New Issue
Block a user