Complete the implementation of up/down-mixing rules for AudioNode
The majority of up/down-mixing is not implemented in AudioBus.h/cpp.
This CL is to implement missing rules and improves an existing layout
test for various channel configurations to challenge the implementation
extensively.
The spec for the mixing rule is here:
http://webaudio.github.io/web-audio-api/#channel-up-mixing-and-down-mixing
BUG=460958, 465908
TEST=LayoutTests/webaudio/audionode-channel-rules.html
Review URL: https://codereview.chromium.org/1773973002
Cr-Commit-Position: refs/heads/master@{#380790}
diff --git a/third_party/WebKit/LayoutTests/webaudio/audionode-channel-rules-expected.txt b/third_party/WebKit/LayoutTests/webaudio/audionode-channel-rules-expected.txt
index b614c5d8..a6e1ece1 100644
--- a/third_party/WebKit/LayoutTests/webaudio/audionode-channel-rules-expected.txt
+++ b/third_party/WebKit/LayoutTests/webaudio/audionode-channel-rules-expected.txt
@@ -2,6 +2,7 @@
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
PASS connections: 1, max, speakers
PASS connections: 2, max, speakers
PASS connections: 3, max, speakers
diff --git a/third_party/WebKit/LayoutTests/webaudio/audionode-channel-rules.html b/third_party/WebKit/LayoutTests/webaudio/audionode-channel-rules.html
index 7dcce63..1644c06bc9 100644
--- a/third_party/WebKit/LayoutTests/webaudio/audionode-channel-rules.html
+++ b/third_party/WebKit/LayoutTests/webaudio/audionode-channel-rules.html
@@ -4,12 +4,11 @@
<head>
<script src="../resources/js-test.js"></script>
<script src="resources/compatibility.js"></script>
-<script type="text/javascript" src="resources/audio-testing.js"></script>
+<script src="resources/audio-testing.js"></script>
+<script src="resources/mixing-rules.js"></script>
</head>
<body>
-<div id="description"></div>
-<div id="console"></div>
<script>
description("Channel mixing rules for AudioNodes.");
@@ -45,96 +44,13 @@
var numberOfTests = mixingRulesList.length * connectionsList.length;
-// 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:
-// channel0: 1 0 0 0 0 0 0 0
-// channel1: 0 1 0 0 0 0 0 0
-// channel2: 0 0 1 0 0 0 0 0
-// channel3: 0 0 0 1 0 0 0 0
-function createTestBuffer(numberOfChannels) {
- var buffer = context.createBuffer(numberOfChannels, singleTestFrameLength, context.sampleRate);
- for (var i = 0; i < numberOfChannels; ++i) {
- var data = buffer.getChannelData(i);
- data[i] = 1;
- }
- return buffer;
-}
-
-// Discrete channel interpretation mixing:
-// https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#UpMix
-// up-mix by filling channels until they run out then ignore remaining dest channels.
-// down-mix by filling as many channels as possible, then dropping remaining source channels.
-function discreteSum(sourceBuffer, destBuffer) {
- if (sourceBuffer.length != destBuffer.length) {
- alert("discreteSum(): invalid AudioBuffer!");
- return;
- }
-
- var numberOfChannels = sourceBuffer.numberOfChannels < destBuffer.numberOfChannels ? sourceBuffer.numberOfChannels : destBuffer.numberOfChannels;
- var length = numberOfChannels;
-
- for (var c = 0; c < numberOfChannels; ++c) {
- var source = sourceBuffer.getChannelData(c);
- var dest = destBuffer.getChannelData(c);
- for (var i = 0; i < length; ++i) {
- dest[i] += source[i];
- }
- }
-}
-
-// Speaker channel interpretation mixing:
-// https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#UpMix
-function speakersSum(sourceBuffer, destBuffer)
-{
- var numberOfSourceChannels = sourceBuffer.numberOfChannels;
- var numberOfDestinationChannels = destBuffer.numberOfChannels;
- var length = destBuffer.length;
-
- if (numberOfDestinationChannels == 2 && numberOfSourceChannels == 1) {
- // Handle mono -> stereo case (summing mono channel into both left and right).
- var source = sourceBuffer.getChannelData(0);
- var destL = destBuffer.getChannelData(0);
- var destR = destBuffer.getChannelData(1);
-
- for (var i = 0; i < length; ++i) {
- destL[i] += source[i];
- destR[i] += source[i];
- }
- } else if (numberOfDestinationChannels == 1 && numberOfSourceChannels == 2) {
- // Handle stereo -> mono case. output += 0.5 * (input.L + input.R).
- var sourceL = sourceBuffer.getChannelData(0);
- var sourceR = sourceBuffer.getChannelData(1);
- var dest = destBuffer.getChannelData(0);
-
- for (var i = 0; i < length; ++i) {
- dest[i] += 0.5 * (sourceL[i] + sourceR[i]);
- }
- } else if (numberOfDestinationChannels == 6 && numberOfSourceChannels == 1) {
- // Handle mono -> 5.1 case, sum mono channel into center.
- var source = sourceBuffer.getChannelData(0);
- var dest = destBuffer.getChannelData(2);
-
- for (var i = 0; i < length; ++i) {
- dest[i] += source[i];
- }
- } else if (numberOfDestinationChannels == 1 && numberOfSourceChannels == 6) {
- // Handle 5.1 -> mono.
- var sourceL = sourceBuffer.getChannelData(0);
- var sourceR = sourceBuffer.getChannelData(1);
- var sourceC = sourceBuffer.getChannelData(2);
- // skip LFE for now, according to current spec.
- var sourceSL = sourceBuffer.getChannelData(4);
- var sourceSR = sourceBuffer.getChannelData(5);
- var dest = destBuffer.getChannelData(0);
-
- for (var i = 0; i < length; ++i) {
- dest[i] += 0.7071 * (sourceL[i] + sourceR[i]) + sourceC[i] + 0.5 * (sourceSL[i] + sourceSR[i]);
- }
- } else {
- // Fallback for unknown combinations.
- discreteSum(sourceBuffer, destBuffer);
- }
+// Print out the information for an individual test case.
+function printTestInformation(testNumber, actualBuffer, expectedBuffer, frameLength, frameOffset) {
+ var actual = stringifyBuffer(actualBuffer, frameLength);
+ var expected = stringifyBuffer(expectedBuffer, frameLength, frameOffset);
+ debug('TEST CASE #' + testNumber + '\n');
+ debug('actual channels:\n' + actual);
+ debug('expected channels:\n' + expected);
}
function scheduleTest(testNumber, connections, channelCount, channelCountMode, channelInterpretation) {
@@ -160,24 +76,6 @@
}
}
-function computeNumberOfChannels(connections, channelCount, channelCountMode) {
- if (channelCountMode == "explicit")
- return channelCount;
-
- var computedNumberOfChannels = 1; // Must have at least one channel.
-
- // Compute "computedNumberOfChannels" based on all the connections.
- for (var i = 0; i < connections.length; ++i) {
- var connectionNumberOfChannels = connections.charCodeAt(i) - "0".charCodeAt(0);
- computedNumberOfChannels = Math.max(computedNumberOfChannels, connectionNumberOfChannels);
- }
-
- if (channelCountMode == "clamped-max")
- computedNumberOfChannels = Math.min(computedNumberOfChannels, channelCount);
-
- return computedNumberOfChannels;
-}
-
function checkTestResult(renderedBuffer, testNumber, connections, channelCount, channelCountMode, channelInterpretation) {
var s = "connections: " + connections + ", " + channelCountMode;
@@ -190,21 +88,6 @@
var computedNumberOfChannels = computeNumberOfChannels(connections, channelCount, channelCountMode);
- // Show rendered output for this test:
- //
- // console.log(s);
- // var sampleFrameOffset = testNumber * singleTestFrameLength;
- // for (var c = 0; c < renderNumberOfChannels; ++c) {
- // var data = renderedBuffer.getChannelData(c);
- // var s = "";
- // for (var sampleFrame = 0; sampleFrame < singleTestFrameLength; ++sampleFrame) {
- // s += data[sampleFrame + sampleFrameOffset] + " ";
- // }
- // s += "\n";
- // console.log(s);
- // }
- // return;
-
// Create a zero-initialized silent AudioBuffer with computedNumberOfChannels.
var destBuffer = context.createBuffer(computedNumberOfChannels, singleTestFrameLength, context.sampleRate);
@@ -222,6 +105,9 @@
}
}
+ // Use this when debugging mixing rules.
+ // printTestInformation(testNumber, renderedBuffer, destBuffer, singleTestFrameLength, sampleFrameOffset);
+
// Validate that destBuffer matches the rendered output.
// We need to check the rendered output at a specific sample-frame-offset corresponding
// to the specific test case we're checking for based on testNumber.
@@ -293,7 +179,7 @@
// Create test buffers from 1 to 8 channels.
testBuffers = new Array();
for (var i = 0; i < renderNumberOfChannels; ++i) {
- testBuffers[i] = createTestBuffer(i + 1);
+ testBuffers[i] = createShiftedImpulseBuffer(context, i + 1, singleTestFrameLength);
}
// Schedule all the tests.
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/mixing-rules.js b/third_party/WebKit/LayoutTests/webaudio/resources/mixing-rules.js
new file mode 100644
index 0000000..bf9a8dd4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/webaudio/resources/mixing-rules.js
@@ -0,0 +1,346 @@
+// 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) {
+ var shiftedImpulseBuffer = context.createBuffer(numberOfChannels, frameLength, context.sampleRate);
+ for (var channel = 0; channel < numberOfChannels; ++channel) {
+ var 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);
+
+ var stringifiedBuffer = '';
+ for (var channel = 0; channel < audioBuffer.numberOfChannels; ++channel) {
+ var channelData = audioBuffer.getChannelData(channel);
+ for (var 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.
+ var computedNumberOfChannels = 1;
+
+ // Compute "computedNumberOfChannels" based on all the connections.
+ for (var i = 0; i < connections.length; ++i) {
+ var 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 (var channel = 0; channel < output.numberOfChannels; ++channel) {
+ var inputChannel = input.getChannelData(channel);
+ var outputChannel = output.getChannelData(channel);
+ for (var 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 + ')';
+ }
+
+ var numberOfChannels = Math.min(input.numberOfChannels, output.numberOfChannels)
+
+ for (var channel = 0; channel < numberOfChannels; ++channel) {
+ var inputChannel = input.getChannelData(channel);
+ var outputChannel = output.getChannelData(channel);
+ for (var 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) {
+ var numberOfInputChannels = input.numberOfChannels;
+ var numberOfOutputChannels = output.numberOfChannels;
+ var 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)) {
+ var inputChannel = input.getChannelData(0);
+ var outputChannel0 = output.getChannelData(0);
+ var 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) {
+ var inputChannel = input.getChannelData(0);
+ var 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)) {
+ var inputChannel0 = input.getChannelData(0);
+ var inputChannel1 = input.getChannelData(1);
+ var outputChannel0 = output.getChannelData(0);
+ var 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) {
+ var inputChannel0 = input.getChannelData(0); // input.L
+ var inputChannel1 = input.getChannelData(1); // input.R
+ var inputChannel2 = input.getChannelData(2); // input.SL
+ var inputChannel3 = input.getChannelData(3); // input.SR
+ var outputChannel0 = output.getChannelData(0); // output.L
+ var outputChannel1 = output.getChannelData(1); // output.R
+ var outputChannel4 = output.getChannelData(4); // output.SL
+ var 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) {
+ var numberOfInputChannels = input.numberOfChannels;
+ var numberOfOutputChannels = output.numberOfChannels;
+ var i, length = output.length;
+
+ // Down-mixing: 2 -> 1
+ // output += 0.5 * (input.L + input.R)
+ if (numberOfInputChannels === 2 && numberOfOutputChannels === 1) {
+ var inputChannel0 = input.getChannelData(0); // input.L
+ var inputChannel1 = input.getChannelData(1); // input.R
+ var 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) {
+ var inputChannel0 = input.getChannelData(0); // input.L
+ var inputChannel1 = input.getChannelData(1); // input.R
+ var inputChannel2 = input.getChannelData(2); // input.SL
+ var inputChannel3 = input.getChannelData(3); // input.SR
+ var 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) {
+ var inputChannel0 = input.getChannelData(0); // input.L
+ var inputChannel1 = input.getChannelData(1); // input.R
+ var inputChannel2 = input.getChannelData(2); // input.C
+ var inputChannel4 = input.getChannelData(4); // input.SL
+ var inputChannel5 = input.getChannelData(5); // input.SR
+ var outputChannel0 = output.getChannelData(0);
+ var 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) {
+ var inputChannel0 = input.getChannelData(0); // input.L
+ var inputChannel1 = input.getChannelData(1); // input.R
+ var inputChannel2 = input.getChannelData(2); // input.SL
+ var inputChannel3 = input.getChannelData(3); // input.SR
+ var outputChannel0 = output.getChannelData(0); // output.L
+ var 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) {
+ var inputChannel0 = input.getChannelData(0); // input.L
+ var inputChannel1 = input.getChannelData(1); // input.R
+ var inputChannel2 = input.getChannelData(2); // input.C
+ var inputChannel4 = input.getChannelData(4); // input.SL
+ var inputChannel5 = input.getChannelData(5); // input.SR
+ var outputChannel0 = output.getChannelData(0); // output.L
+ var outputChannel1 = output.getChannelData(1); // output.R
+ var 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) {
+ var inputChannel0 = input.getChannelData(0); // input.L
+ var inputChannel1 = input.getChannelData(1); // input.R
+ var inputChannel2 = input.getChannelData(2); // input.C
+ var inputChannel4 = input.getChannelData(4); // input.SL
+ var inputChannel5 = input.getChannelData(5); // input.SR
+ var outputChannel0 = output.getChannelData(0); // output.L
+ var outputChannel1 = output.getChannelData(1); // output.R
+ var outputChannel2 = output.getChannelData(2); // output.SL
+ var outputChannel3 = output.getChannelData(3); // output.SR
+ var 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);
+}
diff --git a/third_party/WebKit/Source/platform/audio/AudioBus.cpp b/third_party/WebKit/Source/platform/audio/AudioBus.cpp
index 2b29e70..677b3d7 100644
--- a/third_party/WebKit/Source/platform/audio/AudioBus.cpp
+++ b/third_party/WebKit/Source/platform/audio/AudioBus.cpp
@@ -216,24 +216,9 @@
if (&sourceBus == this)
return;
- unsigned numberOfSourceChannels = sourceBus.numberOfChannels();
- unsigned numberOfDestinationChannels = numberOfChannels();
-
- if (numberOfDestinationChannels == numberOfSourceChannels) {
- for (unsigned i = 0; i < numberOfSourceChannels; ++i)
- channel(i)->copyFrom(sourceBus.channel(i));
- } else {
- switch (channelInterpretation) {
- case Speakers:
- speakersCopyFrom(sourceBus);
- break;
- case Discrete:
- discreteCopyFrom(sourceBus);
- break;
- default:
- ASSERT_NOT_REACHED();
- }
- }
+ // Copying bus is equivalent to zeroing and then summing.
+ zero();
+ sumFrom(sourceBus, channelInterpretation);
}
void AudioBus::sumFrom(const AudioBus& sourceBus, ChannelInterpretation channelInterpretation)
@@ -244,148 +229,26 @@
unsigned numberOfSourceChannels = sourceBus.numberOfChannels();
unsigned numberOfDestinationChannels = numberOfChannels();
- if (numberOfDestinationChannels == numberOfSourceChannels) {
+ // If the channel numbers are equal, perform channels-wise summing.
+ if (numberOfSourceChannels == numberOfDestinationChannels) {
for (unsigned i = 0; i < numberOfSourceChannels; ++i)
channel(i)->sumFrom(sourceBus.channel(i));
- } else {
- switch (channelInterpretation) {
- case Speakers:
- speakersSumFrom(sourceBus);
- break;
- case Discrete:
- discreteSumFrom(sourceBus);
- break;
- default:
- ASSERT_NOT_REACHED();
- }
+
+ return;
}
-}
-void AudioBus::speakersCopyFrom(const AudioBus& sourceBus)
-{
- // FIXME: Implement down mixing 5.1 to stereo.
- // https://bugs.webkit.org/show_bug.cgi?id=79192
-
- unsigned numberOfSourceChannels = sourceBus.numberOfChannels();
- unsigned numberOfDestinationChannels = numberOfChannels();
-
- if (numberOfDestinationChannels == 2 && numberOfSourceChannels == 1) {
- // Handle mono -> stereo case (for now simply copy mono channel into both left and right)
- // FIXME: Really we should apply an equal-power scaling factor here, since we're effectively panning center...
- const AudioChannel* sourceChannel = sourceBus.channel(0);
- channel(0)->copyFrom(sourceChannel);
- channel(1)->copyFrom(sourceChannel);
- } else if (numberOfDestinationChannels == 1 && numberOfSourceChannels == 2) {
- // Handle stereo -> mono case. output = 0.5 * (input.L + input.R).
- AudioBus& sourceBusSafe = const_cast<AudioBus&>(sourceBus);
-
- const float* sourceL = sourceBusSafe.channelByType(ChannelLeft)->data();
- const float* sourceR = sourceBusSafe.channelByType(ChannelRight)->data();
-
- float* destination = channelByType(ChannelLeft)->mutableData();
- vadd(sourceL, 1, sourceR, 1, destination, 1, length());
- float scale = 0.5;
- vsmul(destination, 1, &scale, destination, 1, length());
- } else if (numberOfDestinationChannels == 6 && numberOfSourceChannels == 1) {
- // Handle mono -> 5.1 case, copy mono channel to center.
- channel(2)->copyFrom(sourceBus.channel(0));
- channel(0)->zero();
- channel(1)->zero();
- channel(3)->zero();
- channel(4)->zero();
- channel(5)->zero();
- } else if (numberOfDestinationChannels == 1 && numberOfSourceChannels == 6) {
- // Handle 5.1 -> mono case.
- zero();
- speakersSumFrom5_1_ToMono(sourceBus);
- } else {
- // Fallback for unknown combinations.
- discreteCopyFrom(sourceBus);
- }
-}
-
-void AudioBus::speakersSumFrom(const AudioBus& sourceBus)
-{
- // FIXME: Implement down mixing 5.1 to stereo.
- // https://bugs.webkit.org/show_bug.cgi?id=79192
-
- unsigned numberOfSourceChannels = sourceBus.numberOfChannels();
- unsigned numberOfDestinationChannels = numberOfChannels();
-
- if (numberOfDestinationChannels == 2 && numberOfSourceChannels == 1) {
- // Handle mono -> stereo case (summing mono channel into both left and right).
- const AudioChannel* sourceChannel = sourceBus.channel(0);
- channel(0)->sumFrom(sourceChannel);
- channel(1)->sumFrom(sourceChannel);
- } else if (numberOfDestinationChannels == 1 && numberOfSourceChannels == 2) {
- // Handle stereo -> mono case. output += 0.5 * (input.L + input.R).
- AudioBus& sourceBusSafe = const_cast<AudioBus&>(sourceBus);
-
- const float* sourceL = sourceBusSafe.channelByType(ChannelLeft)->data();
- const float* sourceR = sourceBusSafe.channelByType(ChannelRight)->data();
-
- float* destination = channelByType(ChannelLeft)->mutableData();
- float scale = 0.5;
- vsma(sourceL, 1, &scale, destination, 1, length());
- vsma(sourceR, 1, &scale, destination, 1, length());
- } else if (numberOfDestinationChannels == 6 && numberOfSourceChannels == 1) {
- // Handle mono -> 5.1 case, sum mono channel into center.
- channel(2)->sumFrom(sourceBus.channel(0));
- } else if (numberOfDestinationChannels == 1 && numberOfSourceChannels == 6) {
- // Handle 5.1 -> mono case.
- speakersSumFrom5_1_ToMono(sourceBus);
- } else {
- // Fallback for unknown combinations.
+ // Otherwise perform up/down-mix or the discrete transfer based on the
+ // number of channels and the channel interpretation.
+ switch (channelInterpretation) {
+ case Speakers:
+ if (numberOfSourceChannels < numberOfDestinationChannels)
+ sumFromByUpMixing(sourceBus);
+ else
+ sumFromByDownMixing(sourceBus);
+ break;
+ case Discrete:
discreteSumFrom(sourceBus);
- }
-}
-
-void AudioBus::speakersSumFrom5_1_ToMono(const AudioBus& sourceBus)
-{
- AudioBus& sourceBusSafe = const_cast<AudioBus&>(sourceBus);
-
- const float* sourceL = sourceBusSafe.channelByType(ChannelLeft)->data();
- const float* sourceR = sourceBusSafe.channelByType(ChannelRight)->data();
- const float* sourceC = sourceBusSafe.channelByType(ChannelCenter)->data();
- const float* sourceSL = sourceBusSafe.channelByType(ChannelSurroundLeft)->data();
- const float* sourceSR = sourceBusSafe.channelByType(ChannelSurroundRight)->data();
-
- float* destination = channelByType(ChannelLeft)->mutableData();
-
- AudioFloatArray temp(length());
- float* tempData = temp.data();
-
- // Sum in L and R.
- vadd(sourceL, 1, sourceR, 1, tempData, 1, length());
- float scale = 0.7071;
- vsmul(tempData, 1, &scale, tempData, 1, length());
- vadd(tempData, 1, destination, 1, destination, 1, length());
-
- // Sum in SL and SR.
- vadd(sourceSL, 1, sourceSR, 1, tempData, 1, length());
- scale = 0.5;
- vsmul(tempData, 1, &scale, tempData, 1, length());
- vadd(tempData, 1, destination, 1, destination, 1, length());
-
- // Sum in center.
- vadd(sourceC, 1, destination, 1, destination, 1, length());
-}
-
-void AudioBus::discreteCopyFrom(const AudioBus& sourceBus)
-{
- unsigned numberOfSourceChannels = sourceBus.numberOfChannels();
- unsigned numberOfDestinationChannels = numberOfChannels();
-
- if (numberOfDestinationChannels < numberOfSourceChannels) {
- // Down-mix by copying channels and dropping the remaining.
- for (unsigned i = 0; i < numberOfDestinationChannels; ++i)
- channel(i)->copyFrom(sourceBus.channel(i));
- } else if (numberOfDestinationChannels > numberOfSourceChannels) {
- // Up-mix by copying as many channels as we have, then zeroing remaining channels.
- for (unsigned i = 0; i < numberOfSourceChannels; ++i)
- channel(i)->copyFrom(sourceBus.channel(i));
- for (unsigned i = numberOfSourceChannels; i < numberOfDestinationChannels; ++i)
- channel(i)->zero();
+ break;
}
}
@@ -405,6 +268,172 @@
}
}
+void AudioBus::sumFromByUpMixing(const AudioBus& sourceBus)
+{
+ unsigned numberOfSourceChannels = sourceBus.numberOfChannels();
+ unsigned numberOfDestinationChannels = numberOfChannels();
+
+ if ((numberOfSourceChannels == 1 && numberOfDestinationChannels == 2) || (numberOfSourceChannels == 1 && numberOfDestinationChannels == 4)) {
+ // 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)
+ const AudioChannel* sourceL = sourceBus.channelByType(ChannelLeft);
+ channelByType(ChannelLeft)->sumFrom(sourceL);
+ channelByType(ChannelRight)->sumFrom(sourceL);
+ } else if (numberOfSourceChannels == 1 && numberOfDestinationChannels == 6) {
+ // Up-mixing: 1 -> 5.1
+ // output.L = 0
+ // output.R = 0
+ // output.C = input (put in center channel)
+ // output.LFE = 0
+ // output.SL = 0
+ // output.SR = 0
+ channelByType(ChannelCenter)->sumFrom(sourceBus.channelByType(ChannelLeft));
+ } else if ((numberOfSourceChannels == 2 && numberOfDestinationChannels == 4) || (numberOfSourceChannels == 2 && numberOfDestinationChannels == 6)) {
+ // 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
+ channelByType(ChannelLeft)->sumFrom(sourceBus.channelByType(ChannelLeft));
+ channelByType(ChannelRight)->sumFrom(sourceBus.channelByType(ChannelRight));
+ } else if (numberOfSourceChannels == 4 && numberOfDestinationChannels == 6) {
+ // 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
+ channelByType(ChannelLeft)->sumFrom(sourceBus.channelByType(ChannelLeft));
+ channelByType(ChannelRight)->sumFrom(sourceBus.channelByType(ChannelRight));
+ channelByType(ChannelSurroundLeft)->sumFrom(sourceBus.channelByType(ChannelSurroundLeft));
+ channelByType(ChannelSurroundRight)->sumFrom(sourceBus.channelByType(ChannelSurroundRight));
+ } else {
+ // All other cases, fall back to the discrete sum. This will silence the
+ // excessive channels.
+ discreteSumFrom(sourceBus);
+ }
+}
+
+void AudioBus::sumFromByDownMixing(const AudioBus& sourceBus)
+{
+ unsigned numberOfSourceChannels = sourceBus.numberOfChannels();
+ unsigned numberOfDestinationChannels = numberOfChannels();
+
+ if (numberOfSourceChannels == 2 && numberOfDestinationChannels == 1) {
+ // Down-mixing: 2 -> 1
+ // output = 0.5 * (input.L + input.R)
+ const float* sourceL = sourceBus.channelByType(ChannelLeft)->data();
+ const float* sourceR = sourceBus.channelByType(ChannelRight)->data();
+
+ float* destination = channelByType(ChannelLeft)->mutableData();
+ float scale = 0.5;
+
+ vsma(sourceL, 1, &scale, destination, 1, length());
+ vsma(sourceR, 1, &scale, destination, 1, length());
+ } else if (numberOfSourceChannels == 4 && numberOfDestinationChannels == 1) {
+ // Down-mixing: 4 -> 1
+ // output = 0.25 * (input.L + input.R + input.SL + input.SR)
+ const float* sourceL = sourceBus.channelByType(ChannelLeft)->data();
+ const float* sourceR = sourceBus.channelByType(ChannelRight)->data();
+ const float* sourceSL = sourceBus.channelByType(ChannelSurroundLeft)->data();
+ const float* sourceSR = sourceBus.channelByType(ChannelSurroundRight)->data();
+
+ float* destination = channelByType(ChannelLeft)->mutableData();
+ float scale = 0.25;
+
+ vsma(sourceL, 1, &scale, destination, 1, length());
+ vsma(sourceR, 1, &scale, destination, 1, length());
+ vsma(sourceSL, 1, &scale, destination, 1, length());
+ vsma(sourceSR, 1, &scale, destination, 1, length());
+ } else if (numberOfSourceChannels == 6 && numberOfDestinationChannels == 1) {
+ // Down-mixing: 5.1 -> 1
+ // output = sqrt(1/2) * (input.L + input.R) + input.C
+ // + 0.5 * (input.SL + input.SR)
+ const float* sourceL = sourceBus.channelByType(ChannelLeft)->data();
+ const float* sourceR = sourceBus.channelByType(ChannelRight)->data();
+ const float* sourceC = sourceBus.channelByType(ChannelCenter)->data();
+ const float* sourceSL = sourceBus.channelByType(ChannelSurroundLeft)->data();
+ const float* sourceSR = sourceBus.channelByType(ChannelSurroundRight)->data();
+
+ float* destination = channelByType(ChannelLeft)->mutableData();
+ float scaleSqrtHalf = sqrtf(0.5);
+ float scaleHalf = 0.5;
+
+ vsma(sourceL, 1, &scaleSqrtHalf, destination, 1, length());
+ vsma(sourceR, 1, &scaleSqrtHalf, destination, 1, length());
+ vadd(sourceC, 1, destination, 1, destination, 1, length());
+ vsma(sourceSL, 1, &scaleHalf, destination, 1, length());
+ vsma(sourceSR, 1, &scaleHalf, destination, 1, length());
+ } else if (numberOfSourceChannels == 4 && numberOfDestinationChannels == 2) {
+ // Down-mixing: 4 -> 2
+ // output.L = 0.5 * (input.L + input.SL)
+ // output.R = 0.5 * (input.R + input.SR)
+ const float* sourceL = sourceBus.channelByType(ChannelLeft)->data();
+ const float* sourceR = sourceBus.channelByType(ChannelRight)->data();
+ const float* sourceSL = sourceBus.channelByType(ChannelSurroundLeft)->data();
+ const float* sourceSR = sourceBus.channelByType(ChannelSurroundRight)->data();
+
+ float* destinationL = channelByType(ChannelLeft)->mutableData();
+ float* destinationR = channelByType(ChannelRight)->mutableData();
+ float scaleHalf = 0.5;
+
+ vsma(sourceL, 1, &scaleHalf, destinationL, 1, length());
+ vsma(sourceSL, 1, &scaleHalf, destinationL, 1, length());
+ vsma(sourceR, 1, &scaleHalf, destinationR, 1, length());
+ vsma(sourceSR, 1, &scaleHalf, destinationR, 1, length());
+ } else if (numberOfSourceChannels == 6 && numberOfDestinationChannels == 2) {
+ // 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)
+ const float* sourceL = sourceBus.channelByType(ChannelLeft)->data();
+ const float* sourceR = sourceBus.channelByType(ChannelRight)->data();
+ const float* sourceC = sourceBus.channelByType(ChannelCenter)->data();
+ const float* sourceSL = sourceBus.channelByType(ChannelSurroundLeft)->data();
+ const float* sourceSR = sourceBus.channelByType(ChannelSurroundRight)->data();
+
+ float* destinationL = channelByType(ChannelLeft)->mutableData();
+ float* destinationR = channelByType(ChannelRight)->mutableData();
+ float scaleSqrtHalf = sqrtf(0.5);
+
+ vadd(sourceL, 1, destinationL, 1, destinationL, 1, length());
+ vsma(sourceC, 1, &scaleSqrtHalf, destinationL, 1, length());
+ vsma(sourceSL, 1, &scaleSqrtHalf, destinationL, 1, length());
+ vadd(sourceR, 1, destinationR, 1, destinationR, 1, length());
+ vsma(sourceC, 1, &scaleSqrtHalf, destinationR, 1, length());
+ vsma(sourceSR, 1, &scaleSqrtHalf, destinationR, 1, length());
+ } else if (numberOfSourceChannels == 6 && numberOfDestinationChannels == 4) {
+ // 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
+ const float* sourceL = sourceBus.channelByType(ChannelLeft)->data();
+ const float* sourceR = sourceBus.channelByType(ChannelRight)->data();
+ const float* sourceC = sourceBus.channelByType(ChannelCenter)->data();
+
+ float* destinationL = channelByType(ChannelLeft)->mutableData();
+ float* destinationR = channelByType(ChannelRight)->mutableData();
+ float scaleSqrtHalf = sqrtf(0.5);
+
+ vadd(sourceL, 1, destinationL, 1, destinationL, 1, length());
+ vsma(sourceC, 1, &scaleSqrtHalf, destinationL, 1, length());
+ vadd(sourceR, 1, destinationR, 1, destinationR, 1, length());
+ vsma(sourceC, 1, &scaleSqrtHalf, destinationR, 1, length());
+ channel(2)->sumFrom(sourceBus.channel(4));
+ channel(3)->sumFrom(sourceBus.channel(5));
+ } else {
+ // All other cases, fall back to the discrete sum. This will perform
+ // channel-wise sum until the destination channels run out.
+ discreteSumFrom(sourceBus);
+ }
+}
+
void AudioBus::copyWithGainFrom(const AudioBus &sourceBus, float* lastMixGain, float targetGain)
{
if (!topologyMatches(sourceBus)) {
diff --git a/third_party/WebKit/Source/platform/audio/AudioBus.h b/third_party/WebKit/Source/platform/audio/AudioBus.h
index 5657f4f7..eb20236 100644
--- a/third_party/WebKit/Source/platform/audio/AudioBus.h
+++ b/third_party/WebKit/Source/platform/audio/AudioBus.h
@@ -151,11 +151,12 @@
AudioBus(unsigned numberOfChannels, size_t length, bool allocate);
- void speakersCopyFrom(const AudioBus&);
- void discreteCopyFrom(const AudioBus&);
- void speakersSumFrom(const AudioBus&);
void discreteSumFrom(const AudioBus&);
- void speakersSumFrom5_1_ToMono(const AudioBus&);
+
+ // Up/down-mix by in-place summing upon the existing channel content.
+ // http://webaudio.github.io/web-audio-api/#channel-up-mixing-and-down-mixing
+ void sumFromByUpMixing(const AudioBus&);
+ void sumFromByDownMixing(const AudioBus&);
size_t m_length;
Vector<OwnPtr<AudioChannel>> m_channels;