c@342
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@337
|
2
|
c@337
|
3 #include "dsp/phasevocoder/PhaseVocoder.h"
|
c@337
|
4
|
c@337
|
5 #include "base/Window.h"
|
c@337
|
6
|
c@342
|
7 #include <iostream>
|
c@342
|
8
|
c@342
|
9 using std::cerr;
|
c@342
|
10 using std::endl;
|
c@342
|
11
|
c@337
|
12 #define BOOST_TEST_DYN_LINK
|
c@337
|
13 #define BOOST_TEST_MAIN
|
c@337
|
14
|
c@337
|
15 #include <boost/test/unit_test.hpp>
|
c@337
|
16
|
c@337
|
17 BOOST_AUTO_TEST_SUITE(TestFFT)
|
c@337
|
18
|
c@337
|
19 #define COMPARE_CONST(a, n) \
|
c@337
|
20 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
c@342
|
21 BOOST_CHECK_SMALL(a[cmp_i] - n, 1e-7); \
|
c@337
|
22 }
|
c@337
|
23
|
c@337
|
24 #define COMPARE_ARRAY(a, b) \
|
c@337
|
25 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
c@342
|
26 BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-7); \
|
c@337
|
27 }
|
c@337
|
28
|
c@337
|
29 #define COMPARE_ARRAY_EXACT(a, b) \
|
c@337
|
30 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
|
c@337
|
31 BOOST_CHECK_EQUAL(a[cmp_i], b[cmp_i]); \
|
c@337
|
32 }
|
c@337
|
33
|
c@337
|
34 BOOST_AUTO_TEST_CASE(fullcycle)
|
c@337
|
35 {
|
c@340
|
36 // Cosine with one cycle exactly equal to pvoc hopsize. This is
|
c@340
|
37 // pretty much the most trivial case -- in fact it's
|
c@340
|
38 // indistinguishable from totally silent input (in the phase
|
c@340
|
39 // values) because the measured phases are zero throughout.
|
c@340
|
40
|
c@340
|
41 // We aren't windowing the input frame because (for once) it
|
c@340
|
42 // actually *is* just a short part of a continuous infinite
|
c@340
|
43 // sinusoid.
|
c@337
|
44
|
c@337
|
45 double frame[] = { 1, 0, -1, 0, 1, 0, -1, 0 };
|
c@337
|
46
|
c@340
|
47 PhaseVocoder pvoc(8, 4);
|
c@337
|
48
|
c@337
|
49 // Make these arrays one element too long at each end, so as to
|
c@337
|
50 // test for overruns. For frame size 8, we expect 8/2+1 = 5
|
c@337
|
51 // mag/phase pairs.
|
c@337
|
52 double mag[] = { 999, 999, 999, 999, 999, 999, 999 };
|
c@337
|
53 double phase[] = { 999, 999, 999, 999, 999, 999, 999 };
|
c@340
|
54 double unw[] = { 999, 999, 999, 999, 999, 999, 999 };
|
c@337
|
55
|
c@340
|
56 pvoc.process(frame, mag + 1, phase + 1, unw + 1);
|
c@337
|
57
|
c@337
|
58 double magExpected0[] = { 999, 0, 0, 4, 0, 0, 999 };
|
c@337
|
59 COMPARE_ARRAY_EXACT(mag, magExpected0);
|
c@337
|
60
|
c@337
|
61 double phaseExpected0[] = { 999, 0, 0, 0, 0, 0, 999 };
|
c@337
|
62 COMPARE_ARRAY_EXACT(phase, phaseExpected0);
|
c@337
|
63
|
c@340
|
64 double unwExpected0[] = { 999, 0, 0, 0, 0, 0, 999 };
|
c@340
|
65 COMPARE_ARRAY(unw, unwExpected0);
|
c@340
|
66
|
c@340
|
67 pvoc.process(frame, mag + 1, phase + 1, unw + 1);
|
c@337
|
68
|
c@337
|
69 double magExpected1[] = { 999, 0, 0, 4, 0, 0, 999 };
|
c@337
|
70 COMPARE_ARRAY_EXACT(mag, magExpected1);
|
c@337
|
71
|
c@340
|
72 double phaseExpected1[] = { 999, 0, 0, 0, 0, 0, 999 };
|
c@337
|
73 COMPARE_ARRAY(phase, phaseExpected1);
|
c@338
|
74
|
c@342
|
75 // Derivation of unwrapped values:
|
c@340
|
76 //
|
c@340
|
77 // * Bin 0 (DC) always has phase 0 and expected phase 0
|
c@340
|
78 //
|
c@340
|
79 // * Bin 1 has expected phase pi (the hop size is half a cycle at
|
c@340
|
80 // its frequency), but measured phase 0 (because there is no
|
c@340
|
81 // signal in that bin). So it has phase error -pi, which is
|
c@340
|
82 // mapped into (-pi,pi] range as pi, giving an unwrapped phase
|
c@340
|
83 // of 2*pi.
|
c@340
|
84 //
|
c@340
|
85 // * Bin 2 has expected unwrapped phase 2*pi, measured phase 0,
|
c@340
|
86 // hence error 0 and unwrapped phase 2*pi.
|
c@340
|
87 //
|
c@340
|
88 // * Bin 3 is like bin 1: it has expected phase 3*pi, measured
|
c@340
|
89 // phase 0, so phase error -pi and unwrapped phase 4*pi.
|
c@340
|
90 //
|
c@340
|
91 // * Bin 4 (Nyquist) is like bin 2: expected phase 4*pi, measured
|
c@340
|
92 // phase 0, hence error 0 and unwrapped phase 4*pi.
|
c@340
|
93
|
c@340
|
94 double unwExpected1[] = { 999, 0, 2*M_PI, 2*M_PI, 4*M_PI, 4*M_PI, 999 };
|
c@340
|
95 COMPARE_ARRAY(unw, unwExpected1);
|
c@340
|
96
|
c@340
|
97 pvoc.process(frame, mag + 1, phase + 1, unw + 1);
|
c@338
|
98
|
c@338
|
99 double magExpected2[] = { 999, 0, 0, 4, 0, 0, 999 };
|
c@338
|
100 COMPARE_ARRAY_EXACT(mag, magExpected2);
|
c@338
|
101
|
c@340
|
102 double phaseExpected2[] = { 999, 0, 0, 0, 0, 0, 999 };
|
c@338
|
103 COMPARE_ARRAY(phase, phaseExpected2);
|
c@340
|
104
|
c@340
|
105 double unwExpected2[] = { 999, 0, 4*M_PI, 4*M_PI, 8*M_PI, 8*M_PI, 999 };
|
c@340
|
106 COMPARE_ARRAY(unw, unwExpected2);
|
c@337
|
107 }
|
c@337
|
108
|
c@342
|
109 BOOST_AUTO_TEST_CASE(overlapping)
|
c@342
|
110 {
|
c@342
|
111 // Sine (i.e. cosine starting at phase -pi/2) starting with the
|
c@342
|
112 // first sample, introducing a cosine of half the frequency
|
c@342
|
113 // starting at the fourth sample, i.e. the second hop. The cosine
|
c@342
|
114 // is introduced "by magic", i.e. it doesn't appear in the second
|
c@342
|
115 // half of the first frame (it would have quite strange effects on
|
c@342
|
116 // the first frame if it did).
|
c@340
|
117
|
c@342
|
118 double data[32] = { // 3 x 8-sample frames which we pretend are overlapping
|
c@342
|
119 0, 1, 0, -1, 0, 1, 0, -1,
|
c@342
|
120 1, 1.70710678, 0, -1.70710678, -1, 0.29289322, 0, -0.29289322,
|
c@342
|
121 -1, 0.29289322, 0, -0.29289322, 1, 1.70710678, 0, -1.70710678,
|
c@342
|
122 };
|
c@342
|
123
|
c@342
|
124 PhaseVocoder pvoc(8, 4);
|
c@342
|
125
|
c@342
|
126 // Make these arrays one element too long at each end, so as to
|
c@342
|
127 // test for overruns. For frame size 8, we expect 8/2+1 = 5
|
c@342
|
128 // mag/phase pairs.
|
c@342
|
129 double mag[] = { 999, 999, 999, 999, 999, 999, 999 };
|
c@342
|
130 double phase[] = { 999, 999, 999, 999, 999, 999, 999 };
|
c@342
|
131 double unw[] = { 999, 999, 999, 999, 999, 999, 999 };
|
c@342
|
132
|
c@342
|
133 cerr << "process 0" << endl;
|
c@342
|
134
|
c@342
|
135 pvoc.process(data, mag + 1, phase + 1, unw + 1);
|
c@342
|
136
|
c@342
|
137 double magExpected0[] = { 999, 0, 0, 4, 0, 0, 999 };
|
c@342
|
138 COMPARE_ARRAY(mag, magExpected0);
|
c@342
|
139
|
c@342
|
140 double phaseExpected0[] = { 999, 0, 0, -M_PI/2 , 0, 0, 999 };
|
c@342
|
141 COMPARE_ARRAY(phase, phaseExpected0);
|
c@342
|
142
|
c@342
|
143 double unwExpected0[] = { 999, 0, 0, -M_PI/2, 0, 0, 999 };
|
c@342
|
144 COMPARE_ARRAY(unw, unwExpected0);
|
c@342
|
145
|
c@342
|
146 cerr << "process 1" << endl;
|
c@342
|
147
|
c@342
|
148 pvoc.process(data + 8, mag + 1, phase + 1, unw + 1);
|
c@342
|
149
|
c@342
|
150 double magExpected1[] = { 999, 0, 4, 4, 0, 0, 999 };
|
c@342
|
151 COMPARE_ARRAY(mag, magExpected1);
|
c@342
|
152
|
c@342
|
153 //!!! I don't know why [2] here is -M_PI and not M_PI; and I definitely don't know why [4] here is M_PI. Check these with care
|
c@342
|
154 double phaseExpected1[] = { 999, 0, -M_PI, -M_PI/2, M_PI, 0, 999 };
|
c@342
|
155 COMPARE_ARRAY(phase, phaseExpected1);
|
c@342
|
156
|
c@342
|
157 double unwExpected1[] = { 999, 0, M_PI, 3*M_PI/2, 3*M_PI, 4*M_PI, 999 };
|
c@342
|
158 COMPARE_ARRAY(unw, unwExpected1);
|
c@342
|
159
|
c@342
|
160 cerr << "process 2" << endl;
|
c@342
|
161
|
c@342
|
162 pvoc.process(data + 16, mag + 1, phase + 1, unw + 1);
|
c@342
|
163
|
c@342
|
164 double magExpected2[] = { 999, 0, 4, 4, 0, 0, 999 };
|
c@342
|
165 COMPARE_ARRAY(mag, magExpected2);
|
c@342
|
166
|
c@342
|
167 double phaseExpected2[] = { 999, 0, 0, -M_PI/2, 0, 0, 999 };
|
c@342
|
168 COMPARE_ARRAY(phase, phaseExpected2);
|
c@342
|
169
|
c@342
|
170 double unwExpected2[] = { 999, 0, 2*M_PI, 7*M_PI/2, 6*M_PI, 8*M_PI, 999 };
|
c@342
|
171 COMPARE_ARRAY(unw, unwExpected2);
|
c@342
|
172 }
|
c@340
|
173
|
c@337
|
174 BOOST_AUTO_TEST_SUITE_END()
|
c@337
|
175
|