audionode-connect-method-chaining.html (5512B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>AudioNode.connect() Method Chaining and Validation Tests</title> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="/webaudio/resources/audit-util.js"></script> 8 </head> 9 <body> 10 <script> 11 // Dictionary of AudioNode types and their constructor arguments 12 const audioNodeTypesToTest = [ 13 { name: 'Analyser' }, { name: 'BiquadFilter' }, 14 { name: 'BufferSource' }, { name: 'ChannelMerger', args: [6] }, 15 { name: 'ChannelSplitter', args: [6] }, { name: 'Convolver' }, 16 { name: 'Delay' }, { name: 'DynamicsCompressor' }, { name: 'Gain' }, 17 { name: 'Oscillator' }, { name: 'Panner' }, 18 { name: 'ScriptProcessor', args: [512, 1, 1] }, 19 { name: 'StereoPanner' }, { name: 'WaveShaper' } 20 ]; 21 22 // Verifies that AudioNode.connect() returns the destination node. 23 function verifyConnectReturnValue( 24 t, { sourceNode, destinationNode, returnedValue, description }) { 25 assert_equals( 26 returnedValue, 27 destinationNode, 28 `${description} should return the destination node` 29 ); 30 } 31 32 // Creates a source node and tests .connect() chaining with: 33 // - a simple GainNode 34 // - a BiquadFilterNode with output index 35 // - a ChannelMergerNode with both output and input index 36 function testConnectChainingForNodeType(t, audioContext, nodeInfo) { 37 const sourceNode = 38 audioContext[`create${nodeInfo.name}`].apply(audioContext, 39 nodeInfo.args || []); 40 const sourceTypeName = sourceNode.constructor.name; 41 42 const gainNode = audioContext.createGain(); 43 verifyConnectReturnValue(t, { 44 sourceNode, 45 destinationNode: gainNode, 46 returnedValue: sourceNode.connect(gainNode), 47 description: `${sourceTypeName}.connect(GainNode)` 48 }); 49 50 const biquadNode = audioContext.createBiquadFilter(); 51 verifyConnectReturnValue(t, { 52 sourceNode, 53 destinationNode: biquadNode, 54 returnedValue: sourceNode.connect(biquadNode, 0), 55 description: `${sourceTypeName}.connect(BiquadFilterNode, 0)` 56 }); 57 58 const channelMergerNode = audioContext.createChannelMerger(); 59 verifyConnectReturnValue(t, { 60 sourceNode, 61 destinationNode: channelMergerNode, 62 returnedValue: sourceNode.connect(channelMergerNode, 0, 1), 63 description: `${sourceTypeName}.connect(ChannelMergerNode, 0, 1)` 64 }); 65 } 66 67 test(t => { 68 const audioContext = new AudioContext(); 69 70 for (const nodeInfo of audioNodeTypesToTest) { 71 testConnectChainingForNodeType(t, audioContext, nodeInfo); 72 } 73 }, 'Verify connect() method chaining for common AudioNode types ' + 74 'returns destination'); 75 76 test(t => { 77 const audioContext = new AudioContext(); 78 79 const audioElement = document.createElement('audio'); 80 testConnectChainingForNodeType(t, audioContext, { 81 name: 'MediaElementSource', 82 args: [audioElement] 83 }); 84 85 const mediaStreamDestination = 86 audioContext.createMediaStreamDestination(); 87 const mediaStream = mediaStreamDestination.stream; 88 89 testConnectChainingForNodeType(t, audioContext, { 90 name: 'MediaStreamSource', 91 args: [mediaStream] 92 }); 93 }, 'Verify connect() method chaining for MediaElementSourceNode ' + 94 'and MediaStreamSourceNode'); 95 96 test(t => { 97 const contextA = new AudioContext(); 98 const contextB = new AudioContext(); 99 const gainA1 = contextA.createGain(); 100 const gainA2 = contextA.createGain(); 101 102 assert_throws_dom( 103 'IndexSizeError', 104 () => { 105 gainA1.connect(gainA2, 1).connect(contextA.destination); 106 }, 107 'Connecting to an invalid output index should throw IndexSizeError' 108 ); 109 110 assert_throws_dom( 111 'InvalidAccessError', 112 () => { 113 gainA1.connect(gainA2).connect(contextB.destination); 114 }, 115 'Connecting nodes across different AudioContexts should ' + 116 'throw InvalidAccessError' 117 ); 118 }, 'Test exceptions thrown by invalid connect() chaining'); 119 120 promise_test(async t => { 121 const context = new OfflineAudioContext(1, 128, 8000); 122 123 const constantBuffer = createConstantBuffer(context, 1, 1.0); 124 const bufferSource = context.createBufferSource(); 125 bufferSource.buffer = constantBuffer; 126 bufferSource.loop = true; 127 128 const gainNode1 = context.createGain(); 129 gainNode1.gain.value = 0.5; 130 131 const gainNode2 = context.createGain(); 132 gainNode2.gain.value = 0.25; 133 bufferSource.connect(gainNode1) 134 .connect(gainNode2) 135 .connect(context.destination); 136 bufferSource.start(); 137 138 const renderedBuffer = await context.startRendering(); 139 140 assert_array_approx_equals( 141 renderedBuffer.getChannelData(0), 142 new Float32Array(renderedBuffer.length).fill(0.125), 143 1e-6, 144 'Output of chained gain nodes should be input * 0.5 * 0.25 = 0.125' 145 ); 146 }, 'Test correct signal flow and output value after chaining ' + 147 '.connect() on gain nodes'); 148 </script> 149 </body> 150 </html>