summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc')
-rw-r--r--third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc1563
1 files changed, 1563 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc b/third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc
new file mode 100644
index 0000000000..9e50f994b1
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/audio_processing_impl_unittest.cc
@@ -0,0 +1,1563 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/audio_processing/audio_processing_impl.h"
+
+#include <algorithm>
+#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"
+#include "modules/audio_processing/optionally_built_submodule_creators.h"
+#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
+#include "modules/audio_processing/test/echo_canceller_test_tools.h"
+#include "modules/audio_processing/test/echo_control_mock.h"
+#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"
+
+namespace webrtc {
+namespace {
+
+using ::testing::Invoke;
+using ::testing::NotNull;
+
+class MockInitialize : public AudioProcessingImpl {
+ public:
+ MockInitialize() : AudioProcessingImpl() {}
+
+ MOCK_METHOD(void, InitializeLocked, (), (override));
+ void RealInitializeLocked() {
+ AssertLockedForTest();
+ AudioProcessingImpl::InitializeLocked();
+ }
+
+ MOCK_METHOD(void, AddRef, (), (const, override));
+ MOCK_METHOD(rtc::RefCountReleaseStatus, Release, (), (const, override));
+};
+
+// Creates MockEchoControl instances and provides a raw pointer access to
+// the next created one. The raw pointer is meant to be used with gmock.
+// Returning a pointer of the next created MockEchoControl instance is necessary
+// for the following reasons: (i) gmock expectations must be set before any call
+// occurs, (ii) APM is initialized the first time that
+// AudioProcessingImpl::ProcessStream() is called and the initialization leads
+// to the creation of a new EchoControl object.
+class MockEchoControlFactory : public EchoControlFactory {
+ public:
+ MockEchoControlFactory() : next_mock_(std::make_unique<MockEchoControl>()) {}
+ // Returns a pointer to the next MockEchoControl that this factory creates.
+ MockEchoControl* GetNext() const { return next_mock_.get(); }
+ std::unique_ptr<EchoControl> Create(int sample_rate_hz,
+ int num_render_channels,
+ int num_capture_channels) override {
+ std::unique_ptr<EchoControl> mock = std::move(next_mock_);
+ next_mock_ = std::make_unique<MockEchoControl>();
+ return mock;
+ }
+
+ private:
+ std::unique_ptr<MockEchoControl> next_mock_;
+};
+
+// Mocks EchoDetector and records the first samples of the last analyzed render
+// stream frame. Used to check what data is read by an EchoDetector
+// implementation injected into an APM.
+class TestEchoDetector : public EchoDetector {
+ public:
+ TestEchoDetector()
+ : analyze_render_audio_called_(false),
+ last_render_audio_first_sample_(0.f) {}
+ ~TestEchoDetector() override = default;
+ void AnalyzeRenderAudio(rtc::ArrayView<const float> render_audio) override {
+ last_render_audio_first_sample_ = render_audio[0];
+ analyze_render_audio_called_ = true;
+ }
+ void AnalyzeCaptureAudio(rtc::ArrayView<const float> capture_audio) override {
+ }
+ void Initialize(int capture_sample_rate_hz,
+ int num_capture_channels,
+ int render_sample_rate_hz,
+ int num_render_channels) override {}
+ EchoDetector::Metrics GetMetrics() const override { return {}; }
+ // Returns true if AnalyzeRenderAudio() has been called at least once.
+ bool analyze_render_audio_called() const {
+ return analyze_render_audio_called_;
+ }
+ // Returns the first sample of the last analyzed render frame.
+ float last_render_audio_first_sample() const {
+ return last_render_audio_first_sample_;
+ }
+
+ private:
+ bool analyze_render_audio_called_;
+ float last_render_audio_first_sample_;
+};
+
+// Mocks CustomProcessing and applies ProcessSample() to all the samples.
+// Meant to be injected into an APM to modify samples in a known and detectable
+// way.
+class TestRenderPreProcessor : public CustomProcessing {
+ public:
+ TestRenderPreProcessor() = default;
+ ~TestRenderPreProcessor() = default;
+ void Initialize(int sample_rate_hz, int num_channels) override {}
+ void Process(AudioBuffer* audio) override {
+ for (size_t k = 0; k < audio->num_channels(); ++k) {
+ rtc::ArrayView<float> channel_view(audio->channels()[k],
+ audio->num_frames());
+ std::transform(channel_view.begin(), channel_view.end(),
+ channel_view.begin(), ProcessSample);
+ }
+ }
+ std::string ToString() const override { return "TestRenderPreProcessor"; }
+ void SetRuntimeSetting(AudioProcessing::RuntimeSetting setting) override {}
+ // Modifies a sample. This member is used in Process() to modify a frame and
+ // it is publicly visible to enable tests.
+ static constexpr float ProcessSample(float x) { return 2.f * x; }
+};
+
+// 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;
+}
+
+} // namespace
+
+TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
+ MockInitialize mock;
+ ON_CALL(mock, InitializeLocked)
+ .WillByDefault(Invoke(&mock, &MockInitialize::RealInitializeLocked));
+
+ EXPECT_CALL(mock, InitializeLocked).Times(1);
+ mock.Initialize();
+
+ constexpr size_t kMaxSampleRateHz = 32000;
+ constexpr size_t kMaxNumChannels = 2;
+ std::array<int16_t, kMaxNumChannels * kMaxSampleRateHz / 100> frame;
+ frame.fill(0);
+ StreamConfig config(16000, 1);
+ // Call with the default parameters; there should be an init.
+ EXPECT_CALL(mock, InitializeLocked).Times(0);
+ EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
+ EXPECT_NOERR(
+ mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
+
+ // New sample rate. (Only impacts ProcessStream).
+ config = StreamConfig(32000, 1);
+ EXPECT_CALL(mock, InitializeLocked).Times(1);
+ EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
+
+ // New number of channels.
+ config = StreamConfig(32000, 2);
+ EXPECT_CALL(mock, InitializeLocked).Times(2);
+ EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
+ EXPECT_NOERR(
+ mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
+
+ // A new sample rate passed to ProcessReverseStream should cause an init.
+ config = StreamConfig(16000, 2);
+ EXPECT_CALL(mock, InitializeLocked).Times(1);
+ EXPECT_NOERR(
+ mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
+}
+
+TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
+ rtc::scoped_refptr<AudioProcessing> apm =
+ AudioProcessingBuilderForTesting().Create();
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.pre_amplifier.enabled = true;
+ apm_config.pre_amplifier.fixed_gain_factor = 1.f;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int kSampleRateHz = 48000;
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr size_t kNumChannels = 2;
+
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig config(kSampleRateHz, kNumChannels);
+ frame.fill(kAudioLevel);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+ EXPECT_EQ(frame[100], kAudioLevel)
+ << "With factor 1, frame shouldn't be modified.";
+
+ constexpr float kGainFactor = 2.f;
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor));
+
+ // Process for two frames to have time to ramp up gain.
+ for (int i = 0; i < 2; ++i) {
+ frame.fill(kAudioLevel);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+ }
+ EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
+ << "Frame should be amplified.";
+}
+
+TEST(AudioProcessingImplTest,
+ LevelAdjustmentUpdateCapturePreGainRuntimeSetting) {
+ rtc::scoped_refptr<AudioProcessing> apm =
+ AudioProcessingBuilderForTesting().Create();
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.capture_level_adjustment.enabled = true;
+ apm_config.capture_level_adjustment.pre_gain_factor = 1.f;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int kSampleRateHz = 48000;
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr size_t kNumChannels = 2;
+
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig config(kSampleRateHz, kNumChannels);
+ frame.fill(kAudioLevel);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+ EXPECT_EQ(frame[100], kAudioLevel)
+ << "With factor 1, frame shouldn't be modified.";
+
+ constexpr float kGainFactor = 2.f;
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor));
+
+ // Process for two frames to have time to ramp up gain.
+ for (int i = 0; i < 2; ++i) {
+ frame.fill(kAudioLevel);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+ }
+ EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
+ << "Frame should be amplified.";
+}
+
+TEST(AudioProcessingImplTest,
+ LevelAdjustmentUpdateCapturePostGainRuntimeSetting) {
+ rtc::scoped_refptr<AudioProcessing> apm =
+ AudioProcessingBuilderForTesting().Create();
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.capture_level_adjustment.enabled = true;
+ apm_config.capture_level_adjustment.post_gain_factor = 1.f;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int kSampleRateHz = 48000;
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr size_t kNumChannels = 2;
+
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig config(kSampleRateHz, kNumChannels);
+ frame.fill(kAudioLevel);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+ EXPECT_EQ(frame[100], kAudioLevel)
+ << "With factor 1, frame shouldn't be modified.";
+
+ constexpr float kGainFactor = 2.f;
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePostGain(kGainFactor));
+
+ // Process for two frames to have time to ramp up gain.
+ for (int i = 0; i < 2; ++i) {
+ frame.fill(kAudioLevel);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+ }
+ EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
+ << "Frame should be amplified.";
+}
+
+TEST(AudioProcessingImplTest, EchoControllerObservesSetCaptureUsageChange) {
+ // Tests that the echo controller observes that the capture usage has been
+ // updated.
+ auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
+ const MockEchoControlFactory* echo_control_factory_ptr =
+ echo_control_factory.get();
+
+ rtc::scoped_refptr<AudioProcessing> apm =
+ AudioProcessingBuilderForTesting()
+ .SetEchoControlFactory(std::move(echo_control_factory))
+ .Create();
+
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr int kSampleRateHz = 48000;
+ constexpr int kNumChannels = 2;
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig config(kSampleRateHz, kNumChannels);
+ frame.fill(kAudioLevel);
+
+ MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
+
+ // Ensure that SetCaptureOutputUsage is not called when no runtime settings
+ // are passed.
+ EXPECT_CALL(*echo_control_mock, SetCaptureOutputUsage(testing::_)).Times(0);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ // Ensure that SetCaptureOutputUsage is called with the right information when
+ // a runtime setting is passed.
+ EXPECT_CALL(*echo_control_mock,
+ SetCaptureOutputUsage(/*capture_output_used=*/false))
+ .Times(1);
+ EXPECT_TRUE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/false)));
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ EXPECT_CALL(*echo_control_mock,
+ SetCaptureOutputUsage(/*capture_output_used=*/true))
+ .Times(1);
+ EXPECT_TRUE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/true)));
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ // The number of positions to place items in the queue is equal to the queue
+ // size minus 1.
+ constexpr int kNumSlotsInQueue = RuntimeSettingQueueSize();
+
+ // Ensure that SetCaptureOutputUsage is called with the right information when
+ // many runtime settings are passed.
+ for (int k = 0; k < kNumSlotsInQueue - 1; ++k) {
+ EXPECT_TRUE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/false)));
+ }
+ EXPECT_CALL(*echo_control_mock,
+ SetCaptureOutputUsage(/*capture_output_used=*/false))
+ .Times(kNumSlotsInQueue - 1);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ // Ensure that SetCaptureOutputUsage is properly called with the fallback
+ // value when the runtime settings queue becomes full.
+ for (int k = 0; k < kNumSlotsInQueue; ++k) {
+ EXPECT_TRUE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/false)));
+ }
+ EXPECT_FALSE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/false)));
+ EXPECT_FALSE(apm->PostRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
+ /*capture_output_used=*/false)));
+ EXPECT_CALL(*echo_control_mock,
+ SetCaptureOutputUsage(/*capture_output_used=*/false))
+ .Times(kNumSlotsInQueue);
+ EXPECT_CALL(*echo_control_mock,
+ SetCaptureOutputUsage(/*capture_output_used=*/true))
+ .Times(1);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+}
+
+TEST(AudioProcessingImplTest,
+ EchoControllerObservesPreAmplifierEchoPathGainChange) {
+ // Tests that the echo controller observes an echo path gain change when the
+ // pre-amplifier submodule changes the gain.
+ auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
+ const auto* echo_control_factory_ptr = echo_control_factory.get();
+
+ rtc::scoped_refptr<AudioProcessing> apm =
+ AudioProcessingBuilderForTesting()
+ .SetEchoControlFactory(std::move(echo_control_factory))
+ .Create();
+ // Disable AGC.
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.gain_controller1.enabled = false;
+ apm_config.gain_controller2.enabled = false;
+ apm_config.pre_amplifier.enabled = true;
+ apm_config.pre_amplifier.fixed_gain_factor = 1.f;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr size_t kSampleRateHz = 48000;
+ constexpr size_t kNumChannels = 2;
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig config(kSampleRateHz, kNumChannels);
+ frame.fill(kAudioLevel);
+
+ MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
+
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
+ .Times(1);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
+ .Times(1);
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+}
+
+TEST(AudioProcessingImplTest,
+ EchoControllerObservesLevelAdjustmentPreGainEchoPathGainChange) {
+ // Tests that the echo controller observes an echo path gain change when the
+ // pre-amplifier submodule changes the gain.
+ auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
+ const auto* echo_control_factory_ptr = echo_control_factory.get();
+
+ rtc::scoped_refptr<AudioProcessing> apm =
+ AudioProcessingBuilderForTesting()
+ .SetEchoControlFactory(std::move(echo_control_factory))
+ .Create();
+ // Disable AGC.
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.gain_controller1.enabled = false;
+ apm_config.gain_controller2.enabled = false;
+ apm_config.capture_level_adjustment.enabled = true;
+ apm_config.capture_level_adjustment.pre_gain_factor = 1.f;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr size_t kSampleRateHz = 48000;
+ constexpr size_t kNumChannels = 2;
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig config(kSampleRateHz, kNumChannels);
+ frame.fill(kAudioLevel);
+
+ MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
+
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
+ .Times(1);
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
+ .Times(1);
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
+ apm->ProcessStream(frame.data(), config, config, frame.data());
+}
+
+TEST(AudioProcessingImplTest,
+ EchoControllerObservesAnalogAgc1EchoPathGainChange) {
+ // Tests that the echo controller observes an echo path gain change when the
+ // AGC1 analog adaptive submodule changes the analog gain.
+ auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
+ const auto* echo_control_factory_ptr = echo_control_factory.get();
+
+ rtc::scoped_refptr<AudioProcessing> apm =
+ AudioProcessingBuilderForTesting()
+ .SetEchoControlFactory(std::move(echo_control_factory))
+ .Create();
+ webrtc::AudioProcessing::Config apm_config;
+ // Enable AGC1.
+ apm_config.gain_controller1.enabled = true;
+ apm_config.gain_controller1.analog_gain_controller.enabled = true;
+ apm_config.gain_controller2.enabled = false;
+ apm_config.pre_amplifier.enabled = false;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int16_t kAudioLevel = 1000;
+ constexpr size_t kSampleRateHz = 48000;
+ constexpr size_t kNumChannels = 2;
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig stream_config(kSampleRateHz, kNumChannels);
+ frame.fill(kAudioLevel);
+
+ MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
+
+ constexpr int kInitialStreamAnalogLevel = 123;
+ apm->set_stream_analog_level(kInitialStreamAnalogLevel);
+
+ // When the first fame is processed, no echo path gain change must be
+ // detected.
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
+ .Times(1);
+ apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
+
+ // Simulate the application of the recommended analog level.
+ int recommended_analog_level = apm->recommended_stream_analog_level();
+ if (recommended_analog_level == kInitialStreamAnalogLevel) {
+ // Force an analog gain change if it did not happen.
+ recommended_analog_level++;
+ }
+ apm->set_stream_analog_level(recommended_analog_level);
+
+ // After the first fame and with a stream analog level change, the echo path
+ // gain change must be detected.
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
+ .Times(1);
+ apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
+}
+
+TEST(AudioProcessingImplTest,
+ ProcessWithAgc2AndTransientSuppressorVadModeDefault) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Disabled/");
+ auto apm = AudioProcessingBuilder()
+ .SetConfig({.gain_controller1{.enabled = false}})
+ .Create();
+ ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.gain_controller1.enabled = false;
+ apm_config.gain_controller2.enabled = true;
+ apm_config.gain_controller2.adaptive_digital.enabled = true;
+ apm_config.transient_suppression.enabled = true;
+ apm->ApplyConfig(apm_config);
+ 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);
+ Random random_generator(2341U);
+ constexpr int kFramesToProcess = 10;
+ for (int i = 0; i < kFramesToProcess; ++i) {
+ RandomizeSampleVector(&random_generator, buffer);
+ ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+ channel_pointers),
+ kNoErr);
+ }
+}
+
+TEST(AudioProcessingImplTest,
+ ProcessWithAgc2AndTransientSuppressorVadModeRnnVad) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
+ rtc::scoped_refptr<AudioProcessing> apm = AudioProcessingBuilder().Create();
+ ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.gain_controller1.enabled = false;
+ apm_config.gain_controller2.enabled = true;
+ apm_config.gain_controller2.adaptive_digital.enabled = true;
+ apm_config.transient_suppression.enabled = true;
+ apm->ApplyConfig(apm_config);
+ 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);
+ Random random_generator(2341U);
+ constexpr int kFramesToProcess = 10;
+ for (int i = 0; i < kFramesToProcess; ++i) {
+ RandomizeSampleVector(&random_generator, buffer);
+ ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+ channel_pointers),
+ kNoErr);
+ }
+}
+
+TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) {
+ // Tests that the echo controller observes an echo path gain change when a
+ // playout volume change is reported.
+ auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
+ const auto* echo_control_factory_ptr = echo_control_factory.get();
+
+ rtc::scoped_refptr<AudioProcessing> apm =
+ AudioProcessingBuilderForTesting()
+ .SetEchoControlFactory(std::move(echo_control_factory))
+ .Create();
+ // Disable AGC.
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.gain_controller1.enabled = false;
+ apm_config.gain_controller2.enabled = false;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int16_t kAudioLevel = 10000;
+ constexpr size_t kSampleRateHz = 48000;
+ constexpr size_t kNumChannels = 2;
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig stream_config(kSampleRateHz, kNumChannels);
+ frame.fill(kAudioLevel);
+
+ MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
+
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
+ .Times(1);
+ apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
+
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
+ .Times(1);
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
+ apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
+
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
+ .Times(1);
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
+ apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
+
+ EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
+ EXPECT_CALL(*echo_control_mock,
+ ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
+ .Times(1);
+ apm->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(100));
+ apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
+}
+
+TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) {
+ // Make sure that signal changes caused by a render pre-processing sub-module
+ // take place before any echo detector analysis.
+ auto test_echo_detector = rtc::make_ref_counted<TestEchoDetector>();
+ std::unique_ptr<CustomProcessing> test_render_pre_processor(
+ new TestRenderPreProcessor());
+ // Create APM injecting the test echo detector and render pre-processor.
+ rtc::scoped_refptr<AudioProcessing> apm =
+ AudioProcessingBuilderForTesting()
+ .SetEchoDetector(test_echo_detector)
+ .SetRenderPreProcessing(std::move(test_render_pre_processor))
+ .Create();
+ webrtc::AudioProcessing::Config apm_config;
+ apm_config.pre_amplifier.enabled = true;
+ apm->ApplyConfig(apm_config);
+
+ constexpr int16_t kAudioLevel = 1000;
+ constexpr int kSampleRateHz = 16000;
+ constexpr size_t kNumChannels = 1;
+ // Explicitly initialize APM to ensure no render frames are discarded.
+ const ProcessingConfig processing_config = {{
+ {kSampleRateHz, kNumChannels},
+ {kSampleRateHz, kNumChannels},
+ {kSampleRateHz, kNumChannels},
+ {kSampleRateHz, kNumChannels},
+ }};
+ apm->Initialize(processing_config);
+
+ std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
+ StreamConfig stream_config(kSampleRateHz, kNumChannels);
+
+ constexpr float kAudioLevelFloat = static_cast<float>(kAudioLevel);
+ constexpr float kExpectedPreprocessedAudioLevel =
+ TestRenderPreProcessor::ProcessSample(kAudioLevelFloat);
+ ASSERT_NE(kAudioLevelFloat, kExpectedPreprocessedAudioLevel);
+
+ // Analyze a render stream frame.
+ frame.fill(kAudioLevel);
+ ASSERT_EQ(AudioProcessing::Error::kNoError,
+ apm->ProcessReverseStream(frame.data(), stream_config,
+ stream_config, frame.data()));
+ // Trigger a call to in EchoDetector::AnalyzeRenderAudio() via
+ // ProcessStream().
+ frame.fill(kAudioLevel);
+ ASSERT_EQ(AudioProcessing::Error::kNoError,
+ apm->ProcessStream(frame.data(), stream_config, stream_config,
+ frame.data()));
+ // Regardless of how the call to in EchoDetector::AnalyzeRenderAudio() is
+ // triggered, the line below checks that the call has occurred. If not, the
+ // APM implementation may have changed and this test might need to be adapted.
+ ASSERT_TRUE(test_echo_detector->analyze_render_audio_called());
+ // Check that the data read in EchoDetector::AnalyzeRenderAudio() is that
+ // produced by the render pre-processor.
+ EXPECT_EQ(kExpectedPreprocessedAudioLevel,
+ test_echo_detector->last_render_audio_first_sample());
+}
+
+// Disabling build-optional submodules and trying to enable them via the APM
+// config should be bit-exact with running APM with said submodules disabled.
+// This mainly tests that SetCreateOptionalSubmodulesForTesting has an effect.
+TEST(ApmWithSubmodulesExcludedTest, BitexactWithDisabledModules) {
+ auto apm = rtc::make_ref_counted<AudioProcessingImpl>();
+ ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
+
+ ApmSubmoduleCreationOverrides overrides;
+ overrides.transient_suppression = true;
+ apm->OverrideSubmoduleCreationForTesting(overrides);
+
+ AudioProcessing::Config apm_config = apm->GetConfig();
+ apm_config.transient_suppression.enabled = true;
+ apm->ApplyConfig(apm_config);
+
+ rtc::scoped_refptr<AudioProcessing> apm_reference =
+ AudioProcessingBuilder().Create();
+ apm_config = apm_reference->GetConfig();
+ apm_config.transient_suppression.enabled = false;
+ apm_reference->ApplyConfig(apm_config);
+
+ constexpr int kSampleRateHz = 16000;
+ constexpr int kNumChannels = 1;
+ std::array<float, kSampleRateHz / 100> buffer;
+ std::array<float, kSampleRateHz / 100> buffer_reference;
+ float* channel_pointers[] = {buffer.data()};
+ float* channel_pointers_reference[] = {buffer_reference.data()};
+ StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz,
+ /*num_channels=*/kNumChannels);
+ Random random_generator(2341U);
+ constexpr int kFramesToProcessPerConfiguration = 10;
+
+ for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
+ RandomizeSampleVector(&random_generator, buffer);
+ std::copy(buffer.begin(), buffer.end(), buffer_reference.begin());
+ ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+ channel_pointers),
+ kNoErr);
+ ASSERT_EQ(
+ apm_reference->ProcessStream(channel_pointers_reference, stream_config,
+ stream_config, channel_pointers_reference),
+ kNoErr);
+ for (int j = 0; j < kSampleRateHz / 100; ++j) {
+ EXPECT_EQ(buffer[j], buffer_reference[j]);
+ }
+ }
+}
+
+// Disable transient suppressor creation and run APM in ways that should trigger
+// calls to the transient suppressor API.
+TEST(ApmWithSubmodulesExcludedTest, ReinitializeTransientSuppressor) {
+ auto apm = rtc::make_ref_counted<AudioProcessingImpl>();
+ ASSERT_EQ(apm->Initialize(), kNoErr);
+
+ ApmSubmoduleCreationOverrides overrides;
+ overrides.transient_suppression = true;
+ apm->OverrideSubmoduleCreationForTesting(overrides);
+
+ AudioProcessing::Config config = apm->GetConfig();
+ config.transient_suppression.enabled = true;
+ apm->ApplyConfig(config);
+ // 960 samples per frame: 10 ms of <= 48 kHz audio with <= 2 channels.
+ float buffer[960];
+ float* channel_pointers[] = {&buffer[0], &buffer[480]};
+ Random random_generator(2341U);
+ constexpr int kFramesToProcessPerConfiguration = 3;
+
+ StreamConfig initial_stream_config(/*sample_rate_hz=*/16000,
+ /*num_channels=*/1);
+ for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
+ RandomizeSampleVector(&random_generator, buffer);
+ EXPECT_EQ(apm->ProcessStream(channel_pointers, initial_stream_config,
+ initial_stream_config, channel_pointers),
+ kNoErr);
+ }
+
+ StreamConfig stereo_stream_config(/*sample_rate_hz=*/16000,
+ /*num_channels=*/2);
+ for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
+ RandomizeSampleVector(&random_generator, buffer);
+ EXPECT_EQ(apm->ProcessStream(channel_pointers, stereo_stream_config,
+ stereo_stream_config, channel_pointers),
+ kNoErr);
+ }
+
+ StreamConfig high_sample_rate_stream_config(/*sample_rate_hz=*/48000,
+ /*num_channels=*/2);
+ for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
+ RandomizeSampleVector(&random_generator, buffer);
+ EXPECT_EQ(
+ apm->ProcessStream(channel_pointers, high_sample_rate_stream_config,
+ high_sample_rate_stream_config, channel_pointers),
+ kNoErr);
+ }
+}
+
+// Disable transient suppressor creation and run APM in ways that should trigger
+// calls to the transient suppressor API.
+TEST(ApmWithSubmodulesExcludedTest, ToggleTransientSuppressor) {
+ auto apm = rtc::make_ref_counted<AudioProcessingImpl>();
+ ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
+
+ ApmSubmoduleCreationOverrides overrides;
+ overrides.transient_suppression = true;
+ apm->OverrideSubmoduleCreationForTesting(overrides);
+
+ // 960 samples per frame: 10 ms of <= 48 kHz audio with <= 2 channels.
+ float buffer[960];
+ float* channel_pointers[] = {&buffer[0], &buffer[480]};
+ Random random_generator(2341U);
+ constexpr int kFramesToProcessPerConfiguration = 3;
+ StreamConfig stream_config(/*sample_rate_hz=*/16000,
+ /*num_channels=*/1);
+
+ AudioProcessing::Config config = apm->GetConfig();
+ config.transient_suppression.enabled = true;
+ apm->ApplyConfig(config);
+ for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
+ RandomizeSampleVector(&random_generator, buffer);
+ EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+ channel_pointers),
+ kNoErr);
+ }
+
+ config = apm->GetConfig();
+ config.transient_suppression.enabled = false;
+ apm->ApplyConfig(config);
+ for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
+ RandomizeSampleVector(&random_generator, buffer);
+ EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+ channel_pointers),
+ kNoErr);
+ }
+
+ config = apm->GetConfig();
+ config.transient_suppression.enabled = true;
+ apm->ApplyConfig(config);
+ for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
+ RandomizeSampleVector(&random_generator, buffer);
+ EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+ channel_pointers),
+ kNoErr);
+ }
+}
+
+class StartupInputVolumeParameterizedTest
+ : public ::testing::TestWithParam<int> {};
+
+// Tests that, when no input volume controller is used, the startup input volume
+// is never modified.
+TEST_P(StartupInputVolumeParameterizedTest,
+ WithNoInputVolumeControllerStartupVolumeNotModified) {
+ webrtc::AudioProcessing::Config config;
+ config.gain_controller1.enabled = false;
+ config.gain_controller2.enabled = false;
+ auto apm = AudioProcessingBuilder().SetConfig(config).Create();
+
+ int startup_volume = GetParam();
+ int recommended_volume = ProcessInputVolume(
+ *apm, /*num_frames=*/1, /*initial_volume=*/startup_volume);
+ EXPECT_EQ(recommended_volume, startup_volume);
+}
+
+INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest,
+ StartupInputVolumeParameterizedTest,
+ ::testing::Values(0, 5, 15, 50, 100));
+
+// Tests that, when no input volume controller is used, the recommended input
+// volume always matches the applied one.
+TEST(AudioProcessingImplTest,
+ WithNoInputVolumeControllerAppliedAndRecommendedVolumesMatch) {
+ webrtc::AudioProcessing::Config config;
+ config.gain_controller1.enabled = false;
+ config.gain_controller2.enabled = false;
+ auto apm = AudioProcessingBuilder().SetConfig(config).Create();
+
+ Random rand_gen(42);
+ for (int i = 0; i < 32; ++i) {
+ SCOPED_TRACE(i);
+ int32_t applied_volume = rand_gen.Rand(/*low=*/0, /*high=*/255);
+ int recommended_volume =
+ ProcessInputVolume(*apm, /*num_frames=*/1, applied_volume);
+ EXPECT_EQ(recommended_volume, applied_volume);
+ }
+}
+
+class ApmInputVolumeControllerParametrizedTest
+ : public ::testing::TestWithParam<
+ std::tuple<int, int, AudioProcessing::Config>> {
+ protected:
+ ApmInputVolumeControllerParametrizedTest()
+ : sample_rate_hz_(std::get<0>(GetParam())),
+ num_channels_(std::get<1>(GetParam())),
+ channels_(num_channels_),
+ channel_pointers_(num_channels_) {
+ const int frame_size = sample_rate_hz_ / 100;
+ for (int c = 0; c < num_channels_; ++c) {
+ channels_[c].resize(frame_size);
+ channel_pointers_[c] = channels_[c].data();
+ std::fill(channels_[c].begin(), channels_[c].end(), 0.0f);
+ }
+ }
+
+ int sample_rate_hz() const { return sample_rate_hz_; }
+ int num_channels() const { return num_channels_; }
+ AudioProcessing::Config GetConfig() const { return std::get<2>(GetParam()); }
+
+ float* const* channel_pointers() { return channel_pointers_.data(); }
+
+ private:
+ const int sample_rate_hz_;
+ const int num_channels_;
+ std::vector<std::vector<float>> channels_;
+ std::vector<float*> channel_pointers_;
+};
+
+TEST_P(ApmInputVolumeControllerParametrizedTest,
+ EnforceMinInputVolumeAtStartupWithZeroVolume) {
+ const StreamConfig stream_config(sample_rate_hz(), num_channels());
+ auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
+
+ apm->set_stream_analog_level(0);
+ apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+ channel_pointers());
+ EXPECT_GT(apm->recommended_stream_analog_level(), 0);
+}
+
+TEST_P(ApmInputVolumeControllerParametrizedTest,
+ EnforceMinInputVolumeAtStartupWithNonZeroVolume) {
+ const StreamConfig stream_config(sample_rate_hz(), num_channels());
+ auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
+
+ constexpr int kStartupVolume = 3;
+ apm->set_stream_analog_level(kStartupVolume);
+ apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+ channel_pointers());
+ EXPECT_GT(apm->recommended_stream_analog_level(), kStartupVolume);
+}
+
+TEST_P(ApmInputVolumeControllerParametrizedTest,
+ EnforceMinInputVolumeAfterManualVolumeAdjustment) {
+ const auto config = GetConfig();
+ if (config.gain_controller1.enabled) {
+ // After a downward manual adjustment, AGC1 slowly converges to the minimum
+ // input volume.
+ GTEST_SKIP() << "Does not apply to AGC1";
+ }
+ const StreamConfig stream_config(sample_rate_hz(), num_channels());
+ auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
+
+ apm->set_stream_analog_level(20);
+ apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+ channel_pointers());
+ constexpr int kManuallyAdjustedVolume = 3;
+ apm->set_stream_analog_level(kManuallyAdjustedVolume);
+ apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+ channel_pointers());
+ EXPECT_GT(apm->recommended_stream_analog_level(), kManuallyAdjustedVolume);
+}
+
+TEST_P(ApmInputVolumeControllerParametrizedTest,
+ DoNotEnforceMinInputVolumeAtStartupWithHighVolume) {
+ const StreamConfig stream_config(sample_rate_hz(), num_channels());
+ auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
+
+ constexpr int kStartupVolume = 200;
+ apm->set_stream_analog_level(kStartupVolume);
+ apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+ channel_pointers());
+ EXPECT_EQ(apm->recommended_stream_analog_level(), kStartupVolume);
+}
+
+TEST_P(ApmInputVolumeControllerParametrizedTest,
+ DoNotEnforceMinInputVolumeAfterManualVolumeAdjustmentToZero) {
+ const StreamConfig stream_config(sample_rate_hz(), num_channels());
+ auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
+
+ apm->set_stream_analog_level(100);
+ apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+ channel_pointers());
+ apm->set_stream_analog_level(0);
+ apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+ channel_pointers());
+ EXPECT_EQ(apm->recommended_stream_analog_level(), 0);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AudioProcessingImplTest,
+ ApmInputVolumeControllerParametrizedTest,
+ ::testing::Combine(
+ ::testing::Values(8000, 16000, 32000, 48000), // Sample rates.
+ ::testing::Values(1, 2), // Number of channels.
+ ::testing::Values(
+ // Full AGC1.
+ AudioProcessing::Config{
+ .gain_controller1 = {.enabled = true,
+ .analog_gain_controller =
+ {.enabled = true,
+ .enable_digital_adaptive = true}},
+ .gain_controller2 = {.enabled = false}},
+ // Hybrid AGC.
+ AudioProcessing::Config{
+ .gain_controller1 = {.enabled = true,
+ .analog_gain_controller =
+ {.enabled = true,
+ .enable_digital_adaptive = false}},
+ .gain_controller2 = {.enabled = true,
+ .adaptive_digital = {.enabled = true}}})));
+
+// When the input volume is not emulated and no input volume controller is
+// active, the recommended volume must always be the applied volume.
+TEST(AudioProcessingImplTest,
+ RecommendAppliedInputVolumeWithNoAgcWithNoEmulation) {
+ auto apm = AudioProcessingBuilder()
+ .SetConfig({.capture_level_adjustment = {.enabled = false},
+ .gain_controller1 = {.enabled = false}})
+ .Create();
+
+ constexpr int kOneFrame = 1;
+ EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/123), 123);
+ EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/59), 59);
+ EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135);
+}
+
+// When the input volume is emulated, the recommended volume must always be the
+// applied volume and at any time it must not be that set in the input volume
+// emulator.
+// TODO(bugs.webrtc.org/14581): Enable when APM fixed to let this test pass.
+TEST(AudioProcessingImplTest,
+ DISABLED_RecommendAppliedInputVolumeWithNoAgcWithEmulation) {
+ auto apm =
+ AudioProcessingBuilder()
+ .SetConfig({.capture_level_adjustment = {.enabled = true,
+ .analog_mic_gain_emulation{
+ .enabled = true,
+ .initial_level = 255}},
+ .gain_controller1 = {.enabled = false}})
+ .Create();
+
+ constexpr int kOneFrame = 1;
+ EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/123), 123);
+ EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/59), 59);
+ EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135);
+}
+
+// Even if there is an enabled input volume controller, when the input volume is
+// emulated, the recommended volume is always the applied volume because the
+// active controller must only adjust the internally emulated volume and leave
+// the externally applied volume unchanged.
+// TODO(bugs.webrtc.org/14581): Enable when APM fixed to let this test pass.
+TEST(AudioProcessingImplTest,
+ DISABLED_RecommendAppliedInputVolumeWithAgcWithEmulation) {
+ auto apm =
+ AudioProcessingBuilder()
+ .SetConfig({.capture_level_adjustment = {.enabled = true,
+ .analog_mic_gain_emulation{
+ .enabled = true}},
+ .gain_controller1 = {.enabled = true,
+ .analog_gain_controller{
+ .enabled = true,
+ }}})
+ .Create();
+
+ constexpr int kOneFrame = 1;
+ EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/123), 123);
+ EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/59), 59);
+ EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135);
+}
+
+TEST(AudioProcessingImplTest,
+ Agc2FieldTrialDoNotSwitchToFullAgc2WhenNoAgcIsActive) {
+ constexpr AudioProcessing::Config kOriginal{
+ .gain_controller1{.enabled = false},
+ .gain_controller2{.enabled = false},
+ };
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+ EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
+ EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(kOriginal);
+ adjusted = apm->GetConfig();
+ EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
+ EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
+}
+
+TEST(AudioProcessingImplTest,
+ Agc2FieldTrialDoNotSwitchToFullAgc2WithAgc1Agc2InputVolumeControllers) {
+ constexpr AudioProcessing::Config kOriginal{
+ .gain_controller1{.enabled = true,
+ .analog_gain_controller{.enabled = true}},
+ .gain_controller2{.enabled = true,
+ .input_volume_controller{.enabled = true}},
+ };
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+ EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
+ EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(kOriginal);
+ adjusted = apm->GetConfig();
+ EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
+ EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
+}
+
+class Agc2FieldTrialParametrizedTest
+ : public ::testing::TestWithParam<AudioProcessing::Config> {};
+
+TEST_P(Agc2FieldTrialParametrizedTest, DoNotChangeConfigIfDisabled) {
+ const AudioProcessing::Config original = GetParam();
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Disabled/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+ EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+ EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(original);
+ adjusted = apm->GetConfig();
+ EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+ EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest, DoNotChangeConfigIfNoOverride) {
+ const AudioProcessing::Config original = GetParam();
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,"
+ "switch_to_agc2:false,"
+ "disallow_transient_suppressor_usage:false/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+ EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+ EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(original);
+ adjusted = apm->GetConfig();
+ EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+ EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest, DoNotSwitchToFullAgc2) {
+ const AudioProcessing::Config original = GetParam();
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:false/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+ EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+ EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(original);
+ adjusted = apm->GetConfig();
+ EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+ EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest, SwitchToFullAgc2) {
+ const AudioProcessing::Config original = GetParam();
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+ EXPECT_FALSE(adjusted.gain_controller1.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(original);
+ adjusted = apm->GetConfig();
+ EXPECT_FALSE(adjusted.gain_controller1.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest,
+ SwitchToFullAgc2AndOverrideInputVolumeControllerParameters) {
+ const AudioProcessing::Config original = GetParam();
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true,"
+ "min_input_volume:123,"
+ "clipped_level_min:20,"
+ "clipped_level_step:30,"
+ "clipped_ratio_threshold:0.4,"
+ "clipped_wait_frames:50,"
+ "enable_clipping_predictor:true,"
+ "target_range_max_dbfs:-6,"
+ "target_range_min_dbfs:-70,"
+ "update_input_volume_wait_frames:80,"
+ "speech_probability_threshold:0.9,"
+ "speech_ratio_threshold:1.0/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+ EXPECT_FALSE(adjusted.gain_controller1.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(original);
+ adjusted = apm->GetConfig();
+ EXPECT_FALSE(adjusted.gain_controller1.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest,
+ SwitchToFullAgc2AndOverrideAdaptiveDigitalControllerParameters) {
+ const AudioProcessing::Config original = GetParam();
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true,"
+ "headroom_db:10,"
+ "max_gain_db:20,"
+ "initial_gain_db:7,"
+ "max_gain_change_db_per_second:5,"
+ "max_output_noise_level_dbfs:-40/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+ EXPECT_FALSE(adjusted.gain_controller1.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+ ASSERT_NE(adjusted.gain_controller2.adaptive_digital,
+ original.gain_controller2.adaptive_digital);
+ EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.headroom_db, 10);
+ EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.max_gain_db, 20);
+ EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.initial_gain_db, 7);
+ EXPECT_EQ(
+ adjusted.gain_controller2.adaptive_digital.max_gain_change_db_per_second,
+ 5);
+ EXPECT_EQ(
+ adjusted.gain_controller2.adaptive_digital.max_output_noise_level_dbfs,
+ -40);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(original);
+ adjusted = apm->GetConfig();
+ EXPECT_FALSE(adjusted.gain_controller1.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+ EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+ ASSERT_NE(adjusted.gain_controller2.adaptive_digital,
+ original.gain_controller2.adaptive_digital);
+ EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.headroom_db, 10);
+ EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.max_gain_db, 20);
+ EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.initial_gain_db, 7);
+ EXPECT_EQ(
+ adjusted.gain_controller2.adaptive_digital.max_gain_change_db_per_second,
+ 5);
+ EXPECT_EQ(
+ adjusted.gain_controller2.adaptive_digital.max_output_noise_level_dbfs,
+ -40);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest, ProcessSucceedsWithTs) {
+ AudioProcessing::Config config = GetParam();
+ if (!config.transient_suppression.enabled) {
+ GTEST_SKIP() << "TS is disabled, skip.";
+ }
+
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Disabled/");
+ auto apm = AudioProcessingBuilder().SetConfig(config).Create();
+
+ constexpr int kSampleRateHz = 48000;
+ constexpr int kNumChannels = 1;
+ std::array<float, kSampleRateHz / 100> buffer;
+ float* channel_pointers[] = {buffer.data()};
+ StreamConfig stream_config(kSampleRateHz, kNumChannels);
+ Random random_generator(2341U);
+ constexpr int kFramesToProcess = 10;
+ int volume = 100;
+ for (int i = 0; i < kFramesToProcess; ++i) {
+ SCOPED_TRACE(i);
+ RandomizeSampleVector(&random_generator, buffer);
+ apm->set_stream_analog_level(volume);
+ ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+ channel_pointers),
+ kNoErr);
+ volume = apm->recommended_stream_analog_level();
+ }
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest, ProcessSucceedsWithoutTs) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,"
+ "switch_to_agc2:false,"
+ "disallow_transient_suppressor_usage:true/");
+ auto apm = AudioProcessingBuilder().SetConfig(GetParam()).Create();
+
+ constexpr int kSampleRateHz = 48000;
+ constexpr int kNumChannels = 1;
+ std::array<float, kSampleRateHz / 100> buffer;
+ float* channel_pointers[] = {buffer.data()};
+ StreamConfig stream_config(kSampleRateHz, kNumChannels);
+ Random random_generator(2341U);
+ constexpr int kFramesToProcess = 10;
+ int volume = 100;
+ for (int i = 0; i < kFramesToProcess; ++i) {
+ SCOPED_TRACE(i);
+ RandomizeSampleVector(&random_generator, buffer);
+ apm->set_stream_analog_level(volume);
+ ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+ channel_pointers),
+ kNoErr);
+ volume = apm->recommended_stream_analog_level();
+ }
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest,
+ ProcessSucceedsWhenSwitchToFullAgc2WithTs) {
+ AudioProcessing::Config config = GetParam();
+ if (!config.transient_suppression.enabled) {
+ GTEST_SKIP() << "TS is disabled, skip.";
+ }
+
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,"
+ "switch_to_agc2:true,"
+ "disallow_transient_suppressor_usage:false/");
+ auto apm = AudioProcessingBuilder().SetConfig(config).Create();
+
+ constexpr int kSampleRateHz = 48000;
+ constexpr int kNumChannels = 1;
+ std::array<float, kSampleRateHz / 100> buffer;
+ float* channel_pointers[] = {buffer.data()};
+ StreamConfig stream_config(kSampleRateHz, kNumChannels);
+ Random random_generator(2341U);
+ constexpr int kFramesToProcess = 10;
+ int volume = 100;
+ for (int i = 0; i < kFramesToProcess; ++i) {
+ SCOPED_TRACE(i);
+ RandomizeSampleVector(&random_generator, buffer);
+ apm->set_stream_analog_level(volume);
+ ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+ channel_pointers),
+ kNoErr);
+ volume = apm->recommended_stream_analog_level();
+ }
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest,
+ ProcessSucceedsWhenSwitchToFullAgc2WithoutTs) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,"
+ "switch_to_agc2:true,"
+ "disallow_transient_suppressor_usage:true/");
+ auto apm = AudioProcessingBuilder().SetConfig(GetParam()).Create();
+
+ constexpr int kSampleRateHz = 48000;
+ constexpr int kNumChannels = 1;
+ std::array<float, kSampleRateHz / 100> buffer;
+ float* channel_pointers[] = {buffer.data()};
+ StreamConfig stream_config(kSampleRateHz, kNumChannels);
+ Random random_generator(2341U);
+ constexpr int kFramesToProcess = 10;
+ int volume = 100;
+ for (int i = 0; i < kFramesToProcess; ++i) {
+ SCOPED_TRACE(i);
+ RandomizeSampleVector(&random_generator, buffer);
+ apm->set_stream_analog_level(volume);
+ ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+ channel_pointers),
+ kNoErr);
+ volume = apm->recommended_stream_analog_level();
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AudioProcessingImplTest,
+ Agc2FieldTrialParametrizedTest,
+ ::testing::Values(
+ // Full AGC1, TS disabled.
+ AudioProcessing::Config{
+ .transient_suppression = {.enabled = false},
+ .gain_controller1 =
+ {.enabled = true,
+ .analog_gain_controller = {.enabled = true,
+ .enable_digital_adaptive = true}},
+ .gain_controller2 = {.enabled = false}},
+ // Full AGC1, TS enabled.
+ AudioProcessing::Config{
+ .transient_suppression = {.enabled = true},
+ .gain_controller1 =
+ {.enabled = true,
+ .analog_gain_controller = {.enabled = true,
+ .enable_digital_adaptive = true}},
+ .gain_controller2 = {.enabled = false}},
+ // Hybrid AGC, TS disabled.
+ AudioProcessing::Config{
+ .transient_suppression = {.enabled = false},
+ .gain_controller1 =
+ {.enabled = true,
+ .analog_gain_controller = {.enabled = true,
+ .enable_digital_adaptive = false}},
+ .gain_controller2 = {.enabled = true,
+ .adaptive_digital = {.enabled = true}}},
+ // Hybrid AGC, TS enabled.
+ AudioProcessing::Config{
+ .transient_suppression = {.enabled = true},
+ .gain_controller1 =
+ {.enabled = true,
+ .analog_gain_controller = {.enabled = true,
+ .enable_digital_adaptive = false}},
+ .gain_controller2 = {.enabled = true,
+ .adaptive_digital = {.enabled = true}}}));
+
+TEST(AudioProcessingImplTest, CanDisableTransientSuppressor) {
+ constexpr AudioProcessing::Config kOriginal = {
+ .transient_suppression = {.enabled = false}};
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+ EXPECT_FALSE(adjusted.transient_suppression.enabled);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(kOriginal);
+ adjusted = apm->GetConfig();
+ EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
+}
+
+TEST(AudioProcessingImplTest, CanEnableTs) {
+ constexpr AudioProcessing::Config kOriginal = {
+ .transient_suppression = {.enabled = true}};
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+ EXPECT_TRUE(adjusted.transient_suppression.enabled);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(kOriginal);
+ adjusted = apm->GetConfig();
+ EXPECT_TRUE(adjusted.transient_suppression.enabled);
+}
+
+TEST(AudioProcessingImplTest, CanDisableTsWithAgc2FieldTrialDisabled) {
+ constexpr AudioProcessing::Config kOriginal = {
+ .transient_suppression = {.enabled = false}};
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Disabled/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+ EXPECT_FALSE(adjusted.transient_suppression.enabled);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(kOriginal);
+ adjusted = apm->GetConfig();
+ EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
+}
+
+TEST(AudioProcessingImplTest, CanEnableTsWithAgc2FieldTrialDisabled) {
+ constexpr AudioProcessing::Config kOriginal = {
+ .transient_suppression = {.enabled = true}};
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Disabled/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+ EXPECT_TRUE(adjusted.transient_suppression.enabled);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(kOriginal);
+ adjusted = apm->GetConfig();
+ EXPECT_TRUE(adjusted.transient_suppression.enabled);
+}
+
+TEST(AudioProcessingImplTest,
+ CanDisableTsWithAgc2FieldTrialEnabledAndUsageAllowed) {
+ constexpr AudioProcessing::Config kOriginal = {
+ .transient_suppression = {.enabled = false}};
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,"
+ "disallow_transient_suppressor_usage:false/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+ EXPECT_FALSE(adjusted.transient_suppression.enabled);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(kOriginal);
+ adjusted = apm->GetConfig();
+ EXPECT_FALSE(adjusted.transient_suppression.enabled);
+}
+
+TEST(AudioProcessingImplTest,
+ CanEnableTsWithAgc2FieldTrialEnabledAndUsageAllowed) {
+ constexpr AudioProcessing::Config kOriginal = {
+ .transient_suppression = {.enabled = true}};
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,"
+ "disallow_transient_suppressor_usage:false/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+ EXPECT_TRUE(adjusted.transient_suppression.enabled);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(kOriginal);
+ adjusted = apm->GetConfig();
+ EXPECT_TRUE(adjusted.transient_suppression.enabled);
+}
+
+TEST(AudioProcessingImplTest,
+ CannotEnableTsWithAgc2FieldTrialEnabledAndUsageDisallowed) {
+ constexpr AudioProcessing::Config kOriginal = {
+ .transient_suppression = {.enabled = true}};
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-Audio-GainController2/Enabled,"
+ "disallow_transient_suppressor_usage:true/");
+
+ // Test config application via `AudioProcessing` ctor.
+ auto adjusted =
+ AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+ EXPECT_FALSE(adjusted.transient_suppression.enabled);
+
+ // Test config application via `AudioProcessing::ApplyConfig()`.
+ auto apm = AudioProcessingBuilder().Create();
+ apm->ApplyConfig(kOriginal);
+ adjusted = apm->GetConfig();
+ EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
+}
+
+} // namespace webrtc