Merge from branch single

This commit is contained in:
Chris Cannam
2021-10-14 13:34:33 +01:00
46 changed files with 2285 additions and 561 deletions

View File

@@ -24,4 +24,5 @@ Release/
Debug/
build
build_*
build-*
UpgradeLog*

View File

@@ -31,7 +31,7 @@ PROJECT_NAME = "Rubber Band Library"
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = 1.9.2
PROJECT_NUMBER = 2.0.0
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.

View File

@@ -1,5 +1,5 @@
# Rubber Band
# Rubber Band Library
An audio time-stretching and pitch-shifting library and utility program.
@@ -29,12 +29,12 @@ License (GPL). You can redistribute it and/or modify it under the
terms of the GPL; either version 2 of the License, or (at your option)
any later version. See the file COPYING for more information.
If you wish to distribute code using the Rubber Band Library under
terms other than those of the GNU General Public License, you must
obtain a commercial licence from us before doing so. In particular,
you may not legally distribute through any Apple App Store unless you
have a commercial licence. See https://breakfastquay.com/rubberband/
for licence terms.
If you wish to distribute code using Rubber Band Library under terms
other than those of the GNU General Public License, you must obtain a
commercial licence from us before doing so. In particular, you may not
legally distribute through any Apple App Store unless you have a
commercial licence. See https://breakfastquay.com/rubberband/ for
licence terms.
If you have obtained a valid commercial licence, your licence
supersedes this README and the enclosed COPYING file and you may
@@ -59,7 +59,6 @@ our knowledge. See also the end of this README for detailed terms.
* Intel IPP - Proprietary; licence needed for redistribution
* KissFFT - BSD-like
* libsamplerate - BSD-like from version 0.1.9 onwards
* libresample - LGPL
* Speex - BSD-like
* Pommier math functions - BSD-like
@@ -68,7 +67,7 @@ our knowledge. See also the end of this README for detailed terms.
1. Code components
2. Using the Rubber Band command-line tool
3. Using the Rubber Band Library
3. Using Rubber Band Library
4. Compiling Rubber Band
a. Building on Linux
b. Building on macOS
@@ -91,7 +90,7 @@ Rubber Band consists of:
and FFT code; see section 3a below for details.
* The Rubber Band command-line tool. This is in main/main.cpp.
This program uses the Rubber Band Library and also requires libsndfile
This program uses Rubber Band Library and also requires libsndfile
(http://www.mega-nerd.com/libsndfile/, licensed under the GNU Lesser
General Public License) for audio file loading.
@@ -127,12 +126,12 @@ In particular, different types of music may benefit from different
"crispness" options (-c flag with a numerical argument from 0 to 6).
## 3. Using the Rubber Band Library
## 3. Using Rubber Band Library
The Rubber Band Library has a public API that consists of one C++
class, called RubberBandStretcher in the RubberBand namespace. You
should `#include <rubberband/RubberBandStretcher.h>` to use this
class. There is extensive documentation in the class header.
Rubber Band has a public API that consists of one C++ class, called
`RubberBandStretcher` in the `RubberBand` namespace. You should
`#include <rubberband/RubberBandStretcher.h>` to use this class.
There is extensive documentation in the class header.
A header with C language bindings is also provided in
`<rubberband/rubberband-c.h>`. This is a wrapper around the C++
@@ -158,18 +157,26 @@ for modification and redistribution) unless you have separately
acquired a commercial licence from the author.
## 4. Compiling the Rubber Band Library
## 4. Compiling Rubber Band Library
The primary supported build system for the Rubber Band Library on all
platforms is Meson (https://mesonbuild.com). The Meson build system
can be used to build all targets (static and dynamic library,
command-line utility, and plugins) and to cross-compile.
The primary supported build system for Rubber Band on all platforms is
Meson (https://mesonbuild.com). The Meson build system can be used to
build all targets (static and dynamic library, command-line utility,
and plugins) and to cross-compile.
If you only need a static library and don't wish to use Meson, some
If you only need a static library and don't wish to use Meson, some
alternative build files (Makefiles and Visual C++ projects) are
included in the `otherbuilds` directory. See the platform-specific
build sections below for more details.
☞ If you want to include Rubber Band in a C++ project and would prefer
not to compile a separate library, there is a single `.cpp` file at
`single/RubberBandSingle.cpp` which can be added to your project
as-is. It produces a single compilation-unit build with the built-in
FFT and resampler implementations with no further library
dependencies. See the comments at the top of that file for more
information.
To build with Meson, ensure Meson and Ninja are installed and run:
```
@@ -193,10 +200,10 @@ $ meson build -Dipp_path=/opt/intel/ipp
The options are documented in the library- and platform-specific
sections below.
The Rubber Band Library is written entirely in C++ to the C++98
standard. It is unlikely to make any difference (performance or
otherwise) which C++ standard your compiler uses - as long as it's no
older than C++98!
Rubber Band Library is written entirely in C++ and requires a C++11
compiler. It is unlikely to make any difference (performance or
otherwise) which C++ standard you compile with, as long as it's no
older than C++11.
If you are building this software using either of the Speex or KissFFT
library options, please be sure to review the terms for those
@@ -268,8 +275,8 @@ See "FFT and resampler selection" below for further build options.
Note that you cannot legally distribute applications using Rubber Band
in the Mac App Store, unless you have first obtained a commercial
licence for the Rubber Band Library. GPL code is not permitted in the
app store. See https://breakfastquay.com/technology/license.html for
licence for Rubber Band Library. GPL code is not permitted in the app
store. See https://breakfastquay.com/technology/license.html for
commercial terms.
@@ -295,8 +302,8 @@ See "FFT and resampler selection" below for further build options.
Note that you cannot legally distribute applications using Rubber Band
in the iOS App Store, unless you have a first obtained a commercial
licence for the Rubber Band Library. GPL code is not permitted in the
app store. See https://breakfastquay.com/technology/license.html for
licence for Rubber Band Library. GPL code is not permitted in the app
store. See https://breakfastquay.com/technology/license.html for
commercial terms.
@@ -409,10 +416,21 @@ Library Build option CPP define Notes
---- ------------ ---------- -----
libsamplerate -DHAVE_LIBSAMPLERATE
-Dresampler=libsamplerate Best choice in most cases.
-Dresampler=libsamplerate Default when found.
Good choice in most cases.
Built-in -Dfft=builtin -DUSE_BQRESAMPLER
Default when libsamplerate not found.
Can be distributed with either
the Rubber Band GPL or
commercial licence. Intended to
give best quality for time-varying
pitch shifts in real-time mode.
Newer than, and not as well-tested
as, libsamplerate.
Speex -DUSE_SPEEX
-Dresampler=speex Bundled, can be distributed with
-Dresampler=speex Can be distributed with
either the Rubber Band GPL or
commercial licence.
```

View File

@@ -44,7 +44,7 @@ RubberBandPitchShifter::portNamesMono[PortCountMono] =
"Octaves",
"Crispness",
"Formant Preserving",
"Faster",
"Wet-Dry Mix",
"Input",
"Output"
};
@@ -58,7 +58,7 @@ RubberBandPitchShifter::portNamesStereo[PortCountStereo] =
"Octaves",
"Crispness",
"Formant Preserving",
"Faster",
"Wet-Dry Mix",
"Input L",
"Output L",
"Input R",
@@ -112,7 +112,7 @@ RubberBandPitchShifter::hintsMono[PortCountMono] =
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-3.0, 3.0 },
-2.0, 2.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
@@ -123,10 +123,9 @@ RubberBandPitchShifter::hintsMono[PortCountMono] =
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
0.0, 1.0 },
{ LADSPA_HINT_DEFAULT_0 | // fast
{ LADSPA_HINT_DEFAULT_0 | // wet-dry mix
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
LADSPA_HINT_BOUNDED_ABOVE,
0.0, 1.0 },
{ 0, 0, 0 },
{ 0, 0, 0 }
@@ -149,7 +148,7 @@ RubberBandPitchShifter::hintsStereo[PortCountStereo] =
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-3.0, 3.0 },
-2.0, 2.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
@@ -160,10 +159,9 @@ RubberBandPitchShifter::hintsStereo[PortCountStereo] =
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
0.0, 1.0 },
{ LADSPA_HINT_DEFAULT_0 | // fast
{ LADSPA_HINT_DEFAULT_0 | // wet-dry mix
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
LADSPA_HINT_BOUNDED_ABOVE,
0.0, 1.0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
@@ -237,14 +235,14 @@ RubberBandPitchShifter::RubberBandPitchShifter(int sampleRate, size_t channels)
m_octaves(0),
m_crispness(0),
m_formant(0),
m_fast(0),
m_wetDry(0),
m_ratio(1.0),
m_prevRatio(1.0),
m_currentCrispness(-1),
m_currentFormant(false),
m_currentFast(false),
m_blockSize(1024),
m_reserve(1024),
m_reserve(8192),
m_bufsize(0),
m_minfill(0),
m_stretcher(new RubberBandStretcher
(sampleRate, channels,
@@ -257,19 +255,23 @@ RubberBandPitchShifter::RubberBandPitchShifter(int sampleRate, size_t channels)
m_output = new float *[m_channels];
m_outputBuffer = new RingBuffer<float> *[m_channels];
m_delayMixBuffer = new RingBuffer<float> *[m_channels];
m_scratch = new float *[m_channels];
m_bufsize = m_blockSize + m_reserve + 8192;
for (size_t c = 0; c < m_channels; ++c) {
m_input[c] = 0;
m_output[c] = 0;
int bufsize = m_blockSize + m_reserve + 8192;
m_outputBuffer[c] = new RingBuffer<float>(m_bufsize);
m_delayMixBuffer[c] = new RingBuffer<float>(m_bufsize);
m_outputBuffer[c] = new RingBuffer<float>(bufsize);
m_scratch[c] = new float[bufsize];
for (int i = 0; i < bufsize; ++i) m_scratch[c][i] = 0.f;
m_scratch[c] = new float[m_bufsize];
for (size_t i = 0; i < m_bufsize; ++i) {
m_scratch[c][i] = 0.f;
}
}
activateImpl();
@@ -280,9 +282,11 @@ RubberBandPitchShifter::~RubberBandPitchShifter()
delete m_stretcher;
for (size_t c = 0; c < m_channels; ++c) {
delete m_outputBuffer[c];
delete m_delayMixBuffer[c];
delete[] m_scratch[c];
}
delete[] m_outputBuffer;
delete[] m_delayMixBuffer;
delete[] m_scratch;
delete[] m_output;
delete[] m_input;
@@ -312,7 +316,7 @@ RubberBandPitchShifter::connectPort(LADSPA_Handle handle,
&shifter->m_octaves,
&shifter->m_crispness,
&shifter->m_formant,
&shifter->m_fast,
&shifter->m_wetDry,
&shifter->m_input[0],
&shifter->m_output[0],
&shifter->m_input[1],
@@ -328,11 +332,16 @@ RubberBandPitchShifter::connectPort(LADSPA_Handle handle,
*ports[port] = (float *)location;
if (shifter->m_latency) {
*(shifter->m_latency) =
float(shifter->m_stretcher->getLatency() + shifter->m_reserve);
*(shifter->m_latency) = shifter->getLatency();
}
}
int
RubberBandPitchShifter::getLatency() const
{
return m_reserve;
}
void
RubberBandPitchShifter::activate(LADSPA_Handle handle)
{
@@ -350,20 +359,22 @@ RubberBandPitchShifter::activateImpl()
for (size_t c = 0; c < m_channels; ++c) {
m_outputBuffer[c]->reset();
m_outputBuffer[c]->zero(m_reserve);
}
for (size_t c = 0; c < m_channels; ++c) {
m_delayMixBuffer[c]->reset();
m_delayMixBuffer[c]->zero(getLatency());
}
for (size_t c = 0; c < m_channels; ++c) {
for (size_t i = 0; i < m_bufsize; ++i) {
m_scratch[c][i] = 0.f;
}
}
m_minfill = 0;
// prime stretcher
// for (int i = 0; i < 8; ++i) {
// int reqd = m_stretcher->getSamplesRequired();
// m_stretcher->process(m_scratch, reqd, false);
// int avail = m_stretcher->available();
// if (avail > 0) {
// m_stretcher->retrieve(m_scratch, avail);
// }
// }
m_stretcher->process(m_scratch, m_reserve, false);
}
void
@@ -431,23 +442,6 @@ RubberBandPitchShifter::updateFormant()
m_currentFormant = f;
}
void
RubberBandPitchShifter::updateFast()
{
if (!m_fast) return;
bool f = (*m_fast > 0.5f);
if (f == m_currentFast) return;
RubberBandStretcher *s = m_stretcher;
s->setPitchOption(f ?
RubberBandStretcher::OptionPitchHighSpeed :
RubberBandStretcher::OptionPitchHighConsistency);
m_currentFast = f;
}
void
RubberBandPitchShifter::runImpl(unsigned long insamples)
{
@@ -466,15 +460,29 @@ RubberBandPitchShifter::runImpl(unsigned long insamples)
offset += block;
}
if (m_wetDry) {
for (size_t c = 0; c < m_channels; ++c) {
m_delayMixBuffer[c]->write(m_input[c], insamples);
}
float mix = *m_wetDry;
for (size_t c = 0; c < m_channels; ++c) {
if (mix > 0.0) {
for (unsigned long i = 0; i < insamples; ++i) {
float dry = m_delayMixBuffer[c]->readOne();
m_output[c][i] *= (1.0 - mix);
m_output[c][i] += dry * mix;
}
} else {
m_delayMixBuffer[c]->skip(insamples);
}
}
}
}
void
RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
{
// cerr << "RubberBandPitchShifter::runImpl(" << insamples << ")" << endl;
// static int incount = 0, outcount = 0;
updateRatio();
if (m_ratio != m_prevRatio) {
m_stretcher->setPitchScale(m_ratio);
@@ -482,13 +490,11 @@ RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
}
if (m_latency) {
*m_latency = float(m_stretcher->getLatency() + m_reserve);
// cerr << "latency = " << *m_latency << endl;
*m_latency = getLatency();
}
updateCrispness();
updateFormant();
updateFast();
const int samples = insamples;
int processed = 0;
@@ -496,17 +502,6 @@ RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
float *ptrs[2];
int rs = m_outputBuffer[0]->getReadSpace();
if (rs < int(m_minfill)) {
// cerr << "temporary expansion (have " << rs << ", want " << m_reserve << ")" << endl;
m_stretcher->setTimeRatio(1.1); // fill up temporarily
} else if (rs > 8192) {
// cerr << "temporary reduction (have " << rs << ", want " << m_reserve << ")" << endl;
m_stretcher->setTimeRatio(0.9); // reduce temporarily
} else {
m_stretcher->setTimeRatio(1.0);
}
while (processed < samples) {
// never feed more than the minimum necessary number of
@@ -523,24 +518,18 @@ RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
int avail = m_stretcher->available();
int writable = m_outputBuffer[0]->getWriteSpace();
int outchunk = min(avail, writable);
int outchunk = avail;
if (outchunk > writable) {
cerr << "RubberBandPitchShifter::runImpl: buffer is not large enough: size = " << m_outputBuffer[0]->getSize() << ", chunk = " << outchunk << ", space = " << writable << " (buffer contains " << m_outputBuffer[0]->getReadSpace() << " unread)" << endl;
outchunk = writable;
}
size_t actual = m_stretcher->retrieve(m_scratch, outchunk);
outTotal += actual;
// incount += inchunk;
// outcount += actual;
// cout << "avail: " << avail << ", outchunk = " << outchunk;
// if (actual != outchunk) cout << " (" << actual << ")";
// cout << endl;
outchunk = actual;
for (size_t c = 0; c < m_channels; ++c) {
if (int(m_outputBuffer[c]->getWriteSpace()) < outchunk) {
cerr << "RubberBandPitchShifter::runImpl: buffer overrun: chunk = " << outchunk << ", space = " << m_outputBuffer[c]->getWriteSpace() << endl;
}
m_outputBuffer[c]->write(m_scratch[c], outchunk);
m_outputBuffer[c]->write(m_scratch[c], actual);
}
}
@@ -553,8 +542,9 @@ RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
m_outputBuffer[c]->read(&(m_output[c][offset]), chunk);
}
if (m_minfill == 0) {
m_minfill = m_outputBuffer[0]->getReadSpace();
size_t fill = m_outputBuffer[0]->getReadSpace();
if (fill < m_minfill || m_minfill == 0) {
m_minfill = fill;
// cerr << "minfill = " << m_minfill << endl;
}
}

View File

@@ -48,7 +48,7 @@ protected:
CentsPort = 3,
CrispnessPort = 4,
FormantPort = 5,
FastPort = 6,
WetDryPort = 6,
InputPort1 = 7,
OutputPort1 = 8,
PortCountMono = OutputPort1 + 1,
@@ -83,7 +83,8 @@ protected:
void updateRatio();
void updateCrispness();
void updateFormant();
void updateFast();
int getLatency() const;
float **m_input;
float **m_output;
@@ -93,19 +94,20 @@ protected:
float *m_octaves;
float *m_crispness;
float *m_formant;
float *m_fast;
float *m_wetDry;
double m_ratio;
double m_prevRatio;
int m_currentCrispness;
bool m_currentFormant;
bool m_currentFast;
size_t m_blockSize;
size_t m_reserve;
size_t m_bufsize;
size_t m_minfill;
RubberBand::RubberBandStretcher *m_stretcher;
RubberBand::RingBuffer<float> **m_outputBuffer;
RubberBand::RingBuffer<float> **m_delayMixBuffer;
float **m_scratch;
int m_sampleRate;

View File

@@ -21,7 +21,7 @@
you must obtain a valid commercial licence before doing so.
*/
#include "rubberband/RubberBandStretcher.h"
#include "../rubberband/RubberBandStretcher.h"
#include <iostream>
#include <sndfile.h>
@@ -33,20 +33,17 @@
#include <fstream>
#include "system/sysutils.h"
#include "../src/system/sysutils.h"
#ifdef _MSC_VER
#include "getopt/getopt.h"
#include "../src/getopt/getopt.h"
#else
#include <getopt.h>
#include <unistd.h>
#include <sys/time.h>
#endif
#include "base/Profiler.h"
using namespace std;
using namespace RubberBand;
#include "../src/base/Profiler.h"
#ifdef _WIN32
using RubberBand::gettimeofday;
@@ -57,6 +54,11 @@ using RubberBand::usleep;
#define strdup _strdup
#endif
using RubberBand::RubberBandStretcher;
using std::cerr;
using std::endl;
double tempo_convert(const char *str)
{
char *d = strchr((char *)str, ':');
@@ -105,7 +107,10 @@ int main(int argc, char **argv)
bool haveRatio = false;
std::string mapfile;
std::string timeMapFile;
std::string freqMapFile;
std::string pitchMapFile;
bool freqOrPitchMapSpecified = false;
enum {
NoTransients,
@@ -119,6 +124,8 @@ int main(int argc, char **argv)
SoftDetector
} detector = CompoundDetector;
bool ignoreClipping = false;
while (1) {
int optionIndex = 0;
@@ -151,6 +158,9 @@ int main(int argc, char **argv)
{ "threads", 0, 0, '@' },
{ "quiet", 0, 0, 'q' },
{ "timemap", 1, 0, 'M' },
{ "freqmap", 1, 0, 'Q' },
{ "pitchmap", 1, 0, 'C' },
{ "ignore-clipping", 0, 0, 'i' },
{ 0, 0, 0, 0 }
};
@@ -171,7 +181,7 @@ int main(int argc, char **argv)
case 'R': realtime = true; break;
case 'L': precise = false; break;
case 'P': precise = true; break;
case 'F': formant = true; break;
case 'F': formant = true; break;
case '0': threading = 1; break;
case '@': threading = 2; break;
case '1': transients = NoTransients; crispchanged = true; break;
@@ -186,7 +196,10 @@ int main(int argc, char **argv)
case '%': hqpitch = true; break;
case 'c': crispness = atoi(optarg); break;
case 'q': quiet = true; break;
case 'M': mapfile = optarg; break;
case 'M': timeMapFile = optarg; break;
case 'Q': freqMapFile = optarg; freqOrPitchMapSpecified = true; break;
case 'C': pitchMapFile = optarg; freqOrPitchMapSpecified = true; break;
case 'i': ignoreClipping = true; break;
default: help = true; break;
}
}
@@ -196,6 +209,15 @@ int main(int argc, char **argv)
return 0;
}
if (freqOrPitchMapSpecified) {
if (freqMapFile != "" && pitchMapFile != "") {
cerr << "ERROR: Please specify either pitch map or frequency map, not both" << endl;
return 1;
}
haveRatio = true;
realtime = true;
}
if (help || !haveRatio || optind + 2 != argc) {
cerr << endl;
cerr << "Rubber Band" << endl;
@@ -214,23 +236,45 @@ int main(int argc, char **argv)
cerr << " -p<X>, --pitch <X> Raise pitch by X semitones, or" << endl;
cerr << " -f<X>, --frequency <X> Change frequency by multiple X" << endl;
cerr << endl;
cerr << " -M<F>, --timemap <F> Use file F as the source for key frame map" << endl;
cerr << "The following options provide ways of making the time and frequency ratios" << endl;
cerr << "change during the audio." << endl;
cerr << endl;
cerr << "A map file consists of a series of lines each having two numbers separated" << endl;
cerr << "by a single space. These are source and target sample frame numbers for fixed" << endl;
cerr << "time points within the audio data, defining a varying stretch factor through" << endl;
cerr << "the audio. You must specify an overall stretch factor using e.g. -t as well." << endl;
cerr << " -M<F>, --timemap <F> Use file F as the source for time map" << endl;
cerr << endl;
cerr << "The following options provide a simple way to adjust the sound. See below" << endl;
cerr << " A time map (or key-frame map) file contains a series of lines, each with two" << endl;
cerr << " sample frame numbers separated by a single space. These are source and" << endl;
cerr << " target frames for fixed time points within the audio data, defining a varying" << endl;
cerr << " stretch factor through the audio. When supplying a time map you must specify" << endl;
cerr << " an overall stretch factor using -t, -T, or -D as well, to determine the" << endl;
cerr << " total output duration." << endl;
cerr << endl;
cerr << " --pitchmap <F> Use file F as the source for pitch map" << endl;
cerr << endl;
cerr << " A pitch map file contains a series of lines, each with two values: the input" << endl;
cerr << " sample frame number and a pitch offset in semitones, separated by a single" << endl;
cerr << " space. These specify a varying pitch factor through the audio. The offsets" << endl;
cerr << " are all relative to an initial offset specified by the pitch or frequency" << endl;
cerr << " option, or relative to no shift if neither was specified. Offsets are" << endl;
cerr << " not cumulative. This option implies realtime mode (-R) and also enables a" << endl;
cerr << " high-consistency pitch shifting mode, appropriate for dynamic pitch changes." << endl;
cerr << " Because of the use of realtime mode, the overall duration will not be exact." << endl;
cerr << endl;
cerr << " --freqmap <F> Use file F as the source for frequency map" << endl;
cerr << endl;
cerr << " A frequency map file is like a pitch map, except that its second column" << endl;
cerr << " lists frequency multipliers rather than pitch offsets (like the difference" << endl;
cerr << " between pitch and frequency options above)." << endl;
cerr << endl;
cerr << "The following options provide a simple way to adjust the sound. See below" << endl;
cerr << "for more details." << endl;
cerr << endl;
cerr << " -c<N>, --crisp <N> Crispness (N = 0,1,2,3,4,5,6); default 5 (see below)" << endl;
cerr << " -F, --formant Enable formant preservation when pitch shifting" << endl;
cerr << " -F, --formant Enable formant preservation when pitch shifting" << endl;
cerr << endl;
cerr << "The remaining options fine-tune the processing mode and stretch algorithm." << endl;
cerr << "These are mostly included for test purposes; the default settings and standard" << endl;
cerr << "crispness parameter are intended to provide the best sounding set of options" << endl;
cerr << "for most situations. The default is to use none of these options." << endl;
cerr << "for most situations. The default is to use none of these options." << endl;
cerr << endl;
cerr << " -L, --loose Relax timing in hope of better transient preservation" << endl;
cerr << " -P, --precise Ignored: The opposite of -L, this is default from 1.6" << endl;
@@ -248,6 +292,8 @@ int main(int argc, char **argv)
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 << " --ignore-clipping Ignore clipping at output; the default is to restart" << endl;
cerr << " with reduced gain if clipping occurs" << 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;
@@ -265,7 +311,7 @@ int main(int argc, char **argv)
cerr << " -c 5 default processing options" << endl;
cerr << " -c 6 equivalent to --no-lamination --window-short (may be good for drums)" << endl;
cerr << endl;
return 2;
return 2;
}
if (ratio <= 0.0) {
@@ -278,6 +324,12 @@ int main(int argc, char **argv)
cerr << " provided -- crispness will override these other options" << endl;
}
if (hqpitch && freqOrPitchMapSpecified) {
cerr << "WARNING: High-quality pitch mode selected, but frequency or pitch map file is" << endl;
cerr << " provided -- pitch mode will be overridden by high-consistency mode" << endl;
hqpitch = false;
}
switch (crispness) {
case -1: crispness = 5; break;
case 0: detector = CompoundDetector; transients = NoTransients; lamination = false; longwin = true; shortwin = false; break;
@@ -303,34 +355,35 @@ int main(int argc, char **argv)
cerr << ")" << endl;
}
std::map<size_t, size_t> mapping;
if (mapfile != "") {
std::ifstream ifile(mapfile.c_str());
std::map<size_t, size_t> timeMap;
if (timeMapFile != "") {
std::ifstream ifile(timeMapFile.c_str());
if (!ifile.is_open()) {
cerr << "ERROR: Failed to open time map file \"" << mapfile << "\""
<< endl;
cerr << "ERROR: Failed to open time map file \""
<< timeMapFile << "\"" << endl;
return 1;
}
std::string line;
int lineno = 0;
while (!ifile.eof()) {
std::getline(ifile, line);
while (line.length() > 0 && line[0] == ' ') line = line.substr(1);
while (line.length() > 0 && line[0] == ' ') {
line = line.substr(1);
}
if (line == "") {
++lineno;
continue;
}
std::string::size_type i = line.find_first_of(" ");
if (i == std::string::npos) {
cerr << "ERROR: Time map file \"" << mapfile
cerr << "ERROR: Time map file \"" << timeMapFile
<< "\" is malformed at line " << lineno << endl;
return 1;
}
size_t source = atoi(line.substr(0, i).c_str());
while (i < line.length() && line[i] == ' ') ++i;
size_t target = atoi(line.substr(i).c_str());
mapping[source] = target;
timeMap[source] = target;
if (debug > 0) {
cerr << "adding mapping from " << source << " to " << target << endl;
}
@@ -339,7 +392,57 @@ int main(int argc, char **argv)
ifile.close();
if (!quiet) {
cerr << "Read " << mapping.size() << " line(s) from map file" << endl;
cerr << "Read " << timeMap.size() << " line(s) from time map file" << endl;
}
}
std::map<size_t, double> freqMap;
if (freqOrPitchMapSpecified) {
std::string file = freqMapFile;
bool convertFromPitch = false;
if (pitchMapFile != "") {
file = pitchMapFile;
convertFromPitch = true;
}
std::ifstream ifile(file.c_str());
if (!ifile.is_open()) {
cerr << "ERROR: Failed to open map file \"" << file << "\"" << endl;
return 1;
}
std::string line;
int lineno = 0;
while (!ifile.eof()) {
std::getline(ifile, line);
while (line.length() > 0 && line[0] == ' ') {
line = line.substr(1);
}
if (line == "") {
++lineno;
continue;
}
std::string::size_type i = line.find_first_of(" ");
if (i == std::string::npos) {
cerr << "ERROR: Map file \"" << file
<< "\" is malformed at line " << lineno << endl;
return 1;
}
size_t source = atoi(line.substr(0, i).c_str());
while (i < line.length() && line[i] == ' ') ++i;
double freq = atof(line.substr(i).c_str());
if (convertFromPitch) {
freq = pow(2.0, freq / 12.0);
}
freqMap[source] = freq;
if (debug > 0) {
cerr << "adding mapping for source frame " << source << " of frequency multiplier " << freq << endl;
}
++lineno;
}
ifile.close();
if (!quiet) {
cerr << "Read " << freqMap.size() << " line(s) from frequency map file" << endl;
}
}
@@ -355,9 +458,9 @@ int main(int argc, char **argv)
sndfile = sf_open(fileName, SFM_READ, &sfinfo);
if (!sndfile) {
cerr << "ERROR: Failed to open input file \"" << fileName << "\": "
<< sf_strerror(sndfile) << endl;
return 1;
cerr << "ERROR: Failed to open input file \"" << fileName << "\": "
<< sf_strerror(sndfile) << endl;
return 1;
}
if (sfinfo.samplerate == 0) {
@@ -383,13 +486,10 @@ int main(int argc, char **argv)
sndfileOut = sf_open(fileNameOut, SFM_WRITE, &sfinfoOut) ;
if (!sndfileOut) {
cerr << "ERROR: Failed to open output file \"" << fileNameOut << "\" for writing: "
<< sf_strerror(sndfileOut) << endl;
return 1;
cerr << "ERROR: Failed to open output file \"" << fileNameOut << "\" for writing: "
<< sf_strerror(sndfileOut) << endl;
return 1;
}
int ibs = 1024;
size_t channels = sfinfo.channels;
RubberBandStretcher::Options options = 0;
if (realtime) options |= RubberBandStretcher::OptionProcessRealTime;
@@ -399,9 +499,14 @@ int main(int argc, char **argv)
if (shortwin) options |= RubberBandStretcher::OptionWindowShort;
if (smoothing) options |= RubberBandStretcher::OptionSmoothingOn;
if (formant) options |= RubberBandStretcher::OptionFormantPreserved;
if (hqpitch) options |= RubberBandStretcher::OptionPitchHighQuality;
if (together) options |= RubberBandStretcher::OptionChannelsTogether;
if (freqOrPitchMapSpecified) {
options |= RubberBandStretcher::OptionPitchHighConsistency;
} else if (hqpitch) {
options |= RubberBandStretcher::OptionPitchHighQuality;
}
switch (threading) {
case 0:
options |= RubberBandStretcher::OptionThreadingAuto;
@@ -439,56 +544,267 @@ int main(int argc, char **argv)
}
if (pitchshift != 0.0) {
frequencyshift *= pow(2.0, pitchshift / 12);
frequencyshift *= pow(2.0, pitchshift / 12.0);
}
cerr << "Using time ratio " << ratio;
cerr << " and frequency ratio " << frequencyshift << endl;
if (!freqOrPitchMapSpecified) {
cerr << " and frequency ratio " << frequencyshift << endl;
} else {
cerr << " and initial frequency ratio " << frequencyshift << endl;
}
#ifdef _WIN32
RubberBand::
#endif
timeval tv;
(void)gettimeofday(&tv, 0);
RubberBandStretcher::setDefaultDebugLevel(debug);
RubberBandStretcher ts(sfinfo.samplerate, channels, options,
ratio, frequencyshift);
size_t countIn = 0, countOut = 0;
ts.setExpectedInputDuration(sfinfo.frames);
float gain = 1.f;
bool successful = false;
const size_t channels = sfinfo.channels;
const int bs = 1024;
float **cbuf = new float *[channels];
for (size_t c = 0; c < channels; ++c) {
cbuf[c] = new float[bs];
}
float *ibuf = new float[channels * bs];
float *fbuf = new float[channels * ibs];
float **ibuf = new float *[channels];
for (size_t i = 0; i < channels; ++i) ibuf[i] = new float[ibs];
int thisBlockSize;
int frame = 0;
int percent = 0;
while (!successful) { // we may have to repeat with a modified
// gain, if clipping occurs
successful = true;
sf_seek(sndfile, 0, SEEK_SET);
RubberBandStretcher ts(sfinfo.samplerate, channels, options,
ratio, frequencyshift);
ts.setExpectedInputDuration(sfinfo.frames);
if (!realtime) {
int frame = 0;
int percent = 0;
if (!quiet) {
cerr << "Pass 1: Studying..." << endl;
sf_seek(sndfile, 0, SEEK_SET);
if (!realtime) {
if (!quiet) {
cerr << "Pass 1: Studying..." << endl;
}
while (frame < sfinfo.frames) {
int count = -1;
if ((count = sf_readf_float(sndfile, ibuf, bs)) <= 0) break;
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < count; ++i) {
cbuf[c][i] = ibuf[i * channels + c];
}
}
bool final = (frame + bs >= sfinfo.frames);
ts.study(cbuf, count, final);
int p = int((double(frame) * 100.0) / sfinfo.frames);
if (p > percent || frame == 0) {
percent = p;
if (!quiet) {
cerr << "\r" << percent << "% ";
}
}
frame += bs;
}
if (!quiet) {
cerr << "\rCalculating profile..." << endl;
}
sf_seek(sndfile, 0, SEEK_SET);
}
frame = 0;
percent = 0;
if (!timeMap.empty()) {
ts.setKeyFrameMap(timeMap);
}
std::map<size_t, double>::const_iterator freqMapItr = freqMap.begin();
countIn = 0;
countOut = 0;
bool clipping = false;
// The stretcher only pads the start in offline mode; to avoid
// a fade in at the start, we pad it manually in RT mode
int toDrop = 0;
if (realtime) {
toDrop = int(ts.getLatency());
int toPad = int(round(toDrop * frequencyshift));
if (debug > 0) {
cerr << "padding start with " << toPad
<< " samples in RT mode, will drop " << toDrop
<< " at output" << endl;
}
if (toPad > 0) {
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < bs; ++i) {
cbuf[c][i] = 0.f;
}
}
while (toPad > 0) {
int p = toPad;
if (p > bs) p = bs;
ts.process(cbuf, p, false);
toPad -= p;
}
}
}
while (frame < sfinfo.frames) {
int count = -1;
thisBlockSize = bs;
if ((count = sf_readf_float(sndfile, fbuf, ibs)) <= 0) break;
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < count; ++i) {
float value = fbuf[i * channels + c];
ibuf[c][i] = value;
while (freqMapItr != freqMap.end()) {
size_t nextFreqFrame = freqMapItr->first;
if (nextFreqFrame <= countIn) {
double s = frequencyshift * freqMapItr->second;
if (debug > 0) {
cerr << "at frame " << countIn
<< " (requested at " << freqMapItr->first
<< " [NOT] plus latency " << ts.getLatency()
<< ") updating frequency ratio to " << s << endl;
}
ts.setPitchScale(s);
++freqMapItr;
} else {
if (nextFreqFrame < countIn + thisBlockSize) {
thisBlockSize = nextFreqFrame - countIn;
}
break;
}
}
bool final = (frame + ibs >= sfinfo.frames);
int count = -1;
if ((count = sf_readf_float(sndfile, ibuf, thisBlockSize)) < 0) {
break;
}
countIn += count;
ts.study(ibuf, count, final);
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < count; ++i) {
cbuf[c][i] = ibuf[i * channels + c];
}
}
bool final = (frame + thisBlockSize >= sfinfo.frames);
if (debug > 2) {
cerr << "count = " << count << ", bs = " << thisBlockSize << ", frame = " << frame << ", frames = " << sfinfo.frames << ", final = " << final << endl;
}
ts.process(cbuf, count, final);
int avail;
while ((avail = ts.available()) > 0) {
if (debug > 1) {
cerr << "available = " << avail << endl;
}
thisBlockSize = avail;
if (thisBlockSize > bs) {
thisBlockSize = bs;
}
if (toDrop > 0) {
int dropHere = toDrop;
if (dropHere > thisBlockSize) {
dropHere = thisBlockSize;
}
if (debug > 1) {
cerr << "toDrop = " << toDrop << ", dropping "
<< dropHere << " of " << avail << endl;
}
ts.retrieve(cbuf, dropHere);
toDrop -= dropHere;
avail -= dropHere;
continue;
}
if (debug > 2) {
cerr << "retrieving block of " << thisBlockSize << endl;
}
ts.retrieve(cbuf, thisBlockSize);
if (realtime && final) {
// (in offline mode the stretcher handles this itself)
size_t ideal = size_t(countIn * ratio);
if (debug > 2) {
cerr << "at end, ideal = " << ideal
<< ", countOut = " << countOut
<< ", thisBlockSize = " << thisBlockSize << endl;
}
if (countOut + thisBlockSize > ideal) {
thisBlockSize = ideal - countOut;
if (debug > 1) {
cerr << "truncated final block to " << thisBlockSize
<< endl;
}
}
}
countOut += thisBlockSize;
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < thisBlockSize; ++i) {
float value = gain * cbuf[c][i];
if (ignoreClipping) { // i.e. just clamp, don't bail out
if (value > 1.f) value = 1.f;
if (value < -1.f) value = -1.f;
} else {
if (value >= 1.f || value < -1.f) {
clipping = true;
gain = (0.999f / fabsf(cbuf[c][i]));
}
}
ibuf[i * channels + c] = value;
}
}
sf_writef_float(sndfileOut, ibuf, thisBlockSize);
}
if (clipping) {
if (!quiet) {
cerr << "NOTE: Clipping detected at output sample "
<< countOut << ", restarting with "
<< "reduced gain of " << gain
<< " (supply --ignore-clipping to avoid this)" << endl;
}
const float mingain = 0.75f;
if (gain < mingain) {
cerr << "WARNING: Clipped values were implausibly high: "
<< "something wrong with input or process - "
<< "not reducing gain below " << mingain << endl;
gain = mingain;
ignoreClipping = true;
}
successful = false;
break;
}
if (frame == 0 && !realtime && !quiet) {
cerr << "Pass 2: Processing..." << endl;
}
int p = int((double(frame) * 100.0) / sfinfo.frames);
if (p > percent || frame == 0) {
@@ -498,141 +814,63 @@ int main(int argc, char **argv)
}
}
frame += ibs;
frame += count;
}
if (!quiet) {
cerr << "\rCalculating profile..." << endl;
if (!successful) {
sf_seek(sndfile, 0, SEEK_SET);
sf_seek(sndfileOut, 0, SEEK_SET);
continue;
}
sf_seek(sndfile, 0, SEEK_SET);
}
frame = 0;
percent = 0;
if (!mapping.empty()) {
ts.setKeyFrameMap(mapping);
}
size_t countIn = 0, countOut = 0;
while (frame < sfinfo.frames) {
int count = -1;
if ((count = sf_readf_float(sndfile, fbuf, ibs)) < 0) break;
countIn += count;
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < count; ++i) {
float value = fbuf[i * channels + c];
ibuf[c][i] = value;
}
if (!quiet) {
cerr << "\r " << endl;
}
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();
if (debug > 1) cerr << "available = " << avail << endl;
if (avail > 0) {
float **obf = new float *[channels];
for (size_t i = 0; i < channels; ++i) {
obf[i] = new float[avail];
int avail;
while ((avail = ts.available()) >= 0) {
if (debug > 1) {
cerr << "(completing) available = " << avail << endl;
}
ts.retrieve(obf, avail);
countOut += avail;
float *fobf = new float[channels * avail];
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < avail; ++i) {
float value = obf[c][i];
if (value > 1.f) value = 1.f;
if (value < -1.f) value = -1.f;
fobf[i * channels + c] = value;
if (avail == 0) {
if (realtime ||
(options & RubberBandStretcher::OptionThreadingNever)) {
break;
} else {
usleep(10000);
}
}
// cout << "fobf mean: ";
// double d = 0;
// for (int i = 0; i < avail; ++i) {
// d += fobf[i];
// }
// d /= avail;
// cout << d << endl;
sf_writef_float(sndfileOut, fobf, avail);
delete[] fobf;
for (size_t i = 0; i < channels; ++i) {
delete[] obf[i];
thisBlockSize = avail;
if (thisBlockSize > bs) {
thisBlockSize = bs;
}
delete[] obf;
}
ts.retrieve(cbuf, thisBlockSize);
if (frame == 0 && !realtime && !quiet) {
cerr << "Pass 2: Processing..." << endl;
}
int p = int((double(frame) * 100.0) / sfinfo.frames);
if (p > percent || frame == 0) {
percent = p;
if (!quiet) {
cerr << "\r" << percent << "% ";
}
}
frame += ibs;
}
if (!quiet) {
cerr << "\r " << endl;
}
int avail;
while ((avail = ts.available()) >= 0) {
if (debug > 1) {
cerr << "(completing) available = " << avail << endl;
}
if (avail > 0) {
float **obf = new float *[channels];
for (size_t i = 0; i < channels; ++i) {
obf[i] = new float[avail];
}
ts.retrieve(obf, avail);
countOut += avail;
float *fobf = new float[channels * avail];
countOut += thisBlockSize;
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < avail; ++i) {
float value = obf[c][i];
for (int i = 0; i < thisBlockSize; ++i) {
float value = gain * cbuf[c][i];
if (value > 1.f) value = 1.f;
if (value < -1.f) value = -1.f;
fobf[i * channels + c] = value;
ibuf[i * channels + c] = value;
}
}
sf_writef_float(sndfileOut, fobf, avail);
delete[] fobf;
for (size_t i = 0; i < channels; ++i) {
delete[] obf[i];
}
delete[] obf;
} else {
usleep(10000);
sf_writef_float(sndfileOut, ibuf, thisBlockSize);
}
}
delete[] fbuf;
for (size_t i = 0; i < channels; ++i) delete[] ibuf[i];
delete[] ibuf;
for (size_t c = 0; c < channels; ++c) {
delete[] cbuf[c];
}
delete[] cbuf;
sf_close(sndfile);
sf_close(sndfileOut);
@@ -646,7 +884,7 @@ int main(int argc, char **argv)
#ifdef _WIN32
RubberBand::
#endif
timeval etv;
timeval etv;
(void)gettimeofday(&etv, 0);
etv.tv_sec -= tv.tv_sec;
@@ -657,10 +895,7 @@ int main(int argc, char **argv)
etv.tv_usec -= tv.tv_usec;
double sec = double(etv.tv_sec) + (double(etv.tv_usec) / 1000000.0);
cerr << "elapsed time: " << sec
<< " sec, in frames/sec: " << int64_t(round(countIn/sec))
<< ", out frames/sec: " << int64_t(round(countOut/sec))
<< endl;
cerr << "elapsed time: " << sec << " sec, in frames/sec: " << countIn/sec << ", out frames/sec: " << countOut/sec << endl;
}
RubberBand::Profiler::dump();

