| 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..de4a6e2b55b29d59bef54d3cd5b8938f2af60e4a
|
| --- /dev/null
|
| +++ b/LayoutTests/webaudio/audioparam-setValueCurveAtTime-interpolation.html
|
| @@ -0,0 +1,254 @@
|
| +<!doctype html>
|
| +<html>
|
| + <head>
|
| + <title>Test Interpolation for AudioParam.setValueCurveAtTime</title>
|
| + <script src="../resources/js-test.js"></script>
|
| + <script src="resources/compatibility.js"></script>
|
| + <script src="resources/audio-testing.js"></script>
|
| + </head>
|
| +
|
| + <body>
|
| + <script>
|
| + description("Test Interpolation for AudioParam.setValueCurveAtTime");
|
| + window.jsTestIsAsync = true;
|
| +
|
| + // 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.
|
| +
|
| + // Choose a sample rate that is a multiple of 128, the rendering quantum size. This makes the
|
| + // 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();
|
| +
|
| + // Array of test configs. Each config must specify curveStartTime, curveDuration,
|
| + // curveLength, fullGainTime, maxErrorThreshold, and snrThreshold.
|
| + var testConfigs = [{
|
| + // 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: 5.961e-8,
|
| + snrThreshold: 172.746
|
| + }, {
|
| + // Increase the curve length
|
| + curveStartTime: 256 / sampleRate,
|
| + curveDuration: 300 / sampleRate,
|
| + curveLength: 100,
|
| + fullGainTime: 0.75,
|
| + maxErrorThreshold: 5.961e-8,
|
| + snrThreshold: 172.799
|
| + }, {
|
| + // 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: 5.961e-8,
|
| + snrThreshold: 155.310
|
| + }];
|
| +
|
| + // Creates a function based on the test config that is suitable for use by defineTask().
|
| + function createTaskFunction(config) {
|
| + return function (done) {
|
| + 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) {
|
| + 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(rtoy): Maybe allow more complicated curves?
|
| + 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) {
|
| + 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)
|
| + 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)
|
| + expected[k] = stepSize * (k - curveStartFrame);
|
| +
|
| + // Hold it constant until the next event
|
| + for (; k < fullGainFrame; ++k)
|
| + expected[k] = curveValue;
|
| +
|
| + // Amplitude is one for the rest of the test.
|
| + for (; k < testDurationFrames; ++k)
|
| + expected[k] = 1;
|
| +
|
| + return expected;
|
| + }
|
| +
|
| + audit.runTasks();
|
| +
|
| + successfullyParsed = true;
|
| + </script>
|
| + </body>
|
| +</html>
|
|
|