Merge from branch performance
This commit is contained in:
86
COMPILING.md
86
COMPILING.md
@@ -239,59 +239,67 @@ resampler or libsamplerate.
|
|||||||
|
|
||||||
### FFT libraries supported
|
### FFT libraries supported
|
||||||
|
|
||||||
|
The choice of FFT library makes no difference to output quality, only
|
||||||
|
to CPU usage.
|
||||||
|
|
||||||
```
|
```
|
||||||
Library Build option CPP define Notes
|
Library Build option CPP define Notes
|
||||||
---- ------------ ---------- -----
|
---- ------------ ---------- -----
|
||||||
|
|
||||||
Built-in -Dfft=builtin -DUSE_BUILTIN_FFT
|
Built-in -Dfft=builtin -DUSE_BUILTIN_FFT Default except on macOS/iOS.
|
||||||
Default except on macOS/iOS.
|
|
||||||
Can be distributed with either
|
|
||||||
the Rubber Band GPL or
|
|
||||||
commercial licence.
|
|
||||||
|
|
||||||
Accelerate -Dfft=vdsp -DHAVE_VDSP Default on macOS/iOS.
|
Accelerate -Dfft=vdsp -DHAVE_VDSP Default on macOS/iOS.
|
||||||
Best option on these platforms.
|
Best option on these platforms.
|
||||||
|
|
||||||
FFTW3 -Dfft=fftw -DHAVE_FFTW3 GPL.
|
FFTW3 -Dfft=fftw -DHAVE_FFTW3 A bit faster than built-in,
|
||||||
A bit faster than built-in,
|
a bit slower than Accelerate.
|
||||||
a bit slower than Accelerate.
|
GPL licence.
|
||||||
|
|
||||||
KissFFT -Dfft=kissfft -DHAVE_KISSFFT
|
SLEEF -Dfft=sleef -DHAVE_SLEEF Usually very fast. Not as widely
|
||||||
Single precision.
|
distributed as FFTW3. Requires
|
||||||
Only indicated for use with
|
both libsleef and libsleefdft.
|
||||||
single-precision sample type
|
BSD-ish licence.
|
||||||
(see below).
|
|
||||||
Bundled, can be distributed with
|
|
||||||
either the Rubber Band GPL or
|
|
||||||
commercial licence.
|
|
||||||
|
|
||||||
Intel IPP -Dfft=ipp -DHAVE_IPP Proprietary, can only be used with
|
KissFFT -Dfft=kissfft -DHAVE_KISSFFT Single precision.
|
||||||
Rubber Band commercial licence.
|
Only advisable when using
|
||||||
|
single-precision sample type
|
||||||
|
(see below).
|
||||||
|
BSD-ish licence.
|
||||||
|
|
||||||
|
Intel IPP -Dfft=ipp -DHAVE_IPP Very fast on Intel hardware.
|
||||||
|
Proprietary, can only be used with
|
||||||
|
Rubber Band commercial licence.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Resampler libraries supported
|
### Resampler libraries supported
|
||||||
|
|
||||||
|
The choice of resampler affects both output quality, when
|
||||||
|
pitch-shifting, and CPU usage.
|
||||||
|
|
||||||
```
|
```
|
||||||
Library Build option CPP define Notes
|
Library Build option CPP define Notes
|
||||||
---- ------------ ---------- -----
|
------- ------------ ---------- -----
|
||||||
|
|
||||||
Built-in -Dfft=builtin -DUSE_BQRESAMPLER
|
Built-in -Dfft=builtin -DUSE_BQRESAMPLER Default.
|
||||||
Default.
|
Intended to give high quality
|
||||||
Can be distributed with either
|
for time-varying pitch shifts
|
||||||
the Rubber Band GPL or
|
in real-time mode.
|
||||||
commercial licence. Intended to
|
Not the fastest option.
|
||||||
give best quality for time-varying
|
|
||||||
pitch shifts in real-time mode.
|
|
||||||
Newer than, and not as well-tested
|
|
||||||
as, libsamplerate.
|
|
||||||
|
|
||||||
libsamplerate -DHAVE_LIBSAMPLERATE
|
libsamplerate -Dresampler=libsamplerate -DHAVE_LIBSAMPLERATE Good choice in most cases.
|
||||||
-Dresampler=libsamplerate Good choice in most cases.
|
High quality and usually a bit
|
||||||
|
faster than the built-in option.
|
||||||
|
BSD-ish licence.
|
||||||
|
|
||||||
Speex -DUSE_SPEEX
|
libspeexdsp -Dresampler=libspeexdsp -DHAVE_LIBSPEEXDSP Very fast.
|
||||||
-Dresampler=speex Can be distributed with
|
May not be artifact-free for
|
||||||
either the Rubber Band GPL or
|
time-varying pitch shifts.
|
||||||
commercial licence.
|
BSD-ish licence.
|
||||||
|
|
||||||
|
Bundled Speex -Dresampler=speex -DUSE_SPEEX Older Speex code, bundled for
|
||||||
|
compatibility with some existing
|
||||||
|
projects.
|
||||||
|
Avoid for new projects.
|
||||||
```
|
```
|
||||||
|
|
||||||
## 8. Other supported #defines
|
## 8. Other supported #defines
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ details.
|
|||||||
|
|
||||||
* FFTW3 - GPL; proprietary licence needed for redistribution
|
* FFTW3 - GPL; proprietary licence needed for redistribution
|
||||||
* Intel IPP - Proprietary; licence needed for redistribution
|
* Intel IPP - Proprietary; licence needed for redistribution
|
||||||
|
* SLEEF - BSD-like
|
||||||
* KissFFT - BSD-like
|
* KissFFT - BSD-like
|
||||||
* libsamplerate - BSD-like from version 0.1.9 onwards
|
* libsamplerate - BSD-like from version 0.1.9 onwards
|
||||||
* Speex - BSD-like
|
* Speex - BSD-like
|
||||||
|
|||||||
@@ -418,7 +418,11 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
if (finer) {
|
if (finer) {
|
||||||
cerr << "Using R3 (finer) engine" << endl;
|
if (shortwin) {
|
||||||
|
cerr << "Using intermediate R3 (finer) single-windowed engine" << endl;
|
||||||
|
} else {
|
||||||
|
cerr << "Using R3 (finer) engine" << endl;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cerr << "Using R2 (faster) engine" << endl;
|
cerr << "Using R2 (faster) engine" << endl;
|
||||||
cerr << "Using crispness level: " << crispness << " (";
|
cerr << "Using crispness level: " << crispness << " (";
|
||||||
@@ -669,6 +673,7 @@ int main(int argc, char **argv)
|
|||||||
RubberBandStretcher ts(sfinfo.samplerate, channels, options,
|
RubberBandStretcher ts(sfinfo.samplerate, channels, options,
|
||||||
ratio, frequencyshift);
|
ratio, frequencyshift);
|
||||||
ts.setExpectedInputDuration(sfinfo.frames);
|
ts.setExpectedInputDuration(sfinfo.frames);
|
||||||
|
ts.setMaxProcessSize(bs);
|
||||||
|
|
||||||
int frame = 0;
|
int frame = 0;
|
||||||
int percent = 0;
|
int percent = 0;
|
||||||
|
|||||||
85
meson.build
85
meson.build
@@ -2,7 +2,7 @@
|
|||||||
project(
|
project(
|
||||||
'Rubber Band Library',
|
'Rubber Band Library',
|
||||||
'c', 'cpp',
|
'c', 'cpp',
|
||||||
version: '3.0.0',
|
version: '3.1.0-pre',
|
||||||
license: 'GPL-2.0-or-later',
|
license: 'GPL-2.0-or-later',
|
||||||
default_options: [
|
default_options: [
|
||||||
'cpp_std=c++11',
|
'cpp_std=c++11',
|
||||||
@@ -15,7 +15,7 @@ project(
|
|||||||
meson_version: '>= 0.53.0'
|
meson_version: '>= 0.53.0'
|
||||||
)
|
)
|
||||||
|
|
||||||
rubberband_dynamic_library_version = '2.2.0'
|
rubberband_dynamic_library_version = '2.2.1'
|
||||||
|
|
||||||
system = host_machine.system()
|
system = host_machine.system()
|
||||||
architecture = host_machine.cpu_family()
|
architecture = host_machine.cpu_family()
|
||||||
@@ -49,6 +49,7 @@ library_sources = [
|
|||||||
'src/common/Resampler.cpp',
|
'src/common/Resampler.cpp',
|
||||||
'src/common/StretchCalculator.cpp',
|
'src/common/StretchCalculator.cpp',
|
||||||
'src/common/sysutils.cpp',
|
'src/common/sysutils.cpp',
|
||||||
|
'src/common/mathmisc.cpp',
|
||||||
'src/common/Thread.cpp',
|
'src/common/Thread.cpp',
|
||||||
'src/finer/R3Stretcher.cpp',
|
'src/finer/R3Stretcher.cpp',
|
||||||
]
|
]
|
||||||
@@ -112,7 +113,10 @@ foreach d: get_option('extra_include_dirs')
|
|||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
fftw3_dep = dependency('fftw3', version: '>= 3.0.0', required: false)
|
fftw3_dep = dependency('fftw3', version: '>= 3.0.0', required: false)
|
||||||
|
sleef_dep = dependency('sleef', version: '>= 3.3.0', required: false)
|
||||||
|
sleefdft_dep = dependency('sleefdft', version: '>= 3.3.0', required: false)
|
||||||
samplerate_dep = dependency('samplerate', version: '>= 0.1.8', required: false)
|
samplerate_dep = dependency('samplerate', version: '>= 0.1.8', required: false)
|
||||||
|
speexdsp_dep = dependency('speexdsp', version: '>= 1.0.0', required: false)
|
||||||
sndfile_dep = dependency('sndfile', version: '>= 1.0.16', required: false)
|
sndfile_dep = dependency('sndfile', version: '>= 1.0.16', required: false)
|
||||||
vamp_dep = dependency('vamp-sdk', version: '>= 2.9', required: false)
|
vamp_dep = dependency('vamp-sdk', version: '>= 2.9', required: false)
|
||||||
boost_unit_test_dep = dependency('boost', modules: ['unit_test_framework'], version: '>= 1.73', required: false)
|
boost_unit_test_dep = dependency('boost', modules: ['unit_test_framework'], version: '>= 1.73', required: false)
|
||||||
@@ -164,6 +168,9 @@ if fft == 'builtin'
|
|||||||
if fftw3_dep.found()
|
if fftw3_dep.found()
|
||||||
message('(to use FFTW instead, reconfigure with -Dfft=fftw)')
|
message('(to use FFTW instead, reconfigure with -Dfft=fftw)')
|
||||||
endif
|
endif
|
||||||
|
if sleef_dep.found()
|
||||||
|
message('(to use SLEEF instead, reconfigure with -Dfft=sleef)')
|
||||||
|
endif
|
||||||
feature_defines += ['-DUSE_BUILTIN_FFT']
|
feature_defines += ['-DUSE_BUILTIN_FFT']
|
||||||
|
|
||||||
elif fft == 'kissfft'
|
elif fft == 'kissfft'
|
||||||
@@ -172,25 +179,53 @@ elif fft == 'kissfft'
|
|||||||
if fftw3_dep.found()
|
if fftw3_dep.found()
|
||||||
message('(to use FFTW instead, reconfigure with -Dfft=fftw)')
|
message('(to use FFTW instead, reconfigure with -Dfft=fftw)')
|
||||||
endif
|
endif
|
||||||
|
if sleef_dep.found()
|
||||||
|
message('(to use SLEEF instead, reconfigure with -Dfft=sleef)')
|
||||||
|
endif
|
||||||
feature_sources += ['src/ext/kissfft/kiss_fft.c', 'src/ext/kissfft/kiss_fftr.c']
|
feature_sources += ['src/ext/kissfft/kiss_fft.c', 'src/ext/kissfft/kiss_fftr.c']
|
||||||
feature_defines += ['-DHAVE_KISSFFT']
|
feature_defines += ['-DHAVE_KISSFFT']
|
||||||
general_include_dirs += 'src/ext/kissfft'
|
general_include_dirs += 'src/ext/kissfft'
|
||||||
|
|
||||||
elif fft == 'fftw'
|
elif fft == 'fftw'
|
||||||
if fftw3_dep.found()
|
if not fftw3_dep.found()
|
||||||
config_summary += { 'FFT': 'FFTW' }
|
fftw3_dep = cpp.find_library('fftw3',
|
||||||
message('For FFT: using FFTW')
|
dirs: get_option('extra_lib_dirs'),
|
||||||
pkgconfig_requirements += fftw3_dep
|
has_headers: ['fftw3.h'],
|
||||||
else
|
header_args: extra_include_args,
|
||||||
fftw_dep = cpp.find_library('fftw3',
|
required: true)
|
||||||
dirs: get_option('extra_lib_dirs'),
|
|
||||||
has_headers: ['fftw3.h'],
|
|
||||||
header_args: extra_include_args,
|
|
||||||
required: true)
|
|
||||||
endif
|
endif
|
||||||
|
config_summary += { 'FFT': 'FFTW' }
|
||||||
|
message('For FFT: using FFTW')
|
||||||
|
if sleef_dep.found()
|
||||||
|
message('(to use SLEEF instead, reconfigure with -Dfft=sleef)')
|
||||||
|
endif
|
||||||
|
pkgconfig_requirements += fftw3_dep
|
||||||
feature_dependencies += fftw3_dep
|
feature_dependencies += fftw3_dep
|
||||||
feature_defines += ['-DHAVE_FFTW3', '-DFFTW_DOUBLE_ONLY']
|
feature_defines += ['-DHAVE_FFTW3', '-DFFTW_DOUBLE_ONLY']
|
||||||
|
|
||||||
|
elif fft == 'sleef'
|
||||||
|
if sleefdft_dep.found() and sleef_dep.found()
|
||||||
|
config_summary += { 'FFT': 'SLEEF' }
|
||||||
|
message('For FFT: using SLEEF')
|
||||||
|
pkgconfig_requirements += sleefdft_dep
|
||||||
|
pkgconfig_requirements += sleef_dep
|
||||||
|
else
|
||||||
|
sleefdft_dep = cpp.find_library('sleefdft',
|
||||||
|
dirs: get_option('extra_lib_dirs'),
|
||||||
|
has_headers: ['sleefdft.h'],
|
||||||
|
header_args: extra_include_args,
|
||||||
|
required: true)
|
||||||
|
sleef_dep = cpp.find_library('sleef',
|
||||||
|
dirs: get_option('extra_lib_dirs'),
|
||||||
|
has_headers: ['sleef.h'],
|
||||||
|
header_args: extra_include_args,
|
||||||
|
required: true)
|
||||||
|
config_summary += { 'FFT': 'SLEEF' }
|
||||||
|
endif
|
||||||
|
feature_dependencies += sleefdft_dep
|
||||||
|
feature_dependencies += sleef_dep
|
||||||
|
feature_defines += ['-DHAVE_SLEEF']
|
||||||
|
|
||||||
elif fft == 'vdsp'
|
elif fft == 'vdsp'
|
||||||
config_summary += { 'FFT': 'vDSP' }
|
config_summary += { 'FFT': 'vDSP' }
|
||||||
message('For FFT: using vDSP')
|
message('For FFT: using vDSP')
|
||||||
@@ -223,27 +258,41 @@ if resampler == 'builtin'
|
|||||||
feature_defines += ['-DUSE_BQRESAMPLER']
|
feature_defines += ['-DUSE_BQRESAMPLER']
|
||||||
|
|
||||||
elif resampler == 'libsamplerate'
|
elif resampler == 'libsamplerate'
|
||||||
if samplerate_dep.found()
|
if not samplerate_dep.found()
|
||||||
config_summary += { 'Resampler': 'libsamplerate' }
|
|
||||||
message('For resampler: using libsamplerate')
|
|
||||||
pkgconfig_requirements += samplerate_dep
|
|
||||||
else
|
|
||||||
samplerate_dep = cpp.find_library('samplerate',
|
samplerate_dep = cpp.find_library('samplerate',
|
||||||
dirs: get_option('extra_lib_dirs'),
|
dirs: get_option('extra_lib_dirs'),
|
||||||
has_headers: ['samplerate.h'],
|
has_headers: ['samplerate.h'],
|
||||||
header_args: extra_include_args,
|
header_args: extra_include_args,
|
||||||
required: true)
|
required: true)
|
||||||
endif
|
endif
|
||||||
|
config_summary += { 'Resampler': 'libsamplerate' }
|
||||||
|
message('For resampler: using libsamplerate')
|
||||||
feature_dependencies += samplerate_dep
|
feature_dependencies += samplerate_dep
|
||||||
|
pkgconfig_requirements += samplerate_dep
|
||||||
feature_defines += ['-DHAVE_LIBSAMPLERATE']
|
feature_defines += ['-DHAVE_LIBSAMPLERATE']
|
||||||
|
|
||||||
elif resampler == 'speex'
|
elif resampler == 'speex'
|
||||||
config_summary += { 'Resampler': 'Speex' }
|
config_summary += { 'Resampler': 'Speex' }
|
||||||
message('For resampler: using Speex')
|
message('For resampler: using bundled Speex')
|
||||||
message('(consider libsamplerate if time-varying pitch shift is required)')
|
message('(consider libsamplerate if time-varying pitch shift is required)')
|
||||||
feature_sources += ['src/ext/speex/resample.c']
|
feature_sources += ['src/ext/speex/resample.c']
|
||||||
feature_defines += ['-DUSE_SPEEX']
|
feature_defines += ['-DUSE_SPEEX']
|
||||||
|
|
||||||
|
elif resampler == 'libspeexdsp'
|
||||||
|
if not speexdsp_dep.found()
|
||||||
|
speexdsp_dep = cpp.find_library('speexdsp',
|
||||||
|
dirs: get_option('extra_lib_dirs'),
|
||||||
|
has_headers: ['speex/speex_resampler.h'],
|
||||||
|
header_args: extra_include_args,
|
||||||
|
required: true)
|
||||||
|
endif
|
||||||
|
config_summary += { 'Resampler': 'Speex DSP' }
|
||||||
|
message('For resampler: using Speex DSP library')
|
||||||
|
message('(consider libsamplerate if time-varying pitch shift is required)')
|
||||||
|
feature_dependencies += speexdsp_dep
|
||||||
|
pkgconfig_requirements += speexdsp_dep
|
||||||
|
feature_defines += ['-DHAVE_LIBSPEEXDSP']
|
||||||
|
|
||||||
elif resampler == 'ipp'
|
elif resampler == 'ipp'
|
||||||
if ipp_path != ''
|
if ipp_path != ''
|
||||||
config_summary += { 'Resampler': 'Intel IPP' }
|
config_summary += { 'Resampler': 'Intel IPP' }
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
|
||||||
option('fft',
|
option('fft',
|
||||||
type: 'combo',
|
type: 'combo',
|
||||||
choices: ['auto', 'builtin', 'kissfft', 'fftw', 'vdsp', 'ipp'],
|
choices: ['auto', 'builtin', 'kissfft', 'fftw', 'sleef', 'vdsp', 'ipp'],
|
||||||
value: 'auto',
|
value: 'auto',
|
||||||
description: 'FFT library to use. The default (auto) will use vDSP if available, the builtin implementation otherwise.')
|
description: 'FFT library to use. The default (auto) will use vDSP if available, the builtin implementation otherwise.')
|
||||||
|
|
||||||
option('resampler',
|
option('resampler',
|
||||||
type: 'combo',
|
type: 'combo',
|
||||||
choices: ['auto', 'builtin', 'libsamplerate', 'speex', 'ipp'],
|
choices: ['auto', 'builtin', 'libsamplerate', 'speex', 'libspeexdsp', 'ipp'],
|
||||||
value: 'auto',
|
value: 'auto',
|
||||||
description: 'Resampler library to use. The default (auto) simply uses the builtin implementation.')
|
description: 'Resampler library to use. The default (auto) simply uses the builtin implementation.')
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ RUBBERBAND_SRC_FILES := \
|
|||||||
$(RUBBERBAND_SRC_PATH)/common/Allocators.cpp \
|
$(RUBBERBAND_SRC_PATH)/common/Allocators.cpp \
|
||||||
$(RUBBERBAND_SRC_PATH)/common/StretchCalculator.cpp \
|
$(RUBBERBAND_SRC_PATH)/common/StretchCalculator.cpp \
|
||||||
$(RUBBERBAND_SRC_PATH)/common/sysutils.cpp \
|
$(RUBBERBAND_SRC_PATH)/common/sysutils.cpp \
|
||||||
|
$(RUBBERBAND_SRC_PATH)/common/mathmisc.cpp \
|
||||||
$(RUBBERBAND_SRC_PATH)/common/Thread.cpp \
|
$(RUBBERBAND_SRC_PATH)/common/Thread.cpp \
|
||||||
$(RUBBERBAND_SRC_PATH)/finer/R3StretcherImpl.cpp
|
$(RUBBERBAND_SRC_PATH)/finer/R3StretcherImpl.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ LIBRARY_SOURCES := \
|
|||||||
src/common/Resampler.cpp \
|
src/common/Resampler.cpp \
|
||||||
src/common/StretchCalculator.cpp \
|
src/common/StretchCalculator.cpp \
|
||||||
src/common/sysutils.cpp \
|
src/common/sysutils.cpp \
|
||||||
|
src/common/mathmisc.cpp \
|
||||||
src/common/Thread.cpp \
|
src/common/Thread.cpp \
|
||||||
src/finer/R3Stretcher.cpp
|
src/finer/R3Stretcher.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ LIBRARY_SOURCES := \
|
|||||||
src/common/Resampler.cpp \
|
src/common/Resampler.cpp \
|
||||||
src/common/StretchCalculator.cpp \
|
src/common/StretchCalculator.cpp \
|
||||||
src/common/sysutils.cpp \
|
src/common/sysutils.cpp \
|
||||||
|
src/common/mathmisc.cpp \
|
||||||
src/common/Thread.cpp \
|
src/common/Thread.cpp \
|
||||||
src/finer/R3Stretcher.cpp
|
src/finer/R3Stretcher.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ LIBRARY_SOURCES := \
|
|||||||
src/common/Resampler.cpp \
|
src/common/Resampler.cpp \
|
||||||
src/common/StretchCalculator.cpp \
|
src/common/StretchCalculator.cpp \
|
||||||
src/common/sysutils.cpp \
|
src/common/sysutils.cpp \
|
||||||
|
src/common/mathmisc.cpp \
|
||||||
src/common/Thread.cpp \
|
src/common/Thread.cpp \
|
||||||
src/finer/R3Stretcher.cpp
|
src/finer/R3Stretcher.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ LIBRARY_SOURCES := \
|
|||||||
src/common/Resampler.cpp \
|
src/common/Resampler.cpp \
|
||||||
src/common/StretchCalculator.cpp \
|
src/common/StretchCalculator.cpp \
|
||||||
src/common/sysutils.cpp \
|
src/common/sysutils.cpp \
|
||||||
|
src/common/mathmisc.cpp \
|
||||||
src/common/Thread.cpp \
|
src/common/Thread.cpp \
|
||||||
src/finer/R3Stretcher.cpp
|
src/finer/R3Stretcher.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -157,6 +157,7 @@
|
|||||||
<ClCompile Include="..\src\common\Allocators.cpp" />
|
<ClCompile Include="..\src\common\Allocators.cpp" />
|
||||||
<ClCompile Include="..\src\common\StretchCalculator.cpp" />
|
<ClCompile Include="..\src\common\StretchCalculator.cpp" />
|
||||||
<ClCompile Include="..\src\common\sysutils.cpp" />
|
<ClCompile Include="..\src\common\sysutils.cpp" />
|
||||||
|
<ClCompile Include="..\src\common\mathmisc.cpp" />
|
||||||
<ClCompile Include="..\src\common\Thread.cpp" />
|
<ClCompile Include="..\src\common\Thread.cpp" />
|
||||||
<ClCompile Include="..\src\finer\R3Stretcher.cpp" />
|
<ClCompile Include="..\src\finer\R3Stretcher.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#ifndef RUBBERBAND_STRETCHER_H
|
#ifndef RUBBERBAND_STRETCHER_H
|
||||||
#define RUBBERBAND_STRETCHER_H
|
#define RUBBERBAND_STRETCHER_H
|
||||||
|
|
||||||
#define RUBBERBAND_VERSION "3.0.0"
|
#define RUBBERBAND_VERSION "3.1.0"
|
||||||
#define RUBBERBAND_API_MAJOR_VERSION 2
|
#define RUBBERBAND_API_MAJOR_VERSION 2
|
||||||
#define RUBBERBAND_API_MINOR_VERSION 7
|
#define RUBBERBAND_API_MINOR_VERSION 7
|
||||||
|
|
||||||
@@ -262,26 +262,32 @@ public:
|
|||||||
* situation where \c OptionThreadingAuto would do so, except omit
|
* situation where \c OptionThreadingAuto would do so, except omit
|
||||||
* the check for multiple CPUs and instead assume it to be true.
|
* the check for multiple CPUs and instead assume it to be true.
|
||||||
*
|
*
|
||||||
* 7. Flags prefixed \c OptionWindow control the window size for
|
* 7. Flags prefixed \c OptionWindow influence the window size for
|
||||||
* FFT processing in the R2 engine. (The window size actually
|
* FFT processing. In the R2 engine these affect the resulting
|
||||||
* used will depend on many factors, but it can be influenced.)
|
* sound quality but have relatively little effect on processing
|
||||||
* These options currently have no effect when using the R3
|
* speed. With the R3 engine they can dramatically affect
|
||||||
* engine, but they may do in the future - so code written to use
|
* processing speed as well as output quality. These options may
|
||||||
* R3 now is recommended to use the default. These options may
|
|
||||||
* not be changed after construction.
|
* not be changed after construction.
|
||||||
*
|
*
|
||||||
* \li \c OptionWindowStandard - Use the default window size.
|
* \li \c OptionWindowStandard - Use the default window size.
|
||||||
* The actual size will vary depending on other parameters.
|
* The actual size will vary depending on other parameters.
|
||||||
* This option is expected to produce better results than the
|
* This option is expected to produce better results than the
|
||||||
* other window options in most situations.
|
* other window options in most situations. In the R3 engine
|
||||||
|
* this causes the engine's full multi-resolution processing
|
||||||
|
* scheme to be used.
|
||||||
*
|
*
|
||||||
* \li \c OptionWindowShort - Use a shorter window. This may
|
* \li \c OptionWindowShort - Use a shorter window. With the R2
|
||||||
* result in crisper sound for audio that depends strongly on
|
* engine this may result in crisper sound for audio that
|
||||||
* its timing qualities.
|
* depends strongly on its timing qualities. With the R3 engine,
|
||||||
|
* this causes the engine to be restricted to a single window
|
||||||
|
* size, resulting in both dramatically faster processing and
|
||||||
|
* lower latency than OptionWindowStandard, but at the expense
|
||||||
|
* of some sound quality.
|
||||||
*
|
*
|
||||||
* \li \c OptionWindowLong - Use a longer window. This is
|
* \li \c OptionWindowLong - Use a longer window. With the R2
|
||||||
* likely to result in a smoother sound at the expense of
|
* engine this is likely to result in a smoother sound at the
|
||||||
* clarity and timing.
|
* expense of clarity and timing. The R3 engine currently
|
||||||
|
* ignores this option, treating it like OptionWindowStandard.
|
||||||
*
|
*
|
||||||
* 8. Flags prefixed \c OptionSmoothing control the use of
|
* 8. Flags prefixed \c OptionSmoothing control the use of
|
||||||
* window-presum FFT and time-domain smoothing in the R2
|
* window-presum FFT and time-domain smoothing in the R2
|
||||||
@@ -331,10 +337,10 @@ public:
|
|||||||
* \li \c OptionPitchHighConsistency - Use a method that
|
* \li \c OptionPitchHighConsistency - Use a method that
|
||||||
* supports dynamic pitch changes without discontinuities,
|
* supports dynamic pitch changes without discontinuities,
|
||||||
* including when crossing the 1.0 pitch scale. This may cost
|
* including when crossing the 1.0 pitch scale. This may cost
|
||||||
* more in CPU than the other two options, especially when the
|
* more in CPU than the default, especially when the pitch scale
|
||||||
* pitch scale is exactly 1.0. You should use this option
|
* is exactly 1.0. You should use this option whenever you wish
|
||||||
* whenever you wish to support dynamically changing pitch
|
* to support dynamically changing pitch shift during
|
||||||
* shifts during processing.
|
* processing.
|
||||||
*
|
*
|
||||||
* 11. Flags prefixed \c OptionChannels control the method used
|
* 11. Flags prefixed \c OptionChannels control the method used
|
||||||
* for processing two-channel stereo audio. These have different,
|
* for processing two-channel stereo audio. These have different,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define RUBBERBAND_VERSION "3.0.0"
|
#define RUBBERBAND_VERSION "3.1.0"
|
||||||
#define RUBBERBAND_API_MAJOR_VERSION 2
|
#define RUBBERBAND_API_MAJOR_VERSION 2
|
||||||
#define RUBBERBAND_API_MINOR_VERSION 7
|
#define RUBBERBAND_API_MINOR_VERSION 7
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,8 @@
|
|||||||
standalone library.
|
standalone library.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef ALREADY_CONFIGURED
|
||||||
|
|
||||||
#define USE_BQRESAMPLER 1
|
#define USE_BQRESAMPLER 1
|
||||||
|
|
||||||
#define NO_TIMING 1
|
#define NO_TIMING 1
|
||||||
@@ -56,6 +58,8 @@
|
|||||||
#define USE_BUILTIN_FFT 1
|
#define USE_BUILTIN_FFT 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "../src/faster/AudioCurveCalculator.cpp"
|
#include "../src/faster/AudioCurveCalculator.cpp"
|
||||||
#include "../src/faster/CompoundAudioCurve.cpp"
|
#include "../src/faster/CompoundAudioCurve.cpp"
|
||||||
#include "../src/faster/HighFrequencyAudioCurve.cpp"
|
#include "../src/faster/HighFrequencyAudioCurve.cpp"
|
||||||
@@ -69,6 +73,7 @@
|
|||||||
#include "../src/common/Allocators.cpp"
|
#include "../src/common/Allocators.cpp"
|
||||||
#include "../src/common/StretchCalculator.cpp"
|
#include "../src/common/StretchCalculator.cpp"
|
||||||
#include "../src/common/sysutils.cpp"
|
#include "../src/common/sysutils.cpp"
|
||||||
|
#include "../src/common/mathmisc.cpp"
|
||||||
#include "../src/common/Thread.cpp"
|
#include "../src/common/Thread.cpp"
|
||||||
#include "../src/faster/StretcherChannelData.cpp"
|
#include "../src/faster/StretcherChannelData.cpp"
|
||||||
#include "../src/faster/R2Stretcher.cpp"
|
#include "../src/faster/R2Stretcher.cpp"
|
||||||
|
|||||||
@@ -85,9 +85,9 @@ T *allocate(size_t count)
|
|||||||
#else /* !MALLOC_IS_ALIGNED */
|
#else /* !MALLOC_IS_ALIGNED */
|
||||||
|
|
||||||
// That's the "sufficiently aligned" functions dealt with, the
|
// That's the "sufficiently aligned" functions dealt with, the
|
||||||
// rest need a specific alignment provided to the call. 32-byte
|
// rest need a specific alignment provided to the call. 64-byte
|
||||||
// alignment is required for at least OpenMAX
|
// alignment is enough for 8x8 double operations
|
||||||
static const int alignment = 32;
|
static const int alignment = 64;
|
||||||
|
|
||||||
#ifdef HAVE__ALIGNED_MALLOC
|
#ifdef HAVE__ALIGNED_MALLOC
|
||||||
ptr = _aligned_malloc(count * sizeof(T), alignment);
|
ptr = _aligned_malloc(count * sizeof(T), alignment);
|
||||||
|
|||||||
@@ -31,6 +31,8 @@
|
|||||||
#include "Allocators.h"
|
#include "Allocators.h"
|
||||||
#include "VectorOps.h"
|
#include "VectorOps.h"
|
||||||
|
|
||||||
|
#include "mathmisc.h"
|
||||||
|
|
||||||
#define BQ_R__ R__
|
#define BQ_R__ R__
|
||||||
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
@@ -122,6 +124,7 @@ BQResampler::QualityParams::QualityParams(Quality q)
|
|||||||
k_snr = 70.0;
|
k_snr = 70.0;
|
||||||
k_transition = 0.2;
|
k_transition = 0.2;
|
||||||
cut = 0.9;
|
cut = 0.9;
|
||||||
|
rational_max = 48000;
|
||||||
break;
|
break;
|
||||||
case FastestTolerable:
|
case FastestTolerable:
|
||||||
p_multiple = 62;
|
p_multiple = 62;
|
||||||
@@ -129,6 +132,7 @@ BQResampler::QualityParams::QualityParams(Quality q)
|
|||||||
k_snr = 90.0;
|
k_snr = 90.0;
|
||||||
k_transition = 0.05;
|
k_transition = 0.05;
|
||||||
cut = 0.975;
|
cut = 0.975;
|
||||||
|
rational_max = 96000;
|
||||||
break;
|
break;
|
||||||
case Best:
|
case Best:
|
||||||
p_multiple = 122;
|
p_multiple = 122;
|
||||||
@@ -136,6 +140,7 @@ BQResampler::QualityParams::QualityParams(Quality q)
|
|||||||
k_snr = 100.0;
|
k_snr = 100.0;
|
||||||
k_transition = 0.01;
|
k_transition = 0.01;
|
||||||
cut = 0.995;
|
cut = 0.995;
|
||||||
|
rational_max = 192000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,36 +378,18 @@ BQResampler::fill_params(double ratio, double numd, double denomd) const
|
|||||||
BQResampler::params
|
BQResampler::params
|
||||||
BQResampler::pick_params(double ratio) const
|
BQResampler::pick_params(double ratio) const
|
||||||
{
|
{
|
||||||
// Farey algorithm, see
|
int max_denom;
|
||||||
// https://www.johndcook.com/blog/2010/10/20/best-rational-approximation/
|
if (m_dynamism == RatioMostlyFixed) {
|
||||||
int max_denom = 192000;
|
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 {
|
} else {
|
||||||
return fill_params(ratio, pa, pb);
|
max_denom = m_qparams.rational_max;
|
||||||
|
if (ratio > 1.0) {
|
||||||
|
max_denom = int(ceil(max_denom / ratio));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
int num, denom;
|
||||||
|
pickNearestRational(ratio, max_denom, num, denom);
|
||||||
|
return fill_params(ratio, num, denom);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ private:
|
|||||||
double k_snr;
|
double k_snr;
|
||||||
double k_transition;
|
double k_transition;
|
||||||
double cut;
|
double cut;
|
||||||
|
int rational_max;
|
||||||
QualityParams(Quality);
|
QualityParams(Quality);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,13 @@
|
|||||||
#include <fftw3.h>
|
#include <fftw3.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SLEEF
|
||||||
|
extern "C" {
|
||||||
|
#include <sleef.h>
|
||||||
|
#include <sleefdft.h>
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_VDSP
|
#ifdef HAVE_VDSP
|
||||||
#include <Accelerate/Accelerate.h>
|
#include <Accelerate/Accelerate.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -63,6 +70,7 @@
|
|||||||
|
|
||||||
#ifndef HAVE_IPP
|
#ifndef HAVE_IPP
|
||||||
#ifndef HAVE_FFTW3
|
#ifndef HAVE_FFTW3
|
||||||
|
#ifndef HAVE_SLEEF
|
||||||
#ifndef HAVE_KISSFFT
|
#ifndef HAVE_KISSFFT
|
||||||
#ifndef USE_BUILTIN_FFT
|
#ifndef USE_BUILTIN_FFT
|
||||||
#ifndef HAVE_VDSP
|
#ifndef HAVE_VDSP
|
||||||
@@ -72,6 +80,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -1425,6 +1434,302 @@ pthread_mutex_t D_FFTW::m_commonMutex = PTHREAD_MUTEX_INITIALIZER;
|
|||||||
|
|
||||||
#endif /* HAVE_FFTW3 */
|
#endif /* HAVE_FFTW3 */
|
||||||
|
|
||||||
|
#ifdef HAVE_SLEEF
|
||||||
|
|
||||||
|
class D_SLEEF : public FFTImpl
|
||||||
|
{
|
||||||
|
bool isAligned(const void *ptr) {
|
||||||
|
return ! ((uintptr_t)ptr & 63);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
D_SLEEF(int size) :
|
||||||
|
m_fplanf(0), m_fplani(0), m_fbuf(0), m_fpacked(0),
|
||||||
|
m_dplanf(0), m_dplani(0), m_dbuf(0), m_dpacked(0),
|
||||||
|
m_size(size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~D_SLEEF() {
|
||||||
|
if (m_fplanf) {
|
||||||
|
SleefDFT_dispose(m_fplanf);
|
||||||
|
SleefDFT_dispose(m_fplani);
|
||||||
|
Sleef_free(m_fbuf);
|
||||||
|
Sleef_free(m_fpacked);
|
||||||
|
}
|
||||||
|
if (m_dplanf) {
|
||||||
|
SleefDFT_dispose(m_dplanf);
|
||||||
|
SleefDFT_dispose(m_dplani);
|
||||||
|
Sleef_free(m_dbuf);
|
||||||
|
Sleef_free(m_dpacked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSize() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
FFT::Precisions
|
||||||
|
getSupportedPrecisions() const {
|
||||||
|
return FFT::SinglePrecision | FFT::DoublePrecision;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initFloat() {
|
||||||
|
if (m_fplanf) return;
|
||||||
|
|
||||||
|
m_fbuf = static_cast<float *>
|
||||||
|
(Sleef_malloc(m_size * sizeof(float)));
|
||||||
|
m_fpacked = static_cast<float *>
|
||||||
|
(Sleef_malloc((m_size + 2) * sizeof(float)));
|
||||||
|
|
||||||
|
m_fplanf = SleefDFT_float_init1d
|
||||||
|
(m_size, m_fbuf, m_fpacked,
|
||||||
|
SLEEF_MODE_FORWARD | SLEEF_MODE_REAL | SLEEF_MODE_ESTIMATE);
|
||||||
|
|
||||||
|
m_fplani = SleefDFT_float_init1d
|
||||||
|
(m_size, m_fpacked, m_fbuf,
|
||||||
|
SLEEF_MODE_BACKWARD | SLEEF_MODE_REAL | SLEEF_MODE_ESTIMATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initDouble() {
|
||||||
|
if (m_dplanf) return;
|
||||||
|
|
||||||
|
m_dbuf = static_cast<double *>
|
||||||
|
(Sleef_malloc(m_size * sizeof(double)));
|
||||||
|
m_dpacked = static_cast<double *>
|
||||||
|
(Sleef_malloc((m_size + 2) * sizeof(double)));
|
||||||
|
|
||||||
|
m_dplanf = SleefDFT_double_init1d
|
||||||
|
(m_size, m_dbuf, m_dpacked,
|
||||||
|
SLEEF_MODE_FORWARD | SLEEF_MODE_REAL | SLEEF_MODE_ESTIMATE);
|
||||||
|
|
||||||
|
m_dplani = SleefDFT_double_init1d
|
||||||
|
(m_size, m_dpacked, m_dbuf,
|
||||||
|
SLEEF_MODE_BACKWARD | SLEEF_MODE_REAL | SLEEF_MODE_ESTIMATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void packFloat(const float *BQ_R__ re, const float *BQ_R__ im) {
|
||||||
|
const float *src[2] = { re, im };
|
||||||
|
v_interleave(m_fpacked, src, 2, m_size/2 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void packDouble(const double *BQ_R__ re, const double *BQ_R__ im) {
|
||||||
|
const double *src[2] = { re, im };
|
||||||
|
v_interleave(m_dpacked, src, 2, m_size/2 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unpackFloat(float *BQ_R__ re, float *BQ_R__ im) {
|
||||||
|
float *dst[2] = { re, im };
|
||||||
|
v_deinterleave(dst, m_fpacked, 2, m_size/2 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unpackDouble(double *BQ_R__ re, double *BQ_R__ im) {
|
||||||
|
double *dst[2] = { re, im };
|
||||||
|
v_deinterleave(dst, m_dpacked, 2, m_size/2 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut) {
|
||||||
|
if (!m_dplanf) initDouble();
|
||||||
|
if (isAligned(realIn)) {
|
||||||
|
SleefDFT_double_execute(m_dplanf, realIn, 0);
|
||||||
|
} else {
|
||||||
|
v_copy(m_dbuf, realIn, m_size);
|
||||||
|
SleefDFT_double_execute(m_dplanf, 0, 0);
|
||||||
|
}
|
||||||
|
unpackDouble(realOut, imagOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut) {
|
||||||
|
if (!m_dplanf) initDouble();
|
||||||
|
if (isAligned(realIn) && isAligned(complexOut)) {
|
||||||
|
SleefDFT_double_execute(m_dplanf, realIn, complexOut);
|
||||||
|
} else {
|
||||||
|
v_copy(m_dbuf, realIn, m_size);
|
||||||
|
SleefDFT_double_execute(m_dplanf, 0, 0);
|
||||||
|
v_copy(complexOut, m_dpacked, m_size + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut) {
|
||||||
|
if (!m_dplanf) initDouble();
|
||||||
|
if (isAligned(realIn)) {
|
||||||
|
SleefDFT_double_execute(m_dplanf, realIn, 0);
|
||||||
|
} else {
|
||||||
|
v_copy(m_dbuf, realIn, m_size);
|
||||||
|
SleefDFT_double_execute(m_dplanf, 0, 0);
|
||||||
|
}
|
||||||
|
v_cartesian_interleaved_to_polar(magOut, phaseOut, m_dpacked, m_size/2+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut) {
|
||||||
|
if (!m_dplanf) initDouble();
|
||||||
|
if (isAligned(realIn)) {
|
||||||
|
SleefDFT_double_execute(m_dplanf, realIn, 0);
|
||||||
|
} else {
|
||||||
|
v_copy(m_dbuf, realIn, m_size);
|
||||||
|
SleefDFT_double_execute(m_dplanf, 0, 0);
|
||||||
|
}
|
||||||
|
v_cartesian_interleaved_to_magnitudes(magOut, m_dpacked, m_size/2+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut) {
|
||||||
|
if (!m_fplanf) initFloat();
|
||||||
|
if (isAligned(realIn)) {
|
||||||
|
SleefDFT_float_execute(m_fplanf, realIn, 0);
|
||||||
|
} else {
|
||||||
|
v_copy(m_fbuf, realIn, m_size);
|
||||||
|
SleefDFT_float_execute(m_fplanf, 0, 0);
|
||||||
|
}
|
||||||
|
unpackFloat(realOut, imagOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut) {
|
||||||
|
if (!m_fplanf) initFloat();
|
||||||
|
if (isAligned(realIn) && isAligned(complexOut)) {
|
||||||
|
SleefDFT_float_execute(m_fplanf, realIn, complexOut);
|
||||||
|
} else {
|
||||||
|
v_copy(m_fbuf, realIn, m_size);
|
||||||
|
SleefDFT_float_execute(m_fplanf, 0, 0);
|
||||||
|
v_copy(complexOut, m_fpacked, m_size + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut) {
|
||||||
|
if (!m_fplanf) initFloat();
|
||||||
|
if (isAligned(realIn)) {
|
||||||
|
SleefDFT_float_execute(m_fplanf, realIn, 0);
|
||||||
|
} else {
|
||||||
|
v_copy(m_fbuf, realIn, m_size);
|
||||||
|
SleefDFT_float_execute(m_fplanf, 0, 0);
|
||||||
|
}
|
||||||
|
v_cartesian_interleaved_to_polar(magOut, phaseOut, m_fpacked, m_size/2+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut) {
|
||||||
|
if (!m_fplanf) initFloat();
|
||||||
|
if (isAligned(realIn)) {
|
||||||
|
SleefDFT_float_execute(m_fplanf, realIn, 0);
|
||||||
|
} else {
|
||||||
|
v_copy(m_fbuf, realIn, m_size);
|
||||||
|
SleefDFT_float_execute(m_fplanf, 0, 0);
|
||||||
|
}
|
||||||
|
v_cartesian_interleaved_to_magnitudes(magOut, m_fpacked, m_size/2+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut) {
|
||||||
|
if (!m_dplanf) initDouble();
|
||||||
|
packDouble(realIn, imagIn);
|
||||||
|
if (isAligned(realOut)) {
|
||||||
|
SleefDFT_double_execute(m_dplani, 0, realOut);
|
||||||
|
} else {
|
||||||
|
SleefDFT_double_execute(m_dplani, 0, 0);
|
||||||
|
v_copy(realOut, m_dbuf, m_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut) {
|
||||||
|
if (!m_dplanf) initDouble();
|
||||||
|
if (isAligned(complexIn) && isAligned(realOut)) {
|
||||||
|
SleefDFT_double_execute(m_dplani, complexIn, realOut);
|
||||||
|
} else {
|
||||||
|
v_copy(m_dpacked, complexIn, m_size + 2);
|
||||||
|
SleefDFT_double_execute(m_dplani, 0, 0);
|
||||||
|
v_copy(realOut, m_dbuf, m_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut) {
|
||||||
|
if (!m_dplanf) initDouble();
|
||||||
|
v_polar_to_cartesian_interleaved(m_dpacked, magIn, phaseIn, m_size/2+1);
|
||||||
|
if (isAligned(realOut)) {
|
||||||
|
SleefDFT_double_execute(m_dplani, 0, realOut);
|
||||||
|
} else {
|
||||||
|
SleefDFT_double_execute(m_dplani, 0, 0);
|
||||||
|
v_copy(realOut, m_dbuf, m_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut) {
|
||||||
|
if (!m_dplanf) initDouble();
|
||||||
|
const int hs = m_size/2;
|
||||||
|
for (int i = 0; i <= hs; ++i) {
|
||||||
|
m_dpacked[i*2] = log(magIn[i] + 0.000001);
|
||||||
|
m_dpacked[i*2+1] = 0.0;
|
||||||
|
}
|
||||||
|
if (isAligned(cepOut)) {
|
||||||
|
SleefDFT_double_execute(m_dplani, 0, cepOut);
|
||||||
|
} else {
|
||||||
|
SleefDFT_double_execute(m_dplani, 0, 0);
|
||||||
|
v_copy(cepOut, m_dbuf, m_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut) {
|
||||||
|
if (!m_fplanf) initFloat();
|
||||||
|
packFloat(realIn, imagIn);
|
||||||
|
if (isAligned(realOut)) {
|
||||||
|
SleefDFT_float_execute(m_dplani, 0, realOut);
|
||||||
|
} else {
|
||||||
|
SleefDFT_float_execute(m_fplani, 0, 0);
|
||||||
|
v_copy(realOut, m_fbuf, m_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut) {
|
||||||
|
if (!m_fplanf) initFloat();
|
||||||
|
if (isAligned(complexIn) && isAligned(realOut)) {
|
||||||
|
SleefDFT_float_execute(m_fplani, complexIn, realOut);
|
||||||
|
} else {
|
||||||
|
v_copy(m_fpacked, complexIn, m_size + 2);
|
||||||
|
SleefDFT_float_execute(m_fplani, 0, 0);
|
||||||
|
v_copy(realOut, m_fbuf, m_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut) {
|
||||||
|
if (!m_fplanf) initFloat();
|
||||||
|
v_polar_to_cartesian_interleaved(m_fpacked, magIn, phaseIn, m_size/2+1);
|
||||||
|
if (isAligned(realOut)) {
|
||||||
|
SleefDFT_float_execute(m_fplani, 0, realOut);
|
||||||
|
} else {
|
||||||
|
SleefDFT_float_execute(m_fplani, 0, 0);
|
||||||
|
v_copy(realOut, m_fbuf, m_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut) {
|
||||||
|
if (!m_fplanf) initFloat();
|
||||||
|
const int hs = m_size/2;
|
||||||
|
for (int i = 0; i <= hs; ++i) {
|
||||||
|
m_fpacked[i*2] = logf(magIn[i] + 0.000001f);
|
||||||
|
m_fpacked[i*2+1] = 0.0;
|
||||||
|
}
|
||||||
|
if (isAligned(cepOut)) {
|
||||||
|
SleefDFT_float_execute(m_fplani, 0, cepOut);
|
||||||
|
} else {
|
||||||
|
SleefDFT_float_execute(m_fplani, 0, 0);
|
||||||
|
v_copy(cepOut, m_fbuf, m_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SleefDFT *m_fplanf;
|
||||||
|
SleefDFT *m_fplani;
|
||||||
|
|
||||||
|
float *m_fbuf;
|
||||||
|
float *m_fpacked;
|
||||||
|
|
||||||
|
SleefDFT *m_dplanf;
|
||||||
|
SleefDFT *m_dplani;
|
||||||
|
|
||||||
|
double *m_dbuf;
|
||||||
|
double *m_dpacked;
|
||||||
|
|
||||||
|
const int m_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* HAVE_SLEEF */
|
||||||
|
|
||||||
#ifdef HAVE_KISSFFT
|
#ifdef HAVE_KISSFFT
|
||||||
|
|
||||||
class D_KISSFFT : public FFTImpl
|
class D_KISSFFT : public FFTImpl
|
||||||
@@ -2266,6 +2571,9 @@ getImplementationDetails()
|
|||||||
#ifdef HAVE_FFTW3
|
#ifdef HAVE_FFTW3
|
||||||
impls["fftw"] = SizeConstraintNone;
|
impls["fftw"] = SizeConstraintNone;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_SLEEF
|
||||||
|
impls["sleef"] = SizeConstraintEvenPowerOfTwo;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_KISSFFT
|
#ifdef HAVE_KISSFFT
|
||||||
impls["kissfft"] = SizeConstraintEven;
|
impls["kissfft"] = SizeConstraintEven;
|
||||||
#endif
|
#endif
|
||||||
@@ -2310,7 +2618,7 @@ pickImplementation(int size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string preference[] = {
|
std::string preference[] = {
|
||||||
"ipp", "vdsp", "fftw", "builtin", "kissfft"
|
"ipp", "vdsp", "sleef", "fftw", "builtin", "kissfft"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < int(sizeof(preference)/sizeof(preference[0])); ++i) {
|
for (int i = 0; i < int(sizeof(preference)/sizeof(preference[0])); ++i) {
|
||||||
@@ -2390,6 +2698,10 @@ FFT::FFT(int size, int debugLevel) :
|
|||||||
} else if (impl == "fftw") {
|
} else if (impl == "fftw") {
|
||||||
#ifdef HAVE_FFTW3
|
#ifdef HAVE_FFTW3
|
||||||
d = new FFTs::D_FFTW(size);
|
d = new FFTs::D_FFTW(size);
|
||||||
|
#endif
|
||||||
|
} else if (impl == "sleef") {
|
||||||
|
#ifdef HAVE_SLEEF
|
||||||
|
d = new FFTs::D_SLEEF(size);
|
||||||
#endif
|
#endif
|
||||||
} else if (impl == "kissfft") {
|
} else if (impl == "kissfft") {
|
||||||
#ifdef HAVE_KISSFFT
|
#ifdef HAVE_KISSFFT
|
||||||
@@ -2650,6 +2962,14 @@ FFT::tune()
|
|||||||
candidates["fftw"] = d;
|
candidates["fftw"] = d;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SLEEF
|
||||||
|
os << "Constructing new SLEEF FFT object for size " << size << "..." << std::endl;
|
||||||
|
d = new FFTs::D_SLEEF(size);
|
||||||
|
d->initFloat();
|
||||||
|
d->initDouble();
|
||||||
|
candidates["sleef"] = d;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_KISSFFT
|
#ifdef HAVE_KISSFFT
|
||||||
os << "Constructing new KISSFFT object for size " << size << "..." << std::endl;
|
os << "Constructing new KISSFFT object for size " << size << "..." << std::endl;
|
||||||
d = new FFTs::D_KISSFFT(size);
|
d = new FFTs::D_KISSFFT(size);
|
||||||
|
|||||||
@@ -50,23 +50,23 @@ Profiler::m_worstCalls;
|
|||||||
static Mutex profileMutex;
|
static Mutex profileMutex;
|
||||||
|
|
||||||
void
|
void
|
||||||
Profiler::add(const char *id, float ms)
|
Profiler::add(const char *id, double us)
|
||||||
{
|
{
|
||||||
profileMutex.lock();
|
profileMutex.lock();
|
||||||
|
|
||||||
ProfileMap::iterator pmi = m_profiles.find(id);
|
ProfileMap::iterator pmi = m_profiles.find(id);
|
||||||
if (pmi != m_profiles.end()) {
|
if (pmi != m_profiles.end()) {
|
||||||
++pmi->second.first;
|
++pmi->second.first;
|
||||||
pmi->second.second += ms;
|
pmi->second.second += us;
|
||||||
} else {
|
} else {
|
||||||
m_profiles[id] = TimePair(1, ms);
|
m_profiles[id] = TimePair(1, us);
|
||||||
}
|
}
|
||||||
|
|
||||||
WorstCallMap::iterator wci = m_worstCalls.find(id);
|
WorstCallMap::iterator wci = m_worstCalls.find(id);
|
||||||
if (wci != m_worstCalls.end()) {
|
if (wci != m_worstCalls.end()) {
|
||||||
if (ms > wci->second) wci->second = ms;
|
if (us > wci->second) wci->second = us;
|
||||||
} else {
|
} else {
|
||||||
m_worstCalls[id] = ms;
|
m_worstCalls[id] = us;
|
||||||
}
|
}
|
||||||
|
|
||||||
profileMutex.unlock();
|
profileMutex.unlock();
|
||||||
@@ -95,11 +95,13 @@ Profiler::getReport()
|
|||||||
#endif
|
#endif
|
||||||
report += buffer;
|
report += buffer;
|
||||||
|
|
||||||
typedef std::multimap<float, const char *> TimeRMap;
|
typedef std::multimap<double, const char *> TimeRMap;
|
||||||
typedef std::multimap<int, const char *> IntRMap;
|
typedef std::multimap<int, const char *> IntRMap;
|
||||||
TimeRMap totmap, avgmap, worstmap;
|
TimeRMap totmap, avgmap, worstmap;
|
||||||
IntRMap ncallmap;
|
IntRMap ncallmap;
|
||||||
|
|
||||||
|
const unsigned char mu_s[] = { 0xce, 0xbc, 's', 0x0 };
|
||||||
|
|
||||||
for (ProfileMap::const_iterator i = m_profiles.begin();
|
for (ProfileMap::const_iterator i = m_profiles.begin();
|
||||||
i != m_profiles.end(); ++i) {
|
i != m_profiles.end(); ++i) {
|
||||||
totmap.insert(TimeRMap::value_type(i->second.second, i->first));
|
totmap.insert(TimeRMap::value_type(i->second.second, i->first));
|
||||||
@@ -113,38 +115,6 @@ Profiler::getReport()
|
|||||||
worstmap.insert(TimeRMap::value_type(i->second, i->first));
|
worstmap.insert(TimeRMap::value_type(i->second, i->first));
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(buffer, buflen, "\nBy total:\n");
|
|
||||||
report += buffer;
|
|
||||||
for (TimeRMap::const_iterator i = totmap.end(); i != totmap.begin(); ) {
|
|
||||||
--i;
|
|
||||||
snprintf(buffer, buflen, "%-40s %f ms\n", i->second, i->first);
|
|
||||||
report += buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(buffer, buflen, "\nBy average:\n");
|
|
||||||
report += buffer;
|
|
||||||
for (TimeRMap::const_iterator i = avgmap.end(); i != avgmap.begin(); ) {
|
|
||||||
--i;
|
|
||||||
snprintf(buffer, buflen, "%-40s %f ms\n", i->second, i->first);
|
|
||||||
report += buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(buffer, buflen, "\nBy worst case:\n");
|
|
||||||
report += buffer;
|
|
||||||
for (TimeRMap::const_iterator i = worstmap.end(); i != worstmap.begin(); ) {
|
|
||||||
--i;
|
|
||||||
snprintf(buffer, buflen, "%-40s %f ms\n", i->second, i->first);
|
|
||||||
report += buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(buffer, buflen, "\nBy number of calls:\n");
|
|
||||||
report += buffer;
|
|
||||||
for (IntRMap::const_iterator i = ncallmap.end(); i != ncallmap.begin(); ) {
|
|
||||||
--i;
|
|
||||||
snprintf(buffer, buflen, "%-40s %d\n", i->second, i->first);
|
|
||||||
report += buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(buffer, buflen, "\nBy name:\n");
|
snprintf(buffer, buflen, "\nBy name:\n");
|
||||||
report += buffer;
|
report += buffer;
|
||||||
|
|
||||||
@@ -165,15 +135,47 @@ Profiler::getReport()
|
|||||||
const TimePair &pp(j->second);
|
const TimePair &pp(j->second);
|
||||||
snprintf(buffer, buflen, "%s(%d):\n", *i, pp.first);
|
snprintf(buffer, buflen, "%s(%d):\n", *i, pp.first);
|
||||||
report += buffer;
|
report += buffer;
|
||||||
snprintf(buffer, buflen, "\tReal: \t%f ms \t[%f ms total]\n",
|
snprintf(buffer, buflen, "\tReal: \t%12f %s \t[%f %s total]\n",
|
||||||
(pp.second / pp.first),
|
(pp.second / pp.first), mu_s,
|
||||||
(pp.second));
|
(pp.second), mu_s);
|
||||||
report += buffer;
|
report += buffer;
|
||||||
|
|
||||||
WorstCallMap::const_iterator k = m_worstCalls.find(*i);
|
WorstCallMap::const_iterator k = m_worstCalls.find(*i);
|
||||||
if (k == m_worstCalls.end()) continue;
|
if (k == m_worstCalls.end()) continue;
|
||||||
|
|
||||||
snprintf(buffer, buflen, "\tWorst:\t%f ms/call\n", k->second);
|
snprintf(buffer, buflen, "\tWorst:\t%14f %s/call\n", k->second, mu_s);
|
||||||
|
report += buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buffer, buflen, "\nBy total:\n");
|
||||||
|
report += buffer;
|
||||||
|
for (TimeRMap::const_iterator i = totmap.end(); i != totmap.begin(); ) {
|
||||||
|
--i;
|
||||||
|
snprintf(buffer, buflen, "%-40s %14f %s\n", i->second, i->first, mu_s);
|
||||||
|
report += buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buffer, buflen, "\nBy average:\n");
|
||||||
|
report += buffer;
|
||||||
|
for (TimeRMap::const_iterator i = avgmap.end(); i != avgmap.begin(); ) {
|
||||||
|
--i;
|
||||||
|
snprintf(buffer, buflen, "%-40s %14f %s\n", i->second, i->first, mu_s);
|
||||||
|
report += buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buffer, buflen, "\nBy worst case:\n");
|
||||||
|
report += buffer;
|
||||||
|
for (TimeRMap::const_iterator i = worstmap.end(); i != worstmap.begin(); ) {
|
||||||
|
--i;
|
||||||
|
snprintf(buffer, buflen, "%-40s %14f %s\n", i->second, i->first, mu_s);
|
||||||
|
report += buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buffer, buflen, "\nBy number of calls:\n");
|
||||||
|
report += buffer;
|
||||||
|
for (IntRMap::const_iterator i = ncallmap.end(); i != ncallmap.begin(); ) {
|
||||||
|
--i;
|
||||||
|
snprintf(buffer, buflen, "%-40s %14d\n", i->second, i->first);
|
||||||
report += buffer;
|
report += buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,11 +188,7 @@ Profiler::Profiler(const char* c) :
|
|||||||
m_c(c),
|
m_c(c),
|
||||||
m_ended(false)
|
m_ended(false)
|
||||||
{
|
{
|
||||||
#ifdef PROFILE_CLOCKS
|
m_start = std::chrono::steady_clock::now();
|
||||||
m_start = clock();
|
|
||||||
#else
|
|
||||||
(void)gettimeofday(&m_start, 0);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Profiler::~Profiler()
|
Profiler::~Profiler()
|
||||||
@@ -201,25 +199,9 @@ Profiler::~Profiler()
|
|||||||
void
|
void
|
||||||
Profiler::end()
|
Profiler::end()
|
||||||
{
|
{
|
||||||
#ifdef PROFILE_CLOCKS
|
auto finish = std::chrono::steady_clock::now();
|
||||||
clock_t end = clock();
|
std::chrono::duration<double, std::micro> us = finish - m_start;
|
||||||
clock_t elapsed = end - m_start;
|
add(m_c, us.count());
|
||||||
float ms = float((double(elapsed) / double(CLOCKS_PER_SEC)) * 1000.0);
|
|
||||||
#else
|
|
||||||
struct timeval tv;
|
|
||||||
(void)gettimeofday(&tv, 0);
|
|
||||||
|
|
||||||
tv.tv_sec -= m_start.tv_sec;
|
|
||||||
if (tv.tv_usec < m_start.tv_usec) {
|
|
||||||
tv.tv_usec += 1000000;
|
|
||||||
tv.tv_sec -= 1;
|
|
||||||
}
|
|
||||||
tv.tv_usec -= m_start.tv_usec;
|
|
||||||
float ms = float((double(tv.tv_sec) + (double(tv.tv_usec) / 1000000.0)) * 1000.0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
add(m_c, ms);
|
|
||||||
|
|
||||||
m_ended = true;
|
m_ended = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,14 +39,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NO_TIMING
|
#ifndef NO_TIMING
|
||||||
#ifdef PROFILE_CLOCKS
|
#include <chrono>
|
||||||
#include <time.h>
|
|
||||||
#else
|
|
||||||
#include "sysutils.h"
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include <sys/time.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NO_TIMING
|
#ifndef NO_TIMING
|
||||||
@@ -75,21 +68,17 @@ public:
|
|||||||
static std::string getReport();
|
static std::string getReport();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const char* m_c;
|
const char *const m_c;
|
||||||
#ifdef PROFILE_CLOCKS
|
std::chrono::time_point<std::chrono::steady_clock> m_start;
|
||||||
clock_t m_start;
|
|
||||||
#else
|
|
||||||
struct timeval m_start;
|
|
||||||
#endif
|
|
||||||
bool m_showOnDestruct;
|
bool m_showOnDestruct;
|
||||||
bool m_ended;
|
bool m_ended;
|
||||||
|
|
||||||
typedef std::pair<int, float> TimePair;
|
typedef std::pair<int, double> TimePair;
|
||||||
typedef std::map<const char *, TimePair> ProfileMap;
|
typedef std::map<const char *, TimePair> ProfileMap;
|
||||||
typedef std::map<const char *, float> WorstCallMap;
|
typedef std::map<const char *, double> WorstCallMap;
|
||||||
static ProfileMap m_profiles;
|
static ProfileMap m_profiles;
|
||||||
static WorstCallMap m_worstCalls;
|
static WorstCallMap m_worstCalls;
|
||||||
static void add(const char *, float);
|
static void add(const char *, double);
|
||||||
};
|
};
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
#include "Allocators.h"
|
#include "Allocators.h"
|
||||||
#include "VectorOps.h"
|
#include "VectorOps.h"
|
||||||
|
|
||||||
|
#include "mathmisc.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
@@ -55,6 +57,10 @@
|
|||||||
|
|
||||||
#ifdef USE_SPEEX
|
#ifdef USE_SPEEX
|
||||||
#include "../ext/speex/speex_resampler.h"
|
#include "../ext/speex/speex_resampler.h"
|
||||||
|
#else
|
||||||
|
#ifdef HAVE_LIBSPEEXDSP
|
||||||
|
#include <speex/speex_resampler.h>
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BQRESAMPLER
|
#ifdef USE_BQRESAMPLER
|
||||||
@@ -64,6 +70,7 @@
|
|||||||
#ifndef HAVE_IPP
|
#ifndef HAVE_IPP
|
||||||
#ifndef HAVE_LIBSAMPLERATE
|
#ifndef HAVE_LIBSAMPLERATE
|
||||||
#ifndef HAVE_LIBRESAMPLE
|
#ifndef HAVE_LIBRESAMPLE
|
||||||
|
#ifndef HAVE_LIBSPEEXDSP
|
||||||
#ifndef USE_SPEEX
|
#ifndef USE_SPEEX
|
||||||
#ifndef USE_BQRESAMPLER
|
#ifndef USE_BQRESAMPLER
|
||||||
#error No resampler implementation selected!
|
#error No resampler implementation selected!
|
||||||
@@ -72,6 +79,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#define BQ_R__ R__
|
#define BQ_R__ R__
|
||||||
|
|
||||||
@@ -1106,7 +1114,7 @@ D_BQResampler::reset()
|
|||||||
|
|
||||||
#endif /* USE_BQRESAMPLER */
|
#endif /* USE_BQRESAMPLER */
|
||||||
|
|
||||||
#ifdef USE_SPEEX
|
#if defined(USE_SPEEX) || defined(HAVE_LIBSPEEXDSP)
|
||||||
|
|
||||||
class D_Speex : public Resampler::Impl
|
class D_Speex : public Resampler::Impl
|
||||||
{
|
{
|
||||||
@@ -1214,18 +1222,22 @@ D_Speex::setRatio(double ratio)
|
|||||||
// Speex wants a ratio of two unsigned integers, not a single
|
// Speex wants a ratio of two unsigned integers, not a single
|
||||||
// float. Let's do that.
|
// float. Let's do that.
|
||||||
|
|
||||||
unsigned int big = 272408136U;
|
int max_denom = 48000;
|
||||||
unsigned int denom = 1, num = 1;
|
if (ratio > 1.0) {
|
||||||
|
max_denom = int(ceil(48000 / ratio));
|
||||||
if (ratio < 1.f) {
|
|
||||||
denom = big;
|
|
||||||
double dnum = double(big) * double(ratio);
|
|
||||||
num = (unsigned int)dnum;
|
|
||||||
} else if (ratio > 1.f) {
|
|
||||||
num = big;
|
|
||||||
double ddenom = double(big) / double(ratio);
|
|
||||||
denom = (unsigned int)ddenom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int inum, idenom;
|
||||||
|
pickNearestRational(ratio, max_denom, inum, idenom);
|
||||||
|
|
||||||
|
if (inum < 0 || idenom < 0) {
|
||||||
|
cerr << "Resampler::setRatio: Internal error: "
|
||||||
|
<< "numerator or denominator < 0 ("
|
||||||
|
<< inum << "/" << idenom << ")" << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int num = inum, denom = idenom;
|
||||||
|
|
||||||
if (m_debugLevel > 1) {
|
if (m_debugLevel > 1) {
|
||||||
cerr << "D_Speex: Desired ratio " << ratio << ", requesting ratio "
|
cerr << "D_Speex: Desired ratio " << ratio << ", requesting ratio "
|
||||||
@@ -1240,8 +1252,12 @@ D_Speex::setRatio(double ratio)
|
|||||||
(m_resampler, denom, num, fromRate, toRate);
|
(m_resampler, denom, num, fromRate, toRate);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
cerr << "Resampler::Resampler: failed to set rate on Speex resampler"
|
cerr << "Resampler::Resampler: failed to set rate on Speex resampler"
|
||||||
<< endl;
|
<< " (with ratio = " << ratio << " [ratio-1 = " << ratio - 1.0
|
||||||
|
<< "], denom = " << denom
|
||||||
|
<< ", num = " << num << ", fromRate = " << fromRate
|
||||||
|
<< ", toRate = " << toRate << ", err = " << err
|
||||||
|
<< ")" << endl;
|
||||||
#ifndef NO_EXCEPTIONS
|
#ifndef NO_EXCEPTIONS
|
||||||
throw Resampler::ImplementationError;
|
throw Resampler::ImplementationError;
|
||||||
#endif
|
#endif
|
||||||
@@ -1404,6 +1420,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
#ifdef USE_SPEEX
|
#ifdef USE_SPEEX
|
||||||
m_method = 2;
|
m_method = 2;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_LIBSPEEXDSP
|
||||||
|
m_method = 2;
|
||||||
|
#endif
|
||||||
#ifdef HAVE_LIBRESAMPLE
|
#ifdef HAVE_LIBRESAMPLE
|
||||||
m_method = 3;
|
m_method = 3;
|
||||||
#endif
|
#endif
|
||||||
@@ -1425,6 +1444,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
#ifdef USE_SPEEX
|
#ifdef USE_SPEEX
|
||||||
m_method = 2;
|
m_method = 2;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_LIBSPEEXDSP
|
||||||
|
m_method = 2;
|
||||||
|
#endif
|
||||||
#ifdef USE_BQRESAMPLER
|
#ifdef USE_BQRESAMPLER
|
||||||
m_method = 4;
|
m_method = 4;
|
||||||
#endif
|
#endif
|
||||||
@@ -1443,6 +1465,9 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
#ifdef USE_SPEEX
|
#ifdef USE_SPEEX
|
||||||
m_method = 2;
|
m_method = 2;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_LIBSPEEXDSP
|
||||||
|
m_method = 2;
|
||||||
|
#endif
|
||||||
#ifdef USE_BQRESAMPLER
|
#ifdef USE_BQRESAMPLER
|
||||||
m_method = 4;
|
m_method = 4;
|
||||||
#endif
|
#endif
|
||||||
@@ -1483,7 +1508,7 @@ Resampler::Resampler(Resampler::Parameters params, int channels)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
#ifdef USE_SPEEX
|
#if defined(USE_SPEEX) || defined(HAVE_LIBSPEEXDSP)
|
||||||
d = new Resamplers::D_Speex
|
d = new Resamplers::D_Speex
|
||||||
(params.quality, params.ratioChange,
|
(params.quality, params.ratioChange,
|
||||||
channels,
|
channels,
|
||||||
|
|||||||
69
src/common/mathmisc.cpp
Normal file
69
src/common/mathmisc.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/* -*- 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-2022 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 "mathmisc.h"
|
||||||
|
|
||||||
|
namespace RubberBand {
|
||||||
|
|
||||||
|
void pickNearestRational(double ratio, int max_denom, int &num, int &denom)
|
||||||
|
{
|
||||||
|
// Farey algorithm, see
|
||||||
|
// https://www.johndcook.com/blog/2010/10/20/best-rational-approximation/
|
||||||
|
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) {
|
||||||
|
num = a + c;
|
||||||
|
denom = b + d;
|
||||||
|
return;
|
||||||
|
} else if (d > b) {
|
||||||
|
num = c;
|
||||||
|
denom = d;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
num = a;
|
||||||
|
denom = b;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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))) {
|
||||||
|
num = pc;
|
||||||
|
denom = pd;
|
||||||
|
} else {
|
||||||
|
num = pa;
|
||||||
|
denom = pb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,6 +24,8 @@
|
|||||||
#ifndef RUBBERBAND_MATHMISC_H
|
#ifndef RUBBERBAND_MATHMISC_H
|
||||||
#define RUBBERBAND_MATHMISC_H
|
#define RUBBERBAND_MATHMISC_H
|
||||||
|
|
||||||
|
#include "sysutils.h"
|
||||||
|
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
#endif // M_PI
|
#endif // M_PI
|
||||||
@@ -51,6 +53,8 @@ inline double frequencyForBin(int b, int fftSize, double sampleRate) {
|
|||||||
return (double(b) * sampleRate) / double(fftSize);
|
return (double(b) * sampleRate) / double(fftSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pickNearestRational(double ratio, int maxDenom, int &num, int &denom);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ R2Stretcher::R2Stretcher(size_t sampleRate,
|
|||||||
m_freq2(12000),
|
m_freq2(12000),
|
||||||
m_baseFftSize(m_defaultFftSize)
|
m_baseFftSize(m_defaultFftSize)
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R2Stretcher::R2Stretcher");
|
||||||
|
|
||||||
if (!_initialised) {
|
if (!_initialised) {
|
||||||
system_specific_initialise();
|
system_specific_initialise();
|
||||||
_initialised = true;
|
_initialised = true;
|
||||||
@@ -316,8 +318,7 @@ R2Stretcher::setMaxProcessSize(size_t samples)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
R2Stretcher::setKeyFrameMap(const std::map<size_t, size_t> &
|
R2Stretcher::setKeyFrameMap(const std::map<size_t, size_t> &mapping)
|
||||||
mapping)
|
|
||||||
{
|
{
|
||||||
if (m_realtime) {
|
if (m_realtime) {
|
||||||
m_log.log(0, "R2Stretcher::setKeyFrameMap: Cannot specify key frame map in RT mode");
|
m_log.log(0, "R2Stretcher::setKeyFrameMap: Cannot specify key frame map in RT mode");
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ R2Stretcher::ChannelData::construct(const std::set<size_t> &sizes,
|
|||||||
interpolator = allocate_and_zero<float>(maxSize);
|
interpolator = allocate_and_zero<float>(maxSize);
|
||||||
interpolatorScale = 0;
|
interpolatorScale = 0;
|
||||||
|
|
||||||
|
unityResetLow = 16000.f;
|
||||||
|
|
||||||
for (std::set<size_t>::const_iterator i = sizes.begin();
|
for (std::set<size_t>::const_iterator i = sizes.begin();
|
||||||
i != sizes.end(); ++i) {
|
i != sizes.end(); ++i) {
|
||||||
ffts[*i] = new FFT(*i);
|
ffts[*i] = new FFT(*i);
|
||||||
@@ -111,7 +113,7 @@ R2Stretcher::ChannelData::construct(const std::set<size_t> &sizes,
|
|||||||
|
|
||||||
void
|
void
|
||||||
R2Stretcher::ChannelData::setSizes(size_t windowSize,
|
R2Stretcher::ChannelData::setSizes(size_t windowSize,
|
||||||
size_t fftSize)
|
size_t fftSize)
|
||||||
{
|
{
|
||||||
size_t maxSize = 2 * std::max(windowSize, fftSize);
|
size_t maxSize = 2 * std::max(windowSize, fftSize);
|
||||||
size_t realSize = maxSize / 2 + 1;
|
size_t realSize = maxSize / 2 + 1;
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ public:
|
|||||||
float *ms; // only used when mid-side processing
|
float *ms; // only used when mid-side processing
|
||||||
float *interpolator; // only used when time-domain smoothing is on
|
float *interpolator; // only used when time-domain smoothing is on
|
||||||
int interpolatorScale;
|
int interpolatorScale;
|
||||||
|
float unityResetLow; // for gradual phase-reset on unity ratio
|
||||||
|
|
||||||
float *fltbuf;
|
float *fltbuf;
|
||||||
process_t *dblbuf; // owned by FFT object, only used for time domain FFT i/o
|
process_t *dblbuf; // owned by FFT object, only used for time domain FFT i/o
|
||||||
|
|||||||
@@ -128,10 +128,10 @@ R2Stretcher::resampleBeforeStretching() const
|
|||||||
|
|
||||||
void
|
void
|
||||||
R2Stretcher::prepareChannelMS(size_t c,
|
R2Stretcher::prepareChannelMS(size_t c,
|
||||||
const float *const *inputs,
|
const float *const *inputs,
|
||||||
size_t offset,
|
size_t offset,
|
||||||
size_t samples,
|
size_t samples,
|
||||||
float *prepared)
|
float *prepared)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < samples; ++i) {
|
for (size_t i = 0; i < samples; ++i) {
|
||||||
float left = inputs[0][i + offset];
|
float left = inputs[0][i + offset];
|
||||||
@@ -148,10 +148,10 @@ R2Stretcher::prepareChannelMS(size_t c,
|
|||||||
|
|
||||||
size_t
|
size_t
|
||||||
R2Stretcher::consumeChannel(size_t c,
|
R2Stretcher::consumeChannel(size_t c,
|
||||||
const float *const *inputs,
|
const float *const *inputs,
|
||||||
size_t offset,
|
size_t offset,
|
||||||
size_t samples,
|
size_t samples,
|
||||||
bool final)
|
bool final)
|
||||||
{
|
{
|
||||||
Profiler profiler("R2Stretcher::consumeChannel");
|
Profiler profiler("R2Stretcher::consumeChannel");
|
||||||
|
|
||||||
@@ -405,9 +405,9 @@ R2Stretcher::testInbufReadSpace(size_t c)
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
R2Stretcher::processChunkForChannel(size_t c,
|
R2Stretcher::processChunkForChannel(size_t c,
|
||||||
size_t phaseIncrement,
|
size_t phaseIncrement,
|
||||||
size_t shiftIncrement,
|
size_t shiftIncrement,
|
||||||
bool phaseReset)
|
bool phaseReset)
|
||||||
{
|
{
|
||||||
Profiler profiler("R2Stretcher::processChunkForChannel");
|
Profiler profiler("R2Stretcher::processChunkForChannel");
|
||||||
|
|
||||||
@@ -499,8 +499,8 @@ R2Stretcher::processChunkForChannel(size_t c,
|
|||||||
|
|
||||||
void
|
void
|
||||||
R2Stretcher::calculateIncrements(size_t &phaseIncrementRtn,
|
R2Stretcher::calculateIncrements(size_t &phaseIncrementRtn,
|
||||||
size_t &shiftIncrementRtn,
|
size_t &shiftIncrementRtn,
|
||||||
bool &phaseReset)
|
bool &phaseReset)
|
||||||
{
|
{
|
||||||
Profiler profiler("R2Stretcher::calculateIncrements");
|
Profiler profiler("R2Stretcher::calculateIncrements");
|
||||||
|
|
||||||
@@ -629,9 +629,9 @@ R2Stretcher::calculateIncrements(size_t &phaseIncrementRtn,
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
R2Stretcher::getIncrements(size_t channel,
|
R2Stretcher::getIncrements(size_t channel,
|
||||||
size_t &phaseIncrementRtn,
|
size_t &phaseIncrementRtn,
|
||||||
size_t &shiftIncrementRtn,
|
size_t &shiftIncrementRtn,
|
||||||
bool &phaseReset)
|
bool &phaseReset)
|
||||||
{
|
{
|
||||||
Profiler profiler("R2Stretcher::getIncrements");
|
Profiler profiler("R2Stretcher::getIncrements");
|
||||||
|
|
||||||
@@ -723,8 +723,8 @@ R2Stretcher::analyseChunk(size_t channel)
|
|||||||
|
|
||||||
void
|
void
|
||||||
R2Stretcher::modifyChunk(size_t channel,
|
R2Stretcher::modifyChunk(size_t channel,
|
||||||
size_t outputIncrement,
|
size_t outputIncrement,
|
||||||
bool phaseReset)
|
bool phaseReset)
|
||||||
{
|
{
|
||||||
Profiler profiler("R2Stretcher::modifyChunk");
|
Profiler profiler("R2Stretcher::modifyChunk");
|
||||||
|
|
||||||
@@ -744,12 +744,29 @@ R2Stretcher::modifyChunk(size_t channel,
|
|||||||
int bandlow = lrint((150 * m_fftSize) / rate);
|
int bandlow = lrint((150 * m_fftSize) / rate);
|
||||||
int bandhigh = lrint((1000 * m_fftSize) / rate);
|
int bandhigh = lrint((1000 * m_fftSize) / rate);
|
||||||
|
|
||||||
|
float r = getEffectiveRatio();
|
||||||
|
|
||||||
|
bool unity = (fabsf(r - 1.f) < 1.e-6f);
|
||||||
|
if (unity) {
|
||||||
|
if (!phaseReset) {
|
||||||
|
phaseReset = true;
|
||||||
|
bandlimited = true;
|
||||||
|
bandlow = lrint((cd.unityResetLow * m_fftSize) / rate);
|
||||||
|
bandhigh = count;
|
||||||
|
if (bandlow > 0) {
|
||||||
|
m_log.log(2, "unity: bandlow & high", bandlow, bandhigh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cd.unityResetLow *= 0.9f;
|
||||||
|
} else {
|
||||||
|
cd.unityResetLow = 16000.f;
|
||||||
|
}
|
||||||
|
|
||||||
float freq0 = m_freq0;
|
float freq0 = m_freq0;
|
||||||
float freq1 = m_freq1;
|
float freq1 = m_freq1;
|
||||||
float freq2 = m_freq2;
|
float freq2 = m_freq2;
|
||||||
|
|
||||||
if (laminar) {
|
if (laminar) {
|
||||||
float r = getEffectiveRatio();
|
|
||||||
if (r > 1) {
|
if (r > 1) {
|
||||||
float rf0 = 600 + (600 * ((r-1)*(r-1)*(r-1)*2));
|
float rf0 = 600 + (600 * ((r-1)*(r-1)*(r-1)*2));
|
||||||
float f1ratio = freq1 / freq0;
|
float f1ratio = freq1 / freq0;
|
||||||
@@ -923,7 +940,7 @@ R2Stretcher::formantShiftChunk(size_t channel)
|
|||||||
|
|
||||||
void
|
void
|
||||||
R2Stretcher::synthesiseChunk(size_t channel,
|
R2Stretcher::synthesiseChunk(size_t channel,
|
||||||
size_t shiftIncrement)
|
size_t shiftIncrement)
|
||||||
{
|
{
|
||||||
Profiler profiler("R2Stretcher::synthesiseChunk");
|
Profiler profiler("R2Stretcher::synthesiseChunk");
|
||||||
|
|
||||||
@@ -1085,7 +1102,9 @@ R2Stretcher::writeChunk(size_t channel, size_t shiftIncrement, bool last)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
R2Stretcher::writeOutput(RingBuffer<float> &to, float *from, size_t qty, size_t &outCount, size_t theoreticalOut)
|
R2Stretcher::writeOutput(RingBuffer<float> &to,
|
||||||
|
float *from, size_t qty,
|
||||||
|
size_t &outCount, size_t theoreticalOut)
|
||||||
{
|
{
|
||||||
Profiler profiler("R2Stretcher::writeOutput");
|
Profiler profiler("R2Stretcher::writeOutput");
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "../common/Allocators.h"
|
#include "../common/Allocators.h"
|
||||||
#include "../common/MovingMedian.h"
|
#include "../common/MovingMedian.h"
|
||||||
#include "../common/RingBuffer.h"
|
#include "../common/RingBuffer.h"
|
||||||
|
#include "../common/Profiler.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -97,6 +98,8 @@ public:
|
|||||||
void classify(const process_t *const mag, // input, of at least binCount bins
|
void classify(const process_t *const mag, // input, of at least binCount bins
|
||||||
Classification *classification) // output, of binCount bins
|
Classification *classification) // output, of binCount bins
|
||||||
{
|
{
|
||||||
|
Profiler profiler("BinClassifier::classify");
|
||||||
|
|
||||||
const int n = m_parameters.binCount;
|
const int n = m_parameters.binCount;
|
||||||
|
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "../common/HistogramFilter.h"
|
#include "../common/HistogramFilter.h"
|
||||||
#include "../common/mathmisc.h"
|
#include "../common/mathmisc.h"
|
||||||
|
#include "../common/Profiler.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -65,6 +66,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Segmentation segment(const BinClassifier::Classification *classification) {
|
Segmentation segment(const BinClassifier::Classification *classification) {
|
||||||
|
|
||||||
|
Profiler profiler("BinSegmenter::segment");
|
||||||
|
|
||||||
int n = m_parameters.binCount;
|
int n = m_parameters.binCount;
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
switch (classification[i]) {
|
switch (classification[i]) {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#define RUBBERBAND_GUIDE_H
|
#define RUBBERBAND_GUIDE_H
|
||||||
|
|
||||||
#include "../common/Log.h"
|
#include "../common/Log.h"
|
||||||
|
#include "../common/Profiler.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -68,7 +69,9 @@ public:
|
|||||||
|
|
||||||
struct Guidance {
|
struct Guidance {
|
||||||
FftBand fftBands[3];
|
FftBand fftBands[3];
|
||||||
|
int fftBandCount;
|
||||||
PhaseLockBand phaseLockBands[4];
|
PhaseLockBand phaseLockBands[4];
|
||||||
|
int phaseLockBandCount;
|
||||||
Range kick;
|
Range kick;
|
||||||
Range preKick;
|
Range preKick;
|
||||||
Range highUnlocked;
|
Range highUnlocked;
|
||||||
@@ -95,50 +98,92 @@ public:
|
|||||||
int shortestFftSize;
|
int shortestFftSize;
|
||||||
int classificationFftSize;
|
int classificationFftSize;
|
||||||
BandLimits fftBandLimits[3];
|
BandLimits fftBandLimits[3];
|
||||||
Configuration(int _longestFftSize, int _shortestFftSize,
|
int fftBandLimitCount;
|
||||||
int _classificationFftSize) :
|
Configuration() :
|
||||||
longestFftSize(_longestFftSize),
|
longestFftSize(0), shortestFftSize(0), classificationFftSize(0),
|
||||||
shortestFftSize(_shortestFftSize),
|
fftBandLimitCount(0) { }
|
||||||
classificationFftSize(_classificationFftSize) { }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
double sampleRate;
|
double sampleRate;
|
||||||
Parameters(double _sampleRate) : sampleRate(_sampleRate) { }
|
bool singleWindowMode;
|
||||||
|
Parameters(double _sampleRate, bool _singleWindow) :
|
||||||
|
sampleRate(_sampleRate),
|
||||||
|
singleWindowMode(_singleWindow) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
Guide(Parameters parameters, Log log) :
|
Guide(Parameters parameters, Log log) :
|
||||||
m_parameters(parameters),
|
m_parameters(parameters),
|
||||||
m_log(log),
|
m_log(log)
|
||||||
m_configuration(roundUp(int(ceil(parameters.sampleRate / 16.0))),
|
|
||||||
roundUp(int(ceil(parameters.sampleRate / 64.0))),
|
|
||||||
roundUp(int(ceil(parameters.sampleRate / 32.0)))),
|
|
||||||
m_minLower(500.0), m_minHigher(4000.0),
|
|
||||||
m_defaultLower(700.0), m_defaultHigher(4800.0),
|
|
||||||
m_maxLower(1100.0), m_maxHigher(7000.0)
|
|
||||||
{
|
{
|
||||||
double rate = m_parameters.sampleRate;
|
double rate = m_parameters.sampleRate;
|
||||||
|
double nyquist = rate / 2.0;
|
||||||
|
|
||||||
m_log.log(1, "Guide: rate", rate);
|
m_log.log(1, "Guide: rate and single-window mode",
|
||||||
|
rate, m_parameters.singleWindowMode);
|
||||||
int bandFftSize = roundUp(int(ceil(rate/16.0)));
|
|
||||||
m_configuration.fftBandLimits[0] =
|
|
||||||
BandLimits(bandFftSize, rate, 0.0, m_maxLower);
|
|
||||||
|
|
||||||
// This is the classification and fallback FFT: we need it to
|
|
||||||
// go up to Nyquist so we can seamlessly switch to it for
|
|
||||||
// longer stretches, and down to 0.0 so we can use it for
|
|
||||||
// unity in offline mode
|
|
||||||
bandFftSize = roundUp(int(ceil(rate/32.0)));
|
|
||||||
m_configuration.fftBandLimits[1] =
|
|
||||||
BandLimits(bandFftSize, rate, 0.0, rate / 2.0);
|
|
||||||
|
|
||||||
bandFftSize = roundUp(int(ceil(rate/64.0)));
|
|
||||||
m_configuration.fftBandLimits[2] =
|
|
||||||
BandLimits(bandFftSize, rate, m_minHigher, rate/2.0);
|
|
||||||
|
|
||||||
|
int classificationFftSize =
|
||||||
|
roundUp(int(ceil(parameters.sampleRate / 32.0)));
|
||||||
|
|
||||||
|
m_configuration.classificationFftSize = classificationFftSize;
|
||||||
|
|
||||||
m_log.log(1, "Guide: classification FFT size",
|
m_log.log(1, "Guide: classification FFT size",
|
||||||
m_configuration.classificationFftSize);
|
m_configuration.classificationFftSize);
|
||||||
|
|
||||||
|
if (m_parameters.singleWindowMode) {
|
||||||
|
|
||||||
|
// Single-window mode
|
||||||
|
|
||||||
|
m_configuration.longestFftSize = classificationFftSize;
|
||||||
|
m_configuration.shortestFftSize = classificationFftSize;
|
||||||
|
|
||||||
|
m_defaultLower = nyquist;
|
||||||
|
m_minLower = m_defaultLower;
|
||||||
|
m_maxLower = m_defaultLower;
|
||||||
|
|
||||||
|
m_defaultHigher = nyquist;
|
||||||
|
m_minHigher = m_defaultHigher;
|
||||||
|
m_maxHigher = m_defaultHigher;
|
||||||
|
|
||||||
|
m_configuration.fftBandLimitCount = 1;
|
||||||
|
|
||||||
|
m_configuration.fftBandLimits[0] =
|
||||||
|
BandLimits(classificationFftSize, rate, 0.0, nyquist);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// The normal multi-window mode
|
||||||
|
|
||||||
|
m_configuration.longestFftSize = classificationFftSize * 2;
|
||||||
|
m_configuration.shortestFftSize = classificationFftSize / 2;
|
||||||
|
|
||||||
|
m_defaultLower = 700.0;
|
||||||
|
m_minLower = 500.0;
|
||||||
|
m_maxLower = 1100.0;
|
||||||
|
|
||||||
|
m_defaultHigher = 4800.0;
|
||||||
|
m_minHigher = 4000.0;
|
||||||
|
m_maxHigher = 7000.0;
|
||||||
|
|
||||||
|
m_configuration.fftBandLimitCount = 3;
|
||||||
|
|
||||||
|
m_configuration.fftBandLimits[0] =
|
||||||
|
BandLimits(m_configuration.longestFftSize,
|
||||||
|
rate, 0.0, m_maxLower);
|
||||||
|
|
||||||
|
// This is the classification and fallback FFT: we need it
|
||||||
|
// to go up to Nyquist so we can seamlessly switch to it
|
||||||
|
// for longer stretches, and down to 0.0 so we can use it
|
||||||
|
// for unity in offline mode
|
||||||
|
|
||||||
|
m_configuration.fftBandLimits[1] =
|
||||||
|
BandLimits(classificationFftSize,
|
||||||
|
rate, 0.0, nyquist);
|
||||||
|
|
||||||
|
m_configuration.fftBandLimits[2] =
|
||||||
|
BandLimits(m_configuration.shortestFftSize,
|
||||||
|
rate, m_minHigher, nyquist);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Configuration &getConfiguration() const {
|
const Configuration &getConfiguration() const {
|
||||||
@@ -159,6 +204,8 @@ public:
|
|||||||
bool tighterChannelLock,
|
bool tighterChannelLock,
|
||||||
Guidance &guidance) const {
|
Guidance &guidance) const {
|
||||||
|
|
||||||
|
Profiler profiler("Guide::updateGuidance");
|
||||||
|
|
||||||
bool hadPhaseReset = guidance.phaseReset.present;
|
bool hadPhaseReset = guidance.phaseReset.present;
|
||||||
|
|
||||||
guidance.phaseReset.present = false;
|
guidance.phaseReset.present = false;
|
||||||
@@ -168,13 +215,58 @@ public:
|
|||||||
guidance.channelLock.present = false;
|
guidance.channelLock.present = false;
|
||||||
|
|
||||||
double nyquist = m_parameters.sampleRate / 2.0;
|
double nyquist = m_parameters.sampleRate / 2.0;
|
||||||
guidance.fftBands[0].fftSize = roundUp(int(ceil(nyquist/8.0)));
|
|
||||||
guidance.fftBands[1].fftSize = roundUp(int(ceil(nyquist/16.0)));
|
|
||||||
guidance.fftBands[2].fftSize = roundUp(int(ceil(nyquist/32.0)));
|
|
||||||
|
|
||||||
// This is a vital stop case for PhaseAdvance
|
if (m_parameters.singleWindowMode) {
|
||||||
guidance.phaseLockBands[3].f1 = nyquist;
|
|
||||||
|
|
||||||
|
// All the fft and phase-lock bands are fixed in this
|
||||||
|
// mode. We'll still need to continue to set up phase
|
||||||
|
// reset ranges etc, including the unity case.
|
||||||
|
|
||||||
|
guidance.fftBandCount = 1;
|
||||||
|
guidance.fftBands[0].fftSize = m_configuration.classificationFftSize;
|
||||||
|
guidance.fftBands[0].f0 = 0.0;
|
||||||
|
guidance.fftBands[0].f1 = nyquist;
|
||||||
|
|
||||||
|
guidance.phaseLockBandCount = 3;
|
||||||
|
|
||||||
|
guidance.phaseLockBands[0].p = 1;
|
||||||
|
guidance.phaseLockBands[0].beta = betaFor(1200.0, ratio);
|
||||||
|
guidance.phaseLockBands[0].f0 = 0.0;
|
||||||
|
guidance.phaseLockBands[0].f1 = 1600.0;
|
||||||
|
|
||||||
|
guidance.phaseLockBands[1].p = 2;
|
||||||
|
guidance.phaseLockBands[1].beta = betaFor(4800.0, ratio);
|
||||||
|
guidance.phaseLockBands[1].f0 = 1600.0;
|
||||||
|
guidance.phaseLockBands[1].f1 = 7000.0;
|
||||||
|
|
||||||
|
guidance.phaseLockBands[2].p = 5;
|
||||||
|
guidance.phaseLockBands[2].beta = betaFor(10000.0, ratio);
|
||||||
|
guidance.phaseLockBands[2].f0 = 7000.0;
|
||||||
|
guidance.phaseLockBands[2].f1 = nyquist;
|
||||||
|
|
||||||
|
if (outhop > 256) {
|
||||||
|
guidance.phaseLockBands[2].p = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// The normal multi-window mode
|
||||||
|
|
||||||
|
guidance.fftBandCount = 3;
|
||||||
|
guidance.fftBands[0].fftSize = m_configuration.longestFftSize;
|
||||||
|
guidance.fftBands[1].fftSize = m_configuration.classificationFftSize;
|
||||||
|
guidance.fftBands[2].fftSize = m_configuration.shortestFftSize;
|
||||||
|
|
||||||
|
guidance.phaseLockBandCount = 4;
|
||||||
|
|
||||||
|
// This is a vital stop case for PhaseAdvance
|
||||||
|
guidance.phaseLockBands[3].f1 = nyquist;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've set the counts, and for single-window mode we've set
|
||||||
|
// the band ranges as well - in normal multi-window mode we
|
||||||
|
// still have to do that, but we should do these first
|
||||||
|
|
||||||
if (meanMagnitude < 1.0e-6) {
|
if (meanMagnitude < 1.0e-6) {
|
||||||
updateForSilence(guidance);
|
updateForSilence(guidance);
|
||||||
return;
|
return;
|
||||||
@@ -183,8 +275,6 @@ public:
|
|||||||
if (unityCount > 0) {
|
if (unityCount > 0) {
|
||||||
updateForUnity(guidance,
|
updateForUnity(guidance,
|
||||||
hadPhaseReset,
|
hadPhaseReset,
|
||||||
unityCount,
|
|
||||||
magnitudes,
|
|
||||||
segmentation,
|
segmentation,
|
||||||
realtime);
|
realtime);
|
||||||
return;
|
return;
|
||||||
@@ -199,15 +289,29 @@ public:
|
|||||||
guidance.channelLock.f1 = 600.0;
|
guidance.channelLock.f1 = 600.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool kick =
|
if (!m_parameters.singleWindowMode) {
|
||||||
(segmentation.percussiveBelow > 40.0) &&
|
|
||||||
(prevSegmentation.percussiveBelow < 40.0) &&
|
bool kick =
|
||||||
checkPotentialKick(magnitudes, prevMagnitudes);
|
(segmentation.percussiveBelow > 40.0) &&
|
||||||
|
(prevSegmentation.percussiveBelow < 40.0) &&
|
||||||
|
checkPotentialKick(magnitudes, prevMagnitudes);
|
||||||
|
|
||||||
bool futureKick = !kick &&
|
bool futureKick = !kick &&
|
||||||
(nextSegmentation.percussiveBelow > 40.0) &&
|
(nextSegmentation.percussiveBelow > 40.0) &&
|
||||||
(segmentation.percussiveBelow < 40.0) &&
|
(segmentation.percussiveBelow < 40.0) &&
|
||||||
checkPotentialKick(nextMagnitudes, magnitudes);
|
checkPotentialKick(nextMagnitudes, magnitudes);
|
||||||
|
|
||||||
|
if (kick) {
|
||||||
|
guidance.kick.present = true;
|
||||||
|
guidance.kick.f0 = 0.0;
|
||||||
|
guidance.kick.f1 = segmentation.percussiveBelow;
|
||||||
|
} else if (futureKick) {
|
||||||
|
guidance.preKick.present = true;
|
||||||
|
guidance.preKick.f0 = 0.0;
|
||||||
|
guidance.preKick.f1 = nextSegmentation.percussiveBelow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
std::cout << "d:"
|
std::cout << "d:"
|
||||||
<< prevSegmentation.percussiveBelow << ","
|
<< prevSegmentation.percussiveBelow << ","
|
||||||
@@ -218,15 +322,6 @@ public:
|
|||||||
<< (kick ? "K" : "N") << ","
|
<< (kick ? "K" : "N") << ","
|
||||||
<< (futureKick ? "F" : "N") << std::endl;
|
<< (futureKick ? "F" : "N") << std::endl;
|
||||||
*/
|
*/
|
||||||
if (kick) {
|
|
||||||
guidance.kick.present = true;
|
|
||||||
guidance.kick.f0 = 0.0;
|
|
||||||
guidance.kick.f1 = segmentation.percussiveBelow;
|
|
||||||
} else if (futureKick) {
|
|
||||||
guidance.preKick.present = true;
|
|
||||||
guidance.preKick.f0 = 0.0;
|
|
||||||
guidance.preKick.f1 = nextSegmentation.percussiveBelow;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (segmentation.residualAbove > segmentation.percussiveAbove) {
|
if (segmentation.residualAbove > segmentation.percussiveAbove) {
|
||||||
guidance.highUnlocked.present = true;
|
guidance.highUnlocked.present = true;
|
||||||
@@ -249,60 +344,64 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double prevLower = guidance.fftBands[0].f1;
|
if (!m_parameters.singleWindowMode) {
|
||||||
double lower = descendToValley(prevLower, magnitudes);
|
|
||||||
if (lower > m_maxLower || lower < m_minLower) {
|
// The normal multi-window mode. For single-window we did
|
||||||
lower = m_defaultLower;
|
// this already.
|
||||||
|
|
||||||
|
double prevLower = guidance.fftBands[0].f1;
|
||||||
|
double lower = descendToValley(prevLower, magnitudes);
|
||||||
|
if (lower > m_maxLower || lower < m_minLower) {
|
||||||
|
lower = m_defaultLower;
|
||||||
|
}
|
||||||
|
|
||||||
|
double prevHigher = guidance.fftBands[1].f1;
|
||||||
|
double higher = descendToValley(prevHigher, magnitudes);
|
||||||
|
if (higher > m_maxHigher || higher < m_minHigher) {
|
||||||
|
higher = m_defaultHigher;
|
||||||
|
}
|
||||||
|
|
||||||
|
guidance.fftBands[0].f0 = 0.0;
|
||||||
|
guidance.fftBands[0].f1 = lower;
|
||||||
|
|
||||||
|
guidance.fftBands[1].f0 = lower;
|
||||||
|
guidance.fftBands[1].f1 = higher;
|
||||||
|
|
||||||
|
guidance.fftBands[2].f0 = higher;
|
||||||
|
guidance.fftBands[2].f1 = nyquist;
|
||||||
|
|
||||||
|
if (outhop > 256) {
|
||||||
|
guidance.fftBands[1].f1 = nyquist;
|
||||||
|
guidance.fftBands[2].f0 = nyquist;
|
||||||
|
}
|
||||||
|
|
||||||
|
double mid = std::max(lower, 1600.0);
|
||||||
|
|
||||||
|
guidance.phaseLockBands[0].p = 1;
|
||||||
|
guidance.phaseLockBands[0].beta = betaFor(300.0, ratio);
|
||||||
|
guidance.phaseLockBands[0].f0 = 0.0;
|
||||||
|
guidance.phaseLockBands[0].f1 = lower;
|
||||||
|
|
||||||
|
guidance.phaseLockBands[1].p = 2;
|
||||||
|
guidance.phaseLockBands[1].beta = betaFor(1600.0, ratio);
|
||||||
|
guidance.phaseLockBands[1].f0 = lower;
|
||||||
|
guidance.phaseLockBands[1].f1 = mid;
|
||||||
|
|
||||||
|
guidance.phaseLockBands[2].p = 3;
|
||||||
|
guidance.phaseLockBands[2].beta = betaFor(4800.0, ratio);
|
||||||
|
guidance.phaseLockBands[2].f0 = mid;
|
||||||
|
guidance.phaseLockBands[2].f1 = higher;
|
||||||
|
|
||||||
|
guidance.phaseLockBands[3].p = 4;
|
||||||
|
guidance.phaseLockBands[3].beta = betaFor(10000.0, ratio);
|
||||||
|
guidance.phaseLockBands[3].f0 = higher;
|
||||||
|
guidance.phaseLockBands[3].f1 = nyquist;
|
||||||
|
|
||||||
|
if (outhop > 256) {
|
||||||
|
guidance.phaseLockBands[3].p = 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double prevHigher = guidance.fftBands[1].f1;
|
|
||||||
double higher = descendToValley(prevHigher, magnitudes);
|
|
||||||
if (higher > m_maxHigher || higher < m_minHigher) {
|
|
||||||
higher = m_defaultHigher;
|
|
||||||
}
|
|
||||||
|
|
||||||
guidance.fftBands[0].f0 = 0.0;
|
|
||||||
guidance.fftBands[0].f1 = lower;
|
|
||||||
|
|
||||||
// std::cout << "x:" << lower << std::endl;
|
|
||||||
|
|
||||||
guidance.fftBands[1].f0 = lower;
|
|
||||||
guidance.fftBands[1].f1 = higher;
|
|
||||||
|
|
||||||
guidance.fftBands[2].f0 = higher;
|
|
||||||
guidance.fftBands[2].f1 = nyquist;
|
|
||||||
|
|
||||||
if (outhop > 256) {
|
|
||||||
guidance.fftBands[1].f1 = nyquist;
|
|
||||||
guidance.fftBands[2].f0 = nyquist;
|
|
||||||
}
|
|
||||||
|
|
||||||
double mid = std::max(lower, 1600.0);
|
|
||||||
|
|
||||||
guidance.phaseLockBands[0].p = 1;
|
|
||||||
guidance.phaseLockBands[0].beta = betaFor(300.0, ratio);
|
|
||||||
guidance.phaseLockBands[0].f0 = 0.0;
|
|
||||||
guidance.phaseLockBands[0].f1 = lower;
|
|
||||||
|
|
||||||
guidance.phaseLockBands[1].p = 2;
|
|
||||||
guidance.phaseLockBands[1].beta = betaFor(1600.0, ratio);
|
|
||||||
guidance.phaseLockBands[1].f0 = lower;
|
|
||||||
guidance.phaseLockBands[1].f1 = mid;
|
|
||||||
|
|
||||||
guidance.phaseLockBands[2].p = 3;
|
|
||||||
guidance.phaseLockBands[2].beta = betaFor(5000.0, ratio);
|
|
||||||
guidance.phaseLockBands[2].f0 = mid;
|
|
||||||
guidance.phaseLockBands[2].f1 = higher;
|
|
||||||
|
|
||||||
guidance.phaseLockBands[3].p = 4;
|
|
||||||
guidance.phaseLockBands[3].beta = betaFor(10000.0, ratio);
|
|
||||||
guidance.phaseLockBands[3].f0 = higher;
|
|
||||||
guidance.phaseLockBands[3].f1 = nyquist;
|
|
||||||
|
|
||||||
if (outhop > 256) {
|
|
||||||
guidance.phaseLockBands[3].p = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ratio > 2.0) {
|
if (ratio > 2.0) {
|
||||||
|
|
||||||
// For very long stretches, diffuse is better than
|
// For very long stretches, diffuse is better than
|
||||||
@@ -326,7 +425,7 @@ public:
|
|||||||
guidance.highUnlocked.present = true;
|
guidance.highUnlocked.present = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
std::ostringstream str;
|
std::ostringstream str;
|
||||||
str << "Guidance: FFT bands: ["
|
str << "Guidance: FFT bands: ["
|
||||||
<< guidance.fftBands[0].fftSize << " from "
|
<< guidance.fftBands[0].fftSize << " from "
|
||||||
@@ -341,8 +440,9 @@ public:
|
|||||||
<< guidance.phaseReset.present << " from "
|
<< guidance.phaseReset.present << " from "
|
||||||
<< guidance.phaseReset.f0 << " to " << guidance.phaseReset.f1
|
<< guidance.phaseReset.f0 << " to " << guidance.phaseReset.f1
|
||||||
<< "]" << std::endl;
|
<< "]" << std::endl;
|
||||||
m_parameters.logger(str.str());
|
|
||||||
*/
|
m_log.log(1, str.str().c_str());
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDebugLevel(int level) {
|
void setDebugLevel(int level) {
|
||||||
@@ -374,12 +474,14 @@ protected:
|
|||||||
void updateForSilence(Guidance &guidance) const {
|
void updateForSilence(Guidance &guidance) const {
|
||||||
// std::cout << "phase reset on silence" << std::endl;
|
// std::cout << "phase reset on silence" << std::endl;
|
||||||
double nyquist = m_parameters.sampleRate / 2.0;
|
double nyquist = m_parameters.sampleRate / 2.0;
|
||||||
guidance.fftBands[0].f0 = 0.0;
|
if (!m_parameters.singleWindowMode) {
|
||||||
guidance.fftBands[0].f1 = 0.0;
|
guidance.fftBands[0].f0 = 0.0;
|
||||||
guidance.fftBands[1].f0 = 0.0;
|
guidance.fftBands[0].f1 = 0.0;
|
||||||
guidance.fftBands[1].f1 = nyquist;
|
guidance.fftBands[1].f0 = 0.0;
|
||||||
guidance.fftBands[2].f0 = nyquist;
|
guidance.fftBands[1].f1 = nyquist;
|
||||||
guidance.fftBands[2].f1 = nyquist;
|
guidance.fftBands[2].f0 = nyquist;
|
||||||
|
guidance.fftBands[2].f1 = nyquist;
|
||||||
|
}
|
||||||
guidance.phaseReset.present = true;
|
guidance.phaseReset.present = true;
|
||||||
guidance.phaseReset.f0 = 0.0;
|
guidance.phaseReset.f0 = 0.0;
|
||||||
guidance.phaseReset.f1 = nyquist;
|
guidance.phaseReset.f1 = nyquist;
|
||||||
@@ -387,8 +489,6 @@ protected:
|
|||||||
|
|
||||||
void updateForUnity(Guidance &guidance,
|
void updateForUnity(Guidance &guidance,
|
||||||
bool hadPhaseReset,
|
bool hadPhaseReset,
|
||||||
uint32_t /* unityCount */,
|
|
||||||
const process_t *const /* magnitudes */,
|
|
||||||
const BinSegmenter::Segmentation &segmentation,
|
const BinSegmenter::Segmentation &segmentation,
|
||||||
bool realtime) const {
|
bool realtime) const {
|
||||||
|
|
||||||
@@ -399,25 +499,29 @@ protected:
|
|||||||
if (!realtime) {
|
if (!realtime) {
|
||||||
// ratio can't change, so we are just running 1.0 ratio
|
// ratio can't change, so we are just running 1.0 ratio
|
||||||
// throughout
|
// throughout
|
||||||
guidance.fftBands[0].f0 = 0.0;
|
if (!m_parameters.singleWindowMode) {
|
||||||
guidance.fftBands[0].f1 = 0.0;
|
guidance.fftBands[0].f0 = 0.0;
|
||||||
guidance.fftBands[1].f0 = 0.0;
|
guidance.fftBands[0].f1 = 0.0;
|
||||||
guidance.fftBands[1].f1 = nyquist;
|
guidance.fftBands[1].f0 = 0.0;
|
||||||
guidance.fftBands[2].f0 = nyquist;
|
guidance.fftBands[1].f1 = nyquist;
|
||||||
guidance.fftBands[2].f1 = nyquist;
|
guidance.fftBands[2].f0 = nyquist;
|
||||||
|
guidance.fftBands[2].f1 = nyquist;
|
||||||
|
}
|
||||||
guidance.phaseReset.present = true;
|
guidance.phaseReset.present = true;
|
||||||
guidance.phaseReset.f0 = 0.0;
|
guidance.phaseReset.f0 = 0.0;
|
||||||
guidance.phaseReset.f1 = nyquist;
|
guidance.phaseReset.f1 = nyquist;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
guidance.fftBands[0].f0 = 0.0;
|
if (!m_parameters.singleWindowMode) {
|
||||||
guidance.fftBands[0].f1 = m_minLower;
|
guidance.fftBands[0].f0 = 0.0;
|
||||||
guidance.fftBands[1].f0 = m_minLower;
|
guidance.fftBands[0].f1 = m_minLower;
|
||||||
guidance.fftBands[1].f1 = m_minHigher;
|
guidance.fftBands[1].f0 = m_minLower;
|
||||||
guidance.fftBands[2].f0 = m_minHigher;
|
guidance.fftBands[1].f1 = m_minHigher;
|
||||||
guidance.fftBands[2].f1 = nyquist;
|
guidance.fftBands[2].f0 = m_minHigher;
|
||||||
|
guidance.fftBands[2].f1 = nyquist;
|
||||||
|
}
|
||||||
|
|
||||||
guidance.phaseReset.present = true;
|
guidance.phaseReset.present = true;
|
||||||
|
|
||||||
if (!hadPhaseReset) {
|
if (!hadPhaseReset) {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "../common/Log.h"
|
#include "../common/Log.h"
|
||||||
#include "../common/mathmisc.h"
|
#include "../common/mathmisc.h"
|
||||||
|
#include "../common/Profiler.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -42,8 +43,11 @@ public:
|
|||||||
int fftSize;
|
int fftSize;
|
||||||
double sampleRate;
|
double sampleRate;
|
||||||
int channels;
|
int channels;
|
||||||
Parameters(int _fftSize, double _sampleRate, int _channels) :
|
bool singleWindowMode;
|
||||||
fftSize(_fftSize), sampleRate(_sampleRate), channels(_channels) { }
|
Parameters(int _fftSize, double _sampleRate, int _channels,
|
||||||
|
bool _singleWindow) :
|
||||||
|
fftSize(_fftSize), sampleRate(_sampleRate), channels(_channels),
|
||||||
|
singleWindowMode(_singleWindow) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
GuidedPhaseAdvance(Parameters parameters, Log log) :
|
GuidedPhaseAdvance(Parameters parameters, Log log) :
|
||||||
@@ -93,6 +97,8 @@ public:
|
|||||||
int inhop,
|
int inhop,
|
||||||
int outhop) {
|
int outhop) {
|
||||||
|
|
||||||
|
Profiler profiler("GuidedPhaseAdvance::advance");
|
||||||
|
|
||||||
int myFftBand = 0;
|
int myFftBand = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const auto &fband : guidance[0]->fftBands) {
|
for (const auto &fband : guidance[0]->fftBands) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "R3Stretcher.h"
|
#include "R3Stretcher.h"
|
||||||
|
|
||||||
#include "../common/VectorOpsComplex.h"
|
#include "../common/VectorOpsComplex.h"
|
||||||
|
#include "../common/Profiler.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
@@ -34,13 +35,18 @@ R3Stretcher::R3Stretcher(Parameters parameters,
|
|||||||
double initialPitchScale,
|
double initialPitchScale,
|
||||||
Log log) :
|
Log log) :
|
||||||
m_parameters(parameters),
|
m_parameters(parameters),
|
||||||
|
m_limits(parameters.options),
|
||||||
m_log(log),
|
m_log(log),
|
||||||
m_timeRatio(initialTimeRatio),
|
m_timeRatio(initialTimeRatio),
|
||||||
m_pitchScale(initialPitchScale),
|
m_pitchScale(initialPitchScale),
|
||||||
m_formantScale(0.0),
|
m_formantScale(0.0),
|
||||||
m_guide(Guide::Parameters(m_parameters.sampleRate), m_log),
|
m_guide(Guide::Parameters
|
||||||
|
(m_parameters.sampleRate,
|
||||||
|
m_parameters.options & RubberBandStretcher::OptionWindowShort),
|
||||||
|
m_log),
|
||||||
m_guideConfiguration(m_guide.getConfiguration()),
|
m_guideConfiguration(m_guide.getConfiguration()),
|
||||||
m_channelAssembly(m_parameters.channels),
|
m_channelAssembly(m_parameters.channels),
|
||||||
|
m_useReadahead(true),
|
||||||
m_inhop(1),
|
m_inhop(1),
|
||||||
m_prevInhop(1),
|
m_prevInhop(1),
|
||||||
m_prevOuthop(1),
|
m_prevOuthop(1),
|
||||||
@@ -54,11 +60,23 @@ R3Stretcher::R3Stretcher(Parameters parameters,
|
|||||||
m_totalOutputDuration(0),
|
m_totalOutputDuration(0),
|
||||||
m_mode(ProcessMode::JustCreated)
|
m_mode(ProcessMode::JustCreated)
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R3Stretcher::R3Stretcher");
|
||||||
|
|
||||||
m_log.log(1, "R3Stretcher::R3Stretcher: rate, options",
|
m_log.log(1, "R3Stretcher::R3Stretcher: rate, options",
|
||||||
m_parameters.sampleRate, m_parameters.options);
|
m_parameters.sampleRate, m_parameters.options);
|
||||||
m_log.log(1, "R3Stretcher::R3Stretcher: initial time ratio and pitch scale",
|
m_log.log(1, "R3Stretcher::R3Stretcher: initial time ratio and pitch scale",
|
||||||
m_timeRatio, m_pitchScale);
|
m_timeRatio, m_pitchScale);
|
||||||
|
|
||||||
|
if (isRealTime()) {
|
||||||
|
m_log.log(1, "R3Stretcher::R3Stretcher: real-time mode");
|
||||||
|
} else {
|
||||||
|
m_log.log(1, "R3Stretcher::R3Stretcher: offline mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSingleWindowed()) {
|
||||||
|
m_log.log(1, "R3Stretcher::R3Stretcher: intermediate shorter-window mode requested");
|
||||||
|
}
|
||||||
|
|
||||||
double maxClassifierFrequency = 16000.0;
|
double maxClassifierFrequency = 16000.0;
|
||||||
if (maxClassifierFrequency > m_parameters.sampleRate/2) {
|
if (maxClassifierFrequency > m_parameters.sampleRate/2) {
|
||||||
maxClassifierFrequency = m_parameters.sampleRate/2;
|
maxClassifierFrequency = m_parameters.sampleRate/2;
|
||||||
@@ -74,17 +92,23 @@ R3Stretcher::R3Stretcher(Parameters parameters,
|
|||||||
BinClassifier::Parameters classifierParameters
|
BinClassifier::Parameters classifierParameters
|
||||||
(classificationBins, 9, 1, 10, 2.0, 2.0);
|
(classificationBins, 9, 1, 10, 2.0, 2.0);
|
||||||
|
|
||||||
int inRingBufferSize = m_guideConfiguration.longestFftSize * 2;
|
if (isSingleWindowed()) {
|
||||||
int outRingBufferSize = m_guideConfiguration.longestFftSize * 16;
|
classifierParameters.horizontalFilterLength = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
int inRingBufferSize = getWindowSourceSize() * 2;
|
||||||
|
int outRingBufferSize = getWindowSourceSize() * 16;
|
||||||
|
|
||||||
for (int c = 0; c < m_parameters.channels; ++c) {
|
for (int c = 0; c < m_parameters.channels; ++c) {
|
||||||
m_channelData.push_back(std::make_shared<ChannelData>
|
m_channelData.push_back(std::make_shared<ChannelData>
|
||||||
(segmenterParameters,
|
(segmenterParameters,
|
||||||
classifierParameters,
|
classifierParameters,
|
||||||
m_guideConfiguration.longestFftSize,
|
m_guideConfiguration.longestFftSize,
|
||||||
|
getWindowSourceSize(),
|
||||||
inRingBufferSize,
|
inRingBufferSize,
|
||||||
outRingBufferSize));
|
outRingBufferSize));
|
||||||
for (auto band: m_guideConfiguration.fftBandLimits) {
|
for (int b = 0; b < m_guideConfiguration.fftBandLimitCount; ++b) {
|
||||||
|
const auto &band = m_guideConfiguration.fftBandLimits[b];
|
||||||
int fftSize = band.fftSize;
|
int fftSize = band.fftSize;
|
||||||
m_channelData[c]->scales[fftSize] =
|
m_channelData[c]->scales[fftSize] =
|
||||||
std::make_shared<ChannelScaleData>
|
std::make_shared<ChannelScaleData>
|
||||||
@@ -92,10 +116,12 @@ R3Stretcher::R3Stretcher(Parameters parameters,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto band: m_guideConfiguration.fftBandLimits) {
|
for (int b = 0; b < m_guideConfiguration.fftBandLimitCount; ++b) {
|
||||||
|
const auto &band = m_guideConfiguration.fftBandLimits[b];
|
||||||
int fftSize = band.fftSize;
|
int fftSize = band.fftSize;
|
||||||
GuidedPhaseAdvance::Parameters guidedParameters
|
GuidedPhaseAdvance::Parameters guidedParameters
|
||||||
(fftSize, m_parameters.sampleRate, m_parameters.channels);
|
(fftSize, m_parameters.sampleRate, m_parameters.channels,
|
||||||
|
isSingleWindowed());
|
||||||
m_scaleData[fftSize] = std::make_shared<ScaleData>
|
m_scaleData[fftSize] = std::make_shared<ScaleData>
|
||||||
(guidedParameters, m_log);
|
(guidedParameters, m_log);
|
||||||
}
|
}
|
||||||
@@ -127,30 +153,42 @@ R3Stretcher::R3Stretcher(Parameters parameters,
|
|||||||
}
|
}
|
||||||
|
|
||||||
WindowType
|
WindowType
|
||||||
R3Stretcher::ScaleData::analysisWindowShape(int fftSize)
|
R3Stretcher::ScaleData::analysisWindowShape()
|
||||||
{
|
{
|
||||||
if (fftSize > 2048) return HannWindow;
|
if (singleWindowMode) {
|
||||||
else return NiemitaloForwardWindow;
|
return HannWindow;
|
||||||
|
} else {
|
||||||
|
if (fftSize > 2048) return HannWindow;
|
||||||
|
else return NiemitaloForwardWindow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
R3Stretcher::ScaleData::analysisWindowLength(int fftSize)
|
R3Stretcher::ScaleData::analysisWindowLength()
|
||||||
{
|
{
|
||||||
return fftSize;
|
return fftSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowType
|
WindowType
|
||||||
R3Stretcher::ScaleData::synthesisWindowShape(int fftSize)
|
R3Stretcher::ScaleData::synthesisWindowShape()
|
||||||
{
|
{
|
||||||
if (fftSize > 2048) return HannWindow;
|
if (singleWindowMode) {
|
||||||
else return NiemitaloReverseWindow;
|
return HannWindow;
|
||||||
|
} else {
|
||||||
|
if (fftSize > 2048) return HannWindow;
|
||||||
|
else return NiemitaloReverseWindow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
R3Stretcher::ScaleData::synthesisWindowLength(int fftSize)
|
R3Stretcher::ScaleData::synthesisWindowLength()
|
||||||
{
|
{
|
||||||
if (fftSize > 2048) return fftSize/2;
|
if (singleWindowMode) {
|
||||||
else return fftSize;
|
return fftSize;
|
||||||
|
} else {
|
||||||
|
if (fftSize > 2048) return fftSize/2;
|
||||||
|
else return fftSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -233,6 +271,8 @@ R3Stretcher::setKeyFrameMap(const std::map<size_t, size_t> &mapping)
|
|||||||
void
|
void
|
||||||
R3Stretcher::createResampler()
|
R3Stretcher::createResampler()
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R3Stretcher::createResampler");
|
||||||
|
|
||||||
Resampler::Parameters resamplerParameters;
|
Resampler::Parameters resamplerParameters;
|
||||||
|
|
||||||
if (m_parameters.options & RubberBandStretcher::OptionPitchHighQuality) {
|
if (m_parameters.options & RubberBandStretcher::OptionPitchHighQuality) {
|
||||||
@@ -245,14 +285,12 @@ R3Stretcher::createResampler()
|
|||||||
resamplerParameters.maxBufferSize = m_guideConfiguration.longestFftSize;
|
resamplerParameters.maxBufferSize = m_guideConfiguration.longestFftSize;
|
||||||
|
|
||||||
if (isRealTime()) {
|
if (isRealTime()) {
|
||||||
if (m_parameters.options &
|
// If we knew the caller would never change ratio, we could
|
||||||
RubberBandStretcher::OptionPitchHighConsistency) {
|
// supply RatioMostlyFixed - but it can have such overhead
|
||||||
resamplerParameters.dynamism = Resampler::RatioOftenChanging;
|
// when the ratio *does* change (and it's not RT-safe overhead
|
||||||
resamplerParameters.ratioChange = Resampler::SmoothRatioChange;
|
// either) that a single call would kill RT use
|
||||||
} else {
|
resamplerParameters.dynamism = Resampler::RatioOftenChanging;
|
||||||
resamplerParameters.dynamism = Resampler::RatioMostlyFixed;
|
resamplerParameters.ratioChange = Resampler::SmoothRatioChange;
|
||||||
resamplerParameters.ratioChange = Resampler::SmoothRatioChange;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
resamplerParameters.dynamism = Resampler::RatioMostlyFixed;
|
resamplerParameters.dynamism = Resampler::RatioMostlyFixed;
|
||||||
resamplerParameters.ratioChange = Resampler::SuddenRatioChange;
|
resamplerParameters.ratioChange = Resampler::SuddenRatioChange;
|
||||||
@@ -260,6 +298,20 @@ R3Stretcher::createResampler()
|
|||||||
|
|
||||||
m_resampler = std::unique_ptr<Resampler>
|
m_resampler = std::unique_ptr<Resampler>
|
||||||
(new Resampler(resamplerParameters, m_parameters.channels));
|
(new Resampler(resamplerParameters, m_parameters.channels));
|
||||||
|
|
||||||
|
bool before, after;
|
||||||
|
areWeResampling(&before, &after);
|
||||||
|
if (before) {
|
||||||
|
if (after) {
|
||||||
|
m_log.log(0, "WARNING: createResampler: we think we are resampling both before and after!");
|
||||||
|
} else {
|
||||||
|
m_log.log(1, "createResampler: resampling before");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (after) {
|
||||||
|
m_log.log(1, "createResampler: resampling after");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -283,25 +335,46 @@ R3Stretcher::calculateHop()
|
|||||||
proposedOuthop = pow(2.0, 8.0 + 2.0 * log10(ratio - 0.5));
|
proposedOuthop = pow(2.0, 8.0 + 2.0 * log10(ratio - 0.5));
|
||||||
} else if (ratio < 1.0) {
|
} else if (ratio < 1.0) {
|
||||||
proposedOuthop = pow(2.0, 8.0 + 2.0 * log10(ratio));
|
proposedOuthop = pow(2.0, 8.0 + 2.0 * log10(ratio));
|
||||||
}
|
}
|
||||||
if (proposedOuthop > 512.0) proposedOuthop = 512.0;
|
|
||||||
if (proposedOuthop < 128.0) proposedOuthop = 128.0;
|
|
||||||
|
|
||||||
|
if (isSingleWindowed()) {
|
||||||
|
// the single (shorter) window mode actually uses a longer
|
||||||
|
// synthesis window for the 2048-bin FFT and drops the
|
||||||
|
// 1024-bin one, so it can survive longer hops, which is good
|
||||||
|
// because reduced CPU consumption is the whole motivation
|
||||||
|
proposedOuthop *= 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proposedOuthop > m_limits.maxPreferredOuthop) {
|
||||||
|
proposedOuthop = m_limits.maxPreferredOuthop;
|
||||||
|
}
|
||||||
|
if (proposedOuthop < m_limits.minPreferredOuthop) {
|
||||||
|
proposedOuthop = m_limits.minPreferredOuthop;
|
||||||
|
}
|
||||||
|
|
||||||
m_log.log(1, "calculateHop: ratio and proposed outhop", ratio, proposedOuthop);
|
m_log.log(1, "calculateHop: ratio and proposed outhop", ratio, proposedOuthop);
|
||||||
|
|
||||||
double inhop = proposedOuthop / ratio;
|
double inhop = proposedOuthop / ratio;
|
||||||
if (inhop < 1.0) {
|
if (inhop < m_limits.minInhop) {
|
||||||
m_log.log(0, "WARNING: Extreme ratio yields ideal inhop < 1, results may be suspect", ratio, inhop);
|
m_log.log(0, "WARNING: Ratio yields ideal inhop < minimum, results may be suspect", inhop, m_limits.minInhop);
|
||||||
inhop = 1.0;
|
inhop = m_limits.minInhop;
|
||||||
}
|
}
|
||||||
if (inhop > 1024.0) {
|
if (inhop > m_limits.maxInhop) {
|
||||||
m_log.log(0, "WARNING: Extreme ratio yields ideal inhop > 1024, results may be suspect", ratio, inhop);
|
// Log level 1, this is not as big a deal as < minInhop above
|
||||||
inhop = 1024.0;
|
m_log.log(1, "WARNING: Ratio yields ideal inhop > maximum, results may be suspect", inhop, m_limits.maxInhop);
|
||||||
|
inhop = m_limits.maxInhop;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_inhop = int(floor(inhop));
|
m_inhop = int(floor(inhop));
|
||||||
|
|
||||||
m_log.log(1, "calculateHop: inhop and mean outhop", m_inhop, m_inhop * ratio);
|
m_log.log(1, "calculateHop: inhop and mean outhop", m_inhop, m_inhop * ratio);
|
||||||
|
|
||||||
|
if (m_inhop < m_limits.maxInhopWithReadahead) {
|
||||||
|
m_log.log(1, "calculateHop: using readahead");
|
||||||
|
m_useReadahead = true;
|
||||||
|
} else {
|
||||||
|
m_log.log(1, "calculateHop: not using readahead, inhop too long for buffer in current configuration");
|
||||||
|
m_useReadahead = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -409,7 +482,7 @@ R3Stretcher::getPreferredStartPad() const
|
|||||||
if (!isRealTime()) {
|
if (!isRealTime()) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return m_guideConfiguration.longestFftSize / 2;
|
return getWindowSourceSize() / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,7 +493,7 @@ R3Stretcher::getStartDelay() const
|
|||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
double factor = 0.5 / m_pitchScale;
|
double factor = 0.5 / m_pitchScale;
|
||||||
return size_t(ceil(m_guideConfiguration.longestFftSize * factor));
|
return size_t(ceil(getWindowSourceSize() * factor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,6 +536,8 @@ R3Stretcher::reset()
|
|||||||
void
|
void
|
||||||
R3Stretcher::study(const float *const *, size_t samples, bool)
|
R3Stretcher::study(const float *const *, size_t samples, bool)
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R3Stretcher::study");
|
||||||
|
|
||||||
if (isRealTime()) {
|
if (isRealTime()) {
|
||||||
m_log.log(0, "R3Stretcher::study: Not meaningful in realtime mode");
|
m_log.log(0, "R3Stretcher::study: Not meaningful in realtime mode");
|
||||||
return;
|
return;
|
||||||
@@ -491,10 +566,9 @@ size_t
|
|||||||
R3Stretcher::getSamplesRequired() const
|
R3Stretcher::getSamplesRequired() const
|
||||||
{
|
{
|
||||||
if (available() != 0) return 0;
|
if (available() != 0) return 0;
|
||||||
int longest = m_guideConfiguration.longestFftSize;
|
|
||||||
int rs = m_channelData[0]->inbuf->getReadSpace();
|
int rs = m_channelData[0]->inbuf->getReadSpace();
|
||||||
if (rs < longest) {
|
if (rs < getWindowSourceSize()) {
|
||||||
return longest - rs;
|
return getWindowSourceSize() - rs;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -504,7 +578,7 @@ void
|
|||||||
R3Stretcher::setMaxProcessSize(size_t n)
|
R3Stretcher::setMaxProcessSize(size_t n)
|
||||||
{
|
{
|
||||||
size_t oldSize = m_channelData[0]->inbuf->getSize();
|
size_t oldSize = m_channelData[0]->inbuf->getSize();
|
||||||
size_t newSize = m_guideConfiguration.longestFftSize + n;
|
size_t newSize = getWindowSourceSize() + n;
|
||||||
|
|
||||||
if (newSize > oldSize) {
|
if (newSize > oldSize) {
|
||||||
m_log.log(1, "setMaxProcessSize: resizing from and to", oldSize, newSize);
|
m_log.log(1, "setMaxProcessSize: resizing from and to", oldSize, newSize);
|
||||||
@@ -520,6 +594,8 @@ R3Stretcher::setMaxProcessSize(size_t n)
|
|||||||
void
|
void
|
||||||
R3Stretcher::process(const float *const *input, size_t samples, bool final)
|
R3Stretcher::process(const float *const *input, size_t samples, bool final)
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R3Stretcher::process");
|
||||||
|
|
||||||
if (m_mode == ProcessMode::Finished) {
|
if (m_mode == ProcessMode::Finished) {
|
||||||
m_log.log(0, "R3Stretcher::process: Cannot process again after final chunk");
|
m_log.log(0, "R3Stretcher::process: Cannot process again after final chunk");
|
||||||
return;
|
return;
|
||||||
@@ -557,11 +633,11 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final)
|
|||||||
createResampler();
|
createResampler();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pad to half the longest frame. As with R2, in real-time
|
// Pad to half the frame. As with R2, in real-time mode we
|
||||||
// mode we don't do this -- it's better to start with a
|
// don't do this -- it's better to start with a swoosh
|
||||||
// swoosh than introduce more latency, and we don't want
|
// than introduce more latency, and we don't want gaps
|
||||||
// gaps when the ratio changes.
|
// when the ratio changes.
|
||||||
int pad = m_guideConfiguration.longestFftSize / 2;
|
int pad = getWindowSourceSize() / 2;
|
||||||
m_log.log(1, "offline mode: prefilling with", pad);
|
m_log.log(1, "offline mode: prefilling with", pad);
|
||||||
for (int c = 0; c < m_parameters.channels; ++c) {
|
for (int c = 0; c < m_parameters.channels; ++c) {
|
||||||
m_channelData[c]->inbuf->zero(pad);
|
m_channelData[c]->inbuf->zero(pad);
|
||||||
@@ -583,22 +659,74 @@ R3Stretcher::process(const float *const *input, size_t samples, bool final)
|
|||||||
} else {
|
} else {
|
||||||
m_mode = ProcessMode::Processing;
|
m_mode = ProcessMode::Processing;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ws = m_channelData[0]->inbuf->getWriteSpace();
|
bool resamplingBefore = false;
|
||||||
if (samples > ws) {
|
areWeResampling(&resamplingBefore, nullptr);
|
||||||
m_log.log(0, "R3Stretcher::process: WARNING: Forced to increase input buffer size. Either setMaxProcessSize was not properly called or process is being called repeatedly without retrieve. Write space and samples", ws, samples);
|
|
||||||
size_t newSize = m_channelData[0]->inbuf->getSize() - ws + samples;
|
int channels = m_parameters.channels;
|
||||||
for (int c = 0; c < m_parameters.channels; ++c) {
|
int inputIx = 0;
|
||||||
auto newBuf = m_channelData[c]->inbuf->resized(newSize);
|
|
||||||
m_channelData[c]->inbuf = std::unique_ptr<RingBuffer<float>>(newBuf);
|
while (inputIx < int(samples)) {
|
||||||
|
|
||||||
|
int remaining = int(samples) - inputIx;
|
||||||
|
int ws = m_channelData[0]->inbuf->getWriteSpace();
|
||||||
|
|
||||||
|
if (ws == 0) {
|
||||||
|
consume();
|
||||||
|
ws = m_channelData[0]->inbuf->getWriteSpace();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (ws == 0) {
|
||||||
|
m_log.log(0, "R3Stretcher::process: WARNING: Forced to increase input buffer size. Either setMaxProcessSize was not properly called, process is being called repeatedly without retrieve, or an internal error has led to an incorrect resampler output calculation. Samples to write", remaining);
|
||||||
|
size_t newSize = m_channelData[0]->inbuf->getSize() + remaining;
|
||||||
|
for (int c = 0; c < m_parameters.channels; ++c) {
|
||||||
|
auto newBuf = m_channelData[c]->inbuf->resized(newSize);
|
||||||
|
m_channelData[c]->inbuf =
|
||||||
|
std::unique_ptr<RingBuffer<float>>(newBuf);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resamplingBefore) {
|
||||||
|
|
||||||
for (int c = 0; c < m_parameters.channels; ++c) {
|
for (int c = 0; c < channels; ++c) {
|
||||||
m_channelData[c]->inbuf->write(input[c], samples);
|
auto &cd = m_channelData.at(c);
|
||||||
}
|
m_channelAssembly.resampled[c] = cd->resampled.data();
|
||||||
|
}
|
||||||
|
|
||||||
consume();
|
int resampleBufSize = int(m_channelData.at(0)->resampled.size());
|
||||||
|
int maxResampleOutput = std::min(ws, resampleBufSize);
|
||||||
|
|
||||||
|
int maxResampleInput = int(floor(maxResampleOutput * m_pitchScale));
|
||||||
|
int resampleInput = std::min(remaining, maxResampleInput);
|
||||||
|
if (resampleInput == 0) resampleInput = 1;
|
||||||
|
|
||||||
|
int resampleOutput = m_resampler->resample
|
||||||
|
(m_channelAssembly.resampled.data(),
|
||||||
|
maxResampleOutput,
|
||||||
|
input,
|
||||||
|
resampleInput,
|
||||||
|
1.0 / m_pitchScale,
|
||||||
|
final);
|
||||||
|
|
||||||
|
inputIx += resampleInput;
|
||||||
|
|
||||||
|
for (int c = 0; c < m_parameters.channels; ++c) {
|
||||||
|
m_channelData[c]->inbuf->write
|
||||||
|
(m_channelData.at(c)->resampled.data(),
|
||||||
|
resampleOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
int toWrite = std::min(ws, remaining);
|
||||||
|
for (int c = 0; c < m_parameters.channels; ++c) {
|
||||||
|
m_channelData[c]->inbuf->write (input[c] + inputIx, toWrite);
|
||||||
|
}
|
||||||
|
inputIx += toWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
consume();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@@ -615,6 +743,8 @@ R3Stretcher::available() const
|
|||||||
size_t
|
size_t
|
||||||
R3Stretcher::retrieve(float *const *output, size_t samples) const
|
R3Stretcher::retrieve(float *const *output, size_t samples) const
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R3Stretcher::retrieve");
|
||||||
|
|
||||||
int got = samples;
|
int got = samples;
|
||||||
|
|
||||||
for (int c = 0; c < m_parameters.channels; ++c) {
|
for (int c = 0; c < m_parameters.channels; ++c) {
|
||||||
@@ -633,10 +763,15 @@ R3Stretcher::retrieve(float *const *output, size_t samples) const
|
|||||||
void
|
void
|
||||||
R3Stretcher::consume()
|
R3Stretcher::consume()
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R3Stretcher::consume");
|
||||||
|
|
||||||
int longest = m_guideConfiguration.longestFftSize;
|
int longest = m_guideConfiguration.longestFftSize;
|
||||||
int channels = m_parameters.channels;
|
int channels = m_parameters.channels;
|
||||||
int inhop = m_inhop;
|
int inhop = m_inhop;
|
||||||
|
|
||||||
|
bool resamplingAfter = false;
|
||||||
|
areWeResampling(nullptr, &resamplingAfter);
|
||||||
|
|
||||||
double effectivePitchRatio = 1.0 / m_pitchScale;
|
double effectivePitchRatio = 1.0 / m_pitchScale;
|
||||||
if (m_resampler) {
|
if (m_resampler) {
|
||||||
effectivePitchRatio =
|
effectivePitchRatio =
|
||||||
@@ -680,6 +815,8 @@ R3Stretcher::consume()
|
|||||||
|
|
||||||
while (cd0->outbuf->getWriteSpace() >= outhop) {
|
while (cd0->outbuf->getWriteSpace() >= outhop) {
|
||||||
|
|
||||||
|
Profiler profiler("R3Stretcher::consume/loop");
|
||||||
|
|
||||||
// NB our ChannelData, ScaleData, and ChannelScaleData maps
|
// NB our ChannelData, ScaleData, and ChannelScaleData maps
|
||||||
// contain shared_ptrs; whenever we retain one of them in a
|
// contain shared_ptrs; whenever we retain one of them in a
|
||||||
// variable, we do so by reference to avoid copying the
|
// variable, we do so by reference to avoid copying the
|
||||||
@@ -687,7 +824,7 @@ R3Stretcher::consume()
|
|||||||
// the map iterators
|
// the map iterators
|
||||||
|
|
||||||
int readSpace = cd0->inbuf->getReadSpace();
|
int readSpace = cd0->inbuf->getReadSpace();
|
||||||
if (readSpace < longest) {
|
if (readSpace < getWindowSourceSize()) {
|
||||||
if (m_mode == ProcessMode::Finished) {
|
if (m_mode == ProcessMode::Finished) {
|
||||||
if (readSpace == 0) {
|
if (readSpace == 0) {
|
||||||
int fill = cd0->scales.at(longest)->accumulatorFill;
|
int fill = cd0->scales.at(longest)->accumulatorFill;
|
||||||
@@ -745,17 +882,8 @@ R3Stretcher::consume()
|
|||||||
|
|
||||||
// Resample
|
// Resample
|
||||||
|
|
||||||
bool resampling = false;
|
|
||||||
if (m_resampler) {
|
|
||||||
if (m_pitchScale != 1.0 ||
|
|
||||||
(m_parameters.options &
|
|
||||||
RubberBandStretcher::OptionPitchHighConsistency)) {
|
|
||||||
resampling = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int resampledCount = 0;
|
int resampledCount = 0;
|
||||||
if (resampling) {
|
if (resamplingAfter) {
|
||||||
for (int c = 0; c < channels; ++c) {
|
for (int c = 0; c < channels; ++c) {
|
||||||
auto &cd = m_channelData.at(c);
|
auto &cd = m_channelData.at(c);
|
||||||
m_channelAssembly.mixdown[c] = cd->mixdown.data();
|
m_channelAssembly.mixdown[c] = cd->mixdown.data();
|
||||||
@@ -773,7 +901,7 @@ R3Stretcher::consume()
|
|||||||
// Emit
|
// Emit
|
||||||
|
|
||||||
int writeCount = outhop;
|
int writeCount = outhop;
|
||||||
if (resampling) {
|
if (resamplingAfter) {
|
||||||
writeCount = resampledCount;
|
writeCount = resampledCount;
|
||||||
}
|
}
|
||||||
if (!isRealTime()) {
|
if (!isRealTime()) {
|
||||||
@@ -798,7 +926,7 @@ R3Stretcher::consume()
|
|||||||
|
|
||||||
for (int c = 0; c < channels; ++c) {
|
for (int c = 0; c < channels; ++c) {
|
||||||
auto &cd = m_channelData.at(c);
|
auto &cd = m_channelData.at(c);
|
||||||
if (resampling) {
|
if (resamplingAfter) {
|
||||||
cd->outbuf->write(cd->resampled.data(), writeCount);
|
cd->outbuf->write(cd->resampled.data(), writeCount);
|
||||||
} else {
|
} else {
|
||||||
cd->outbuf->write(cd->mixdown.data(), writeCount);
|
cd->outbuf->write(cd->mixdown.data(), writeCount);
|
||||||
@@ -827,61 +955,68 @@ R3Stretcher::consume()
|
|||||||
void
|
void
|
||||||
R3Stretcher::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
|
R3Stretcher::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R3Stretcher::analyseChannel");
|
||||||
|
|
||||||
|
auto &cd = m_channelData.at(c);
|
||||||
|
|
||||||
|
int sourceSize = cd->windowSource.size();
|
||||||
|
process_t *buf = cd->windowSource.data();
|
||||||
|
|
||||||
|
int readSpace = cd->inbuf->getReadSpace();
|
||||||
|
if (readSpace < sourceSize) {
|
||||||
|
cd->inbuf->peek(buf, readSpace);
|
||||||
|
v_zero(buf + readSpace, sourceSize - readSpace);
|
||||||
|
} else {
|
||||||
|
cd->inbuf->peek(buf, sourceSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have an unwindowed time-domain frame in buf that is as long
|
||||||
|
// as required for the union of all FFT sizes and readahead
|
||||||
|
// hops. Populate the various sizes from it with aligned centres,
|
||||||
|
// windowing as we copy. The classification scale is handled
|
||||||
|
// separately because it has readahead, so skip it here. (In
|
||||||
|
// single-window mode that means we do nothing here, since the
|
||||||
|
// classification scale is the only one.)
|
||||||
|
|
||||||
int longest = m_guideConfiguration.longestFftSize;
|
int longest = m_guideConfiguration.longestFftSize;
|
||||||
int classify = m_guideConfiguration.classificationFftSize;
|
int classify = m_guideConfiguration.classificationFftSize;
|
||||||
|
|
||||||
auto &cd = m_channelData.at(c);
|
|
||||||
process_t *buf = cd->scales.at(longest)->timeDomain.data();
|
|
||||||
|
|
||||||
int readSpace = cd->inbuf->getReadSpace();
|
|
||||||
if (readSpace < longest) {
|
|
||||||
cd->inbuf->peek(buf, readSpace);
|
|
||||||
v_zero(buf + readSpace, longest - readSpace);
|
|
||||||
} else {
|
|
||||||
cd->inbuf->peek(buf, longest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have a single unwindowed frame at the longest FFT size
|
|
||||||
// ("scale"). Populate the shorter FFT sizes from the centre of
|
|
||||||
// it, windowing as we copy. The classification scale is handled
|
|
||||||
// separately because it has readahead, so skip it here as well as
|
|
||||||
// the longest. (In practice this means we are probably only
|
|
||||||
// populating one scale)
|
|
||||||
|
|
||||||
for (auto &it: cd->scales) {
|
for (auto &it: cd->scales) {
|
||||||
int fftSize = it.first;
|
int fftSize = it.first;
|
||||||
if (fftSize == classify || fftSize == longest) continue;
|
if (fftSize == classify) continue;
|
||||||
int offset = (longest - fftSize) / 2;
|
int offset = (longest - fftSize) / 2;
|
||||||
m_scaleData.at(fftSize)->analysisWindow.cut
|
m_scaleData.at(fftSize)->analysisWindow.cut
|
||||||
(buf + offset, it.second->timeDomain.data());
|
(buf + offset, it.second->timeDomain.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The classification scale has a one-hop readahead, so populate
|
|
||||||
// the readahead from further down the long unwindowed frame.
|
|
||||||
|
|
||||||
auto &classifyScale = cd->scales.at(classify);
|
auto &classifyScale = cd->scales.at(classify);
|
||||||
ClassificationReadaheadData &readahead = cd->readahead;
|
ClassificationReadaheadData &readahead = cd->readahead;
|
||||||
|
bool copyFromReadahead = false;
|
||||||
|
|
||||||
|
if (m_useReadahead) {
|
||||||
|
|
||||||
|
// The classification scale has a one-hop readahead, so
|
||||||
|
// populate the readahead from further down the long
|
||||||
|
// unwindowed frame.
|
||||||
|
|
||||||
m_scaleData.at(classify)->analysisWindow.cut
|
m_scaleData.at(classify)->analysisWindow.cut
|
||||||
(buf + (longest - classify) / 2 + inhop,
|
(buf + (longest - classify) / 2 + inhop,
|
||||||
readahead.timeDomain.data());
|
readahead.timeDomain.data());
|
||||||
|
|
||||||
// If inhop has changed since the previous frame, we'll have to
|
// If inhop has changed since the previous frame, we must
|
||||||
// populate the classification scale (but for analysis/resynthesis
|
// populate the classification scale (but for
|
||||||
// rather than classification) anew rather than reuse the previous
|
// analysis/resynthesis rather than classification) anew
|
||||||
// readahead. Pity...
|
// rather than reuse the previous frame's readahead.
|
||||||
|
|
||||||
bool haveValidReadahead = cd->haveReadahead;
|
copyFromReadahead = cd->haveReadahead;
|
||||||
if (inhop != prevInhop) haveValidReadahead = false;
|
if (inhop != prevInhop) copyFromReadahead = false;
|
||||||
|
}
|
||||||
if (!haveValidReadahead) {
|
|
||||||
|
if (!copyFromReadahead) {
|
||||||
m_scaleData.at(classify)->analysisWindow.cut
|
m_scaleData.at(classify)->analysisWindow.cut
|
||||||
(buf + (longest - classify) / 2,
|
(buf + (longest - classify) / 2,
|
||||||
classifyScale->timeDomain.data());
|
classifyScale->timeDomain.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally window the longest scale
|
|
||||||
m_scaleData.at(longest)->analysisWindow.cut(buf);
|
|
||||||
|
|
||||||
// FFT shift, forward FFT, and carry out cartesian-polar
|
// FFT shift, forward FFT, and carry out cartesian-polar
|
||||||
// conversion for each FFT size.
|
// conversion for each FFT size.
|
||||||
@@ -892,64 +1027,68 @@ R3Stretcher::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
|
|||||||
// where the inhop has changed as above, in which case we need to
|
// where the inhop has changed as above, in which case we need to
|
||||||
// do both readahead and current)
|
// do both readahead and current)
|
||||||
|
|
||||||
if (haveValidReadahead) {
|
if (m_useReadahead) {
|
||||||
v_copy(classifyScale->mag.data(),
|
|
||||||
readahead.mag.data(),
|
|
||||||
classifyScale->bufSize);
|
|
||||||
v_copy(classifyScale->phase.data(),
|
|
||||||
readahead.phase.data(),
|
|
||||||
classifyScale->bufSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
v_fftshift(readahead.timeDomain.data(), classify);
|
if (copyFromReadahead) {
|
||||||
m_scaleData.at(classify)->fft.forward(readahead.timeDomain.data(),
|
v_copy(classifyScale->mag.data(),
|
||||||
classifyScale->real.data(),
|
readahead.mag.data(),
|
||||||
classifyScale->imag.data());
|
classifyScale->bufSize);
|
||||||
|
v_copy(classifyScale->phase.data(),
|
||||||
for (const auto &b : m_guideConfiguration.fftBandLimits) {
|
readahead.phase.data(),
|
||||||
if (b.fftSize == classify) {
|
classifyScale->bufSize);
|
||||||
|
|
||||||
ToPolarSpec spec;
|
|
||||||
spec.magFromBin = 0;
|
|
||||||
spec.magBinCount = classify/2 + 1;
|
|
||||||
spec.polarFromBin = b.b0min;
|
|
||||||
spec.polarBinCount = b.b1max - b.b0min + 1;
|
|
||||||
convertToPolar(readahead.mag.data(),
|
|
||||||
readahead.phase.data(),
|
|
||||||
classifyScale->real.data(),
|
|
||||||
classifyScale->imag.data(),
|
|
||||||
spec);
|
|
||||||
|
|
||||||
v_scale(classifyScale->mag.data(),
|
|
||||||
1.0 / double(classify),
|
|
||||||
classifyScale->mag.size());
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cd->haveReadahead = true;
|
v_fftshift(readahead.timeDomain.data(), classify);
|
||||||
|
m_scaleData.at(classify)->fft.forward(readahead.timeDomain.data(),
|
||||||
|
classifyScale->real.data(),
|
||||||
|
classifyScale->imag.data());
|
||||||
|
|
||||||
|
for (int b = 0; b < m_guideConfiguration.fftBandLimitCount; ++b) {
|
||||||
|
const auto &band = m_guideConfiguration.fftBandLimits[b];
|
||||||
|
if (band.fftSize == classify) {
|
||||||
|
ToPolarSpec spec;
|
||||||
|
spec.magFromBin = 0;
|
||||||
|
spec.magBinCount = classify/2 + 1;
|
||||||
|
spec.polarFromBin = band.b0min;
|
||||||
|
spec.polarBinCount = band.b1max - band.b0min + 1;
|
||||||
|
convertToPolar(readahead.mag.data(),
|
||||||
|
readahead.phase.data(),
|
||||||
|
classifyScale->real.data(),
|
||||||
|
classifyScale->imag.data(),
|
||||||
|
spec);
|
||||||
|
|
||||||
|
v_scale(classifyScale->mag.data(),
|
||||||
|
1.0 / double(classify),
|
||||||
|
classifyScale->mag.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cd->haveReadahead = true;
|
||||||
|
}
|
||||||
|
|
||||||
// For the others (and the classify as well, if the inhop has
|
// For the others (and the classify as well, if the inhop has
|
||||||
// changed or we haven't filled the readahead yet) we operate
|
// changed or we aren't using readahead or haven't filled the
|
||||||
// directly in the scale data and restrict the range for
|
// readahead yet) we operate directly in the scale data and
|
||||||
// cartesian-polar conversion
|
// restrict the range for cartesian-polar conversion
|
||||||
|
|
||||||
for (auto &it: cd->scales) {
|
for (auto &it: cd->scales) {
|
||||||
int fftSize = it.first;
|
int fftSize = it.first;
|
||||||
if (fftSize == classify && haveValidReadahead) {
|
if (fftSize == classify && copyFromReadahead) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &scale = it.second;
|
auto &scale = it.second;
|
||||||
|
|
||||||
v_fftshift(scale->timeDomain.data(), fftSize);
|
v_fftshift(scale->timeDomain.data(), fftSize);
|
||||||
|
|
||||||
m_scaleData.at(fftSize)->fft.forward(scale->timeDomain.data(),
|
m_scaleData.at(fftSize)->fft.forward(scale->timeDomain.data(),
|
||||||
scale->real.data(),
|
scale->real.data(),
|
||||||
scale->imag.data());
|
scale->imag.data());
|
||||||
|
|
||||||
for (const auto &b : m_guideConfiguration.fftBandLimits) {
|
for (int b = 0; b < m_guideConfiguration.fftBandLimitCount; ++b) {
|
||||||
if (b.fftSize == fftSize) {
|
const auto &band = m_guideConfiguration.fftBandLimits[b];
|
||||||
|
if (band.fftSize == fftSize) {
|
||||||
|
|
||||||
ToPolarSpec spec;
|
ToPolarSpec spec;
|
||||||
|
|
||||||
@@ -957,17 +1096,17 @@ R3Stretcher::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
|
|||||||
// range, as all the magnitudes (though not
|
// range, as all the magnitudes (though not
|
||||||
// necessarily all phases) are potentially relevant to
|
// necessarily all phases) are potentially relevant to
|
||||||
// classification and formant analysis. But this case
|
// classification and formant analysis. But this case
|
||||||
// here only happens if we don't haveValidReadahead -
|
// here only happens if we don't copyFromReadahead -
|
||||||
// the normal case is above and just copies from the
|
// the normal case is above and, er, copies from the
|
||||||
// previous readahead.
|
// previous readahead.
|
||||||
if (fftSize == classify) {
|
if (fftSize == classify) {
|
||||||
spec.magFromBin = 0;
|
spec.magFromBin = 0;
|
||||||
spec.magBinCount = classify/2 + 1;
|
spec.magBinCount = classify/2 + 1;
|
||||||
spec.polarFromBin = b.b0min;
|
spec.polarFromBin = band.b0min;
|
||||||
spec.polarBinCount = b.b1max - b.b0min + 1;
|
spec.polarBinCount = band.b1max - band.b0min + 1;
|
||||||
} else {
|
} else {
|
||||||
spec.magFromBin = b.b0min;
|
spec.magFromBin = band.b0min;
|
||||||
spec.magBinCount = b.b1max - b.b0min + 1;
|
spec.magBinCount = band.b1max - band.b0min + 1;
|
||||||
spec.polarFromBin = spec.magFromBin;
|
spec.polarFromBin = spec.magFromBin;
|
||||||
spec.polarBinCount = spec.magBinCount;
|
spec.polarBinCount = spec.magBinCount;
|
||||||
}
|
}
|
||||||
@@ -997,8 +1136,14 @@ R3Stretcher::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
|
|||||||
|
|
||||||
v_copy(cd->classification.data(), cd->nextClassification.data(),
|
v_copy(cd->classification.data(), cd->nextClassification.data(),
|
||||||
cd->classification.size());
|
cd->classification.size());
|
||||||
cd->classifier->classify(readahead.mag.data(),
|
|
||||||
cd->nextClassification.data());
|
if (m_useReadahead) {
|
||||||
|
cd->classifier->classify(readahead.mag.data(),
|
||||||
|
cd->nextClassification.data());
|
||||||
|
} else {
|
||||||
|
cd->classifier->classify(classifyScale->mag.data(),
|
||||||
|
cd->nextClassification.data());
|
||||||
|
}
|
||||||
|
|
||||||
cd->prevSegmentation = cd->segmentation;
|
cd->prevSegmentation = cd->segmentation;
|
||||||
cd->segmentation = cd->nextSegmentation;
|
cd->segmentation = cd->nextSegmentation;
|
||||||
@@ -1037,20 +1182,39 @@ R3Stretcher::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
|
|||||||
|
|
||||||
bool tighterChannelLock =
|
bool tighterChannelLock =
|
||||||
m_parameters.options & RubberBandStretcher::OptionChannelsTogether;
|
m_parameters.options & RubberBandStretcher::OptionChannelsTogether;
|
||||||
|
|
||||||
|
double magMean = v_mean(classifyScale->mag.data() + 1, classify/2);
|
||||||
|
|
||||||
|
if (m_useReadahead) {
|
||||||
|
m_guide.updateGuidance(ratio,
|
||||||
|
prevOuthop,
|
||||||
|
classifyScale->mag.data(),
|
||||||
|
classifyScale->prevMag.data(),
|
||||||
|
cd->readahead.mag.data(),
|
||||||
|
cd->segmentation,
|
||||||
|
cd->prevSegmentation,
|
||||||
|
cd->nextSegmentation,
|
||||||
|
magMean,
|
||||||
|
m_unityCount,
|
||||||
|
isRealTime(),
|
||||||
|
tighterChannelLock,
|
||||||
|
cd->guidance);
|
||||||
|
} else {
|
||||||
|
m_guide.updateGuidance(ratio,
|
||||||
|
prevOuthop,
|
||||||
|
classifyScale->prevMag.data(),
|
||||||
|
classifyScale->prevMag.data(),
|
||||||
|
classifyScale->mag.data(),
|
||||||
|
cd->segmentation,
|
||||||
|
cd->prevSegmentation,
|
||||||
|
cd->nextSegmentation,
|
||||||
|
magMean,
|
||||||
|
m_unityCount,
|
||||||
|
isRealTime(),
|
||||||
|
tighterChannelLock,
|
||||||
|
cd->guidance);
|
||||||
|
}
|
||||||
|
|
||||||
m_guide.updateGuidance(ratio,
|
|
||||||
prevOuthop,
|
|
||||||
classifyScale->mag.data(),
|
|
||||||
classifyScale->prevMag.data(),
|
|
||||||
cd->readahead.mag.data(),
|
|
||||||
cd->segmentation,
|
|
||||||
cd->prevSegmentation,
|
|
||||||
cd->nextSegmentation,
|
|
||||||
v_mean(classifyScale->mag.data() + 1, classify/2),
|
|
||||||
m_unityCount,
|
|
||||||
isRealTime(),
|
|
||||||
tighterChannelLock,
|
|
||||||
cd->guidance);
|
|
||||||
/*
|
/*
|
||||||
if (c == 0) {
|
if (c == 0) {
|
||||||
if (cd->guidance.kick.present) {
|
if (cd->guidance.kick.present) {
|
||||||
@@ -1067,6 +1231,8 @@ R3Stretcher::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
|
|||||||
void
|
void
|
||||||
R3Stretcher::analyseFormant(int c)
|
R3Stretcher::analyseFormant(int c)
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R3Stretcher::analyseFormant");
|
||||||
|
|
||||||
auto &cd = m_channelData.at(c);
|
auto &cd = m_channelData.at(c);
|
||||||
auto &f = *cd->formant;
|
auto &f = *cd->formant;
|
||||||
|
|
||||||
@@ -1101,6 +1267,8 @@ R3Stretcher::analyseFormant(int c)
|
|||||||
void
|
void
|
||||||
R3Stretcher::adjustFormant(int c)
|
R3Stretcher::adjustFormant(int c)
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R3Stretcher::adjustFormant");
|
||||||
|
|
||||||
auto &cd = m_channelData.at(c);
|
auto &cd = m_channelData.at(c);
|
||||||
|
|
||||||
for (auto &it : cd->scales) {
|
for (auto &it : cd->scales) {
|
||||||
@@ -1116,9 +1284,10 @@ R3Stretcher::adjustFormant(int c)
|
|||||||
process_t maxRatio = 60.0;
|
process_t maxRatio = 60.0;
|
||||||
process_t minRatio = 1.0 / maxRatio;
|
process_t minRatio = 1.0 / maxRatio;
|
||||||
|
|
||||||
for (const auto &b : m_guideConfiguration.fftBandLimits) {
|
for (int b = 0; b < m_guideConfiguration.fftBandLimitCount; ++b) {
|
||||||
if (b.fftSize != fftSize) continue;
|
const auto &band = m_guideConfiguration.fftBandLimits[b];
|
||||||
for (int i = b.b0min; i < b.b1max && i < highBin; ++i) {
|
if (band.fftSize != fftSize) continue;
|
||||||
|
for (int i = band.b0min; i < band.b1max && i < highBin; ++i) {
|
||||||
process_t source = cd->formant->envelopeAt(i * sourceFactor);
|
process_t source = cd->formant->envelopeAt(i * sourceFactor);
|
||||||
process_t target = cd->formant->envelopeAt(i * targetFactor);
|
process_t target = cd->formant->envelopeAt(i * targetFactor);
|
||||||
if (target > 0.0) {
|
if (target > 0.0) {
|
||||||
@@ -1135,6 +1304,10 @@ R3Stretcher::adjustFormant(int c)
|
|||||||
void
|
void
|
||||||
R3Stretcher::adjustPreKick(int c)
|
R3Stretcher::adjustPreKick(int c)
|
||||||
{
|
{
|
||||||
|
if (isSingleWindowed()) return;
|
||||||
|
|
||||||
|
Profiler profiler("R3Stretcher::adjustPreKick");
|
||||||
|
|
||||||
auto &cd = m_channelData.at(c);
|
auto &cd = m_channelData.at(c);
|
||||||
auto fftSize = cd->guidance.fftBands[0].fftSize;
|
auto fftSize = cd->guidance.fftBands[0].fftSize;
|
||||||
if (cd->guidance.preKick.present) {
|
if (cd->guidance.preKick.present) {
|
||||||
@@ -1166,12 +1339,17 @@ R3Stretcher::adjustPreKick(int c)
|
|||||||
void
|
void
|
||||||
R3Stretcher::synthesiseChannel(int c, int outhop, bool draining)
|
R3Stretcher::synthesiseChannel(int c, int outhop, bool draining)
|
||||||
{
|
{
|
||||||
|
Profiler profiler("R3Stretcher::synthesiseChannel");
|
||||||
|
|
||||||
int longest = m_guideConfiguration.longestFftSize;
|
int longest = m_guideConfiguration.longestFftSize;
|
||||||
|
|
||||||
auto &cd = m_channelData.at(c);
|
auto &cd = m_channelData.at(c);
|
||||||
|
|
||||||
for (const auto &band : cd->guidance.fftBands) {
|
for (int b = 0; b < cd->guidance.fftBandCount; ++b) {
|
||||||
|
|
||||||
|
const auto &band = cd->guidance.fftBands[b];
|
||||||
int fftSize = band.fftSize;
|
int fftSize = band.fftSize;
|
||||||
|
|
||||||
auto &scale = cd->scales.at(fftSize);
|
auto &scale = cd->scales.at(fftSize);
|
||||||
auto &scaleData = m_scaleData.at(fftSize);
|
auto &scaleData = m_scaleData.at(fftSize);
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,28 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
struct Limits {
|
||||||
|
int minPreferredOuthop;
|
||||||
|
int maxPreferredOuthop;
|
||||||
|
int minInhop;
|
||||||
|
int maxInhopWithReadahead;
|
||||||
|
int maxInhop;
|
||||||
|
Limits(RubberBandStretcher::Options options) :
|
||||||
|
minPreferredOuthop(128),
|
||||||
|
maxPreferredOuthop(512),
|
||||||
|
minInhop(1),
|
||||||
|
maxInhopWithReadahead(1024),
|
||||||
|
maxInhop(1024) {
|
||||||
|
if (options & RubberBandStretcher::OptionWindowShort) {
|
||||||
|
// See note in calculateHop
|
||||||
|
minPreferredOuthop = 256;
|
||||||
|
maxPreferredOuthop = 640;
|
||||||
|
maxInhopWithReadahead = 512;
|
||||||
|
maxInhop = 1560;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct ClassificationReadaheadData {
|
struct ClassificationReadaheadData {
|
||||||
FixedVector<process_t> timeDomain;
|
FixedVector<process_t> timeDomain;
|
||||||
FixedVector<process_t> mag;
|
FixedVector<process_t> mag;
|
||||||
@@ -185,6 +207,7 @@ protected:
|
|||||||
|
|
||||||
struct ChannelData {
|
struct ChannelData {
|
||||||
std::map<int, std::shared_ptr<ChannelScaleData>> scales;
|
std::map<int, std::shared_ptr<ChannelScaleData>> scales;
|
||||||
|
FixedVector<process_t> windowSource;
|
||||||
ClassificationReadaheadData readahead;
|
ClassificationReadaheadData readahead;
|
||||||
bool haveReadahead;
|
bool haveReadahead;
|
||||||
std::unique_ptr<BinClassifier> classifier;
|
std::unique_ptr<BinClassifier> classifier;
|
||||||
@@ -203,9 +226,11 @@ protected:
|
|||||||
ChannelData(BinSegmenter::Parameters segmenterParameters,
|
ChannelData(BinSegmenter::Parameters segmenterParameters,
|
||||||
BinClassifier::Parameters classifierParameters,
|
BinClassifier::Parameters classifierParameters,
|
||||||
int longestFftSize,
|
int longestFftSize,
|
||||||
|
int windowSourceSize,
|
||||||
int inRingBufferSize,
|
int inRingBufferSize,
|
||||||
int outRingBufferSize) :
|
int outRingBufferSize) :
|
||||||
scales(),
|
scales(),
|
||||||
|
windowSource(windowSourceSize, 0.0),
|
||||||
readahead(segmenterParameters.fftSize),
|
readahead(segmenterParameters.fftSize),
|
||||||
haveReadahead(false),
|
haveReadahead(false),
|
||||||
classifier(new BinClassifier(classifierParameters)),
|
classifier(new BinClassifier(classifierParameters)),
|
||||||
@@ -215,7 +240,7 @@ protected:
|
|||||||
BinClassifier::Classification::Residual),
|
BinClassifier::Classification::Residual),
|
||||||
segmenter(new BinSegmenter(segmenterParameters)),
|
segmenter(new BinSegmenter(segmenterParameters)),
|
||||||
segmentation(), prevSegmentation(), nextSegmentation(),
|
segmentation(), prevSegmentation(), nextSegmentation(),
|
||||||
mixdown(longestFftSize, 0.f), // though it could be shorter
|
mixdown(longestFftSize, 0.f),
|
||||||
resampled(outRingBufferSize, 0.f),
|
resampled(outRingBufferSize, 0.f),
|
||||||
inbuf(new RingBuffer<float>(inRingBufferSize)),
|
inbuf(new RingBuffer<float>(inRingBufferSize)),
|
||||||
outbuf(new RingBuffer<float>(outRingBufferSize)),
|
outbuf(new RingBuffer<float>(outRingBufferSize)),
|
||||||
@@ -253,19 +278,22 @@ protected:
|
|||||||
|
|
||||||
struct ScaleData {
|
struct ScaleData {
|
||||||
int fftSize;
|
int fftSize;
|
||||||
|
bool singleWindowMode;
|
||||||
FFT fft;
|
FFT fft;
|
||||||
Window<process_t> analysisWindow;
|
Window<process_t> analysisWindow;
|
||||||
Window<process_t> synthesisWindow;
|
Window<process_t> synthesisWindow;
|
||||||
process_t windowScaleFactor;
|
process_t windowScaleFactor;
|
||||||
GuidedPhaseAdvance guided;
|
GuidedPhaseAdvance guided;
|
||||||
|
|
||||||
ScaleData(GuidedPhaseAdvance::Parameters guidedParameters,
|
ScaleData(GuidedPhaseAdvance::Parameters guidedParameters,
|
||||||
Log log) :
|
Log log) :
|
||||||
fftSize(guidedParameters.fftSize),
|
fftSize(guidedParameters.fftSize),
|
||||||
|
singleWindowMode(guidedParameters.singleWindowMode),
|
||||||
fft(fftSize),
|
fft(fftSize),
|
||||||
analysisWindow(analysisWindowShape(fftSize),
|
analysisWindow(analysisWindowShape(),
|
||||||
analysisWindowLength(fftSize)),
|
analysisWindowLength()),
|
||||||
synthesisWindow(synthesisWindowShape(fftSize),
|
synthesisWindow(synthesisWindowShape(),
|
||||||
synthesisWindowLength(fftSize)),
|
synthesisWindowLength()),
|
||||||
windowScaleFactor(0.0),
|
windowScaleFactor(0.0),
|
||||||
guided(guidedParameters, log)
|
guided(guidedParameters, log)
|
||||||
{
|
{
|
||||||
@@ -277,13 +305,14 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowType analysisWindowShape(int fftSize);
|
WindowType analysisWindowShape();
|
||||||
int analysisWindowLength(int fftSize);
|
int analysisWindowLength();
|
||||||
WindowType synthesisWindowShape(int fftSize);
|
WindowType synthesisWindowShape();
|
||||||
int synthesisWindowLength(int fftSize);
|
int synthesisWindowLength();
|
||||||
};
|
};
|
||||||
|
|
||||||
Parameters m_parameters;
|
Parameters m_parameters;
|
||||||
|
const Limits m_limits;
|
||||||
Log m_log;
|
Log m_log;
|
||||||
|
|
||||||
std::atomic<double> m_timeRatio;
|
std::atomic<double> m_timeRatio;
|
||||||
@@ -297,6 +326,7 @@ protected:
|
|||||||
ChannelAssembly m_channelAssembly;
|
ChannelAssembly m_channelAssembly;
|
||||||
std::unique_ptr<StretchCalculator> m_calculator;
|
std::unique_ptr<StretchCalculator> m_calculator;
|
||||||
std::unique_ptr<Resampler> m_resampler;
|
std::unique_ptr<Resampler> m_resampler;
|
||||||
|
bool m_useReadahead;
|
||||||
std::atomic<int> m_inhop;
|
std::atomic<int> m_inhop;
|
||||||
int m_prevInhop;
|
int m_prevInhop;
|
||||||
int m_prevOuthop;
|
int m_prevOuthop;
|
||||||
@@ -367,6 +397,44 @@ protected:
|
|||||||
return m_parameters.options &
|
return m_parameters.options &
|
||||||
RubberBandStretcher::OptionProcessRealTime;
|
RubberBandStretcher::OptionProcessRealTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void areWeResampling(bool *before, bool *after) const {
|
||||||
|
|
||||||
|
if (before) *before = false;
|
||||||
|
if (after) *after = false;
|
||||||
|
if (!m_resampler) return;
|
||||||
|
|
||||||
|
if (m_parameters.options &
|
||||||
|
RubberBandStretcher::OptionPitchHighConsistency) {
|
||||||
|
if (after) *after = true;
|
||||||
|
|
||||||
|
} else if (m_pitchScale != 1.0) {
|
||||||
|
if (m_pitchScale > 1.0 &&
|
||||||
|
(m_parameters.options &
|
||||||
|
RubberBandStretcher::OptionPitchHighQuality)) {
|
||||||
|
if (after) *after = true;
|
||||||
|
} else if (m_pitchScale < 1.0) {
|
||||||
|
if (after) *after = true;
|
||||||
|
} else {
|
||||||
|
if (before) *before = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSingleWindowed() const {
|
||||||
|
return m_parameters.options &
|
||||||
|
RubberBandStretcher::OptionWindowShort;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getWindowSourceSize() const {
|
||||||
|
int sz = m_guideConfiguration.classificationFftSize +
|
||||||
|
m_limits.maxInhopWithReadahead;
|
||||||
|
if (m_guideConfiguration.longestFftSize > sz) {
|
||||||
|
return m_guideConfiguration.longestFftSize;
|
||||||
|
} else {
|
||||||
|
return sz;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user