| 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. | |
| 6 #define _USE_MATH_DEFINES | |
| 7 | |
| 8 #include "media/base/channel_mixer.h" | 5 #include "media/base/channel_mixer.h" |
| 9 | 6 |
| 10 #include <algorithm> | |
| 11 #include <cmath> | |
| 12 | |
| 13 #include "base/logging.h" | 7 #include "base/logging.h" |
| 14 #include "media/audio/audio_parameters.h" | 8 #include "media/audio/audio_parameters.h" |
| 15 #include "media/base/audio_bus.h" | 9 #include "media/base/audio_bus.h" |
| 10 #include "media/base/channel_mixing_matrix.h" |
| 16 #include "media/base/vector_math.h" | 11 #include "media/base/vector_math.h" |
| 17 | 12 |
| 18 namespace media { | 13 namespace media { |
| 19 | 14 |
| 20 // Default scale factor for mixing two channels together. We use a different | |
| 21 // value for stereo -> mono and mono -> stereo mixes. | |
| 22 static const float kEqualPowerScale = static_cast<float>(M_SQRT1_2); | |
| 23 | |
| 24 static void ValidateLayout(ChannelLayout layout) { | |
| 25 CHECK_NE(layout, CHANNEL_LAYOUT_NONE); | |
| 26 CHECK_LE(layout, CHANNEL_LAYOUT_MAX); | |
| 27 CHECK_NE(layout, CHANNEL_LAYOUT_UNSUPPORTED); | |
| 28 CHECK_NE(layout, CHANNEL_LAYOUT_DISCRETE); | |
| 29 CHECK_NE(layout, CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC); | |
| 30 | |
| 31 // Verify there's at least one channel. Should always be true here by virtue | |
| 32 // of not being one of the invalid layouts, but lets double check to be sure. | |
| 33 int channel_count = ChannelLayoutToChannelCount(layout); | |
| 34 DCHECK_GT(channel_count, 0); | |
| 35 | |
| 36 // If we have more than one channel, verify a symmetric layout for sanity. | |
| 37 // The unit test will verify all possible layouts, so this can be a DCHECK. | |
| 38 // Symmetry allows simplifying the matrix building code by allowing us to | |
| 39 // assume that if one channel of a pair exists, the other will too. | |
| 40 if (channel_count > 1) { | |
| 41 // Assert that LEFT exists if and only if RIGHT exists, and so on. | |
| 42 DCHECK_EQ(ChannelOrder(layout, LEFT) >= 0, | |
| 43 ChannelOrder(layout, RIGHT) >= 0); | |
| 44 DCHECK_EQ(ChannelOrder(layout, SIDE_LEFT) >= 0, | |
| 45 ChannelOrder(layout, SIDE_RIGHT) >= 0); | |
| 46 DCHECK_EQ(ChannelOrder(layout, BACK_LEFT) >= 0, | |
| 47 ChannelOrder(layout, BACK_RIGHT) >= 0); | |
| 48 DCHECK_EQ(ChannelOrder(layout, LEFT_OF_CENTER) >= 0, | |
| 49 ChannelOrder(layout, RIGHT_OF_CENTER) >= 0); | |
| 50 } else { | |
| 51 DCHECK_EQ(layout, CHANNEL_LAYOUT_MONO); | |
| 52 } | |
| 53 } | |
| 54 | |
| 55 class MatrixBuilder { | |
| 56 public: | |
| 57 MatrixBuilder(ChannelLayout input_layout, int input_channels, | |
| 58 ChannelLayout output_layout, int output_channels) | |
| 59 : input_layout_(input_layout), | |
| 60 input_channels_(input_channels), | |
| 61 output_layout_(output_layout), | |
| 62 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, | 15 ChannelMixer::ChannelMixer(ChannelLayout input_layout, |
| 114 ChannelLayout output_layout) { | 16 ChannelLayout output_layout) { |
| 115 Initialize(input_layout, | 17 Initialize(input_layout, |
| 116 ChannelLayoutToChannelCount(input_layout), | 18 ChannelLayoutToChannelCount(input_layout), |
| 117 output_layout, | 19 output_layout, |
| 118 ChannelLayoutToChannelCount(output_layout)); | 20 ChannelLayoutToChannelCount(output_layout)); |
| 119 } | 21 } |
| 120 | 22 |
| 121 ChannelMixer::ChannelMixer( | 23 ChannelMixer::ChannelMixer( |
| 122 const AudioParameters& input, const AudioParameters& output) { | 24 const AudioParameters& input, const AudioParameters& output) { |
| 123 Initialize(input.channel_layout(), | 25 Initialize(input.channel_layout(), |
| 124 input.channels(), | 26 input.channels(), |
| 125 output.channel_layout(), | 27 output.channel_layout(), |
| 126 output.channels()); | 28 output.channels()); |
| 127 } | 29 } |
| 128 | 30 |
| 129 void ChannelMixer::Initialize( | 31 void ChannelMixer::Initialize( |
| 130 ChannelLayout input_layout, int input_channels, | 32 ChannelLayout input_layout, int input_channels, |
| 131 ChannelLayout output_layout, int output_channels) { | 33 ChannelLayout output_layout, int output_channels) { |
| 132 // Stereo down mix should never be the output layout. | |
| 133 CHECK_NE(output_layout, CHANNEL_LAYOUT_STEREO_DOWNMIX); | |
| 134 | |
| 135 // Verify that the layouts are supported | |
| 136 if (input_layout != CHANNEL_LAYOUT_DISCRETE) | |
| 137 ValidateLayout(input_layout); | |
| 138 if (output_layout != CHANNEL_LAYOUT_DISCRETE) | |
| 139 ValidateLayout(output_layout); | |
| 140 | |
| 141 // Create the transformation matrix | 34 // Create the transformation matrix |
| 142 MatrixBuilder matrix_builder(input_layout, input_channels, | 35 ChannelMixingMatrix matrix_builder(input_layout, input_channels, |
| 143 output_layout, output_channels); | 36 output_layout, output_channels); |
| 144 remapping_ = matrix_builder.CreateTransformationMatrix(&matrix_); | 37 remapping_ = matrix_builder.CreateTransformationMatrix(&matrix_); |
| 145 } | 38 } |
| 146 | 39 |
| 147 bool MatrixBuilder::CreateTransformationMatrix( | |
| 148 std::vector< std::vector<float> >* matrix) { | |
| 149 matrix_ = matrix; | |
| 150 | |
| 151 // Size out the initial matrix. | |
| 152 matrix_->reserve(output_channels_); | |
| 153 for (int output_ch = 0; output_ch < output_channels_; ++output_ch) | |
| 154 matrix_->push_back(std::vector<float>(input_channels_, 0)); | |
| 155 | |
| 156 // First check for discrete case. | |
| 157 if (input_layout_ == CHANNEL_LAYOUT_DISCRETE || | |
| 158 output_layout_ == CHANNEL_LAYOUT_DISCRETE) { | |
| 159 // If the number of input channels is more than output channels, then | |
| 160 // copy as many as we can then drop the remaining input channels. | |
| 161 // If the number of input channels is less than output channels, then | |
| 162 // copy them all, then zero out the remaining output channels. | |
| 163 int passthrough_channels = std::min(input_channels_, output_channels_); | |
| 164 for (int i = 0; i < passthrough_channels; ++i) | |
| 165 (*matrix_)[i][i] = 1; | |
| 166 | |
| 167 return true; | |
| 168 } | |
| 169 | |
| 170 // Route matching channels and figure out which ones aren't accounted for. | |
| 171 for (Channels ch = LEFT; ch < CHANNELS_MAX + 1; | |
| 172 ch = static_cast<Channels>(ch + 1)) { | |
| 173 int input_ch_index = ChannelOrder(input_layout_, ch); | |
| 174 if (input_ch_index < 0) | |
| 175 continue; | |
| 176 | |
| 177 int output_ch_index = ChannelOrder(output_layout_, ch); | |
| 178 if (output_ch_index < 0) { | |
| 179 unaccounted_inputs_.push_back(ch); | |
| 180 continue; | |
| 181 } | |
| 182 | |
| 183 DCHECK_LT(static_cast<size_t>(output_ch_index), matrix_->size()); | |
| 184 DCHECK_LT(static_cast<size_t>(input_ch_index), | |
| 185 (*matrix_)[output_ch_index].size()); | |
| 186 (*matrix_)[output_ch_index][input_ch_index] = 1; | |
| 187 } | |
| 188 | |
| 189 // If all input channels are accounted for, there's nothing left to do. | |
| 190 if (unaccounted_inputs_.empty()) { | |
| 191 // Since all output channels map directly to inputs we can optimize. | |
| 192 return true; | |
| 193 } | |
| 194 | |
| 195 // Mix front LR into center. | |
| 196 if (IsUnaccounted(LEFT)) { | |
| 197 // When down mixing to mono from stereo, we need to be careful of full scale | |
| 198 // stereo mixes. Scaling by 1 / sqrt(2) here will likely lead to clipping | |
| 199 // so we use 1 / 2 instead. | |
| 200 float scale = | |
| 201 (output_layout_ == CHANNEL_LAYOUT_MONO && input_channels_ == 2) ? | |
| 202 0.5 : kEqualPowerScale; | |
| 203 Mix(LEFT, CENTER, scale); | |
| 204 Mix(RIGHT, CENTER, scale); | |
| 205 } | |
| 206 | |
| 207 // Mix center into front LR. | |
| 208 if (IsUnaccounted(CENTER)) { | |
| 209 // When up mixing from mono, just do a copy to front LR. | |
| 210 float scale = | |
| 211 (input_layout_ == CHANNEL_LAYOUT_MONO) ? 1 : kEqualPowerScale; | |
| 212 MixWithoutAccounting(CENTER, LEFT, scale); | |
| 213 Mix(CENTER, RIGHT, scale); | |
| 214 } | |
| 215 | |
| 216 // Mix back LR into: side LR || back center || front LR || front center. | |
| 217 if (IsUnaccounted(BACK_LEFT)) { | |
| 218 if (HasOutputChannel(SIDE_LEFT)) { | |
| 219 // If the input has side LR, mix back LR into side LR, but instead if the | |
| 220 // input doesn't have side LR (but output does) copy back LR to side LR. | |
| 221 float scale = HasInputChannel(SIDE_LEFT) ? kEqualPowerScale : 1; | |
| 222 Mix(BACK_LEFT, SIDE_LEFT, scale); | |
| 223 Mix(BACK_RIGHT, SIDE_RIGHT, scale); | |
| 224 } else if (HasOutputChannel(BACK_CENTER)) { | |
| 225 // Mix back LR into back center. | |
| 226 Mix(BACK_LEFT, BACK_CENTER, kEqualPowerScale); | |
| 227 Mix(BACK_RIGHT, BACK_CENTER, kEqualPowerScale); | |
| 228 } else if (output_layout_ > CHANNEL_LAYOUT_MONO) { | |
| 229 // Mix back LR into front LR. | |
| 230 Mix(BACK_LEFT, LEFT, kEqualPowerScale); | |
| 231 Mix(BACK_RIGHT, RIGHT, kEqualPowerScale); | |
| 232 } else { | |
| 233 // Mix back LR into front center. | |
| 234 Mix(BACK_LEFT, CENTER, kEqualPowerScale); | |
| 235 Mix(BACK_RIGHT, CENTER, kEqualPowerScale); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 // Mix side LR into: back LR || back center || front LR || front center. | |
| 240 if (IsUnaccounted(SIDE_LEFT)) { | |
| 241 if (HasOutputChannel(BACK_LEFT)) { | |
| 242 // If the input has back LR, mix side LR into back LR, but instead if the | |
| 243 // input doesn't have back LR (but output does) copy side LR to back LR. | |
| 244 float scale = HasInputChannel(BACK_LEFT) ? kEqualPowerScale : 1; | |
| 245 Mix(SIDE_LEFT, BACK_LEFT, scale); | |
| 246 Mix(SIDE_RIGHT, BACK_RIGHT, scale); | |
| 247 } else if (HasOutputChannel(BACK_CENTER)) { | |
| 248 // Mix side LR into back center. | |
| 249 Mix(SIDE_LEFT, BACK_CENTER, kEqualPowerScale); | |
| 250 Mix(SIDE_RIGHT, BACK_CENTER, kEqualPowerScale); | |
| 251 } else if (output_layout_ > CHANNEL_LAYOUT_MONO) { | |
| 252 // Mix side LR into front LR. | |
| 253 Mix(SIDE_LEFT, LEFT, kEqualPowerScale); | |
| 254 Mix(SIDE_RIGHT, RIGHT, kEqualPowerScale); | |
| 255 } else { | |
| 256 // Mix side LR into front center. | |
| 257 Mix(SIDE_LEFT, CENTER, kEqualPowerScale); | |
| 258 Mix(SIDE_RIGHT, CENTER, kEqualPowerScale); | |
| 259 } | |
| 260 } | |
| 261 | |
| 262 // Mix back center into: back LR || side LR || front LR || front center. | |
| 263 if (IsUnaccounted(BACK_CENTER)) { | |
| 264 if (HasOutputChannel(BACK_LEFT)) { | |
| 265 // Mix back center into back LR. | |
| 266 MixWithoutAccounting(BACK_CENTER, BACK_LEFT, kEqualPowerScale); | |
| 267 Mix(BACK_CENTER, BACK_RIGHT, kEqualPowerScale); | |
| 268 } else if (HasOutputChannel(SIDE_LEFT)) { | |
| 269 // Mix back center into side LR. | |
| 270 MixWithoutAccounting(BACK_CENTER, SIDE_LEFT, kEqualPowerScale); | |
| 271 Mix(BACK_CENTER, SIDE_RIGHT, kEqualPowerScale); | |
| 272 } else if (output_layout_ > CHANNEL_LAYOUT_MONO) { | |
| 273 // Mix back center into front LR. | |
| 274 // TODO(dalecurtis): Not sure about these values? | |
| 275 MixWithoutAccounting(BACK_CENTER, LEFT, kEqualPowerScale); | |
| 276 Mix(BACK_CENTER, RIGHT, kEqualPowerScale); | |
| 277 } else { | |
| 278 // Mix back center into front center. | |
| 279 // TODO(dalecurtis): Not sure about these values? | |
| 280 Mix(BACK_CENTER, CENTER, kEqualPowerScale); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 // Mix LR of center into: front LR || front center. | |
| 285 if (IsUnaccounted(LEFT_OF_CENTER)) { | |
| 286 if (HasOutputChannel(LEFT)) { | |
| 287 // Mix LR of center into front LR. | |
| 288 Mix(LEFT_OF_CENTER, LEFT, kEqualPowerScale); | |
| 289 Mix(RIGHT_OF_CENTER, RIGHT, kEqualPowerScale); | |
| 290 } else { | |
| 291 // Mix LR of center into front center. | |
| 292 Mix(LEFT_OF_CENTER, CENTER, kEqualPowerScale); | |
| 293 Mix(RIGHT_OF_CENTER, CENTER, kEqualPowerScale); | |
| 294 } | |
| 295 } | |
| 296 | |
| 297 // Mix LFE into: front center || front LR. | |
| 298 if (IsUnaccounted(LFE)) { | |
| 299 if (!HasOutputChannel(CENTER)) { | |
| 300 // Mix LFE into front LR. | |
| 301 MixWithoutAccounting(LFE, LEFT, kEqualPowerScale); | |
| 302 Mix(LFE, RIGHT, kEqualPowerScale); | |
| 303 } else { | |
| 304 // Mix LFE into front center. | |
| 305 Mix(LFE, CENTER, kEqualPowerScale); | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 // All channels should now be accounted for. | |
| 310 DCHECK(unaccounted_inputs_.empty()); | |
| 311 | |
| 312 // See if the output |matrix_| is simply a remapping matrix. If each input | |
| 313 // channel maps to a single output channel we can simply remap. Doing this | |
| 314 // programmatically is less fragile than logic checks on channel mappings. | |
| 315 for (int output_ch = 0; output_ch < output_channels_; ++output_ch) { | |
| 316 int input_mappings = 0; | |
| 317 for (int input_ch = 0; input_ch < input_channels_; ++input_ch) { | |
| 318 // We can only remap if each row contains a single scale of 1. I.e., each | |
| 319 // output channel is mapped from a single unscaled input channel. | |
| 320 if ((*matrix_)[output_ch][input_ch] != 1 || ++input_mappings > 1) | |
| 321 return false; | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 // If we've gotten here, |matrix_| is simply a remapping. | |
| 326 return true; | |
| 327 } | |
| 328 | |
| 329 ChannelMixer::~ChannelMixer() {} | 40 ChannelMixer::~ChannelMixer() {} |
| 330 | 41 |
| 331 void ChannelMixer::Transform(const AudioBus* input, AudioBus* output) { | 42 void ChannelMixer::Transform(const AudioBus* input, AudioBus* output) { |
| 332 CHECK_EQ(matrix_.size(), static_cast<size_t>(output->channels())); | 43 CHECK_EQ(matrix_.size(), static_cast<size_t>(output->channels())); |
| 333 CHECK_EQ(matrix_[0].size(), static_cast<size_t>(input->channels())); | 44 CHECK_EQ(matrix_[0].size(), static_cast<size_t>(input->channels())); |
| 334 CHECK_EQ(input->frames(), output->frames()); | 45 CHECK_EQ(input->frames(), output->frames()); |
| 335 | 46 |
| 336 // Zero initialize |output| so we're accumulating from zero. | 47 // Zero initialize |output| so we're accumulating from zero. |
| 337 output->Zero(); | 48 output->Zero(); |
| 338 | 49 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 358 // Scale should always be positive. Don't bother scaling by zero. | 69 // Scale should always be positive. Don't bother scaling by zero. |
| 359 DCHECK_GE(scale, 0); | 70 DCHECK_GE(scale, 0); |
| 360 if (scale > 0) { | 71 if (scale > 0) { |
| 361 vector_math::FMAC(input->channel(input_ch), scale, output->frames(), | 72 vector_math::FMAC(input->channel(input_ch), scale, output->frames(), |
| 362 output->channel(output_ch)); | 73 output->channel(output_ch)); |
| 363 } | 74 } |
| 364 } | 75 } |
| 365 } | 76 } |
| 366 } | 77 } |
| 367 | 78 |
| 368 void MatrixBuilder::AccountFor(Channels ch) { | |
| 369 unaccounted_inputs_.erase(std::find( | |
| 370 unaccounted_inputs_.begin(), unaccounted_inputs_.end(), ch)); | |
| 371 } | |
| 372 | |
| 373 bool MatrixBuilder::IsUnaccounted(Channels ch) const { | |
| 374 return std::find(unaccounted_inputs_.begin(), unaccounted_inputs_.end(), | |
| 375 ch) != unaccounted_inputs_.end(); | |
| 376 } | |
| 377 | |
| 378 bool MatrixBuilder::HasInputChannel(Channels ch) const { | |
| 379 return ChannelOrder(input_layout_, ch) >= 0; | |
| 380 } | |
| 381 | |
| 382 bool MatrixBuilder::HasOutputChannel(Channels ch) const { | |
| 383 return ChannelOrder(output_layout_, ch) >= 0; | |
| 384 } | |
| 385 | |
| 386 void MatrixBuilder::Mix(Channels input_ch, Channels output_ch, float scale) { | |
| 387 MixWithoutAccounting(input_ch, output_ch, scale); | |
| 388 AccountFor(input_ch); | |
| 389 } | |
| 390 | |
| 391 void MatrixBuilder::MixWithoutAccounting(Channels input_ch, Channels output_ch, | |
| 392 float scale) { | |
| 393 int input_ch_index = ChannelOrder(input_layout_, input_ch); | |
| 394 int output_ch_index = ChannelOrder(output_layout_, output_ch); | |
| 395 | |
| 396 DCHECK(IsUnaccounted(input_ch)); | |
| 397 DCHECK_GE(input_ch_index, 0); | |
| 398 DCHECK_GE(output_ch_index, 0); | |
| 399 | |
| 400 DCHECK_EQ((*matrix_)[output_ch_index][input_ch_index], 0); | |
| 401 (*matrix_)[output_ch_index][input_ch_index] = scale; | |
| 402 } | |
| 403 | |
| 404 } // namespace media | 79 } // namespace media |
| OLD | NEW |