From e449805f42bde45141af03ae991165009510e563 Mon Sep 17 00:00:00 2001 From: Alessio Bazzica Date: Mon, 17 Dec 2018 09:44:06 +0100 Subject: [PATCH] APM unit test: echo path gain change events notified. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL adds two unit tests to make sure that, when an echo path gain change occurs, the echo canceller is notified. Such a change can be caused by (i) a pre-amplifier gain change or (ii) an analog gain change. Bug: webrtc:7494 Change-Id: Ia47cfbbc5694340cd3e760d8d3c3393f79897a9d Reviewed-on: https://webrtc-review.googlesource.com/c/111780 Commit-Queue: Alessio Bazzica Reviewed-by: Per Ã…hgren Reviewed-by: Karl Wiberg Cr-Commit-Position: refs/heads/master@{#26190} --- modules/audio_processing/BUILD.gn | 3 + .../audio_processing_impl_unittest.cc | 113 +++++++++++++++++- .../audio_processing/test/echo_control_mock.h | 33 +++++ 3 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 modules/audio_processing/test/echo_control_mock.h diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn index 9b59558f89..714ad4b8f1 100644 --- a/modules/audio_processing/BUILD.gn +++ b/modules/audio_processing/BUILD.gn @@ -459,6 +459,8 @@ if (rtc_include_tests) { ":audioproc_unittest_proto", ":runtime_settings_protobuf_utils", "../../api/audio:audio_frame_api", + "../../api/audio:echo_control", + "../../rtc_base:rtc_base_tests_utils", "../../rtc_base:rtc_task_queue", "aec_dump", "aec_dump:aec_dump_unittests", @@ -485,6 +487,7 @@ if (rtc_include_tests) { "test/echo_canceller_test_tools.cc", "test/echo_canceller_test_tools.h", "test/echo_canceller_test_tools_unittest.cc", + "test/echo_control_mock.h", "test/test_utils.h", "voice_detection_unittest.cc", ] diff --git a/modules/audio_processing/audio_processing_impl_unittest.cc b/modules/audio_processing/audio_processing_impl_unittest.cc index 8926f0223f..3a3133a788 100644 --- a/modules/audio_processing/audio_processing_impl_unittest.cc +++ b/modules/audio_processing/audio_processing_impl_unittest.cc @@ -10,18 +10,24 @@ #include "modules/audio_processing/audio_processing_impl.h" +#include + +#include "absl/memory/memory.h" #include "modules/audio_processing/include/audio_processing.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/refcountedobject.h" #include "rtc_base/scoped_ref_ptr.h" #include "test/gmock.h" #include "test/gtest.h" -using ::testing::Invoke; - namespace webrtc { namespace { +using ::testing::Invoke; +using ::testing::NotNull; + class MockInitialize : public AudioProcessingImpl { public: explicit MockInitialize(const webrtc::Config& config) @@ -36,6 +42,28 @@ class MockInitialize : public AudioProcessingImpl { MOCK_CONST_METHOD0(Release, rtc::RefCountReleaseStatus()); }; +// 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_(absl::make_unique()) {} + // Returns a pointer to the next MockEchoControl that this factory creates. + MockEchoControl* GetNext() const { return next_mock_.get(); } + std::unique_ptr Create(int sample_rate_hz) override { + std::unique_ptr mock = std::move(next_mock_); + next_mock_ = absl::make_unique(); + return mock; + } + + private: + std::unique_ptr next_mock_; +}; + void InitializeAudioFrame(size_t input_rate, size_t num_channels, AudioFrame* frame) { @@ -183,6 +211,87 @@ TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) { << "Frame should be amplified."; } +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 = absl::make_unique(); + const auto* echo_control_factory_ptr = echo_control_factory.get(); + + std::unique_ptr apm( + AudioProcessingBuilder() + .SetEchoControlFactory(std::move(echo_control_factory)) + .Create()); + apm->gain_control()->Enable(false); // Disable AGC. + apm->gain_control()->set_mode(GainControl::Mode::kFixedDigital); + webrtc::AudioProcessing::Config apm_config; + 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); + + AudioFrame frame; + constexpr int16_t kAudioLevel = 10000; + constexpr size_t kSampleRateHz = 48000; + constexpr size_t kNumChannels = 2; + InitializeAudioFrame(kSampleRateHz, kNumChannels, &frame); + FillFixedFrame(kAudioLevel, &frame); + + MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); + + EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1); + EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), false)).Times(1); + apm->ProcessStream(&frame); + + EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1); + EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), true)).Times(1); + apm->SetRuntimeSetting( + AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f)); + apm->ProcessStream(&frame); +} + +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 = absl::make_unique(); + const auto* echo_control_factory_ptr = echo_control_factory.get(); + + std::unique_ptr apm( + AudioProcessingBuilder() + .SetEchoControlFactory(std::move(echo_control_factory)) + .Create()); + apm->gain_control()->Enable(true); // Enable AGC. + apm->gain_control()->set_mode(GainControl::Mode::kAdaptiveAnalog); + webrtc::AudioProcessing::Config apm_config; + apm_config.gain_controller2.enabled = false; + apm_config.pre_amplifier.enabled = false; + apm->ApplyConfig(apm_config); + + AudioFrame frame; + constexpr int16_t kAudioLevel = 1000; + constexpr size_t kSampleRateHz = 48000; + constexpr size_t kNumChannels = 2; + InitializeAudioFrame(kSampleRateHz, kNumChannels, &frame); + FillFixedFrame(kAudioLevel, &frame); + + MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext(); + + const int initial_analog_gain = apm->gain_control()->stream_analog_level(); + EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1); + EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), false)).Times(1); + apm->ProcessStream(&frame); + + // Force an analog gain change if it did not happen. + if (initial_analog_gain == apm->gain_control()->stream_analog_level()) { + apm->gain_control()->set_stream_analog_level(initial_analog_gain + 1); + } + + EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1); + EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), true)).Times(1); + apm->ProcessStream(&frame); +} + TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) { // Make sure that signal changes caused by a render pre-processing sub-module // take place before any echo detector analysis. diff --git a/modules/audio_processing/test/echo_control_mock.h b/modules/audio_processing/test/echo_control_mock.h new file mode 100644 index 0000000000..0386762ed4 --- /dev/null +++ b/modules/audio_processing/test/echo_control_mock.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018 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. + */ + +#ifndef MODULES_AUDIO_PROCESSING_TEST_ECHO_CONTROL_MOCK_H_ +#define MODULES_AUDIO_PROCESSING_TEST_ECHO_CONTROL_MOCK_H_ + +#include "api/audio/echo_control.h" +#include "test/gmock.h" + +namespace webrtc { + +class AudioBuffer; + +class MockEchoControl : public EchoControl { + public: + MOCK_METHOD1(AnalyzeRender, void(AudioBuffer* render)); + MOCK_METHOD1(AnalyzeCapture, void(AudioBuffer* capture)); + MOCK_METHOD2(ProcessCapture, + void(AudioBuffer* capture, bool echo_path_change)); + MOCK_CONST_METHOD0(GetMetrics, EchoControl::Metrics()); + MOCK_METHOD1(SetAudioBufferDelay, void(size_t delay_ms)); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_TEST_ECHO_CONTROL_MOCK_H_