From bd681b97582b0e1c4afe93b67e8ce84f3bad7dc4 Mon Sep 17 00:00:00 2001 From: "henrik.lundin" Date: Mon, 5 Dec 2016 09:08:42 -0800 Subject: [PATCH] AGC: Route clipping parameter from webrtc::Config to AGC This change enables experimentation with the clipping minimum level parameter in the gain control. BUG=webrtc:6622 CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_chromium_rel_ng Review-Url: https://codereview.webrtc.org/2543753006 Cr-Commit-Position: refs/heads/master@{#15426} --- .../agc/agc_manager_direct.cc | 34 ++++++++++--------- .../audio_processing/agc/agc_manager_direct.h | 7 ++-- .../agc/agc_manager_direct_unittest.cc | 10 +++--- .../audio_processing/audio_processing_impl.cc | 6 +++- .../audio_processing/audio_processing_impl.h | 6 +++- .../include/audio_processing.h | 16 ++++++--- .../audio_processing/test/debug_dump_test.cc | 31 +++++++++++++++++ 7 files changed, 81 insertions(+), 29 deletions(-) diff --git a/webrtc/modules/audio_processing/agc/agc_manager_direct.cc b/webrtc/modules/audio_processing/agc/agc_manager_direct.cc index f8fc310c57..70a0e1f2b2 100644 --- a/webrtc/modules/audio_processing/agc/agc_manager_direct.cc +++ b/webrtc/modules/audio_processing/agc/agc_manager_direct.cc @@ -27,8 +27,6 @@ namespace webrtc { namespace { -// Lowest the microphone level can be lowered due to clipping. -const int kClippedLevelMin = 170; // Amount the microphone level is lowered with every clipping event. const int kClippedLevelStep = 15; // Proportion of clipped samples required to declare a clipping event. @@ -115,7 +113,8 @@ class DebugFile { AgcManagerDirect::AgcManagerDirect(GainControl* gctrl, VolumeCallbacks* volume_callbacks, - int startup_min_level) + int startup_min_level, + int clipped_level_min) : agc_(new Agc()), gctrl_(gctrl), volume_callbacks_(volume_callbacks), @@ -130,14 +129,15 @@ AgcManagerDirect::AgcManagerDirect(GainControl* gctrl, check_volume_on_next_process_(true), // Check at startup. startup_(true), startup_min_level_(ClampLevel(startup_min_level)), + clipped_level_min_(clipped_level_min), file_preproc_(new DebugFile("agc_preproc.pcm")), - file_postproc_(new DebugFile("agc_postproc.pcm")) { -} + file_postproc_(new DebugFile("agc_postproc.pcm")) {} AgcManagerDirect::AgcManagerDirect(Agc* agc, GainControl* gctrl, VolumeCallbacks* volume_callbacks, - int startup_min_level) + int startup_min_level, + int clipped_level_min) : agc_(agc), gctrl_(gctrl), volume_callbacks_(volume_callbacks), @@ -152,9 +152,9 @@ AgcManagerDirect::AgcManagerDirect(Agc* agc, check_volume_on_next_process_(true), // Check at startup. startup_(true), startup_min_level_(ClampLevel(startup_min_level)), + clipped_level_min_(clipped_level_min), file_preproc_(new DebugFile("agc_preproc.pcm")), - file_postproc_(new DebugFile("agc_postproc.pcm")) { -} + file_postproc_(new DebugFile("agc_postproc.pcm")) {} AgcManagerDirect::~AgcManagerDirect() {} @@ -218,14 +218,14 @@ void AgcManagerDirect::AnalyzePreProcess(int16_t* audio, << clipped_ratio; // Always decrease the maximum level, even if the current level is below // threshold. - SetMaxLevel(std::max(kClippedLevelMin, max_level_ - kClippedLevelStep)); + SetMaxLevel(std::max(clipped_level_min_, max_level_ - kClippedLevelStep)); RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed", - level_ - kClippedLevelStep >= kClippedLevelMin); - if (level_ > kClippedLevelMin) { + level_ - kClippedLevelStep >= clipped_level_min_); + if (level_ > clipped_level_min_) { // Don't try to adjust the level if we're already below the limit. As // a consequence, if the user has brought the level above the limit, we // will still not react until the postproc updates the level. - SetLevel(std::max(kClippedLevelMin, level_ - kClippedLevelStep)); + SetLevel(std::max(clipped_level_min_, level_ - kClippedLevelStep)); // Reset the AGC since the level has changed. agc_->Reset(); } @@ -301,13 +301,15 @@ void AgcManagerDirect::SetLevel(int new_level) { } void AgcManagerDirect::SetMaxLevel(int level) { - RTC_DCHECK_GE(level, kClippedLevelMin); + RTC_DCHECK_GE(level, clipped_level_min_); max_level_ = level; // Scale the |kSurplusCompressionGain| linearly across the restricted // level range. - max_compression_gain_ = kMaxCompressionGain + std::floor( - (1.f * kMaxMicLevel - max_level_) / (kMaxMicLevel - kClippedLevelMin) * - kSurplusCompressionGain + 0.5f); + max_compression_gain_ = + kMaxCompressionGain + std::floor((1.f * kMaxMicLevel - max_level_) / + (kMaxMicLevel - clipped_level_min_) * + kSurplusCompressionGain + + 0.5f); LOG(LS_INFO) << "[agc] max_level_=" << max_level_ << ", max_compression_gain_=" << max_compression_gain_; } diff --git a/webrtc/modules/audio_processing/agc/agc_manager_direct.h b/webrtc/modules/audio_processing/agc/agc_manager_direct.h index 6b16e8b776..578a0f38f7 100644 --- a/webrtc/modules/audio_processing/agc/agc_manager_direct.h +++ b/webrtc/modules/audio_processing/agc/agc_manager_direct.h @@ -46,13 +46,15 @@ class AgcManagerDirect final { // outside that range will be clamped. AgcManagerDirect(GainControl* gctrl, VolumeCallbacks* volume_callbacks, - int startup_min_level); + int startup_min_level, + int clipped_level_min); // Dependency injection for testing. Don't delete |agc| as the memory is owned // by the manager. AgcManagerDirect(Agc* agc, GainControl* gctrl, VolumeCallbacks* volume_callbacks, - int startup_min_level); + int startup_min_level, + int clipped_level_min); ~AgcManagerDirect(); int Initialize(); @@ -98,6 +100,7 @@ class AgcManagerDirect final { bool check_volume_on_next_process_; bool startup_; int startup_min_level_; + const int clipped_level_min_; std::unique_ptr file_preproc_; std::unique_ptr file_postproc_; diff --git a/webrtc/modules/audio_processing/agc/agc_manager_direct_unittest.cc b/webrtc/modules/audio_processing/agc/agc_manager_direct_unittest.cc index 0a3ba84e59..b582ae5669 100644 --- a/webrtc/modules/audio_processing/agc/agc_manager_direct_unittest.cc +++ b/webrtc/modules/audio_processing/agc/agc_manager_direct_unittest.cc @@ -33,6 +33,7 @@ const int kSampleRateHz = 32000; const int kNumChannels = 1; const int kSamplesPerChannel = kSampleRateHz / 100; const int kInitialVolume = 128; +constexpr int kClippedMin = 165; // Arbitrary, but different from the default. const float kAboveClippedThreshold = 0.2f; class TestVolumeCallbacks : public VolumeCallbacks { @@ -50,7 +51,8 @@ class TestVolumeCallbacks : public VolumeCallbacks { class AgcManagerDirectTest : public ::testing::Test { protected: AgcManagerDirectTest() - : agc_(new MockAgc), manager_(agc_, &gctrl_, &volume_, kInitialVolume) { + : agc_(new MockAgc), + manager_(agc_, &gctrl_, &volume_, kInitialVolume, kClippedMin) { ExpectInitialize(); manager_.Initialize(); } @@ -505,13 +507,13 @@ TEST_F(AgcManagerDirectTest, ClippingLoweringIsLimited) { .WillOnce(Return(kAboveClippedThreshold)); EXPECT_CALL(*agc_, Reset()).Times(1); CallPreProc(1); - EXPECT_EQ(170, volume_.GetMicVolume()); + EXPECT_EQ(kClippedMin, volume_.GetMicVolume()); EXPECT_CALL(*agc_, AnalyzePreproc(_, _)) .WillRepeatedly(Return(kAboveClippedThreshold)); EXPECT_CALL(*agc_, Reset()).Times(0); CallPreProc(1000); - EXPECT_EQ(170, volume_.GetMicVolume()); + EXPECT_EQ(kClippedMin, volume_.GetMicVolume()); } TEST_F(AgcManagerDirectTest, ClippingMaxIsRespectedWhenEqualToLevel) { @@ -589,7 +591,7 @@ TEST_F(AgcManagerDirectTest, MaxCompressionIsIncreasedAfterClipping) { .WillOnce(Return(kAboveClippedThreshold)); EXPECT_CALL(*agc_, Reset()).Times(1); CallPreProc(1); - EXPECT_EQ(170, volume_.GetMicVolume()); + EXPECT_EQ(kClippedMin, volume_.GetMicVolume()); // Current level is now at the minimum, but the maximum allowed level still // has more to decrease. diff --git a/webrtc/modules/audio_processing/audio_processing_impl.cc b/webrtc/modules/audio_processing/audio_processing_impl.cc index 2379cd1be8..a490b0a9e0 100644 --- a/webrtc/modules/audio_processing/audio_processing_impl.cc +++ b/webrtc/modules/audio_processing/audio_processing_impl.cc @@ -302,6 +302,7 @@ AudioProcessingImpl::AudioProcessingImpl(const webrtc::Config& config, public_submodules_(new ApmPublicSubmodules()), private_submodules_(new ApmPrivateSubmodules(beamformer)), constants_(config.Get().startup_min_volume, + config.Get().clipped_level_min, #if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) false), #else @@ -488,7 +489,7 @@ int AudioProcessingImpl::InitializeLocked() { private_submodules_->agc_manager.reset(new AgcManagerDirect( public_submodules_->gain_control.get(), public_submodules_->gain_control_for_experimental_agc.get(), - constants_.agc_startup_min_volume)); + constants_.agc_startup_min_volume, constants_.agc_clipped_level_min)); } private_submodules_->agc_manager->Initialize(); private_submodules_->agc_manager->SetCaptureMuted( @@ -1853,6 +1854,9 @@ int AudioProcessingImpl::WriteConfigMessage(bool forced) { if (capture_nonlocked_.level_controller_enabled) { experiments_description += "LevelController;"; } + if (constants_.agc_clipped_level_min != kClippedLevelMin) { + experiments_description += "AgcClippingLevelExperiment;"; + } config.set_experiments_description(experiments_description); std::string serialized_config = config.SerializeAsString(); diff --git a/webrtc/modules/audio_processing/audio_processing_impl.h b/webrtc/modules/audio_processing/audio_processing_impl.h index 4db2272c81..0375a25328 100644 --- a/webrtc/modules/audio_processing/audio_processing_impl.h +++ b/webrtc/modules/audio_processing/audio_processing_impl.h @@ -329,11 +329,15 @@ class AudioProcessingImpl : public AudioProcessing { // APM constants. const struct ApmConstants { - ApmConstants(int agc_startup_min_volume, bool use_experimental_agc) + ApmConstants(int agc_startup_min_volume, + int agc_clipped_level_min, + bool use_experimental_agc) : // Format of processing streams at input/output call sites. agc_startup_min_volume(agc_startup_min_volume), + agc_clipped_level_min(agc_clipped_level_min), use_experimental_agc(use_experimental_agc) {} int agc_startup_min_volume; + int agc_clipped_level_min; bool use_experimental_agc; } constants_; diff --git a/webrtc/modules/audio_processing/include/audio_processing.h b/webrtc/modules/audio_processing/include/audio_processing.h index d3ae3e8a6a..d03c7f4ab4 100644 --- a/webrtc/modules/audio_processing/include/audio_processing.h +++ b/webrtc/modules/audio_processing/include/audio_processing.h @@ -114,15 +114,21 @@ static const int kAgcStartupMinVolume = 85; #else static const int kAgcStartupMinVolume = 0; #endif // defined(WEBRTC_CHROMIUM_BUILD) +static constexpr int kClippedLevelMin = 170; struct ExperimentalAgc { - ExperimentalAgc() : enabled(true), startup_min_volume(kAgcStartupMinVolume) {} - explicit ExperimentalAgc(bool enabled) - : enabled(enabled), startup_min_volume(kAgcStartupMinVolume) {} + ExperimentalAgc() = default; + explicit ExperimentalAgc(bool enabled) : enabled(enabled) {} ExperimentalAgc(bool enabled, int startup_min_volume) : enabled(enabled), startup_min_volume(startup_min_volume) {} + ExperimentalAgc(bool enabled, int startup_min_volume, int clipped_level_min) + : enabled(enabled), + startup_min_volume(startup_min_volume), + clipped_level_min(clipped_level_min) {} static const ConfigOptionID identifier = ConfigOptionID::kExperimentalAgc; - bool enabled; - int startup_min_volume; + bool enabled = true; + int startup_min_volume = kAgcStartupMinVolume; + // Lowest microphone level that will be applied in response to clipping. + int clipped_level_min = kClippedLevelMin; }; // Use to enable experimental noise suppression. It can be set in the diff --git a/webrtc/modules/audio_processing/test/debug_dump_test.cc b/webrtc/modules/audio_processing/test/debug_dump_test.cc index ffaf60984e..ebf899f82a 100644 --- a/webrtc/modules/audio_processing/test/debug_dump_test.cc +++ b/webrtc/modules/audio_processing/test/debug_dump_test.cc @@ -379,6 +379,8 @@ TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) { Config config; config.Set(new RefinedAdaptiveFilter(true)); config.Set(new EchoCanceller3(true)); + // Arbitrarily set clipping gain to 17, which will never be the default. + config.Set(new ExperimentalAgc(true, 0, 17)); DebugDumpGenerator generator(config, AudioProcessing::Config()); generator.StartRecording(); generator.Process(100); @@ -398,6 +400,8 @@ TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) { msg->experiments_description().c_str()); EXPECT_PRED_FORMAT2(testing::IsSubstring, "AEC3", msg->experiments_description().c_str()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "AgcClippingLevelExperiment", + msg->experiments_description().c_str()); } } } @@ -424,6 +428,8 @@ TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringExclusive) { msg->experiments_description().c_str()); EXPECT_PRED_FORMAT2(testing::IsNotSubstring, "AEC3", msg->experiments_description().c_str()); + EXPECT_PRED_FORMAT2(testing::IsNotSubstring, "AgcClippingLevelExperiment", + msg->experiments_description().c_str()); } } } @@ -477,6 +483,31 @@ TEST_F(DebugDumpTest, VerifyLevelControllerExperimentalString) { } } +TEST_F(DebugDumpTest, VerifyAgcClippingLevelExperimentalString) { + Config config; + // Arbitrarily set clipping gain to 17, which will never be the default. + config.Set(new ExperimentalAgc(true, 0, 17)); + DebugDumpGenerator generator(config, AudioProcessing::Config()); + generator.StartRecording(); + generator.Process(100); + generator.StopRecording(); + + DebugDumpReplayer debug_dump_replayer_; + + ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name())); + + while (const rtc::Optional event = + debug_dump_replayer_.GetNextEvent()) { + debug_dump_replayer_.RunNextEvent(); + if (event->type() == audioproc::Event::CONFIG) { + const audioproc::Config* msg = &event->config(); + ASSERT_TRUE(msg->has_experiments_description()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "AgcClippingLevelExperiment", + msg->experiments_description().c_str()); + } + } +} + TEST_F(DebugDumpTest, VerifyEmptyExperimentalString) { Config config; DebugDumpGenerator generator(config, AudioProcessing::Config());