mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00
Unify AGC2 experiment field trials into one
In order to experiment with AGC2 and TS at the same time, 3 field trials are removed and merged into `WebRTC-Audio-GainController2`, which is existing. New parameters for the `WebRTC-Audio-GainController2` field trial: - `switch_to_agc2`: true by default; when true, the gain control switches to AGC2 (both for the input volume and for the adaptive digital gain); - `min_input_volume`: minimum input volume enforced by the input volume controller when the applied input volume is not zero; - `disallow_transient_suppressor_usage`: when true, TS is never created. Removed field trials: - `WebRTC-Audio-Agc2-MinInputVolume`: now a parameter of `WebRTC-Audio-GainController2`; - `WebRTC-ApmTransientSuppressorKillSwitch`: now a parameter of `WebRTC-Audio-GainController2`; - `WebRTC-Audio-TransientSuppressorVadMode`: automatically inferred from `WebRTC-Audio-GainController2`. Bug: webrtc:7494 Change-Id: I452798c0862d71f9adae6d163fe841df05ca44d6 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/287861 Commit-Queue: Alessio Bazzica <alessiob@webrtc.org> Reviewed-by: Hanna Silen <silen@webrtc.org> Cr-Commit-Position: refs/heads/main@{#38890}
This commit is contained in:
parent
597a2ba41a
commit
3b51cd328e
6 changed files with 795 additions and 1189 deletions
|
@ -50,29 +50,6 @@ Agc1ClippingPredictorConfig CreateClippingPredictorConfig(bool enabled) {
|
|||
return config;
|
||||
}
|
||||
|
||||
// Returns the minimum input volume to recommend.
|
||||
// If the "WebRTC-Audio-Agc2-MinInputVolume" field trial is specified, parses it
|
||||
// and returns the value specified after "Enabled-" if valid - i.e., in the
|
||||
// range 0-255. Otherwise returns the default value.
|
||||
// Example:
|
||||
// "WebRTC-Audio-Agc2-MinInputVolume/Enabled-80" => returns 80.
|
||||
int GetMinInputVolume() {
|
||||
constexpr int kDefaultMinInputVolume = 12;
|
||||
constexpr char kFieldTrial[] = "WebRTC-Audio-Agc2-MinInputVolume";
|
||||
if (!webrtc::field_trial::IsEnabled(kFieldTrial)) {
|
||||
return kDefaultMinInputVolume;
|
||||
}
|
||||
std::string field_trial_str = webrtc::field_trial::FindFullName(kFieldTrial);
|
||||
int min_input_volume = -1;
|
||||
sscanf(field_trial_str.c_str(), "Enabled-%d", &min_input_volume);
|
||||
if (min_input_volume >= 0 && min_input_volume <= 255) {
|
||||
return min_input_volume;
|
||||
}
|
||||
RTC_LOG(LS_WARNING) << "[AGC2] Invalid volume for " << kFieldTrial
|
||||
<< ", ignored.";
|
||||
return kDefaultMinInputVolume;
|
||||
}
|
||||
|
||||
// Returns an input volume in the [`min_input_volume`, `kMaxInputVolume`] range
|
||||
// that reduces `gain_error_db`, which is a gain error estimated when
|
||||
// `input_volume` was applied, according to a fixed gain map.
|
||||
|
@ -377,7 +354,7 @@ void MonoInputVolumeController::UpdateInputVolume(int rms_error_db) {
|
|||
InputVolumeController::InputVolumeController(int num_capture_channels,
|
||||
const Config& config)
|
||||
: num_capture_channels_(num_capture_channels),
|
||||
min_input_volume_(GetMinInputVolume()),
|
||||
min_input_volume_(config.min_input_volume),
|
||||
capture_output_used_(true),
|
||||
clipped_level_step_(config.clipped_level_step),
|
||||
clipped_ratio_threshold_(config.clipped_ratio_threshold),
|
||||
|
|
|
@ -35,6 +35,9 @@ class InputVolumeController final {
|
|||
public:
|
||||
// Config for the constructor.
|
||||
struct Config {
|
||||
// Minimum input volume that can be recommended. Not enforced when the
|
||||
// applied input volume is zero outside startup.
|
||||
int min_input_volume = 20;
|
||||
// Lowest input volume level that will be applied in response to clipping.
|
||||
int clipped_level_min = 70;
|
||||
// Amount input volume level is lowered with every clipping event. Limited
|
||||
|
@ -52,13 +55,9 @@ class InputVolumeController final {
|
|||
// [`target_range_min_dbfs`, `target_range_max_dbfs`], no input volume
|
||||
// adjustments are done based on the speech level. For speech levels below
|
||||
// and above the range, the targets `target_range_min_dbfs` and
|
||||
// `target_range_max_dbfs` are used, respectively. The example values
|
||||
// `target_range_max_dbfs` -18 and `target_range_min_dbfs` -48 refer to a
|
||||
// configuration where the zero-digital-gain target is -18 dBFS and the
|
||||
// digital gain control is expected to compensate for speech level errors
|
||||
// up to -30 dB.
|
||||
int target_range_max_dbfs = -18;
|
||||
int target_range_min_dbfs = -48;
|
||||
// `target_range_max_dbfs` are used, respectively.
|
||||
int target_range_max_dbfs = -30;
|
||||
int target_range_min_dbfs = -50;
|
||||
// Number of wait frames between the recommended input volume updates.
|
||||
int update_input_volume_wait_frames = 100;
|
||||
// Speech probability threshold: speech probabilities below the threshold
|
||||
|
|
|
@ -38,7 +38,7 @@ constexpr int kNumChannels = 1;
|
|||
constexpr int kInitialInputVolume = 128;
|
||||
constexpr int kClippedMin = 165; // Arbitrary, but different from the default.
|
||||
constexpr float kAboveClippedThreshold = 0.2f;
|
||||
constexpr int kMinMicLevel = 12;
|
||||
constexpr int kMinMicLevel = 20;
|
||||
constexpr int kClippedLevelStep = 15;
|
||||
constexpr float kClippedRatioThreshold = 0.1f;
|
||||
constexpr int kClippedWaitFrames = 300;
|
||||
|
@ -56,7 +56,6 @@ using ClippingPredictorConfig = AudioProcessing::Config::GainController1::
|
|||
|
||||
using InputVolumeControllerConfig = InputVolumeController::Config;
|
||||
|
||||
constexpr InputVolumeControllerConfig kDefaultInputVolumeControllerConfig{};
|
||||
constexpr ClippingPredictorConfig kDefaultClippingPredictorConfig{};
|
||||
|
||||
std::unique_ptr<InputVolumeController> CreateInputVolumeController(
|
||||
|
@ -66,6 +65,7 @@ std::unique_ptr<InputVolumeController> CreateInputVolumeController(
|
|||
bool enable_clipping_predictor = false,
|
||||
int update_input_volume_wait_frames = 0) {
|
||||
InputVolumeControllerConfig config{
|
||||
.min_input_volume = kMinMicLevel,
|
||||
.clipped_level_min = kClippedMin,
|
||||
.clipped_level_step = clipped_level_step,
|
||||
.clipped_ratio_threshold = clipped_ratio_threshold,
|
||||
|
@ -82,34 +82,6 @@ std::unique_ptr<InputVolumeController> CreateInputVolumeController(
|
|||
config);
|
||||
}
|
||||
|
||||
constexpr char kMinInputVolumeFieldTrial[] = "WebRTC-Audio-Agc2-MinInputVolume";
|
||||
|
||||
std::string GetAgcMinInputVolumeFieldTrial(const std::string& value) {
|
||||
char field_trial_buffer[64];
|
||||
rtc::SimpleStringBuilder builder(field_trial_buffer);
|
||||
builder << kMinInputVolumeFieldTrial << "/" << value << "/";
|
||||
return builder.str();
|
||||
}
|
||||
|
||||
std::string GetAgcMinInputVolumeFieldTrialEnabled(
|
||||
int enabled_value,
|
||||
const std::string& suffix = "") {
|
||||
RTC_DCHECK_GE(enabled_value, 0);
|
||||
RTC_DCHECK_LE(enabled_value, 255);
|
||||
char field_trial_buffer[64];
|
||||
rtc::SimpleStringBuilder builder(field_trial_buffer);
|
||||
builder << kMinInputVolumeFieldTrial << "/Enabled-" << enabled_value << suffix
|
||||
<< "/";
|
||||
return builder.str();
|
||||
}
|
||||
|
||||
std::string GetAgcMinInputVolumeFieldTrial(absl::optional<int> volume) {
|
||||
if (volume.has_value()) {
|
||||
return GetAgcMinInputVolumeFieldTrialEnabled(*volume);
|
||||
}
|
||||
return GetAgcMinInputVolumeFieldTrial("Disabled");
|
||||
}
|
||||
|
||||
// (Over)writes `samples_value` for the samples in `audio_buffer`.
|
||||
// When `clipped_ratio`, a value in [0, 1], is greater than 0, the corresponding
|
||||
// fraction of the frame is set to a full scale value to simulate clipping.
|
||||
|
@ -150,31 +122,6 @@ void WriteAlternatingAudioBufferSamples(float samples_value,
|
|||
}
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// TODO(bugs.webrtc.org/7494): Delete this helper, use
|
||||
// `InputVolumeControllerTestHelper::CallAgcSequence()` instead.
|
||||
int CallAnalyzeAndRecommend(int num_calls,
|
||||
int initial_volume,
|
||||
const AudioBuffer& audio_buffer,
|
||||
float speech_probability,
|
||||
absl::optional<float> speech_level_dbfs,
|
||||
InputVolumeController& controller) {
|
||||
RTC_DCHECK(controller.capture_output_used());
|
||||
int volume = initial_volume;
|
||||
for (int n = 0; n < num_calls; ++n) {
|
||||
controller.AnalyzeInputAudio(volume, audio_buffer);
|
||||
const auto recommended_input_volume =
|
||||
controller.RecommendInputVolume(speech_probability, speech_level_dbfs);
|
||||
|
||||
// Expect no errors: Applied volume set for every frame;
|
||||
// `RecommendInputVolume()` returns a non-empty value.
|
||||
EXPECT_TRUE(recommended_input_volume.has_value());
|
||||
|
||||
volume = *recommended_input_volume;
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
|
||||
// Reads a given number of 10 ms chunks from a PCM file and feeds them to
|
||||
// `InputVolumeController`.
|
||||
class SpeechSamplesReader {
|
||||
|
@ -379,24 +326,12 @@ class InputVolumeControllerTestHelper {
|
|||
};
|
||||
|
||||
class InputVolumeControllerParametrizedTest
|
||||
: public ::testing::TestWithParam<absl::optional<int>> {
|
||||
protected:
|
||||
InputVolumeControllerParametrizedTest()
|
||||
: field_trials_(GetAgcMinInputVolumeFieldTrial(GetParam())) {}
|
||||
|
||||
int GetMinInputVolume() const { return GetParam().value_or(kMinMicLevel); }
|
||||
|
||||
private:
|
||||
test::ScopedFieldTrials field_trials_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(,
|
||||
InputVolumeControllerParametrizedTest,
|
||||
::testing::Values(absl::nullopt, 12, 20));
|
||||
: public ::testing::TestWithParam<int> {};
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
StartupMinVolumeConfigurationIsRespected) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = GetParam()});
|
||||
|
||||
EXPECT_EQ(*helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
||||
kSpeechLevel),
|
||||
|
@ -404,7 +339,9 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest, MicVolumeResponseToRmsError) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
||||
config.min_input_volume = GetParam();
|
||||
InputVolumeControllerTestHelper helper(config);
|
||||
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
||||
kHighSpeechProbability, kSpeechLevel);
|
||||
|
||||
|
@ -446,7 +383,10 @@ TEST_P(InputVolumeControllerParametrizedTest, MicVolumeResponseToRmsError) {
|
|||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest, MicVolumeIsLimited) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
||||
const int min_input_volume = GetParam();
|
||||
config.min_input_volume = min_input_volume;
|
||||
InputVolumeControllerTestHelper helper(config);
|
||||
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
||||
kHighSpeechProbability, kSpeechLevel);
|
||||
|
||||
|
@ -492,16 +432,18 @@ TEST_P(InputVolumeControllerParametrizedTest, MicVolumeIsLimited) {
|
|||
// Won't go lower than the minimum.
|
||||
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
||||
kHighSpeechProbability, 22.0f);
|
||||
EXPECT_EQ(volume, std::max(18, GetMinInputVolume()));
|
||||
EXPECT_EQ(volume, std::max(18, min_input_volume));
|
||||
|
||||
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
||||
kHighSpeechProbability, 22.0f);
|
||||
EXPECT_EQ(volume, std::max(12, GetMinInputVolume()));
|
||||
EXPECT_EQ(volume, std::max(12, min_input_volume));
|
||||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest, NoActionWhileMuted) {
|
||||
InputVolumeControllerTestHelper helper_1;
|
||||
InputVolumeControllerTestHelper helper_2;
|
||||
InputVolumeControllerTestHelper helper_1(
|
||||
/*config=*/{.min_input_volume = GetParam()});
|
||||
InputVolumeControllerTestHelper helper_2(
|
||||
/*config=*/{.min_input_volume = GetParam()});
|
||||
|
||||
int volume_1 = *helper_1.CallAgcSequence(/*applied_input_volume=*/255,
|
||||
kHighSpeechProbability, kSpeechLevel,
|
||||
|
@ -531,7 +473,8 @@ TEST_P(InputVolumeControllerParametrizedTest, NoActionWhileMuted) {
|
|||
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
UnmutingChecksVolumeWithoutRaising) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = GetParam()});
|
||||
helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -548,7 +491,9 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest, UnmutingRaisesTooLowVolume) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
const int min_input_volume = GetParam();
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = min_input_volume});
|
||||
helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -560,12 +505,14 @@ TEST_P(InputVolumeControllerParametrizedTest, UnmutingRaisesTooLowVolume) {
|
|||
EXPECT_EQ(
|
||||
helper.CallRecommendInputVolume(/*num_calls=*/1, kInputVolume,
|
||||
kHighSpeechProbability, kSpeechLevel),
|
||||
GetMinInputVolume());
|
||||
min_input_volume);
|
||||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
ManualLevelChangeResultsInNoSetMicCall) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
||||
config.min_input_volume = GetParam();
|
||||
InputVolumeControllerTestHelper helper(config);
|
||||
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
||||
kHighSpeechProbability, kSpeechLevel);
|
||||
|
||||
|
@ -589,7 +536,9 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
RecoveryAfterManualLevelChangeFromMax) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
||||
config.min_input_volume = GetParam();
|
||||
InputVolumeControllerTestHelper helper(config);
|
||||
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
||||
kHighSpeechProbability, kSpeechLevel);
|
||||
|
||||
|
@ -621,7 +570,10 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
// of the input volume.
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
EnforceMinInputVolumeDuringUpwardsAdjustment) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
const int min_input_volume = GetParam();
|
||||
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
||||
config.min_input_volume = min_input_volume;
|
||||
InputVolumeControllerTestHelper helper(config);
|
||||
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
||||
kHighSpeechProbability, kSpeechLevel);
|
||||
|
||||
|
@ -631,19 +583,19 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
/*num_calls=*/1, /*initial_volume=*/1, kHighSpeechProbability, -17.0f);
|
||||
|
||||
// Trigger an upward adjustment of the input volume.
|
||||
EXPECT_EQ(volume, GetMinInputVolume());
|
||||
EXPECT_EQ(volume, min_input_volume);
|
||||
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
||||
kHighSpeechProbability, -29.0f);
|
||||
EXPECT_EQ(volume, GetMinInputVolume());
|
||||
EXPECT_EQ(volume, min_input_volume);
|
||||
volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
|
||||
kHighSpeechProbability, -30.0f);
|
||||
EXPECT_EQ(volume, GetMinInputVolume());
|
||||
EXPECT_EQ(volume, min_input_volume);
|
||||
|
||||
// After a number of consistently low speech level observations, the input
|
||||
// volume is eventually raised above the minimum.
|
||||
volume = helper.CallRecommendInputVolume(/*num_calls=*/10, volume,
|
||||
kHighSpeechProbability, -38.0f);
|
||||
EXPECT_GT(volume, GetMinInputVolume());
|
||||
EXPECT_GT(volume, min_input_volume);
|
||||
}
|
||||
|
||||
// Checks that, when the min mic level override is specified, AGC immediately
|
||||
|
@ -651,7 +603,9 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
// minimum gain to enforce.
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
RecoveryAfterManualLevelChangeBelowMin) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
const int min_input_volume = GetParam();
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = min_input_volume});
|
||||
int volume = *helper.CallAgcSequence(kInitialInputVolume,
|
||||
kHighSpeechProbability, kSpeechLevel);
|
||||
|
||||
|
@ -659,11 +613,12 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
// AGC won't take any action.
|
||||
volume = helper.CallRecommendInputVolume(
|
||||
/*num_calls=*/1, /*initial_volume=*/1, kHighSpeechProbability, -17.0f);
|
||||
EXPECT_EQ(volume, GetMinInputVolume());
|
||||
EXPECT_EQ(volume, min_input_volume);
|
||||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest, NoClippingHasNoImpact) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = GetParam()});
|
||||
helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -673,7 +628,8 @@ TEST_P(InputVolumeControllerParametrizedTest, NoClippingHasNoImpact) {
|
|||
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
ClippingUnderThresholdHasNoImpact) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = GetParam()});
|
||||
helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -682,7 +638,8 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest, ClippingLowersVolume) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = GetParam()});
|
||||
helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -692,7 +649,8 @@ TEST_P(InputVolumeControllerParametrizedTest, ClippingLowersVolume) {
|
|||
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
WaitingPeriodBetweenClippingChecks) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = GetParam()});
|
||||
helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -710,7 +668,9 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest, ClippingLoweringIsLimited) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
||||
config.min_input_volume = GetParam();
|
||||
InputVolumeControllerTestHelper helper(config);
|
||||
helper.CallAgcSequence(/*applied_input_volume=*/180, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -725,7 +685,8 @@ TEST_P(InputVolumeControllerParametrizedTest, ClippingLoweringIsLimited) {
|
|||
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
ClippingMaxIsRespectedWhenEqualToLevel) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = GetParam()});
|
||||
helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -740,7 +701,9 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
ClippingMaxIsRespectedWhenHigherThanLevel) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
||||
config.min_input_volume = GetParam();
|
||||
InputVolumeControllerTestHelper helper(config);
|
||||
helper.CallAgcSequence(/*applied_input_volume=*/200, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -758,7 +721,9 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest, UserCanRaiseVolumeAfterClipping) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
||||
config.min_input_volume = GetParam();
|
||||
InputVolumeControllerTestHelper helper(config);
|
||||
helper.CallAgcSequence(/*applied_input_volume=*/225, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -787,7 +752,9 @@ TEST_P(InputVolumeControllerParametrizedTest, UserCanRaiseVolumeAfterClipping) {
|
|||
|
||||
TEST_P(InputVolumeControllerParametrizedTest,
|
||||
ClippingDoesNotPullLowVolumeBackUp) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
||||
config.min_input_volume = GetParam();
|
||||
InputVolumeControllerTestHelper helper(config);
|
||||
helper.CallAgcSequence(/*applied_input_volume=*/80, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -798,7 +765,8 @@ TEST_P(InputVolumeControllerParametrizedTest,
|
|||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest, TakesNoActionOnZeroMicVolume) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = GetParam()});
|
||||
helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
|
||||
kSpeechLevel);
|
||||
|
||||
|
@ -809,7 +777,9 @@ TEST_P(InputVolumeControllerParametrizedTest, TakesNoActionOnZeroMicVolume) {
|
|||
}
|
||||
|
||||
TEST_P(InputVolumeControllerParametrizedTest, ClippingDetectionLowersVolume) {
|
||||
InputVolumeControllerTestHelper helper;
|
||||
InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
|
||||
config.min_input_volume = GetParam();
|
||||
InputVolumeControllerTestHelper helper(config);
|
||||
int volume = *helper.CallAgcSequence(/*applied_input_volume=*/255,
|
||||
kHighSpeechProbability, kSpeechLevel,
|
||||
/*num_calls=*/1);
|
||||
|
@ -829,298 +799,6 @@ TEST_P(InputVolumeControllerParametrizedTest, ClippingDetectionLowersVolume) {
|
|||
EXPECT_EQ(volume, 240);
|
||||
}
|
||||
|
||||
TEST(InputVolumeControllerTest, MinInputVolumeDefault) {
|
||||
std::unique_ptr<InputVolumeController> controller =
|
||||
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
||||
kClippedWaitFrames);
|
||||
EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(),
|
||||
kMinMicLevel);
|
||||
}
|
||||
|
||||
TEST(InputVolumeControllerTest, MinInputVolumeDisabled) {
|
||||
for (const std::string& field_trial_suffix : {"", "_20220210"}) {
|
||||
test::ScopedFieldTrials field_trial(
|
||||
GetAgcMinInputVolumeFieldTrial("Disabled" + field_trial_suffix));
|
||||
std::unique_ptr<InputVolumeController> controller =
|
||||
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
||||
kClippedWaitFrames);
|
||||
|
||||
EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(),
|
||||
kMinMicLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that a field-trial parameter outside of the valid range [0,255] is
|
||||
// ignored.
|
||||
TEST(InputVolumeControllerTest, MinInputVolumeOutOfRangeAbove) {
|
||||
test::ScopedFieldTrials field_trial(
|
||||
GetAgcMinInputVolumeFieldTrial("Enabled-256"));
|
||||
std::unique_ptr<InputVolumeController> controller =
|
||||
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
||||
kClippedWaitFrames);
|
||||
EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(),
|
||||
kMinMicLevel);
|
||||
}
|
||||
|
||||
// Checks that a field-trial parameter outside of the valid range [0,255] is
|
||||
// ignored.
|
||||
TEST(InputVolumeControllerTest, MinInputVolumeOutOfRangeBelow) {
|
||||
test::ScopedFieldTrials field_trial(
|
||||
GetAgcMinInputVolumeFieldTrial("Enabled--1"));
|
||||
std::unique_ptr<InputVolumeController> controller =
|
||||
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
||||
kClippedWaitFrames);
|
||||
EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(),
|
||||
kMinMicLevel);
|
||||
}
|
||||
|
||||
// Verifies that a valid experiment changes the minimum microphone level. The
|
||||
// start volume is larger than the min level and should therefore not be
|
||||
// changed.
|
||||
TEST(InputVolumeControllerTest, MinInputVolumeEnabled50) {
|
||||
constexpr int kMinInputVolume = 50;
|
||||
for (const std::string& field_trial_suffix : {"", "_20220210"}) {
|
||||
SCOPED_TRACE(field_trial_suffix);
|
||||
test::ScopedFieldTrials field_trial(GetAgcMinInputVolumeFieldTrialEnabled(
|
||||
kMinInputVolume, field_trial_suffix));
|
||||
std::unique_ptr<InputVolumeController> controller =
|
||||
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
||||
kClippedWaitFrames);
|
||||
|
||||
EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(),
|
||||
kMinInputVolume);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is
|
||||
// specified with a valid value, the mic level never gets lowered beyond the
|
||||
// override value in the presence of clipping.
|
||||
TEST(InputVolumeControllerTest, MinInputVolumeCheckMinLevelWithClipping) {
|
||||
constexpr int kMinInputVolume = 250;
|
||||
|
||||
// Create and initialize two AGCs by specifying and leaving unspecified the
|
||||
// relevant field trial.
|
||||
const auto factory = []() {
|
||||
std::unique_ptr<InputVolumeController> controller =
|
||||
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
||||
kClippedWaitFrames);
|
||||
controller->Initialize();
|
||||
return controller;
|
||||
};
|
||||
std::unique_ptr<InputVolumeController> controller = factory();
|
||||
std::unique_ptr<InputVolumeController> controller_with_override;
|
||||
{
|
||||
test::ScopedFieldTrials field_trial(
|
||||
GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume));
|
||||
controller_with_override = factory();
|
||||
}
|
||||
|
||||
// Create a test input signal which containts 80% of clipped samples.
|
||||
AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz,
|
||||
1);
|
||||
WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
|
||||
audio_buffer);
|
||||
|
||||
// Simulate 4 seconds of clipping; it is expected to trigger a downward
|
||||
// adjustment of the analog gain. Use low speech probability to limit the
|
||||
// volume changes to clipping handling.
|
||||
const int volume = CallAnalyzeAndRecommend(
|
||||
/*num_calls=*/400, kInitialInputVolume, audio_buffer,
|
||||
kLowSpeechProbability, /*speech_level_dbfs=*/-42.0f, *controller);
|
||||
const int volume_with_override = CallAnalyzeAndRecommend(
|
||||
/*num_calls=*/400, kInitialInputVolume, audio_buffer,
|
||||
kLowSpeechProbability, /*speech_level_dbfs=*/-42.0f,
|
||||
*controller_with_override);
|
||||
|
||||
// Make sure that an adaptation occurred.
|
||||
ASSERT_GT(volume, 0);
|
||||
|
||||
// Check that the test signal triggers a larger downward adaptation for
|
||||
// `controller`, which is allowed to reach a lower gain.
|
||||
EXPECT_GT(volume_with_override, volume);
|
||||
// Check that the gain selected by `controller_with_override` equals the
|
||||
// minimum value overridden via field trial.
|
||||
EXPECT_EQ(volume_with_override, kMinInputVolume);
|
||||
}
|
||||
|
||||
// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is
|
||||
// specified with a valid value, the mic level never gets lowered beyond the
|
||||
// override value in the presence of clipping when RMS error is not empty.
|
||||
// TODO(webrtc:7494): Revisit the test after moving the number of update wait
|
||||
// frames to APM config. The test passes but internally the gain update timing
|
||||
// differs.
|
||||
TEST(InputVolumeControllerTest,
|
||||
MinInputVolumeCheckMinLevelWithClippingWithRmsError) {
|
||||
constexpr int kMinInputVolume = 250;
|
||||
|
||||
// Create and initialize two AGCs by specifying and leaving unspecified the
|
||||
// relevant field trial.
|
||||
const auto factory = []() {
|
||||
std::unique_ptr<InputVolumeController> controller =
|
||||
CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
|
||||
kClippedWaitFrames);
|
||||
controller->Initialize();
|
||||
return controller;
|
||||
};
|
||||
std::unique_ptr<InputVolumeController> controller = factory();
|
||||
std::unique_ptr<InputVolumeController> controller_with_override;
|
||||
{
|
||||
test::ScopedFieldTrials field_trial(
|
||||
GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume));
|
||||
controller_with_override = factory();
|
||||
}
|
||||
|
||||
// Create a test input signal which containts 80% of clipped samples.
|
||||
AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz,
|
||||
1);
|
||||
WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
|
||||
audio_buffer);
|
||||
|
||||
// Simulate 4 seconds of clipping; it is expected to trigger a downward
|
||||
// adjustment of the analog gain.
|
||||
const int volume = CallAnalyzeAndRecommend(
|
||||
/*num_calls=*/400, kInitialInputVolume, audio_buffer,
|
||||
kHighSpeechProbability,
|
||||
/*speech_level_dbfs=*/-18.0f, *controller);
|
||||
const int volume_with_override = CallAnalyzeAndRecommend(
|
||||
/*num_calls=*/400, kInitialInputVolume, audio_buffer,
|
||||
kHighSpeechProbability,
|
||||
/*speech_level_dbfs=*/-18.0f, *controller_with_override);
|
||||
|
||||
// Make sure that an adaptation occurred.
|
||||
ASSERT_GT(volume, 0);
|
||||
|
||||
// Check that the test signal triggers a larger downward adaptation for
|
||||
// `controller`, which is allowed to reach a lower gain.
|
||||
EXPECT_GT(volume_with_override, volume);
|
||||
|
||||
// Check that the gain selected by `controller_with_override` equals the
|
||||
// minimum value overridden via field trial.
|
||||
EXPECT_EQ(volume_with_override, kMinInputVolume);
|
||||
}
|
||||
|
||||
// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is
|
||||
// specified with a value lower than the `clipped_level_min`, the behavior of
|
||||
// the analog gain controller is the same as that obtained when the field trial
|
||||
// is not specified.
|
||||
TEST(InputVolumeControllerTest, MinInputVolumeCompareMicLevelWithClipping) {
|
||||
// Create and initialize two AGCs by specifying and leaving unspecified the
|
||||
// relevant field trial.
|
||||
const auto factory = []() {
|
||||
// Use a large clipped level step to more quickly decrease the analog gain
|
||||
// with clipping.
|
||||
InputVolumeControllerConfig config = kDefaultInputVolumeControllerConfig;
|
||||
config.clipped_level_step = 64;
|
||||
config.clipped_ratio_threshold = kClippedRatioThreshold;
|
||||
config.clipped_wait_frames = kClippedWaitFrames;
|
||||
auto controller = std::make_unique<InputVolumeController>(
|
||||
/*num_capture_channels=*/1, config);
|
||||
controller->Initialize();
|
||||
return controller;
|
||||
};
|
||||
std::unique_ptr<InputVolumeController> controller = factory();
|
||||
std::unique_ptr<InputVolumeController> controller_with_override;
|
||||
{
|
||||
constexpr int kMinInputVolume = 20;
|
||||
static_assert(kDefaultInputVolumeControllerConfig.clipped_level_min >=
|
||||
kMinInputVolume,
|
||||
"Use a lower override value.");
|
||||
test::ScopedFieldTrials field_trial(
|
||||
GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume));
|
||||
controller_with_override = factory();
|
||||
}
|
||||
|
||||
// Create a test input signal which containts 80% of clipped samples.
|
||||
AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz,
|
||||
1);
|
||||
WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
|
||||
audio_buffer);
|
||||
|
||||
// Simulate 4 seconds of clipping; it is expected to trigger a downward
|
||||
// adjustment of the analog gain. Use low speech probability to limit the
|
||||
// volume changes to clipping handling.
|
||||
const int volume = CallAnalyzeAndRecommend(
|
||||
/*num_calls=*/400, kInitialInputVolume, audio_buffer,
|
||||
kLowSpeechProbability, /*speech_level_dbfs=*/-18, *controller);
|
||||
const int volume_with_override = CallAnalyzeAndRecommend(
|
||||
/*num_calls=*/400, kInitialInputVolume, audio_buffer,
|
||||
kLowSpeechProbability, /*speech_level_dbfs=*/-18,
|
||||
*controller_with_override);
|
||||
|
||||
// Make sure that an adaptation occurred.
|
||||
ASSERT_GT(volume, 0);
|
||||
|
||||
// Check that the selected analog gain is the same for both controllers and
|
||||
// that it equals the minimum level reached when clipping is handled. That is
|
||||
// expected because the minimum microphone level override is less than the
|
||||
// minimum level used when clipping is detected.
|
||||
EXPECT_EQ(volume, volume_with_override);
|
||||
EXPECT_EQ(volume_with_override,
|
||||
kDefaultInputVolumeControllerConfig.clipped_level_min);
|
||||
}
|
||||
|
||||
// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is
|
||||
// specified with a value lower than the `clipped_level_min`, the behavior of
|
||||
// the analog gain controller is the same as that obtained when the field trial
|
||||
// is not specified.
|
||||
// TODO(webrtc:7494): Revisit the test after moving the number of update wait
|
||||
// frames to APM config. The test passes but internally the gain update timing
|
||||
// differs.
|
||||
TEST(InputVolumeControllerTest,
|
||||
MinInputVolumeCompareMicLevelWithClippingWithRmsError) {
|
||||
// Create and initialize two AGCs by specifying and leaving unspecified the
|
||||
// relevant field trial.
|
||||
const auto factory = []() {
|
||||
// Use a large clipped level step to more quickly decrease the analog gain
|
||||
// with clipping.
|
||||
InputVolumeControllerConfig config = kDefaultInputVolumeControllerConfig;
|
||||
config.clipped_level_step = 64;
|
||||
config.clipped_ratio_threshold = kClippedRatioThreshold;
|
||||
config.clipped_wait_frames = kClippedWaitFrames;
|
||||
auto controller = std::make_unique<InputVolumeController>(
|
||||
/*num_capture_channels=*/1, config);
|
||||
controller->Initialize();
|
||||
return controller;
|
||||
};
|
||||
std::unique_ptr<InputVolumeController> controller = factory();
|
||||
std::unique_ptr<InputVolumeController> controller_with_override;
|
||||
{
|
||||
constexpr int kMinInputVolume = 20;
|
||||
static_assert(kDefaultInputVolumeControllerConfig.clipped_level_min >=
|
||||
kMinInputVolume,
|
||||
"Use a lower override value.");
|
||||
test::ScopedFieldTrials field_trial(
|
||||
GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume));
|
||||
controller_with_override = factory();
|
||||
}
|
||||
|
||||
// Create a test input signal which containts 80% of clipped samples.
|
||||
AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz,
|
||||
1);
|
||||
WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
|
||||
audio_buffer);
|
||||
|
||||
const int volume = CallAnalyzeAndRecommend(
|
||||
/*num_calls=*/400, kInitialInputVolume, audio_buffer,
|
||||
/*speech_probability=*/0.7f,
|
||||
/*speech_level_dbfs=*/-18.0f, *controller);
|
||||
const int volume_with_override = CallAnalyzeAndRecommend(
|
||||
/*num_calls=*/400, kInitialInputVolume, audio_buffer,
|
||||
/*speech_probability=*/0.7f,
|
||||
/*speech_level_dbfs=*/-18.0f, *controller_with_override);
|
||||
|
||||
// Make sure that an adaptation occurred.
|
||||
ASSERT_GT(volume, 0);
|
||||
|
||||
// Check that the selected analog gain is the same for both controllers and
|
||||
// that it equals the minimum level reached when clipping is handled. That is
|
||||
// expected because the minimum microphone level override is less than the
|
||||
// minimum level used when clipping is detected.
|
||||
EXPECT_EQ(volume, volume_with_override);
|
||||
EXPECT_EQ(volume_with_override,
|
||||
kDefaultInputVolumeControllerConfig.clipped_level_min);
|
||||
}
|
||||
|
||||
// TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_level_step`.
|
||||
// TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_ratio_threshold`.
|
||||
// TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_wait_frames`.
|
||||
|
@ -1416,6 +1094,42 @@ TEST(InputVolumeControllerTest, UpdateInputVolumeWaitFramesIsEffective) {
|
|||
ASSERT_GT(volume_wait_100, kInputVolume);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(,
|
||||
InputVolumeControllerParametrizedTest,
|
||||
::testing::Values(12, 20));
|
||||
|
||||
TEST(InputVolumeControllerTest,
|
||||
MinInputVolumeEnforcedWithClippingWhenAboveClippedLevelMin) {
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = 80, .clipped_level_min = 70});
|
||||
|
||||
// Trigger a downward adjustment caused by clipping input. Use a low speech
|
||||
// probability to limit the volume changes to clipping handling.
|
||||
WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
|
||||
helper.audio_buffer);
|
||||
constexpr int kNumCalls = 800;
|
||||
helper.CallAgcSequence(/*applied_input_volume=*/100, kLowSpeechProbability,
|
||||
/*speech_level_dbfs=*/-18.0f, kNumCalls);
|
||||
|
||||
EXPECT_EQ(helper.controller.recommended_input_volume(), 80);
|
||||
}
|
||||
|
||||
TEST(InputVolumeControllerTest,
|
||||
ClippedlevelMinEnforcedWithClippingWhenAboveMinInputVolume) {
|
||||
InputVolumeControllerTestHelper helper(
|
||||
/*config=*/{.min_input_volume = 70, .clipped_level_min = 80});
|
||||
|
||||
// Trigger a downward adjustment caused by clipping input. Use a low speech
|
||||
// probability to limit the volume changes to clipping handling.
|
||||
WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
|
||||
helper.audio_buffer);
|
||||
constexpr int kNumCalls = 800;
|
||||
helper.CallAgcSequence(/*applied_input_volume=*/100, kLowSpeechProbability,
|
||||
/*speech_level_dbfs=*/-18.0f, kNumCalls);
|
||||
|
||||
EXPECT_EQ(helper.controller.recommended_input_volume(), 80);
|
||||
}
|
||||
|
||||
TEST(InputVolumeControllerTest, SpeechRatioThresholdIsEffective) {
|
||||
constexpr int kInputVolume = kInitialInputVolume;
|
||||
// Create two input volume controllers with 10 frames between volume updates
|
||||
|
|
|
@ -69,29 +69,6 @@ bool UseSetupSpecificDefaultAec3Congfig() {
|
|||
"WebRTC-Aec3SetupSpecificDefaultConfigDefaultsKillSwitch");
|
||||
}
|
||||
|
||||
// If the "WebRTC-Audio-TransientSuppressorVadMode" field trial is unspecified,
|
||||
// returns `TransientSuppressor::VadMode::kDefault`, otherwise parses the field
|
||||
// trial and returns the specified mode:
|
||||
// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-Default returns `kDefault`;
|
||||
// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-RnnVad returns `kRnnVad`;
|
||||
// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-NoVad returns `kNoVad`.
|
||||
TransientSuppressor::VadMode GetTransientSuppressorVadMode() {
|
||||
constexpr char kFieldTrial[] = "WebRTC-Audio-TransientSuppressorVadMode";
|
||||
std::string full_name = webrtc::field_trial::FindFullName(kFieldTrial);
|
||||
if (full_name.empty() || absl::EndsWith(full_name, "-Default")) {
|
||||
return TransientSuppressor::VadMode::kDefault;
|
||||
}
|
||||
if (absl::EndsWith(full_name, "-RnnVad")) {
|
||||
return TransientSuppressor::VadMode::kRnnVad;
|
||||
}
|
||||
if (absl::EndsWith(full_name, "-NoVad")) {
|
||||
return TransientSuppressor::VadMode::kNoVad;
|
||||
}
|
||||
// Fallback to default.
|
||||
RTC_LOG(LS_WARNING) << "Invalid parameter for " << kFieldTrial;
|
||||
return TransientSuppressor::VadMode::kDefault;
|
||||
}
|
||||
|
||||
// Identify the native processing rate that best handles a sample rate.
|
||||
int SuitableProcessRate(int minimum_rate,
|
||||
int max_splitting_rate,
|
||||
|
@ -325,17 +302,44 @@ int HandleUnsupportedAudioFormats(const float* const* src,
|
|||
return error_code;
|
||||
}
|
||||
|
||||
const absl::optional<AudioProcessingImpl::GainController2ConfigOverride>
|
||||
GetGainController2ConfigOverride() {
|
||||
using DownmixMethod = AudioProcessing::Config::Pipeline::DownmixMethod;
|
||||
|
||||
void SetDownmixMethod(AudioBuffer& buffer, DownmixMethod method) {
|
||||
switch (method) {
|
||||
case DownmixMethod::kAverageChannels:
|
||||
buffer.set_downmixing_by_averaging();
|
||||
break;
|
||||
case DownmixMethod::kUseFirstChannel:
|
||||
buffer.set_downmixing_to_specific_channel(/*channel=*/0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int kUnspecifiedDataDumpInputVolume = -100;
|
||||
|
||||
} // namespace
|
||||
|
||||
// Throughout webrtc, it's assumed that success is represented by zero.
|
||||
static_assert(AudioProcessing::kNoError == 0, "kNoError must be zero");
|
||||
|
||||
absl::optional<AudioProcessingImpl::GainController2ExperimentParams>
|
||||
AudioProcessingImpl::GetGainController2ExperimentParams() {
|
||||
constexpr char kFieldTrialName[] = "WebRTC-Audio-GainController2";
|
||||
|
||||
if (!field_trial::IsEnabled(kFieldTrialName)) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
constexpr InputVolumeController::Config kDefaultInputVolumeControllerConfig;
|
||||
|
||||
FieldTrialFlag enabled("Enabled", false);
|
||||
|
||||
// Whether the gain control should switch to AGC2. Enabled by default.
|
||||
FieldTrialParameter<bool> switch_to_agc2("switch_to_agc2", true);
|
||||
|
||||
// AGC2 input volume controller configuration.
|
||||
constexpr InputVolumeController::Config kDefaultInputVolumeControllerConfig;
|
||||
FieldTrialConstrained<int> min_input_volume(
|
||||
"min_input_volume", kDefaultInputVolumeControllerConfig.min_input_volume,
|
||||
0, 255);
|
||||
FieldTrialConstrained<int> clipped_level_min(
|
||||
"clipped_level_min",
|
||||
kDefaultInputVolumeControllerConfig.clipped_level_min, 0, 255);
|
||||
|
@ -369,9 +373,9 @@ GetGainController2ConfigOverride() {
|
|||
"speech_ratio_threshold",
|
||||
kDefaultInputVolumeControllerConfig.speech_ratio_threshold, 0, 1);
|
||||
|
||||
// AGC2 adaptive digital controller configuration.
|
||||
constexpr AudioProcessing::Config::GainController2::AdaptiveDigital
|
||||
kDefaultAdaptiveDigitalConfig;
|
||||
|
||||
FieldTrialConstrained<double> headroom_db(
|
||||
"headroom_db", kDefaultAdaptiveDigitalConfig.headroom_db, 0,
|
||||
absl::nullopt);
|
||||
|
@ -390,83 +394,102 @@ GetGainController2ConfigOverride() {
|
|||
kDefaultAdaptiveDigitalConfig.max_output_noise_level_dbfs, absl::nullopt,
|
||||
0);
|
||||
|
||||
// Transient suppressor.
|
||||
FieldTrialParameter<bool> disallow_transient_suppressor_usage(
|
||||
"disallow_transient_suppressor_usage", false);
|
||||
|
||||
// Field-trial based override for the input volume controller and adaptive
|
||||
// digital configs.
|
||||
const std::string field_trial_name =
|
||||
field_trial::FindFullName(kFieldTrialName);
|
||||
|
||||
ParseFieldTrial(
|
||||
{&enabled, &clipped_level_min, &clipped_level_step,
|
||||
&clipped_ratio_threshold, &clipped_wait_frames,
|
||||
{&enabled, &switch_to_agc2, &min_input_volume, &clipped_level_min,
|
||||
&clipped_level_step, &clipped_ratio_threshold, &clipped_wait_frames,
|
||||
&enable_clipping_predictor, &target_range_max_dbfs,
|
||||
&target_range_min_dbfs, &update_input_volume_wait_frames,
|
||||
&speech_probability_threshold, &speech_ratio_threshold, &headroom_db,
|
||||
&max_gain_db, &initial_gain_db, &max_gain_change_db_per_second,
|
||||
&max_output_noise_level_dbfs},
|
||||
field_trial_name);
|
||||
|
||||
&max_output_noise_level_dbfs, &disallow_transient_suppressor_usage},
|
||||
field_trial::FindFullName(kFieldTrialName));
|
||||
// Checked already by `IsEnabled()` before parsing, therefore always true.
|
||||
RTC_DCHECK(enabled);
|
||||
|
||||
return AudioProcessingImpl::GainController2ConfigOverride{
|
||||
.input_volume_controller_config =
|
||||
{
|
||||
.clipped_level_min = static_cast<int>(clipped_level_min.Get()),
|
||||
.clipped_level_step = static_cast<int>(clipped_level_step.Get()),
|
||||
.clipped_ratio_threshold =
|
||||
static_cast<float>(clipped_ratio_threshold.Get()),
|
||||
.clipped_wait_frames =
|
||||
static_cast<int>(clipped_wait_frames.Get()),
|
||||
.enable_clipping_predictor =
|
||||
static_cast<bool>(enable_clipping_predictor.Get()),
|
||||
.target_range_max_dbfs =
|
||||
static_cast<int>(target_range_max_dbfs.Get()),
|
||||
.target_range_min_dbfs =
|
||||
static_cast<int>(target_range_min_dbfs.Get()),
|
||||
.update_input_volume_wait_frames =
|
||||
static_cast<int>(update_input_volume_wait_frames.Get()),
|
||||
.speech_probability_threshold =
|
||||
static_cast<float>(speech_probability_threshold.Get()),
|
||||
.speech_ratio_threshold =
|
||||
static_cast<float>(speech_ratio_threshold.Get()),
|
||||
},
|
||||
.adaptive_digital_config =
|
||||
{
|
||||
.headroom_db = static_cast<float>(headroom_db.Get()),
|
||||
.max_gain_db = static_cast<float>(max_gain_db.Get()),
|
||||
.initial_gain_db = static_cast<float>(initial_gain_db.Get()),
|
||||
.max_gain_change_db_per_second =
|
||||
static_cast<float>(max_gain_change_db_per_second.Get()),
|
||||
.max_output_noise_level_dbfs =
|
||||
static_cast<float>(max_output_noise_level_dbfs.Get()),
|
||||
},
|
||||
};
|
||||
const bool do_not_change_agc_config = !switch_to_agc2.Get();
|
||||
if (do_not_change_agc_config && !disallow_transient_suppressor_usage.Get()) {
|
||||
// Return an unspecifed value since, in this case, both the AGC2 and TS
|
||||
// configurations won't be adjusted.
|
||||
return absl::nullopt;
|
||||
}
|
||||
using Params = AudioProcessingImpl::GainController2ExperimentParams;
|
||||
if (do_not_change_agc_config) {
|
||||
// Return a value that leaves the AGC2 config unchanged and that always
|
||||
// disables TS.
|
||||
return Params{.agc2_config = absl::nullopt,
|
||||
.disallow_transient_suppressor_usage = true};
|
||||
}
|
||||
// Return a value that switches all the gain control to AGC2.
|
||||
return Params{
|
||||
.agc2_config =
|
||||
Params::Agc2Config{
|
||||
.input_volume_controller =
|
||||
{
|
||||
.min_input_volume = min_input_volume.Get(),
|
||||
.clipped_level_min = clipped_level_min.Get(),
|
||||
.clipped_level_step = clipped_level_step.Get(),
|
||||
.clipped_ratio_threshold =
|
||||
static_cast<float>(clipped_ratio_threshold.Get()),
|
||||
.clipped_wait_frames = clipped_wait_frames.Get(),
|
||||
.enable_clipping_predictor =
|
||||
enable_clipping_predictor.Get(),
|
||||
.target_range_max_dbfs = target_range_max_dbfs.Get(),
|
||||
.target_range_min_dbfs = target_range_min_dbfs.Get(),
|
||||
.update_input_volume_wait_frames =
|
||||
update_input_volume_wait_frames.Get(),
|
||||
.speech_probability_threshold = static_cast<float>(
|
||||
speech_probability_threshold.Get()),
|
||||
.speech_ratio_threshold =
|
||||
static_cast<float>(speech_ratio_threshold.Get()),
|
||||
},
|
||||
.adaptive_digital_controller =
|
||||
{
|
||||
.headroom_db = static_cast<float>(headroom_db.Get()),
|
||||
.max_gain_db = static_cast<float>(max_gain_db.Get()),
|
||||
.initial_gain_db =
|
||||
static_cast<float>(initial_gain_db.Get()),
|
||||
.max_gain_change_db_per_second = static_cast<float>(
|
||||
max_gain_change_db_per_second.Get()),
|
||||
.max_output_noise_level_dbfs =
|
||||
static_cast<float>(max_output_noise_level_dbfs.Get()),
|
||||
}},
|
||||
.disallow_transient_suppressor_usage =
|
||||
disallow_transient_suppressor_usage.Get()};
|
||||
}
|
||||
|
||||
// If `disallow_transient_supporessor_usage` is true, disables transient
|
||||
// suppression. When `gain_controller2_config_override` is specified,
|
||||
// switches all gain control to AGC2.
|
||||
AudioProcessing::Config AdjustConfig(
|
||||
AudioProcessing::Config AudioProcessingImpl::AdjustConfig(
|
||||
const AudioProcessing::Config& config,
|
||||
bool disallow_transient_supporessor_usage,
|
||||
const absl::optional<AudioProcessingImpl::GainController2ConfigOverride>&
|
||||
gain_controller2_config_override) {
|
||||
const absl::optional<AudioProcessingImpl::GainController2ExperimentParams>&
|
||||
experiment_params) {
|
||||
if (!experiment_params.has_value() ||
|
||||
(!experiment_params->agc2_config.has_value() &&
|
||||
!experiment_params->disallow_transient_suppressor_usage)) {
|
||||
// When the experiment parameters are unspecified or when the AGC and TS
|
||||
// configuration are not overridden, return the unmodified configuration.
|
||||
return config;
|
||||
}
|
||||
|
||||
AudioProcessing::Config adjusted_config = config;
|
||||
|
||||
// Override the transient suppressor configuration.
|
||||
if (disallow_transient_supporessor_usage) {
|
||||
if (experiment_params->disallow_transient_suppressor_usage) {
|
||||
adjusted_config.transient_suppression.enabled = false;
|
||||
}
|
||||
|
||||
// Override the auto gain control configuration if the AGC1 analog gain
|
||||
// controller is active and `gain_controller2_config_override` is
|
||||
// specified.
|
||||
// controller is active and `experiment_params->agc2_config` is specified.
|
||||
const bool agc1_analog_enabled =
|
||||
config.gain_controller1.enabled &&
|
||||
(config.gain_controller1.mode ==
|
||||
AudioProcessing::Config::GainController1::kAdaptiveAnalog ||
|
||||
config.gain_controller1.analog_gain_controller.enabled);
|
||||
if (agc1_analog_enabled && gain_controller2_config_override.has_value()) {
|
||||
if (agc1_analog_enabled && experiment_params->agc2_config.has_value()) {
|
||||
// Check that the unadjusted AGC config meets the preconditions.
|
||||
const bool hybrid_agc_config_detected =
|
||||
config.gain_controller1.enabled &&
|
||||
|
@ -499,7 +522,7 @@ AudioProcessing::Config AdjustConfig(
|
|||
adjusted_config.gain_controller2.enabled = true;
|
||||
adjusted_config.gain_controller2.input_volume_controller.enabled = true;
|
||||
adjusted_config.gain_controller2.adaptive_digital =
|
||||
gain_controller2_config_override->adaptive_digital_config;
|
||||
experiment_params->agc2_config->adaptive_digital_controller;
|
||||
adjusted_config.gain_controller2.adaptive_digital.enabled = true;
|
||||
}
|
||||
}
|
||||
|
@ -507,26 +530,21 @@ AudioProcessing::Config AdjustConfig(
|
|||
return adjusted_config;
|
||||
}
|
||||
|
||||
using DownmixMethod = AudioProcessing::Config::Pipeline::DownmixMethod;
|
||||
|
||||
void SetDownmixMethod(AudioBuffer& buffer, DownmixMethod method) {
|
||||
switch (method) {
|
||||
case DownmixMethod::kAverageChannels:
|
||||
buffer.set_downmixing_by_averaging();
|
||||
break;
|
||||
case DownmixMethod::kUseFirstChannel:
|
||||
buffer.set_downmixing_to_specific_channel(/*channel=*/0);
|
||||
break;
|
||||
TransientSuppressor::VadMode AudioProcessingImpl::GetTransientSuppressorVadMode(
|
||||
const absl::optional<AudioProcessingImpl::GainController2ExperimentParams>&
|
||||
params) {
|
||||
if (params.has_value() && params->agc2_config.has_value() &&
|
||||
!params->disallow_transient_suppressor_usage) {
|
||||
// When the experiment is active, the gain control switches to AGC2 and TS
|
||||
// can be active, use the RNN VAD to control TS. This choice will also
|
||||
// disable the internal RNN VAD in AGC2.
|
||||
return TransientSuppressor::VadMode::kRnnVad;
|
||||
}
|
||||
// If TS is disabled, the returned value does not matter. If enabled, use the
|
||||
// default VAD.
|
||||
return TransientSuppressor::VadMode::kDefault;
|
||||
}
|
||||
|
||||
constexpr int kUnspecifiedDataDumpInputVolume = -100;
|
||||
|
||||
} // namespace
|
||||
|
||||
// Throughout webrtc, it's assumed that success is represented by zero.
|
||||
static_assert(AudioProcessing::kNoError == 0, "kNoError must be zero");
|
||||
|
||||
AudioProcessingImpl::SubmoduleStates::SubmoduleStates(
|
||||
bool capture_post_processor_enabled,
|
||||
bool render_pre_processor_enabled,
|
||||
|
@ -644,20 +662,17 @@ AudioProcessingImpl::AudioProcessingImpl(
|
|||
: data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)),
|
||||
use_setup_specific_default_aec3_config_(
|
||||
UseSetupSpecificDefaultAec3Congfig()),
|
||||
gain_controller2_config_override_(GetGainController2ConfigOverride()),
|
||||
gain_controller2_experiment_params_(GetGainController2ExperimentParams()),
|
||||
use_denormal_disabler_(
|
||||
!field_trial::IsEnabled("WebRTC-ApmDenormalDisablerKillSwitch")),
|
||||
disallow_transient_supporessor_usage_(
|
||||
field_trial::IsEnabled("WebRTC-ApmTransientSuppressorKillSwitch")),
|
||||
transient_suppressor_vad_mode_(GetTransientSuppressorVadMode()),
|
||||
transient_suppressor_vad_mode_(
|
||||
GetTransientSuppressorVadMode(gain_controller2_experiment_params_)),
|
||||
capture_runtime_settings_(RuntimeSettingQueueSize()),
|
||||
render_runtime_settings_(RuntimeSettingQueueSize()),
|
||||
capture_runtime_settings_enqueuer_(&capture_runtime_settings_),
|
||||
render_runtime_settings_enqueuer_(&render_runtime_settings_),
|
||||
echo_control_factory_(std::move(echo_control_factory)),
|
||||
config_(AdjustConfig(config,
|
||||
disallow_transient_supporessor_usage_,
|
||||
gain_controller2_config_override_)),
|
||||
config_(AdjustConfig(config, gain_controller2_experiment_params_)),
|
||||
submodule_states_(!!capture_post_processor,
|
||||
!!render_pre_processor,
|
||||
!!capture_analyzer),
|
||||
|
@ -893,8 +908,7 @@ void AudioProcessingImpl::ApplyConfig(const AudioProcessing::Config& config) {
|
|||
MutexLock lock_capture(&mutex_capture_);
|
||||
|
||||
const auto adjusted_config =
|
||||
AdjustConfig(config, disallow_transient_supporessor_usage_,
|
||||
gain_controller2_config_override_);
|
||||
AdjustConfig(config, gain_controller2_experiment_params_);
|
||||
RTC_LOG(LS_INFO) << "AudioProcessing::ApplyConfig: "
|
||||
<< adjusted_config.ToString();
|
||||
|
||||
|
@ -2340,11 +2354,16 @@ void AudioProcessingImpl::InitializeGainController2(bool config_has_changed) {
|
|||
if (!submodules_.gain_controller2 || config_has_changed) {
|
||||
const bool use_internal_vad =
|
||||
transient_suppressor_vad_mode_ != TransientSuppressor::VadMode::kRnnVad;
|
||||
const bool input_volume_controller_config_overridden =
|
||||
gain_controller2_experiment_params_.has_value() &&
|
||||
gain_controller2_experiment_params_->agc2_config.has_value();
|
||||
const InputVolumeController::Config input_volume_controller_config =
|
||||
input_volume_controller_config_overridden
|
||||
? gain_controller2_experiment_params_->agc2_config
|
||||
->input_volume_controller
|
||||
: InputVolumeController::Config{};
|
||||
submodules_.gain_controller2 = std::make_unique<GainController2>(
|
||||
config_.gain_controller2,
|
||||
gain_controller2_config_override_.has_value()
|
||||
? gain_controller2_config_override_->input_volume_controller_config
|
||||
: InputVolumeController::Config{},
|
||||
config_.gain_controller2, input_volume_controller_config,
|
||||
proc_fullband_sample_rate_hz(), num_proc_channels(), use_internal_vad);
|
||||
submodules_.gain_controller2->SetCaptureOutputUsed(
|
||||
capture_.capture_output_used);
|
||||
|
|
|
@ -138,14 +138,6 @@ class AudioProcessingImpl : public AudioProcessing {
|
|||
|
||||
AudioProcessing::Config GetConfig() const override;
|
||||
|
||||
// TODO(bugs.webrtc.org/7494): Remove when the related field trial is
|
||||
// removed.
|
||||
struct GainController2ConfigOverride {
|
||||
InputVolumeController::Config input_volume_controller_config;
|
||||
AudioProcessing::Config::GainController2::AdaptiveDigital
|
||||
adaptive_digital_config;
|
||||
};
|
||||
|
||||
protected:
|
||||
// Overridden in a mock.
|
||||
virtual void InitializeLocked()
|
||||
|
@ -199,19 +191,47 @@ class AudioProcessingImpl : public AudioProcessing {
|
|||
static std::atomic<int> instance_count_;
|
||||
const bool use_setup_specific_default_aec3_config_;
|
||||
|
||||
// TODO(bugs.webrtc.org/7494): Remove when the linked field trial is removed.
|
||||
// Override based on the "WebRTC-Audio-GainController2" field trial for the
|
||||
// AGC2 input volume controller and adaptive digital controller configuration.
|
||||
const absl::optional<GainController2ConfigOverride>
|
||||
gain_controller2_config_override_;
|
||||
// Parameters for the "GainController2" experiment which determines whether
|
||||
// the following APM sub-modules are created and, if so, their configurations:
|
||||
// AGC2 (`gain_controller2`), AGC1 (`gain_control`, `agc_manager`) and TS
|
||||
// (`transient_suppressor`).
|
||||
// TODO(bugs.webrtc.org/7494): Remove when the "WebRTC-Audio-GainController2"
|
||||
// field trial is removed.
|
||||
struct GainController2ExperimentParams {
|
||||
struct Agc2Config {
|
||||
InputVolumeController::Config input_volume_controller;
|
||||
AudioProcessing::Config::GainController2::AdaptiveDigital
|
||||
adaptive_digital_controller;
|
||||
};
|
||||
// When `agc2_config` is specified, all gain control switches to AGC2 and
|
||||
// the configuration is overridden.
|
||||
absl::optional<Agc2Config> agc2_config;
|
||||
// When true, the transient suppressor submodule is never created regardless
|
||||
// of the APM configuration.
|
||||
bool disallow_transient_suppressor_usage;
|
||||
};
|
||||
// Specified when the "WebRTC-Audio-GainController2" field trial is specified.
|
||||
// TODO(bugs.webrtc.org/7494): Remove when the "WebRTC-Audio-GainController2"
|
||||
// field trial is removed.
|
||||
const absl::optional<GainController2ExperimentParams>
|
||||
gain_controller2_experiment_params_;
|
||||
|
||||
// Parses the "WebRTC-Audio-GainController2" field trial. If disabled, returns
|
||||
// an unspecified value.
|
||||
static absl::optional<GainController2ExperimentParams>
|
||||
GetGainController2ExperimentParams();
|
||||
|
||||
// When `experiment_params` is specified, returns an APM configuration
|
||||
// modified according to the experiment parameters. Otherwise returns
|
||||
// `config`.
|
||||
static AudioProcessing::Config AdjustConfig(
|
||||
const AudioProcessing::Config& config,
|
||||
const absl::optional<GainController2ExperimentParams>& experiment_params);
|
||||
static TransientSuppressor::VadMode GetTransientSuppressorVadMode(
|
||||
const absl::optional<GainController2ExperimentParams>& experiment_params);
|
||||
|
||||
const bool use_denormal_disabler_;
|
||||
|
||||
// When true, the transient suppressor submodule is never created regardless
|
||||
// of the APM configuration.
|
||||
// TODO(bugs.webrtc.org/13663): Remove when the linked field trial is removed.
|
||||
const bool disallow_transient_supporessor_usage_;
|
||||
|
||||
const TransientSuppressor::VadMode transient_suppressor_vad_mode_;
|
||||
|
||||
SwapQueue<RuntimeSetting> capture_runtime_settings_;
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue