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

Side by Side Diff: third_party/WebKit/LayoutTests/webaudio/resources/biquad-testing.js

Issue 1361233004: Implement IIRFilter node for WebAudio. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix test issue by printing fewer digits. Created 4 years, 11 months 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
1 // Globals, to make testing and debugging easier. 1 // Globals, to make testing and debugging easier.
2 var context; 2 var context;
3 var filter; 3 var filter;
4 var signal; 4 var signal;
5 var renderedBuffer; 5 var renderedBuffer;
6 var renderedData; 6 var renderedData;
7 7
8 var sampleRate = 44100.0; 8 var sampleRate = 44100.0;
9 var pulseLengthFrames = .1 * sampleRate; 9 var pulseLengthFrames = .1 * sampleRate;
10 10
(...skipping 10 matching lines...) Expand all
21 21
22 // How long to render. Must be long enough for all of the filters we 22 // How long to render. Must be long enough for all of the filters we
23 // want to test. 23 // want to test.
24 var renderLengthSeconds = timeStep * (maxFilters + 1) ; 24 var renderLengthSeconds = timeStep * (maxFilters + 1) ;
25 25
26 var renderLengthSamples = Math.round(renderLengthSeconds * sampleRate); 26 var renderLengthSamples = Math.round(renderLengthSeconds * sampleRate);
27 27
28 // Number of filters that will be processed. 28 // Number of filters that will be processed.
29 var nFilters; 29 var nFilters;
30 30
31 // A biquad filter has a z-transform of
32 // H(z) = (b0 + b1 / z + b2 / z^2) / (1 + a1 / z + a2 / z^2)
33 //
34 // The formulas for the various filters were taken from
35 // http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt.
36
37
38 // Lowpass filter.
39 function createLowpassFilter(freq, q, gain) {
40 var b0;
41 var b1;
42 var b2;
43 var a1;
44 var a2;
45
46 if (freq == 1) {
47 // The formula below works, except for roundoff. When freq = 1,
48 // the filter is just a wire, so hardwire the coefficients.
49 b0 = 1;
50 b1 = 0;
51 b2 = 0;
52 a1 = 0;
53 a2 = 0;
54 } else {
55 var g = Math.pow(10, q / 20);
56 var d = Math.sqrt((4 - Math.sqrt(16 - 16 / (g * g))) / 2);
57 var theta = Math.PI * freq;
58 var sn = d * Math.sin(theta) / 2;
59 var beta = 0.5 * (1 - sn) / (1 + sn);
60 var gamma = (0.5 + beta) * Math.cos(theta);
61 var alpha = 0.25 * (0.5 + beta - gamma);
62
63 b0 = 2 * alpha;
64 b1 = 4 * alpha;
65 b2 = 2 * alpha;
66 a1 = 2 * (-gamma);
67 a2 = 2 * beta;
68 }
69
70 return {b0 : b0, b1 : b1, b2 : b2, a1 : a1, a2 : a2};
71 }
72
73 function createHighpassFilter(freq, q, gain) {
74 var b0;
75 var b1;
76 var b2;
77 var a1;
78 var a2;
79
80 if (freq == 1) {
81 // The filter is 0
82 b0 = 0;
83 b1 = 0;
84 b2 = 0;
85 a1 = 0;
86 a2 = 0;
87 } else if (freq == 0) {
88 // The filter is 1. Computation of coefficients below is ok, but
89 // there's a pole at 1 and a zero at 1, so round-off could make
90 // the filter unstable.
91 b0 = 1;
92 b1 = 0;
93 b2 = 0;
94 a1 = 0;
95 a2 = 0;
96 } else {
97 var g = Math.pow(10, q / 20);
98 var d = Math.sqrt((4 - Math.sqrt(16 - 16 / (g * g))) / 2);
99 var theta = Math.PI * freq;
100 var sn = d * Math.sin(theta) / 2;
101 var beta = 0.5 * (1 - sn) / (1 + sn);
102 var gamma = (0.5 + beta) * Math.cos(theta);
103 var alpha = 0.25 * (0.5 + beta + gamma);
104
105 b0 = 2 * alpha;
106 b1 = -4 * alpha;
107 b2 = 2 * alpha;
108 a1 = 2 * (-gamma);
109 a2 = 2 * beta;
110 }
111
112 return {b0 : b0, b1 : b1, b2 : b2, a1 : a1, a2 : a2};
113 }
114
115 function normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2) {
116 var scale = 1 / a0;
117
118 return {b0 : b0 * scale,
119 b1 : b1 * scale,
120 b2 : b2 * scale,
121 a1 : a1 * scale,
122 a2 : a2 * scale};
123 }
124
125 function createBandpassFilter(freq, q, gain) {
126 var b0;
127 var b1;
128 var b2;
129 var a0;
130 var a1;
131 var a2;
132 var coef;
133
134 if (freq > 0 && freq < 1) {
135 var w0 = Math.PI * freq;
136 if (q > 0) {
137 var alpha = Math.sin(w0) / (2 * q);
138 var k = Math.cos(w0);
139
140 b0 = alpha;
141 b1 = 0;
142 b2 = -alpha;
143 a0 = 1 + alpha;
144 a1 = -2 * k;
145 a2 = 1 - alpha;
146
147 coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
148 } else {
149 // q = 0, and frequency is not 0 or 1. The above formula has a
150 // divide by zero problem. The limit of the z-transform as q
151 // approaches 0 is 1, so set the filter that way.
152 coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
153 }
154 } else {
155 // When freq = 0 or 1, the z-transform is identically 0,
156 // independent of q.
157 coef = {b0 : 0, b1 : 0, b2 : 0, a1 : 0, a2 : 0}
158 }
159
160 return coef;
161 }
162
163 function createLowShelfFilter(freq, q, gain) {
164 // q not used
165 var b0;
166 var b1;
167 var b2;
168 var a0;
169 var a1;
170 var a2;
171 var coef;
172
173 var S = 1;
174 var A = Math.pow(10, gain / 40);
175
176 if (freq == 1) {
177 // The filter is just a constant gain
178 coef = {b0 : A * A, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
179 } else if (freq == 0) {
180 // The filter is 1
181 coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
182 } else {
183 var w0 = Math.PI * freq;
184 var alpha = 1 / 2 * Math.sin(w0) * Math.sqrt((A + 1 / A) * (1 / S - 1) + 2);
185 var k = Math.cos(w0);
186 var k2 = 2 * Math.sqrt(A) * alpha;
187 var Ap1 = A + 1;
188 var Am1 = A - 1;
189
190 b0 = A * (Ap1 - Am1 * k + k2);
191 b1 = 2 * A * (Am1 - Ap1 * k);
192 b2 = A * (Ap1 - Am1 * k - k2);
193 a0 = Ap1 + Am1 * k + k2;
194 a1 = -2 * (Am1 + Ap1 * k);
195 a2 = Ap1 + Am1 * k - k2;
196 coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
197 }
198
199 return coef;
200 }
201
202 function createHighShelfFilter(freq, q, gain) {
203 // q not used
204 var b0;
205 var b1;
206 var b2;
207 var a0;
208 var a1;
209 var a2;
210 var coef;
211
212 var A = Math.pow(10, gain / 40);
213
214 if (freq == 1) {
215 // When freq = 1, the z-transform is 1
216 coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
217 } else if (freq > 0) {
218 var w0 = Math.PI * freq;
219 var S = 1;
220 var alpha = 0.5 * Math.sin(w0) * Math.sqrt((A + 1 / A) * (1 / S - 1) + 2 );
221 var k = Math.cos(w0);
222 var k2 = 2 * Math.sqrt(A) * alpha;
223 var Ap1 = A + 1;
224 var Am1 = A - 1;
225
226 b0 = A * (Ap1 + Am1 * k + k2);
227 b1 = -2 * A * (Am1 + Ap1 * k);
228 b2 = A * (Ap1 + Am1 * k - k2);
229 a0 = Ap1 - Am1 * k + k2;
230 a1 = 2 * (Am1 - Ap1*k);
231 a2 = Ap1 - Am1 * k-k2;
232
233 coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
234 } else {
235 // When freq = 0, the filter is just a gain
236 coef = {b0 : A * A, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
237 }
238
239 return coef;
240 }
241
242 function createPeakingFilter(freq, q, gain) {
243 var b0;
244 var b1;
245 var b2;
246 var a0;
247 var a1;
248 var a2;
249 var coef;
250
251 var A = Math.pow(10, gain / 40);
252
253 if (freq > 0 && freq < 1) {
254 if (q > 0) {
255 var w0 = Math.PI * freq;
256 var alpha = Math.sin(w0) / (2 * q);
257 var k = Math.cos(w0);
258
259 b0 = 1 + alpha * A;
260 b1 = -2 * k;
261 b2 = 1 - alpha * A;
262 a0 = 1 + alpha / A;
263 a1 = -2 * k;
264 a2 = 1 - alpha / A;
265
266 coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
267 } else {
268 // q = 0, we have a divide by zero problem in the formulas
269 // above. But if we look at the z-transform, we see that the
270 // limit as q approaches 0 is A^2.
271 coef = {b0 : A * A, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
272 }
273 } else {
274 // freq = 0 or 1, the z-transform is 1
275 coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
276 }
277
278 return coef;
279 }
280
281 function createNotchFilter(freq, q, gain) {
282 var b0;
283 var b1;
284 var b2;
285 var a0;
286 var a1;
287 var a2;
288 var coef;
289
290 if (freq > 0 && freq < 1) {
291 if (q > 0) {
292 var w0 = Math.PI * freq;
293 var alpha = Math.sin(w0) / (2 * q);
294 var k = Math.cos(w0);
295
296 b0 = 1;
297 b1 = -2 * k;
298 b2 = 1;
299 a0 = 1 + alpha;
300 a1 = -2 * k;
301 a2 = 1 - alpha;
302 coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
303 } else {
304 // When q = 0, we get a divide by zero above. The limit of the
305 // z-transform as q approaches 0 is 0, so set the coefficients
306 // appropriately.
307 coef = {b0 : 0, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
308 }
309 } else {
310 // When freq = 0 or 1, the z-transform is 1
311 coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
312 }
313
314 return coef;
315 }
316
317 function createAllpassFilter(freq, q, gain) {
318 var b0;
319 var b1;
320 var b2;
321 var a0;
322 var a1;
323 var a2;
324 var coef;
325
326 if (freq > 0 && freq < 1) {
327 if (q > 0) {
328 var w0 = Math.PI * freq;
329 var alpha = Math.sin(w0) / (2 * q);
330 var k = Math.cos(w0);
331
332 b0 = 1 - alpha;
333 b1 = -2 * k;
334 b2 = 1 + alpha;
335 a0 = 1 + alpha;
336 a1 = -2 * k;
337 a2 = 1 - alpha;
338 coef = normalizeFilterCoefficients(b0, b1, b2, a0, a1, a2);
339 } else {
340 // q = 0
341 coef = {b0 : -1, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
342 }
343 } else {
344 coef = {b0 : 1, b1 : 0, b2 : 0, a1 : 0, a2 : 0};
345 }
346
347 return coef;
348 }
349
350 // Map the filter type name to a function that computes the filter coefficents f or the given filter
351 // type.
352 var filterCreatorFunction = {"lowpass": createLowpassFilter,
353 "highpass": createHighpassFilter,
354 "bandpass": createBandpassFilter,
355 "lowshelf": createLowShelfFilter,
356 "highshelf": createHighShelfFilter,
357 "peaking": createPeakingFilter,
358 "notch": createNotchFilter,
359 "allpass": createAllpassFilter};
360
361 var filterTypeName = {"lowpass": "Lowpass filter",
362 "highpass": "Highpass filter",
363 "bandpass": "Bandpass filter",
364 "lowshelf": "Lowshelf filter",
365 "highshelf": "Highshelf filter",
366 "peaking": "Peaking filter",
367 "notch": "Notch filter",
368 "allpass": "Allpass filter"};
369
370 function createFilter(filterType, freq, q, gain) {
371 return filterCreatorFunction[filterType](freq, q, gain);
372 }
373
374 function filterData(filterCoef, signal, len) {
375 var y = new Array(len);
376 var b0 = filterCoef.b0;
377 var b1 = filterCoef.b1;
378 var b2 = filterCoef.b2;
379 var a1 = filterCoef.a1;
380 var a2 = filterCoef.a2;
381
382 // Prime the pump. (Assumes the signal has length >= 2!)
383 y[0] = b0 * signal[0];
384 y[1] = b0 * signal[1] + b1 * signal[0] - a1 * y[0];
385
386 // Filter all of the signal that we have.
387 for (var k = 2; k < Math.min(signal.length, len); ++k) {
388 y[k] = b0 * signal[k] + b1 * signal[k-1] + b2 * signal[k-2] - a1 * y[k-1 ] - a2 * y[k-2];
389 }
390
391 // If we need to filter more, but don't have any signal left,
392 // assume the signal is zero.
393 for (var k = signal.length; k < len; ++k) {
394 y[k] = - a1 * y[k-1] - a2 * y[k-2];
395 }
396
397 return y;
398 }
399
400 function createImpulseBuffer(context, length) { 31 function createImpulseBuffer(context, length) {
401 var impulse = context.createBuffer(1, length, context.sampleRate); 32 var impulse = context.createBuffer(1, length, context.sampleRate);
402 var data = impulse.getChannelData(0); 33 var data = impulse.getChannelData(0);
403 for (var k = 1; k < data.length; ++k) { 34 for (var k = 1; k < data.length; ++k) {
404 data[k] = 0; 35 data[k] = 0;
405 } 36 }
406 data[0] = 1; 37 data[0] = 1;
407 38
408 return impulse; 39 return impulse;
409 } 40 }
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
539 } 170 }
540 171
541 if (success) { 172 if (success) {
542 testPassed("Test signal was correctly filtered."); 173 testPassed("Test signal was correctly filtered.");
543 } else { 174 } else {
544 testFailed("Test signal was not correctly filtered."); 175 testFailed("Test signal was not correctly filtered.");
545 } 176 }
546 finishJSTest(); 177 finishJSTest();
547 } 178 }
548 } 179 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698