summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface')
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html72
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html184
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/silent-inputs.html103
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-copy-curve.html100
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-limits.html110
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-simple.html61
-rw-r--r--testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper.html127
7 files changed, 757 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html
new file mode 100644
index 0000000000..7aa33ca5aa
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Test Constructor: WaveShaper
+ </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/webaudio/resources/audit-util.js"></script>
+ <script src="/webaudio/resources/audit.js"></script>
+ <script src="/webaudio/resources/audionodeoptions.js"></script>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let context;
+
+ let audit = Audit.createTaskRunner();
+
+ audit.define('initialize', (task, should) => {
+ context = initializeContext(should);
+ task.done();
+ });
+
+ audit.define('incorrect construction', (task, should) => {
+ testInvalidConstructor(should, 'WaveShaperNode', context);
+ task.done();
+ });
+
+ audit.define('valid default construction', (task, should) => {
+ let prefix = 'node0';
+ let node = testDefaultConstructor(should, 'WaveShaperNode', context, {
+ prefix: prefix,
+ numberOfInputs: 1,
+ numberOfOutputs: 1,
+ channelCount: 2,
+ channelCountMode: 'max',
+ channelInterpretation: 'speakers'
+ });
+
+ testDefaultAttributes(should, node, prefix, [
+ {name: 'curve', value: null}, {name: 'oversample', value: 'none'}
+ ]);
+
+ task.done();
+ });
+
+ audit.define('test AudioNodeOptions', (task, should) => {
+ testAudioNodeOptions(should, context, 'WaveShaperNode');
+ task.done();
+ });
+
+ audit.define('valid non-default', (task, should) => {
+ // Construct an WaveShaperNode with options
+ let options = {curve: Float32Array.from([1, 2, 3]), oversample: '4x'};
+ let node;
+
+ let message =
+ 'node1 = new WaveShaperNode(, ' + JSON.stringify(options) + ')';
+ should(() => {
+ node = new WaveShaperNode(context, options);
+ }, message).notThrow();
+ should(node.curve, 'node1.curve').beEqualToArray(options.curve);
+ should(node.oversample, 'node1.oversample')
+ .beEqualTo(options.oversample);
+
+ task.done();
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html
new file mode 100644
index 0000000000..d09cf78fd8
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html
@@ -0,0 +1,184 @@
+<!doctype html>
+<html>
+<head>
+ <title>WaveShaperNode interface - Curve tests | WebAudio</title>
+
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="log">
+ </div>
+
+ <script type="text/javascript">
+ var sampleRate=44100.0;
+ var tolerance=0.01;
+
+ /*
+ Testing that -1, 0 and +1 map correctly to curve (with 1:1 correlation)
+ =======================================================================
+ From the specification:
+ The input signal is nominally within the range -1 -> +1.
+ Each input sample within this range will index into the shaping curve with a signal level of zero corresponding
+ to the center value of the curve array.
+ */
+ (function() {
+ var threeElementCurve=[2.0, -3.0, 4.0];
+ var inputData=[-1.0, 0, 1.0];
+ var expectedData=[2.0, -3.0, 4.0];
+ executeTest(threeElementCurve, inputData, expectedData, "Testing that -1, 0 and +1 map correctly to curve (with 1:1 correlation)");
+ })();
+
+ /*
+ Testing interpolation (where inputs don't correlate directly to curve elements)
+ ===============================================================================
+ From the specification:
+ The implementation must perform linear interpolation between adjacent points in the curve.
+ */
+ (function() {
+ var threeElementCurve=[2.0, -3.0, 4.0];
+ var inputData=[-0.5, +0.5, +0.75];
+ var expectedData=[-0.5, +0.5, +2.25];
+ executeTest(threeElementCurve, inputData, expectedData, "Testing interpolation (where inputs don't correlate directly to curve elements)");
+ })();
+
+ /*
+ Testing out-of-range inputs (should be mapped to the first/last elements of the curve)
+ ======================================================================================
+ From the specification:
+ Any sample value less than -1 will correspond to the first value in the curve array.
+ Any sample value greater than +1 will correspond to the last value in the curve array.
+ */
+ (function() {
+ var threeElementCurve=[2.0, -3.0, 4.0];
+ var inputData=[-1.5, +1.5];
+ var expectedData=[2.0, 4.0];
+ executeTest(threeElementCurve, inputData, expectedData, "Testing out-of-range inputs (should be mapped to the first/last elements of the curve)");
+ })();
+
+ /*
+ Testing a 2-element curve (does not have a middle element)
+ ==========================================================
+ From the specification:
+ Each input sample within this range will index into the shaping curve with a signal level of zero corresponding
+ to the center value of the curve array.
+ The implementation must perform linear interpolation between adjacent points in the curve.
+ */
+ (function() {
+ var twoElementCurve=[2.0, -2.0];
+ var inputData=[-1.0, 0, 1.0];
+ var expectedData=[2.0, 0.0, -2.0];
+ executeTest(twoElementCurve, inputData, expectedData, "Testing a 2-element curve (does not have a middle element)");
+ })();
+
+ /*
+ Testing a 4-element curve (does not have a middle element)
+ ==========================================================
+ From the specification:
+ Each input sample within this range will index into the shaping curve with a signal level of zero corresponding
+ to the center value of the curve array.
+ The implementation must perform linear interpolation between adjacent points in the curve.
+ */
+ (function() {
+ var fourElementCurve=[1.0, 2.0, 4.0, 7.0];
+ var inputData=[-1.0, 0, 1.0];
+ var expectedData=[1.0, 3.0, 7.0];
+ executeTest(fourElementCurve, inputData, expectedData, "Testing a 4-element curve (does not have a middle element)");
+ })();
+
+ /*
+ Testing a huge curve
+ ====================
+ From the specification:
+ Each input sample within this range will index into the shaping curve with a signal level of zero corresponding
+ to the center value of the curve array.
+ */
+ (function() {
+ var bigCurve=[];
+ for(var i=0;i<=60000;i++) { bigCurve.push(i/3.5435); }
+ var inputData=[-1.0, 0, 1.0];
+ var expectedData=[bigCurve[0], bigCurve[30000], bigCurve[60000]];
+ executeTest(bigCurve, inputData, expectedData, "Testing a huge curve");
+ })();
+
+ /*
+ Testing single-element curve (boundary condition)
+ =================================================
+ From the specification:
+ Each input sample within this range will index into the shaping curve with a signal level of zero corresponding
+ to the center value of the curve array.
+ Any sample value less than -1 will correspond to the first value in the curve array.
+ Any sample value greater than +1 will correspond to the last value in the curve array.
+ The implementation must perform linear interpolation between adjacent points in the curve.
+ */
+
+ /*
+ Testing null curve (should return input values)
+ ===============================================
+ From the specification:
+ Initially the curve attribute is null, which means that the WaveShaperNode will pass its input to its output
+ without modification.
+ */
+ (function() {
+ var inputData=[-1.0, 0, 1.0, 2.0];
+ var expectedData=[-1.0, 0.0, 1.0, 2.0];
+ executeTest(null, inputData, expectedData, "Testing null curve (should return input values)");
+ })();
+
+ /**
+ * Function that does the actual testing (using an asynchronous test).
+ * @param {?Array.<number>} curveData - Array containing values for the WaveShaper curve.
+ * @param {!Array.<number>} inputData - Array containing values for the input stream.
+ * @param {!Array.<number>} expectedData - Array containing expected results for each of the corresponding inputs.
+ * @param {!string} testName - Name of the test case.
+ */
+ function executeTest(curveData, inputData, expectedData, testName) {
+ var stTest=async_test("WaveShaperNode - "+testName);
+ stTest.step(function() {
+
+ // Create offline audio context.
+ var ac=new OfflineAudioContext(1, inputData.length, sampleRate);
+
+ // Create the WaveShaper and its curve.
+ var waveShaper=ac.createWaveShaper();
+ if(curveData!=null) {
+ var curve=new Float32Array(curveData.length);
+ for(var i=0;i<curveData.length;i++) { curve[i]=curveData[i]; }
+ waveShaper.curve=curve;
+ }
+ waveShaper.connect(ac.destination);
+
+ // Create buffer containing the input values.
+ var inputBuffer=ac.createBuffer(1, Math.max(inputData.length, 2), sampleRate);
+ var d=inputBuffer.getChannelData(0);
+ for(var i=0;i<inputData.length;i++) { d[i]=inputData[i]; }
+
+ // Play the input buffer through the WaveShaper.
+ var src=ac.createBufferSource();
+ src.buffer=inputBuffer;
+ src.connect(waveShaper);
+ src.start();
+
+ // Test the outputs match the expected values.
+ ac.oncomplete=stTest.step_func_done(function(ev) {
+ var d=ev.renderedBuffer.getChannelData(0);
+
+ for(var i=0;i<expectedData.length;i++) {
+ var curveText="null";
+ if(curve!=null) {
+ if(curveData.length<20) {
+ curveText=curveData.join(",");
+ } else {
+ curveText="TooBigToDisplay ("+(curveData.length-1)+" elements)";
+ }
+ }
+ var comment="Input="+inputData[i]+", Curve=["+curveText+"] >>> ";
+ assert_approx_equals(d[i], expectedData[i], tolerance, comment);
+ }
+ });
+ ac.startRendering();
+ });
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/silent-inputs.html b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/silent-inputs.html
new file mode 100644
index 0000000000..45d2c9ad4b
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/silent-inputs.html
@@ -0,0 +1,103 @@
+<!doctype html>
+<html>
+ <head>
+ <title>
+ Test Silent Inputs to WaveShaperNode
+ </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/webaudio/resources/audit-util.js"></script>
+ <script src="/webaudio/resources/audit.js"></script>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let audit = Audit.createTaskRunner();
+ let sampleRate = 16000;
+
+ // Identity curve for the wave shaper: the input value is mapped directly
+ // to the output value.
+ let identityCurve = [-1, 0, 1];
+ let nonZeroCurve = [0.5, 0.5, 0.5];
+
+ audit.define(
+ {
+ label: 'test-0',
+ description: 'curve output is non-zero for silent inputs'
+ },
+ (task, should) => {
+ let {context, source, shaper} =
+ setupGraph(nonZeroCurve, sampleRate, sampleRate);
+
+ source.offset.setValueAtTime(0, 0);
+
+ context.startRendering()
+ .then(audioBuffer => {
+ should(
+ audioBuffer.getChannelData(0),
+ 'WaveShaper with silent inputs and curve ' +
+ JSON.stringify(shaper.curve))
+ .beConstantValueOf(0.5);
+ })
+ .then(() => task.done());
+ });
+
+ audit.define(
+ {
+ label: 'test-1',
+ description: '2x curve output is non-zero for silent inputs'
+ },
+ (task, should) => {
+ let {context, source, shaper} =
+ setupGraph(nonZeroCurve, sampleRate, sampleRate);
+
+ source.offset.setValueAtTime(0, 0);
+ shaper.overSample = '2x';
+
+ context.startRendering()
+ .then(audioBuffer => {
+ should(
+ audioBuffer.getChannelData(0),
+ 'WaveShaper with ' + shaper.overSample +
+ ' oversample, silent inputs, and curve ' +
+ JSON.stringify(shaper.curve))
+ .beConstantValueOf(0.5);
+ })
+ .then(() => task.done());
+ });
+
+ audit.define(
+ {
+ label: 'test-2',
+ description: 'curve output is non-zero for no inputs'
+ },
+ (task, should) => {
+ let {context, source, shaper} =
+ setupGraph(nonZeroCurve, sampleRate, sampleRate);
+
+ source.disconnect();
+
+ context.startRendering()
+ .then(audioBuffer => {
+ should(
+ audioBuffer.getChannelData(0),
+ 'WaveShaper with no inputs and curve ' +
+ JSON.stringify(shaper.curve))
+ .beConstantValueOf(0.5);
+ })
+ .then(() => task.done());
+ });
+
+ function setupGraph(curve, testFrames, sampleRate) {
+ let context = new OfflineAudioContext(1, testFrames, sampleRate);
+ let source = new ConstantSourceNode(context);
+ let shaper = new WaveShaperNode(context, {curve: curve});
+
+ source.connect(shaper).connect(context.destination);
+
+ return {context: context, source: source, shaper: shaper};
+ }
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-copy-curve.html b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-copy-curve.html
new file mode 100644
index 0000000000..e897ac08a1
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-copy-curve.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Test WaveShaper Copies Curve Data
+ </title>
+ <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>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ // Sample rate and number of frames are fairly arbitrary. We need to
+ // render, however, at least 384 frames. 1024 is a nice small value.
+ let sampleRate = 16000;
+ let renderFrames = 1024;
+
+ let audit = Audit.createTaskRunner();
+
+ audit.define(
+ {
+ label: 'test copying',
+ description: 'Modifying curve should not modify WaveShaper'
+ },
+ (task, should) => {
+ // Two-channel context; channel 0 contains the test data and channel
+ // 1 contains the expected result. Channel 1 has the normal
+ // WaveShaper output and channel 0 has the WaveShaper output with a
+ // modified curve.
+ let context = new OfflineAudioContext(2, renderFrames, sampleRate);
+
+ // Just use a default oscillator as the source. Doesn't really
+ // matter what we use.
+ let src = context.createOscillator();
+ src.type = 'sawtooth';
+
+ // Create the wave shapers: ws0 is the test shaper, and ws1 is the
+ // reference wave shaper.
+ let ws0 = context.createWaveShaper();
+ let ws1 = context.createWaveShaper();
+
+ // Wave shaper curves. Doesn't really matter what we use as long as
+ // it modifies the input in some way. Thus, keep it simple and just
+ // invert the input.
+ let desiredCurve = [1, 0, -1];
+ let curve0 = Float32Array.from(desiredCurve);
+ let curve1 = Float32Array.from(desiredCurve);
+
+ ws0.curve = curve0;
+ ws1.curve = curve1;
+
+ let merger = context.createChannelMerger(2);
+
+ // Connect the graph
+ src.connect(ws0);
+ src.connect(ws1);
+
+ ws0.connect(merger, 0, 0);
+ ws1.connect(merger, 0, 1);
+
+ merger.connect(context.destination);
+
+ // Let the context run for a bit and then modify the curve for ws0.
+ // Doesn't really matter what we modify the curve to as long as it's
+ // different.
+ context.suspend(256 / context.sampleRate)
+ .then(() => {
+ should(
+ () => {
+ curve0[0] = -0.5;
+ curve0[1] = 0.125;
+ curve0[2] = 0.75;
+ },
+ `Modifying curve array at time ${context.currentTime}`)
+ .notThrow();
+ })
+ .then(context.resume.bind(context));
+
+ src.start();
+
+ context.startRendering()
+ .then(function(renderedBuffer) {
+ let actual = renderedBuffer.getChannelData(0);
+ let expected = renderedBuffer.getChannelData(1);
+
+ // Modifying the wave shaper curve should not modify the
+ // output so the outputs from the two wave shaper nodes should
+ // be exactly identical.
+ should(actual, 'Output of WaveShaper with modified curve')
+ .beEqualToArray(expected);
+
+ })
+ .then(() => task.done());
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-limits.html b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-limits.html
new file mode 100644
index 0000000000..13e88be567
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-limits.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ waveshaper-limits.html
+ </title>
+ <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>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let audit = Audit.createTaskRunner();
+
+ let context;
+ let bufferData;
+ let outputData;
+ let reference;
+
+ let sampleRate = 48000;
+ // Must be odd so we have an exact middle point.
+ let testFrames = 23;
+ let scale = 1 / ((testFrames - 1) / 2 - 1);
+ // Number of decimal digits to print
+ let decimals = 6;
+ // Required accuracy
+ let diffThreshold = Math.pow(10, -decimals);
+
+ // Generate reference data
+ function generateReference() {
+ // The curve data is 0, 1, 0, and the input data is a ramp from -1+eps
+ // to 1+eps. Then the output is a ramp from 0 to 1 back to 0.
+ let ref = new Float32Array(testFrames);
+ let midPoint = (testFrames - 1) / 2;
+ // First sample is below -1 at -1-scale.
+ ref[0] = 0;
+ // Generate ramp up to the mid-point
+ for (let k = 0; k < midPoint; ++k) {
+ ref[k + 1] = k * scale;
+ }
+ // The value at the mid-point must be 1, from the curve
+ ref[midPoint] = 1;
+ // Generate a ramp from 1 down to 0
+ for (let k = midPoint; k < testFrames - 1; ++k) {
+ ref[k + 1] = 2 - k * scale;
+ }
+ // The last sample is out of range at 1+scale
+ ref[testFrames - 1] = 0;
+ return ref;
+ }
+
+ function checkResult(renderedBuffer, should) {
+ outputData = renderedBuffer.getChannelData(0);
+ reference = generateReference();
+ let success = true;
+ // Verify that every output value matches our expected reference value.
+ for (let k = 0; k < outputData.length; ++k) {
+ let diff = outputData[k] - reference[k];
+ should(
+ Math.abs(diff),
+ 'Max error mapping ' + bufferData[k].toFixed(decimals) + ' to ' +
+ outputData[k].toFixed(decimals))
+ .beLessThanOrEqualTo(diffThreshold);
+ }
+ }
+
+ audit.define(
+ {
+ label: 'test',
+ description:
+ 'WaveShaperNode including values outside the range of [-1,1]'
+ },
+ function(task, should) {
+ context = new OfflineAudioContext(1, testFrames, sampleRate);
+ // Create input values between -1.1 and 1.1
+ let buffer =
+ context.createBuffer(1, testFrames, context.sampleRate);
+ bufferData = new Float32Array(testFrames);
+ let start = -1 - scale;
+ for (let k = 0; k < testFrames; ++k) {
+ bufferData[k] = k * scale + start;
+ }
+ buffer.copyToChannel(bufferData, 0);
+
+ let source = context.createBufferSource();
+ source.buffer = buffer;
+
+ // Create simple waveshaper. It should map -1 to 0, 0 to 1, and +1
+ // to 0 and interpolate all points in between using a simple linear
+ // interpolator.
+ let shaper = context.createWaveShaper();
+ let curve = new Float32Array(3);
+ curve[0] = 0;
+ curve[1] = 1;
+ curve[2] = 0;
+ shaper.curve = curve;
+ source.connect(shaper);
+ shaper.connect(context.destination);
+
+ source.start();
+ context.startRendering()
+ .then(buffer => checkResult(buffer, should))
+ .then(() => task.done());
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-simple.html b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-simple.html
new file mode 100644
index 0000000000..affd0c58af
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper-simple.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Simple Tests of WaveShaperNode
+ </title>
+ <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>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let audit = Audit.createTaskRunner();
+
+ audit.define('simple', (task, should) => {
+ let context = new OfflineAudioContext(1, 1, 48000);
+ let shaper = context.createWaveShaper();
+
+ // Verify default values are correct.
+ should(shaper.curve, 'Initial WaveShaper.curve').beEqualTo(null);
+ should(shaper.oversample, 'Initial WaveShaper.oversample')
+ .beEqualTo('none');
+
+ // Set oversample and verify that it is set correctly.
+ should(() => shaper.oversample = '2x', 'Setting oversample to "2x"')
+ .notThrow();
+ should(shaper.oversample, 'Waveshaper.oversample = "2x"')
+ .beEqualTo('2x');
+
+ should(() => shaper.oversample = '4x', 'Setting oversample to "4x"')
+ .notThrow();
+ should(shaper.oversample, 'Waveshaper.oversample = "4x"')
+ .beEqualTo('4x');
+
+ should(
+ () => shaper.oversample = 'invalid',
+ 'Setting oversample to "invalid"')
+ .notThrow();
+ should(shaper.oversample, 'Waveshaper.oversample = "invalid"')
+ .beEqualTo('4x');
+
+ // Set the curve and verify that the returned curve is the same as what
+ // it was set to.
+ let curve = Float32Array.from([-1, 0.25, .75]);
+ should(() => shaper.curve = curve, 'Setting curve to [' + curve + ']')
+ .notThrow();
+ should(shaper.curve, 'WaveShaper.curve').beEqualToArray(curve);
+
+ // Verify setting the curve to null works.
+ should(() => shaper.curve = null, 'Setting curve back to null')
+ .notThrow();
+ should(shaper.curve, 'Waveshaper.curve = null').beEqualTo(null);
+
+ task.done();
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper.html b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper.html
new file mode 100644
index 0000000000..8bfa009b18
--- /dev/null
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/waveshaper.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ waveshaper.html
+ </title>
+ <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>
+ <script src="../../resources/buffer-loader.js"></script>
+ </head>
+ <body>
+ <script id="layout-test-code">
+ let audit = Audit.createTaskRunner();
+
+ let sampleRate = 44100;
+ let lengthInSeconds = 4;
+ let numberOfRenderFrames = sampleRate * lengthInSeconds;
+ let numberOfCurveFrames = 65536;
+ let inputBuffer;
+ let waveShapingCurve;
+
+ let context;
+
+ function generateInputBuffer() {
+ // Create mono input buffer.
+ let buffer =
+ context.createBuffer(1, numberOfRenderFrames, context.sampleRate);
+ let data = buffer.getChannelData(0);
+
+ // Generate an input vector with values from -1 -> +1 over a duration of
+ // lengthInSeconds. This exercises the full nominal input range and will
+ // touch every point of the shaping curve.
+ for (let i = 0; i < numberOfRenderFrames; ++i) {
+ let x = i / numberOfRenderFrames; // 0 -> 1
+ x = 2 * x - 1; // -1 -> +1
+ data[i] = x;
+ }
+
+ return buffer;
+ }
+
+ // Generates a symmetric curve: Math.atan(5 * x) / (0.5 * Math.PI)
+ // (with x == 0 corresponding to the center of the array)
+ // This curve is arbitrary, but would be useful in the real-world.
+ // To some extent, the actual curve we choose is not important in this
+ // test, since the input vector walks through all possible curve values.
+ function generateWaveShapingCurve() {
+ let curve = new Float32Array(numberOfCurveFrames);
+
+ let n = numberOfCurveFrames;
+ let n2 = n / 2;
+
+ for (let i = 0; i < n; ++i) {
+ let x = (i - n2) / n2;
+ let y = Math.atan(5 * x) / (0.5 * Math.PI);
+ }
+
+ return curve;
+ }
+
+ function checkShapedCurve(buffer, should) {
+ let inputData = inputBuffer.getChannelData(0);
+ let outputData = buffer.getChannelData(0);
+
+ let success = true;
+
+ // Go through every sample and make sure it has been shaped exactly
+ // according to the shaping curve we gave it.
+ for (let i = 0; i < buffer.length; ++i) {
+ let input = inputData[i];
+
+ // Calculate an index based on input -1 -> +1 with 0 being at the
+ // center of the curve data.
+ let index = Math.floor(numberOfCurveFrames * 0.5 * (input + 1));
+
+ // Clip index to the input range of the curve.
+ // This takes care of input outside of nominal range -1 -> +1
+ index = index < 0 ? 0 : index;
+ index =
+ index > numberOfCurveFrames - 1 ? numberOfCurveFrames - 1 : index;
+
+ let expectedOutput = waveShapingCurve[index];
+
+ let output = outputData[i];
+
+ if (output != expectedOutput) {
+ success = false;
+ break;
+ }
+ }
+
+ should(
+ success, 'WaveShaperNode applied non-linear distortion correctly')
+ .beTrue();
+ }
+
+ audit.define('test', function(task, should) {
+ // Create offline audio context.
+ context = new OfflineAudioContext(1, numberOfRenderFrames, sampleRate);
+
+ // source -> waveshaper -> destination
+ let source = context.createBufferSource();
+ let waveshaper = context.createWaveShaper();
+ source.connect(waveshaper);
+ waveshaper.connect(context.destination);
+
+ // Create an input test vector.
+ inputBuffer = generateInputBuffer();
+ source.buffer = inputBuffer;
+
+ // We'll apply non-linear distortion according to this shaping curve.
+ waveShapingCurve = generateWaveShapingCurve();
+ waveshaper.curve = waveShapingCurve;
+
+ source.start(0);
+
+ context.startRendering()
+ .then(buffer => checkShapedCurve(buffer, should))
+ .then(task.done.bind(task));
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>