Chromium Code Reviews| Index: LayoutTests/webaudio/audioparam-setValueCurveAtTime-interpolation.html |
| diff --git a/LayoutTests/webaudio/audioparam-setValueCurveAtTime-interpolation.html b/LayoutTests/webaudio/audioparam-setValueCurveAtTime-interpolation.html |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..17a626e6818eb38c5a4a6ec1461b3a33fb93174a |
| --- /dev/null |
| +++ b/LayoutTests/webaudio/audioparam-setValueCurveAtTime-interpolation.html |
| @@ -0,0 +1,276 @@ |
| +<!doctype html> |
| +<html> |
| + <head> |
| + <title>Test Interpolation for AudioParam.setValueCurveAtTime</title> |
| + <script src="resources/compatibility.js"></script> |
| + <script src="resources/audio-testing.js"></script> |
| + <script src="../resources/js-test.js"></script> |
|
hongchan
2015/08/07 17:32:51
js-test.js should be the first.
Raymond Toy
2015/08/07 18:58:32
Done.
|
| + </head> |
| + |
| + <body> |
| + <script> |
| + description("Test Interpolation for AudioParam.setValueCurveAtTime"); |
| + |
| + // Play a constant signal through a gain node that is automated using setValueCurveAtTime with |
| + // a 2-element curve. The output should be a linear change. |
| + |
| + // Chose a sample rate that is a multiple of 128, the rendering quantum size. This makes the |
|
hongchan
2015/08/07 17:32:51
Chose -> Choose
Raymond Toy
2015/08/07 18:58:32
Done.
|
| + // math work out to be nice numbers. |
| + var sampleRate = 25600; |
| + var testDurationSec = 1; |
| + var testDurationFrames = testDurationSec * sampleRate; |
| + |
| + // Where the curve starts and its duration. This MUST be less than the total rendering time. |
| + var curveStartTime = 256 / sampleRate; |
| + var curveDuration = 300 / sampleRate;; |
| + var curveValue = 0.75; |
| + |
| + // At this time, the gain node goes to gain 1. This is used to make sure the value curve is |
| + // propagated correctly until the next event. |
| + var fullGainTime = 0.75; |
| + |
| + // Thresholds use to determine if the test passes; these are experimentally determined. The |
| + // SNR between the actual and expected result should be at least |snrThreshold|. The maximum |
| + // difference betwen them should not exceed |maxErrorThreshold|. |
| + var snrThreshold = 10000; |
| + var maxErrorThreshold = 0; |
| + |
| + var context; |
| + var actualResult; |
| + var expectedResult; |
| + |
| + var audit = Audit.createTaskRunner(); |
| + |
| + audit.defineTask("initialize", function (done) { |
| + window.jsTestIsAsync = true; |
|
hongchan
2015/08/07 17:32:51
This can be done right after description() method.
Raymond Toy
2015/08/07 18:58:33
Done. And initialize task deleted.
|
| + done(); |
| + }); |
| + |
| + |
| + // Array of test configs. Each config must specify curveStartTime, curveDuration, |
| + // curveLength, fullGainTime, maxErrorThreshold, and snrThreshold. |
| + var testConfigs = [ |
|
hongchan
2015/08/07 17:32:51
This can be re-written:
var testConfig = [{
/
Raymond Toy
2015/08/07 18:58:32
Done.
|
| + // The main test |
| + { |
| + curveStartTime: 256 / sampleRate, |
| + curveDuration: 300 / sampleRate, |
| + curveLength: 2, |
| + fullGainTime: 0.75, |
| + maxErrorThreshold: 0, |
| + snrThreshold: 10000 |
| + }, |
| + // Increase the curve length |
| + { |
| + curveStartTime: 256 / sampleRate, |
| + curveDuration: 300 / sampleRate, |
| + curveLength: 3, |
| + fullGainTime: 0.75, |
| + maxErrorThreshold: 0, |
| + snrThreshold: 10000 |
| + }, |
| + // Increase the curve length |
| + { |
| + curveStartTime: 256 / sampleRate, |
| + curveDuration: 300 / sampleRate, |
| + curveLength: 16, |
| + fullGainTime: 0.75, |
| + maxErrorThreshold: 0, |
| + snrThreshold: 10000 |
| + }, |
| + // Increase the curve length |
| + { |
| + curveStartTime: 256 / sampleRate, |
| + curveDuration: 300 / sampleRate, |
| + curveLength: 100, |
| + fullGainTime: 0.75, |
| + maxErrorThreshold: 0, |
| + snrThreshold: 10000 |
| + }, |
| + // Corner case with duration less than a frame! |
| + { |
| + curveStartTime: 256 / sampleRate, |
| + curveDuration: 0.25 / sampleRate, |
| + curveLength: 2, |
| + fullGainTime: 0.75, |
| + maxErrorThreshold: 0, |
| + snrThreshold: 10000 |
| + }, |
| + // Short duration test |
| + { |
| + curveStartTime: 256 / sampleRate, |
| + curveDuration: 2 / sampleRate, |
| + curveLength: 2, |
| + fullGainTime: 0.75, |
| + maxErrorThreshold: 0, |
| + snrThreshold: 10000 |
| + }, |
| + // Short duration test with many points. |
| + { |
| + curveStartTime: 256 / sampleRate, |
| + curveDuration: 2 / sampleRate, |
| + curveLength: 8, |
| + fullGainTime: 0.75, |
| + maxErrorThreshold: 0, |
| + snrThreshold: 10000 |
| + }, |
| + // Long duration, big curve |
| + { |
| + curveStartTime: 256 / sampleRate, |
| + curveDuration: .5, |
| + curveLength: 1000, |
| + fullGainTime: 0.75, |
| + maxErrorThreshold: 0, |
| + snrThreshold: 10000 |
| + } |
| + ]; |
| + |
| + // Creates a function based on the test config that is suitable for use by defineTask(). |
| + function createTaskFunction (config) { |
|
hongchan
2015/08/07 17:32:51
[Optional] Usually the named function does not hav
Raymond Toy
2015/08/07 18:58:33
Done.
|
| + return function (done) { |
|
hongchan
2015/08/07 17:32:51
The anonymous function can have a space between th
Raymond Toy
2015/08/07 18:58:33
Acknowledged.
|
| + runTest(config).then(done); |
| + }; |
| + } |
| + |
| + // Define a task for each config, in the order listed in testConfigs. |
| + for (var k = 0; k < testConfigs.length; ++k) { |
| + var config = testConfigs[k]; |
| + var name = k + ":curve=" + config.curveLength + ",duration=" + (config.curveDuration * sampleRate); |
| + audit.defineTask(name, createTaskFunction(config)); |
| + } |
| + |
| + // Must be the last defined task. |
| + audit.defineTask("end", function (done) { |
| + finishJSTest(); |
| + done(); |
| + }); |
| + |
| + function runTest (config) { |
|
hongchan
2015/08/07 17:32:51
No space between the name in the parentheses. The
Raymond Toy
2015/08/07 18:58:32
Done.
|
| + context = new OfflineAudioContext(1, testDurationFrames, sampleRate); |
| + |
| + // A constant audio source of value 1. |
| + var source = context.createBufferSource(); |
| + source.buffer = createConstantBuffer(context, 1, 1); |
| + source.loop = true; |
| + |
| + // The value curve for testing. Just to make things easy for testing, make the curve a |
| + // simple ramp up to curveValue. |
| + // TODO: Maybe allow more complicated curves? |
|
hongchan
2015/08/07 17:32:51
TODO(rtoy)
Raymond Toy
2015/08/07 18:58:32
Done.
|
| + var curve = new Float32Array(config.curveLength); |
| + for (var k = 0; k < config.curveLength; ++k) { |
| + curve[k] = curveValue / (config.curveLength - 1) * k; |
| + } |
| + |
| + // A gain node that is to be automated using setValueCurveAtTime. |
| + var gain = context.createGain(); |
| + gain.gain.value = 0; |
| + gain.gain.setValueCurveAtTime(curve, config.curveStartTime, config.curveDuration); |
| + // This is to verify that setValueCurveAtTime ends appropriately. |
| + gain.gain.setValueAtTime(1, config.fullGainTime); |
| + |
| + source.connect(gain); |
| + gain.connect(context.destination); |
| + source.start(); |
| + |
| + // Some consistency checks on the test parameters |
| + Should("Check: Curve end time", config.curveStartTime + config.curveDuration) |
| + .beLessThanOrEqualTo(testDurationSec); |
| + Should("Check: Full gain start time", config.fullGainTime).beLessThanOrEqualTo(testDurationSec); |
| + Should("Check: Full gain start time", config.fullGainTime).beGreaterThanOrEqualTo(config.curveStartTime + config.curveDuration); |
| + |
| + // Rock and roll! |
| + return context.startRendering().then(checkResult(config)); |
| + } |
| + |
| + // Return a function to check that the rendered result matches the expected result. |
| + function checkResult(config) |
|
hongchan
2015/08/07 17:32:51
Let's move the curly brace one line up.
Raymond Toy
2015/08/07 18:58:33
Done.
|
| + { |
| + return function (renderedBuffer) { |
| + var success = true; |
| + |
| + actualResult = renderedBuffer.getChannelData(0); |
| + expectedResult = computeExpectedResult(config); |
| + |
| + // Compute the SNR and max absolute difference between the actual and expected result. |
| + var SNR = 10*Math.log10(computeSNR(actualResult, expectedResult)); |
| + var maxDiff = -1; |
| + var posn = -1; |
| + |
| + for (var k = 0; k < actualResult.length; ++k) { |
| + var diff = Math.abs(actualResult[k] - expectedResult[k]); |
| + if (maxDiff < diff) { |
| + maxDiff = diff; |
| + posn = k; |
| + } |
| + } |
| + |
| + success = success && Should("SNR", SNR).beGreaterThanOrEqualTo(config.snrThreshold); |
| + |
| + if (maxDiff <= config.maxErrorThreshold) { |
| + testPassed("Max difference is less than or equal to " + config.maxErrorThreshold + "."); |
| + } else { |
| + testFailed("Max difference (" + maxDiff + ") NOT less than or equal to " + |
| + config.maxErrorThreshold + " at frame " + posn + "."); |
| + success = false; |
| + } |
| + |
| + var message = "Test: curve length = " + config.curveLength + "; duration frames = " + |
| + config.curveDuration * sampleRate + ".\n"; |
| + |
| + if (success) |
| + testPassed(message); |
| + else |
| + testFailed(message); |
| + } |
| + } |
| + |
| + // Compute the expected result based on the config settings. |
| + function computeExpectedResult(config) |
| + { |
| + // The automation curve starts at |curveStartTime| and has duration |curveDuration|. So, |
| + // the output should be zero until curveStartTime, linearly ramp up from there to |
| + // |curveValue|, and then be constant 1 from then to the end of the buffer. |
| + |
| + var expected = new Float32Array(testDurationFrames); |
| + |
| + var curveStartFrame = config.curveStartTime * sampleRate; |
| + var curveEndFrame = Math.floor((config.curveStartTime + config.curveDuration) * sampleRate); |
| + var fullGainFrame = config.fullGainTime * sampleRate; |
| + |
| + // TODO(crbug.com/517491): setValueAtTime is off by one rendering quantum. |
| + fullGainFrame += 128; |
| + |
| + var k; |
| + |
| + // Zero out the start. |
| + for (k = 0; k < curveStartFrame; ++k) { |
|
hongchan
2015/08/07 17:32:51
One-line loop block does not curly embraces around
Raymond Toy
2015/08/07 18:58:32
Done.
|
| + expected[k] = 0; |
| + } |
| + |
| + // Linearly ramp now. This assumes that the actual curve used is a linear ramp, even if |
| + // there are many curve points. |
| + var stepSize = curveValue / (config.curveDuration * sampleRate - 1); |
| + for (; k < curveEndFrame; ++k) { |
|
hongchan
2015/08/07 17:32:51
ditto.
Raymond Toy
2015/08/07 18:58:32
Done.
|
| + expected[k] = stepSize * (k - curveStartFrame); |
| + } |
| + |
| + // Hold it constant until the next event |
| + for (; k < fullGainFrame; ++k) { |
|
hongchan
2015/08/07 17:32:51
ditto.
Raymond Toy
2015/08/07 18:58:32
Done.
|
| + expected[k] = curveValue; |
| + } |
| + |
| + // Amplitude is one for the rest of the test. |
| + for (; k < testDurationFrames; ++k) { |
|
hongchan
2015/08/07 17:32:51
ditto.
Raymond Toy
2015/08/07 18:58:32
Done.
|
| + expected[k] = 1; |
| + } |
| + |
| + return expected; |
| + } |
| + |
| + //runTest(); |
|
hongchan
2015/08/07 17:32:51
Why is this commented out? Let's remove if this is
Raymond Toy
2015/08/07 18:58:33
Done.
|
| + |
| + audit.runTasks(); |
| + |
| + successfullyParsed = true; |
| + </script> |
| + </body> |
| +</html> |