Start to bring in unit tests
This commit is contained in:
@@ -26,3 +26,7 @@ build
|
||||
build_*
|
||||
build-*
|
||||
UpgradeLog*
|
||||
out-*/
|
||||
playlist-out/*
|
||||
formant-out-*/
|
||||
out*.wav
|
||||
|
||||
57
meson.build
57
meson.build
@@ -83,6 +83,15 @@ lv2_sources = [
|
||||
'ladspa-lv2/libmain-lv2.cpp',
|
||||
]
|
||||
|
||||
unit_test_sources = [
|
||||
'src/test/TestAllocators.cpp',
|
||||
'src/test/TestFFT.cpp',
|
||||
'src/test/TestResampler.cpp',
|
||||
'src/test/TestVectorOpsComplex.cpp',
|
||||
'src/test/TestVectorOps.cpp',
|
||||
'src/test/test.cpp',
|
||||
]
|
||||
|
||||
general_include_dirs = [
|
||||
'rubberband',
|
||||
'src',
|
||||
@@ -100,6 +109,7 @@ fftw3_dep = dependency('fftw3', version: '>= 3.0.0', required: false)
|
||||
samplerate_dep = dependency('samplerate', version: '>= 0.1.8', required: false)
|
||||
sndfile_dep = dependency('sndfile', version: '>= 1.0.16', required: false)
|
||||
vamp_dep = dependency('vamp-sdk', version: '>= 2.9', required: false)
|
||||
boost_unit_test_dep = dependency('boost_unit_test_framework', required: false)
|
||||
thread_dep = dependency('threads')
|
||||
have_ladspa = cpp.has_header('ladspa.h', args: extra_include_args)
|
||||
have_lv2 = cpp.has_header('lv2.h', args: extra_include_args)
|
||||
@@ -312,6 +322,15 @@ if not sndfile_dep.found()
|
||||
endif
|
||||
have_sndfile = sndfile_dep.found()
|
||||
|
||||
if not boost_unit_test_dep.found()
|
||||
boost_unit_test_dep = cpp.find_library('boost_unit_test_framework',
|
||||
dirs: get_option('extra_lib_dirs'),
|
||||
has_headers: ['boost/test/unit_test.hpp'],
|
||||
header_args: extra_include_args,
|
||||
required: false)
|
||||
endif
|
||||
have_boost_unit_test = boost_unit_test_dep.found()
|
||||
|
||||
|
||||
# General platform and compiler expectations
|
||||
|
||||
@@ -430,6 +449,7 @@ if cpp.get_id() == 'msvc'
|
||||
rubberband_lv2_name = 'lv2-rubberband'
|
||||
rubberband_vamp_name = 'vamp-rubberband'
|
||||
rubberband_jni_name = 'rubberband-jni'
|
||||
unit_tests_name = 'tests'
|
||||
else
|
||||
rubberband_library_name = 'rubberband'
|
||||
rubberband_dynamic_name = 'rubberband'
|
||||
@@ -438,6 +458,7 @@ else
|
||||
rubberband_lv2_name = 'lv2-rubberband'
|
||||
rubberband_vamp_name = 'vamp-rubberband'
|
||||
rubberband_jni_name = 'rubberband-jni'
|
||||
unit_tests_name = 'tests'
|
||||
endif
|
||||
|
||||
rubberband_objlib = static_library(
|
||||
@@ -686,6 +707,42 @@ else
|
||||
message('Not building command-line utility: libsndfile dependency not found')
|
||||
endif
|
||||
|
||||
if have_boost_unit_test
|
||||
target_summary += { 'Unit tests': [ true, 'Name: ' + unit_tests_name ] }
|
||||
message('Will build unit tests: use "meson test -C <builddir>" to run them')
|
||||
unit_tests = executable(
|
||||
unit_tests_name,
|
||||
unit_test_sources,
|
||||
cpp_args: general_compile_args,
|
||||
c_args: general_compile_args,
|
||||
link_args: [
|
||||
arch_flags,
|
||||
feature_libraries,
|
||||
],
|
||||
dependencies: [
|
||||
rubberband_objlib_dep,
|
||||
general_dependencies,
|
||||
boost_unit_test_dep,
|
||||
],
|
||||
install: false,
|
||||
build_by_default: false
|
||||
)
|
||||
general_test_args = [ '--log_level=message' ]
|
||||
test('Allocators',
|
||||
unit_tests, args: [ '--run_test=TestAllocators', general_test_args ])
|
||||
test('FFT',
|
||||
unit_tests, args: [ '--run_test=TestFFT', general_test_args ])
|
||||
test('Resampler',
|
||||
unit_tests, args: [ '--run_test=TestResampler', general_test_args ])
|
||||
test('VectorOps',
|
||||
unit_tests, args: [ '--run_test=TestVectorOps', general_test_args ])
|
||||
test('VectorOpsComplex',
|
||||
unit_tests, args: [ '--run_test=TestVectorOpsComplex', general_test_args ])
|
||||
else
|
||||
target_summary += { 'Unit tests': false }
|
||||
message('Not building unit tests: boost_unit_test_framework dependency not found')
|
||||
endif
|
||||
|
||||
pkg.generate(
|
||||
name: 'rubberband',
|
||||
description: 'Audio time-stretching and pitch-shifting library',
|
||||
|
||||
@@ -214,17 +214,38 @@ public:
|
||||
guidance.phaseReset.f1 = std::max(segmentation.residualAbove,
|
||||
nextSegmentation.residualAbove);
|
||||
}
|
||||
|
||||
/*
|
||||
double higher = snapToTrough(m_defaultHigher, troughs);
|
||||
if (higher > m_maxHigher) higher = m_maxHigher;
|
||||
|
||||
double lower = snapToTrough(m_defaultLower, troughs);
|
||||
if (lower > m_maxLower) lower = m_maxLower;
|
||||
*/
|
||||
/*
|
||||
double prevHigher = guidance.fftBands[1].f1;
|
||||
double higher = snapToTrough(prevHigher, troughs, magnitudes);
|
||||
if (higher < m_minHigher || higher > m_maxHigher) {
|
||||
higher = snapToTrough(m_defaultHigher, troughs, magnitudes);
|
||||
if (higher < m_minHigher || higher > m_maxHigher) {
|
||||
higher = prevHigher;
|
||||
}
|
||||
}
|
||||
*/
|
||||
double higher = m_defaultHigher;
|
||||
|
||||
double prevLower = guidance.fftBands[0].f1;
|
||||
double lower = snapToTrough(prevLower, troughs, magnitudes);
|
||||
if (lower < m_minLower || lower > m_maxLower) {
|
||||
lower = snapToTrough(m_defaultLower, troughs, magnitudes);
|
||||
if (lower < m_minLower || lower > m_maxLower) {
|
||||
lower = prevLower;
|
||||
}
|
||||
}
|
||||
|
||||
guidance.fftBands[0].f0 = 0.0;
|
||||
guidance.fftBands[0].f1 = lower;
|
||||
|
||||
// std::cout << "x:" << lower << std::endl;
|
||||
std::cout << "x:" << lower << std::endl;
|
||||
|
||||
guidance.fftBands[1].f0 = lower;
|
||||
guidance.fftBands[1].f1 = higher;
|
||||
@@ -322,8 +343,22 @@ protected:
|
||||
return (here > 10.e-3 && here > there * 1.4);
|
||||
}
|
||||
|
||||
double snapToTrough(double f, const int *const troughs) const {
|
||||
return frequencyForBin(troughs[binForFrequency(f)]);
|
||||
double snapToTrough(double f,
|
||||
const int *const troughs,
|
||||
const double *const magnitudes) const {
|
||||
// return frequencyForBin(troughs[binForFrequency(f)]);
|
||||
int bin = binForFrequency(f);
|
||||
int snapped = troughs[bin];
|
||||
double sf = frequencyForBin(snapped);
|
||||
std::cout << "snapToTrough: " << f << " -> bin " << bin << " -> snapped " << snapped << " -> " << sf << std::endl;
|
||||
for (int i = -3; i <= 3; ++i) {
|
||||
if (i == 0) std::cout << "[";
|
||||
std::cout << magnitudes[bin + i];
|
||||
if (i == 0) std::cout << "]";
|
||||
std::cout << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return sf;
|
||||
}
|
||||
|
||||
double betaFor(double f, double ratio) const {
|
||||
|
||||
@@ -654,7 +654,7 @@ R3StretcherImpl::analyseChannel(int c, int inhop, int prevInhop, int prevOuthop)
|
||||
cd->nextSegmentation = cd->segmenter->segment(cd->nextClassification.data());
|
||||
|
||||
m_troughPicker.findNearestAndNextPeaks
|
||||
(classifyScale->mag.data(), 1, nullptr,
|
||||
(classifyScale->mag.data(), 3, nullptr,
|
||||
classifyScale->troughs.data());
|
||||
|
||||
double instantaneousRatio = double(prevOuthop) / double(prevInhop);
|
||||
|
||||
104
src/test/TestAllocators.cpp
Normal file
104
src/test/TestAllocators.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/VectorOps.h"
|
||||
#include "../common/Allocators.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
using namespace RubberBand;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(TestAllocators)
|
||||
|
||||
#define COMPARE_ARRAY(a, b) \
|
||||
for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-14); \
|
||||
}
|
||||
|
||||
#define COMPARE_N(a, b, n) \
|
||||
for (int cmp_i = 0; cmp_i < n; ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-14); \
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(alloc_dealloc)
|
||||
{
|
||||
double *v = allocate<double>(4);
|
||||
v[0] = 0.1;
|
||||
v[1] = 2.0;
|
||||
v[2] = -0.3;
|
||||
v[3] = 4.0;
|
||||
double *e = allocate<double>(4);
|
||||
e[0] = -0.3;
|
||||
e[1] = 4.0;
|
||||
e[2] = 0.1;
|
||||
e[3] = 2.0;
|
||||
v_fftshift(v, 4);
|
||||
COMPARE_N(v, e, 4);
|
||||
deallocate(v);
|
||||
deallocate(e);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(alloc_zero)
|
||||
{
|
||||
double *v = allocate_and_zero<double>(4);
|
||||
BOOST_CHECK_EQUAL(v[0], 0.f);
|
||||
BOOST_CHECK_EQUAL(v[1], 0.f);
|
||||
BOOST_CHECK_EQUAL(v[2], 0.f);
|
||||
BOOST_CHECK_EQUAL(v[3], 0.f);
|
||||
deallocate(v);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(alloc_dealloc_channels)
|
||||
{
|
||||
double **v = allocate_channels<double>(2, 4);
|
||||
v[0][0] = 0.1;
|
||||
v[0][1] = 2.0;
|
||||
v[0][2] = -0.3;
|
||||
v[0][3] = 4.0;
|
||||
v[1][0] = -0.3;
|
||||
v[1][1] = 4.0;
|
||||
v[1][2] = 0.1;
|
||||
v[1][3] = 2.0;
|
||||
v_fftshift(v[0], 4);
|
||||
COMPARE_N(v[0], v[1], 4);
|
||||
deallocate_channels(v, 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(stl)
|
||||
{
|
||||
std::vector<double, StlAllocator<double> > v;
|
||||
v.push_back(0.1);
|
||||
v.push_back(2.0);
|
||||
v.push_back(-0.3);
|
||||
v.push_back(4.0);
|
||||
double e[] = { -0.3, 4.0, 0.1, 2.0 };
|
||||
v_fftshift(v.data(), 4);
|
||||
COMPARE_N(v.data(), e, 4);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
723
src/test/TestFFT.cpp
Normal file
723
src/test/TestFFT.cpp
Normal file
@@ -0,0 +1,723 @@
|
||||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/FFT.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
|
||||
using namespace RubberBand;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(TestFFT)
|
||||
|
||||
#define DEFINE_EPS(fft) \
|
||||
float epsf = 1e-6f; \
|
||||
double eps; \
|
||||
if (fft.getSupportedPrecisions() & FFT::DoublePrecision) { \
|
||||
eps = 1e-14; \
|
||||
} else { \
|
||||
eps = epsf; \
|
||||
} \
|
||||
(void)epsf; (void)eps;
|
||||
|
||||
#define USING_FFT(n) \
|
||||
FFT fft(n); \
|
||||
DEFINE_EPS(fft);
|
||||
|
||||
#define COMPARE(a, b) BOOST_CHECK_SMALL(a-b, eps)
|
||||
#define COMPARE_F(a, b) BOOST_CHECK_SMALL(a-b, epsf)
|
||||
|
||||
#define COMPARE_ZERO(a) BOOST_CHECK_SMALL(a, eps)
|
||||
#define COMPARE_ZERO_F(a) BOOST_CHECK_SMALL(a, epsf)
|
||||
|
||||
#define COMPARE_ALL(a, x) \
|
||||
for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i] - x, eps); \
|
||||
}
|
||||
#define COMPARE_ALL_F(a, x) \
|
||||
for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i] - x, epsf); \
|
||||
}
|
||||
#define COMPARE_ARR(a, b, n) \
|
||||
for (int cmp_i = 0; cmp_i < n; ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], eps); \
|
||||
}
|
||||
#define COMPARE_SCALED(a, b, s) \
|
||||
for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i]/s - b[cmp_i], eps); \
|
||||
}
|
||||
#define COMPARE_SCALED_N(a, b, n, s) \
|
||||
for (int cmp_i = 0; cmp_i < n; ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i]/s - b[cmp_i], eps); \
|
||||
}
|
||||
#define COMPARE_SCALED_F(a, b, s) \
|
||||
for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i]/s - b[cmp_i], epsf); \
|
||||
}
|
||||
|
||||
#define ONE_IMPL_AUTO_TEST_CASE(name, impl) \
|
||||
BOOST_AUTO_TEST_CASE(name##_##impl) \
|
||||
{ \
|
||||
std::set<std::string> impls = FFT::getImplementations(); \
|
||||
if (impls.find(#impl) == impls.end()) return; \
|
||||
FFT::setDefaultImplementation(#impl); \
|
||||
performTest_##name(); \
|
||||
FFT::setDefaultImplementation(""); \
|
||||
}
|
||||
|
||||
// If you add an implementation in FFT.cpp, add it also to
|
||||
// ALL_IMPL_AUTO_TEST_CASE and all_implementations[] below
|
||||
|
||||
#define ALL_IMPL_AUTO_TEST_CASE(name) \
|
||||
void performTest_##name (); \
|
||||
ONE_IMPL_AUTO_TEST_CASE(name, ipp); \
|
||||
ONE_IMPL_AUTO_TEST_CASE(name, vdsp); \
|
||||
ONE_IMPL_AUTO_TEST_CASE(name, fftw); \
|
||||
ONE_IMPL_AUTO_TEST_CASE(name, kissfft); \
|
||||
ONE_IMPL_AUTO_TEST_CASE(name, builtin); \
|
||||
ONE_IMPL_AUTO_TEST_CASE(name, dft); \
|
||||
void performTest_##name ()
|
||||
|
||||
std::string all_implementations[] = {
|
||||
"ipp", "vdsp", "fftw", "kissfft", "builtin", "dft"
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(showImplementations)
|
||||
{
|
||||
std::set<std::string> impls = FFT::getImplementations();
|
||||
BOOST_TEST_MESSAGE("The following implementations are compiled in and will be tested:");
|
||||
for (int i = 0; i < int(sizeof(all_implementations)/sizeof(all_implementations[0])); ++i) {
|
||||
if (impls.find(all_implementations[i]) != impls.end()) {
|
||||
BOOST_TEST_MESSAGE(" +" << all_implementations[i]);
|
||||
}
|
||||
}
|
||||
BOOST_TEST_MESSAGE("The following implementations are NOT compiled in and will not be tested:");
|
||||
for (int i = 0; i < int(sizeof(all_implementations)/sizeof(all_implementations[0])); ++i) {
|
||||
if (impls.find(all_implementations[i]) == impls.end()) {
|
||||
BOOST_TEST_MESSAGE(" -" << all_implementations[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 1a. Simple synthetic signals, transforms to separate real/imag arrays,
|
||||
* double-precision
|
||||
*/
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(dc)
|
||||
{
|
||||
// DC-only signal. The DC bin is purely real
|
||||
double in[] = { 1, 1, 1, 1 };
|
||||
double re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE(re[0], 4.0);
|
||||
COMPARE_ZERO(re[1]);
|
||||
COMPARE_ZERO(re[2]);
|
||||
COMPARE_ALL(im, 0.0);
|
||||
double back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(sine)
|
||||
{
|
||||
// Sine. Output is purely imaginary
|
||||
double in[] = { 0, 1, 0, -1 };
|
||||
double re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ALL(re, 0.0);
|
||||
COMPARE_ZERO(im[0]);
|
||||
COMPARE(im[1], -2.0);
|
||||
COMPARE_ZERO(im[2]);
|
||||
double back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(sine_8)
|
||||
{
|
||||
// Longer sine. With only 4 elements, the real transform only
|
||||
// needs to get the DC and Nyquist bins right for its two complex
|
||||
// sub-transforms. We need a longer test to check the real
|
||||
// transform is working properly.
|
||||
double cospi4 = 0.5 * sqrt(2.0);
|
||||
double in[] = { 0, cospi4, 1.0, cospi4, 0.0, -cospi4, -1.0, -cospi4 };
|
||||
double re[5], im[5];
|
||||
USING_FFT(8);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ALL(re, 0.0);
|
||||
COMPARE_ZERO(im[0]);
|
||||
COMPARE(im[1], -4.0);
|
||||
COMPARE_ZERO(im[2]);
|
||||
COMPARE_ZERO(im[3]);
|
||||
COMPARE_ZERO(im[4]);
|
||||
double back[8];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED(back, in, 8);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(cosine)
|
||||
{
|
||||
// Cosine. Output is purely real
|
||||
double in[] = { 1, 0, -1, 0 };
|
||||
double re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ZERO(re[0]);
|
||||
COMPARE(re[1], 2.0);
|
||||
COMPARE_ZERO(re[2]);
|
||||
COMPARE_ALL(im, 0.0);
|
||||
double back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(cosine_8)
|
||||
{
|
||||
// Longer cosine.
|
||||
double cospi4 = 0.5 * sqrt(2.0);
|
||||
double in[] = { 1.0, cospi4, 0.0, -cospi4, -1.0, -cospi4, 0.0, cospi4 };
|
||||
double re[5], im[5];
|
||||
USING_FFT(8);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ALL(im, 0.0);
|
||||
COMPARE_ZERO(re[0]);
|
||||
COMPARE(re[1], 4.0);
|
||||
COMPARE_ZERO(re[2]);
|
||||
COMPARE_ZERO(re[3]);
|
||||
COMPARE_ZERO(re[4]);
|
||||
double back[8];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED(back, in, 8);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(sineCosine)
|
||||
{
|
||||
// Sine and cosine mixed
|
||||
double in[] = { 0.5, 1, -0.5, -1 };
|
||||
double re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ZERO(re[0]);
|
||||
COMPARE(re[1], 1.0);
|
||||
COMPARE_ZERO(re[2]);
|
||||
COMPARE_ZERO(im[0]);
|
||||
COMPARE(im[1], -2.0);
|
||||
COMPARE_ZERO(im[2]);
|
||||
double back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(nyquist)
|
||||
{
|
||||
double in[] = { 1, -1, 1, -1 };
|
||||
double re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ZERO(re[0]);
|
||||
COMPARE_ZERO(re[1]);
|
||||
COMPARE(re[2], 4.0);
|
||||
COMPARE_ALL(im, 0.0);
|
||||
double back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(dirac)
|
||||
{
|
||||
double in[] = { 1, 0, 0, 0 };
|
||||
double re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE(re[0], 1.0);
|
||||
COMPARE(re[1], 1.0);
|
||||
COMPARE(re[2], 1.0);
|
||||
COMPARE_ALL(im, 0.0);
|
||||
double back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED(back, in, 4);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 1b. Simple synthetic signals, transforms to separate real/imag arrays,
|
||||
* single-precision (i.e. single-precision version of 1a)
|
||||
*/
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(dcF)
|
||||
{
|
||||
// DC-only signal. The DC bin is purely real
|
||||
float in[] = { 1, 1, 1, 1 };
|
||||
float re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_F(re[0], 4.0f);
|
||||
COMPARE_ZERO_F(re[1]);
|
||||
COMPARE_ZERO_F(re[2]);
|
||||
COMPARE_ALL_F(im, 0.0f);
|
||||
float back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED_F(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(sineF)
|
||||
{
|
||||
// Sine. Output is purely imaginary
|
||||
float in[] = { 0, 1, 0, -1 };
|
||||
float re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ALL_F(re, 0.0f);
|
||||
COMPARE_ZERO_F(im[0]);
|
||||
COMPARE_F(im[1], -2.0f);
|
||||
COMPARE_ZERO_F(im[2]);
|
||||
float back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED_F(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(cosineF)
|
||||
{
|
||||
// Cosine. Output is purely real
|
||||
float in[] = { 1, 0, -1, 0 };
|
||||
float re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ZERO_F(re[0]);
|
||||
COMPARE_F(re[1], 2.0f);
|
||||
COMPARE_ZERO_F(re[2]);
|
||||
COMPARE_ALL_F(im, 0.0f);
|
||||
float back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED_F(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(sineCosineF)
|
||||
{
|
||||
// Sine and cosine mixed
|
||||
float in[] = { 0.5, 1, -0.5, -1 };
|
||||
float re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ZERO_F(re[0]);
|
||||
COMPARE_F(re[1], 1.0f);
|
||||
COMPARE_ZERO_F(re[2]);
|
||||
COMPARE_ZERO_F(im[0]);
|
||||
COMPARE_F(im[1], -2.0f);
|
||||
COMPARE_ZERO_F(im[2]);
|
||||
float back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED_F(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(nyquistF)
|
||||
{
|
||||
float in[] = { 1, -1, 1, -1 };
|
||||
float re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ZERO_F(re[0]);
|
||||
COMPARE_ZERO_F(re[1]);
|
||||
COMPARE_F(re[2], 4.0f);
|
||||
COMPARE_ALL_F(im, 0.0f);
|
||||
float back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED_F(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(diracF)
|
||||
{
|
||||
float in[] = { 1, 0, 0, 0 };
|
||||
float re[3], im[3];
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_F(re[0], 1.0f);
|
||||
COMPARE_F(re[1], 1.0f);
|
||||
COMPARE_F(re[2], 1.0f);
|
||||
COMPARE_ALL_F(im, 0.0f);
|
||||
float back[4];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED_F(back, in, 4);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 2a. Subset of synthetic signals, testing different output formats
|
||||
* (interleaved complex, polar, magnitude-only, and our weird
|
||||
* cepstral thing), double-precision
|
||||
*/
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(interleaved)
|
||||
{
|
||||
// Sine and cosine mixed, test output format
|
||||
double in[] = { 0.5, 1, -0.5, -1 };
|
||||
double out[6];
|
||||
USING_FFT(4);
|
||||
fft.forwardInterleaved(in, out);
|
||||
COMPARE_ZERO(out[0]);
|
||||
COMPARE_ZERO(out[1]);
|
||||
COMPARE(out[2], 1.0);
|
||||
COMPARE(out[3], -2.0);
|
||||
COMPARE_ZERO(out[4]);
|
||||
COMPARE_ZERO(out[5]);
|
||||
double back[4];
|
||||
fft.inverseInterleaved(out, back);
|
||||
COMPARE_SCALED(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(sinePolar)
|
||||
{
|
||||
double in[] = { 0, 1, 0, -1 };
|
||||
double mag[3], phase[3];
|
||||
USING_FFT(4);
|
||||
fft.forwardPolar(in, mag, phase);
|
||||
COMPARE_ZERO(mag[0]);
|
||||
COMPARE(mag[1], 2.0);
|
||||
COMPARE_ZERO(mag[2]);
|
||||
// No meaningful tests for phase[i] where mag[i]==0 (phase
|
||||
// could legitimately be anything)
|
||||
COMPARE(phase[1], -M_PI/2.0);
|
||||
double back[4];
|
||||
fft.inversePolar(mag, phase, back);
|
||||
COMPARE_SCALED(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(cosinePolar)
|
||||
{
|
||||
double in[] = { 1, 0, -1, 0 };
|
||||
double mag[3], phase[3];
|
||||
USING_FFT(4);
|
||||
fft.forwardPolar(in, mag, phase);
|
||||
COMPARE_ZERO(mag[0]);
|
||||
COMPARE(mag[1], 2.0);
|
||||
COMPARE_ZERO(mag[2]);
|
||||
// No meaningful tests for phase[i] where mag[i]==0 (phase
|
||||
// could legitimately be anything)
|
||||
COMPARE_ZERO(phase[1]);
|
||||
double back[4];
|
||||
fft.inversePolar(mag, phase, back);
|
||||
COMPARE_SCALED(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(magnitude)
|
||||
{
|
||||
// Sine and cosine mixed
|
||||
double in[] = { 0.5, 1, -0.5, -1 };
|
||||
double out[3];
|
||||
USING_FFT(4);
|
||||
fft.forwardMagnitude(in, out);
|
||||
COMPARE_ZERO(out[0]);
|
||||
COMPARE_F(float(out[1]), sqrtf(5.0));
|
||||
COMPARE_ZERO(out[2]);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(cepstrum)
|
||||
{
|
||||
double in[] = { 1, 0, 0, 0, 1, 0, 0, 0 };
|
||||
double mag[5];
|
||||
USING_FFT(8);
|
||||
fft.forwardMagnitude(in, mag);
|
||||
double cep[8];
|
||||
fft.inverseCepstral(mag, cep);
|
||||
BOOST_CHECK_SMALL(cep[1], 1e-9);
|
||||
BOOST_CHECK_SMALL(cep[2], 1e-9);
|
||||
BOOST_CHECK_SMALL(cep[3], 1e-9);
|
||||
BOOST_CHECK_SMALL(cep[5], 1e-9);
|
||||
BOOST_CHECK_SMALL(cep[6], 1e-9);
|
||||
BOOST_CHECK_SMALL(cep[7], 1e-9);
|
||||
BOOST_CHECK_SMALL(-6.561181 - cep[0]/8, 0.000001);
|
||||
BOOST_CHECK_SMALL( 7.254329 - cep[4]/8, 0.000001);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 2b. Subset of synthetic signals, testing different output formats
|
||||
* (interleaved complex, polar, magnitude-only, and our weird
|
||||
* cepstral thing), single-precision (i.e. single-precision
|
||||
* version of 2a)
|
||||
*/
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(interleavedF)
|
||||
{
|
||||
// Sine and cosine mixed, test output format
|
||||
float in[] = { 0.5, 1, -0.5, -1 };
|
||||
float out[6];
|
||||
USING_FFT(4);
|
||||
fft.forwardInterleaved(in, out);
|
||||
COMPARE_ZERO_F(out[0]);
|
||||
COMPARE_ZERO_F(out[1]);
|
||||
COMPARE_F(out[2], 1.0f);
|
||||
COMPARE_F(out[3], -2.0f);
|
||||
COMPARE_ZERO_F(out[4]);
|
||||
COMPARE_ZERO_F(out[5]);
|
||||
float back[4];
|
||||
fft.inverseInterleaved(out, back);
|
||||
COMPARE_SCALED_F(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(cosinePolarF)
|
||||
{
|
||||
float in[] = { 1, 0, -1, 0 };
|
||||
float mag[3], phase[3];
|
||||
USING_FFT(4);
|
||||
fft.forwardPolar(in, mag, phase);
|
||||
COMPARE_ZERO_F(mag[0]);
|
||||
COMPARE_F(mag[1], 2.0f);
|
||||
COMPARE_ZERO_F(mag[2]);
|
||||
// No meaningful tests for phase[i] where mag[i]==0 (phase
|
||||
// could legitimately be anything)
|
||||
COMPARE_ZERO_F(phase[1]);
|
||||
float back[4];
|
||||
fft.inversePolar(mag, phase, back);
|
||||
COMPARE_SCALED_F(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(sinePolarF)
|
||||
{
|
||||
float in[] = { 0, 1, 0, -1 };
|
||||
float mag[3], phase[3];
|
||||
USING_FFT(4);
|
||||
fft.forwardPolar(in, mag, phase);
|
||||
COMPARE_ZERO_F(mag[0]);
|
||||
COMPARE_F(mag[1], 2.0f);
|
||||
COMPARE_ZERO_F(mag[2]);
|
||||
// No meaningful tests for phase[i] where mag[i]==0 (phase
|
||||
// could legitimately be anything)
|
||||
COMPARE_F(phase[1], -float(M_PI)/2.0f);
|
||||
float back[4];
|
||||
fft.inversePolar(mag, phase, back);
|
||||
COMPARE_SCALED_F(back, in, 4);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(magnitudeF)
|
||||
{
|
||||
// Sine and cosine mixed
|
||||
float in[] = { 0.5, 1, -0.5, -1 };
|
||||
float out[3];
|
||||
USING_FFT(4);
|
||||
fft.forwardMagnitude(in, out);
|
||||
COMPARE_ZERO_F(out[0]);
|
||||
COMPARE_F(float(out[1]), sqrtf(5.0f));
|
||||
COMPARE_ZERO_F(out[2]);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(cepstrumF)
|
||||
{
|
||||
float in[] = { 1, 0, 0, 0, 1, 0, 0, 0 };
|
||||
float mag[5];
|
||||
USING_FFT(8);
|
||||
fft.forwardMagnitude(in, mag);
|
||||
float cep[8];
|
||||
fft.inverseCepstral(mag, cep);
|
||||
COMPARE_ZERO_F(cep[1]);
|
||||
COMPARE_ZERO_F(cep[2]);
|
||||
COMPARE_ZERO_F(cep[3]);
|
||||
COMPARE_ZERO_F(cep[5]);
|
||||
COMPARE_ZERO_F(cep[6]);
|
||||
COMPARE_ZERO_F(cep[7]);
|
||||
BOOST_CHECK_SMALL(-6.561181 - cep[0]/8, 0.000001);
|
||||
BOOST_CHECK_SMALL( 7.254329 - cep[4]/8, 0.000001);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 4. Bounds checking, double-precision and single-precision
|
||||
*/
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(forwardArrayBounds)
|
||||
{
|
||||
double in[] = { 1, 1, -1, -1 };
|
||||
|
||||
// Initialise output bins to something recognisable, so we can
|
||||
// tell if they haven't been written
|
||||
double re[] = { 999, 999, 999, 999, 999 };
|
||||
double im[] = { 999, 999, 999, 999, 999 };
|
||||
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re+1, im+1);
|
||||
|
||||
// Check we haven't overrun the output arrays
|
||||
COMPARE(re[0], 999.0);
|
||||
COMPARE(im[0], 999.0);
|
||||
COMPARE(re[4], 999.0);
|
||||
COMPARE(im[4], 999.0);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(inverseArrayBounds)
|
||||
{
|
||||
// The inverse transform is only supposed to refer to the first
|
||||
// N/2+1 bins and synthesise the rest rather than read them - so
|
||||
// initialise the next one to some value that would mess up the
|
||||
// results if it were used
|
||||
double re[] = { 0, 1, 0, 456 };
|
||||
double im[] = { 0, -2, 0, 456 };
|
||||
|
||||
// Initialise output bins to something recognisable, so we can
|
||||
// tell if they haven't been written
|
||||
double out[] = { 999, 999, 999, 999, 999, 999 };
|
||||
|
||||
USING_FFT(4);
|
||||
fft.inverse(re, im, out+1);
|
||||
|
||||
// Check we haven't overrun the output arrays
|
||||
COMPARE(out[0], 999.0);
|
||||
COMPARE(out[5], 999.0);
|
||||
|
||||
// And check the results are as we expect, i.e. that we haven't
|
||||
// used the bogus final bin
|
||||
COMPARE(out[1] / 4, 0.5);
|
||||
COMPARE(out[2] / 4, 1.0);
|
||||
COMPARE(out[3] / 4, -0.5);
|
||||
COMPARE(out[4] / 4, -1.0);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(forwardArrayBoundsF)
|
||||
{
|
||||
float in[] = { 1, 1, -1, -1 };
|
||||
|
||||
// Initialise output bins to something recognisable, so we can
|
||||
// tell if they haven't been written
|
||||
float re[] = { 999, 999, 999, 999, 999 };
|
||||
float im[] = { 999, 999, 999, 999, 999 };
|
||||
|
||||
USING_FFT(4);
|
||||
fft.forward(in, re+1, im+1);
|
||||
|
||||
// Check we haven't overrun the output arrays
|
||||
COMPARE_F(re[0], 999.0f);
|
||||
COMPARE_F(im[0], 999.0f);
|
||||
COMPARE_F(re[4], 999.0f);
|
||||
COMPARE_F(im[4], 999.0f);
|
||||
}
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(inverseArrayBoundsF)
|
||||
{
|
||||
// The inverse transform is only supposed to refer to the first
|
||||
// N/2+1 bins and synthesise the rest rather than read them - so
|
||||
// initialise the next one to some value that would mess up the
|
||||
// results if it were used
|
||||
float re[] = { 0, 1, 0, 456 };
|
||||
float im[] = { 0, -2, 0, 456 };
|
||||
|
||||
// Initialise output bins to something recognisable, so we can
|
||||
// tell if they haven't been written
|
||||
float out[] = { 999, 999, 999, 999, 999, 999 };
|
||||
|
||||
USING_FFT(4);
|
||||
fft.inverse(re, im, out+1);
|
||||
|
||||
// Check we haven't overrun the output arrays
|
||||
COMPARE_F(out[0], 999.0f);
|
||||
COMPARE_F(out[5], 999.0f);
|
||||
|
||||
// And check the results are as we expect, i.e. that we haven't
|
||||
// used the bogus final bin
|
||||
COMPARE_F(out[1] / 4.0f, 0.5f);
|
||||
COMPARE_F(out[2] / 4.0f, 1.0f);
|
||||
COMPARE_F(out[3] / 4.0f, -0.5f);
|
||||
COMPARE_F(out[4] / 4.0f, -1.0f);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 6. Slightly longer transforms of pseudorandom data.
|
||||
*/
|
||||
|
||||
ALL_IMPL_AUTO_TEST_CASE(random_precalc_16)
|
||||
{
|
||||
double in[] = {
|
||||
-0.24392125308057722, 0.03443898163344272, 0.3448145656738877,
|
||||
-0.9625837464603908, 3.366568317669671, 0.9947191221586653,
|
||||
-1.5038984435999945, 1.3859898682581235, -1.1230576306688778,
|
||||
-1.6757487116512024, -1.5874436867863229, -2.0794018781307155,
|
||||
-0.5450152775818973, 0.7530907176983748, 1.0743170685904255,
|
||||
3.1787609811018775
|
||||
};
|
||||
double expected_re[] = {
|
||||
1.41162899482, 7.63975551593, -1.20622641052, -1.77829578443,
|
||||
3.12678465246, -2.84220463109, -7.17083743716, 0.497290409945,
|
||||
-1.84690167439,
|
||||
};
|
||||
double expected_im[] = {
|
||||
0.0, -4.67826048083, 8.58829211964, 4.96449646815,
|
||||
1.41626511493, -3.77219223978, 6.96219662744, 2.23138519225,
|
||||
0.0,
|
||||
};
|
||||
double re[9], im[9];
|
||||
USING_FFT(16);
|
||||
if (eps < 1e-11) {
|
||||
eps = 1e-11;
|
||||
}
|
||||
fft.forward(in, re, im);
|
||||
COMPARE_ARR(re, expected_re, 9);
|
||||
COMPARE_ARR(im, expected_im, 9);
|
||||
double back[16];
|
||||
fft.inverse(re, im, back);
|
||||
COMPARE_SCALED(back, in, 16);
|
||||
}
|
||||
|
||||
/* This one has data from a PRNG, with a fixed seed. Must pass two
|
||||
* tests: (i) same as DFT; (ii) inverse produces original input (after
|
||||
* scaling) */
|
||||
ALL_IMPL_AUTO_TEST_CASE(random)
|
||||
{
|
||||
const int n = 64;
|
||||
double *in = new double[n];
|
||||
double *re = new double[n/2 + 1];
|
||||
double *im = new double[n/2 + 1];
|
||||
double *re_compare = new double[n/2 + 1];
|
||||
double *im_compare = new double[n/2 + 1];
|
||||
double *back = new double[n];
|
||||
srand48(0);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
in[i] = drand48() * 4.0 - 2.0;
|
||||
}
|
||||
USING_FFT(n);
|
||||
if (eps < 1e-11) {
|
||||
eps = 1e-11;
|
||||
}
|
||||
fft.forward(in, re, im);
|
||||
fft.inverse(re, im, back);
|
||||
FFT::setDefaultImplementation("dft");
|
||||
fft.forward(in, re_compare, im_compare);
|
||||
COMPARE_ARR(re, re_compare, n/2 + 1);
|
||||
COMPARE_ARR(im, im_compare, n/2 + 1);
|
||||
COMPARE_SCALED_N(back, in, n, n);
|
||||
delete[] back;
|
||||
delete[] im_compare;
|
||||
delete[] re_compare;
|
||||
delete[] im;
|
||||
delete[] re;
|
||||
delete[] in;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
218
src/test/TestResampler.cpp
Normal file
218
src/test/TestResampler.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/Resampler.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace RubberBand;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(TestResampler)
|
||||
|
||||
#define LEN(a) (int(sizeof(a)/sizeof(a[0])))
|
||||
|
||||
static vector<float>
|
||||
sine(double samplerate, double frequency, int nsamples)
|
||||
{
|
||||
vector<float> v(nsamples, 0.f);
|
||||
for (int i = 0; i < nsamples; ++i) {
|
||||
v[i] = sin ((i * 2.0 * M_PI * frequency) / samplerate);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
#define COMPARE_N(a, b, n) \
|
||||
for (int cmp_i = 0; cmp_i < n; ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL((a)[cmp_i] - (b)[cmp_i], 1e-4f); \
|
||||
}
|
||||
|
||||
static const float guard_value = -999.f;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(interpolated_sine_1ch_interleaved)
|
||||
{
|
||||
// Interpolating a sinusoid should give us a sinusoid, once we've
|
||||
// dropped the first few samples
|
||||
vector<float> in = sine(8, 2, 1000); // 2Hz wave at 8Hz: [ 0, 1, 0, -1 ] etc
|
||||
vector<float> expected = sine(16, 2, 2000);
|
||||
vector<float> out(in.size() * 2 + 1, guard_value);
|
||||
Resampler r(Resampler::Parameters(), 1);
|
||||
int returned = r.resampleInterleaved
|
||||
(out.data(), out.size(), in.data(), in.size(), 2, true);
|
||||
|
||||
// because final was true, we expect to have exactly the right
|
||||
// number of samples back
|
||||
BOOST_CHECK_EQUAL(returned, int(in.size() * 2));
|
||||
BOOST_CHECK_NE(out[returned-1], guard_value);
|
||||
BOOST_CHECK_EQUAL(out[returned], guard_value);
|
||||
|
||||
// and they should match the expected data, at least in the middle
|
||||
const float *outf = out.data() + 200, *expectedf = expected.data() + 200;
|
||||
COMPARE_N(outf, expectedf, 600);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(interpolated_sine_1ch_noninterleaved)
|
||||
{
|
||||
// Interpolating a sinusoid should give us a sinusoid, once we've
|
||||
// dropped the first few samples
|
||||
vector<float> in = sine(8, 2, 1000); // 2Hz wave at 8Hz: [ 0, 1, 0, -1 ] etc
|
||||
vector<float> expected = sine(16, 2, 2000);
|
||||
vector<float> out(in.size() * 2 + 1, guard_value);
|
||||
const float *in_data = in.data();
|
||||
float *out_data = out.data();
|
||||
Resampler r(Resampler::Parameters(), 1);
|
||||
int returned = r.resample
|
||||
(&out_data, out.size(), &in_data, in.size(), 2, true);
|
||||
|
||||
// because final was true, we expect to have exactly the right
|
||||
// number of samples back
|
||||
BOOST_CHECK_EQUAL(returned, int(in.size() * 2));
|
||||
BOOST_CHECK_NE(out[returned-1], guard_value);
|
||||
BOOST_CHECK_EQUAL(out[returned], guard_value);
|
||||
|
||||
// and they should match the expected data, at least in the middle
|
||||
const float *outf = out.data() + 200, *expectedf = expected.data() + 200;
|
||||
COMPARE_N(outf, expectedf, 600);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(overrun_interleaved)
|
||||
{
|
||||
// Check that the outcount argument is correctly used: any samples
|
||||
// already in the out buffer beyond outcount*channels must be left
|
||||
// untouched. We test this with short buffers (likely to be
|
||||
// shorter than the resampler filter length) and longer ones, with
|
||||
// resampler ratios that reduce, leave unchanged, and raise the
|
||||
// sample rate, and with all quality settings.
|
||||
|
||||
// Options are ordered from most normal/likely to least.
|
||||
|
||||
int channels = 2;
|
||||
|
||||
int lengths[] = { 6000, 6 };
|
||||
int constructionBufferSizes[] = { 0, 1000 };
|
||||
double ratios[] = { 1.0, 10.0, 0.1 };
|
||||
Resampler::Quality qualities[] = {
|
||||
Resampler::FastestTolerable, Resampler::Best, Resampler::Fastest
|
||||
};
|
||||
|
||||
bool failed = false;
|
||||
|
||||
for (int li = 0; li < LEN(lengths); ++li) {
|
||||
for (int cbi = 0; cbi < LEN(constructionBufferSizes); ++cbi) {
|
||||
for (int ri = 0; ri < LEN(ratios); ++ri) {
|
||||
for (int qi = 0; qi < LEN(qualities); ++qi) {
|
||||
|
||||
int length = lengths[li];
|
||||
double ratio = ratios[ri];
|
||||
Resampler::Parameters parameters;
|
||||
parameters.quality = qualities[qi];
|
||||
parameters.maxBufferSize = constructionBufferSizes[cbi];
|
||||
Resampler r(parameters, channels);
|
||||
|
||||
float *inbuf = new float[length * channels];
|
||||
for (int i = 0; i < length; ++i) {
|
||||
for (int c = 0; c < channels; ++c) {
|
||||
inbuf[i*channels+c] =
|
||||
sinf((i * 2.0 * M_PI * 440.0) / 44100.0);
|
||||
}
|
||||
}
|
||||
|
||||
int outcount = int(round(length * ratio));
|
||||
outcount -= 10;
|
||||
if (outcount < 1) outcount = 1;
|
||||
int outbuflen = outcount + 10;
|
||||
float *outbuf = new float[outbuflen * channels];
|
||||
for (int i = 0; i < outbuflen; ++i) {
|
||||
for (int c = 0; c < channels; ++c) {
|
||||
outbuf[i*channels+c] = guard_value;
|
||||
}
|
||||
}
|
||||
/*
|
||||
cerr << "\nTesting with length = " << length << ", ratio = "
|
||||
<< ratio << ", outcount = " << outcount << ", final = false"
|
||||
<< endl;
|
||||
*/
|
||||
int returned = r.resampleInterleaved
|
||||
(outbuf, outcount, inbuf, length, ratio, false);
|
||||
|
||||
BOOST_CHECK_LE(returned, outcount);
|
||||
|
||||
for (int i = returned; i < outbuflen; ++i) {
|
||||
for (int c = 0; c < channels; ++c) {
|
||||
BOOST_CHECK_EQUAL(outbuf[i*channels+c], guard_value);
|
||||
if (outbuf[i*channels+c] != guard_value) {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
cerr << "Test failed, abandoning remaining loop cycles"
|
||||
<< endl;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
cerr << "\nContinuing with length = " << length << ", ratio = "
|
||||
<< ratio << ", outcount = " << outcount << ", final = true"
|
||||
<< endl;
|
||||
*/
|
||||
returned = r.resampleInterleaved
|
||||
(outbuf, outcount, inbuf, length, ratio, true);
|
||||
|
||||
BOOST_CHECK_LE(returned, outcount);
|
||||
|
||||
for (int i = returned; i < outbuflen; ++i) {
|
||||
for (int c = 0; c < channels; ++c) {
|
||||
BOOST_CHECK_EQUAL(outbuf[i*channels+c], guard_value);
|
||||
if (outbuf[i*channels+c] != guard_value) {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] outbuf;
|
||||
delete[] inbuf;
|
||||
|
||||
if (failed) {
|
||||
cerr << "Test failed, abandoning remaining loop cycles"
|
||||
<< endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) break;
|
||||
}
|
||||
if (failed) break;
|
||||
}
|
||||
if (failed) break;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
249
src/test/TestVectorOps.cpp
Normal file
249
src/test/TestVectorOps.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/VectorOps.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
using namespace RubberBand;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(TestVectorOps)
|
||||
|
||||
#define COMPARE_ARRAY(a, b) \
|
||||
for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-14); \
|
||||
}
|
||||
|
||||
#define COMPARE_N(a, b, n) \
|
||||
for (int cmp_i = 0; cmp_i < n; ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-14); \
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(add)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, 3.0 };
|
||||
double b[] = { -1.0, 3.0, -4.5 };
|
||||
double expected[] = { 0.0, 5.0, -1.5 };
|
||||
v_add(a, b, 3);
|
||||
COMPARE_N(a, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(add_with_gain)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, 3.0 };
|
||||
double b[] = { -1.0, 3.0, -4.5 };
|
||||
double expected[] = { -0.5, 6.5, -3.75 };
|
||||
v_add_with_gain(a, b, 1.5, 3);
|
||||
COMPARE_N(a, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(subtract)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, 3.0 };
|
||||
double b[] = { -1.0, 3.0, -4.5 };
|
||||
double expected[] = { 2.0, -1.0, 7.5 };
|
||||
v_subtract(a, b, 3);
|
||||
COMPARE_N(a, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(scale)
|
||||
{
|
||||
double a[] = { -1.0, 3.0, -4.5 };
|
||||
double scale = -0.5;
|
||||
double expected[] = { 0.5, -1.5, 2.25 };
|
||||
v_scale(a, scale, 3);
|
||||
COMPARE_N(a, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(multiply)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, 3.0 };
|
||||
double b[] = { -1.0, 3.0, -4.5 };
|
||||
double expected[] = { -1.0, 6.0, -13.5 };
|
||||
v_multiply(a, b, 3);
|
||||
COMPARE_N(a, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(multiply_and_add)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, 3.0 };
|
||||
double b[] = { -1.0, 3.0, -4.5 };
|
||||
double c[] = { 3.0, -1.0, 4.0 };
|
||||
double expected[] = { 2.0, 5.0, -9.5 };
|
||||
v_multiply_and_add(c, a, b, 3);
|
||||
COMPARE_N(c, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(divide)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, 3.0 };
|
||||
double b[] = { -1.0, 3.0, -4.5 };
|
||||
double expected[] = { -1.0, 2.0/3.0, 3.0/-4.5 };
|
||||
v_divide(a, b, 3);
|
||||
COMPARE_N(a, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sum)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, -3.5 };
|
||||
double s = v_sum(a, 3);
|
||||
BOOST_CHECK_EQUAL(s, -0.5);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(multiply_and_sum)
|
||||
{
|
||||
double a[] = { 2.0, 0.0, -1.5 };
|
||||
double b[] = { 3.0, 4.0, 5.0 };
|
||||
double s = v_multiply_and_sum(a, b, 3);
|
||||
BOOST_CHECK_EQUAL(s, -1.5);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(log)
|
||||
{
|
||||
double a[] = { 1.0, 1.0 / M_E, M_E };
|
||||
double expected[] = { 0.0, -1.0, 1.0 };
|
||||
v_log(a, 3);
|
||||
COMPARE_N(a, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(exp)
|
||||
{
|
||||
double a[] = { 0.0, -1.0, 2.0 };
|
||||
double expected[] = { 1.0, 1.0 / M_E, M_E * M_E };
|
||||
v_exp(a, 3);
|
||||
COMPARE_N(a, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sqrt)
|
||||
{
|
||||
double a[] = { 0.0, 1.0, 4.0 };
|
||||
double expected[] = { 0.0, 1.0, 2.0 };
|
||||
v_sqrt(a, 3);
|
||||
COMPARE_N(a, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(square)
|
||||
{
|
||||
double a[] = { 0.0, 1.5, -2.0 };
|
||||
double expected[] = { 0.0, 2.25, 4.0 };
|
||||
v_square(a, 3);
|
||||
COMPARE_N(a, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(abs)
|
||||
{
|
||||
double a[] = { -1.9, 0.0, 0.01, -0.0 };
|
||||
double expected[] = { 1.9, 0.0, 0.01, 0.0 };
|
||||
v_abs(a, 4);
|
||||
COMPARE_N(a, expected, 4);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(mean)
|
||||
{
|
||||
double a[] = { -1.0, 1.6, 3.0 };
|
||||
double s = v_mean(a, 3);
|
||||
BOOST_CHECK_EQUAL(s, 1.2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(interleave_1)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, 3.0 };
|
||||
double *ch[] = { a };
|
||||
double o[3];
|
||||
double expected[] = { 1.0, 2.0, 3.0 };
|
||||
v_interleave(o, ch, 1, 3);
|
||||
COMPARE_N(o, expected, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(interleave_2)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, 3.0 };
|
||||
double b[] = { 4.0, 5.0, 6.0 };
|
||||
double *ch[] = { a, b };
|
||||
double o[6];
|
||||
double expected[] = { 1.0, 4.0, 2.0, 5.0, 3.0, 6.0 };
|
||||
v_interleave(o, ch, 2, 3);
|
||||
COMPARE_N(o, expected, 6);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(interleave_3)
|
||||
{
|
||||
double a[] = { 1.0, 2.0 };
|
||||
double b[] = { 3.0, 4.0 };
|
||||
double c[] = { 5.0, 6.0 };
|
||||
double *ch[] = { a, b, c };
|
||||
double o[6];
|
||||
double expected[] = { 1.0, 3.0, 5.0, 2.0, 4.0, 6.0 };
|
||||
v_interleave(o, ch, 3, 2);
|
||||
COMPARE_N(o, expected, 6);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(deinterleave_1)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, 3.0 };
|
||||
double o[3];
|
||||
double *oo[] = { o };
|
||||
double *expected[] = { a };
|
||||
v_deinterleave(oo, a, 1, 3);
|
||||
COMPARE_N(oo[0], expected[0], 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(deinterleave_2)
|
||||
{
|
||||
double a[] = { 1.0, 4.0, 2.0, 5.0, 3.0, 6.0 };
|
||||
double o1[3], o2[3];
|
||||
double *oo[] = { o1, o2 };
|
||||
double e1[] = { 1.0, 2.0, 3.0 }, e2[] = { 4.0, 5.0, 6.0 };
|
||||
double *expected[] = { e1, e2 };
|
||||
v_deinterleave(oo, a, 2, 3);
|
||||
COMPARE_N(oo[0], expected[0], 3);
|
||||
COMPARE_N(oo[1], expected[1], 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(deinterleave_3)
|
||||
{
|
||||
double a[] = { 1.0, 3.0, 5.0, 2.0, 4.0, 6.0 };
|
||||
double o1[2], o2[2], o3[2];
|
||||
double *oo[] = { o1, o2, o3 };
|
||||
double e1[] = { 1.0, 2.0 }, e2[] = { 3.0, 4.0 }, e3[] = { 5.0, 6.0 };
|
||||
double *expected[] = { e1, e2, e3 };
|
||||
v_deinterleave(oo, a, 3, 2);
|
||||
COMPARE_N(oo[0], expected[0], 2);
|
||||
COMPARE_N(oo[1], expected[1], 2);
|
||||
COMPARE_N(oo[2], expected[2], 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(fftshift)
|
||||
{
|
||||
double a[] = { 0.1, 2.0, -0.3, 4.0 };
|
||||
double e[] = { -0.3, 4.0, 0.1, 2.0 };
|
||||
v_fftshift(a, 4);
|
||||
COMPARE_N(a, e, 4);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
134
src/test/TestVectorOpsComplex.cpp
Normal file
134
src/test/TestVectorOpsComplex.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../common/VectorOpsComplex.h"
|
||||
#include "../common/VectorOps.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
using namespace RubberBand;
|
||||
|
||||
using namespace std;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(TestVectorOpsComplex)
|
||||
|
||||
#ifdef USE_APPROXIMATE_ATAN2
|
||||
static const double eps = 5.0e-3;
|
||||
static const double eps_approx = eps;
|
||||
#else
|
||||
static const double eps = 1.0e-14;
|
||||
static const double eps_approx = 1.0e-8;
|
||||
#endif
|
||||
|
||||
#define COMPARE_N(a, b, n) \
|
||||
for (int cmp_i = 0; cmp_i < n; ++cmp_i) { \
|
||||
BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], eps); \
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cartesian_to_magnitudes)
|
||||
{
|
||||
double re[] = { 1.0, 3.0 };
|
||||
double im[] = { 2.0, -4.0 };
|
||||
double o[2];
|
||||
double expected[] = { sqrt(5.0), 5.0 };
|
||||
v_cartesian_to_magnitudes(o, re, im, 2);
|
||||
COMPARE_N(o, expected, 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cartesian_interleaved_to_magnitudes)
|
||||
{
|
||||
double a[] = { 1.0, 2.0, 3.0, -4.0 };
|
||||
double o[2];
|
||||
double expected[] = { sqrt(5.0), 5.0 };
|
||||
v_cartesian_interleaved_to_magnitudes(o, a, 2);
|
||||
COMPARE_N(o, expected, 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cartesian_to_polar)
|
||||
{
|
||||
double re[] = { 0.0, 1.0, 0.0 };
|
||||
double im[] = { 0.0, 1.0, -1.0 };
|
||||
double mo[3], po[3];
|
||||
double me[] = { 0.0, sqrt(2.0), 1.0 };
|
||||
double pe[] = { 0.0, M_PI / 4.0, -M_PI * 0.5 };
|
||||
v_cartesian_to_polar(mo, po, re, im, 3);
|
||||
COMPARE_N(mo, me, 3);
|
||||
COMPARE_N(po, pe, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cartesian_to_polar_interleaved_inplace)
|
||||
{
|
||||
double a[] = { 0.0, 0.0, 1.0, 1.0, 0.0, -1.0 };
|
||||
double e[] = { 0.0, 0.0, sqrt(2.0), M_PI / 4.0, 1.0, -M_PI * 0.5 };
|
||||
v_cartesian_to_polar_interleaved_inplace(a, 3);
|
||||
COMPARE_N(a, e, 6);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cartesian_interleaved_to_polar)
|
||||
{
|
||||
double a[] = { 0.0, 0.0, 1.0, 1.0, 0.0, -1.0 };
|
||||
double mo[3], po[3];
|
||||
double me[] = { 0.0, sqrt(2.0), 1.0 };
|
||||
double pe[] = { 0.0, M_PI / 4.0, -M_PI * 0.5 };
|
||||
v_cartesian_interleaved_to_polar(mo, po, a, 3);
|
||||
COMPARE_N(mo, me, 3);
|
||||
COMPARE_N(po, pe, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(polar_to_cartesian)
|
||||
{
|
||||
double m[] = { 0.0, sqrt(2.0), 1.0 };
|
||||
double p[] = { 0.0, M_PI / 4.0, -M_PI * 0.5 };
|
||||
double ro[3], io[3];
|
||||
double re[] = { 0.0, 1.0, 0.0 };
|
||||
double ie[] = { 0.0, 1.0, -1.0 };
|
||||
v_polar_to_cartesian(ro, io, m, p, 3);
|
||||
COMPARE_N(ro, re, 3);
|
||||
COMPARE_N(io, ie, 3);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(polar_to_cartesian_interleaved_inplace)
|
||||
{
|
||||
double a[] = { 0.0, 0.0, sqrt(2.0), M_PI / 4.0, 1.0, -M_PI * 0.5 };
|
||||
double e[] = { 0.0, 0.0, 1.0, 1.0, 0.0, -1.0 };
|
||||
v_polar_interleaved_to_cartesian_inplace(a, 3);
|
||||
COMPARE_N(a, e, 6);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(polar_to_cartesian_interleaved)
|
||||
{
|
||||
double m[] = { 0.0, sqrt(2.0), 1.0 };
|
||||
double p[] = { 0.0, M_PI / 4.0, -M_PI * 0.5 };
|
||||
double o[6];
|
||||
double e[] = { 0.0, 0.0, 1.0, 1.0, 0.0, -1.0 };
|
||||
v_polar_to_cartesian_interleaved(o, m, p, 3);
|
||||
COMPARE_N(o, e, 6);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
26
src/test/test.cpp
Normal file
26
src/test/test.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#define BOOST_TEST_MODULE RubberBand
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#include <boost/test/unit_test.hpp>
|
||||
Reference in New Issue
Block a user