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}
This commit is contained in:
Bjorn Volcker 2015-04-15 11:42:40 +02:00
parent 19a3807b36
commit adc46c4cf7
6 changed files with 53 additions and 33 deletions

View File

@ -48,7 +48,6 @@ const float kCompressionGainStep = 0.05f;
const int kMaxMicLevel = 255; const int kMaxMicLevel = 255;
static_assert(kGainMapSize > kMaxMicLevel, "gain map too small"); static_assert(kGainMapSize > kMaxMicLevel, "gain map too small");
const int kMinMicLevel = 12; const int kMinMicLevel = 12;
const int kMinInitMicLevel = 85;
// Prevent very large microphone level changes. // Prevent very large microphone level changes.
const int kMaxResidualGainChange = 15; const int kMaxResidualGainChange = 15;
@ -57,6 +56,10 @@ const int kMaxResidualGainChange = 15;
// restrictions from clipping events. // restrictions from clipping events.
const int kSurplusCompressionGain = 6; 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) { int LevelFromGainError(int gain_error, int level) {
assert(level >= 0 && level <= kMaxMicLevel); assert(level >= 0 && level <= kMaxMicLevel);
if (gain_error == 0) { if (gain_error == 0) {
@ -109,7 +112,8 @@ class DebugFile {
}; };
AgcManagerDirect::AgcManagerDirect(GainControl* gctrl, AgcManagerDirect::AgcManagerDirect(GainControl* gctrl,
VolumeCallbacks* volume_callbacks) VolumeCallbacks* volume_callbacks,
int startup_min_level)
: agc_(new Agc()), : agc_(new Agc()),
gctrl_(gctrl), gctrl_(gctrl),
volume_callbacks_(volume_callbacks), volume_callbacks_(volume_callbacks),
@ -123,13 +127,15 @@ AgcManagerDirect::AgcManagerDirect(GainControl* gctrl,
capture_muted_(false), capture_muted_(false),
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)),
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)
: agc_(agc), : agc_(agc),
gctrl_(gctrl), gctrl_(gctrl),
volume_callbacks_(volume_callbacks), volume_callbacks_(volume_callbacks),
@ -143,6 +149,7 @@ AgcManagerDirect::AgcManagerDirect(Agc* agc,
capture_muted_(false), capture_muted_(false),
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)),
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")) {
} }
@ -336,7 +343,7 @@ int AgcManagerDirect::CheckVolumeAndReset() {
} }
LOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level; LOG(LS_INFO) << "[agc] Initial GetMicVolume()=" << level;
int minLevel = startup_ ? kMinInitMicLevel : kMinMicLevel; int minLevel = startup_ ? startup_min_level_ : kMinMicLevel;
if (level < minLevel) { if (level < minLevel) {
level = minLevel; level = minLevel;
LOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level; LOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level;

View File

@ -41,12 +41,17 @@ class AgcManagerDirect {
public: public:
// AgcManagerDirect will configure GainControl internally. The user is // AgcManagerDirect will configure GainControl internally. The user is
// responsible for processing the audio using it after the call to Process. // 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 // 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);
~AgcManagerDirect(); ~AgcManagerDirect();
int Initialize(); int Initialize();
@ -88,6 +93,7 @@ class AgcManagerDirect {
bool capture_muted_; bool capture_muted_;
bool check_volume_on_next_process_; bool check_volume_on_next_process_;
bool startup_; bool startup_;
int startup_min_level_;
rtc::scoped_ptr<DebugFile> file_preproc_; rtc::scoped_ptr<DebugFile> file_preproc_;
rtc::scoped_ptr<DebugFile> file_postproc_; rtc::scoped_ptr<DebugFile> file_postproc_;

View File

@ -177,6 +177,7 @@ AudioProcessingImpl::AudioProcessingImpl(const Config& config,
#else #else
use_new_agc_(config.Get<ExperimentalAgc>().enabled), use_new_agc_(config.Get<ExperimentalAgc>().enabled),
#endif #endif
agc_startup_min_volume_(config.Get<ExperimentalAgc>().startup_min_volume),
transient_suppressor_enabled_(config.Get<ExperimentalNs>().enabled), transient_suppressor_enabled_(config.Get<ExperimentalNs>().enabled),
beamformer_enabled_(config.Get<Beamforming>().enabled), beamformer_enabled_(config.Get<Beamforming>().enabled),
beamformer_(beamformer), beamformer_(beamformer),
@ -285,15 +286,9 @@ int AudioProcessingImpl::InitializeLocked() {
} }
} }
int err = InitializeExperimentalAgc(); InitializeExperimentalAgc();
if (err != kNoError) {
return err;
}
err = InitializeTransient(); InitializeTransient();
if (err != kNoError) {
return err;
}
InitializeBeamformer(); InitializeBeamformer();
@ -959,19 +954,19 @@ bool AudioProcessingImpl::analysis_needed(bool is_data_processed) const {
return false; return false;
} }
int AudioProcessingImpl::InitializeExperimentalAgc() { void AudioProcessingImpl::InitializeExperimentalAgc() {
if (use_new_agc_) { if (use_new_agc_) {
if (!agc_manager_.get()) { if (!agc_manager_.get()) {
agc_manager_.reset( agc_manager_.reset(new AgcManagerDirect(gain_control_,
new AgcManagerDirect(gain_control_, gain_control_for_new_agc_.get())); gain_control_for_new_agc_.get(),
agc_startup_min_volume_));
} }
agc_manager_->Initialize(); agc_manager_->Initialize();
agc_manager_->SetCaptureMuted(output_will_be_muted_); agc_manager_->SetCaptureMuted(output_will_be_muted_);
} }
return kNoError;
} }
int AudioProcessingImpl::InitializeTransient() { void AudioProcessingImpl::InitializeTransient() {
if (transient_suppressor_enabled_) { if (transient_suppressor_enabled_) {
if (!transient_suppressor_.get()) { if (!transient_suppressor_.get()) {
transient_suppressor_.reset(new TransientSuppressor()); transient_suppressor_.reset(new TransientSuppressor());
@ -980,7 +975,6 @@ int AudioProcessingImpl::InitializeTransient() {
split_rate_, split_rate_,
fwd_out_format_.num_channels()); fwd_out_format_.num_channels());
} }
return kNoError;
} }
void AudioProcessingImpl::InitializeBeamformer() { void AudioProcessingImpl::InitializeBeamformer() {

View File

@ -170,8 +170,8 @@ class AudioProcessingImpl : public AudioProcessing {
bool output_copy_needed(bool is_data_processed) const; bool output_copy_needed(bool is_data_processed) const;
bool synthesis_needed(bool is_data_processed) const; bool synthesis_needed(bool is_data_processed) const;
bool analysis_needed(bool is_data_processed) const; bool analysis_needed(bool is_data_processed) const;
int InitializeExperimentalAgc() EXCLUSIVE_LOCKS_REQUIRED(crit_); void InitializeExperimentalAgc() EXCLUSIVE_LOCKS_REQUIRED(crit_);
int InitializeTransient() EXCLUSIVE_LOCKS_REQUIRED(crit_); void InitializeTransient() EXCLUSIVE_LOCKS_REQUIRED(crit_);
void InitializeBeamformer() EXCLUSIVE_LOCKS_REQUIRED(crit_); void InitializeBeamformer() EXCLUSIVE_LOCKS_REQUIRED(crit_);
EchoCancellationImpl* echo_cancellation_; EchoCancellationImpl* echo_cancellation_;
@ -217,6 +217,7 @@ class AudioProcessingImpl : public AudioProcessing {
// Only set through the constructor's Config parameter. // Only set through the constructor's Config parameter.
const bool use_new_agc_; const bool use_new_agc_;
rtc::scoped_ptr<AgcManagerDirect> agc_manager_ GUARDED_BY(crit_); rtc::scoped_ptr<AgcManagerDirect> agc_manager_ GUARDED_BY(crit_);
int agc_startup_min_volume_;
bool transient_suppressor_enabled_; bool transient_suppressor_enabled_;
rtc::scoped_ptr<TransientSuppressor> transient_suppressor_; rtc::scoped_ptr<TransientSuppressor> transient_suppressor_;

View File

@ -72,12 +72,21 @@ struct ReportedDelay {
bool enabled; bool enabled;
}; };
// Must be provided through AudioProcessing::Create(Confg&). It will have no // Use to enable experimental gain control (AGC). At startup the experimental
// impact if used with AudioProcessing::SetExtraOptions(). // 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 { struct ExperimentalAgc {
ExperimentalAgc() : enabled(true) {} ExperimentalAgc() : enabled(true), startup_min_volume(kAgcStartupMinVolume) {}
explicit ExperimentalAgc(bool enabled) : enabled(enabled) {} 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; bool enabled;
int startup_min_volume;
}; };
// 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

@ -147,25 +147,28 @@ AgcManager::AgcManager(VoiceEngine* voe)
config.Set<ExperimentalAgc>(new ExperimentalAgc(false)); config.Set<ExperimentalAgc>(new ExperimentalAgc(false));
audioproc_.reset(AudioProcessing::Create(config)); audioproc_.reset(AudioProcessing::Create(config));
direct_.reset(new AgcManagerDirect(audioproc_->gain_control(), direct_.reset(new AgcManagerDirect(audioproc_->gain_control(),
volume_callbacks_.get())); volume_callbacks_.get(),
kAgcStartupMinVolume));
media_callback_.reset(new MediaCallback(direct_.get(), media_callback_.reset(new MediaCallback(direct_.get(),
audioproc_.get(), audioproc_.get(),
crit_.get())); crit_.get()));
preproc_callback_.reset(new PreprocCallback(direct_.get(), crit_.get())); preproc_callback_.reset(new PreprocCallback(direct_.get(), crit_.get()));
} }
AgcManager::AgcManager(VoEExternalMedia* media, VoEVolumeControl* volume, AgcManager::AgcManager(VoEExternalMedia* media,
Agc* agc, AudioProcessing* audioproc) VoEVolumeControl* volume,
Agc* agc,
AudioProcessing* audioproc)
: media_(media), : media_(media),
volume_callbacks_(new AgcManagerVolume(volume)), volume_callbacks_(new AgcManagerVolume(volume)),
crit_(CriticalSectionWrapper::CreateCriticalSection()), crit_(CriticalSectionWrapper::CreateCriticalSection()),
audioproc_(audioproc), audioproc_(audioproc),
direct_(new AgcManagerDirect(agc, direct_(new AgcManagerDirect(agc,
audioproc_->gain_control(), audioproc_->gain_control(),
volume_callbacks_.get())), volume_callbacks_.get(),
media_callback_(new MediaCallback(direct_.get(), kAgcStartupMinVolume)),
audioproc_.get(), media_callback_(
crit_.get())), new MediaCallback(direct_.get(), audioproc_.get(), crit_.get())),
preproc_callback_(new PreprocCallback(direct_.get(), crit_.get())), preproc_callback_(new PreprocCallback(direct_.get(), crit_.get())),
enabled_(false), enabled_(false),
initialized_(false) { initialized_(false) {