diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc index 6dba8d7e63..96193fb1a7 100644 --- a/modules/audio_processing/audio_processing_impl.cc +++ b/modules/audio_processing/audio_processing_impl.cc @@ -830,6 +830,10 @@ void AudioProcessingImpl::HandleCaptureOutputUsedSetting( submodules_.noise_suppressor->SetCaptureOutputUsage( capture_.capture_output_used); } + if (submodules_.gain_controller2) { + submodules_.gain_controller2->SetCaptureOutputUsed( + capture_.capture_output_used); + } } void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) { @@ -1001,7 +1005,9 @@ void AudioProcessingImpl::HandleCaptureRuntimeSettings() { // TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump. break; case RuntimeSetting::Type::kCaptureCompressionGain: { - if (!submodules_.agc_manager) { + if (!submodules_.agc_manager && + !(submodules_.gain_controller2 && + config_.gain_controller2.input_volume_controller.enabled)) { float value; setting.GetFloat(&value); int int_value = static_cast(value + .5f); @@ -1337,6 +1343,16 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { submodules_.agc_manager->AnalyzePreProcess(*capture_buffer); } + if (submodules_.gain_controller2 && + config_.gain_controller2.input_volume_controller.enabled) { + // Expect the volume to be available if the input controller is enabled. + RTC_DCHECK(capture_.applied_input_volume.has_value()); + if (capture_.applied_input_volume.has_value()) { + submodules_.gain_controller2->Analyze(*capture_.applied_input_volume, + *capture_buffer); + } + } + if (submodule_states_.CaptureMultiBandSubModulesActive() && SampleRateSupportsMultiBand( capture_nonlocked_.capture_processing_format.sample_rate_hz())) { @@ -1490,6 +1506,8 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { } if (submodules_.gain_controller2) { + // TODO(bugs.webrtc.org/7494): Let AGC2 detect applied input volume + // changes. submodules_.gain_controller2->Process( voice_probability, capture_.applied_input_volume_changed, capture_buffer); @@ -1819,6 +1837,13 @@ void AudioProcessingImpl::UpdateRecommendedInputVolumeLocked() { return; } + if (submodules_.gain_controller2 && + config_.gain_controller2.input_volume_controller.enabled) { + capture_.recommended_input_volume = + submodules_.gain_controller2->GetRecommendedInputVolume(); + return; + } + capture_.recommended_input_volume = capture_.applied_input_volume; } @@ -2017,6 +2042,16 @@ void AudioProcessingImpl::InitializeEchoController() { } void AudioProcessingImpl::InitializeGainController1() { + if (config_.gain_controller2.enabled && + config_.gain_controller2.input_volume_controller.enabled && + config_.gain_controller1.enabled && + (config_.gain_controller1.mode == + AudioProcessing::Config::GainController1::kAdaptiveAnalog || + config_.gain_controller1.analog_gain_controller.enabled)) { + RTC_LOG(LS_ERROR) << "APM configuration not valid: " + << "Multiple input volume controllers enabled."; + } + if (!config_.gain_controller1.enabled) { submodules_.agc_manager.reset(); submodules_.gain_control.reset(); @@ -2090,6 +2125,8 @@ void AudioProcessingImpl::InitializeGainController2(bool config_has_changed) { submodules_.gain_controller2 = std::make_unique( config_.gain_controller2, proc_fullband_sample_rate_hz(), num_input_channels(), use_internal_vad); + submodules_.gain_controller2->SetCaptureOutputUsed( + capture_.capture_output_used); } } diff --git a/modules/audio_processing/audio_processing_impl_unittest.cc b/modules/audio_processing/audio_processing_impl_unittest.cc index 21365e075e..fea7a8c74e 100644 --- a/modules/audio_processing/audio_processing_impl_unittest.cc +++ b/modules/audio_processing/audio_processing_impl_unittest.cc @@ -132,13 +132,20 @@ class TestRenderPreProcessor : public CustomProcessing { }; // Creates a simple `AudioProcessing` instance for APM input volume testing -// with analog and digital AGC enabled. -rtc::scoped_refptr CreateApmForInputVolumeTest() { +// 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. - config.gain_controller1.enabled = true; - config.gain_controller1.analog_gain_controller.enabled = true; - // Enable AGC2 adaptive digital. + // 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; @@ -146,6 +153,7 @@ rtc::scoped_refptr CreateApmForInputVolumeTest() { auto apm(AudioProcessingBuilder().Create()); apm->ApplyConfig(config); + return apm; } @@ -177,25 +185,30 @@ int ProcessInputVolume(AudioProcessing& apm, 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[64]; + 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" is removed. +// "WebRTC-Audio-2ndAgcMinMicLevelExperiment" and +// "WebRTC-Audio-Agc2-MinInputVolume" are removed. class InputVolumeStartupParameterizedTest - : public ::testing::TestWithParam>> { + : public ::testing::TestWithParam< + std::tuple, bool, bool>> { protected: InputVolumeStartupParameterizedTest() : field_trials_( @@ -204,6 +217,12 @@ class InputVolumeStartupParameterizedTest 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_; @@ -211,7 +230,7 @@ class InputVolumeStartupParameterizedTest class InputVolumeNotZeroParameterizedTest : public ::testing::TestWithParam< - std::tuple>> { + std::tuple, bool, bool>> { protected: InputVolumeNotZeroParameterizedTest() : field_trials_( @@ -224,13 +243,20 @@ class InputVolumeNotZeroParameterizedTest 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>> { + : public ::testing::TestWithParam< + std::tuple, bool, bool>> { protected: InputVolumeZeroParameterizedTest() : field_trials_( @@ -239,6 +265,12 @@ class InputVolumeZeroParameterizedTest 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_; @@ -934,33 +966,57 @@ TEST_P(InputVolumeStartupParameterizedTest, const int applied_startup_input_volume = GetStartupVolume(); const int expected_volume = std::max(applied_startup_input_volume, GetMinVolume()); - auto apm = CreateApmForInputVolumeTest(); + 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); - ASSERT_EQ(recommended_input_volume, expected_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 only if -// "WebRTC-Audio-2ndAgcMinMicLevelExperiment" is enabled. +// 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()); - auto apm = CreateApmForInputVolumeTest(); + 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 (GetMinMicLevelExperimentEnabled()) { - ASSERT_EQ(recommended_input_volume, expected_volume); - } else { + + 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); + } } } @@ -970,15 +1026,25 @@ TEST_P(InputVolumeZeroParameterizedTest, VerifyMinVolumeNotAppliedAfterManualVolumeAdjustments) { constexpr int kZeroVolume = 0; const int applied_startup_input_volume = GetStartupVolume(); - auto apm = CreateApmForInputVolumeTest(); + 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=*/1, kZeroVolume); - ASSERT_NE(recommended_input_volume, recommended_input_volume_after_startup); - ASSERT_EQ(recommended_input_volume, 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); + } } // Tests that the minimum input volume is applied if the volume is not zero @@ -987,15 +1053,26 @@ TEST_P(InputVolumeNotZeroParameterizedTest, VerifyMinVolumeAppliedAfterAutomaticVolumeAdjustments) { const int applied_startup_input_volume = GetStartupVolume(); const int applied_input_volume = GetVolume(); - auto apm = CreateApmForInputVolumeTest(); + 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=*/400, applied_input_volume); ASSERT_NE(applied_input_volume, 0); - if (recommended_input_volume != applied_input_volume) { - ASSERT_GE(recommended_input_volume, GetMinVolume()); + + 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()); + } } } @@ -1005,35 +1082,51 @@ TEST_P(InputVolumeZeroParameterizedTest, VerifyMinVolumeNotAppliedAfterAutomaticVolumeAdjustments) { constexpr int kZeroVolume = 0; const int applied_startup_input_volume = GetStartupVolume(); - auto apm = CreateApmForInputVolumeTest(); + 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); - ASSERT_NE(recommended_input_volume, recommended_input_volume_after_startup); - ASSERT_EQ(recommended_input_volume, 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))); + 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))); + 20), + ::testing::Bool(), + ::testing::Bool())); INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest, InputVolumeZeroParameterizedTest, ::testing::Combine(::testing::Values(0, 5, 15), ::testing::Values(absl::nullopt, - 20))); + 20), + ::testing::Bool(), + ::testing::Bool())); // When the input volume is not emulated and no input volume controller is // active, the recommended volume must always be the applied volume.