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}
This commit is contained in:
henrik.lundin
2016-12-05 09:08:42 -08:00
committed by Commit bot
parent db752f9b37
commit bd681b9758
7 changed files with 81 additions and 29 deletions

View File

@ -27,8 +27,6 @@ namespace webrtc {
namespace { 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. // Amount the microphone level is lowered with every clipping event.
const int kClippedLevelStep = 15; const int kClippedLevelStep = 15;
// Proportion of clipped samples required to declare a clipping event. // Proportion of clipped samples required to declare a clipping event.
@ -115,7 +113,8 @@ class DebugFile {
AgcManagerDirect::AgcManagerDirect(GainControl* gctrl, AgcManagerDirect::AgcManagerDirect(GainControl* gctrl,
VolumeCallbacks* volume_callbacks, VolumeCallbacks* volume_callbacks,
int startup_min_level) int startup_min_level,
int clipped_level_min)
: agc_(new Agc()), : agc_(new Agc()),
gctrl_(gctrl), gctrl_(gctrl),
volume_callbacks_(volume_callbacks), volume_callbacks_(volume_callbacks),
@ -130,14 +129,15 @@ AgcManagerDirect::AgcManagerDirect(GainControl* gctrl,
check_volume_on_next_process_(true), // Check at startup. check_volume_on_next_process_(true), // Check at startup.
startup_(true), startup_(true),
startup_min_level_(ClampLevel(startup_min_level)), startup_min_level_(ClampLevel(startup_min_level)),
clipped_level_min_(clipped_level_min),
file_preproc_(new DebugFile("agc_preproc.pcm")), 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, AgcManagerDirect::AgcManagerDirect(Agc* agc,
GainControl* gctrl, GainControl* gctrl,
VolumeCallbacks* volume_callbacks, VolumeCallbacks* volume_callbacks,
int startup_min_level) int startup_min_level,
int clipped_level_min)
: agc_(agc), : agc_(agc),
gctrl_(gctrl), gctrl_(gctrl),
volume_callbacks_(volume_callbacks), volume_callbacks_(volume_callbacks),
@ -152,9 +152,9 @@ AgcManagerDirect::AgcManagerDirect(Agc* agc,
check_volume_on_next_process_(true), // Check at startup. check_volume_on_next_process_(true), // Check at startup.
startup_(true), startup_(true),
startup_min_level_(ClampLevel(startup_min_level)), startup_min_level_(ClampLevel(startup_min_level)),
clipped_level_min_(clipped_level_min),
file_preproc_(new DebugFile("agc_preproc.pcm")), file_preproc_(new DebugFile("agc_preproc.pcm")),
file_postproc_(new DebugFile("agc_postproc.pcm")) { file_postproc_(new DebugFile("agc_postproc.pcm")) {}
}
AgcManagerDirect::~AgcManagerDirect() {} AgcManagerDirect::~AgcManagerDirect() {}
@ -218,14 +218,14 @@ void AgcManagerDirect::AnalyzePreProcess(int16_t* audio,
<< clipped_ratio; << clipped_ratio;
// Always decrease the maximum level, even if the current level is below // Always decrease the maximum level, even if the current level is below
// threshold. // threshold.
SetMaxLevel(std::max(kClippedLevelMin, max_level_ - kClippedLevelStep)); SetMaxLevel(std::max(clipped_level_min_, max_level_ - kClippedLevelStep));
RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed", RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.AgcClippingAdjustmentAllowed",
level_ - kClippedLevelStep >= kClippedLevelMin); level_ - kClippedLevelStep >= clipped_level_min_);
if (level_ > kClippedLevelMin) { if (level_ > clipped_level_min_) {
// Don't try to adjust the level if we're already below the limit. As // 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 // a consequence, if the user has brought the level above the limit, we
// will still not react until the postproc updates the level. // 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. // Reset the AGC since the level has changed.
agc_->Reset(); agc_->Reset();
} }
@ -301,13 +301,15 @@ void AgcManagerDirect::SetLevel(int new_level) {
} }
void AgcManagerDirect::SetMaxLevel(int level) { void AgcManagerDirect::SetMaxLevel(int level) {
RTC_DCHECK_GE(level, kClippedLevelMin); RTC_DCHECK_GE(level, clipped_level_min_);
max_level_ = level; max_level_ = level;
// Scale the |kSurplusCompressionGain| linearly across the restricted // Scale the |kSurplusCompressionGain| linearly across the restricted
// level range. // level range.
max_compression_gain_ = kMaxCompressionGain + std::floor( max_compression_gain_ =
(1.f * kMaxMicLevel - max_level_) / (kMaxMicLevel - kClippedLevelMin) * kMaxCompressionGain + std::floor((1.f * kMaxMicLevel - max_level_) /
kSurplusCompressionGain + 0.5f); (kMaxMicLevel - clipped_level_min_) *
kSurplusCompressionGain +
0.5f);
LOG(LS_INFO) << "[agc] max_level_=" << max_level_ LOG(LS_INFO) << "[agc] max_level_=" << max_level_
<< ", max_compression_gain_=" << max_compression_gain_; << ", max_compression_gain_=" << max_compression_gain_;
} }

View File

@ -46,13 +46,15 @@ class AgcManagerDirect final {
// outside that range will be clamped. // outside that range will be clamped.
AgcManagerDirect(GainControl* gctrl, AgcManagerDirect(GainControl* gctrl,
VolumeCallbacks* volume_callbacks, 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 // Dependency injection for testing. Don't delete |agc| as the memory is owned
// by the manager. // by the manager.
AgcManagerDirect(Agc* agc, AgcManagerDirect(Agc* agc,
GainControl* gctrl, GainControl* gctrl,
VolumeCallbacks* volume_callbacks, VolumeCallbacks* volume_callbacks,
int startup_min_level); int startup_min_level,
int clipped_level_min);
~AgcManagerDirect(); ~AgcManagerDirect();
int Initialize(); int Initialize();
@ -98,6 +100,7 @@ class AgcManagerDirect final {
bool check_volume_on_next_process_; bool check_volume_on_next_process_;
bool startup_; bool startup_;
int startup_min_level_; int startup_min_level_;
const int clipped_level_min_;
std::unique_ptr<DebugFile> file_preproc_; std::unique_ptr<DebugFile> file_preproc_;
std::unique_ptr<DebugFile> file_postproc_; std::unique_ptr<DebugFile> file_postproc_;

View File

@ -33,6 +33,7 @@ const int kSampleRateHz = 32000;
const int kNumChannels = 1; const int kNumChannels = 1;
const int kSamplesPerChannel = kSampleRateHz / 100; const int kSamplesPerChannel = kSampleRateHz / 100;
const int kInitialVolume = 128; const int kInitialVolume = 128;
constexpr int kClippedMin = 165; // Arbitrary, but different from the default.
const float kAboveClippedThreshold = 0.2f; const float kAboveClippedThreshold = 0.2f;
class TestVolumeCallbacks : public VolumeCallbacks { class TestVolumeCallbacks : public VolumeCallbacks {
@ -50,7 +51,8 @@ class TestVolumeCallbacks : public VolumeCallbacks {
class AgcManagerDirectTest : public ::testing::Test { class AgcManagerDirectTest : public ::testing::Test {
protected: protected:
AgcManagerDirectTest() AgcManagerDirectTest()
: agc_(new MockAgc), manager_(agc_, &gctrl_, &volume_, kInitialVolume) { : agc_(new MockAgc),
manager_(agc_, &gctrl_, &volume_, kInitialVolume, kClippedMin) {
ExpectInitialize(); ExpectInitialize();
manager_.Initialize(); manager_.Initialize();
} }
@ -505,13 +507,13 @@ TEST_F(AgcManagerDirectTest, ClippingLoweringIsLimited) {
.WillOnce(Return(kAboveClippedThreshold)); .WillOnce(Return(kAboveClippedThreshold));
EXPECT_CALL(*agc_, Reset()).Times(1); EXPECT_CALL(*agc_, Reset()).Times(1);
CallPreProc(1); CallPreProc(1);
EXPECT_EQ(170, volume_.GetMicVolume()); EXPECT_EQ(kClippedMin, volume_.GetMicVolume());
EXPECT_CALL(*agc_, AnalyzePreproc(_, _)) EXPECT_CALL(*agc_, AnalyzePreproc(_, _))
.WillRepeatedly(Return(kAboveClippedThreshold)); .WillRepeatedly(Return(kAboveClippedThreshold));
EXPECT_CALL(*agc_, Reset()).Times(0); EXPECT_CALL(*agc_, Reset()).Times(0);
CallPreProc(1000); CallPreProc(1000);
EXPECT_EQ(170, volume_.GetMicVolume()); EXPECT_EQ(kClippedMin, volume_.GetMicVolume());
} }
TEST_F(AgcManagerDirectTest, ClippingMaxIsRespectedWhenEqualToLevel) { TEST_F(AgcManagerDirectTest, ClippingMaxIsRespectedWhenEqualToLevel) {
@ -589,7 +591,7 @@ TEST_F(AgcManagerDirectTest, MaxCompressionIsIncreasedAfterClipping) {
.WillOnce(Return(kAboveClippedThreshold)); .WillOnce(Return(kAboveClippedThreshold));
EXPECT_CALL(*agc_, Reset()).Times(1); EXPECT_CALL(*agc_, Reset()).Times(1);
CallPreProc(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 // Current level is now at the minimum, but the maximum allowed level still
// has more to decrease. // has more to decrease.

View File

@ -302,6 +302,7 @@ AudioProcessingImpl::AudioProcessingImpl(const webrtc::Config& config,
public_submodules_(new ApmPublicSubmodules()), public_submodules_(new ApmPublicSubmodules()),
private_submodules_(new ApmPrivateSubmodules(beamformer)), private_submodules_(new ApmPrivateSubmodules(beamformer)),
constants_(config.Get<ExperimentalAgc>().startup_min_volume, constants_(config.Get<ExperimentalAgc>().startup_min_volume,
config.Get<ExperimentalAgc>().clipped_level_min,
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) #if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
false), false),
#else #else
@ -488,7 +489,7 @@ int AudioProcessingImpl::InitializeLocked() {
private_submodules_->agc_manager.reset(new AgcManagerDirect( private_submodules_->agc_manager.reset(new AgcManagerDirect(
public_submodules_->gain_control.get(), public_submodules_->gain_control.get(),
public_submodules_->gain_control_for_experimental_agc.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->Initialize();
private_submodules_->agc_manager->SetCaptureMuted( private_submodules_->agc_manager->SetCaptureMuted(
@ -1853,6 +1854,9 @@ int AudioProcessingImpl::WriteConfigMessage(bool forced) {
if (capture_nonlocked_.level_controller_enabled) { if (capture_nonlocked_.level_controller_enabled) {
experiments_description += "LevelController;"; experiments_description += "LevelController;";
} }
if (constants_.agc_clipped_level_min != kClippedLevelMin) {
experiments_description += "AgcClippingLevelExperiment;";
}
config.set_experiments_description(experiments_description); config.set_experiments_description(experiments_description);
std::string serialized_config = config.SerializeAsString(); std::string serialized_config = config.SerializeAsString();

View File

@ -329,11 +329,15 @@ class AudioProcessingImpl : public AudioProcessing {
// APM constants. // APM constants.
const struct ApmConstants { 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. : // Format of processing streams at input/output call sites.
agc_startup_min_volume(agc_startup_min_volume), agc_startup_min_volume(agc_startup_min_volume),
agc_clipped_level_min(agc_clipped_level_min),
use_experimental_agc(use_experimental_agc) {} use_experimental_agc(use_experimental_agc) {}
int agc_startup_min_volume; int agc_startup_min_volume;
int agc_clipped_level_min;
bool use_experimental_agc; bool use_experimental_agc;
} constants_; } constants_;

View File

@ -114,15 +114,21 @@ static const int kAgcStartupMinVolume = 85;
#else #else
static const int kAgcStartupMinVolume = 0; static const int kAgcStartupMinVolume = 0;
#endif // defined(WEBRTC_CHROMIUM_BUILD) #endif // defined(WEBRTC_CHROMIUM_BUILD)
static constexpr int kClippedLevelMin = 170;
struct ExperimentalAgc { struct ExperimentalAgc {
ExperimentalAgc() : enabled(true), startup_min_volume(kAgcStartupMinVolume) {} ExperimentalAgc() = default;
explicit ExperimentalAgc(bool enabled) explicit ExperimentalAgc(bool enabled) : enabled(enabled) {}
: enabled(enabled), startup_min_volume(kAgcStartupMinVolume) {}
ExperimentalAgc(bool enabled, int startup_min_volume) ExperimentalAgc(bool enabled, int startup_min_volume)
: enabled(enabled), startup_min_volume(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; static const ConfigOptionID identifier = ConfigOptionID::kExperimentalAgc;
bool enabled; bool enabled = true;
int startup_min_volume; 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 // Use to enable experimental noise suppression. It can be set in the

View File

@ -379,6 +379,8 @@ TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) {
Config config; Config config;
config.Set<RefinedAdaptiveFilter>(new RefinedAdaptiveFilter(true)); config.Set<RefinedAdaptiveFilter>(new RefinedAdaptiveFilter(true));
config.Set<EchoCanceller3>(new EchoCanceller3(true)); config.Set<EchoCanceller3>(new EchoCanceller3(true));
// Arbitrarily set clipping gain to 17, which will never be the default.
config.Set<ExperimentalAgc>(new ExperimentalAgc(true, 0, 17));
DebugDumpGenerator generator(config, AudioProcessing::Config()); DebugDumpGenerator generator(config, AudioProcessing::Config());
generator.StartRecording(); generator.StartRecording();
generator.Process(100); generator.Process(100);
@ -398,6 +400,8 @@ TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) {
msg->experiments_description().c_str()); msg->experiments_description().c_str());
EXPECT_PRED_FORMAT2(testing::IsSubstring, "AEC3", EXPECT_PRED_FORMAT2(testing::IsSubstring, "AEC3",
msg->experiments_description().c_str()); 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()); msg->experiments_description().c_str());
EXPECT_PRED_FORMAT2(testing::IsNotSubstring, "AEC3", EXPECT_PRED_FORMAT2(testing::IsNotSubstring, "AEC3",
msg->experiments_description().c_str()); 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<ExperimentalAgc>(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<audioproc::Event> 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) { TEST_F(DebugDumpTest, VerifyEmptyExperimentalString) {
Config config; Config config;
DebugDumpGenerator generator(config, AudioProcessing::Config()); DebugDumpGenerator generator(config, AudioProcessing::Config());