Mercurial > hg > qm-dsp
comparison tests/TestPhaseVocoder.cpp @ 347:e3dedded9c4d
Merge from pvoc branch
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Fri, 04 Oct 2013 16:43:44 +0100 |
parents | 04d134031a15 |
children | 6ec45e85ed81 |
comparison
equal
deleted
inserted
replaced
336:f665f9ce2fd1 | 347:e3dedded9c4d |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 #include "dsp/phasevocoder/PhaseVocoder.h" | |
4 | |
5 #include "base/Window.h" | |
6 | |
7 #include <iostream> | |
8 | |
9 using std::cerr; | |
10 using std::endl; | |
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(TestFFT) | |
18 | |
19 #define COMPARE_CONST(a, n) \ | |
20 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \ | |
21 BOOST_CHECK_SMALL(a[cmp_i] - n, 1e-7); \ | |
22 } | |
23 | |
24 #define COMPARE_ARRAY(a, b) \ | |
25 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \ | |
26 BOOST_CHECK_SMALL(a[cmp_i] - b[cmp_i], 1e-7); \ | |
27 } | |
28 | |
29 #define COMPARE_ARRAY_EXACT(a, b) \ | |
30 for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \ | |
31 BOOST_CHECK_EQUAL(a[cmp_i], b[cmp_i]); \ | |
32 } | |
33 | |
34 BOOST_AUTO_TEST_CASE(fullcycle) | |
35 { | |
36 // Cosine with one cycle exactly equal to pvoc hopsize. This is | |
37 // pretty much the most trivial case -- in fact it's | |
38 // indistinguishable from totally silent input (in the phase | |
39 // values) because the measured phases are zero throughout. | |
40 | |
41 // We aren't windowing the input frame because (for once) it | |
42 // actually *is* just a short part of a continuous infinite | |
43 // sinusoid. | |
44 | |
45 double frame[] = { 1, 0, -1, 0, 1, 0, -1, 0 }; | |
46 | |
47 PhaseVocoder pvoc(8, 4); | |
48 | |
49 // Make these arrays one element too long at each end, so as to | |
50 // test for overruns. For frame size 8, we expect 8/2+1 = 5 | |
51 // mag/phase pairs. | |
52 double mag[] = { 999, 999, 999, 999, 999, 999, 999 }; | |
53 double phase[] = { 999, 999, 999, 999, 999, 999, 999 }; | |
54 double unw[] = { 999, 999, 999, 999, 999, 999, 999 }; | |
55 | |
56 pvoc.processTimeDomain(frame, mag + 1, phase + 1, unw + 1); | |
57 | |
58 double magExpected0[] = { 999, 0, 0, 4, 0, 0, 999 }; | |
59 COMPARE_ARRAY_EXACT(mag, magExpected0); | |
60 | |
61 double phaseExpected0[] = { 999, 0, 0, 0, 0, 0, 999 }; | |
62 COMPARE_ARRAY(phase, phaseExpected0); | |
63 | |
64 double unwExpected0[] = { 999, 0, 0, 0, 0, 0, 999 }; | |
65 COMPARE_ARRAY(unw, unwExpected0); | |
66 | |
67 pvoc.processTimeDomain(frame, mag + 1, phase + 1, unw + 1); | |
68 | |
69 double magExpected1[] = { 999, 0, 0, 4, 0, 0, 999 }; | |
70 COMPARE_ARRAY_EXACT(mag, magExpected1); | |
71 | |
72 double phaseExpected1[] = { 999, 0, 0, 0, 0, 0, 999 }; | |
73 COMPARE_ARRAY(phase, phaseExpected1); | |
74 | |
75 // Derivation of unwrapped values: | |
76 // | |
77 // * Bin 0 (DC) always has phase 0 and expected phase 0 | |
78 // | |
79 // * Bin 1 has expected phase pi (the hop size is half a cycle at | |
80 // its frequency), but measured phase 0 (because there is no | |
81 // signal in that bin). So it has phase error -pi, which is | |
82 // mapped into (-pi,pi] range as pi, giving an unwrapped phase | |
83 // of 2*pi. | |
84 // | |
85 // * Bin 2 has expected phase 2*pi, measured phase 0, hence error | |
86 // 0 and unwrapped phase 2*pi. | |
87 // | |
88 // * Bin 3 is like bin 1: it has expected phase 3*pi, measured | |
89 // phase 0, so phase error -pi and unwrapped phase 4*pi. | |
90 // | |
91 // * Bin 4 (Nyquist) has expected phase 4*pi, measured phase 0, | |
92 // hence error 0 and unwrapped phase 4*pi. | |
93 | |
94 double unwExpected1[] = { 999, 0, 2*M_PI, 2*M_PI, 4*M_PI, 4*M_PI, 999 }; | |
95 COMPARE_ARRAY(unw, unwExpected1); | |
96 | |
97 pvoc.processTimeDomain(frame, mag + 1, phase + 1, unw + 1); | |
98 | |
99 double magExpected2[] = { 999, 0, 0, 4, 0, 0, 999 }; | |
100 COMPARE_ARRAY_EXACT(mag, magExpected2); | |
101 | |
102 double phaseExpected2[] = { 999, 0, 0, 0, 0, 0, 999 }; | |
103 COMPARE_ARRAY(phase, phaseExpected2); | |
104 | |
105 double unwExpected2[] = { 999, 0, 4*M_PI, 4*M_PI, 8*M_PI, 8*M_PI, 999 }; | |
106 COMPARE_ARRAY(unw, unwExpected2); | |
107 } | |
108 | |
109 BOOST_AUTO_TEST_CASE(overlapping) | |
110 { | |
111 // Sine (i.e. cosine starting at phase -pi/2) starting with the | |
112 // first sample, introducing a cosine of half the frequency | |
113 // starting at the fourth sample, i.e. the second hop. The cosine | |
114 // is introduced "by magic", i.e. it doesn't appear in the second | |
115 // half of the first frame (it would have quite strange effects on | |
116 // the first frame if it did). | |
117 | |
118 double data[32] = { // 3 x 8-sample frames which we pretend are overlapping | |
119 0, 1, 0, -1, 0, 1, 0, -1, | |
120 1, 1.70710678, 0, -1.70710678, -1, 0.29289322, 0, -0.29289322, | |
121 -1, 0.29289322, 0, -0.29289322, 1, 1.70710678, 0, -1.70710678, | |
122 }; | |
123 | |
124 PhaseVocoder pvoc(8, 4); | |
125 | |
126 // Make these arrays one element too long at each end, so as to | |
127 // test for overruns. For frame size 8, we expect 8/2+1 = 5 | |
128 // mag/phase pairs. | |
129 double mag[] = { 999, 999, 999, 999, 999, 999, 999 }; | |
130 double phase[] = { 999, 999, 999, 999, 999, 999, 999 }; | |
131 double unw[] = { 999, 999, 999, 999, 999, 999, 999 }; | |
132 | |
133 pvoc.processTimeDomain(data, mag + 1, phase + 1, unw + 1); | |
134 | |
135 double magExpected0[] = { 999, 0, 0, 4, 0, 0, 999 }; | |
136 COMPARE_ARRAY(mag, magExpected0); | |
137 | |
138 double phaseExpected0[] = { 999, 0, 0, -M_PI/2 , 0, 0, 999 }; | |
139 COMPARE_ARRAY(phase, phaseExpected0); | |
140 | |
141 double unwExpected0[] = { 999, 0, 0, -M_PI/2, 0, 0, 999 }; | |
142 COMPARE_ARRAY(unw, unwExpected0); | |
143 | |
144 pvoc.processTimeDomain(data + 8, mag + 1, phase + 1, unw + 1); | |
145 | |
146 double magExpected1[] = { 999, 0, 4, 4, 0, 0, 999 }; | |
147 COMPARE_ARRAY(mag, magExpected1); | |
148 | |
149 // Derivation of unwrapped values: | |
150 // | |
151 // * Bin 0 (DC) always has phase 0 and expected phase 0 | |
152 // | |
153 // * Bin 1 has a new signal, a cosine starting with phase 0. But | |
154 // because of the "FFT shift" which the phase vocoder carries | |
155 // out to place zero phase in the centre of the (usually | |
156 // windowed) frame, and because a single cycle at this frequency | |
157 // spans the whole frame, this bin actually has measured phase | |
158 // of either pi or -pi. (The shift doesn't affect those | |
159 // higher-frequency bins whose signals fit exact multiples of a | |
160 // cycle into a frame.) This maps into (-pi,pi] as pi, which | |
161 // matches the expected phase, hence unwrapped phase is also pi. | |
162 // | |
163 // * Bin 2 has expected phase 3pi/2 (being the previous measured | |
164 // phase of -pi/2 plus advance of 2pi). It has the same measured | |
165 // phase as last time around, -pi/2, which is consistent with | |
166 // the expected phase, so the unwrapped phase is 3pi/2. | |
167 //!!! | |
168 // * Bin 3 is a bit of a puzzle -- it has an effectively zero | |
169 // magnitude but a non-zero measured phase. Spectral leakage? | |
170 // | |
171 // * Bin 4 (Nyquist) has expected phase 4*pi, measured phase 0, | |
172 // hence error 0 and unwrapped phase 4*pi. | |
173 | |
174 double phaseExpected1[] = { 999, 0, -M_PI, -M_PI/2, M_PI, 0, 999 }; | |
175 COMPARE_ARRAY(phase, phaseExpected1); | |
176 | |
177 double unwExpected1[] = { 999, 0, M_PI, 3*M_PI/2, 3*M_PI, 4*M_PI, 999 }; | |
178 COMPARE_ARRAY(unw, unwExpected1); | |
179 | |
180 pvoc.processTimeDomain(data + 16, mag + 1, phase + 1, unw + 1); | |
181 | |
182 double magExpected2[] = { 999, 0, 4, 4, 0, 0, 999 }; | |
183 COMPARE_ARRAY(mag, magExpected2); | |
184 | |
185 double phaseExpected2[] = { 999, 0, 0, -M_PI/2, 0, 0, 999 }; | |
186 COMPARE_ARRAY(phase, phaseExpected2); | |
187 | |
188 double unwExpected2[] = { 999, 0, 2*M_PI, 7*M_PI/2, 6*M_PI, 8*M_PI, 999 }; | |
189 COMPARE_ARRAY(unw, unwExpected2); | |
190 } | |
191 | |
192 BOOST_AUTO_TEST_SUITE_END() | |
193 |