To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at https://github.com/cannam/constant-q-cpp/ .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Revision:

root / test / TestResampler.cpp

History | View | Annotate | Download (8.03 KB)

1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2

    
3
#include "dsp/Resampler.h"
4

    
5
#include "dsp/Window.h"
6
#include "dsp/FFT.h"
7

    
8
#include <iostream>
9

    
10
#include <cmath>
11

    
12
#define BOOST_TEST_DYN_LINK
13
#define BOOST_TEST_MAIN
14

    
15
#include <boost/test/unit_test.hpp>
16

    
17
BOOST_AUTO_TEST_SUITE(TestResampler)
18

    
19
using std::cout;
20
using std::endl;
21
using std::vector;
22

    
23
void
24
testResamplerOneShot(int sourceRate,
25
                     int targetRate,
26
                     int n,
27
                     double *in,
28
                     int m,
29
                     double *expected,
30
                     int skip)
31
{
32
    vector<double> resampled = Resampler::resample(sourceRate, targetRate,
33
                                                   in, n);
34
    if (skip == 0) {
35
        BOOST_CHECK_EQUAL(resampled.size(), m);
36
    }
37
    for (int i = 0; i < m; ++i) {
38
        BOOST_CHECK_SMALL(resampled[i + skip] - expected[i], 1e-6);
39
    }
40
}
41

    
42
void
43
testResampler(int sourceRate,
44
              int targetRate,
45
              int n,
46
              double *in,
47
              int m,
48
              double *expected)
