| // Utilities for mixing rule testing. |
| // http://webaudio.github.io/web-audio-api/#channel-up-mixing-and-down-mixing |
| |
| |
| /** |
| * Create an n-channel buffer, with all sample data zero except for a shifted |
| * impulse. The impulse position depends on the channel index. For example, for |
| * a 4-channel buffer: |
| * channel 0: 1 0 0 0 0 0 0 0 |
| * channel 1: 0 1 0 0 0 0 0 0 |
| * channel 2: 0 0 1 0 0 0 0 0 |
| * channel 3: 0 0 0 1 0 0 0 0 |
| * @param {AudioContext} context Associated AudioContext. |
| * @param {Number} numberOfChannels Number of channels of test buffer. |
| * @param {Number} frameLength Buffer length in frames. |
| * @return {AudioBuffer} |
| */ |
| function createShiftedImpulseBuffer(context, numberOfChannels, frameLength) { |
| let shiftedImpulseBuffer = |
| context.createBuffer(numberOfChannels, frameLength, context.sampleRate); |
| for (let channel = 0; channel < numberOfChannels; ++channel) { |
| let data = shiftedImpulseBuffer.getChannelData(channel); |
| data[channel] = 1; |
| } |
| |
| return shiftedImpulseBuffer; |
| } |
| |
| /** |
| * Create a string that displays the content of AudioBuffer. |
| * @param {AudioBuffer} audioBuffer AudioBuffer object to stringify. |
| * @param {Number} frameLength Number of frames to be printed. |
| * @param {Number} frameOffset Starting frame position for printing. |
| * @return {String} |
| */ |
| function stringifyBuffer(audioBuffer, frameLength, frameOffset) { |
| frameOffset = (frameOffset || 0); |
| |
| let stringifiedBuffer = ''; |
| for (let channel = 0; channel < audioBuffer.numberOfChannels; ++channel) { |
| let channelData = audioBuffer.getChannelData(channel); |
| for (let i = 0; i < frameLength; ++i) |
| stringifiedBuffer += channelData[i + frameOffset] + ' '; |
| stringifiedBuffer += '\n'; |
| } |
| |
| return stringifiedBuffer; |
| } |
| |
| /** |
| * Compute number of channels from the connection. |
| * http://webaudio.github.io/web-audio-api/#dfn-computednumberofchannels |
| * @param {String} connections A string specifies the connection. For |
| * example, the string "128" means 3 |
| * connections, having 1, 2, and 8 channels |
| * respectively. |
| * @param {Number} channelCount Channel count. |
| * @param {String} channelCountMode Channel count mode. |
| * @return {Number} Computed number of channels. |
| */ |
| function computeNumberOfChannels(connections, channelCount, channelCountMode) { |
| if (channelCountMode == 'explicit') |
| return channelCount; |
| |
| // Must have at least one channel. |
| let computedNumberOfChannels = 1; |
| |
| // Compute "computedNumberOfChannels" based on all the connections. |
| for (let i = 0; i < connections.length; ++i) { |
| let connectionNumberOfChannels = parseInt(connections[i]); |
| computedNumberOfChannels = |
| Math.max(computedNumberOfChannels, connectionNumberOfChannels); |
| } |
| |
| if (channelCountMode == 'clamped-max') |
| computedNumberOfChannels = Math.min(computedNumberOfChannels, channelCount); |
| |
| return computedNumberOfChannels; |
| } |
| |
| /** |
| * Apply up/down-mixing (in-place summing) based on 'speaker' interpretation. |
| * @param {AudioBuffer} input Input audio buffer. |
| * @param {AudioBuffer} output Output audio buffer. |
| */ |
| function speakersSum(input, output) { |
| if (input.length != output.length) { |
| throw '[mixing-rules.js] speakerSum(): buffer lengths mismatch (input: ' + |
| input.length + ', output: ' + output.length + ')'; |
| } |
| |
| if (input.numberOfChannels === output.numberOfChannels) { |
| for (let channel = 0; channel < output.numberOfChannels; ++channel) { |
| let inputChannel = input.getChannelData(channel); |
| let outputChannel = output.getChannelData(channel); |
| for (let i = 0; i < outputChannel.length; i++) |
| outputChannel[i] += inputChannel[i]; |
| } |
| } else if (input.numberOfChannels < output.numberOfChannels) { |
| processUpMix(input, output); |
| } else { |
| processDownMix(input, output); |
| } |
| } |
| |
| /** |
| * In-place summing to |output| based on 'discrete' channel interpretation. |
| * @param {AudioBuffer} input Input audio buffer. |
| * @param {AudioBuffer} output Output audio buffer. |
| */ |
| function discreteSum(input, output) { |
| if (input.length != output.length) { |
| throw '[mixing-rules.js] speakerSum(): buffer lengths mismatch (input: ' + |
| input.length + ', output: ' + output.length + ')'; |
| } |
| |
| let numberOfChannels = |
| Math.min(input.numberOfChannels, output.numberOfChannels) |
| |
| for (let channel = 0; channel < numberOfChannels; ++channel) { |
| let inputChannel = input.getChannelData(channel); |
| let outputChannel = output.getChannelData(channel); |
| for (let i = 0; i < outputChannel.length; i++) |
| outputChannel[i] += inputChannel[i]; |
| } |
| } |
| |
| /** |
| * Perform up-mix by in-place summing to |output| buffer. |
| * @param {AudioBuffer} input Input audio buffer. |
| * @param {AudioBuffer} output Output audio buffer. |
| */ |
| function processUpMix(input, output) { |
| let numberOfInputChannels = input.numberOfChannels; |
| let numberOfOutputChannels = output.numberOfChannels; |
| let i, length = output.length; |
| |
| // Up-mixing: 1 -> 2, 1 -> 4 |
| // output.L += input |
| // output.R += input |
| // output.SL += 0 (in the case of 1 -> 4) |
| // output.SR += 0 (in the case of 1 -> 4) |
| if ((numberOfInputChannels === 1 && numberOfOutputChannels === 2) || |
| (numberOfInputChannels === 1 && numberOfOutputChannels === 4)) { |
| let inputChannel = input.getChannelData(0); |
| let outputChannel0 = output.getChannelData(0); |
| let outputChannel1 = output.getChannelData(1); |
| for (i = 0; i < length; i++) { |
| outputChannel0[i] += inputChannel[i]; |
| outputChannel1[i] += inputChannel[i]; |
| } |
| |
| return; |
| } |
| |
| // Up-mixing: 1 -> 5.1 |
| // output.L += 0 |
| // output.R += 0 |
| // output.C += input |
| // output.LFE += 0 |
| // output.SL += 0 |
| // output.SR += 0 |
| if (numberOfInputChannels == 1 && numberOfOutputChannels == 6) { |
| let inputChannel = input.getChannelData(0); |
| let outputChannel2 = output.getChannelData(2); |
| for (i = 0; i < length; i++) |
| outputChannel2[i] += inputChannel[i]; |
| |
| return; |
| } |
| |
| // Up-mixing: 2 -> 4, 2 -> 5.1 |
| // output.L += input.L |
| // output.R += input.R |
| // output.C += 0 (in the case of 2 -> 5.1) |
| // output.LFE += 0 (in the case of 2 -> 5.1) |
| // output.SL += 0 |
| // output.SR += 0 |
| if ((numberOfInputChannels === 2 && numberOfOutputChannels === 4) || |
| (numberOfInputChannels === 2 && numberOfOutputChannels === 6)) { |
| let inputChannel0 = input.getChannelData(0); |
| let inputChannel1 = input.getChannelData(1); |
| let outputChannel0 = output.getChannelData(0); |
| let outputChannel1 = output.getChannelData(1); |
| for (i = 0; i < length; i++) { |
| outputChannel0[i] += inputChannel0[i]; |
| outputChannel1[i] += inputChannel1[i]; |
| } |
| |
| return; |
| } |
| |
| // Up-mixing: 4 -> 5.1 |
| // output.L += input.L |
| // output.R += input.R |
| // output.C += 0 |
| // output.LFE += 0 |
| // output.SL += input.SL |
| // output.SR += input.SR |
| if (numberOfInputChannels === 4 && numberOfOutputChannels === 6) { |
| let inputChannel0 = input.getChannelData(0); // input.L |
| let inputChannel1 = input.getChannelData(1); // input.R |
| let inputChannel2 = input.getChannelData(2); // input.SL |
| let inputChannel3 = input.getChannelData(3); // input.SR |
| let outputChannel0 = output.getChannelData(0); // output.L |
| let outputChannel1 = output.getChannelData(1); // output.R |
| let outputChannel4 = output.getChannelData(4); // output.SL |
| let outputChannel5 = output.getChannelData(5); // output.SR |
| for (i = 0; i < length; i++) { |
| outputChannel0[i] += inputChannel0[i]; |
| outputChannel1[i] += inputChannel1[i]; |
| outputChannel4[i] += inputChannel2[i]; |
| outputChannel5[i] += inputChannel3[i]; |
| } |
| |
| return; |
| } |
| |
| // All other cases, fall back to the discrete sum. |
| discreteSum(input, output); |
| } |
| |
| /** |
| * Perform down-mix by in-place summing to |output| buffer. |
| * @param {AudioBuffer} input Input audio buffer. |
| * @param {AudioBuffer} output Output audio buffer. |
| */ |
| function processDownMix(input, output) { |
| let numberOfInputChannels = input.numberOfChannels; |
| let numberOfOutputChannels = output.numberOfChannels; |
| let i, length = output.length; |
| |
| // Down-mixing: 2 -> 1 |
| // output += 0.5 * (input.L + input.R) |
| if (numberOfInputChannels === 2 && numberOfOutputChannels === 1) { |
| let inputChannel0 = input.getChannelData(0); // input.L |
| let inputChannel1 = input.getChannelData(1); // input.R |
| let outputChannel0 = output.getChannelData(0); |
| for (i = 0; i < length; i++) |
| outputChannel0[i] += 0.5 * (inputChannel0[i] + inputChannel1[i]); |
| |
| return; |
| } |
| |
| // Down-mixing: 4 -> 1 |
| // output += 0.25 * (input.L + input.R + input.SL + input.SR) |
| if (numberOfInputChannels === 4 && numberOfOutputChannels === 1) { |
| let inputChannel0 = input.getChannelData(0); // input.L |
| let inputChannel1 = input.getChannelData(1); // input.R |
| let inputChannel2 = input.getChannelData(2); // input.SL |
| let inputChannel3 = input.getChannelData(3); // input.SR |
| let outputChannel0 = output.getChannelData(0); |
| for (i = 0; i < length; i++) { |
| outputChannel0[i] += 0.25 * |
| (inputChannel0[i] + inputChannel1[i] + inputChannel2[i] + |
| inputChannel3[i]); |
| } |
| |
| return; |
| } |
| |
| // Down-mixing: 5.1 -> 1 |
| // output += sqrt(1/2) * (input.L + input.R) + input.C |
| // + 0.5 * (input.SL + input.SR) |
| if (numberOfInputChannels === 6 && numberOfOutputChannels === 1) { |
| let inputChannel0 = input.getChannelData(0); // input.L |
| let inputChannel1 = input.getChannelData(1); // input.R |
| let inputChannel2 = input.getChannelData(2); // input.C |
| let inputChannel4 = input.getChannelData(4); // input.SL |
| let inputChannel5 = input.getChannelData(5); // input.SR |
| let outputChannel0 = output.getChannelData(0); |
| let scaleSqrtHalf = Math.sqrt(0.5); |
| for (i = 0; i < length; i++) { |
| outputChannel0[i] += |
| scaleSqrtHalf * (inputChannel0[i] + inputChannel1[i]) + |
| inputChannel2[i] + 0.5 * (inputChannel4[i] + inputChannel5[i]); |
| } |
| |
| return; |
| } |
| |
| // Down-mixing: 4 -> 2 |
| // output.L += 0.5 * (input.L + input.SL) |
| // output.R += 0.5 * (input.R + input.SR) |
| if (numberOfInputChannels == 4 && numberOfOutputChannels == 2) { |
| let inputChannel0 = input.getChannelData(0); // input.L |
| let inputChannel1 = input.getChannelData(1); // input.R |
| let inputChannel2 = input.getChannelData(2); // input.SL |
| let inputChannel3 = input.getChannelData(3); // input.SR |
| let outputChannel0 = output.getChannelData(0); // output.L |
| let outputChannel1 = output.getChannelData(1); // output.R |
| for (i = 0; i < length; i++) { |
| outputChannel0[i] += 0.5 * (inputChannel0[i] + inputChannel2[i]); |
| outputChannel1[i] += 0.5 * (inputChannel1[i] + inputChannel3[i]); |
| } |
| |
| return; |
| } |
| |
| // Down-mixing: 5.1 -> 2 |
| // output.L += input.L + sqrt(1/2) * (input.C + input.SL) |
| // output.R += input.R + sqrt(1/2) * (input.C + input.SR) |
| if (numberOfInputChannels == 6 && numberOfOutputChannels == 2) { |
| let inputChannel0 = input.getChannelData(0); // input.L |
| let inputChannel1 = input.getChannelData(1); // input.R |
| let inputChannel2 = input.getChannelData(2); // input.C |
| let inputChannel4 = input.getChannelData(4); // input.SL |
| let inputChannel5 = input.getChannelData(5); // input.SR |
| let outputChannel0 = output.getChannelData(0); // output.L |
| let outputChannel1 = output.getChannelData(1); // output.R |
| let scaleSqrtHalf = Math.sqrt(0.5); |
| for (i = 0; i < length; i++) { |
| outputChannel0[i] += inputChannel0[i] + |
| scaleSqrtHalf * (inputChannel2[i] + inputChannel4[i]); |
| outputChannel1[i] += inputChannel1[i] + |
| scaleSqrtHalf * (inputChannel2[i] + inputChannel5[i]); |
| } |
| |
| return; |
| } |
| |
| // Down-mixing: 5.1 -> 4 |
| // output.L += input.L + sqrt(1/2) * input.C |
| // output.R += input.R + sqrt(1/2) * input.C |
| // output.SL += input.SL |
| // output.SR += input.SR |
| if (numberOfInputChannels === 6 && numberOfOutputChannels === 4) { |
| let inputChannel0 = input.getChannelData(0); // input.L |
| let inputChannel1 = input.getChannelData(1); // input.R |
| let inputChannel2 = input.getChannelData(2); // input.C |
| let inputChannel4 = input.getChannelData(4); // input.SL |
| let inputChannel5 = input.getChannelData(5); // input.SR |
| let outputChannel0 = output.getChannelData(0); // output.L |
| let outputChannel1 = output.getChannelData(1); // output.R |
| let outputChannel2 = output.getChannelData(2); // output.SL |
| let outputChannel3 = output.getChannelData(3); // output.SR |
| let scaleSqrtHalf = Math.sqrt(0.5); |
| for (i = 0; i < length; i++) { |
| outputChannel0[i] += inputChannel0[i] + scaleSqrtHalf * inputChannel2[i]; |
| outputChannel1[i] += inputChannel1[i] + scaleSqrtHalf * inputChannel2[i]; |
| outputChannel2[i] += inputChannel4[i]; |
| outputChannel3[i] += inputChannel5[i]; |
| } |
| |
| return; |
| } |
| |
| // All other cases, fall back to the discrete sum. |
| discreteSum(input, output); |
| } |