channel_mixing_matrix_unittest.cc (15742B)
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 <cstddef> 14 #include <vector> 15 16 #include "api/audio/channel_layout.h" 17 #include "audio/utility/channel_mixer.h" 18 #include "rtc_base/strings/string_builder.h" 19 #include "test/gtest.h" 20 21 namespace webrtc { 22 23 // Test all possible layout conversions can be constructed and mixed. 24 // Also ensure that the channel matrix fulfill certain conditions when remapping 25 // is supported. 26 TEST(ChannelMixingMatrixTest, ConstructAllPossibleLayouts) { 27 for (ChannelLayout input_layout = CHANNEL_LAYOUT_MONO; 28 input_layout <= CHANNEL_LAYOUT_MAX; 29 input_layout = static_cast<ChannelLayout>(input_layout + 1)) { 30 for (ChannelLayout output_layout = CHANNEL_LAYOUT_MONO; 31 output_layout <= CHANNEL_LAYOUT_MAX; 32 output_layout = static_cast<ChannelLayout>(output_layout + 1)) { 33 // DISCRETE, BITSTREAM can't be tested here based on the current approach. 34 // CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC is not mixable. 35 // Stereo down mix should never be the output layout. 36 if (input_layout == CHANNEL_LAYOUT_BITSTREAM || 37 input_layout == CHANNEL_LAYOUT_DISCRETE || 38 input_layout == CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC || 39 output_layout == CHANNEL_LAYOUT_BITSTREAM || 40 output_layout == CHANNEL_LAYOUT_DISCRETE || 41 output_layout == CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC || 42 output_layout == CHANNEL_LAYOUT_STEREO_DOWNMIX) { 43 continue; 44 } 45 46 StringBuilder ss; 47 ss << "Input Layout: " << input_layout 48 << ", Output Layout: " << output_layout; 49 SCOPED_TRACE(ss.str()); 50 ChannelMixingMatrix matrix_builder( 51 input_layout, ChannelLayoutToChannelCount(input_layout), 52 output_layout, ChannelLayoutToChannelCount(output_layout)); 53 const int input_channels = ChannelLayoutToChannelCount(input_layout); 54 const int output_channels = ChannelLayoutToChannelCount(output_layout); 55 std::vector<std::vector<float>> matrix; 56 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 57 58 if (remapping) { 59 // Also ensure that (when remapping can take place), a maximum of one 60 // input channel is included per output. This knowledge will simplify 61 // the channel mixing algorithm since it allows us to find the only 62 // scale factor which equals 1.0 and copy that input to its 63 // corresponding output. If no such factor can be found, the 64 // corresponding output can be set to zero. 65 for (int i = 0; i < output_channels; i++) { 66 EXPECT_EQ(static_cast<size_t>(input_channels), matrix[i].size()); 67 int num_input_channels_accounted_for_per_output = 0; 68 for (int j = 0; j < input_channels; j++) { 69 float scale = matrix[i][j]; 70 if (scale > 0) { 71 EXPECT_EQ(scale, 1.0f); 72 num_input_channels_accounted_for_per_output++; 73 } 74 } 75 // Each output channel shall contain contribution from one or less 76 // input channels. 77 EXPECT_LE(num_input_channels_accounted_for_per_output, 1); 78 } 79 } 80 } 81 } 82 } 83 84 // Verify channels are mixed and scaled correctly. 85 TEST(ChannelMixingMatrixTest, StereoToMono) { 86 ChannelLayout input_layout = CHANNEL_LAYOUT_STEREO; 87 ChannelLayout output_layout = CHANNEL_LAYOUT_MONO; 88 ChannelMixingMatrix matrix_builder( 89 input_layout, ChannelLayoutToChannelCount(input_layout), output_layout, 90 ChannelLayoutToChannelCount(output_layout)); 91 std::vector<std::vector<float>> matrix; 92 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 93 94 // Input: stereo 95 // LEFT RIGHT 96 // Output: mono CENTER 0.5 0.5 97 // 98 EXPECT_FALSE(remapping); 99 EXPECT_EQ(1u, matrix.size()); 100 EXPECT_EQ(2u, matrix[0].size()); 101 EXPECT_EQ(0.5f, matrix[0][0]); 102 EXPECT_EQ(0.5f, matrix[0][1]); 103 } 104 105 TEST(ChannelMixingMatrixTest, MonoToStereo) { 106 ChannelLayout input_layout = CHANNEL_LAYOUT_MONO; 107 ChannelLayout output_layout = CHANNEL_LAYOUT_STEREO; 108 ChannelMixingMatrix matrix_builder( 109 input_layout, ChannelLayoutToChannelCount(input_layout), output_layout, 110 ChannelLayoutToChannelCount(output_layout)); 111 std::vector<std::vector<float>> matrix; 112 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 113 114 // Input: mono 115 // CENTER 116 // Output: stereo LEFT 1 117 // RIGHT 1 118 // 119 EXPECT_TRUE(remapping); 120 EXPECT_EQ(2u, matrix.size()); 121 EXPECT_EQ(1u, matrix[0].size()); 122 EXPECT_EQ(1.0f, matrix[0][0]); 123 EXPECT_EQ(1u, matrix[1].size()); 124 EXPECT_EQ(1.0f, matrix[1][0]); 125 } 126 127 TEST(ChannelMixingMatrixTest, MonoToTwoOne) { 128 ChannelLayout input_layout = CHANNEL_LAYOUT_MONO; 129 ChannelLayout output_layout = CHANNEL_LAYOUT_2_1; 130 ChannelMixingMatrix matrix_builder( 131 input_layout, ChannelLayoutToChannelCount(input_layout), output_layout, 132 ChannelLayoutToChannelCount(output_layout)); 133 std::vector<std::vector<float>> matrix; 134 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 135 136 // Input: mono 137 // CENTER 138 // Output: 2.1 FRONT_LEFT 1 139 // FRONT_RIGHT 1 140 // BACK_CENTER 0 141 // 142 EXPECT_TRUE(remapping); 143 EXPECT_EQ(3u, matrix.size()); 144 EXPECT_EQ(1u, matrix[0].size()); 145 EXPECT_EQ(1.0f, matrix[0][0]); 146 EXPECT_EQ(1.0f, matrix[1][0]); 147 EXPECT_EQ(0.0f, matrix[2][0]); 148 } 149 150 TEST(ChannelMixingMatrixTest, MonoToFiveOne) { 151 ChannelLayout input_layout = CHANNEL_LAYOUT_MONO; 152 ChannelLayout output_layout = CHANNEL_LAYOUT_5_1; 153 const int input_channels = ChannelLayoutToChannelCount(input_layout); 154 const int output_channels = ChannelLayoutToChannelCount(output_layout); 155 ChannelMixingMatrix matrix_builder(input_layout, input_channels, 156 output_layout, output_channels); 157 std::vector<std::vector<float>> matrix; 158 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 159 // Input: mono 160 // CENTER 161 // Output: 5.1 LEFT 1 162 // RIGHT 1 163 // CENTER 0 164 // LFE 0 165 // SIDE_LEFT 0 166 // SIDE_RIGHT 0 167 // 168 EXPECT_TRUE(remapping); 169 EXPECT_EQ(static_cast<size_t>(output_channels), matrix.size()); 170 for (int n = 0; n < output_channels; n++) { 171 EXPECT_EQ(static_cast<size_t>(input_channels), matrix[n].size()); 172 if (n == LEFT || n == RIGHT) { 173 EXPECT_EQ(1.0f, matrix[n][0]); 174 } else { 175 EXPECT_EQ(0.0f, matrix[n][0]); 176 } 177 } 178 } 179 180 TEST(ChannelMixingMatrixTest, MonoToSevenOne) { 181 ChannelLayout input_layout = CHANNEL_LAYOUT_MONO; 182 ChannelLayout output_layout = CHANNEL_LAYOUT_7_1; 183 const int input_channels = ChannelLayoutToChannelCount(input_layout); 184 const int output_channels = ChannelLayoutToChannelCount(output_layout); 185 ChannelMixingMatrix matrix_builder(input_layout, input_channels, 186 output_layout, output_channels); 187 std::vector<std::vector<float>> matrix; 188 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 189 // Input: mono 190 // CENTER 191 // Output: 7.1 LEFT 1 192 // RIGHT 1 193 // CENTER 0 194 // LFE 0 195 // SIDE_LEFT 0 196 // SIDE_RIGHT 0 197 // BACK_LEFT 0 198 // BACK_RIGHT 0 199 // 200 EXPECT_TRUE(remapping); 201 EXPECT_EQ(static_cast<size_t>(output_channels), matrix.size()); 202 for (int n = 0; n < output_channels; n++) { 203 EXPECT_EQ(static_cast<size_t>(input_channels), matrix[n].size()); 204 if (n == LEFT || n == RIGHT) { 205 EXPECT_EQ(1.0f, matrix[n][0]); 206 } else { 207 EXPECT_EQ(0.0f, matrix[n][0]); 208 } 209 } 210 } 211 212 TEST(ChannelMixingMatrixTest, FiveOneToMono) { 213 ChannelLayout input_layout = CHANNEL_LAYOUT_5_1; 214 ChannelLayout output_layout = CHANNEL_LAYOUT_MONO; 215 ChannelMixingMatrix matrix_builder( 216 input_layout, ChannelLayoutToChannelCount(input_layout), output_layout, 217 ChannelLayoutToChannelCount(output_layout)); 218 std::vector<std::vector<float>> matrix; 219 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 220 221 // Note: 1/sqrt(2) is shown as 0.707. 222 // 223 // Input: 5.1 224 // LEFT RIGHT CENTER LFE SIDE_LEFT SIDE_RIGHT 225 // Output: mono CENTER 0.707 0.707 1 0.707 0.707 0.707 226 // 227 EXPECT_FALSE(remapping); 228 EXPECT_EQ(1u, matrix.size()); 229 EXPECT_EQ(6u, matrix[0].size()); 230 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[0][0]); 231 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[0][1]); 232 // The center channel will be mixed at scale 1. 233 EXPECT_EQ(1.0f, matrix[0][2]); 234 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[0][3]); 235 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[0][4]); 236 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[0][5]); 237 } 238 239 TEST(ChannelMixingMatrixTest, FiveOneBackToStereo) { 240 // Front L, Front R, Front C, LFE, Back L, Back R 241 ChannelLayout input_layout = CHANNEL_LAYOUT_5_1_BACK; 242 ChannelLayout output_layout = CHANNEL_LAYOUT_STEREO; 243 const int input_channels = ChannelLayoutToChannelCount(input_layout); 244 const int output_channels = ChannelLayoutToChannelCount(output_layout); 245 ChannelMixingMatrix matrix_builder(input_layout, input_channels, 246 output_layout, output_channels); 247 std::vector<std::vector<float>> matrix; 248 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 249 250 // Note: 1/sqrt(2) is shown as 0.707. 251 // Note: The Channels enumerator is given by {LEFT = 0, RIGHT, CENTER, LFE, 252 // BACK_LEFT, BACK_RIGHT,...}, hence we can use the enumerator values as 253 // indexes in the matrix when verifying the scaling factors. 254 // 255 // Input: 5.1 256 // LEFT RIGHT CENTER LFE BACK_LEFT BACK_RIGHT 257 // Output: stereo LEFT 1 0 0.707 0.707 0.707 0 258 // RIGHT 0 1 0.707 0.707 0 0.707 259 // 260 EXPECT_FALSE(remapping); 261 EXPECT_EQ(static_cast<size_t>(output_channels), matrix.size()); 262 EXPECT_EQ(static_cast<size_t>(input_channels), matrix[LEFT].size()); 263 EXPECT_EQ(static_cast<size_t>(input_channels), matrix[RIGHT].size()); 264 EXPECT_EQ(1.0f, matrix[LEFT][LEFT]); 265 EXPECT_EQ(1.0f, matrix[RIGHT][RIGHT]); 266 EXPECT_EQ(0.0f, matrix[LEFT][RIGHT]); 267 EXPECT_EQ(0.0f, matrix[RIGHT][LEFT]); 268 EXPECT_EQ(0.0f, matrix[LEFT][BACK_RIGHT]); 269 EXPECT_EQ(0.0f, matrix[RIGHT][BACK_LEFT]); 270 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[LEFT][CENTER]); 271 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[LEFT][LFE]); 272 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[LEFT][BACK_LEFT]); 273 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[RIGHT][CENTER]); 274 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[RIGHT][LFE]); 275 EXPECT_FLOAT_EQ(ChannelMixer::kHalfPower, matrix[RIGHT][BACK_RIGHT]); 276 } 277 278 TEST(ChannelMixingMatrixTest, FiveOneToSevenOne) { 279 // Front L, Front R, Front C, LFE, Side L, Side R 280 ChannelLayout input_layout = CHANNEL_LAYOUT_5_1; 281 // Front L, Front R, Front C, LFE, Side L, Side R, Back L, Back R 282 ChannelLayout output_layout = CHANNEL_LAYOUT_7_1; 283 const int input_channels = ChannelLayoutToChannelCount(input_layout); 284 const int output_channels = ChannelLayoutToChannelCount(output_layout); 285 ChannelMixingMatrix matrix_builder(input_layout, input_channels, 286 output_layout, output_channels); 287 std::vector<std::vector<float>> matrix; 288 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 289 290 // Input: 5.1 291 // LEFT RIGHT CENTER LFE SIDE_LEFT SIDE_RIGHT 292 // Output: 7.1 LEFT 1 0 0 0 0 0 293 // RIGHT 0 1 0 0 0 0 294 // CENTER 0 0 1 0 0 0 295 // LFE 0 0 0 1 0 0 296 // SIDE_LEFT 0 0 0 0 1 0 297 // SIDE_RIGHT 0 0 0 0 0 1 298 // BACK_LEFT 0 0 0 0 0 0 299 // BACK_RIGHT 0 0 0 0 0 0 300 // 301 EXPECT_TRUE(remapping); 302 EXPECT_EQ(static_cast<size_t>(output_channels), matrix.size()); 303 for (int i = 0; i < output_channels; i++) { 304 EXPECT_EQ(static_cast<size_t>(input_channels), matrix[i].size()); 305 for (int j = 0; j < input_channels; j++) { 306 if (i == j) { 307 EXPECT_EQ(1.0f, matrix[i][j]); 308 } else { 309 EXPECT_EQ(0.0f, matrix[i][j]); 310 } 311 } 312 } 313 } 314 315 TEST(ChannelMixingMatrixTest, StereoToFiveOne) { 316 ChannelLayout input_layout = CHANNEL_LAYOUT_STEREO; 317 ChannelLayout output_layout = CHANNEL_LAYOUT_5_1; 318 const int input_channels = ChannelLayoutToChannelCount(input_layout); 319 const int output_channels = ChannelLayoutToChannelCount(output_layout); 320 ChannelMixingMatrix matrix_builder(input_layout, input_channels, 321 output_layout, output_channels); 322 std::vector<std::vector<float>> matrix; 323 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 324 325 // Input: Stereo 326 // LEFT RIGHT 327 // Output: 5.1 LEFT 1 0 328 // RIGHT 0 1 329 // CENTER 0 0 330 // LFE 0 0 331 // SIDE_LEFT 0 0 332 // SIDE_RIGHT 0 0 333 // 334 EXPECT_TRUE(remapping); 335 EXPECT_EQ(static_cast<size_t>(output_channels), matrix.size()); 336 for (int n = 0; n < output_channels; n++) { 337 EXPECT_EQ(static_cast<size_t>(input_channels), matrix[n].size()); 338 if (n == LEFT) { 339 EXPECT_EQ(1.0f, matrix[LEFT][LEFT]); 340 EXPECT_EQ(0.0f, matrix[LEFT][RIGHT]); 341 } else if (n == RIGHT) { 342 EXPECT_EQ(0.0f, matrix[RIGHT][LEFT]); 343 EXPECT_EQ(1.0f, matrix[RIGHT][RIGHT]); 344 } else { 345 EXPECT_EQ(0.0f, matrix[n][LEFT]); 346 EXPECT_EQ(0.0f, matrix[n][RIGHT]); 347 } 348 } 349 } 350 351 TEST(ChannelMixingMatrixTest, DiscreteToDiscrete) { 352 const struct { 353 int input_channels; 354 int output_channels; 355 } test_case[] = { 356 {.input_channels = 2, .output_channels = 2}, 357 {.input_channels = 2, .output_channels = 5}, 358 {.input_channels = 5, .output_channels = 2}, 359 }; 360 361 for (auto [input_channels, output_channels] : test_case) { 362 ChannelMixingMatrix matrix_builder(CHANNEL_LAYOUT_DISCRETE, input_channels, 363 CHANNEL_LAYOUT_DISCRETE, 364 output_channels); 365 std::vector<std::vector<float>> matrix; 366 bool remapping = matrix_builder.CreateTransformationMatrix(&matrix); 367 EXPECT_TRUE(remapping); 368 EXPECT_EQ(static_cast<size_t>(output_channels), matrix.size()); 369 for (int i = 0; i < output_channels; i++) { 370 EXPECT_EQ(static_cast<size_t>(input_channels), matrix[i].size()); 371 for (int j = 0; j < input_channels; j++) { 372 if (i == j) { 373 EXPECT_EQ(1.0f, matrix[i][j]); 374 } else { 375 EXPECT_EQ(0.0f, matrix[i][j]); 376 } 377 } 378 } 379 } 380 } 381 382 } // namespace webrtc