49
{
50
    // Here we provide the input in chunks (of varying size)
51

    
52
    Resampler r(sourceRate, targetRate);
53
    int latency = r.getLatency();
54

    
55
    int m1 = m + latency;
56
    int n1 = int((m1 * sourceRate) / targetRate);
57

    
58
    double *inPadded = new double[n1];
59
    double *outPadded = new double[m1];
60

    
61
    for (int i = 0; i < n1; ++i) {
62
        if (i < n) inPadded[i] = in[i];
63
        else inPadded[i] = 0.0;
64
    }
65
    
66
    for (int i = 0; i < m1; ++i) {
67
        outPadded[i] = -999.0;
68
    }
69

    
70
    int chunkSize = 1;
71
    int got = 0;
72
    int i = 0;
73

    
74
    while (true) {
75
        got += r.process(inPadded + i, outPadded + got, chunkSize);
76
        i = i + chunkSize;
77
        chunkSize = chunkSize + 1;
78
        if (i >= n1) {
79
            break;
80
        } else if (i + chunkSize >= n1) {
81
            chunkSize = n1 - i;
82
        } else if (chunkSize > 15) {
83
            chunkSize = 1;
84
        }
85
    }
86

    
87
    BOOST_CHECK_EQUAL(got, m1);
88

    
89
    for (int i = latency; i < m1; ++i) {
90
        BOOST_CHECK_SMALL(outPadded[i] - expected[i-latency], 1e-8);
91
    }
92

    
93
    delete[] outPadded;
94
    delete[] inPadded;
95
}
96

    
97
BOOST_AUTO_TEST_CASE(sameRateOneShot)
98
{
99
    double d[] = { 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 };
100
    testResamplerOneShot(4, 4, 10, d, 10, d, 0);
101
}
102

    
103
BOOST_AUTO_TEST_CASE(sameRate)
104
{
105
    double d[] = { 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 };
106
    testResampler(4, 4, 10, d, 10, d);
107
}
108

    
109
BOOST_AUTO_TEST_CASE(interpolatedMisc)
110
{
111
    // Interpolating any signal by N should give a signal in which
112
    // every Nth sample is the original signal
113
    double in[] = { 0, 0.1, -0.3, -0.4, -0.3, 0, 0.5, 0.2, 0.8, -0.1 };
114
    int n = sizeof(in)/sizeof(in[0]);
115
    for (int factor = 2; factor < 10; ++factor) {
116
        vector<double> out = Resampler::resample(6, 6 * factor, in, n);
117
        for (int i = 0; i < n; ++i) {
118
            BOOST_CHECK_SMALL(out[i * factor] - in[i], 1e-5);
119
        }
120
    }
121
}
122

    
123
BOOST_AUTO_TEST_CASE(interpolatedSine)
124
{
125
    // Interpolating a sinusoid should give us a sinusoid, once we've
126
    // dropped the first few samples
127
    double in[1000];
128
    double out[2000];
129
    for (int i = 0; i < 1000; ++i) {
130
        in[i] = sin(i * M_PI / 2.0);
131
    }
132
    for (int i = 0; i < 2000; ++i) {
133
        out[i] = sin(i * M_PI / 4.0);
134
    }
135
    testResamplerOneShot(8, 16, 1000, in, 200, out, 512);
136
}
137

    
138
BOOST_AUTO_TEST_CASE(decimatedSine)
139
{
140
    // Decimating a sinusoid should give us a sinusoid, once we've
141
    // dropped the first few samples
142
    double in[2000];
143
    double out[1000];
144
    for (int i = 0; i < 2000; ++i) {
145
        in[i] = sin(i * M_PI / 8.0);
146
    }
147
    for (int i = 0; i < 1000; ++i) {
148
        out[i] = sin(i * M_PI / 4.0);
149
    }
150
    testResamplerOneShot(16, 8, 2000, in, 200, out, 256);
151
}
152

    
153
double
154
measureSinFreq(const vector<double> &v, int rate, int countCycles)
155
{
156
    int n = v.size();
157
    int firstPeak = -1;
158
    int lastPeak = -1;
159
    int nPeaks = 0;
160
    // count +ve peaks
161
    for (int i = v.size()/4; i + 1 < n; ++i) {
162
        // allow some fuzz
163
        int x0 = int(10000 * v[i-1]);
164
        int x1 = int(10000 * v[i]);
165
        int x2 = int(10000 * v[i+1]);
166
        if (x1 > 0 && x1 > x0 && x1 >= x2) {
167
            if (firstPeak < 0) firstPeak = i;
168
            lastPeak = i;
169
            ++nPeaks;
170
            if (nPeaks == countCycles) break;
171
        }
172
    }
173
    int nCycles = nPeaks - 1;
174
    if (nCycles <= 0) return 0.0;
175
    double cycle = double(lastPeak - firstPeak) / nCycles;
176
//    cout << "lastPeak = " << lastPeak << ", firstPeak = " << firstPeak << ", dist = " << lastPeak - firstPeak << ", nCycles = " << nCycles << ", cycle = " << cycle << endl;
177
    return rate / cycle;
178
}
179

    
180
void
181
testSinFrequency(int freq,
182
                 int sourceRate,
183
                 int targetRate)
