OLD | NEW |
---|---|
(Empty) | |
1 <!doctype html> | |
hongchan
2016/12/20 19:48:59
Can we replace all 'var's to 'let'?
Raymond Toy
2016/12/20 22:40:09
Done.
| |
2 <html> | |
3 <head> | |
4 <script src="../../resources/testharness.js"></script> | |
5 <script src="../../resources/testharnessreport.js"></script> | |
6 <script src="../resources/audit-util.js"></script> | |
7 <script src="../resources/audit.js"></script> | |
8 <title>Test CancelValuesAndHoldAtTime</title> | |
9 </head> | |
10 | |
11 <body> | |
12 <script> | |
13 var sampleRate = 48000; | |
14 var renderDuration = 0.5; | |
15 | |
16 var audit = Audit.createTaskRunner(); | |
17 | |
18 // The first few tasks test the cancellation of each relevant automation f unction. For the | |
19 // test, a simple linear ramp from 0 to 1 is used to start things off. Th en the automation to | |
20 // be tested is scheduled and cancelled. | |
21 | |
22 audit.define("linear", function (task, should) { | |
23 task.describe("Cancel linearRampToValueAtTime"); | |
24 cancelTest(should, linearRampTest("linearRampToValueAtTime"), { | |
25 valueThreshold: 8.3998e-5, | |
26 curveThreshold: 0 | |
27 }).then(task.done.bind(task)); | |
28 }); | |
29 | |
30 audit.define("exponential", function (task, should) { | |
31 task.describe("Cancel exponentialRampAtTime"); | |
32 // Cancel an exponential ramp. The thresholds are experimentally determ ined. | |
33 cancelTest(should, function (g, v0, t0, cancelTime) { | |
34 // Initialize values to 0. | |
35 g[0].gain.setValueAtTime(0, 0); | |
36 g[1].gain.setValueAtTime(0, 0); | |
37 // Schedule a short linear ramp to start things off. | |
38 g[0].gain.linearRampToValueAtTime(v0, t0); | |
39 g[1].gain.linearRampToValueAtTime(v0, t0); | |
40 | |
41 // After the linear ramp, schedule an exponential ramp to the end. (T his is the event | |
42 // that will be be cancelled. | |
43 var v1 = 0.001; | |
44 var t1 = renderDuration; | |
45 | |
46 g[0].gain.exponentialRampToValueAtTime(v1, t1); | |
47 g[1].gain.exponentialRampToValueAtTime(v1, t1); | |
48 | |
49 expectedConstant = Math.fround(v0 * Math.pow(v1 / v0, (cancelTime - t0 ) / (t1 - | |
50 t0))); | |
51 return { | |
52 expectedConstant: expectedConstant, | |
53 autoMessage: "exponentialRampToValue(" + v1 + ", " + t1 + ")", | |
54 summary: "exponentialRampToValueAtTime", | |
55 }; | |
56 }, { | |
57 valueThreshold: 1.8664e-6, | |
58 curveThreshold: 5.9605e-8 | |
59 }).then(task.done.bind(task)); | |
60 }); | |
61 | |
62 audit.define("setTarget", function (task, should) { | |
63 task.describe("Cancel setTargetAtTime"); | |
64 // Cancel a setTarget event. | |
65 cancelTest(should, function (g, v0, t0, cancelTime) { | |
66 // Initialize values to 0. | |
67 g[0].gain.setValueAtTime(0, 0); | |
68 g[1].gain.setValueAtTime(0, 0); | |
69 // Schedule a short linear ramp to start things off. | |
70 g[0].gain.linearRampToValueAtTime(v0, t0); | |
71 g[1].gain.linearRampToValueAtTime(v0, t0); | |
72 | |
73 // At the end of the linear ramp, schedule a setTarget. (This is the event that will be | |
74 // cancelled.) | |
75 var v1 = 0; | |
76 var t1 = t0; | |
77 var timeConstant = 0.05; | |
78 | |
79 g[0].gain.setTargetAtTime(v1, t1, timeConstant); | |
80 g[1].gain.setTargetAtTime(v1, t1, timeConstant); | |
81 | |
82 expectedConstant = Math.fround(v1 + (v0 - v1) * Math.exp(-(cancelTime - t0) / | |
83 timeConstant)); | |
84 return { | |
85 expectedConstant: expectedConstant, | |
86 autoMessage: "setTargetAtTime(" + v1 + ", " + t1 + ", " + timeConsta nt + ")", | |
87 summary: "setTargetAtTime", | |
88 }; | |
89 }, { | |
90 valueThreshold: 4.5267e-7, //1.1317e-7, | |
91 curveThreshold: 0 | |
92 }).then(task.done.bind(task)); | |
93 }); | |
94 | |
95 audit.define("setValueCurve", function (task, should) { | |
96 task.describe("Cancel setValueCurveAtTime"); | |
97 // Cancel a setValueCurve event. | |
98 cancelTest(should, function (g, v0, t0, cancelTime) { | |
99 // Initialize values to 0. | |
100 g[0].gain.setValueAtTime(0, 0); | |
101 g[1].gain.setValueAtTime(0, 0); | |
102 // Schedule a short linear ramp to start things off. | |
103 g[0].gain.linearRampToValueAtTime(v0, t0); | |
104 g[1].gain.linearRampToValueAtTime(v0, t0); | |
105 | |
106 // After the linear ramp, schedule a setValuesCurve. (This is the even t that will be | |
107 // cancelled.) | |
108 var v1 = 0; | |
109 var duration = renderDuration - t0; | |
110 | |
111 // For simplicity, a 2-point curve so we get a linear interpolated res ult. | |
112 var curve = Float32Array.from([v0, 0]); | |
113 | |
114 g[0].gain.setValueCurveAtTime(curve, t0, duration); | |
115 g[1].gain.setValueCurveAtTime(curve, t0, duration); | |
116 | |
117 var index = Math.floor((curve.length - 1) / duration * (cancelTime - t 0)); | |
118 | |
119 var curvePointsPerFrame = (curve.length - 1) / duration / sampleRate; | |
120 var virtualIndex = (cancelTime - t0) * sampleRate * curvePointsPerFram e; | |
121 | |
122 var delta = virtualIndex - index; | |
123 expectedConstant = curve[0] + (curve[1] - curve[0]) * delta; | |
124 return { | |
125 expectedConstant: expectedConstant, | |
126 autoMessage: "setValueCurveAtTime([" + curve + "], " + t0 + ", " + d uration + ")", | |
127 summary: "setValueCurveAtTime", | |
128 }; | |
129 }, { | |
130 valueThreshold: 9.5368e-9, | |
131 curveThreshold: 0 | |
132 }).then(task.done.bind(task)); | |
133 }); | |
134 | |
135 audit.define("setValueCurve after end", function (task, should) { | |
136 task.describe("Cancel setValueCurveAtTime after the end"); | |
137 cancelTest(should, function (g, v0, t0, cancelTime) { | |
138 // Initialize values to 0. | |
139 g[0].gain.setValueAtTime(0, 0); | |
140 g[1].gain.setValueAtTime(0, 0); | |
141 // Schedule a short linear ramp to start things off. | |
142 g[0].gain.linearRampToValueAtTime(v0, t0); | |
143 g[1].gain.linearRampToValueAtTime(v0, t0); | |
144 | |
145 // After the linear ramp, schedule a setValuesCurve. (This is the even t that will be | |
146 // cancelled.) Make sure the curve ends before the cancellation time. | |
147 var v1 = 0; | |
148 var duration = cancelTime - t0 - 0.125; | |
149 | |
150 // For simplicity, a 2-point curve so we get a linear interpolated res ult. | |
151 var curve = Float32Array.from([v0, 0]); | |
152 | |
153 g[0].gain.setValueCurveAtTime(curve, t0, duration); | |
154 g[1].gain.setValueCurveAtTime(curve, t0, duration); | |
155 | |
156 expectedConstant = curve[1]; | |
157 return { | |
158 expectedConstant: expectedConstant, | |
159 autoMessage: "setValueCurveAtTime([" + curve + "], " + t0 + ", " + d uration + ")", | |
160 summary: "setValueCurveAtTime", | |
161 }; | |
162 }, { | |
163 valueThreshold: 0, | |
164 curveThreshold: 0 | |
165 }).then(task.done.bind(task)); | |
166 }); | |
167 | |
168 // Special case where we schedule a setTarget and there is no earlier auto mation event. This | |
169 // tests that we pick up the starting point correctly from the last settin g of the AudioParam | |
170 // value attribute. | |
171 | |
172 | |
173 audit.define("initial setTarget", function (task, should) { | |
174 task.describe("Cancel with initial setTargetAtTime"); | |
175 cancelTest(should, function (g, v0, t0, cancelTime) { | |
176 var t0 = 0; | |
177 var v1 = 0; | |
178 var timeConstant = 0.1; | |
179 g[0].gain.value = 1; | |
180 g[0].gain.setTargetAtTime(v1, t0, timeConstant); | |
181 g[1].gain.value = 1; | |
182 g[1].gain.setTargetAtTime(v1, t0, timeConstant); | |
183 | |
184 var expectedConstant = Math.fround(v1 + (v0 - v1) * Math.exp(-(cancelT ime - t0) / | |
185 timeConstant)); | |
186 | |
187 return { | |
188 expectedConstant: expectedConstant, | |
189 autoMessage: "setTargetAtTime(" + v1 + ", " + t0 + ", " + timeConsta nt + ")", | |
190 summary: "Initial setTargetAtTime", | |
191 }; | |
192 }, { | |
193 valueThreshold: 1.0892e-6, //9.0767e-7, | |
194 curveThreshold: 0 | |
195 }).then(task.done.bind(task)); | |
196 }); | |
197 | |
198 // Test automations scheduled after the call to cancelValuesAndHoldAtTime. Very similar to the | |
199 // above tests, but we also schedule an event after cancelValuesAndHoldAtT ime and verify that | |
200 // curve after cancellation has the correct values. | |
201 | |
202 audit.define("post cancel: Linear", function (task, should) { | |
203 // Run the cancel test using a linearRamp as the event to be cancelled. Then schedule | |
204 // another linear ramp after the cancellation. | |
205 task.describe("LinearRamp after cancelling"); | |
206 cancelTest(should, linearRampTest("Post cancellation linearRampToValueAt Time"), { | |
207 valueThreshold: 8.3998e-5, | |
208 curveThreshold: 0 | |
209 }, function (g, cancelTime, expectedConstant) { | |
210 // Schedule the linear ramp on g[0], and do the same for g[2], using t he starting point | |
211 // given by expectedConstant. | |
212 var v2 = 2; | |
213 var t2 = cancelTime + 0.125; | |
214 g[0].gain.linearRampToValueAtTime(v2, t2); | |
215 g[2].gain.setValueAtTime(expectedConstant, cancelTime); | |
216 g[2].gain.linearRampToValueAtTime(v2, t2); | |
217 return { | |
218 constantEndTime: cancelTime, | |
219 message: "Post linearRamp(" + v2 + ", " + t2 + ")" | |
220 }; | |
221 }).then(task.done.bind(task)); | |
222 }); | |
223 | |
224 audit.define("post cancel: Exponential", function (task, should) { | |
225 task.describe("ExponentialRamp after cancelling"); | |
226 // Run the cancel test using a linearRamp as the event to be cancelled. Then schedule | |
227 // an exponential ramp after the cancellation. | |
228 cancelTest(should, linearRampTest("Post cancel exponentialRampToValueAtT ime"), { | |
229 valueThreshold: 8.3998e-5, | |
230 curveThreshold: 0 | |
231 }, function (g, cancelTime, expectedConstant) { | |
232 // Schedule the exponential ramp on g[0], and do the same for g[2], us ing the starting | |
233 // point given by expectedConstant. | |
234 var v2 = 2; | |
235 var t2 = cancelTime + 0.125; | |
236 g[0].gain.exponentialRampToValueAtTime(v2, t2); | |
237 g[2].gain.setValueAtTime(expectedConstant, cancelTime); | |
238 g[2].gain.exponentialRampToValueAtTime(v2, t2); | |
239 return { | |
240 constantEndTime: cancelTime, | |
241 message: "Post exponentialRamp(" + v2 + ", " + t2 + ")" | |
242 }; | |
243 }).then(task.done.bind(task)); | |
244 }); | |
245 | |
246 audit.define("post cancel: ValueCurve", function (task, should) { | |
247 // Run the cancel test using a linearRamp as the event to be cancelled. Then schedule | |
248 // a setValueCurve after the cancellation. | |
249 cancelTest(should, linearRampTest("Post cancel setValueCurveAtTime"), { | |
250 valueThreshold: 8.3998e-5, | |
251 curveThreshold: 0 | |
252 }, function (g, cancelTime, expectedConstant) { | |
253 // Schedule the exponential ramp on g[0], and do the same for g[2], us ing the starting | |
254 // point given by expectedConstant. | |
255 var t2 = cancelTime + 0.125; | |
256 var duration = 0.125; | |
257 var curve = Float32Array.from([.125, 2]); | |
258 g[0].gain.setValueCurveAtTime(curve, t2, duration); | |
259 g[2].gain.setValueAtTime(expectedConstant, cancelTime); | |
260 g[2].gain.setValueCurveAtTime(curve, t2, duration); | |
261 return { | |
262 constantEndTime: cancelTime, | |
263 message: "Post setValueCurve([" + curve + "], " + t2 + ", " + durati on + ")", | |
264 errorThreshold: 8.3998e-5 | |
265 }; | |
266 }).then(task.done.bind(task)); | |
267 }); | |
268 | |
269 audit.define("post cancel: setTarget", function (task, should) { | |
270 // Run the cancel test using a linearRamp as the event to be cancelled. Then schedule | |
271 // a setTarget after the cancellation. | |
272 cancelTest(should, linearRampTest("Post cancel setTargetAtTime"), { | |
273 valueThreshold: 8.3998e-5, | |
274 curveThreshold: 0 | |
275 }, function (g, cancelTime, expectedConstant) { | |
276 // Schedule the exponential ramp on g[0], and do the same for g[2], us ing the starting | |
277 // point given by expectedConstant. | |
278 var v2 = 0.125; | |
279 var t2 = cancelTime + 0.125; | |
280 var timeConstant = 0.1; | |
281 g[0].gain.setTargetAtTime(v2, t2, timeConstant); | |
282 g[2].gain.setValueAtTime(expectedConstant, cancelTime); | |
283 g[2].gain.setTargetAtTime(v2, t2, timeConstant); | |
284 return { | |
285 constantEndTime: cancelTime + 0.125, | |
286 message: "Post setTargetAtTime(" + v2 + ", " + t2 + ", " + timeConst ant + ")", | |
287 errorThreshold: 8.4037e-5 | |
288 }; | |
289 }).then(task.done.bind(task)); | |
290 }); | |
291 | |
292 audit.define("post cancel: setValue", function (task, should) { | |
293 // Run the cancel test using a linearRamp as the event to be cancelled. Then schedule | |
294 // a setTarget after the cancellation. | |
295 cancelTest(should, linearRampTest("Post cancel setValueAtTime"), { | |
296 valueThreshold: 8.3998e-5, | |
297 curveThreshold: 0 | |
298 }, function (g, cancelTime, expectedConstant) { | |
299 // Schedule the exponential ramp on g[0], and do the same for g[2], us ing the starting | |
300 // point given by expectedConstant. | |
301 var v2 = 0.125; | |
302 var t2 = cancelTime + 0.125; | |
303 g[0].gain.setValueAtTime(v2, t2); | |
304 g[2].gain.setValueAtTime(expectedConstant, cancelTime); | |
305 g[2].gain.setValueAtTime(v2, t2); | |
306 return { | |
307 constantEndTime: cancelTime + 0.125, | |
308 message: "Post setValueAtTime(" + v2 + ", " + t2 + ")" | |
309 }; | |
310 }).then(task.done.bind(task)); | |
311 }); | |
312 | |
313 audit.run(); | |
314 | |
315 // Common function for doing a linearRamp test. This just does a linear r amp from 0 to v0 at | |
316 // from time 0 to t0. Then another linear ramp is scheduled from v0 to 0 from time t0 to t1. | |
317 // This is the ramp that is to be cancelled. | |
318 function linearRampTest(message) { | |
319 return function (g, v0, t0, cancelTime) { | |
320 g[0].gain.setValueAtTime(0, 0); | |
321 g[1].gain.setValueAtTime(0, 0); | |
322 g[0].gain.linearRampToValueAtTime(v0, t0); | |
323 g[1].gain.linearRampToValueAtTime(v0, t0); | |
324 | |
325 var v1 = 0; | |
326 var t1 = renderDuration; | |
327 g[0].gain.linearRampToValueAtTime(v1, t1); | |
328 g[1].gain.linearRampToValueAtTime(v1, t1); | |
329 | |
330 expectedConstant = Math.fround(v0 + (v1 - v0) * (cancelTime - t0) / (t 1 - t0)); | |
331 | |
332 return { | |
333 expectedConstant: expectedConstant, | |
334 autoMessage: "linearRampToValue(" + v1 + ", " + t1 + ")", | |
335 summary: message, | |
336 }; | |
337 } | |
338 } | |
339 | |
340 // Run the cancellation test. A set of automations is created and canceled . | |
hongchan
2016/12/20 19:48:58
It would be great if we can apply the 80-col rule
Raymond Toy
2016/12/20 22:40:09
Done.
| |
341 // | |
342 // |testFunction| is a function that generates the automation to be tested . It is given an | |
343 // array of 3 gain nodes, the value and time of an initial linear ramp, an d the time where the | |
344 // cancellation should occur. The function must do the automations for th e first two gain | |
345 // nodes. It must return a dictionary with |expectedConstant| being the v alue at the | |
346 // cancellation time, |autoMessage| for message to describe the test, and |summary| for | |
347 // general summary message to be printed at the end of the test. | |
348 // | |
349 // |thresholdOptions| is a property bag that specifies the error threshold to | |
350 // use. |thresholdOptions.valueThreshold| is the error threshold for compa ring the actual | |
351 // constant output after cancelling to the expected value. |thresholdOpti ons.curveThreshold| | |
352 // is the error threshold for comparing the actual and expected automation curves before the | |
353 // cancelation point. | |
354 // | |
355 // For cancellation tests, |postCancelTest| is a function that schedules s ome automation after | |
356 // the cancellation. It takes 3 arguments: an array of the gain nodes, th e cancellation time, | |
357 // and the expected value at the cancellation time. This function must re turn a dictionary | |
358 // consisting of |constantEndtime| indicating when the held constant from cancellation stops | |
359 // being constant, |message| giving a summary of what automation is being used, and | |
360 // |errorThreshold| that is the error threshold between the expected curve and the actual | |
361 // curve. | |
362 // | |
363 function cancelTest(should, testerFunction, thresholdOptions, postCancelTe st) { | |
364 // Create a context with three channels. Channel 0 is the test channel containing the | |
365 // actual output that includes the cancellation of events. Channel 1 is the expected data | |
366 // upto the cancellation so we can verify the cancellation produced the correct result. | |
367 // Channel 2 is for verifying events inserted after the cancellation so we can verify that | |
368 // automations are correctly generated after the cancellation point. | |
369 var context = new OfflineAudioContext(3, renderDuration * sampleRate, sa mpleRate); | |
370 | |
371 // Test source is a constant signal | |
372 var src = context.createBufferSource(); | |
373 src.buffer = createConstantBuffer(context, 1, 1); | |
374 src.loop = true; | |
375 | |
376 // We'll do the automation tests with three gain nodes. One (g0) will h ave | |
377 // cancelValuesAndHoldAtTime and the other (g1) will not. g1 is used as the expected result for | |
378 // that automation up to the cancellation point. They should be the sam e. The third node | |
379 // (g2) is used for testing automations inserted after the cancellation point, if any. g2 | |
380 // is the expected result from the cancellation point to the end of the test. | |
381 | |
382 var g0 = context.createGain(); | |
383 var g1 = context.createGain(); | |
384 var g2 = context.createGain(); | |
385 var v0 = 1; | |
386 var t0 = 0.01; | |
387 | |
388 var cancelTime = renderDuration / 2; | |
389 | |
390 // Test automation here. The tester function is responsible for setting up the gain nodes | |
391 // with the desired automation for testing. | |
392 autoResult = testerFunction([g0, g1, g2], v0, t0, cancelTime); | |
393 var expectedConstant = autoResult.expectedConstant; | |
394 var autoMessage = autoResult.autoMessage; | |
395 var summaryMessage = autoResult.summary; | |
396 | |
397 // Cancel scheduled events somewhere in the middle of the test automatio n. | |
398 g0.gain.cancelValuesAndHoldAtTime(cancelTime); | |
399 | |
400 var constantEndTime; | |
401 if (postCancelTest) { | |
402 postResult = postCancelTest([g0, g1, g2], cancelTime, expectedConstant ); | |
403 constantEndTime = postResult.constantEndTime; | |
404 } | |
405 | |
406 // Connect everything together (with a merger to make a two-channel resu lt). Channel 0 is | |
407 // the test (with cancelValuesAndHoldAtTime) and channel 1 is the refere nce (without | |
408 // cancelValuesAndHoldAtTime). Channel 1 is used to verify that everyth ing up to the | |
409 // cancellation has the correct values. | |
410 src.connect(g0); | |
411 src.connect(g1); | |
412 src.connect(g2); | |
413 var merger = context.createChannelMerger(3); | |
414 g0.connect(merger, 0, 0); | |
415 g1.connect(merger, 0, 1); | |
416 g2.connect(merger, 0, 2); | |
417 merger.connect(context.destination); | |
418 | |
419 // Go! | |
420 src.start(); | |
421 | |
422 return context.startRendering().then(function (buffer) { | |
423 var actual = buffer.getChannelData(0); | |
424 var expected = buffer.getChannelData(1); | |
425 | |
426 // The actual output should be a constant from the cancel time to the end. We use the | |
427 // last value of the actual output as the constant, but we also want t o compare that with | |
428 // what we thought it should really be. | |
429 | |
430 var cancelFrame = Math.ceil(cancelTime * sampleRate); | |
431 | |
432 // Verify that the curves up to the cancel time are "identical". The should be but | |
433 // round-off may make them differ slightly due to the way cancelling i s done. | |
434 var endFrame = Math.floor(cancelTime * sampleRate); | |
435 should(actual.slice(0, endFrame), | |
436 autoMessage + " up to time " + cancelTime) | |
437 .beCloseToArray(expected.slice(0, endFrame), {absoluteThreshold: thr esholdOptions.curveThreshold}); | |
438 | |
439 // Verify the output after the cancellation is a constant. | |
440 var actualTail; | |
441 | |
442 if (postCancelTest) { | |
443 var constantEndFrame = Math.ceil(constantEndTime * sampleRate); | |
444 actualTail = actual.slice(cancelFrame, constantEndFrame); | |
445 } else { | |
446 actualTail = actual.slice(cancelFrame); | |
447 } | |
448 | |
449 var actualConstant = actual[cancelFrame]; | |
450 | |
451 should(actualTail, "Cancelling " + autoMessage + " at time " + cancelT ime) | |
452 .beConstantValueOf(actualConstant); | |
453 | |
454 // Verify that the constant is the value we expect. | |
455 should(actualConstant, "Expected value for cancelling " + autoMessage + " at time " + | |
456 cancelTime) | |
457 .beCloseTo(expectedConstant, {threshold: thresholdOptions.valueThres hold}); | |
458 | |
459 // Verify the curve after the constantEndTime matches our expectations . | |
460 if (postCancelTest) { | |
461 var c2 = buffer.getChannelData(2); | |
462 should(actual.slice(constantEndFrame), postResult.message) | |
463 .beCloseToArray(c2.slice(constantEndFrame), {absoluteThreshold: p ostResult.errorThreshold || 0}); | |
464 } | |
465 | |
466 }); | |
467 } | |
468 </script> | |
469 </body> | |
470 </html> | |
OLD | NEW |