diff --git a/media/engine/apm_helpers.cc b/media/engine/apm_helpers.cc index 164ca3c88a..374c0951bf 100644 --- a/media/engine/apm_helpers.cc +++ b/media/engine/apm_helpers.cc @@ -11,7 +11,6 @@ #include "media/engine/apm_helpers.h" #include "modules/audio_processing/include/audio_processing.h" -#include "modules/audio_processing/include/gain_control.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -24,59 +23,18 @@ void Init(AudioProcessing* apm) { constexpr int kMinVolumeLevel = 0; constexpr int kMaxVolumeLevel = 255; + AudioProcessing::Config config = apm->GetConfig(); +#if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) + config.gain_controller1.mode = config.gain_controller1.kFixedDigital; +#else + config.gain_controller1.mode = config.gain_controller1.kAdaptiveAnalog; +#endif + RTC_LOG(LS_INFO) << "Setting AGC mode to " << config.gain_controller1.mode; // This is the initialization which used to happen in VoEBase::Init(), but // which is not covered by the WVoE::ApplyOptions(). - GainControl* gc = apm->gain_control(); - if (gc->set_analog_level_limits(kMinVolumeLevel, kMaxVolumeLevel) != 0) { - RTC_DLOG(LS_ERROR) << "Failed to set analog level limits with minimum: " - << kMinVolumeLevel - << " and maximum: " << kMaxVolumeLevel; - } -} - -AgcConfig GetAgcConfig(AudioProcessing* apm) { - RTC_DCHECK(apm); - AgcConfig result; - result.targetLeveldBOv = apm->gain_control()->target_level_dbfs(); - result.digitalCompressionGaindB = apm->gain_control()->compression_gain_db(); - result.limiterEnable = apm->gain_control()->is_limiter_enabled(); - return result; -} - -void SetAgcConfig(AudioProcessing* apm, const AgcConfig& config) { - RTC_DCHECK(apm); - GainControl* gc = apm->gain_control(); - if (gc->set_target_level_dbfs(config.targetLeveldBOv) != 0) { - RTC_LOG(LS_ERROR) << "Failed to set target level: " - << config.targetLeveldBOv; - } - if (gc->set_compression_gain_db(config.digitalCompressionGaindB) != 0) { - RTC_LOG(LS_ERROR) << "Failed to set compression gain: " - << config.digitalCompressionGaindB; - } - if (gc->enable_limiter(config.limiterEnable) != 0) { - RTC_LOG(LS_ERROR) << "Failed to set limiter on/off: " - << config.limiterEnable; - } -} - -void SetAgcStatus(AudioProcessing* apm, bool enable) { - RTC_DCHECK(apm); -#if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) - GainControl::Mode agc_mode = GainControl::kFixedDigital; -#else - GainControl::Mode agc_mode = GainControl::kAdaptiveAnalog; -#endif - GainControl* gc = apm->gain_control(); - if (gc->set_mode(agc_mode) != 0) { - RTC_LOG(LS_ERROR) << "Failed to set AGC mode: " << agc_mode; - return; - } - if (gc->Enable(enable) != 0) { - RTC_LOG(LS_ERROR) << "Failed to enable/disable AGC: " << enable; - return; - } - RTC_LOG(LS_INFO) << "AGC set to " << enable << " with mode " << agc_mode; + config.gain_controller1.analog_level_minimum = kMinVolumeLevel; + config.gain_controller1.analog_level_maximum = kMaxVolumeLevel; + apm->ApplyConfig(config); } void SetEcStatus(AudioProcessing* apm, bool enable, EcModes mode) { diff --git a/media/engine/apm_helpers.h b/media/engine/apm_helpers.h index 6881622b65..ac61768d85 100644 --- a/media/engine/apm_helpers.h +++ b/media/engine/apm_helpers.h @@ -22,18 +22,9 @@ enum EcModes { kEcAecm, // AEC mobile. }; -struct AgcConfig { - uint16_t targetLeveldBOv; - uint16_t digitalCompressionGaindB; - bool limiterEnable; -}; - namespace apm_helpers { void Init(AudioProcessing* apm); -AgcConfig GetAgcConfig(AudioProcessing* apm); -void SetAgcConfig(AudioProcessing* apm, const AgcConfig& config); -void SetAgcStatus(AudioProcessing* apm, bool enable); void SetEcStatus(AudioProcessing* apm, bool enable, EcModes mode); void SetEcMetricsStatus(AudioProcessing* apm, bool enable); void SetAecmMode(AudioProcessing* apm, bool enable_cng); diff --git a/media/engine/apm_helpers_unittest.cc b/media/engine/apm_helpers_unittest.cc index 5b0ed23031..e418795b13 100644 --- a/media/engine/apm_helpers_unittest.cc +++ b/media/engine/apm_helpers_unittest.cc @@ -18,8 +18,6 @@ namespace webrtc { namespace { -constexpr AgcConfig kDefaultAgcConfig = {3, 9, true}; - struct TestHelper { TestHelper() { // This replicates the conditions from voe_auto_test. @@ -39,63 +37,6 @@ struct TestHelper { }; } // namespace -TEST(ApmHelpersTest, AgcConfig_DefaultConfiguration) { - TestHelper helper; - AgcConfig agc_config = apm_helpers::GetAgcConfig(helper.apm()); - - EXPECT_EQ(kDefaultAgcConfig.targetLeveldBOv, agc_config.targetLeveldBOv); - EXPECT_EQ(kDefaultAgcConfig.digitalCompressionGaindB, - agc_config.digitalCompressionGaindB); - EXPECT_EQ(kDefaultAgcConfig.limiterEnable, agc_config.limiterEnable); -} - -TEST(ApmHelpersTest, AgcConfig_GetAndSet) { - const AgcConfig agc_config = {11, 17, false}; - - TestHelper helper; - apm_helpers::SetAgcConfig(helper.apm(), agc_config); - AgcConfig actual_config = apm_helpers::GetAgcConfig(helper.apm()); - - EXPECT_EQ(agc_config.digitalCompressionGaindB, - actual_config.digitalCompressionGaindB); - EXPECT_EQ(agc_config.limiterEnable, actual_config.limiterEnable); - EXPECT_EQ(agc_config.targetLeveldBOv, actual_config.targetLeveldBOv); -} - -TEST(ApmHelpersTest, AgcStatus_DefaultMode) { - TestHelper helper; - GainControl* gc = helper.apm()->gain_control(); - EXPECT_FALSE(gc->is_enabled()); -#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR - EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode()); -#elif defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) - EXPECT_EQ(GainControl::kFixedDigital, gc->mode()); -#else - EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode()); -#endif -} - -TEST(ApmHelpersTest, AgcStatus_EnableDisable) { - TestHelper helper; - GainControl* gc = helper.apm()->gain_control(); -#if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) - apm_helpers::SetAgcStatus(helper.apm(), false); - EXPECT_FALSE(gc->is_enabled()); - EXPECT_EQ(GainControl::kFixedDigital, gc->mode()); - - apm_helpers::SetAgcStatus(helper.apm(), true); - EXPECT_TRUE(gc->is_enabled()); - EXPECT_EQ(GainControl::kFixedDigital, gc->mode()); -#else - apm_helpers::SetAgcStatus(helper.apm(), false); - EXPECT_FALSE(gc->is_enabled()); - EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode()); - apm_helpers::SetAgcStatus(helper.apm(), true); - EXPECT_TRUE(gc->is_enabled()); - EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode()); -#endif -} - TEST(ApmHelpersTest, EcStatus_DefaultMode) { TestHelper helper; webrtc::AudioProcessing::Config config = helper.apm()->GetConfig(); diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc index 9fc9d08362..cbf66ba466 100644 --- a/media/engine/webrtc_voice_engine.cc +++ b/media/engine/webrtc_voice_engine.cc @@ -260,10 +260,6 @@ void WebRtcVoiceEngine::Init() { // Connect the ADM to our audio path. adm()->RegisterAudioCallback(audio_state()->audio_transport()); - // Save the default AGC configuration settings. This must happen before - // calling ApplyOptions or the default will be overwritten. - default_agc_config_ = webrtc::apm_helpers::GetAgcConfig(apm()); - // Set default engine options. { AudioOptions options; @@ -428,24 +424,6 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { << "Disabling AGC since built-in AGC will be used instead"; } } - webrtc::apm_helpers::SetAgcStatus(apm(), *options.auto_gain_control); - } - - if (options.tx_agc_target_dbov || options.tx_agc_digital_compression_gain || - options.tx_agc_limiter) { - // Override default_agc_config_. Generally, an unset option means "leave - // the VoE bits alone" in this function, so we want whatever is set to be - // stored as the new "default". If we didn't, then setting e.g. - // tx_agc_target_dbov would reset digital compression gain and limiter - // settings. - default_agc_config_.targetLeveldBOv = options.tx_agc_target_dbov.value_or( - default_agc_config_.targetLeveldBOv); - default_agc_config_.digitalCompressionGaindB = - options.tx_agc_digital_compression_gain.value_or( - default_agc_config_.digitalCompressionGaindB); - default_agc_config_.limiterEnable = - options.tx_agc_limiter.value_or(default_agc_config_.limiterEnable); - webrtc::apm_helpers::SetAgcConfig(apm(), default_agc_config_); } if (options.noise_suppression) { @@ -524,6 +502,22 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { webrtc::AudioProcessing::Config apm_config = apm()->GetConfig(); + if (options.auto_gain_control) { + const bool enabled = *options.auto_gain_control; + apm_config.gain_controller1.enabled = enabled; + RTC_LOG(LS_INFO) << "Setting AGC to " << enabled; + } + if (options.tx_agc_target_dbov) { + apm_config.gain_controller1.target_level_dbfs = *options.tx_agc_target_dbov; + } + if (options.tx_agc_digital_compression_gain) { + apm_config.gain_controller1.compression_gain_db = + *options.tx_agc_digital_compression_gain; + } + if (options.tx_agc_limiter) { + apm_config.gain_controller1.enable_limiter = *options.tx_agc_limiter; + } + if (options.highpass_filter) { apm_config.high_pass_filter.enabled = *options.highpass_filter; } diff --git a/media/engine/webrtc_voice_engine.h b/media/engine/webrtc_voice_engine.h index 3bce78de0a..1cf1ccb07f 100644 --- a/media/engine/webrtc_voice_engine.h +++ b/media/engine/webrtc_voice_engine.h @@ -82,10 +82,6 @@ class WebRtcVoiceEngine final : public VoiceEngineInterface { // Stops AEC dump. void StopAecDump() override; - const webrtc::AudioProcessing::Config GetApmConfigForTest() const { - return apm()->GetConfig(); - } - private: // Every option that is "set" will be applied. Every option not "set" will be // ignored. This allows us to selectively turn on and off different options @@ -124,7 +120,6 @@ class WebRtcVoiceEngine final : public VoiceEngineInterface { bool is_dumping_aec_ = false; bool initialized_ = false; - webrtc::AgcConfig default_agc_config_; // Cache received extended_filter_aec, delay_agnostic_aec and experimental_ns // values, and apply them in case they are missing in the audio options. // We need to do this because SetExtraOptions() will revert to defaults for diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc index a8935e1f8f..8930b9940f 100644 --- a/media/engine/webrtc_voice_engine_unittest.cc +++ b/media/engine/webrtc_voice_engine_unittest.cc @@ -77,11 +77,12 @@ const uint32_t kSsrcs4[] = {11, 200, 30, 44}; constexpr int kRtpHistoryMs = 5000; -constexpr webrtc::GainControl::Mode kDefaultAgcMode = +constexpr webrtc::AudioProcessing::Config::GainController1::Mode + kDefaultAgcMode = #if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) - webrtc::GainControl::kFixedDigital; + webrtc::AudioProcessing::Config::GainController1::kFixedDigital; #else - webrtc::GainControl::kAdaptiveAnalog; + webrtc::AudioProcessing::Config::GainController1::kAdaptiveAnalog; #endif constexpr webrtc::NoiseSuppression::Level kDefaultNsLevel = @@ -169,7 +170,6 @@ class WebRtcVoiceEngineTestFake : public testing::Test { explicit WebRtcVoiceEngineTestFake(const char* field_trials) : apm_(new rtc::RefCountedObject< StrictMock>()), - apm_gc_(*apm_->gain_control()), apm_ns_(*apm_->noise_suppression()), call_(), override_field_trials_(field_trials) { @@ -181,17 +181,8 @@ class WebRtcVoiceEngineTestFake : public testing::Test { EXPECT_CALL(*apm_, SetExtraOptions(testing::_)); EXPECT_CALL(*apm_, DetachAecDump()); // Default Options. - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, set_analog_level_limits(0, 255)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0)); - // Init does not overwrite default AGC config. - EXPECT_CALL(apm_gc_, target_level_dbfs()).WillOnce(Return(1)); - EXPECT_CALL(apm_gc_, compression_gain_db()).WillRepeatedly(Return(5)); - EXPECT_CALL(apm_gc_, is_limiter_enabled()).WillRepeatedly(Return(true)); - EXPECT_CALL(apm_gc_, set_compression_gain_db(5)).WillRepeatedly(Return(0)); - EXPECT_CALL(apm_gc_, enable_limiter(true)).WillRepeatedly(Return(0)); // TODO(kwiberg): We should use mock factories here, but a bunch of // the tests here probe the specific set of codecs provided by the builtin // factories. Those tests should probably be moved elsewhere. @@ -207,6 +198,8 @@ class WebRtcVoiceEngineTestFake : public testing::Test { EXPECT_TRUE(IsEchoCancellationEnabled()); EXPECT_TRUE(IsHighPassFilterEnabled()); EXPECT_TRUE(IsTypingDetectionEnabled()); + VerifyGainControlEnabledCorrectly(); + VerifyGainControlDefaultSettings(); } bool SetupChannel() { @@ -737,22 +730,34 @@ class WebRtcVoiceEngineTestFake : public testing::Test { } } + void VerifyGainControlEnabledCorrectly() { + EXPECT_TRUE(apm_config_.gain_controller1.enabled); + EXPECT_EQ(kDefaultAgcMode, apm_config_.gain_controller1.mode); + EXPECT_EQ(0, apm_config_.gain_controller1.analog_level_minimum); + EXPECT_EQ(255, apm_config_.gain_controller1.analog_level_maximum); + } + + void VerifyGainControlDefaultSettings() { + EXPECT_EQ(3, apm_config_.gain_controller1.target_level_dbfs); + EXPECT_EQ(9, apm_config_.gain_controller1.compression_gain_db); + EXPECT_TRUE(apm_config_.gain_controller1.enable_limiter); + } + bool IsEchoCancellationEnabled() { - return engine_->GetApmConfigForTest().echo_canceller.enabled; + return apm_config_.echo_canceller.enabled; } bool IsHighPassFilterEnabled() { - return engine_->GetApmConfigForTest().high_pass_filter.enabled; + return apm_config_.high_pass_filter.enabled; } bool IsTypingDetectionEnabled() { - return engine_->GetApmConfigForTest().voice_detection.enabled; + return apm_config_.voice_detection.enabled; } protected: StrictMock adm_; rtc::scoped_refptr> apm_; - webrtc::test::MockGainControl& apm_gc_; webrtc::test::MockNoiseSuppression& apm_ns_; cricket::FakeCall call_; std::unique_ptr engine_; @@ -2308,18 +2313,40 @@ TEST_F(WebRtcVoiceEngineTestFake, PlayoutWithMultipleStreams) { TEST_F(WebRtcVoiceEngineTestFake, TxAgcConfigViaOptions) { EXPECT_TRUE(SetupSendStream()); EXPECT_CALL(adm_, BuiltInAGCIsAvailable()) - .Times(1) + .Times(testing::AtLeast(1)) .WillRepeatedly(Return(false)); - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).Times(1).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(true)).Times(1).WillOnce(Return(0)); - send_parameters_.options.tx_agc_target_dbov = 3; - send_parameters_.options.tx_agc_digital_compression_gain = 9; - send_parameters_.options.tx_agc_limiter = true; - send_parameters_.options.auto_gain_control = true; - EXPECT_CALL(apm_gc_, set_target_level_dbfs(3)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, set_compression_gain_db(9)).WillRepeatedly(Return(0)); - EXPECT_CALL(apm_gc_, enable_limiter(true)).WillRepeatedly(Return(0)); + const auto& agc_config = apm_config_.gain_controller1; + + // Ensure default options. + VerifyGainControlEnabledCorrectly(); + VerifyGainControlDefaultSettings(); + + send_parameters_.options.auto_gain_control = false; SetSendParameters(send_parameters_); + EXPECT_FALSE(agc_config.enabled); + send_parameters_.options.auto_gain_control = absl::nullopt; + + send_parameters_.options.tx_agc_target_dbov = 5; + SetSendParameters(send_parameters_); + EXPECT_EQ(5, agc_config.target_level_dbfs); + send_parameters_.options.tx_agc_target_dbov = absl::nullopt; + + send_parameters_.options.tx_agc_digital_compression_gain = 10; + SetSendParameters(send_parameters_); + EXPECT_EQ(10, agc_config.compression_gain_db); + send_parameters_.options.tx_agc_digital_compression_gain = absl::nullopt; + + send_parameters_.options.tx_agc_limiter = false; + SetSendParameters(send_parameters_); + EXPECT_FALSE(agc_config.enable_limiter); + send_parameters_.options.tx_agc_limiter = absl::nullopt; + + SetSendParameters(send_parameters_); + // Expect all options to have been preserved. + EXPECT_FALSE(agc_config.enabled); + EXPECT_EQ(5, agc_config.target_level_dbfs); + EXPECT_EQ(10, agc_config.compression_gain_db); + EXPECT_FALSE(agc_config.enable_limiter); } TEST_F(WebRtcVoiceEngineTestFake, SetAudioNetworkAdaptorViaOptions) { @@ -2898,22 +2925,18 @@ TEST_F(WebRtcVoiceEngineTestFake, SetAudioOptions) { EXPECT_TRUE(IsEchoCancellationEnabled()); // Turn off AGC - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0)); send_parameters_.options.auto_gain_control = false; SetSendParameters(send_parameters_); EXPECT_TRUE(IsEchoCancellationEnabled()); + EXPECT_FALSE(apm_config_.gain_controller1.enabled); // Turn AGC back on - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); send_parameters_.options.auto_gain_control = true; SetSendParameters(send_parameters_); EXPECT_TRUE(IsEchoCancellationEnabled()); + EXPECT_TRUE(apm_config_.gain_controller1.enabled); // Turn off other options. - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0)); send_parameters_.options.noise_suppression = false; @@ -2922,14 +2945,14 @@ TEST_F(WebRtcVoiceEngineTestFake, SetAudioOptions) { SetSendParameters(send_parameters_); EXPECT_TRUE(IsEchoCancellationEnabled()); EXPECT_FALSE(IsHighPassFilterEnabled()); + EXPECT_TRUE(apm_config_.gain_controller1.enabled); // Set options again to ensure it has no impact. - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0)); SetSendParameters(send_parameters_); EXPECT_TRUE(IsEchoCancellationEnabled()); + EXPECT_TRUE(apm_config_.gain_controller1.enabled); } TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { @@ -2948,11 +2971,6 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { .WillRepeatedly(Return(false)); EXPECT_CALL(adm_, Recording()).Times(2).WillRepeatedly(Return(false)); EXPECT_CALL(adm_, InitRecording()).Times(2).WillRepeatedly(Return(0)); - webrtc::AudioProcessing::Config apm_config; - EXPECT_CALL(*apm_, GetConfig()) - .WillRepeatedly(ReturnPointee(&apm_config)); - EXPECT_CALL(*apm_, ApplyConfig(_)) - .WillRepeatedly(SaveArg<0>(&apm_config)); EXPECT_CALL(*apm_, SetExtraOptions(testing::_)).Times(10); std::unique_ptr channel1( @@ -2979,26 +2997,25 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { parameters_options_all.options.echo_cancellation = true; parameters_options_all.options.auto_gain_control = true; parameters_options_all.options.noise_suppression = true; - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).Times(2).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(true)).Times(2).WillRepeatedly(Return(0)); EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).Times(2).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, Enable(true)).Times(2).WillRepeatedly(Return(0)); EXPECT_TRUE(channel1->SetSendParameters(parameters_options_all)); EXPECT_TRUE(IsEchoCancellationEnabled()); + VerifyGainControlEnabledCorrectly(); EXPECT_EQ(parameters_options_all.options, channel1->options()); EXPECT_TRUE(channel2->SetSendParameters(parameters_options_all)); EXPECT_TRUE(IsEchoCancellationEnabled()); + VerifyGainControlEnabledCorrectly(); EXPECT_EQ(parameters_options_all.options, channel2->options()); // unset NS cricket::AudioSendParameters parameters_options_no_ns = send_parameters_; parameters_options_no_ns.options.noise_suppression = false; - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0)); EXPECT_TRUE(channel1->SetSendParameters(parameters_options_no_ns)); EXPECT_TRUE(IsEchoCancellationEnabled()); + VerifyGainControlEnabledCorrectly(); cricket::AudioOptions expected_options = parameters_options_all.options; expected_options.echo_cancellation = true; expected_options.auto_gain_control = true; @@ -3008,49 +3025,44 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { // unset AGC cricket::AudioSendParameters parameters_options_no_agc = send_parameters_; parameters_options_no_agc.options.auto_gain_control = false; - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0)); EXPECT_TRUE(channel2->SetSendParameters(parameters_options_no_agc)); EXPECT_TRUE(IsEchoCancellationEnabled()); + EXPECT_FALSE(apm_config_.gain_controller1.enabled); expected_options.echo_cancellation = true; expected_options.auto_gain_control = false; expected_options.noise_suppression = true; EXPECT_EQ(expected_options, channel2->options()); - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0)); EXPECT_TRUE(channel_->SetSendParameters(parameters_options_all)); EXPECT_TRUE(IsEchoCancellationEnabled()); + VerifyGainControlEnabledCorrectly(); - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0)); channel1->SetSend(true); EXPECT_TRUE(IsEchoCancellationEnabled()); + VerifyGainControlEnabledCorrectly(); - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0)); channel2->SetSend(true); EXPECT_TRUE(IsEchoCancellationEnabled()); + EXPECT_FALSE(apm_config_.gain_controller1.enabled); // Make sure settings take effect while we are sending. cricket::AudioSendParameters parameters_options_no_agc_nor_ns = send_parameters_; parameters_options_no_agc_nor_ns.options.auto_gain_control = false; parameters_options_no_agc_nor_ns.options.noise_suppression = false; - EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0)); - EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0)); EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0)); EXPECT_TRUE(channel2->SetSendParameters(parameters_options_no_agc_nor_ns)); EXPECT_TRUE(IsEchoCancellationEnabled()); + EXPECT_FALSE(apm_config_.gain_controller1.enabled); expected_options.echo_cancellation = true; expected_options.auto_gain_control = false; expected_options.noise_suppression = false; @@ -3065,11 +3077,6 @@ TEST_F(WebRtcVoiceEngineTestFake, TestSetDscpOptions) { std::unique_ptr channel; webrtc::RtpParameters parameters; - webrtc::AudioProcessing::Config apm_config; - EXPECT_CALL(*apm_, GetConfig()) - .WillRepeatedly(ReturnPointee(&apm_config)); - EXPECT_CALL(*apm_, ApplyConfig(_)) - .WillRepeatedly(SaveArg<0>(&apm_config)); EXPECT_CALL(*apm_, SetExtraOptions(testing::_)).Times(3); channel.reset(static_cast( diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn index 5b64ef37bb..4c84235df3 100644 --- a/modules/audio_processing/BUILD.gn +++ b/modules/audio_processing/BUILD.gn @@ -154,6 +154,7 @@ rtc_static_library("audio_processing") { ":audio_processing_c", ":audio_processing_statistics", ":config", + ":gain_control_config_proxy", ":gain_control_interface", ":noise_suppression_proxy", "../..:webrtc_common", @@ -214,6 +215,19 @@ rtc_source_set("gain_control_interface") { ] } +rtc_source_set("gain_control_config_proxy") { + sources = [ + "gain_control_config_proxy.cc", + "gain_control_config_proxy.h", + ] + deps = [ + ":api", + ":gain_control_interface", + "../../rtc_base:criticalsection", + "../../rtc_base:macromagic", + ] +} + rtc_source_set("noise_suppression_proxy") { sources = [ "noise_suppression_proxy.cc", @@ -414,6 +428,7 @@ if (rtc_include_tests) { "config_unittest.cc", "echo_cancellation_impl_unittest.cc", "echo_control_mobile_unittest.cc", + "gain_control_config_proxy_unittest.cc", "gain_controller2_unittest.cc", "splitting_filter_unittest.cc", "test/fake_recording_device_unittest.cc", @@ -438,6 +453,7 @@ if (rtc_include_tests) { ":audioproc_test_utils", ":config", ":file_audio_generator_unittests", + ":gain_control_config_proxy", ":mocks", "../..:webrtc_common", "../../api:array_view", diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.cc b/modules/audio_processing/aec_dump/aec_dump_impl.cc index 706c0521a2..a915222456 100644 --- a/modules/audio_processing/aec_dump/aec_dump_impl.cc +++ b/modules/audio_processing/aec_dump/aec_dump_impl.cc @@ -188,6 +188,10 @@ void AecDumpImpl::WriteRuntimeSetting( setting->set_custom_render_processing_setting(x); break; } + case AudioProcessing::RuntimeSetting::Type::kCaptureCompressionGain: + // Runtime AGC1 compression gain is ignored. + // TODO(http://bugs.webrtc.org/10432): Store compression gain in aecdumps. + break; case AudioProcessing::RuntimeSetting::Type::kNotSpecified: RTC_NOTREACHED(); break; diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc index 504eb31b76..4d24ee662d 100644 --- a/modules/audio_processing/audio_processing_impl.cc +++ b/modules/audio_processing/audio_processing_impl.cc @@ -28,6 +28,7 @@ #include "modules/audio_processing/common.h" #include "modules/audio_processing/echo_cancellation_impl.h" #include "modules/audio_processing/echo_control_mobile_impl.h" +#include "modules/audio_processing/gain_control_config_proxy.h" #include "modules/audio_processing/gain_control_for_experimental_agc.h" #include "modules/audio_processing/gain_control_impl.h" #include "modules/audio_processing/gain_controller2.h" @@ -127,6 +128,19 @@ NoiseSuppression::Level NsConfigLevelToInterfaceLevel( } } +GainControl::Mode Agc1ConfigModeToInterfaceMode( + AudioProcessing::Config::GainController1::Mode mode) { + using Agc1Config = AudioProcessing::Config::GainController1; + switch (mode) { + case Agc1Config::kAdaptiveAnalog: + return GainControl::kAdaptiveAnalog; + case Agc1Config::kAdaptiveDigital: + return GainControl::kAdaptiveDigital; + case Agc1Config::kFixedDigital: + return GainControl::kFixedDigital; + } +} + // Maximum lengths that frame of samples being passed from the render side to // the capture side can have (does not apply to AEC3). static const size_t kMaxAllowedValuesOfSamplesPerBand = 160; @@ -254,13 +268,14 @@ struct AudioProcessingImpl::ApmPublicSubmodules { // Accessed externally of APM without any lock acquired. // TODO(bugs.webrtc.org/9947): Move these submodules into private_submodules_ // when their pointer-to-submodule API functions are gone. - std::unique_ptr gain_control; std::unique_ptr level_estimator; std::unique_ptr noise_suppression; std::unique_ptr noise_suppression_proxy; std::unique_ptr voice_detection; + std::unique_ptr gain_control; std::unique_ptr gain_control_for_experimental_agc; + std::unique_ptr gain_control_config_proxy; // Accessed internally from both render and capture. std::unique_ptr transient_suppressor; @@ -393,7 +408,7 @@ AudioProcessingImpl::AudioProcessingImpl( capture_nonlocked_.echo_controller_enabled = static_cast(echo_control_factory_); - public_submodules_->gain_control.reset(new GainControlImpl(&crit_capture_)); + public_submodules_->gain_control.reset(new GainControlImpl()); public_submodules_->level_estimator.reset( new LevelEstimatorImpl(&crit_capture_)); public_submodules_->noise_suppression.reset( @@ -403,8 +418,10 @@ AudioProcessingImpl::AudioProcessingImpl( public_submodules_->voice_detection.reset( new VoiceDetectionImpl(&crit_capture_)); public_submodules_->gain_control_for_experimental_agc.reset( - new GainControlForExperimentalAgc(public_submodules_->gain_control.get(), - &crit_capture_)); + new GainControlForExperimentalAgc( + public_submodules_->gain_control.get())); + public_submodules_->gain_control_config_proxy.reset( + new GainControlConfigProxy(&crit_capture_, this, agc1())); // If no echo detector is injected, use the ResidualEchoDetector. if (!private_submodules_->echo_detector) { @@ -680,6 +697,20 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { config_.echo_canceller.legacy_moderate_suppression_level != config.echo_canceller.legacy_moderate_suppression_level); + const bool agc1_config_changed = + config_.gain_controller1.enabled != config.gain_controller1.enabled || + config_.gain_controller1.mode != config.gain_controller1.mode || + config_.gain_controller1.target_level_dbfs != + config.gain_controller1.target_level_dbfs || + config_.gain_controller1.compression_gain_db != + config.gain_controller1.compression_gain_db || + config_.gain_controller1.enable_limiter != + config.gain_controller1.enable_limiter || + config_.gain_controller1.analog_level_minimum != + config.gain_controller1.analog_level_minimum || + config_.gain_controller1.analog_level_maximum != + config.gain_controller1.analog_level_maximum; + config_ = config; if (aec_config_changed) { @@ -696,6 +727,10 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { RTC_LOG(LS_INFO) << "Highpass filter activated: " << config_.high_pass_filter.enabled; + if (agc1_config_changed) { + ApplyAgc1Config(config_.gain_controller1); + } + const bool config_ok = GainController2::Validate(config_.gain_controller2); if (!config_ok) { RTC_LOG(LS_ERROR) << "AudioProcessing module config error\n" @@ -730,6 +765,38 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) { } } +void AudioProcessingImpl::ApplyAgc1Config( + const Config::GainController1& config) { + GainControl* agc = agc1(); + int error = agc->Enable(config.enabled); + RTC_DCHECK_EQ(kNoError, error); + error = agc->set_mode(Agc1ConfigModeToInterfaceMode(config.mode)); + RTC_DCHECK_EQ(kNoError, error); + error = agc->set_target_level_dbfs(config.target_level_dbfs); + RTC_DCHECK_EQ(kNoError, error); + error = agc->set_compression_gain_db(config.compression_gain_db); + RTC_DCHECK_EQ(kNoError, error); + error = agc->enable_limiter(config.enable_limiter); + RTC_DCHECK_EQ(kNoError, error); + error = agc->set_analog_level_limits(config.analog_level_minimum, + config.analog_level_maximum); + RTC_DCHECK_EQ(kNoError, error); +} + +GainControl* AudioProcessingImpl::agc1() { + if (constants_.use_experimental_agc) { + return public_submodules_->gain_control_for_experimental_agc.get(); + } + return public_submodules_->gain_control.get(); +} + +const GainControl* AudioProcessingImpl::agc1() const { + if (constants_.use_experimental_agc) { + return public_submodules_->gain_control_for_experimental_agc.get(); + } + return public_submodules_->gain_control.get(); +} + void AudioProcessingImpl::SetExtraOptions(const webrtc::Config& config) { // Run in a single-threaded manner when setting the extra options. rtc::CritScope cs_render(&crit_render_); @@ -793,6 +860,7 @@ void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) { RTC_NOTREACHED(); return; case RuntimeSetting::Type::kCapturePreGain: + case RuntimeSetting::Type::kCaptureCompressionGain: capture_runtime_settings_enqueuer_.Enqueue(setting); return; } @@ -919,6 +987,15 @@ void AudioProcessingImpl::HandleCaptureRuntimeSettings() { } // TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump. break; + case RuntimeSetting::Type::kCaptureCompressionGain: { + float value; + setting.GetFloat(&value); + int int_value = static_cast(value + .5f); + config_.gain_controller1.compression_gain_db = int_value; + int error = agc1()->set_compression_gain_db(int_value); + RTC_DCHECK_EQ(kNoError, error); + break; + } case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting: RTC_NOTREACHED(); break; @@ -941,9 +1018,8 @@ void AudioProcessingImpl::HandleRenderRuntimeSettings() { private_submodules_->render_pre_processor->SetRuntimeSetting(setting); } break; - case RuntimeSetting::Type::kCapturePreGain: - RTC_NOTREACHED(); - break; + case RuntimeSetting::Type::kCapturePreGain: // fall-through + case RuntimeSetting::Type::kCaptureCompressionGain: // fall-through case RuntimeSetting::Type::kNotSpecified: RTC_NOTREACHED(); break; @@ -1235,7 +1311,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { if (private_submodules_->echo_controller) { // Detect and flag any change in the analog gain. - int analog_mic_level = gain_control()->stream_analog_level(); + int analog_mic_level = agc1()->stream_analog_level(); capture_.echo_path_gain_change = capture_.prev_analog_mic_level != analog_mic_level && capture_.prev_analog_mic_level != -1; @@ -1389,7 +1465,7 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { if (config_.gain_controller2.enabled) { private_submodules_->gain_controller2->NotifyAnalogLevel( - gain_control()->stream_analog_level()); + agc1()->stream_analog_level()); private_submodules_->gain_controller2->Process(capture_buffer); } @@ -1620,6 +1696,17 @@ int AudioProcessingImpl::delay_offset_ms() const { return capture_.delay_offset_ms; } +void AudioProcessingImpl::set_stream_analog_level(int level) { + rtc::CritScope cs_capture(&crit_capture_); + int error = agc1()->set_stream_analog_level(level); + RTC_DCHECK_EQ(kNoError, error); +} + +int AudioProcessingImpl::recommended_stream_analog_level() const { + rtc::CritScope cs_capture(&crit_capture_); + return agc1()->stream_analog_level(); +} + void AudioProcessingImpl::AttachAecDump(std::unique_ptr aec_dump) { RTC_DCHECK(aec_dump); rtc::CritScope cs_render(&crit_render_); @@ -1707,10 +1794,7 @@ AudioProcessingStats AudioProcessingImpl::GetStatistics( } GainControl* AudioProcessingImpl::gain_control() const { - if (constants_.use_experimental_agc) { - return public_submodules_->gain_control_for_experimental_agc.get(); - } - return public_submodules_->gain_control.get(); + return public_submodules_->gain_control_config_proxy.get(); } LevelEstimator* AudioProcessingImpl::level_estimator() const { @@ -2032,7 +2116,7 @@ void AudioProcessingImpl::RecordAudioProcessingState() { audio_proc_state.delay = capture_nonlocked_.stream_delay_ms; audio_proc_state.drift = private_submodules_->echo_cancellation->stream_drift_samples(); - audio_proc_state.level = gain_control()->stream_analog_level(); + audio_proc_state.level = agc1()->stream_analog_level(); audio_proc_state.keypress = capture_.key_pressed; aec_dump_->AddAudioProcessingState(audio_proc_state); } diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h index f164407255..9cd4f78b3f 100644 --- a/modules/audio_processing/audio_processing_impl.h +++ b/modules/audio_processing/audio_processing_impl.h @@ -84,6 +84,8 @@ class AudioProcessingImpl : public AudioProcessing { void set_delay_offset_ms(int offset) override; int delay_offset_ms() const override; void set_stream_key_pressed(bool key_pressed) override; + void set_stream_analog_level(int level) override; + int recommended_stream_analog_level() const override; // Render-side exclusive methods possibly running APM in a // multi-threaded manner. Acquire the render lock. @@ -255,6 +257,13 @@ class AudioProcessingImpl : public AudioProcessing { void HandleCaptureRuntimeSettings() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_); void HandleRenderRuntimeSettings() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_render_); + void ApplyAgc1Config(const Config::GainController1& agc_config) + RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_); + + // Returns a direct pointer to the AGC1 submodule: either a GainControlImpl + // or GainControlForExperimentalAgc instance. + GainControl* agc1(); + const GainControl* agc1() const; void EmptyQueuedRenderAudio(); void AllocateRenderQueue() diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc index d1e5801ca5..b5fb2b85c8 100644 --- a/modules/audio_processing/audio_processing_unittest.cc +++ b/modules/audio_processing/audio_processing_unittest.cc @@ -182,17 +182,21 @@ void EnableAllAPComponents(AudioProcessing* ap) { apm_config.echo_canceller.enabled = true; #if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE) apm_config.echo_canceller.mobile_mode = true; - EXPECT_NOERR(ap->gain_control()->set_mode(GainControl::kAdaptiveDigital)); - EXPECT_NOERR(ap->gain_control()->Enable(true)); + + apm_config.gain_controller1.enabled = true; + apm_config.gain_controller1.mode = + AudioProcessing::Config::GainController1::kAdaptiveDigital; #elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE) // TODO(peah): Update tests to instead use AEC3. apm_config.echo_canceller.use_legacy_aec = true; apm_config.echo_canceller.mobile_mode = false; apm_config.echo_canceller.legacy_moderate_suppression_level = true; - EXPECT_NOERR(ap->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); - EXPECT_NOERR(ap->gain_control()->set_analog_level_limits(0, 255)); - EXPECT_NOERR(ap->gain_control()->Enable(true)); + apm_config.gain_controller1.enabled = true; + apm_config.gain_controller1.mode = + AudioProcessing::Config::GainController1::kAdaptiveAnalog; + apm_config.gain_controller1.analog_level_minimum = 0; + apm_config.gain_controller1.analog_level_maximum = 255; #endif apm_config.high_pass_filter.enabled = true; @@ -958,12 +962,7 @@ TEST_F(ApmTest, GainControl) { apm_->gain_control()->set_mode(mode[i])); EXPECT_EQ(mode[i], apm_->gain_control()->mode()); } - // Testing invalid target levels - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_target_level_dbfs(-3)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_target_level_dbfs(-40)); - // Testing valid target levels + // Testing target levels EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_target_level_dbfs( apm_->gain_control()->target_level_dbfs())); @@ -975,13 +974,7 @@ TEST_F(ApmTest, GainControl) { EXPECT_EQ(level_dbfs[i], apm_->gain_control()->target_level_dbfs()); } - // Testing invalid compression gains - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_compression_gain_db(-1)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_compression_gain_db(100)); - - // Testing valid compression gains + // Testing compression gains EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_compression_gain_db( apm_->gain_control()->compression_gain_db())); @@ -990,6 +983,7 @@ TEST_F(ApmTest, GainControl) { for (size_t i = 0; i < arraysize(gain_db); i++) { EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_compression_gain_db(gain_db[i])); + ProcessStreamChooser(kFloatFormat); EXPECT_EQ(gain_db[i], apm_->gain_control()->compression_gain_db()); } @@ -999,19 +993,7 @@ TEST_F(ApmTest, GainControl) { EXPECT_EQ(apm_->kNoError, apm_->gain_control()->enable_limiter(true)); EXPECT_TRUE(apm_->gain_control()->is_limiter_enabled()); - // Testing invalid level limits - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_analog_level_limits(-1, 512)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_analog_level_limits(100000, 512)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_analog_level_limits(512, -1)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_analog_level_limits(512, 100000)); - EXPECT_EQ(apm_->kBadParameterError, - apm_->gain_control()->set_analog_level_limits(512, 255)); - - // Testing valid level limits + // Testing level limits EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_analog_level_limits( apm_->gain_control()->analog_level_minimum(), @@ -1038,6 +1020,46 @@ TEST_F(ApmTest, GainControl) { EXPECT_FALSE(apm_->gain_control()->is_enabled()); } +#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) +TEST_F(ApmTest, GainControlDiesOnTooLowTargetLevelDbfs) { + EXPECT_DEATH(apm_->gain_control()->set_target_level_dbfs(-1), ""); +} + +TEST_F(ApmTest, GainControlDiesOnTooHighTargetLevelDbfs) { + EXPECT_DEATH(apm_->gain_control()->set_target_level_dbfs(32), ""); +} + +TEST_F(ApmTest, GainControlDiesOnTooLowCompressionGainDb) { + EXPECT_DEATH(apm_->gain_control()->set_compression_gain_db(-1), ""); +} + +TEST_F(ApmTest, GainControlDiesOnTooHighCompressionGainDb) { + EXPECT_DEATH(apm_->gain_control()->set_compression_gain_db(91), ""); +} + +TEST_F(ApmTest, GainControlDiesOnTooLowAnalogLevelLowerLimit) { + EXPECT_DEATH(apm_->gain_control()->set_analog_level_limits(-1, 512), ""); +} + +TEST_F(ApmTest, GainControlDiesOnTooHighAnalogLevelUpperLimit) { + EXPECT_DEATH(apm_->gain_control()->set_analog_level_limits(512, 65536), ""); +} + +TEST_F(ApmTest, GainControlDiesOnInvertedAnalogLevelLimits) { + EXPECT_DEATH(apm_->gain_control()->set_analog_level_limits(512, 255), ""); +} + +TEST_F(ApmTest, ApmDiesOnTooLowAnalogLevel) { + apm_->gain_control()->set_analog_level_limits(255, 512); + EXPECT_DEATH(apm_->set_stream_analog_level(254), ""); +} + +TEST_F(ApmTest, ApmDiesOnTooHighAnalogLevel) { + apm_->gain_control()->set_analog_level_limits(255, 512); + EXPECT_DEATH(apm_->set_stream_analog_level(513), ""); +} +#endif + void ApmTest::RunQuantizedVolumeDoesNotGetStuckTest(int sample_rate) { Init(sample_rate, sample_rate, sample_rate, 2, 2, 2, false); EXPECT_EQ(apm_->kNoError, diff --git a/modules/audio_processing/gain_control_config_proxy.cc b/modules/audio_processing/gain_control_config_proxy.cc new file mode 100644 index 0000000000..28e21177fe --- /dev/null +++ b/modules/audio_processing/gain_control_config_proxy.cc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/gain_control_config_proxy.h" + +namespace webrtc { +namespace { + +AudioProcessing::Config::GainController1::Mode InterfaceModeToConfigMode( + GainControl::Mode agc_mode) { + using AgcConfig = AudioProcessing::Config::GainController1; + switch (agc_mode) { + case GainControl::kAdaptiveAnalog: + return AgcConfig::kAdaptiveAnalog; + case GainControl::kAdaptiveDigital: + return AgcConfig::kAdaptiveDigital; + case GainControl::kFixedDigital: + return AgcConfig::kFixedDigital; + } +} +} // namespace + +GainControlConfigProxy::GainControlConfigProxy( + rtc::CriticalSection* crit_capture, + AudioProcessing* apm, + GainControl* agc) + : crit_capture_(crit_capture), apm_(apm), agc_(agc) { + RTC_DCHECK(apm); + RTC_DCHECK(agc); + RTC_DCHECK(crit_capture); +} + +GainControlConfigProxy::~GainControlConfigProxy() = default; + +int GainControlConfigProxy::set_stream_analog_level(int level) { + apm_->set_stream_analog_level(level); + return AudioProcessing::kNoError; +} + +int GainControlConfigProxy::stream_analog_level() const { + return apm_->recommended_stream_analog_level(); +} + +int GainControlConfigProxy::Enable(bool enable) { + auto apm_config = apm_->GetConfig(); + apm_config.gain_controller1.enabled = enable; + apm_->ApplyConfig(apm_config); + return AudioProcessing::kNoError; +} + +int GainControlConfigProxy::set_mode(Mode mode) { + auto config = apm_->GetConfig(); + config.gain_controller1.mode = InterfaceModeToConfigMode(mode); + apm_->ApplyConfig(config); + return AudioProcessing::kNoError; +} + +int GainControlConfigProxy::set_target_level_dbfs(int level) { + auto config = apm_->GetConfig(); + config.gain_controller1.target_level_dbfs = level; + apm_->ApplyConfig(config); + return AudioProcessing::kNoError; +} + +int GainControlConfigProxy::set_compression_gain_db(int gain) { + apm_->SetRuntimeSetting( + AudioProcessing::RuntimeSetting::CreateCompressionGainDb(gain)); + return AudioProcessing::kNoError; +} + +int GainControlConfigProxy::enable_limiter(bool enable) { + auto config = apm_->GetConfig(); + config.gain_controller1.enable_limiter = enable; + apm_->ApplyConfig(config); + return AudioProcessing::kNoError; +} + +int GainControlConfigProxy::set_analog_level_limits(int minimum, int maximum) { + auto config = apm_->GetConfig(); + config.gain_controller1.analog_level_minimum = minimum; + config.gain_controller1.analog_level_maximum = maximum; + apm_->ApplyConfig(config); + return AudioProcessing::kNoError; +} + +bool GainControlConfigProxy::is_limiter_enabled() const { + rtc::CritScope cs_capture(crit_capture_); + return agc_->is_limiter_enabled(); +} + +int GainControlConfigProxy::compression_gain_db() const { + rtc::CritScope cs_capture(crit_capture_); + return agc_->compression_gain_db(); +} + +bool GainControlConfigProxy::is_enabled() const { + rtc::CritScope cs_capture(crit_capture_); + return agc_->is_enabled(); +} + +GainControl::Mode GainControlConfigProxy::mode() const { + rtc::CritScope cs_capture(crit_capture_); + return agc_->mode(); +} + +int GainControlConfigProxy::target_level_dbfs() const { + rtc::CritScope cs_capture(crit_capture_); + return agc_->target_level_dbfs(); +} + +int GainControlConfigProxy::analog_level_minimum() const { + rtc::CritScope cs_capture(crit_capture_); + return agc_->analog_level_minimum(); +} + +int GainControlConfigProxy::analog_level_maximum() const { + rtc::CritScope cs_capture(crit_capture_); + return agc_->analog_level_maximum(); +} + +bool GainControlConfigProxy::stream_is_saturated() const { + rtc::CritScope cs_capture(crit_capture_); + return agc_->stream_is_saturated(); +} +} // namespace webrtc diff --git a/modules/audio_processing/gain_control_config_proxy.h b/modules/audio_processing/gain_control_config_proxy.h new file mode 100644 index 0000000000..04ed5360dd --- /dev/null +++ b/modules/audio_processing/gain_control_config_proxy.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_AUDIO_PROCESSING_GAIN_CONTROL_CONFIG_PROXY_H_ +#define MODULES_AUDIO_PROCESSING_GAIN_CONTROL_CONFIG_PROXY_H_ + +#include "modules/audio_processing/include/audio_processing.h" +#include "modules/audio_processing/include/gain_control.h" +#include "rtc_base/critical_section.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +// This class forwards all gain control configuration to the audio processing +// module, for compatibility with AudioProcessing::Config. +class GainControlConfigProxy : public GainControl { + public: + GainControlConfigProxy(rtc::CriticalSection* crit_capture, + AudioProcessing* apm, + GainControl* agc); + GainControlConfigProxy(const GainControlConfigProxy&) = delete; + GainControlConfigProxy& operator=(const GainControlConfigProxy&) = delete; + + ~GainControlConfigProxy() override; + + private: + // GainControl API during processing. + int set_stream_analog_level(int level) override; + int stream_analog_level() const override; + + // GainControl config setters. + int Enable(bool enable) override; + int set_mode(Mode mode) override; + int set_target_level_dbfs(int level) override; + int set_compression_gain_db(int gain) override; + int enable_limiter(bool enable) override; + int set_analog_level_limits(int minimum, int maximum) override; + + // GainControl config getters. + bool is_enabled() const override; + bool is_limiter_enabled() const override; + int compression_gain_db() const override; + int target_level_dbfs() const override; + int analog_level_minimum() const override; + int analog_level_maximum() const override; + bool stream_is_saturated() const override; + Mode mode() const override; + + rtc::CriticalSection* crit_capture_ = nullptr; + AudioProcessing* apm_ = nullptr; + GainControl* agc_ RTC_GUARDED_BY(crit_capture_) = nullptr; +}; + +} // namespace webrtc +#endif // MODULES_AUDIO_PROCESSING_GAIN_CONTROL_CONFIG_PROXY_H_ diff --git a/modules/audio_processing/gain_control_config_proxy_unittest.cc b/modules/audio_processing/gain_control_config_proxy_unittest.cc new file mode 100644 index 0000000000..e5204e87cb --- /dev/null +++ b/modules/audio_processing/gain_control_config_proxy_unittest.cc @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_processing/gain_control_config_proxy.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "modules/audio_processing/include/mock_audio_processing.h" +#include "rtc_base/critical_section.h" +#include "rtc_base/ref_counted_object.h" +#include "test/gtest.h" + +namespace webrtc { +class GainControlConfigProxyTest : public testing::Test { + protected: + GainControlConfigProxyTest() + : apm_(new rtc::RefCountedObject< + testing::StrictMock>()), + agc_(), + proxy_(&lock_, apm_, &agc_) { + EXPECT_CALL(*apm_, GetConfig()) + .WillRepeatedly(testing::ReturnPointee(&apm_config_)); + EXPECT_CALL(*apm_, ApplyConfig(testing::_)) + .WillRepeatedly(testing::SaveArg<0>(&apm_config_)); + } + + GainControl* proxy() { return &proxy_; } + + rtc::scoped_refptr> apm_; + testing::StrictMock agc_; + AudioProcessing::Config apm_config_; + + private: + rtc::CriticalSection lock_; + GainControlConfigProxy proxy_; +}; + +// GainControl API during processing. +TEST_F(GainControlConfigProxyTest, SetStreamAnalogLevel) { + EXPECT_CALL(*apm_, set_stream_analog_level(100)); + proxy()->set_stream_analog_level(100); +} + +TEST_F(GainControlConfigProxyTest, StreamAnalogLevel) { + EXPECT_CALL(*apm_, recommended_stream_analog_level()) + .WillOnce(testing::Return(100)); + EXPECT_EQ(100, proxy()->stream_analog_level()); +} + +// GainControl config setters. +TEST_F(GainControlConfigProxyTest, SetEnable) { + proxy()->Enable(true); + EXPECT_TRUE(apm_config_.gain_controller1.enabled); + + proxy()->Enable(false); + EXPECT_FALSE(apm_config_.gain_controller1.enabled); +} + +TEST_F(GainControlConfigProxyTest, SetMode) { + proxy()->set_mode(GainControl::Mode::kAdaptiveAnalog); + EXPECT_EQ(apm_config_.gain_controller1.kAdaptiveAnalog, + apm_config_.gain_controller1.mode); + + proxy()->set_mode(GainControl::Mode::kAdaptiveDigital); + EXPECT_EQ(apm_config_.gain_controller1.kAdaptiveDigital, + apm_config_.gain_controller1.mode); + + proxy()->set_mode(GainControl::Mode::kFixedDigital); + EXPECT_EQ(apm_config_.gain_controller1.kFixedDigital, + apm_config_.gain_controller1.mode); +} + +TEST_F(GainControlConfigProxyTest, SetTargetLevelDbfs) { + proxy()->set_target_level_dbfs(17); + EXPECT_EQ(17, apm_config_.gain_controller1.target_level_dbfs); +} + +TEST_F(GainControlConfigProxyTest, SetCompressionGainDb) { + AudioProcessing::RuntimeSetting setting; + EXPECT_CALL(*apm_, SetRuntimeSetting(testing::_)) + .WillOnce(testing::SaveArg<0>(&setting)); + proxy()->set_compression_gain_db(17); + EXPECT_EQ(AudioProcessing::RuntimeSetting::Type::kCaptureCompressionGain, + setting.type()); + float value; + setting.GetFloat(&value); + EXPECT_EQ(17, static_cast(value + .5f)); +} + +TEST_F(GainControlConfigProxyTest, SetEnableLimiter) { + proxy()->enable_limiter(true); + EXPECT_TRUE(apm_config_.gain_controller1.enable_limiter); + proxy()->enable_limiter(false); + EXPECT_FALSE(apm_config_.gain_controller1.enable_limiter); +} + +TEST_F(GainControlConfigProxyTest, SetAnalogLevelLimits) { + proxy()->set_analog_level_limits(100, 300); + EXPECT_EQ(100, apm_config_.gain_controller1.analog_level_minimum); + EXPECT_EQ(300, apm_config_.gain_controller1.analog_level_maximum); +} + +TEST_F(GainControlConfigProxyTest, GetEnabled) { + EXPECT_CALL(agc_, is_enabled()) + .WillOnce(testing::Return(true)) + .WillOnce(testing::Return(false)); + EXPECT_TRUE(proxy()->is_enabled()); + EXPECT_FALSE(proxy()->is_enabled()); +} + +TEST_F(GainControlConfigProxyTest, GetLimiterEnabled) { + EXPECT_CALL(agc_, is_enabled()) + .WillOnce(testing::Return(true)) + .WillOnce(testing::Return(false)); + EXPECT_TRUE(proxy()->is_enabled()); + EXPECT_FALSE(proxy()->is_enabled()); +} + +TEST_F(GainControlConfigProxyTest, GetCompressionGainDb) { + EXPECT_CALL(agc_, compression_gain_db()).WillOnce(testing::Return(17)); + EXPECT_EQ(17, proxy()->compression_gain_db()); +} + +TEST_F(GainControlConfigProxyTest, GetTargetLevelDbfs) { + EXPECT_CALL(agc_, target_level_dbfs()).WillOnce(testing::Return(17)); + EXPECT_EQ(17, proxy()->target_level_dbfs()); +} + +TEST_F(GainControlConfigProxyTest, GetAnalogLevelMinimum) { + EXPECT_CALL(agc_, analog_level_minimum()).WillOnce(testing::Return(17)); + EXPECT_EQ(17, proxy()->analog_level_minimum()); +} + +TEST_F(GainControlConfigProxyTest, GetAnalogLevelMaximum) { + EXPECT_CALL(agc_, analog_level_maximum()).WillOnce(testing::Return(17)); + EXPECT_EQ(17, proxy()->analog_level_maximum()); +} + +TEST_F(GainControlConfigProxyTest, GetStreamIsSaturated) { + EXPECT_CALL(agc_, stream_is_saturated()) + .WillOnce(testing::Return(true)) + .WillOnce(testing::Return(false)); + EXPECT_TRUE(proxy()->stream_is_saturated()); + EXPECT_FALSE(proxy()->stream_is_saturated()); +} + +TEST_F(GainControlConfigProxyTest, GetMode) { + EXPECT_CALL(agc_, mode()) + .WillOnce(testing::Return(GainControl::Mode::kAdaptiveAnalog)) + .WillOnce(testing::Return(GainControl::Mode::kAdaptiveDigital)) + .WillOnce(testing::Return(GainControl::Mode::kFixedDigital)); + EXPECT_EQ(GainControl::Mode::kAdaptiveAnalog, proxy()->mode()); + EXPECT_EQ(GainControl::Mode::kAdaptiveDigital, proxy()->mode()); + EXPECT_EQ(GainControl::Mode::kFixedDigital, proxy()->mode()); +} + +} // namespace webrtc diff --git a/modules/audio_processing/gain_control_for_experimental_agc.cc b/modules/audio_processing/gain_control_for_experimental_agc.cc index 5cb22f882f..9e4d4f52d0 100644 --- a/modules/audio_processing/gain_control_for_experimental_agc.cc +++ b/modules/audio_processing/gain_control_for_experimental_agc.cc @@ -20,13 +20,11 @@ namespace webrtc { int GainControlForExperimentalAgc::instance_counter_ = 0; GainControlForExperimentalAgc::GainControlForExperimentalAgc( - GainControl* gain_control, - rtc::CriticalSection* crit_capture) + GainControl* gain_control) : data_dumper_( new ApmDataDumper(rtc::AtomicOps::Increment(&instance_counter_))), real_gain_control_(gain_control), - volume_(0), - crit_capture_(crit_capture) {} + volume_(0) {} GainControlForExperimentalAgc::~GainControlForExperimentalAgc() = default; @@ -39,7 +37,6 @@ bool GainControlForExperimentalAgc::is_enabled() const { } int GainControlForExperimentalAgc::set_stream_analog_level(int level) { - rtc::CritScope cs_capture(crit_capture_); data_dumper_->DumpRaw("experimental_gain_control_set_stream_analog_level", 1, &level); do_log_level_ = true; @@ -47,8 +44,7 @@ int GainControlForExperimentalAgc::set_stream_analog_level(int level) { return AudioProcessing::kNoError; } -int GainControlForExperimentalAgc::stream_analog_level() { - rtc::CritScope cs_capture(crit_capture_); +int GainControlForExperimentalAgc::stream_analog_level() const { if (do_log_level_) { data_dumper_->DumpRaw("experimental_gain_control_stream_analog_level", 1, &volume_); @@ -107,12 +103,10 @@ bool GainControlForExperimentalAgc::stream_is_saturated() const { } void GainControlForExperimentalAgc::SetMicVolume(int volume) { - rtc::CritScope cs_capture(crit_capture_); volume_ = volume; } int GainControlForExperimentalAgc::GetMicVolume() { - rtc::CritScope cs_capture(crit_capture_); return volume_; } diff --git a/modules/audio_processing/gain_control_for_experimental_agc.h b/modules/audio_processing/gain_control_for_experimental_agc.h index 8f5681f0ea..59328cd1e9 100644 --- a/modules/audio_processing/gain_control_for_experimental_agc.h +++ b/modules/audio_processing/gain_control_for_experimental_agc.h @@ -13,8 +13,6 @@ #include "modules/audio_processing/agc/agc_manager_direct.h" #include "modules/audio_processing/include/audio_processing.h" -#include "rtc_base/constructor_magic.h" -#include "rtc_base/critical_section.h" #include "rtc_base/thread_checker.h" namespace webrtc { @@ -35,15 +33,18 @@ class ApmDataDumper; class GainControlForExperimentalAgc : public GainControl, public VolumeCallbacks { public: - GainControlForExperimentalAgc(GainControl* gain_control, - rtc::CriticalSection* crit_capture); + explicit GainControlForExperimentalAgc(GainControl* gain_control); + GainControlForExperimentalAgc(const GainControlForExperimentalAgc&) = delete; + GainControlForExperimentalAgc& operator=( + const GainControlForExperimentalAgc&) = delete; + ~GainControlForExperimentalAgc() override; // GainControl implementation. int Enable(bool enable) override; bool is_enabled() const override; int set_stream_analog_level(int level) override; - int stream_analog_level() override; + int stream_analog_level() const override; int set_mode(Mode mode) override; Mode mode() const override; int set_target_level_dbfs(int level) override; @@ -67,10 +68,8 @@ class GainControlForExperimentalAgc : public GainControl, std::unique_ptr data_dumper_; GainControl* real_gain_control_; int volume_; - rtc::CriticalSection* crit_capture_; - bool do_log_level_ = true; + mutable bool do_log_level_ = true; static int instance_counter_; - RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(GainControlForExperimentalAgc); }; } // namespace webrtc diff --git a/modules/audio_processing/gain_control_impl.cc b/modules/audio_processing/gain_control_impl.cc index cd21e4c93e..47cbe52094 100644 --- a/modules/audio_processing/gain_control_impl.cc +++ b/modules/audio_processing/gain_control_impl.cc @@ -89,9 +89,8 @@ class GainControlImpl::GainController { int GainControlImpl::instance_counter_ = 0; -GainControlImpl::GainControlImpl(rtc::CriticalSection* crit_capture) - : crit_capture_(crit_capture), - data_dumper_(new ApmDataDumper(instance_counter_)), +GainControlImpl::GainControlImpl() + : data_dumper_(new ApmDataDumper(instance_counter_)), mode_(kAdaptiveAnalog), minimum_capture_level_(0), maximum_capture_level_(255), @@ -100,15 +99,12 @@ GainControlImpl::GainControlImpl(rtc::CriticalSection* crit_capture) compression_gain_db_(9), analog_capture_level_(0), was_analog_level_set_(false), - stream_is_saturated_(false) { - RTC_DCHECK(crit_capture); -} + stream_is_saturated_(false) {} GainControlImpl::~GainControlImpl() {} void GainControlImpl::ProcessRenderAudio( rtc::ArrayView packed_render_audio) { - rtc::CritScope cs_capture(crit_capture_); if (!enabled_) { return; } @@ -131,8 +127,6 @@ void GainControlImpl::PackRenderAudioBuffer( } int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { - rtc::CritScope cs(crit_capture_); - if (!enabled_) { return AudioProcessing::kNoError; } @@ -178,8 +172,6 @@ int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) { int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio, bool stream_has_echo) { - rtc::CritScope cs(crit_capture_); - if (!enabled_) { return AudioProcessing::kNoError; } @@ -235,13 +227,11 @@ int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio, } int GainControlImpl::compression_gain_db() const { - rtc::CritScope cs(crit_capture_); return compression_gain_db_; } // TODO(ajm): ensure this is called under kAdaptiveAnalog. int GainControlImpl::set_stream_analog_level(int level) { - rtc::CritScope cs(crit_capture_); data_dumper_->DumpRaw("gain_control_set_stream_analog_level", 1, &level); was_analog_level_set_ = true; @@ -253,8 +243,7 @@ int GainControlImpl::set_stream_analog_level(int level) { return AudioProcessing::kNoError; } -int GainControlImpl::stream_analog_level() { - rtc::CritScope cs(crit_capture_); +int GainControlImpl::stream_analog_level() const { data_dumper_->DumpRaw("gain_control_stream_analog_level", 1, &analog_capture_level_); // TODO(ajm): enable this assertion? @@ -264,7 +253,6 @@ int GainControlImpl::stream_analog_level() { } int GainControlImpl::Enable(bool enable) { - rtc::CritScope cs_capture(crit_capture_); if (enable && !enabled_) { enabled_ = enable; // Must be set before Initialize() is called. @@ -278,12 +266,10 @@ int GainControlImpl::Enable(bool enable) { } bool GainControlImpl::is_enabled() const { - rtc::CritScope cs(crit_capture_); return enabled_; } int GainControlImpl::set_mode(Mode mode) { - rtc::CritScope cs_capture(crit_capture_); if (MapSetting(mode) == -1) { return AudioProcessing::kBadParameterError; } @@ -296,7 +282,6 @@ int GainControlImpl::set_mode(Mode mode) { } GainControl::Mode GainControlImpl::mode() const { - rtc::CritScope cs(crit_capture_); return mode_; } @@ -316,7 +301,6 @@ int GainControlImpl::set_analog_level_limits(int minimum, int maximum) { size_t num_proc_channels_local = 0u; int sample_rate_hz_local = 0; { - rtc::CritScope cs(crit_capture_); minimum_capture_level_ = minimum; maximum_capture_level_ = maximum; @@ -331,17 +315,14 @@ int GainControlImpl::set_analog_level_limits(int minimum, int maximum) { } int GainControlImpl::analog_level_minimum() const { - rtc::CritScope cs(crit_capture_); return minimum_capture_level_; } int GainControlImpl::analog_level_maximum() const { - rtc::CritScope cs(crit_capture_); return maximum_capture_level_; } bool GainControlImpl::stream_is_saturated() const { - rtc::CritScope cs(crit_capture_); return stream_is_saturated_; } @@ -349,13 +330,11 @@ int GainControlImpl::set_target_level_dbfs(int level) { if (level > 31 || level < 0) { return AudioProcessing::kBadParameterError; } - rtc::CritScope cs(crit_capture_); target_level_dbfs_ = level; return Configure(); } int GainControlImpl::target_level_dbfs() const { - rtc::CritScope cs(crit_capture_); return target_level_dbfs_; } @@ -363,24 +342,20 @@ int GainControlImpl::set_compression_gain_db(int gain) { if (gain < 0 || gain > 90) { return AudioProcessing::kBadParameterError; } - rtc::CritScope cs(crit_capture_); compression_gain_db_ = gain; return Configure(); } int GainControlImpl::enable_limiter(bool enable) { - rtc::CritScope cs(crit_capture_); limiter_enabled_ = enable; return Configure(); } bool GainControlImpl::is_limiter_enabled() const { - rtc::CritScope cs(crit_capture_); return limiter_enabled_; } void GainControlImpl::Initialize(size_t num_proc_channels, int sample_rate_hz) { - rtc::CritScope cs_capture(crit_capture_); data_dumper_->InitiateNewSetOfRecordings(); num_proc_channels_ = num_proc_channels; diff --git a/modules/audio_processing/gain_control_impl.h b/modules/audio_processing/gain_control_impl.h index 9dfe0f1ffd..36b84eed81 100644 --- a/modules/audio_processing/gain_control_impl.h +++ b/modules/audio_processing/gain_control_impl.h @@ -20,8 +20,6 @@ #include "api/array_view.h" #include "modules/audio_processing/include/gain_control.h" #include "rtc_base/constructor_magic.h" -#include "rtc_base/critical_section.h" -#include "rtc_base/thread_annotations.h" namespace webrtc { @@ -30,7 +28,10 @@ class AudioBuffer; class GainControlImpl : public GainControl { public: - explicit GainControlImpl(rtc::CriticalSection* crit_capture); + GainControlImpl(); + GainControlImpl(const GainControlImpl&) = delete; + GainControlImpl& operator=(const GainControlImpl&) = delete; + ~GainControlImpl() override; void ProcessRenderAudio(rtc::ArrayView packed_render_audio); @@ -44,7 +45,7 @@ class GainControlImpl : public GainControl { // GainControl implementation. bool is_enabled() const override; - int stream_analog_level() override; + int stream_analog_level() const override; bool is_limiter_enabled() const override; Mode mode() const override; @@ -66,31 +67,28 @@ class GainControlImpl : public GainControl { int analog_level_maximum() const override; bool stream_is_saturated() const override; - int Configure() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_); - - rtc::CriticalSection* const crit_capture_; + int Configure(); std::unique_ptr data_dumper_; bool enabled_ = false; - Mode mode_ RTC_GUARDED_BY(crit_capture_); - int minimum_capture_level_ RTC_GUARDED_BY(crit_capture_); - int maximum_capture_level_ RTC_GUARDED_BY(crit_capture_); - bool limiter_enabled_ RTC_GUARDED_BY(crit_capture_); - int target_level_dbfs_ RTC_GUARDED_BY(crit_capture_); - int compression_gain_db_ RTC_GUARDED_BY(crit_capture_); - int analog_capture_level_ RTC_GUARDED_BY(crit_capture_); - bool was_analog_level_set_ RTC_GUARDED_BY(crit_capture_); - bool stream_is_saturated_ RTC_GUARDED_BY(crit_capture_); + Mode mode_; + int minimum_capture_level_; + int maximum_capture_level_; + bool limiter_enabled_; + int target_level_dbfs_; + int compression_gain_db_; + int analog_capture_level_; + bool was_analog_level_set_; + bool stream_is_saturated_; std::vector> gain_controllers_; - absl::optional num_proc_channels_ RTC_GUARDED_BY(crit_capture_); - absl::optional sample_rate_hz_ RTC_GUARDED_BY(crit_capture_); + absl::optional num_proc_channels_; + absl::optional sample_rate_hz_; static int instance_counter_; - RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(GainControlImpl); }; } // namespace webrtc diff --git a/modules/audio_processing/gain_control_unittest.cc b/modules/audio_processing/gain_control_unittest.cc index 891e78a394..e249a11cad 100644 --- a/modules/audio_processing/gain_control_unittest.cc +++ b/modules/audio_processing/gain_control_unittest.cc @@ -72,8 +72,7 @@ void RunBitExactnessTest(int sample_rate_hz, int analog_level_max, int achieved_stream_analog_level_reference, rtc::ArrayView output_reference) { - rtc::CriticalSection crit_capture; - GainControlImpl gain_controller(&crit_capture); + GainControlImpl gain_controller; SetupComponent(sample_rate_hz, mode, target_level_dbfs, stream_analog_level, compression_gain_db, enable_limiter, analog_level_min, analog_level_max, &gain_controller); diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h index 6a0917a8f0..1d421f57f3 100644 --- a/modules/audio_processing/include/audio_processing.h +++ b/modules/audio_processing/include/audio_processing.h @@ -239,6 +239,11 @@ class AudioProcessing : public rtc::RefCountInterface { // The parameters and behavior of the audio processing module are controlled // by changing the default values in the AudioProcessing::Config struct. // The config is applied by passing the struct to the ApplyConfig method. + // + // This config is intended to be used during setup, and to enable/disable + // top-level processing effects. Use during processing may cause undesired + // submodule resets, affecting the audio quality. Use the RuntimeSetting + // construct for runtime configuration. struct Config { // Enabled the pre-amplifier. It amplifies the capture signal // before any other processing is done. @@ -273,6 +278,59 @@ class AudioProcessing : public rtc::RefCountInterface { bool enabled = false; } voice_detection; + // Enables automatic gain control (AGC) functionality. + // The automatic gain control (AGC) component brings the signal to an + // appropriate range. This is done by applying a digital gain directly and, + // in the analog mode, prescribing an analog gain to be applied at the audio + // HAL. + // Recommended to be enabled on the client-side. + struct GainController1 { + bool enabled = false; + enum Mode { + // Adaptive mode intended for use if an analog volume control is + // available on the capture device. It will require the user to provide + // coupling between the OS mixer controls and AGC through the + // stream_analog_level() functions. + // It consists of an analog gain prescription for the audio device and a + // digital compression stage. + kAdaptiveAnalog, + // Adaptive mode intended for situations in which an analog volume + // control is unavailable. It operates in a similar fashion to the + // adaptive analog mode, but with scaling instead applied in the digital + // domain. As with the analog mode, it additionally uses a digital + // compression stage. + kAdaptiveDigital, + // Fixed mode which enables only the digital compression stage also used + // by the two adaptive modes. + // It is distinguished from the adaptive modes by considering only a + // short time-window of the input signal. It applies a fixed gain + // through most of the input level range, and compresses (gradually + // reduces gain with increasing level) the input signal at higher + // levels. This mode is preferred on embedded devices where the capture + // signal level is predictable, so that a known gain can be applied. + kFixedDigital + }; + Mode mode = kAdaptiveAnalog; + // Sets the target peak level (or envelope) of the AGC in dBFs (decibels + // from digital full-scale). The convention is to use positive values. For + // instance, passing in a value of 3 corresponds to -3 dBFs, or a target + // level 3 dB below full-scale. Limited to [0, 31]. + int target_level_dbfs = 3; + // Sets the maximum gain the digital compression stage may apply, in dB. A + // higher number corresponds to greater compression, while a value of 0 + // will leave the signal uncompressed. Limited to [0, 90]. + // For updates after APM setup, use a RuntimeSetting instead. + int compression_gain_db = 9; + // When enabled, the compression stage will hard limit the signal to the + // target level. Otherwise, the signal will be compressed but not limited + // above the target level. + bool enable_limiter = true; + // Sets the minimum and maximum analog levels of the audio capture device. + // Must be set if an analog mode is used. Limited to [0, 65535]. + int analog_level_minimum = 0; + int analog_level_maximum = 255; + } gain_controller1; + // Enables the next generation AGC functionality. This feature replaces the // standard methods of gain control in the previous AGC. Enabling this // submodule enables an adaptive digital AGC followed by a limiter. By @@ -332,6 +390,7 @@ class AudioProcessing : public rtc::RefCountInterface { enum class Type { kNotSpecified, kCapturePreGain, + kCaptureCompressionGain, kCustomRenderProcessingRuntimeSetting }; @@ -343,6 +402,14 @@ class AudioProcessing : public rtc::RefCountInterface { return {Type::kCapturePreGain, gain}; } + // Corresponds to Config::GainController1::compression_gain_db, but for + // runtime configuration. + static RuntimeSetting CreateCompressionGainDb(int gain_db) { + RTC_DCHECK_GE(gain_db, 0); + RTC_DCHECK_LE(gain_db, 90); + return {Type::kCaptureCompressionGain, static_cast(gain_db)}; + } + static RuntimeSetting CreateCustomRenderSetting(float payload) { return {Type::kCustomRenderProcessingRuntimeSetting, payload}; } @@ -489,6 +556,16 @@ class AudioProcessing : public rtc::RefCountInterface { const StreamConfig& output_config, float* const* dest) = 0; + // This must be called prior to ProcessStream() if and only if adaptive analog + // gain control is enabled, to pass the current analog level from the audio + // HAL. Must be within the range provided in Config::GainController1. + virtual void set_stream_analog_level(int level) = 0; + + // When an analog mode is set, this should be called after ProcessStream() + // to obtain the recommended new analog level for the audio HAL. It is the + // user's responsibility to apply this level. + virtual int recommended_stream_analog_level() const = 0; + // This must be called if and only if echo processing is enabled. // // Sets the |delay| in ms between ProcessReverseStream() receiving a far-end @@ -553,6 +630,12 @@ class AudioProcessing : public rtc::RefCountInterface { // remote track. virtual AudioProcessingStats GetStatistics(bool has_remote_tracks) const = 0; + // DEPRECATED. + // TODO(https://crbug.com/webrtc/9878): Remove. + // Configure via AudioProcessing::ApplyConfig during setup. + // Set runtime settings via AudioProcessing::SetRuntimeSetting. + // Get stats via AudioProcessing::GetStatistics. + // // These provide access to the component interfaces and should never return // NULL. The pointers will be valid for the lifetime of the APM instance. // The memory for these objects is entirely managed internally. diff --git a/modules/audio_processing/include/gain_control.h b/modules/audio_processing/include/gain_control.h index 420b1c6e38..69208a760f 100644 --- a/modules/audio_processing/include/gain_control.h +++ b/modules/audio_processing/include/gain_control.h @@ -31,7 +31,7 @@ class GainControl { // When an analog mode is set, this should be called after |ProcessStream()| // to obtain the recommended new analog level for the audio HAL. It is the // users responsibility to apply this level. - virtual int stream_analog_level() = 0; + virtual int stream_analog_level() const = 0; enum Mode { // Adaptive mode intended for use if an analog volume control is available diff --git a/modules/audio_processing/include/mock_audio_processing.h b/modules/audio_processing/include/mock_audio_processing.h index f00a16d5e2..7504b5186c 100644 --- a/modules/audio_processing/include/mock_audio_processing.h +++ b/modules/audio_processing/include/mock_audio_processing.h @@ -27,7 +27,7 @@ class MockGainControl : public GainControl { MOCK_METHOD1(Enable, int(bool enable)); MOCK_CONST_METHOD0(is_enabled, bool()); MOCK_METHOD1(set_stream_analog_level, int(int level)); - MOCK_METHOD0(stream_analog_level, int()); + MOCK_CONST_METHOD0(stream_analog_level, int()); MOCK_METHOD1(set_mode, int(Mode mode)); MOCK_CONST_METHOD0(mode, Mode()); MOCK_METHOD1(set_target_level_dbfs, int(int level)); @@ -163,6 +163,8 @@ class MockAudioProcessing : public testing::NiceMock { MOCK_METHOD1(set_stream_key_pressed, void(bool key_pressed)); MOCK_METHOD1(set_delay_offset_ms, void(int offset)); MOCK_CONST_METHOD0(delay_offset_ms, int()); + MOCK_METHOD1(set_stream_analog_level, void(int)); + MOCK_CONST_METHOD0(recommended_stream_analog_level, int()); virtual void AttachAecDump(std::unique_ptr aec_dump) {} MOCK_METHOD0(DetachAecDump, void()); diff --git a/test/fuzzers/agc_fuzzer.cc b/test/fuzzers/agc_fuzzer.cc index 9854a78ddb..a330c7b785 100644 --- a/test/fuzzers/agc_fuzzer.cc +++ b/test/fuzzers/agc_fuzzer.cc @@ -113,8 +113,7 @@ void FuzzOneInput(const uint8_t* data, size_t size) { return; } test::FuzzDataHelper fuzz_data(rtc::ArrayView(data, size)); - rtc::CriticalSection crit_capture; - auto gci = absl::make_unique(&crit_capture); + auto gci = absl::make_unique(); FuzzGainController(&fuzz_data, gci.get()); } } // namespace webrtc diff --git a/test/fuzzers/audio_processing_configs_fuzzer.cc b/test/fuzzers/audio_processing_configs_fuzzer.cc index cf41da2e43..89c0552aab 100644 --- a/test/fuzzers/audio_processing_configs_fuzzer.cc +++ b/test/fuzzers/audio_processing_configs_fuzzer.cc @@ -125,6 +125,8 @@ std::unique_ptr CreateApm(test::FuzzDataHelper* fuzz_data, apm_config.echo_canceller.mobile_mode = use_aecm; apm_config.residual_echo_detector.enabled = red; apm_config.high_pass_filter.enabled = hpf; + apm_config.gain_controller1.enabled = use_agc; + apm_config.gain_controller1.enable_limiter = use_agc_limiter; apm_config.gain_controller2.enabled = use_agc2; apm_config.gain_controller2.fixed_digital.gain_db = gain_controller2_gain_db; apm_config.gain_controller2.adaptive_digital.enabled = @@ -141,10 +143,8 @@ std::unique_ptr CreateApm(test::FuzzDataHelper* fuzz_data, apm_config.voice_detection.enabled = use_vad; apm->ApplyConfig(apm_config); - apm->gain_control()->Enable(use_agc); apm->level_estimator()->Enable(use_le); apm->voice_detection()->Enable(use_vad); - apm->gain_control()->enable_limiter(use_agc_limiter); return apm; }