* 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
|
# 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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -107,4 +107,4 @@ GETOPT_API int getopt __P((int, char * const *, const char *));
|
|||||||
__END_DECLS
|
__END_DECLS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* !_GETOPT_H_ */
|
#endif
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
Reference in New Issue
Block a user