From 3b51cd328ecc9e88739ea4f7a0d8d3e74efb5c50 Mon Sep 17 00:00:00 2001 From: Alessio Bazzica Date: Wed, 14 Dec 2022 16:36:10 +0100 Subject: [PATCH] Unify AGC2 experiment field trials into one In order to experiment with AGC2 and TS at the same time, 3 field trials are removed and merged into `WebRTC-Audio-GainController2`, which is existing. New parameters for the `WebRTC-Audio-GainController2` field trial: - `switch_to_agc2`: true by default; when true, the gain control switches to AGC2 (both for the input volume and for the adaptive digital gain); - `min_input_volume`: minimum input volume enforced by the input volume controller when the applied input volume is not zero; - `disallow_transient_suppressor_usage`: when true, TS is never created. Removed field trials: - `WebRTC-Audio-Agc2-MinInputVolume`: now a parameter of `WebRTC-Audio-GainController2`; - `WebRTC-ApmTransientSuppressorKillSwitch`: now a parameter of `WebRTC-Audio-GainController2`; - `WebRTC-Audio-TransientSuppressorVadMode`: automatically inferred from `WebRTC-Audio-GainController2`. Bug: webrtc:7494 Change-Id: I452798c0862d71f9adae6d163fe841df05ca44d6 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/287861 Commit-Queue: Alessio Bazzica Reviewed-by: Hanna Silen Cr-Commit-Position: refs/heads/main@{#38890} --- .../agc2/input_volume_controller.cc | 25 +- .../agc2/input_volume_controller.h | 13 +- .../agc2/input_volume_controller_unittest.cc | 496 ++----- .../audio_processing/audio_processing_impl.cc | 241 ++-- .../audio_processing/audio_processing_impl.h | 56 +- .../audio_processing_impl_unittest.cc | 1153 ++++++++--------- 6 files changed, 795 insertions(+), 1189 deletions(-) diff --git a/modules/audio_processing/agc2/input_volume_controller.cc b/modules/audio_processing/agc2/input_volume_controller.cc index fe1b3d9289..bcc650fb3e 100644 --- a/modules/audio_processing/agc2/input_volume_controller.cc +++ b/modules/audio_processing/agc2/input_volume_controller.cc @@ -50,29 +50,6 @@ Agc1ClippingPredictorConfig CreateClippingPredictorConfig(bool enabled) { return config; } -// Returns the minimum input volume to recommend. -// If the "WebRTC-Audio-Agc2-MinInputVolume" field trial is specified, parses it -// and returns the value specified after "Enabled-" if valid - i.e., in the -// range 0-255. Otherwise returns the default value. -// Example: -// "WebRTC-Audio-Agc2-MinInputVolume/Enabled-80" => returns 80. -int GetMinInputVolume() { - constexpr int kDefaultMinInputVolume = 12; - constexpr char kFieldTrial[] = "WebRTC-Audio-Agc2-MinInputVolume"; - if (!webrtc::field_trial::IsEnabled(kFieldTrial)) { - return kDefaultMinInputVolume; - } - std::string field_trial_str = webrtc::field_trial::FindFullName(kFieldTrial); - int min_input_volume = -1; - sscanf(field_trial_str.c_str(), "Enabled-%d", &min_input_volume); - if (min_input_volume >= 0 && min_input_volume <= 255) { - return min_input_volume; - } - RTC_LOG(LS_WARNING) << "[AGC2] Invalid volume for " << kFieldTrial - << ", ignored."; - return kDefaultMinInputVolume; -} - // Returns an input volume in the [`min_input_volume`, `kMaxInputVolume`] range // that reduces `gain_error_db`, which is a gain error estimated when // `input_volume` was applied, according to a fixed gain map. @@ -377,7 +354,7 @@ void MonoInputVolumeController::UpdateInputVolume(int rms_error_db) { InputVolumeController::InputVolumeController(int num_capture_channels, const Config& config) : num_capture_channels_(num_capture_channels), - min_input_volume_(GetMinInputVolume()), + min_input_volume_(config.min_input_volume), capture_output_used_(true), clipped_level_step_(config.clipped_level_step), clipped_ratio_threshold_(config.clipped_ratio_threshold), diff --git a/modules/audio_processing/agc2/input_volume_controller.h b/modules/audio_processing/agc2/input_volume_controller.h index 95ed160659..40eae8879e 100644 --- a/modules/audio_processing/agc2/input_volume_controller.h +++ b/modules/audio_processing/agc2/input_volume_controller.h @@ -35,6 +35,9 @@ class InputVolumeController final { public: // Config for the constructor. struct Config { + // Minimum input volume that can be recommended. Not enforced when the + // applied input volume is zero outside startup. + int min_input_volume = 20; // Lowest input volume level that will be applied in response to clipping. int clipped_level_min = 70; // Amount input volume level is lowered with every clipping event. Limited @@ -52,13 +55,9 @@ class InputVolumeController final { // [`target_range_min_dbfs`, `target_range_max_dbfs`], no input volume // adjustments are done based on the speech level. For speech levels below // and above the range, the targets `target_range_min_dbfs` and - // `target_range_max_dbfs` are used, respectively. The example values - // `target_range_max_dbfs` -18 and `target_range_min_dbfs` -48 refer to a - // configuration where the zero-digital-gain target is -18 dBFS and the - // digital gain control is expected to compensate for speech level errors - // up to -30 dB. - int target_range_max_dbfs = -18; - int target_range_min_dbfs = -48; + // `target_range_max_dbfs` are used, respectively. + int target_range_max_dbfs = -30; + int target_range_min_dbfs = -50; // Number of wait frames between the recommended input volume updates. int update_input_volume_wait_frames = 100; // Speech probability threshold: speech probabilities below the threshold diff --git a/modules/audio_processing/agc2/input_volume_controller_unittest.cc b/modules/audio_processing/agc2/input_volume_controller_unittest.cc index f1ce5c4b4b..3979b2d236 100644 --- a/modules/audio_processing/agc2/input_volume_controller_unittest.cc +++ b/modules/audio_processing/agc2/input_volume_controller_unittest.cc @@ -38,7 +38,7 @@ constexpr int kNumChannels = 1; constexpr int kInitialInputVolume = 128; constexpr int kClippedMin = 165; // Arbitrary, but different from the default. constexpr float kAboveClippedThreshold = 0.2f; -constexpr int kMinMicLevel = 12; +constexpr int kMinMicLevel = 20; constexpr int kClippedLevelStep = 15; constexpr float kClippedRatioThreshold = 0.1f; constexpr int kClippedWaitFrames = 300; @@ -56,7 +56,6 @@ using ClippingPredictorConfig = AudioProcessing::Config::GainController1:: using InputVolumeControllerConfig = InputVolumeController::Config; -constexpr InputVolumeControllerConfig kDefaultInputVolumeControllerConfig{}; constexpr ClippingPredictorConfig kDefaultClippingPredictorConfig{}; std::unique_ptr CreateInputVolumeController( @@ -66,6 +65,7 @@ std::unique_ptr CreateInputVolumeController( bool enable_clipping_predictor = false, int update_input_volume_wait_frames = 0) { InputVolumeControllerConfig config{ + .min_input_volume = kMinMicLevel, .clipped_level_min = kClippedMin, .clipped_level_step = clipped_level_step, .clipped_ratio_threshold = clipped_ratio_threshold, @@ -82,34 +82,6 @@ std::unique_ptr CreateInputVolumeController( config); } -constexpr char kMinInputVolumeFieldTrial[] = "WebRTC-Audio-Agc2-MinInputVolume"; - -std::string GetAgcMinInputVolumeFieldTrial(const std::string& value) { - char field_trial_buffer[64]; - rtc::SimpleStringBuilder builder(field_trial_buffer); - builder << kMinInputVolumeFieldTrial << "/" << value << "/"; - return builder.str(); -} - -std::string GetAgcMinInputVolumeFieldTrialEnabled( - int enabled_value, - const std::string& suffix = "") { - RTC_DCHECK_GE(enabled_value, 0); - RTC_DCHECK_LE(enabled_value, 255); - char field_trial_buffer[64]; - rtc::SimpleStringBuilder builder(field_trial_buffer); - builder << kMinInputVolumeFieldTrial << "/Enabled-" << enabled_value << suffix - << "/"; - return builder.str(); -} - -std::string GetAgcMinInputVolumeFieldTrial(absl::optional volume) { - if (volume.has_value()) { - return GetAgcMinInputVolumeFieldTrialEnabled(*volume); - } - return GetAgcMinInputVolumeFieldTrial("Disabled"); -} - // (Over)writes `samples_value` for the samples in `audio_buffer`. // When `clipped_ratio`, a value in [0, 1], is greater than 0, the corresponding // fraction of the frame is set to a full scale value to simulate clipping. @@ -150,31 +122,6 @@ void WriteAlternatingAudioBufferSamples(float samples_value, } } -// Deprecated. -// TODO(bugs.webrtc.org/7494): Delete this helper, use -// `InputVolumeControllerTestHelper::CallAgcSequence()` instead. -int CallAnalyzeAndRecommend(int num_calls, - int initial_volume, - const AudioBuffer& audio_buffer, - float speech_probability, - absl::optional speech_level_dbfs, - InputVolumeController& controller) { - RTC_DCHECK(controller.capture_output_used()); - int volume = initial_volume; - for (int n = 0; n < num_calls; ++n) { - controller.AnalyzeInputAudio(volume, audio_buffer); - const auto recommended_input_volume = - controller.RecommendInputVolume(speech_probability, speech_level_dbfs); - - // Expect no errors: Applied volume set for every frame; - // `RecommendInputVolume()` returns a non-empty value. - EXPECT_TRUE(recommended_input_volume.has_value()); - - volume = *recommended_input_volume; - } - return volume; -} - // Reads a given number of 10 ms chunks from a PCM file and feeds them to // `InputVolumeController`. class SpeechSamplesReader { @@ -379,24 +326,12 @@ class InputVolumeControllerTestHelper { }; class InputVolumeControllerParametrizedTest - : public ::testing::TestWithParam> { - protected: - InputVolumeControllerParametrizedTest() - : field_trials_(GetAgcMinInputVolumeFieldTrial(GetParam())) {} - - int GetMinInputVolume() const { return GetParam().value_or(kMinMicLevel); } - - private: - test::ScopedFieldTrials field_trials_; -}; - -INSTANTIATE_TEST_SUITE_P(, - InputVolumeControllerParametrizedTest, - ::testing::Values(absl::nullopt, 12, 20)); + : public ::testing::TestWithParam {}; TEST_P(InputVolumeControllerParametrizedTest, StartupMinVolumeConfigurationIsRespected) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = GetParam()}); EXPECT_EQ(*helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel), @@ -404,7 +339,9 @@ TEST_P(InputVolumeControllerParametrizedTest, } TEST_P(InputVolumeControllerParametrizedTest, MicVolumeResponseToRmsError) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + config.min_input_volume = GetParam(); + InputVolumeControllerTestHelper helper(config); int volume = *helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -446,7 +383,10 @@ TEST_P(InputVolumeControllerParametrizedTest, MicVolumeResponseToRmsError) { } TEST_P(InputVolumeControllerParametrizedTest, MicVolumeIsLimited) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + const int min_input_volume = GetParam(); + config.min_input_volume = min_input_volume; + InputVolumeControllerTestHelper helper(config); int volume = *helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -492,16 +432,18 @@ TEST_P(InputVolumeControllerParametrizedTest, MicVolumeIsLimited) { // Won't go lower than the minimum. volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume, kHighSpeechProbability, 22.0f); - EXPECT_EQ(volume, std::max(18, GetMinInputVolume())); + EXPECT_EQ(volume, std::max(18, min_input_volume)); volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume, kHighSpeechProbability, 22.0f); - EXPECT_EQ(volume, std::max(12, GetMinInputVolume())); + EXPECT_EQ(volume, std::max(12, min_input_volume)); } TEST_P(InputVolumeControllerParametrizedTest, NoActionWhileMuted) { - InputVolumeControllerTestHelper helper_1; - InputVolumeControllerTestHelper helper_2; + InputVolumeControllerTestHelper helper_1( + /*config=*/{.min_input_volume = GetParam()}); + InputVolumeControllerTestHelper helper_2( + /*config=*/{.min_input_volume = GetParam()}); int volume_1 = *helper_1.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability, kSpeechLevel, @@ -531,7 +473,8 @@ TEST_P(InputVolumeControllerParametrizedTest, NoActionWhileMuted) { TEST_P(InputVolumeControllerParametrizedTest, UnmutingChecksVolumeWithoutRaising) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = GetParam()}); helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -548,7 +491,9 @@ TEST_P(InputVolumeControllerParametrizedTest, } TEST_P(InputVolumeControllerParametrizedTest, UnmutingRaisesTooLowVolume) { - InputVolumeControllerTestHelper helper; + const int min_input_volume = GetParam(); + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = min_input_volume}); helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -560,12 +505,14 @@ TEST_P(InputVolumeControllerParametrizedTest, UnmutingRaisesTooLowVolume) { EXPECT_EQ( helper.CallRecommendInputVolume(/*num_calls=*/1, kInputVolume, kHighSpeechProbability, kSpeechLevel), - GetMinInputVolume()); + min_input_volume); } TEST_P(InputVolumeControllerParametrizedTest, ManualLevelChangeResultsInNoSetMicCall) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + config.min_input_volume = GetParam(); + InputVolumeControllerTestHelper helper(config); int volume = *helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -589,7 +536,9 @@ TEST_P(InputVolumeControllerParametrizedTest, TEST_P(InputVolumeControllerParametrizedTest, RecoveryAfterManualLevelChangeFromMax) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + config.min_input_volume = GetParam(); + InputVolumeControllerTestHelper helper(config); int volume = *helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -621,7 +570,10 @@ TEST_P(InputVolumeControllerParametrizedTest, // of the input volume. TEST_P(InputVolumeControllerParametrizedTest, EnforceMinInputVolumeDuringUpwardsAdjustment) { - InputVolumeControllerTestHelper helper; + const int min_input_volume = GetParam(); + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + config.min_input_volume = min_input_volume; + InputVolumeControllerTestHelper helper(config); int volume = *helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -631,19 +583,19 @@ TEST_P(InputVolumeControllerParametrizedTest, /*num_calls=*/1, /*initial_volume=*/1, kHighSpeechProbability, -17.0f); // Trigger an upward adjustment of the input volume. - EXPECT_EQ(volume, GetMinInputVolume()); + EXPECT_EQ(volume, min_input_volume); volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume, kHighSpeechProbability, -29.0f); - EXPECT_EQ(volume, GetMinInputVolume()); + EXPECT_EQ(volume, min_input_volume); volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume, kHighSpeechProbability, -30.0f); - EXPECT_EQ(volume, GetMinInputVolume()); + EXPECT_EQ(volume, min_input_volume); // After a number of consistently low speech level observations, the input // volume is eventually raised above the minimum. volume = helper.CallRecommendInputVolume(/*num_calls=*/10, volume, kHighSpeechProbability, -38.0f); - EXPECT_GT(volume, GetMinInputVolume()); + EXPECT_GT(volume, min_input_volume); } // Checks that, when the min mic level override is specified, AGC immediately @@ -651,7 +603,9 @@ TEST_P(InputVolumeControllerParametrizedTest, // minimum gain to enforce. TEST_P(InputVolumeControllerParametrizedTest, RecoveryAfterManualLevelChangeBelowMin) { - InputVolumeControllerTestHelper helper; + const int min_input_volume = GetParam(); + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = min_input_volume}); int volume = *helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -659,11 +613,12 @@ TEST_P(InputVolumeControllerParametrizedTest, // AGC won't take any action. volume = helper.CallRecommendInputVolume( /*num_calls=*/1, /*initial_volume=*/1, kHighSpeechProbability, -17.0f); - EXPECT_EQ(volume, GetMinInputVolume()); + EXPECT_EQ(volume, min_input_volume); } TEST_P(InputVolumeControllerParametrizedTest, NoClippingHasNoImpact) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = GetParam()}); helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -673,7 +628,8 @@ TEST_P(InputVolumeControllerParametrizedTest, NoClippingHasNoImpact) { TEST_P(InputVolumeControllerParametrizedTest, ClippingUnderThresholdHasNoImpact) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = GetParam()}); helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -682,7 +638,8 @@ TEST_P(InputVolumeControllerParametrizedTest, } TEST_P(InputVolumeControllerParametrizedTest, ClippingLowersVolume) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = GetParam()}); helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability, kSpeechLevel); @@ -692,7 +649,8 @@ TEST_P(InputVolumeControllerParametrizedTest, ClippingLowersVolume) { TEST_P(InputVolumeControllerParametrizedTest, WaitingPeriodBetweenClippingChecks) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = GetParam()}); helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability, kSpeechLevel); @@ -710,7 +668,9 @@ TEST_P(InputVolumeControllerParametrizedTest, } TEST_P(InputVolumeControllerParametrizedTest, ClippingLoweringIsLimited) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + config.min_input_volume = GetParam(); + InputVolumeControllerTestHelper helper(config); helper.CallAgcSequence(/*applied_input_volume=*/180, kHighSpeechProbability, kSpeechLevel); @@ -725,7 +685,8 @@ TEST_P(InputVolumeControllerParametrizedTest, ClippingLoweringIsLimited) { TEST_P(InputVolumeControllerParametrizedTest, ClippingMaxIsRespectedWhenEqualToLevel) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = GetParam()}); helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability, kSpeechLevel); @@ -740,7 +701,9 @@ TEST_P(InputVolumeControllerParametrizedTest, TEST_P(InputVolumeControllerParametrizedTest, ClippingMaxIsRespectedWhenHigherThanLevel) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + config.min_input_volume = GetParam(); + InputVolumeControllerTestHelper helper(config); helper.CallAgcSequence(/*applied_input_volume=*/200, kHighSpeechProbability, kSpeechLevel); @@ -758,7 +721,9 @@ TEST_P(InputVolumeControllerParametrizedTest, } TEST_P(InputVolumeControllerParametrizedTest, UserCanRaiseVolumeAfterClipping) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + config.min_input_volume = GetParam(); + InputVolumeControllerTestHelper helper(config); helper.CallAgcSequence(/*applied_input_volume=*/225, kHighSpeechProbability, kSpeechLevel); @@ -787,7 +752,9 @@ TEST_P(InputVolumeControllerParametrizedTest, UserCanRaiseVolumeAfterClipping) { TEST_P(InputVolumeControllerParametrizedTest, ClippingDoesNotPullLowVolumeBackUp) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + config.min_input_volume = GetParam(); + InputVolumeControllerTestHelper helper(config); helper.CallAgcSequence(/*applied_input_volume=*/80, kHighSpeechProbability, kSpeechLevel); @@ -798,7 +765,8 @@ TEST_P(InputVolumeControllerParametrizedTest, } TEST_P(InputVolumeControllerParametrizedTest, TakesNoActionOnZeroMicVolume) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = GetParam()}); helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability, kSpeechLevel); @@ -809,7 +777,9 @@ TEST_P(InputVolumeControllerParametrizedTest, TakesNoActionOnZeroMicVolume) { } TEST_P(InputVolumeControllerParametrizedTest, ClippingDetectionLowersVolume) { - InputVolumeControllerTestHelper helper; + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + config.min_input_volume = GetParam(); + InputVolumeControllerTestHelper helper(config); int volume = *helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability, kSpeechLevel, /*num_calls=*/1); @@ -829,298 +799,6 @@ TEST_P(InputVolumeControllerParametrizedTest, ClippingDetectionLowersVolume) { EXPECT_EQ(volume, 240); } -TEST(InputVolumeControllerTest, MinInputVolumeDefault) { - std::unique_ptr controller = - CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames); - EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(), - kMinMicLevel); -} - -TEST(InputVolumeControllerTest, MinInputVolumeDisabled) { - for (const std::string& field_trial_suffix : {"", "_20220210"}) { - test::ScopedFieldTrials field_trial( - GetAgcMinInputVolumeFieldTrial("Disabled" + field_trial_suffix)); - std::unique_ptr controller = - CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames); - - EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(), - kMinMicLevel); - } -} - -// Checks that a field-trial parameter outside of the valid range [0,255] is -// ignored. -TEST(InputVolumeControllerTest, MinInputVolumeOutOfRangeAbove) { - test::ScopedFieldTrials field_trial( - GetAgcMinInputVolumeFieldTrial("Enabled-256")); - std::unique_ptr controller = - CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames); - EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(), - kMinMicLevel); -} - -// Checks that a field-trial parameter outside of the valid range [0,255] is -// ignored. -TEST(InputVolumeControllerTest, MinInputVolumeOutOfRangeBelow) { - test::ScopedFieldTrials field_trial( - GetAgcMinInputVolumeFieldTrial("Enabled--1")); - std::unique_ptr controller = - CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames); - EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(), - kMinMicLevel); -} - -// Verifies that a valid experiment changes the minimum microphone level. The -// start volume is larger than the min level and should therefore not be -// changed. -TEST(InputVolumeControllerTest, MinInputVolumeEnabled50) { - constexpr int kMinInputVolume = 50; - for (const std::string& field_trial_suffix : {"", "_20220210"}) { - SCOPED_TRACE(field_trial_suffix); - test::ScopedFieldTrials field_trial(GetAgcMinInputVolumeFieldTrialEnabled( - kMinInputVolume, field_trial_suffix)); - std::unique_ptr controller = - CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames); - - EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(), - kMinInputVolume); - } -} - -// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is -// specified with a valid value, the mic level never gets lowered beyond the -// override value in the presence of clipping. -TEST(InputVolumeControllerTest, MinInputVolumeCheckMinLevelWithClipping) { - constexpr int kMinInputVolume = 250; - - // Create and initialize two AGCs by specifying and leaving unspecified the - // relevant field trial. - const auto factory = []() { - std::unique_ptr controller = - CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames); - controller->Initialize(); - return controller; - }; - std::unique_ptr controller = factory(); - std::unique_ptr controller_with_override; - { - test::ScopedFieldTrials field_trial( - GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume)); - controller_with_override = factory(); - } - - // Create a test input signal which containts 80% of clipped samples. - AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz, - 1); - WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f, - audio_buffer); - - // Simulate 4 seconds of clipping; it is expected to trigger a downward - // adjustment of the analog gain. Use low speech probability to limit the - // volume changes to clipping handling. - const int volume = CallAnalyzeAndRecommend( - /*num_calls=*/400, kInitialInputVolume, audio_buffer, - kLowSpeechProbability, /*speech_level_dbfs=*/-42.0f, *controller); - const int volume_with_override = CallAnalyzeAndRecommend( - /*num_calls=*/400, kInitialInputVolume, audio_buffer, - kLowSpeechProbability, /*speech_level_dbfs=*/-42.0f, - *controller_with_override); - - // Make sure that an adaptation occurred. - ASSERT_GT(volume, 0); - - // Check that the test signal triggers a larger downward adaptation for - // `controller`, which is allowed to reach a lower gain. - EXPECT_GT(volume_with_override, volume); - // Check that the gain selected by `controller_with_override` equals the - // minimum value overridden via field trial. - EXPECT_EQ(volume_with_override, kMinInputVolume); -} - -// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is -// specified with a valid value, the mic level never gets lowered beyond the -// override value in the presence of clipping when RMS error is not empty. -// TODO(webrtc:7494): Revisit the test after moving the number of update wait -// frames to APM config. The test passes but internally the gain update timing -// differs. -TEST(InputVolumeControllerTest, - MinInputVolumeCheckMinLevelWithClippingWithRmsError) { - constexpr int kMinInputVolume = 250; - - // Create and initialize two AGCs by specifying and leaving unspecified the - // relevant field trial. - const auto factory = []() { - std::unique_ptr controller = - CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames); - controller->Initialize(); - return controller; - }; - std::unique_ptr controller = factory(); - std::unique_ptr controller_with_override; - { - test::ScopedFieldTrials field_trial( - GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume)); - controller_with_override = factory(); - } - - // Create a test input signal which containts 80% of clipped samples. - AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz, - 1); - WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f, - audio_buffer); - - // Simulate 4 seconds of clipping; it is expected to trigger a downward - // adjustment of the analog gain. - const int volume = CallAnalyzeAndRecommend( - /*num_calls=*/400, kInitialInputVolume, audio_buffer, - kHighSpeechProbability, - /*speech_level_dbfs=*/-18.0f, *controller); - const int volume_with_override = CallAnalyzeAndRecommend( - /*num_calls=*/400, kInitialInputVolume, audio_buffer, - kHighSpeechProbability, - /*speech_level_dbfs=*/-18.0f, *controller_with_override); - - // Make sure that an adaptation occurred. - ASSERT_GT(volume, 0); - - // Check that the test signal triggers a larger downward adaptation for - // `controller`, which is allowed to reach a lower gain. - EXPECT_GT(volume_with_override, volume); - - // Check that the gain selected by `controller_with_override` equals the - // minimum value overridden via field trial. - EXPECT_EQ(volume_with_override, kMinInputVolume); -} - -// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is -// specified with a value lower than the `clipped_level_min`, the behavior of -// the analog gain controller is the same as that obtained when the field trial -// is not specified. -TEST(InputVolumeControllerTest, MinInputVolumeCompareMicLevelWithClipping) { - // Create and initialize two AGCs by specifying and leaving unspecified the - // relevant field trial. - const auto factory = []() { - // Use a large clipped level step to more quickly decrease the analog gain - // with clipping. - InputVolumeControllerConfig config = kDefaultInputVolumeControllerConfig; - config.clipped_level_step = 64; - config.clipped_ratio_threshold = kClippedRatioThreshold; - config.clipped_wait_frames = kClippedWaitFrames; - auto controller = std::make_unique( - /*num_capture_channels=*/1, config); - controller->Initialize(); - return controller; - }; - std::unique_ptr controller = factory(); - std::unique_ptr controller_with_override; - { - constexpr int kMinInputVolume = 20; - static_assert(kDefaultInputVolumeControllerConfig.clipped_level_min >= - kMinInputVolume, - "Use a lower override value."); - test::ScopedFieldTrials field_trial( - GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume)); - controller_with_override = factory(); - } - - // Create a test input signal which containts 80% of clipped samples. - AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz, - 1); - WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f, - audio_buffer); - - // Simulate 4 seconds of clipping; it is expected to trigger a downward - // adjustment of the analog gain. Use low speech probability to limit the - // volume changes to clipping handling. - const int volume = CallAnalyzeAndRecommend( - /*num_calls=*/400, kInitialInputVolume, audio_buffer, - kLowSpeechProbability, /*speech_level_dbfs=*/-18, *controller); - const int volume_with_override = CallAnalyzeAndRecommend( - /*num_calls=*/400, kInitialInputVolume, audio_buffer, - kLowSpeechProbability, /*speech_level_dbfs=*/-18, - *controller_with_override); - - // Make sure that an adaptation occurred. - ASSERT_GT(volume, 0); - - // Check that the selected analog gain is the same for both controllers and - // that it equals the minimum level reached when clipping is handled. That is - // expected because the minimum microphone level override is less than the - // minimum level used when clipping is detected. - EXPECT_EQ(volume, volume_with_override); - EXPECT_EQ(volume_with_override, - kDefaultInputVolumeControllerConfig.clipped_level_min); -} - -// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is -// specified with a value lower than the `clipped_level_min`, the behavior of -// the analog gain controller is the same as that obtained when the field trial -// is not specified. -// TODO(webrtc:7494): Revisit the test after moving the number of update wait -// frames to APM config. The test passes but internally the gain update timing -// differs. -TEST(InputVolumeControllerTest, - MinInputVolumeCompareMicLevelWithClippingWithRmsError) { - // Create and initialize two AGCs by specifying and leaving unspecified the - // relevant field trial. - const auto factory = []() { - // Use a large clipped level step to more quickly decrease the analog gain - // with clipping. - InputVolumeControllerConfig config = kDefaultInputVolumeControllerConfig; - config.clipped_level_step = 64; - config.clipped_ratio_threshold = kClippedRatioThreshold; - config.clipped_wait_frames = kClippedWaitFrames; - auto controller = std::make_unique( - /*num_capture_channels=*/1, config); - controller->Initialize(); - return controller; - }; - std::unique_ptr controller = factory(); - std::unique_ptr controller_with_override; - { - constexpr int kMinInputVolume = 20; - static_assert(kDefaultInputVolumeControllerConfig.clipped_level_min >= - kMinInputVolume, - "Use a lower override value."); - test::ScopedFieldTrials field_trial( - GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume)); - controller_with_override = factory(); - } - - // Create a test input signal which containts 80% of clipped samples. - AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz, - 1); - WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f, - audio_buffer); - - const int volume = CallAnalyzeAndRecommend( - /*num_calls=*/400, kInitialInputVolume, audio_buffer, - /*speech_probability=*/0.7f, - /*speech_level_dbfs=*/-18.0f, *controller); - const int volume_with_override = CallAnalyzeAndRecommend( - /*num_calls=*/400, kInitialInputVolume, audio_buffer, - /*speech_probability=*/0.7f, - /*speech_level_dbfs=*/-18.0f, *controller_with_override); - - // Make sure that an adaptation occurred. - ASSERT_GT(volume, 0); - - // Check that the selected analog gain is the same for both controllers and - // that it equals the minimum level reached when clipping is handled. That is - // expected because the minimum microphone level override is less than the - // minimum level used when clipping is detected. - EXPECT_EQ(volume, volume_with_override); - EXPECT_EQ(volume_with_override, - kDefaultInputVolumeControllerConfig.clipped_level_min); -} - // TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_level_step`. // TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_ratio_threshold`. // TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_wait_frames`. @@ -1416,6 +1094,42 @@ TEST(InputVolumeControllerTest, UpdateInputVolumeWaitFramesIsEffective) { ASSERT_GT(volume_wait_100, kInputVolume); } +INSTANTIATE_TEST_SUITE_P(, + InputVolumeControllerParametrizedTest, + ::testing::Values(12, 20)); + +TEST(InputVolumeControllerTest, + MinInputVolumeEnforcedWithClippingWhenAboveClippedLevelMin) { + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = 80, .clipped_level_min = 70}); + + // Trigger a downward adjustment caused by clipping input. Use a low speech + // probability to limit the volume changes to clipping handling. + WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f, + helper.audio_buffer); + constexpr int kNumCalls = 800; + helper.CallAgcSequence(/*applied_input_volume=*/100, kLowSpeechProbability, + /*speech_level_dbfs=*/-18.0f, kNumCalls); + + EXPECT_EQ(helper.controller.recommended_input_volume(), 80); +} + +TEST(InputVolumeControllerTest, + ClippedlevelMinEnforcedWithClippingWhenAboveMinInputVolume) { + InputVolumeControllerTestHelper helper( + /*config=*/{.min_input_volume = 70, .clipped_level_min = 80}); + + // Trigger a downward adjustment caused by clipping input. Use a low speech + // probability to limit the volume changes to clipping handling. + WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f, + helper.audio_buffer); + constexpr int kNumCalls = 800; + helper.CallAgcSequence(/*applied_input_volume=*/100, kLowSpeechProbability, + /*speech_level_dbfs=*/-18.0f, kNumCalls); + + EXPECT_EQ(helper.controller.recommended_input_volume(), 80); +} + TEST(InputVolumeControllerTest, SpeechRatioThresholdIsEffective) { constexpr int kInputVolume = kInitialInputVolume; // Create two input volume controllers with 10 frames between volume updates diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc index d28a44b4f6..e92ae6dffd 100644 --- a/modules/audio_processing/audio_processing_impl.cc +++ b/modules/audio_processing/audio_processing_impl.cc @@ -69,29 +69,6 @@ bool UseSetupSpecificDefaultAec3Congfig() { "WebRTC-Aec3SetupSpecificDefaultConfigDefaultsKillSwitch"); } -// If the "WebRTC-Audio-TransientSuppressorVadMode" field trial is unspecified, -// returns `TransientSuppressor::VadMode::kDefault`, otherwise parses the field -// trial and returns the specified mode: -// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-Default returns `kDefault`; -// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-RnnVad returns `kRnnVad`; -// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-NoVad returns `kNoVad`. -TransientSuppressor::VadMode GetTransientSuppressorVadMode() { - constexpr char kFieldTrial[] = "WebRTC-Audio-TransientSuppressorVadMode"; - std::string full_name = webrtc::field_trial::FindFullName(kFieldTrial); - if (full_name.empty() || absl::EndsWith(full_name, "-Default")) { - return TransientSuppressor::VadMode::kDefault; - } - if (absl::EndsWith(full_name, "-RnnVad")) { - return TransientSuppressor::VadMode::kRnnVad; - } - if (absl::EndsWith(full_name, "-NoVad")) { - return TransientSuppressor::VadMode::kNoVad; - } - // Fallback to default. - RTC_LOG(LS_WARNING) << "Invalid parameter for " << kFieldTrial; - return TransientSuppressor::VadMode::kDefault; -} - // Identify the native processing rate that best handles a sample rate. int SuitableProcessRate(int minimum_rate, int max_splitting_rate, @@ -325,17 +302,44 @@ int HandleUnsupportedAudioFormats(const float* const* src, return error_code; } -const absl::optional -GetGainController2ConfigOverride() { +using DownmixMethod = AudioProcessing::Config::Pipeline::DownmixMethod; + +void SetDownmixMethod(AudioBuffer& buffer, DownmixMethod method) { + switch (method) { + case DownmixMethod::kAverageChannels: + buffer.set_downmixing_by_averaging(); + break; + case DownmixMethod::kUseFirstChannel: + buffer.set_downmixing_to_specific_channel(/*channel=*/0); + break; + } +} + +constexpr int kUnspecifiedDataDumpInputVolume = -100; + +} // namespace + +// Throughout webrtc, it's assumed that success is represented by zero. +static_assert(AudioProcessing::kNoError == 0, "kNoError must be zero"); + +absl::optional +AudioProcessingImpl::GetGainController2ExperimentParams() { constexpr char kFieldTrialName[] = "WebRTC-Audio-GainController2"; if (!field_trial::IsEnabled(kFieldTrialName)) { return absl::nullopt; } - constexpr InputVolumeController::Config kDefaultInputVolumeControllerConfig; - FieldTrialFlag enabled("Enabled", false); + + // Whether the gain control should switch to AGC2. Enabled by default. + FieldTrialParameter switch_to_agc2("switch_to_agc2", true); + + // AGC2 input volume controller configuration. + constexpr InputVolumeController::Config kDefaultInputVolumeControllerConfig; + FieldTrialConstrained min_input_volume( + "min_input_volume", kDefaultInputVolumeControllerConfig.min_input_volume, + 0, 255); FieldTrialConstrained clipped_level_min( "clipped_level_min", kDefaultInputVolumeControllerConfig.clipped_level_min, 0, 255); @@ -369,9 +373,9 @@ GetGainController2ConfigOverride() { "speech_ratio_threshold", kDefaultInputVolumeControllerConfig.speech_ratio_threshold, 0, 1); + // AGC2 adaptive digital controller configuration. constexpr AudioProcessing::Config::GainController2::AdaptiveDigital kDefaultAdaptiveDigitalConfig; - FieldTrialConstrained headroom_db( "headroom_db", kDefaultAdaptiveDigitalConfig.headroom_db, 0, absl::nullopt); @@ -390,83 +394,102 @@ GetGainController2ConfigOverride() { kDefaultAdaptiveDigitalConfig.max_output_noise_level_dbfs, absl::nullopt, 0); + // Transient suppressor. + FieldTrialParameter disallow_transient_suppressor_usage( + "disallow_transient_suppressor_usage", false); + // Field-trial based override for the input volume controller and adaptive // digital configs. - const std::string field_trial_name = - field_trial::FindFullName(kFieldTrialName); - ParseFieldTrial( - {&enabled, &clipped_level_min, &clipped_level_step, - &clipped_ratio_threshold, &clipped_wait_frames, + {&enabled, &switch_to_agc2, &min_input_volume, &clipped_level_min, + &clipped_level_step, &clipped_ratio_threshold, &clipped_wait_frames, &enable_clipping_predictor, &target_range_max_dbfs, &target_range_min_dbfs, &update_input_volume_wait_frames, &speech_probability_threshold, &speech_ratio_threshold, &headroom_db, &max_gain_db, &initial_gain_db, &max_gain_change_db_per_second, - &max_output_noise_level_dbfs}, - field_trial_name); - + &max_output_noise_level_dbfs, &disallow_transient_suppressor_usage}, + field_trial::FindFullName(kFieldTrialName)); // Checked already by `IsEnabled()` before parsing, therefore always true. RTC_DCHECK(enabled); - return AudioProcessingImpl::GainController2ConfigOverride{ - .input_volume_controller_config = - { - .clipped_level_min = static_cast(clipped_level_min.Get()), - .clipped_level_step = static_cast(clipped_level_step.Get()), - .clipped_ratio_threshold = - static_cast(clipped_ratio_threshold.Get()), - .clipped_wait_frames = - static_cast(clipped_wait_frames.Get()), - .enable_clipping_predictor = - static_cast(enable_clipping_predictor.Get()), - .target_range_max_dbfs = - static_cast(target_range_max_dbfs.Get()), - .target_range_min_dbfs = - static_cast(target_range_min_dbfs.Get()), - .update_input_volume_wait_frames = - static_cast(update_input_volume_wait_frames.Get()), - .speech_probability_threshold = - static_cast(speech_probability_threshold.Get()), - .speech_ratio_threshold = - static_cast(speech_ratio_threshold.Get()), - }, - .adaptive_digital_config = - { - .headroom_db = static_cast(headroom_db.Get()), - .max_gain_db = static_cast(max_gain_db.Get()), - .initial_gain_db = static_cast(initial_gain_db.Get()), - .max_gain_change_db_per_second = - static_cast(max_gain_change_db_per_second.Get()), - .max_output_noise_level_dbfs = - static_cast(max_output_noise_level_dbfs.Get()), - }, - }; + const bool do_not_change_agc_config = !switch_to_agc2.Get(); + if (do_not_change_agc_config && !disallow_transient_suppressor_usage.Get()) { + // Return an unspecifed value since, in this case, both the AGC2 and TS + // configurations won't be adjusted. + return absl::nullopt; + } + using Params = AudioProcessingImpl::GainController2ExperimentParams; + if (do_not_change_agc_config) { + // Return a value that leaves the AGC2 config unchanged and that always + // disables TS. + return Params{.agc2_config = absl::nullopt, + .disallow_transient_suppressor_usage = true}; + } + // Return a value that switches all the gain control to AGC2. + return Params{ + .agc2_config = + Params::Agc2Config{ + .input_volume_controller = + { + .min_input_volume = min_input_volume.Get(), + .clipped_level_min = clipped_level_min.Get(), + .clipped_level_step = clipped_level_step.Get(), + .clipped_ratio_threshold = + static_cast(clipped_ratio_threshold.Get()), + .clipped_wait_frames = clipped_wait_frames.Get(), + .enable_clipping_predictor = + enable_clipping_predictor.Get(), + .target_range_max_dbfs = target_range_max_dbfs.Get(), + .target_range_min_dbfs = target_range_min_dbfs.Get(), + .update_input_volume_wait_frames = + update_input_volume_wait_frames.Get(), + .speech_probability_threshold = static_cast( + speech_probability_threshold.Get()), + .speech_ratio_threshold = + static_cast(speech_ratio_threshold.Get()), + }, + .adaptive_digital_controller = + { + .headroom_db = static_cast(headroom_db.Get()), + .max_gain_db = static_cast(max_gain_db.Get()), + .initial_gain_db = + static_cast(initial_gain_db.Get()), + .max_gain_change_db_per_second = static_cast( + max_gain_change_db_per_second.Get()), + .max_output_noise_level_dbfs = + static_cast(max_output_noise_level_dbfs.Get()), + }}, + .disallow_transient_suppressor_usage = + disallow_transient_suppressor_usage.Get()}; } -// If `disallow_transient_supporessor_usage` is true, disables transient -// suppression. When `gain_controller2_config_override` is specified, -// switches all gain control to AGC2. -AudioProcessing::Config AdjustConfig( +AudioProcessing::Config AudioProcessingImpl::AdjustConfig( const AudioProcessing::Config& config, - bool disallow_transient_supporessor_usage, - const absl::optional& - gain_controller2_config_override) { + const absl::optional& + experiment_params) { + if (!experiment_params.has_value() || + (!experiment_params->agc2_config.has_value() && + !experiment_params->disallow_transient_suppressor_usage)) { + // When the experiment parameters are unspecified or when the AGC and TS + // configuration are not overridden, return the unmodified configuration. + return config; + } + AudioProcessing::Config adjusted_config = config; // Override the transient suppressor configuration. - if (disallow_transient_supporessor_usage) { + if (experiment_params->disallow_transient_suppressor_usage) { adjusted_config.transient_suppression.enabled = false; } // Override the auto gain control configuration if the AGC1 analog gain - // controller is active and `gain_controller2_config_override` is - // specified. + // controller is active and `experiment_params->agc2_config` is specified. const bool agc1_analog_enabled = config.gain_controller1.enabled && (config.gain_controller1.mode == AudioProcessing::Config::GainController1::kAdaptiveAnalog || config.gain_controller1.analog_gain_controller.enabled); - if (agc1_analog_enabled && gain_controller2_config_override.has_value()) { + if (agc1_analog_enabled && experiment_params->agc2_config.has_value()) { // Check that the unadjusted AGC config meets the preconditions. const bool hybrid_agc_config_detected = config.gain_controller1.enabled && @@ -499,7 +522,7 @@ AudioProcessing::Config AdjustConfig( adjusted_config.gain_controller2.enabled = true; adjusted_config.gain_controller2.input_volume_controller.enabled = true; adjusted_config.gain_controller2.adaptive_digital = - gain_controller2_config_override->adaptive_digital_config; + experiment_params->agc2_config->adaptive_digital_controller; adjusted_config.gain_controller2.adaptive_digital.enabled = true; } } @@ -507,26 +530,21 @@ AudioProcessing::Config AdjustConfig( return adjusted_config; } -using DownmixMethod = AudioProcessing::Config::Pipeline::DownmixMethod; - -void SetDownmixMethod(AudioBuffer& buffer, DownmixMethod method) { - switch (method) { - case DownmixMethod::kAverageChannels: - buffer.set_downmixing_by_averaging(); - break; - case DownmixMethod::kUseFirstChannel: - buffer.set_downmixing_to_specific_channel(/*channel=*/0); - break; +TransientSuppressor::VadMode AudioProcessingImpl::GetTransientSuppressorVadMode( + const absl::optional& + params) { + if (params.has_value() && params->agc2_config.has_value() && + !params->disallow_transient_suppressor_usage) { + // When the experiment is active, the gain control switches to AGC2 and TS + // can be active, use the RNN VAD to control TS. This choice will also + // disable the internal RNN VAD in AGC2. + return TransientSuppressor::VadMode::kRnnVad; } + // If TS is disabled, the returned value does not matter. If enabled, use the + // default VAD. + return TransientSuppressor::VadMode::kDefault; } -constexpr int kUnspecifiedDataDumpInputVolume = -100; - -} // namespace - -// Throughout webrtc, it's assumed that success is represented by zero. -static_assert(AudioProcessing::kNoError == 0, "kNoError must be zero"); - AudioProcessingImpl::SubmoduleStates::SubmoduleStates( bool capture_post_processor_enabled, bool render_pre_processor_enabled, @@ -644,20 +662,17 @@ AudioProcessingImpl::AudioProcessingImpl( : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)), use_setup_specific_default_aec3_config_( UseSetupSpecificDefaultAec3Congfig()), - gain_controller2_config_override_(GetGainController2ConfigOverride()), + gain_controller2_experiment_params_(GetGainController2ExperimentParams()), use_denormal_disabler_( !field_trial::IsEnabled("WebRTC-ApmDenormalDisablerKillSwitch")), - disallow_transient_supporessor_usage_( - field_trial::IsEnabled("WebRTC-ApmTransientSuppressorKillSwitch")), - transient_suppressor_vad_mode_(GetTransientSuppressorVadMode()), + transient_suppressor_vad_mode_( + GetTransientSuppressorVadMode(gain_controller2_experiment_params_)), capture_runtime_settings_(RuntimeSettingQueueSize()), render_runtime_settings_(RuntimeSettingQueueSize()), capture_runtime_settings_enqueuer_(&capture_runtime_settings_), render_runtime_settings_enqueuer_(&render_runtime_settings_), echo_control_factory_(std::move(echo_control_factory)), - config_(AdjustConfig(config, - disallow_transient_supporessor_usage_, - gain_controller2_config_override_)), + config_(AdjustConfig(config, gain_controller2_experiment_params_)), submodule_states_(!!capture_post_processor, !!render_pre_processor, !!capture_analyzer), @@ -893,8 +908,7 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { MutexLock lock_capture(&mutex_capture_); const auto adjusted_config = - AdjustConfig(config, disallow_transient_supporessor_usage_, - gain_controller2_config_override_); + AdjustConfig(config, gain_controller2_experiment_params_); RTC_LOG(LS_INFO) << "AudioProcessing::ApplyConfig: " << adjusted_config.ToString(); @@ -2340,11 +2354,16 @@ void AudioProcessingImpl::InitializeGainController2(bool config_has_changed) { if (!submodules_.gain_controller2 || config_has_changed) { const bool use_internal_vad = transient_suppressor_vad_mode_ != TransientSuppressor::VadMode::kRnnVad; + const bool input_volume_controller_config_overridden = + gain_controller2_experiment_params_.has_value() && + gain_controller2_experiment_params_->agc2_config.has_value(); + const InputVolumeController::Config input_volume_controller_config = + input_volume_controller_config_overridden + ? gain_controller2_experiment_params_->agc2_config + ->input_volume_controller + : InputVolumeController::Config{}; submodules_.gain_controller2 = std::make_unique( - config_.gain_controller2, - gain_controller2_config_override_.has_value() - ? gain_controller2_config_override_->input_volume_controller_config - : InputVolumeController::Config{}, + config_.gain_controller2, input_volume_controller_config, proc_fullband_sample_rate_hz(), num_proc_channels(), use_internal_vad); submodules_.gain_controller2->SetCaptureOutputUsed( capture_.capture_output_used); diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h index 0f74c3059f..8ee07edbe2 100644 --- a/modules/audio_processing/audio_processing_impl.h +++ b/modules/audio_processing/audio_processing_impl.h @@ -138,14 +138,6 @@ class AudioProcessingImpl : public AudioProcessing { AudioProcessing::Config GetConfig() const override; - // TODO(bugs.webrtc.org/7494): Remove when the related field trial is - // removed. - struct GainController2ConfigOverride { - InputVolumeController::Config input_volume_controller_config; - AudioProcessing::Config::GainController2::AdaptiveDigital - adaptive_digital_config; - }; - protected: // Overridden in a mock. virtual void InitializeLocked() @@ -199,19 +191,47 @@ class AudioProcessingImpl : public AudioProcessing { static std::atomic instance_count_; const bool use_setup_specific_default_aec3_config_; - // TODO(bugs.webrtc.org/7494): Remove when the linked field trial is removed. - // Override based on the "WebRTC-Audio-GainController2" field trial for the - // AGC2 input volume controller and adaptive digital controller configuration. - const absl::optional - gain_controller2_config_override_; + // Parameters for the "GainController2" experiment which determines whether + // the following APM sub-modules are created and, if so, their configurations: + // AGC2 (`gain_controller2`), AGC1 (`gain_control`, `agc_manager`) and TS + // (`transient_suppressor`). + // TODO(bugs.webrtc.org/7494): Remove when the "WebRTC-Audio-GainController2" + // field trial is removed. + struct GainController2ExperimentParams { + struct Agc2Config { + InputVolumeController::Config input_volume_controller; + AudioProcessing::Config::GainController2::AdaptiveDigital + adaptive_digital_controller; + }; + // When `agc2_config` is specified, all gain control switches to AGC2 and + // the configuration is overridden. + absl::optional agc2_config; + // When true, the transient suppressor submodule is never created regardless + // of the APM configuration. + bool disallow_transient_suppressor_usage; + }; + // Specified when the "WebRTC-Audio-GainController2" field trial is specified. + // TODO(bugs.webrtc.org/7494): Remove when the "WebRTC-Audio-GainController2" + // field trial is removed. + const absl::optional + gain_controller2_experiment_params_; + + // Parses the "WebRTC-Audio-GainController2" field trial. If disabled, returns + // an unspecified value. + static absl::optional + GetGainController2ExperimentParams(); + + // When `experiment_params` is specified, returns an APM configuration + // modified according to the experiment parameters. Otherwise returns + // `config`. + static AudioProcessing::Config AdjustConfig( + const AudioProcessing::Config& config, + const absl::optional& experiment_params); + static TransientSuppressor::VadMode GetTransientSuppressorVadMode( + const absl::optional& experiment_params); const bool use_denormal_disabler_; - // When true, the transient suppressor submodule is never created regardless - // of the APM configuration. - // TODO(bugs.webrtc.org/13663): Remove when the linked field trial is removed. - const bool disallow_transient_supporessor_usage_; - const TransientSuppressor::VadMode transient_suppressor_vad_mode_; SwapQueue capture_runtime_settings_; diff --git a/modules/audio_processing/audio_processing_impl_unittest.cc b/modules/audio_processing/audio_processing_impl_unittest.cc index 65bda71f58..e48a5d8883 100644 --- a/modules/audio_processing/audio_processing_impl_unittest.cc +++ b/modules/audio_processing/audio_processing_impl_unittest.cc @@ -10,6 +10,7 @@ #include "modules/audio_processing/audio_processing_impl.h" +#include #include #include #include @@ -131,32 +132,6 @@ class TestRenderPreProcessor : public CustomProcessing { static constexpr float ProcessSample(float x) { return 2.f * x; } }; -// Creates a simple `AudioProcessing` instance for APM input volume testing -// with AGC1 analog and/or AGC2 input volume controller enabled and AGC2 -// digital controller enabled. -rtc::scoped_refptr CreateApmForInputVolumeTest( - bool agc1_analog_gain_controller_enabled, - bool agc2_input_volume_controller_enabled) { - webrtc::AudioProcessing::Config config; - // Enable AGC1 analog controller. - config.gain_controller1.enabled = agc1_analog_gain_controller_enabled; - config.gain_controller1.analog_gain_controller.enabled = - agc1_analog_gain_controller_enabled; - // Enable AG2 input volume controller - config.gain_controller2.input_volume_controller.enabled = - agc2_input_volume_controller_enabled; - // Enable AGC2 adaptive digital controller. - config.gain_controller1.analog_gain_controller.enable_digital_adaptive = - false; - config.gain_controller2.enabled = true; - config.gain_controller2.adaptive_digital.enabled = true; - - auto apm(AudioProcessingBuilder().Create()); - apm->ApplyConfig(config); - - return apm; -} - // Runs `apm` input processing for volume adjustments for `num_frames` random // frames starting from the volume `initial_volume`. This includes three steps: // 1) Set the input volume 2) Process the stream 3) Set the new recommended @@ -183,99 +158,6 @@ int ProcessInputVolume(AudioProcessing& apm, return recommended_input_volume; } -constexpr char kMinMicLevelFieldTrial[] = - "WebRTC-Audio-2ndAgcMinMicLevelExperiment"; -constexpr char kMinInputVolumeFieldTrial[] = "WebRTC-Audio-Agc2-MinInputVolume"; -constexpr int kMinInputVolume = 12; - -std::string GetMinMicLevelExperimentFieldTrial(absl::optional value) { - char field_trial_buffer[128]; - rtc::SimpleStringBuilder builder(field_trial_buffer); - if (value.has_value()) { - RTC_DCHECK_GE(*value, 0); - RTC_DCHECK_LE(*value, 255); - builder << kMinMicLevelFieldTrial << "/Enabled-" << *value << "/"; - builder << kMinInputVolumeFieldTrial << "/Enabled-" << *value << "/"; - } else { - builder << kMinMicLevelFieldTrial << "/Disabled/"; - builder << kMinInputVolumeFieldTrial << "/Disabled/"; - } - return builder.str(); -} - -// TODO(webrtc:7494): Remove the fieldtrial from the input volume tests when -// "WebRTC-Audio-2ndAgcMinMicLevelExperiment" and -// "WebRTC-Audio-Agc2-MinInputVolume" are removed. -class InputVolumeStartupParameterizedTest - : public ::testing::TestWithParam< - std::tuple, bool, bool>> { - protected: - InputVolumeStartupParameterizedTest() - : field_trials_( - GetMinMicLevelExperimentFieldTrial(std::get<1>(GetParam()))) {} - int GetStartupVolume() const { return std::get<0>(GetParam()); } - int GetMinVolume() const { - return std::get<1>(GetParam()).value_or(kMinInputVolume); - } - bool GetAgc1AnalogControllerEnabled() const { - return std::get<2>(GetParam()); - } - bool GetAgc2InputVolumeControllerEnabled() const { - return std::get<3>(GetParam()); - } - - private: - test::ScopedFieldTrials field_trials_; -}; - -class InputVolumeNotZeroParameterizedTest - : public ::testing::TestWithParam< - std::tuple, bool, bool>> { - protected: - InputVolumeNotZeroParameterizedTest() - : field_trials_( - GetMinMicLevelExperimentFieldTrial(std::get<2>(GetParam()))) {} - int GetStartupVolume() const { return std::get<0>(GetParam()); } - int GetVolume() const { return std::get<1>(GetParam()); } - int GetMinVolume() const { - return std::get<2>(GetParam()).value_or(kMinInputVolume); - } - bool GetMinMicLevelExperimentEnabled() { - return std::get<2>(GetParam()).has_value(); - } - bool GetAgc1AnalogControllerEnabled() const { - return std::get<3>(GetParam()); - } - bool GetAgc2InputVolumeControllerEnabled() const { - return std::get<4>(GetParam()); - } - - private: - test::ScopedFieldTrials field_trials_; -}; - -class InputVolumeZeroParameterizedTest - : public ::testing::TestWithParam< - std::tuple, bool, bool>> { - protected: - InputVolumeZeroParameterizedTest() - : field_trials_( - GetMinMicLevelExperimentFieldTrial(std::get<1>(GetParam()))) {} - int GetStartupVolume() const { return std::get<0>(GetParam()); } - int GetMinVolume() const { - return std::get<1>(GetParam()).value_or(kMinInputVolume); - } - bool GetAgc1AnalogControllerEnabled() const { - return std::get<2>(GetParam()); - } - bool GetAgc2InputVolumeControllerEnabled() const { - return std::get<3>(GetParam()); - } - - private: - test::ScopedFieldTrials field_trials_; -}; - } // namespace TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) { @@ -644,8 +526,10 @@ TEST(AudioProcessingImplTest, TEST(AudioProcessingImplTest, ProcessWithAgc2AndTransientSuppressorVadModeDefault) { webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-TransientSuppressorVadMode/Enabled-Default/"); - rtc::scoped_refptr apm = AudioProcessingBuilder().Create(); + "WebRTC-Audio-GainController2/Disabled/"); + auto apm = AudioProcessingBuilder() + .SetConfig({.gain_controller1{.enabled = false}}) + .Create(); ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError); webrtc::AudioProcessing::Config apm_config; apm_config.gain_controller1.enabled = false; @@ -675,7 +559,7 @@ TEST(AudioProcessingImplTest, TEST(AudioProcessingImplTest, ProcessWithAgc2AndTransientSuppressorVadModeRnnVad) { webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-TransientSuppressorVadMode/Enabled-RnnVad/"); + "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/"); rtc::scoped_refptr apm = AudioProcessingBuilder().Create(); ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError); webrtc::AudioProcessing::Config apm_config; @@ -958,224 +842,116 @@ TEST(ApmWithSubmodulesExcludedTest, ToggleTransientSuppressor) { } } -TEST(AudioProcessingImplTest, CanDisableTransientSuppressor) { - // Do not explicitly disable "WebRTC-ApmTransientSuppressorKillSwitch" since - // to check that, by default, it is disabled. - auto apm = AudioProcessingBuilder() - .SetConfig({.transient_suppression = {.enabled = false}}) - .Create(); - EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled); -} - -TEST(AudioProcessingImplTest, CanEnableTransientSuppressor) { - // Do not explicitly disable "WebRTC-ApmTransientSuppressorKillSwitch" since - // to check that, by default, it is disabled. - auto apm = AudioProcessingBuilder() - .SetConfig({.transient_suppression = {.enabled = true}}) - .Create(); - EXPECT_TRUE(apm->GetConfig().transient_suppression.enabled); -} - -TEST(AudioProcessingImplTest, CanDisableTransientSuppressorIfUsageAllowed) { - // Disable the field trial that disallows to enable transient suppression. - test::ScopedFieldTrials field_trials( - "WebRTC-ApmTransientSuppressorKillSwitch/Disabled/"); - auto apm = AudioProcessingBuilder() - .SetConfig({.transient_suppression = {.enabled = false}}) - .Create(); - EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled); -} - -TEST(AudioProcessingImplTest, CanEnableTransientSuppressorIfUsageAllowed) { - // Disable the field trial that disallows to enable transient suppression. - test::ScopedFieldTrials field_trials( - "WebRTC-ApmTransientSuppressorKillSwitch/Disabled/"); - auto apm = AudioProcessingBuilder() - .SetConfig({.transient_suppression = {.enabled = true}}) - .Create(); - EXPECT_TRUE(apm->GetConfig().transient_suppression.enabled); -} - -TEST(AudioProcessingImplTest, - CannotEnableTransientSuppressorIfUsageDisallowed) { - // Enable the field trial that disallows to enable transient suppression. - test::ScopedFieldTrials field_trials( - "WebRTC-ApmTransientSuppressorKillSwitch/Enabled/"); - auto apm = AudioProcessingBuilder() - .SetConfig({.transient_suppression = {.enabled = true}}) - .Create(); - EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled); -} - -// TODO(bugs.webrtc.org/7494): Test AGCs with different multi-channel configs. - -// Tests that the minimum startup volume is applied at the startup. -TEST_P(InputVolumeStartupParameterizedTest, - VerifyStartupMinVolumeAppliedAtStartup) { - const int applied_startup_input_volume = GetStartupVolume(); - const int expected_volume = - std::max(applied_startup_input_volume, GetMinVolume()); - const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled(); - const bool agc2_input_volume_controller_enabled = - GetAgc2InputVolumeControllerEnabled(); - auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled, - agc2_input_volume_controller_enabled); - - const int recommended_input_volume = - ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume); - - if (!agc1_analog_controller_enabled && - !agc2_input_volume_controller_enabled) { - // No input volume changes if none of the analog controllers is enabled. - ASSERT_EQ(recommended_input_volume, applied_startup_input_volume); - } else { - ASSERT_EQ(recommended_input_volume, expected_volume); - } -} - -// Tests that the minimum input volume is applied if the volume is manually -// adjusted to a non-zero value 1) always for AGC2 input volume controller and -// 2) only if "WebRTC-Audio-2ndAgcMinMicLevelExperiment" is enabled for AGC1 -// analog controller. -TEST_P(InputVolumeNotZeroParameterizedTest, - VerifyMinVolumeMaybeAppliedAfterManualVolumeAdjustments) { - const int applied_startup_input_volume = GetStartupVolume(); - const int applied_input_volume = GetVolume(); - const int expected_volume = std::max(applied_input_volume, GetMinVolume()); - const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled(); - const bool agc2_input_volume_controller_enabled = - GetAgc2InputVolumeControllerEnabled(); - auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled, - agc2_input_volume_controller_enabled); - - ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume); - const int recommended_input_volume = - ProcessInputVolume(*apm, /*num_frames=*/1, applied_input_volume); - - ASSERT_NE(applied_input_volume, 0); - - if (!agc1_analog_controller_enabled && - !agc2_input_volume_controller_enabled) { - // No input volume changes if none of the analog controllers is enabled. - ASSERT_EQ(recommended_input_volume, applied_input_volume); - } else { - if (GetMinMicLevelExperimentEnabled() || - (!agc1_analog_controller_enabled && - agc2_input_volume_controller_enabled)) { - ASSERT_EQ(recommended_input_volume, expected_volume); - } else { - ASSERT_EQ(recommended_input_volume, applied_input_volume); +class ApmInputVolumeControllerParametrizedTest + : public ::testing::TestWithParam< + std::tuple> { + protected: + ApmInputVolumeControllerParametrizedTest() + : sample_rate_hz_(std::get<0>(GetParam())), + num_channels_(std::get<1>(GetParam())), + channels_(num_channels_), + channel_pointers_(num_channels_) { + const int frame_size = sample_rate_hz_ / 100; + for (int c = 0; c < num_channels_; ++c) { + channels_[c].resize(frame_size); + channel_pointers_[c] = channels_[c].data(); + std::fill(channels_[c].begin(), channels_[c].end(), 0.0f); } } + + int sample_rate_hz() const { return sample_rate_hz_; } + int num_channels() const { return num_channels_; } + AudioProcessing::Config GetConfig() const { return std::get<2>(GetParam()); } + + float* const* channel_pointers() { return channel_pointers_.data(); } + + private: + const int sample_rate_hz_; + const int num_channels_; + std::vector> channels_; + std::vector channel_pointers_; +}; + +TEST_P(ApmInputVolumeControllerParametrizedTest, + EnforceMinInputVolumeAtStartupWithZeroVolume) { + const StreamConfig stream_config(sample_rate_hz(), num_channels()); + auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create(); + + apm->set_stream_analog_level(0); + apm->ProcessStream(channel_pointers(), stream_config, stream_config, + channel_pointers()); + EXPECT_GT(apm->recommended_stream_analog_level(), 0); } -// Tests that the minimum input volume is not applied if the volume is manually -// adjusted to zero. -TEST_P(InputVolumeZeroParameterizedTest, - VerifyMinVolumeNotAppliedAfterManualVolumeAdjustments) { - constexpr int kZeroVolume = 0; - const int applied_startup_input_volume = GetStartupVolume(); - const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled(); - const bool agc2_input_volume_controller_enabled = - GetAgc2InputVolumeControllerEnabled(); - auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled, - agc2_input_volume_controller_enabled); +TEST_P(ApmInputVolumeControllerParametrizedTest, + EnforceMinInputVolumeAtStartupWithNonZeroVolume) { + const StreamConfig stream_config(sample_rate_hz(), num_channels()); + auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create(); - const int recommended_input_volume_after_startup = - ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume); - const int recommended_input_volume = - ProcessInputVolume(*apm, /*num_frames=*/1, kZeroVolume); + constexpr int kStartupVolume = 3; + apm->set_stream_analog_level(kStartupVolume); + apm->ProcessStream(channel_pointers(), stream_config, stream_config, + channel_pointers()); + EXPECT_GT(apm->recommended_stream_analog_level(), kStartupVolume); +} - if (!agc1_analog_controller_enabled && - !agc2_input_volume_controller_enabled) { - // No input volume changes if none of the analog controllers is enabled. - ASSERT_EQ(recommended_input_volume, kZeroVolume); - } else { - ASSERT_NE(recommended_input_volume, recommended_input_volume_after_startup); - ASSERT_EQ(recommended_input_volume, kZeroVolume); +TEST_P(ApmInputVolumeControllerParametrizedTest, + EnforceMinInputVolumeAfterManualVolumeAdjustment) { + const auto config = GetConfig(); + if (config.gain_controller1.enabled) { + // After a downward manual adjustment, AGC1 slowly converges to the minimum + // input volume. + GTEST_SKIP() << "Does not apply to AGC1"; } + const StreamConfig stream_config(sample_rate_hz(), num_channels()); + auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create(); + + apm->set_stream_analog_level(20); + apm->ProcessStream(channel_pointers(), stream_config, stream_config, + channel_pointers()); + constexpr int kManuallyAdjustedVolume = 3; + apm->set_stream_analog_level(kManuallyAdjustedVolume); + apm->ProcessStream(channel_pointers(), stream_config, stream_config, + channel_pointers()); + EXPECT_GT(apm->recommended_stream_analog_level(), kManuallyAdjustedVolume); } -// Tests that the minimum input volume is applied if the volume is not zero -// before it is automatically adjusted. -TEST_P(InputVolumeNotZeroParameterizedTest, - VerifyMinVolumeAppliedAfterAutomaticVolumeAdjustments) { - const int applied_startup_input_volume = GetStartupVolume(); - const int applied_input_volume = GetVolume(); - const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled(); - const bool agc2_input_volume_controller_enabled = - GetAgc2InputVolumeControllerEnabled(); - auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled, - agc2_input_volume_controller_enabled); +TEST_P(ApmInputVolumeControllerParametrizedTest, + DoNotEnforceMinInputVolumeAfterManualVolumeAdjustmentToZero) { + const StreamConfig stream_config(sample_rate_hz(), num_channels()); + auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create(); - ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume); - const int recommended_input_volume = - ProcessInputVolume(*apm, /*num_frames=*/400, applied_input_volume); - - ASSERT_NE(applied_input_volume, 0); - - if (!agc1_analog_controller_enabled && - !agc2_input_volume_controller_enabled) { - // No input volume changes if none of the analog controllers is enabled. - ASSERT_EQ(recommended_input_volume, applied_input_volume); - } else { - if (recommended_input_volume != applied_input_volume) { - ASSERT_GE(recommended_input_volume, GetMinVolume()); - } - } + apm->set_stream_analog_level(100); + apm->ProcessStream(channel_pointers(), stream_config, stream_config, + channel_pointers()); + apm->set_stream_analog_level(0); + apm->ProcessStream(channel_pointers(), stream_config, stream_config, + channel_pointers()); + EXPECT_EQ(apm->recommended_stream_analog_level(), 0); } -// Tests that the minimum input volume is not applied if the volume is zero -// before it is automatically adjusted. -TEST_P(InputVolumeZeroParameterizedTest, - VerifyMinVolumeNotAppliedAfterAutomaticVolumeAdjustments) { - constexpr int kZeroVolume = 0; - const int applied_startup_input_volume = GetStartupVolume(); - const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled(); - const bool agc2_input_volume_controller_enabled = - GetAgc2InputVolumeControllerEnabled(); - auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled, - agc2_input_volume_controller_enabled); - - const int recommended_input_volume_after_startup = - ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume); - const int recommended_input_volume = - ProcessInputVolume(*apm, /*num_frames=*/400, kZeroVolume); - - if (!agc1_analog_controller_enabled && - !agc2_input_volume_controller_enabled) { - // No input volume changes if none of the analog controllers is enabled. - ASSERT_EQ(recommended_input_volume, kZeroVolume); - } else { - ASSERT_NE(recommended_input_volume, recommended_input_volume_after_startup); - ASSERT_EQ(recommended_input_volume, kZeroVolume); - } -} - -INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest, - InputVolumeStartupParameterizedTest, - ::testing::Combine(::testing::Values(0, 5, 30), - ::testing::Values(absl::nullopt, - 20), - ::testing::Bool(), - ::testing::Bool())); - -INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest, - InputVolumeNotZeroParameterizedTest, - ::testing::Combine(::testing::Values(0, 5, 15), - ::testing::Values(1, 5, 30), - ::testing::Values(absl::nullopt, - 20), - ::testing::Bool(), - ::testing::Bool())); - -INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest, - InputVolumeZeroParameterizedTest, - ::testing::Combine(::testing::Values(0, 5, 15), - ::testing::Values(absl::nullopt, - 20), - ::testing::Bool(), - ::testing::Bool())); +INSTANTIATE_TEST_SUITE_P( + AudioProcessingImplTest, + ApmInputVolumeControllerParametrizedTest, + ::testing::Combine( + ::testing::Values(8000, 16000, 32000, 48000), // Sample rates. + ::testing::Values(1, 2), // Number of channels. + ::testing::Values( + // Full AGC1. + AudioProcessing::Config{ + .gain_controller1 = {.enabled = true, + .analog_gain_controller = + {.enabled = true, + .enable_digital_adaptive = true}}, + .gain_controller2 = {.enabled = false}}, + // Hybrid AGC. + AudioProcessing::Config{ + .gain_controller1 = {.enabled = true, + .analog_gain_controller = + {.enabled = true, + .enable_digital_adaptive = false}}, + .gain_controller2 = {.enabled = true, + .adaptive_digital = {.enabled = true}}}))); // When the input volume is not emulated and no input volume controller is // active, the recommended volume must always be the applied volume. @@ -1237,53 +1013,336 @@ TEST(AudioProcessingImplTest, EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135); } -class GainController2FieldTrialParametrizedTest +TEST(AudioProcessingImplTest, + Agc2FieldTrialDoNotSwitchToFullAgc2WhenNoAgcIsActive) { + constexpr AudioProcessing::Config kOriginal{ + .gain_controller1{.enabled = false}, + .gain_controller2{.enabled = false}, + }; + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/"); + + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig(); + EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1); + EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2); + + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(kOriginal); + adjusted = apm->GetConfig(); + EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1); + EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2); +} + +TEST(AudioProcessingImplTest, + Agc2FieldTrialDoNotSwitchToFullAgc2WithAgc1Agc2InputVolumeControllers) { + constexpr AudioProcessing::Config kOriginal{ + .gain_controller1{.enabled = true, + .analog_gain_controller{.enabled = true}}, + .gain_controller2{.enabled = true, + .input_volume_controller{.enabled = true}}, + }; + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/"); + + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig(); + EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1); + EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2); + + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(kOriginal); + adjusted = apm->GetConfig(); + EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1); + EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2); +} + +class Agc2FieldTrialParametrizedTest : public ::testing::TestWithParam {}; -TEST_P(GainController2FieldTrialParametrizedTest, - CheckAgc2AdaptiveDigitalOverridesApplied) { +TEST_P(Agc2FieldTrialParametrizedTest, DoNotChangeConfigIfDisabled) { + const AudioProcessing::Config original = GetParam(); webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-GainController2/" - "Enabled," - "enable_clipping_predictor:true," + "WebRTC-Audio-GainController2/Disabled/"); + + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(original).Create()->GetConfig(); + EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1); + EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2); + + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(original); + adjusted = apm->GetConfig(); + EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1); + EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2); +} + +TEST_P(Agc2FieldTrialParametrizedTest, DoNotChangeConfigIfNoOverride) { + const AudioProcessing::Config original = GetParam(); + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled," + "switch_to_agc2:false," + "disallow_transient_suppressor_usage:false/"); + + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(original).Create()->GetConfig(); + EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1); + EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2); + + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(original); + adjusted = apm->GetConfig(); + EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1); + EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2); +} + +TEST_P(Agc2FieldTrialParametrizedTest, DoNotSwitchToFullAgc2) { + const AudioProcessing::Config original = GetParam(); + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:false/"); + + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(original).Create()->GetConfig(); + EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1); + EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2); + + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(original); + adjusted = apm->GetConfig(); + EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1); + EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2); +} + +TEST_P(Agc2FieldTrialParametrizedTest, SwitchToFullAgc2) { + const AudioProcessing::Config original = GetParam(); + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/"); + + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(original).Create()->GetConfig(); + EXPECT_FALSE(adjusted.gain_controller1.enabled); + EXPECT_TRUE(adjusted.gain_controller2.enabled); + EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled); + EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled); + + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(original); + adjusted = apm->GetConfig(); + EXPECT_FALSE(adjusted.gain_controller1.enabled); + EXPECT_TRUE(adjusted.gain_controller2.enabled); + EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled); + EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled); +} + +TEST_P(Agc2FieldTrialParametrizedTest, + SwitchToFullAgc2AndOverrideInputVolumeControllerParameters) { + const AudioProcessing::Config original = GetParam(); + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true," + "min_input_volume:123," "clipped_level_min:20," "clipped_level_step:30," "clipped_ratio_threshold:0.4," "clipped_wait_frames:50," + "enable_clipping_predictor:true," "target_range_max_dbfs:-6," "target_range_min_dbfs:-70," "update_input_volume_wait_frames:80," "speech_probability_threshold:0.9," - "speech_ratio_threshold:1.0," + "speech_ratio_threshold:1.0/"); + + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(original).Create()->GetConfig(); + EXPECT_FALSE(adjusted.gain_controller1.enabled); + EXPECT_TRUE(adjusted.gain_controller2.enabled); + EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled); + EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled); + + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(original); + adjusted = apm->GetConfig(); + EXPECT_FALSE(adjusted.gain_controller1.enabled); + EXPECT_TRUE(adjusted.gain_controller2.enabled); + EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled); + EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled); +} + +TEST_P(Agc2FieldTrialParametrizedTest, + SwitchToFullAgc2AndOverrideAdaptiveDigitalControllerParameters) { + const AudioProcessing::Config original = GetParam(); + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true," "headroom_db:10," "max_gain_db:20," "initial_gain_db:7," "max_gain_change_db_per_second:5," "max_output_noise_level_dbfs:-40/"); - auto adjusted_config = - AudioProcessingBuilder().SetConfig(GetParam()).Create()->GetConfig(); + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(original).Create()->GetConfig(); + EXPECT_FALSE(adjusted.gain_controller1.enabled); + EXPECT_TRUE(adjusted.gain_controller2.enabled); + EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled); + EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled); + ASSERT_NE(adjusted.gain_controller2.adaptive_digital, + original.gain_controller2.adaptive_digital); + EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.headroom_db, 10); + EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.max_gain_db, 20); + EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.initial_gain_db, 7); + EXPECT_EQ( + adjusted.gain_controller2.adaptive_digital.max_gain_change_db_per_second, + 5); + EXPECT_EQ( + adjusted.gain_controller2.adaptive_digital.max_output_noise_level_dbfs, + -40); - EXPECT_FALSE(adjusted_config.gain_controller1.enabled); - EXPECT_TRUE(adjusted_config.gain_controller2.enabled); - EXPECT_TRUE(adjusted_config.gain_controller2.adaptive_digital.enabled); - EXPECT_TRUE(adjusted_config.gain_controller2.input_volume_controller.enabled); + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(original); + adjusted = apm->GetConfig(); + EXPECT_FALSE(adjusted.gain_controller1.enabled); + EXPECT_TRUE(adjusted.gain_controller2.enabled); + EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled); + EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled); + ASSERT_NE(adjusted.gain_controller2.adaptive_digital, + original.gain_controller2.adaptive_digital); + EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.headroom_db, 10); + EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.max_gain_db, 20); + EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.initial_gain_db, 7); + EXPECT_EQ( + adjusted.gain_controller2.adaptive_digital.max_gain_change_db_per_second, + 5); + EXPECT_EQ( + adjusted.gain_controller2.adaptive_digital.max_output_noise_level_dbfs, + -40); +} - EXPECT_EQ(adjusted_config.gain_controller2.adaptive_digital.headroom_db, 10); - EXPECT_EQ(adjusted_config.gain_controller2.adaptive_digital.max_gain_db, 20); - EXPECT_EQ(adjusted_config.gain_controller2.adaptive_digital.initial_gain_db, - 7); - EXPECT_EQ(adjusted_config.gain_controller2.adaptive_digital - .max_gain_change_db_per_second, - 5); - EXPECT_EQ(adjusted_config.gain_controller2.adaptive_digital - .max_output_noise_level_dbfs, - -40); +TEST_P(Agc2FieldTrialParametrizedTest, ProcessSucceedsWithTs) { + AudioProcessing::Config config = GetParam(); + config.transient_suppression.enabled = true; + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Disabled/"); + auto apm = AudioProcessingBuilder().SetConfig(config).Create(); + + constexpr int kSampleRateHz = 48000; + constexpr int kNumChannels = 1; + std::array buffer; + float* channel_pointers[] = {buffer.data()}; + StreamConfig stream_config(kSampleRateHz, kNumChannels); + Random random_generator(2341U); + constexpr int kFramesToProcess = 10; + int volume = 100; + for (int i = 0; i < kFramesToProcess; ++i) { + SCOPED_TRACE(i); + RandomizeSampleVector(&random_generator, buffer); + apm->set_stream_analog_level(volume); + ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config, + channel_pointers), + kNoErr); + volume = apm->recommended_stream_analog_level(); + } +} + +TEST_P(Agc2FieldTrialParametrizedTest, ProcessSucceedsWithoutTs) { + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled," + "switch_to_agc2:false," + "disallow_transient_suppressor_usage:true/"); + auto apm = AudioProcessingBuilder().SetConfig(GetParam()).Create(); + + constexpr int kSampleRateHz = 48000; + constexpr int kNumChannels = 1; + std::array buffer; + float* channel_pointers[] = {buffer.data()}; + StreamConfig stream_config(kSampleRateHz, kNumChannels); + Random random_generator(2341U); + constexpr int kFramesToProcess = 10; + int volume = 100; + for (int i = 0; i < kFramesToProcess; ++i) { + SCOPED_TRACE(i); + RandomizeSampleVector(&random_generator, buffer); + apm->set_stream_analog_level(volume); + ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config, + channel_pointers), + kNoErr); + volume = apm->recommended_stream_analog_level(); + } +} + +TEST_P(Agc2FieldTrialParametrizedTest, + ProcessSucceedsWhenSwitchToFullAgc2WithTs) { + AudioProcessing::Config config = GetParam(); + config.transient_suppression.enabled = true; + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled," + "switch_to_agc2:true," + "disallow_transient_suppressor_usage:false/"); + auto apm = AudioProcessingBuilder().SetConfig(config).Create(); + + constexpr int kSampleRateHz = 48000; + constexpr int kNumChannels = 1; + std::array buffer; + float* channel_pointers[] = {buffer.data()}; + StreamConfig stream_config(kSampleRateHz, kNumChannels); + Random random_generator(2341U); + constexpr int kFramesToProcess = 10; + int volume = 100; + for (int i = 0; i < kFramesToProcess; ++i) { + SCOPED_TRACE(i); + RandomizeSampleVector(&random_generator, buffer); + apm->set_stream_analog_level(volume); + ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config, + channel_pointers), + kNoErr); + volume = apm->recommended_stream_analog_level(); + } +} + +TEST_P(Agc2FieldTrialParametrizedTest, + ProcessSucceedsWhenSwitchToFullAgc2WithoutTs) { + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled," + "switch_to_agc2:true," + "disallow_transient_suppressor_usage:true/"); + auto apm = AudioProcessingBuilder().SetConfig(GetParam()).Create(); + + constexpr int kSampleRateHz = 48000; + constexpr int kNumChannels = 1; + std::array buffer; + float* channel_pointers[] = {buffer.data()}; + StreamConfig stream_config(kSampleRateHz, kNumChannels); + Random random_generator(2341U); + constexpr int kFramesToProcess = 10; + int volume = 100; + for (int i = 0; i < kFramesToProcess; ++i) { + SCOPED_TRACE(i); + RandomizeSampleVector(&random_generator, buffer); + apm->set_stream_analog_level(volume); + ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config, + channel_pointers), + kNoErr); + volume = apm->recommended_stream_analog_level(); + } } INSTANTIATE_TEST_SUITE_P( AudioProcessingImplTest, - GainController2FieldTrialParametrizedTest, + Agc2FieldTrialParametrizedTest, ::testing::Values( // Full AGC1. AudioProcessing::Config{ @@ -1301,314 +1360,132 @@ INSTANTIATE_TEST_SUITE_P( .gain_controller2 = {.enabled = true, .adaptive_digital = {.enabled = true}}})); -TEST(AudioProcessingImplGainController2FieldTrialTest, - ConfigAdjustedWhenExperimentEnabledAndAgc1AnalogEnabled) { - constexpr AudioProcessing::Config::GainController2::AdaptiveDigital - kDefaultAdaptiveDigitalConfig; +TEST(AudioProcessingImplTest, CanDisableTransientSuppressor) { + constexpr AudioProcessing::Config kOriginal = { + .transient_suppression = {.enabled = false}}; + + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig(); + EXPECT_FALSE(adjusted.transient_suppression.enabled); + + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(kOriginal); + adjusted = apm->GetConfig(); + EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled); +} + +TEST(AudioProcessingImplTest, CanEnableTs) { + constexpr AudioProcessing::Config kOriginal = { + .transient_suppression = {.enabled = true}}; + + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig(); + EXPECT_TRUE(adjusted.transient_suppression.enabled); + + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(kOriginal); + adjusted = apm->GetConfig(); + EXPECT_TRUE(adjusted.transient_suppression.enabled); +} + +TEST(AudioProcessingImplTest, CanDisableTsWithAgc2FieldTrialDisabled) { + constexpr AudioProcessing::Config kOriginal = { + .transient_suppression = {.enabled = false}}; webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-GainController2/" - "Enabled," - "enable_clipping_predictor:true," - "clipped_level_min:20," - "clipped_level_step:30," - "clipped_ratio_threshold:0.4," - "clipped_wait_frames:50," - "target_range_max_dbfs:-6," - "target_range_min_dbfs:-70," - "update_input_volume_wait_frames:80," - "speech_probability_threshold:0.9," - "speech_ratio_threshold:1.0," - "headroom_db:10," - "max_gain_db:20," - "initial_gain_db:7," - "max_gain_change_db_per_second:3," - "max_output_noise_level_dbfs:-40/"); + "WebRTC-Audio-GainController2/Disabled/"); - AudioProcessingBuilderForTesting apm_builder; + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig(); + EXPECT_FALSE(adjusted.transient_suppression.enabled); - // Set a config with analog AGC1 enabled. - AudioProcessing::Config config; - config.gain_controller1.enabled = true; - config.gain_controller1.analog_gain_controller.enabled = true; - config.gain_controller1.analog_gain_controller.enable_digital_adaptive = true; - config.gain_controller2.enabled = false; - config.gain_controller1.mode = - AudioProcessing::Config::GainController1::kAdaptiveAnalog; - - EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled); - - apm_builder.SetConfig(config); - - auto apm = apm_builder.Create(); - auto adjusted_config = apm->GetConfig(); - - // Expect the config to be adjusted. - EXPECT_FALSE(adjusted_config.gain_controller1.enabled); - EXPECT_FALSE(adjusted_config.gain_controller1.analog_gain_controller.enabled); - EXPECT_TRUE(adjusted_config.gain_controller2.enabled); - EXPECT_TRUE(adjusted_config.gain_controller2.adaptive_digital.enabled); - EXPECT_TRUE(adjusted_config.gain_controller2.input_volume_controller.enabled); - EXPECT_NE(adjusted_config.gain_controller2.adaptive_digital, - kDefaultAdaptiveDigitalConfig); - - // Change config back and compare. - adjusted_config.gain_controller1.enabled = config.gain_controller1.enabled; - adjusted_config.gain_controller1.analog_gain_controller.enabled = - config.gain_controller1.analog_gain_controller.enabled; - adjusted_config.gain_controller2.enabled = config.gain_controller2.enabled; - adjusted_config.gain_controller2.adaptive_digital.enabled = - config.gain_controller2.adaptive_digital.enabled; - adjusted_config.gain_controller2.input_volume_controller.enabled = - config.gain_controller2.input_volume_controller.enabled; - adjusted_config.gain_controller2.adaptive_digital = - config.gain_controller2.adaptive_digital; - - EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString())); + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(kOriginal); + adjusted = apm->GetConfig(); + EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled); } -TEST(AudioProcessingImplGainController2FieldTrialTest, - ConfigAdjustedWhenExperimentEnabledAndHybridAgcEnabled) { - constexpr AudioProcessing::Config::GainController2::AdaptiveDigital - kDefaultAdaptiveDigitalConfig; +TEST(AudioProcessingImplTest, CanEnableTsWithAgc2FieldTrialDisabled) { + constexpr AudioProcessing::Config kOriginal = { + .transient_suppression = {.enabled = true}}; webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-GainController2/" - "Enabled," - "enable_clipping_predictor:true," - "clipped_level_min:20," - "clipped_level_step:30," - "clipped_ratio_threshold:0.4," - "clipped_wait_frames:50," - "target_range_max_dbfs:-6," - "target_range_min_dbfs:-70," - "update_input_volume_wait_frames:80," - "speech_probability_threshold:0.9," - "speech_ratio_threshold:1.0," - "headroom_db:10," - "max_gain_db:20," - "initial_gain_db:7," - "max_gain_change_db_per_second:3," - "max_output_noise_level_dbfs:-40/"); + "WebRTC-Audio-GainController2/Disabled/"); - AudioProcessingBuilderForTesting apm_builder; + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig(); + EXPECT_TRUE(adjusted.transient_suppression.enabled); - // Set a config with hybrid AGC enabled. - AudioProcessing::Config config; - config.gain_controller1.enabled = true; - config.gain_controller1.analog_gain_controller.enabled = true; - config.gain_controller1.analog_gain_controller.enable_digital_adaptive = - false; - config.gain_controller2.enabled = true; - config.gain_controller2.adaptive_digital.enabled = true; - config.gain_controller1.mode = - AudioProcessing::Config::GainController1::kAdaptiveAnalog; - - EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled); - - apm_builder.SetConfig(config); - - auto apm = apm_builder.Create(); - auto adjusted_config = apm->GetConfig(); - - // Expect the config to be adjusted. - EXPECT_FALSE(adjusted_config.gain_controller1.enabled); - EXPECT_FALSE(adjusted_config.gain_controller1.analog_gain_controller.enabled); - EXPECT_TRUE(adjusted_config.gain_controller2.enabled); - EXPECT_TRUE(adjusted_config.gain_controller2.adaptive_digital.enabled); - EXPECT_TRUE(adjusted_config.gain_controller2.input_volume_controller.enabled); - EXPECT_NE(adjusted_config.gain_controller2.adaptive_digital, - kDefaultAdaptiveDigitalConfig); - - // Change config back and compare. - adjusted_config.gain_controller1.enabled = config.gain_controller1.enabled; - adjusted_config.gain_controller1.analog_gain_controller.enabled = - config.gain_controller1.analog_gain_controller.enabled; - adjusted_config.gain_controller2.enabled = config.gain_controller2.enabled; - adjusted_config.gain_controller2.adaptive_digital.enabled = - config.gain_controller2.adaptive_digital.enabled; - adjusted_config.gain_controller2.input_volume_controller.enabled = - config.gain_controller2.input_volume_controller.enabled; - adjusted_config.gain_controller2.adaptive_digital = - config.gain_controller2.adaptive_digital; - - EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString())); + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(kOriginal); + adjusted = apm->GetConfig(); + EXPECT_TRUE(adjusted.transient_suppression.enabled); } -TEST(AudioProcessingImplGainController2FieldTrialTest, - ConfigNotAdjustedWhenExperimentEnabledAndAgc1AnalogNotEnabled) { +TEST(AudioProcessingImplTest, + CanDisableTsWithAgc2FieldTrialEnabledAndUsageAllowed) { + constexpr AudioProcessing::Config kOriginal = { + .transient_suppression = {.enabled = false}}; webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-GainController2/" - "Enabled," - "enable_clipping_predictor:true," - "clipped_level_min:20," - "clipped_level_step:30," - "clipped_ratio_threshold:0.4," - "clipped_wait_frames:50," - "target_range_max_dbfs:-6," - "target_range_min_dbfs:-70," - "update_input_volume_wait_frames:80," - "speech_probability_threshold:0.9," - "speech_ratio_threshold:1.0," - "headroom_db:10," - "max_gain_db:20," - "initial_gain_db:7," - "max_gain_change_db_per_second:3," - "max_output_noise_level_dbfs:-40/"); + "WebRTC-Audio-GainController2/Enabled," + "disallow_transient_suppressor_usage:false/"); - AudioProcessingBuilderForTesting apm_builder; + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig(); + EXPECT_FALSE(adjusted.transient_suppression.enabled); - // Set a config with analog AGC1 not enabled. - AudioProcessing::Config config; - config.gain_controller1.enabled = false; - config.gain_controller1.analog_gain_controller.enabled = true; - config.gain_controller1.analog_gain_controller.enable_digital_adaptive = true; - config.gain_controller2.enabled = false; - config.gain_controller1.mode = - AudioProcessing::Config::GainController1::kAdaptiveAnalog; - - EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled); - - apm_builder.SetConfig(config); - - auto apm = apm_builder.Create(); - auto adjusted_config = apm->GetConfig(); - - EXPECT_EQ(config.gain_controller1.enabled, - adjusted_config.gain_controller1.enabled); - EXPECT_EQ(config.gain_controller1.analog_gain_controller.enabled, - adjusted_config.gain_controller1.analog_gain_controller.enabled); - EXPECT_EQ(config.gain_controller2.enabled, - adjusted_config.gain_controller2.enabled); - EXPECT_EQ(config.gain_controller2.adaptive_digital.enabled, - adjusted_config.gain_controller2.adaptive_digital.enabled); - EXPECT_FALSE( - adjusted_config.gain_controller2.input_volume_controller.enabled); - - EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString())); + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(kOriginal); + adjusted = apm->GetConfig(); + EXPECT_FALSE(adjusted.transient_suppression.enabled); } -TEST(AudioProcessingImplGainController2FieldTrialTest, - ConfigNotAdjustedWhenExperimentEnabledAndHybridAgcNotEnabled) { +TEST(AudioProcessingImplTest, + CanEnableTsWithAgc2FieldTrialEnabledAndUsageAllowed) { + constexpr AudioProcessing::Config kOriginal = { + .transient_suppression = {.enabled = true}}; webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-GainController2/" - "Enabled," - "enable_clipping_predictor:true," - "clipped_level_min:20," - "clipped_level_step:30," - "clipped_ratio_threshold:0.4," - "clipped_wait_frames:50," - "target_range_max_dbfs:-6," - "target_range_min_dbfs:-70," - "update_input_volume_wait_frames:80," - "speech_probability_threshold:0.9," - "speech_ratio_threshold:1.0," - "headroom_db:10," - "max_gain_db:20," - "initial_gain_db:7," - "max_gain_change_db_per_second:3," - "max_output_noise_level_dbfs:-40/"); + "WebRTC-Audio-GainController2/Enabled," + "disallow_transient_suppressor_usage:false/"); - AudioProcessingBuilderForTesting apm_builder; + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig(); + EXPECT_TRUE(adjusted.transient_suppression.enabled); - // Set a config with hybrid AGC analog not enabled. - AudioProcessing::Config config; - config.gain_controller1.enabled = false; - config.gain_controller1.analog_gain_controller.enabled = true; - config.gain_controller1.analog_gain_controller.enable_digital_adaptive = - false; - config.gain_controller2.enabled = true; - config.gain_controller2.adaptive_digital.enabled = true; - config.gain_controller1.mode = - AudioProcessing::Config::GainController1::kAdaptiveAnalog; - - EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled); - - apm_builder.SetConfig(config); - - auto apm = apm_builder.Create(); - auto adjusted_config = apm->GetConfig(); - - EXPECT_EQ(config.gain_controller1.enabled, - adjusted_config.gain_controller1.enabled); - EXPECT_EQ(config.gain_controller1.analog_gain_controller.enabled, - adjusted_config.gain_controller1.analog_gain_controller.enabled); - EXPECT_EQ(config.gain_controller2.enabled, - adjusted_config.gain_controller2.enabled); - EXPECT_EQ(config.gain_controller2.adaptive_digital.enabled, - adjusted_config.gain_controller2.adaptive_digital.enabled); - EXPECT_FALSE( - adjusted_config.gain_controller2.input_volume_controller.enabled); - - EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString())); + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(kOriginal); + adjusted = apm->GetConfig(); + EXPECT_TRUE(adjusted.transient_suppression.enabled); } -TEST(AudioProcessingImplGainController2FieldTrialTest, - ConfigNotAdjustedWhenExperimentNotEnabledAndAgc1AnalogEnabled) { - AudioProcessingBuilderForTesting apm_builder; +TEST(AudioProcessingImplTest, + CannotEnableTsWithAgc2FieldTrialEnabledAndUsageDisallowed) { + constexpr AudioProcessing::Config kOriginal = { + .transient_suppression = {.enabled = true}}; + webrtc::test::ScopedFieldTrials field_trials( + "WebRTC-Audio-GainController2/Enabled," + "disallow_transient_suppressor_usage:true/"); - // Set a config with analog AGC1 analog enabled. - AudioProcessing::Config config; - config.gain_controller1.enabled = true; - config.gain_controller1.analog_gain_controller.enabled = true; - config.gain_controller1.analog_gain_controller.enable_digital_adaptive = true; - config.gain_controller2.enabled = false; - config.gain_controller1.mode = - AudioProcessing::Config::GainController1::kAdaptiveAnalog; + // Test config application via `AudioProcessing` ctor. + auto adjusted = + AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig(); + EXPECT_FALSE(adjusted.transient_suppression.enabled); - EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled); - - apm_builder.SetConfig(config); - - auto apm = apm_builder.Create(); - auto adjusted_config = apm->GetConfig(); - - EXPECT_EQ(config.gain_controller1.enabled, - adjusted_config.gain_controller1.enabled); - EXPECT_EQ(config.gain_controller1.analog_gain_controller.enabled, - adjusted_config.gain_controller1.analog_gain_controller.enabled); - EXPECT_EQ(config.gain_controller2.enabled, - adjusted_config.gain_controller2.enabled); - EXPECT_EQ(config.gain_controller2.adaptive_digital.enabled, - adjusted_config.gain_controller2.adaptive_digital.enabled); - EXPECT_FALSE( - adjusted_config.gain_controller2.input_volume_controller.enabled); - - EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString())); -} - -TEST(AudioProcessingImplGainController2FieldTrialTest, - ConfigNotAdjustedWhenExperimentNotEnabledAndHybridAgcEnabled) { - AudioProcessingBuilderForTesting apm_builder; - - // Set a config with hybrid AGC enabled. - AudioProcessing::Config config; - config.gain_controller1.enabled = true; - config.gain_controller1.analog_gain_controller.enabled = true; - config.gain_controller1.analog_gain_controller.enable_digital_adaptive = - false; - config.gain_controller2.enabled = true; - config.gain_controller2.adaptive_digital.enabled = true; - config.gain_controller1.mode = - AudioProcessing::Config::GainController1::kAdaptiveAnalog; - - EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled); - - apm_builder.SetConfig(config); - - auto apm = apm_builder.Create(); - auto adjusted_config = apm->GetConfig(); - - EXPECT_EQ(config.gain_controller1.enabled, - adjusted_config.gain_controller1.enabled); - EXPECT_EQ(config.gain_controller1.analog_gain_controller.enabled, - adjusted_config.gain_controller1.analog_gain_controller.enabled); - EXPECT_EQ(config.gain_controller2.enabled, - adjusted_config.gain_controller2.enabled); - EXPECT_EQ(config.gain_controller2.adaptive_digital.enabled, - adjusted_config.gain_controller2.adaptive_digital.enabled); - EXPECT_FALSE( - adjusted_config.gain_controller2.input_volume_controller.enabled); - - EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString())); + // Test config application via `AudioProcessing::ApplyConfig()`. + auto apm = AudioProcessingBuilder().Create(); + apm->ApplyConfig(kOriginal); + adjusted = apm->GetConfig(); + EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled); } } // namespace webrtc