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