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 |