OLD | NEW |
(Empty) | |
| 1 <!doctype html> |
| 2 <html> |
| 3 <head> |
| 4 <title>Test Convolver Channel Outputs for Response with 1 channel</title> |
| 5 <script src="../../resources/testharness.js"></script> |
| 6 <script src="../../resources/testharnessreport.js"></script> |
| 7 <script src="../resources/audit-util.js"></script> |
| 8 <script src="../resources/audit.js"></script> |
| 9 </head> |
| 10 |
| 11 <body> |
| 12 <script> |
| 13 // Test various convolver configurations when the convolver response has |
| 14 // one channel (mono). |
| 15 // |
| 16 // Fairly arbitrary sample rate, except that we want the rate to be a |
| 17 // power of two so that 1/sampleRate is exactly respresentable as a |
| 18 // single-precision float. |
| 19 let sampleRate = 8192; |
| 20 |
| 21 // A fairly arbitrary number of frames, except the number of frames should |
| 22 // be more than a few render quanta. |
| 23 let renderFrames = 10 * 128; |
| 24 |
| 25 let audit = Audit.createTaskRunner(); |
| 26 |
| 27 // Convolver response |
| 28 let response; |
| 29 |
| 30 audit.define( |
| 31 { |
| 32 label: 'initialize', |
| 33 description: 'Convolver response with one channel' |
| 34 }, |
| 35 (task, should) => { |
| 36 // Convolver response |
| 37 should( |
| 38 () => { |
| 39 response = new AudioBuffer( |
| 40 {numberOfChannels: 1, length: 2, sampleRate: sampleRate}); |
| 41 response.getChannelData(0)[1] = 1; |
| 42 }, |
| 43 'new AudioBuffer({numberOfChannels: 1, length: 2, sampleRate: '
+ |
| 44 sampleRate + '})') |
| 45 .notThrow(); |
| 46 |
| 47 task.done(); |
| 48 }); |
| 49 |
| 50 audit.define( |
| 51 {label: '1-channel input', description: 'produces 1-channel output'}, |
| 52 (task, should) => { |
| 53 // Create a 3-channel context: channel 0 = convolver under test, |
| 54 // channel 1: test that convolver output is not stereo, channel 2: |
| 55 // expected output. The context MUST be discrete so that the |
| 56 // channels don't get mixed in some unexpected way. |
| 57 let context = new OfflineAudioContext(3, renderFrames, sampleRate); |
| 58 context.destination.channelInterpretation = 'discrete'; |
| 59 |
| 60 let src = new OscillatorNode(context); |
| 61 let conv = new ConvolverNode( |
| 62 context, {disableNormalization: true, buffer: response}); |
| 63 |
| 64 // Splitter node to verify that the output of the convolver is mono. |
| 65 // channelInterpretation must be 'discrete' so we don't do any |
| 66 // mixing of the input to the node. |
| 67 let splitter = new ChannelSplitterNode( |
| 68 context, |
| 69 {numberOfOutputs: 2, channelInterpretation: 'discrete'}); |
| 70 |
| 71 // Final merger to feed all of the individual channels into the |
| 72 // destination. |
| 73 let merger = new ChannelMergerNode(context, {numberOfInputs: 3}); |
| 74 |
| 75 src.connect(conv).connect(splitter); |
| 76 splitter.connect(merger, 0, 0); |
| 77 splitter.connect(merger, 1, 1); |
| 78 |
| 79 // The convolver response is a 1-sample delay. Use a delay node to |
| 80 // implement this. |
| 81 let delay = |
| 82 new DelayNode(context, {delayTime: 1 / context.sampleRate}); |
| 83 src.connect(delay); |
| 84 delay.connect(merger, 0, 2); |
| 85 |
| 86 merger.connect(context.destination); |
| 87 |
| 88 src.start(); |
| 89 |
| 90 context.startRendering() |
| 91 .then(audioBuffer => { |
| 92 // Extract out the three channels |
| 93 let actual = audioBuffer.getChannelData(0); |
| 94 let c1 = audioBuffer.getChannelData(1); |
| 95 let expected = audioBuffer.getChannelData(2); |
| 96 |
| 97 // c1 is expected to be zero. |
| 98 should(c1, '1: Channel 1').beConstantValueOf(0); |
| 99 |
| 100 // The expected and actual results should be identical |
| 101 should(actual, 'Convolver output').beEqualToArray(expected); |
| 102 }) |
| 103 .then(() => task.done()); |
| 104 }); |
| 105 |
| 106 audit.define( |
| 107 {label: '2-channel input', description: 'produces 2-channel output'}, |
| 108 (task, should) => { |
| 109 downMixTest({numberOfInputs: 2, prefix: '2'}, should) |
| 110 .then(() => task.done()); |
| 111 }); |
| 112 |
| 113 audit.define( |
| 114 { |
| 115 label: '3-channel input', |
| 116 description: '3->2 downmix producing 2-channel output' |
| 117 }, |
| 118 (task, should) => { |
| 119 downMixTest({numberOfInputs: 3, prefix: '3'}, should) |
| 120 .then(() => task.done()); |
| 121 }); |
| 122 |
| 123 audit.define( |
| 124 { |
| 125 label: '4-channel input', |
| 126 description: '4->2 downmix producing 2-channel output' |
| 127 }, |
| 128 (task, should) => { |
| 129 downMixTest({numberOfInputs: 4, prefix: '4'}, should) |
| 130 .then(() => task.done()); |
| 131 }); |
| 132 |
| 133 audit.define( |
| 134 { |
| 135 label: '5.1-channel input', |
| 136 description: '5.1->2 downmix producing 2-channel output' |
| 137 }, |
| 138 (task, should) => { |
| 139 downMixTest({numberOfInputs: 6, prefix: '5.1'}, should) |
| 140 .then(() => task.done()); |
| 141 }); |
| 142 |
| 143 function downMixTest(options, should) { |
| 144 // Create an 4-channel offline context. The first two channels are for |
| 145 // the stereo output of the convolver and the next two channels are for |
| 146 // the reference stereo signal. |
| 147 let context = new OfflineAudioContext(4, renderFrames, sampleRate); |
| 148 context.destination.channelInterpretation = 'discrete'; |
| 149 |
| 150 // Create oscillators for use as the input. The type and frequency is |
| 151 // arbitrary except that oscillators must be different. |
| 152 let src = new Array(options.numberOfInputs); |
| 153 for (let k = 0; k < src.length; ++k) { |
| 154 src[k] = new OscillatorNode( |
| 155 context, {type: 'square', frequency: 440 + 220 * k}); |
| 156 } |
| 157 |
| 158 // Merger to combine the oscillators into one output stream. |
| 159 let srcMerger = |
| 160 new ChannelMergerNode(context, {numberOfInputs: src.length}); |
| 161 |
| 162 for (let k = 0; k < src.length; ++k) { |
| 163 src[k].connect(srcMerger, 0, k); |
| 164 } |
| 165 |
| 166 // Convolver under test. |
| 167 let conv = new ConvolverNode( |
| 168 context, {disableNormalization: true, buffer: response}); |
| 169 srcMerger.connect(conv); |
| 170 |
| 171 // Splitter to get individual channels of the convolver output so we can |
| 172 // feed them (eventually) to the context in the right set of channels. |
| 173 let splitter = new ChannelSplitterNode(context, {numberOfOutputs: 2}); |
| 174 conv.connect(splitter); |
| 175 |
| 176 // Reference graph consists of a delay node to simulate the response of |
| 177 // the convolver. (The convolver response is designed this way.) |
| 178 let delay = new DelayNode(context, {delayTime: 1 / context.sampleRate}); |
| 179 |
| 180 // Gain node to mix the sources to stereo in the desired way. (Could be |
| 181 // done in the delay node, but let's keep the mixing separated from the |
| 182 // functionality.) |
| 183 let gainMixer = new GainNode( |
| 184 context, {channelCount: 2, channelCountMode: 'explicit'}); |
| 185 srcMerger.connect(gainMixer); |
| 186 |
| 187 // Splitter to extract the channels of the reference signal. |
| 188 let refSplitter = |
| 189 new ChannelSplitterNode(context, {numberOfOutputs: 2}); |
| 190 gainMixer.connect(delay).connect(refSplitter); |
| 191 |
| 192 // Final merger to bring back the individual channels from the convolver |
| 193 // and the reference in the right order for the destination. |
| 194 let finalMerger = new ChannelMergerNode( |
| 195 context, {numberOfInputs: context.destination.channelCount}); |
| 196 |
| 197 // First two channels are for the convolver output, and the next two are |
| 198 // for the reference. |
| 199 splitter.connect(finalMerger, 0, 0); |
| 200 splitter.connect(finalMerger, 1, 1); |
| 201 refSplitter.connect(finalMerger, 0, 2); |
| 202 refSplitter.connect(finalMerger, 1, 3); |
| 203 |
| 204 finalMerger.connect(context.destination); |
| 205 |
| 206 // Start the sources at last. |
| 207 for (let k = 0; k < src.length; ++k) { |
| 208 src[k].start(); |
| 209 } |
| 210 |
| 211 return context.startRendering().then(audioBuffer => { |
| 212 // Extract the various channels out |
| 213 let actual0 = audioBuffer.getChannelData(0); |
| 214 let actual1 = audioBuffer.getChannelData(1); |
| 215 let expected0 = audioBuffer.getChannelData(2); |
| 216 let expected1 = audioBuffer.getChannelData(3); |
| 217 |
| 218 // Verify that each output channel of the convolver matches |
| 219 // the delayed signal from the reference |
| 220 should(actual0, options.prefix + ': Channel 0') |
| 221 .beEqualToArray(expected0); |
| 222 should(actual1, options.prefix + ': Channel 1') |
| 223 .beEqualToArray(expected1); |
| 224 }); |
| 225 } |
| 226 |
| 227 audit.run(); |
| 228 </script> |
| 229 </body> |
| 230 </html> |
OLD | NEW |