diff --git a/modules/audio_processing/agc2/BUILD.gn b/modules/audio_processing/agc2/BUILD.gn index 1bdafc46f3..cdea022ea8 100644 --- a/modules/audio_processing/agc2/BUILD.gn +++ b/modules/audio_processing/agc2/BUILD.gn @@ -187,36 +187,6 @@ rtc_library("input_volume_controller") { sources = [ "input_volume_controller.cc", "input_volume_controller.h", - ] - - configs += [ "..:apm_debug_dump" ] - - deps = [ - ":clipping_predictor", - ":gain_map", - "..:api", - "..:apm_logging", - "..:audio_buffer", - "..:audio_frame_view", - "../../../api:array_view", - "../../../common_audio", - "../../../common_audio:common_audio_c", - "../../../rtc_base:checks", - "../../../rtc_base:gtest_prod", - "../../../rtc_base:logging", - "../../../rtc_base:safe_minmax", - "../../../system_wrappers:field_trial", - "../../../system_wrappers:metrics", - "../agc:gain_control_interface", - "../agc:level_estimation", - "../vad", - ] - - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] -} - -rtc_library("speech_probability_buffer") { - sources = [ "speech_probability_buffer.cc", "speech_probability_buffer.h", ] @@ -226,10 +196,26 @@ rtc_library("speech_probability_buffer") { "./*", ] + configs += [ "..:apm_debug_dump" ] + deps = [ + ":clipping_predictor", + ":gain_map", + "..:api", + "..:audio_buffer", + "..:audio_frame_view", + "../../../api:array_view", + "../../../rtc_base:checks", "../../../rtc_base:checks", "../../../rtc_base:gtest_prod", + "../../../rtc_base:gtest_prod", + "../../../rtc_base:logging", + "../../../rtc_base:safe_minmax", + "../../../system_wrappers:field_trial", + "../../../system_wrappers:metrics", ] + + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } rtc_library("noise_level_estimator") { @@ -381,6 +367,7 @@ rtc_library("input_volume_controller_unittests") { sources = [ "clipping_predictor_level_buffer_unittest.cc", "clipping_predictor_unittest.cc", + "input_volume_controller_unittest.cc", "speech_probability_buffer_unittest.cc", ] @@ -389,11 +376,19 @@ rtc_library("input_volume_controller_unittests") { deps = [ ":clipping_predictor", ":gain_map", - ":speech_probability_buffer", + ":input_volume_controller", + "..:api", + "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:random", "../../../rtc_base:safe_conversions", + "../../../rtc_base:safe_minmax", + "../../../rtc_base:stringutils", + "../../../system_wrappers:metrics", + "../../../test:field_trial", + "../../../test:fileutils", "../../../test:test_support", + "//testing/gtest", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] diff --git a/modules/audio_processing/agc2/input_volume_controller.cc b/modules/audio_processing/agc2/input_volume_controller.cc index bc8b735982..116136e923 100644 --- a/modules/audio_processing/agc2/input_volume_controller.cc +++ b/modules/audio_processing/agc2/input_volume_controller.cc @@ -14,8 +14,6 @@ #include #include "api/array_view.h" -#include "common_audio/include/audio_util.h" -#include "modules/audio_processing/agc/gain_control.h" #include "modules/audio_processing/agc2/gain_map_internal.h" #include "modules/audio_processing/include/audio_frame_view.h" #include "rtc_base/checks.h" @@ -32,12 +30,6 @@ namespace { // quantization) before we assume the user has manually adjusted the microphone. constexpr int kLevelQuantizationSlack = 25; -constexpr int kDefaultCompressionGain = 7; -constexpr int kMaxCompressionGain = 12; -constexpr int kMinCompressionGain = 2; -// Controls the rate of compression changes towards the target. -constexpr float kCompressionGainStep = 0.05f; - constexpr int kMaxMicLevel = 255; static_assert(kGainMapSize > kMaxMicLevel, "gain map too small"); constexpr int kMinMicLevel = 12; @@ -45,23 +37,25 @@ constexpr int kMinMicLevel = 12; // Prevent very large microphone level changes. constexpr int kMaxResidualGainChange = 15; -// Maximum additional gain allowed to compensate for microphone level -// restrictions from clipping events. -constexpr int kSurplusCompressionGain = 6; - // Target speech level (dBFs) and speech probability threshold used to compute -// the RMS error override in `GetSpeechLevelErrorDb()`. These are only used for -// computing the error override and they are not passed to `agc_`. -// TODO(webrtc:7494): Move these to a config and pass in the ctor. +// the RMS error in `GetSpeechLevelErrorDb()`. +// TODO(webrtc:7494): Move these to a config and pass in the ctor with +// kOverrideWaitFrames = 100. constexpr float kOverrideTargetSpeechLevelDbfs = -18.0f; constexpr float kOverrideSpeechProbabilitySilenceThreshold = 0.5f; -// The minimum number of frames between `UpdateGain()` calls. -// TODO(webrtc:7494): Move this to a config and pass in the ctor with -// kOverrideWaitFrames = 100. Default value zero needed for the unit tests. constexpr int kOverrideWaitFrames = 0; -using AnalogAgcConfig = - AudioProcessing::Config::GainController1::AnalogGainController; +using Agc1ClippingPredictorConfig = AudioProcessing::Config::GainController1:: + AnalogGainController::ClippingPredictor; + +// TODO(webrtc:7494): Hardcode clipping predictor parameters and remove this +// function after no longer needed in the ctor. +Agc1ClippingPredictorConfig CreateClippingPredictorConfig(bool enabled) { + Agc1ClippingPredictorConfig config; + config.enabled = enabled; + + return config; +} // Returns whether a fall-back solution to choose the maximum level should be // chosen. @@ -169,42 +163,33 @@ int GetSpeechLevelErrorDb(float speech_level_dbfs, float speech_probability) { } // namespace -RecommendedInputVolumeEstimator::RecommendedInputVolumeEstimator( - ApmDataDumper* data_dumper, +MonoInputVolumeController::MonoInputVolumeController( int startup_min_level, int clipped_level_min, bool disable_digital_adaptive, - int min_mic_level) + int min_mic_level, + int max_digital_gain_db, + int min_digital_gain_db) : min_mic_level_(min_mic_level), disable_digital_adaptive_(disable_digital_adaptive), - agc_(std::make_unique()), + max_digital_gain_db_(max_digital_gain_db), + min_digital_gain_db_(min_digital_gain_db), max_level_(kMaxMicLevel), - max_compression_gain_(kMaxCompressionGain), - target_compression_(kDefaultCompressionGain), - compression_(target_compression_), - compression_accumulator_(compression_), startup_min_level_(ClampLevel(startup_min_level, min_mic_level_)), clipped_level_min_(clipped_level_min) {} -RecommendedInputVolumeEstimator::~RecommendedInputVolumeEstimator() = default; +MonoInputVolumeController::~MonoInputVolumeController() = default; -void RecommendedInputVolumeEstimator::Initialize() { +void MonoInputVolumeController::Initialize() { max_level_ = kMaxMicLevel; - max_compression_gain_ = kMaxCompressionGain; - target_compression_ = disable_digital_adaptive_ ? 0 : kDefaultCompressionGain; - compression_ = disable_digital_adaptive_ ? 0 : target_compression_; - compression_accumulator_ = compression_; capture_output_used_ = true; check_volume_on_next_process_ = true; frames_since_update_gain_ = 0; is_first_frame_ = true; } -void RecommendedInputVolumeEstimator::Process( - rtc::ArrayView audio, +void MonoInputVolumeController::Process( absl::optional rms_error_override) { - new_compression_to_set_ = absl::nullopt; - if (check_volume_on_next_process_) { check_volume_on_next_process_ = false; // We have to wait until the first process call to check the volume, @@ -212,14 +197,8 @@ void RecommendedInputVolumeEstimator::Process( CheckVolumeAndReset(); } - agc_->Process(audio); - - // Always check if `agc_` has a new error available. If yes, `agc_` gets - // reset. - // TODO(webrtc:7494) Replace the `agc_` call `GetRmsErrorDb()` with `Reset()` - // if an error override is used. int rms_error = 0; - bool update_gain = agc_->GetRmsErrorDb(&rms_error); + bool update_gain = false; if (rms_error_override.has_value()) { if (is_first_frame_ || frames_since_update_gain_ < kOverrideWaitFrames) { update_gain = false; @@ -233,17 +212,13 @@ void RecommendedInputVolumeEstimator::Process( UpdateGain(rms_error); } - if (!disable_digital_adaptive_) { - UpdateCompressor(); - } - is_first_frame_ = false; if (frames_since_update_gain_ < kOverrideWaitFrames) { ++frames_since_update_gain_; } } -void RecommendedInputVolumeEstimator::HandleClipping(int clipped_level_step) { +void MonoInputVolumeController::HandleClipping(int clipped_level_step) { RTC_DCHECK_GT(clipped_level_step, 0); // Always decrease the maximum level, even if the current level is below // threshold. @@ -257,14 +232,12 @@ void RecommendedInputVolumeEstimator::HandleClipping(int clipped_level_step) { // 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_ - clipped_level_step)); - // Reset the AGCs for all channels since the level has changed. - agc_->Reset(); frames_since_update_gain_ = 0; is_first_frame_ = false; } } -void RecommendedInputVolumeEstimator::SetLevel(int new_level) { +void MonoInputVolumeController::SetLevel(int new_level) { int voe_level = recommended_input_volume_; if (voe_level == 0) { RTC_DLOG(LS_INFO) @@ -292,9 +265,7 @@ void RecommendedInputVolumeEstimator::SetLevel(int new_level) { SetMaxLevel(level_); } // Take no action in this case, since we can't be sure when the volume - // was manually adjusted. The compressor will still provide some of the - // desired gain change. - agc_->Reset(); + // was manually adjusted. frames_since_update_gain_ = 0; is_first_frame_ = false; return; @@ -311,21 +282,13 @@ void RecommendedInputVolumeEstimator::SetLevel(int new_level) { level_ = new_level; } -void RecommendedInputVolumeEstimator::SetMaxLevel(int level) { +void MonoInputVolumeController::SetMaxLevel(int level) { 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 - clipped_level_min_) * - kSurplusCompressionGain + - 0.5f); - RTC_DLOG(LS_INFO) << "[agc] max_level_=" << max_level_ - << ", max_compression_gain_=" << max_compression_gain_; + RTC_DLOG(LS_INFO) << "[agc] max_level_=" << max_level_; } -void RecommendedInputVolumeEstimator::HandleCaptureOutputUsedChange( +void MonoInputVolumeController::HandleCaptureOutputUsedChange( bool capture_output_used) { if (capture_output_used_ == capture_output_used) { return; @@ -338,7 +301,7 @@ void RecommendedInputVolumeEstimator::HandleCaptureOutputUsedChange( } } -int RecommendedInputVolumeEstimator::CheckVolumeAndReset() { +int MonoInputVolumeController::CheckVolumeAndReset() { int level = recommended_input_volume_; // Reasons for taking action at startup: // 1) A person starting a call is expected to be heard. @@ -362,11 +325,12 @@ int RecommendedInputVolumeEstimator::CheckVolumeAndReset() { RTC_DLOG(LS_INFO) << "[agc] Initial volume too low, raising to " << level; recommended_input_volume_ = level; } - agc_->Reset(); + level_ = level; startup_ = false; frames_since_update_gain_ = 0; is_first_frame_ = true; + return 0; } @@ -376,136 +340,57 @@ int RecommendedInputVolumeEstimator::CheckVolumeAndReset() { // // If the slider needs to be moved, we check first if the user has adjusted // it, in which case we take no action and cache the updated level. -void RecommendedInputVolumeEstimator::UpdateGain(int rms_error_db) { +void MonoInputVolumeController::UpdateGain(int rms_error_db) { int rms_error = rms_error_db; // Always reset the counter regardless of whether the gain is changed - // or not. This matches with the bahvior of `agc_` where the histogram is - // reset every time an RMS error is successfully read. + // or not. frames_since_update_gain_ = 0; - // The compressor will always add at least kMinCompressionGain. In effect, - // this adjusts our target gain upward by the same amount and rms_error - // needs to reflect that. - rms_error += kMinCompressionGain; + int raw_digital_gain = 0; + if (!disable_digital_adaptive_) { + rms_error += min_digital_gain_db_; - // Handle as much error as possible with the compressor first. - int raw_compression = - rtc::SafeClamp(rms_error, kMinCompressionGain, max_compression_gain_); - - // Deemphasize the compression gain error. Move halfway between the current - // target and the newly received target. This serves to soften perceptible - // intra-talkspurt adjustments, at the cost of some adaptation speed. - if ((raw_compression == max_compression_gain_ && - target_compression_ == max_compression_gain_ - 1) || - (raw_compression == kMinCompressionGain && - target_compression_ == kMinCompressionGain + 1)) { - // Special case to allow the target to reach the endpoints of the - // compression range. The deemphasis would otherwise halt it at 1 dB shy. - target_compression_ = raw_compression; - } else { - target_compression_ = - (raw_compression - target_compression_) / 2 + target_compression_; + raw_digital_gain = + rtc::SafeClamp(rms_error, min_digital_gain_db_, max_digital_gain_db_); } - // Residual error will be handled by adjusting the volume slider. Use the - // raw rather than deemphasized compression here as we would otherwise - // shrink the amount of slack the compressor provides. const int residual_gain = - rtc::SafeClamp(rms_error - raw_compression, -kMaxResidualGainChange, + rtc::SafeClamp(rms_error - raw_digital_gain, -kMaxResidualGainChange, kMaxResidualGainChange); + RTC_DLOG(LS_INFO) << "[agc] rms_error=" << rms_error - << ", target_compression=" << target_compression_ << ", residual_gain=" << residual_gain; - if (residual_gain == 0) - return; - int old_level = level_; + if (residual_gain == 0) { + return; + } + SetLevel(LevelFromGainError(residual_gain, level_, min_mic_level_)); - if (old_level != level_) { - // level_ was updated by SetLevel; log the new value. - RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.AgcSetLevel", level_, 1, - kMaxMicLevel, 50); - // Reset the AGC since the level has changed. - agc_->Reset(); - } -} - -void RecommendedInputVolumeEstimator::UpdateCompressor() { - calls_since_last_gain_log_++; - if (calls_since_last_gain_log_ == 100) { - calls_since_last_gain_log_ = 0; - RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainApplied", - compression_, 0, kMaxCompressionGain, - kMaxCompressionGain + 1); - } - if (compression_ == target_compression_) { - return; - } - - // Adapt the compression gain slowly towards the target, in order to avoid - // highly perceptible changes. - if (target_compression_ > compression_) { - compression_accumulator_ += kCompressionGainStep; - } else { - compression_accumulator_ -= kCompressionGainStep; - } - - // The compressor accepts integer gains in dB. Adjust the gain when - // we've come within half a stepsize of the nearest integer. (We don't - // check for equality due to potential floating point imprecision). - int new_compression = compression_; - int nearest_neighbor = std::floor(compression_accumulator_ + 0.5); - if (std::fabs(compression_accumulator_ - nearest_neighbor) < - kCompressionGainStep / 2) { - new_compression = nearest_neighbor; - } - - // Set the new compression gain. - if (new_compression != compression_) { - RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc.DigitalGainUpdated", - new_compression, 0, kMaxCompressionGain, - kMaxCompressionGain + 1); - compression_ = new_compression; - compression_accumulator_ = new_compression; - new_compression_to_set_ = compression_; - } } std::atomic InputVolumeController::instance_counter_(0); -InputVolumeController::InputVolumeController( - const AudioProcessing::Config::GainController1::AnalogGainController& - analog_config, - Agc* agc) - : InputVolumeController(/*num_capture_channels=*/1, analog_config) { - RTC_DCHECK(channel_agcs_[0]); - RTC_DCHECK(agc); - channel_agcs_[0]->set_agc(agc); -} - -InputVolumeController::InputVolumeController( - int num_capture_channels, - const AnalogAgcConfig& analog_config) - : analog_controller_enabled_(analog_config.enabled), +InputVolumeController::InputVolumeController(int num_capture_channels, + const Config& config) + : analog_controller_enabled_(config.enabled), min_mic_level_override_(GetMinMicLevelOverride()), - data_dumper_(new ApmDataDumper(instance_counter_.fetch_add(1) + 1)), use_min_channel_level_(!UseMaxAnalogChannelLevel()), num_capture_channels_(num_capture_channels), - disable_digital_adaptive_(!analog_config.enable_digital_adaptive), - frames_since_clipped_(analog_config.clipped_wait_frames), + disable_digital_adaptive_(!config.digital_adaptive_follows), + frames_since_clipped_(config.clipped_wait_frames), capture_output_used_(true), - clipped_level_step_(analog_config.clipped_level_step), - clipped_ratio_threshold_(analog_config.clipped_ratio_threshold), - clipped_wait_frames_(analog_config.clipped_wait_frames), - channel_agcs_(num_capture_channels), - new_compressions_to_set_(num_capture_channels), - clipping_predictor_( - CreateClippingPredictor(num_capture_channels, - analog_config.clipping_predictor)), + clipped_level_step_(config.clipped_level_step), + clipped_ratio_threshold_(config.clipped_ratio_threshold), + clipped_wait_frames_(config.clipped_wait_frames), + channel_controllers_(num_capture_channels), + clipping_predictor_(CreateClippingPredictor( + num_capture_channels, + CreateClippingPredictorConfig(config.enable_clipping_predictor))), use_clipping_predictor_step_( !!clipping_predictor_ && - analog_config.clipping_predictor.use_predicted_step), + CreateClippingPredictorConfig(config.enable_clipping_predictor) + .use_predicted_step), clipping_rate_log_(0.0f), clipping_rate_log_counter_(0) { RTC_LOG(LS_INFO) << "[agc] analog controller enabled: " @@ -515,32 +400,30 @@ InputVolumeController::InputVolumeController( << " (overridden: " << (min_mic_level_override_.has_value() ? "yes" : "no") << ")"; - RTC_LOG(LS_INFO) << "[agc] Startup min volume: " - << analog_config.startup_min_volume; - for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { - ApmDataDumper* data_dumper_ch = ch == 0 ? data_dumper_.get() : nullptr; + RTC_LOG(LS_INFO) << "[agc] Startup min volume: " << config.startup_min_volume; - channel_agcs_[ch] = std::make_unique( - data_dumper_ch, analog_config.startup_min_volume, - analog_config.clipped_level_min, disable_digital_adaptive_, - min_mic_level); + for (auto& controller : channel_controllers_) { + controller = std::make_unique( + config.startup_min_volume, config.clipped_level_min, + disable_digital_adaptive_, min_mic_level, config.max_digital_gain_db, + config.min_digital_gain_db); } - RTC_DCHECK(!channel_agcs_.empty()); + + RTC_DCHECK(!channel_controllers_.empty()); RTC_DCHECK_GT(clipped_level_step_, 0); RTC_DCHECK_LE(clipped_level_step_, 255); RTC_DCHECK_GT(clipped_ratio_threshold_, 0.0f); RTC_DCHECK_LT(clipped_ratio_threshold_, 1.0f); RTC_DCHECK_GT(clipped_wait_frames_, 0); - channel_agcs_[0]->ActivateLogging(); + channel_controllers_[0]->ActivateLogging(); } InputVolumeController::~InputVolumeController() {} void InputVolumeController::Initialize() { RTC_DLOG(LS_INFO) << "InputVolumeController::Initialize"; - data_dumper_->InitiateNewSetOfRecordings(); - for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { - channel_agcs_[ch]->Initialize(); + for (auto& controller : channel_controllers_) { + controller->Initialize(); } capture_output_used_ = true; @@ -549,26 +432,6 @@ void InputVolumeController::Initialize() { clipping_rate_log_counter_ = 0; } -void InputVolumeController::SetupDigitalGainControl( - GainControl& gain_control) const { - if (gain_control.set_mode(GainControl::kFixedDigital) != 0) { - RTC_LOG(LS_ERROR) << "set_mode(GainControl::kFixedDigital) failed."; - } - const int target_level_dbfs = disable_digital_adaptive_ ? 0 : 2; - if (gain_control.set_target_level_dbfs(target_level_dbfs) != 0) { - RTC_LOG(LS_ERROR) << "set_target_level_dbfs() failed."; - } - const int compression_gain_db = - disable_digital_adaptive_ ? 0 : kDefaultCompressionGain; - if (gain_control.set_compression_gain_db(compression_gain_db) != 0) { - RTC_LOG(LS_ERROR) << "set_compression_gain_db() failed."; - } - const bool enable_limiter = !disable_digital_adaptive_; - if (gain_control.enable_limiter(enable_limiter) != 0) { - RTC_LOG(LS_ERROR) << "enable_limiter() failed."; - } -} - void InputVolumeController::AnalyzePreProcess(const AudioBuffer& audio_buffer) { const float* const* audio = audio_buffer.channels_const(); size_t samples_per_channel = audio_buffer.num_frames(); @@ -585,15 +448,13 @@ void InputVolumeController::AnalyzePreProcess(const AudioBuffer& audio_buffer) { clipping_predictor_->Analyze(frame); } - // Check for clipped samples, as the AGC has difficulty detecting pitch - // under clipping distortion. We do this in the preprocessing phase in order + // Check for clipped samples. We do this in the preprocessing phase in order // to catch clipped echo as well. // // If we find a sufficiently clipped frame, drop the current microphone level // and enforce a new maximum level, dropped the same amount from the current // maximum. This harsh treatment is an effort to avoid repeated clipped echo - // events. As compensation for this restriction, the maximum compression - // gain is increased, through SetMaxLevel(). + // events. float clipped_ratio = ComputeClippedRatio(audio, num_capture_channels_, samples_per_channel); clipping_rate_log_ = std::max(clipped_ratio, clipping_rate_log_); @@ -617,7 +478,7 @@ void InputVolumeController::AnalyzePreProcess(const AudioBuffer& audio_buffer) { for (int channel = 0; channel < num_capture_channels_; ++channel) { const auto step = clipping_predictor_->EstimateClippedLevelStep( channel, recommended_input_volume_, clipped_level_step_, - channel_agcs_[channel]->min_mic_level(), kMaxMicLevel); + channel_controllers_[channel]->min_mic_level(), kMaxMicLevel); if (step.has_value()) { predicted_step = std::max(predicted_step, step.value()); clipping_predicted = true; @@ -638,7 +499,7 @@ void InputVolumeController::AnalyzePreProcess(const AudioBuffer& audio_buffer) { } if (clipping_detected || (clipping_predicted && use_clipping_predictor_step_)) { - for (auto& state_ch : channel_agcs_) { + for (auto& state_ch : channel_controllers_) { state_ch->HandleClipping(step); } frames_since_clipped_ = 0; @@ -649,13 +510,7 @@ void InputVolumeController::AnalyzePreProcess(const AudioBuffer& audio_buffer) { AggregateChannelLevels(); } -void InputVolumeController::Process(const AudioBuffer& audio_buffer) { - Process(audio_buffer, /*speech_probability=*/absl::nullopt, - /*speech_level_dbfs=*/absl::nullopt); -} - -void InputVolumeController::Process(const AudioBuffer& audio_buffer, - absl::optional speech_probability, +void InputVolumeController::Process(absl::optional speech_probability, absl::optional speech_level_dbfs) { AggregateChannelLevels(); @@ -663,53 +518,34 @@ void InputVolumeController::Process(const AudioBuffer& audio_buffer, return; } - const size_t num_frames_per_band = audio_buffer.num_frames_per_band(); - absl::optional rms_error_override = absl::nullopt; + absl::optional rms_error_override; if (speech_probability.has_value() && speech_level_dbfs.has_value()) { rms_error_override = GetSpeechLevelErrorDb(*speech_level_dbfs, *speech_probability); } - for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { - std::array audio_data; - int16_t* audio_use = audio_data.data(); - FloatS16ToS16(audio_buffer.split_bands_const_f(ch)[0], num_frames_per_band, - audio_use); - channel_agcs_[ch]->Process({audio_use, num_frames_per_band}, - rms_error_override); - new_compressions_to_set_[ch] = channel_agcs_[ch]->new_compression(); + + for (auto& controller : channel_controllers_) { + controller->Process(rms_error_override); } AggregateChannelLevels(); } -absl::optional InputVolumeController::GetDigitalComressionGain() { - return new_compressions_to_set_[channel_controlling_gain_]; -} - void InputVolumeController::HandleCaptureOutputUsedChange( bool capture_output_used) { - for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { - channel_agcs_[ch]->HandleCaptureOutputUsedChange(capture_output_used); + for (auto& controller : channel_controllers_) { + controller->HandleCaptureOutputUsedChange(capture_output_used); } capture_output_used_ = capture_output_used; } -float InputVolumeController::voice_probability() const { - float max_prob = 0.f; - for (const auto& state_ch : channel_agcs_) { - max_prob = std::max(max_prob, state_ch->voice_probability()); - } - - return max_prob; -} - void InputVolumeController::set_stream_analog_level(int level) { if (!analog_controller_enabled_) { recommended_input_volume_ = level; } - for (size_t ch = 0; ch < channel_agcs_.size(); ++ch) { - channel_agcs_[ch]->set_stream_analog_level(level); + for (auto& controller : channel_controllers_) { + controller->set_stream_analog_level(level); } AggregateChannelLevels(); @@ -717,19 +553,19 @@ void InputVolumeController::set_stream_analog_level(int level) { void InputVolumeController::AggregateChannelLevels() { int new_recommended_input_volume = - channel_agcs_[0]->recommended_analog_level(); + channel_controllers_[0]->recommended_analog_level(); channel_controlling_gain_ = 0; if (use_min_channel_level_) { - for (size_t ch = 1; ch < channel_agcs_.size(); ++ch) { - int level = channel_agcs_[ch]->recommended_analog_level(); + for (size_t ch = 1; ch < channel_controllers_.size(); ++ch) { + int level = channel_controllers_[ch]->recommended_analog_level(); if (level < new_recommended_input_volume) { new_recommended_input_volume = level; channel_controlling_gain_ = static_cast(ch); } } } else { - for (size_t ch = 1; ch < channel_agcs_.size(); ++ch) { - int level = channel_agcs_[ch]->recommended_analog_level(); + for (size_t ch = 1; ch < channel_controllers_.size(); ++ch) { + int level = channel_controllers_[ch]->recommended_analog_level(); if (level > new_recommended_input_volume) { new_recommended_input_volume = level; channel_controlling_gain_ = static_cast(ch); diff --git a/modules/audio_processing/agc2/input_volume_controller.h b/modules/audio_processing/agc2/input_volume_controller.h index f8524aa0ae..423c4435ff 100644 --- a/modules/audio_processing/agc2/input_volume_controller.h +++ b/modules/audio_processing/agc2/input_volume_controller.h @@ -17,35 +17,55 @@ #include "absl/types/optional.h" #include "api/array_view.h" -#include "modules/audio_processing/agc/agc.h" #include "modules/audio_processing/agc2/clipping_predictor.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/include/audio_processing.h" -#include "modules/audio_processing/logging/apm_data_dumper.h" #include "rtc_base/gtest_prod_util.h" namespace webrtc { -class RecommendedInputVolumeEstimator; -class GainControl; +class MonoInputVolumeController; -// Adaptive Gain Controller (AGC) that controls the input volume and a digital -// gain. The input volume controller recommends what volume to use, handles -// volume changes and clipping. In particular, it handles changes triggered by -// the user (e.g., volume set to zero by a HW mute button). The digital -// controller chooses and applies the digital compression gain. -// This class is not thread-safe. +// Input volume controller that controls the input volume. The input volume +// controller recommends what volume to use, handles volume changes and +// clipping. In particular, it handles changes triggered by the user (e.g., +// volume set to zero by a HW mute button). The digital controller chooses and +// applies the digital compression gain. This class is not thread-safe. // TODO(bugs.webrtc.org/7494): Use applied/recommended input volume naming // convention. class InputVolumeController final { public: + // Config for the constructor. + struct Config { + bool enabled = false; + // TODO(bugs.webrtc.org/1275566): Describe `startup_min_volume`. + int startup_min_volume = 0; + // Lowest analog microphone level that will be applied in response to + // clipping. + int clipped_level_min = 70; + // If true, an adaptive digital gain is applied. + bool digital_adaptive_follows = 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; + // Enables clipping prediction functionality. + bool enable_clipping_predictor = false; + // Minimum and maximum digital gain used before input volume is + // adjusted. + int max_digital_gain_db = 30; + int min_digital_gain_db = 0; + }; + // Ctor. `num_capture_channels` specifies the number of channels for the audio // passed to `AnalyzePreProcess()` and `Process()`. Clamps - // `analog_config.startup_min_level` in the [12, 255] range. - InputVolumeController( - int num_capture_channels, - const AudioProcessing::Config::GainController1::AnalogGainController& - analog_config); + // `config.startup_min_level` in the [12, 255] range. + InputVolumeController(int num_capture_channels, const Config& config); ~InputVolumeController(); InputVolumeController(const InputVolumeController&) = delete; @@ -53,11 +73,6 @@ class InputVolumeController final { void Initialize(); - // Configures `gain_control` to work as a fixed digital controller so that the - // adaptive part is only handled by this gain controller. Must be called if - // `gain_control` is also used to avoid the side-effects of running two AGCs. - void SetupDigitalGainControl(GainControl& gain_control) const; - // Sets the applied input volume. void set_stream_analog_level(int level); @@ -69,20 +84,13 @@ class InputVolumeController final { // prediction (if enabled). Must be called after `set_stream_analog_level()`. void AnalyzePreProcess(const AudioBuffer& audio_buffer); - // Processes `audio_buffer`. Chooses a digital compression gain and the new - // input volume to recommend. Must be called after `AnalyzePreProcess()`. If - // `speech_probability` (range [0.0f, 1.0f]) and `speech_level_dbfs` (range - // [-90.f, 30.0f]) are given, uses them to override the estimated RMS error. - // TODO(webrtc:7494): This signature is needed for testing purposes, unify - // the signatures when the clean-up is done. - void Process(const AudioBuffer& audio_buffer, - absl::optional speech_probability, + // Chooses a digital compression gain and the new input volume to recommend. + // Must be called after `AnalyzePreProcess()`. `speech_probability` + // (range [0.0f, 1.0f]) and `speech_level_dbfs` (range [-90.f, 30.0f]) are + // used to compute the RMS error. + void Process(absl::optional speech_probability, absl::optional speech_level_dbfs); - // Processes `audio_buffer`. Chooses a digital compression gain and the new - // input volume to recommend. Must be called after `AnalyzePreProcess()`. - void Process(const AudioBuffer& audio_buffer); - // TODO(bugs.webrtc.org/7494): Return recommended input volume and remove // `recommended_analog_level()`. // Returns the recommended input volume. If the input volume contoller is @@ -138,25 +146,17 @@ class InputVolumeController final { UnusedClippingPredictionsProduceEqualAnalogLevels); FRIEND_TEST_ALL_PREFIXES(InputVolumeControllerParametrizedTest, EmptyRmsErrorOverrideHasNoEffect); - FRIEND_TEST_ALL_PREFIXES(InputVolumeControllerParametrizedTest, - NonEmptyRmsErrorOverrideHasEffect); - - // Ctor that creates a single channel AGC and by injecting `agc`. - // `agc` will be owned by this class; hence, do not delete it. - InputVolumeController( - const AudioProcessing::Config::GainController1::AnalogGainController& - analog_config, - Agc* agc); void AggregateChannelLevels(); const bool analog_controller_enabled_; const absl::optional min_mic_level_override_; - std::unique_ptr data_dumper_; static std::atomic instance_counter_; const bool use_min_channel_level_; const int num_capture_channels_; + + // TODO(webrtc:7494): Replace with `digital_adaptive_follows_`. const bool disable_digital_adaptive_; int frames_since_clipped_; @@ -178,8 +178,7 @@ class InputVolumeController final { const float clipped_ratio_threshold_; const int clipped_wait_frames_; - std::vector> channel_agcs_; - std::vector> new_compressions_to_set_; + std::vector> channel_controllers_; const std::unique_ptr clipping_predictor_; const bool use_clipping_predictor_step_; @@ -189,18 +188,18 @@ class InputVolumeController final { // TODO(bugs.webrtc.org/7494): Use applied/recommended input volume naming // convention. -class RecommendedInputVolumeEstimator { +class MonoInputVolumeController { public: - RecommendedInputVolumeEstimator(ApmDataDumper* data_dumper, - int startup_min_level, - int clipped_level_min, - bool disable_digital_adaptive, - int min_mic_level); - ~RecommendedInputVolumeEstimator(); - RecommendedInputVolumeEstimator(const RecommendedInputVolumeEstimator&) = + MonoInputVolumeController(int startup_min_level, + int clipped_level_min, + bool disable_digital_adaptive, + int min_mic_level, + int max_digital_gain_db, + int min_digital_gain_db); + ~MonoInputVolumeController(); + MonoInputVolumeController(const MonoInputVolumeController&) = delete; + MonoInputVolumeController& operator=(const MonoInputVolumeController&) = delete; - RecommendedInputVolumeEstimator& operator=( - const RecommendedInputVolumeEstimator&) = delete; void Initialize(); void HandleCaptureOutputUsedChange(bool capture_output_used); @@ -213,25 +212,16 @@ class RecommendedInputVolumeEstimator { // `set_stream_analog_level()`. void HandleClipping(int clipped_level_step); - // Analyzes `audio`, requests the RMS error from AGC, updates the recommended - // input volume based on the estimated speech level and, if enabled, updates - // the (digital) compression gain to be applied by `agc_`. Must be called - // after `HandleClipping()`. If `rms_error_override` has a value, RMS error - // from AGC is overridden by it. - void Process(rtc::ArrayView audio, - absl::optional rms_error_override); + // Updates the recommended input volume based on the estimated speech level + // RMS error. Must be called after `HandleClipping()`. + void Process(absl::optional rms_error_override); // Returns the recommended input volume. Must be called after `Process()`. int recommended_analog_level() const { return recommended_input_volume_; } - float voice_probability() const { return agc_->voice_probability(); } void ActivateLogging() { log_to_histograms_ = true; } - absl::optional new_compression() const { - return new_compression_to_set_; - } // Only used for testing. - void set_agc(Agc* agc) { agc_.reset(agc); } int min_mic_level() const { return min_mic_level_; } int startup_min_level() const { return startup_min_level_; } @@ -240,29 +230,27 @@ class RecommendedInputVolumeEstimator { // by the user, in which case no action is taken. void SetLevel(int new_level); - // Set the maximum input volume the AGC is allowed to apply. Also updates the - // maximum compression gain to compensate. The volume must be at least - // `kClippedLevelMin`. + // Set the maximum input volume the input volume controller is allowed to + // apply. The volume must be at least `kClippedLevelMin`. void SetMaxLevel(int level); int CheckVolumeAndReset(); void UpdateGain(int rms_error_db); - void UpdateCompressor(); const int min_mic_level_; + + // TODO(webrtc:7494): Replace with `digital_adaptive_follows_`. const bool disable_digital_adaptive_; - std::unique_ptr agc_; + const int max_digital_gain_db_; + const int min_digital_gain_db_; + int level_ = 0; int max_level_; - int max_compression_gain_; - int target_compression_; - int compression_; - float compression_accumulator_; + bool capture_output_used_ = true; bool check_volume_on_next_process_ = true; bool startup_ = true; int startup_min_level_; - int calls_since_last_gain_log_ = 0; // TODO(bugs.webrtc.org/7494): Create a separate member for the applied // input volume. @@ -272,13 +260,12 @@ class RecommendedInputVolumeEstimator { // recommended input volume. int recommended_input_volume_ = 0; - absl::optional new_compression_to_set_; bool log_to_histograms_ = false; + const int clipped_level_min_; // Frames since the last `UpdateGain()` call. int frames_since_update_gain_ = 0; - // Set to true for the first frame after startup and reset, otherwise false. bool is_first_frame_ = true; }; diff --git a/modules/audio_processing/agc2/input_volume_controller_unittest.cc b/modules/audio_processing/agc2/input_volume_controller_unittest.cc index e994fd0ae6..a4b73a42ee 100644 --- a/modules/audio_processing/agc2/input_volume_controller_unittest.cc +++ b/modules/audio_processing/agc2/input_volume_controller_unittest.cc @@ -17,9 +17,6 @@ #include #include -#include "modules/audio_processing/agc/gain_control.h" -#include "modules/audio_processing/agc/mock_agc.h" -#include "modules/audio_processing/include/mock_audio_processing.h" #include "rtc_base/numerics/safe_minmax.h" #include "rtc_base/strings/string_builder.h" #include "test/field_trial.h" @@ -47,55 +44,39 @@ constexpr float kClippedRatioThreshold = 0.1f; constexpr int kClippedWaitFrames = 300; constexpr float kHighSpeechProbability = 0.7f; constexpr float kSpeechLevel = -25.0f; +constexpr int kMaxDigitalGainDb = 12; +constexpr int kMinDigitalGainDb = 2; constexpr float kMinSample = std::numeric_limits::min(); constexpr float kMaxSample = std::numeric_limits::max(); -using AnalogAgcConfig = - AudioProcessing::Config::GainController1::AnalogGainController; using ClippingPredictorConfig = AudioProcessing::Config::GainController1:: AnalogGainController::ClippingPredictor; -constexpr AnalogAgcConfig kDefaultAnalogConfig{}; -class MockGainControl : public GainControl { - public: - virtual ~MockGainControl() {} - MOCK_METHOD(int, set_stream_analog_level, (int level), (override)); - MOCK_METHOD(int, stream_analog_level, (), (const, override)); - MOCK_METHOD(int, set_mode, (Mode mode), (override)); - MOCK_METHOD(Mode, mode, (), (const, override)); - MOCK_METHOD(int, set_target_level_dbfs, (int level), (override)); - MOCK_METHOD(int, target_level_dbfs, (), (const, override)); - MOCK_METHOD(int, set_compression_gain_db, (int gain), (override)); - MOCK_METHOD(int, compression_gain_db, (), (const, override)); - MOCK_METHOD(int, enable_limiter, (bool enable), (override)); - MOCK_METHOD(bool, is_limiter_enabled, (), (const, override)); - MOCK_METHOD(int, - set_analog_level_limits, - (int minimum, int maximum), - (override)); - MOCK_METHOD(int, analog_level_minimum, (), (const, override)); - MOCK_METHOD(int, analog_level_maximum, (), (const, override)); - MOCK_METHOD(bool, stream_is_saturated, (), (const, override)); -}; +using InputVolumeControllerConfig = InputVolumeController::Config; + +constexpr InputVolumeControllerConfig kDefaultInputVolumeControllerConfig{}; +constexpr ClippingPredictorConfig kDefaultClippingPredictorConfig{}; -// TODO(bugs.webrtc.org/12874): Remove and use designated initializers once -// fixed. std::unique_ptr CreateInputVolumeController( int startup_min_volume, int clipped_level_step, float clipped_ratio_threshold, int clipped_wait_frames, - const ClippingPredictorConfig& clipping_predictor_config = - kDefaultAnalogConfig.clipping_predictor) { - AnalogAgcConfig config; - config.startup_min_volume = startup_min_volume; - config.clipped_level_min = kClippedMin; - config.enable_digital_adaptive = false; - config.clipped_level_step = clipped_level_step; - config.clipped_ratio_threshold = clipped_ratio_threshold; - config.clipped_wait_frames = clipped_wait_frames; - config.clipping_predictor = clipping_predictor_config; + bool enable_clipping_predictor = false) { + InputVolumeControllerConfig config{ + .enabled = true, + .startup_min_volume = startup_min_volume, + .clipped_level_min = kClippedMin, + .digital_adaptive_follows = false, + .clipped_level_step = clipped_level_step, + .clipped_ratio_threshold = clipped_ratio_threshold, + .clipped_wait_frames = clipped_wait_frames, + .enable_clipping_predictor = enable_clipping_predictor, + .max_digital_gain_db = kMaxDigitalGainDb, + .min_digital_gain_db = kMinDigitalGainDb, + }; + return std::make_unique(/*num_capture_channels=*/1, config); } @@ -203,8 +184,7 @@ void CallPreProcessAndProcess(int num_calls, InputVolumeController& manager) { for (int n = 0; n < num_calls; ++n) { manager.AnalyzePreProcess(audio_buffer); - manager.Process(audio_buffer, speech_probability_override, - speech_level_override); + manager.Process(speech_probability_override, speech_level_override); } } @@ -232,40 +212,12 @@ class SpeechSamplesReader { RTC_CHECK(is_); } - // Reads `num_frames` 10 ms frames from the beginning of the PCM file, applies - // `gain_db` and feeds the frames into `agc` by calling `AnalyzePreProcess()` - // and `Process()` for each frame. Reads the number of 10 ms frames available - // in the PCM file if `num_frames` is too large - i.e., does not loop. - void Feed(int num_frames, int gain_db, InputVolumeController& agc) { - float gain = std::pow(10.0f, gain_db / 20.0f); // From dB to linear gain. - is_.seekg(0, is_.beg); // Start from the beginning of the PCM file. - - // Read and feed frames. - for (int i = 0; i < num_frames; ++i) { - is_.read(reinterpret_cast(buffer_.data()), buffer_num_bytes_); - if (is_.gcount() < buffer_num_bytes_) { - // EOF reached. Stop. - break; - } - // Apply gain and copy samples into `audio_buffer_`. - std::transform(buffer_.begin(), buffer_.end(), - audio_buffer_.channels()[0], [gain](int16_t v) -> float { - return rtc::SafeClamp(static_cast(v) * gain, - kMinSample, kMaxSample); - }); - - agc.AnalyzePreProcess(audio_buffer_); - agc.Process(audio_buffer_); - } - } - // Reads `num_frames` 10 ms frames from the beginning of the PCM file, applies // `gain_db` and feeds the frames into `agc` by calling `AnalyzePreProcess()` // and `Process()` for each frame. Reads the number of 10 ms frames available // in the PCM file if `num_frames` is too large - i.e., does not loop. // `speech_probability_override` and `speech_level_override` are passed to - // `Process()` where they are used to override the `agc` RMS error if they - // have a value. + // `Process()`. void Feed(int num_frames, int gain_db, absl::optional speech_probability_override, @@ -289,8 +241,7 @@ class SpeechSamplesReader { }); agc.AnalyzePreProcess(audio_buffer_); - agc.Process(audio_buffer_, speech_probability_override, - speech_level_override); + agc.Process(speech_probability_override, speech_level_override); } } @@ -305,29 +256,32 @@ class SpeechSamplesReader { // TODO(bugs.webrtc.org/12874): Use constexpr struct with designated // initializers once fixed. -constexpr AnalogAgcConfig GetAnalogAgcTestConfig() { - AnalogAgcConfig config; - config.enabled = true; - config.startup_min_volume = kInitialInputVolume; - config.clipped_level_min = kClippedMin; - config.enable_digital_adaptive = true; - config.clipped_level_step = kClippedLevelStep; - config.clipped_ratio_threshold = kClippedRatioThreshold; - config.clipped_wait_frames = kClippedWaitFrames; - config.clipping_predictor = kDefaultAnalogConfig.clipping_predictor; +constexpr InputVolumeControllerConfig GetInputVolumeControllerTestConfig() { + InputVolumeControllerConfig config{ + .enabled = true, + .startup_min_volume = kInitialInputVolume, + .clipped_level_min = kClippedMin, + .digital_adaptive_follows = true, + .clipped_level_step = kClippedLevelStep, + .clipped_ratio_threshold = kClippedRatioThreshold, + .clipped_wait_frames = kClippedWaitFrames, + .enable_clipping_predictor = kDefaultClippingPredictorConfig.enabled, + .max_digital_gain_db = kMaxDigitalGainDb, + .min_digital_gain_db = kMinDigitalGainDb, + }; return config; } -constexpr AnalogAgcConfig GetDisabledAnalogAgcConfig() { - AnalogAgcConfig config = GetAnalogAgcTestConfig(); +constexpr InputVolumeControllerConfig GetDisabledInputVolumeControllerConfig() { + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); config.enabled = false; return config; } // Helper class that provides an `InputVolumeController` instance with an -// injected `Agc` mock, an `AudioBuffer` instance and `CallAgcSequence()`, a -// helper method that runs the `InputVolumeController` instance on the -// `AudioBuffer` one by sticking to the API contract. +// `AudioBuffer` instance and `CallAgcSequence()`, a helper method that runs the +// `InputVolumeController` instance on the `AudioBuffer` one by sticking to the +// API contract. class InputVolumeControllerTestHelper { public: // Ctor. Initializes `audio_buffer` with zeros. @@ -338,10 +292,9 @@ class InputVolumeControllerTestHelper { kNumChannels, kSampleRateHz, kNumChannels), - mock_agc(new ::testing::NiceMock()), - manager(GetAnalogAgcTestConfig(), mock_agc) { + manager(/*num_capture_channels=*/1, + GetInputVolumeControllerTestConfig()) { manager.Initialize(); - manager.SetupDigitalGainControl(mock_gain_control); WriteAudioBufferSamples(/*samples_value=*/0.0f, /*clipped_ratio=*/0.0f, audio_buffer); } @@ -350,40 +303,25 @@ class InputVolumeControllerTestHelper { // contract, namely: // - Sets the applied input volume; // - Uses `audio_buffer` to call `AnalyzePreProcess()` and `Process()`; - // - Sets the digital compression gain, if specified, on the injected - // `mock_agc`. Returns the recommended input volume. The RMS error from - // AGC is replaced by an override value if `speech_probability_override` - // and `speech_level_override` have a value. + // Returns the recommended input volume. int CallAgcSequence(int applied_input_volume, absl::optional speech_probability_override, absl::optional speech_level_override) { manager.set_stream_analog_level(applied_input_volume); manager.AnalyzePreProcess(audio_buffer); - manager.Process(audio_buffer, speech_probability_override, - speech_level_override); - absl::optional digital_gain = manager.GetDigitalComressionGain(); - if (digital_gain) { - mock_gain_control.set_compression_gain_db(*digital_gain); - } + manager.Process(speech_probability_override, speech_level_override); + return manager.recommended_analog_level(); } // Deprecated. // TODO(bugs.webrtc.org/7494): Let the caller write `audio_buffer` and use - // `CallAgcSequence()`. The RMS error from AGC is replaced by an override - // value if `speech_probability_override` and `speech_level_override` have - // a value. + // `CallAgcSequence()`. void CallProcess(int num_calls, absl::optional speech_probability_override, absl::optional speech_level_override) { for (int i = 0; i < num_calls; ++i) { - EXPECT_CALL(*mock_agc, Process(_)).WillOnce(Return()); - manager.Process(audio_buffer, speech_probability_override, - speech_level_override); - absl::optional new_digital_gain = manager.GetDigitalComressionGain(); - if (new_digital_gain) { - mock_gain_control.set_compression_gain_db(*new_digital_gain); - } + manager.Process(speech_probability_override, speech_level_override); } } @@ -431,9 +369,7 @@ class InputVolumeControllerTestHelper { } AudioBuffer audio_buffer; - MockAgc* mock_agc; InputVolumeController manager; - MockGainControl mock_gain_control; }; class InputVolumeControllerParametrizedTest @@ -464,7 +400,7 @@ INSTANTIATE_TEST_SUITE_P( , InputVolumeControllerParametrizedTest, ::testing::Combine(testing::Values(absl::nullopt, 12, 20), - testing::Bool())); + testing::Values(true))); // Checks that when the analog controller is disabled, no downward adaptation // takes place. @@ -473,34 +409,32 @@ INSTANTIATE_TEST_SUITE_P( // differs. TEST_P(InputVolumeControllerParametrizedTest, DisabledAnalogAgcDoesNotAdaptDownwards) { - InputVolumeController manager_no_analog_agc(kNumChannels, - GetDisabledAnalogAgcConfig()); + InputVolumeController manager_no_analog_agc( + kNumChannels, GetDisabledInputVolumeControllerConfig()); manager_no_analog_agc.Initialize(); - InputVolumeController manager_with_analog_agc(kNumChannels, - GetAnalogAgcTestConfig()); + InputVolumeController manager_with_analog_agc( + kNumChannels, GetInputVolumeControllerTestConfig()); manager_with_analog_agc.Initialize(); AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz, kNumChannels, kSampleRateHz, kNumChannels); - constexpr int kAnalogLevel = 250; - static_assert(kAnalogLevel > kInitialInputVolume, "Increase `kAnalogLevel`."); - manager_no_analog_agc.set_stream_analog_level(kAnalogLevel); - manager_with_analog_agc.set_stream_analog_level(kAnalogLevel); + constexpr int kInputVolume = 250; + static_assert(kInputVolume > kInitialInputVolume, "Increase `kInputVolume`."); + manager_no_analog_agc.set_stream_analog_level(kInputVolume); + manager_with_analog_agc.set_stream_analog_level(kInputVolume); // Make a first call with input that doesn't clip in order to let the // controller read the input volume. That is needed because clipping input // causes the controller to stay in idle state for - // `AnalogAgcConfig::clipped_wait_frames` frames. + // `InputVolumeControllerConfig::clipped_wait_frames` frames. WriteAudioBufferSamples(/*samples_value=*/0.0f, /*clipping_ratio=*/0.0f, audio_buffer); manager_no_analog_agc.AnalyzePreProcess(audio_buffer); manager_with_analog_agc.AnalyzePreProcess(audio_buffer); - manager_no_analog_agc.Process(audio_buffer, - GetOverrideOrEmpty(kHighSpeechProbability), + manager_no_analog_agc.Process(GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(-18.0f)); - manager_with_analog_agc.Process(audio_buffer, - GetOverrideOrEmpty(kHighSpeechProbability), + manager_with_analog_agc.Process(GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(-18.0f)); // Feed clipping input to trigger a downward adapation of the analog level. @@ -508,17 +442,15 @@ TEST_P(InputVolumeControllerParametrizedTest, audio_buffer); manager_no_analog_agc.AnalyzePreProcess(audio_buffer); manager_with_analog_agc.AnalyzePreProcess(audio_buffer); - manager_no_analog_agc.Process(audio_buffer, - GetOverrideOrEmpty(kHighSpeechProbability), + manager_no_analog_agc.Process(GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(-10.0f)); - manager_with_analog_agc.Process(audio_buffer, - GetOverrideOrEmpty(kHighSpeechProbability), + manager_with_analog_agc.Process(GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(-10.0f)); // Check that no adaptation occurs when the analog controller is disabled // and make sure that the test triggers a downward adaptation otherwise. - EXPECT_EQ(manager_no_analog_agc.recommended_analog_level(), kAnalogLevel); - ASSERT_LT(manager_with_analog_agc.recommended_analog_level(), kAnalogLevel); + EXPECT_EQ(manager_no_analog_agc.recommended_analog_level(), kInputVolume); + ASSERT_LT(manager_with_analog_agc.recommended_analog_level(), kInputVolume); } // Checks that when the analog controller is disabled, no upward adaptation @@ -528,16 +460,16 @@ TEST_P(InputVolumeControllerParametrizedTest, // differs. TEST_P(InputVolumeControllerParametrizedTest, DisabledAnalogAgcDoesNotAdaptUpwards) { - InputVolumeController manager_no_analog_agc(kNumChannels, - GetDisabledAnalogAgcConfig()); + InputVolumeController manager_no_analog_agc( + kNumChannels, GetDisabledInputVolumeControllerConfig()); manager_no_analog_agc.Initialize(); - InputVolumeController manager_with_analog_agc(kNumChannels, - GetAnalogAgcTestConfig()); + InputVolumeController manager_with_analog_agc( + kNumChannels, GetInputVolumeControllerTestConfig()); manager_with_analog_agc.Initialize(); - constexpr int kAnalogLevel = kInitialInputVolume; - manager_no_analog_agc.set_stream_analog_level(kAnalogLevel); - manager_with_analog_agc.set_stream_analog_level(kAnalogLevel); + constexpr int kInputVolume = kInitialInputVolume; + manager_no_analog_agc.set_stream_analog_level(kInputVolume); + manager_with_analog_agc.set_stream_analog_level(kInputVolume); // Feed speech with low energy to trigger an upward adapation of the analog // level. @@ -551,16 +483,18 @@ TEST_P(InputVolumeControllerParametrizedTest, // Check that no adaptation occurs when the analog controller is disabled // and make sure that the test triggers an upward adaptation otherwise. - EXPECT_EQ(manager_no_analog_agc.recommended_analog_level(), kAnalogLevel); - ASSERT_GT(manager_with_analog_agc.recommended_analog_level(), kAnalogLevel); + EXPECT_EQ(manager_no_analog_agc.recommended_analog_level(), kInputVolume); + ASSERT_GT(manager_with_analog_agc.recommended_analog_level(), kInputVolume); } TEST_P(InputVolumeControllerParametrizedTest, StartupMinVolumeConfigurationIsRespected) { InputVolumeControllerTestHelper helper; + helper.CallAgcSequence(kInitialInputVolume, GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); + EXPECT_EQ(kInitialInputVolume, helper.manager.recommended_analog_level()); } @@ -572,56 +506,38 @@ TEST_P(InputVolumeControllerParametrizedTest, MicVolumeResponseToRmsError) { helper.CallAgcSequence(kInitialInputVolume, speech_probability_override, GetOverrideOrEmpty(kSpeechLevel)); - // Compressor default; no residual error. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(5), Return(true))); + // Inside the digital gain's window; no change of volume. helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-23.0f)); - // Inside the compressor's window; no change of volume. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(10), Return(true))); + // Inside the digital gain's window; no change of volume. helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-28.0f)); - // Above the compressor's window; volume should be increased. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(11), Return(true))); + // Above the digital gain's window; volume should be increased. helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-29.0f)); EXPECT_EQ(130, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(20), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-38.0f)); EXPECT_EQ(168, helper.manager.recommended_analog_level()); - // Inside the compressor's window; no change of volume. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(5), Return(true))); + // Inside the digital gain's window; no change of volume. helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-23.0f)); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(0), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-18.0f)); - // Below the compressor's window; volume should be decreased. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-1), Return(true))); + // Below the digial gain's window; volume should be decreased. helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-17.0f)); EXPECT_EQ(167, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-1), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-17.0f)); EXPECT_EQ(163, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-9), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-9.0f)); EXPECT_EQ(129, helper.manager.recommended_analog_level()); @@ -636,269 +552,56 @@ TEST_P(InputVolumeControllerParametrizedTest, MicVolumeIsLimited) { GetOverrideOrEmpty(kSpeechLevel)); // Maximum upwards change is limited. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(30), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-48.0f)); EXPECT_EQ(183, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(30), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-48.0f)); EXPECT_EQ(243, helper.manager.recommended_analog_level()); // Won't go higher than the maximum. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(30), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-48.0f)); EXPECT_EQ(255, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-1), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-17.0f)); EXPECT_EQ(254, helper.manager.recommended_analog_level()); // Maximum downwards change is limited. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-40), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(22.0f)); EXPECT_EQ(194, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-40), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(22.0f)); EXPECT_EQ(137, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-40), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(22.0f)); EXPECT_EQ(88, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-40), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(22.0f)); EXPECT_EQ(54, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-40), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(22.0f)); EXPECT_EQ(33, helper.manager.recommended_analog_level()); // Won't go lower than the minimum. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-40), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(22.0f)); EXPECT_EQ(std::max(18, GetMinMicLevel()), helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-40), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(22.0f)); EXPECT_EQ(std::max(12, GetMinMicLevel()), helper.manager.recommended_analog_level()); } -TEST_P(InputVolumeControllerParametrizedTest, CompressorStepsTowardsTarget) { - constexpr absl::optional kNoOverride = absl::nullopt; - const auto speech_probability_override = - GetOverrideOrEmpty(kHighSpeechProbability); - - InputVolumeControllerTestHelper helper; - helper.CallAgcSequence(kInitialInputVolume, speech_probability_override, - GetOverrideOrEmpty(kSpeechLevel)); - - // Compressor default; no call to set_compression_gain_db. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(5), Return(true))) - .WillRepeatedly(Return(false)); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/1, speech_probability_override, - GetOverrideOrEmpty(-23.0f)); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - // The mock `GetRmsErrorDb()` returns false; mimic this by passing - // absl::nullopt as an override. - helper.CallProcess(/*num_calls=*/19, kNoOverride, kNoOverride); - - // Moves slowly upwards. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(9), Return(true))) - .WillRepeatedly(Return(false)); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/1, speech_probability_override, - GetOverrideOrEmpty(-27.0f)); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/18, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(8)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/1, kNoOverride, kNoOverride); - - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/19, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(9)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/1, kNoOverride, kNoOverride); - - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - - // Moves slowly downward, then reverses before reaching the original target. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(5), Return(true))) - .WillRepeatedly(Return(false)); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/1, speech_probability_override, - GetOverrideOrEmpty(-23.0f)); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/18, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(8)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/1, kNoOverride, kNoOverride); - - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(9), Return(true))) - .WillRepeatedly(Return(false)); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/1, speech_probability_override, - GetOverrideOrEmpty(-27.0f)); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/18, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(9)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/1, kNoOverride, kNoOverride); - - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); -} - -TEST_P(InputVolumeControllerParametrizedTest, CompressorErrorIsDeemphasized) { - constexpr absl::optional kNoOverride = absl::nullopt; - const auto speech_probability_override = - GetOverrideOrEmpty(kHighSpeechProbability); - - InputVolumeControllerTestHelper helper; - helper.CallAgcSequence(kInitialInputVolume, speech_probability_override, - GetOverrideOrEmpty(kSpeechLevel)); - - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(10), Return(true))) - .WillRepeatedly(Return(false)); - helper.CallProcess(/*num_calls=*/1, speech_probability_override, - GetOverrideOrEmpty(-28.0f)); - // The mock `GetRmsErrorDb()` returns false; mimic this by passing - // absl::nullopt as an override. - helper.CallProcess(/*num_calls=*/18, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(8)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(9)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/1, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(0), Return(true))) - .WillRepeatedly(Return(false)); - helper.CallProcess(/*num_calls=*/1, speech_probability_override, - GetOverrideOrEmpty(-18.0f)); - helper.CallProcess(/*num_calls=*/18, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(8)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(7)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(6)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/1, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(_)).Times(0); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); -} - -TEST_P(InputVolumeControllerParametrizedTest, CompressorReachesMaximum) { - constexpr absl::optional kNoOverride = absl::nullopt; - const auto speech_probability_override = - GetOverrideOrEmpty(kHighSpeechProbability); - - InputVolumeControllerTestHelper helper; - helper.CallAgcSequence(kInitialInputVolume, speech_probability_override, - GetOverrideOrEmpty(kSpeechLevel)); - - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(10), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(10), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(10), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(10), Return(true))) - .WillRepeatedly(Return(false)); - helper.CallProcess(/*num_calls=*/4, speech_probability_override, - GetOverrideOrEmpty(-28.0f)); - // The mock `GetRmsErrorDb()` returns false; mimic this by passing - // absl::nullopt as an override. - helper.CallProcess(/*num_calls=*/15, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(8)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(9)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(10)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(11)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(12)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/1, kNoOverride, kNoOverride); -} - -TEST_P(InputVolumeControllerParametrizedTest, CompressorReachesMinimum) { - constexpr absl::optional kNoOverride = absl::nullopt; - const auto speech_probability_override = - GetOverrideOrEmpty(kHighSpeechProbability); - - InputVolumeControllerTestHelper helper; - helper.CallAgcSequence(kInitialInputVolume, speech_probability_override, - GetOverrideOrEmpty(kSpeechLevel)); - - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(0), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(0), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(0), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(0), Return(true))) - .WillRepeatedly(Return(false)); - helper.CallProcess(/*num_calls=*/4, speech_probability_override, - GetOverrideOrEmpty(-18.0f)); - // The mock `GetRmsErrorDb()` returns false; mimic this by passing - // absl::nullopt as an override. - helper.CallProcess(/*num_calls=*/15, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(6)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(5)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(4)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(3)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(2)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/1, kNoOverride, kNoOverride); -} - TEST_P(InputVolumeControllerParametrizedTest, NoActionWhileMuted) { InputVolumeControllerTestHelper helper; helper.CallAgcSequence(kInitialInputVolume, @@ -906,15 +609,8 @@ TEST_P(InputVolumeControllerParametrizedTest, NoActionWhileMuted) { GetOverrideOrEmpty(kSpeechLevel)); helper.manager.HandleCaptureOutputUsedChange(false); - helper.manager.Process(helper.audio_buffer, - GetOverrideOrEmpty(kHighSpeechProbability), + helper.manager.Process(GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); - - absl::optional new_digital_gain = - helper.manager.GetDigitalComressionGain(); - if (new_digital_gain) { - helper.mock_gain_control.set_compression_gain_db(*new_digital_gain); - } } TEST_P(InputVolumeControllerParametrizedTest, @@ -929,10 +625,8 @@ TEST_P(InputVolumeControllerParametrizedTest, constexpr int kInputVolume = 127; helper.manager.set_stream_analog_level(kInputVolume); - EXPECT_CALL(*helper.mock_agc, Reset()); // SetMicVolume should not be called. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)).WillOnce(Return(false)); helper.CallProcess(/*num_calls=*/1, GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); @@ -950,9 +644,7 @@ TEST_P(InputVolumeControllerParametrizedTest, UnmutingRaisesTooLowVolume) { constexpr int kInputVolume = 11; helper.manager.set_stream_analog_level(kInputVolume); - EXPECT_CALL(*helper.mock_agc, Reset()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)).WillOnce(Return(false)); helper.CallProcess(/*num_calls=*/1, GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); @@ -968,14 +660,6 @@ TEST_P(InputVolumeControllerParametrizedTest, helper.CallAgcSequence(kInitialInputVolume, speech_probability_override, GetOverrideOrEmpty(kSpeechLevel)); - // Change outside of compressor's range, which would normally trigger a call - // to `SetMicVolume()`. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(11), Return(true))); - - // When the analog volume changes, the gain controller is reset. - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); - // GetMicVolume returns a value outside of the quantization slack, indicating // a manual volume change. ASSERT_NE(helper.manager.recommended_analog_level(), 154); @@ -985,17 +669,12 @@ TEST_P(InputVolumeControllerParametrizedTest, EXPECT_EQ(154, helper.manager.recommended_analog_level()); // Do the same thing, except downwards now. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-1), Return(true))); helper.manager.set_stream_analog_level(100); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-17.0f)); EXPECT_EQ(100, helper.manager.recommended_analog_level()); // And finally verify the AGC continues working without a manual change. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-1), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-17.0f)); EXPECT_EQ(99, helper.manager.recommended_analog_level()); @@ -1012,8 +691,6 @@ TEST_P(InputVolumeControllerParametrizedTest, // Force the mic up to max volume. Takes a few steps due to the residual // gain limitation. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(30), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-48.0f)); EXPECT_EQ(183, helper.manager.recommended_analog_level()); @@ -1025,17 +702,12 @@ TEST_P(InputVolumeControllerParametrizedTest, EXPECT_EQ(255, helper.manager.recommended_analog_level()); // Manual change does not result in SetMicVolume call. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-1), Return(true))); helper.manager.set_stream_analog_level(50); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-17.0f)); EXPECT_EQ(50, helper.manager.recommended_analog_level()); // Continues working as usual afterwards. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(20), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-38.0f)); @@ -1060,29 +732,20 @@ TEST_P(InputVolumeControllerParametrizedTest, // Manual change below min, but strictly positive, otherwise AGC won't take // any action. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-1), Return(true))); helper.manager.set_stream_analog_level(1); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-17.0f)); EXPECT_EQ(1, helper.manager.recommended_analog_level()); // Continues working as usual afterwards. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(11), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-29.0f)); EXPECT_EQ(2, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(30), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-48.0f)); EXPECT_EQ(11, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(20), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-38.0f)); EXPECT_EQ(18, helper.manager.recommended_analog_level()); @@ -1106,10 +769,7 @@ TEST_P(InputVolumeControllerParametrizedTest, // Manual change below min, but strictly positive, otherwise // AGC won't take any action. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-1), Return(true))); helper.manager.set_stream_analog_level(1); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-17.0f)); EXPECT_EQ(GetMinMicLevel(), helper.manager.recommended_analog_level()); @@ -1142,7 +802,6 @@ TEST_P(InputVolumeControllerParametrizedTest, ClippingLowersVolume) { GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/0.2); EXPECT_EQ(240, helper.manager.recommended_analog_level()); } @@ -1154,16 +813,13 @@ TEST_P(InputVolumeControllerParametrizedTest, GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); EXPECT_EQ(240, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(0); helper.CallPreProc(/*num_calls=*/300, /*clipped_ratio=*/kAboveClippedThreshold); EXPECT_EQ(240, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); EXPECT_EQ(225, helper.manager.recommended_analog_level()); } @@ -1174,11 +830,9 @@ TEST_P(InputVolumeControllerParametrizedTest, ClippingLoweringIsLimited) { GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); EXPECT_EQ(kClippedMin, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(0); helper.CallPreProc(/*num_calls=*/1000, /*clipped_ratio=*/kAboveClippedThreshold); EXPECT_EQ(kClippedMin, helper.manager.recommended_analog_level()); @@ -1194,12 +848,9 @@ TEST_P(InputVolumeControllerParametrizedTest, speech_probability_override, GetOverrideOrEmpty(kSpeechLevel)); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); EXPECT_EQ(240, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(30), Return(true))); helper.CallProcess(/*num_calls=*/10, speech_probability_override, GetOverrideOrEmpty(-48.0f)); EXPECT_EQ(240, helper.manager.recommended_analog_level()); @@ -1215,12 +866,9 @@ TEST_P(InputVolumeControllerParametrizedTest, speech_probability_override, GetOverrideOrEmpty(kSpeechLevel)); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); EXPECT_EQ(185, helper.manager.recommended_analog_level()); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(40), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-58.0f)); EXPECT_EQ(240, helper.manager.recommended_analog_level()); @@ -1229,105 +877,6 @@ TEST_P(InputVolumeControllerParametrizedTest, EXPECT_EQ(240, helper.manager.recommended_analog_level()); } -TEST_P(InputVolumeControllerParametrizedTest, - MaxCompressionIsIncreasedAfterClipping) { - constexpr absl::optional kNoOverride = absl::nullopt; - const auto speech_probability_override = - GetOverrideOrEmpty(kHighSpeechProbability); - - InputVolumeControllerTestHelper helper; - helper.CallAgcSequence(/*applied_input_volume=*/210, - speech_probability_override, - GetOverrideOrEmpty(kSpeechLevel)); - - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); - helper.CallPreProc(/*num_calls=*/1, kAboveClippedThreshold); - EXPECT_EQ(195, helper.manager.recommended_analog_level()); - - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(11), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(11), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(11), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(11), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(11), Return(true))) - .WillRepeatedly(Return(false)); - helper.CallProcess(/*num_calls=*/5, speech_probability_override, - GetOverrideOrEmpty(-29.0f)); - // The mock `GetRmsErrorDb()` returns false; mimic this by passing - // absl::nullopt as an override. - helper.CallProcess(/*num_calls=*/14, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(8)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(9)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(10)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(11)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(12)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(13)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/1, kNoOverride, kNoOverride); - - // Continue clipping until we hit the maximum surplus compression. - helper.CallPreProc(/*num_calls=*/300, - /*clipped_ratio=*/kAboveClippedThreshold); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); - helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); - EXPECT_EQ(180, helper.manager.recommended_analog_level()); - - helper.CallPreProc(/*num_calls=*/300, - /*clipped_ratio=*/kAboveClippedThreshold); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); - helper.CallPreProc(1, kAboveClippedThreshold); - EXPECT_EQ(kClippedMin, helper.manager.recommended_analog_level()); - - // Current level is now at the minimum, but the maximum allowed level still - // has more to decrease. - helper.CallPreProc(/*num_calls=*/300, - /*clipped_ratio=*/kAboveClippedThreshold); - helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); - - helper.CallPreProc(/*num_calls=*/300, - /*clipped_ratio=*/kAboveClippedThreshold); - helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); - - helper.CallPreProc(/*num_calls=*/300, - /*clipped_ratio=*/kAboveClippedThreshold); - helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); - - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(16), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(16), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(16), Return(true))) - .WillOnce(DoAll(SetArgPointee<0>(16), Return(true))) - .WillRepeatedly(Return(false)); - helper.CallProcess(/*num_calls=*/4, speech_probability_override, - GetOverrideOrEmpty(-34.0f)); - helper.CallProcess(/*num_calls=*/15, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(14)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(15)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(16)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(17)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/20, kNoOverride, kNoOverride); - EXPECT_CALL(helper.mock_gain_control, set_compression_gain_db(18)) - .WillOnce(Return(0)); - helper.CallProcess(/*num_calls=*/1, kNoOverride, kNoOverride); -} - TEST_P(InputVolumeControllerParametrizedTest, UserCanRaiseVolumeAfterClipping) { const auto speech_probability_override = GetOverrideOrEmpty(kHighSpeechProbability); @@ -1337,35 +886,24 @@ TEST_P(InputVolumeControllerParametrizedTest, UserCanRaiseVolumeAfterClipping) { speech_probability_override, GetOverrideOrEmpty(kSpeechLevel)); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); EXPECT_EQ(210, helper.manager.recommended_analog_level()); - // High enough error to trigger a volume check. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(14), Return(true))); // User changed the volume. helper.manager.set_stream_analog_level(250); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(AtLeast(1)); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-32.0f)); EXPECT_EQ(250, helper.manager.recommended_analog_level()); // Move down... - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(-10), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-8.0f)); EXPECT_EQ(210, helper.manager.recommended_analog_level()); // And back up to the new max established by the user. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(40), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-58.0f)); EXPECT_EQ(250, helper.manager.recommended_analog_level()); // Will not move above new maximum. - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillOnce(DoAll(SetArgPointee<0>(30), Return(true))); helper.CallProcess(/*num_calls=*/1, speech_probability_override, GetOverrideOrEmpty(-48.0f)); EXPECT_EQ(250, helper.manager.recommended_analog_level()); @@ -1378,7 +916,6 @@ TEST_P(InputVolumeControllerParametrizedTest, GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); - EXPECT_CALL(*helper.mock_agc, Reset()).Times(0); int initial_volume = helper.manager.recommended_analog_level(); helper.CallPreProc(/*num_calls=*/1, /*clipped_ratio=*/kAboveClippedThreshold); EXPECT_EQ(initial_volume, helper.manager.recommended_analog_level()); @@ -1390,8 +927,6 @@ TEST_P(InputVolumeControllerParametrizedTest, TakesNoActionOnZeroMicVolume) { GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); - EXPECT_CALL(*helper.mock_agc, GetRmsErrorDb(_)) - .WillRepeatedly(DoAll(SetArgPointee<0>(30), Return(true))); helper.manager.set_stream_analog_level(0); helper.CallProcess(/*num_calls=*/10, GetOverrideOrEmpty(kHighSpeechProbability), @@ -1427,31 +962,12 @@ TEST_P(InputVolumeControllerParametrizedTest, EXPECT_EQ(255, helper.manager.recommended_analog_level()); } -TEST_P(InputVolumeControllerParametrizedTest, DisableDigitalDisablesDigital) { - if (IsRmsErrorOverridden()) { - GTEST_SKIP() << "Skipped. RMS error override does not affect the test."; - } - - auto agc = std::unique_ptr(new ::testing::NiceMock()); - MockGainControl mock_gain_control; - EXPECT_CALL(mock_gain_control, set_mode(GainControl::kFixedDigital)); - EXPECT_CALL(mock_gain_control, set_target_level_dbfs(0)); - EXPECT_CALL(mock_gain_control, set_compression_gain_db(0)); - EXPECT_CALL(mock_gain_control, enable_limiter(false)); - - AnalogAgcConfig config; - config.enable_digital_adaptive = false; - auto manager = std::make_unique(kNumChannels, config); - manager->Initialize(); - manager->SetupDigitalGainControl(mock_gain_control); -} - TEST(InputVolumeControllerTest, AgcMinMicLevelExperimentDefault) { std::unique_ptr manager = CreateInputVolumeController(kInitialInputVolume, kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames); - EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel); - EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), + EXPECT_EQ(manager->channel_controllers_[0]->min_mic_level(), kMinMicLevel); + EXPECT_EQ(manager->channel_controllers_[0]->startup_min_level(), kInitialInputVolume); } @@ -1462,8 +978,9 @@ TEST(InputVolumeControllerTest, AgcMinMicLevelExperimentDisabled) { std::unique_ptr manager = CreateInputVolumeController(kInitialInputVolume, kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames); - EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel); - EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), + + EXPECT_EQ(manager->channel_controllers_[0]->min_mic_level(), kMinMicLevel); + EXPECT_EQ(manager->channel_controllers_[0]->startup_min_level(), kInitialInputVolume); } } @@ -1476,8 +993,8 @@ TEST(InputVolumeControllerTest, AgcMinMicLevelExperimentOutOfRangeAbove) { std::unique_ptr manager = CreateInputVolumeController(kInitialInputVolume, kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames); - EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel); - EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), + EXPECT_EQ(manager->channel_controllers_[0]->min_mic_level(), kMinMicLevel); + EXPECT_EQ(manager->channel_controllers_[0]->startup_min_level(), kInitialInputVolume); } @@ -1489,8 +1006,8 @@ TEST(InputVolumeControllerTest, AgcMinMicLevelExperimentOutOfRangeBelow) { std::unique_ptr manager = CreateInputVolumeController(kInitialInputVolume, kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames); - EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel); - EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), + EXPECT_EQ(manager->channel_controllers_[0]->min_mic_level(), kMinMicLevel); + EXPECT_EQ(manager->channel_controllers_[0]->startup_min_level(), kInitialInputVolume); } @@ -1507,8 +1024,10 @@ TEST(InputVolumeControllerTest, AgcMinMicLevelExperimentEnabled50) { std::unique_ptr manager = CreateInputVolumeController(kInitialInputVolume, kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames); - EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevelOverride); - EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), + + EXPECT_EQ(manager->channel_controllers_[0]->min_mic_level(), + kMinMicLevelOverride); + EXPECT_EQ(manager->channel_controllers_[0]->startup_min_level(), kInitialInputVolume); } } @@ -1561,6 +1080,7 @@ TEST(InputVolumeControllerTest, // `manager`, which is allowed to reach a lower gain. EXPECT_GT(manager_with_override->recommended_analog_level(), manager->recommended_analog_level()); + // Check that the gain selected by `manager_with_override` equals the minimum // value overridden via field trial. EXPECT_EQ(manager_with_override->recommended_analog_level(), @@ -1609,9 +1129,8 @@ TEST(InputVolumeControllerTest, /*speech_probability_level=*/-18.0f, *manager); CallPreProcessAndProcess( /*num_calls=*/400, audio_buffer, - /*speech_probability_override=*/absl::optional(0.7f), - /*speech_probability_level=*/absl::optional(-18.0f), - *manager_with_override); + /*speech_probability_override=*/0.7f, + /*speech_probability_level=*/-18.0f, *manager_with_override); // Make sure that an adaptation occurred. ASSERT_GT(manager->recommended_analog_level(), 0); @@ -1637,9 +1156,10 @@ TEST(InputVolumeControllerTest, const auto factory = []() { // Use a large clipped level step to more quickly decrease the analog gain // with clipping. - AnalogAgcConfig config = kDefaultAnalogConfig; + InputVolumeControllerConfig config = kDefaultInputVolumeControllerConfig; + config.enabled = true; config.startup_min_volume = kInitialInputVolume; - config.enable_digital_adaptive = false; + config.digital_adaptive_follows = false; config.clipped_level_step = 64; config.clipped_ratio_threshold = kClippedRatioThreshold; config.clipped_wait_frames = kClippedWaitFrames; @@ -1653,9 +1173,9 @@ TEST(InputVolumeControllerTest, std::unique_ptr manager_with_override; { constexpr int kMinMicLevelOverride = 20; - static_assert( - kDefaultAnalogConfig.clipped_level_min >= kMinMicLevelOverride, - "Use a lower override value."); + static_assert(kDefaultInputVolumeControllerConfig.clipped_level_min >= + kMinMicLevelOverride, + "Use a lower override value."); test::ScopedFieldTrials field_trial( GetAgcMinMicLevelExperimentFieldTrialEnabled(kMinMicLevelOverride)); manager_with_override = factory(); @@ -1687,7 +1207,7 @@ TEST(InputVolumeControllerTest, EXPECT_EQ(manager->recommended_analog_level(), manager_with_override->recommended_analog_level()); EXPECT_EQ(manager_with_override->recommended_analog_level(), - kDefaultAnalogConfig.clipped_level_min); + kDefaultInputVolumeControllerConfig.clipped_level_min); } // Checks that, when the "WebRTC-Audio-AgcMinMicLevelExperiment" field trial is @@ -1704,9 +1224,10 @@ TEST(InputVolumeControllerTest, const auto factory = []() { // Use a large clipped level step to more quickly decrease the analog gain // with clipping. - AnalogAgcConfig config = kDefaultAnalogConfig; + InputVolumeControllerConfig config = kDefaultInputVolumeControllerConfig; + config.enabled = true; config.startup_min_volume = kInitialInputVolume; - config.enable_digital_adaptive = false; + config.digital_adaptive_follows = false; config.clipped_level_step = 64; config.clipped_ratio_threshold = kClippedRatioThreshold; config.clipped_wait_frames = kClippedWaitFrames; @@ -1720,9 +1241,9 @@ TEST(InputVolumeControllerTest, std::unique_ptr manager_with_override; { constexpr int kMinMicLevelOverride = 20; - static_assert( - kDefaultAnalogConfig.clipped_level_min >= kMinMicLevelOverride, - "Use a lower override value."); + static_assert(kDefaultInputVolumeControllerConfig.clipped_level_min >= + kMinMicLevelOverride, + "Use a lower override value."); test::ScopedFieldTrials field_trial( GetAgcMinMicLevelExperimentFieldTrialEnabled(kMinMicLevelOverride)); manager_with_override = factory(); @@ -1736,14 +1257,12 @@ TEST(InputVolumeControllerTest, CallPreProcessAndProcess( /*num_calls=*/400, audio_buffer, - /*speech_probability_override=*/absl::optional(0.7f), - /*speech_level_override=*/absl::optional(-18.0f), *manager); + /*speech_probability_override=*/0.7f, + /*speech_level_override=*/-18.0f, *manager); CallPreProcessAndProcess( /*num_calls=*/400, audio_buffer, - /*speech_probability_override=*/absl::optional(0.7f), - /*speech_level_override=*/absl::optional(-18.0f), - *manager_with_override); - + /*speech_probability_override=*/0.7f, + /*speech_level_override=*/-18.0f, *manager_with_override); // Make sure that an adaptation occurred. ASSERT_GT(manager->recommended_analog_level(), 0); @@ -1754,7 +1273,7 @@ TEST(InputVolumeControllerTest, EXPECT_EQ(manager->recommended_analog_level(), manager_with_override->recommended_analog_level()); EXPECT_EQ(manager_with_override->recommended_analog_level(), - kDefaultAnalogConfig.clipped_level_min); + kDefaultInputVolumeControllerConfig.clipped_level_min); } // TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_level_step`. @@ -1790,14 +1309,11 @@ TEST_P(InputVolumeControllerParametrizedTest, GTEST_SKIP() << "Skipped. RMS error override does not affect the test."; } - // TODO(bugs.webrtc.org/12874): Use designated initializers once fixed. - ClippingPredictorConfig config; - config.enabled = false; - std::unique_ptr manager = CreateInputVolumeController( kInitialInputVolume, kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames, config); + kClippedWaitFrames, /*enable_clipping_predictor=*/false); manager->Initialize(); + EXPECT_FALSE(manager->clipping_predictor_enabled()); EXPECT_FALSE(manager->use_clipping_predictor_step()); } @@ -1818,15 +1334,11 @@ TEST_P(InputVolumeControllerParametrizedTest, GTEST_SKIP() << "Skipped. RMS error override does not affect the test."; } - // TODO(bugs.webrtc.org/12874): Use designated initializers once fixed. - ClippingPredictorConfig config; - config.enabled = true; - config.use_predicted_step = true; - std::unique_ptr manager = CreateInputVolumeController( kInitialInputVolume, kClippedLevelStep, kClippedRatioThreshold, - kClippedWaitFrames, config); + kClippedWaitFrames, /*enable_clipping_predictor=*/true); manager->Initialize(); + EXPECT_TRUE(manager->clipping_predictor_enabled()); EXPECT_TRUE(manager->use_clipping_predictor_step()); } @@ -1836,15 +1348,15 @@ TEST_P(InputVolumeControllerParametrizedTest, AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz, kNumChannels, kSampleRateHz, kNumChannels); - AnalogAgcConfig config = GetAnalogAgcTestConfig(); - config.clipping_predictor.enabled = false; - InputVolumeController manager(config, new ::testing::NiceMock()); + InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig(); + config.enable_clipping_predictor = false; + InputVolumeController manager(/*num_capture_channels=*/1, config); manager.Initialize(); manager.set_stream_analog_level(/*level=*/255); EXPECT_FALSE(manager.clipping_predictor_enabled()); EXPECT_FALSE(manager.use_clipping_predictor_step()); EXPECT_EQ(manager.recommended_analog_level(), 255); - manager.Process(audio_buffer, GetOverrideOrEmpty(kHighSpeechProbability), + manager.Process(GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); CallPreProcessAudioBuffer(/*num_calls=*/10, /*peak_ratio=*/0.99f, manager); EXPECT_EQ(manager.recommended_analog_level(), 255); @@ -1859,15 +1371,18 @@ TEST_P(InputVolumeControllerParametrizedTest, AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz, kNumChannels, kSampleRateHz, kNumChannels); - AnalogAgcConfig config_with_prediction = GetAnalogAgcTestConfig(); - config_with_prediction.clipping_predictor.enabled = true; - config_with_prediction.clipping_predictor.use_predicted_step = true; - AnalogAgcConfig config_without_prediction = GetAnalogAgcTestConfig(); - config_without_prediction.clipping_predictor.enabled = false; - InputVolumeController manager_with_prediction( - config_with_prediction, new ::testing::NiceMock()); - InputVolumeController manager_without_prediction( - config_without_prediction, new ::testing::NiceMock()); + InputVolumeControllerConfig config_with_prediction = + GetInputVolumeControllerTestConfig(); + config_with_prediction.enable_clipping_predictor = true; + + InputVolumeControllerConfig config_without_prediction = + GetInputVolumeControllerTestConfig(); + + config_without_prediction.enable_clipping_predictor = false; + InputVolumeController manager_without_prediction(/*num_capture_channels=*/1, + config_without_prediction); + InputVolumeController manager_with_prediction(/*num_capture_channels=*/1, + config_with_prediction); manager_with_prediction.Initialize(); manager_without_prediction.Initialize(); @@ -1878,12 +1393,12 @@ TEST_P(InputVolumeControllerParametrizedTest, constexpr float kZeroPeakRatio = 0.0f; manager_with_prediction.set_stream_analog_level(kInitialLevel); manager_without_prediction.set_stream_analog_level(kInitialLevel); - manager_with_prediction.Process(audio_buffer, - GetOverrideOrEmpty(kHighSpeechProbability), + + manager_with_prediction.Process(GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); - manager_without_prediction.Process(audio_buffer, - GetOverrideOrEmpty(kHighSpeechProbability), + manager_without_prediction.Process(GetOverrideOrEmpty(kHighSpeechProbability), GetOverrideOrEmpty(kSpeechLevel)); + EXPECT_TRUE(manager_with_prediction.clipping_predictor_enabled()); EXPECT_FALSE(manager_without_prediction.clipping_predictor_enabled()); EXPECT_TRUE(manager_with_prediction.use_clipping_predictor_step()); @@ -1962,186 +1477,25 @@ TEST_P(InputVolumeControllerParametrizedTest, kInitialLevel - 2 * kClippedLevelStep); } -TEST_P(InputVolumeControllerParametrizedTest, - UnusedClippingPredictionsProduceEqualAnalogLevels) { - AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz, - kNumChannels, kSampleRateHz, kNumChannels); - - AnalogAgcConfig config_with_prediction = GetAnalogAgcTestConfig(); - config_with_prediction.clipping_predictor.enabled = true; - config_with_prediction.clipping_predictor.use_predicted_step = false; - AnalogAgcConfig config_without_prediction = GetAnalogAgcTestConfig(); - config_without_prediction.clipping_predictor.enabled = false; - InputVolumeController manager_with_prediction( - config_with_prediction, new ::testing::NiceMock()); - InputVolumeController manager_without_prediction( - config_without_prediction, new ::testing::NiceMock()); - - constexpr int kInitialLevel = 255; - constexpr float kClippingPeakRatio = 1.0f; - constexpr float kCloseToClippingPeakRatio = 0.99f; - constexpr float kZeroPeakRatio = 0.0f; - manager_with_prediction.Initialize(); - manager_without_prediction.Initialize(); - manager_with_prediction.set_stream_analog_level(kInitialLevel); - manager_without_prediction.set_stream_analog_level(kInitialLevel); - manager_with_prediction.Process(audio_buffer, - GetOverrideOrEmpty(kHighSpeechProbability), - GetOverrideOrEmpty(kSpeechLevel)); - manager_without_prediction.Process(audio_buffer, - GetOverrideOrEmpty(kHighSpeechProbability), - GetOverrideOrEmpty(kSpeechLevel)); - - EXPECT_TRUE(manager_with_prediction.clipping_predictor_enabled()); - EXPECT_FALSE(manager_without_prediction.clipping_predictor_enabled()); - EXPECT_FALSE(manager_with_prediction.use_clipping_predictor_step()); - EXPECT_EQ(manager_with_prediction.recommended_analog_level(), kInitialLevel); - EXPECT_EQ(manager_without_prediction.recommended_analog_level(), - kInitialLevel); - - // Expect no change in the analog level for non-clipping frames. - CallPreProcessAudioBuffer(/*num_calls=*/10, kCloseToClippingPeakRatio, - manager_with_prediction); - CallPreProcessAudioBuffer(/*num_calls=*/10, kCloseToClippingPeakRatio, - manager_without_prediction); - EXPECT_EQ(manager_with_prediction.recommended_analog_level(), - manager_without_prediction.recommended_analog_level()); - - // Expect no change for non-clipping frames. - CallPreProcessAudioBuffer(kClippedWaitFrames, kCloseToClippingPeakRatio, - manager_with_prediction); - CallPreProcessAudioBuffer(kClippedWaitFrames, kCloseToClippingPeakRatio, - manager_without_prediction); - EXPECT_EQ(manager_with_prediction.recommended_analog_level(), - manager_without_prediction.recommended_analog_level()); - - // Expect no change for non-clipping frames. - CallPreProcessAudioBuffer(/*num_calls=*/10, kCloseToClippingPeakRatio, - manager_with_prediction); - CallPreProcessAudioBuffer(/*num_calls=*/10, kCloseToClippingPeakRatio, - manager_without_prediction); - EXPECT_EQ(manager_with_prediction.recommended_analog_level(), - manager_without_prediction.recommended_analog_level()); - - // Expect no change when clipping is not detected or predicted. - CallPreProcessAudioBuffer(2 * kClippedWaitFrames, kZeroPeakRatio, - manager_with_prediction); - CallPreProcessAudioBuffer(2 * kClippedWaitFrames, kZeroPeakRatio, - manager_without_prediction); - EXPECT_EQ(manager_with_prediction.recommended_analog_level(), - manager_without_prediction.recommended_analog_level()); - - // Expect a change for clipping frames. - CallPreProcessAudioBuffer(/*num_calls=*/1, kClippingPeakRatio, - manager_with_prediction); - CallPreProcessAudioBuffer(/*num_calls=*/1, kClippingPeakRatio, - manager_without_prediction); - EXPECT_EQ(manager_with_prediction.recommended_analog_level(), - manager_without_prediction.recommended_analog_level()); - - // Expect no change during waiting. - CallPreProcessAudioBuffer(kClippedWaitFrames, kClippingPeakRatio, - manager_with_prediction); - CallPreProcessAudioBuffer(kClippedWaitFrames, kClippingPeakRatio, - manager_without_prediction); - EXPECT_EQ(manager_with_prediction.recommended_analog_level(), - manager_without_prediction.recommended_analog_level()); - - // Expect a change for clipping frames. - CallPreProcessAudioBuffer(/*num_calls=*/1, kClippingPeakRatio, - manager_with_prediction); - CallPreProcessAudioBuffer(/*num_calls=*/1, kClippingPeakRatio, - manager_without_prediction); - EXPECT_EQ(manager_with_prediction.recommended_analog_level(), - manager_without_prediction.recommended_analog_level()); -} - -// Checks that passing an empty speech level and probability overrides to -// `Process()` has the same effect as passing no overrides. +// Checks that passing an empty speech level has no effect on the input volume. TEST_P(InputVolumeControllerParametrizedTest, EmptyRmsErrorOverrideHasNoEffect) { - InputVolumeController manager_1(kNumChannels, GetAnalogAgcTestConfig()); - InputVolumeController manager_2(kNumChannels, GetAnalogAgcTestConfig()); - manager_1.Initialize(); - manager_2.Initialize(); + InputVolumeController manager(kNumChannels, + GetInputVolumeControllerTestConfig()); + manager.Initialize(); - constexpr int kAnalogLevel = 50; - manager_1.set_stream_analog_level(kAnalogLevel); - manager_2.set_stream_analog_level(kAnalogLevel); + constexpr int kInputVolume = kInitialInputVolume; + manager.set_stream_analog_level(kInputVolume); - // Feed speech with low energy to trigger an upward adapation of the analog - // level. + // Feed speech with low energy that would trigger an upward adapation of + // the analog level if an speech level and RMS values were not empty. constexpr int kNumFrames = 125; constexpr int kGainDb = -20; SpeechSamplesReader reader; + reader.Feed(kNumFrames, kGainDb, absl::nullopt, absl::nullopt, manager); - // Check the initial input volume. - ASSERT_EQ(manager_1.recommended_analog_level(), kAnalogLevel); - ASSERT_EQ(manager_2.recommended_analog_level(), kAnalogLevel); - - reader.Feed(kNumFrames, kGainDb, absl::nullopt, absl::nullopt, manager_1); - reader.Feed(kNumFrames, kGainDb, manager_2); - - // Check that the states are the same and adaptation occurs. - EXPECT_EQ(manager_1.recommended_analog_level(), - manager_2.recommended_analog_level()); - ASSERT_GT(manager_1.recommended_analog_level(), kAnalogLevel); - EXPECT_EQ(manager_1.voice_probability(), manager_2.voice_probability()); - EXPECT_EQ(manager_1.frames_since_clipped_, manager_2.frames_since_clipped_); - - // Check that the states of the channel AGCs are the same. - EXPECT_EQ(manager_1.num_channels(), manager_2.num_channels()); - for (int i = 0; i < manager_1.num_channels(); ++i) { - EXPECT_EQ(manager_1.channel_agcs_[i]->recommended_analog_level(), - manager_2.channel_agcs_[i]->recommended_analog_level()); - EXPECT_EQ(manager_1.channel_agcs_[i]->voice_probability(), - manager_2.channel_agcs_[i]->voice_probability()); - } -} - -// Checks that passing a non-empty speech level and probability overrides to -// `Process()` has an effect. -TEST_P(InputVolumeControllerParametrizedTest, - NonEmptyRmsErrorOverrideHasEffect) { - InputVolumeController manager_1(kNumChannels, GetAnalogAgcTestConfig()); - InputVolumeController manager_2(kNumChannels, GetAnalogAgcTestConfig()); - manager_1.Initialize(); - manager_2.Initialize(); - - constexpr int kAnalogLevel = 50; - manager_1.set_stream_analog_level(kAnalogLevel); - manager_2.set_stream_analog_level(kAnalogLevel); - - // Feed speech with low energy to trigger an upward adapation of the analog - // level. - constexpr int kNumFrames = 125; - constexpr int kGainDb = -20; - SpeechSamplesReader reader; - - // Check the initial input volume. - ASSERT_EQ(manager_1.recommended_analog_level(), kAnalogLevel); - ASSERT_EQ(manager_2.recommended_analog_level(), kAnalogLevel); - - reader.Feed(kNumFrames, kGainDb, - absl::optional(kHighSpeechProbability), - absl::optional(kSpeechLevel), manager_1); - reader.Feed(kNumFrames, kGainDb, manager_2); - - // Check that different adaptation occurs. The voice probability estimate from - // AGC is not affected. - ASSERT_GT(manager_1.recommended_analog_level(), kAnalogLevel); - ASSERT_GT(manager_2.recommended_analog_level(), kAnalogLevel); - ASSERT_NE(manager_1.recommended_analog_level(), - manager_2.recommended_analog_level()); - EXPECT_EQ(manager_1.voice_probability(), manager_2.voice_probability()); - - EXPECT_EQ(manager_1.num_channels(), manager_2.num_channels()); - for (int i = 0; i < manager_1.num_channels(); ++i) { - EXPECT_NE(manager_1.channel_agcs_[i]->recommended_analog_level(), - manager_2.channel_agcs_[i]->recommended_analog_level()); - EXPECT_EQ(manager_1.channel_agcs_[i]->voice_probability(), - manager_2.channel_agcs_[i]->voice_probability()); - } + // Check that no adaptation occurs. + ASSERT_EQ(manager.recommended_analog_level(), kInputVolume); } } // namespace webrtc diff --git a/modules/audio_processing/include/audio_processing.cc b/modules/audio_processing/include/audio_processing.cc index 86edaee087..83917c25c5 100644 --- a/modules/audio_processing/include/audio_processing.cc +++ b/modules/audio_processing/include/audio_processing.cc @@ -97,10 +97,16 @@ bool Agc2Config::AdaptiveDigital::operator==( max_output_noise_level_dbfs == rhs.max_output_noise_level_dbfs; } +bool Agc2Config::InputVolumeController::operator==( + const Agc2Config::InputVolumeController& rhs) const { + return enabled == rhs.enabled; +} + bool Agc2Config::operator==(const Agc2Config& rhs) const { return enabled == rhs.enabled && fixed_digital.gain_db == rhs.fixed_digital.gain_db && - adaptive_digital == rhs.adaptive_digital; + adaptive_digital == rhs.adaptive_digital && + input_volume_controller == rhs.input_volume_controller; } bool AudioProcessing::Config::CaptureLevelAdjustment::operator==( @@ -204,7 +210,8 @@ std::string AudioProcessing::Config::ToString() const { << gain_controller2.adaptive_digital.max_gain_change_db_per_second << ", max_output_noise_level_dbfs: " << gain_controller2.adaptive_digital.max_output_noise_level_dbfs - << "}}"; + << " }, input_volume_control : { enabled " + << gain_controller2.input_volume_controller.enabled << "}}"; return builder.str(); } diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h index 03e88acdec..c0b3d52f3f 100644 --- a/modules/audio_processing/include/audio_processing.h +++ b/modules/audio_processing/include/audio_processing.h @@ -357,6 +357,15 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { float max_gain_change_db_per_second = 3.0f; float max_output_noise_level_dbfs = -50.0f; } adaptive_digital; + + // Enables input volume control in AGC2. + struct InputVolumeController { + bool operator==(const InputVolumeController& rhs) const; + bool operator!=(const InputVolumeController& rhs) const { + return !(*this == rhs); + } + bool enabled = false; + } input_volume_controller; } gain_controller2; std::string ToString() const;