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

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

Issue 2033503004: Avoid slow AudioParam automation path when possible (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Move m_smoothedValue from AudioParam to AudioParamTimeline. Created 4 years, 6 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 <!doctype html> 1 <!doctype html>
2 <html> 2 <html>
3 <head> 3 <head>
4 <title>Biquad Automation Test</title> 4 <title>Biquad Automation Test</title>
5 <script src="../resources/js-test.js"></script> 5 <script src="../resources/js-test.js"></script>
6 <script src="resources/compatibility.js"></script> 6 <script src="resources/compatibility.js"></script>
7 <script src="resources/audio-testing.js"></script> 7 <script src="resources/audio-testing.js"></script>
8 <script src="resources/biquad-filters.js"></script> 8 <script src="resources/biquad-filters.js"></script>
9 <script src="resources/audioparam-testing.js"></script> 9 <script src="resources/audioparam-testing.js"></script>
10 </head> 10 </head>
(...skipping 20 matching lines...) Expand all
31 // Generate the filter coefficients for the specified filter using the giv en parameters for 31 // Generate the filter coefficients for the specified filter using the giv en parameters for
32 // the given duration. |filterTypeFunction| is a function that returns th e filter 32 // the given duration. |filterTypeFunction| is a function that returns th e filter
33 // coefficients for one set of parameters. |parameters| is a property bag that contains the 33 // coefficients for one set of parameters. |parameters| is a property bag that contains the
34 // start and end values (as an array) for each of the biquad attributes. The properties are 34 // start and end values (as an array) for each of the biquad attributes. The properties are
35 // |freq|, |Q|, |gain|, and |detune|. |duration| is the number of seconds for which the 35 // |freq|, |Q|, |gain|, and |detune|. |duration| is the number of seconds for which the
36 // coefficients are generated. 36 // coefficients are generated.
37 // 37 //
38 // A property bag with properties |b0|, |b1|, |b2|, |a1|, |a2|. Each prop ery is an array 38 // A property bag with properties |b0|, |b1|, |b2|, |a1|, |a2|. Each prop ery is an array
39 // consisting of the coefficients for the time-varying biquad filter. 39 // consisting of the coefficients for the time-varying biquad filter.
40 function generateFilterCoefficients(filterTypeFunction, parameters, durati on) { 40 function generateFilterCoefficients(filterTypeFunction, parameters, durati on) {
41 var renderEndFrame = Math.ceil(renderDuration * sampleRate);
41 var endFrame = Math.ceil(duration * sampleRate); 42 var endFrame = Math.ceil(duration * sampleRate);
42 var nCoef = endFrame; 43 var nCoef = renderEndFrame;
43 var b0 = new Float64Array(nCoef); 44 var b0 = new Float64Array(nCoef);
44 var b1 = new Float64Array(nCoef); 45 var b1 = new Float64Array(nCoef);
45 var b2 = new Float64Array(nCoef); 46 var b2 = new Float64Array(nCoef);
46 var a1 = new Float64Array(nCoef); 47 var a1 = new Float64Array(nCoef);
47 var a2 = new Float64Array(nCoef); 48 var a2 = new Float64Array(nCoef);
48 49
49 var k = 0; 50 var k = 0;
50 // If the property is not given, use the defaults. 51 // If the property is not given, use the defaults.
51 var freqs = parameters.freq || [350, 350]; 52 var freqs = parameters.freq || [350, 350];
52 var qs = parameters.Q || [1, 1]; 53 var qs = parameters.Q || [1, 1];
53 var gains = parameters.gain || [0, 0]; 54 var gains = parameters.gain || [0, 0];
54 var detunes = parameters.detune || [0, 0]; 55 var detunes = parameters.detune || [0, 0];
55 56
56 for (var frame = 0; frame < endFrame; ++frame) { 57 for (var frame = 0; frame <= endFrame; ++frame) {
57 // Apply linear ramp at frame |frame|. 58 // Apply linear ramp at frame |frame|.
58 var f = linearRamp(frame / sampleRate, freqs[0], freqs[1], 0, durati on); 59 var f = linearRamp(frame / sampleRate, freqs[0], freqs[1], 0, durati on);
59 var q = linearRamp(frame / sampleRate, qs[0], qs[1], 0, duration); 60 var q = linearRamp(frame / sampleRate, qs[0], qs[1], 0, duration);
60 var g = linearRamp(frame / sampleRate, gains[0], gains[1], 0, durati on); 61 var g = linearRamp(frame / sampleRate, gains[0], gains[1], 0, durati on);
61 var d = linearRamp(frame / sampleRate, detunes[0], detunes[1], 0, du ration); 62 var d = linearRamp(frame / sampleRate, detunes[0], detunes[1], 0, du ration);
62 63
63 // Compute actual frequency parameter 64 // Compute actual frequency parameter
64 f = f * Math.pow(2, d / 1200); 65 f = f * Math.pow(2, d / 1200);
65 66
66 // Compute filter coefficients 67 // Compute filter coefficients
67 var coef = filterTypeFunction(f / (sampleRate / 2), q, g); 68 var coef = filterTypeFunction(f / (sampleRate / 2), q, g);
68 b0[k] = coef.b0; 69 b0[k] = coef.b0;
69 b1[k] = coef.b1; 70 b1[k] = coef.b1;
70 b2[k] = coef.b2; 71 b2[k] = coef.b2;
71 a1[k] = coef.a1; 72 a1[k] = coef.a1;
72 a2[k] = coef.a2; 73 a2[k] = coef.a2;
73 ++k; 74 ++k;
74 } 75 }
75 76
77 // Fill the rest of the arrays with the constant value to the end of
78 // the rendering duration.
79 b0.fill(b0[endFrame], endFrame + 1);
80 b1.fill(b1[endFrame], endFrame + 1);
81 b2.fill(b2[endFrame], endFrame + 1);
82 a1.fill(a1[endFrame], endFrame + 1);
83 a2.fill(a2[endFrame], endFrame + 1);
84
76 return {b0: b0, b1: b1, b2: b2, a1: a1, a2: a2}; 85 return {b0: b0, b1: b1, b2: b2, a1: a1, a2: a2};
77 } 86 }
78 87
79 // Apply the given time-varying biquad filter to the given signal, |signal |. |coef| should be 88 // Apply the given time-varying biquad filter to the given signal, |signal |. |coef| should be
80 // the time-varying coefficients of the filter, as returned by |generateFi lterCoefficients|. 89 // the time-varying coefficients of the filter, as returned by |generateFi lterCoefficients|.
81 function timeVaryingFilter(signal, coef) { 90 function timeVaryingFilter(signal, coef) {
82 var length = signal.length; 91 var length = signal.length;
83 // Use double precision for the internal computations. 92 // Use double precision for the internal computations.
84 var y = new Float64Array(length); 93 var y = new Float64Array(length);
85 94
(...skipping 27 matching lines...) Expand all
113 f.connect(context.destination); 122 f.connect(context.destination);
114 123
115 src.start(); 124 src.start();
116 125
117 return {filter: f, source: b}; 126 return {filter: f, source: b};
118 } 127 }
119 128
120 function createFilterVerifier(filterCreator, threshold, parameters, input, message) { 129 function createFilterVerifier(filterCreator, threshold, parameters, input, message) {
121 return function (resultBuffer) { 130 return function (resultBuffer) {
122 var actual = resultBuffer.getChannelData(0); 131 var actual = resultBuffer.getChannelData(0);
123 var coefs = generateFilterCoefficients(filterCreator, parameters, rend erDuration); 132 var coefs = generateFilterCoefficients(filterCreator, parameters, rend erDuration / 2);
hongchan 2016/06/20 16:14:40 Why half?
Raymond Toy 2016/06/20 16:57:56 Actually, any value less than renderDuration - 128
124 133
125 reference = timeVaryingFilter(input, coefs); 134 reference = timeVaryingFilter(input, coefs);
126 135
127 Should(message, actual).beCloseToArray(reference, threshold); 136 Should(message, actual, {
137 verbose: true
138 }).beCloseToArray(reference, threshold);
128 }; 139 };
129 } 140 }
130 141
131 // Automate just the frequency parameter. A bandpass filter is used where the center 142 // Automate just the frequency parameter. A bandpass filter is used where the center
132 // frequency is swept across the source (which is a simple tone). 143 // frequency is swept across the source (which is a simple tone).
133 audit.defineTask("automate-freq", function (done) { 144 audit.defineTask("automate-freq", function (done) {
134 var context = new OfflineAudioContext(1, renderDuration * sampleRate, sa mpleRate); 145 var context = new OfflineAudioContext(1, renderDuration * sampleRate, sa mpleRate);
135 146
136 // Center frequency of bandpass filter and also the frequency of the tes t tone. 147 // Center frequency of bandpass filter and also the frequency of the tes t tone.
137 var centerFreq = 10*440; 148 var centerFreq = 10*440;
138 149
139 // Sweep the frequency +/- 9*440 Hz from the center. This should cause the output to low at 150 // Sweep the frequency +/- 9*440 Hz from the center. This should cause the output to low at
hongchan 2016/06/20 16:14:40 Now it's 5, not 9.
Raymond Toy 2016/06/20 16:57:56 Oops. I'll fix the comment. This change is to ke
140 // the beginning and end of the test where the done is outside the pass band of the filter, 151 // the beginning and end of the test where the done is outside the pass band of the filter,
141 // but high in the center where the tone is near the center of the pass band. 152 // but high in the center where the tone is near the center of the pass band.
142 var parameters = { 153 var parameters = {
143 freq: [centerFreq - 9*440, centerFreq + 9*440] 154 freq: [centerFreq - 5*440, centerFreq + 5*440]
144 } 155 }
145 var graph = configureGraph(context, centerFreq); 156 var graph = configureGraph(context, centerFreq);
146 var f = graph.filter; 157 var f = graph.filter;
147 var b = graph.source; 158 var b = graph.source;
148 159
149 f.type = "bandpass"; 160 f.type = "bandpass";
150 f.frequency.setValueAtTime(parameters.freq[0], 0); 161 f.frequency.setValueAtTime(parameters.freq[0], 0);
151 f.frequency.linearRampToValueAtTime(parameters.freq[1], renderDuration); 162 f.frequency.linearRampToValueAtTime(parameters.freq[1], renderDuration / 2);
152 163
153 context.startRendering() 164 context.startRendering()
154 .then(createFilterVerifier(createBandpassFilter, 5e-5, parameters, b.g etChannelData(0), 165 .then(createFilterVerifier(createBandpassFilter, 4.8429e-6, parameters , b.getChannelData(0),
155 "Output of bandpass filter with frequency automation")) 166 "Output of bandpass filter with frequency automation"))
156 .then(done); 167 .then(done);
157 }); 168 });
158 169
159 // Automate just the Q parameter. A bandpass filter is used where the Q o f the filter is 170 // Automate just the Q parameter. A bandpass filter is used where the Q o f the filter is
160 // swept. 171 // swept.
161 audit.defineTask("automate-q", function (done) { 172 audit.defineTask("automate-q", function (done) {
162 var context = new OfflineAudioContext(1, renderDuration * sampleRate, sa mpleRate); 173 var context = new OfflineAudioContext(1, renderDuration * sampleRate, sa mpleRate);
163 174
164 // The frequency of the test tone. 175 // The frequency of the test tone.
165 var centerFreq = 440; 176 var centerFreq = 440;
166 177
167 // Sweep the Q paramter between 1 and 200. This will cause the output o f the filter to pass 178 // Sweep the Q paramter between 1 and 200. This will cause the output o f the filter to pass
168 // most of the tone at the beginning to passing less of the tone at the end. This is 179 // most of the tone at the beginning to passing less of the tone at the end. This is
169 // because we set center frequency of the bandpass filter to be slightly off from the actual 180 // because we set center frequency of the bandpass filter to be slightly off from the actual
170 // tone. 181 // tone.
171 var parameters = { 182 var parameters = {
172 Q: [1, 200], 183 Q: [1, 200],
173 // Center frequency of the bandpass filter is just 25 Hz above the ton e frequency. 184 // Center frequency of the bandpass filter is just 25 Hz above the ton e frequency.
174 freq: [centerFreq + 25, centerFreq + 25] 185 freq: [centerFreq + 25, centerFreq + 25]
175 }; 186 };
176 var graph = configureGraph(context, centerFreq); 187 var graph = configureGraph(context, centerFreq);
177 var f = graph.filter; 188 var f = graph.filter;
178 var b = graph.source; 189 var b = graph.source;
179 190
180 f.type = "bandpass"; 191 f.type = "bandpass";
181 f.frequency.value = parameters.freq[0]; 192 f.frequency.value = parameters.freq[0];
182 f.Q.setValueAtTime(parameters.Q[0], 0); 193 f.Q.setValueAtTime(parameters.Q[0], 0);
183 f.Q.linearRampToValueAtTime(parameters.Q[1], renderDuration); 194 f.Q.linearRampToValueAtTime(parameters.Q[1], renderDuration / 2);
184 195
185 context.startRendering() 196 context.startRendering()
186 .then(createFilterVerifier(createBandpassFilter, 1.4e-6, parameters, b .getChannelData(0), 197 .then(createFilterVerifier(createBandpassFilter, 1.1062e-6, parameters , b.getChannelData(0),
187 "Output of bandpass filter with Q automation")) 198 "Output of bandpass filter with Q automation"))
188 .then(done); 199 .then(done);
189 }); 200 });
190 201
191 // Automate just the gain of the lowshelf filter. A test tone will be in the lowshelf part of 202 // Automate just the gain of the lowshelf filter. A test tone will be in the lowshelf part of
192 // the filter. The output will vary as the gain of the lowshelf is change d. 203 // the filter. The output will vary as the gain of the lowshelf is change d.
193 audit.defineTask("automate-gain", function (done) { 204 audit.defineTask("automate-gain", function (done) {
194 var context = new OfflineAudioContext(1, renderDuration * sampleRate, sa mpleRate); 205 var context = new OfflineAudioContext(1, renderDuration * sampleRate, sa mpleRate);
195 206
196 // Frequency of the test tone. 207 // Frequency of the test tone.
197 var centerFreq = 440; 208 var centerFreq = 440;
198 209
199 // Set the cutoff frequency of the lowshelf to be significantly higher t han the test tone. 210 // Set the cutoff frequency of the lowshelf to be significantly higher t han the test tone.
200 // Sweep the gain from 20 dB to -20 dB. (We go from 20 to -20 to easily verify that the 211 // Sweep the gain from 20 dB to -20 dB. (We go from 20 to -20 to easily verify that the
201 // filter didn't go unstable.) 212 // filter didn't go unstable.)
202 var parameters = { 213 var parameters = {
203 freq: [3500, 3500], 214 freq: [3500, 3500],
204 gain: [20, -20] 215 gain: [20, -20]
205 } 216 }
206 var graph = configureGraph(context, centerFreq); 217 var graph = configureGraph(context, centerFreq);
207 var f = graph.filter; 218 var f = graph.filter;
208 var b = graph.source; 219 var b = graph.source;
209 220
210 f.type = "lowshelf"; 221 f.type = "lowshelf";
211 f.frequency.value = parameters.freq[0]; 222 f.frequency.value = parameters.freq[0];
212 f.gain.setValueAtTime(parameters.gain[0], 0); 223 f.gain.setValueAtTime(parameters.gain[0], 0);
213 f.gain.linearRampToValueAtTime(parameters.gain[1], renderDuration); 224 f.gain.linearRampToValueAtTime(parameters.gain[1], renderDuration / 2);
214 225
215 context.startRendering() 226 context.startRendering()
216 .then(createFilterVerifier(createLowShelfFilter, 8e-6, parameters, b.g etChannelData(0), 227 .then(createFilterVerifier(createLowShelfFilter, 1.4306e-5, parameters , b.getChannelData(0),
217 "Output of lowshelf filter with gain automation")) 228 "Output of lowshelf filter with gain automation"))
218 .then(done); 229 .then(done);
219 }); 230 });
220 231
221 // Automate just the detune parameter. Basically the same test as for the frequncy parameter 232 // Automate just the detune parameter. Basically the same test as for the frequncy parameter
222 // but we just use the detune parameter to modulate the frequency paramete r. 233 // but we just use the detune parameter to modulate the frequency paramete r.
223 audit.defineTask("automate-detune", function (done) { 234 audit.defineTask("automate-detune", function (done) {
224 var context = new OfflineAudioContext(1, renderDuration * sampleRate, sa mpleRate); 235 var context = new OfflineAudioContext(1, renderDuration * sampleRate, sa mpleRate);
225 var centerFreq = 10*440; 236 var centerFreq = 10*440;
226 var parameters = { 237 var parameters = {
227 freq: [centerFreq, centerFreq], 238 freq: [centerFreq, centerFreq],
228 detune: [-10*1200, 10*1200] 239 detune: [-10*1200, 10*1200]
229 }; 240 };
230 var graph = configureGraph(context, centerFreq); 241 var graph = configureGraph(context, centerFreq);
231 var f = graph.filter; 242 var f = graph.filter;
232 var b = graph.source; 243 var b = graph.source;
233 244
234 f.type = "bandpass"; 245 f.type = "bandpass";
235 f.frequency.value = parameters.freq[0]; 246 f.frequency.value = parameters.freq[0];
236 f.detune.setValueAtTime(parameters.detune[0], 0); 247 f.detune.setValueAtTime(parameters.detune[0], 0);
237 f.detune.linearRampToValueAtTime(parameters.detune[1], renderDuration); 248 f.detune.linearRampToValueAtTime(parameters.detune[1], renderDuration / 2);
238 249
239 context.startRendering() 250 context.startRendering()
240 .then(createFilterVerifier(createBandpassFilter, 5e-6, parameters, b.g etChannelData(0), 251 .then(createFilterVerifier(createBandpassFilter, 2.9535e-5, parameters , b.getChannelData(0),
241 "Output of bandpass filter with detune automation")) 252 "Output of bandpass filter with detune automation"))
242 .then(done); 253 .then(done);
243 }); 254 });
244 255
245 // Automate all of the filter parameters at once. This is a basic check t hat everything is 256 // Automate all of the filter parameters at once. This is a basic check t hat everything is
246 // working. A peaking filter is used because it uses all of the parameter s. 257 // working. A peaking filter is used because it uses all of the parameter s.
247 audit.defineTask("automate-all", function (done) { 258 audit.defineTask("automate-all", function (done) {
248 var context = new OfflineAudioContext(1, renderDuration * sampleRate, sa mpleRate); 259 var context = new OfflineAudioContext(1, renderDuration * sampleRate, sa mpleRate);
249 var graph = configureGraph(context, 10*440); 260 var graph = configureGraph(context, 10*440);
250 var f = graph.filter; 261 var f = graph.filter;
251 var b = graph.source; 262 var b = graph.source;
252 263
253 // Sweep all of the filter parameters. These are pretty much arbitrary. 264 // Sweep all of the filter parameters. These are pretty much arbitrary.
254 var parameters = { 265 var parameters = {
255 freq: [10000, 100], 266 freq: [8000, 100],
256 Q: [f.Q.value, .0001], 267 Q: [f.Q.value, .0001],
257 gain: [f.gain.value, 20], 268 gain: [f.gain.value, 20],
258 detune: [2400, -2400] 269 detune: [2400, -2400]
259 }; 270 };
260 271
261 f.type = "peaking"; 272 f.type = "peaking";
262 // Set starting points for all parameters of the filter. Start at 10 kH z for the center 273 // Set starting points for all parameters of the filter. Start at 10 kH z for the center
263 // frequency, and the defaults for Q and gain. 274 // frequency, and the defaults for Q and gain.
264 f.frequency.setValueAtTime(parameters.freq[0], 0); 275 f.frequency.setValueAtTime(parameters.freq[0], 0);
265 f.Q.setValueAtTime(parameters.Q[0], 0); 276 f.Q.setValueAtTime(parameters.Q[0], 0);
266 f.gain.setValueAtTime(parameters.gain[0], 0); 277 f.gain.setValueAtTime(parameters.gain[0], 0);
267 f.detune.setValueAtTime(parameters.detune[0], 0); 278 f.detune.setValueAtTime(parameters.detune[0], 0);
268 279
269 // Linear ramp each parameter 280 // Linear ramp each parameter
270 f.frequency.linearRampToValueAtTime(parameters.freq[1], renderDuration); 281 f.frequency.linearRampToValueAtTime(parameters.freq[1], renderDuration / 2);
271 f.Q.linearRampToValueAtTime(parameters.Q[1], renderDuration); 282 f.Q.linearRampToValueAtTime(parameters.Q[1], renderDuration / 2);
272 f.gain.linearRampToValueAtTime(parameters.gain[1], renderDuration); 283 f.gain.linearRampToValueAtTime(parameters.gain[1], renderDuration / 2);
273 f.detune.linearRampToValueAtTime(parameters.detune[1], renderDuration); 284 f.detune.linearRampToValueAtTime(parameters.detune[1], renderDuration / 2);
hongchan 2016/06/20 16:14:40 If all the renderDuration is divided by 2, can we
274 285
275 context.startRendering() 286 context.startRendering()
276 .then(createFilterVerifier(createPeakingFilter, 3.3e-4, parameters, b. getChannelData(0), 287 .then(createFilterVerifier(createPeakingFilter, 3.1233e-4, parameters, b.getChannelData(0),
277 "Output of peaking filter with automation of all parameters")) 288 "Output of peaking filter with automation of all parameters"))
278 .then(done); 289 .then(done);
279 }); 290 });
280 291
281 // Test that modulation of the frequency parameter of the filter works. A sinusoid of 440 Hz 292 // Test that modulation of the frequency parameter of the filter works. A sinusoid of 440 Hz
282 // is the test signal that is applied to a bandpass biquad filter. The fr equency parameter of 293 // is the test signal that is applied to a bandpass biquad filter. The fr equency parameter of
283 // the filter is modulated by a sinusoid at 103 Hz, and the frequency modu lation varies from 294 // the filter is modulated by a sinusoid at 103 Hz, and the frequency modu lation varies from
284 // 116 to 412 Hz. (This test was taken from the description in 295 // 116 to 412 Hz. (This test was taken from the description in
285 // https://github.com/WebAudio/web-audio-api/issues/509#issuecomment-94731 355) 296 // https://github.com/WebAudio/web-audio-api/issues/509#issuecomment-94731 355)
286 audit.defineTask("modulation", function (done) { 297 audit.defineTask("modulation", function (done) {
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 b1[k] = c.b1; 343 b1[k] = c.b1;
333 b2[k] = c.b2; 344 b2[k] = c.b2;
334 a1[k] = c.a1; 345 a1[k] = c.a1;
335 a2[k] = c.a2; 346 a2[k] = c.a2;
336 } 347 }
337 reference = timeVaryingFilter(b.getChannelData(0), 348 reference = timeVaryingFilter(b.getChannelData(0),
338 {b0: b0, b1: b1, b2: b2, a1: a1, a2: a2}); 349 {b0: b0, b1: b1, b2: b2, a1: a1, a2: a2});
339 350
340 Should("Output of bandpass filter with sinusoidal modulation of ban dpass center frequency", 351 Should("Output of bandpass filter with sinusoidal modulation of ban dpass center frequency",
341 actual) 352 actual)
342 .beCloseToArray(reference, 4e-6); 353 .beCloseToArray(reference, 3.9787e-5);
343 }) 354 })
344 .then(done); 355 .then(done);
345 }); 356 });
346 357
347 // All done! 358 // All done!
348 audit.defineTask("finish", function (done) { 359 audit.defineTask("finish", function (done) {
349 finishJSTest(); 360 finishJSTest();
350 done(); 361 done();
351 }); 362 });
352 363
353 audit.runTasks(); 364 audit.runTasks();
354 </script> 365 </script>
355 </body> 366 </body>
356 </html> 367 </html>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698