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