AudioProcessingImpl: Add input volume unit tests

Bug: webrtc:7494
Change-Id: I5a32359cacfb7cd6b610ae13b95f92283c761362
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/275500
Commit-Queue: Hanna Silen <silen@webrtc.org>
Reviewed-by: Alessio Bazzica <alessiob@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38132}
This commit is contained in:
Hanna Silen 2022-09-16 11:38:56 +02:00 committed by WebRTC LUCI CQ
parent 50a2a73ed9
commit c69188d15a

View File

@ -12,7 +12,9 @@
#include <array>
#include <memory>
#include <tuple>
#include "absl/types/optional.h"
#include "api/make_ref_counted.h"
#include "api/scoped_refptr.h"
#include "modules/audio_processing/include/audio_processing.h"
@ -23,6 +25,7 @@
#include "modules/audio_processing/test/test_utils.h"
#include "rtc_base/checks.h"
#include "rtc_base/random.h"
#include "rtc_base/strings/string_builder.h"
#include "test/field_trial.h"
#include "test/gmock.h"
#include "test/gtest.h"
@ -128,6 +131,123 @@ class TestRenderPreProcessor : public CustomProcessing {
static constexpr float ProcessSample(float x) { return 2.f * x; }
};
// Creates a simple `AudioProcessing` instance for APM input volume testing
// with analog and digital AGC enabled and minimum volume `startup_min_volume`
// at the startup.
rtc::scoped_refptr<AudioProcessing> CreateApmForInputVolumeTest(
int startup_min_volume) {
webrtc::AudioProcessing::Config config;
// Enable AGC1 analog.
config.gain_controller1.enabled = true;
config.gain_controller1.analog_gain_controller.enabled = true;
config.gain_controller1.analog_gain_controller.startup_min_volume =
startup_min_volume;
// Enable AGC2 digital.
config.gain_controller2.enabled = true;
config.gain_controller2.adaptive_digital.enabled = true;
auto apm(AudioProcessingBuilder().Create());
apm->ApplyConfig(config);
return apm;
}
// Runs `apm` input processing for volume adjustments for `num_frames` random
// frames starting from the volume `initial_volume`. This includes three steps:
// 1) Set the input volume 2) Process the stream 3) Set the new recommended
// input volume. Returns the new recommended input volume.
int ProcessInputVolume(AudioProcessing& apm,
int num_frames,
int initial_volume) {
constexpr int kSampleRateHz = 48000;
constexpr int kNumChannels = 1;
std::array<float, kSampleRateHz / 100> buffer;
float* channel_pointers[] = {buffer.data()};
StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz,
/*num_channels=*/kNumChannels);
int recommended_input_volume = initial_volume;
for (int i = 0; i < num_frames; ++i) {
Random random_generator(2341U);
RandomizeSampleVector(&random_generator, buffer);
apm.set_stream_analog_level(recommended_input_volume);
apm.ProcessStream(channel_pointers, stream_config, stream_config,
channel_pointers);
recommended_input_volume = apm.recommended_stream_analog_level();
}
return recommended_input_volume;
}
constexpr char kMinMicLevelFieldTrial[] =
"WebRTC-Audio-2ndAgcMinMicLevelExperiment";
constexpr int kMinInputVolume = 12;
std::string GetMinMicLevelExperimentFieldTrial(absl::optional<int> value) {
char field_trial_buffer[64];
rtc::SimpleStringBuilder builder(field_trial_buffer);
if (value.has_value()) {
RTC_DCHECK_GE(*value, 0);
RTC_DCHECK_LE(*value, 255);
builder << kMinMicLevelFieldTrial << "/Enabled-" << *value << "/";
} else {
builder << kMinMicLevelFieldTrial << "/Disabled/";
}
return builder.str();
}
// TODO(webrtc:7494): Remove the fieldtrial from the input volume tests when
// "WebRTC-Audio-2ndAgcMinMicLevelExperiment" is removed.
class InputVolumeStartupParameterizedTest
: public ::testing::TestWithParam<
std::tuple<int, int, absl::optional<int>>> {
protected:
InputVolumeStartupParameterizedTest()
: field_trials_(
GetMinMicLevelExperimentFieldTrial(std::get<2>(GetParam()))) {}
int GetMinStartupVolume() const { return std::get<0>(GetParam()); }
int GetStartupVolume() const { return std::get<1>(GetParam()); }
int GetMinVolume() const {
return std::get<2>(GetParam()).value_or(kMinInputVolume);
}
private:
test::ScopedFieldTrials field_trials_;
};
class InputVolumeNotZeroParameterizedTest
: public ::testing::TestWithParam<
std::tuple<int, int, absl::optional<int>>> {
protected:
InputVolumeNotZeroParameterizedTest()
: field_trials_(
GetMinMicLevelExperimentFieldTrial(std::get<2>(GetParam()))) {}
int GetStartupVolume() const { return std::get<0>(GetParam()); }
int GetVolume() const { return std::get<1>(GetParam()); }
int GetMinVolume() const {
return std::get<2>(GetParam()).value_or(kMinInputVolume);
}
bool GetMinMicLevelExperimentEnabled() {
return std::get<2>(GetParam()).has_value();
}
private:
test::ScopedFieldTrials field_trials_;
};
class InputVolumeZeroParameterizedTest
: public ::testing::TestWithParam<std::tuple<int, absl::optional<int>>> {
protected:
InputVolumeZeroParameterizedTest()
: field_trials_(
GetMinMicLevelExperimentFieldTrial(std::get<1>(GetParam()))) {}
int GetStartupVolume() const { return std::get<0>(GetParam()); }
int GetMinVolume() const {
return std::get<1>(GetParam()).value_or(kMinInputVolume);
}
private:
test::ScopedFieldTrials field_trials_;
};
} // namespace
TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
@ -813,4 +933,119 @@ TEST(ApmWithSubmodulesExcludedTest, ToggleTransientSuppressor) {
kNoErr);
}
}
// Tests that the minimum startup volume is applied at the startup.
TEST_P(InputVolumeStartupParameterizedTest,
VerifyStartupMinVolumeAppliedAtStartup) {
const int applied_startup_input_volume = GetStartupVolume();
const int startup_min_volume = GetMinStartupVolume();
const int min_volume = std::max(startup_min_volume, GetMinVolume());
const int expected_volume =
std::max(applied_startup_input_volume, min_volume);
auto apm(CreateApmForInputVolumeTest(startup_min_volume));
const int recommended_input_volume =
ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
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.
TEST_P(InputVolumeNotZeroParameterizedTest,
VerifyMinVolumeMaybeAppliedAfterManualVolumeAdjustments) {
constexpr int kStartupMinVolume = 0;
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(kStartupMinVolume));
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()) {
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.
TEST_P(InputVolumeZeroParameterizedTest,
VerifyMinVolumeNotAppliedAfterManualVolumeAdjustments) {
constexpr int kStartupMinVolume = 0;
constexpr int kZeroVolume = 0;
const int applied_startup_input_volume = GetStartupVolume();
auto apm(CreateApmForInputVolumeTest(kStartupMinVolume));
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);
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.
TEST_P(InputVolumeNotZeroParameterizedTest,
VerifyMinVolumeAppliedAfterAutomaticVolumeAdjustments) {
constexpr int kStartupMinVolume = 0;
const int applied_startup_input_volume = GetStartupVolume();
const int applied_input_volume = GetVolume();
auto apm(CreateApmForInputVolumeTest(kStartupMinVolume));
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 (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.
TEST_P(InputVolumeZeroParameterizedTest,
VerifyMinVolumeNotAppliedAfterAutomaticVolumeAdjustments) {
constexpr int kStartupMinVolume = 0;
constexpr int kZeroVolume = 0;
const int applied_startup_input_volume = GetStartupVolume();
auto apm(CreateApmForInputVolumeTest(kStartupMinVolume));
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);
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, 15),
::testing::Values(0, 5, 30),
::testing::Values(absl::nullopt,
20)));
INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest,
InputVolumeNotZeroParameterizedTest,
::testing::Combine(::testing::Values(0, 5, 15),
::testing::Values(1, 5, 30),
::testing::Values(absl::nullopt,
20)));
INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest,
InputVolumeZeroParameterizedTest,
::testing::Combine(::testing::Values(0, 5, 15),
::testing::Values(absl::nullopt,
20)));
} // namespace webrtc