Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(76)

Side by Side Diff: media/base/channel_mixer.cc

Issue 672793002: Expose the internal MatrixBuilder class as ChannelMixingMatrix. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: List expected_value first in ASSERT_FLOAT_EQ Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/base/BUILD.gn ('k') | media/base/channel_mixer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« no previous file with comments | « media/base/BUILD.gn ('k') | media/base/channel_mixer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698