| <!DOCTYPE html> |
| <html> |
| <head> |
| <title> |
| audionode-disconnect-audioparam.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"> |
| let renderQuantum = 128; |
| |
| let sampleRate = 44100; |
| let renderDuration = 0.5; |
| let disconnectTime = 0.5 * renderDuration; |
| |
| let audit = Audit.createTaskRunner(); |
| |
| // Calculate the index for disconnection. |
| function getDisconnectIndex(disconnectTime) { |
| let disconnectIndex = disconnectTime * sampleRate; |
| disconnectIndex = renderQuantum * |
| Math.floor((disconnectIndex + renderQuantum - 1) / renderQuantum); |
| return disconnectIndex; |
| } |
| |
| // Get the index of value change. |
| function getValueChangeIndex(array, targetValue) { |
| return array.findIndex(function(element, index) { |
| if (element === targetValue) |
| return true; |
| }); |
| } |
| |
| // Task 1: test disconnect(AudioParam) method. |
| audit.define('disconnect(AudioParam)', (task, should) => { |
| // Creates a buffer source with value [1] and then connect it to two |
| // gain nodes in series. The output of the buffer source is lowered by |
| // half |
| // (* 0.5) and then connected to two |.gain| AudioParams in each gain |
| // node. |
| // |
| // (1) bufferSource => gain1 => gain2 |
| // (2) bufferSource => half => gain1.gain |
| // (3) half => gain2.gain |
| // |
| // This graph should produce the output of 2.25 (= 1 * 1.5 * 1.5). After |
| // disconnecting (3), it should produce 1.5. |
| let context = |
| new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate); |
| let source = context.createBufferSource(); |
| let buffer1ch = createConstantBuffer(context, 1, 1); |
| let half = context.createGain(); |
| let gain1 = context.createGain(); |
| let gain2 = context.createGain(); |
| |
| source.buffer = buffer1ch; |
| source.loop = true; |
| half.gain.value = 0.5; |
| |
| source.connect(gain1); |
| gain1.connect(gain2); |
| gain2.connect(context.destination); |
| source.connect(half); |
| |
| // Connecting |half| to both |gain1.gain| and |gain2.gain| amplifies the |
| // signal by 2.25 (= 1.5 * 1.5) because each gain node amplifies the |
| // signal by 1.5 (= 1.0 + 0.5). |
| half.connect(gain1.gain); |
| half.connect(gain2.gain); |
| |
| source.start(); |
| |
| // Schedule the disconnection at the half of render duration. |
| context.suspend(disconnectTime).then(function() { |
| half.disconnect(gain2.gain); |
| context.resume(); |
| }); |
| |
| context.startRendering() |
| .then(function(buffer) { |
| let channelData = buffer.getChannelData(0); |
| let disconnectIndex = getDisconnectIndex(disconnectTime); |
| let valueChangeIndex = getValueChangeIndex(channelData, 1.5); |
| |
| // Expected values are: 1 * 1.5 * 1.5 -> 1 * 1.5 = [2.25, 1.5] |
| should(channelData, 'Channel #0').containValues([2.25, 1.5]); |
| should(valueChangeIndex, 'The index of value change') |
| .beEqualTo(disconnectIndex); |
| }) |
| .then(() => task.done()); |
| }); |
| |
| // Task 2: test disconnect(AudioParam, output) method. |
| audit.define('disconnect(AudioParam, output)', (task, should) => { |
| // Create a 2-channel buffer source with [1, 2] in each channel and |
| // make a serial connection through gain1 and gain 2. The make the |
| // buffer source half with a gain node and connect it to a 2-output |
| // splitter. Connect each output to 2 gain AudioParams respectively. |
| // |
| // (1) bufferSource => gain1 => gain2 |
| // (2) bufferSource => half => splitter(2) |
| // (3) splitter#0 => gain1.gain |
| // (4) splitter#1 => gain2.gain |
| // |
| // This graph should produce 3 (= 1 * 1.5 * 2) and 6 (= 2 * 1.5 * 2) for |
| // each channel. After disconnecting (4), it should output 1.5 and 3. |
| let context = |
| new OfflineAudioContext(2, renderDuration * sampleRate, sampleRate); |
| let source = context.createBufferSource(); |
| let buffer2ch = createConstantBuffer(context, 1, [1, 2]); |
| let splitter = context.createChannelSplitter(2); |
| let half = context.createGain(); |
| let gain1 = context.createGain(); |
| let gain2 = context.createGain(); |
| |
| source.buffer = buffer2ch; |
| source.loop = true; |
| half.gain.value = 0.5; |
| |
| source.connect(gain1); |
| gain1.connect(gain2); |
| gain2.connect(context.destination); |
| |
| // |source| originally is [1, 2] but it becomes [0.5, 1] after 0.5 gain. |
| // Each splitter's output will be applied to |gain1.gain| and |
| // |gain2.gain| respectively in an additive fashion. |
| source.connect(half); |
| half.connect(splitter); |
| |
| // This amplifies the signal by 1.5. (= 1.0 + 0.5) |
| splitter.connect(gain1.gain, 0); |
| |
| // This amplifies the signal by 2. (= 1.0 + 1.0) |
| splitter.connect(gain2.gain, 1); |
| |
| source.start(); |
| |
| // Schedule the disconnection at the half of render duration. |
| context.suspend(disconnectTime).then(function() { |
| splitter.disconnect(gain2.gain, 1); |
| context.resume(); |
| }); |
| |
| context.startRendering() |
| .then(function(buffer) { |
| let channelData0 = buffer.getChannelData(0); |
| let channelData1 = buffer.getChannelData(1); |
| |
| let disconnectIndex = getDisconnectIndex(disconnectTime); |
| let valueChangeIndexCh0 = getValueChangeIndex(channelData0, 1.5); |
| let valueChangeIndexCh1 = getValueChangeIndex(channelData1, 3); |
| |
| // Expected values are: 1 * 1.5 * 2 -> 1 * 1.5 = [3, 1.5] |
| should(channelData0, 'Channel #0').containValues([3, 1.5]); |
| should( |
| valueChangeIndexCh0, |
| 'The index of value change in channel #0') |
| .beEqualTo(disconnectIndex); |
| |
| // Expected values are: 2 * 1.5 * 2 -> 2 * 1.5 = [6, 3] |
| should(channelData1, 'Channel #1').containValues([6, 3]); |
| should( |
| valueChangeIndexCh1, |
| 'The index of value change in channel #1') |
| .beEqualTo(disconnectIndex); |
| }) |
| .then(() => task.done()); |
| }); |
| |
| // Task 3: exception checks. |
| audit.define('exceptions', (task, should) => { |
| let context = new AudioContext(); |
| let gain1 = context.createGain(); |
| let splitter = context.createChannelSplitter(2); |
| let gain2 = context.createGain(); |
| let gain3 = context.createGain(); |
| |
| // Connect a splitter to gain nodes and merger so we can test the |
| // possible ways of disconnecting the nodes to verify that appropriate |
| // exceptions are thrown. |
| gain1.connect(splitter); |
| splitter.connect(gain2.gain, 0); |
| splitter.connect(gain3.gain, 1); |
| gain2.connect(gain3); |
| gain3.connect(context.destination); |
| |
| // gain1 is not connected to gain3.gain. Exception should be thrown. |
| should( |
| function() { |
| gain1.disconnect(gain3.gain); |
| }, |
| 'gain1.disconnect(gain3.gain)') |
| .throw(DOMException, 'InvalidAccessError'); |
| |
| // When the output index is good but the destination is invalid. |
| should( |
| function() { |
| splitter.disconnect(gain1.gain, 1); |
| }, |
| 'splitter.disconnect(gain1.gain, 1)') |
| .throw(DOMException, 'InvalidAccessError'); |
| |
| // When both arguments are wrong, throw IndexSizeError first. |
| should( |
| function() { |
| splitter.disconnect(gain1.gain, 2); |
| }, |
| 'splitter.disconnect(gain1.gain, 2)') |
| .throw(DOMException, 'IndexSizeError'); |
| |
| task.done(); |
| }); |
| |
| audit.run(); |
| </script> |
| </body> |
| </html> |