| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // MSVC++ requires this to be set before any other includes to get M_SQRT1_2. | 5 // MSVC++ requires this to be set before any other includes to get M_SQRT1_2. |
| 6 #define _USE_MATH_DEFINES | 6 #define _USE_MATH_DEFINES |
| 7 | 7 |
| 8 #include "media/base/channel_mixer.h" | 8 #include "media/base/channel_mixing_matrix.h" |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| 11 #include <cmath> | 11 #include <cmath> |
| 12 | 12 |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "media/audio/audio_parameters.h" | |
| 15 #include "media/base/audio_bus.h" | |
| 16 #include "media/base/vector_math.h" | |
| 17 | 14 |
| 18 namespace media { | 15 namespace media { |
| 19 | 16 |
| 20 // Default scale factor for mixing two channels together. We use a different | 17 // Default scale factor for mixing two channels together. We use a different |
| 21 // value for stereo -> mono and mono -> stereo mixes. | 18 // value for stereo -> mono and mono -> stereo mixes. |
| 22 static const float kEqualPowerScale = static_cast<float>(M_SQRT1_2); | 19 static const float kEqualPowerScale = static_cast<float>(M_SQRT1_2); |
| 23 | 20 |
| 24 static void ValidateLayout(ChannelLayout layout) { | 21 static void ValidateLayout(ChannelLayout layout) { |
| 25 CHECK_NE(layout, CHANNEL_LAYOUT_NONE); | 22 CHECK_NE(layout, CHANNEL_LAYOUT_NONE); |
| 26 CHECK_LE(layout, CHANNEL_LAYOUT_MAX); | 23 CHECK_LE(layout, CHANNEL_LAYOUT_MAX); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 45 ChannelOrder(layout, SIDE_RIGHT) >= 0); | 42 ChannelOrder(layout, SIDE_RIGHT) >= 0); |
| 46 DCHECK_EQ(ChannelOrder(layout, BACK_LEFT) >= 0, | 43 DCHECK_EQ(ChannelOrder(layout, BACK_LEFT) >= 0, |
| 47 ChannelOrder(layout, BACK_RIGHT) >= 0); | 44 ChannelOrder(layout, BACK_RIGHT) >= 0); |
| 48 DCHECK_EQ(ChannelOrder(layout, LEFT_OF_CENTER) >= 0, | 45 DCHECK_EQ(ChannelOrder(layout, LEFT_OF_CENTER) >= 0, |
| 49 ChannelOrder(layout, RIGHT_OF_CENTER) >= 0); | 46 ChannelOrder(layout, RIGHT_OF_CENTER) >= 0); |
| 50 } else { | 47 } else { |
| 51 DCHECK_EQ(layout, CHANNEL_LAYOUT_MONO); | 48 DCHECK_EQ(layout, CHANNEL_LAYOUT_MONO); |
| 52 } | 49 } |
| 53 } | 50 } |
| 54 | 51 |
| 55 class MatrixBuilder { | 52 ChannelMixingMatrix::ChannelMixingMatrix(ChannelLayout input_layout, |
| 56 public: | 53 int input_channels, |
| 57 MatrixBuilder(ChannelLayout input_layout, int input_channels, | 54 ChannelLayout output_layout, |
| 58 ChannelLayout output_layout, int output_channels) | 55 int output_channels) |
| 59 : input_layout_(input_layout), | 56 : input_layout_(input_layout), |
| 60 input_channels_(input_channels), | 57 input_channels_(input_channels), |
| 61 output_layout_(output_layout), | 58 output_layout_(output_layout), |
| 62 output_channels_(output_channels) { | 59 output_channels_(output_channels) { |
| 63 // Special case for 5.0, 5.1 with back channels when upmixed to 7.0, 7.1, | |
| 64 // which should map the back LR to side LR. | |
| 65 if (input_layout_ == CHANNEL_LAYOUT_5_0_BACK && | |
| 66 output_layout_ == CHANNEL_LAYOUT_7_0) { | |
| 67 input_layout_ = CHANNEL_LAYOUT_5_0; | |
| 68 } else if (input_layout_ == CHANNEL_LAYOUT_5_1_BACK && | |
| 69 output_layout_ == CHANNEL_LAYOUT_7_1) { | |
| 70 input_layout_ = CHANNEL_LAYOUT_5_1; | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 ~MatrixBuilder() { } | |
| 75 | |
| 76 // Create the transformation matrix of input channels to output channels. | |
| 77 // Updates the empty matrix with the transformation, and returns true | |
| 78 // if the transformation is just a remapping of channels (no mixing). | |
| 79 bool CreateTransformationMatrix(std::vector< std::vector<float> >* matrix); | |
| 80 | |
| 81 private: | |
| 82 // Result transformation of input channels to output channels | |
| 83 std::vector< std::vector<float> >* matrix_; | |
| 84 | |
| 85 // Input and output channel layout provided during construction. | |
| 86 ChannelLayout input_layout_; | |
| 87 int input_channels_; | |
| 88 ChannelLayout output_layout_; | |
| 89 int output_channels_; | |
| 90 | |
| 91 // Helper variable for tracking which inputs are currently unaccounted, | |
| 92 // should be empty after construction completes. | |
| 93 std::vector<Channels> unaccounted_inputs_; | |
| 94 | |
| 95 // Helper methods for managing unaccounted input channels. | |
| 96 void AccountFor(Channels ch); | |
| 97 bool IsUnaccounted(Channels ch) const; | |
| 98 | |
| 99 // Helper methods for checking if |ch| exists in either |input_layout_| or | |
| 100 // |output_layout_| respectively. | |
| 101 bool HasInputChannel(Channels ch) const; | |
| 102 bool HasOutputChannel(Channels ch) const; | |
| 103 | |
| 104 // Helper methods for updating |matrix_| with the proper value for | |
| 105 // mixing |input_ch| into |output_ch|. MixWithoutAccounting() does not | |
| 106 // remove the channel from |unaccounted_inputs_|. | |
| 107 void Mix(Channels input_ch, Channels output_ch, float scale); | |
| 108 void MixWithoutAccounting(Channels input_ch, Channels output_ch, float scale); | |
| 109 | |
| 110 DISALLOW_COPY_AND_ASSIGN(MatrixBuilder); | |
| 111 }; | |
| 112 | |
| 113 ChannelMixer::ChannelMixer(ChannelLayout input_layout, | |
| 114 ChannelLayout output_layout) { | |
| 115 Initialize(input_layout, | |
| 116 ChannelLayoutToChannelCount(input_layout), | |
| 117 output_layout, | |
| 118 ChannelLayoutToChannelCount(output_layout)); | |
| 119 } | |
| 120 | |
| 121 ChannelMixer::ChannelMixer( | |
| 122 const AudioParameters& input, const AudioParameters& output) { | |
| 123 Initialize(input.channel_layout(), | |
| 124 input.channels(), | |
| 125 output.channel_layout(), | |
| 126 output.channels()); | |
| 127 } | |
| 128 | |
| 129 void ChannelMixer::Initialize( | |
| 130 ChannelLayout input_layout, int input_channels, | |
| 131 ChannelLayout output_layout, int output_channels) { | |
| 132 // Stereo down mix should never be the output layout. | 60 // Stereo down mix should never be the output layout. |
| 133 CHECK_NE(output_layout, CHANNEL_LAYOUT_STEREO_DOWNMIX); | 61 CHECK_NE(output_layout, CHANNEL_LAYOUT_STEREO_DOWNMIX); |
| 134 | 62 |
| 135 // Verify that the layouts are supported | 63 // Verify that the layouts are supported |
| 136 if (input_layout != CHANNEL_LAYOUT_DISCRETE) | 64 if (input_layout != CHANNEL_LAYOUT_DISCRETE) |
| 137 ValidateLayout(input_layout); | 65 ValidateLayout(input_layout); |
| 138 if (output_layout != CHANNEL_LAYOUT_DISCRETE) | 66 if (output_layout != CHANNEL_LAYOUT_DISCRETE) |
| 139 ValidateLayout(output_layout); | 67 ValidateLayout(output_layout); |
| 140 | 68 |
| 141 // Create the transformation matrix | 69 // Special case for 5.0, 5.1 with back channels when upmixed to 7.0, 7.1, |
| 142 MatrixBuilder matrix_builder(input_layout, input_channels, | 70 // which should map the back LR to side LR. |
| 143 output_layout, output_channels); | 71 if (input_layout_ == CHANNEL_LAYOUT_5_0_BACK && |
| 144 remapping_ = matrix_builder.CreateTransformationMatrix(&matrix_); | 72 output_layout_ == CHANNEL_LAYOUT_7_0) { |
| 73 input_layout_ = CHANNEL_LAYOUT_5_0; |
| 74 } else if (input_layout_ == CHANNEL_LAYOUT_5_1_BACK && |
| 75 output_layout_ == CHANNEL_LAYOUT_7_1) { |
| 76 input_layout_ = CHANNEL_LAYOUT_5_1; |
| 77 } |
| 145 } | 78 } |
| 146 | 79 |
| 147 bool MatrixBuilder::CreateTransformationMatrix( | 80 ChannelMixingMatrix::~ChannelMixingMatrix() { |
| 148 std::vector< std::vector<float> >* matrix) { | 81 } |
| 82 |
| 83 bool ChannelMixingMatrix::CreateTransformationMatrix( |
| 84 std::vector<std::vector<float>>* matrix) { |
| 149 matrix_ = matrix; | 85 matrix_ = matrix; |
| 150 | 86 |
| 151 // Size out the initial matrix. | 87 // Size out the initial matrix. |
| 152 matrix_->reserve(output_channels_); | 88 matrix_->reserve(output_channels_); |
| 153 for (int output_ch = 0; output_ch < output_channels_; ++output_ch) | 89 for (int output_ch = 0; output_ch < output_channels_; ++output_ch) |
| 154 matrix_->push_back(std::vector<float>(input_channels_, 0)); | 90 matrix_->push_back(std::vector<float>(input_channels_, 0)); |
| 155 | 91 |
| 156 // First check for discrete case. | 92 // First check for discrete case. |
| 157 if (input_layout_ == CHANNEL_LAYOUT_DISCRETE || | 93 if (input_layout_ == CHANNEL_LAYOUT_DISCRETE || |
| 158 output_layout_ == CHANNEL_LAYOUT_DISCRETE) { | 94 output_layout_ == CHANNEL_LAYOUT_DISCRETE) { |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 319 // output channel is mapped from a single unscaled input channel. | 255 // output channel is mapped from a single unscaled input channel. |
| 320 if ((*matrix_)[output_ch][input_ch] != 1 || ++input_mappings > 1) | 256 if ((*matrix_)[output_ch][input_ch] != 1 || ++input_mappings > 1) |
| 321 return false; | 257 return false; |
| 322 } | 258 } |
| 323 } | 259 } |
| 324 | 260 |
| 325 // If we've gotten here, |matrix_| is simply a remapping. | 261 // If we've gotten here, |matrix_| is simply a remapping. |
| 326 return true; | 262 return true; |
| 327 } | 263 } |
| 328 | 264 |
| 329 ChannelMixer::~ChannelMixer() {} | 265 void ChannelMixingMatrix::AccountFor(Channels ch) { |
| 330 | |
| 331 void ChannelMixer::Transform(const AudioBus* input, AudioBus* output) { | |
| 332 CHECK_EQ(matrix_.size(), static_cast<size_t>(output->channels())); | |
| 333 CHECK_EQ(matrix_[0].size(), static_cast<size_t>(input->channels())); | |
| 334 CHECK_EQ(input->frames(), output->frames()); | |
| 335 | |
| 336 // Zero initialize |output| so we're accumulating from zero. | |
| 337 output->Zero(); | |
| 338 | |
| 339 // If we're just remapping we can simply copy the correct input to output. | |
| 340 if (remapping_) { | |
| 341 for (int output_ch = 0; output_ch < output->channels(); ++output_ch) { | |
| 342 for (int input_ch = 0; input_ch < input->channels(); ++input_ch) { | |
| 343 float scale = matrix_[output_ch][input_ch]; | |
| 344 if (scale > 0) { | |
| 345 DCHECK_EQ(scale, 1.0f); | |
| 346 memcpy(output->channel(output_ch), input->channel(input_ch), | |
| 347 sizeof(*output->channel(output_ch)) * output->frames()); | |
| 348 break; | |
| 349 } | |
| 350 } | |
| 351 } | |
| 352 return; | |
| 353 } | |
| 354 | |
| 355 for (int output_ch = 0; output_ch < output->channels(); ++output_ch) { | |
| 356 for (int input_ch = 0; input_ch < input->channels(); ++input_ch) { | |
| 357 float scale = matrix_[output_ch][input_ch]; | |
| 358 // Scale should always be positive. Don't bother scaling by zero. | |
| 359 DCHECK_GE(scale, 0); | |
| 360 if (scale > 0) { | |
| 361 vector_math::FMAC(input->channel(input_ch), scale, output->frames(), | |
| 362 output->channel(output_ch)); | |
| 363 } | |
| 364 } | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 void MatrixBuilder::AccountFor(Channels ch) { | |
| 369 unaccounted_inputs_.erase(std::find( | 266 unaccounted_inputs_.erase(std::find( |
| 370 unaccounted_inputs_.begin(), unaccounted_inputs_.end(), ch)); | 267 unaccounted_inputs_.begin(), unaccounted_inputs_.end(), ch)); |
| 371 } | 268 } |
| 372 | 269 |
| 373 bool MatrixBuilder::IsUnaccounted(Channels ch) const { | 270 bool ChannelMixingMatrix::IsUnaccounted(Channels ch) const { |
| 374 return std::find(unaccounted_inputs_.begin(), unaccounted_inputs_.end(), | 271 return std::find(unaccounted_inputs_.begin(), unaccounted_inputs_.end(), |
| 375 ch) != unaccounted_inputs_.end(); | 272 ch) != unaccounted_inputs_.end(); |
| 376 } | 273 } |
| 377 | 274 |
| 378 bool MatrixBuilder::HasInputChannel(Channels ch) const { | 275 bool ChannelMixingMatrix::HasInputChannel(Channels ch) const { |
| 379 return ChannelOrder(input_layout_, ch) >= 0; | 276 return ChannelOrder(input_layout_, ch) >= 0; |
| 380 } | 277 } |
| 381 | 278 |
| 382 bool MatrixBuilder::HasOutputChannel(Channels ch) const { | 279 bool ChannelMixingMatrix::HasOutputChannel(Channels ch) const { |
| 383 return ChannelOrder(output_layout_, ch) >= 0; | 280 return ChannelOrder(output_layout_, ch) >= 0; |
| 384 } | 281 } |
| 385 | 282 |
| 386 void MatrixBuilder::Mix(Channels input_ch, Channels output_ch, float scale) { | 283 void ChannelMixingMatrix::Mix(Channels input_ch, |
| 284 Channels output_ch, |
| 285 float scale) { |
| 387 MixWithoutAccounting(input_ch, output_ch, scale); | 286 MixWithoutAccounting(input_ch, output_ch, scale); |
| 388 AccountFor(input_ch); | 287 AccountFor(input_ch); |
| 389 } | 288 } |
| 390 | 289 |
| 391 void MatrixBuilder::MixWithoutAccounting(Channels input_ch, Channels output_ch, | 290 void ChannelMixingMatrix::MixWithoutAccounting(Channels input_ch, |
| 392 float scale) { | 291 Channels output_ch, |
| 292 float scale) { |
| 393 int input_ch_index = ChannelOrder(input_layout_, input_ch); | 293 int input_ch_index = ChannelOrder(input_layout_, input_ch); |
| 394 int output_ch_index = ChannelOrder(output_layout_, output_ch); | 294 int output_ch_index = ChannelOrder(output_layout_, output_ch); |
| 395 | 295 |
| 396 DCHECK(IsUnaccounted(input_ch)); | 296 DCHECK(IsUnaccounted(input_ch)); |
| 397 DCHECK_GE(input_ch_index, 0); | 297 DCHECK_GE(input_ch_index, 0); |
| 398 DCHECK_GE(output_ch_index, 0); | 298 DCHECK_GE(output_ch_index, 0); |
| 399 | 299 |
| 400 DCHECK_EQ((*matrix_)[output_ch_index][input_ch_index], 0); | 300 DCHECK_EQ((*matrix_)[output_ch_index][input_ch_index], 0); |
| 401 (*matrix_)[output_ch_index][input_ch_index] = scale; | 301 (*matrix_)[output_ch_index][input_ch_index] = scale; |
| 402 } | 302 } |
| 403 | 303 |
| 404 } // namespace media | 304 } // namespace media |
| OLD | NEW |