| OLD | NEW |
| (Empty) | |
| 1 // Utilities for mixing rule testing. |
| 2 // http://webaudio.github.io/web-audio-api/#channel-up-mixing-and-down-mixing |
| 3 |
| 4 |
| 5 /** |
| 6 * Create an n-channel buffer, with all sample data zero except for a shifted |
| 7 * impulse. The impulse position depends on the channel index. For example, for |
| 8 * a 4-channel buffer: |
| 9 * channel 0: 1 0 0 0 0 0 0 0 |
| 10 * channel 1: 0 1 0 0 0 0 0 0 |
| 11 * channel 2: 0 0 1 0 0 0 0 0 |
| 12 * channel 3: 0 0 0 1 0 0 0 0 |
| 13 * @param {AudioContext} context Associated AudioContext. |
| 14 * @param {Number} numberOfChannels Number of channels of test buffer. |
| 15 * @param {Number} frameLength Buffer length in frames. |
| 16 * @return {AudioBuffer} |
| 17 */ |
| 18 function createShiftedImpulseBuffer(context, numberOfChannels, frameLength) { |
| 19 var shiftedImpulseBuffer = context.createBuffer(numberOfChannels, frameLengt
h, context.sampleRate); |
| 20 for (var channel = 0; channel < numberOfChannels; ++channel) { |
| 21 var data = shiftedImpulseBuffer.getChannelData(channel); |
| 22 data[channel] = 1; |
| 23 } |
| 24 |
| 25 return shiftedImpulseBuffer; |
| 26 } |
| 27 |
| 28 /** |
| 29 * Create a string that displays the content of AudioBuffer. |
| 30 * @param {AudioBuffer} audioBuffer AudioBuffer object to stringify. |
| 31 * @param {Number} frameLength Number of frames to be printed. |
| 32 * @param {Number} frameOffset Starting frame position for printing. |
| 33 * @return {String} |
| 34 */ |
| 35 function stringifyBuffer(audioBuffer, frameLength, frameOffset) { |
| 36 frameOffset = (frameOffset || 0); |
| 37 |
| 38 var stringifiedBuffer = ''; |
| 39 for (var channel = 0; channel < audioBuffer.numberOfChannels; ++channel) { |
| 40 var channelData = audioBuffer.getChannelData(channel); |
| 41 for (var i = 0; i < frameLength; ++i) |
| 42 stringifiedBuffer += channelData[i + frameOffset] + ' '; |
| 43 stringifiedBuffer += '\n'; |
| 44 } |
| 45 |
| 46 return stringifiedBuffer; |
| 47 } |
| 48 |
| 49 /** |
| 50 * Compute number of channels from the connection. |
| 51 * http://webaudio.github.io/web-audio-api/#dfn-computednumberofchannels |
| 52 * @param {String} connections A string specifies the connection. For |
| 53 * example, the string "128" means 3 |
| 54 * connections, having 1, 2, and 8 channels |
| 55 * respectively. |
| 56 * @param {Number} channelCount Channel count. |
| 57 * @param {String} channelCountMode Channel count mode. |
| 58 * @return {Number} Computed number of channels. |
| 59 */ |
| 60 function computeNumberOfChannels(connections, channelCount, channelCountMode) { |
| 61 if (channelCountMode == "explicit") |
| 62 return channelCount; |
| 63 |
| 64 // Must have at least one channel. |
| 65 var computedNumberOfChannels = 1; |
| 66 |
| 67 // Compute "computedNumberOfChannels" based on all the connections. |
| 68 for (var i = 0; i < connections.length; ++i) { |
| 69 var connectionNumberOfChannels = parseInt(connections[i]); |
| 70 computedNumberOfChannels = Math.max(computedNumberOfChannels, connectionNumb
erOfChannels); |
| 71 } |
| 72 |
| 73 if (channelCountMode == "clamped-max") |
| 74 computedNumberOfChannels = Math.min(computedNumberOfChannels, channelCount); |
| 75 |
| 76 return computedNumberOfChannels; |
| 77 } |
| 78 |
| 79 /** |
| 80 * Apply up/down-mixing (in-place summing) based on 'speaker' interpretation. |
| 81 * @param {AudioBuffer} input Input audio buffer. |
| 82 * @param {AudioBuffer} output Output audio buffer. |
| 83 */ |
| 84 function speakersSum(input, output) { |
| 85 if (input.length != output.length) { |
| 86 throw '[mixing-rules.js] speakerSum(): buffer lengths mismatch (input: ' |
| 87 + input.length + ', output: ' + output.length + ')'; |
| 88 } |
| 89 |
| 90 if (input.numberOfChannels === output.numberOfChannels) { |
| 91 for (var channel = 0; channel < output.numberOfChannels; ++channel) { |
| 92 var inputChannel = input.getChannelData(channel); |
| 93 var outputChannel = output.getChannelData(channel); |
| 94 for (var i = 0; i < outputChannel.length; i++) |
| 95 outputChannel[i] += inputChannel[i]; |
| 96 } |
| 97 } else if (input.numberOfChannels < output.numberOfChannels) { |
| 98 processUpMix(input, output); |
| 99 } else { |
| 100 processDownMix(input, output); |
| 101 } |
| 102 } |
| 103 |
| 104 /** |
| 105 * In-place summing to |output| based on 'discrete' channel interpretation. |
| 106 * @param {AudioBuffer} input Input audio buffer. |
| 107 * @param {AudioBuffer} output Output audio buffer. |
| 108 */ |
| 109 function discreteSum(input, output) { |
| 110 if (input.length != output.length) { |
| 111 throw '[mixing-rules.js] speakerSum(): buffer lengths mismatch (input: ' |
| 112 + input.length + ', output: ' + output.length + ')'; |
| 113 } |
| 114 |
| 115 var numberOfChannels = Math.min(input.numberOfChannels, output.numberOfChannel
s) |
| 116 |
| 117 for (var channel = 0; channel < numberOfChannels; ++channel) { |
| 118 var inputChannel = input.getChannelData(channel); |
| 119 var outputChannel = output.getChannelData(channel); |
| 120 for (var i = 0; i < outputChannel.length; i++) |
| 121 outputChannel[i] += inputChannel[i]; |
| 122 } |
| 123 } |
| 124 |
| 125 /** |
| 126 * Perform up-mix by in-place summing to |output| buffer. |
| 127 * @param {AudioBuffer} input Input audio buffer. |
| 128 * @param {AudioBuffer} output Output audio buffer. |
| 129 */ |
| 130 function processUpMix(input, output) { |
| 131 var numberOfInputChannels = input.numberOfChannels; |
| 132 var numberOfOutputChannels = output.numberOfChannels; |
| 133 var i, length = output.length; |
| 134 |
| 135 // Up-mixing: 1 -> 2, 1 -> 4 |
| 136 // output.L += input |
| 137 // output.R += input |
| 138 // output.SL += 0 (in the case of 1 -> 4) |
| 139 // output.SR += 0 (in the case of 1 -> 4) |
| 140 if ((numberOfInputChannels === 1 && numberOfOutputChannels === 2) || |
| 141 (numberOfInputChannels === 1 && numberOfOutputChannels === 4)) { |
| 142 var inputChannel = input.getChannelData(0); |
| 143 var outputChannel0 = output.getChannelData(0); |
| 144 var outputChannel1 = output.getChannelData(1); |
| 145 for (i = 0; i < length; i++) { |
| 146 outputChannel0[i] += inputChannel[i]; |
| 147 outputChannel1[i] += inputChannel[i]; |
| 148 } |
| 149 |
| 150 return; |
| 151 } |
| 152 |
| 153 // Up-mixing: 1 -> 5.1 |
| 154 // output.L += 0 |
| 155 // output.R += 0 |
| 156 // output.C += input |
| 157 // output.LFE += 0 |
| 158 // output.SL += 0 |
| 159 // output.SR += 0 |
| 160 if (numberOfInputChannels == 1 && numberOfOutputChannels == 6) { |
| 161 var inputChannel = input.getChannelData(0); |
| 162 var outputChannel2 = output.getChannelData(2); |
| 163 for (i = 0; i < length; i++) |
| 164 outputChannel2[i] += inputChannel[i]; |
| 165 |
| 166 return; |
| 167 } |
| 168 |
| 169 // Up-mixing: 2 -> 4, 2 -> 5.1 |
| 170 // output.L += input.L |
| 171 // output.R += input.R |
| 172 // output.C += 0 (in the case of 2 -> 5.1) |
| 173 // output.LFE += 0 (in the case of 2 -> 5.1) |
| 174 // output.SL += 0 |
| 175 // output.SR += 0 |
| 176 if ((numberOfInputChannels === 2 && numberOfOutputChannels === 4) || |
| 177 (numberOfInputChannels === 2 && numberOfOutputChannels === 6)) { |
| 178 var inputChannel0 = input.getChannelData(0); |
| 179 var inputChannel1 = input.getChannelData(1); |
| 180 var outputChannel0 = output.getChannelData(0); |
| 181 var outputChannel1 = output.getChannelData(1); |
| 182 for (i = 0; i < length; i++) { |
| 183 outputChannel0[i] += inputChannel0[i]; |
| 184 outputChannel1[i] += inputChannel1[i]; |
| 185 } |
| 186 |
| 187 return; |
| 188 } |
| 189 |
| 190 // Up-mixing: 4 -> 5.1 |
| 191 // output.L += input.L |
| 192 // output.R += input.R |
| 193 // output.C += 0 |
| 194 // output.LFE += 0 |
| 195 // output.SL += input.SL |
| 196 // output.SR += input.SR |
| 197 if (numberOfInputChannels === 4 && numberOfOutputChannels === 6) { |
| 198 var inputChannel0 = input.getChannelData(0); // input.L |
| 199 var inputChannel1 = input.getChannelData(1); // input.R |
| 200 var inputChannel2 = input.getChannelData(2); // input.SL |
| 201 var inputChannel3 = input.getChannelData(3); // input.SR |
| 202 var outputChannel0 = output.getChannelData(0); // output.L |
| 203 var outputChannel1 = output.getChannelData(1); // output.R |
| 204 var outputChannel4 = output.getChannelData(4); // output.SL |
| 205 var outputChannel5 = output.getChannelData(5); // output.SR |
| 206 for (i = 0; i < length; i++) { |
| 207 outputChannel0[i] += inputChannel0[i]; |
| 208 outputChannel1[i] += inputChannel1[i]; |
| 209 outputChannel4[i] += inputChannel2[i]; |
| 210 outputChannel5[i] += inputChannel3[i]; |
| 211 } |
| 212 |
| 213 return; |
| 214 } |
| 215 |
| 216 // All other cases, fall back to the discrete sum. |
| 217 discreteSum(input, output); |
| 218 } |
| 219 |
| 220 /** |
| 221 * Perform down-mix by in-place summing to |output| buffer. |
| 222 * @param {AudioBuffer} input Input audio buffer. |
| 223 * @param {AudioBuffer} output Output audio buffer. |
| 224 */ |
| 225 function processDownMix(input, output) { |
| 226 var numberOfInputChannels = input.numberOfChannels; |
| 227 var numberOfOutputChannels = output.numberOfChannels; |
| 228 var i, length = output.length; |
| 229 |
| 230 // Down-mixing: 2 -> 1 |
| 231 // output += 0.5 * (input.L + input.R) |
| 232 if (numberOfInputChannels === 2 && numberOfOutputChannels === 1) { |
| 233 var inputChannel0 = input.getChannelData(0); // input.L |
| 234 var inputChannel1 = input.getChannelData(1); // input.R |
| 235 var outputChannel0 = output.getChannelData(0); |
| 236 for (i = 0; i < length; i++) |
| 237 outputChannel0[i] += 0.5 * (inputChannel0[i] + inputChannel1[i]); |
| 238 |
| 239 return; |
| 240 } |
| 241 |
| 242 // Down-mixing: 4 -> 1 |
| 243 // output += 0.25 * (input.L + input.R + input.SL + input.SR) |
| 244 if (numberOfInputChannels === 4 && numberOfOutputChannels === 1) { |
| 245 var inputChannel0 = input.getChannelData(0); // input.L |
| 246 var inputChannel1 = input.getChannelData(1); // input.R |
| 247 var inputChannel2 = input.getChannelData(2); // input.SL |
| 248 var inputChannel3 = input.getChannelData(3); // input.SR |
| 249 var outputChannel0 = output.getChannelData(0); |
| 250 for (i = 0; i < length; i++) { |
| 251 outputChannel0[i] += 0.25 * (inputChannel0[i] + inputChannel1[i] |
| 252 + inputChannel2[i] + inputChannel3[i]); |
| 253 } |
| 254 |
| 255 return; |
| 256 } |
| 257 |
| 258 // Down-mixing: 5.1 -> 1 |
| 259 // output += sqrt(1/2) * (input.L + input.R) + input.C |
| 260 // + 0.5 * (input.SL + input.SR) |
| 261 if (numberOfInputChannels === 6 && numberOfOutputChannels === 1) { |
| 262 var inputChannel0 = input.getChannelData(0); // input.L |
| 263 var inputChannel1 = input.getChannelData(1); // input.R |
| 264 var inputChannel2 = input.getChannelData(2); // input.C |
| 265 var inputChannel4 = input.getChannelData(4); // input.SL |
| 266 var inputChannel5 = input.getChannelData(5); // input.SR |
| 267 var outputChannel0 = output.getChannelData(0); |
| 268 var scaleSqrtHalf = Math.sqrt(0.5); |
| 269 for (i = 0; i < length; i++) { |
| 270 outputChannel0[i] += |
| 271 scaleSqrtHalf * (inputChannel0[i] + inputChannel1[i]) |
| 272 + inputChannel2[i] + 0.5 * (inputChannel4[i] + inputChannel5[i]); |
| 273 } |
| 274 |
| 275 return; |
| 276 } |
| 277 |
| 278 // Down-mixing: 4 -> 2 |
| 279 // output.L += 0.5 * (input.L + input.SL) |
| 280 // output.R += 0.5 * (input.R + input.SR) |
| 281 if (numberOfInputChannels == 4 && numberOfOutputChannels == 2) { |
| 282 var inputChannel0 = input.getChannelData(0); // input.L |
| 283 var inputChannel1 = input.getChannelData(1); // input.R |
| 284 var inputChannel2 = input.getChannelData(2); // input.SL |
| 285 var inputChannel3 = input.getChannelData(3); // input.SR |
| 286 var outputChannel0 = output.getChannelData(0); // output.L |
| 287 var outputChannel1 = output.getChannelData(1); // output.R |
| 288 for (i = 0; i < length; i++) { |
| 289 outputChannel0[i] += 0.5 * (inputChannel0[i] + inputChannel2[i]); |
| 290 outputChannel1[i] += 0.5 * (inputChannel1[i] + inputChannel3[i]); |
| 291 } |
| 292 |
| 293 return; |
| 294 } |
| 295 |
| 296 // Down-mixing: 5.1 -> 2 |
| 297 // output.L += input.L + sqrt(1/2) * (input.C + input.SL) |
| 298 // output.R += input.R + sqrt(1/2) * (input.C + input.SR) |
| 299 if (numberOfInputChannels == 6 && numberOfOutputChannels == 2) { |
| 300 var inputChannel0 = input.getChannelData(0); // input.L |
| 301 var inputChannel1 = input.getChannelData(1); // input.R |
| 302 var inputChannel2 = input.getChannelData(2); // input.C |
| 303 var inputChannel4 = input.getChannelData(4); // input.SL |
| 304 var inputChannel5 = input.getChannelData(5); // input.SR |
| 305 var outputChannel0 = output.getChannelData(0); // output.L |
| 306 var outputChannel1 = output.getChannelData(1); // output.R |
| 307 var scaleSqrtHalf = Math.sqrt(0.5); |
| 308 for (i = 0; i < length; i++) { |
| 309 outputChannel0[i] += inputChannel0[i] |
| 310 + scaleSqrtHalf * (inputChannel2[i] + inputChannel4[i]); |
| 311 outputChannel1[i] += inputChannel1[i] |
| 312 + scaleSqrtHalf * (inputChannel2[i] + inputChannel5[i]); |
| 313 } |
| 314 |
| 315 return; |
| 316 } |
| 317 |
| 318 // Down-mixing: 5.1 -> 4 |
| 319 // output.L += input.L + sqrt(1/2) * input.C |
| 320 // output.R += input.R + sqrt(1/2) * input.C |
| 321 // output.SL += input.SL |
| 322 // output.SR += input.SR |
| 323 if (numberOfInputChannels === 6 && numberOfOutputChannels === 4) { |
| 324 var inputChannel0 = input.getChannelData(0); // input.L |
| 325 var inputChannel1 = input.getChannelData(1); // input.R |
| 326 var inputChannel2 = input.getChannelData(2); // input.C |
| 327 var inputChannel4 = input.getChannelData(4); // input.SL |
| 328 var inputChannel5 = input.getChannelData(5); // input.SR |
| 329 var outputChannel0 = output.getChannelData(0); // output.L |
| 330 var outputChannel1 = output.getChannelData(1); // output.R |
| 331 var outputChannel2 = output.getChannelData(2); // output.SL |
| 332 var outputChannel3 = output.getChannelData(3); // output.SR |
| 333 var scaleSqrtHalf = Math.sqrt(0.5); |
| 334 for (i = 0; i < length; i++) { |
| 335 outputChannel0[i] += inputChannel0[i] + scaleSqrtHalf * inputChannel2[i]; |
| 336 outputChannel1[i] += inputChannel1[i] + scaleSqrtHalf * inputChannel2[i]; |
| 337 outputChannel2[i] += inputChannel4[i]; |
| 338 outputChannel3[i] += inputChannel5[i]; |
| 339 } |
| 340 |
| 341 return; |
| 342 } |
| 343 |
| 344 // All other cases, fall back to the discrete sum. |
| 345 discreteSum(input, output); |
| 346 } |
| OLD | NEW |