diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface')
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> |