184
{
185
    // Resampling a sinusoid and then resampling back should give us a
186
    // sinusoid of the same frequency as we started with
187

    
188
    int nCycles = 500;
189

    
190
    int duration = int(nCycles * float(sourceRate) / float(freq));
191
//    cout << "freq = " << freq << ", sourceRate = " << sourceRate << ", targetRate = " << targetRate << ", duration = " << duration << endl;
192

    
193
    vector<double> in(duration, 0);
194
    for (int i = 0; i < duration; ++i) {
195
        in[i] = sin(i * M_PI * 2.0 * freq / sourceRate);
196
    }
197

    
198
    vector<double> out = Resampler::resample(sourceRate, targetRate,
199
                                             in.data(), in.size());
200

    
201
    vector<double> back = Resampler::resample(targetRate, sourceRate,
202
                                              out.data(), out.size());
203

    
204
    BOOST_CHECK_EQUAL(in.size(), back.size());
205

    
206
    double inFreq = measureSinFreq(in, sourceRate, nCycles / 2);
207
    double backFreq = measureSinFreq(back, sourceRate, nCycles / 2);
208
    
209
    BOOST_CHECK_SMALL(inFreq - backFreq, 1e-8);
210
}
211

    
212
// In each of the following we use a frequency that has an exact cycle
213
// length in samples at the lowest sample rate, so that we can easily
214
// rule out errors in measuring the cycle length after resampling. If
215
// the resampler gets its input or output rate wrong, that will show
216
// up no matter what the test signal's initial frequency is.
217

    
218
BOOST_AUTO_TEST_CASE(downUp2)
219
{
220
    testSinFrequency(441, 44100, 22050);
221
}
222

    
223
BOOST_AUTO_TEST_CASE(downUp5)
224
{
225
    testSinFrequency(300, 15000, 3000);
226
}
227

    
228
BOOST_AUTO_TEST_CASE(downUp16)
229
{
230
    testSinFrequency(300, 48000, 3000);
231
}
232

    
233
BOOST_AUTO_TEST_CASE(upDown2)
234
{
235
    testSinFrequency(441, 44100, 88200);
236
}
237

    
238
BOOST_AUTO_TEST_CASE(upDown5)
239
{
240
    testSinFrequency(300, 3000, 15000);
241
}
242

    
243
BOOST_AUTO_TEST_CASE(upDown16)
244
{
245
    testSinFrequency(300, 3000, 48000);
246
}
247

    
248
vector<double>
249
squareWave(int rate, double freq, int n)
250
{
251
    //!!! todo: hoist, test
252
    vector<double> v(n, 0.0);
253
    for (int h = 0; h < (rate/4)/freq; ++h) {
254
        double m = h * 2 + 1;
255
        double scale = 1.0 / m;
256
        for (int i = 0; i < n; ++i) {
257
            double s = scale * sin((i * 2.0 * M_PI * m * freq) / rate);
258
            v[i] += s;
259
        }
260
    }
261
    return v;
262
}
263

    
264
void
265
testSpectrum(int inrate, int outrate)
266
{
267
    // One second of a square wave
268
    int freq = 500;
269

    
270
    vector<double> square =
271
        squareWave(inrate, freq, inrate);
272

    
273
    vector<double> maybeSquare = 
274
        Resampler::resample(inrate, outrate, square.data(), square.size());
275

    
276
    BOOST_CHECK_EQUAL(maybeSquare.size(), outrate);
277

    
278
    Window<double>(HanningWindow, inrate).cut(square.data());
279
    Window<double>(HanningWindow, outrate).cut(maybeSquare.data());
280

    
281
    // forward magnitude with size inrate, outrate
282

    
283
    vector<double> inSpectrum(inrate, 0.0);
284
    FFTReal(inrate).forwardMagnitude(square.data(), inSpectrum.data());
285
    for (int i = 0; i < (int)inSpectrum.size(); ++i) {
286
        inSpectrum[i] /= inrate;
287
    }
288

    
289
    vector<double> outSpectrum(outrate, 0.0);
290
    FFTReal(outrate).forwardMagnitude(maybeSquare.data(), outSpectrum.data());
291
    for (int i = 0; i < (int)outSpectrum.size(); ++i) {
292
        outSpectrum[i] /= outrate;
293
    }
294

    
295
    // Don't compare bins any higher than 96% of Nyquist freq of lower sr
296
    int lengthOfInterest = (inrate < outrate ? inrate : outrate) / 2;
297
    lengthOfInterest = lengthOfInterest - (lengthOfInterest / 25);
298

    
299
    for (int i = 0; i < lengthOfInterest; ++i) {
300
        BOOST_CHECK_SMALL(inSpectrum[i] - outSpectrum[i], 1e-7);
301
    }
302
}
303
/*
304
BOOST_AUTO_TEST_CASE(spectrum)
305
{
306
    int rates[] = { 8000, 22050, 44100, 48000 };
307
    for (int i = 0; i < (int)(sizeof(rates)/sizeof(rates[0])); ++i) {
308
            for (int j = 0; j < (int)(sizeof(rates)/sizeof(rates[0])); ++j) {
309
            testSpectrum(rates[i], rates[j]);
310
        }
311
    }
312
}
313
*/
314
BOOST_AUTO_TEST_SUITE_END()
315