Retry aa3219ce27ef5471c19670fe5145b136925cbc89:
[chromium-blink-merge.git] / media / base / channel_mixing_matrix.cc
blobeee0efd0c33017196f5c3e6481408ac8918285dc
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
3 // found in the LICENSE file.
5 // MSVC++ requires this to be set before any other includes to get M_SQRT1_2.
6 #define _USE_MATH_DEFINES
8 #include "media/base/channel_mixing_matrix.h"
10 #include <algorithm>
11 #include <cmath>
13 #include "base/logging.h"
15 namespace media {
17 // Default scale factor for mixing two channels together. We use a different
18 // value for stereo -> mono and mono -> stereo mixes.
19 static const float kEqualPowerScale = static_cast<float>(M_SQRT1_2);
21 static void ValidateLayout(ChannelLayout layout) {
22 CHECK_NE(layout, CHANNEL_LAYOUT_NONE);
23 CHECK_LE(layout, CHANNEL_LAYOUT_MAX);
24 CHECK_NE(layout, CHANNEL_LAYOUT_UNSUPPORTED);
25 CHECK_NE(layout, CHANNEL_LAYOUT_DISCRETE);
26 CHECK_NE(layout, CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC);
28 // Verify there's at least one channel. Should always be true here by virtue
29 // of not being one of the invalid layouts, but lets double check to be sure.
30 int channel_count = ChannelLayoutToChannelCount(layout);
31 DCHECK_GT(channel_count, 0);
33 // If we have more than one channel, verify a symmetric layout for sanity.
34 // The unit test will verify all possible layouts, so this can be a DCHECK.
35 // Symmetry allows simplifying the matrix building code by allowing us to
36 // assume that if one channel of a pair exists, the other will too.
37 if (channel_count > 1) {
38 // Assert that LEFT exists if and only if RIGHT exists, and so on.
39 DCHECK_EQ(ChannelOrder(layout, LEFT) >= 0,
40 ChannelOrder(layout, RIGHT) >= 0);
41 DCHECK_EQ(ChannelOrder(layout, SIDE_LEFT) >= 0,
42 ChannelOrder(layout, SIDE_RIGHT) >= 0);
43 DCHECK_EQ(ChannelOrder(layout, BACK_LEFT) >= 0,
44 ChannelOrder(layout, BACK_RIGHT) >= 0);
45 DCHECK_EQ(ChannelOrder(layout, LEFT_OF_CENTER) >= 0,
46 ChannelOrder(layout, RIGHT_OF_CENTER) >= 0);
47 } else {
48 DCHECK_EQ(layout, CHANNEL_LAYOUT_MONO);
52 ChannelMixingMatrix::ChannelMixingMatrix(ChannelLayout input_layout,
53 int input_channels,
54 ChannelLayout output_layout,
55 int output_channels)
56 : input_layout_(input_layout),
57 input_channels_(input_channels),
58 output_layout_(output_layout),
59 output_channels_(output_channels) {
60 // Stereo down mix should never be the output layout.
61 CHECK_NE(output_layout, CHANNEL_LAYOUT_STEREO_DOWNMIX);
63 // Verify that the layouts are supported
64 if (input_layout != CHANNEL_LAYOUT_DISCRETE)
65 ValidateLayout(input_layout);
66 if (output_layout != CHANNEL_LAYOUT_DISCRETE)
67 ValidateLayout(output_layout);
69 // Special case for 5.0, 5.1 with back channels when upmixed to 7.0, 7.1,
70 // which should map the back LR to side LR.
71 if (input_layout_ == CHANNEL_LAYOUT_5_0_BACK &&
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;
80 ChannelMixingMatrix::~ChannelMixingMatrix() {
83 bool ChannelMixingMatrix::CreateTransformationMatrix(
84 std::vector<std::vector<float>>* matrix) {
85 matrix_ = matrix;
87 // Size out the initial matrix.
88 matrix_->reserve(output_channels_);
89 for (int output_ch = 0; output_ch < output_channels_; ++output_ch)
90 matrix_->push_back(std::vector<float>(input_channels_, 0));
92 // First check for discrete case.
93 if (input_layout_ == CHANNEL_LAYOUT_DISCRETE ||
94 output_layout_ == CHANNEL_LAYOUT_DISCRETE) {
95 // If the number of input channels is more than output channels, then
96 // copy as many as we can then drop the remaining input channels.
97 // If the number of input channels is less than output channels, then
98 // copy them all, then zero out the remaining output channels.
99 int passthrough_channels = std::min(input_channels_, output_channels_);
100 for (int i = 0; i < passthrough_channels; ++i)
101 (*matrix_)[i][i] = 1;
103 return true;
106 // Route matching channels and figure out which ones aren't accounted for.
107 for (Channels ch = LEFT; ch < CHANNELS_MAX + 1;
108 ch = static_cast<Channels>(ch + 1)) {
109 int input_ch_index = ChannelOrder(input_layout_, ch);
110 if (input_ch_index < 0)
111 continue;
113 int output_ch_index = ChannelOrder(output_layout_, ch);
114 if (output_ch_index < 0) {
115 unaccounted_inputs_.push_back(ch);
116 continue;
119 DCHECK_LT(static_cast<size_t>(output_ch_index), matrix_->size());
120 DCHECK_LT(static_cast<size_t>(input_ch_index),
121 (*matrix_)[output_ch_index].size());
122 (*matrix_)[output_ch_index][input_ch_index] = 1;
125 // If all input channels are accounted for, there's nothing left to do.
126 if (unaccounted_inputs_.empty()) {
127 // Since all output channels map directly to inputs we can optimize.
128 return true;
131 // Mix front LR into center.
132 if (IsUnaccounted(LEFT)) {
133 // When down mixing to mono from stereo, we need to be careful of full scale
134 // stereo mixes. Scaling by 1 / sqrt(2) here will likely lead to clipping
135 // so we use 1 / 2 instead.
136 float scale =
137 (output_layout_ == CHANNEL_LAYOUT_MONO && input_channels_ == 2) ?
138 0.5 : kEqualPowerScale;
139 Mix(LEFT, CENTER, scale);
140 Mix(RIGHT, CENTER, scale);
143 // Mix center into front LR.
144 if (IsUnaccounted(CENTER)) {
145 // When up mixing from mono, just do a copy to front LR.
146 float scale =
147 (input_layout_ == CHANNEL_LAYOUT_MONO) ? 1 : kEqualPowerScale;
148 MixWithoutAccounting(CENTER, LEFT, scale);
149 Mix(CENTER, RIGHT, scale);
152 // Mix back LR into: side LR || back center || front LR || front center.
153 if (IsUnaccounted(BACK_LEFT)) {
154 if (HasOutputChannel(SIDE_LEFT)) {
155 // If the input has side LR, mix back LR into side LR, but instead if the
156 // input doesn't have side LR (but output does) copy back LR to side LR.
157 float scale = HasInputChannel(SIDE_LEFT) ? kEqualPowerScale : 1;
158 Mix(BACK_LEFT, SIDE_LEFT, scale);
159 Mix(BACK_RIGHT, SIDE_RIGHT, scale);
160 } else if (HasOutputChannel(BACK_CENTER)) {
161 // Mix back LR into back center.
162 Mix(BACK_LEFT, BACK_CENTER, kEqualPowerScale);
163 Mix(BACK_RIGHT, BACK_CENTER, kEqualPowerScale);
164 } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
165 // Mix back LR into front LR.
166 Mix(BACK_LEFT, LEFT, kEqualPowerScale);
167 Mix(BACK_RIGHT, RIGHT, kEqualPowerScale);
168 } else {
169 // Mix back LR into front center.
170 Mix(BACK_LEFT, CENTER, kEqualPowerScale);
171 Mix(BACK_RIGHT, CENTER, kEqualPowerScale);
175 // Mix side LR into: back LR || back center || front LR || front center.
176 if (IsUnaccounted(SIDE_LEFT)) {
177 if (HasOutputChannel(BACK_LEFT)) {
178 // If the input has back LR, mix side LR into back LR, but instead if the
179 // input doesn't have back LR (but output does) copy side LR to back LR.
180 float scale = HasInputChannel(BACK_LEFT) ? kEqualPowerScale : 1;
181 Mix(SIDE_LEFT, BACK_LEFT, scale);
182 Mix(SIDE_RIGHT, BACK_RIGHT, scale);
183 } else if (HasOutputChannel(BACK_CENTER)) {
184 // Mix side LR into back center.
185 Mix(SIDE_LEFT, BACK_CENTER, kEqualPowerScale);
186 Mix(SIDE_RIGHT, BACK_CENTER, kEqualPowerScale);
187 } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
188 // Mix side LR into front LR.
189 Mix(SIDE_LEFT, LEFT, kEqualPowerScale);
190 Mix(SIDE_RIGHT, RIGHT, kEqualPowerScale);
191 } else {
192 // Mix side LR into front center.
193 Mix(SIDE_LEFT, CENTER, kEqualPowerScale);
194 Mix(SIDE_RIGHT, CENTER, kEqualPowerScale);
198 // Mix back center into: back LR || side LR || front LR || front center.
199 if (IsUnaccounted(BACK_CENTER)) {
200 if (HasOutputChannel(BACK_LEFT)) {
201 // Mix back center into back LR.
202 MixWithoutAccounting(BACK_CENTER, BACK_LEFT, kEqualPowerScale);
203 Mix(BACK_CENTER, BACK_RIGHT, kEqualPowerScale);
204 } else if (HasOutputChannel(SIDE_LEFT)) {
205 // Mix back center into side LR.
206 MixWithoutAccounting(BACK_CENTER, SIDE_LEFT, kEqualPowerScale);
207 Mix(BACK_CENTER, SIDE_RIGHT, kEqualPowerScale);
208 } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
209 // Mix back center into front LR.
210 // TODO(dalecurtis): Not sure about these values?
211 MixWithoutAccounting(BACK_CENTER, LEFT, kEqualPowerScale);
212 Mix(BACK_CENTER, RIGHT, kEqualPowerScale);
213 } else {
214 // Mix back center into front center.
215 // TODO(dalecurtis): Not sure about these values?
216 Mix(BACK_CENTER, CENTER, kEqualPowerScale);
220 // Mix LR of center into: front LR || front center.
221 if (IsUnaccounted(LEFT_OF_CENTER)) {
222 if (HasOutputChannel(LEFT)) {
223 // Mix LR of center into front LR.
224 Mix(LEFT_OF_CENTER, LEFT, kEqualPowerScale);
225 Mix(RIGHT_OF_CENTER, RIGHT, kEqualPowerScale);
226 } else {
227 // Mix LR of center into front center.
228 Mix(LEFT_OF_CENTER, CENTER, kEqualPowerScale);
229 Mix(RIGHT_OF_CENTER, CENTER, kEqualPowerScale);
233 // Mix LFE into: front center || front LR.
234 if (IsUnaccounted(LFE)) {
235 if (!HasOutputChannel(CENTER)) {
236 // Mix LFE into front LR.
237 MixWithoutAccounting(LFE, LEFT, kEqualPowerScale);
238 Mix(LFE, RIGHT, kEqualPowerScale);
239 } else {
240 // Mix LFE into front center.
241 Mix(LFE, CENTER, kEqualPowerScale);
245 // All channels should now be accounted for.
246 DCHECK(unaccounted_inputs_.empty());
248 // See if the output |matrix_| is simply a remapping matrix. If each input
249 // channel maps to a single output channel we can simply remap. Doing this
250 // programmatically is less fragile than logic checks on channel mappings.
251 for (int output_ch = 0; output_ch < output_channels_; ++output_ch) {
252 int input_mappings = 0;
253 for (int input_ch = 0; input_ch < input_channels_; ++input_ch) {
254 // We can only remap if each row contains a single scale of 1. I.e., each
255 // output channel is mapped from a single unscaled input channel.
256 if ((*matrix_)[output_ch][input_ch] != 1 || ++input_mappings > 1)
257 return false;
261 // If we've gotten here, |matrix_| is simply a remapping.
262 return true;
265 void ChannelMixingMatrix::AccountFor(Channels ch) {
266 unaccounted_inputs_.erase(std::find(
267 unaccounted_inputs_.begin(), unaccounted_inputs_.end(), ch));
270 bool ChannelMixingMatrix::IsUnaccounted(Channels ch) const {
271 return std::find(unaccounted_inputs_.begin(), unaccounted_inputs_.end(),
272 ch) != unaccounted_inputs_.end();
275 bool ChannelMixingMatrix::HasInputChannel(Channels ch) const {
276 return ChannelOrder(input_layout_, ch) >= 0;
279 bool ChannelMixingMatrix::HasOutputChannel(Channels ch) const {
280 return ChannelOrder(output_layout_, ch) >= 0;
283 void ChannelMixingMatrix::Mix(Channels input_ch,
284 Channels output_ch,
285 float scale) {
286 MixWithoutAccounting(input_ch, output_ch, scale);
287 AccountFor(input_ch);
290 void ChannelMixingMatrix::MixWithoutAccounting(Channels input_ch,
291 Channels output_ch,
292 float scale) {
293 int input_ch_index = ChannelOrder(input_layout_, input_ch);
294 int output_ch_index = ChannelOrder(output_layout_, output_ch);
296 DCHECK(IsUnaccounted(input_ch));
297 DCHECK_GE(input_ch_index, 0);
298 DCHECK_GE(output_ch_index, 0);
300 DCHECK_EQ((*matrix_)[output_ch_index][input_ch_index], 0);
301 (*matrix_)[output_ch_index][input_ch_index] = scale;
304 } // namespace media