blob: 25d6d866535bc8ae30c1143b4f6012bdcb04ce16 [file] [log] [blame]
/*
* 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 <array>
#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<float, 2> average_energies = {1.0f, 1.0f};
std::array<int, 2> num_frames_since_activity = {0, 0};
std::array<float, 2> 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<float, 2> average_energies = {1.0f, 1.0f};
std::array<int, 2> num_frames_since_activity = {
kNumFramesSinceActivityForInactive, kNumFramesSinceActivityForInactive};
std::array<float, 2> 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<float, 2> average_energies = {1.0f, 1.0f};
std::array<int, 2> num_frames_since_activity = {
kNumFramesSinceActivityForActive, kNumFramesSinceActivityForActive};
std::array<float, 2> 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<int> {
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<float, 2> average_energies;
average_energies[AffectedChannel()] = kAverageEnergyForInactive;
average_energies[OtherChannel()] = kAverageEnergyForActive;
std::array<int, 2> num_frames_since_activity;
num_frames_since_activity[AffectedChannel()] =
kNumFramesSinceActivityForInactive;
num_frames_since_activity[OtherChannel()] = kNumFramesSinceActivityForActive;
std::array<float, 2> 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<float, 2> average_energies;
average_energies[AffectedChannel()] = 51.0f;
average_energies[OtherChannel()] = 1.0f;
std::array<int, 2> num_frames_since_activity = {
kNumFramesSinceActivityForActive, kNumFramesSinceActivityForActive};
std::array<float, 2> 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<float, 2> average_energies;
average_energies[AffectedChannel()] = 5000.0f;
average_energies[OtherChannel()] = 1000.0f;
std::array<int, 2> num_frames_since_activity = {
kNumFramesSinceActivityForActive, kNumFramesSinceActivityForActive};
std::array<float, 2> 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<float, 2> average_energies;
average_energies[AffectedChannel()] = kAverageEnergyForInactive;
average_energies[OtherChannel()] = kAverageEnergyForActive;
std::array<int, 2> num_frames_since_activity;
num_frames_since_activity[AffectedChannel()] =
kNumFramesSinceActivityForInactive;
num_frames_since_activity[OtherChannel()] = kNumFramesSinceActivityForActive;
std::array<float, 2> saturation_factors = {0.09f, 0.09f};
EXPECT_EQ(
logic.SelectStereoChannelMixing(
average_energies, num_frames_since_activity, saturation_factors),
StereoMixingVariant::kUseAverage);
}
} // namespace
} // namespace webrtc