View File

@@ -2,7 +2,7 @@
project(
'Rubber Band Library',
'c', 'cpp',
version: '1.9.2',
version: '2.0.0',
license: 'GPL-2.0-or-later',
default_options: [
# All Rubber Band code is actually C++98, but some compilers no
@@ -15,7 +15,7 @@ project(
meson_version: '>= 0.53.0'
)
rubberband_dynamic_library_version = '2.1.4'
rubberband_dynamic_library_version = '2.1.5'
system = host_machine.system()
architecture = host_machine.cpu_family()
@@ -140,7 +140,7 @@ if resampler == 'auto'
if samplerate_dep.found()
resampler = 'libsamplerate'
else
resampler = 'speex'
resampler = 'builtin'
endif
endif
@@ -199,7 +199,13 @@ else
endif # fft
if resampler == 'libsamplerate'
if resampler == 'builtin'
config_summary += { 'Resampler': 'Built-in' }
message('For resampler: using built-in implementation')
library_sources += 'src/dsp/BQResampler.cpp'
feature_defines += ['-DUSE_BQRESAMPLER']
elif resampler == 'libsamplerate'
if samplerate_dep.found()
config_summary += { 'Resampler': 'libsamplerate' }
message('For resampler: using libsamplerate')
@@ -441,7 +447,7 @@ if not get_option('no_shared')
message('Will build Rubber Band Library shared library')
rubberband_dynamic = shared_library(
rubberband_dynamic_name,
objects: rubberband_static.extract_all_objects(),
objects: rubberband_static.extract_all_objects(recursive: true),
link_args: [
arch_flags,
feature_libraries,

View File

@@ -7,9 +7,9 @@ option('fft',
option('resampler',
type: 'combo',
choices: ['auto', 'libsamplerate', 'speex', 'ipp'],
choices: ['auto', 'builtin', 'libsamplerate', 'speex', 'ipp'],
value: 'auto',
description: 'Resampler library to use. Recommended is libsamplerate. The default (auto) will use libsamplerate if available, speex otherwise.')
description: 'Resampler library to use. The default (auto) will use libsamplerate if available, the builtin implementation otherwise.')
option('ipp_path',
type: 'string',

View File

@@ -6,7 +6,7 @@ OPTFLAGS := -DNDEBUG -ffast-math -O3 -ftree-vectorize
ARCHFLAGS :=
CXXFLAGS := -std=c++98 $(ARCHFLAGS) $(OPTFLAGS) -I. -Isrc -Irubberband -DHAVE_LIBSAMPLERATE -DUSE_BUILTIN_FFT -DNO_THREAD_CHECKS -DUSE_PTHREADS -DNO_TIMING -DHAVE_POSIX_MEMALIGN -DNDEBUG
CXXFLAGS := -std=c++11 $(ARCHFLAGS) $(OPTFLAGS) -I. -Isrc -Irubberband -DHAVE_LIBSAMPLERATE -DUSE_BUILTIN_FFT -DNO_THREAD_CHECKS -DUSE_PTHREADS -DNO_TIMING -DHAVE_POSIX_MEMALIGN -DNDEBUG
CFLAGS := $(ARCHFLAGS) $(OPTFLAGS)

View File

@@ -1,13 +1,47 @@
#!/bin/bash
set -eu
if [ ! -d /Applications ]; then
# Assumed to be Linux
echo " *** Building static library using Linux-specific Makefile"
# make -f otherbuilds/Makefile.linux clean
make -f otherbuilds/Makefile.linux
echo " *** Linking against static library"
g++ main/main.cpp lib/librubberband.a -I. -Isrc -o test -lsndfile -lsamplerate -lpthread
echo " *** Running build from Linux-specific Makefile"
./test -V
echo " *** Building with single-file source"
g++ main/main.cpp single/RubberBandSingle.cpp -o test_single -lsndfile
echo " *** Running build from single-file source"
./test_single -V
echo " *** OK"
else
make -f otherbuilds/Makefile.macos
c++ main/main.cpp lib/librubberband.a -I. -Isrc -o test -lsndfile -lsamplerate -framework Accelerate
./test -V
echo " *** Building static library using macOS-specific Makefile"
make -f otherbuilds/Makefile.macos clean
make -f otherbuilds/Makefile.macos
echo " *** Linking against static library"
c++ main/main.cpp lib/librubberband.a -I. -Isrc -o test -lsndfile -lsamplerate -framework Accelerate
echo " *** Running build from macOS-specific Makefile"
./test -V
echo " *** Building with single-file source"
c++ main/main.cpp single/RubberBandSingle.cpp -o test_single -lsndfile -framework Accelerate
echo " *** Running build from single-file source"
./test_single -V
echo " *** Building static library using iOS-specific Makefile"
make -f otherbuilds/Makefile.ios clean
make -f otherbuilds/Makefile.ios
fi

View File

@@ -24,7 +24,7 @@
#ifndef RUBBERBAND_STRETCHER_H
#define RUBBERBAND_STRETCHER_H
#define RUBBERBAND_VERSION "1.9.2"
#define RUBBERBAND_VERSION "2.0.0"
#define RUBBERBAND_API_MAJOR_VERSION 2
#define RUBBERBAND_API_MINOR_VERSION 6

View File

@@ -28,7 +28,7 @@
extern "C" {
#endif
#define RUBBERBAND_VERSION "1.9.2"
#define RUBBERBAND_VERSION "2.0.0"
#define RUBBERBAND_API_MAJOR_VERSION 2
#define RUBBERBAND_API_MINOR_VERSION 6

View File

@@ -0,0 +1,80 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2021 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
/*
RubberBandSingle.cpp
This is a single-file compilation unit for Rubber Band Library.
To use the library in your project without building it separately,
include in your code either rubberband/RubberBandStretcher.h for
use in C++ or rubberband/rubberband-c.h if you need the C
interface, then add this single C++ source file to your build.
Don't move this file into your source tree - keep it in the same
place relative to the other Rubber Band code, so that the relative
include paths work, and just add it to your list of build files.
This produces a build using the built-in FFT and resampler
implementations, except on Apple platforms, where the vDSP FFT is
used (and where you will need the Accelerate framework when
linking). It should work correctly on any supported platform, but
may not be the fastest configuration available. For further
customisation, consider using the full build system to make a
standalone library.
*/
#define USE_BQRESAMPLER 1
#define NO_TIMING 1
#define NO_THREADING 1
#define NO_THREAD_CHECKS 1
#if defined(__APPLE__)
#define HAVE_VDSP 1
#else
#define USE_BUILTIN_FFT 1
#endif
#include "../src/audiocurves/CompoundAudioCurve.cpp"
#include "../src/audiocurves/SpectralDifferenceAudioCurve.cpp"
#include "../src/audiocurves/HighFrequencyAudioCurve.cpp"
#include "../src/audiocurves/SilentAudioCurve.cpp"
#include "../src/audiocurves/ConstantAudioCurve.cpp"
#include "../src/audiocurves/PercussiveAudioCurve.cpp"
#include "../src/base/Profiler.cpp"
#include "../src/dsp/AudioCurveCalculator.cpp"
#include "../src/dsp/FFT.cpp"
#include "../src/dsp/Resampler.cpp"
#include "../src/dsp/BQResampler.cpp"
#include "../src/system/Allocators.cpp"
#include "../src/system/sysutils.cpp"
#include "../src/system/Thread.cpp"
#include "../src/RubberBandStretcher.cpp"
#include "../src/StretchCalculator.cpp"
#include "../src/StretcherChannelData.cpp"
#include "../src/StretcherImpl.cpp"
#include "../src/StretcherProcess.cpp"
#include "../src/rubberband-c.cpp"

View File

@@ -41,12 +41,14 @@ StretchCalculator::StretchCalculator(size_t sampleRate,
m_sampleRate(sampleRate),
m_increment(inputIncrement),
m_prevDf(0),
m_divergence(0),
m_recovery(0),
m_prevRatio(1.0),
m_prevTimeRatio(1.0),
m_transientAmnesty(0),
m_debugLevel(0),
m_useHardPeaks(useHardPeaks)
m_useHardPeaks(useHardPeaks),
m_inFrameCounter(0),
m_frameCheckpoint(0, 0),
m_outFrameCounter(0)
{
// std::cerr << "StretchCalculator::StretchCalculator: useHardPeaks = " << useHardPeaks << std::endl;
}
@@ -318,18 +320,108 @@ StretchCalculator::mapPeaks(std::vector<Peak> &peaks,
}
}
int
StretchCalculator::calculateSingle(double ratio,
float df,
size_t increment)
int64_t
StretchCalculator::expectedOutFrame(int64_t inFrame, double timeRatio)
{
int64_t checkpointedAt = m_frameCheckpoint.first;
int64_t checkpointed = m_frameCheckpoint.second;
return int64_t(round(checkpointed + (inFrame - checkpointedAt) * timeRatio));
}
int
StretchCalculator::calculateSingle(double timeRatio,
double effectivePitchRatio,
float df,
size_t inIncrement,
size_t analysisWindowSize,
size_t synthesisWindowSize)
{
double ratio = timeRatio / effectivePitchRatio;
int increment = int(inIncrement);
if (increment == 0) increment = m_increment;
int outIncrement = lrint(increment * ratio); // the normal case
bool isTransient = false;
// We want to ensure, as close as possible, that the phase reset
// points appear at _exactly_ the right audio frame numbers.
// points appear at the right audio frame numbers. To this end we
// track the incoming frame number, its corresponding expected
// output frame number, and the actual output frame number
// projected based on the ratios provided.
//
// There are two subtleties:
//
// (1) on a ratio change, we need to checkpoint the expected
// output frame number reached so far and start counting again
// with the new ratio. We could do this with a reset to zero, but
// it's easier to reason about absolute input/output frame
// matches, so for the moment at least we're doing this by
// explicitly checkpointing the current numbers (hence the use of
// the above expectedOutFrame() function which refers to the
// last checkpointed values).
//
// (2) in the case of a pitch shift in a configuration where
// resampling occurs after stretching, all of our output
// increments will be effectively modified by resampling after we
// return. This is why we separate out timeRatio and
// effectivePitchRatio arguments - the former is the ratio that
// has already been applied and the latter is the ratio that will
// be applied by any subsequent resampling step (which will be 1.0
// / pitchScale if resampling is happening after stretching). So
// the overall ratio is timeRatio / effectivePitchRatio.
bool ratioChanged = (ratio != m_prevRatio);
if (ratioChanged) {
// Reset our frame counters from the ratio change.
// m_outFrameCounter tracks the frames counted at output from
// this function, which normally precedes resampling - hence
// the use of timeRatio rather than ratio here
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator: ratio changed from " << m_prevRatio << " to " << ratio << std::endl;
}
int64_t toCheckpoint = expectedOutFrame
(m_inFrameCounter, m_prevTimeRatio);
m_frameCheckpoint =
std::pair<int64_t, int64_t>(m_inFrameCounter, toCheckpoint);
}
m_prevRatio = ratio;
m_prevTimeRatio = timeRatio;
if (m_debugLevel > 2) {
std::cerr << "StretchCalculator::calculateSingle: timeRatio = "
<< timeRatio << ", effectivePitchRatio = "
<< effectivePitchRatio << " (that's 1.0 / "
<< (1.0 / effectivePitchRatio)
<< "), ratio = " << ratio << ", df = " << df
<< ", inIncrement = " << inIncrement
<< ", default outIncrement = " << outIncrement
<< ", analysisWindowSize = " << analysisWindowSize
<< ", synthesisWindowSize = " << synthesisWindowSize
<< std::endl;
std::cerr << "inFrameCounter = " << m_inFrameCounter
<< ", outFrameCounter = " << m_outFrameCounter
<< std::endl;
std::cerr << "The next sample out is input sample " << m_inFrameCounter << std::endl;
}
int64_t intended = expectedOutFrame
(m_inFrameCounter + analysisWindowSize/4, timeRatio);
int64_t projected = int64_t
(round(m_outFrameCounter + (synthesisWindowSize/4 * effectivePitchRatio)));
int64_t divergence = projected - intended;
if (m_debugLevel > 2) {
std::cerr << "for current frame + quarter frame: intended " << intended << ", projected " << projected << ", divergence " << divergence << std::endl;
}
// In principle, the threshold depends on chunk size: larger chunk
// sizes need higher thresholds. Since chunk size depends on
// ratio, I suppose we could in theory calculate the threshold
@@ -340,7 +432,13 @@ StretchCalculator::calculateSingle(double ratio,
// if (ratio > 1) transientThreshold = 0.25f;
if (m_useHardPeaks && df > m_prevDf * 1.1f && df > transientThreshold) {
isTransient = true;
if (divergence > 1000 || divergence < -1000) {
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: transient, but we're not permitting it because the divergence (" << divergence << ") is too great" << std::endl;
}
} else {
isTransient = true;
}
}
if (m_debugLevel > 2) {
@@ -350,62 +448,91 @@ StretchCalculator::calculateSingle(double ratio,
m_prevDf = df;
bool ratioChanged = (ratio != m_prevRatio);
m_prevRatio = ratio;
if (isTransient && m_transientAmnesty == 0) {
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: transient (df " << df << ", threshold " << transientThreshold << ")" << std::endl;
if (m_transientAmnesty > 0) {
if (isTransient) {
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: transient, but we have an amnesty (df " << df << ", threshold " << transientThreshold << ")" << std::endl;
}
isTransient = false;
}
--m_transientAmnesty;
}
if (isTransient) {
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: transient at (df " << df << ", threshold " << transientThreshold << ")" << std::endl;
}
m_divergence += increment - (increment * ratio);
// as in offline mode, 0.05 sec approx min between transients
m_transientAmnesty =
lrint(ceil(double(m_sampleRate) / (20 * double(increment))));
m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment);
return -int(increment);
outIncrement = increment;
} else {
double recovery = 0.0;
if (divergence > 1000 || divergence < -1000) {
recovery = divergence / ((m_sampleRate / 10.0) / increment);
} else if (divergence > 100 || divergence < -100) {
recovery = divergence / ((m_sampleRate / 20.0) / increment);
} else {
recovery = divergence / 4.0;
}
int incr = lrint(outIncrement - recovery);
if (m_debugLevel > 2 || (m_debugLevel > 1 && divergence != 0)) {
std::cerr << "divergence = " << divergence << ", recovery = " << recovery << ", incr = " << incr << ", ";
}
int minIncr = lrint(increment * ratio * 0.3);
int maxIncr = lrint(increment * ratio * 2);
if (incr < minIncr) {
incr = minIncr;
} else if (incr > maxIncr) {
incr = maxIncr;
}
if (m_debugLevel > 2 || (m_debugLevel > 1 && divergence != 0)) {
std::cerr << "clamped into [" << minIncr << ", " << maxIncr
<< "] becomes " << incr << std::endl;
}
if (incr < 0) {
std::cerr << "WARNING: internal error: incr < 0 in calculateSingle"
<< std::endl;
outIncrement = 0;
} else {
outIncrement = incr;
}
}
if (ratioChanged) {
m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment);
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: returning isTransient = "
<< isTransient << ", outIncrement = " << outIncrement
<< std::endl;
}
if (m_transientAmnesty > 0) --m_transientAmnesty;
int incr = lrint(increment * ratio - m_recovery);
if (m_debugLevel > 2 || (m_debugLevel > 1 && m_divergence != 0)) {
std::cerr << "divergence = " << m_divergence << ", recovery = " << m_recovery << ", incr = " << incr << ", ";
m_inFrameCounter += inIncrement;
m_outFrameCounter += outIncrement * effectivePitchRatio;
if (isTransient) {
return -outIncrement;
} else {
return outIncrement;
}
if (incr < lrint((increment * ratio) / 2)) {
incr = lrint((increment * ratio) / 2);
} else if (incr > lrint(increment * ratio * 2)) {
incr = lrint(increment * ratio * 2);
}
double divdiff = (increment * ratio) - incr;
if (m_debugLevel > 2 || (m_debugLevel > 1 && m_divergence != 0)) {
std::cerr << "divdiff = " << divdiff << std::endl;
}
double prevDivergence = m_divergence;
m_divergence -= divdiff;
if ((prevDivergence < 0 && m_divergence > 0) ||
(prevDivergence > 0 && m_divergence < 0)) {
m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment);
}
return incr;
}
void
StretchCalculator::reset()
{
m_prevDf = 0;
m_divergence = 0;
m_recovery = 0;
m_prevRatio = 1.0;
m_prevTimeRatio = 1.0;
m_inFrameCounter = 0;
m_frameCheckpoint = std::pair<int64_t, int64_t>(0, 0);
m_outFrameCounter = 0.0;
m_transientAmnesty = 0;
m_keyFrameMap.clear();
}

View File

@@ -68,8 +68,12 @@ public:
* If increment is non-zero, use it for the input increment for
* this block in preference to m_increment.
*/
int calculateSingle(double ratio, float curveValue,
size_t increment = 0);
int calculateSingle(double timeRatio,
double effectivePitchRatio,
float curveValue,
size_t increment,
size_t analysisWindowSize,
size_t synthesisWindowSize);
void setUseHardPeaks(bool use) { m_useHardPeaks = use; }
@@ -104,12 +108,15 @@ protected:
size_t m_sampleRate;
size_t m_increment;
float m_prevDf;
double m_divergence;
float m_recovery;
float m_prevRatio;
double m_prevRatio;
double m_prevTimeRatio;
int m_transientAmnesty; // only in RT mode; handled differently offline
int m_debugLevel;
bool m_useHardPeaks;
int64_t m_inFrameCounter;
std::pair<int64_t, int64_t> m_frameCheckpoint;
int64_t expectedOutFrame(int64_t inFrame, double timeRatio);
double m_outFrameCounter;
std::map<size_t, size_t> m_keyFrameMap;
std::vector<Peak> m_peaks;

View File

@@ -27,8 +27,7 @@
#include "StretcherImpl.h"
#include <set>
//#define EXPERIMENT 1
#include <atomic>
namespace RubberBand
{
@@ -124,11 +123,11 @@ public:
size_t chunkCount;
size_t inCount;
long inputSize; // set only after known (when data ended); -1 previously
std::atomic<int64_t> inputSize; // set only after known (when data ended); -1 previously
size_t outCount;
bool draining;
bool outputComplete;
std::atomic<bool> draining;
std::atomic<bool> outputComplete;
FFT *fft;
std::map<size_t, FFT *> ffts;

View File

@@ -544,8 +544,8 @@ RubberBandStretcher::Impl::calculateSizes()
// ratio) for any chunk.
if (m_debugLevel > 0) {
cerr << "configure: time ratio = " << m_timeRatio << ", pitch scale = " << m_pitchScale << ", effective ratio = " << getEffectiveRatio() << endl;
cerr << "configure: analysis window size = " << m_aWindowSize << ", synthesis window size = " << m_sWindowSize << ", fft size = " << m_fftSize << ", increment = " << m_increment << " (approx output increment = " << int(lrint(m_increment * getEffectiveRatio())) << ")" << endl;
cerr << "calculateSizes: time ratio = " << m_timeRatio << ", pitch scale = " << m_pitchScale << ", effective ratio = " << getEffectiveRatio() << endl;
cerr << "calculateSizes: analysis window size = " << m_aWindowSize << ", synthesis window size = " << m_sWindowSize << ", fft size = " << m_fftSize << ", increment = " << m_increment << " (approx output increment = " << int(lrint(m_increment * getEffectiveRatio())) << ")" << endl;
}
if (std::max(m_aWindowSize, m_sWindowSize) > m_maxProcessSize) {
@@ -575,15 +575,17 @@ RubberBandStretcher::Impl::calculateSizes()
}
if (m_debugLevel > 0) {
cerr << "configure: outbuf size = " << m_outbufSize << endl;
cerr << "calculateSizes: outbuf size = " << m_outbufSize << endl;
}
}
void
RubberBandStretcher::Impl::configure()
{
// std::cerr << "configure[" << this << "]: realtime = " << m_realtime << ", pitch scale = "
// << m_pitchScale << ", channels = " << m_channels << std::endl;
if (m_debugLevel > 0) {
std::cerr << "configure[" << this << "]: realtime = " << m_realtime << ", pitch scale = "
<< m_pitchScale << ", channels = " << m_channels << std::endl;
}
size_t prevFftSize = m_fftSize;
size_t prevAWindowSize = m_aWindowSize;
@@ -674,8 +676,18 @@ RubberBandStretcher::Impl::configure()
Resampler::Parameters params;
params.quality = Resampler::FastestTolerable;
if (m_realtime) {
params.dynamism = Resampler::RatioOftenChanging;
params.ratioChange = Resampler::SmoothRatioChange;
} else {
// ratio can't be changed in offline mode
params.dynamism = Resampler::RatioMostlyFixed;
params.ratioChange = Resampler::SuddenRatioChange;
}
params.maxBufferSize = 4096 * 16;
params.debugLevel = m_debugLevel;
params.debugLevel = (m_debugLevel > 0 ? m_debugLevel-1 : 0);
m_channelData[c]->resampler = new Resampler(params, 1);
@@ -734,7 +746,7 @@ RubberBandStretcher::Impl::configure()
if (!m_realtime) {
if (m_debugLevel > 1) {
cerr << "Not real time mode: prefilling" << endl;
cerr << "Not real time mode: prefilling with " << m_aWindowSize/2 << " samples" << endl;
}
for (size_t c = 0; c < m_channels; ++c) {
m_channelData[c]->reset();
@@ -767,6 +779,8 @@ RubberBandStretcher::Impl::reconfigure()
calculateSizes();
bool somethingChanged = false;
// There are various allocations in this function, but they should
// never happen in normal use -- they just recover from the case
// where not all of the things we need were correctly created when
@@ -801,12 +815,15 @@ RubberBandStretcher::Impl::reconfigure()
m_channelData[c]->setSizes(std::max(m_aWindowSize, m_sWindowSize),
m_fftSize);
}
somethingChanged = true;
}
if (m_outbufSize != prevOutbufSize) {
for (size_t c = 0; c < m_channels; ++c) {
m_channelData[c]->setOutbufSize(m_outbufSize);
}
somethingChanged = true;
}
if (m_pitchScale != 1.0) {
@@ -818,8 +835,10 @@ RubberBandStretcher::Impl::reconfigure()
Resampler::Parameters params;
params.quality = Resampler::FastestTolerable;
params.dynamism = Resampler::RatioOftenChanging;
params.ratioChange = Resampler::SmoothRatioChange;
params.maxBufferSize = m_sWindowSize;
params.debugLevel = m_debugLevel;
params.debugLevel = (m_debugLevel > 0 ? m_debugLevel-1 : 0);
m_channelData[c]->resampler = new Resampler(params, 1);
@@ -827,6 +846,8 @@ RubberBandStretcher::Impl::reconfigure()
lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale));
if (rbs < m_increment * 16) rbs = m_increment * 16;
m_channelData[c]->setResampleBufSize(rbs);
somethingChanged = true;
}
}
@@ -836,6 +857,15 @@ RubberBandStretcher::Impl::reconfigure()
if (m_stretchAudioCurve) {
m_stretchAudioCurve->setFftSize(m_fftSize);
}
somethingChanged = true;
}
if (m_debugLevel > 0) {
if (somethingChanged) {
std::cerr << "reconfigure: at least one parameter changed" << std::endl;
} else {
std::cerr << "reconfigure: nothing changed" << std::endl;
}
}
}
@@ -843,7 +873,7 @@ size_t
RubberBandStretcher::Impl::getLatency() const
{
if (!m_realtime) return 0;
return int((m_aWindowSize/2) / m_pitchScale + 1);
return lrint((m_aWindowSize/2) / m_pitchScale);
}
void
@@ -1347,12 +1377,12 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo
}
#endif
if (m_debugLevel > 2) {
if (m_debugLevel > 1) {
if (!allConsumed) cerr << "process looping" << endl;
}
}
if (m_debugLevel > 2) {
if (m_debugLevel > 1) {
cerr << "process returning" << endl;
}

View File

@@ -24,7 +24,7 @@
#ifndef RUBBERBAND_STRETCHERIMPL_H
#define RUBBERBAND_STRETCHERIMPL_H
#include "rubberband/RubberBandStretcher.h"
#include "../rubberband/RubberBandStretcher.h"
#include "dsp/Window.h"
#include "dsp/SincWindow.h"

View File

@@ -189,6 +189,8 @@ RubberBandStretcher::Impl::consumeChannel(size_t c,
if (resampling) {
Profiler profiler2("RubberBandStretcher::Impl::resample");
toWrite = int(ceil(samples / m_pitchScale));
if (writable < toWrite) {
samples = int(floor(writable * m_pitchScale));
@@ -282,7 +284,7 @@ RubberBandStretcher::Impl::processChunks(size_t c, bool &any, bool &last)
while (!last) {
if (!testInbufReadSpace(c)) {
if (m_debugLevel > 2) {
if (m_debugLevel > 1) {
cerr << "processChunks: out of input" << endl;
}
break;
@@ -347,7 +349,7 @@ RubberBandStretcher::Impl::processOneChunk()
for (size_t c = 0; c < m_channels; ++c) {
if (!testInbufReadSpace(c)) {
if (m_debugLevel > 2) {
if (m_debugLevel > 1) {
cerr << "processOneChunk: out of input" << endl;
}
return false;
@@ -402,7 +404,7 @@ RubberBandStretcher::Impl::testInbufReadSpace(size_t c)
if (!m_threaded) {
#endif
if (m_debugLevel > 1) {
cerr << "WARNING: RubberBandStretcher: read space < chunk size ("
cerr << "Note: RubberBandStretcher: read space < chunk size ("
<< inbuf.getReadSpace() << " < " << m_aWindowSize
<< ") when not all input written, on processChunks for channel " << c << endl;
}
@@ -616,8 +618,14 @@ RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn,
}
}
double effectivePitchRatio = 1.0 / m_pitchScale;
if (cd.resampler) {
effectivePitchRatio = cd.resampler->getEffectiveRatio(effectivePitchRatio);
}
int incr = m_stretchCalculator->calculateSingle
(getEffectiveRatio(), df, m_increment);
(m_timeRatio, effectivePitchRatio, df, m_increment,
m_aWindowSize, m_sWindowSize);
if (m_lastProcessPhaseResetDf.getWriteSpace() > 0) {
m_lastProcessPhaseResetDf.write(&df, 1);
@@ -1071,6 +1079,8 @@ RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, boo
(m_pitchScale != 1.0 || m_options & OptionPitchHighConsistency) &&
cd.resampler) {
Profiler profiler2("RubberBandStretcher::Impl::resample");
size_t reqSize = int(ceil(si / m_pitchScale));
if (reqSize > cd.resamplebufSize) {
// This shouldn't normally happen -- the buffer is

View File

@@ -23,7 +23,7 @@
#include "CompoundAudioCurve.h"
#include "dsp/MovingMedian.h"
#include "../dsp/MovingMedian.h"
#include <iostream>

View File

@@ -24,10 +24,9 @@
#ifndef RUBBERBAND_COMPOUND_AUDIO_CURVE_H
#define RUBBERBAND_COMPOUND_AUDIO_CURVE_H
#include "dsp/AudioCurveCalculator.h"
#include "PercussiveAudioCurve.h"
#include "HighFrequencyAudioCurve.h"
#include "dsp/SampleFilter.h"
#include "../dsp/SampleFilter.h"
namespace RubberBand
{

View File

@@ -24,7 +24,7 @@
#ifndef RUBBERBAND_CONSTANT_AUDIO_CURVE_H
#define RUBBERBAND_CONSTANT_AUDIO_CURVE_H
#include "dsp/AudioCurveCalculator.h"
#include "../dsp/AudioCurveCalculator.h"
namespace RubberBand
{

View File

@@ -24,7 +24,7 @@
#ifndef RUBBERBAND_HIGHFREQUENCY_AUDIO_CURVE_H
#define RUBBERBAND_HIGHFREQUENCY_AUDIO_CURVE_H
#include "dsp/AudioCurveCalculator.h"
#include "../dsp/AudioCurveCalculator.h"
namespace RubberBand
{

View File

@@ -23,8 +23,8 @@
#include "PercussiveAudioCurve.h"
#include "system/Allocators.h"
#include "system/VectorOps.h"
#include "../system/Allocators.h"
#include "../system/VectorOps.h"
#include <cmath>
#include <iostream>

View File

@@ -24,7 +24,7 @@
#ifndef RUBBERBAND_PERCUSSIVE_AUDIO_CURVE_H
#define RUBBERBAND_PERCUSSIVE_AUDIO_CURVE_H
#include "dsp/AudioCurveCalculator.h"
#include "../dsp/AudioCurveCalculator.h"
namespace RubberBand
{

View File

@@ -24,7 +24,7 @@
#ifndef RUBBERBAND_SILENT_AUDIO_CURVE_H
#define RUBBERBAND_SILENT_AUDIO_CURVE_H
#include "dsp/AudioCurveCalculator.h"
#include "../dsp/AudioCurveCalculator.h"
namespace RubberBand
{

View File

@@ -23,8 +23,8 @@
#include "SpectralDifferenceAudioCurve.h"
#include "system/Allocators.h"
#include "system/VectorOps.h"
#include "../system/Allocators.h"
#include "../system/VectorOps.h"
namespace RubberBand
{

View File

@@ -24,8 +24,8 @@
#ifndef RUBBERBAND_SPECTRALDIFFERENCE_AUDIO_CURVE_H
#define RUBBERBAND_SPECTRALDIFFERENCE_AUDIO_CURVE_H
#include "dsp/AudioCurveCalculator.h"
#include "dsp/Window.h"
#include "../dsp/AudioCurveCalculator.h"
#include "../dsp/Window.h"
namespace RubberBand
{

View File

@@ -23,7 +23,7 @@
#include "Profiler.h"
#include "system/Thread.h"
#include "../system/Thread.h"
#include <algorithm>
#include <set>

View File

@@ -42,7 +42,7 @@
#ifdef PROFILE_CLOCKS
#include <time.h>
#else
#include "system/sysutils.h"
#include "../system/sysutils.h"
#ifndef _WIN32
#include <sys/time.h>
#endif

View File

@@ -28,11 +28,13 @@
//#define DEBUG_RINGBUFFER 1
#include "system/sysutils.h"
#include "system/Allocators.h"
#include "../system/sysutils.h"
#include "../system/Allocators.h"
#include <iostream>
#include <atomic>
namespace RubberBand {
/**
@@ -174,11 +176,11 @@ public:
int zero(int n);
protected:
T *const R__ m_buffer;
int m_writer;
int m_reader;
const int m_size;
bool m_mlocked;
T *const R__ m_buffer;
std::atomic<int> m_writer;
std::atomic<int> m_reader;
const int m_size;
bool m_mlocked;
int readSpaceFor(int w, int r) const {
int space;
@@ -243,7 +245,8 @@ RingBuffer<T> *
RingBuffer<T>::resized(int newSize) const
{
RingBuffer<T> *newBuffer = new RingBuffer<T>(newSize);
MBARRIER();
int w = m_writer;
int r = m_reader;
@@ -273,7 +276,8 @@ RingBuffer<T>::reset()
std::cerr << "RingBuffer<T>[" << this << "]::reset" << std::endl;
#endif
m_reader = m_writer;
int r = m_reader;
m_writer = r;
}
template <typename T>
@@ -302,7 +306,6 @@ RingBuffer<T>::read(S *const R__ destination, int n)
if (n > available) {
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;
@@ -320,7 +323,6 @@ RingBuffer<T>::read(S *const R__ destination, int n)
r += n;
while (r >= m_size) r -= m_size;
MBARRIER();
m_reader = r;
return n;
@@ -355,7 +357,6 @@ RingBuffer<T>::readAdding(S *const R__ destination, int n)
r += n;
while (r >= m_size) r -= m_size;
MBARRIER();
m_reader = r;
return n;
@@ -377,7 +378,6 @@ RingBuffer<T>::readOne()
T value = m_buffer[r];
if (++r == m_size) r = 0;
MBARRIER();
m_reader = r;
return value;
@@ -447,7 +447,6 @@ RingBuffer<T>::skip(int n)
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;

View File

@@ -33,9 +33,9 @@
#include <sys/time.h>
#endif
#include "system/Thread.h"
#include "system/sysutils.h"
#include "system/Allocators.h"
#include "../system/Thread.h"
#include "../system/sysutils.h"
#include "../system/Allocators.h"
//#define DEBUG_SCAVENGER 1

View File

@@ -26,8 +26,7 @@
#include <sys/types.h>
#include "system/sysutils.h"
#include "../system/sysutils.h"
namespace RubberBand
{

646
src/dsp/BQResampler.cpp Normal file
View File

@@ -0,0 +1,646 @@
//* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2021 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#include "BQResampler.h"
#include <cmath>
#include <iostream>
#include "../system/Allocators.h"
#include "../system/VectorOps.h"
#define BQ_R__ R__
using std::vector;
using std::cerr;
using std::endl;
using std::min;
using std::max;
namespace RubberBand {
BQResampler::BQResampler(Parameters parameters, int channels) :
m_qparams(parameters.quality),
m_dynamism(parameters.dynamism),
m_ratio_change(parameters.ratioChange),
m_debug_level(parameters.debugLevel),
m_initial_rate(parameters.referenceSampleRate),
m_channels(channels),
m_fade_count(0),
m_initialised(false)
{
if (m_debug_level > 0) {
cerr << "BQResampler::BQResampler: "
<< (m_dynamism == RatioOftenChanging ? "often-changing" : "mostly-fixed")
<< ", "
<< (m_ratio_change == SmoothRatioChange ? "smooth" : "sudden")
<< " ratio changes, ref " << m_initial_rate << " Hz" << endl;
}
if (m_dynamism == RatioOftenChanging) {
m_proto_length = m_qparams.proto_p * m_qparams.p_multiple + 1;
if (m_debug_level > 0) {
cerr << "BQResampler: creating prototype filter of length "
<< m_proto_length << endl;
}
m_prototype = make_filter(m_proto_length, m_qparams.proto_p);
m_prototype.push_back(0.0); // interpolate without fear
}
int phase_reserve = 2 * int(round(m_initial_rate));
int buffer_reserve = 1000 * m_channels;
m_state_a.phase_info.reserve(phase_reserve);
m_state_a.buffer.reserve(buffer_reserve);
if (m_dynamism == RatioOftenChanging) {
m_state_b.phase_info.reserve(phase_reserve);
m_state_b.buffer.reserve(buffer_reserve);
}
m_s = &m_state_a;
m_fade = &m_state_b;
}
BQResampler::BQResampler(const BQResampler &other) :
m_qparams(other.m_qparams),
m_dynamism(other.m_dynamism),
m_ratio_change(other.m_ratio_change),
m_debug_level(other.m_debug_level),
m_initial_rate(other.m_initial_rate),
m_channels(other.m_channels),
m_state_a(other.m_state_a),
m_state_b(other.m_state_b),
m_fade_count(other.m_fade_count),
m_prototype(other.m_prototype),
m_proto_length(other.m_proto_length),
m_initialised(other.m_initialised)
{
if (other.m_s == &(other.m_state_a)) {
m_s = &m_state_a;
m_fade = &m_state_b;
} else {
m_s = &m_state_b;
m_fade = &m_state_a;
}
}
void
BQResampler::reset()
{
m_initialised = false;
m_fade_count = 0;
}
BQResampler::QualityParams::QualityParams(Quality q)
{
switch (q) {
case Fastest:
p_multiple = 12;
proto_p = 160;
k_snr = 70.0;
k_transition = 0.2;
cut = 0.9;
break;
case FastestTolerable:
p_multiple = 62;
proto_p = 160;
k_snr = 90.0;
k_transition = 0.05;
cut = 0.975;
break;
case Best:
p_multiple = 122;
proto_p = 800;
k_snr = 100.0;
k_transition = 0.01;
cut = 0.995;
break;
}
}
int
BQResampler::resampleInterleaved(float *const out,
int outspace,
const float *const in,
int incount,
double ratio,
bool final) {
int fade_length = round(m_initial_rate / 1000.0);
if (fade_length < 6) {
fade_length = 6;
}
int max_fade = min(outspace, int(floor(incount * ratio))) / 2;
if (fade_length > max_fade) {
fade_length = max_fade;
}
if (!m_initialised) {
state_for_ratio(*m_s, ratio, *m_fade);
m_initialised = true;
} else if (ratio != m_s->parameters.ratio) {
state *tmp = m_fade;
m_fade = m_s;
m_s = tmp;
state_for_ratio(*m_s, ratio, *m_fade);
if (m_ratio_change == SmoothRatioChange) {
if (m_debug_level > 0) {
cerr << "BQResampler: ratio changed, beginning fade of length "
<< fade_length << endl;
}
m_fade_count = fade_length;
}
}
int i = 0, o = 0;
int bufsize = m_s->buffer.size();
int incount_samples = incount * m_channels;
int outspace_samples = outspace * m_channels;
while (o < outspace_samples) {
while (i < incount_samples && m_s->fill < bufsize) {
m_s->buffer[m_s->fill++] = in[i++];
}
if (m_s->fill == bufsize) {
out[o++] = reconstruct_one(m_s);
} else if (final && m_s->fill > m_s->centre) {
out[o++] = reconstruct_one(m_s);
} else if (final && m_s->fill == m_s->centre &&
m_s->current_phase != m_s->initial_phase) {
out[o++] = reconstruct_one(m_s);
} else {
break;
}
}
int fbufsize = m_fade->buffer.size();
int fi = 0, fo = 0;
while (fo < o && m_fade_count > 0) {
while (fi < incount_samples && m_fade->fill < fbufsize) {
m_fade->buffer[m_fade->fill++] = in[fi++];
}
if (m_fade->fill == fbufsize) {
double r = reconstruct_one(m_fade);
double fadeWith = out[fo];
double extent = double(m_fade_count - 1) / double(fade_length);
double mixture = 0.5 * (1.0 - cos(M_PI * extent));
double mixed = r * mixture + fadeWith * (1.0 - mixture);
out[fo] = mixed;
++fo;
if (m_fade->current_channel == 0) {
--m_fade_count;
}
} else {
break;
}
}
return o / m_channels;
}
double
BQResampler::getEffectiveRatio(double ratio) const {
if (m_initialised && ratio == m_s->parameters.ratio) {
return m_s->parameters.effective;
} else {
return pick_params(ratio).effective;
}
}
int
BQResampler::gcd(int a, int b) const
{
int c = a % b;
if (c == 0) return b;
else return gcd(b, c);
}
double
BQResampler::bessel0(double x) const
{
static double facsquared[] = {
0.0, 1.0, 4.0, 36.0,
576.0, 14400.0, 518400.0, 25401600.0,
1625702400.0, 131681894400.0, 1.316818944E13, 1.59335092224E15,
2.29442532803E17, 3.87757880436E19, 7.60005445655E21,
1.71001225272E24, 4.37763136697E26, 1.26513546506E29,
4.09903890678E31, 1.47975304535E34
};
static int nterms = sizeof(facsquared) / sizeof(facsquared[0]);
double b = 1.0;
for (int n = 1; n < nterms; ++n) {
double ff = facsquared[n];
double term = pow(x / 2.0, n * 2.0) / ff;
b += term;
}
return b;
}
vector<double>
BQResampler::kaiser(double beta, int len) const
{
double denominator = bessel0(beta);
int half = (len % 2 == 0 ? len/2 : (len+1)/2);
vector<double> v(len, 0.0);
for (int n = 0; n < half; ++n) {
double k = (2.0 * n) / (len-1) - 1.0;
v[n] = bessel0 (beta * sqrt(1.0 - k*k)) / denominator;
}
for (int n = half; n < len; ++n) {
v[n] = v[len-1 - n];
}
return v;
}
void
BQResampler::kaiser_params(double attenuation,
double transition,
double &beta,
int &len) const
{
if (attenuation > 21.0) {
len = 1 + ceil((attenuation - 7.95) / (2.285 * transition));
} else {
len = 1 + ceil(5.79 / transition);
}
beta = 0.0;
if (attenuation > 50.0) {
beta = 0.1102 * (attenuation - 8.7);
} else if (attenuation > 21.0) {
beta = 0.5842 * (pow (attenuation - 21.0, 0.4)) +
0.07886 * (attenuation - 21.0);
}
}
vector<double>
BQResampler::kaiser_for(double attenuation,
double transition,
int minlen,
int maxlen) const
{
double beta;
int m;
kaiser_params(attenuation, transition, beta, m);
int mb = m;
if (maxlen > 0 && mb > maxlen - 1) {
mb = maxlen - 1;
} else if (minlen > 0 && mb < minlen) {
mb = minlen;
}
if (mb % 2 == 0) ++mb;
if (m_debug_level > 0) {
cerr << "BQResampler: window attenuation " << attenuation
<< ", transition " << transition
<< " -> length " << m << " adjusted to " << mb
<< ", beta " << beta << endl;
}
return kaiser(beta, mb);
}
void
BQResampler::sinc_multiply(double peak_to_zero, vector<double> &buf) const
{
int len = int(buf.size());
if (len < 2) return;
int left = len / 2;
int right = (len + 1) / 2;
double m = M_PI / peak_to_zero;
for (int i = 1; i <= right; ++i) {
double x = i * m;
double sinc = sin(x) / x;
if (i <= left) {
buf[left - i] *= sinc;
}
if (i < right) {
buf[i + left] *= sinc;
}
}
}
BQResampler::params
BQResampler::fill_params(double ratio, int num, int denom) const
{
params p;
int g = gcd (num, denom);
p.ratio = ratio;
p.numerator = num / g;
p.denominator = denom / g;
p.effective = double(p.numerator) / double(p.denominator);
p.peak_to_zero = max(p.denominator, p.numerator);
p.peak_to_zero /= m_qparams.cut;
p.scale = double(p.numerator) / double(p.peak_to_zero);
if (m_debug_level > 0) {
cerr << "BQResampler: ratio " << p.ratio
<< " -> fraction " << p.numerator << "/" << p.denominator
<< " with error " << p.effective - p.ratio
<< endl;
cerr << "BQResampler: peak-to-zero " << p.peak_to_zero
<< ", scale " << p.scale
<< endl;
}
return p;
}
BQResampler::params
BQResampler::pick_params(double ratio) const
{
// Farey algorithm, see
// https://www.johndcook.com/blog/2010/10/20/best-rational-approximation/
int max_denom = 192000;
double a = 0.0, b = 1.0, c = 1.0, d = 0.0;
double pa = a, pb = b, pc = c, pd = d;
double eps = 1e-9;
while (b <= max_denom && d <= max_denom) {
double mediant = (a + c) / (b + d);
if (fabs(ratio - mediant) < eps) {
if (b + d <= max_denom) {
return fill_params(ratio, a + c, b + d);
} else if (d > b) {
return fill_params(ratio, c, d);
} else {
return fill_params(ratio, a, b);
}
}
if (ratio > mediant) {
pa = a; pb = b;
a += c; b += d;
} else {
pc = c; pd = d;
c += a; d += b;
}
}
if (fabs(ratio - (pc / pd)) < fabs(ratio - (pa / pb))) {
return fill_params(ratio, pc, pd);
} else {
return fill_params(ratio, pa, pb);
}
}
void
BQResampler::phase_data_for(vector<BQResampler::phase_rec> &target_phase_data,
floatbuf &target_phase_sorted_filter,
int filter_length,
const vector<double> *filter,
int initial_phase,
int input_spacing,
int output_spacing) const
{
target_phase_data.clear();
target_phase_data.reserve(input_spacing);
for (int p = 0; p < input_spacing; ++p) {
int next_phase = p - output_spacing;
while (next_phase < 0) next_phase += input_spacing;
next_phase %= input_spacing;
double dspace = double(input_spacing);
int zip_length = ceil(double(filter_length - p) / dspace);
int drop = ceil(double(max(0, output_spacing - p)) / dspace);
phase_rec phase;
phase.next_phase = next_phase;
phase.drop = drop;
phase.length = zip_length;
phase.start_index = 0; // we fill this in below if needed
target_phase_data.push_back(phase);
}
if (m_dynamism == RatioMostlyFixed) {
if (!filter) throw std::logic_error("filter required at phase_data_for in RatioMostlyFixed mode");
target_phase_sorted_filter.clear();
target_phase_sorted_filter.reserve(filter_length);
for (int p = initial_phase; ; ) {
phase_rec &phase = target_phase_data[p];
phase.start_index = target_phase_sorted_filter.size();
for (int i = 0; i < phase.length; ++i) {
target_phase_sorted_filter.push_back
((*filter)[i * input_spacing + p]);
}
p = phase.next_phase;
if (p == initial_phase) {
break;
}
}
}
}
vector<double>
BQResampler::make_filter(int filter_length, double peak_to_zero) const
{
vector<double> filter;
filter.reserve(filter_length);
vector<double> kaiser = kaiser_for(m_qparams.k_snr, m_qparams.k_transition,
1, filter_length);
int k_length = kaiser.size();
if (k_length == filter_length) {
sinc_multiply(peak_to_zero, kaiser);
return kaiser;
} else {
kaiser.push_back(0.0);
double m = double(k_length - 1) / double(filter_length - 1);
for (int i = 0; i < filter_length; ++i) {
double ix = i * m;
int iix = floor(ix);
double remainder = ix - iix;
double value = 0.0;
value += kaiser[iix] * (1.0 - remainder);
value += kaiser[iix+1] * remainder;
filter.push_back(value);
}
sinc_multiply(peak_to_zero, filter);
return filter;
}
}
void
BQResampler::state_for_ratio(BQResampler::state &target_state,
double ratio,
const BQResampler::state &BQ_R__ prev_state) const
{
params parameters = pick_params(ratio);
target_state.parameters = parameters;
target_state.filter_length =
int(parameters.peak_to_zero * m_qparams.p_multiple + 1);
if (target_state.filter_length % 2 == 0) {
++target_state.filter_length;
}
int half_length = target_state.filter_length / 2; // nb length is odd
int input_spacing = parameters.numerator;
int initial_phase = half_length % input_spacing;
target_state.initial_phase = initial_phase;
target_state.current_phase = initial_phase;
if (m_dynamism == RatioMostlyFixed) {
if (m_debug_level > 0) {
cerr << "BQResampler: creating filter of length "
<< target_state.filter_length << endl;
}
vector<double> filter =
make_filter(target_state.filter_length, parameters.peak_to_zero);
phase_data_for(target_state.phase_info,
target_state.phase_sorted_filter,
target_state.filter_length, &filter,
target_state.initial_phase,
input_spacing,
parameters.denominator);
} else {
phase_data_for(target_state.phase_info,
target_state.phase_sorted_filter,
target_state.filter_length, 0,
target_state.initial_phase,
input_spacing,
parameters.denominator);
}
int buffer_left = half_length / input_spacing;
int buffer_right = buffer_left + 1;
int buffer_length = buffer_left + buffer_right;
buffer_length = max(buffer_length,
int(prev_state.buffer.size() / m_channels));
target_state.centre = buffer_length / 2;
target_state.left = target_state.centre - buffer_left;
target_state.fill = target_state.centre;
buffer_length *= m_channels;
target_state.centre *= m_channels;
target_state.left *= m_channels;
target_state.fill *= m_channels;
int n_phases = int(target_state.phase_info.size());
if (m_debug_level > 0) {
cerr << "BQResampler: " << m_channels << " channel(s) interleaved"
<< ", buffer left " << buffer_left
<< ", right " << buffer_right
<< ", total " << buffer_length << endl;
cerr << "BQResampler: input spacing " << input_spacing
<< ", output spacing " << parameters.denominator
<< ", initial phase " << initial_phase
<< " of " << n_phases << endl;
}
if (prev_state.buffer.size() > 0) {
if (int(prev_state.buffer.size()) == buffer_length) {
target_state.buffer = prev_state.buffer;
target_state.fill = prev_state.fill;
} else {
target_state.buffer = floatbuf(buffer_length, 0.0);
for (int i = 0; i < prev_state.fill; ++i) {
int offset = i - prev_state.centre;
int new_ix = offset + target_state.centre;
if (new_ix >= 0 && new_ix < buffer_length) {
target_state.buffer[new_ix] = prev_state.buffer[i];
target_state.fill = new_ix + 1;
}
}
}
int phases_then = int(prev_state.phase_info.size());
double distance_through =
double(prev_state.current_phase) / double(phases_then);
target_state.current_phase = round(n_phases * distance_through);
if (target_state.current_phase >= n_phases) {
target_state.current_phase = n_phases - 1;
}
} else {
target_state.buffer = floatbuf(buffer_length, 0.0);
}
}
double
BQResampler::reconstruct_one(state *s) const
{
const phase_rec &pr = s->phase_info[s->current_phase];
int phase_length = pr.length;
double result = 0.0;
int dot_length =
min(phase_length,
(int(s->buffer.size()) - s->left) / m_channels);
if (m_dynamism == RatioMostlyFixed) {
int phase_start = pr.start_index;
if (m_channels == 1) {
result = v_multiply_and_sum
(s->phase_sorted_filter.data() + phase_start,
s->buffer.data() + s->left,
dot_length);
} else {
for (int i = 0; i < dot_length; ++i) {
result +=
s->phase_sorted_filter[phase_start + i] *
s->buffer[s->left + i * m_channels + s->current_channel];
}
}
} else {
double m = double(m_proto_length - 1) / double(s->filter_length - 1);
for (int i = 0; i < dot_length; ++i) {
double sample =
s->buffer[s->left + i * m_channels + s->current_channel];
int filter_index = i * s->parameters.numerator + s->current_phase;
double proto_index = m * filter_index;
int iix = floor(proto_index);
double remainder = proto_index - iix;
double filter_value = m_prototype[iix] * (1.0 - remainder);
filter_value += m_prototype[iix+1] * remainder;
result += filter_value * sample;
}
}
s->current_channel = (s->current_channel + 1) % m_channels;
if (s->current_channel == 0) {
if (pr.drop > 0) {
int drop = pr.drop * m_channels;
v_move(s->buffer.data(), s->buffer.data() + drop,
int(s->buffer.size()) - drop);
for (int i = 1; i <= drop; ++i) {
s->buffer[s->buffer.size() - i] = 0.0;
}
s->fill -= drop;
}
s->current_phase = pr.next_phase;
}
return result * s->parameters.scale;
}
}

167
src/dsp/BQResampler.h Normal file
View File

@@ -0,0 +1,167 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2021 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef BQ_BQRESAMPLER_H
#define BQ_BQRESAMPLER_H
#include <vector>
#include "../system/Allocators.h"
#include "../system/VectorOps.h"
namespace RubberBand {
class BQResampler
{
public:
enum Quality { Best, FastestTolerable, Fastest };
enum Dynamism { RatioOftenChanging, RatioMostlyFixed };
enum RatioChange { SmoothRatioChange, SuddenRatioChange };
struct Parameters {
Quality quality;
Dynamism dynamism;
RatioChange ratioChange;
double referenceSampleRate;
int debugLevel;
Parameters() :
quality(FastestTolerable),
dynamism(RatioMostlyFixed),
ratioChange(SmoothRatioChange),
referenceSampleRate(44100),
debugLevel(0) { }
};
BQResampler(Parameters parameters, int channels);
BQResampler(const BQResampler &);
int resampleInterleaved(float *const out, int outspace,
const float *const in, int incount,
double ratio, bool final);
double getEffectiveRatio(double ratio) const;
void reset();
private:
struct QualityParams {
int p_multiple;
int proto_p;
double k_snr;
double k_transition;
double cut;
QualityParams(Quality);
};
const QualityParams m_qparams;
const Dynamism m_dynamism;
const RatioChange m_ratio_change;
const int m_debug_level;
const double m_initial_rate;
const int m_channels;
struct params {
double ratio;
int numerator;
int denominator;
double effective;
double peak_to_zero;
double scale;
params() : ratio(1.0), numerator(1), denominator(1),
effective(1.0), peak_to_zero(0), scale(1.0) { }
};
struct phase_rec {
int next_phase;
int length;
int start_index;
int drop;
phase_rec() : next_phase(0), length(0), start_index(0), drop(0) { }
};
typedef std::vector<float, RubberBand::StlAllocator<float> > floatbuf;
struct state {
params parameters;
int initial_phase;
int current_phase;
int current_channel;
int filter_length;
std::vector<phase_rec> phase_info;
floatbuf phase_sorted_filter;
floatbuf buffer;
int left;
int centre;
int fill;
state() : initial_phase(0), current_phase(0), current_channel(0),
filter_length(0), left(0), centre(0), fill(0) { }
};
state m_state_a;
state m_state_b;
state *m_s; // points at either m_state_a or m_state_b
state *m_fade; // whichever one m_s does not point to
int m_fade_count;
std::vector<double> m_prototype;
int m_proto_length;
bool m_initialised;
int gcd(int a, int b) const;
double bessel0(double x) const;
std::vector<double> kaiser(double beta, int len) const;
void kaiser_params(double attenuation, double transition,
double &beta, int &len) const;
std::vector<double> kaiser_for(double attenuation, double transition,
int minlen, int maxlen) const;
void sinc_multiply(double peak_to_zero, std::vector<double> &buf) const;
params fill_params(double ratio, int num, int denom) const;
params pick_params(double ratio) const;
std::vector<double> make_filter(int filter_length,
double peak_to_zero) const;
void phase_data_for(std::vector<phase_rec> &target_phase_data,
floatbuf &target_phase_sorted_filter,
int filter_length,
const std::vector<double> *filter,
int initial_phase,
int input_spacing,
int output_spacing) const;
void state_for_ratio(state &target_state,
double ratio,
const state &R__ prev_state) const;
double reconstruct_one(state *s) const;
BQResampler &operator=(const BQResampler &); // not provided
};
}
#endif

View File

@@ -22,11 +22,11 @@
*/
#include "FFT.h"
#include "system/Thread.h"
#include "base/Profiler.h"
#include "system/Allocators.h"
#include "system/VectorOps.h"
#include "system/VectorOpsComplex.h"
#include "../system/Thread.h"
#include "../base/Profiler.h"
#include "../system/Allocators.h"
#include "../system/VectorOps.h"
#include "../system/VectorOpsComplex.h"
// Define USE_FFTW_WISDOM if you are defining HAVE_FFTW3 and you want
// to use FFTW_MEASURE mode with persistent wisdom files. This will

View File

@@ -24,7 +24,7 @@
#ifndef RUBBERBAND_FFT_H
#define RUBBERBAND_FFT_H
#include "system/sysutils.h"
#include "../system/sysutils.h"
#include <string>
#include <set>

View File

@@ -26,7 +26,7 @@
#include "SampleFilter.h"
#include "system/Allocators.h"
#include "../system/Allocators.h"
#include <algorithm>

View File

@@ -1,4 +1,4 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
@@ -22,7 +22,9 @@
*/
#include "Resampler.h"
#include "base/Profiler.h"
#include "../system/Allocators.h"
#include "../system/VectorOps.h"
#include <cstdlib>
#include <cmath>
@@ -30,9 +32,6 @@
#include <iostream>
#include <algorithm>
#include "system/Allocators.h"
#include "system/VectorOps.h"
#ifdef HAVE_IPP
#include <ippversion.h>
#if (IPP_VERSION_MAJOR < 7)
@@ -42,6 +41,10 @@
#endif
#endif
#ifdef HAVE_SAMPLERATE
#define HAVE_LIBSAMPLERATE 1
#endif
#ifdef HAVE_LIBSAMPLERATE
#include <samplerate.h>
#endif
@@ -51,18 +54,26 @@
#endif
#ifdef USE_SPEEX
#include "speex/speex_resampler.h"
#include "../speex/speex_resampler.h"
#endif
#ifdef USE_BQRESAMPLER
#include "BQResampler.h"
#endif
#ifndef HAVE_IPP
#ifndef HAVE_LIBSAMPLERATE
#ifndef HAVE_LIBRESAMPLE
#ifndef USE_SPEEX
#ifndef USE_BQRESAMPLER
#error No resampler implementation selected!
#endif
#endif
#endif
#endif
#endif
#define BQ_R__ R__
using namespace std;
@@ -73,21 +84,22 @@ class Resampler::Impl
public:
virtual ~Impl() { }
virtual int resample(float *const R__ *const R__ out,
virtual int resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const R__ *const R__ in,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final) = 0;
virtual int resampleInterleaved(float *const R__ out,
virtual int resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const R__ in,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final) = 0;
virtual int getChannelCount() const = 0;
virtual double getEffectiveRatio(double ratio) const = 0;
virtual void reset() = 0;
};
@@ -99,25 +111,27 @@ namespace Resamplers {
class D_IPP : public Resampler::Impl
{
public:
D_IPP(Resampler::Quality quality, int channels, double initialSampleRate,
D_IPP(Resampler::Quality quality, Resampler::RatioChange,
int channels, double initialSampleRate,
int maxBufferSize, int debugLevel);
~D_IPP();
int resample(float *const R__ *const R__ out,
int resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const R__ *const R__ in,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final);
int resampleInterleaved(float *const R__ out,
int resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const R__ in,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final = false);
int getChannelCount() const { return m_channels; }
double getEffectiveRatio(double ratio) const { return ratio; }
void reset();
@@ -144,6 +158,7 @@ protected:
};
D_IPP::D_IPP(Resampler::Quality /* quality */,
Resampler::RatioChange /* ratioChange */,
int channels, double initialSampleRate,
int maxBufferSize, int debugLevel) :
m_state(0),
@@ -152,7 +167,7 @@ D_IPP::D_IPP(Resampler::Quality /* quality */,
m_debugLevel(debugLevel)
{
if (m_debugLevel > 0) {
cerr << "Resampler::Resampler: using IPP implementation" << endl;
cerr << "Resampler::Resampler: using implementation: IPP" << endl;
}
m_window = 32;
@@ -190,7 +205,7 @@ D_IPP::D_IPP(Resampler::Quality /* quality */,
setBufSize(maxBufferSize + m_history);
if (m_debugLevel > 1) {
cerr << "D_IPP: bufsize = " << m_bufsize << ", window = " << m_window << ", nStep = " << nStep << ", history = " << m_history << endl;
cerr << "bufsize = " << m_bufsize << ", window = " << m_window << ", nStep = " << nStep << ", history = " << m_history << endl;
}
int specSize = 0;
@@ -214,18 +229,13 @@ D_IPP::D_IPP(Resampler::Quality /* quality */,
9.0f,
m_state[c],
hint);
if (m_debugLevel > 1) {
cerr << "D_IPP: Resampler state size = " << specSize << ", allocated at "
<< m_state[c] << endl;
}
m_lastread[c] = m_history;
m_time[c] = m_history;
}
if (m_debugLevel > 1) {
cerr << "D_IPP: Resampler init done" << endl;
cerr << "Resampler init done" << endl;
}
}
@@ -248,9 +258,9 @@ D_IPP::setBufSize(int sz)
{
if (m_debugLevel > 1) {
if (m_bufsize > 0) {
cerr << "D_IPP: resize bufsize " << m_bufsize << " -> ";
cerr << "resize bufsize " << m_bufsize << " -> ";
} else {
cerr << "D_IPP: initialise bufsize to ";
cerr << "initialise bufsize to ";
}
}
@@ -263,13 +273,13 @@ D_IPP::setBufSize(int sz)
int n1 = m_bufsize + m_history + 2;
if (m_debugLevel > 1) {
cerr << "D_IPP: inbuf allocating " << m_bufsize << " + " << m_history << " + 2 = " << n1 << endl;
cerr << "inbuf allocating " << m_bufsize << " + " << m_history << " + 2 = " << n1 << endl;
}
int n2 = (int)lrintf(ceil((m_bufsize - m_history) * m_factor + 2));
if (m_debugLevel > 1) {
cerr << "D_IPP: outbuf allocating (" << m_bufsize << " - " << m_history << ") * " << m_factor << " + 2 = " << n2 << endl;
cerr << "outbuf allocating (" << m_bufsize << " - " << m_history << ") * " << m_factor << " + 2 = " << n2 << endl;
}
m_inbuf = reallocate_and_zero_extend_channels
@@ -277,30 +287,15 @@ D_IPP::setBufSize(int sz)
m_outbuf = reallocate_and_zero_extend_channels
(m_outbuf, m_channels, m_outbufsz, m_channels, n2);
m_inbufsz = n1;
m_outbufsz = n2;
if (m_debugLevel > 2) {
cerr << "D_IPP: inbuf ptr = " << m_inbuf << ", channel inbufs ";
for (int c = 0; c < m_channels; ++c) {
cerr << m_inbuf[c] << " ";
}
cerr << "at " << m_inbufsz * sizeof(float) << " bytes each" << endl;
cerr << "D_IPP: outbuf ptr = " << m_outbuf << ", channel outbufs ";
for (int c = 0; c < m_channels; ++c) {
cerr << m_outbuf[c] << " ";
}
cerr << "at " << m_outbufsz * sizeof(float) << " bytes each" << endl;
}
}
int
D_IPP::resample(float *const R__ *const R__ out,
D_IPP::resample(float *const BQ_R__ *const BQ_R__ out,
int outspace,
const float *const R__ *const R__ in,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final)
@@ -311,7 +306,7 @@ D_IPP::resample(float *const R__ *const R__ out,
}
if (m_debugLevel > 2) {
cerr << "D_IPP: incount = " << incount << ", ratio = " << ratio << ", est space = " << lrintf(ceil(incount * ratio)) << ", outspace = " << outspace << ", final = " << final << endl;
cerr << "incount = " << incount << ", ratio = " << ratio << ", est space = " << lrintf(ceil(incount * ratio)) << ", outspace = " << outspace << ", final = " << final << endl;
}
for (int c = 0; c < m_channels; ++c) {
@@ -328,7 +323,7 @@ D_IPP::resample(float *const R__ *const R__ out,
}
if (m_debugLevel > 2) {
cerr << "D_IPP: lastread advanced to " << m_lastread[0] << endl;
cerr << "lastread advanced to " << m_lastread[0] << endl;
}
int got = doResample(outspace, ratio, final);
@@ -341,9 +336,9 @@ D_IPP::resample(float *const R__ *const R__ out,
}
int
D_IPP::resampleInterleaved(float *const R__ out,
D_IPP::resampleInterleaved(float *const BQ_R__ out,
int outspace,
const float *const R__ in,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final)
@@ -354,7 +349,7 @@ D_IPP::resampleInterleaved(float *const R__ out,
}
if (m_debugLevel > 2) {
cerr << "D_IPP: incount = " << incount << ", ratio = " << ratio << ", est space = " << lrintf(ceil(incount * ratio)) << ", outspace = " << outspace << ", final = " << final << endl;
cerr << "incount = " << incount << ", ratio = " << ratio << ", est space = " << lrintf(ceil(incount * ratio)) << ", outspace = " << outspace << ", final = " << final << endl;
}
for (int c = 0; c < m_channels; ++c) {
@@ -371,7 +366,7 @@ D_IPP::resampleInterleaved(float *const R__ out,
}
if (m_debugLevel > 2) {
cerr << "D_IPP: lastread advanced to " << m_lastread[0] << " after injection of "
cerr << "lastread advanced to " << m_lastread[0] << " after injection of "
<< incount << " samples" << endl;
}
@@ -392,20 +387,20 @@ D_IPP::doResample(int outspace, double ratio, bool final)
int n = m_lastread[c] - m_history - int(m_time[c]);
if (c == 0 && m_debugLevel > 2) {
cerr << "D_IPP: at start, lastread = " << m_lastread[c] << ", history = "
cerr << "at start, lastread = " << m_lastread[c] << ", history = "
<< m_history << ", time = " << m_time[c] << ", therefore n = "
<< n << endl;
}
if (n <= 0) {
if (c == 0 && m_debugLevel > 1) {
cerr << "D_IPP: not enough input samples to do anything" << endl;
cerr << "not enough input samples to do anything" << endl;
}
continue;
}
if (c == 0 && m_debugLevel > 2) {
cerr << "D_IPP: before resample call, time = " << m_time[c] << endl;
cerr << "before resample call, time = " << m_time[c] << endl;
}
// We're committed to not overrunning outspace, so we need to
@@ -414,7 +409,7 @@ D_IPP::doResample(int outspace, double ratio, bool final)
int limit = int(floor(outspace / ratio));
if (n > limit) {
if (c == 0 && m_debugLevel > 1) {
cerr << "D_IPP: trimming input samples from " << n << " to " << limit
cerr << "trimming input samples from " << n << " to " << limit
<< " to avoid overrunning " << outspace << " at output"
<< endl;
}
@@ -431,26 +426,26 @@ D_IPP::doResample(int outspace, double ratio, bool final)
m_state[c]);
int t = int(floor(m_time[c]));
int moveFrom = t - m_history;
if (c == 0 && m_debugLevel > 2) {
cerr << "D_IPP: converted " << n << " samples to " << outcount
cerr << "converted " << n << " samples to " << outcount
<< " (nb outbufsz = " << m_outbufsz
<< "), time advanced to " << m_time[c] << endl;
cerr << "D_IPP: rounding time to " << t << ", lastread = "
cerr << "rounding time to " << t << ", lastread = "
<< m_lastread[c] << ", history = " << m_history << endl;
cerr << "D_IPP: will move " << m_lastread[c] - moveFrom
cerr << "will move " << m_lastread[c] - moveFrom
<< " unconverted samples back from index " << moveFrom
<< " to 0" << endl;
}
if (moveFrom >= m_lastread[c]) {
moveFrom = m_lastread[c];
if (c == 0 && m_debugLevel > 2) {
cerr << "D_IPP: number of samples to move is <= 0, "
cerr << "number of samples to move is <= 0, "
<< "not actually moving any" << endl;
}
} else {
@@ -464,7 +459,7 @@ D_IPP::doResample(int outspace, double ratio, bool final)
m_time[c] -= moveFrom;
if (c == 0 && m_debugLevel > 2) {
cerr << "D_IPP: lastread reduced to " << m_lastread[c]
cerr << "lastread reduced to " << m_lastread[c]
<< ", time reduced to " << m_time[c]
<< endl;
}
@@ -483,7 +478,7 @@ D_IPP::doResample(int outspace, double ratio, bool final)
int additionalcount = 0;
if (c == 0 && m_debugLevel > 2) {
cerr << "D_IPP: final call, padding input with " << m_history
cerr << "final call, padding input with " << m_history
<< " zeros (symmetrical with m_history)" << endl;
}
@@ -492,14 +487,14 @@ D_IPP::doResample(int outspace, double ratio, bool final)
}
if (c == 0 && m_debugLevel > 2) {
cerr << "D_IPP: before resample call, time = " << m_time[c] << endl;
cerr << "before resample call, time = " << m_time[c] << endl;
}
int nAdditional = m_lastread[c] - int(m_time[c]);
if (n + nAdditional > limit) {
if (c == 0 && m_debugLevel > 1) {
cerr << "D_IPP: trimming final input samples from " << nAdditional
cerr << "trimming final input samples from " << nAdditional
<< " to " << (limit - n)
<< " to avoid overrunning " << outspace << " at output"
<< endl;
@@ -517,9 +512,9 @@ D_IPP::doResample(int outspace, double ratio, bool final)
m_state[c]);
if (c == 0 && m_debugLevel > 2) {
cerr << "D_IPP: converted " << n << " samples to " << additionalcount
cerr << "converted " << n << " samples to " << additionalcount
<< ", time advanced to " << m_time[c] << endl;
cerr << "D_IPP: outcount = " << outcount << ", additionalcount = " << additionalcount << ", sum " << outcount + additionalcount << endl;
cerr << "outcount = " << outcount << ", additionalcount = " << additionalcount << ", sum " << outcount + additionalcount << endl;
}
if (c == 0) {
@@ -529,7 +524,7 @@ D_IPP::doResample(int outspace, double ratio, bool final)
}
if (m_debugLevel > 2) {
cerr << "D_IPP: returning " << outcount << " samples" << endl;
cerr << "returning " << outcount << " samples" << endl;
}
return outcount;
@@ -548,25 +543,27 @@ D_IPP::reset()
class D_SRC : public Resampler::Impl
{
public:
D_SRC(Resampler::Quality quality, int channels, double initialSampleRate,
D_SRC(Resampler::Quality quality, Resampler::RatioChange ratioChange,
int channels, double initialSampleRate,
int maxBufferSize, int m_debugLevel);
~D_SRC();
int resample(float *const R__ *const R__ out,
int resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const R__ *const R__ in,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final);
int resampleInterleaved(float *const R__ out,
int resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const R__ in,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final = false);
int getChannelCount() const { return m_channels; }
double getEffectiveRatio(double ratio) const { return ratio; }
void reset();
@@ -579,11 +576,12 @@ protected:
int m_ioutsize;
double m_prevRatio;
bool m_ratioUnset;
bool m_smoothRatios;
int m_debugLevel;
};
D_SRC::D_SRC(Resampler::Quality quality, int channels, double,
int maxBufferSize, int debugLevel) :
D_SRC::D_SRC(Resampler::Quality quality, Resampler::RatioChange ratioChange,
int channels, double, int maxBufferSize, int debugLevel) :
m_src(0),
m_iin(0),
m_iout(0),
@@ -592,25 +590,41 @@ D_SRC::D_SRC(Resampler::Quality quality, int channels, double,
m_ioutsize(0),
m_prevRatio(1.0),
m_ratioUnset(true),
m_smoothRatios(ratioChange == Resampler::SmoothRatioChange),
m_debugLevel(debugLevel)
{
if (m_debugLevel > 0) {
cerr << "Resampler::Resampler: using libsamplerate implementation"
<< endl;
cerr << "Resampler::Resampler: using implementation: libsamplerate"
<< endl;
}
if (channels < 1) {
cerr << "Resampler::Resampler: unable to create resampler: invalid channel count " << channels << " supplied" << endl;
#ifdef NO_EXCEPTIONS
throw Resampler::ImplementationError;
#endif
return;
}
int err = 0;
m_src = src_new(quality == Resampler::Best ? SRC_SINC_BEST_QUALITY :
quality == Resampler::Fastest ? SRC_LINEAR :
SRC_SINC_FASTEST,
quality == Resampler::Fastest ? SRC_SINC_FASTEST :
SRC_SINC_MEDIUM_QUALITY,
channels, &err);
if (err) {
cerr << "Resampler::Resampler: failed to create libsamplerate resampler: "
<< src_strerror(err) << endl;
<< src_strerror(err) << endl;
#ifndef NO_EXCEPTIONS
throw Resampler::ImplementationError;
#endif
return;
} else if (!m_src) {
cerr << "Resampler::Resampler: failed to create libsamplerate resampler, but no error reported?" << endl;
#ifndef NO_EXCEPTIONS
throw Resampler::ImplementationError;
#endif
return;
}
if (maxBufferSize > 0 && m_channels > 1) {
@@ -631,9 +645,9 @@ D_SRC::~D_SRC()
}
int
D_SRC::resample(float *const R__ *const R__ out,
D_SRC::resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const R__ *const R__ in,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final)
@@ -661,15 +675,15 @@ D_SRC::resample(float *const R__ *const R__ out,
}
int
D_SRC::resampleInterleaved(float *const R__ out,
D_SRC::resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const R__ in,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final)
{
SRC_DATA data;
// libsamplerate smooths the filter change over the duration of
// the processing block to avoid artifacts due to sudden changes,
// and it uses outcount to determine how long to smooth the change
@@ -682,7 +696,7 @@ D_SRC::resampleInterleaved(float *const R__ out,
outcount = int(ceil(incount * ratio) + 5);
}
if (m_ratioUnset) {
if (m_ratioUnset || !m_smoothRatios) {
// The first time we set a ratio, we want to do it directly
src_set_ratio(m_src, ratio);
@@ -724,10 +738,9 @@ D_SRC::resampleInterleaved(float *const R__ out,
data.input_frames = incount;
data.output_frames = outcount;
data.src_ratio = ratio;
data.end_of_input = (final ? 1 : 0);
int err = src_process(m_src, &data);
if (err) {
@@ -737,7 +750,7 @@ D_SRC::resampleInterleaved(float *const R__ out,
throw Resampler::ImplementationError;
#endif
}
return (int)data.output_frames_gen;
}
@@ -755,25 +768,27 @@ D_SRC::reset()
class D_Resample : public Resampler::Impl
{
public:
D_Resample(Resampler::Quality quality, int channels, double initialSampleRate,
D_Resample(Resampler::Quality quality, Resampler::RatioChange,
int channels, double initialSampleRate,
int maxBufferSize, int m_debugLevel);
~D_Resample();
int resample(float *const R__ *const R__ out,
int resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const R__ *const R__ in,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final);
int resampleInterleaved(float *const R__ out,
int resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const R__ in,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final);
int getChannelCount() const { return m_channels; }
double getEffectiveRatio(double ratio) const { return ratio; }
void reset();
@@ -799,7 +814,7 @@ D_Resample::D_Resample(Resampler::Quality quality,
m_debugLevel(debugLevel)
{
if (m_debugLevel > 0) {
cerr << "Resampler::Resampler: using libresample implementation"
cerr << "Resampler::Resampler: using implementation: libresample"
<< endl;
}
@@ -836,9 +851,9 @@ D_Resample::~D_Resample()
}
int
D_Resample::resample(float *const R__ *const R__ out,
D_Resample::resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const R__ *const R__ in,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final)
@@ -895,9 +910,9 @@ D_Resample::resample(float *const R__ *const R__ out,
}
int
D_Resample::resampleInterleaved(float *const R__ out,
D_Resample::resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const R__ in,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final)
@@ -937,30 +952,186 @@ D_Resample::reset()
#endif /* HAVE_LIBRESAMPLE */
#ifdef USE_BQRESAMPLER
class D_BQResampler : public Resampler::Impl
{
public:
D_BQResampler(Resampler::Parameters params, int channels);
~D_BQResampler();
int resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final);
int resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final = false);
int getChannelCount() const {
return m_channels;
}
double getEffectiveRatio(double ratio) const {
return m_resampler->getEffectiveRatio(ratio);
}
void reset();
protected:
BQResampler *m_resampler;
float *m_iin;
float *m_iout;
int m_channels;
int m_iinsize;
int m_ioutsize;
int m_debugLevel;
};
D_BQResampler::D_BQResampler(Resampler::Parameters params, int channels) :
m_resampler(0),
m_iin(0),
m_iout(0),
m_channels(channels),
m_iinsize(0),
m_ioutsize(0),
m_debugLevel(params.debugLevel)
{
if (m_debugLevel > 0) {
cerr << "Resampler::Resampler: using implementation: BQResampler" << endl;
}
BQResampler::Parameters rparams;
switch (params.quality) {
case Resampler::Best:
rparams.quality = BQResampler::Best;
break;
case Resampler::FastestTolerable:
rparams.quality = BQResampler::FastestTolerable;
break;
case Resampler::Fastest:
rparams.quality = BQResampler::Fastest;
break;
}
switch (params.dynamism) {
case Resampler::RatioOftenChanging:
rparams.dynamism = BQResampler::RatioOftenChanging;
break;
case Resampler::RatioMostlyFixed:
rparams.dynamism = BQResampler::RatioMostlyFixed;
break;
}
switch (params.ratioChange) {
case Resampler::SmoothRatioChange:
rparams.ratioChange = BQResampler::SmoothRatioChange;
break;
case Resampler::SuddenRatioChange:
rparams.ratioChange = BQResampler::SuddenRatioChange;
break;
}
rparams.referenceSampleRate = params.initialSampleRate;
rparams.debugLevel = params.debugLevel;
m_resampler = new BQResampler(rparams, m_channels);
if (params.maxBufferSize > 0 && m_channels > 1) {
m_iinsize = params.maxBufferSize * m_channels;
m_ioutsize = params.maxBufferSize * m_channels * 2;
m_iin = allocate<float>(m_iinsize);
m_iout = allocate<float>(m_ioutsize);
}
}
D_BQResampler::~D_BQResampler()
{
delete m_resampler;
deallocate(m_iin);
deallocate(m_iout);
}
int
D_BQResampler::resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final)
{
if (m_channels == 1) {
return resampleInterleaved(*out, outcount, *in, incount, ratio, final);
}
if (incount * m_channels > m_iinsize) {
m_iin = reallocate<float>(m_iin, m_iinsize, incount * m_channels);
m_iinsize = incount * m_channels;
}
if (outcount * m_channels > m_ioutsize) {
m_iout = reallocate<float>(m_iout, m_ioutsize, outcount * m_channels);
m_ioutsize = outcount * m_channels;
}
v_interleave(m_iin, in, m_channels, incount);
int n = resampleInterleaved(m_iout, outcount, m_iin, incount, ratio, final);
v_deinterleave(out, m_iout, m_channels, n);
return n;
}
int
D_BQResampler::resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final)
{
return m_resampler->resampleInterleaved(out, outcount,
in, incount,
ratio, final);
}
void
D_BQResampler::reset()
{
m_resampler->reset();
}
#endif /* USE_BQRESAMPLER */
#ifdef USE_SPEEX
class D_Speex : public Resampler::Impl
{
public:
D_Speex(Resampler::Quality quality, int channels, double initialSampleRate,
D_Speex(Resampler::Quality quality, Resampler::RatioChange,
int channels, double initialSampleRate,
int maxBufferSize, int debugLevel);
~D_Speex();
int resample(float *const R__ *const R__ out,
int resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const R__ *const R__ in,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final);
int resampleInterleaved(float *const R__ out,
int resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const R__ in,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final = false);
int getChannelCount() const { return m_channels; }
double getEffectiveRatio(double ratio) const { return ratio; }
void reset();
@@ -982,7 +1153,7 @@ protected:
double ratio, bool final);
};
D_Speex::D_Speex(Resampler::Quality quality,
D_Speex::D_Speex(Resampler::Quality quality, Resampler::RatioChange,
int channels, double initialSampleRate,
int maxBufferSize, int debugLevel) :
m_resampler(0),
@@ -1000,7 +1171,7 @@ D_Speex::D_Speex(Resampler::Quality quality,
quality == Resampler::Fastest ? 0 : 4);
if (m_debugLevel > 0) {
cerr << "Resampler::Resampler: using Speex implementation with q = "
cerr << "Resampler::Resampler: using implementation: Speex with q = "
<< q << endl;
}
@@ -1093,9 +1264,9 @@ D_Speex::setRatio(double ratio)
}
int
D_Speex::resample(float *const R__ *const R__ out,
D_Speex::resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const R__ *const R__ in,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final)
@@ -1136,9 +1307,9 @@ D_Speex::resample(float *const R__ *const R__ out,
}
int
D_Speex::resampleInterleaved(float *const R__ out,
D_Speex::resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const R__ in,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final)
@@ -1236,6 +1407,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
#ifdef HAVE_LIBRESAMPLE
m_method = 3;
#endif
#ifdef USE_BQRESAMPLER
m_method = 4;
#endif
#ifdef HAVE_LIBSAMPLERATE
m_method = 1;
#endif
@@ -1251,6 +1425,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
#ifdef USE_SPEEX
m_method = 2;
#endif
#ifdef USE_BQRESAMPLER
m_method = 4;
#endif
#ifdef HAVE_LIBSAMPLERATE
m_method = 1;
#endif
@@ -1266,6 +1443,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
#ifdef USE_SPEEX
m_method = 2;
#endif
#ifdef USE_BQRESAMPLER
m_method = 4;
#endif
#ifdef HAVE_LIBSAMPLERATE
m_method = 1;
#endif
@@ -1281,7 +1461,7 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
case 0:
#ifdef HAVE_IPP
d = new Resamplers::D_IPP
(params.quality,
(params.quality, params.ratioChange,
channels,
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
#else
@@ -1293,7 +1473,7 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
case 1:
#ifdef HAVE_LIBSAMPLERATE
d = new Resamplers::D_SRC
(params.quality,
(params.quality, params.ratioChange,
channels,
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
#else
@@ -1305,7 +1485,7 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
case 2:
#ifdef USE_SPEEX
d = new Resamplers::D_Speex
(params.quality,
(params.quality, params.ratioChange,
channels,
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
#else
@@ -1317,12 +1497,21 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
case 3:
#ifdef HAVE_LIBRESAMPLE
d = new Resamplers::D_Resample
(params.quality,
(params.quality, params.ratioChange,
channels,
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
#else
cerr << "Resampler::Resampler: No implementation available!" << endl;
abort();
#endif
break;
case 4:
#ifdef USE_BQRESAMPLER
d = new Resamplers::D_BQResampler(params, channels);
#else
cerr << "Resampler::Resampler: No implementation available!" << endl;
abort();
#endif
break;
}
@@ -1340,26 +1529,24 @@ Resampler::~Resampler()
}
int
Resampler::resample(float *const R__ *const R__ out,
Resampler::resample(float *const BQ_R__ *const BQ_R__ out,
int outcount,
const float *const R__ *const R__ in,
const float *const BQ_R__ *const BQ_R__ in,
int incount,
double ratio,
bool final)
{
Profiler profiler("Resampler::resample");
return d->resample(out, outcount, in, incount, ratio, final);
}
int
Resampler::resampleInterleaved(float *const R__ out,
Resampler::resampleInterleaved(float *const BQ_R__ out,
int outcount,
const float *const R__ in,
const float *const BQ_R__ in,
int incount,
double ratio,
bool final)
{
Profiler profiler("Resampler::resampleInterleaved");
return d->resampleInterleaved(out, outcount, in, incount, ratio, final);
}
@@ -1369,6 +1556,12 @@ Resampler::getChannelCount() const
return d->getChannelCount();
}
double
Resampler::getEffectiveRatio(double ratio) const
{
return d->getEffectiveRatio(ratio);
}
void
Resampler::reset()
{

View File

@@ -1,4 +1,4 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
//* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
@@ -24,7 +24,7 @@
#ifndef RUBBERBAND_RESAMPLER_H
#define RUBBERBAND_RESAMPLER_H
#include "system/sysutils.h"
#include "../system/sysutils.h"
namespace RubberBand {
@@ -32,6 +32,9 @@ class Resampler
{
public:
enum Quality { Best, FastestTolerable, Fastest };
enum Dynamism { RatioOftenChanging, RatioMostlyFixed };
enum RatioChange { SmoothRatioChange, SuddenRatioChange };
enum Exception { ImplementationError };
struct Parameters {
@@ -41,6 +44,22 @@ public:
*/
Quality quality;
/**
* Performance hint indicating whether the ratio is expected
* to change regularly or not. If not, more work may happen on
* ratio changes to reduce work when ratio is unchanged.
*/
Dynamism dynamism;
/**
* Hint indicating whether to smooth transitions, via filter
* interpolation or some such method, at ratio change
* boundaries, or whether to make a precise switch to the new
* ratio without regard to audible artifacts. The actual
* effect of this depends on the implementation in use.
*/
RatioChange ratioChange;
/**
* Rate of expected input prior to resampling: may be used to
* determine the filter bandwidth for the quality setting. If
@@ -67,6 +86,8 @@ public:
Parameters() :
quality(FastestTolerable),
dynamism(RatioMostlyFixed),
ratioChange(SmoothRatioChange),
initialSampleRate(44100),
maxBufferSize(0),
debugLevel(0) { }
@@ -125,8 +146,25 @@ public:
double ratio,
bool final = false);
/**
* Return the channel count provided on construction.
*/
int getChannelCount() const;
/**
* Return the ratio that will be actually used when the given
* ratio is requested. For example, if the resampler internally
* uses a rational approximation of the given ratio, this will
* return the closest double to that approximation. Not all
* implementations support this; an implementation that does not
* will just return the given ratio.
*/
double getEffectiveRatio(double ratio) const;
/**
* Reset the internal processing state so that the next call
* behaves as if the resampler had just been constructed.
*/
void reset();
class Impl;

View File

@@ -29,9 +29,9 @@
#include <cstdlib>
#include <map>
#include "system/sysutils.h"
#include "system/VectorOps.h"
#include "system/Allocators.h"
#include "../system/sysutils.h"
#include "../system/VectorOps.h"
#include "../system/Allocators.h"
namespace RubberBand {

View File

@@ -28,9 +28,9 @@
#include <cstdlib>
#include <map>
#include "system/sysutils.h"
#include "system/VectorOps.h"
#include "system/Allocators.h"
#include "../system/sysutils.h"
#include "../system/VectorOps.h"
#include "../system/Allocators.h"
namespace RubberBand {

View File

@@ -21,8 +21,8 @@
you must obtain a valid commercial licence before doing so.
*/
#include "rubberband/rubberband-c.h"
#include "rubberband/RubberBandStretcher.h"
#include "../rubberband/rubberband-c.h"
#include "../rubberband/RubberBandStretcher.h"
struct RubberBandState_
{

View File

@@ -29,6 +29,8 @@
#include <new> // for std::bad_alloc
#include <stdlib.h>
#include <stdexcept>
#ifndef HAVE_POSIX_MEMALIGN
#ifndef _WIN32
#ifndef __APPLE__
@@ -309,6 +311,96 @@ private:
T *m_t;
};
/** Allocator for use with STL classes, e.g. vector, to ensure
* alignment. Based on example code by Stephan T. Lavavej.
*
* e.g. std::vector<float, StlAllocator<float> > v;
*/
template <typename T>
class StlAllocator
{
public:
typedef T *pointer;
typedef const T *const_pointer;
typedef T &reference;
typedef const T &const_reference;
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
StlAllocator() { }
StlAllocator(const StlAllocator&) { }
template <typename U> StlAllocator(const StlAllocator<U>&) { }
~StlAllocator() { }
T *
allocate(const size_t n) const {
if (n == 0) return 0;
if (n > max_size()) {
#ifndef NO_EXCEPTIONS
throw std::length_error("Size overflow in StlAllocator::allocate()");
#else
abort();
#endif
}
return RubberBand::allocate<T>(n);
}
void
deallocate(T *const p, const size_t) const {
RubberBand::deallocate(p);
}
template <typename U>
T *
allocate(const size_t n, const U *) const {
return allocate(n);
}
T *
address(T &r) const {
return &r;
}
const T *
address(const T &s) const {
return &s;
}
size_t
max_size() const {
return (static_cast<size_t>(0) - static_cast<size_t>(1)) / sizeof(T);
}
template <typename U> struct rebind {
typedef StlAllocator<U> other;
};
bool
operator==(const StlAllocator &) const {
return true;
}
bool
operator!=(const StlAllocator &) const {
return false;
}
void
construct(T *const p, const T &t) const {
void *const pv = static_cast<void *>(p);
new (pv) T(t);
}
void
destroy(T *const p) const {
p->~T();
}
private:
StlAllocator& operator=(const StlAllocator&);
};
}
#endif

View File

@@ -491,6 +491,58 @@ inline T v_sum(const T *const R__ src,
return result;
}
template<typename T>
inline T v_multiply_and_sum(const T *const R__ src1,
const T *const R__ src2,
const int count)
{
T result = T();
for (int i = 0; i < count; ++i) {
result += src1[i] * src2[i];
}
return result;
}
#if defined HAVE_IPP
template<>
inline float v_multiply_and_sum(const float *const R__ src1,
const float *const R__ src2,
const int count)
{
float dp;
ippsDotProd_32f(src1, src2, count, &dp);
return dp;
}
template<>
inline double v_multiply_and_sum(const double *const R__ src1,
const double *const R__ src2,
const int count)
{
double dp;
ippsDotProd_64f(src1, src2, count, &dp);
return dp;
}
#elif defined HAVE_VDSP
template<>
inline float v_multiply_and_sum(const float *const R__ src1,
const float *const R__ src2,
const int count)
{
float dp;
vDSP_dotpr(src1, 1, src2, 1, &dp, count);
return dp;
}
template<>
inline double v_multiply_and_sum(const double *const R__ src1,
const double *const R__ src2,
const int count)
{
double dp;
vDSP_dotprD(src1, 1, src2, 1, &dp, count);
return dp;
}
#endif
template<typename T>
inline void v_log(T *const R__ dst,
const int count)

View File

@@ -135,8 +135,8 @@ extern void system_memorybarrier();
#include <dlfcn.h>
#include <stdio.h>
#define MLOCK(a,b) ::mlock((char *)(a),(b))
#define MUNLOCK(a,b) (::munlock((char *)(a),(b)) ? (::perror("munlock failed"), 0) : 0)
#define MLOCK(a,b) mlock((char *)(a),(b))
#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__