Start to bring in unit tests

This commit is contained in:
Chris Cannam
2022-06-08 09:57:12 +01:00
parent 36f993b9a6
commit 95a1d6df25
10 changed files with 1556 additions and 6 deletions

View File

@@ -26,3 +26,7 @@ build
build_*
build-*
UpgradeLog*
out-*/
playlist-out/*
formant-out-*/
out*.wav

View File

@@ -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',

View File

@@ -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 {

View File

@@ -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
View 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
View 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
View 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
View 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()

View 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
View 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>