Mercurial > hg > svcore
comparison data/model/test/TestFFTModel.h @ 1126:39019ce29178 tony-2.0-integration
Merge through to branch for Tony 2.0
author | Chris Cannam |
---|---|
date | Thu, 20 Aug 2015 14:54:21 +0100 |
parents | 457a1a619c5f |
children | 87ae75da6527 |
comparison
equal
deleted
inserted
replaced
1119:e22bfe8ca248 | 1126:39019ce29178 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 Sonic Visualiser | |
5 An audio file viewer and annotation editor. | |
6 Centre for Digital Music, Queen Mary, University of London. | |
7 | |
8 This program is free software; you can redistribute it and/or | |
9 modify it under the terms of the GNU General Public License as | |
10 published by the Free Software Foundation; either version 2 of the | |
11 License, or (at your option) any later version. See the file | |
12 COPYING included with this distribution for more information. | |
13 */ | |
14 | |
15 #ifndef TEST_FFT_MODEL_H | |
16 #define TEST_FFT_MODEL_H | |
17 | |
18 #include "../FFTModel.h" | |
19 | |
20 #include "MockWaveModel.h" | |
21 | |
22 #include "Compares.h" | |
23 | |
24 #include <QObject> | |
25 #include <QtTest> | |
26 #include <QDir> | |
27 | |
28 #include <iostream> | |
29 #include <complex> | |
30 | |
31 using namespace std; | |
32 | |
33 class TestFFTModel : public QObject | |
34 { | |
35 Q_OBJECT | |
36 | |
37 private: | |
38 void test(DenseTimeValueModel *model, | |
39 WindowType window, int windowSize, int windowIncrement, int fftSize, | |
40 int columnNo, vector<vector<complex<float>>> expectedValues, | |
41 int expectedWidth) { | |
42 for (int ch = 0; in_range_for(expectedValues, ch); ++ch) { | |
43 FFTModel fftm(model, ch, window, windowSize, windowIncrement, fftSize); | |
44 QCOMPARE(fftm.getWidth(), expectedWidth); | |
45 int hs1 = fftSize/2 + 1; | |
46 QCOMPARE(fftm.getHeight(), hs1); | |
47 vector<float> reals(hs1 + 1, 0.f); | |
48 vector<float> imags(hs1 + 1, 0.f); | |
49 reals[hs1] = 999.f; // overrun guards | |
50 imags[hs1] = 999.f; | |
51 for (int stepThrough = 0; stepThrough <= 1; ++stepThrough) { | |
52 if (stepThrough) { | |
53 // Read through the columns in order instead of | |
54 // randomly accessing the one we want. This is to | |
55 // exercise the case where the FFT model saves | |
56 // part of each input frame and moves along by | |
57 // only the non-overlapping distance | |
58 for (int sc = 0; sc < columnNo; ++sc) { | |
59 fftm.getValuesAt(sc, &reals[0], &imags[0]); | |
60 } | |
61 } | |
62 fftm.getValuesAt(columnNo, &reals[0], &imags[0]); | |
63 for (int i = 0; i < hs1; ++i) { | |
64 float eRe = expectedValues[ch][i].real(); | |
65 float eIm = expectedValues[ch][i].imag(); | |
66 float thresh = 1e-5f; | |
67 if (abs(reals[i] - eRe) > thresh || | |
68 abs(imags[i] - eIm) > thresh) { | |
69 cerr << "ERROR: output is not as expected for column " | |
70 << i << " in channel " << ch << " (stepThrough = " | |
71 << stepThrough << ")" << endl; | |
72 cerr << "expected : "; | |
73 for (int j = 0; j < hs1; ++j) { | |
74 cerr << expectedValues[ch][j] << " "; | |
75 } | |
76 cerr << "\nactual : "; | |
77 for (int j = 0; j < hs1; ++j) { | |
78 cerr << complex<float>(reals[j], imags[j]) << " "; | |
79 } | |
80 cerr << endl; | |
81 } | |
82 COMPARE_FUZZIER_F(reals[i], eRe); | |
83 COMPARE_FUZZIER_F(imags[i], eIm); | |
84 } | |
85 QCOMPARE(reals[hs1], 999.f); | |
86 QCOMPARE(imags[hs1], 999.f); | |
87 } | |
88 } | |
89 } | |
90 | |
91 private slots: | |
92 | |
93 // NB. FFTModel columns are centred on the sample frame, and in | |
94 // particular this means column 0 is centred at sample 0 (i.e. it | |
95 // contains only half the window-size worth of real samples, the | |
96 // others are 0-valued from before the origin). Generally in | |
97 // these tests we are padding our signal with half a window of | |
98 // zeros, in order that the result for column 0 is all zeros | |
99 // (rather than something with a step in it that is harder to | |
100 // reason about the FFT of) and the results for subsequent columns | |
101 // are those of our expected signal. | |
102 | |
103 void dc_simple_rect() { | |
104 MockWaveModel mwm({ DC }, 16, 4); | |
105 test(&mwm, RectangularWindow, 8, 8, 8, 0, | |
106 { { {}, {}, {}, {}, {} } }, 4); | |
107 test(&mwm, RectangularWindow, 8, 8, 8, 1, | |
108 { { { 4.f, 0.f }, {}, {}, {}, {} } }, 4); | |
109 test(&mwm, RectangularWindow, 8, 8, 8, 2, | |
110 { { { 4.f, 0.f }, {}, {}, {}, {} } }, 4); | |
111 test(&mwm, RectangularWindow, 8, 8, 8, 3, | |
112 { { {}, {}, {}, {}, {} } }, 4); | |
113 } | |
114 | |
115 void dc_simple_hann() { | |
116 // The Hann window function is a simple sinusoid with period | |
117 // equal to twice the window size, and it halves the DC energy | |
118 MockWaveModel mwm({ DC }, 16, 4); | |
119 test(&mwm, HanningWindow, 8, 8, 8, 0, | |
120 { { {}, {}, {}, {}, {} } }, 4); | |
121 test(&mwm, HanningWindow, 8, 8, 8, 1, | |
122 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 4); | |
123 test(&mwm, HanningWindow, 8, 8, 8, 2, | |
124 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 4); | |
125 test(&mwm, HanningWindow, 8, 8, 8, 3, | |
126 { { {}, {}, {}, {}, {} } }, 4); | |
127 } | |
128 | |
129 void dc_simple_hann_halfoverlap() { | |
130 MockWaveModel mwm({ DC }, 16, 4); | |
131 test(&mwm, HanningWindow, 8, 4, 8, 0, | |
132 { { {}, {}, {}, {}, {} } }, 7); | |
133 test(&mwm, HanningWindow, 8, 4, 8, 2, | |
134 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 7); | |
135 test(&mwm, HanningWindow, 8, 4, 8, 3, | |
136 { { { 4.f, 0.f }, { 2.f, 0.f }, {}, {}, {} } }, 7); | |
137 test(&mwm, HanningWindow, 8, 4, 8, 6, | |
138 { { {}, {}, {}, {}, {} } }, 7); | |
139 } | |
140 | |
141 void sine_simple_rect() { | |
142 MockWaveModel mwm({ Sine }, 16, 4); | |
143 // Sine: output is purely imaginary. Note the sign is flipped | |
144 // (normally the first half of the output would have negative | |
145 // sign for a sine starting at 0) because the model does an | |
146 // FFT shift to centre the phase | |
147 test(&mwm, RectangularWindow, 8, 8, 8, 0, | |
148 { { {}, {}, {}, {}, {} } }, 4); | |
149 test(&mwm, RectangularWindow, 8, 8, 8, 1, | |
150 { { {}, { 0.f, 2.f }, {}, {}, {} } }, 4); | |
151 test(&mwm, RectangularWindow, 8, 8, 8, 2, | |
152 { { {}, { 0.f, 2.f }, {}, {}, {} } }, 4); | |
153 test(&mwm, RectangularWindow, 8, 8, 8, 3, | |
154 { { {}, {}, {}, {}, {} } }, 4); | |
155 } | |
156 | |
157 void cosine_simple_rect() { | |
158 MockWaveModel mwm({ Cosine }, 16, 4); | |
159 // Cosine: output is purely real. Note the sign is flipped | |
160 // because the model does an FFT shift to centre the phase | |
161 test(&mwm, RectangularWindow, 8, 8, 8, 0, | |
162 { { {}, {}, {}, {}, {} } }, 4); | |
163 test(&mwm, RectangularWindow, 8, 8, 8, 1, | |
164 { { {}, { -2.f, 0.f }, {}, {}, {} } }, 4); | |
165 test(&mwm, RectangularWindow, 8, 8, 8, 2, | |
166 { { {}, { -2.f, 0.f }, {}, {}, {} } }, 4); | |
167 test(&mwm, RectangularWindow, 8, 8, 8, 3, | |
168 { { {}, {}, {}, {}, {} } }, 4); | |
169 } | |
170 | |
171 void twochan_simple_rect() { | |
172 MockWaveModel mwm({ Sine, Cosine }, 16, 4); | |
173 // Test that the two channels are read and converted separately | |
174 test(&mwm, RectangularWindow, 8, 8, 8, 0, | |
175 { | |
176 { {}, {}, {}, {}, {} }, | |
177 { {}, {}, {}, {}, {} } | |
178 }, 4); | |
179 test(&mwm, RectangularWindow, 8, 8, 8, 1, | |
180 { | |
181 { {}, { 0.f, 2.f }, {}, {}, {} }, | |
182 { {}, { -2.f, 0.f }, {}, {}, {} } | |
183 }, 4); | |
184 test(&mwm, RectangularWindow, 8, 8, 8, 2, | |
185 { | |
186 { {}, { 0.f, 2.f }, {}, {}, {} }, | |
187 { {}, { -2.f, 0.f }, {}, {}, {} } | |
188 }, 4); | |
189 test(&mwm, RectangularWindow, 8, 8, 8, 3, | |
190 { | |
191 { {}, {}, {}, {}, {} }, | |
192 { {}, {}, {}, {}, {} } | |
193 }, 4); | |
194 } | |
195 | |
196 void nyquist_simple_rect() { | |
197 MockWaveModel mwm({ Nyquist }, 16, 4); | |
198 // Again, the sign is flipped. This has the same amount of | |
199 // energy as the DC example | |
200 test(&mwm, RectangularWindow, 8, 8, 8, 0, | |
201 { { {}, {}, {}, {}, {} } }, 4); | |
202 test(&mwm, RectangularWindow, 8, 8, 8, 1, | |
203 { { {}, {}, {}, {}, { -4.f, 0.f } } }, 4); | |
204 test(&mwm, RectangularWindow, 8, 8, 8, 2, | |
205 { { {}, {}, {}, {}, { -4.f, 0.f } } }, 4); | |
206 test(&mwm, RectangularWindow, 8, 8, 8, 3, | |
207 { { {}, {}, {}, {}, {} } }, 4); | |
208 } | |
209 | |
210 void dirac_simple_rect() { | |
211 MockWaveModel mwm({ Dirac }, 16, 4); | |
212 // The window scales by 0.5 and some signs are flipped. Only | |
213 // column 1 has any data (the single impulse). | |
214 test(&mwm, RectangularWindow, 8, 8, 8, 0, | |
215 { { {}, {}, {}, {}, {} } }, 4); | |
216 test(&mwm, RectangularWindow, 8, 8, 8, 1, | |
217 { { { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f } } }, 4); | |
218 test(&mwm, RectangularWindow, 8, 8, 8, 2, | |
219 { { {}, {}, {}, {}, {} } }, 4); | |
220 test(&mwm, RectangularWindow, 8, 8, 8, 3, | |
221 { { {}, {}, {}, {}, {} } }, 4); | |
222 } | |
223 | |
224 void dirac_simple_rect_2() { | |
225 MockWaveModel mwm({ Dirac }, 16, 8); | |
226 // With 8 samples padding, the FFT shift places the first | |
227 // Dirac impulse at the start of column 1, thus giving all | |
228 // positive values | |
229 test(&mwm, RectangularWindow, 8, 8, 8, 0, | |
230 { { {}, {}, {}, {}, {} } }, 5); | |
231 test(&mwm, RectangularWindow, 8, 8, 8, 1, | |
232 { { { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f } } }, 5); | |
233 test(&mwm, RectangularWindow, 8, 8, 8, 2, | |
234 { { {}, {}, {}, {}, {} } }, 5); | |
235 test(&mwm, RectangularWindow, 8, 8, 8, 3, | |
236 { { {}, {}, {}, {}, {} } }, 5); | |
237 test(&mwm, RectangularWindow, 8, 8, 8, 4, | |
238 { { {}, {}, {}, {}, {} } }, 5); | |
239 } | |
240 | |
241 void dirac_simple_rect_halfoverlap() { | |
242 MockWaveModel mwm({ Dirac }, 16, 4); | |
243 test(&mwm, RectangularWindow, 8, 4, 8, 0, | |
244 { { {}, {}, {}, {}, {} } }, 7); | |
245 test(&mwm, RectangularWindow, 8, 4, 8, 1, | |
246 { { { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f }, { 0.5f, 0.f } } }, 7); | |
247 test(&mwm, RectangularWindow, 8, 4, 8, 2, | |
248 { { { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f }, { -0.5f, 0.f }, { 0.5f, 0.f } } }, 7); | |
249 test(&mwm, RectangularWindow, 8, 4, 8, 3, | |
250 { { {}, {}, {}, {}, {} } }, 7); | |
251 } | |
252 | |
253 }; | |
254 | |
255 #endif |