Make AgcManagerDirect clipping parameters configurable
Bug: webrtc:12774
Change-Id: I99824b5aabe6f921a5db425dd1c1c1d4c606186c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/219681
Commit-Queue: Hanna Silen <silen@webrtc.org>
Reviewed-by: Alessio Bazzica <alessiob@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34069}
diff --git a/modules/audio_processing/agc/agc_manager_direct.cc b/modules/audio_processing/agc/agc_manager_direct.cc
index 2454d1b..ebd978b 100644
--- a/modules/audio_processing/agc/agc_manager_direct.cc
+++ b/modules/audio_processing/agc/agc_manager_direct.cc
@@ -27,33 +27,26 @@
namespace {
-// Amount the microphone level is lowered with every clipping event.
-const int kClippedLevelStep = 15;
-// Proportion of clipped samples required to declare a clipping event.
-const float kClippedRatioThreshold = 0.1f;
-// Time in frames to wait after a clipping event before checking again.
-const int kClippedWaitFrames = 300;
-
// Amount of error we tolerate in the microphone level (presumably due to OS
// quantization) before we assume the user has manually adjusted the microphone.
-const int kLevelQuantizationSlack = 25;
+constexpr int kLevelQuantizationSlack = 25;
-const int kDefaultCompressionGain = 7;
-const int kMaxCompressionGain = 12;
-const int kMinCompressionGain = 2;
+constexpr int kDefaultCompressionGain = 7;
+constexpr int kMaxCompressionGain = 12;
+constexpr int kMinCompressionGain = 2;
// Controls the rate of compression changes towards the target.
-const float kCompressionGainStep = 0.05f;
+constexpr float kCompressionGainStep = 0.05f;
-const int kMaxMicLevel = 255;
+constexpr int kMaxMicLevel = 255;
static_assert(kGainMapSize > kMaxMicLevel, "gain map too small");
-const int kMinMicLevel = 12;
+constexpr int kMinMicLevel = 12;
// Prevent very large microphone level changes.
-const int kMaxResidualGainChange = 15;
+constexpr int kMaxResidualGainChange = 15;
// Maximum additional gain allowed to compensate for microphone level
// restrictions from clipping events.
-const int kSurplusCompressionGain = 6;
+constexpr int kSurplusCompressionGain = 6;
// Returns whether a fall-back solution to choose the maximum level should be
// chosen.
@@ -182,19 +175,19 @@
}
}
-void MonoAgc::HandleClipping() {
+void MonoAgc::HandleClipping(int clipped_level_step) {
// Always decrease the maximum level, even if the current level is below
// threshold.
- SetMaxLevel(std::max(clipped_level_min_, max_level_ - kClippedLevelStep));
+ SetMaxLevel(std::max(clipped_level_min_, max_level_ - clipped_level_step));
if (log_to_histograms_) {
RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed",
- level_ - kClippedLevelStep >= clipped_level_min_);
+ level_ - clipped_level_step >= 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(clipped_level_min_, level_ - kClippedLevelStep));
+ SetLevel(std::max(clipped_level_min_, level_ - clipped_level_step));
// Reset the AGCs for all channels since the level has changed.
agc_->Reset();
}
@@ -404,12 +397,18 @@
AgcManagerDirect::AgcManagerDirect(Agc* agc,
int startup_min_level,
int clipped_level_min,
- int sample_rate_hz)
+ int sample_rate_hz,
+ int clipped_level_step,
+ float clipped_ratio_threshold,
+ int clipped_wait_frames)
: AgcManagerDirect(/*num_capture_channels*/ 1,
startup_min_level,
clipped_level_min,
/*disable_digital_adaptive*/ false,
- sample_rate_hz) {
+ sample_rate_hz,
+ clipped_level_step,
+ clipped_ratio_threshold,
+ clipped_wait_frames) {
RTC_DCHECK(channel_agcs_[0]);
RTC_DCHECK(agc);
channel_agcs_[0]->set_agc(agc);
@@ -419,15 +418,21 @@
int startup_min_level,
int clipped_level_min,
bool disable_digital_adaptive,
- int sample_rate_hz)
+ int sample_rate_hz,
+ int clipped_level_step,
+ float clipped_ratio_threshold,
+ int clipped_wait_frames)
: data_dumper_(
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_counter_))),
use_min_channel_level_(!UseMaxAnalogChannelLevel()),
sample_rate_hz_(sample_rate_hz),
num_capture_channels_(num_capture_channels),
disable_digital_adaptive_(disable_digital_adaptive),
- frames_since_clipped_(kClippedWaitFrames),
+ frames_since_clipped_(clipped_wait_frames),
capture_output_used_(true),
+ clipped_level_step_(clipped_level_step),
+ clipped_ratio_threshold_(clipped_ratio_threshold),
+ clipped_wait_frames_(clipped_wait_frames),
channel_agcs_(num_capture_channels),
new_compressions_to_set_(num_capture_channels) {
const int min_mic_level = GetMinMicLevel();
@@ -438,7 +443,13 @@
data_dumper_ch, startup_min_level, clipped_level_min,
disable_digital_adaptive_, min_mic_level);
}
- RTC_DCHECK_LT(0, channel_agcs_.size());
+ RTC_DCHECK(!channel_agcs_.empty());
+ RTC_DCHECK_GT(clipped_level_step, 0);
+ RTC_DCHECK_LE(clipped_level_step, 255);
+ RTC_DCHECK_GT(clipped_ratio_threshold, 0.f);
+ RTC_DCHECK_LT(clipped_ratio_threshold, 1.f);
+ RTC_DCHECK_GT(clipped_wait_frames, 0);
+
channel_agcs_[0]->ActivateLogging();
}
@@ -489,7 +500,7 @@
return;
}
- if (frames_since_clipped_ < kClippedWaitFrames) {
+ if (frames_since_clipped_ < clipped_wait_frames_) {
++frames_since_clipped_;
return;
}
@@ -506,11 +517,11 @@
float clipped_ratio =
ComputeClippedRatio(audio, num_capture_channels_, samples_per_channel);
- if (clipped_ratio > kClippedRatioThreshold) {
+ if (clipped_ratio > clipped_ratio_threshold_) {
RTC_DLOG(LS_INFO) << "[agc] Clipping detected. clipped_ratio="
<< clipped_ratio;
for (auto& state_ch : channel_agcs_) {
- state_ch->HandleClipping();
+ state_ch->HandleClipping(clipped_level_step_);
}
frames_since_clipped_ = 0;
}
diff --git a/modules/audio_processing/agc/agc_manager_direct.h b/modules/audio_processing/agc/agc_manager_direct.h
index f9417cf..e0be1a0 100644
--- a/modules/audio_processing/agc/agc_manager_direct.h
+++ b/modules/audio_processing/agc/agc_manager_direct.h
@@ -34,12 +34,20 @@
// AgcManagerDirect will configure GainControl internally. The user is
// responsible for processing the audio using it after the call to Process.
// The operating range of startup_min_level is [12, 255] and any input value
- // outside that range will be clamped.
+ // outside that range will be clamped. `clipped_level_step` is the amount
+ // the microphone level is lowered with every clipping event, limited to
+ // (0, 255]. `clipped_ratio_threshold` is the proportion of clipped
+ // samples required to declare a clipping event, limited to (0.f, 1.f).
+ // `clipped_wait_frames` is the time in frames to wait after a clipping event
+ // before checking again, limited to values higher than 0.
AgcManagerDirect(int num_capture_channels,
int startup_min_level,
int clipped_level_min,
bool disable_digital_adaptive,
- int sample_rate_hz);
+ int sample_rate_hz,
+ int clipped_level_step,
+ float clipped_ratio_threshold,
+ int clipped_wait_frames);
~AgcManagerDirect();
AgcManagerDirect(const AgcManagerDirect&) = delete;
@@ -81,13 +89,18 @@
AgcMinMicLevelExperimentEnabled50);
FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectStandaloneTest,
AgcMinMicLevelExperimentEnabledAboveStartupLevel);
+ FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectStandaloneTest,
+ ClippingParametersVerified);
// Dependency injection for testing. Don't delete |agc| as the memory is owned
// by the manager.
AgcManagerDirect(Agc* agc,
int startup_min_level,
int clipped_level_min,
- int sample_rate_hz);
+ int sample_rate_hz,
+ int clipped_level_step,
+ float clipped_ratio_threshold,
+ int clipped_wait_frames);
void AnalyzePreProcess(const float* const* audio, size_t samples_per_channel);
@@ -105,6 +118,10 @@
bool capture_output_used_;
int channel_controlling_gain_ = 0;
+ const int clipped_level_step_;
+ const float clipped_ratio_threshold_;
+ const int clipped_wait_frames_;
+
std::vector<std::unique_ptr<MonoAgc>> channel_agcs_;
std::vector<absl::optional<int>> new_compressions_to_set_;
};
@@ -123,7 +140,7 @@
void Initialize();
void HandleCaptureOutputUsedChange(bool capture_output_used);
- void HandleClipping();
+ void HandleClipping(int clipped_level_step);
void Process(const int16_t* audio,
size_t samples_per_channel,
diff --git a/modules/audio_processing/agc/agc_manager_direct_unittest.cc b/modules/audio_processing/agc/agc_manager_direct_unittest.cc
index 1954ed4..6fdfa6d 100644
--- a/modules/audio_processing/agc/agc_manager_direct_unittest.cc
+++ b/modules/audio_processing/agc/agc_manager_direct_unittest.cc
@@ -26,13 +26,16 @@
namespace webrtc {
namespace {
-const int kSampleRateHz = 32000;
-const int kNumChannels = 1;
-const int kSamplesPerChannel = kSampleRateHz / 100;
-const int kInitialVolume = 128;
+constexpr int kSampleRateHz = 32000;
+constexpr int kNumChannels = 1;
+constexpr int kSamplesPerChannel = kSampleRateHz / 100;
+constexpr int kInitialVolume = 128;
constexpr int kClippedMin = 165; // Arbitrary, but different from the default.
-const float kAboveClippedThreshold = 0.2f;
-const int kMinMicLevel = 12;
+constexpr float kAboveClippedThreshold = 0.2f;
+constexpr int kMinMicLevel = 12;
+constexpr int kClippedLevelStep = 15;
+constexpr float kClippedRatioThreshold = 0.1f;
+constexpr int kClippedWaitFrames = 300;
class MockGainControl : public GainControl {
public:
@@ -57,10 +60,14 @@
};
std::unique_ptr<AgcManagerDirect> CreateAgcManagerDirect(
- int startup_min_level) {
+ int startup_min_level,
+ int clipped_level_step,
+ float clipped_ratio_threshold,
+ int clipped_wait_frames) {
return std::make_unique<AgcManagerDirect>(
/*num_capture_channels=*/1, startup_min_level, kClippedMin,
- /*disable_digital_adaptive=*/true, kSampleRateHz);
+ /*disable_digital_adaptive=*/true, kSampleRateHz, clipped_level_step,
+ clipped_ratio_threshold, clipped_wait_frames);
}
} // namespace
@@ -69,7 +76,13 @@
protected:
AgcManagerDirectTest()
: agc_(new MockAgc),
- manager_(agc_, kInitialVolume, kClippedMin, kSampleRateHz),
+ manager_(agc_,
+ kInitialVolume,
+ kClippedMin,
+ kSampleRateHz,
+ kClippedLevelStep,
+ kClippedRatioThreshold,
+ kClippedWaitFrames),
audio(kNumChannels),
audio_data(kNumChannels * kSamplesPerChannel, 0.f) {
ExpectInitialize();
@@ -705,14 +718,16 @@
EXPECT_CALL(gctrl, enable_limiter(false));
std::unique_ptr<AgcManagerDirect> manager =
- CreateAgcManagerDirect(kInitialVolume);
+ CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep,
+ kClippedRatioThreshold, kClippedWaitFrames);
manager->Initialize();
manager->SetupDigitalGainControl(&gctrl);
}
TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperiment) {
std::unique_ptr<AgcManagerDirect> manager =
- CreateAgcManagerDirect(kInitialVolume);
+ CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep,
+ kClippedRatioThreshold, kClippedWaitFrames);
EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume);
}
@@ -721,7 +736,8 @@
test::ScopedFieldTrials field_trial(
"WebRTC-Audio-AgcMinMicLevelExperiment/Disabled/");
std::unique_ptr<AgcManagerDirect> manager =
- CreateAgcManagerDirect(kInitialVolume);
+ CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep,
+ kClippedRatioThreshold, kClippedWaitFrames);
EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume);
}
@@ -732,7 +748,8 @@
test::ScopedFieldTrials field_trial(
"WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-256/");
std::unique_ptr<AgcManagerDirect> manager =
- CreateAgcManagerDirect(kInitialVolume);
+ CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep,
+ kClippedRatioThreshold, kClippedWaitFrames);
EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume);
}
@@ -743,7 +760,8 @@
test::ScopedFieldTrials field_trial(
"WebRTC-Audio-AgcMinMicLevelExperiment/Enabled--1/");
std::unique_ptr<AgcManagerDirect> manager =
- CreateAgcManagerDirect(kInitialVolume);
+ CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep,
+ kClippedRatioThreshold, kClippedWaitFrames);
EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel);
EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume);
}
@@ -755,7 +773,8 @@
test::ScopedFieldTrials field_trial(
"WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-50/");
std::unique_ptr<AgcManagerDirect> manager =
- CreateAgcManagerDirect(kInitialVolume);
+ CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep,
+ kClippedRatioThreshold, kClippedWaitFrames);
EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), 50);
EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume);
}
@@ -768,9 +787,33 @@
test::ScopedFieldTrials field_trial(
"WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-50/");
std::unique_ptr<AgcManagerDirect> manager =
- CreateAgcManagerDirect(/*startup_min_level=*/30);
+ CreateAgcManagerDirect(/*startup_min_level=*/30, kClippedLevelStep,
+ kClippedRatioThreshold, kClippedWaitFrames);
EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), 50);
EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), 50);
}
+// TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_level_step`.
+// TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_ratio_threshold`.
+// TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_wait_frames`.
+// Verifies that configurable clipping parameters are initialized as intended.
+TEST(AgcManagerDirectStandaloneTest, ClippingParametersVerified) {
+ std::unique_ptr<AgcManagerDirect> manager =
+ CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep,
+ kClippedRatioThreshold, kClippedWaitFrames);
+ manager->Initialize();
+ EXPECT_EQ(manager->clipped_level_step_, kClippedLevelStep);
+ EXPECT_EQ(manager->clipped_ratio_threshold_, kClippedRatioThreshold);
+ EXPECT_EQ(manager->clipped_wait_frames_, kClippedWaitFrames);
+ std::unique_ptr<AgcManagerDirect> manager_custom =
+ CreateAgcManagerDirect(kInitialVolume,
+ /*clipped_level_step*/ 10,
+ /*clipped_ratio_threshold*/ 0.2f,
+ /*clipped_wait_frames*/ 50);
+ manager_custom->Initialize();
+ EXPECT_EQ(manager_custom->clipped_level_step_, 10);
+ EXPECT_EQ(manager_custom->clipped_ratio_threshold_, 0.2f);
+ EXPECT_EQ(manager_custom->clipped_wait_frames_, 50);
+}
+
} // namespace webrtc
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index ac4d6a8..3c5d9fb 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -1918,7 +1918,10 @@
config_.gain_controller1.analog_gain_controller.clipped_level_min,
!config_.gain_controller1.analog_gain_controller
.enable_digital_adaptive,
- capture_nonlocked_.split_rate));
+ capture_nonlocked_.split_rate,
+ config_.gain_controller1.analog_gain_controller.clipped_level_step,
+ config_.gain_controller1.analog_gain_controller.clipped_ratio_threshold,
+ config_.gain_controller1.analog_gain_controller.clipped_wait_frames));
if (re_creation) {
submodules_.agc_manager->set_stream_analog_level(stream_analog_level);
}
diff --git a/modules/audio_processing/include/audio_processing.cc b/modules/audio_processing/include/audio_processing.cc
index f50a283..29dcd66 100644
--- a/modules/audio_processing/include/audio_processing.cc
+++ b/modules/audio_processing/include/audio_processing.cc
@@ -77,7 +77,11 @@
analog_lhs.startup_min_volume == analog_rhs.startup_min_volume &&
analog_lhs.clipped_level_min == analog_rhs.clipped_level_min &&
analog_lhs.enable_digital_adaptive ==
- analog_rhs.enable_digital_adaptive;
+ analog_rhs.enable_digital_adaptive &&
+ analog_lhs.clipped_level_step == analog_rhs.clipped_level_step &&
+ analog_lhs.clipped_ratio_threshold ==
+ analog_rhs.clipped_ratio_threshold &&
+ analog_lhs.clipped_wait_frames == analog_rhs.clipped_wait_frames;
}
bool Agc2Config::AdaptiveDigital::operator==(
@@ -157,6 +161,12 @@
<< gain_controller1.analog_gain_controller.clipped_level_min
<< ", enable_digital_adaptive: "
<< gain_controller1.analog_gain_controller.enable_digital_adaptive
+ << ", clipped_level_step: "
+ << gain_controller1.analog_gain_controller.clipped_level_step
+ << ", clipped_ratio_threshold: "
+ << gain_controller1.analog_gain_controller.clipped_ratio_threshold
+ << ", clipped_wait_frames: "
+ << gain_controller1.analog_gain_controller.clipped_wait_frames
<< " }}, gain_controller2: { enabled: " << gain_controller2.enabled
<< ", fixed_digital: { gain_db: "
<< gain_controller2.fixed_digital.gain_db
diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h
index 6bc50f0..6622097 100644
--- a/modules/audio_processing/include/audio_processing.h
+++ b/modules/audio_processing/include/audio_processing.h
@@ -59,9 +59,9 @@
//
// Must be provided through AudioProcessingBuilder().Create(config).
#if defined(WEBRTC_CHROMIUM_BUILD)
-static const int kAgcStartupMinVolume = 85;
+static constexpr int kAgcStartupMinVolume = 85;
#else
-static const int kAgcStartupMinVolume = 0;
+static constexpr int kAgcStartupMinVolume = 0;
#endif // defined(WEBRTC_CHROMIUM_BUILD)
static constexpr int kClippedLevelMin = 70;
@@ -334,6 +334,15 @@
// clipping.
int clipped_level_min = kClippedLevelMin;
bool enable_digital_adaptive = true;
+ // Amount the microphone level is lowered with every clipping event.
+ // Limited to (0, 255].
+ int clipped_level_step = 15;
+ // Proportion of clipped samples required to declare a clipping event.
+ // Limited to (0.f, 1.f).
+ float clipped_ratio_threshold = 0.1f;
+ // Time in frames to wait after a clipping event before checking again.
+ // Limited to values higher than 0.
+ int clipped_wait_frames = 300;
} analog_gain_controller;
} gain_controller1;