diff options
Diffstat (limited to 'testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface')
4 files changed, 480 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html b/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html new file mode 100644 index 0000000000..9409f1ffce --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html @@ -0,0 +1,131 @@ +<!DOCTYPE html> +<html> + <head> + <title> + Test Constructor: StereoPanner + </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('invalid constructor', (task, should) => { + testInvalidConstructor(should, 'StereoPannerNode', context); + task.done(); + }); + + audit.define('default constructor', (task, should) => { + let prefix = 'node0'; + let node = testDefaultConstructor(should, 'StereoPannerNode', context, { + prefix: prefix, + numberOfInputs: 1, + numberOfOutputs: 1, + channelCount: 2, + channelCountMode: 'clamped-max', + channelInterpretation: 'speakers' + }); + + testDefaultAttributes(should, node, prefix, [{name: 'pan', value: 0}]); + + task.done(); + }); + + audit.define('test AudioNodeOptions', (task, should) => { + // Can't use testAudioNodeOptions because the constraints for this node + // are not supported there. + let node; + + // An array of tests. + [{ + // Test that we can set the channel count to 1 or 2 and that other + // channel counts throw an error. + attribute: 'channelCount', + tests: [ + {value: 1}, {value: 2}, {value: 0, error: 'NotSupportedError'}, + {value: 3, error: 'NotSupportedError'}, + {value: 99, error: 'NotSupportedError'} + ] + }, + { + // Test channelCountMode. A mode of "max" is illegal, but others are + // ok. But also throw an error of unknown values. + attribute: 'channelCountMode', + tests: [ + {value: 'clamped-max'}, {value: 'explicit'}, + {value: 'max', error: 'NotSupportedError'}, + {value: 'foobar', error: TypeError} + ] + }, + { + // Test channelInterpretation can be set for valid values and an + // error is thrown for others. + attribute: 'channelInterpretation', + tests: [ + {value: 'speakers'}, {value: 'discrete'}, + {value: 'foobar', error: TypeError} + ] + }].forEach(entry => { + entry.tests.forEach(testItem => { + let options = {}; + options[entry.attribute] = testItem.value; + + const testFunction = () => { + node = new StereoPannerNode(context, options); + }; + const testDescription = + `new StereoPannerNode(c, ${JSON.stringify(options)})`; + + if (testItem.error) { + testItem.error === TypeError + ? should(testFunction, testDescription).throw(TypeError) + : should(testFunction, testDescription) + .throw(DOMException, 'NotSupportedError'); + } else { + should(testFunction, testDescription).notThrow(); + should(node[entry.attribute], `node.${entry.attribute}`) + .beEqualTo(options[entry.attribute]); + } + }); + }); + + task.done(); + }); + + audit.define('constructor with options', (task, should) => { + let node; + let options = { + pan: 0.75, + }; + + should( + () => { + node = new StereoPannerNode(context, options); + }, + 'node1 = new StereoPannerNode(, ' + JSON.stringify(options) + ')') + .notThrow(); + should( + node instanceof StereoPannerNode, + 'node1 instanceof StereoPannerNode') + .beEqualTo(true); + + should(node.pan.value, 'node1.pan.value').beEqualTo(options.pan); + + task.done(); + }); + + audit.run(); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/no-dezippering.html b/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/no-dezippering.html new file mode 100644 index 0000000000..355db8b9dc --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/no-dezippering.html @@ -0,0 +1,261 @@ +<!DOCTYPE html> +<html> + <head> + <title> + Test StereoPannerNode Has No Dezippering + </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"> + // Arbitrary sample rate except that it should be a power of two to + // eliminate any round-off in computing frame boundaries. + let sampleRate = 16384; + + let audit = Audit.createTaskRunner(); + + audit.define( + { + label: 'test mono input', + description: 'Test StereoPanner with mono input has no dezippering' + }, + (task, should) => { + let context = new OfflineAudioContext(2, sampleRate, sampleRate); + let src = new ConstantSourceNode(context, {offset: 1}); + let p = new StereoPannerNode(context, {pan: -1}); + + src.connect(p).connect(context.destination); + src.start(); + + // Frame at which to change pan value. + let panFrame = 256; + context.suspend(panFrame / context.sampleRate) + .then(() => p.pan.value = 1) + .then(() => context.resume()); + + context.startRendering() + .then(renderedBuffer => { + let c0 = renderedBuffer.getChannelData(0); + let c1 = renderedBuffer.getChannelData(1); + + // The first part should be full left. + should( + c0.slice(0, panFrame), 'Mono: Left channel, pan = -1: ') + .beConstantValueOf(1); + should( + c1.slice(0, panFrame), 'Mono: Right channel, pan = -1:') + .beConstantValueOf(0); + + // The second part should be full right, but due to roundoff, + // the left channel won't be exactly zero. Compare the left + // channel against zero with a threshold instead. + let tail = c0.slice(panFrame); + let zero = new Float32Array(tail.length); + + should(c0.slice(panFrame), 'Mono: Left channel, pan = 1: ') + .beCloseToArray(zero, {absoluteThreshold: 6.1233e-17}); + should(c1.slice(panFrame), 'Mono: Right channel, pan = 1:') + .beConstantValueOf(1); + }) + .then(() => task.done()); + }); + + audit.define( + { + label: 'test stereo input', + description: + 'Test StereoPanner with stereo input has no dezippering' + }, + (task, should) => { + let context = new OfflineAudioContext(2, sampleRate, sampleRate); + + // Create stereo source from two constant source nodes. + let s0 = new ConstantSourceNode(context, {offset: 1}); + let s1 = new ConstantSourceNode(context, {offset: 2}); + let merger = new ChannelMergerNode(context, {numberOfInputs: 2}); + + s0.connect(merger, 0, 0); + s1.connect(merger, 0, 1); + + let p = new StereoPannerNode(context, {pan: -1}); + + merger.connect(p).connect(context.destination); + s0.start(); + s1.start(); + + // Frame at which to change pan value. + let panFrame = 256; + context.suspend(panFrame / context.sampleRate) + .then(() => p.pan.value = 1) + .then(() => context.resume()); + + context.startRendering() + .then(renderedBuffer => { + let c0 = renderedBuffer.getChannelData(0); + let c1 = renderedBuffer.getChannelData(1); + + // The first part should be full left. + should( + c0.slice(0, panFrame), 'Stereo: Left channel, pan = -1: ') + .beConstantValueOf(3); + should( + c1.slice(0, panFrame), 'Stereo: Right channel, pan = -1:') + .beConstantValueOf(0); + + // The second part should be full right, but due to roundoff, + // the left channel won't be exactly zero. Compare the left + // channel against zero with a threshold instead. + let tail = c0.slice(panFrame); + let zero = new Float32Array(tail.length); + + should(c0.slice(panFrame), 'Stereo: Left channel, pan = 1: ') + .beCloseToArray(zero, {absoluteThreshold: 6.1233e-17}); + should(c1.slice(panFrame), 'Stereo: Right channel, pan = 1:') + .beConstantValueOf(3); + }) + .then(() => task.done()); + }); + + audit.define( + { + label: 'test mono input setValue', + description: 'Test StereoPanner with mono input value setter ' + + 'vs setValueAtTime' + }, + (task, should) => { + let context = new OfflineAudioContext(4, sampleRate, sampleRate); + + let src = new OscillatorNode(context); + + src.start(); + testWithSetValue(context, src, should, { + prefix: 'Mono' + }).then(() => task.done()); + }); + + audit.define( + { + label: 'test stereo input setValue', + description: 'Test StereoPanner with mono input value setter ' + + ' vs setValueAtTime' + }, + (task, should) => { + let context = new OfflineAudioContext(4, sampleRate, sampleRate); + + let src0 = new OscillatorNode(context, {frequency: 800}); + let src1 = new OscillatorNode(context, {frequency: 250}); + let merger = new ChannelMergerNode(context, {numberOfChannels: 2}); + + src0.connect(merger, 0, 0); + src1.connect(merger, 0, 1); + + src0.start(); + src1.start(); + + testWithSetValue(context, merger, should, { + prefix: 'Stereo' + }).then(() => task.done()); + }); + + audit.define( + { + label: 'test mono input automation', + description: 'Test StereoPanner with mono input and automation' + }, + (task, should) => { + let context = new OfflineAudioContext(4, sampleRate, sampleRate); + + let src0 = new OscillatorNode(context, {frequency: 800}); + let src1 = new OscillatorNode(context, {frequency: 250}); + let merger = new ChannelMergerNode(context, {numberOfChannels: 2}); + + src0.connect(merger, 0, 0); + src1.connect(merger, 0, 1); + + src0.start(); + src1.start(); + + let mod = new OscillatorNode(context, {frequency: 100}); + mod.start(); + + testWithSetValue(context, merger, should, { + prefix: 'Modulated Stereo', + modulator: (testNode, refNode) => { + mod.connect(testNode.pan); + mod.connect(refNode.pan); + } + }).then(() => task.done()); + }); + + + function testWithSetValue(context, src, should, options) { + let merger = new ChannelMergerNode( + context, {numberOfInputs: context.destination.channelCount}); + merger.connect(context.destination); + + let pannerRef = new StereoPannerNode(context, {pan: -0.3}); + let pannerTest = + new StereoPannerNode(context, {pan: pannerRef.pan.value}); + + let refSplitter = + new ChannelSplitterNode(context, {numberOfOutputs: 2}); + let testSplitter = + new ChannelSplitterNode(context, {numberOfOutputs: 2}); + + pannerRef.connect(refSplitter); + pannerTest.connect(testSplitter); + + testSplitter.connect(merger, 0, 0); + testSplitter.connect(merger, 1, 1); + refSplitter.connect(merger, 0, 2); + refSplitter.connect(merger, 1, 3); + + src.connect(pannerRef); + src.connect(pannerTest); + + let changeTime = 3 * RENDER_QUANTUM_FRAMES / context.sampleRate; + // An arbitrary position, different from the default pan value. + let newPanPosition = .71; + + pannerRef.pan.setValueAtTime(newPanPosition, changeTime); + context.suspend(changeTime) + .then(() => pannerTest.pan.value = newPanPosition) + .then(() => context.resume()); + + if (options.modulator) { + options.modulator(pannerTest, pannerRef); + } + return context.startRendering().then(renderedBuffer => { + let actual = new Array(2); + let expected = new Array(2); + + actual[0] = renderedBuffer.getChannelData(0); + actual[1] = renderedBuffer.getChannelData(1); + expected[0] = renderedBuffer.getChannelData(2); + expected[1] = renderedBuffer.getChannelData(3); + + let label = ['Left', 'Right']; + + for (let k = 0; k < 2; ++k) { + let match = + should( + actual[k], + options.prefix + ' ' + label[k] + ' .value setter output') + .beCloseToArray(expected[k], {absoluteThreshold: 1.192094e-7}); + should( + match, + options.prefix + ' ' + label[k] + + ' .value setter output matches setValueAtTime output') + .beTrue(); + } + + }); + } + + audit.run(); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/stereopannernode-basic.html b/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/stereopannernode-basic.html new file mode 100644 index 0000000000..48bacb08c6 --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/stereopannernode-basic.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> + <head> + <title> + stereopannernode-basic.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(); + + audit.define( + { + label: 'test', + description: + 'Attributes and basic functionality of StereoPannerNode' + }, + (task, should) => { + + let context = new AudioContext(); + let panner = context.createStereoPanner(); + + should(panner.numberOfInputs, 'panner.numberOfInputs').beEqualTo(1); + should(panner.numberOfOutputs, 'panner.numberOfOutputs') + .beEqualTo(1); + should(panner.pan.defaultValue, 'panner.pan.defaultValue') + .beEqualTo(0.0); + should(() => panner.pan.value = 1.0, 'panner.pan.value = 1.0') + .notThrow(); + should(panner.pan.value, 'panner.pan.value').beEqualTo(1.0); + + should(() => panner.channelCount = 1, 'panner.channelCount = 1') + .notThrow(); + should(() => panner.channelCount = 3, 'panner.channelCount = 3') + .throw(); + should( + () => panner.channelCountMode = 'explicit', + 'panner.channelCountMode = "explicit"') + .notThrow(); + should( + () => panner.channelCountMode = 'max', + 'panner.channelCountMode = "max"') + .throw(); + + task.done(); + }); + audit.run(); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/stereopannernode-panning.html b/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/stereopannernode-panning.html new file mode 100644 index 0000000000..f683fd78bf --- /dev/null +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-stereopanner-interface/stereopannernode-panning.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> + <head> + <title> + stereopannernode-panning.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/stereopanner-testing.js"></script> + </head> + <body> + <script id="layout-test-code"> + let audit = Audit.createTaskRunner(); + + audit.define('mono-test', (task, should) => { + StereoPannerTest + .create(should, {numberOfInputChannels: 1, prefix: 'Mono: '}) + .run() + .then(() => task.done()); + }); + + audit.define('stereo-test', (task, should) => { + StereoPannerTest + .create(should, {numberOfInputChannels: 2, prefix: 'Stereo: '}) + .run() + .then(() => task.done()); + }); + + audit.run(); + </script> + </body> +</html> |