From adc46c4cf7dd8a015b0757c99787d80525c123ab Mon Sep 17 00:00:00 2001 From: Bjorn Volcker Date: Wed, 15 Apr 2015 11:42:40 +0200 Subject: [PATCH] audio_processing/agc: Adds config to set minimum microphone volume at startup The AGC is currently bumping up the mic volume to 33% at startup if it is below that level. This is to avoid getting stuck in a poor state from which the AGC can not move, simply a too low input audio level. For some users, 33% is instead too loud. This CL gives the user the possibility to set that level at create time. - Extends the Config ExperimentalAgc with a startup_mic_volume for the user to set if desired. Note that the bump up does not apply to the legacy AGC and the "regular" AGC is controlled by ExperimentalAgc. - Without any actions, the same default value as previously is used. - In addition I removed a return value from InitializeExperimentalAgc() and InitializeTransient() This has been tested by building Chromium on Mac and verify through apprtc that 1) startup_mic_volume = 128 bumps up to 50%. 2) startup_mic_volume = 500 (out of range) bumps up to 100%. 3) startup_mic_volume = 0 bumps up to 4%, the AGC min level. BUG=4529 TESTED=locally R=andrew@webrtc.org, kwiberg@webrtc.org Review URL: https://webrtc-codereview.appspot.com/43109004 Cr-Commit-Position: refs/heads/master@{#9004} --- .../agc/agc_manager_direct.cc | 15 +++++++++---- .../audio_processing/agc/agc_manager_direct.h | 10 +++++++-- .../audio_processing/audio_processing_impl.cc | 22 +++++++------------ .../audio_processing/audio_processing_impl.h | 5 +++-- .../include/audio_processing.h | 17 ++++++++++---- webrtc/tools/agc/agc_manager.cc | 17 ++++++++------ 6 files changed, 53 insertions(+), 33 deletions(-) diff --git a/webrtc/modules/audio_processing/agc/agc_manager_direct.cc b/webrtc/modules/audio_processing/agc/agc_manager_direct.cc index 24fbd56eb..573d48cdb 100644 --- a/webrtc/modules/audio_processing/agc/agc_manager_direct.cc +++ b/webrtc/modules/audio_processing/agc/agc_manager_direct.cc @@ -48,7 +48,6 @@ const float kCompressionGainStep = 0.05f; const int kMaxMicLevel = 255; static_assert(kGainMapSize > kMaxMicLevel, "gain map too small"); const int kMinMicLevel = 12; -const int kMinInitMicLevel = 85; // Prevent very large microphone level changes. const int kMaxResidualGainChange = 15; @@ -57,6 +56,10 @@ const int kMaxResidualGainChange = 15; // restrictions from clipping events. const int kSurplusCompressionGain = 6; +int ClampLevel(int mic_level) { + return std::min(std::max(kMinMicLevel, mic_level), kMaxMicLevel); +} + int LevelFromGainError(int gain_error, int level) { assert(level >= 0 && level <= kMaxMicLevel); if (gain_error == 0) { @@ -109,7 +112,8 @@ class DebugFile { }; AgcManagerDirect::AgcManagerDirect(GainControl* gctrl, - VolumeCallbacks* volume_callbacks) + VolumeCallbacks* volume_callbacks, + int startup_min_level) : agc_(new Agc()), gctrl_(gctrl), volume_callbacks_(volume_callbacks), @@ -123,13 +127,15 @@ AgcManagerDirect::AgcManagerDirect(GainControl* gctrl, capture_muted_(false), check_volume_on_next_process_(true), // Check at startup. startup_(true), + startup_min_level_(ClampLevel(startup_min_level)), file_preproc_(new DebugFile("agc_preproc.pcm")), file_postproc_(new DebugFile("agc_postproc.pcm")) { } AgcManagerDirect::AgcManagerDirect(Agc* agc, GainControl* gctrl, - VolumeCallbacks* volume_callbacks) + VolumeCallbacks* volume_callbacks, + int startup_min_level) : agc_(agc), gctrl_(gctrl), volume_callbacks_(volume_callbacks), @@ -143,6 +149,7 @@ AgcManagerDirect::AgcManagerDirect(Agc* agc, capture_muted_(false), check_volume_on_next_process_(true), // Check at startup. startup_(true), + startup_min_level_(ClampLevel(startup_min_level)), file_preproc_(new DebugFile("agc_preproc.pcm")), file_postproc_(new DebugFile("agc_postproc.pcm")) { } @@ -336,7 +343,7 @@ int AgcManagerDirect::CheckVolumeAndReset() { } LOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level; - int minLevel = startup_ ? kMinInitMicLevel : kMinMicLevel; + int minLevel = startup_ ? startup_min_level_ : kMinMicLevel; if (level < minLevel) { level = minLevel; LOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level; diff --git a/webrtc/modules/audio_processing/agc/agc_manager_direct.h b/webrtc/modules/audio_processing/agc/agc_manager_direct.h index 05b770183..d12acf30d 100644 --- a/webrtc/modules/audio_processing/agc/agc_manager_direct.h +++ b/webrtc/modules/audio_processing/agc/agc_manager_direct.h @@ -41,12 +41,17 @@ class AgcManagerDirect { public: // AgcManagerDirect will configure GainControl internally. The user is // responsible for processing the audio using it after the call to Process. - AgcManagerDirect(GainControl* gctrl, VolumeCallbacks* volume_callbacks); + // The operating range of startup_min_level is [12, 255] and any input value + // outside that range will be clamped. + AgcManagerDirect(GainControl* gctrl, + VolumeCallbacks* volume_callbacks, + int startup_min_level); // Dependency injection for testing. Don't delete |agc| as the memory is owned // by the manager. AgcManagerDirect(Agc* agc, GainControl* gctrl, - VolumeCallbacks* volume_callbacks); + VolumeCallbacks* volume_callbacks, + int startup_min_level); ~AgcManagerDirect(); int Initialize(); @@ -88,6 +93,7 @@ class AgcManagerDirect { bool capture_muted_; bool check_volume_on_next_process_; bool startup_; + int startup_min_level_; rtc::scoped_ptr file_preproc_; rtc::scoped_ptr file_postproc_; diff --git a/webrtc/modules/audio_processing/audio_processing_impl.cc b/webrtc/modules/audio_processing/audio_processing_impl.cc index 1074b6182..e8210c935 100644 --- a/webrtc/modules/audio_processing/audio_processing_impl.cc +++ b/webrtc/modules/audio_processing/audio_processing_impl.cc @@ -177,6 +177,7 @@ AudioProcessingImpl::AudioProcessingImpl(const Config& config, #else use_new_agc_(config.Get().enabled), #endif + agc_startup_min_volume_(config.Get().startup_min_volume), transient_suppressor_enabled_(config.Get().enabled), beamformer_enabled_(config.Get().enabled), beamformer_(beamformer), @@ -285,15 +286,9 @@ int AudioProcessingImpl::InitializeLocked() { } } - int err = InitializeExperimentalAgc(); - if (err != kNoError) { - return err; - } + InitializeExperimentalAgc(); - err = InitializeTransient(); - if (err != kNoError) { - return err; - } + InitializeTransient(); InitializeBeamformer(); @@ -959,19 +954,19 @@ bool AudioProcessingImpl::analysis_needed(bool is_data_processed) const { return false; } -int AudioProcessingImpl::InitializeExperimentalAgc() { +void AudioProcessingImpl::InitializeExperimentalAgc() { if (use_new_agc_) { if (!agc_manager_.get()) { - agc_manager_.reset( - new AgcManagerDirect(gain_control_, gain_control_for_new_agc_.get())); + agc_manager_.reset(new AgcManagerDirect(gain_control_, + gain_control_for_new_agc_.get(), + agc_startup_min_volume_)); } agc_manager_->Initialize(); agc_manager_->SetCaptureMuted(output_will_be_muted_); } - return kNoError; } -int AudioProcessingImpl::InitializeTransient() { +void AudioProcessingImpl::InitializeTransient() { if (transient_suppressor_enabled_) { if (!transient_suppressor_.get()) { transient_suppressor_.reset(new TransientSuppressor()); @@ -980,7 +975,6 @@ int AudioProcessingImpl::InitializeTransient() { split_rate_, fwd_out_format_.num_channels()); } - return kNoError; } void AudioProcessingImpl::InitializeBeamformer() { diff --git a/webrtc/modules/audio_processing/audio_processing_impl.h b/webrtc/modules/audio_processing/audio_processing_impl.h index 500f08e82..dbc1f25db 100644 --- a/webrtc/modules/audio_processing/audio_processing_impl.h +++ b/webrtc/modules/audio_processing/audio_processing_impl.h @@ -170,8 +170,8 @@ class AudioProcessingImpl : public AudioProcessing { bool output_copy_needed(bool is_data_processed) const; bool synthesis_needed(bool is_data_processed) const; bool analysis_needed(bool is_data_processed) const; - int InitializeExperimentalAgc() EXCLUSIVE_LOCKS_REQUIRED(crit_); - int InitializeTransient() EXCLUSIVE_LOCKS_REQUIRED(crit_); + void InitializeExperimentalAgc() EXCLUSIVE_LOCKS_REQUIRED(crit_); + void InitializeTransient() EXCLUSIVE_LOCKS_REQUIRED(crit_); void InitializeBeamformer() EXCLUSIVE_LOCKS_REQUIRED(crit_); EchoCancellationImpl* echo_cancellation_; @@ -217,6 +217,7 @@ class AudioProcessingImpl : public AudioProcessing { // Only set through the constructor's Config parameter. const bool use_new_agc_; rtc::scoped_ptr agc_manager_ GUARDED_BY(crit_); + int agc_startup_min_volume_; bool transient_suppressor_enabled_; rtc::scoped_ptr transient_suppressor_; diff --git a/webrtc/modules/audio_processing/include/audio_processing.h b/webrtc/modules/audio_processing/include/audio_processing.h index 72553ffa6..f91d53aff 100644 --- a/webrtc/modules/audio_processing/include/audio_processing.h +++ b/webrtc/modules/audio_processing/include/audio_processing.h @@ -72,12 +72,21 @@ struct ReportedDelay { bool enabled; }; -// Must be provided through AudioProcessing::Create(Confg&). It will have no -// impact if used with AudioProcessing::SetExtraOptions(). +// Use to enable experimental gain control (AGC). At startup the experimental +// AGC moves the microphone volume up to |startup_min_volume| if the current +// microphone volume is set too low. The value is clamped to its operating range +// [12, 255]. Here, 255 maps to 100%. +// +// Must be provided through AudioProcessing::Create(Confg&). +static const int kAgcStartupMinVolume = 85; struct ExperimentalAgc { - ExperimentalAgc() : enabled(true) {} - explicit ExperimentalAgc(bool enabled) : enabled(enabled) {} + ExperimentalAgc() : enabled(true), startup_min_volume(kAgcStartupMinVolume) {} + ExperimentalAgc(bool enabled) + : enabled(enabled), startup_min_volume(kAgcStartupMinVolume) {} + ExperimentalAgc(bool enabled, int startup_min_volume) + : enabled(enabled), startup_min_volume(startup_min_volume) {} bool enabled; + int startup_min_volume; }; // Use to enable experimental noise suppression. It can be set in the diff --git a/webrtc/tools/agc/agc_manager.cc b/webrtc/tools/agc/agc_manager.cc index 83c0d0075..3d7f624c9 100644 --- a/webrtc/tools/agc/agc_manager.cc +++ b/webrtc/tools/agc/agc_manager.cc @@ -147,25 +147,28 @@ AgcManager::AgcManager(VoiceEngine* voe) config.Set(new ExperimentalAgc(false)); audioproc_.reset(AudioProcessing::Create(config)); direct_.reset(new AgcManagerDirect(audioproc_->gain_control(), - volume_callbacks_.get())); + volume_callbacks_.get(), + kAgcStartupMinVolume)); media_callback_.reset(new MediaCallback(direct_.get(), audioproc_.get(), crit_.get())); preproc_callback_.reset(new PreprocCallback(direct_.get(), crit_.get())); } -AgcManager::AgcManager(VoEExternalMedia* media, VoEVolumeControl* volume, - Agc* agc, AudioProcessing* audioproc) +AgcManager::AgcManager(VoEExternalMedia* media, + VoEVolumeControl* volume, + Agc* agc, + AudioProcessing* audioproc) : media_(media), volume_callbacks_(new AgcManagerVolume(volume)), crit_(CriticalSectionWrapper::CreateCriticalSection()), audioproc_(audioproc), direct_(new AgcManagerDirect(agc, audioproc_->gain_control(), - volume_callbacks_.get())), - media_callback_(new MediaCallback(direct_.get(), - audioproc_.get(), - crit_.get())), + volume_callbacks_.get(), + kAgcStartupMinVolume)), + media_callback_( + new MediaCallback(direct_.get(), audioproc_.get(), crit_.get())), preproc_callback_(new PreprocCallback(direct_.get(), crit_.get())), enabled_(false), initialized_(false) {