annotate tests/TestResampler.cpp @ 381:88971211795c

Lower filter cutoff to below target Nyquist when downsampling
author Chris Cannam <c.cannam@qmul.ac.uk>
date Fri, 01 Nov 2013 12:07:08 +0000
parents ad21307eaf99
children fd207df9432e
rev   line source
c@375 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@375 2
c@375 3 #include "dsp/rateconversion/Resampler.h"
c@375 4
c@375 5 #include "base/Window.h"
c@375 6 #include "dsp/transforms/FFT.h"
c@375 7
c@375 8 #include <iostream>
c@375 9
c@375 10 #include <cmath>
c@375 11
c@375 12 #define BOOST_TEST_DYN_LINK
c@375 13 #define BOOST_TEST_MAIN
c@375 14
c@375 15 #include <boost/test/unit_test.hpp>
c@375 16
c@375 17 BOOST_AUTO_TEST_SUITE(TestResampler)
c@375 18
c@375 19 using std::cout;
c@375 20 using std::endl;
c@375 21 using std::vector;
c@375 22
c@375 23 void
c@375 24 testResamplerOneShot(int sourceRate,
c@375 25 int targetRate,
c@375 26 int n,
c@375 27 double *in,
c@375 28 int m,
c@375 29 double *expected,
c@375 30 int skip)
c@375 31 {
c@375 32 vector<double> resampled = Resampler::resample(sourceRate, targetRate,
c@375 33 in, n);
c@375 34 if (skip == 0) {
c@375 35 BOOST_CHECK_EQUAL(resampled.size(), m);
c@375 36 }
c@375 37 for (int i = 0; i < m; ++i) {
c@375 38 BOOST_CHECK_SMALL(resampled[i + skip] - expected[i], 1e-6);
c@375 39 }
c@375 40 }
c@375 41
c@375 42 void
c@375 43 testResampler(int sourceRate,
c@375 44 int targetRate,
c@375 45 int n,
c@375 46 double *in,
c@375 47 int m,
c@375 48 double *expected)
c@375 49 {
c@375 50 // Here we provide the input in chunks (of varying size)
c@375 51
c@375 52 Resampler r(sourceRate, targetRate);
c@375 53 int latency = r.getLatency();
c@375 54
c@375 55 int m1 = m + latency;
c@375 56 int n1 = int((m1 * sourceRate) / targetRate);
c@375 57
c@375 58 double *inPadded = new double[n1];
c@375 59 double *outPadded = new double[m1];
c@375 60
c@375 61 for (int i = 0; i < n1; ++i) {
c@375 62 if (i < n) inPadded[i] = in[i];
c@375 63 else inPadded[i] = 0.0;
c@375 64 }
c@375 65
c@375 66 for (int i = 0; i < m1; ++i) {
c@375 67 outPadded[i] = -999.0;
c@375 68 }
c@375 69
c@375 70 int chunkSize = 1;
c@375 71 int got = 0;
c@375 72 int i = 0;
c@375 73
c@375 74 while (true) {
c@375 75 got += r.process(inPadded + i, outPadded + got, chunkSize);
c@375 76 i = i + chunkSize;
c@375 77 chunkSize = chunkSize + 1;
c@375 78 if (i >= n1) {
c@375 79 break;
c@375 80 } else if (i + chunkSize >= n1) {
c@375 81 chunkSize = n1 - i;
c@375 82 } else if (chunkSize > 15) {
c@375 83 chunkSize = 1;
c@375 84 }
c@375 85 }
c@375 86
c@375 87 BOOST_CHECK_EQUAL(got, m1);
c@375 88
c@375 89 for (int i = latency; i < m1; ++i) {
c@375 90 BOOST_CHECK_SMALL(outPadded[i] - expected[i-latency], 1e-8);
c@375 91 }
c@375 92
c@375 93 delete[] outPadded;
c@375 94 delete[] inPadded;
c@375 95 }
c@375 96
c@375 97 BOOST_AUTO_TEST_CASE(sameRateOneShot)
c@375 98 {
c@375 99 double d[] = { 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 };
c@375 100 testResamplerOneShot(4, 4, 10, d, 10, d, 0);
c@375 101 }
c@375 102
c@375 103 BOOST_AUTO_TEST_CASE(sameRate)
c@375 104 {
c@375 105 double d[] = { 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 };
c@375 106 testResampler(4, 4, 10, d, 10, d);
c@375 107 }
c@375 108
c@375 109 BOOST_AUTO_TEST_CASE(interpolatedMisc)
c@375 110 {
c@375 111 // Interpolating any signal by N should give a signal in which
c@375 112 // every Nth sample is the original signal
c@375 113 double in[] = { 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 };
c@375 114 int n = sizeof(in)/sizeof(in[0]);
c@375 115 for (int factor = 2; factor < 10; ++factor) {
c@375 116 vector<double> out = Resampler::resample(6, 6 * factor, in, n);
c@375 117 for (int i = 0; i < n; ++i) {
c@375 118 BOOST_CHECK_SMALL(out[i * factor] - in[i], 1e-5);
c@375 119 }
c@375 120 }
c@375 121 }
c@375 122
c@375 123 BOOST_AUTO_TEST_CASE(interpolatedSine)
c@375 124 {
c@375 125 // Interpolating a sinusoid should give us a sinusoid, once we've
c@375 126 // dropped the first few samples
c@375 127 double in[1000];
c@375 128 double out[2000];
c@375 129 for (int i = 0; i < 1000; ++i) {
c@375 130 in[i] = sin(i * M_PI / 2.0);
c@375 131 }
c@375 132 for (int i = 0; i < 2000; ++i) {
c@375 133 out[i] = sin(i * M_PI / 4.0);
c@375 134 }
c@375 135 testResamplerOneShot(8, 16, 1000, in, 200, out, 512);
c@375 136 }
c@375 137
c@375 138 BOOST_AUTO_TEST_CASE(decimatedSine)
c@375 139 {
c@375 140 // Decimating a sinusoid should give us a sinusoid, once we've
c@375 141 // dropped the first few samples
c@375 142 double in[2000];
c@375 143 double out[1000];
c@375 144 for (int i = 0; i < 2000; ++i) {
c@375 145 in[i] = sin(i * M_PI / 8.0);
c@375 146 }
c@375 147 for (int i = 0; i < 1000; ++i) {
c@375 148 out[i] = sin(i * M_PI / 4.0);
c@375 149 }
c@375 150 testResamplerOneShot(16, 8, 2000, in, 200, out, 256);
c@375 151 }
c@375 152
c@375 153 vector<double>
c@375 154 squareWave(int rate, double freq, int n)
c@375 155 {
c@375 156 //!!! todo: hoist, test
c@375 157 vector<double> v(n, 0.0);
c@375 158 for (int h = 0; h < (rate/4)/freq; ++h) {
c@375 159 double m = h * 2 + 1;
c@375 160 double scale = 1.0 / m;
c@375 161 for (int i = 0; i < n; ++i) {
c@375 162 double s = scale * sin((i * 2.0 * M_PI * m * freq) / rate);
c@375 163 v[i] += s;
c@375 164 }
c@375 165 }
c@375 166 return v;
c@375 167 }
c@375 168
c@375 169 void
c@375 170 testSpectrum(int inrate, int outrate)
c@375 171 {
c@375 172 // One second of a square wave
c@375 173 int freq = 500;
c@375 174
c@375 175 vector<double> square =
c@375 176 squareWave(inrate, freq, inrate);
c@375 177
c@375 178 vector<double> maybeSquare =
c@375 179 Resampler::resample(inrate, outrate, square.data(), square.size());
c@375 180
c@375 181 BOOST_CHECK_EQUAL(maybeSquare.size(), outrate);
c@375 182
c@375 183 Window<double>(HanningWindow, inrate).cut(square.data());
c@375 184 Window<double>(HanningWindow, outrate).cut(maybeSquare.data());
c@375 185
c@375 186 // forward magnitude with size inrate, outrate
c@375 187
c@375 188 vector<double> inSpectrum(inrate, 0.0);
c@375 189 FFTReal(inrate).forwardMagnitude(square.data(), inSpectrum.data());
c@375 190 for (int i = 0; i < (int)inSpectrum.size(); ++i) {
c@375 191 inSpectrum[i] /= inrate;
c@375 192 }
c@375 193
c@375 194 vector<double> outSpectrum(outrate, 0.0);
c@375 195 FFTReal(outrate).forwardMagnitude(maybeSquare.data(), outSpectrum.data());
c@375 196 for (int i = 0; i < (int)outSpectrum.size(); ++i) {
c@375 197 outSpectrum[i] /= outrate;
c@375 198 }
c@375 199
c@381 200 // Don't compare bins any higher than 96% of Nyquist freq of lower sr
c@375 201 int lengthOfInterest = (inrate < outrate ? inrate : outrate) / 2;
c@381 202 lengthOfInterest = lengthOfInterest - (lengthOfInterest / 25);
c@375 203
c@375 204 for (int i = 0; i < lengthOfInterest; ++i) {
c@375 205 BOOST_CHECK_SMALL(inSpectrum[i] - outSpectrum[i], 1e-7);
c@375 206 }
c@375 207 }
c@375 208
c@375 209 BOOST_AUTO_TEST_CASE(spectrum)
c@375 210 {
c@375 211 int rates[] = { 8000, 22050, 44100, 48000 };
c@375 212 for (int i = 0; i < (int)(sizeof(rates)/sizeof(rates[0])); ++i) {
c@375 213 for (int j = 0; j < (int)(sizeof(rates)/sizeof(rates[0])); ++j) {
c@375 214 testSpectrum(rates[i], rates[j]);
c@375 215 }
c@375 216 }
c@375 217 }
c@375 218
c@375 219 BOOST_AUTO_TEST_SUITE_END()
c@375 220