| <!DOCTYPE html> |
| <html> |
| <head> |
| <title> |
| audionode-connect-method-chaining.html |
| </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"> |
| // AudioNode dictionary with associated arguments. |
| let nodeDictionary = [ |
| {name: 'Analyser'}, {name: 'BiquadFilter'}, {name: 'BufferSource'}, |
| {name: 'ChannelMerger', args: [6]}, |
| {name: 'ChannelSplitter', args: [6]}, {name: 'Convolver'}, |
| {name: 'Delay', args: []}, {name: 'DynamicsCompressor'}, {name: 'Gain'}, |
| {name: 'Oscillator'}, {name: 'Panner'}, |
| {name: 'ScriptProcessor', args: [512, 1, 1]}, {name: 'StereoPanner'}, |
| {name: 'WaveShaper'} |
| ]; |
| |
| |
| function verifyReturnedNode(should, config) { |
| should( |
| config.destination === config.returned, |
| 'The return value of ' + config.desc + ' matches the destination ' + |
| config.returned.constructor.name) |
| .beEqualTo(true); |
| } |
| |
| // Test utility for batch method checking: in order to test 3 method |
| // signatures, so we create 3 dummy destinations. |
| // 1) .connect(GainNode) |
| // 2) .connect(BiquadFilterNode, output) |
| // 3) .connect(ChannelMergerNode, output, input) |
| function testConnectMethod(context, should, options) { |
| let source = |
| context['create' + options.name].apply(context, options.args); |
| let sourceName = source.constructor.name; |
| |
| let destination1 = context.createGain(); |
| verifyReturnedNode(should, { |
| source: source, |
| destination: destination1, |
| returned: source.connect(destination1), |
| desc: sourceName + '.connect(' + destination1.constructor.name + ')' |
| }); |
| |
| let destination2 = context.createBiquadFilter(); |
| verifyReturnedNode(should, { |
| source: source, |
| destination: destination2, |
| returned: source.connect(destination2, 0), |
| desc: |
| sourceName + '.connect(' + destination2.constructor.name + ', 0)' |
| }); |
| |
| let destination3 = context.createChannelMerger(); |
| verifyReturnedNode(should, { |
| source: source, |
| destination: destination3, |
| returned: source.connect(destination3, 0, 1), |
| desc: sourceName + '.connect(' + destination3.constructor.name + |
| ', 0, 1)' |
| }); |
| } |
| |
| |
| let audit = Audit.createTaskRunner(); |
| |
| // Task: testing entries from the dictionary. |
| audit.define('from-dictionary', (task, should) => { |
| let context = new AudioContext(); |
| |
| for (let i = 0; i < nodeDictionary.length; i++) |
| testConnectMethod(context, should, nodeDictionary[i]); |
| |
| task.done(); |
| }); |
| |
| // Task: testing Media* nodes. |
| audit.define('media-group', (task, should) => { |
| let context = new AudioContext(); |
| |
| // Test MediaElementSourceNode needs an <audio> element. |
| let mediaElement = document.createElement('audio'); |
| testConnectMethod( |
| context, should, |
| {name: 'MediaElementSource', args: [mediaElement]}); |
| |
| // MediaStreamAudioDestinationNode has no output so it connect method |
| // chaining isn't possible. |
| |
| // MediaStreamSourceNode requires 'stream' object to be constructed, |
| // which is a part of MediaStreamDestinationNode. |
| let streamDestination = context.createMediaStreamDestination(); |
| let stream = streamDestination.stream; |
| testConnectMethod( |
| context, should, {name: 'MediaStreamSource', args: [stream]}); |
| |
| task.done(); |
| }); |
| |
| // Task: test the exception thrown by invalid operation. |
| audit.define('invalid-operation', (task, should) => { |
| let contextA = new AudioContext(); |
| let contextB = new AudioContext(); |
| let gain1 = contextA.createGain(); |
| let gain2 = contextA.createGain(); |
| |
| // Test if the first connection throws correctly. The first gain node |
| // does not have the second output, so it should throw. |
| should(function() { |
| gain1.connect(gain2, 1).connect(contextA.destination); |
| }, 'Connecting with an invalid output').throw(DOMException, 'IndexSizeError'); |
| |
| // Test if the second connection throws correctly. The contextB's |
| // destination is not compatible with the nodes from contextA, thus the |
| // first connection succeeds but the second one should throw. |
| should( |
| function() { |
| gain1.connect(gain2).connect(contextB.destination); |
| }, |
| 'Connecting to a node from the different context') |
| .throw(DOMException, 'InvalidAccessError'); |
| |
| task.done(); |
| }); |
| |
| // Task: verify if the method chaining actually works. |
| audit.define('verification', (task, should) => { |
| // We pick the lowest sample rate allowed to run the test efficiently. |
| let context = new OfflineAudioContext(1, 128, 8000); |
| |
| let constantBuffer = createConstantBuffer(context, 1, 1.0); |
| |
| let source = context.createBufferSource(); |
| source.buffer = constantBuffer; |
| source.loop = true; |
| |
| let gain1 = context.createGain(); |
| gain1.gain.value = 0.5; |
| let gain2 = context.createGain(); |
| gain2.gain.value = 0.25; |
| |
| source.connect(gain1).connect(gain2).connect(context.destination); |
| source.start(); |
| |
| context.startRendering() |
| .then(function(buffer) { |
| should( |
| buffer.getChannelData(0), |
| 'The output of chained connection of gain nodes') |
| .beConstantValueOf(0.125); |
| }) |
| .then(() => task.done()); |
| }); |
| |
| audit.run(); |
| </script> |
| </body> |
| </html> |