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 |