AgcManagerDirect ctor API and doc string improved

Bug: chromium:1275566
Change-Id: Iedc8f5cbbf65fbf018da9df1aaa1f8ade1bbc063
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/268840
Reviewed-by: Hanna Silen <silen@webrtc.org>
Commit-Queue: Alessio Bazzica <alessiob@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37569}
This commit is contained in:
Alessio Bazzica
2022-07-19 12:18:38 +02:00
committed by WebRTC LUCI CQ
parent ba5700171f
commit 866caeb62c
6 changed files with 137 additions and 162 deletions

View File

@ -24,6 +24,7 @@ rtc_library("agc") {
":gain_control_interface",
":gain_map",
":level_estimation",
"..:api",
"..:apm_logging",
"..:audio_buffer",
"..:audio_frame_view",

View File

@ -53,8 +53,8 @@ constexpr int kSurplusCompressionGain = 6;
// frames).
constexpr int kClippingPredictorEvaluatorHistorySize = 500;
using ClippingPredictorConfig = AudioProcessing::Config::GainController1::
AnalogGainController::ClippingPredictor;
using AnalogAgcConfig =
AudioProcessing::Config::GainController1::AnalogGainController;
// Returns whether a fall-back solution to choose the maximum level should be
// chosen.
@ -441,51 +441,35 @@ void MonoAgc::UpdateCompressor() {
std::atomic<int> AgcManagerDirect::instance_counter_(0);
AgcManagerDirect::AgcManagerDirect(
Agc* agc,
int startup_min_level,
int clipped_level_min,
int clipped_level_step,
float clipped_ratio_threshold,
int clipped_wait_frames,
const ClippingPredictorConfig& clipping_config)
: AgcManagerDirect(/*num_capture_channels=*/1,
startup_min_level,
clipped_level_min,
/*disable_digital_adaptive=*/false,
clipped_level_step,
clipped_ratio_threshold,
clipped_wait_frames,
clipping_config) {
const AudioProcessing::Config::GainController1::AnalogGainController&
analog_config,
Agc* agc)
: AgcManagerDirect(/*num_capture_channels=*/1, analog_config) {
RTC_DCHECK(channel_agcs_[0]);
RTC_DCHECK(agc);
channel_agcs_[0]->set_agc(agc);
}
AgcManagerDirect::AgcManagerDirect(
int num_capture_channels,
int startup_min_level,
int clipped_level_min,
bool disable_digital_adaptive,
int clipped_level_step,
float clipped_ratio_threshold,
int clipped_wait_frames,
const ClippingPredictorConfig& clipping_config)
AgcManagerDirect::AgcManagerDirect(int num_capture_channels,
const AnalogAgcConfig& analog_config)
: 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_(disable_digital_adaptive),
frames_since_clipped_(clipped_wait_frames),
disable_digital_adaptive_(!analog_config.enable_digital_adaptive),
frames_since_clipped_(analog_config.clipped_wait_frames),
capture_output_used_(true),
clipped_level_step_(clipped_level_step),
clipped_ratio_threshold_(clipped_ratio_threshold),
clipped_wait_frames_(clipped_wait_frames),
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, clipping_config)),
use_clipping_predictor_step_(!!clipping_predictor_ &&
clipping_config.use_predicted_step),
CreateClippingPredictor(num_capture_channels,
analog_config.clipping_predictor)),
use_clipping_predictor_step_(
!!clipping_predictor_ &&
analog_config.clipping_predictor.use_predicted_step),
clipping_predictor_evaluator_(kClippingPredictorEvaluatorHistorySize),
clipping_predictor_log_counter_(0),
clipping_rate_log_(0.0f),
@ -499,15 +483,16 @@ AgcManagerDirect::AgcManagerDirect(
ApmDataDumper* data_dumper_ch = ch == 0 ? data_dumper_.get() : nullptr;
channel_agcs_[ch] = std::make_unique<MonoAgc>(
data_dumper_ch, startup_min_level, clipped_level_min,
disable_digital_adaptive_, min_mic_level);
data_dumper_ch, analog_config.startup_min_volume,
analog_config.clipped_level_min, disable_digital_adaptive_,
min_mic_level);
}
RTC_DCHECK(!channel_agcs_.empty());
RTC_DCHECK_GT(clipped_level_step, 0);
RTC_DCHECK_LE(clipped_level_step, 255);
RTC_DCHECK_GT(clipped_ratio_threshold, 0.f);
RTC_DCHECK_LT(clipped_ratio_threshold, 1.f);
RTC_DCHECK_GT(clipped_wait_frames, 0);
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();
}
@ -529,22 +514,21 @@ void AgcManagerDirect::Initialize() {
}
void AgcManagerDirect::SetupDigitalGainControl(
GainControl* gain_control) const {
RTC_DCHECK(gain_control);
if (gain_control->set_mode(GainControl::kFixedDigital) != 0) {
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) {
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) {
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) {
if (gain_control.enable_limiter(enable_limiter) != 0) {
RTC_LOG(LS_ERROR) << "enable_limiter() failed.";
}
}

View File

@ -20,6 +20,7 @@
#include "modules/audio_processing/agc/clipping_predictor.h"
#include "modules/audio_processing/agc/clipping_predictor_evaluator.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"
@ -28,53 +29,60 @@ namespace webrtc {
class MonoAgc;
class GainControl;
// Direct interface to use AGC to set volume and compression values.
// AudioProcessing uses this interface directly to integrate the callback-less
// AGC.
//
// This class is not thread-safe.
// Adaptive Gain Controller (AGC) that combines an analog and digital gain
// controller. The digital controller determines and applies the digital
// compression gain. The analog controller recommends what input volume (a.k.a.,
// analog level) to use, handles input volume changes and input clipping. In
// particular, it handles input volume changes triggered by the user (e.g.,
// input volume set to zero by a HW mute button). This class is not thread-safe.
class AgcManagerDirect final {
public:
// AgcManagerDirect will configure GainControl internally. The user is
// responsible for processing the audio using it after the call to Process.
// The operating range of startup_min_level is [12, 255] and any input value
// outside that range will be clamped. `clipped_level_step` is the amount
// the microphone level is lowered with every clipping event, limited to
// (0, 255]. `clipped_ratio_threshold` is the proportion of clipped
// samples required to declare a clipping event, limited to (0.f, 1.f).
// `clipped_wait_frames` is the time in frames to wait after a clipping event
// before checking again, limited to values higher than 0.
// 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.
AgcManagerDirect(
int num_capture_channels,
int startup_min_level,
int clipped_level_min,
bool disable_digital_adaptive,
int clipped_level_step,
float clipped_ratio_threshold,
int clipped_wait_frames,
const AudioProcessing::Config::GainController1::AnalogGainController::
ClippingPredictor& clipping_config);
const AudioProcessing::Config::GainController1::AnalogGainController&
analog_config);
~AgcManagerDirect();
AgcManagerDirect(const AgcManagerDirect&) = delete;
AgcManagerDirect& operator=(const AgcManagerDirect&) = delete;
void Initialize();
void SetupDigitalGainControl(GainControl* gain_control) const;
// 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;
// Analyzes `audio` before `Process()` is called so that the analysis can be
// performed before external digital processing operations take place (e.g.,
// echo cancellation). The analysis consists of input clipping detection and
// prediction (if enabled).
void AnalyzePreProcess(const AudioBuffer* audio);
// Processes `audio`. Chooses and applies a digital compression gain on each
// channel and chooses the new input volume to recommend. Undefined behavior
// if `AnalyzePreProcess()` is not called beforehand.
void Process(const AudioBuffer* audio);
// Call when the capture stream output has been flagged to be used/not-used.
// If unused, the manager disregards all incoming audio.
void HandleCaptureOutputUsedChange(bool capture_output_used);
float voice_probability() const;
// Returns the recommended input volume.
int stream_analog_level() const { return stream_analog_level_; }
// Sets the current input volume.
void set_stream_analog_level(int level);
int num_channels() const { return num_capture_channels_; }
// If available, returns a new compression gain for the digital gain control.
// If available, returns the latest digital compression gain that has been
// applied.
absl::optional<int> GetDigitalComressionGain();
// Returns true if clipping prediction is enabled.
@ -109,17 +117,12 @@ class AgcManagerDirect final {
FRIEND_TEST_ALL_PREFIXES(AgcManagerDirectTest,
UnusedClippingPredictionsProduceEqualAnalogLevels);
// Dependency injection for testing. Don't delete `agc` as the memory is owned
// by the manager.
// Ctor that creates a single channel AGC and by injecting `agc`.
// `agc` will be owned by this class; hence, do not delete it.
AgcManagerDirect(
Agc* agc,
int startup_min_level,
int clipped_level_min,
int clipped_level_step,
float clipped_ratio_threshold,
int clipped_wait_frames,
const AudioProcessing::Config::GainController1::AnalogGainController::
ClippingPredictor& clipping_config);
const AudioProcessing::Config::GainController1::AnalogGainController&
analog_config,
Agc* agc);
void AnalyzePreProcess(const float* const* audio, size_t samples_per_channel);

View File

@ -40,11 +40,11 @@ constexpr int kClippedLevelStep = 15;
constexpr float kClippedRatioThreshold = 0.1f;
constexpr int kClippedWaitFrames = 300;
constexpr AudioProcessing::Config::GainController1::AnalogGainController
kDefaultAnalogConfig{};
using AnalogAgcConfig =
AudioProcessing::Config::GainController1::AnalogGainController;
using ClippingPredictorConfig = AudioProcessing::Config::GainController1::
AnalogGainController::ClippingPredictor;
constexpr AnalogAgcConfig kDefaultAnalogConfig{};
class MockGainControl : public GainControl {
public:
@ -68,28 +68,24 @@ class MockGainControl : public GainControl {
MOCK_METHOD(bool, stream_is_saturated, (), (const, override));
};
// TODO(bugs.webrtc.org/12874): Remove and use designated initializers once
// fixed.
std::unique_ptr<AgcManagerDirect> CreateAgcManagerDirect(
int startup_min_level,
int clipped_level_step,
float clipped_ratio_threshold,
int clipped_wait_frames) {
return std::make_unique<AgcManagerDirect>(
/*num_capture_channels=*/1, startup_min_level, kClippedMin,
/*disable_digital_adaptive=*/true, clipped_level_step,
clipped_ratio_threshold, clipped_wait_frames,
kDefaultAnalogConfig.clipping_predictor);
}
std::unique_ptr<AgcManagerDirect> CreateAgcManagerDirect(
int startup_min_level,
int startup_min_volume,
int clipped_level_step,
float clipped_ratio_threshold,
int clipped_wait_frames,
const ClippingPredictorConfig& clipping_cfg) {
return std::make_unique<AgcManagerDirect>(
/*num_capture_channels=*/1, startup_min_level, kClippedMin,
/*disable_digital_adaptive=*/true, clipped_level_step,
clipped_ratio_threshold, clipped_wait_frames, clipping_cfg);
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;
return std::make_unique<AgcManagerDirect>(/*num_capture_channels=*/1, config);
}
// Calls `AnalyzePreProcess()` on `manager` `num_calls` times. `peak_ratio` is a
@ -185,6 +181,20 @@ void CallPreProcessAndProcess(int num_calls,
} // namespace
// TODO(bugs.webrtc.org/12874): Use constexpr struct with designated
// initializers once fixed.
constexpr AnalogAgcConfig GetAnalogAgcTestConfig() {
AnalogAgcConfig config;
config.startup_min_volume = kInitialVolume;
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;
return config;
};
class AgcManagerDirectTestHelper {
public:
AgcManagerDirectTestHelper()
@ -197,16 +207,10 @@ class AgcManagerDirectTestHelper {
audio(kNumChannels),
audio_data(kNumChannels * kSamplesPerChannel, 0.0f),
mock_agc(new MockAgc()),
manager(mock_agc,
kInitialVolume,
kClippedMin,
kClippedLevelStep,
kClippedRatioThreshold,
kClippedWaitFrames,
kDefaultAnalogConfig.clipping_predictor) {
manager(GetAnalogAgcTestConfig(), mock_agc) {
ExpectInitialize();
manager.Initialize();
manager.SetupDigitalGainControl(&mock_gain_control);
manager.SetupDigitalGainControl(mock_gain_control);
for (size_t ch = 0; ch < kNumChannels; ++ch) {
audio[ch] = &audio_data[ch * kSamplesPerChannel];
}
@ -947,11 +951,11 @@ TEST(AgcManagerDirectTest, DisableDigitalDisablesDigital) {
EXPECT_CALL(mock_gain_control, set_compression_gain_db(0));
EXPECT_CALL(mock_gain_control, enable_limiter(false));
std::unique_ptr<AgcManagerDirect> manager =
CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep,
kClippedRatioThreshold, kClippedWaitFrames);
AnalogAgcConfig config;
config.enable_digital_adaptive = false;
auto manager = std::make_unique<AgcManagerDirect>(kNumChannels, config);
manager->Initialize();
manager->SetupDigitalGainControl(&mock_gain_control);
manager->SetupDigitalGainControl(mock_gain_control);
}
TEST(AgcManagerDirectTest, AgcMinMicLevelExperimentDefault) {
@ -1075,12 +1079,14 @@ TEST(AgcManagerDirectTest,
const auto factory = []() {
// Use a large clipped level step to more quickly decrease the analog gain
// with clipping.
auto controller = std::make_unique<AgcManagerDirect>(
/*num_capture_channels=*/1, kInitialVolume,
kDefaultAnalogConfig.clipped_level_min,
/*disable_digital_adaptive=*/true, /*clipped_level_step=*/64,
kClippedRatioThreshold, kClippedWaitFrames,
kDefaultAnalogConfig.clipping_predictor);
AnalogAgcConfig config = kDefaultAnalogConfig;
config.startup_min_volume = kInitialVolume;
config.enable_digital_adaptive = false;
config.clipped_level_step = 64;
config.clipped_ratio_threshold = kClippedRatioThreshold;
config.clipped_wait_frames = kClippedWaitFrames;
auto controller =
std::make_unique<AgcManagerDirect>(/*num_capture_channels=*/1, config);
controller->Initialize();
controller->set_stream_analog_level(kInitialVolume);
return controller;
@ -1181,11 +1187,9 @@ TEST(AgcManagerDirectTest, DisableClippingPredictorDoesNotLowerVolume) {
AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz,
kNumChannels, kSampleRateHz, kNumChannels);
// TODO(bugs.webrtc.org/12874): Use designated initializers once fixed.
constexpr ClippingPredictorConfig kConfig{/*enabled=*/false};
AgcManagerDirect manager(new ::testing::NiceMock<MockAgc>(), kInitialVolume,
kClippedMin, kClippedLevelStep,
kClippedRatioThreshold, kClippedWaitFrames, kConfig);
AnalogAgcConfig config = GetAnalogAgcTestConfig();
config.clipping_predictor.enabled = false;
AgcManagerDirect manager(config, new ::testing::NiceMock<MockAgc>());
manager.Initialize();
manager.set_stream_analog_level(/*level=*/255);
EXPECT_FALSE(manager.clipping_predictor_enabled());
@ -1204,20 +1208,15 @@ TEST(AgcManagerDirectTest, UsedClippingPredictionsProduceLowerAnalogLevels) {
AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz,
kNumChannels, kSampleRateHz, kNumChannels);
// TODO(bugs.webrtc.org/12874): Use designated initializers once fixed.
ClippingPredictorConfig config_with_prediction;
config_with_prediction.enabled = true;
config_with_prediction.use_predicted_step = true;
ClippingPredictorConfig config_without_prediction;
config_without_prediction.enabled = false;
AgcManagerDirect manager_with_prediction(
new ::testing::NiceMock<MockAgc>(), kInitialVolume, kClippedMin,
kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames,
config_with_prediction);
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;
AgcManagerDirect manager_with_prediction(config_with_prediction,
new ::testing::NiceMock<MockAgc>());
AgcManagerDirect manager_without_prediction(
new ::testing::NiceMock<MockAgc>(), kInitialVolume, kClippedMin,
kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames,
config_without_prediction);
config_without_prediction, new ::testing::NiceMock<MockAgc>());
manager_with_prediction.Initialize();
manager_without_prediction.Initialize();
@ -1307,20 +1306,15 @@ TEST(AgcManagerDirectTest, UnusedClippingPredictionsProduceEqualAnalogLevels) {
AudioBuffer audio_buffer(kSampleRateHz, kNumChannels, kSampleRateHz,
kNumChannels, kSampleRateHz, kNumChannels);
// TODO(bugs.webrtc.org/12874): Use designated initializers once fixed.
ClippingPredictorConfig config_with_prediction;
config_with_prediction.enabled = true;
config_with_prediction.use_predicted_step = false;
ClippingPredictorConfig config_without_prediction;
config_without_prediction.enabled = false;
AgcManagerDirect manager_with_prediction(
new ::testing::NiceMock<MockAgc>(), kInitialVolume, kClippedMin,
kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames,
config_with_prediction);
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;
AgcManagerDirect manager_with_prediction(config_with_prediction,
new ::testing::NiceMock<MockAgc>());
AgcManagerDirect manager_without_prediction(
new ::testing::NiceMock<MockAgc>(), kInitialVolume, kClippedMin,
kClippedLevelStep, kClippedRatioThreshold, kClippedWaitFrames,
config_without_prediction);
config_without_prediction, new ::testing::NiceMock<MockAgc>());
constexpr int kInitialLevel = 255;
constexpr float kClippingPeakRatio = 1.0f;

View File

@ -1889,22 +1889,13 @@ void AudioProcessingImpl::InitializeGainController1() {
stream_analog_level = submodules_.agc_manager->stream_analog_level();
}
submodules_.agc_manager.reset(new AgcManagerDirect(
num_proc_channels(),
config_.gain_controller1.analog_gain_controller.startup_min_volume,
config_.gain_controller1.analog_gain_controller.clipped_level_min,
!config_.gain_controller1.analog_gain_controller
.enable_digital_adaptive,
config_.gain_controller1.analog_gain_controller.clipped_level_step,
config_.gain_controller1.analog_gain_controller.clipped_ratio_threshold,
config_.gain_controller1.analog_gain_controller.clipped_wait_frames,
config_.gain_controller1.analog_gain_controller.clipping_predictor));
num_proc_channels(), config_.gain_controller1.analog_gain_controller));
if (re_creation) {
submodules_.agc_manager->set_stream_analog_level(stream_analog_level);
}
}
submodules_.agc_manager->Initialize();
submodules_.agc_manager->SetupDigitalGainControl(
submodules_.gain_control.get());
submodules_.agc_manager->SetupDigitalGainControl(*submodules_.gain_control);
submodules_.agc_manager->HandleCaptureOutputUsedChange(
capture_.capture_output_used);
}

View File

@ -286,10 +286,12 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface {
// Enables the analog gain controller functionality.
struct AnalogGainController {
bool enabled = true;
// TODO(bugs.webrtc.org/1275566): Describe `startup_min_volume`.
int startup_min_volume = kAgcStartupMinVolume;
// Lowest analog microphone level that will be applied in response to
// clipping.
int clipped_level_min = kClippedLevelMin;
// If true, an adaptive digital gain is applied.
bool enable_digital_adaptive = true;
// Amount the microphone level is lowered with every clipping event.
// Limited to (0, 255].