/* * Copyright (c) 2025 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "modules/audio_processing/capture_mixer/remixing_logic.h" #include #include "modules/audio_processing/capture_mixer/channel_content_remixer.h" #include "test/gtest.h" namespace webrtc { namespace { constexpr int kNumFramesSinceActivityForInactive = 101; constexpr int kNumFramesSinceActivityForActive = 99; constexpr int kAverageEnergyForInactive = 10.0f; constexpr int kAverageEnergyForActive = 10000.0f; constexpr int kSilentChannelsModeExitFrames = 1001; constexpr int kSaturatedChannelsModeExitFrames = 301; constexpr int kImbalancedChannelsModeExitFrames = 301; TEST(RemixingLogicTest, InitialState) { RemixingLogic logic(480, RemixingLogic::Settings(true, true, true)); std::array average_energies = {1.0f, 1.0f}; std::array num_frames_since_activity = {0, 0}; std::array saturation_factors = {1.0f, 1.0f}; // The initial mixing is kUseAverage, but since both channels are active and // balanced, it will switch to kUseBothChannels. EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), StereoMixingVariant::kUseBothChannels); } TEST(RemixingLogicTest, InactiveChannels) { RemixingLogic logic(480, RemixingLogic::Settings(true, true, true)); std::array average_energies = {1.0f, 1.0f}; std::array num_frames_since_activity = { kNumFramesSinceActivityForInactive, kNumFramesSinceActivityForInactive}; std::array saturation_factors = {1.0f, 1.0f}; EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), StereoMixingVariant::kUseAverage); } TEST(RemixingLogicTest, BalancedActiveNotSaturated) { RemixingLogic logic(480, RemixingLogic::Settings(true, true, true)); std::array average_energies = {1.0f, 1.0f}; std::array num_frames_since_activity = { kNumFramesSinceActivityForActive, kNumFramesSinceActivityForActive}; std::array saturation_factors = {1.0f, 1.0f}; EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), StereoMixingVariant::kUseBothChannels); } class RemixingLogicParamTest : public ::testing::TestWithParam { protected: int AffectedChannel() const { return GetParam(); } int OtherChannel() const { return 1 - GetParam(); } }; INSTANTIATE_TEST_SUITE_P(ChannelPermutations, RemixingLogicParamTest, ::testing::Values(0, 1)); TEST_P(RemixingLogicParamTest, OneChannelSilent) { RemixingLogic logic(480, RemixingLogic::Settings(true, true, true)); std::array average_energies; average_energies[AffectedChannel()] = kAverageEnergyForInactive; average_energies[OtherChannel()] = kAverageEnergyForActive; std::array num_frames_since_activity; num_frames_since_activity[AffectedChannel()] = kNumFramesSinceActivityForInactive; num_frames_since_activity[OtherChannel()] = kNumFramesSinceActivityForActive; std::array saturation_factors = {1.0f, 1.0f}; // Enters kSilentChannel mode, uses kUseAverage. EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), StereoMixingVariant::kUseAverage); // Stays in kSilentChannel mode. num_frames_since_activity[AffectedChannel()] = kNumFramesSinceActivityForActive; average_energies[AffectedChannel()] = kAverageEnergyForActive; for (int i = 0; i < kSilentChannelsModeExitFrames - 1; ++i) { EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), StereoMixingVariant::kUseAverage); } // Exits kSilentChannel mode. EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), StereoMixingVariant::kUseBothChannels); } TEST_P(RemixingLogicParamTest, LargelyImbalancedChannels) { RemixingLogic logic(480, RemixingLogic::Settings(true, true, true)); std::array average_energies; average_energies[AffectedChannel()] = 51.0f; average_energies[OtherChannel()] = 1.0f; std::array num_frames_since_activity = { kNumFramesSinceActivityForActive, kNumFramesSinceActivityForActive}; std::array saturation_factors = {1.0f, 1.0f}; const auto expected_variant = AffectedChannel() == 0 ? StereoMixingVariant::kUseChannel0 : StereoMixingVariant::kUseChannel1; // Enters kImbalancedChannels mode, uses louder channel. EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), expected_variant); // Stays in kImbalancedChannels mode. average_energies[AffectedChannel()] = 1.0f; for (int i = 0; i < kImbalancedChannelsModeExitFrames - 1; ++i) { EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), expected_variant); } // Exits kImbalancedChannels mode. EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), StereoMixingVariant::kUseBothChannels); } TEST_P(RemixingLogicParamTest, ModeratelyImbalancedAndSaturated) { RemixingLogic logic(480, RemixingLogic::Settings(true, true, true)); std::array average_energies; average_energies[AffectedChannel()] = 5000.0f; average_energies[OtherChannel()] = 1000.0f; std::array num_frames_since_activity = { kNumFramesSinceActivityForActive, kNumFramesSinceActivityForActive}; std::array saturation_factors; saturation_factors[AffectedChannel()] = 0.81f; saturation_factors[OtherChannel()] = 0.09f; const auto expected_variant = AffectedChannel() == 0 ? StereoMixingVariant::kUseChannel1 : StereoMixingVariant::kUseChannel0; // Enters kSaturatedChannel mode, uses less saturated channel. EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), expected_variant); // Stays in kSaturatedChannel mode. average_energies = {1000.0f, 1000.0f}; saturation_factors = {0.09f, 0.09f}; for (int i = 0; i < kSaturatedChannelsModeExitFrames - 1; ++i) { EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), expected_variant); } // Exits kSaturatedChannel mode. EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), StereoMixingVariant::kUseBothChannels); } TEST_P(RemixingLogicParamTest, PrecedenceSilentOverImbalanced) { RemixingLogic logic(480, RemixingLogic::Settings(true, true, true)); std::array average_energies; average_energies[AffectedChannel()] = kAverageEnergyForInactive; average_energies[OtherChannel()] = kAverageEnergyForActive; std::array num_frames_since_activity; num_frames_since_activity[AffectedChannel()] = kNumFramesSinceActivityForInactive; num_frames_since_activity[OtherChannel()] = kNumFramesSinceActivityForActive; std::array saturation_factors = {0.09f, 0.09f}; EXPECT_EQ( logic.SelectStereoChannelMixing( average_energies, num_frames_since_activity, saturation_factors), StereoMixingVariant::kUseAverage); } } // namespace } // namespace webrtc