AudioProcessingImpl: Add the use of AGC2 InputVolumeController

The integration relies on GainController2 methods Process() and
GetRecommendedInputVolume() to internally take into account whether
the input volume controller is enabled in the ctor or not. These
methods are called for every frame processed if GainController2 is
enabled. Analyze() is called if the input volume controller is
enabled.

The functionality can be enabled from the APM config and is not
enabled by default. If multiple input volume controllers are enabled,
an error is logged.

Tested: Bitexact on a large number of aecdumps if not enabled
Bug: webrtc:7494
Change-Id: I9105483be34eb95fab3c46afbbd368802e956fad
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/282720
Reviewed-by: Alessio Bazzica <alessiob@webrtc.org>
Reviewed-by: Sam Zackrisson <saza@webrtc.org>
Commit-Queue: Hanna Silen <silen@webrtc.org>
Reviewed-by: Per Åhgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38776}
This commit is contained in:
Hanna Silen 2022-11-30 15:16:21 +01:00 committed by WebRTC LUCI CQ
parent 256d3ee2bf
commit d4dbe4527d
2 changed files with 162 additions and 32 deletions

View file

@ -830,6 +830,10 @@ void AudioProcessingImpl::HandleCaptureOutputUsedSetting(
submodules_.noise_suppressor->SetCaptureOutputUsage(
capture_.capture_output_used);
}
if (submodules_.gain_controller2) {
submodules_.gain_controller2->SetCaptureOutputUsed(
capture_.capture_output_used);
}
}
void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) {
@ -1001,7 +1005,9 @@ void AudioProcessingImpl::HandleCaptureRuntimeSettings() {
// TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump.
break;
case RuntimeSetting::Type::kCaptureCompressionGain: {
if (!submodules_.agc_manager) {
if (!submodules_.agc_manager &&
!(submodules_.gain_controller2 &&
config_.gain_controller2.input_volume_controller.enabled)) {
float value;
setting.GetFloat(&value);
int int_value = static_cast<int>(value + .5f);
@ -1337,6 +1343,16 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
submodules_.agc_manager->AnalyzePreProcess(*capture_buffer);
}
if (submodules_.gain_controller2 &&
config_.gain_controller2.input_volume_controller.enabled) {
// Expect the volume to be available if the input controller is enabled.
RTC_DCHECK(capture_.applied_input_volume.has_value());
if (capture_.applied_input_volume.has_value()) {
submodules_.gain_controller2->Analyze(*capture_.applied_input_volume,
*capture_buffer);
}
}
if (submodule_states_.CaptureMultiBandSubModulesActive() &&
SampleRateSupportsMultiBand(
capture_nonlocked_.capture_processing_format.sample_rate_hz())) {
@ -1490,6 +1506,8 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
}
if (submodules_.gain_controller2) {
// TODO(bugs.webrtc.org/7494): Let AGC2 detect applied input volume
// changes.
submodules_.gain_controller2->Process(
voice_probability, capture_.applied_input_volume_changed,
capture_buffer);
@ -1819,6 +1837,13 @@ void AudioProcessingImpl::UpdateRecommendedInputVolumeLocked() {
return;
}
if (submodules_.gain_controller2 &&
config_.gain_controller2.input_volume_controller.enabled) {
capture_.recommended_input_volume =
submodules_.gain_controller2->GetRecommendedInputVolume();
return;
}
capture_.recommended_input_volume = capture_.applied_input_volume;
}
@ -2017,6 +2042,16 @@ void AudioProcessingImpl::InitializeEchoController() {
}
void AudioProcessingImpl::InitializeGainController1() {
if (config_.gain_controller2.enabled &&
config_.gain_controller2.input_volume_controller.enabled &&
config_.gain_controller1.enabled &&
(config_.gain_controller1.mode ==
AudioProcessing::Config::GainController1::kAdaptiveAnalog ||
config_.gain_controller1.analog_gain_controller.enabled)) {
RTC_LOG(LS_ERROR) << "APM configuration not valid: "
<< "Multiple input volume controllers enabled.";
}
if (!config_.gain_controller1.enabled) {
submodules_.agc_manager.reset();
submodules_.gain_control.reset();
@ -2090,6 +2125,8 @@ void AudioProcessingImpl::InitializeGainController2(bool config_has_changed) {
submodules_.gain_controller2 = std::make_unique<GainController2>(
config_.gain_controller2, proc_fullband_sample_rate_hz(),
num_input_channels(), use_internal_vad);
submodules_.gain_controller2->SetCaptureOutputUsed(
capture_.capture_output_used);
}
}

View file

@ -132,13 +132,20 @@ class TestRenderPreProcessor : public CustomProcessing {
};
// Creates a simple `AudioProcessing` instance for APM input volume testing
// with analog and digital AGC enabled.
rtc::scoped_refptr<AudioProcessing> CreateApmForInputVolumeTest() {
// with AGC1 analog and/or AGC2 input volume controller enabled and AGC2
// digital controller enabled.
rtc::scoped_refptr<AudioProcessing> CreateApmForInputVolumeTest(
bool agc1_analog_gain_controller_enabled,
bool agc2_input_volume_controller_enabled) {
webrtc::AudioProcessing::Config config;
// Enable AGC1 analog.
config.gain_controller1.enabled = true;
config.gain_controller1.analog_gain_controller.enabled = true;
// Enable AGC2 adaptive digital.
// Enable AGC1 analog controller.
config.gain_controller1.enabled = agc1_analog_gain_controller_enabled;
config.gain_controller1.analog_gain_controller.enabled =
agc1_analog_gain_controller_enabled;
// Enable AG2 input volume controller
config.gain_controller2.input_volume_controller.enabled =
agc2_input_volume_controller_enabled;
// Enable AGC2 adaptive digital controller.
config.gain_controller1.analog_gain_controller.enable_digital_adaptive =
false;
config.gain_controller2.enabled = true;
@ -146,6 +153,7 @@ rtc::scoped_refptr<AudioProcessing> CreateApmForInputVolumeTest() {
auto apm(AudioProcessingBuilder().Create());
apm->ApplyConfig(config);
return apm;
}
@ -177,25 +185,30 @@ int ProcessInputVolume(AudioProcessing& apm,
constexpr char kMinMicLevelFieldTrial[] =
"WebRTC-Audio-2ndAgcMinMicLevelExperiment";
constexpr char kMinInputVolumeFieldTrial[] = "WebRTC-Audio-Agc2-MinInputVolume";
constexpr int kMinInputVolume = 12;
std::string GetMinMicLevelExperimentFieldTrial(absl::optional<int> value) {
char field_trial_buffer[64];
char field_trial_buffer[128];
rtc::SimpleStringBuilder builder(field_trial_buffer);
if (value.has_value()) {
RTC_DCHECK_GE(*value, 0);
RTC_DCHECK_LE(*value, 255);
builder << kMinMicLevelFieldTrial << "/Enabled-" << *value << "/";
builder << kMinInputVolumeFieldTrial << "/Enabled-" << *value << "/";
} else {
builder << kMinMicLevelFieldTrial << "/Disabled/";
builder << kMinInputVolumeFieldTrial << "/Disabled/";
}
return builder.str();
}
// TODO(webrtc:7494): Remove the fieldtrial from the input volume tests when
// "WebRTC-Audio-2ndAgcMinMicLevelExperiment" is removed.
// "WebRTC-Audio-2ndAgcMinMicLevelExperiment" and
// "WebRTC-Audio-Agc2-MinInputVolume" are removed.
class InputVolumeStartupParameterizedTest
: public ::testing::TestWithParam<std::tuple<int, absl::optional<int>>> {
: public ::testing::TestWithParam<
std::tuple<int, absl::optional<int>, bool, bool>> {
protected:
InputVolumeStartupParameterizedTest()
: field_trials_(
@ -204,6 +217,12 @@ class InputVolumeStartupParameterizedTest
int GetMinVolume() const {
return std::get<1>(GetParam()).value_or(kMinInputVolume);
}
bool GetAgc1AnalogControllerEnabled() const {
return std::get<2>(GetParam());
}
bool GetAgc2InputVolumeControllerEnabled() const {
return std::get<3>(GetParam());
}
private:
test::ScopedFieldTrials field_trials_;
@ -211,7 +230,7 @@ class InputVolumeStartupParameterizedTest
class InputVolumeNotZeroParameterizedTest
: public ::testing::TestWithParam<
std::tuple<int, int, absl::optional<int>>> {
std::tuple<int, int, absl::optional<int>, bool, bool>> {
protected:
InputVolumeNotZeroParameterizedTest()
: field_trials_(
@ -224,13 +243,20 @@ class InputVolumeNotZeroParameterizedTest
bool GetMinMicLevelExperimentEnabled() {
return std::get<2>(GetParam()).has_value();
}
bool GetAgc1AnalogControllerEnabled() const {
return std::get<3>(GetParam());
}
bool GetAgc2InputVolumeControllerEnabled() const {
return std::get<4>(GetParam());
}
private:
test::ScopedFieldTrials field_trials_;
};
class InputVolumeZeroParameterizedTest
: public ::testing::TestWithParam<std::tuple<int, absl::optional<int>>> {
: public ::testing::TestWithParam<
std::tuple<int, absl::optional<int>, bool, bool>> {
protected:
InputVolumeZeroParameterizedTest()
: field_trials_(
@ -239,6 +265,12 @@ class InputVolumeZeroParameterizedTest
int GetMinVolume() const {
return std::get<1>(GetParam()).value_or(kMinInputVolume);
}
bool GetAgc1AnalogControllerEnabled() const {
return std::get<2>(GetParam());
}
bool GetAgc2InputVolumeControllerEnabled() const {
return std::get<3>(GetParam());
}
private:
test::ScopedFieldTrials field_trials_;
@ -934,35 +966,59 @@ TEST_P(InputVolumeStartupParameterizedTest,
const int applied_startup_input_volume = GetStartupVolume();
const int expected_volume =
std::max(applied_startup_input_volume, GetMinVolume());
auto apm = CreateApmForInputVolumeTest();
const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled();
const bool agc2_input_volume_controller_enabled =
GetAgc2InputVolumeControllerEnabled();
auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled,
agc2_input_volume_controller_enabled);
const int recommended_input_volume =
ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
if (!agc1_analog_controller_enabled &&
!agc2_input_volume_controller_enabled) {
// No input volume changes if none of the analog controllers is enabled.
ASSERT_EQ(recommended_input_volume, applied_startup_input_volume);
} else {
ASSERT_EQ(recommended_input_volume, expected_volume);
}
}
// Tests that the minimum input volume is applied if the volume is manually
// adjusted to a non-zero value only if
// "WebRTC-Audio-2ndAgcMinMicLevelExperiment" is enabled.
// adjusted to a non-zero value 1) always for AGC2 input volume controller and
// 2) only if "WebRTC-Audio-2ndAgcMinMicLevelExperiment" is enabled for AGC1
// analog controller.
TEST_P(InputVolumeNotZeroParameterizedTest,
VerifyMinVolumeMaybeAppliedAfterManualVolumeAdjustments) {
const int applied_startup_input_volume = GetStartupVolume();
const int applied_input_volume = GetVolume();
const int expected_volume = std::max(applied_input_volume, GetMinVolume());
auto apm = CreateApmForInputVolumeTest();
const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled();
const bool agc2_input_volume_controller_enabled =
GetAgc2InputVolumeControllerEnabled();
auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled,
agc2_input_volume_controller_enabled);
ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
const int recommended_input_volume =
ProcessInputVolume(*apm, /*num_frames=*/1, applied_input_volume);
ASSERT_NE(applied_input_volume, 0);
if (GetMinMicLevelExperimentEnabled()) {
if (!agc1_analog_controller_enabled &&
!agc2_input_volume_controller_enabled) {
// No input volume changes if none of the analog controllers is enabled.
ASSERT_EQ(recommended_input_volume, applied_input_volume);
} else {
if (GetMinMicLevelExperimentEnabled() ||
(!agc1_analog_controller_enabled &&
agc2_input_volume_controller_enabled)) {
ASSERT_EQ(recommended_input_volume, expected_volume);
} else {
ASSERT_EQ(recommended_input_volume, applied_input_volume);
}
}
}
// Tests that the minimum input volume is not applied if the volume is manually
// adjusted to zero.
@ -970,16 +1026,26 @@ TEST_P(InputVolumeZeroParameterizedTest,
VerifyMinVolumeNotAppliedAfterManualVolumeAdjustments) {
constexpr int kZeroVolume = 0;
const int applied_startup_input_volume = GetStartupVolume();
auto apm = CreateApmForInputVolumeTest();
const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled();
const bool agc2_input_volume_controller_enabled =
GetAgc2InputVolumeControllerEnabled();
auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled,
agc2_input_volume_controller_enabled);
const int recommended_input_volume_after_startup =
ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
const int recommended_input_volume =
ProcessInputVolume(*apm, /*num_frames=*/1, kZeroVolume);
if (!agc1_analog_controller_enabled &&
!agc2_input_volume_controller_enabled) {
// No input volume changes if none of the analog controllers is enabled.
ASSERT_EQ(recommended_input_volume, kZeroVolume);
} else {
ASSERT_NE(recommended_input_volume, recommended_input_volume_after_startup);
ASSERT_EQ(recommended_input_volume, kZeroVolume);
}
}
// Tests that the minimum input volume is applied if the volume is not zero
// before it is automatically adjusted.
@ -987,17 +1053,28 @@ TEST_P(InputVolumeNotZeroParameterizedTest,
VerifyMinVolumeAppliedAfterAutomaticVolumeAdjustments) {
const int applied_startup_input_volume = GetStartupVolume();
const int applied_input_volume = GetVolume();
auto apm = CreateApmForInputVolumeTest();
const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled();
const bool agc2_input_volume_controller_enabled =
GetAgc2InputVolumeControllerEnabled();
auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled,
agc2_input_volume_controller_enabled);
ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
const int recommended_input_volume =
ProcessInputVolume(*apm, /*num_frames=*/400, applied_input_volume);
ASSERT_NE(applied_input_volume, 0);
if (!agc1_analog_controller_enabled &&
!agc2_input_volume_controller_enabled) {
// No input volume changes if none of the analog controllers is enabled.
ASSERT_EQ(recommended_input_volume, applied_input_volume);
} else {
if (recommended_input_volume != applied_input_volume) {
ASSERT_GE(recommended_input_volume, GetMinVolume());
}
}
}
// Tests that the minimum input volume is not applied if the volume is zero
// before it is automatically adjusted.
@ -1005,35 +1082,51 @@ TEST_P(InputVolumeZeroParameterizedTest,
VerifyMinVolumeNotAppliedAfterAutomaticVolumeAdjustments) {
constexpr int kZeroVolume = 0;
const int applied_startup_input_volume = GetStartupVolume();
auto apm = CreateApmForInputVolumeTest();
const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled();
const bool agc2_input_volume_controller_enabled =
GetAgc2InputVolumeControllerEnabled();
auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled,
agc2_input_volume_controller_enabled);
const int recommended_input_volume_after_startup =
ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
const int recommended_input_volume =
ProcessInputVolume(*apm, /*num_frames=*/400, kZeroVolume);
if (!agc1_analog_controller_enabled &&
!agc2_input_volume_controller_enabled) {
// No input volume changes if none of the analog controllers is enabled.
ASSERT_EQ(recommended_input_volume, kZeroVolume);
} else {
ASSERT_NE(recommended_input_volume, recommended_input_volume_after_startup);
ASSERT_EQ(recommended_input_volume, kZeroVolume);
}
}
INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest,
InputVolumeStartupParameterizedTest,
::testing::Combine(::testing::Values(0, 5, 30),
::testing::Values(absl::nullopt,
20)));
20),
::testing::Bool(),
::testing::Bool()));
INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest,
InputVolumeNotZeroParameterizedTest,
::testing::Combine(::testing::Values(0, 5, 15),
::testing::Values(1, 5, 30),
::testing::Values(absl::nullopt,
20)));
20),
::testing::Bool(),
::testing::Bool()));
INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest,
InputVolumeZeroParameterizedTest,
::testing::Combine(::testing::Values(0, 5, 15),
::testing::Values(absl::nullopt,
20)));
20),
::testing::Bool(),
::testing::Bool()));
// When the input volume is not emulated and no input volume controller is
// active, the recommended volume must always be the applied volume.