Merge from branch single
This commit is contained in:
@@ -24,4 +24,5 @@ Release/
|
|||||||
Debug/
|
Debug/
|
||||||
build
|
build
|
||||||
build_*
|
build_*
|
||||||
|
build-*
|
||||||
UpgradeLog*
|
UpgradeLog*
|
||||||
|
|||||||
2
Doxyfile
2
Doxyfile
@@ -31,7 +31,7 @@ PROJECT_NAME = "Rubber Band Library"
|
|||||||
# This could be handy for archiving the generated documentation or
|
# This could be handy for archiving the generated documentation or
|
||||||
# if some version control system is used.
|
# if some version control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = 1.9.2
|
PROJECT_NUMBER = 2.0.0
|
||||||
|
|
||||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||||
# base path where the generated documentation will be put.
|
# base path where the generated documentation will be put.
|
||||||
|
|||||||
80
README.md
80
README.md
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
# Rubber Band
|
# Rubber Band Library
|
||||||
|
|
||||||
An audio time-stretching and pitch-shifting library and utility program.
|
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)
|
terms of the GPL; either version 2 of the License, or (at your option)
|
||||||
any later version. See the file COPYING for more information.
|
any later version. See the file COPYING for more information.
|
||||||
|
|
||||||
If you wish to distribute code using the Rubber Band Library under
|
If you wish to distribute code using Rubber Band Library under terms
|
||||||
terms other than those of the GNU General Public License, you must
|
other than those of the GNU General Public License, you must obtain a
|
||||||
obtain a commercial licence from us before doing so. In particular,
|
commercial licence from us before doing so. In particular, you may not
|
||||||
you may not legally distribute through any Apple App Store unless you
|
legally distribute through any Apple App Store unless you have a
|
||||||
have a commercial licence. See https://breakfastquay.com/rubberband/
|
commercial licence. See https://breakfastquay.com/rubberband/ for
|
||||||
for licence terms.
|
licence terms.
|
||||||
|
|
||||||
If you have obtained a valid commercial licence, your licence
|
If you have obtained a valid commercial licence, your licence
|
||||||
supersedes this README and the enclosed COPYING file and you may
|
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
|
* Intel IPP - Proprietary; licence needed for redistribution
|
||||||
* KissFFT - BSD-like
|
* KissFFT - BSD-like
|
||||||
* libsamplerate - BSD-like from version 0.1.9 onwards
|
* libsamplerate - BSD-like from version 0.1.9 onwards
|
||||||
* libresample - LGPL
|
|
||||||
* Speex - BSD-like
|
* Speex - BSD-like
|
||||||
* Pommier math functions - 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
|
1. Code components
|
||||||
2. Using the Rubber Band command-line tool
|
2. Using the Rubber Band command-line tool
|
||||||
3. Using the Rubber Band Library
|
3. Using Rubber Band Library
|
||||||
4. Compiling Rubber Band
|
4. Compiling Rubber Band
|
||||||
a. Building on Linux
|
a. Building on Linux
|
||||||
b. Building on macOS
|
b. Building on macOS
|
||||||
@@ -91,7 +90,7 @@ Rubber Band consists of:
|
|||||||
and FFT code; see section 3a below for details.
|
and FFT code; see section 3a below for details.
|
||||||
|
|
||||||
* The Rubber Band command-line tool. This is in main/main.cpp.
|
* 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
|
(http://www.mega-nerd.com/libsndfile/, licensed under the GNU Lesser
|
||||||
General Public License) for audio file loading.
|
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).
|
"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++
|
Rubber Band has a public API that consists of one C++ class, called
|
||||||
class, called RubberBandStretcher in the RubberBand namespace. You
|
`RubberBandStretcher` in the `RubberBand` namespace. You should
|
||||||
should `#include <rubberband/RubberBandStretcher.h>` to use this
|
`#include <rubberband/RubberBandStretcher.h>` to use this class.
|
||||||
class. There is extensive documentation in the class header.
|
There is extensive documentation in the class header.
|
||||||
|
|
||||||
A header with C language bindings is also provided in
|
A header with C language bindings is also provided in
|
||||||
`<rubberband/rubberband-c.h>`. This is a wrapper around the C++
|
`<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.
|
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
|
The primary supported build system for Rubber Band on all platforms is
|
||||||
platforms is Meson (https://mesonbuild.com). The Meson build system
|
Meson (https://mesonbuild.com). The Meson build system can be used to
|
||||||
can be used to build all targets (static and dynamic library,
|
build all targets (static and dynamic library, command-line utility,
|
||||||
command-line utility, and plugins) and to cross-compile.
|
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
|
alternative build files (Makefiles and Visual C++ projects) are
|
||||||
included in the `otherbuilds` directory. See the platform-specific
|
included in the `otherbuilds` directory. See the platform-specific
|
||||||
build sections below for more details.
|
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:
|
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
|
The options are documented in the library- and platform-specific
|
||||||
sections below.
|
sections below.
|
||||||
|
|
||||||
The Rubber Band Library is written entirely in C++ to the C++98
|
Rubber Band Library is written entirely in C++ and requires a C++11
|
||||||
standard. It is unlikely to make any difference (performance or
|
compiler. It is unlikely to make any difference (performance or
|
||||||
otherwise) which C++ standard your compiler uses - as long as it's no
|
otherwise) which C++ standard you compile with, as long as it's no
|
||||||
older than C++98!
|
older than C++11.
|
||||||
|
|
||||||
If you are building this software using either of the Speex or KissFFT
|
If you are building this software using either of the Speex or KissFFT
|
||||||
library options, please be sure to review the terms for those
|
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
|
Note that you cannot legally distribute applications using Rubber Band
|
||||||
in the Mac App Store, unless you have first obtained a commercial
|
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
|
licence for Rubber Band Library. GPL code is not permitted in the app
|
||||||
app store. See https://breakfastquay.com/technology/license.html for
|
store. See https://breakfastquay.com/technology/license.html for
|
||||||
commercial terms.
|
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
|
Note that you cannot legally distribute applications using Rubber Band
|
||||||
in the iOS App Store, unless you have a first obtained a commercial
|
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
|
licence for Rubber Band Library. GPL code is not permitted in the app
|
||||||
app store. See https://breakfastquay.com/technology/license.html for
|
store. See https://breakfastquay.com/technology/license.html for
|
||||||
commercial terms.
|
commercial terms.
|
||||||
|
|
||||||
|
|
||||||
@@ -409,10 +416,21 @@ Library Build option CPP define Notes
|
|||||||
---- ------------ ---------- -----
|
---- ------------ ---------- -----
|
||||||
|
|
||||||
libsamplerate -DHAVE_LIBSAMPLERATE
|
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
|
Speex -DUSE_SPEEX
|
||||||
-Dresampler=speex Bundled, can be distributed with
|
-Dresampler=speex Can be distributed with
|
||||||
either the Rubber Band GPL or
|
either the Rubber Band GPL or
|
||||||
commercial licence.
|
commercial licence.
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ RubberBandPitchShifter::portNamesMono[PortCountMono] =
|
|||||||
"Octaves",
|
"Octaves",
|
||||||
"Crispness",
|
"Crispness",
|
||||||
"Formant Preserving",
|
"Formant Preserving",
|
||||||
"Faster",
|
"Wet-Dry Mix",
|
||||||
"Input",
|
"Input",
|
||||||
"Output"
|
"Output"
|
||||||
};
|
};
|
||||||
@@ -58,7 +58,7 @@ RubberBandPitchShifter::portNamesStereo[PortCountStereo] =
|
|||||||
"Octaves",
|
"Octaves",
|
||||||
"Crispness",
|
"Crispness",
|
||||||
"Formant Preserving",
|
"Formant Preserving",
|
||||||
"Faster",
|
"Wet-Dry Mix",
|
||||||
"Input L",
|
"Input L",
|
||||||
"Output L",
|
"Output L",
|
||||||
"Input R",
|
"Input R",
|
||||||
@@ -112,7 +112,7 @@ RubberBandPitchShifter::hintsMono[PortCountMono] =
|
|||||||
LADSPA_HINT_BOUNDED_BELOW |
|
LADSPA_HINT_BOUNDED_BELOW |
|
||||||
LADSPA_HINT_BOUNDED_ABOVE |
|
LADSPA_HINT_BOUNDED_ABOVE |
|
||||||
LADSPA_HINT_INTEGER,
|
LADSPA_HINT_INTEGER,
|
||||||
-3.0, 3.0 },
|
-2.0, 2.0 },
|
||||||
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
|
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
|
||||||
LADSPA_HINT_BOUNDED_BELOW |
|
LADSPA_HINT_BOUNDED_BELOW |
|
||||||
LADSPA_HINT_BOUNDED_ABOVE |
|
LADSPA_HINT_BOUNDED_ABOVE |
|
||||||
@@ -123,10 +123,9 @@ RubberBandPitchShifter::hintsMono[PortCountMono] =
|
|||||||
LADSPA_HINT_BOUNDED_ABOVE |
|
LADSPA_HINT_BOUNDED_ABOVE |
|
||||||
LADSPA_HINT_TOGGLED,
|
LADSPA_HINT_TOGGLED,
|
||||||
0.0, 1.0 },
|
0.0, 1.0 },
|
||||||
{ LADSPA_HINT_DEFAULT_0 | // fast
|
{ LADSPA_HINT_DEFAULT_0 | // wet-dry mix
|
||||||
LADSPA_HINT_BOUNDED_BELOW |
|
LADSPA_HINT_BOUNDED_BELOW |
|
||||||
LADSPA_HINT_BOUNDED_ABOVE |
|
LADSPA_HINT_BOUNDED_ABOVE,
|
||||||
LADSPA_HINT_TOGGLED,
|
|
||||||
0.0, 1.0 },
|
0.0, 1.0 },
|
||||||
{ 0, 0, 0 },
|
{ 0, 0, 0 },
|
||||||
{ 0, 0, 0 }
|
{ 0, 0, 0 }
|
||||||
@@ -149,7 +148,7 @@ RubberBandPitchShifter::hintsStereo[PortCountStereo] =
|
|||||||
LADSPA_HINT_BOUNDED_BELOW |
|
LADSPA_HINT_BOUNDED_BELOW |
|
||||||
LADSPA_HINT_BOUNDED_ABOVE |
|
LADSPA_HINT_BOUNDED_ABOVE |
|
||||||
LADSPA_HINT_INTEGER,
|
LADSPA_HINT_INTEGER,
|
||||||
-3.0, 3.0 },
|
-2.0, 2.0 },
|
||||||
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
|
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
|
||||||
LADSPA_HINT_BOUNDED_BELOW |
|
LADSPA_HINT_BOUNDED_BELOW |
|
||||||
LADSPA_HINT_BOUNDED_ABOVE |
|
LADSPA_HINT_BOUNDED_ABOVE |
|
||||||
@@ -160,10 +159,9 @@ RubberBandPitchShifter::hintsStereo[PortCountStereo] =
|
|||||||
LADSPA_HINT_BOUNDED_ABOVE |
|
LADSPA_HINT_BOUNDED_ABOVE |
|
||||||
LADSPA_HINT_TOGGLED,
|
LADSPA_HINT_TOGGLED,
|
||||||
0.0, 1.0 },
|
0.0, 1.0 },
|
||||||
{ LADSPA_HINT_DEFAULT_0 | // fast
|
{ LADSPA_HINT_DEFAULT_0 | // wet-dry mix
|
||||||
LADSPA_HINT_BOUNDED_BELOW |
|
LADSPA_HINT_BOUNDED_BELOW |
|
||||||
LADSPA_HINT_BOUNDED_ABOVE |
|
LADSPA_HINT_BOUNDED_ABOVE,
|
||||||
LADSPA_HINT_TOGGLED,
|
|
||||||
0.0, 1.0 },
|
0.0, 1.0 },
|
||||||
{ 0, 0, 0 },
|
{ 0, 0, 0 },
|
||||||
{ 0, 0, 0 },
|
{ 0, 0, 0 },
|
||||||
@@ -237,14 +235,14 @@ RubberBandPitchShifter::RubberBandPitchShifter(int sampleRate, size_t channels)
|
|||||||
m_octaves(0),
|
m_octaves(0),
|
||||||
m_crispness(0),
|
m_crispness(0),
|
||||||
m_formant(0),
|
m_formant(0),
|
||||||
m_fast(0),
|
m_wetDry(0),
|
||||||
m_ratio(1.0),
|
m_ratio(1.0),
|
||||||
m_prevRatio(1.0),
|
m_prevRatio(1.0),
|
||||||
m_currentCrispness(-1),
|
m_currentCrispness(-1),
|
||||||
m_currentFormant(false),
|
m_currentFormant(false),
|
||||||
m_currentFast(false),
|
|
||||||
m_blockSize(1024),
|
m_blockSize(1024),
|
||||||
m_reserve(1024),
|
m_reserve(8192),
|
||||||
|
m_bufsize(0),
|
||||||
m_minfill(0),
|
m_minfill(0),
|
||||||
m_stretcher(new RubberBandStretcher
|
m_stretcher(new RubberBandStretcher
|
||||||
(sampleRate, channels,
|
(sampleRate, channels,
|
||||||
@@ -257,19 +255,23 @@ RubberBandPitchShifter::RubberBandPitchShifter(int sampleRate, size_t channels)
|
|||||||
m_output = new float *[m_channels];
|
m_output = new float *[m_channels];
|
||||||
|
|
||||||
m_outputBuffer = new RingBuffer<float> *[m_channels];
|
m_outputBuffer = new RingBuffer<float> *[m_channels];
|
||||||
|
m_delayMixBuffer = new RingBuffer<float> *[m_channels];
|
||||||
m_scratch = new 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) {
|
for (size_t c = 0; c < m_channels; ++c) {
|
||||||
|
|
||||||
m_input[c] = 0;
|
m_input[c] = 0;
|
||||||
m_output[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[m_bufsize];
|
||||||
|
for (size_t i = 0; i < m_bufsize; ++i) {
|
||||||
m_scratch[c] = new float[bufsize];
|
m_scratch[c][i] = 0.f;
|
||||||
for (int i = 0; i < bufsize; ++i) m_scratch[c][i] = 0.f;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
activateImpl();
|
activateImpl();
|
||||||
@@ -280,9 +282,11 @@ RubberBandPitchShifter::~RubberBandPitchShifter()
|
|||||||
delete m_stretcher;
|
delete m_stretcher;
|
||||||
for (size_t c = 0; c < m_channels; ++c) {
|
for (size_t c = 0; c < m_channels; ++c) {
|
||||||
delete m_outputBuffer[c];
|
delete m_outputBuffer[c];
|
||||||
|
delete m_delayMixBuffer[c];
|
||||||
delete[] m_scratch[c];
|
delete[] m_scratch[c];
|
||||||
}
|
}
|
||||||
delete[] m_outputBuffer;
|
delete[] m_outputBuffer;
|
||||||
|
delete[] m_delayMixBuffer;
|
||||||
delete[] m_scratch;
|
delete[] m_scratch;
|
||||||
delete[] m_output;
|
delete[] m_output;
|
||||||
delete[] m_input;
|
delete[] m_input;
|
||||||
@@ -312,7 +316,7 @@ RubberBandPitchShifter::connectPort(LADSPA_Handle handle,
|
|||||||
&shifter->m_octaves,
|
&shifter->m_octaves,
|
||||||
&shifter->m_crispness,
|
&shifter->m_crispness,
|
||||||
&shifter->m_formant,
|
&shifter->m_formant,
|
||||||
&shifter->m_fast,
|
&shifter->m_wetDry,
|
||||||
&shifter->m_input[0],
|
&shifter->m_input[0],
|
||||||
&shifter->m_output[0],
|
&shifter->m_output[0],
|
||||||
&shifter->m_input[1],
|
&shifter->m_input[1],
|
||||||
@@ -328,11 +332,16 @@ RubberBandPitchShifter::connectPort(LADSPA_Handle handle,
|
|||||||
*ports[port] = (float *)location;
|
*ports[port] = (float *)location;
|
||||||
|
|
||||||
if (shifter->m_latency) {
|
if (shifter->m_latency) {
|
||||||
*(shifter->m_latency) =
|
*(shifter->m_latency) = shifter->getLatency();
|
||||||
float(shifter->m_stretcher->getLatency() + shifter->m_reserve);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
RubberBandPitchShifter::getLatency() const
|
||||||
|
{
|
||||||
|
return m_reserve;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RubberBandPitchShifter::activate(LADSPA_Handle handle)
|
RubberBandPitchShifter::activate(LADSPA_Handle handle)
|
||||||
{
|
{
|
||||||
@@ -350,20 +359,22 @@ RubberBandPitchShifter::activateImpl()
|
|||||||
|
|
||||||
for (size_t c = 0; c < m_channels; ++c) {
|
for (size_t c = 0; c < m_channels; ++c) {
|
||||||
m_outputBuffer[c]->reset();
|
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;
|
m_minfill = 0;
|
||||||
|
|
||||||
// prime stretcher
|
m_stretcher->process(m_scratch, m_reserve, false);
|
||||||
// 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);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -431,23 +442,6 @@ RubberBandPitchShifter::updateFormant()
|
|||||||
m_currentFormant = f;
|
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
|
void
|
||||||
RubberBandPitchShifter::runImpl(unsigned long insamples)
|
RubberBandPitchShifter::runImpl(unsigned long insamples)
|
||||||
{
|
{
|
||||||
@@ -466,15 +460,29 @@ RubberBandPitchShifter::runImpl(unsigned long insamples)
|
|||||||
|
|
||||||
offset += block;
|
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
|
void
|
||||||
RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
|
RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
|
||||||
{
|
{
|
||||||
// cerr << "RubberBandPitchShifter::runImpl(" << insamples << ")" << endl;
|
|
||||||
|
|
||||||
// static int incount = 0, outcount = 0;
|
|
||||||
|
|
||||||
updateRatio();
|
updateRatio();
|
||||||
if (m_ratio != m_prevRatio) {
|
if (m_ratio != m_prevRatio) {
|
||||||
m_stretcher->setPitchScale(m_ratio);
|
m_stretcher->setPitchScale(m_ratio);
|
||||||
@@ -482,13 +490,11 @@ RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_latency) {
|
if (m_latency) {
|
||||||
*m_latency = float(m_stretcher->getLatency() + m_reserve);
|
*m_latency = getLatency();
|
||||||
// cerr << "latency = " << *m_latency << endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCrispness();
|
updateCrispness();
|
||||||
updateFormant();
|
updateFormant();
|
||||||
updateFast();
|
|
||||||
|
|
||||||
const int samples = insamples;
|
const int samples = insamples;
|
||||||
int processed = 0;
|
int processed = 0;
|
||||||
@@ -496,17 +502,6 @@ RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
|
|||||||
|
|
||||||
float *ptrs[2];
|
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) {
|
while (processed < samples) {
|
||||||
|
|
||||||
// never feed more than the minimum necessary number of
|
// 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 avail = m_stretcher->available();
|
||||||
int writable = m_outputBuffer[0]->getWriteSpace();
|
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);
|
size_t actual = m_stretcher->retrieve(m_scratch, outchunk);
|
||||||
outTotal += actual;
|
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) {
|
for (size_t c = 0; c < m_channels; ++c) {
|
||||||
if (int(m_outputBuffer[c]->getWriteSpace()) < outchunk) {
|
m_outputBuffer[c]->write(m_scratch[c], actual);
|
||||||
cerr << "RubberBandPitchShifter::runImpl: buffer overrun: chunk = " << outchunk << ", space = " << m_outputBuffer[c]->getWriteSpace() << endl;
|
|
||||||
}
|
|
||||||
m_outputBuffer[c]->write(m_scratch[c], outchunk);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,8 +542,9 @@ RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
|
|||||||
m_outputBuffer[c]->read(&(m_output[c][offset]), chunk);
|
m_outputBuffer[c]->read(&(m_output[c][offset]), chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_minfill == 0) {
|
size_t fill = m_outputBuffer[0]->getReadSpace();
|
||||||
m_minfill = m_outputBuffer[0]->getReadSpace();
|
if (fill < m_minfill || m_minfill == 0) {
|
||||||
|
m_minfill = fill;
|
||||||
// cerr << "minfill = " << m_minfill << endl;
|
// cerr << "minfill = " << m_minfill << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ protected:
|
|||||||
CentsPort = 3,
|
CentsPort = 3,
|
||||||
CrispnessPort = 4,
|
CrispnessPort = 4,
|
||||||
FormantPort = 5,
|
FormantPort = 5,
|
||||||
FastPort = 6,
|
WetDryPort = 6,
|
||||||
InputPort1 = 7,
|
InputPort1 = 7,
|
||||||
OutputPort1 = 8,
|
OutputPort1 = 8,
|
||||||
PortCountMono = OutputPort1 + 1,
|
PortCountMono = OutputPort1 + 1,
|
||||||
@@ -83,7 +83,8 @@ protected:
|
|||||||
void updateRatio();
|
void updateRatio();
|
||||||
void updateCrispness();
|
void updateCrispness();
|
||||||
void updateFormant();
|
void updateFormant();
|
||||||
void updateFast();
|
|
||||||
|
int getLatency() const;
|
||||||
|
|
||||||
float **m_input;
|
float **m_input;
|
||||||
float **m_output;
|
float **m_output;
|
||||||
@@ -93,19 +94,20 @@ protected:
|
|||||||
float *m_octaves;
|
float *m_octaves;
|
||||||
float *m_crispness;
|
float *m_crispness;
|
||||||
float *m_formant;
|
float *m_formant;
|
||||||
float *m_fast;
|
float *m_wetDry;
|
||||||
double m_ratio;
|
double m_ratio;
|
||||||
double m_prevRatio;
|
double m_prevRatio;
|
||||||
int m_currentCrispness;
|
int m_currentCrispness;
|
||||||
bool m_currentFormant;
|
bool m_currentFormant;
|
||||||
bool m_currentFast;
|
|
||||||
|
|
||||||
size_t m_blockSize;
|
size_t m_blockSize;
|
||||||
size_t m_reserve;
|
size_t m_reserve;
|
||||||
|
size_t m_bufsize;
|
||||||
size_t m_minfill;
|
size_t m_minfill;
|
||||||
|
|
||||||
RubberBand::RubberBandStretcher *m_stretcher;
|
RubberBand::RubberBandStretcher *m_stretcher;
|
||||||
RubberBand::RingBuffer<float> **m_outputBuffer;
|
RubberBand::RingBuffer<float> **m_outputBuffer;
|
||||||
|
RubberBand::RingBuffer<float> **m_delayMixBuffer;
|
||||||
float **m_scratch;
|
float **m_scratch;
|
||||||
|
|
||||||
int m_sampleRate;
|
int m_sampleRate;
|
||||||
|
|||||||
451
main/main.cpp
451
main/main.cpp
@@ -21,7 +21,7 @@
|
|||||||
you must obtain a valid commercial licence before doing so.
|
you must obtain a valid commercial licence before doing so.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "rubberband/RubberBandStretcher.h"
|
#include "../rubberband/RubberBandStretcher.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sndfile.h>
|
#include <sndfile.h>
|
||||||
@@ -33,20 +33,17 @@
|
|||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include "system/sysutils.h"
|
#include "../src/system/sysutils.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#include "getopt/getopt.h"
|
#include "../src/getopt/getopt.h"
|
||||||
#else
|
#else
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "base/Profiler.h"
|
#include "../src/base/Profiler.h"
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace RubberBand;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
using RubberBand::gettimeofday;
|
using RubberBand::gettimeofday;
|
||||||
@@ -57,6 +54,11 @@ using RubberBand::usleep;
|
|||||||
#define strdup _strdup
|
#define strdup _strdup
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using RubberBand::RubberBandStretcher;
|
||||||
|
|
||||||
|
using std::cerr;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
double tempo_convert(const char *str)
|
double tempo_convert(const char *str)
|
||||||
{
|
{
|
||||||
char *d = strchr((char *)str, ':');
|
char *d = strchr((char *)str, ':');
|
||||||
@@ -105,7 +107,10 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
bool haveRatio = false;
|
bool haveRatio = false;
|
||||||
|
|
||||||
std::string mapfile;
|
std::string timeMapFile;
|
||||||
|
std::string freqMapFile;
|
||||||
|
std::string pitchMapFile;
|
||||||
|
bool freqOrPitchMapSpecified = false;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
NoTransients,
|
NoTransients,
|
||||||
@@ -119,6 +124,8 @@ int main(int argc, char **argv)
|
|||||||
SoftDetector
|
SoftDetector
|
||||||
} detector = CompoundDetector;
|
} detector = CompoundDetector;
|
||||||
|
|
||||||
|
bool ignoreClipping = false;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int optionIndex = 0;
|
int optionIndex = 0;
|
||||||
|
|
||||||
@@ -151,6 +158,9 @@ int main(int argc, char **argv)
|
|||||||
{ "threads", 0, 0, '@' },
|
{ "threads", 0, 0, '@' },
|
||||||
{ "quiet", 0, 0, 'q' },
|
{ "quiet", 0, 0, 'q' },
|
||||||
{ "timemap", 1, 0, 'M' },
|
{ "timemap", 1, 0, 'M' },
|
||||||
|
{ "freqmap", 1, 0, 'Q' },
|
||||||
|
{ "pitchmap", 1, 0, 'C' },
|
||||||
|
{ "ignore-clipping", 0, 0, 'i' },
|
||||||
{ 0, 0, 0, 0 }
|
{ 0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -186,7 +196,10 @@ int main(int argc, char **argv)
|
|||||||
case '%': hqpitch = true; break;
|
case '%': hqpitch = true; break;
|
||||||
case 'c': crispness = atoi(optarg); break;
|
case 'c': crispness = atoi(optarg); break;
|
||||||
case 'q': quiet = true; 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;
|
default: help = true; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,6 +209,15 @@ int main(int argc, char **argv)
|
|||||||
return 0;
|
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) {
|
if (help || !haveRatio || optind + 2 != argc) {
|
||||||
cerr << endl;
|
cerr << endl;
|
||||||
cerr << "Rubber Band" << endl;
|
cerr << "Rubber Band" << endl;
|
||||||
@@ -214,12 +236,34 @@ int main(int argc, char **argv)
|
|||||||
cerr << " -p<X>, --pitch <X> Raise pitch by X semitones, or" << endl;
|
cerr << " -p<X>, --pitch <X> Raise pitch by X semitones, or" << endl;
|
||||||
cerr << " -f<X>, --frequency <X> Change frequency by multiple X" << endl;
|
cerr << " -f<X>, --frequency <X> Change frequency by multiple X" << endl;
|
||||||
cerr << 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 << endl;
|
||||||
cerr << "A map file consists of a series of lines each having two numbers separated" << endl;
|
cerr << " -M<F>, --timemap <F> Use file F as the source for time map" << endl;
|
||||||
cerr << "by a single space. These are source and target sample frame numbers for fixed" << endl;
|
cerr << endl;
|
||||||
cerr << "time points within the audio data, defining a varying stretch factor through" << endl;
|
cerr << " A time map (or key-frame map) file contains a series of lines, each with two" << endl;
|
||||||
cerr << "the audio. You must specify an overall stretch factor using e.g. -t as well." << 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 << endl;
|
||||||
cerr << "The following options provide a simple way to adjust the sound. See below" << endl;
|
cerr << "The following options provide a simple way to adjust the sound. See below" << endl;
|
||||||
cerr << "for more details." << endl;
|
cerr << "for more details." << 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 << " --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 << " --centre-focus Preserve focus of centre material in stereo" << endl;
|
||||||
cerr << " (at a cost in width and individual channel quality)" << 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 << endl;
|
||||||
cerr << " -d<N>, --debug <N> Select debug level (N = 0,1,2,3); default 0, full 3" << endl;
|
cerr << " -d<N>, --debug <N> Select debug level (N = 0,1,2,3); default 0, full 3" << endl;
|
||||||
cerr << " (N.B. debug level 3 includes audible ticks in output)" << endl;
|
cerr << " (N.B. debug level 3 includes audible ticks in output)" << endl;
|
||||||
@@ -278,6 +324,12 @@ int main(int argc, char **argv)
|
|||||||
cerr << " provided -- crispness will override these other options" << endl;
|
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) {
|
switch (crispness) {
|
||||||
case -1: crispness = 5; break;
|
case -1: crispness = 5; break;
|
||||||
case 0: detector = CompoundDetector; transients = NoTransients; lamination = false; longwin = true; shortwin = false; 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;
|
cerr << ")" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<size_t, size_t> mapping;
|
std::map<size_t, size_t> timeMap;
|
||||||
|
if (timeMapFile != "") {
|
||||||
if (mapfile != "") {
|
std::ifstream ifile(timeMapFile.c_str());
|
||||||
std::ifstream ifile(mapfile.c_str());
|
|
||||||
if (!ifile.is_open()) {
|
if (!ifile.is_open()) {
|
||||||
cerr << "ERROR: Failed to open time map file \"" << mapfile << "\""
|
cerr << "ERROR: Failed to open time map file \""
|
||||||
<< endl;
|
<< timeMapFile << "\"" << endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
std::string line;
|
std::string line;
|
||||||
int lineno = 0;
|
int lineno = 0;
|
||||||
while (!ifile.eof()) {
|
while (!ifile.eof()) {
|
||||||
std::getline(ifile, line);
|
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 == "") {
|
if (line == "") {
|
||||||
++lineno;
|
++lineno;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::string::size_type i = line.find_first_of(" ");
|
std::string::size_type i = line.find_first_of(" ");
|
||||||
if (i == std::string::npos) {
|
if (i == std::string::npos) {
|
||||||
cerr << "ERROR: Time map file \"" << mapfile
|
cerr << "ERROR: Time map file \"" << timeMapFile
|
||||||
<< "\" is malformed at line " << lineno << endl;
|
<< "\" is malformed at line " << lineno << endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
size_t source = atoi(line.substr(0, i).c_str());
|
size_t source = atoi(line.substr(0, i).c_str());
|
||||||
while (i < line.length() && line[i] == ' ') ++i;
|
while (i < line.length() && line[i] == ' ') ++i;
|
||||||
size_t target = atoi(line.substr(i).c_str());
|
size_t target = atoi(line.substr(i).c_str());
|
||||||
mapping[source] = target;
|
timeMap[source] = target;
|
||||||
if (debug > 0) {
|
if (debug > 0) {
|
||||||
cerr << "adding mapping from " << source << " to " << target << endl;
|
cerr << "adding mapping from " << source << " to " << target << endl;
|
||||||
}
|
}
|
||||||
@@ -339,7 +392,57 @@ int main(int argc, char **argv)
|
|||||||
ifile.close();
|
ifile.close();
|
||||||
|
|
||||||
if (!quiet) {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,9 +491,6 @@ int main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ibs = 1024;
|
|
||||||
size_t channels = sfinfo.channels;
|
|
||||||
|
|
||||||
RubberBandStretcher::Options options = 0;
|
RubberBandStretcher::Options options = 0;
|
||||||
if (realtime) options |= RubberBandStretcher::OptionProcessRealTime;
|
if (realtime) options |= RubberBandStretcher::OptionProcessRealTime;
|
||||||
if (precise) options |= RubberBandStretcher::OptionStretchPrecise;
|
if (precise) options |= RubberBandStretcher::OptionStretchPrecise;
|
||||||
@@ -399,9 +499,14 @@ int main(int argc, char **argv)
|
|||||||
if (shortwin) options |= RubberBandStretcher::OptionWindowShort;
|
if (shortwin) options |= RubberBandStretcher::OptionWindowShort;
|
||||||
if (smoothing) options |= RubberBandStretcher::OptionSmoothingOn;
|
if (smoothing) options |= RubberBandStretcher::OptionSmoothingOn;
|
||||||
if (formant) options |= RubberBandStretcher::OptionFormantPreserved;
|
if (formant) options |= RubberBandStretcher::OptionFormantPreserved;
|
||||||
if (hqpitch) options |= RubberBandStretcher::OptionPitchHighQuality;
|
|
||||||
if (together) options |= RubberBandStretcher::OptionChannelsTogether;
|
if (together) options |= RubberBandStretcher::OptionChannelsTogether;
|
||||||
|
|
||||||
|
if (freqOrPitchMapSpecified) {
|
||||||
|
options |= RubberBandStretcher::OptionPitchHighConsistency;
|
||||||
|
} else if (hqpitch) {
|
||||||
|
options |= RubberBandStretcher::OptionPitchHighQuality;
|
||||||
|
}
|
||||||
|
|
||||||
switch (threading) {
|
switch (threading) {
|
||||||
case 0:
|
case 0:
|
||||||
options |= RubberBandStretcher::OptionThreadingAuto;
|
options |= RubberBandStretcher::OptionThreadingAuto;
|
||||||
@@ -439,11 +544,16 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pitchshift != 0.0) {
|
if (pitchshift != 0.0) {
|
||||||
frequencyshift *= pow(2.0, pitchshift / 12);
|
frequencyshift *= pow(2.0, pitchshift / 12.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
cerr << "Using time ratio " << ratio;
|
cerr << "Using time ratio " << ratio;
|
||||||
|
|
||||||
|
if (!freqOrPitchMapSpecified) {
|
||||||
cerr << " and frequency ratio " << frequencyshift << endl;
|
cerr << " and frequency ratio " << frequencyshift << endl;
|
||||||
|
} else {
|
||||||
|
cerr << " and initial frequency ratio " << frequencyshift << endl;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
RubberBand::
|
RubberBand::
|
||||||
@@ -453,15 +563,30 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
RubberBandStretcher::setDefaultDebugLevel(debug);
|
RubberBandStretcher::setDefaultDebugLevel(debug);
|
||||||
|
|
||||||
|
size_t countIn = 0, countOut = 0;
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
int thisBlockSize;
|
||||||
|
|
||||||
|
while (!successful) { // we may have to repeat with a modified
|
||||||
|
// gain, if clipping occurs
|
||||||
|
successful = true;
|
||||||
|
|
||||||
RubberBandStretcher ts(sfinfo.samplerate, channels, options,
|
RubberBandStretcher ts(sfinfo.samplerate, channels, options,
|
||||||
ratio, frequencyshift);
|
ratio, frequencyshift);
|
||||||
|
|
||||||
ts.setExpectedInputDuration(sfinfo.frames);
|
ts.setExpectedInputDuration(sfinfo.frames);
|
||||||
|
|
||||||
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 frame = 0;
|
int frame = 0;
|
||||||
int percent = 0;
|
int percent = 0;
|
||||||
|
|
||||||
@@ -476,19 +601,17 @@ int main(int argc, char **argv)
|
|||||||
while (frame < sfinfo.frames) {
|
while (frame < sfinfo.frames) {
|
||||||
|
|
||||||
int count = -1;
|
int count = -1;
|
||||||
|
if ((count = sf_readf_float(sndfile, ibuf, bs)) <= 0) break;
|
||||||
if ((count = sf_readf_float(sndfile, fbuf, ibs)) <= 0) break;
|
|
||||||
|
|
||||||
for (size_t c = 0; c < channels; ++c) {
|
for (size_t c = 0; c < channels; ++c) {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
float value = fbuf[i * channels + c];
|
cbuf[c][i] = ibuf[i * channels + c];
|
||||||
ibuf[c][i] = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool final = (frame + ibs >= sfinfo.frames);
|
bool final = (frame + bs >= sfinfo.frames);
|
||||||
|
|
||||||
ts.study(ibuf, count, final);
|
ts.study(cbuf, count, final);
|
||||||
|
|
||||||
int p = int((double(frame) * 100.0) / sfinfo.frames);
|
int p = int((double(frame) * 100.0) / sfinfo.frames);
|
||||||
if (p > percent || frame == 0) {
|
if (p > percent || frame == 0) {
|
||||||
@@ -498,7 +621,7 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frame += ibs;
|
frame += bs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
@@ -511,67 +634,172 @@ int main(int argc, char **argv)
|
|||||||
frame = 0;
|
frame = 0;
|
||||||
percent = 0;
|
percent = 0;
|
||||||
|
|
||||||
if (!mapping.empty()) {
|
if (!timeMap.empty()) {
|
||||||
ts.setKeyFrameMap(mapping);
|
ts.setKeyFrameMap(timeMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t countIn = 0, countOut = 0;
|
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) {
|
while (frame < sfinfo.frames) {
|
||||||
|
|
||||||
int count = -1;
|
thisBlockSize = bs;
|
||||||
|
|
||||||
if ((count = sf_readf_float(sndfile, fbuf, ibs)) < 0) break;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = -1;
|
||||||
|
if ((count = sf_readf_float(sndfile, ibuf, thisBlockSize)) < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
countIn += count;
|
countIn += count;
|
||||||
|
|
||||||
for (size_t c = 0; c < channels; ++c) {
|
for (size_t c = 0; c < channels; ++c) {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
float value = fbuf[i * channels + c];
|
cbuf[c][i] = ibuf[i * channels + c];
|
||||||
ibuf[c][i] = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool final = (frame + ibs >= sfinfo.frames);
|
bool final = (frame + thisBlockSize >= sfinfo.frames);
|
||||||
|
|
||||||
if (debug > 2) {
|
if (debug > 2) {
|
||||||
cerr << "count = " << count << ", ibs = " << ibs << ", frame = " << frame << ", frames = " << sfinfo.frames << ", final = " << final << endl;
|
cerr << "count = " << count << ", bs = " << thisBlockSize << ", frame = " << frame << ", frames = " << sfinfo.frames << ", final = " << final << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
ts.process(ibuf, count, final);
|
ts.process(cbuf, count, final);
|
||||||
|
|
||||||
int avail = ts.available();
|
int avail;
|
||||||
if (debug > 1) cerr << "available = " << avail << endl;
|
while ((avail = ts.available()) > 0) {
|
||||||
|
if (debug > 1) {
|
||||||
if (avail > 0) {
|
cerr << "available = " << avail << endl;
|
||||||
float **obf = new float *[channels];
|
|
||||||
for (size_t i = 0; i < channels; ++i) {
|
|
||||||
obf[i] = new float[avail];
|
|
||||||
}
|
}
|
||||||
ts.retrieve(obf, avail);
|
|
||||||
countOut += avail;
|
thisBlockSize = avail;
|
||||||
float *fobf = new float[channels * 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 (size_t c = 0; c < channels; ++c) {
|
||||||
for (int i = 0; i < avail; ++i) {
|
for (int i = 0; i < thisBlockSize; ++i) {
|
||||||
float value = obf[c][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;
|
||||||
if (value < -1.f) value = -1.f;
|
if (value < -1.f) value = -1.f;
|
||||||
fobf[i * channels + c] = value;
|
} else {
|
||||||
|
if (value >= 1.f || value < -1.f) {
|
||||||
|
clipping = true;
|
||||||
|
gain = (0.999f / fabsf(cbuf[c][i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// cout << "fobf mean: ";
|
ibuf[i * channels + c] = value;
|
||||||
// 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];
|
|
||||||
}
|
}
|
||||||
delete[] obf;
|
}
|
||||||
|
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) {
|
if (frame == 0 && !realtime && !quiet) {
|
||||||
@@ -586,53 +814,63 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frame += ibs;
|
frame += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!successful) {
|
||||||
|
sf_seek(sndfile, 0, SEEK_SET);
|
||||||
|
sf_seek(sndfileOut, 0, SEEK_SET);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
cerr << "\r " << endl;
|
cerr << "\r " << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
int avail;
|
int avail;
|
||||||
|
|
||||||
while ((avail = ts.available()) >= 0) {
|
while ((avail = ts.available()) >= 0) {
|
||||||
|
|
||||||
if (debug > 1) {
|
if (debug > 1) {
|
||||||
cerr << "(completing) available = " << avail << endl;
|
cerr << "(completing) available = " << avail << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avail > 0) {
|
if (avail == 0) {
|
||||||
float **obf = new float *[channels];
|
if (realtime ||
|
||||||
for (size_t i = 0; i < channels; ++i) {
|
(options & RubberBandStretcher::OptionThreadingNever)) {
|
||||||
obf[i] = new float[avail];
|
break;
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sf_writef_float(sndfileOut, fobf, avail);
|
|
||||||
delete[] fobf;
|
|
||||||
for (size_t i = 0; i < channels; ++i) {
|
|
||||||
delete[] obf[i];
|
|
||||||
}
|
|
||||||
delete[] obf;
|
|
||||||
} else {
|
} else {
|
||||||
usleep(10000);
|
usleep(10000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[] fbuf;
|
thisBlockSize = avail;
|
||||||
|
if (thisBlockSize > bs) {
|
||||||
|
thisBlockSize = bs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.retrieve(cbuf, thisBlockSize);
|
||||||
|
|
||||||
|
countOut += thisBlockSize;
|
||||||
|
|
||||||
|
for (size_t c = 0; c < channels; ++c) {
|
||||||
|
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;
|
||||||
|
ibuf[i * channels + c] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sf_writef_float(sndfileOut, ibuf, thisBlockSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < channels; ++i) delete[] ibuf[i];
|
|
||||||
delete[] ibuf;
|
delete[] ibuf;
|
||||||
|
|
||||||
|
for (size_t c = 0; c < channels; ++c) {
|
||||||
|
delete[] cbuf[c];
|
||||||
|
}
|
||||||
|
delete[] cbuf;
|
||||||
|
|
||||||
sf_close(sndfile);
|
sf_close(sndfile);
|
||||||
sf_close(sndfileOut);
|
sf_close(sndfileOut);
|
||||||
|
|
||||||
@@ -657,10 +895,7 @@ int main(int argc, char **argv)
|
|||||||
etv.tv_usec -= tv.tv_usec;
|
etv.tv_usec -= tv.tv_usec;
|
||||||
|
|
||||||
double sec = double(etv.tv_sec) + (double(etv.tv_usec) / 1000000.0);
|
double sec = double(etv.tv_sec) + (double(etv.tv_usec) / 1000000.0);
|
||||||
cerr << "elapsed time: " << sec
|
cerr << "elapsed time: " << sec << " sec, in frames/sec: " << countIn/sec << ", out frames/sec: " << countOut/sec << endl;
|
||||||
<< " sec, in frames/sec: " << int64_t(round(countIn/sec))
|
|
||||||
<< ", out frames/sec: " << int64_t(round(countOut/sec))
|
|
||||||
<< endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RubberBand::Profiler::dump();
|
RubberBand::Profiler::dump();
|
||||||
|
|||||||
16
meson.build
16
meson.build
@@ -2,7 +2,7 @@
|
|||||||
project(
|
project(
|
||||||
'Rubber Band Library',
|
'Rubber Band Library',
|
||||||
'c', 'cpp',
|
'c', 'cpp',
|
||||||
version: '1.9.2',
|
version: '2.0.0',
|
||||||
license: 'GPL-2.0-or-later',
|
license: 'GPL-2.0-or-later',
|
||||||
default_options: [
|
default_options: [
|
||||||
# All Rubber Band code is actually C++98, but some compilers no
|
# All Rubber Band code is actually C++98, but some compilers no
|
||||||
@@ -15,7 +15,7 @@ project(
|
|||||||
meson_version: '>= 0.53.0'
|
meson_version: '>= 0.53.0'
|
||||||
)
|
)
|
||||||
|
|
||||||
rubberband_dynamic_library_version = '2.1.4'
|
rubberband_dynamic_library_version = '2.1.5'
|
||||||
|
|
||||||
system = host_machine.system()
|
system = host_machine.system()
|
||||||
architecture = host_machine.cpu_family()
|
architecture = host_machine.cpu_family()
|
||||||
@@ -140,7 +140,7 @@ if resampler == 'auto'
|
|||||||
if samplerate_dep.found()
|
if samplerate_dep.found()
|
||||||
resampler = 'libsamplerate'
|
resampler = 'libsamplerate'
|
||||||
else
|
else
|
||||||
resampler = 'speex'
|
resampler = 'builtin'
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -199,7 +199,13 @@ else
|
|||||||
|
|
||||||
endif # fft
|
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()
|
if samplerate_dep.found()
|
||||||
config_summary += { 'Resampler': 'libsamplerate' }
|
config_summary += { 'Resampler': 'libsamplerate' }
|
||||||
message('For resampler: using libsamplerate')
|
message('For resampler: using libsamplerate')
|
||||||
@@ -441,7 +447,7 @@ if not get_option('no_shared')
|
|||||||
message('Will build Rubber Band Library shared library')
|
message('Will build Rubber Band Library shared library')
|
||||||
rubberband_dynamic = shared_library(
|
rubberband_dynamic = shared_library(
|
||||||
rubberband_dynamic_name,
|
rubberband_dynamic_name,
|
||||||
objects: rubberband_static.extract_all_objects(),
|
objects: rubberband_static.extract_all_objects(recursive: true),
|
||||||
link_args: [
|
link_args: [
|
||||||
arch_flags,
|
arch_flags,
|
||||||
feature_libraries,
|
feature_libraries,
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ option('fft',
|
|||||||
|
|
||||||
option('resampler',
|
option('resampler',
|
||||||
type: 'combo',
|
type: 'combo',
|
||||||
choices: ['auto', 'libsamplerate', 'speex', 'ipp'],
|
choices: ['auto', 'builtin', 'libsamplerate', 'speex', 'ipp'],
|
||||||
value: 'auto',
|
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',
|
option('ipp_path',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ OPTFLAGS := -DNDEBUG -ffast-math -O3 -ftree-vectorize
|
|||||||
|
|
||||||
ARCHFLAGS :=
|
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)
|
CFLAGS := $(ARCHFLAGS) $(OPTFLAGS)
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,47 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
if [ ! -d /Applications ]; then
|
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
|
make -f otherbuilds/Makefile.linux
|
||||||
|
|
||||||
|
echo " *** Linking against static library"
|
||||||
g++ main/main.cpp lib/librubberband.a -I. -Isrc -o test -lsndfile -lsamplerate -lpthread
|
g++ main/main.cpp lib/librubberband.a -I. -Isrc -o test -lsndfile -lsamplerate -lpthread
|
||||||
|
|
||||||
|
echo " *** Running build from Linux-specific Makefile"
|
||||||
./test -V
|
./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
|
else
|
||||||
make -f otherbuilds/Makefile.macos
|
|
||||||
c++ main/main.cpp lib/librubberband.a -I. -Isrc -o test -lsndfile -lsamplerate -framework Accelerate
|
echo " *** Building static library using macOS-specific Makefile"
|
||||||
./test -V
|
|
||||||
make -f otherbuilds/Makefile.macos clean
|
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
|
make -f otherbuilds/Makefile.ios
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#ifndef RUBBERBAND_STRETCHER_H
|
#ifndef RUBBERBAND_STRETCHER_H
|
||||||
#define 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_MAJOR_VERSION 2
|
||||||
#define RUBBERBAND_API_MINOR_VERSION 6
|
#define RUBBERBAND_API_MINOR_VERSION 6
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define RUBBERBAND_VERSION "1.9.2"
|
#define RUBBERBAND_VERSION "2.0.0"
|
||||||
#define RUBBERBAND_API_MAJOR_VERSION 2
|
#define RUBBERBAND_API_MAJOR_VERSION 2
|
||||||
#define RUBBERBAND_API_MINOR_VERSION 6
|
#define RUBBERBAND_API_MINOR_VERSION 6
|
||||||
|
|
||||||
|
|||||||
80
single/RubberBandSingle.cpp
Normal file
80
single/RubberBandSingle.cpp
Normal 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"
|
||||||
|
|
||||||
@@ -41,12 +41,14 @@ StretchCalculator::StretchCalculator(size_t sampleRate,
|
|||||||
m_sampleRate(sampleRate),
|
m_sampleRate(sampleRate),
|
||||||
m_increment(inputIncrement),
|
m_increment(inputIncrement),
|
||||||
m_prevDf(0),
|
m_prevDf(0),
|
||||||
m_divergence(0),
|
|
||||||
m_recovery(0),
|
|
||||||
m_prevRatio(1.0),
|
m_prevRatio(1.0),
|
||||||
|
m_prevTimeRatio(1.0),
|
||||||
m_transientAmnesty(0),
|
m_transientAmnesty(0),
|
||||||
m_debugLevel(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;
|
// std::cerr << "StretchCalculator::StretchCalculator: useHardPeaks = " << useHardPeaks << std::endl;
|
||||||
}
|
}
|
||||||
@@ -318,17 +320,107 @@ StretchCalculator::mapPeaks(std::vector<Peak> &peaks,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int64_t
|
||||||
StretchCalculator::calculateSingle(double ratio,
|
StretchCalculator::expectedOutFrame(int64_t inFrame, double timeRatio)
|
||||||
float df,
|
|
||||||
size_t increment)
|
|
||||||
{
|
{
|
||||||
|
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;
|
if (increment == 0) increment = m_increment;
|
||||||
|
|
||||||
|
int outIncrement = lrint(increment * ratio); // the normal case
|
||||||
bool isTransient = false;
|
bool isTransient = false;
|
||||||
|
|
||||||
// We want to ensure, as close as possible, that the phase reset
|
// 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
|
// In principle, the threshold depends on chunk size: larger chunk
|
||||||
// sizes need higher thresholds. Since chunk size depends on
|
// sizes need higher thresholds. Since chunk size depends on
|
||||||
@@ -340,8 +432,14 @@ StretchCalculator::calculateSingle(double ratio,
|
|||||||
// if (ratio > 1) transientThreshold = 0.25f;
|
// if (ratio > 1) transientThreshold = 0.25f;
|
||||||
|
|
||||||
if (m_useHardPeaks && df > m_prevDf * 1.1f && df > transientThreshold) {
|
if (m_useHardPeaks && df > m_prevDf * 1.1f && df > transientThreshold) {
|
||||||
|
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;
|
isTransient = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_debugLevel > 2) {
|
if (m_debugLevel > 2) {
|
||||||
std::cerr << "df = " << df << ", prevDf = " << m_prevDf
|
std::cerr << "df = " << df << ", prevDf = " << m_prevDf
|
||||||
@@ -350,62 +448,91 @@ StretchCalculator::calculateSingle(double ratio,
|
|||||||
|
|
||||||
m_prevDf = df;
|
m_prevDf = df;
|
||||||
|
|
||||||
bool ratioChanged = (ratio != m_prevRatio);
|
if (m_transientAmnesty > 0) {
|
||||||
m_prevRatio = ratio;
|
if (isTransient) {
|
||||||
|
|
||||||
if (isTransient && m_transientAmnesty == 0) {
|
|
||||||
if (m_debugLevel > 1) {
|
if (m_debugLevel > 1) {
|
||||||
std::cerr << "StretchCalculator::calculateSingle: transient (df " << df << ", threshold " << transientThreshold << ")" << std::endl;
|
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
|
// as in offline mode, 0.05 sec approx min between transients
|
||||||
m_transientAmnesty =
|
m_transientAmnesty =
|
||||||
lrint(ceil(double(m_sampleRate) / (20 * double(increment))));
|
lrint(ceil(double(m_sampleRate) / (20 * double(increment))));
|
||||||
|
|
||||||
m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment);
|
outIncrement = increment;
|
||||||
return -int(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ratioChanged) {
|
int incr = lrint(outIncrement - recovery);
|
||||||
m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment);
|
if (m_debugLevel > 2 || (m_debugLevel > 1 && divergence != 0)) {
|
||||||
|
std::cerr << "divergence = " << divergence << ", recovery = " << recovery << ", incr = " << incr << ", ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_transientAmnesty > 0) --m_transientAmnesty;
|
int minIncr = lrint(increment * ratio * 0.3);
|
||||||
|
int maxIncr = lrint(increment * ratio * 2);
|
||||||
|
|
||||||
int incr = lrint(increment * ratio - m_recovery);
|
if (incr < minIncr) {
|
||||||
if (m_debugLevel > 2 || (m_debugLevel > 1 && m_divergence != 0)) {
|
incr = minIncr;
|
||||||
std::cerr << "divergence = " << m_divergence << ", recovery = " << m_recovery << ", incr = " << incr << ", ";
|
} else if (incr > maxIncr) {
|
||||||
}
|
incr = maxIncr;
|
||||||
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 && divergence != 0)) {
|
||||||
|
std::cerr << "clamped into [" << minIncr << ", " << maxIncr
|
||||||
if (m_debugLevel > 2 || (m_debugLevel > 1 && m_divergence != 0)) {
|
<< "] becomes " << incr << std::endl;
|
||||||
std::cerr << "divdiff = " << divdiff << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double prevDivergence = m_divergence;
|
if (incr < 0) {
|
||||||
m_divergence -= divdiff;
|
std::cerr << "WARNING: internal error: incr < 0 in calculateSingle"
|
||||||
if ((prevDivergence < 0 && m_divergence > 0) ||
|
<< std::endl;
|
||||||
(prevDivergence > 0 && m_divergence < 0)) {
|
outIncrement = 0;
|
||||||
m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment);
|
} else {
|
||||||
|
outIncrement = incr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return incr;
|
if (m_debugLevel > 1) {
|
||||||
|
std::cerr << "StretchCalculator::calculateSingle: returning isTransient = "
|
||||||
|
<< isTransient << ", outIncrement = " << outIncrement
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_inFrameCounter += inIncrement;
|
||||||
|
m_outFrameCounter += outIncrement * effectivePitchRatio;
|
||||||
|
|
||||||
|
if (isTransient) {
|
||||||
|
return -outIncrement;
|
||||||
|
} else {
|
||||||
|
return outIncrement;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
StretchCalculator::reset()
|
StretchCalculator::reset()
|
||||||
{
|
{
|
||||||
m_prevDf = 0;
|
m_prevDf = 0;
|
||||||
m_divergence = 0;
|
|
||||||
m_recovery = 0;
|
|
||||||
m_prevRatio = 1.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_transientAmnesty = 0;
|
||||||
m_keyFrameMap.clear();
|
m_keyFrameMap.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,8 +68,12 @@ public:
|
|||||||
* If increment is non-zero, use it for the input increment for
|
* If increment is non-zero, use it for the input increment for
|
||||||
* this block in preference to m_increment.
|
* this block in preference to m_increment.
|
||||||
*/
|
*/
|
||||||
int calculateSingle(double ratio, float curveValue,
|
int calculateSingle(double timeRatio,
|
||||||
size_t increment = 0);
|
double effectivePitchRatio,
|
||||||
|
float curveValue,
|
||||||
|
size_t increment,
|
||||||
|
size_t analysisWindowSize,
|
||||||
|
size_t synthesisWindowSize);
|
||||||
|
|
||||||
void setUseHardPeaks(bool use) { m_useHardPeaks = use; }
|
void setUseHardPeaks(bool use) { m_useHardPeaks = use; }
|
||||||
|
|
||||||
@@ -104,12 +108,15 @@ protected:
|
|||||||
size_t m_sampleRate;
|
size_t m_sampleRate;
|
||||||
size_t m_increment;
|
size_t m_increment;
|
||||||
float m_prevDf;
|
float m_prevDf;
|
||||||
double m_divergence;
|
double m_prevRatio;
|
||||||
float m_recovery;
|
double m_prevTimeRatio;
|
||||||
float m_prevRatio;
|
|
||||||
int m_transientAmnesty; // only in RT mode; handled differently offline
|
int m_transientAmnesty; // only in RT mode; handled differently offline
|
||||||
int m_debugLevel;
|
int m_debugLevel;
|
||||||
bool m_useHardPeaks;
|
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::map<size_t, size_t> m_keyFrameMap;
|
||||||
std::vector<Peak> m_peaks;
|
std::vector<Peak> m_peaks;
|
||||||
|
|||||||
@@ -27,8 +27,7 @@
|
|||||||
#include "StretcherImpl.h"
|
#include "StretcherImpl.h"
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <atomic>
|
||||||
//#define EXPERIMENT 1
|
|
||||||
|
|
||||||
namespace RubberBand
|
namespace RubberBand
|
||||||
{
|
{
|
||||||
@@ -124,11 +123,11 @@ public:
|
|||||||
|
|
||||||
size_t chunkCount;
|
size_t chunkCount;
|
||||||
size_t inCount;
|
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;
|
size_t outCount;
|
||||||
|
|
||||||
bool draining;
|
std::atomic<bool> draining;
|
||||||
bool outputComplete;
|
std::atomic<bool> outputComplete;
|
||||||
|
|
||||||
FFT *fft;
|
FFT *fft;
|
||||||
std::map<size_t, FFT *> ffts;
|
std::map<size_t, FFT *> ffts;
|
||||||
|
|||||||
@@ -544,8 +544,8 @@ RubberBandStretcher::Impl::calculateSizes()
|
|||||||
// ratio) for any chunk.
|
// ratio) for any chunk.
|
||||||
|
|
||||||
if (m_debugLevel > 0) {
|
if (m_debugLevel > 0) {
|
||||||
cerr << "configure: time ratio = " << m_timeRatio << ", pitch scale = " << m_pitchScale << ", effective ratio = " << getEffectiveRatio() << endl;
|
cerr << "calculateSizes: 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: 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) {
|
if (std::max(m_aWindowSize, m_sWindowSize) > m_maxProcessSize) {
|
||||||
@@ -575,15 +575,17 @@ RubberBandStretcher::Impl::calculateSizes()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_debugLevel > 0) {
|
if (m_debugLevel > 0) {
|
||||||
cerr << "configure: outbuf size = " << m_outbufSize << endl;
|
cerr << "calculateSizes: outbuf size = " << m_outbufSize << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RubberBandStretcher::Impl::configure()
|
RubberBandStretcher::Impl::configure()
|
||||||
{
|
{
|
||||||
// std::cerr << "configure[" << this << "]: realtime = " << m_realtime << ", pitch scale = "
|
if (m_debugLevel > 0) {
|
||||||
// << m_pitchScale << ", channels = " << m_channels << std::endl;
|
std::cerr << "configure[" << this << "]: realtime = " << m_realtime << ", pitch scale = "
|
||||||
|
<< m_pitchScale << ", channels = " << m_channels << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
size_t prevFftSize = m_fftSize;
|
size_t prevFftSize = m_fftSize;
|
||||||
size_t prevAWindowSize = m_aWindowSize;
|
size_t prevAWindowSize = m_aWindowSize;
|
||||||
@@ -674,8 +676,18 @@ RubberBandStretcher::Impl::configure()
|
|||||||
|
|
||||||
Resampler::Parameters params;
|
Resampler::Parameters params;
|
||||||
params.quality = Resampler::FastestTolerable;
|
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.maxBufferSize = 4096 * 16;
|
||||||
params.debugLevel = m_debugLevel;
|
params.debugLevel = (m_debugLevel > 0 ? m_debugLevel-1 : 0);
|
||||||
|
|
||||||
m_channelData[c]->resampler = new Resampler(params, 1);
|
m_channelData[c]->resampler = new Resampler(params, 1);
|
||||||
|
|
||||||
@@ -734,7 +746,7 @@ RubberBandStretcher::Impl::configure()
|
|||||||
|
|
||||||
if (!m_realtime) {
|
if (!m_realtime) {
|
||||||
if (m_debugLevel > 1) {
|
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) {
|
for (size_t c = 0; c < m_channels; ++c) {
|
||||||
m_channelData[c]->reset();
|
m_channelData[c]->reset();
|
||||||
@@ -767,6 +779,8 @@ RubberBandStretcher::Impl::reconfigure()
|
|||||||
|
|
||||||
calculateSizes();
|
calculateSizes();
|
||||||
|
|
||||||
|
bool somethingChanged = false;
|
||||||
|
|
||||||
// There are various allocations in this function, but they should
|
// There are various allocations in this function, but they should
|
||||||
// never happen in normal use -- they just recover from the case
|
// never happen in normal use -- they just recover from the case
|
||||||
// where not all of the things we need were correctly created when
|
// 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_channelData[c]->setSizes(std::max(m_aWindowSize, m_sWindowSize),
|
||||||
m_fftSize);
|
m_fftSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
somethingChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_outbufSize != prevOutbufSize) {
|
if (m_outbufSize != prevOutbufSize) {
|
||||||
for (size_t c = 0; c < m_channels; ++c) {
|
for (size_t c = 0; c < m_channels; ++c) {
|
||||||
m_channelData[c]->setOutbufSize(m_outbufSize);
|
m_channelData[c]->setOutbufSize(m_outbufSize);
|
||||||
}
|
}
|
||||||
|
somethingChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pitchScale != 1.0) {
|
if (m_pitchScale != 1.0) {
|
||||||
@@ -818,8 +835,10 @@ RubberBandStretcher::Impl::reconfigure()
|
|||||||
|
|
||||||
Resampler::Parameters params;
|
Resampler::Parameters params;
|
||||||
params.quality = Resampler::FastestTolerable;
|
params.quality = Resampler::FastestTolerable;
|
||||||
|
params.dynamism = Resampler::RatioOftenChanging;
|
||||||
|
params.ratioChange = Resampler::SmoothRatioChange;
|
||||||
params.maxBufferSize = m_sWindowSize;
|
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);
|
m_channelData[c]->resampler = new Resampler(params, 1);
|
||||||
|
|
||||||
@@ -827,6 +846,8 @@ RubberBandStretcher::Impl::reconfigure()
|
|||||||
lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale));
|
lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale));
|
||||||
if (rbs < m_increment * 16) rbs = m_increment * 16;
|
if (rbs < m_increment * 16) rbs = m_increment * 16;
|
||||||
m_channelData[c]->setResampleBufSize(rbs);
|
m_channelData[c]->setResampleBufSize(rbs);
|
||||||
|
|
||||||
|
somethingChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -836,6 +857,15 @@ RubberBandStretcher::Impl::reconfigure()
|
|||||||
if (m_stretchAudioCurve) {
|
if (m_stretchAudioCurve) {
|
||||||
m_stretchAudioCurve->setFftSize(m_fftSize);
|
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
|
RubberBandStretcher::Impl::getLatency() const
|
||||||
{
|
{
|
||||||
if (!m_realtime) return 0;
|
if (!m_realtime) return 0;
|
||||||
return int((m_aWindowSize/2) / m_pitchScale + 1);
|
return lrint((m_aWindowSize/2) / m_pitchScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1347,12 +1377,12 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (m_debugLevel > 2) {
|
if (m_debugLevel > 1) {
|
||||||
if (!allConsumed) cerr << "process looping" << endl;
|
if (!allConsumed) cerr << "process looping" << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_debugLevel > 2) {
|
if (m_debugLevel > 1) {
|
||||||
cerr << "process returning" << endl;
|
cerr << "process returning" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#ifndef RUBBERBAND_STRETCHERIMPL_H
|
#ifndef RUBBERBAND_STRETCHERIMPL_H
|
||||||
#define RUBBERBAND_STRETCHERIMPL_H
|
#define RUBBERBAND_STRETCHERIMPL_H
|
||||||
|
|
||||||
#include "rubberband/RubberBandStretcher.h"
|
#include "../rubberband/RubberBandStretcher.h"
|
||||||
|
|
||||||
#include "dsp/Window.h"
|
#include "dsp/Window.h"
|
||||||
#include "dsp/SincWindow.h"
|
#include "dsp/SincWindow.h"
|
||||||
|
|||||||
@@ -189,6 +189,8 @@ RubberBandStretcher::Impl::consumeChannel(size_t c,
|
|||||||
|
|
||||||
if (resampling) {
|
if (resampling) {
|
||||||
|
|
||||||
|
Profiler profiler2("RubberBandStretcher::Impl::resample");
|
||||||
|
|
||||||
toWrite = int(ceil(samples / m_pitchScale));
|
toWrite = int(ceil(samples / m_pitchScale));
|
||||||
if (writable < toWrite) {
|
if (writable < toWrite) {
|
||||||
samples = int(floor(writable * m_pitchScale));
|
samples = int(floor(writable * m_pitchScale));
|
||||||
@@ -282,7 +284,7 @@ RubberBandStretcher::Impl::processChunks(size_t c, bool &any, bool &last)
|
|||||||
while (!last) {
|
while (!last) {
|
||||||
|
|
||||||
if (!testInbufReadSpace(c)) {
|
if (!testInbufReadSpace(c)) {
|
||||||
if (m_debugLevel > 2) {
|
if (m_debugLevel > 1) {
|
||||||
cerr << "processChunks: out of input" << endl;
|
cerr << "processChunks: out of input" << endl;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -347,7 +349,7 @@ RubberBandStretcher::Impl::processOneChunk()
|
|||||||
|
|
||||||
for (size_t c = 0; c < m_channels; ++c) {
|
for (size_t c = 0; c < m_channels; ++c) {
|
||||||
if (!testInbufReadSpace(c)) {
|
if (!testInbufReadSpace(c)) {
|
||||||
if (m_debugLevel > 2) {
|
if (m_debugLevel > 1) {
|
||||||
cerr << "processOneChunk: out of input" << endl;
|
cerr << "processOneChunk: out of input" << endl;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -402,7 +404,7 @@ RubberBandStretcher::Impl::testInbufReadSpace(size_t c)
|
|||||||
if (!m_threaded) {
|
if (!m_threaded) {
|
||||||
#endif
|
#endif
|
||||||
if (m_debugLevel > 1) {
|
if (m_debugLevel > 1) {
|
||||||
cerr << "WARNING: RubberBandStretcher: read space < chunk size ("
|
cerr << "Note: RubberBandStretcher: read space < chunk size ("
|
||||||
<< inbuf.getReadSpace() << " < " << m_aWindowSize
|
<< inbuf.getReadSpace() << " < " << m_aWindowSize
|
||||||
<< ") when not all input written, on processChunks for channel " << c << endl;
|
<< ") 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
|
int incr = m_stretchCalculator->calculateSingle
|
||||||
(getEffectiveRatio(), df, m_increment);
|
(m_timeRatio, effectivePitchRatio, df, m_increment,
|
||||||
|
m_aWindowSize, m_sWindowSize);
|
||||||
|
|
||||||
if (m_lastProcessPhaseResetDf.getWriteSpace() > 0) {
|
if (m_lastProcessPhaseResetDf.getWriteSpace() > 0) {
|
||||||
m_lastProcessPhaseResetDf.write(&df, 1);
|
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) &&
|
(m_pitchScale != 1.0 || m_options & OptionPitchHighConsistency) &&
|
||||||
cd.resampler) {
|
cd.resampler) {
|
||||||
|
|
||||||
|
Profiler profiler2("RubberBandStretcher::Impl::resample");
|
||||||
|
|
||||||
size_t reqSize = int(ceil(si / m_pitchScale));
|
size_t reqSize = int(ceil(si / m_pitchScale));
|
||||||
if (reqSize > cd.resamplebufSize) {
|
if (reqSize > cd.resamplebufSize) {
|
||||||
// This shouldn't normally happen -- the buffer is
|
// This shouldn't normally happen -- the buffer is
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include "CompoundAudioCurve.h"
|
#include "CompoundAudioCurve.h"
|
||||||
|
|
||||||
#include "dsp/MovingMedian.h"
|
#include "../dsp/MovingMedian.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
|||||||
@@ -24,10 +24,9 @@
|
|||||||
#ifndef RUBBERBAND_COMPOUND_AUDIO_CURVE_H
|
#ifndef RUBBERBAND_COMPOUND_AUDIO_CURVE_H
|
||||||
#define RUBBERBAND_COMPOUND_AUDIO_CURVE_H
|
#define RUBBERBAND_COMPOUND_AUDIO_CURVE_H
|
||||||
|
|
||||||
#include "dsp/AudioCurveCalculator.h"
|
|
||||||
#include "PercussiveAudioCurve.h"
|
#include "PercussiveAudioCurve.h"
|
||||||
#include "HighFrequencyAudioCurve.h"
|
#include "HighFrequencyAudioCurve.h"
|
||||||
#include "dsp/SampleFilter.h"
|
#include "../dsp/SampleFilter.h"
|
||||||
|
|
||||||
namespace RubberBand
|
namespace RubberBand
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#ifndef RUBBERBAND_CONSTANT_AUDIO_CURVE_H
|
#ifndef RUBBERBAND_CONSTANT_AUDIO_CURVE_H
|
||||||
#define RUBBERBAND_CONSTANT_AUDIO_CURVE_H
|
#define RUBBERBAND_CONSTANT_AUDIO_CURVE_H
|
||||||
|
|
||||||
#include "dsp/AudioCurveCalculator.h"
|
#include "../dsp/AudioCurveCalculator.h"
|
||||||
|
|
||||||
namespace RubberBand
|
namespace RubberBand
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#ifndef RUBBERBAND_HIGHFREQUENCY_AUDIO_CURVE_H
|
#ifndef RUBBERBAND_HIGHFREQUENCY_AUDIO_CURVE_H
|
||||||
#define RUBBERBAND_HIGHFREQUENCY_AUDIO_CURVE_H
|
#define RUBBERBAND_HIGHFREQUENCY_AUDIO_CURVE_H
|
||||||
|
|
||||||
#include "dsp/AudioCurveCalculator.h"
|
#include "../dsp/AudioCurveCalculator.h"
|
||||||
|
|
||||||
namespace RubberBand
|
namespace RubberBand
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
|
|
||||||
#include "PercussiveAudioCurve.h"
|
#include "PercussiveAudioCurve.h"
|
||||||
|
|
||||||
#include "system/Allocators.h"
|
#include "../system/Allocators.h"
|
||||||
#include "system/VectorOps.h"
|
#include "../system/VectorOps.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#ifndef RUBBERBAND_PERCUSSIVE_AUDIO_CURVE_H
|
#ifndef RUBBERBAND_PERCUSSIVE_AUDIO_CURVE_H
|
||||||
#define RUBBERBAND_PERCUSSIVE_AUDIO_CURVE_H
|
#define RUBBERBAND_PERCUSSIVE_AUDIO_CURVE_H
|
||||||
|
|
||||||
#include "dsp/AudioCurveCalculator.h"
|
#include "../dsp/AudioCurveCalculator.h"
|
||||||
|
|
||||||
namespace RubberBand
|
namespace RubberBand
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#ifndef RUBBERBAND_SILENT_AUDIO_CURVE_H
|
#ifndef RUBBERBAND_SILENT_AUDIO_CURVE_H
|
||||||
#define RUBBERBAND_SILENT_AUDIO_CURVE_H
|
#define RUBBERBAND_SILENT_AUDIO_CURVE_H
|
||||||
|
|
||||||
#include "dsp/AudioCurveCalculator.h"
|
#include "../dsp/AudioCurveCalculator.h"
|
||||||
|
|
||||||
namespace RubberBand
|
namespace RubberBand
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
|
|
||||||
#include "SpectralDifferenceAudioCurve.h"
|
#include "SpectralDifferenceAudioCurve.h"
|
||||||
|
|
||||||
#include "system/Allocators.h"
|
#include "../system/Allocators.h"
|
||||||
#include "system/VectorOps.h"
|
#include "../system/VectorOps.h"
|
||||||
|
|
||||||
namespace RubberBand
|
namespace RubberBand
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
#ifndef RUBBERBAND_SPECTRALDIFFERENCE_AUDIO_CURVE_H
|
#ifndef RUBBERBAND_SPECTRALDIFFERENCE_AUDIO_CURVE_H
|
||||||
#define RUBBERBAND_SPECTRALDIFFERENCE_AUDIO_CURVE_H
|
#define RUBBERBAND_SPECTRALDIFFERENCE_AUDIO_CURVE_H
|
||||||
|
|
||||||
#include "dsp/AudioCurveCalculator.h"
|
#include "../dsp/AudioCurveCalculator.h"
|
||||||
#include "dsp/Window.h"
|
#include "../dsp/Window.h"
|
||||||
|
|
||||||
namespace RubberBand
|
namespace RubberBand
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#include "Profiler.h"
|
#include "Profiler.h"
|
||||||
|
|
||||||
#include "system/Thread.h"
|
#include "../system/Thread.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
#ifdef PROFILE_CLOCKS
|
#ifdef PROFILE_CLOCKS
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#else
|
#else
|
||||||
#include "system/sysutils.h"
|
#include "../system/sysutils.h"
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -28,11 +28,13 @@
|
|||||||
|
|
||||||
//#define DEBUG_RINGBUFFER 1
|
//#define DEBUG_RINGBUFFER 1
|
||||||
|
|
||||||
#include "system/sysutils.h"
|
#include "../system/sysutils.h"
|
||||||
#include "system/Allocators.h"
|
#include "../system/Allocators.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
namespace RubberBand {
|
namespace RubberBand {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -175,8 +177,8 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
T *const R__ m_buffer;
|
T *const R__ m_buffer;
|
||||||
int m_writer;
|
std::atomic<int> m_writer;
|
||||||
int m_reader;
|
std::atomic<int> m_reader;
|
||||||
const int m_size;
|
const int m_size;
|
||||||
bool m_mlocked;
|
bool m_mlocked;
|
||||||
|
|
||||||
@@ -244,6 +246,7 @@ RingBuffer<T>::resized(int newSize) const
|
|||||||
{
|
{
|
||||||
RingBuffer<T> *newBuffer = new RingBuffer<T>(newSize);
|
RingBuffer<T> *newBuffer = new RingBuffer<T>(newSize);
|
||||||
|
|
||||||
|
MBARRIER();
|
||||||
int w = m_writer;
|
int w = m_writer;
|
||||||
int r = m_reader;
|
int r = m_reader;
|
||||||
|
|
||||||
@@ -273,7 +276,8 @@ RingBuffer<T>::reset()
|
|||||||
std::cerr << "RingBuffer<T>[" << this << "]::reset" << std::endl;
|
std::cerr << "RingBuffer<T>[" << this << "]::reset" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_reader = m_writer;
|
int r = m_reader;
|
||||||
|
m_writer = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -302,7 +306,6 @@ RingBuffer<T>::read(S *const R__ destination, int n)
|
|||||||
if (n > available) {
|
if (n > available) {
|
||||||
std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only "
|
std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only "
|
||||||
<< available << " available" << std::endl;
|
<< available << " available" << std::endl;
|
||||||
//!!! v_zero(destination + available, n - available);
|
|
||||||
n = available;
|
n = available;
|
||||||
}
|
}
|
||||||
if (n == 0) return n;
|
if (n == 0) return n;
|
||||||
@@ -320,7 +323,6 @@ RingBuffer<T>::read(S *const R__ destination, int n)
|
|||||||
r += n;
|
r += n;
|
||||||
while (r >= m_size) r -= m_size;
|
while (r >= m_size) r -= m_size;
|
||||||
|
|
||||||
MBARRIER();
|
|
||||||
m_reader = r;
|
m_reader = r;
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
@@ -355,7 +357,6 @@ RingBuffer<T>::readAdding(S *const R__ destination, int n)
|
|||||||
r += n;
|
r += n;
|
||||||
while (r >= m_size) r -= m_size;
|
while (r >= m_size) r -= m_size;
|
||||||
|
|
||||||
MBARRIER();
|
|
||||||
m_reader = r;
|
m_reader = r;
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
@@ -377,7 +378,6 @@ RingBuffer<T>::readOne()
|
|||||||
T value = m_buffer[r];
|
T value = m_buffer[r];
|
||||||
if (++r == m_size) r = 0;
|
if (++r == m_size) r = 0;
|
||||||
|
|
||||||
MBARRIER();
|
|
||||||
m_reader = r;
|
m_reader = r;
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@@ -447,7 +447,6 @@ RingBuffer<T>::skip(int n)
|
|||||||
r += n;
|
r += n;
|
||||||
while (r >= m_size) r -= m_size;
|
while (r >= m_size) r -= m_size;
|
||||||
|
|
||||||
// No memory barrier required, because we didn't read any data
|
|
||||||
m_reader = r;
|
m_reader = r;
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
|
|||||||
@@ -33,9 +33,9 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "system/Thread.h"
|
#include "../system/Thread.h"
|
||||||
#include "system/sysutils.h"
|
#include "../system/sysutils.h"
|
||||||
#include "system/Allocators.h"
|
#include "../system/Allocators.h"
|
||||||
|
|
||||||
//#define DEBUG_SCAVENGER 1
|
//#define DEBUG_SCAVENGER 1
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,7 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "../system/sysutils.h"
|
||||||
#include "system/sysutils.h"
|
|
||||||
|
|
||||||
namespace RubberBand
|
namespace RubberBand
|
||||||
{
|
{
|
||||||
|
|||||||
646
src/dsp/BQResampler.cpp
Normal file
646
src/dsp/BQResampler.cpp
Normal 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
167
src/dsp/BQResampler.h
Normal 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
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "FFT.h"
|
#include "FFT.h"
|
||||||
#include "system/Thread.h"
|
#include "../system/Thread.h"
|
||||||
#include "base/Profiler.h"
|
#include "../base/Profiler.h"
|
||||||
#include "system/Allocators.h"
|
#include "../system/Allocators.h"
|
||||||
#include "system/VectorOps.h"
|
#include "../system/VectorOps.h"
|
||||||
#include "system/VectorOpsComplex.h"
|
#include "../system/VectorOpsComplex.h"
|
||||||
|
|
||||||
// Define USE_FFTW_WISDOM if you are defining HAVE_FFTW3 and you want
|
// Define USE_FFTW_WISDOM if you are defining HAVE_FFTW3 and you want
|
||||||
// to use FFTW_MEASURE mode with persistent wisdom files. This will
|
// to use FFTW_MEASURE mode with persistent wisdom files. This will
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#ifndef RUBBERBAND_FFT_H
|
#ifndef RUBBERBAND_FFT_H
|
||||||
#define RUBBERBAND_FFT_H
|
#define RUBBERBAND_FFT_H
|
||||||
|
|
||||||
#include "system/sysutils.h"
|
#include "../system/sysutils.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
#include "SampleFilter.h"
|
#include "SampleFilter.h"
|
||||||
|
|
||||||
#include "system/Allocators.h"
|
#include "../system/Allocators.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Resampler.h"
|
#include "Resampler.h"
|
||||||
#include "base/Profiler.h"
|
|
||||||
|
#include "../system/Allocators.h"
|
||||||
|
#include "../system/VectorOps.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -30,9 +32,6 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "system/Allocators.h"
|
|
||||||
#include "system/VectorOps.h"
|
|
||||||
|
|
||||||
#ifdef HAVE_IPP
|
#ifdef HAVE_IPP
|
||||||
#include <ippversion.h>
|
#include <ippversion.h>
|
||||||
#if (IPP_VERSION_MAJOR < 7)
|
#if (IPP_VERSION_MAJOR < 7)
|
||||||
@@ -42,6 +41,10 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SAMPLERATE
|
||||||
|
#define HAVE_LIBSAMPLERATE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_LIBSAMPLERATE
|
#ifdef HAVE_LIBSAMPLERATE
|
||||||
#include <samplerate.h>
|
#include <samplerate.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -51,18 +54,26 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SPEEX
|
#ifdef USE_SPEEX
|
||||||
#include "speex/speex_resampler.h"
|
#include "../speex/speex_resampler.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_BQRESAMPLER
|
||||||
|
#include "BQResampler.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef HAVE_IPP
|
#ifndef HAVE_IPP
|
||||||
#ifndef HAVE_LIBSAMPLERATE
|
#ifndef HAVE_LIBSAMPLERATE
|
||||||
#ifndef HAVE_LIBRESAMPLE
|
#ifndef HAVE_LIBRESAMPLE
|
||||||
#ifndef USE_SPEEX
|
#ifndef USE_SPEEX
|
||||||
|
#ifndef USE_BQRESAMPLER
|
||||||
#error No resampler implementation selected!
|
#error No resampler implementation selected!
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BQ_R__ R__
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@@ -73,21 +84,22 @@ class Resampler::Impl
|
|||||||
public:
|
public:
|
||||||
virtual ~Impl() { }
|
virtual ~Impl() { }
|
||||||
|
|
||||||
virtual int resample(float *const R__ *const R__ out,
|
virtual int resample(float *const BQ_R__ *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ *const R__ in,
|
const float *const BQ_R__ *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final) = 0;
|
bool final) = 0;
|
||||||
|
|
||||||
virtual int resampleInterleaved(float *const R__ out,
|
virtual int resampleInterleaved(float *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ in,
|
const float *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final) = 0;
|
bool final) = 0;
|
||||||
|
|
||||||
virtual int getChannelCount() const = 0;
|
virtual int getChannelCount() const = 0;
|
||||||
|
virtual double getEffectiveRatio(double ratio) const = 0;
|
||||||
|
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
};
|
};
|
||||||
@@ -99,25 +111,27 @@ namespace Resamplers {
|
|||||||
class D_IPP : public Resampler::Impl
|
class D_IPP : public Resampler::Impl
|
||||||
{
|
{
|
||||||
public:
|
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);
|
int maxBufferSize, int debugLevel);
|
||||||
~D_IPP();
|
~D_IPP();
|
||||||
|
|
||||||
int resample(float *const R__ *const R__ out,
|
int resample(float *const BQ_R__ *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ *const R__ in,
|
const float *const BQ_R__ *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final);
|
bool final);
|
||||||
|
|
||||||
int resampleInterleaved(float *const R__ out,
|
int resampleInterleaved(float *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ in,
|
const float *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final = false);
|
bool final = false);
|
||||||
|
|
||||||
int getChannelCount() const { return m_channels; }
|
int getChannelCount() const { return m_channels; }
|
||||||
|
double getEffectiveRatio(double ratio) const { return ratio; }
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
@@ -144,6 +158,7 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
D_IPP::D_IPP(Resampler::Quality /* quality */,
|
D_IPP::D_IPP(Resampler::Quality /* quality */,
|
||||||
|
Resampler::RatioChange /* ratioChange */,
|
||||||
int channels, double initialSampleRate,
|
int channels, double initialSampleRate,
|
||||||
int maxBufferSize, int debugLevel) :
|
int maxBufferSize, int debugLevel) :
|
||||||
m_state(0),
|
m_state(0),
|
||||||
@@ -152,7 +167,7 @@ D_IPP::D_IPP(Resampler::Quality /* quality */,
|
|||||||
m_debugLevel(debugLevel)
|
m_debugLevel(debugLevel)
|
||||||
{
|
{
|
||||||
if (m_debugLevel > 0) {
|
if (m_debugLevel > 0) {
|
||||||
cerr << "Resampler::Resampler: using IPP implementation" << endl;
|
cerr << "Resampler::Resampler: using implementation: IPP" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_window = 32;
|
m_window = 32;
|
||||||
@@ -190,7 +205,7 @@ D_IPP::D_IPP(Resampler::Quality /* quality */,
|
|||||||
setBufSize(maxBufferSize + m_history);
|
setBufSize(maxBufferSize + m_history);
|
||||||
|
|
||||||
if (m_debugLevel > 1) {
|
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;
|
int specSize = 0;
|
||||||
@@ -215,17 +230,12 @@ D_IPP::D_IPP(Resampler::Quality /* quality */,
|
|||||||
m_state[c],
|
m_state[c],
|
||||||
hint);
|
hint);
|
||||||
|
|
||||||
if (m_debugLevel > 1) {
|
|
||||||
cerr << "D_IPP: Resampler state size = " << specSize << ", allocated at "
|
|
||||||
<< m_state[c] << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_lastread[c] = m_history;
|
m_lastread[c] = m_history;
|
||||||
m_time[c] = m_history;
|
m_time[c] = m_history;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_debugLevel > 1) {
|
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_debugLevel > 1) {
|
||||||
if (m_bufsize > 0) {
|
if (m_bufsize > 0) {
|
||||||
cerr << "D_IPP: resize bufsize " << m_bufsize << " -> ";
|
cerr << "resize bufsize " << m_bufsize << " -> ";
|
||||||
} else {
|
} 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;
|
int n1 = m_bufsize + m_history + 2;
|
||||||
|
|
||||||
if (m_debugLevel > 1) {
|
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));
|
int n2 = (int)lrintf(ceil((m_bufsize - m_history) * m_factor + 2));
|
||||||
|
|
||||||
if (m_debugLevel > 1) {
|
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
|
m_inbuf = reallocate_and_zero_extend_channels
|
||||||
@@ -280,27 +290,12 @@ D_IPP::setBufSize(int sz)
|
|||||||
|
|
||||||
m_inbufsz = n1;
|
m_inbufsz = n1;
|
||||||
m_outbufsz = n2;
|
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
|
int
|
||||||
D_IPP::resample(float *const R__ *const R__ out,
|
D_IPP::resample(float *const BQ_R__ *const BQ_R__ out,
|
||||||
int outspace,
|
int outspace,
|
||||||
const float *const R__ *const R__ in,
|
const float *const BQ_R__ *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final)
|
bool final)
|
||||||
@@ -311,7 +306,7 @@ D_IPP::resample(float *const R__ *const R__ out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_debugLevel > 2) {
|
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) {
|
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) {
|
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);
|
int got = doResample(outspace, ratio, final);
|
||||||
@@ -341,9 +336,9 @@ D_IPP::resample(float *const R__ *const R__ out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
D_IPP::resampleInterleaved(float *const R__ out,
|
D_IPP::resampleInterleaved(float *const BQ_R__ out,
|
||||||
int outspace,
|
int outspace,
|
||||||
const float *const R__ in,
|
const float *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final)
|
bool final)
|
||||||
@@ -354,7 +349,7 @@ D_IPP::resampleInterleaved(float *const R__ out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_debugLevel > 2) {
|
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) {
|
for (int c = 0; c < m_channels; ++c) {
|
||||||
@@ -371,7 +366,7 @@ D_IPP::resampleInterleaved(float *const R__ out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_debugLevel > 2) {
|
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;
|
<< 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]);
|
int n = m_lastread[c] - m_history - int(m_time[c]);
|
||||||
|
|
||||||
if (c == 0 && m_debugLevel > 2) {
|
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 = "
|
<< m_history << ", time = " << m_time[c] << ", therefore n = "
|
||||||
<< n << endl;
|
<< n << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n <= 0) {
|
if (n <= 0) {
|
||||||
if (c == 0 && m_debugLevel > 1) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c == 0 && m_debugLevel > 2) {
|
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
|
// 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));
|
int limit = int(floor(outspace / ratio));
|
||||||
if (n > limit) {
|
if (n > limit) {
|
||||||
if (c == 0 && m_debugLevel > 1) {
|
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"
|
<< " to avoid overrunning " << outspace << " at output"
|
||||||
<< endl;
|
<< endl;
|
||||||
}
|
}
|
||||||
@@ -435,12 +430,12 @@ D_IPP::doResample(int outspace, double ratio, bool final)
|
|||||||
int moveFrom = t - m_history;
|
int moveFrom = t - m_history;
|
||||||
|
|
||||||
if (c == 0 && m_debugLevel > 2) {
|
if (c == 0 && m_debugLevel > 2) {
|
||||||
cerr << "D_IPP: converted " << n << " samples to " << outcount
|
cerr << "converted " << n << " samples to " << outcount
|
||||||
<< " (nb outbufsz = " << m_outbufsz
|
<< " (nb outbufsz = " << m_outbufsz
|
||||||
<< "), time advanced to " << m_time[c] << endl;
|
<< "), 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;
|
<< 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
|
<< " unconverted samples back from index " << moveFrom
|
||||||
<< " to 0" << endl;
|
<< " to 0" << endl;
|
||||||
}
|
}
|
||||||
@@ -450,7 +445,7 @@ D_IPP::doResample(int outspace, double ratio, bool final)
|
|||||||
moveFrom = m_lastread[c];
|
moveFrom = m_lastread[c];
|
||||||
|
|
||||||
if (c == 0 && m_debugLevel > 2) {
|
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;
|
<< "not actually moving any" << endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -464,7 +459,7 @@ D_IPP::doResample(int outspace, double ratio, bool final)
|
|||||||
m_time[c] -= moveFrom;
|
m_time[c] -= moveFrom;
|
||||||
|
|
||||||
if (c == 0 && m_debugLevel > 2) {
|
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]
|
<< ", time reduced to " << m_time[c]
|
||||||
<< endl;
|
<< endl;
|
||||||
}
|
}
|
||||||
@@ -483,7 +478,7 @@ D_IPP::doResample(int outspace, double ratio, bool final)
|
|||||||
int additionalcount = 0;
|
int additionalcount = 0;
|
||||||
|
|
||||||
if (c == 0 && m_debugLevel > 2) {
|
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;
|
<< " 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) {
|
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]);
|
int nAdditional = m_lastread[c] - int(m_time[c]);
|
||||||
|
|
||||||
if (n + nAdditional > limit) {
|
if (n + nAdditional > limit) {
|
||||||
if (c == 0 && m_debugLevel > 1) {
|
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 " << (limit - n)
|
||||||
<< " to avoid overrunning " << outspace << " at output"
|
<< " to avoid overrunning " << outspace << " at output"
|
||||||
<< endl;
|
<< endl;
|
||||||
@@ -517,9 +512,9 @@ D_IPP::doResample(int outspace, double ratio, bool final)
|
|||||||
m_state[c]);
|
m_state[c]);
|
||||||
|
|
||||||
if (c == 0 && m_debugLevel > 2) {
|
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;
|
<< ", 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) {
|
if (c == 0) {
|
||||||
@@ -529,7 +524,7 @@ D_IPP::doResample(int outspace, double ratio, bool final)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_debugLevel > 2) {
|
if (m_debugLevel > 2) {
|
||||||
cerr << "D_IPP: returning " << outcount << " samples" << endl;
|
cerr << "returning " << outcount << " samples" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return outcount;
|
return outcount;
|
||||||
@@ -548,25 +543,27 @@ D_IPP::reset()
|
|||||||
class D_SRC : public Resampler::Impl
|
class D_SRC : public Resampler::Impl
|
||||||
{
|
{
|
||||||
public:
|
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);
|
int maxBufferSize, int m_debugLevel);
|
||||||
~D_SRC();
|
~D_SRC();
|
||||||
|
|
||||||
int resample(float *const R__ *const R__ out,
|
int resample(float *const BQ_R__ *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ *const R__ in,
|
const float *const BQ_R__ *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final);
|
bool final);
|
||||||
|
|
||||||
int resampleInterleaved(float *const R__ out,
|
int resampleInterleaved(float *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ in,
|
const float *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final = false);
|
bool final = false);
|
||||||
|
|
||||||
int getChannelCount() const { return m_channels; }
|
int getChannelCount() const { return m_channels; }
|
||||||
|
double getEffectiveRatio(double ratio) const { return ratio; }
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
@@ -579,11 +576,12 @@ protected:
|
|||||||
int m_ioutsize;
|
int m_ioutsize;
|
||||||
double m_prevRatio;
|
double m_prevRatio;
|
||||||
bool m_ratioUnset;
|
bool m_ratioUnset;
|
||||||
|
bool m_smoothRatios;
|
||||||
int m_debugLevel;
|
int m_debugLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
D_SRC::D_SRC(Resampler::Quality quality, int channels, double,
|
D_SRC::D_SRC(Resampler::Quality quality, Resampler::RatioChange ratioChange,
|
||||||
int maxBufferSize, int debugLevel) :
|
int channels, double, int maxBufferSize, int debugLevel) :
|
||||||
m_src(0),
|
m_src(0),
|
||||||
m_iin(0),
|
m_iin(0),
|
||||||
m_iout(0),
|
m_iout(0),
|
||||||
@@ -592,17 +590,26 @@ D_SRC::D_SRC(Resampler::Quality quality, int channels, double,
|
|||||||
m_ioutsize(0),
|
m_ioutsize(0),
|
||||||
m_prevRatio(1.0),
|
m_prevRatio(1.0),
|
||||||
m_ratioUnset(true),
|
m_ratioUnset(true),
|
||||||
|
m_smoothRatios(ratioChange == Resampler::SmoothRatioChange),
|
||||||
m_debugLevel(debugLevel)
|
m_debugLevel(debugLevel)
|
||||||
{
|
{
|
||||||
if (m_debugLevel > 0) {
|
if (m_debugLevel > 0) {
|
||||||
cerr << "Resampler::Resampler: using libsamplerate implementation"
|
cerr << "Resampler::Resampler: using implementation: libsamplerate"
|
||||||
<< endl;
|
<< 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;
|
int err = 0;
|
||||||
m_src = src_new(quality == Resampler::Best ? SRC_SINC_BEST_QUALITY :
|
m_src = src_new(quality == Resampler::Best ? SRC_SINC_BEST_QUALITY :
|
||||||
quality == Resampler::Fastest ? SRC_LINEAR :
|
quality == Resampler::Fastest ? SRC_SINC_FASTEST :
|
||||||
SRC_SINC_FASTEST,
|
SRC_SINC_MEDIUM_QUALITY,
|
||||||
channels, &err);
|
channels, &err);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -611,6 +618,13 @@ D_SRC::D_SRC(Resampler::Quality quality, int channels, double,
|
|||||||
#ifndef NO_EXCEPTIONS
|
#ifndef NO_EXCEPTIONS
|
||||||
throw Resampler::ImplementationError;
|
throw Resampler::ImplementationError;
|
||||||
#endif
|
#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) {
|
if (maxBufferSize > 0 && m_channels > 1) {
|
||||||
@@ -631,9 +645,9 @@ D_SRC::~D_SRC()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
D_SRC::resample(float *const R__ *const R__ out,
|
D_SRC::resample(float *const BQ_R__ *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ *const R__ in,
|
const float *const BQ_R__ *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final)
|
bool final)
|
||||||
@@ -661,9 +675,9 @@ D_SRC::resample(float *const R__ *const R__ out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
D_SRC::resampleInterleaved(float *const R__ out,
|
D_SRC::resampleInterleaved(float *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ in,
|
const float *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final)
|
bool final)
|
||||||
@@ -682,7 +696,7 @@ D_SRC::resampleInterleaved(float *const R__ out,
|
|||||||
outcount = int(ceil(incount * ratio) + 5);
|
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
|
// The first time we set a ratio, we want to do it directly
|
||||||
src_set_ratio(m_src, ratio);
|
src_set_ratio(m_src, ratio);
|
||||||
@@ -724,7 +738,6 @@ D_SRC::resampleInterleaved(float *const R__ out,
|
|||||||
|
|
||||||
data.input_frames = incount;
|
data.input_frames = incount;
|
||||||
data.output_frames = outcount;
|
data.output_frames = outcount;
|
||||||
|
|
||||||
data.src_ratio = ratio;
|
data.src_ratio = ratio;
|
||||||
data.end_of_input = (final ? 1 : 0);
|
data.end_of_input = (final ? 1 : 0);
|
||||||
|
|
||||||
@@ -755,25 +768,27 @@ D_SRC::reset()
|
|||||||
class D_Resample : public Resampler::Impl
|
class D_Resample : public Resampler::Impl
|
||||||
{
|
{
|
||||||
public:
|
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);
|
int maxBufferSize, int m_debugLevel);
|
||||||
~D_Resample();
|
~D_Resample();
|
||||||
|
|
||||||
int resample(float *const R__ *const R__ out,
|
int resample(float *const BQ_R__ *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ *const R__ in,
|
const float *const BQ_R__ *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final);
|
bool final);
|
||||||
|
|
||||||
int resampleInterleaved(float *const R__ out,
|
int resampleInterleaved(float *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ in,
|
const float *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final);
|
bool final);
|
||||||
|
|
||||||
int getChannelCount() const { return m_channels; }
|
int getChannelCount() const { return m_channels; }
|
||||||
|
double getEffectiveRatio(double ratio) const { return ratio; }
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
@@ -799,7 +814,7 @@ D_Resample::D_Resample(Resampler::Quality quality,
|
|||||||
m_debugLevel(debugLevel)
|
m_debugLevel(debugLevel)
|
||||||
{
|
{
|
||||||
if (m_debugLevel > 0) {
|
if (m_debugLevel > 0) {
|
||||||
cerr << "Resampler::Resampler: using libresample implementation"
|
cerr << "Resampler::Resampler: using implementation: libresample"
|
||||||
<< endl;
|
<< endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -836,9 +851,9 @@ D_Resample::~D_Resample()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
D_Resample::resample(float *const R__ *const R__ out,
|
D_Resample::resample(float *const BQ_R__ *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ *const R__ in,
|
const float *const BQ_R__ *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final)
|
bool final)
|
||||||
@@ -895,9 +910,9 @@ D_Resample::resample(float *const R__ *const R__ out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
D_Resample::resampleInterleaved(float *const R__ out,
|
D_Resample::resampleInterleaved(float *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ in,
|
const float *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final)
|
bool final)
|
||||||
@@ -937,30 +952,186 @@ D_Resample::reset()
|
|||||||
|
|
||||||
#endif /* HAVE_LIBRESAMPLE */
|
#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
|
#ifdef USE_SPEEX
|
||||||
|
|
||||||
class D_Speex : public Resampler::Impl
|
class D_Speex : public Resampler::Impl
|
||||||
{
|
{
|
||||||
public:
|
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);
|
int maxBufferSize, int debugLevel);
|
||||||
~D_Speex();
|
~D_Speex();
|
||||||
|
|
||||||
int resample(float *const R__ *const R__ out,
|
int resample(float *const BQ_R__ *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ *const R__ in,
|
const float *const BQ_R__ *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final);
|
bool final);
|
||||||
|
|
||||||
int resampleInterleaved(float *const R__ out,
|
int resampleInterleaved(float *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ in,
|
const float *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final = false);
|
bool final = false);
|
||||||
|
|
||||||
int getChannelCount() const { return m_channels; }
|
int getChannelCount() const { return m_channels; }
|
||||||
|
double getEffectiveRatio(double ratio) const { return ratio; }
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
@@ -982,7 +1153,7 @@ protected:
|
|||||||
double ratio, bool final);
|
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 channels, double initialSampleRate,
|
||||||
int maxBufferSize, int debugLevel) :
|
int maxBufferSize, int debugLevel) :
|
||||||
m_resampler(0),
|
m_resampler(0),
|
||||||
@@ -1000,7 +1171,7 @@ D_Speex::D_Speex(Resampler::Quality quality,
|
|||||||
quality == Resampler::Fastest ? 0 : 4);
|
quality == Resampler::Fastest ? 0 : 4);
|
||||||
|
|
||||||
if (m_debugLevel > 0) {
|
if (m_debugLevel > 0) {
|
||||||
cerr << "Resampler::Resampler: using Speex implementation with q = "
|
cerr << "Resampler::Resampler: using implementation: Speex with q = "
|
||||||
<< q << endl;
|
<< q << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1093,9 +1264,9 @@ D_Speex::setRatio(double ratio)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
D_Speex::resample(float *const R__ *const R__ out,
|
D_Speex::resample(float *const BQ_R__ *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ *const R__ in,
|
const float *const BQ_R__ *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final)
|
bool final)
|
||||||
@@ -1136,9 +1307,9 @@ D_Speex::resample(float *const R__ *const R__ out,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
D_Speex::resampleInterleaved(float *const R__ out,
|
D_Speex::resampleInterleaved(float *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ in,
|
const float *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final)
|
bool final)
|
||||||
@@ -1236,6 +1407,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
#ifdef HAVE_LIBRESAMPLE
|
#ifdef HAVE_LIBRESAMPLE
|
||||||
m_method = 3;
|
m_method = 3;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BQRESAMPLER
|
||||||
|
m_method = 4;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_LIBSAMPLERATE
|
#ifdef HAVE_LIBSAMPLERATE
|
||||||
m_method = 1;
|
m_method = 1;
|
||||||
#endif
|
#endif
|
||||||
@@ -1251,6 +1425,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
#ifdef USE_SPEEX
|
#ifdef USE_SPEEX
|
||||||
m_method = 2;
|
m_method = 2;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BQRESAMPLER
|
||||||
|
m_method = 4;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_LIBSAMPLERATE
|
#ifdef HAVE_LIBSAMPLERATE
|
||||||
m_method = 1;
|
m_method = 1;
|
||||||
#endif
|
#endif
|
||||||
@@ -1266,6 +1443,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
#ifdef USE_SPEEX
|
#ifdef USE_SPEEX
|
||||||
m_method = 2;
|
m_method = 2;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BQRESAMPLER
|
||||||
|
m_method = 4;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_LIBSAMPLERATE
|
#ifdef HAVE_LIBSAMPLERATE
|
||||||
m_method = 1;
|
m_method = 1;
|
||||||
#endif
|
#endif
|
||||||
@@ -1281,7 +1461,7 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
case 0:
|
case 0:
|
||||||
#ifdef HAVE_IPP
|
#ifdef HAVE_IPP
|
||||||
d = new Resamplers::D_IPP
|
d = new Resamplers::D_IPP
|
||||||
(params.quality,
|
(params.quality, params.ratioChange,
|
||||||
channels,
|
channels,
|
||||||
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
||||||
#else
|
#else
|
||||||
@@ -1293,7 +1473,7 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
case 1:
|
case 1:
|
||||||
#ifdef HAVE_LIBSAMPLERATE
|
#ifdef HAVE_LIBSAMPLERATE
|
||||||
d = new Resamplers::D_SRC
|
d = new Resamplers::D_SRC
|
||||||
(params.quality,
|
(params.quality, params.ratioChange,
|
||||||
channels,
|
channels,
|
||||||
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
||||||
#else
|
#else
|
||||||
@@ -1305,7 +1485,7 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
case 2:
|
case 2:
|
||||||
#ifdef USE_SPEEX
|
#ifdef USE_SPEEX
|
||||||
d = new Resamplers::D_Speex
|
d = new Resamplers::D_Speex
|
||||||
(params.quality,
|
(params.quality, params.ratioChange,
|
||||||
channels,
|
channels,
|
||||||
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
||||||
#else
|
#else
|
||||||
@@ -1317,12 +1497,21 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
case 3:
|
case 3:
|
||||||
#ifdef HAVE_LIBRESAMPLE
|
#ifdef HAVE_LIBRESAMPLE
|
||||||
d = new Resamplers::D_Resample
|
d = new Resamplers::D_Resample
|
||||||
(params.quality,
|
(params.quality, params.ratioChange,
|
||||||
channels,
|
channels,
|
||||||
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
params.initialSampleRate, params.maxBufferSize, params.debugLevel);
|
||||||
#else
|
#else
|
||||||
cerr << "Resampler::Resampler: No implementation available!" << endl;
|
cerr << "Resampler::Resampler: No implementation available!" << endl;
|
||||||
abort();
|
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
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1340,26 +1529,24 @@ Resampler::~Resampler()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
Resampler::resample(float *const R__ *const R__ out,
|
Resampler::resample(float *const BQ_R__ *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ *const R__ in,
|
const float *const BQ_R__ *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final)
|
bool final)
|
||||||
{
|
{
|
||||||
Profiler profiler("Resampler::resample");
|
|
||||||
return d->resample(out, outcount, in, incount, ratio, final);
|
return d->resample(out, outcount, in, incount, ratio, final);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
Resampler::resampleInterleaved(float *const R__ out,
|
Resampler::resampleInterleaved(float *const BQ_R__ out,
|
||||||
int outcount,
|
int outcount,
|
||||||
const float *const R__ in,
|
const float *const BQ_R__ in,
|
||||||
int incount,
|
int incount,
|
||||||
double ratio,
|
double ratio,
|
||||||
bool final)
|
bool final)
|
||||||
{
|
{
|
||||||
Profiler profiler("Resampler::resampleInterleaved");
|
|
||||||
return d->resampleInterleaved(out, outcount, in, incount, ratio, final);
|
return d->resampleInterleaved(out, outcount, in, incount, ratio, final);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1369,6 +1556,12 @@ Resampler::getChannelCount() const
|
|||||||
return d->getChannelCount();
|
return d->getChannelCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
Resampler::getEffectiveRatio(double ratio) const
|
||||||
|
{
|
||||||
|
return d->getEffectiveRatio(ratio);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Resampler::reset()
|
Resampler::reset()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
Rubber Band Library
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
#ifndef RUBBERBAND_RESAMPLER_H
|
#ifndef RUBBERBAND_RESAMPLER_H
|
||||||
#define RUBBERBAND_RESAMPLER_H
|
#define RUBBERBAND_RESAMPLER_H
|
||||||
|
|
||||||
#include "system/sysutils.h"
|
#include "../system/sysutils.h"
|
||||||
|
|
||||||
namespace RubberBand {
|
namespace RubberBand {
|
||||||
|
|
||||||
@@ -32,6 +32,9 @@ class Resampler
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Quality { Best, FastestTolerable, Fastest };
|
enum Quality { Best, FastestTolerable, Fastest };
|
||||||
|
enum Dynamism { RatioOftenChanging, RatioMostlyFixed };
|
||||||
|
enum RatioChange { SmoothRatioChange, SuddenRatioChange };
|
||||||
|
|
||||||
enum Exception { ImplementationError };
|
enum Exception { ImplementationError };
|
||||||
|
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
@@ -41,6 +44,22 @@ public:
|
|||||||
*/
|
*/
|
||||||
Quality quality;
|
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
|
* Rate of expected input prior to resampling: may be used to
|
||||||
* determine the filter bandwidth for the quality setting. If
|
* determine the filter bandwidth for the quality setting. If
|
||||||
@@ -67,6 +86,8 @@ public:
|
|||||||
|
|
||||||
Parameters() :
|
Parameters() :
|
||||||
quality(FastestTolerable),
|
quality(FastestTolerable),
|
||||||
|
dynamism(RatioMostlyFixed),
|
||||||
|
ratioChange(SmoothRatioChange),
|
||||||
initialSampleRate(44100),
|
initialSampleRate(44100),
|
||||||
maxBufferSize(0),
|
maxBufferSize(0),
|
||||||
debugLevel(0) { }
|
debugLevel(0) { }
|
||||||
@@ -125,8 +146,25 @@ public:
|
|||||||
double ratio,
|
double ratio,
|
||||||
bool final = false);
|
bool final = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the channel count provided on construction.
|
||||||
|
*/
|
||||||
int getChannelCount() const;
|
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();
|
void reset();
|
||||||
|
|
||||||
class Impl;
|
class Impl;
|
||||||
|
|||||||
@@ -29,9 +29,9 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "system/sysutils.h"
|
#include "../system/sysutils.h"
|
||||||
#include "system/VectorOps.h"
|
#include "../system/VectorOps.h"
|
||||||
#include "system/Allocators.h"
|
#include "../system/Allocators.h"
|
||||||
|
|
||||||
namespace RubberBand {
|
namespace RubberBand {
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,9 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "system/sysutils.h"
|
#include "../system/sysutils.h"
|
||||||
#include "system/VectorOps.h"
|
#include "../system/VectorOps.h"
|
||||||
#include "system/Allocators.h"
|
#include "../system/Allocators.h"
|
||||||
|
|
||||||
namespace RubberBand {
|
namespace RubberBand {
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
you must obtain a valid commercial licence before doing so.
|
you must obtain a valid commercial licence before doing so.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "rubberband/rubberband-c.h"
|
#include "../rubberband/rubberband-c.h"
|
||||||
#include "rubberband/RubberBandStretcher.h"
|
#include "../rubberband/RubberBandStretcher.h"
|
||||||
|
|
||||||
struct RubberBandState_
|
struct RubberBandState_
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
#include <new> // for std::bad_alloc
|
#include <new> // for std::bad_alloc
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#ifndef HAVE_POSIX_MEMALIGN
|
#ifndef HAVE_POSIX_MEMALIGN
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
@@ -309,6 +311,96 @@ private:
|
|||||||
T *m_t;
|
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
|
#endif
|
||||||
|
|||||||
@@ -491,6 +491,58 @@ inline T v_sum(const T *const R__ src,
|
|||||||
return result;
|
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>
|
template<typename T>
|
||||||
inline void v_log(T *const R__ dst,
|
inline void v_log(T *const R__ dst,
|
||||||
const int count)
|
const int count)
|
||||||
|
|||||||
@@ -135,8 +135,8 @@ extern void system_memorybarrier();
|
|||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#define MLOCK(a,b) ::mlock((char *)(a),(b))
|
#define MLOCK(a,b) mlock((char *)(a),(b))
|
||||||
#define MUNLOCK(a,b) (::munlock((char *)(a),(b)) ? (::perror("munlock failed"), 0) : 0)
|
#define MUNLOCK(a,b) (munlock((char *)(a),(b)) ? (perror("munlock failed"), 0) : 0)
|
||||||
#define MUNLOCK_SAMPLEBLOCK(a) do { if (!(a).empty()) { const float &b = *(a).begin(); MUNLOCK(&b, (a).capacity() * sizeof(float)); } } while(0);
|
#define MUNLOCK_SAMPLEBLOCK(a) do { if (!(a).empty()) { const float &b = *(a).begin(); MUNLOCK(&b, (a).capacity() * sizeof(float)); } } while(0);
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|||||||
Reference in New Issue
Block a user