audionode-disconnect-audioparam.html (8347B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title> 5 audionode-disconnect-audioparam.html 6 </title> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 <script src="/webaudio/resources/audit-util.js"></script> 10 <script src="/webaudio/resources/audit.js"></script> 11 </head> 12 <body> 13 <script id="layout-test-code"> 14 let renderQuantum = 128; 15 16 let sampleRate = 44100; 17 let renderDuration = 0.5; 18 let disconnectTime = 0.5 * renderDuration; 19 20 let audit = Audit.createTaskRunner(); 21 22 // Calculate the index for disconnection. 23 function getDisconnectIndex(disconnectTime) { 24 let disconnectIndex = disconnectTime * sampleRate; 25 disconnectIndex = renderQuantum * 26 Math.floor((disconnectIndex + renderQuantum - 1) / renderQuantum); 27 return disconnectIndex; 28 } 29 30 // Get the index of value change. 31 function getValueChangeIndex(array, targetValue) { 32 return array.findIndex(function(element, index) { 33 if (element === targetValue) 34 return true; 35 }); 36 } 37 38 // Task 1: test disconnect(AudioParam) method. 39 audit.define('disconnect(AudioParam)', (task, should) => { 40 // Creates a buffer source with value [1] and then connect it to two 41 // gain nodes in series. The output of the buffer source is lowered by 42 // half 43 // (* 0.5) and then connected to two |.gain| AudioParams in each gain 44 // node. 45 // 46 // (1) bufferSource => gain1 => gain2 47 // (2) bufferSource => half => gain1.gain 48 // (3) half => gain2.gain 49 // 50 // This graph should produce the output of 2.25 (= 1 * 1.5 * 1.5). After 51 // disconnecting (3), it should produce 1.5. 52 let context = 53 new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate); 54 let source = context.createBufferSource(); 55 let buffer1ch = createConstantBuffer(context, 1, 1); 56 let half = context.createGain(); 57 let gain1 = context.createGain(); 58 let gain2 = context.createGain(); 59 60 source.buffer = buffer1ch; 61 source.loop = true; 62 half.gain.value = 0.5; 63 64 source.connect(gain1); 65 gain1.connect(gain2); 66 gain2.connect(context.destination); 67 source.connect(half); 68 69 // Connecting |half| to both |gain1.gain| and |gain2.gain| amplifies the 70 // signal by 2.25 (= 1.5 * 1.5) because each gain node amplifies the 71 // signal by 1.5 (= 1.0 + 0.5). 72 half.connect(gain1.gain); 73 half.connect(gain2.gain); 74 75 source.start(); 76 77 // Schedule the disconnection at the half of render duration. 78 context.suspend(disconnectTime).then(function() { 79 half.disconnect(gain2.gain); 80 context.resume(); 81 }); 82 83 context.startRendering() 84 .then(function(buffer) { 85 let channelData = buffer.getChannelData(0); 86 let disconnectIndex = getDisconnectIndex(disconnectTime); 87 let valueChangeIndex = getValueChangeIndex(channelData, 1.5); 88 89 // Expected values are: 1 * 1.5 * 1.5 -> 1 * 1.5 = [2.25, 1.5] 90 should(channelData, 'Channel #0').containValues([2.25, 1.5]); 91 should(valueChangeIndex, 'The index of value change') 92 .beEqualTo(disconnectIndex); 93 }) 94 .then(() => task.done()); 95 }); 96 97 // Task 2: test disconnect(AudioParam, output) method. 98 audit.define('disconnect(AudioParam, output)', (task, should) => { 99 // Create a 2-channel buffer source with [1, 2] in each channel and 100 // make a serial connection through gain1 and gain 2. The make the 101 // buffer source half with a gain node and connect it to a 2-output 102 // splitter. Connect each output to 2 gain AudioParams respectively. 103 // 104 // (1) bufferSource => gain1 => gain2 105 // (2) bufferSource => half => splitter(2) 106 // (3) splitter#0 => gain1.gain 107 // (4) splitter#1 => gain2.gain 108 // 109 // This graph should produce 3 (= 1 * 1.5 * 2) and 6 (= 2 * 1.5 * 2) for 110 // each channel. After disconnecting (4), it should output 1.5 and 3. 111 let context = 112 new OfflineAudioContext(2, renderDuration * sampleRate, sampleRate); 113 let source = context.createBufferSource(); 114 let buffer2ch = createConstantBuffer(context, 1, [1, 2]); 115 let splitter = context.createChannelSplitter(2); 116 let half = context.createGain(); 117 let gain1 = context.createGain(); 118 let gain2 = context.createGain(); 119 120 source.buffer = buffer2ch; 121 source.loop = true; 122 half.gain.value = 0.5; 123 124 source.connect(gain1); 125 gain1.connect(gain2); 126 gain2.connect(context.destination); 127 128 // |source| originally is [1, 2] but it becomes [0.5, 1] after 0.5 gain. 129 // Each splitter's output will be applied to |gain1.gain| and 130 // |gain2.gain| respectively in an additive fashion. 131 source.connect(half); 132 half.connect(splitter); 133 134 // This amplifies the signal by 1.5. (= 1.0 + 0.5) 135 splitter.connect(gain1.gain, 0); 136 137 // This amplifies the signal by 2. (= 1.0 + 1.0) 138 splitter.connect(gain2.gain, 1); 139 140 source.start(); 141 142 // Schedule the disconnection at the half of render duration. 143 context.suspend(disconnectTime).then(function() { 144 splitter.disconnect(gain2.gain, 1); 145 context.resume(); 146 }); 147 148 context.startRendering() 149 .then(function(buffer) { 150 let channelData0 = buffer.getChannelData(0); 151 let channelData1 = buffer.getChannelData(1); 152 153 let disconnectIndex = getDisconnectIndex(disconnectTime); 154 let valueChangeIndexCh0 = getValueChangeIndex(channelData0, 1.5); 155 let valueChangeIndexCh1 = getValueChangeIndex(channelData1, 3); 156 157 // Expected values are: 1 * 1.5 * 2 -> 1 * 1.5 = [3, 1.5] 158 should(channelData0, 'Channel #0').containValues([3, 1.5]); 159 should( 160 valueChangeIndexCh0, 161 'The index of value change in channel #0') 162 .beEqualTo(disconnectIndex); 163 164 // Expected values are: 2 * 1.5 * 2 -> 2 * 1.5 = [6, 3] 165 should(channelData1, 'Channel #1').containValues([6, 3]); 166 should( 167 valueChangeIndexCh1, 168 'The index of value change in channel #1') 169 .beEqualTo(disconnectIndex); 170 }) 171 .then(() => task.done()); 172 }); 173 174 // Task 3: exception checks. 175 audit.define('exceptions', (task, should) => { 176 let context = new AudioContext(); 177 let gain1 = context.createGain(); 178 let splitter = context.createChannelSplitter(2); 179 let gain2 = context.createGain(); 180 let gain3 = context.createGain(); 181 182 // Connect a splitter to gain nodes and merger so we can test the 183 // possible ways of disconnecting the nodes to verify that appropriate 184 // exceptions are thrown. 185 gain1.connect(splitter); 186 splitter.connect(gain2.gain, 0); 187 splitter.connect(gain3.gain, 1); 188 gain2.connect(gain3); 189 gain3.connect(context.destination); 190 191 // gain1 is not connected to gain3.gain. Exception should be thrown. 192 should( 193 function() { 194 gain1.disconnect(gain3.gain); 195 }, 196 'gain1.disconnect(gain3.gain)') 197 .throw(DOMException, 'InvalidAccessError'); 198 199 // When the output index is good but the destination is invalid. 200 should( 201 function() { 202 splitter.disconnect(gain1.gain, 1); 203 }, 204 'splitter.disconnect(gain1.gain, 1)') 205 .throw(DOMException, 'InvalidAccessError'); 206 207 // When both arguments are wrong, throw IndexSizeError first. 208 should( 209 function() { 210 splitter.disconnect(gain1.gain, 2); 211 }, 212 'splitter.disconnect(gain1.gain, 2)') 213 .throw(DOMException, 'IndexSizeError'); 214 215 task.done(); 216 }); 217 218 audit.run(); 219 </script> 220 </body> 221 </html>