1 /*
2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "audio/utility/channel_mixing_matrix.h"
12
13 #include <stddef.h>
14
15 #include <algorithm>
16
17 #include "audio/utility/channel_mixer.h"
18 #include "rtc_base/checks.h"
19 #include "rtc_base/logging.h"
20 #include "system_wrappers/include/field_trial.h"
21
22 namespace webrtc {
23
24 namespace {
25
26 // Selects the default usage of VoIP channel mapping adjustments.
UseChannelMappingAdjustmentsByDefault()27 bool UseChannelMappingAdjustmentsByDefault() {
28 return !field_trial::IsEnabled(
29 "WebRTC-VoIPChannelRemixingAdjustmentKillSwitch");
30 }
31
32 } // namespace
33
ValidateLayout(ChannelLayout layout)34 static void ValidateLayout(ChannelLayout layout) {
35 RTC_CHECK_NE(layout, CHANNEL_LAYOUT_NONE);
36 RTC_CHECK_LE(layout, CHANNEL_LAYOUT_MAX);
37 RTC_CHECK_NE(layout, CHANNEL_LAYOUT_UNSUPPORTED);
38 RTC_CHECK_NE(layout, CHANNEL_LAYOUT_DISCRETE);
39 RTC_CHECK_NE(layout, CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC);
40
41 // Verify there's at least one channel. Should always be true here by virtue
42 // of not being one of the invalid layouts, but lets double check to be sure.
43 int channel_count = ChannelLayoutToChannelCount(layout);
44 RTC_DCHECK_GT(channel_count, 0);
45
46 // If we have more than one channel, verify a symmetric layout for sanity.
47 // The unit test will verify all possible layouts, so this can be a DCHECK.
48 // Symmetry allows simplifying the matrix building code by allowing us to
49 // assume that if one channel of a pair exists, the other will too.
50 if (channel_count > 1) {
51 // Assert that LEFT exists if and only if RIGHT exists, and so on.
52 RTC_DCHECK_EQ(ChannelOrder(layout, LEFT) >= 0,
53 ChannelOrder(layout, RIGHT) >= 0);
54 RTC_DCHECK_EQ(ChannelOrder(layout, SIDE_LEFT) >= 0,
55 ChannelOrder(layout, SIDE_RIGHT) >= 0);
56 RTC_DCHECK_EQ(ChannelOrder(layout, BACK_LEFT) >= 0,
57 ChannelOrder(layout, BACK_RIGHT) >= 0);
58 RTC_DCHECK_EQ(ChannelOrder(layout, LEFT_OF_CENTER) >= 0,
59 ChannelOrder(layout, RIGHT_OF_CENTER) >= 0);
60 } else {
61 RTC_DCHECK_EQ(layout, CHANNEL_LAYOUT_MONO);
62 }
63 }
64
ChannelMixingMatrix(ChannelLayout input_layout,int input_channels,ChannelLayout output_layout,int output_channels)65 ChannelMixingMatrix::ChannelMixingMatrix(ChannelLayout input_layout,
66 int input_channels,
67 ChannelLayout output_layout,
68 int output_channels)
69 : use_voip_channel_mapping_adjustments_(
70 UseChannelMappingAdjustmentsByDefault()),
71 input_layout_(input_layout),
72 input_channels_(input_channels),
73 output_layout_(output_layout),
74 output_channels_(output_channels) {
75 // Stereo down mix should never be the output layout.
76 RTC_CHECK_NE(output_layout, CHANNEL_LAYOUT_STEREO_DOWNMIX);
77
78 // Verify that the layouts are supported
79 if (input_layout != CHANNEL_LAYOUT_DISCRETE)
80 ValidateLayout(input_layout);
81 if (output_layout != CHANNEL_LAYOUT_DISCRETE)
82 ValidateLayout(output_layout);
83
84 // Special case for 5.0, 5.1 with back channels when upmixed to 7.0, 7.1,
85 // which should map the back LR to side LR.
86 if (input_layout_ == CHANNEL_LAYOUT_5_0_BACK &&
87 output_layout_ == CHANNEL_LAYOUT_7_0) {
88 input_layout_ = CHANNEL_LAYOUT_5_0;
89 } else if (input_layout_ == CHANNEL_LAYOUT_5_1_BACK &&
90 output_layout_ == CHANNEL_LAYOUT_7_1) {
91 input_layout_ = CHANNEL_LAYOUT_5_1;
92 }
93 }
94
95 ChannelMixingMatrix::~ChannelMixingMatrix() = default;
96
CreateTransformationMatrix(std::vector<std::vector<float>> * matrix)97 bool ChannelMixingMatrix::CreateTransformationMatrix(
98 std::vector<std::vector<float>>* matrix) {
99 matrix_ = matrix;
100
101 // Size out the initial matrix.
102 matrix_->reserve(output_channels_);
103 for (int output_ch = 0; output_ch < output_channels_; ++output_ch)
104 matrix_->push_back(std::vector<float>(input_channels_, 0));
105
106 // First check for discrete case.
107 if (input_layout_ == CHANNEL_LAYOUT_DISCRETE ||
108 output_layout_ == CHANNEL_LAYOUT_DISCRETE) {
109 // If the number of input channels is more than output channels, then
110 // copy as many as we can then drop the remaining input channels.
111 // If the number of input channels is less than output channels, then
112 // copy them all, then zero out the remaining output channels.
113 int passthrough_channels = std::min(input_channels_, output_channels_);
114 for (int i = 0; i < passthrough_channels; ++i)
115 (*matrix_)[i][i] = 1;
116
117 return true;
118 }
119
120 // If specified, use adjusted channel mapping for the VoIP scenario.
121 if (use_voip_channel_mapping_adjustments_ &&
122 input_layout_ == CHANNEL_LAYOUT_MONO &&
123 ChannelLayoutToChannelCount(output_layout_) >= 2) {
124 // Only place the mono input in the front left and right channels.
125 (*matrix_)[0][0] = 1.f;
126 (*matrix_)[1][0] = 1.f;
127
128 for (size_t output_ch = 2; output_ch < matrix_->size(); ++output_ch) {
129 (*matrix_)[output_ch][0] = 0.f;
130 }
131 return true;
132 }
133
134 // Route matching channels and figure out which ones aren't accounted for.
135 for (Channels ch = LEFT; ch < CHANNELS_MAX + 1;
136 ch = static_cast<Channels>(ch + 1)) {
137 int input_ch_index = ChannelOrder(input_layout_, ch);
138 if (input_ch_index < 0)
139 continue;
140
141 int output_ch_index = ChannelOrder(output_layout_, ch);
142 if (output_ch_index < 0) {
143 unaccounted_inputs_.push_back(ch);
144 continue;
145 }
146
147 RTC_DCHECK_LT(static_cast<size_t>(output_ch_index), matrix_->size());
148 RTC_DCHECK_LT(static_cast<size_t>(input_ch_index),
149 (*matrix_)[output_ch_index].size());
150 (*matrix_)[output_ch_index][input_ch_index] = 1;
151 }
152
153 // If all input channels are accounted for, there's nothing left to do.
154 if (unaccounted_inputs_.empty()) {
155 // Since all output channels map directly to inputs we can optimize.
156 return true;
157 }
158
159 // Mix front LR into center.
160 if (IsUnaccounted(LEFT)) {
161 // When down mixing to mono from stereo, we need to be careful of full scale
162 // stereo mixes. Scaling by 1 / sqrt(2) here will likely lead to clipping
163 // so we use 1 / 2 instead.
164 float scale =
165 (output_layout_ == CHANNEL_LAYOUT_MONO && input_channels_ == 2)
166 ? 0.5
167 : ChannelMixer::kHalfPower;
168 Mix(LEFT, CENTER, scale);
169 Mix(RIGHT, CENTER, scale);
170 }
171
172 // Mix center into front LR.
173 if (IsUnaccounted(CENTER)) {
174 // When up mixing from mono, just do a copy to front LR.
175 float scale =
176 (input_layout_ == CHANNEL_LAYOUT_MONO) ? 1 : ChannelMixer::kHalfPower;
177 MixWithoutAccounting(CENTER, LEFT, scale);
178 Mix(CENTER, RIGHT, scale);
179 }
180
181 // Mix back LR into: side LR || back center || front LR || front center.
182 if (IsUnaccounted(BACK_LEFT)) {
183 if (HasOutputChannel(SIDE_LEFT)) {
184 // If the input has side LR, mix back LR into side LR, but instead if the
185 // input doesn't have side LR (but output does) copy back LR to side LR.
186 float scale = HasInputChannel(SIDE_LEFT) ? ChannelMixer::kHalfPower : 1;
187 Mix(BACK_LEFT, SIDE_LEFT, scale);
188 Mix(BACK_RIGHT, SIDE_RIGHT, scale);
189 } else if (HasOutputChannel(BACK_CENTER)) {
190 // Mix back LR into back center.
191 Mix(BACK_LEFT, BACK_CENTER, ChannelMixer::kHalfPower);
192 Mix(BACK_RIGHT, BACK_CENTER, ChannelMixer::kHalfPower);
193 } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
194 // Mix back LR into front LR.
195 Mix(BACK_LEFT, LEFT, ChannelMixer::kHalfPower);
196 Mix(BACK_RIGHT, RIGHT, ChannelMixer::kHalfPower);
197 } else {
198 // Mix back LR into front center.
199 Mix(BACK_LEFT, CENTER, ChannelMixer::kHalfPower);
200 Mix(BACK_RIGHT, CENTER, ChannelMixer::kHalfPower);
201 }
202 }
203
204 // Mix side LR into: back LR || back center || front LR || front center.
205 if (IsUnaccounted(SIDE_LEFT)) {
206 if (HasOutputChannel(BACK_LEFT)) {
207 // If the input has back LR, mix side LR into back LR, but instead if the
208 // input doesn't have back LR (but output does) copy side LR to back LR.
209 float scale = HasInputChannel(BACK_LEFT) ? ChannelMixer::kHalfPower : 1;
210 Mix(SIDE_LEFT, BACK_LEFT, scale);
211 Mix(SIDE_RIGHT, BACK_RIGHT, scale);
212 } else if (HasOutputChannel(BACK_CENTER)) {
213 // Mix side LR into back center.
214 Mix(SIDE_LEFT, BACK_CENTER, ChannelMixer::kHalfPower);
215 Mix(SIDE_RIGHT, BACK_CENTER, ChannelMixer::kHalfPower);
216 } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
217 // Mix side LR into front LR.
218 Mix(SIDE_LEFT, LEFT, ChannelMixer::kHalfPower);
219 Mix(SIDE_RIGHT, RIGHT, ChannelMixer::kHalfPower);
220 } else {
221 // Mix side LR into front center.
222 Mix(SIDE_LEFT, CENTER, ChannelMixer::kHalfPower);
223 Mix(SIDE_RIGHT, CENTER, ChannelMixer::kHalfPower);
224 }
225 }
226
227 // Mix back center into: back LR || side LR || front LR || front center.
228 if (IsUnaccounted(BACK_CENTER)) {
229 if (HasOutputChannel(BACK_LEFT)) {
230 // Mix back center into back LR.
231 MixWithoutAccounting(BACK_CENTER, BACK_LEFT, ChannelMixer::kHalfPower);
232 Mix(BACK_CENTER, BACK_RIGHT, ChannelMixer::kHalfPower);
233 } else if (HasOutputChannel(SIDE_LEFT)) {
234 // Mix back center into side LR.
235 MixWithoutAccounting(BACK_CENTER, SIDE_LEFT, ChannelMixer::kHalfPower);
236 Mix(BACK_CENTER, SIDE_RIGHT, ChannelMixer::kHalfPower);
237 } else if (output_layout_ > CHANNEL_LAYOUT_MONO) {
238 // Mix back center into front LR.
239 // TODO(dalecurtis): Not sure about these values?
240 MixWithoutAccounting(BACK_CENTER, LEFT, ChannelMixer::kHalfPower);
241 Mix(BACK_CENTER, RIGHT, ChannelMixer::kHalfPower);
242 } else {
243 // Mix back center into front center.
244 // TODO(dalecurtis): Not sure about these values?
245 Mix(BACK_CENTER, CENTER, ChannelMixer::kHalfPower);
246 }
247 }
248
249 // Mix LR of center into: front LR || front center.
250 if (IsUnaccounted(LEFT_OF_CENTER)) {
251 if (HasOutputChannel(LEFT)) {
252 // Mix LR of center into front LR.
253 Mix(LEFT_OF_CENTER, LEFT, ChannelMixer::kHalfPower);
254 Mix(RIGHT_OF_CENTER, RIGHT, ChannelMixer::kHalfPower);
255 } else {
256 // Mix LR of center into front center.
257 Mix(LEFT_OF_CENTER, CENTER, ChannelMixer::kHalfPower);
258 Mix(RIGHT_OF_CENTER, CENTER, ChannelMixer::kHalfPower);
259 }
260 }
261
262 // Mix LFE into: front center || front LR.
263 if (IsUnaccounted(LFE)) {
264 if (!HasOutputChannel(CENTER)) {
265 // Mix LFE into front LR.
266 MixWithoutAccounting(LFE, LEFT, ChannelMixer::kHalfPower);
267 Mix(LFE, RIGHT, ChannelMixer::kHalfPower);
268 } else {
269 // Mix LFE into front center.
270 Mix(LFE, CENTER, ChannelMixer::kHalfPower);
271 }
272 }
273
274 // All channels should now be accounted for.
275 RTC_DCHECK(unaccounted_inputs_.empty());
276
277 // See if the output |matrix_| is simply a remapping matrix. If each input
278 // channel maps to a single output channel we can simply remap. Doing this
279 // programmatically is less fragile than logic checks on channel mappings.
280 for (int output_ch = 0; output_ch < output_channels_; ++output_ch) {
281 int input_mappings = 0;
282 for (int input_ch = 0; input_ch < input_channels_; ++input_ch) {
283 // We can only remap if each row contains a single scale of 1. I.e., each
284 // output channel is mapped from a single unscaled input channel.
285 if ((*matrix_)[output_ch][input_ch] != 1 || ++input_mappings > 1)
286 return false;
287 }
288 }
289
290 // If we've gotten here, |matrix_| is simply a remapping.
291 return true;
292 }
293
AccountFor(Channels ch)294 void ChannelMixingMatrix::AccountFor(Channels ch) {
295 unaccounted_inputs_.erase(
296 std::find(unaccounted_inputs_.begin(), unaccounted_inputs_.end(), ch));
297 }
298
IsUnaccounted(Channels ch) const299 bool ChannelMixingMatrix::IsUnaccounted(Channels ch) const {
300 return std::find(unaccounted_inputs_.begin(), unaccounted_inputs_.end(),
301 ch) != unaccounted_inputs_.end();
302 }
303
HasInputChannel(Channels ch) const304 bool ChannelMixingMatrix::HasInputChannel(Channels ch) const {
305 return ChannelOrder(input_layout_, ch) >= 0;
306 }
307
HasOutputChannel(Channels ch) const308 bool ChannelMixingMatrix::HasOutputChannel(Channels ch) const {
309 return ChannelOrder(output_layout_, ch) >= 0;
310 }
311
Mix(Channels input_ch,Channels output_ch,float scale)312 void ChannelMixingMatrix::Mix(Channels input_ch,
313 Channels output_ch,
314 float scale) {
315 MixWithoutAccounting(input_ch, output_ch, scale);
316 AccountFor(input_ch);
317 }
318
MixWithoutAccounting(Channels input_ch,Channels output_ch,float scale)319 void ChannelMixingMatrix::MixWithoutAccounting(Channels input_ch,
320 Channels output_ch,
321 float scale) {
322 int input_ch_index = ChannelOrder(input_layout_, input_ch);
323 int output_ch_index = ChannelOrder(output_layout_, output_ch);
324
325 RTC_DCHECK(IsUnaccounted(input_ch));
326 RTC_DCHECK_GE(input_ch_index, 0);
327 RTC_DCHECK_GE(output_ch_index, 0);
328
329 RTC_DCHECK_EQ((*matrix_)[output_ch_index][input_ch_index], 0);
330 (*matrix_)[output_ch_index][input_ch_index] = scale;
331 }
332
333 } // namespace webrtc
334