Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / LayoutTests / webaudio / biquad-getFrequencyResponse.html
blob3a6c01da56eed90477d5b9ac46501f6af5a1e4dc
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2 <html>
3 <head>
4 <script src="resources/compatibility.js"></script>
5 <script src="resources/audio-testing.js"></script>
6 <script src="resources/biquad-testing.js"></script>
7 <script src="../resources/js-test.js"></script>
8 </head>
10 <body>
11 <div id="description"></div>
12 <div id="console"></div>
14 <script>
15 description("Test Biquad getFrequencyResponse() functionality.");
17 // Test the frequency response of a biquad filter. We compute the frequency response for a simple
18 // peaking biquad filter and compare it with the expected frequency response. The actual filter
19 // used doesn't matter since we're testing getFrequencyResponse and not the actual filter output.
20 // The filters are extensively tested in other biquad tests.
22 var context;
24 // The biquad filter node.
25 var filter;
27 // The magnitude response of the biquad filter.
28 var magResponse;
30 // The phase response of the biquad filter.
31 var phaseResponse;
33 // Number of frequency samples to take.
34 var numberOfFrequencies = 1000;
36 // The filter parameters.
37 var filterCutoff = 1000; // Hz.
38 var filterQ = 1;
39 var filterGain = 5; // Decibels.
41 // The maximum allowed error in the magnitude response.
42 var maxAllowedMagError = 5.7e-7;
44 // The maximum allowed error in the phase response.
45 var maxAllowedPhaseError = 4.7e-8;
47 // The magnitudes and phases of the reference frequency response.
48 var magResponse;
49 var phaseResponse;
51 // The magnitudes and phases of the reference frequency response.
52 var expectedMagnitudes;
53 var expectedPhases;
55 // Convert frequency in Hz to a normalized frequency between 0 to 1 with 1 corresponding to the
56 // Nyquist frequency.
57 function normalizedFrequency(freqHz, sampleRate)
59 var nyquist = sampleRate / 2;
60 return freqHz / nyquist;
63 // Get the filter response at a (normalized) frequency |f| for the filter with coefficients |coef|.
64 function getResponseAt(coef, f)
66 var b0 = coef.b0;
67 var b1 = coef.b1;
68 var b2 = coef.b2;
69 var a1 = coef.a1;
70 var a2 = coef.a2;
72 // H(z) = (b0 + b1 / z + b2 / z^2) / (1 + a1 / z + a2 / z^2)
74 // Compute H(exp(i * pi * f)). No native complex numbers in javascript, so break H(exp(i * pi * // f))
75 // in to the real and imaginary parts of the numerator and denominator. Let omega = pi * f.
76 // Then the numerator is
78 // b0 + b1 * cos(omega) + b2 * cos(2 * omega) - i * (b1 * sin(omega) + b2 * sin(2 * omega))
80 // and the denominator is
82 // 1 + a1 * cos(omega) + a2 * cos(2 * omega) - i * (a1 * sin(omega) + a2 * sin(2 * omega))
84 // Compute the magnitude and phase from the real and imaginary parts.
86 var omega = Math.PI * f;
87 var numeratorReal = b0 + b1 * Math.cos(omega) + b2 * Math.cos(2 * omega);
88 var numeratorImag = -(b1 * Math.sin(omega) + b2 * Math.sin(2 * omega));
89 var denominatorReal = 1 + a1 * Math.cos(omega) + a2 * Math.cos(2 * omega);
90 var denominatorImag = -(a1 * Math.sin(omega) + a2 * Math.sin(2 * omega));
92 var magnitude = Math.sqrt((numeratorReal * numeratorReal + numeratorImag * numeratorImag)
93 / (denominatorReal * denominatorReal + denominatorImag * denominatorImag));
94 var phase = Math.atan2(numeratorImag, numeratorReal) - Math.atan2(denominatorImag, denominatorReal);
96 if (phase >= Math.PI) {
97 phase -= 2 * Math.PI;
98 } else if (phase <= -Math.PI) {
99 phase += 2 * Math.PI;
102 return {magnitude : magnitude, phase : phase};
105 // Compute the reference frequency response for the biquad filter |filter| at the frequency samples
106 // given by |frequencies|.
107 function frequencyResponseReference(filter, frequencies)
109 var sampleRate = filter.context.sampleRate;
110 var normalizedFreq = normalizedFrequency(filter.frequency.value, sampleRate);
111 var filterCoefficients = createFilter(filter.type, normalizedFreq, filter.Q.value, filter.gain.value);
113 var magnitudes = [];
114 var phases = [];
116 for (var k = 0; k < frequencies.length; ++k) {
117 var response = getResponseAt(filterCoefficients, normalizedFrequency(frequencies[k], sampleRate));
118 magnitudes.push(response.magnitude);
119 phases.push(response.phase);
122 return {magnitudes : magnitudes, phases : phases};
125 // Compute a set of linearly spaced frequencies.
126 function createFrequencies(nFrequencies, sampleRate)
128 var frequencies = new Float32Array(nFrequencies);
129 var nyquist = sampleRate / 2;
130 var freqDelta = nyquist / nFrequencies;
132 for (var k = 0; k < nFrequencies; ++k) {
133 frequencies[k] = k * freqDelta;
136 return frequencies;
139 function linearToDecibels(x)
141 if (x) {
142 return 20 * Math.log(x) / Math.LN10;
143 } else {
144 return -1000;
148 // Look through the array and find any NaN or infinity. Returns the index of the first occurence or
149 // -1 if none.
150 function findBadNumber(signal)
152 for (var k = 0; k < signal.length; ++k) {
153 if (!isValidNumber(signal[k])) {
154 return k;
157 return -1;
160 // Compute absolute value of the difference between phase angles, taking into account the wrapping
161 // of phases.
162 function absolutePhaseDifference(x, y)
164 var diff = Math.abs(x - y);
166 if (diff > Math.PI) {
167 diff = 2 * Math.PI - diff;
169 return diff;
172 // Compare the frequency response with our expected response.
173 function compareResponses(filter, frequencies, magResponse, phaseResponse)
175 var expectedResponse = frequencyResponseReference(filter, frequencies);
177 expectedMagnitudes = expectedResponse.magnitudes;
178 expectedPhases = expectedResponse.phases;
180 var n = magResponse.length;
181 var success = true;
182 var badResponse = false;
184 var maxMagError = -1;
185 var maxMagErrorIndex = -1;
187 var k;
188 var hasBadNumber;
190 hasBadNumber = findBadNumber(magResponse);
191 if (hasBadNumber >= 0) {
192 testFailed("Magnitude response has NaN or infinity at " + hasBadNumber);
193 success = false;
194 badResponse = true;
197 hasBadNumber = findBadNumber(phaseResponse);
198 if (hasBadNumber >= 0) {
199 testFailed("Phase response has NaN or infinity at " + hasBadNumber);
200 success = false;
201 badResponse = true;
204 // These aren't testing the implementation itself. Instead, these are sanity checks on the
205 // reference. Failure here does not imply an error in the implementation.
206 hasBadNumber = findBadNumber(expectedMagnitudes);
207 if (hasBadNumber >= 0) {
208 testFailed("Expected magnitude response has NaN or infinity at " + hasBadNumber);
209 success = false;
210 badResponse = true;
213 hasBadNumber = findBadNumber(expectedPhases);
214 if (hasBadNumber >= 0) {
215 testFailed("Expected phase response has NaN or infinity at " + hasBadNumber);
216 success = false;
217 badResponse = true;
220 // If we found a NaN or infinity, the following tests aren't very helpful, especially for NaN.
221 // We run them anyway, after printing a warning message.
223 if (badResponse) {
224 testFailed("NaN or infinity in the actual or expected results makes the following test results suspect.");
225 success = false;
228 for (k = 0; k < n; ++k) {
229 var error = Math.abs(linearToDecibels(magResponse[k]) - linearToDecibels(expectedMagnitudes[k]));
230 if (error > maxMagError) {
231 maxMagError = error;
232 maxMagErrorIndex = k;
236 if (maxMagError > maxAllowedMagError) {
237 var message = "Magnitude error (" + maxMagError + " dB)";
238 message += " exceeded threshold at " + frequencies[maxMagErrorIndex];
239 message += " Hz. Actual: " + linearToDecibels(magResponse[maxMagErrorIndex]);
240 message += " dB, expected: " + linearToDecibels(expectedMagnitudes[maxMagErrorIndex]) + " dB.";
241 testFailed(message);
242 success = false;
243 } else {
244 testPassed("Magnitude response within acceptable threshold.");
247 var maxPhaseError = -1;
248 var maxPhaseErrorIndex = -1;
250 for (k = 0; k < n; ++k) {
251 var error = absolutePhaseDifference(phaseResponse[k], expectedPhases[k]);
252 if (error > maxPhaseError) {
253 maxPhaseError = error;
254 maxPhaseErrorIndex = k;
258 if (maxPhaseError > maxAllowedPhaseError) {
259 var message = "Phase error (radians) (" + maxPhaseError;
260 message += ") exceeded threshold at " + frequencies[maxPhaseErrorIndex];
261 message += " Hz. Actual: " + phaseResponse[maxPhaseErrorIndex];
262 message += " expected: " + expectedPhases[maxPhaseErrorIndex];
263 testFailed(message);
264 success = false;
265 } else {
266 testPassed("Phase response within acceptable threshold.");
270 return success;
273 function runTest()
275 if (window.testRunner) {
276 testRunner.dumpAsText();
277 testRunner.waitUntilDone();
280 window.jsTestIsAsync = true;
282 context = new AudioContext();
284 filter = context.createBiquadFilter();
286 // Arbitrarily test a peaking filter, but any kind of filter can be tested.
287 filter.type = "peaking";
288 filter.frequency.value = filterCutoff;
289 filter.Q.value = filterQ;
290 filter.gain.value = filterGain;
292 var frequencies = createFrequencies(numberOfFrequencies, context.sampleRate);
293 magResponse = new Float32Array(numberOfFrequencies);
294 phaseResponse = new Float32Array(numberOfFrequencies);
296 filter.getFrequencyResponse(frequencies, magResponse, phaseResponse);
297 var success = compareResponses(filter, frequencies, magResponse, phaseResponse);
299 if (success) {
300 testPassed("Frequency response was correct.");
301 } else {
302 testFailed("Frequency response was incorrect.");
305 finishJSTest();
308 runTest();
309 successfullyParsed = true;
311 </script>
313 </body>
314 </html>