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