Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(185)

Side by Side Diff: third_party/WebKit/LayoutTests/webaudio/biquad-getFrequencyResponse.html

Issue 2581463002: Refactor WebAudio test directory (Closed)
Patch Set: Use correct path for wav result files Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2 <html>
3 <head>
4 <script src="../resources/js-test.js"></script>
5 <script src="resources/compatibility.js"></script>
6 <script src="resources/audit-util.js"></script>
7 <script src="resources/audio-testing.js"></script>
8 <script src="resources/biquad-filters.js"></script>
9 <script src="resources/biquad-testing.js"></script>
10 </head>
11
12 <body>
13 <div id="description"></div>
14 <div id="console"></div>
15
16 <script>
17 description("Test Biquad getFrequencyResponse() functionality.");
18
19 // Test the frequency response of a biquad filter. We compute the frequency res ponse for a simple
20 // peaking biquad filter and compare it with the expected frequency response. T he actual filter
21 // used doesn't matter since we're testing getFrequencyResponse and not the actu al filter output.
22 // The filters are extensively tested in other biquad tests.
23
24 var context;
25
26 // The biquad filter node.
27 var filter;
28
29 // The magnitude response of the biquad filter.
30 var magResponse;
31
32 // The phase response of the biquad filter.
33 var phaseResponse;
34
35 // Number of frequency samples to take.
36 var numberOfFrequencies = 1000;
37
38 // The filter parameters.
39 var filterCutoff = 1000; // Hz.
40 var filterQ = 1;
41 var filterGain = 5; // Decibels.
42
43 // The maximum allowed error in the magnitude response.
44 var maxAllowedMagError = 5.7e-7;
45
46 // The maximum allowed error in the phase response.
47 var maxAllowedPhaseError = 4.7e-8;
48
49 // The magnitudes and phases of the reference frequency response.
50 var magResponse;
51 var phaseResponse;
52
53 // The magnitudes and phases of the reference frequency response.
54 var expectedMagnitudes;
55 var expectedPhases;
56
57 // Convert frequency in Hz to a normalized frequency between 0 to 1 with 1 corre sponding to the
58 // Nyquist frequency.
59 function normalizedFrequency(freqHz, sampleRate)
60 {
61 var nyquist = sampleRate / 2;
62 return freqHz / nyquist;
63 }
64
65 // Get the filter response at a (normalized) frequency |f| for the filter with c oefficients |coef|.
66 function getResponseAt(coef, f)
67 {
68 var b0 = coef.b0;
69 var b1 = coef.b1;
70 var b2 = coef.b2;
71 var a1 = coef.a1;
72 var a2 = coef.a2;
73
74 // H(z) = (b0 + b1 / z + b2 / z^2) / (1 + a1 / z + a2 / z^2)
75 //
76 // Compute H(exp(i * pi * f)). No native complex numbers in javascript, so break H(exp(i * pi * // f))
77 // in to the real and imaginary parts of the numerator and denominator. Let omega = pi * f.
78 // Then the numerator is
79 //
80 // b0 + b1 * cos(omega) + b2 * cos(2 * omega) - i * (b1 * sin(omega) + b2 * sin(2 * omega))
81 //
82 // and the denominator is
83 //
84 // 1 + a1 * cos(omega) + a2 * cos(2 * omega) - i * (a1 * sin(omega) + a2 * s in(2 * omega))
85 //
86 // Compute the magnitude and phase from the real and imaginary parts.
87
88 var omega = Math.PI * f;
89 var numeratorReal = b0 + b1 * Math.cos(omega) + b2 * Math.cos(2 * omega);
90 var numeratorImag = -(b1 * Math.sin(omega) + b2 * Math.sin(2 * omega));
91 var denominatorReal = 1 + a1 * Math.cos(omega) + a2 * Math.cos(2 * omega);
92 var denominatorImag = -(a1 * Math.sin(omega) + a2 * Math.sin(2 * omega));
93
94 var magnitude = Math.sqrt((numeratorReal * numeratorReal + numeratorImag * n umeratorImag)
95 / (denominatorReal * denominatorReal + denominator Imag * denominatorImag));
96 var phase = Math.atan2(numeratorImag, numeratorReal) - Math.atan2(denominato rImag, denominatorReal);
97
98 if (phase >= Math.PI) {
99 phase -= 2 * Math.PI;
100 } else if (phase <= -Math.PI) {
101 phase += 2 * Math.PI;
102 }
103
104 return {magnitude : magnitude, phase : phase};
105 }
106
107 // Compute the reference frequency response for the biquad filter |filter| at th e frequency samples
108 // given by |frequencies|.
109 function frequencyResponseReference(filter, frequencies)
110 {
111 var sampleRate = filter.context.sampleRate;
112 var normalizedFreq = normalizedFrequency(filter.frequency.value, sampleRate) ;
113 var filterCoefficients = createFilter(filter.type, normalizedFreq, filter.Q. value, filter.gain.value);
114
115 var magnitudes = [];
116 var phases = [];
117
118 for (var k = 0; k < frequencies.length; ++k) {
119 var response = getResponseAt(filterCoefficients, normalizedFrequency(fre quencies[k], sampleRate));
120 magnitudes.push(response.magnitude);
121 phases.push(response.phase);
122 }
123
124 return {magnitudes : magnitudes, phases : phases};
125 }
126
127 // Compute a set of linearly spaced frequencies.
128 function createFrequencies(nFrequencies, sampleRate)
129 {
130 var frequencies = new Float32Array(nFrequencies);
131 var nyquist = sampleRate / 2;
132 var freqDelta = nyquist / nFrequencies;
133
134 for (var k = 0; k < nFrequencies; ++k) {
135 frequencies[k] = k * freqDelta;
136 }
137
138 return frequencies;
139 }
140
141 function linearToDecibels(x)
142 {
143 if (x) {
144 return 20 * Math.log(x) / Math.LN10;
145 } else {
146 return -1000;
147 }
148 }
149
150 // Look through the array and find any NaN or infinity. Returns the index of the first occurence or
151 // -1 if none.
152 function findBadNumber(signal)
153 {
154 for (var k = 0; k < signal.length; ++k) {
155 if (!isValidNumber(signal[k])) {
156 return k;
157 }
158 }
159 return -1;
160 }
161
162 // Compute absolute value of the difference between phase angles, taking into ac count the wrapping
163 // of phases.
164 function absolutePhaseDifference(x, y)
165 {
166 var diff = Math.abs(x - y);
167
168 if (diff > Math.PI) {
169 diff = 2 * Math.PI - diff;
170 }
171 return diff;
172 }
173
174 // Compare the frequency response with our expected response.
175 function compareResponses(filter, frequencies, magResponse, phaseResponse)
176 {
177 var expectedResponse = frequencyResponseReference(filter, frequencies);
178
179 expectedMagnitudes = expectedResponse.magnitudes;
180 expectedPhases = expectedResponse.phases;
181
182 var n = magResponse.length;
183 var success = true;
184 var badResponse = false;
185
186 var maxMagError = -1;
187 var maxMagErrorIndex = -1;
188
189 var k;
190 var hasBadNumber;
191
192 hasBadNumber = findBadNumber(magResponse);
193 if (hasBadNumber >= 0) {
194 testFailed("Magnitude response has NaN or infinity at " + hasBadNumber);
195 success = false;
196 badResponse = true;
197 }
198
199 hasBadNumber = findBadNumber(phaseResponse);
200 if (hasBadNumber >= 0) {
201 testFailed("Phase response has NaN or infinity at " + hasBadNumber);
202 success = false;
203 badResponse = true;
204 }
205
206 // These aren't testing the implementation itself. Instead, these are sanit y checks on the
207 // reference. Failure here does not imply an error in the implementation.
208 hasBadNumber = findBadNumber(expectedMagnitudes);
209 if (hasBadNumber >= 0) {
210 testFailed("Expected magnitude response has NaN or infinity at " + hasBa dNumber);
211 success = false;
212 badResponse = true;
213 }
214
215 hasBadNumber = findBadNumber(expectedPhases);
216 if (hasBadNumber >= 0) {
217 testFailed("Expected phase response has NaN or infinity at " + hasBadNum ber);
218 success = false;
219 badResponse = true;
220 }
221
222 // If we found a NaN or infinity, the following tests aren't very helpful, e specially for NaN.
223 // We run them anyway, after printing a warning message.
224
225 if (badResponse) {
226 testFailed("NaN or infinity in the actual or expected results makes the following test results suspect.");
227 success = false;
228 }
229
230 for (k = 0; k < n; ++k) {
231 var error = Math.abs(linearToDecibels(magResponse[k]) - linearToDecibels (expectedMagnitudes[k]));
232 if (error > maxMagError) {
233 maxMagError = error;
234 maxMagErrorIndex = k;
235 }
236 }
237
238 if (maxMagError > maxAllowedMagError) {
239 var message = "Magnitude error (" + maxMagError + " dB)";
240 message += " exceeded threshold at " + frequencies[maxMagErrorIndex];
241 message += " Hz. Actual: " + linearToDecibels(magResponse[maxMagErrorIn dex]);
242 message += " dB, expected: " + linearToDecibels(expectedMagnitudes[maxMa gErrorIndex]) + " dB.";
243 testFailed(message);
244 success = false;
245 } else {
246 testPassed("Magnitude response within acceptable threshold.");
247 }
248
249 var maxPhaseError = -1;
250 var maxPhaseErrorIndex = -1;
251
252 for (k = 0; k < n; ++k) {
253 var error = absolutePhaseDifference(phaseResponse[k], expectedPhases[k]) ;
254 if (error > maxPhaseError) {
255 maxPhaseError = error;
256 maxPhaseErrorIndex = k;
257 }
258 }
259
260 if (maxPhaseError > maxAllowedPhaseError) {
261 var message = "Phase error (radians) (" + maxPhaseError;
262 message += ") exceeded threshold at " + frequencies[maxPhaseErrorIndex];
263 message += " Hz. Actual: " + phaseResponse[maxPhaseErrorIndex];
264 message += " expected: " + expectedPhases[maxPhaseErrorIndex];
265 testFailed(message);
266 success = false;
267 } else {
268 testPassed("Phase response within acceptable threshold.");
269 }
270
271
272 return success;
273 }
274
275 function runTest()
276 {
277 if (window.testRunner) {
278 testRunner.dumpAsText();
279 testRunner.waitUntilDone();
280 }
281
282 window.jsTestIsAsync = true;
283
284 context = new AudioContext();
285
286 filter = context.createBiquadFilter();
287
288 // Arbitrarily test a peaking filter, but any kind of filter can be tested.
289 filter.type = "peaking";
290 filter.frequency.value = filterCutoff;
291 filter.Q.value = filterQ;
292 filter.gain.value = filterGain;
293
294 var frequencies = createFrequencies(numberOfFrequencies, context.sampleRate) ;
295 magResponse = new Float32Array(numberOfFrequencies);
296 phaseResponse = new Float32Array(numberOfFrequencies);
297
298 filter.getFrequencyResponse(frequencies, magResponse, phaseResponse);
299 var success = compareResponses(filter, frequencies, magResponse, phaseRespon se);
300
301 if (success) {
302 testPassed("Frequency response was correct.");
303 } else {
304 testFailed("Frequency response was incorrect.");
305 }
306
307 finishJSTest();
308 }
309
310 runTest();
311 successfullyParsed = true;
312
313 </script>
314
315 </body>
316 </html>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698