blob: 65de4d6a050c04cc20506e9dc6d976e94c86c7fc [file] [log] [blame]
andrew@webrtc.org60730cf2014-01-07 17:45:09 +00001/*
2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_processing/audio_processing_impl.h"
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000012
Per Åhgren2507f8c2020-03-19 12:33:29 +010013#include <array>
Alessio Bazzicae4498052018-12-17 09:44:06 +010014#include <memory>
15
Mirko Bonadeid9708072019-01-25 20:26:48 +010016#include "api/scoped_refptr.h"
Per Åhgren652ada52021-03-03 10:52:44 +000017#include "modules/audio_processing/common.h"
Alessio Bazzicad2b97402018-08-09 14:23:11 +020018#include "modules/audio_processing/include/audio_processing.h"
Sam Zackrissonb37e59d2020-04-27 08:39:33 +020019#include "modules/audio_processing/optionally_built_submodule_creators.h"
Per Åhgrencc73ed32020-04-26 23:56:17 +020020#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
Sam Zackrissonb37e59d2020-04-27 08:39:33 +020021#include "modules/audio_processing/test/echo_canceller_test_tools.h"
Alessio Bazzicae4498052018-12-17 09:44:06 +010022#include "modules/audio_processing/test/echo_control_mock.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "modules/audio_processing/test/test_utils.h"
Alessio Bazzicae4498052018-12-17 09:44:06 +010024#include "rtc_base/checks.h"
Sam Zackrissonb37e59d2020-04-27 08:39:33 +020025#include "rtc_base/random.h"
Steve Anton10542f22019-01-11 09:11:00 -080026#include "rtc_base/ref_counted_object.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020027#include "test/gmock.h"
28#include "test/gtest.h"
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000029
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000030namespace webrtc {
peaha9cc40b2017-06-29 08:32:09 -070031namespace {
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000032
Alessio Bazzicae4498052018-12-17 09:44:06 +010033using ::testing::Invoke;
34using ::testing::NotNull;
35
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000036class MockInitialize : public AudioProcessingImpl {
37 public:
peah88ac8532016-09-12 16:47:25 -070038 explicit MockInitialize(const webrtc::Config& config)
39 : AudioProcessingImpl(config) {}
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000040
Per Åhgren0ade9832020-09-01 23:57:20 +020041 MOCK_METHOD(void, InitializeLocked, (), (override));
42 void RealInitializeLocked() RTC_NO_THREAD_SAFETY_ANALYSIS {
43 AudioProcessingImpl::InitializeLocked();
pbos@webrtc.org788acd12014-12-15 09:41:24 +000044 }
peaha9cc40b2017-06-29 08:32:09 -070045
Danil Chapovalov704fb552020-05-18 15:10:15 +020046 MOCK_METHOD(void, AddRef, (), (const, override));
47 MOCK_METHOD(rtc::RefCountReleaseStatus, Release, (), (const, override));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000048};
49
Alessio Bazzicae4498052018-12-17 09:44:06 +010050// Creates MockEchoControl instances and provides a raw pointer access to
51// the next created one. The raw pointer is meant to be used with gmock.
52// Returning a pointer of the next created MockEchoControl instance is necessary
53// for the following reasons: (i) gmock expectations must be set before any call
54// occurs, (ii) APM is initialized the first time that
55// AudioProcessingImpl::ProcessStream() is called and the initialization leads
56// to the creation of a new EchoControl object.
57class MockEchoControlFactory : public EchoControlFactory {
58 public:
Mirko Bonadei317a1f02019-09-17 17:06:18 +020059 MockEchoControlFactory() : next_mock_(std::make_unique<MockEchoControl>()) {}
Alessio Bazzicae4498052018-12-17 09:44:06 +010060 // Returns a pointer to the next MockEchoControl that this factory creates.
61 MockEchoControl* GetNext() const { return next_mock_.get(); }
Per Åhgren4e5c7092019-11-01 20:44:11 +010062 std::unique_ptr<EchoControl> Create(int sample_rate_hz,
63 int num_render_channels,
64 int num_capture_channels) override {
Alessio Bazzicae4498052018-12-17 09:44:06 +010065 std::unique_ptr<EchoControl> mock = std::move(next_mock_);
Mirko Bonadei317a1f02019-09-17 17:06:18 +020066 next_mock_ = std::make_unique<MockEchoControl>();
Alessio Bazzicae4498052018-12-17 09:44:06 +010067 return mock;
68 }
69
70 private:
71 std::unique_ptr<MockEchoControl> next_mock_;
72};
73
Alessio Bazzicad2b97402018-08-09 14:23:11 +020074// Mocks EchoDetector and records the first samples of the last analyzed render
75// stream frame. Used to check what data is read by an EchoDetector
76// implementation injected into an APM.
77class TestEchoDetector : public EchoDetector {
78 public:
79 TestEchoDetector()
80 : analyze_render_audio_called_(false),
81 last_render_audio_first_sample_(0.f) {}
82 ~TestEchoDetector() override = default;
83 void AnalyzeRenderAudio(rtc::ArrayView<const float> render_audio) override {
84 last_render_audio_first_sample_ = render_audio[0];
85 analyze_render_audio_called_ = true;
86 }
87 void AnalyzeCaptureAudio(rtc::ArrayView<const float> capture_audio) override {
88 }
89 void Initialize(int capture_sample_rate_hz,
90 int num_capture_channels,
91 int render_sample_rate_hz,
92 int num_render_channels) override {}
93 EchoDetector::Metrics GetMetrics() const override { return {}; }
94 // Returns true if AnalyzeRenderAudio() has been called at least once.
95 bool analyze_render_audio_called() const {
96 return analyze_render_audio_called_;
97 }
98 // Returns the first sample of the last analyzed render frame.
99 float last_render_audio_first_sample() const {
100 return last_render_audio_first_sample_;
101 }
102
103 private:
104 bool analyze_render_audio_called_;
105 float last_render_audio_first_sample_;
106};
107
108// Mocks CustomProcessing and applies ProcessSample() to all the samples.
109// Meant to be injected into an APM to modify samples in a known and detectable
110// way.
111class TestRenderPreProcessor : public CustomProcessing {
112 public:
113 TestRenderPreProcessor() = default;
114 ~TestRenderPreProcessor() = default;
115 void Initialize(int sample_rate_hz, int num_channels) override {}
116 void Process(AudioBuffer* audio) override {
117 for (size_t k = 0; k < audio->num_channels(); ++k) {
Per Åhgrend47941e2019-08-22 11:51:13 +0200118 rtc::ArrayView<float> channel_view(audio->channels()[k],
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200119 audio->num_frames());
120 std::transform(channel_view.begin(), channel_view.end(),
121 channel_view.begin(), ProcessSample);
122 }
Mirko Bonadeic4dd7302019-02-25 09:12:02 +0100123 }
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200124 std::string ToString() const override { return "TestRenderPreProcessor"; }
125 void SetRuntimeSetting(AudioProcessing::RuntimeSetting setting) override {}
126 // Modifies a sample. This member is used in Process() to modify a frame and
127 // it is publicly visible to enable tests.
128 static constexpr float ProcessSample(float x) { return 2.f * x; }
129};
130
peaha9cc40b2017-06-29 08:32:09 -0700131} // namespace
132
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000133TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
Per Åhgren2507f8c2020-03-19 12:33:29 +0100134 webrtc::Config webrtc_config;
135 MockInitialize mock(webrtc_config);
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000136 ON_CALL(mock, InitializeLocked())
137 .WillByDefault(Invoke(&mock, &MockInitialize::RealInitializeLocked));
138
139 EXPECT_CALL(mock, InitializeLocked()).Times(1);
140 mock.Initialize();
141
Per Åhgren2507f8c2020-03-19 12:33:29 +0100142 constexpr size_t kMaxSampleRateHz = 32000;
143 constexpr size_t kMaxNumChannels = 2;
144 std::array<int16_t, kMaxNumChannels * kMaxSampleRateHz / 100> frame;
145 frame.fill(0);
146 StreamConfig config(16000, 1, /*has_keyboard=*/false);
peah2ace3f92016-09-10 04:42:27 -0700147 // Call with the default parameters; there should be an init.
Per Åhgren4bdced52017-06-27 16:00:38 +0200148 EXPECT_CALL(mock, InitializeLocked()).Times(0);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100149 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
Per Åhgren2507f8c2020-03-19 12:33:29 +0100150 EXPECT_NOERR(
151 mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000152
153 // New sample rate. (Only impacts ProcessStream).
Per Åhgren2507f8c2020-03-19 12:33:29 +0100154 config = StreamConfig(32000, 1, /*has_keyboard=*/false);
Yves Gerey665174f2018-06-19 15:03:05 +0200155 EXPECT_CALL(mock, InitializeLocked()).Times(1);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100156 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000157
158 // New number of channels.
peah2ace3f92016-09-10 04:42:27 -0700159 // TODO(peah): Investigate why this causes 2 inits.
Per Åhgren2507f8c2020-03-19 12:33:29 +0100160 config = StreamConfig(32000, 2, /*has_keyboard=*/false);
Yves Gerey665174f2018-06-19 15:03:05 +0200161 EXPECT_CALL(mock, InitializeLocked()).Times(2);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100162 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000163 // ProcessStream sets num_channels_ == num_output_channels.
Per Åhgren2507f8c2020-03-19 12:33:29 +0100164 EXPECT_NOERR(
165 mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000166
aluebsb0319552016-03-17 20:39:53 -0700167 // A new sample rate passed to ProcessReverseStream should cause an init.
Per Åhgren2507f8c2020-03-19 12:33:29 +0100168 config = StreamConfig(16000, 2, /*has_keyboard=*/false);
Alex Luebs5b830fe2016-03-08 17:52:52 +0100169 EXPECT_CALL(mock, InitializeLocked()).Times(1);
Per Åhgren2507f8c2020-03-19 12:33:29 +0100170 EXPECT_NOERR(
171 mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000172}
173
Alessio Bazzicac054e782018-04-16 12:10:09 +0200174TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
Per Åhgrencc73ed32020-04-26 23:56:17 +0200175 std::unique_ptr<AudioProcessing> apm(
176 AudioProcessingBuilderForTesting().Create());
Alex Loikob5c9a792018-04-16 16:31:22 +0200177 webrtc::AudioProcessing::Config apm_config;
178 apm_config.pre_amplifier.enabled = true;
179 apm_config.pre_amplifier.fixed_gain_factor = 1.f;
180 apm->ApplyConfig(apm_config);
181
Per Åhgren2507f8c2020-03-19 12:33:29 +0100182 constexpr int kSampleRateHz = 48000;
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200183 constexpr int16_t kAudioLevel = 10000;
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200184 constexpr size_t kNumChannels = 2;
Alex Loikob5c9a792018-04-16 16:31:22 +0200185
Per Åhgren2507f8c2020-03-19 12:33:29 +0100186 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
187 StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
188 frame.fill(kAudioLevel);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100189 apm->ProcessStream(frame.data(), config, config, frame.data());
Per Åhgren2507f8c2020-03-19 12:33:29 +0100190 EXPECT_EQ(frame[100], kAudioLevel)
Alex Loikob5c9a792018-04-16 16:31:22 +0200191 << "With factor 1, frame shouldn't be modified.";
192
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200193 constexpr float kGainFactor = 2.f;
Alex Loikob5c9a792018-04-16 16:31:22 +0200194 apm->SetRuntimeSetting(
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200195 AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor));
Alex Loikob5c9a792018-04-16 16:31:22 +0200196
197 // Process for two frames to have time to ramp up gain.
198 for (int i = 0; i < 2; ++i) {
Per Åhgren2507f8c2020-03-19 12:33:29 +0100199 frame.fill(kAudioLevel);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100200 apm->ProcessStream(frame.data(), config, config, frame.data());
Alex Loikob5c9a792018-04-16 16:31:22 +0200201 }
Per Åhgren2507f8c2020-03-19 12:33:29 +0100202 EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
Alex Loikob5c9a792018-04-16 16:31:22 +0200203 << "Frame should be amplified.";
Alessio Bazzicac054e782018-04-16 12:10:09 +0200204}
205
Per Åhgren652ada52021-03-03 10:52:44 +0000206TEST(AudioProcessingImplTest, EchoControllerObservesSetCaptureUsageChange) {
207 // Tests that the echo controller observes that the capture usage has been
208 // updated.
209 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
210 const MockEchoControlFactory* echo_control_factory_ptr =
211 echo_control_factory.get();
212
213 std::unique_ptr<AudioProcessing> apm(
214 AudioProcessingBuilderForTesting()
215 .SetEchoControlFactory(std::move(echo_control_factory))
216 .Create());
217
218 constexpr int16_t kAudioLevel = 10000;
219 constexpr int kSampleRateHz = 48000;
220 constexpr int kNumChannels = 2;
221 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
222 StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
223 frame.fill(kAudioLevel);
224
225 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
226
227 // Ensure that SetCaptureOutputUsage is not called when no runtime settings
228 // are passed.
229 EXPECT_CALL(*echo_control_mock, SetCaptureOutputUsage(testing::_)).Times(0);
230 apm->ProcessStream(frame.data(), config, config, frame.data());
231
232 // Ensure that SetCaptureOutputUsage is called with the right information when
233 // a runtime setting is passed.
234 EXPECT_CALL(*echo_control_mock,
235 SetCaptureOutputUsage(/*capture_output_used=*/false))
236 .Times(1);
237 EXPECT_TRUE(apm->PostRuntimeSetting(
238 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
239 /*capture_output_used=*/false)));
240 apm->ProcessStream(frame.data(), config, config, frame.data());
241
242 EXPECT_CALL(*echo_control_mock,
243 SetCaptureOutputUsage(/*capture_output_used=*/true))
244 .Times(1);
245 EXPECT_TRUE(apm->PostRuntimeSetting(
246 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
247 /*capture_output_used=*/true)));
248 apm->ProcessStream(frame.data(), config, config, frame.data());
249
250 // The number of positions to place items in the queue is equal to the queue
251 // size minus 1.
252 constexpr int kNumSlotsInQueue = RuntimeSettingQueueSize();
253
254 // Ensure that SetCaptureOutputUsage is called with the right information when
255 // many runtime settings are passed.
256 for (int k = 0; k < kNumSlotsInQueue - 1; ++k) {
257 EXPECT_TRUE(apm->PostRuntimeSetting(
258 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
259 /*capture_output_used=*/false)));
260 }
261 EXPECT_CALL(*echo_control_mock,
262 SetCaptureOutputUsage(/*capture_output_used=*/false))
263 .Times(kNumSlotsInQueue - 1);
264 apm->ProcessStream(frame.data(), config, config, frame.data());
265
266 // Ensure that SetCaptureOutputUsage is properly called with the fallback
267 // value when the runtime settings queue becomes full.
268 for (int k = 0; k < kNumSlotsInQueue; ++k) {
269 EXPECT_TRUE(apm->PostRuntimeSetting(
270 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
271 /*capture_output_used=*/false)));
272 }
273 EXPECT_FALSE(apm->PostRuntimeSetting(
274 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
275 /*capture_output_used=*/false)));
276 EXPECT_FALSE(apm->PostRuntimeSetting(
277 AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(
278 /*capture_output_used=*/false)));
279 EXPECT_CALL(*echo_control_mock,
280 SetCaptureOutputUsage(/*capture_output_used=*/false))
281 .Times(kNumSlotsInQueue);
282 EXPECT_CALL(*echo_control_mock,
283 SetCaptureOutputUsage(/*capture_output_used=*/true))
284 .Times(1);
285 apm->ProcessStream(frame.data(), config, config, frame.data());
286}
287
Alessio Bazzicae4498052018-12-17 09:44:06 +0100288TEST(AudioProcessingImplTest,
289 EchoControllerObservesPreAmplifierEchoPathGainChange) {
290 // Tests that the echo controller observes an echo path gain change when the
291 // pre-amplifier submodule changes the gain.
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200292 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
Alessio Bazzicae4498052018-12-17 09:44:06 +0100293 const auto* echo_control_factory_ptr = echo_control_factory.get();
294
295 std::unique_ptr<AudioProcessing> apm(
Per Åhgrencc73ed32020-04-26 23:56:17 +0200296 AudioProcessingBuilderForTesting()
Alessio Bazzicae4498052018-12-17 09:44:06 +0100297 .SetEchoControlFactory(std::move(echo_control_factory))
298 .Create());
Sam Zackrisson41478c72019-10-15 10:10:26 +0200299 // Disable AGC.
Alessio Bazzicae4498052018-12-17 09:44:06 +0100300 webrtc::AudioProcessing::Config apm_config;
Sam Zackrisson41478c72019-10-15 10:10:26 +0200301 apm_config.gain_controller1.enabled = false;
Alessio Bazzicae4498052018-12-17 09:44:06 +0100302 apm_config.gain_controller2.enabled = false;
303 apm_config.pre_amplifier.enabled = true;
304 apm_config.pre_amplifier.fixed_gain_factor = 1.f;
305 apm->ApplyConfig(apm_config);
306
Alessio Bazzicae4498052018-12-17 09:44:06 +0100307 constexpr int16_t kAudioLevel = 10000;
308 constexpr size_t kSampleRateHz = 48000;
309 constexpr size_t kNumChannels = 2;
Per Åhgren2507f8c2020-03-19 12:33:29 +0100310 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
311 StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
312 frame.fill(kAudioLevel);
Alessio Bazzicae4498052018-12-17 09:44:06 +0100313
314 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
315
Per Åhgren0aefbf02019-08-23 21:29:17 +0200316 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistbf47f342019-05-09 10:50:31 +0200317 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100318 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
Fredrik Hernqvistbf47f342019-05-09 10:50:31 +0200319 .Times(1);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100320 apm->ProcessStream(frame.data(), config, config, frame.data());
Alessio Bazzicae4498052018-12-17 09:44:06 +0100321
Per Åhgren0aefbf02019-08-23 21:29:17 +0200322 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistbf47f342019-05-09 10:50:31 +0200323 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100324 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
Fredrik Hernqvistbf47f342019-05-09 10:50:31 +0200325 .Times(1);
Alessio Bazzicae4498052018-12-17 09:44:06 +0100326 apm->SetRuntimeSetting(
327 AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
Per Åhgrendc5522b2020-03-19 14:55:58 +0100328 apm->ProcessStream(frame.data(), config, config, frame.data());
Alessio Bazzicae4498052018-12-17 09:44:06 +0100329}
330
331TEST(AudioProcessingImplTest,
332 EchoControllerObservesAnalogAgc1EchoPathGainChange) {
333 // Tests that the echo controller observes an echo path gain change when the
334 // AGC1 analog adaptive submodule changes the analog gain.
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200335 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
Alessio Bazzicae4498052018-12-17 09:44:06 +0100336 const auto* echo_control_factory_ptr = echo_control_factory.get();
337
338 std::unique_ptr<AudioProcessing> apm(
Per Åhgrencc73ed32020-04-26 23:56:17 +0200339 AudioProcessingBuilderForTesting()
Alessio Bazzicae4498052018-12-17 09:44:06 +0100340 .SetEchoControlFactory(std::move(echo_control_factory))
341 .Create());
Alessio Bazzicae4498052018-12-17 09:44:06 +0100342 webrtc::AudioProcessing::Config apm_config;
Sam Zackrisson41478c72019-10-15 10:10:26 +0200343 // Enable AGC1.
344 apm_config.gain_controller1.enabled = true;
345 apm_config.gain_controller1.mode =
346 AudioProcessing::Config::GainController1::kAdaptiveAnalog;
Alessio Bazzicae4498052018-12-17 09:44:06 +0100347 apm_config.gain_controller2.enabled = false;
348 apm_config.pre_amplifier.enabled = false;
349 apm->ApplyConfig(apm_config);
350
Alessio Bazzicae4498052018-12-17 09:44:06 +0100351 constexpr int16_t kAudioLevel = 1000;
352 constexpr size_t kSampleRateHz = 48000;
353 constexpr size_t kNumChannels = 2;
Per Åhgren2507f8c2020-03-19 12:33:29 +0100354 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
355 StreamConfig stream_config(kSampleRateHz, kNumChannels,
356 /*has_keyboard=*/false);
357 frame.fill(kAudioLevel);
Alessio Bazzicae4498052018-12-17 09:44:06 +0100358
359 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
360
Sam Zackrisson41478c72019-10-15 10:10:26 +0200361 const int initial_analog_gain = apm->recommended_stream_analog_level();
Per Åhgren0aefbf02019-08-23 21:29:17 +0200362 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100363 EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), testing::_, false))
364 .Times(1);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100365 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Alessio Bazzicae4498052018-12-17 09:44:06 +0100366
367 // Force an analog gain change if it did not happen.
Sam Zackrisson41478c72019-10-15 10:10:26 +0200368 if (initial_analog_gain == apm->recommended_stream_analog_level()) {
369 apm->set_stream_analog_level(initial_analog_gain + 1);
Alessio Bazzicae4498052018-12-17 09:44:06 +0100370 }
371
Per Åhgren0aefbf02019-08-23 21:29:17 +0200372 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100373 EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), testing::_, true))
374 .Times(1);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100375 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Alessio Bazzicae4498052018-12-17 09:44:06 +0100376}
377
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200378TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) {
379 // Tests that the echo controller observes an echo path gain change when a
380 // playout volume change is reported.
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200381 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200382 const auto* echo_control_factory_ptr = echo_control_factory.get();
383
384 std::unique_ptr<AudioProcessing> apm(
Per Åhgrencc73ed32020-04-26 23:56:17 +0200385 AudioProcessingBuilderForTesting()
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200386 .SetEchoControlFactory(std::move(echo_control_factory))
387 .Create());
Sam Zackrisson41478c72019-10-15 10:10:26 +0200388 // Disable AGC.
389 webrtc::AudioProcessing::Config apm_config;
390 apm_config.gain_controller1.enabled = false;
391 apm_config.gain_controller2.enabled = false;
392 apm->ApplyConfig(apm_config);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200393
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200394 constexpr int16_t kAudioLevel = 10000;
395 constexpr size_t kSampleRateHz = 48000;
396 constexpr size_t kNumChannels = 2;
Per Åhgren2507f8c2020-03-19 12:33:29 +0100397 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
398 StreamConfig stream_config(kSampleRateHz, kNumChannels,
399 /*has_keyboard=*/false);
400 frame.fill(kAudioLevel);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200401
402 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
403
Per Åhgren0aefbf02019-08-23 21:29:17 +0200404 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200405 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100406 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200407 .Times(1);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100408 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200409
Per Åhgren0aefbf02019-08-23 21:29:17 +0200410 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200411 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100412 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200413 .Times(1);
414 apm->SetRuntimeSetting(
415 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
Per Åhgrendc5522b2020-03-19 14:55:58 +0100416 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200417
Per Åhgren0aefbf02019-08-23 21:29:17 +0200418 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200419 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100420 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200421 .Times(1);
422 apm->SetRuntimeSetting(
423 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
Per Åhgrendc5522b2020-03-19 14:55:58 +0100424 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200425
Per Åhgren0aefbf02019-08-23 21:29:17 +0200426 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200427 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100428 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200429 .Times(1);
430 apm->SetRuntimeSetting(
431 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(100));
Per Åhgrendc5522b2020-03-19 14:55:58 +0100432 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200433}
434
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200435TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) {
436 // Make sure that signal changes caused by a render pre-processing sub-module
437 // take place before any echo detector analysis.
438 rtc::scoped_refptr<TestEchoDetector> test_echo_detector(
439 new rtc::RefCountedObject<TestEchoDetector>());
440 std::unique_ptr<CustomProcessing> test_render_pre_processor(
441 new TestRenderPreProcessor());
442 // Create APM injecting the test echo detector and render pre-processor.
443 std::unique_ptr<AudioProcessing> apm(
Per Åhgrencc73ed32020-04-26 23:56:17 +0200444 AudioProcessingBuilderForTesting()
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200445 .SetEchoDetector(test_echo_detector)
446 .SetRenderPreProcessing(std::move(test_render_pre_processor))
447 .Create());
448 webrtc::AudioProcessing::Config apm_config;
449 apm_config.pre_amplifier.enabled = true;
450 apm_config.residual_echo_detector.enabled = true;
451 apm->ApplyConfig(apm_config);
452
453 constexpr int16_t kAudioLevel = 1000;
454 constexpr int kSampleRateHz = 16000;
455 constexpr size_t kNumChannels = 1;
Sam Zackrisson12e319a2020-01-03 14:54:20 +0100456 // Explicitly initialize APM to ensure no render frames are discarded.
457 const ProcessingConfig processing_config = {{
458 {kSampleRateHz, kNumChannels, /*has_keyboard=*/false},
459 {kSampleRateHz, kNumChannels, /*has_keyboard=*/false},
460 {kSampleRateHz, kNumChannels, /*has_keyboard=*/false},
461 {kSampleRateHz, kNumChannels, /*has_keyboard=*/false},
462 }};
463 apm->Initialize(processing_config);
464
Per Åhgren2507f8c2020-03-19 12:33:29 +0100465 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
466 StreamConfig stream_config(kSampleRateHz, kNumChannels,
467 /*has_keyboard=*/false);
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200468
469 constexpr float kAudioLevelFloat = static_cast<float>(kAudioLevel);
470 constexpr float kExpectedPreprocessedAudioLevel =
471 TestRenderPreProcessor::ProcessSample(kAudioLevelFloat);
472 ASSERT_NE(kAudioLevelFloat, kExpectedPreprocessedAudioLevel);
473
474 // Analyze a render stream frame.
Per Åhgren2507f8c2020-03-19 12:33:29 +0100475 frame.fill(kAudioLevel);
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200476 ASSERT_EQ(AudioProcessing::Error::kNoError,
Per Åhgren2507f8c2020-03-19 12:33:29 +0100477 apm->ProcessReverseStream(frame.data(), stream_config,
478 stream_config, frame.data()));
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200479 // Trigger a call to in EchoDetector::AnalyzeRenderAudio() via
480 // ProcessStream().
Per Åhgren2507f8c2020-03-19 12:33:29 +0100481 frame.fill(kAudioLevel);
482 ASSERT_EQ(AudioProcessing::Error::kNoError,
483 apm->ProcessStream(frame.data(), stream_config, stream_config,
Per Åhgrendc5522b2020-03-19 14:55:58 +0100484 frame.data()));
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200485 // Regardless of how the call to in EchoDetector::AnalyzeRenderAudio() is
486 // triggered, the line below checks that the call has occurred. If not, the
487 // APM implementation may have changed and this test might need to be adapted.
488 ASSERT_TRUE(test_echo_detector->analyze_render_audio_called());
489 // Check that the data read in EchoDetector::AnalyzeRenderAudio() is that
490 // produced by the render pre-processor.
491 EXPECT_EQ(kExpectedPreprocessedAudioLevel,
492 test_echo_detector->last_render_audio_first_sample());
493}
494
Sam Zackrissonb37e59d2020-04-27 08:39:33 +0200495// Disabling build-optional submodules and trying to enable them via the APM
496// config should be bit-exact with running APM with said submodules disabled.
497// This mainly tests that SetCreateOptionalSubmodulesForTesting has an effect.
498TEST(ApmWithSubmodulesExcludedTest, BitexactWithDisabledModules) {
499 rtc::scoped_refptr<AudioProcessingImpl> apm =
500 new rtc::RefCountedObject<AudioProcessingImpl>(webrtc::Config());
501 ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
502
503 ApmSubmoduleCreationOverrides overrides;
504 overrides.transient_suppression = true;
505 apm->OverrideSubmoduleCreationForTesting(overrides);
506
507 AudioProcessing::Config apm_config = apm->GetConfig();
508 apm_config.transient_suppression.enabled = true;
509 apm->ApplyConfig(apm_config);
510
511 rtc::scoped_refptr<AudioProcessing> apm_reference =
512 AudioProcessingBuilder().Create();
513 apm_config = apm_reference->GetConfig();
514 apm_config.transient_suppression.enabled = false;
515 apm_reference->ApplyConfig(apm_config);
516
517 constexpr int kSampleRateHz = 16000;
518 constexpr int kNumChannels = 1;
519 std::array<float, kSampleRateHz / 100> buffer;
520 std::array<float, kSampleRateHz / 100> buffer_reference;
521 float* channel_pointers[] = {buffer.data()};
522 float* channel_pointers_reference[] = {buffer_reference.data()};
523 StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz,
524 /*num_channels=*/kNumChannels,
525 /*has_keyboard=*/false);
526 Random random_generator(2341U);
527 constexpr int kFramesToProcessPerConfiguration = 10;
528
529 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
530 RandomizeSampleVector(&random_generator, buffer);
531 std::copy(buffer.begin(), buffer.end(), buffer_reference.begin());
532 ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
533 channel_pointers),
534 kNoErr);
535 ASSERT_EQ(
536 apm_reference->ProcessStream(channel_pointers_reference, stream_config,
537 stream_config, channel_pointers_reference),
538 kNoErr);
539 for (int j = 0; j < kSampleRateHz / 100; ++j) {
540 EXPECT_EQ(buffer[j], buffer_reference[j]);
541 }
542 }
543}
544
545// Disable transient suppressor creation and run APM in ways that should trigger
546// calls to the transient suppressor API.
547TEST(ApmWithSubmodulesExcludedTest, ReinitializeTransientSuppressor) {
548 rtc::scoped_refptr<AudioProcessingImpl> apm =
549 new rtc::RefCountedObject<AudioProcessingImpl>(webrtc::Config());
550 ASSERT_EQ(apm->Initialize(), kNoErr);
551
552 ApmSubmoduleCreationOverrides overrides;
553 overrides.transient_suppression = true;
554 apm->OverrideSubmoduleCreationForTesting(overrides);
555
556 AudioProcessing::Config config = apm->GetConfig();
557 config.transient_suppression.enabled = true;
558 apm->ApplyConfig(config);
559 // 960 samples per frame: 10 ms of <= 48 kHz audio with <= 2 channels.
560 float buffer[960];
561 float* channel_pointers[] = {&buffer[0], &buffer[480]};
562 Random random_generator(2341U);
563 constexpr int kFramesToProcessPerConfiguration = 3;
564
565 StreamConfig initial_stream_config(/*sample_rate_hz=*/16000,
566 /*num_channels=*/1,
567 /*has_keyboard=*/false);
568 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
569 RandomizeSampleVector(&random_generator, buffer);
570 EXPECT_EQ(apm->ProcessStream(channel_pointers, initial_stream_config,
571 initial_stream_config, channel_pointers),
572 kNoErr);
573 }
574
575 StreamConfig stereo_stream_config(/*sample_rate_hz=*/16000,
576 /*num_channels=*/2,
577 /*has_keyboard=*/false);
578 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
579 RandomizeSampleVector(&random_generator, buffer);
580 EXPECT_EQ(apm->ProcessStream(channel_pointers, stereo_stream_config,
581 stereo_stream_config, channel_pointers),
582 kNoErr);
583 }
584
585 StreamConfig high_sample_rate_stream_config(/*sample_rate_hz=*/48000,
586 /*num_channels=*/1,
587 /*has_keyboard=*/false);
588 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
589 RandomizeSampleVector(&random_generator, buffer);
590 EXPECT_EQ(
591 apm->ProcessStream(channel_pointers, high_sample_rate_stream_config,
592 high_sample_rate_stream_config, channel_pointers),
593 kNoErr);
594 }
595
596 StreamConfig keyboard_stream_config(/*sample_rate_hz=*/16000,
597 /*num_channels=*/1,
598 /*has_keyboard=*/true);
599 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
600 RandomizeSampleVector(&random_generator, buffer);
601 EXPECT_EQ(apm->ProcessStream(channel_pointers, keyboard_stream_config,
602 keyboard_stream_config, channel_pointers),
603 kNoErr);
604 }
605}
606
607// Disable transient suppressor creation and run APM in ways that should trigger
608// calls to the transient suppressor API.
609TEST(ApmWithSubmodulesExcludedTest, ToggleTransientSuppressor) {
610 rtc::scoped_refptr<AudioProcessingImpl> apm =
611 new rtc::RefCountedObject<AudioProcessingImpl>(webrtc::Config());
612 ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
613
614 ApmSubmoduleCreationOverrides overrides;
615 overrides.transient_suppression = true;
616 apm->OverrideSubmoduleCreationForTesting(overrides);
617
618 // 960 samples per frame: 10 ms of <= 48 kHz audio with <= 2 channels.
619 float buffer[960];
620 float* channel_pointers[] = {&buffer[0], &buffer[480]};
621 Random random_generator(2341U);
622 constexpr int kFramesToProcessPerConfiguration = 3;
623 StreamConfig stream_config(/*sample_rate_hz=*/16000,
624 /*num_channels=*/1,
625 /*has_keyboard=*/false);
626
627 AudioProcessing::Config config = apm->GetConfig();
628 config.transient_suppression.enabled = true;
629 apm->ApplyConfig(config);
630 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
631 RandomizeSampleVector(&random_generator, buffer);
632 EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
633 channel_pointers),
634 kNoErr);
635 }
636
637 config = apm->GetConfig();
638 config.transient_suppression.enabled = false;
639 apm->ApplyConfig(config);
640 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
641 RandomizeSampleVector(&random_generator, buffer);
642 EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
643 channel_pointers),
644 kNoErr);
645 }
646
647 config = apm->GetConfig();
648 config.transient_suppression.enabled = true;
649 apm->ApplyConfig(config);
650 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
651 RandomizeSampleVector(&random_generator, buffer);
652 EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
653 channel_pointers),
654 kNoErr);
655 }
656}
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000657} // namespace webrtc