annotate dsp/rateconversion/TestResampler.cpp @ 368:ee8ace7fdc88

Some fixes, and start on spectrum test
author Chris Cannam <c.cannam@qmul.ac.uk>
date Tue, 15 Oct 2013 18:27:19 +0100
parents 0721657fdd1d
children b21e97d570be
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@362 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@366 152
c@368 153 vector<double>
c@368 154 squareWave(int rate, int 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@368 160 double scale = 1 / m;
c@368 161 for (int i = 0; i < n; ++i) {
c@368 162 v[i] += scale * sin(i * 2 * M_PI * freq / rate);
c@368 163 }
c@368 164 }
c@368 165 return v;
c@368 166 }
c@368 167
c@368 168 void
c@368 169 testSpectrum(int inrate, int outrate)
c@368 170 {
c@368 171 // One second of a square wave
c@368 172 int freq = 500;
c@368 173
c@368 174 std::cerr << "inrate = " << inrate << ", outrate = " << outrate << ", freq * outrate / inrate = " << (double(freq) * double(outrate)) / double(inrate) << std::endl;
c@368 175
c@368 176 std::cerr << "making square wave... ";
c@368 177 vector<double> square =
c@368 178 squareWave(inrate, freq, inrate);
c@368 179 std::cerr << "done" << std::endl;
c@368 180
c@368 181 vector<double> maybeSquare =
c@368 182 Resampler::resample(inrate, outrate, square.data(), square.size());
c@368 183
c@368 184 BOOST_CHECK_EQUAL(maybeSquare.size(), outrate);
c@368 185
c@368 186 Window<double>(HanningWindow, inrate).cut(square.data());
c@368 187 Window<double>(HanningWindow, outrate).cut(maybeSquare.data());
c@368 188
c@368 189 // forward magnitude with size inrate, outrate
c@368 190
c@368 191 vector<double> inSpectrum(inrate, 0.0);
c@368 192 FFTReal(inrate).forwardMagnitude(square.data(), inSpectrum.data());
c@368 193
c@368 194 vector<double> outSpectrum(outrate, 0.0);
c@368 195 FFTReal(outrate).forwardMagnitude(maybeSquare.data(), outSpectrum.data());
c@368 196
c@368 197 // Don't compare bins any higher than 99% of Nyquist freq of lower sr
c@368 198 int lengthOfInterest = (inrate < outrate ? inrate : outrate) / 2;
c@368 199 lengthOfInterest = lengthOfInterest - (lengthOfInterest / 100);
c@368 200
c@368 201 for (int i = 0; i < lengthOfInterest; ++i) {
c@368 202 BOOST_CHECK_SMALL(inSpectrum[i] - outSpectrum[i], 1e-7);
c@368 203 }
c@368 204 }
c@368 205
c@368 206 BOOST_AUTO_TEST_CASE(spectrum)
c@368 207 {
c@368 208 int rates[] = { 8000, 22050, 44100, 48000 };
c@368 209 for (int i = 0; i < sizeof(rates)/sizeof(rates[0]); ++i) {
c@368 210 for (int j = 0; j < sizeof(rates)/sizeof(rates[0]); ++j) {
c@368 211 testSpectrum(rates[i], rates[j]);
c@368 212 }
c@368 213 }
c@368 214 }
c@368 215
c@362 216 BOOST_AUTO_TEST_SUITE_END()
c@362 217