summaryrefslogtreecommitdiffstats
path: root/media/base
diff options
context:
space:
mode:
authorjrummell@chromium.org <jrummell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-26 08:36:41 +0000
committerjrummell@chromium.org <jrummell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-26 08:36:41 +0000
commit9f3b46d0a7413e88907ee8d89de0a6a648548fda (patch)
tree1778c88bda8146137a9fe752e2db4cd3e33b1726 /media/base
parent1dc8ff9ddc29df8a60e9246f99ce9fa20e50cbd4 (diff)
downloadchromium_src-9f3b46d0a7413e88907ee8d89de0a6a648548fda.zip
chromium_src-9f3b46d0a7413e88907ee8d89de0a6a648548fda.tar.gz
chromium_src-9f3b46d0a7413e88907ee8d89de0a6a648548fda.tar.bz2
Refactor creation of the transform matrix in channel_mixer
BUG= Review URL: https://chromiumcodereview.appspot.com/12880008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190598 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/base')
-rw-r--r--media/base/channel_mixer.cc149
-rw-r--r--media/base/channel_mixer.h27
-rw-r--r--media/base/channel_mixer_unittest.cc72
3 files changed, 165 insertions, 83 deletions
diff --git a/media/base/channel_mixer.cc b/media/base/channel_mixer.cc
index 9f8f57a..3de63fe 100644
--- a/media/base/channel_mixer.cc
+++ b/media/base/channel_mixer.cc
@@ -53,6 +53,65 @@ static void ValidateLayout(ChannelLayout layout) {
return;
}
+class MatrixBuilder {
+ public:
+ MatrixBuilder(ChannelLayout input_layout, int input_channels,
+ ChannelLayout output_layout, int output_channels)
+ : input_layout_(input_layout),
+ input_channels_(input_channels),
+ output_layout_(output_layout),
+ output_channels_(output_channels) {
+ // Special case for 5.0, 5.1 with back channels when upmixed to 7.0, 7.1,
+ // which should map the back LR to side LR.
+ if (input_layout_ == CHANNEL_LAYOUT_5_0_BACK &&
+ output_layout_ == CHANNEL_LAYOUT_7_0) {
+ input_layout_ = CHANNEL_LAYOUT_5_0;
+ } else if (input_layout_ == CHANNEL_LAYOUT_5_1_BACK &&
+ output_layout_ == CHANNEL_LAYOUT_7_1) {
+ input_layout_ = CHANNEL_LAYOUT_5_1;
+ }
+ }
+
+ ~MatrixBuilder() { }
+
+ // Create the transformation matrix of input channels to output channels.
+ // Updates the empty matrix with the transformation, and returns true
+ // if the transformation is just a remapping of channels (no mixing).
+ bool CreateTransformationMatrix(std::vector< std::vector<float> >* matrix);
+
+ private:
+ // Result transformation of input channels to output channels
+ std::vector< std::vector<float> >* matrix_;
+
+ // Input and output channel layout provided during construction.
+ ChannelLayout input_layout_;
+ int input_channels_;
+ ChannelLayout output_layout_;
+ int output_channels_;
+
+ // Helper variable for tracking which inputs are currently unaccounted,
+ // should be empty after construction completes.
+ std::vector<Channels> unaccounted_inputs_;
+
+ // Helper methods for managing unaccounted input channels.
+ void AccountFor(Channels ch);
+ bool IsUnaccounted(Channels ch);
+
+ // Helper methods for checking if |ch| exists in either |input_layout_| or
+ // |output_layout_| respectively.
+ bool HasInputChannel(Channels ch);
+ bool HasOutputChannel(Channels ch);
+
+ // Helper methods for updating |matrix_| with the proper value for
+ // mixing |input_ch| into |output_ch|. MixWithoutAccounting() does not
+ // remove the channel from |unaccounted_inputs_|.
+ void Mix(Channels input_ch, Channels output_ch, float scale);
+ void MixWithoutAccounting(Channels input_ch, Channels output_ch,
+ float scale);
+
+ DISALLOW_COPY_AND_ASSIGN(MatrixBuilder);
+};
+
ChannelMixer::ChannelMixer(ChannelLayout input_layout,
ChannelLayout output_layout) {
Initialize(input_layout,
@@ -72,22 +131,29 @@ ChannelMixer::ChannelMixer(
void ChannelMixer::Initialize(
ChannelLayout input_layout, int input_channels,
ChannelLayout output_layout, int output_channels) {
- input_layout_ = input_layout;
- output_layout_ = output_layout;
- remapping_ = false;
-
// Stereo down mix should never be the output layout.
- CHECK_NE(output_layout_, CHANNEL_LAYOUT_STEREO_DOWNMIX);
+ CHECK_NE(output_layout, CHANNEL_LAYOUT_STEREO_DOWNMIX);
+
+ // Verify that the layouts are supported
+ if (input_layout != CHANNEL_LAYOUT_DISCRETE)
+ ValidateLayout(input_layout);
+ if (output_layout != CHANNEL_LAYOUT_DISCRETE)
+ ValidateLayout(output_layout);
+
+ // Create the transformation matrix
+ MatrixBuilder matrix_builder(input_layout, input_channels,
+ output_layout, output_channels);
+ remapping_ = matrix_builder.CreateTransformationMatrix(&matrix_);
+}
- if (input_layout_ != CHANNEL_LAYOUT_DISCRETE)
- ValidateLayout(input_layout_);
- if (output_layout_ != CHANNEL_LAYOUT_DISCRETE)
- ValidateLayout(output_layout_);
+bool MatrixBuilder::CreateTransformationMatrix(
+ std::vector< std::vector<float> >* matrix) {
+ matrix_ = matrix;
// Size out the initial matrix.
- matrix_.reserve(output_channels);
- for (int output_ch = 0; output_ch < output_channels; ++output_ch)
- matrix_.push_back(std::vector<float>(input_channels, 0));
+ matrix_->reserve(output_channels_);
+ for (int output_ch = 0; output_ch < output_channels_; ++output_ch)
+ matrix_->push_back(std::vector<float>(input_channels_, 0));
// First check for discrete case.
if (input_layout_ == CHANNEL_LAYOUT_DISCRETE ||
@@ -96,49 +162,36 @@ void ChannelMixer::Initialize(
// copy as many as we can then drop the remaining input channels.
// If the number of input channels is less than output channels, then
// copy them all, then zero out the remaining output channels.
- int passthrough_channels = std::min(input_channels, output_channels);
+ int passthrough_channels = std::min(input_channels_, output_channels_);
for (int i = 0; i < passthrough_channels; ++i)
- matrix_[i][i] = 1;
-
- remapping_ = true;
- return;
- }
+ (*matrix_)[i][i] = 1;
- // Special case for 5.0, 5.1 with back channels when upmixed to 7.0, 7.1,
- // which should map the back LR to side LR.
- if (input_layout_ == CHANNEL_LAYOUT_5_0_BACK &&
- output_layout_ == CHANNEL_LAYOUT_7_0) {
- input_layout_ = CHANNEL_LAYOUT_5_0;
- } else if (input_layout_ == CHANNEL_LAYOUT_5_1_BACK &&
- output_layout_ == CHANNEL_LAYOUT_7_1) {
- input_layout_ = CHANNEL_LAYOUT_5_1;
+ return true;
}
// Route matching channels and figure out which ones aren't accounted for.
for (Channels ch = LEFT; ch < CHANNELS_MAX;
ch = static_cast<Channels>(ch + 1)) {
int input_ch_index = ChannelOrder(input_layout_, ch);
- int output_ch_index = ChannelOrder(output_layout_, ch);
-
if (input_ch_index < 0)
continue;
+ int output_ch_index = ChannelOrder(output_layout_, ch);
if (output_ch_index < 0) {
unaccounted_inputs_.push_back(ch);
continue;
}
- DCHECK_LT(static_cast<size_t>(output_ch_index), matrix_.size());
+ DCHECK_LT(static_cast<size_t>(output_ch_index), matrix_->size());
DCHECK_LT(static_cast<size_t>(input_ch_index),
- matrix_[output_ch_index].size());
- matrix_[output_ch_index][input_ch_index] = 1;
+ (*matrix_)[output_ch_index].size());
+ (*matrix_)[output_ch_index][input_ch_index] = 1;
}
// If all input channels are accounted for, there's nothing left to do.
if (unaccounted_inputs_.empty()) {
// Since all output channels map directly to inputs we can optimize.
- remapping_ = true;
- return;
+ return true;
}
// Mix front LR into center.
@@ -147,7 +200,7 @@ void ChannelMixer::Initialize(
// stereo mixes. Scaling by 1 / sqrt(2) here will likely lead to clipping
// so we use 1 / 2 instead.
float scale =
- (output_layout_ == CHANNEL_LAYOUT_MONO && input_channels == 2) ?
+ (output_layout_ == CHANNEL_LAYOUT_MONO && input_channels_ == 2) ?
0.5 : kEqualPowerScale;
Mix(LEFT, CENTER, scale);
Mix(RIGHT, CENTER, scale);
@@ -261,18 +314,18 @@ void ChannelMixer::Initialize(
// See if the output |matrix_| is simply a remapping matrix. If each input
// channel maps to a single output channel we can simply remap. Doing this
// programmatically is less fragile than logic checks on channel mappings.
- for (int output_ch = 0; output_ch < output_channels; ++output_ch) {
+ for (int output_ch = 0; output_ch < output_channels_; ++output_ch) {
int input_mappings = 0;
- for (int input_ch = 0; input_ch < input_channels; ++input_ch) {
+ for (int input_ch = 0; input_ch < input_channels_; ++input_ch) {
// We can only remap if each row contains a single scale of 1. I.e., each
// output channel is mapped from a single unscaled input channel.
- if (matrix_[output_ch][input_ch] != 1 || ++input_mappings > 1)
- return;
+ if ((*matrix_)[output_ch][input_ch] != 1 || ++input_mappings > 1)
+ return false;
}
}
// If we've gotten here, |matrix_| is simply a remapping.
- remapping_ = true;
+ return true;
}
ChannelMixer::~ChannelMixer() {}
@@ -314,31 +367,31 @@ void ChannelMixer::Transform(const AudioBus* input, AudioBus* output) {
}
}
-void ChannelMixer::AccountFor(Channels ch) {
+void MatrixBuilder::AccountFor(Channels ch) {
unaccounted_inputs_.erase(std::find(
unaccounted_inputs_.begin(), unaccounted_inputs_.end(), ch));
}
-bool ChannelMixer::IsUnaccounted(Channels ch) {
+bool MatrixBuilder::IsUnaccounted(Channels ch) {
return std::find(unaccounted_inputs_.begin(), unaccounted_inputs_.end(),
ch) != unaccounted_inputs_.end();
}
-bool ChannelMixer::HasInputChannel(Channels ch) {
+bool MatrixBuilder::HasInputChannel(Channels ch) {
return ChannelOrder(input_layout_, ch) >= 0;
}
-bool ChannelMixer::HasOutputChannel(Channels ch) {
+bool MatrixBuilder::HasOutputChannel(Channels ch) {
return ChannelOrder(output_layout_, ch) >= 0;
}
-void ChannelMixer::Mix(Channels input_ch, Channels output_ch, float scale) {
+void MatrixBuilder::Mix(Channels input_ch, Channels output_ch, float scale) {
MixWithoutAccounting(input_ch, output_ch, scale);
AccountFor(input_ch);
}
-void ChannelMixer::MixWithoutAccounting(Channels input_ch, Channels output_ch,
- float scale) {
+void MatrixBuilder::MixWithoutAccounting(Channels input_ch, Channels output_ch,
+ float scale) {
int input_ch_index = ChannelOrder(input_layout_, input_ch);
int output_ch_index = ChannelOrder(output_layout_, output_ch);
@@ -346,8 +399,8 @@ void ChannelMixer::MixWithoutAccounting(Channels input_ch, Channels output_ch,
DCHECK_GE(input_ch_index, 0);
DCHECK_GE(output_ch_index, 0);
- DCHECK_EQ(matrix_[output_ch_index][input_ch_index], 0);
- matrix_[output_ch_index][input_ch_index] = scale;
+ DCHECK_EQ((*matrix_)[output_ch_index][input_ch_index], 0);
+ (*matrix_)[output_ch_index][input_ch_index] = scale;
}
} // namespace media
diff --git a/media/base/channel_mixer.h b/media/base/channel_mixer.h
index c88669d..ea3cbf81 100644
--- a/media/base/channel_mixer.h
+++ b/media/base/channel_mixer.h
@@ -28,35 +28,12 @@ class MEDIA_EXPORT ChannelMixer {
ChannelMixer(const AudioParameters& input, const AudioParameters& output);
~ChannelMixer();
- void Initialize(ChannelLayout input_layout, int input_channels,
- ChannelLayout output_layout, int output_channels);
-
// Transforms all channels from |input| into |output| channels.
void Transform(const AudioBus* input, AudioBus* output);
private:
- // Constructor helper methods for managing unaccounted input channels.
- void AccountFor(Channels ch);
- bool IsUnaccounted(Channels ch);
-
- // Helper methods for checking if |ch| exists in either |input_layout_| or
- // |output_layout_| respectively.
- bool HasInputChannel(Channels ch);
- bool HasOutputChannel(Channels ch);
-
- // Constructor helper methods for updating |matrix_| with the proper value for
- // mixing |input_ch| into |output_ch|. MixWithoutAccounting() does not remove
- // the channel from |unaccounted_inputs_|.
- void Mix(Channels input_ch, Channels output_ch, float scale);
- void MixWithoutAccounting(Channels input_ch, Channels output_ch, float scale);
-
- // Input and output channel layout provided during construction.
- ChannelLayout input_layout_;
- ChannelLayout output_layout_;
-
- // Helper variable for tracking which inputs are currently unaccounted, should
- // be empty after construction completes.
- std::vector<Channels> unaccounted_inputs_;
+ void Initialize(ChannelLayout input_layout, int input_channels,
+ ChannelLayout output_layout, int output_channels);
// 2D matrix of output channels to input channels.
std::vector< std::vector<float> > matrix_;
diff --git a/media/base/channel_mixer_unittest.cc b/media/base/channel_mixer_unittest.cc
index 3e44409..935d060 100644
--- a/media/base/channel_mixer_unittest.cc
+++ b/media/base/channel_mixer_unittest.cc
@@ -8,6 +8,7 @@
#include <cmath>
#include "base/stringprintf.h"
+#include "media/audio/audio_parameters.h"
#include "media/base/audio_bus.h"
#include "media/base/channel_mixer.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -54,6 +55,20 @@ struct ChannelMixerTestData {
channel_values(channel_values),
num_channel_values(num_channel_values),
scale(scale) {
+ input_channels = ChannelLayoutToChannelCount(input_layout);
+ output_channels = ChannelLayoutToChannelCount(output_layout);
+ }
+
+ ChannelMixerTestData(ChannelLayout input_layout, int input_channels,
+ ChannelLayout output_layout, int output_channels,
+ float* channel_values, int num_channel_values)
+ : input_layout(input_layout),
+ input_channels(input_channels),
+ output_layout(output_layout),
+ output_channels(output_channels),
+ channel_values(channel_values),
+ num_channel_values(num_channel_values),
+ scale(1.0f) {
}
std::string DebugString() const {
@@ -63,7 +78,9 @@ struct ChannelMixerTestData {
}
ChannelLayout input_layout;
+ int input_channels;
ChannelLayout output_layout;
+ int output_channels;
float* channel_values;
int num_channel_values;
float scale;
@@ -79,13 +96,24 @@ class ChannelMixerTest : public testing::TestWithParam<ChannelMixerTestData> {};
// output channels have the same value.
TEST_P(ChannelMixerTest, Mixing) {
ChannelLayout input_layout = GetParam().input_layout;
- ChannelLayout output_layout = GetParam().output_layout;
+ int input_channels = GetParam().input_channels;
+ scoped_ptr<AudioBus> input_bus = AudioBus::Create(input_channels, kFrames);
+ AudioParameters input_audio(AudioParameters::AUDIO_PCM_LINEAR,
+ input_layout,
+ AudioParameters::kAudioCDSampleRate, 16,
+ kFrames);
+ if (input_layout == CHANNEL_LAYOUT_DISCRETE)
+ input_audio.SetDiscreteChannels(input_channels);
- ChannelMixer mixer(input_layout, output_layout);
- scoped_ptr<AudioBus> input_bus = AudioBus::Create(
- ChannelLayoutToChannelCount(input_layout), kFrames);
- scoped_ptr<AudioBus> output_bus = AudioBus::Create(
- ChannelLayoutToChannelCount(output_layout), kFrames);
+ ChannelLayout output_layout = GetParam().output_layout;
+ int output_channels = GetParam().output_channels;
+ scoped_ptr<AudioBus> output_bus = AudioBus::Create(output_channels, kFrames);
+ AudioParameters output_audio(AudioParameters::AUDIO_PCM_LINEAR,
+ output_layout,
+ AudioParameters::kAudioCDSampleRate, 16,
+ kFrames);
+ if (output_layout == CHANNEL_LAYOUT_DISCRETE)
+ output_audio.SetDiscreteChannels(output_channels);
const float* channel_values = GetParam().channel_values;
ASSERT_EQ(input_bus->channels(), GetParam().num_channel_values);
@@ -98,11 +126,25 @@ TEST_P(ChannelMixerTest, Mixing) {
expected_value += channel_values[ch] * scale;
}
+ ChannelMixer mixer(input_audio, output_audio);
mixer.Transform(input_bus.get(), output_bus.get());
- for (int ch = 0; ch < output_bus->channels(); ++ch) {
- for (int frame = 0; frame < output_bus->frames(); ++frame) {
- ASSERT_FLOAT_EQ(output_bus->channel(ch)[frame], expected_value);
+ // Validate the output channel
+ if (input_layout != CHANNEL_LAYOUT_DISCRETE) {
+ for (int ch = 0; ch < output_bus->channels(); ++ch) {
+ for (int frame = 0; frame < output_bus->frames(); ++frame) {
+ ASSERT_FLOAT_EQ(output_bus->channel(ch)[frame], expected_value);
+ }
+ }
+ } else {
+ // Processing discrete mixing. If there is a matching input channel,
+ // then the output channel should be set. If no input channel,
+ // output channel should be 0
+ for (int ch = 0; ch < output_bus->channels(); ++ch) {
+ expected_value = (ch < input_channels) ? channel_values[ch] : 0;
+ for (int frame = 0; frame < output_bus->frames(); ++frame) {
+ ASSERT_FLOAT_EQ(output_bus->channel(ch)[frame], expected_value);
+ }
}
}
}
@@ -111,6 +153,7 @@ static float kStereoToMonoValues[] = { 0.5f, 0.75f };
static float kMonoToStereoValues[] = { 0.5f };
// Zero the center channel since it will be mixed at scale 1 vs M_SQRT1_2.
static float kFiveOneToMonoValues[] = { 0.1f, 0.2f, 0.0f, 0.4f, 0.5f, 0.6f };
+static float kFiveDiscreteValues[] = { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f };
// Run through basic sanity tests for some common conversions.
INSTANTIATE_TEST_CASE_P(ChannelMixerTest, ChannelMixerTest, testing::Values(
@@ -122,7 +165,16 @@ INSTANTIATE_TEST_CASE_P(ChannelMixerTest, ChannelMixerTest, testing::Values(
1.0f),
ChannelMixerTestData(CHANNEL_LAYOUT_5_1, CHANNEL_LAYOUT_MONO,
kFiveOneToMonoValues, arraysize(kFiveOneToMonoValues),
- static_cast<float>(M_SQRT1_2))
+ static_cast<float>(M_SQRT1_2)),
+ ChannelMixerTestData(CHANNEL_LAYOUT_DISCRETE, 2,
+ CHANNEL_LAYOUT_DISCRETE, 2,
+ kStereoToMonoValues, arraysize(kStereoToMonoValues)),
+ ChannelMixerTestData(CHANNEL_LAYOUT_DISCRETE, 2,
+ CHANNEL_LAYOUT_DISCRETE, 5,
+ kStereoToMonoValues, arraysize(kStereoToMonoValues)),
+ ChannelMixerTestData(CHANNEL_LAYOUT_DISCRETE, 5,
+ CHANNEL_LAYOUT_DISCRETE, 2,
+ kFiveDiscreteValues, arraysize(kFiveDiscreteValues))
));
} // namespace media