Chromium Code Reviews| Index: third_party/WebKit/LayoutTests/webaudio/AudioParam/audioparam-cancel-and-hold.html |
| diff --git a/third_party/WebKit/LayoutTests/webaudio/AudioParam/audioparam-cancel-and-hold.html b/third_party/WebKit/LayoutTests/webaudio/AudioParam/audioparam-cancel-and-hold.html |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c904e6eb8e752d355b15a89c9eb049d161bcfbab |
| --- /dev/null |
| +++ b/third_party/WebKit/LayoutTests/webaudio/AudioParam/audioparam-cancel-and-hold.html |
| @@ -0,0 +1,470 @@ |
| +<!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.
|
| +<html> |
| + <head> |
| + <script src="../../resources/testharness.js"></script> |
| + <script src="../../resources/testharnessreport.js"></script> |
| + <script src="../resources/audit-util.js"></script> |
| + <script src="../resources/audit.js"></script> |
| + <title>Test CancelValuesAndHoldAtTime</title> |
| + </head> |
| + |
| + <body> |
| + <script> |
| + var sampleRate = 48000; |
| + var renderDuration = 0.5; |
| + |
| + var audit = Audit.createTaskRunner(); |
| + |
| + // The first few tasks test the cancellation of each relevant automation function. For the |
| + // test, a simple linear ramp from 0 to 1 is used to start things off. Then the automation to |
| + // be tested is scheduled and cancelled. |
| + |
| + audit.define("linear", function (task, should) { |
| + task.describe("Cancel linearRampToValueAtTime"); |
| + cancelTest(should, linearRampTest("linearRampToValueAtTime"), { |
| + valueThreshold: 8.3998e-5, |
| + curveThreshold: 0 |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + audit.define("exponential", function (task, should) { |
| + task.describe("Cancel exponentialRampAtTime"); |
| + // Cancel an exponential ramp. The thresholds are experimentally determined. |
| + cancelTest(should, function (g, v0, t0, cancelTime) { |
| + // Initialize values to 0. |
| + g[0].gain.setValueAtTime(0, 0); |
| + g[1].gain.setValueAtTime(0, 0); |
| + // Schedule a short linear ramp to start things off. |
| + g[0].gain.linearRampToValueAtTime(v0, t0); |
| + g[1].gain.linearRampToValueAtTime(v0, t0); |
| + |
| + // After the linear ramp, schedule an exponential ramp to the end. (This is the event |
| + // that will be be cancelled. |
| + var v1 = 0.001; |
| + var t1 = renderDuration; |
| + |
| + g[0].gain.exponentialRampToValueAtTime(v1, t1); |
| + g[1].gain.exponentialRampToValueAtTime(v1, t1); |
| + |
| + expectedConstant = Math.fround(v0 * Math.pow(v1 / v0, (cancelTime - t0) / (t1 - |
| + t0))); |
| + return { |
| + expectedConstant: expectedConstant, |
| + autoMessage: "exponentialRampToValue(" + v1 + ", " + t1 + ")", |
| + summary: "exponentialRampToValueAtTime", |
| + }; |
| + }, { |
| + valueThreshold: 1.8664e-6, |
| + curveThreshold: 5.9605e-8 |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + audit.define("setTarget", function (task, should) { |
| + task.describe("Cancel setTargetAtTime"); |
| + // Cancel a setTarget event. |
| + cancelTest(should, function (g, v0, t0, cancelTime) { |
| + // Initialize values to 0. |
| + g[0].gain.setValueAtTime(0, 0); |
| + g[1].gain.setValueAtTime(0, 0); |
| + // Schedule a short linear ramp to start things off. |
| + g[0].gain.linearRampToValueAtTime(v0, t0); |
| + g[1].gain.linearRampToValueAtTime(v0, t0); |
| + |
| + // At the end of the linear ramp, schedule a setTarget. (This is the event that will be |
| + // cancelled.) |
| + var v1 = 0; |
| + var t1 = t0; |
| + var timeConstant = 0.05; |
| + |
| + g[0].gain.setTargetAtTime(v1, t1, timeConstant); |
| + g[1].gain.setTargetAtTime(v1, t1, timeConstant); |
| + |
| + expectedConstant = Math.fround(v1 + (v0 - v1) * Math.exp(-(cancelTime - t0) / |
| + timeConstant)); |
| + return { |
| + expectedConstant: expectedConstant, |
| + autoMessage: "setTargetAtTime(" + v1 + ", " + t1 + ", " + timeConstant + ")", |
| + summary: "setTargetAtTime", |
| + }; |
| + }, { |
| + valueThreshold: 4.5267e-7, //1.1317e-7, |
| + curveThreshold: 0 |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + audit.define("setValueCurve", function (task, should) { |
| + task.describe("Cancel setValueCurveAtTime"); |
| + // Cancel a setValueCurve event. |
| + cancelTest(should, function (g, v0, t0, cancelTime) { |
| + // Initialize values to 0. |
| + g[0].gain.setValueAtTime(0, 0); |
| + g[1].gain.setValueAtTime(0, 0); |
| + // Schedule a short linear ramp to start things off. |
| + g[0].gain.linearRampToValueAtTime(v0, t0); |
| + g[1].gain.linearRampToValueAtTime(v0, t0); |
| + |
| + // After the linear ramp, schedule a setValuesCurve. (This is the event that will be |
| + // cancelled.) |
| + var v1 = 0; |
| + var duration = renderDuration - t0; |
| + |
| + // For simplicity, a 2-point curve so we get a linear interpolated result. |
| + var curve = Float32Array.from([v0, 0]); |
| + |
| + g[0].gain.setValueCurveAtTime(curve, t0, duration); |
| + g[1].gain.setValueCurveAtTime(curve, t0, duration); |
| + |
| + var index = Math.floor((curve.length - 1) / duration * (cancelTime - t0)); |
| + |
| + var curvePointsPerFrame = (curve.length - 1) / duration / sampleRate; |
| + var virtualIndex = (cancelTime - t0) * sampleRate * curvePointsPerFrame; |
| + |
| + var delta = virtualIndex - index; |
| + expectedConstant = curve[0] + (curve[1] - curve[0]) * delta; |
| + return { |
| + expectedConstant: expectedConstant, |
| + autoMessage: "setValueCurveAtTime([" + curve + "], " + t0 + ", " + duration + ")", |
| + summary: "setValueCurveAtTime", |
| + }; |
| + }, { |
| + valueThreshold: 9.5368e-9, |
| + curveThreshold: 0 |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + audit.define("setValueCurve after end", function (task, should) { |
| + task.describe("Cancel setValueCurveAtTime after the end"); |
| + cancelTest(should, function (g, v0, t0, cancelTime) { |
| + // Initialize values to 0. |
| + g[0].gain.setValueAtTime(0, 0); |
| + g[1].gain.setValueAtTime(0, 0); |
| + // Schedule a short linear ramp to start things off. |
| + g[0].gain.linearRampToValueAtTime(v0, t0); |
| + g[1].gain.linearRampToValueAtTime(v0, t0); |
| + |
| + // After the linear ramp, schedule a setValuesCurve. (This is the event that will be |
| + // cancelled.) Make sure the curve ends before the cancellation time. |
| + var v1 = 0; |
| + var duration = cancelTime - t0 - 0.125; |
| + |
| + // For simplicity, a 2-point curve so we get a linear interpolated result. |
| + var curve = Float32Array.from([v0, 0]); |
| + |
| + g[0].gain.setValueCurveAtTime(curve, t0, duration); |
| + g[1].gain.setValueCurveAtTime(curve, t0, duration); |
| + |
| + expectedConstant = curve[1]; |
| + return { |
| + expectedConstant: expectedConstant, |
| + autoMessage: "setValueCurveAtTime([" + curve + "], " + t0 + ", " + duration + ")", |
| + summary: "setValueCurveAtTime", |
| + }; |
| + }, { |
| + valueThreshold: 0, |
| + curveThreshold: 0 |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + // Special case where we schedule a setTarget and there is no earlier automation event. This |
| + // tests that we pick up the starting point correctly from the last setting of the AudioParam |
| + // value attribute. |
| + |
| + |
| + audit.define("initial setTarget", function (task, should) { |
| + task.describe("Cancel with initial setTargetAtTime"); |
| + cancelTest(should, function (g, v0, t0, cancelTime) { |
| + var t0 = 0; |
| + var v1 = 0; |
| + var timeConstant = 0.1; |
| + g[0].gain.value = 1; |
| + g[0].gain.setTargetAtTime(v1, t0, timeConstant); |
| + g[1].gain.value = 1; |
| + g[1].gain.setTargetAtTime(v1, t0, timeConstant); |
| + |
| + var expectedConstant = Math.fround(v1 + (v0 - v1) * Math.exp(-(cancelTime - t0) / |
| + timeConstant)); |
| + |
| + return { |
| + expectedConstant: expectedConstant, |
| + autoMessage: "setTargetAtTime(" + v1 + ", " + t0 + ", " + timeConstant + ")", |
| + summary: "Initial setTargetAtTime", |
| + }; |
| + }, { |
| + valueThreshold: 1.0892e-6, //9.0767e-7, |
| + curveThreshold: 0 |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + // Test automations scheduled after the call to cancelValuesAndHoldAtTime. Very similar to the |
| + // above tests, but we also schedule an event after cancelValuesAndHoldAtTime and verify that |
| + // curve after cancellation has the correct values. |
| + |
| + audit.define("post cancel: Linear", function (task, should) { |
| + // Run the cancel test using a linearRamp as the event to be cancelled. Then schedule |
| + // another linear ramp after the cancellation. |
| + task.describe("LinearRamp after cancelling"); |
| + cancelTest(should, linearRampTest("Post cancellation linearRampToValueAtTime"), { |
| + valueThreshold: 8.3998e-5, |
| + curveThreshold: 0 |
| + }, function (g, cancelTime, expectedConstant) { |
| + // Schedule the linear ramp on g[0], and do the same for g[2], using the starting point |
| + // given by expectedConstant. |
| + var v2 = 2; |
| + var t2 = cancelTime + 0.125; |
| + g[0].gain.linearRampToValueAtTime(v2, t2); |
| + g[2].gain.setValueAtTime(expectedConstant, cancelTime); |
| + g[2].gain.linearRampToValueAtTime(v2, t2); |
| + return { |
| + constantEndTime: cancelTime, |
| + message: "Post linearRamp(" + v2 + ", " + t2 + ")" |
| + }; |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + audit.define("post cancel: Exponential", function (task, should) { |
| + task.describe("ExponentialRamp after cancelling"); |
| + // Run the cancel test using a linearRamp as the event to be cancelled. Then schedule |
| + // an exponential ramp after the cancellation. |
| + cancelTest(should, linearRampTest("Post cancel exponentialRampToValueAtTime"), { |
| + valueThreshold: 8.3998e-5, |
| + curveThreshold: 0 |
| + }, function (g, cancelTime, expectedConstant) { |
| + // Schedule the exponential ramp on g[0], and do the same for g[2], using the starting |
| + // point given by expectedConstant. |
| + var v2 = 2; |
| + var t2 = cancelTime + 0.125; |
| + g[0].gain.exponentialRampToValueAtTime(v2, t2); |
| + g[2].gain.setValueAtTime(expectedConstant, cancelTime); |
| + g[2].gain.exponentialRampToValueAtTime(v2, t2); |
| + return { |
| + constantEndTime: cancelTime, |
| + message: "Post exponentialRamp(" + v2 + ", " + t2 + ")" |
| + }; |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + audit.define("post cancel: ValueCurve", function (task, should) { |
| + // Run the cancel test using a linearRamp as the event to be cancelled. Then schedule |
| + // a setValueCurve after the cancellation. |
| + cancelTest(should, linearRampTest("Post cancel setValueCurveAtTime"), { |
| + valueThreshold: 8.3998e-5, |
| + curveThreshold: 0 |
| + }, function (g, cancelTime, expectedConstant) { |
| + // Schedule the exponential ramp on g[0], and do the same for g[2], using the starting |
| + // point given by expectedConstant. |
| + var t2 = cancelTime + 0.125; |
| + var duration = 0.125; |
| + var curve = Float32Array.from([.125, 2]); |
| + g[0].gain.setValueCurveAtTime(curve, t2, duration); |
| + g[2].gain.setValueAtTime(expectedConstant, cancelTime); |
| + g[2].gain.setValueCurveAtTime(curve, t2, duration); |
| + return { |
| + constantEndTime: cancelTime, |
| + message: "Post setValueCurve([" + curve + "], " + t2 + ", " + duration + ")", |
| + errorThreshold: 8.3998e-5 |
| + }; |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + audit.define("post cancel: setTarget", function (task, should) { |
| + // Run the cancel test using a linearRamp as the event to be cancelled. Then schedule |
| + // a setTarget after the cancellation. |
| + cancelTest(should, linearRampTest("Post cancel setTargetAtTime"), { |
| + valueThreshold: 8.3998e-5, |
| + curveThreshold: 0 |
| + }, function (g, cancelTime, expectedConstant) { |
| + // Schedule the exponential ramp on g[0], and do the same for g[2], using the starting |
| + // point given by expectedConstant. |
| + var v2 = 0.125; |
| + var t2 = cancelTime + 0.125; |
| + var timeConstant = 0.1; |
| + g[0].gain.setTargetAtTime(v2, t2, timeConstant); |
| + g[2].gain.setValueAtTime(expectedConstant, cancelTime); |
| + g[2].gain.setTargetAtTime(v2, t2, timeConstant); |
| + return { |
| + constantEndTime: cancelTime + 0.125, |
| + message: "Post setTargetAtTime(" + v2 + ", " + t2 + ", " + timeConstant + ")", |
| + errorThreshold: 8.4037e-5 |
| + }; |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + audit.define("post cancel: setValue", function (task, should) { |
| + // Run the cancel test using a linearRamp as the event to be cancelled. Then schedule |
| + // a setTarget after the cancellation. |
| + cancelTest(should, linearRampTest("Post cancel setValueAtTime"), { |
| + valueThreshold: 8.3998e-5, |
| + curveThreshold: 0 |
| + }, function (g, cancelTime, expectedConstant) { |
| + // Schedule the exponential ramp on g[0], and do the same for g[2], using the starting |
| + // point given by expectedConstant. |
| + var v2 = 0.125; |
| + var t2 = cancelTime + 0.125; |
| + g[0].gain.setValueAtTime(v2, t2); |
| + g[2].gain.setValueAtTime(expectedConstant, cancelTime); |
| + g[2].gain.setValueAtTime(v2, t2); |
| + return { |
| + constantEndTime: cancelTime + 0.125, |
| + message: "Post setValueAtTime(" + v2 + ", " + t2 + ")" |
| + }; |
| + }).then(task.done.bind(task)); |
| + }); |
| + |
| + audit.run(); |
| + |
| + // Common function for doing a linearRamp test. This just does a linear ramp from 0 to v0 at |
| + // from time 0 to t0. Then another linear ramp is scheduled from v0 to 0 from time t0 to t1. |
| + // This is the ramp that is to be cancelled. |
| + function linearRampTest(message) { |
| + return function (g, v0, t0, cancelTime) { |
| + g[0].gain.setValueAtTime(0, 0); |
| + g[1].gain.setValueAtTime(0, 0); |
| + g[0].gain.linearRampToValueAtTime(v0, t0); |
| + g[1].gain.linearRampToValueAtTime(v0, t0); |
| + |
| + var v1 = 0; |
| + var t1 = renderDuration; |
| + g[0].gain.linearRampToValueAtTime(v1, t1); |
| + g[1].gain.linearRampToValueAtTime(v1, t1); |
| + |
| + expectedConstant = Math.fround(v0 + (v1 - v0) * (cancelTime - t0) / (t1 - t0)); |
| + |
| + return { |
| + expectedConstant: expectedConstant, |
| + autoMessage: "linearRampToValue(" + v1 + ", " + t1 + ")", |
| + summary: message, |
| + }; |
| + } |
| + } |
| + |
| + // 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.
|
| + // |
| + // |testFunction| is a function that generates the automation to be tested. It is given an |
| + // array of 3 gain nodes, the value and time of an initial linear ramp, and the time where the |
| + // cancellation should occur. The function must do the automations for the first two gain |
| + // nodes. It must return a dictionary with |expectedConstant| being the value at the |
| + // cancellation time, |autoMessage| for message to describe the test, and |summary| for |
| + // general summary message to be printed at the end of the test. |
| + // |
| + // |thresholdOptions| is a property bag that specifies the error threshold to |
| + // use. |thresholdOptions.valueThreshold| is the error threshold for comparing the actual |
| + // constant output after cancelling to the expected value. |thresholdOptions.curveThreshold| |
| + // is the error threshold for comparing the actual and expected automation curves before the |
| + // cancelation point. |
| + // |
| + // For cancellation tests, |postCancelTest| is a function that schedules some automation after |
| + // the cancellation. It takes 3 arguments: an array of the gain nodes, the cancellation time, |
| + // and the expected value at the cancellation time. This function must return a dictionary |
| + // consisting of |constantEndtime| indicating when the held constant from cancellation stops |
| + // being constant, |message| giving a summary of what automation is being used, and |
| + // |errorThreshold| that is the error threshold between the expected curve and the actual |
| + // curve. |
| + // |
| + function cancelTest(should, testerFunction, thresholdOptions, postCancelTest) { |
| + // Create a context with three channels. Channel 0 is the test channel containing the |
| + // actual output that includes the cancellation of events. Channel 1 is the expected data |
| + // upto the cancellation so we can verify the cancellation produced the correct result. |
| + // Channel 2 is for verifying events inserted after the cancellation so we can verify that |
| + // automations are correctly generated after the cancellation point. |
| + var context = new OfflineAudioContext(3, renderDuration * sampleRate, sampleRate); |
| + |
| + // Test source is a constant signal |
| + var src = context.createBufferSource(); |
| + src.buffer = createConstantBuffer(context, 1, 1); |
| + src.loop = true; |
| + |
| + // We'll do the automation tests with three gain nodes. One (g0) will have |
| + // cancelValuesAndHoldAtTime and the other (g1) will not. g1 is used as the expected result for |
| + // that automation up to the cancellation point. They should be the same. The third node |
| + // (g2) is used for testing automations inserted after the cancellation point, if any. g2 |
| + // is the expected result from the cancellation point to the end of the test. |
| + |
| + var g0 = context.createGain(); |
| + var g1 = context.createGain(); |
| + var g2 = context.createGain(); |
| + var v0 = 1; |
| + var t0 = 0.01; |
| + |
| + var cancelTime = renderDuration / 2; |
| + |
| + // Test automation here. The tester function is responsible for setting up the gain nodes |
| + // with the desired automation for testing. |
| + autoResult = testerFunction([g0, g1, g2], v0, t0, cancelTime); |
| + var expectedConstant = autoResult.expectedConstant; |
| + var autoMessage = autoResult.autoMessage; |
| + var summaryMessage = autoResult.summary; |
| + |
| + // Cancel scheduled events somewhere in the middle of the test automation. |
| + g0.gain.cancelValuesAndHoldAtTime(cancelTime); |
| + |
| + var constantEndTime; |
| + if (postCancelTest) { |
| + postResult = postCancelTest([g0, g1, g2], cancelTime, expectedConstant); |
| + constantEndTime = postResult.constantEndTime; |
| + } |
| + |
| + // Connect everything together (with a merger to make a two-channel result). Channel 0 is |
| + // the test (with cancelValuesAndHoldAtTime) and channel 1 is the reference (without |
| + // cancelValuesAndHoldAtTime). Channel 1 is used to verify that everything up to the |
| + // cancellation has the correct values. |
| + src.connect(g0); |
| + src.connect(g1); |
| + src.connect(g2); |
| + var merger = context.createChannelMerger(3); |
| + g0.connect(merger, 0, 0); |
| + g1.connect(merger, 0, 1); |
| + g2.connect(merger, 0, 2); |
| + merger.connect(context.destination); |
| + |
| + // Go! |
| + src.start(); |
| + |
| + return context.startRendering().then(function (buffer) { |
| + var actual = buffer.getChannelData(0); |
| + var expected = buffer.getChannelData(1); |
| + |
| + // The actual output should be a constant from the cancel time to the end. We use the |
| + // last value of the actual output as the constant, but we also want to compare that with |
| + // what we thought it should really be. |
| + |
| + var cancelFrame = Math.ceil(cancelTime * sampleRate); |
| + |
| + // Verify that the curves up to the cancel time are "identical". The should be but |
| + // round-off may make them differ slightly due to the way cancelling is done. |
| + var endFrame = Math.floor(cancelTime * sampleRate); |
| + should(actual.slice(0, endFrame), |
| + autoMessage + " up to time " + cancelTime) |
| + .beCloseToArray(expected.slice(0, endFrame), {absoluteThreshold: thresholdOptions.curveThreshold}); |
| + |
| + // Verify the output after the cancellation is a constant. |
| + var actualTail; |
| + |
| + if (postCancelTest) { |
| + var constantEndFrame = Math.ceil(constantEndTime * sampleRate); |
| + actualTail = actual.slice(cancelFrame, constantEndFrame); |
| + } else { |
| + actualTail = actual.slice(cancelFrame); |
| + } |
| + |
| + var actualConstant = actual[cancelFrame]; |
| + |
| + should(actualTail, "Cancelling " + autoMessage + " at time " + cancelTime) |
| + .beConstantValueOf(actualConstant); |
| + |
| + // Verify that the constant is the value we expect. |
| + should(actualConstant, "Expected value for cancelling " + autoMessage + " at time " + |
| + cancelTime) |
| + .beCloseTo(expectedConstant, {threshold: thresholdOptions.valueThreshold}); |
| + |
| + // Verify the curve after the constantEndTime matches our expectations. |
| + if (postCancelTest) { |
| + var c2 = buffer.getChannelData(2); |
| + should(actual.slice(constantEndFrame), postResult.message) |
| + .beCloseToArray(c2.slice(constantEndFrame), {absoluteThreshold: postResult.errorThreshold || 0}); |
| + } |
| + |
| + }); |
| + } |
| + </script> |
| + </body> |
| +</html> |