annotate dsp/rateconversion/TestResampler.cpp @ 372:d464286c007b

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