1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2 
3 #include "bqresample/Resampler.h"
4 
5 #define BOOST_TEST_DYN_LINK
6 #define BOOST_TEST_MAIN
7 
8 #include <boost/test/unit_test.hpp>
9 
10 #include <stdexcept>
11 #include <vector>
12 #include <cmath>
13 #include <iostream>
14 
15 using namespace std;
16 using namespace breakfastquay;
17 
BOOST_AUTO_TEST_SUITE(TestResampler)18 BOOST_AUTO_TEST_SUITE(TestResampler)
19 
20 #define LEN(a) (int(sizeof(a)/sizeof(a[0])))
21 
22 static vector<float>
23 sine(double samplerate, double frequency, int nsamples)
24 {
25     vector<float> v(nsamples, 0.f);
26     for (int i = 0; i < nsamples; ++i) {
27         v[i] = sin ((i * 2.0 * M_PI * frequency) / samplerate);
28     }
29     return v;
30 }
31 
32 #define COMPARE_N(a, b, n)                    \
33     for (int cmp_i = 0; cmp_i < n; ++cmp_i) { \
34         BOOST_CHECK_SMALL((a)[cmp_i] - (b)[cmp_i], 1e-4f);      \
35     }
36 
37 static const float guard_value = -999.f;
38 
BOOST_AUTO_TEST_CASE(interpolated_sine_1ch_interleaved)39 BOOST_AUTO_TEST_CASE(interpolated_sine_1ch_interleaved)
40 {
41     // Interpolating a sinusoid should give us a sinusoid, once we've
42     // dropped the first few samples
43     vector<float> in = sine(8, 2, 1000); // 2Hz wave at 8Hz: [ 0, 1, 0, -1 ] etc
44     vector<float> expected = sine(16, 2, 2000);
45     vector<float> out(in.size() * 2 + 1, guard_value);
46     Resampler r(Resampler::Parameters(), 1);
47     int returned = r.resampleInterleaved
48         (out.data(), out.size(), in.data(), in.size(), 2, true);
49 
50     // because final was true, we expect to have exactly the right
51     // number of samples back
52     BOOST_CHECK_EQUAL(returned, int(in.size() * 2));
53     BOOST_CHECK_NE(out[returned-1], guard_value);
54     BOOST_CHECK_EQUAL(out[returned], guard_value);
55 
56     // and they should match the expected data, at least in the middle
57     const float *outf = out.data() + 200, *expectedf = expected.data() + 200;
58     COMPARE_N(outf, expectedf, 600);
59 }
60 
BOOST_AUTO_TEST_CASE(interpolated_sine_1ch_noninterleaved)61 BOOST_AUTO_TEST_CASE(interpolated_sine_1ch_noninterleaved)
62 {
63     // Interpolating a sinusoid should give us a sinusoid, once we've
64     // dropped the first few samples
65     vector<float> in = sine(8, 2, 1000); // 2Hz wave at 8Hz: [ 0, 1, 0, -1 ] etc
66     vector<float> expected = sine(16, 2, 2000);
67     vector<float> out(in.size() * 2 + 1, guard_value);
68     const float *in_data = in.data();
69     float *out_data = out.data();
70     Resampler r(Resampler::Parameters(), 1);
71     int returned = r.resample
72         (&out_data, out.size(), &in_data, in.size(), 2, true);
73 
74     // because final was true, we expect to have exactly the right
75     // number of samples back
76     BOOST_CHECK_EQUAL(returned, int(in.size() * 2));
77     BOOST_CHECK_NE(out[returned-1], guard_value);
78     BOOST_CHECK_EQUAL(out[returned], guard_value);
79 
80     // and they should match the expected data, at least in the middle
81     const float *outf = out.data() + 200, *expectedf = expected.data() + 200;
82     COMPARE_N(outf, expectedf, 600);
83 }
84 
BOOST_AUTO_TEST_CASE(overrun_interleaved)85 BOOST_AUTO_TEST_CASE(overrun_interleaved)
86 {
87     // Check that the outcount argument is correctly used: any samples
88     // already in the out buffer beyond outcount*channels must be left
89     // untouched. We test this with short buffers (likely to be
90     // shorter than the resampler filter length) and longer ones, with
91     // resampler ratios that reduce, leave unchanged, and raise the
92     // sample rate, and with all quality settings.
93 
94     // Options are ordered from most normal/likely to least.
95 
96     int channels = 2;
97 
98     int lengths[] = { 6000, 6 };
99     int constructionBufferSizes[] = { 0, 1000 };
100     double ratios[] = { 1.0, 10.0, 0.1 };
101     Resampler::Quality qualities[] = {
102         Resampler::FastestTolerable, Resampler::Best, Resampler::Fastest
103     };
104 
105     bool failed = false;
106 
107     for (int li = 0; li < LEN(lengths); ++li) {
108         for (int cbi = 0; cbi < LEN(constructionBufferSizes); ++cbi) {
109             for (int ri = 0; ri < LEN(ratios); ++ri) {
110                 for (int qi = 0; qi < LEN(qualities); ++qi) {
111 
112                     int length = lengths[li];
113                     double ratio = ratios[ri];
114                     Resampler::Parameters parameters;
115                     parameters.quality = qualities[qi];
116                     parameters.maxBufferSize = constructionBufferSizes[cbi];
117                     Resampler r(parameters, channels);
118 
119                     float *inbuf = new float[length * channels];
120                     for (int i = 0; i < length; ++i) {
121                         for (int c = 0; c < channels; ++c) {
122                             inbuf[i*channels+c] =
123                                 sinf((i * 2.0 * M_PI * 440.0) / 44100.0);
124                         }
125                     }
126 
127                     int outcount = int(round(length * ratio));
128                     outcount -= 10;
129                     if (outcount < 1) outcount = 1;
130                     int outbuflen = outcount + 10;
131                     float *outbuf = new float[outbuflen * channels];
132                     for (int i = 0; i < outbuflen; ++i) {
133                         for (int c = 0; c < channels; ++c) {
134                             outbuf[i*channels+c] = guard_value;
135                         }
136                     }
137 /*
138                     cerr << "\nTesting with length = " << length << ", ratio = "
139                          << ratio << ", outcount = " << outcount << ", final = false"
140                          << endl;
141 */
142                     int returned = r.resampleInterleaved
143                         (outbuf, outcount, inbuf, length, ratio, false);
144 
145                     BOOST_CHECK_LE(returned, outcount);
146 
147                     for (int i = returned; i < outbuflen; ++i) {
148                         for (int c = 0; c < channels; ++c) {
149                             BOOST_CHECK_EQUAL(outbuf[i*channels+c], guard_value);
150                             if (outbuf[i*channels+c] != guard_value) {
151                                 failed = true;
152                             }
153                         }
154                     }
155 
156                     if (failed) {
157                         cerr << "Test failed, abandoning remaining loop cycles"
158                              << endl;
159                         break;
160                     }
161 /*
162                     cerr << "\nContinuing with length = " << length << ", ratio = "
163                          << ratio << ", outcount = " << outcount << ", final = true"
164                          << endl;
165 */
166                     returned = r.resampleInterleaved
167                         (outbuf, outcount, inbuf, length, ratio, true);
168 
169                     BOOST_CHECK_LE(returned, outcount);
170 
171                     for (int i = returned; i < outbuflen; ++i) {
172                         for (int c = 0; c < channels; ++c) {
173                             BOOST_CHECK_EQUAL(outbuf[i*channels+c], guard_value);
174                             if (outbuf[i*channels+c] != guard_value) {
175                                 failed = true;
176                             }
177                         }
178                     }
179 
180                     delete[] outbuf;
181                     delete[] inbuf;
182 
183                     if (failed) {
184                         cerr << "Test failed, abandoning remaining loop cycles"
185                              << endl;
186                         break;
187                     }
188                 }
189 
190                 if (failed) break;
191             }
192             if (failed) break;
193         }
194         if (failed) break;
195     }
196 }
197 
198 BOOST_AUTO_TEST_SUITE_END()
199 
200