blob: d688db0274061b56878bdca65563acf3cbd56111 [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
Alessio Bazzicae4498052018-12-17 09:44:06 +010013#include <memory>
14
15#include "absl/memory/memory.h"
Mirko Bonadeid9708072019-01-25 20:26:48 +010016#include "api/scoped_refptr.h"
Alessio Bazzicad2b97402018-08-09 14:23:11 +020017#include "modules/audio_processing/include/audio_processing.h"
Alessio Bazzicae4498052018-12-17 09:44:06 +010018#include "modules/audio_processing/test/echo_control_mock.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "modules/audio_processing/test/test_utils.h"
Alessio Bazzicae4498052018-12-17 09:44:06 +010020#include "rtc_base/checks.h"
Steve Anton10542f22019-01-11 09:11:00 -080021#include "rtc_base/ref_counted_object.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "test/gmock.h"
23#include "test/gtest.h"
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000024
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000025namespace webrtc {
peaha9cc40b2017-06-29 08:32:09 -070026namespace {
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000027
Alessio Bazzicae4498052018-12-17 09:44:06 +010028using ::testing::Invoke;
29using ::testing::NotNull;
30
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000031class MockInitialize : public AudioProcessingImpl {
32 public:
peah88ac8532016-09-12 16:47:25 -070033 explicit MockInitialize(const webrtc::Config& config)
34 : AudioProcessingImpl(config) {}
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000035
andrew@webrtc.orge84978f2014-01-25 02:09:06 +000036 MOCK_METHOD0(InitializeLocked, int());
danilchap56359be2017-09-07 07:53:45 -070037 int RealInitializeLocked() RTC_NO_THREAD_SAFETY_ANALYSIS {
pbos@webrtc.org788acd12014-12-15 09:41:24 +000038 return AudioProcessingImpl::InitializeLocked();
39 }
peaha9cc40b2017-06-29 08:32:09 -070040
Niels Möller6f72f562017-10-19 13:15:17 +020041 MOCK_CONST_METHOD0(AddRef, void());
42 MOCK_CONST_METHOD0(Release, rtc::RefCountReleaseStatus());
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000043};
44
Alessio Bazzicae4498052018-12-17 09:44:06 +010045// Creates MockEchoControl instances and provides a raw pointer access to
46// the next created one. The raw pointer is meant to be used with gmock.
47// Returning a pointer of the next created MockEchoControl instance is necessary
48// for the following reasons: (i) gmock expectations must be set before any call
49// occurs, (ii) APM is initialized the first time that
50// AudioProcessingImpl::ProcessStream() is called and the initialization leads
51// to the creation of a new EchoControl object.
52class MockEchoControlFactory : public EchoControlFactory {
53 public:
54 MockEchoControlFactory() : next_mock_(absl::make_unique<MockEchoControl>()) {}
55 // Returns a pointer to the next MockEchoControl that this factory creates.
56 MockEchoControl* GetNext() const { return next_mock_.get(); }
57 std::unique_ptr<EchoControl> Create(int sample_rate_hz) override {
58 std::unique_ptr<EchoControl> mock = std::move(next_mock_);
59 next_mock_ = absl::make_unique<MockEchoControl>();
60 return mock;
61 }
62
63 private:
64 std::unique_ptr<MockEchoControl> next_mock_;
65};
66
Alessio Bazzicad2b97402018-08-09 14:23:11 +020067void InitializeAudioFrame(size_t input_rate,
68 size_t num_channels,
69 AudioFrame* frame) {
Alex Loikob5c9a792018-04-16 16:31:22 +020070 const size_t samples_per_input_channel = rtc::CheckedDivExact(
71 input_rate, static_cast<size_t>(rtc::CheckedDivExact(
72 1000, AudioProcessing::kChunkSizeMs)));
Alex Loikob5c9a792018-04-16 16:31:22 +020073 RTC_DCHECK_LE(samples_per_input_channel * num_channels,
74 AudioFrame::kMaxDataSizeSamples);
Alessio Bazzicad2b97402018-08-09 14:23:11 +020075 frame->samples_per_channel_ = samples_per_input_channel;
76 frame->sample_rate_hz_ = input_rate;
77 frame->num_channels_ = num_channels;
78}
79
80void FillFixedFrame(int16_t audio_level, AudioFrame* frame) {
81 const size_t num_samples = frame->samples_per_channel_ * frame->num_channels_;
82 for (size_t i = 0; i < num_samples; ++i) {
83 frame->mutable_data()[i] = audio_level;
Alex Loikob5c9a792018-04-16 16:31:22 +020084 }
85}
86
Alessio Bazzicad2b97402018-08-09 14:23:11 +020087// Mocks EchoDetector and records the first samples of the last analyzed render
88// stream frame. Used to check what data is read by an EchoDetector
89// implementation injected into an APM.
90class TestEchoDetector : public EchoDetector {
91 public:
92 TestEchoDetector()
93 : analyze_render_audio_called_(false),
94 last_render_audio_first_sample_(0.f) {}
95 ~TestEchoDetector() override = default;
96 void AnalyzeRenderAudio(rtc::ArrayView<const float> render_audio) override {
97 last_render_audio_first_sample_ = render_audio[0];
98 analyze_render_audio_called_ = true;
99 }
100 void AnalyzeCaptureAudio(rtc::ArrayView<const float> capture_audio) override {
101 }
102 void Initialize(int capture_sample_rate_hz,
103 int num_capture_channels,
104 int render_sample_rate_hz,
105 int num_render_channels) override {}
106 EchoDetector::Metrics GetMetrics() const override { return {}; }
107 // Returns true if AnalyzeRenderAudio() has been called at least once.
108 bool analyze_render_audio_called() const {
109 return analyze_render_audio_called_;
110 }
111 // Returns the first sample of the last analyzed render frame.
112 float last_render_audio_first_sample() const {
113 return last_render_audio_first_sample_;
114 }
115
116 private:
117 bool analyze_render_audio_called_;
118 float last_render_audio_first_sample_;
119};
120
121// Mocks CustomProcessing and applies ProcessSample() to all the samples.
122// Meant to be injected into an APM to modify samples in a known and detectable
123// way.
124class TestRenderPreProcessor : public CustomProcessing {
125 public:
126 TestRenderPreProcessor() = default;
127 ~TestRenderPreProcessor() = default;
128 void Initialize(int sample_rate_hz, int num_channels) override {}
129 void Process(AudioBuffer* audio) override {
130 for (size_t k = 0; k < audio->num_channels(); ++k) {
Steve Antonf254e9e2019-08-21 17:52:28 +0000131 rtc::ArrayView<float> channel_view(audio->channels_f()[k],
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200132 audio->num_frames());
133 std::transform(channel_view.begin(), channel_view.end(),
134 channel_view.begin(), ProcessSample);
135 }
Mirko Bonadeic4dd7302019-02-25 09:12:02 +0100136 }
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200137 std::string ToString() const override { return "TestRenderPreProcessor"; }
138 void SetRuntimeSetting(AudioProcessing::RuntimeSetting setting) override {}
139 // Modifies a sample. This member is used in Process() to modify a frame and
140 // it is publicly visible to enable tests.
141 static constexpr float ProcessSample(float x) { return 2.f * x; }
142};
143
peaha9cc40b2017-06-29 08:32:09 -0700144} // namespace
145
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000146TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
peah88ac8532016-09-12 16:47:25 -0700147 webrtc::Config config;
andrew@webrtc.orge84978f2014-01-25 02:09:06 +0000148 MockInitialize mock(config);
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000149 ON_CALL(mock, InitializeLocked())
150 .WillByDefault(Invoke(&mock, &MockInitialize::RealInitializeLocked));
151
152 EXPECT_CALL(mock, InitializeLocked()).Times(1);
153 mock.Initialize();
154
155 AudioFrame frame;
peah2ace3f92016-09-10 04:42:27 -0700156 // Call with the default parameters; there should be an init.
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000157 frame.num_channels_ = 1;
158 SetFrameSampleRate(&frame, 16000);
Per Ã…hgren4bdced52017-06-27 16:00:38 +0200159 EXPECT_CALL(mock, InitializeLocked()).Times(0);
andrew@webrtc.orga8b97372014-03-10 22:26:12 +0000160 EXPECT_NOERR(mock.ProcessStream(&frame));
aluebsb0319552016-03-17 20:39:53 -0700161 EXPECT_NOERR(mock.ProcessReverseStream(&frame));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000162
163 // New sample rate. (Only impacts ProcessStream).
164 SetFrameSampleRate(&frame, 32000);
Yves Gerey665174f2018-06-19 15:03:05 +0200165 EXPECT_CALL(mock, InitializeLocked()).Times(1);
andrew@webrtc.orga8b97372014-03-10 22:26:12 +0000166 EXPECT_NOERR(mock.ProcessStream(&frame));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000167
168 // New number of channels.
peah2ace3f92016-09-10 04:42:27 -0700169 // TODO(peah): Investigate why this causes 2 inits.
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000170 frame.num_channels_ = 2;
Yves Gerey665174f2018-06-19 15:03:05 +0200171 EXPECT_CALL(mock, InitializeLocked()).Times(2);
andrew@webrtc.orga8b97372014-03-10 22:26:12 +0000172 EXPECT_NOERR(mock.ProcessStream(&frame));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000173 // ProcessStream sets num_channels_ == num_output_channels.
174 frame.num_channels_ = 2;
aluebsb0319552016-03-17 20:39:53 -0700175 EXPECT_NOERR(mock.ProcessReverseStream(&frame));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000176
aluebsb0319552016-03-17 20:39:53 -0700177 // A new sample rate passed to ProcessReverseStream should cause an init.
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000178 SetFrameSampleRate(&frame, 16000);
Alex Luebs5b830fe2016-03-08 17:52:52 +0100179 EXPECT_CALL(mock, InitializeLocked()).Times(1);
aluebsb0319552016-03-17 20:39:53 -0700180 EXPECT_NOERR(mock.ProcessReverseStream(&frame));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000181}
182
Alessio Bazzicac054e782018-04-16 12:10:09 +0200183TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
Alex Loikob5c9a792018-04-16 16:31:22 +0200184 std::unique_ptr<AudioProcessing> apm(AudioProcessingBuilder().Create());
185 webrtc::AudioProcessing::Config apm_config;
186 apm_config.pre_amplifier.enabled = true;
187 apm_config.pre_amplifier.fixed_gain_factor = 1.f;
188 apm->ApplyConfig(apm_config);
189
190 AudioFrame frame;
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200191 constexpr int16_t kAudioLevel = 10000;
192 constexpr size_t kSampleRateHz = 48000;
193 constexpr size_t kNumChannels = 2;
194 InitializeAudioFrame(kSampleRateHz, kNumChannels, &frame);
Alex Loikob5c9a792018-04-16 16:31:22 +0200195
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200196 FillFixedFrame(kAudioLevel, &frame);
Alex Loikob5c9a792018-04-16 16:31:22 +0200197 apm->ProcessStream(&frame);
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200198 EXPECT_EQ(frame.data()[100], kAudioLevel)
Alex Loikob5c9a792018-04-16 16:31:22 +0200199 << "With factor 1, frame shouldn't be modified.";
200
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200201 constexpr float kGainFactor = 2.f;
Alex Loikob5c9a792018-04-16 16:31:22 +0200202 apm->SetRuntimeSetting(
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200203 AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor));
Alex Loikob5c9a792018-04-16 16:31:22 +0200204
205 // Process for two frames to have time to ramp up gain.
206 for (int i = 0; i < 2; ++i) {
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200207 FillFixedFrame(kAudioLevel, &frame);
Alex Loikob5c9a792018-04-16 16:31:22 +0200208 apm->ProcessStream(&frame);
209 }
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200210 EXPECT_EQ(frame.data()[100], kGainFactor * kAudioLevel)
Alex Loikob5c9a792018-04-16 16:31:22 +0200211 << "Frame should be amplified.";
Alessio Bazzicac054e782018-04-16 12:10:09 +0200212}
213
Alessio Bazzicae4498052018-12-17 09:44:06 +0100214TEST(AudioProcessingImplTest,
215 EchoControllerObservesPreAmplifierEchoPathGainChange) {
216 // Tests that the echo controller observes an echo path gain change when the
217 // pre-amplifier submodule changes the gain.
218 auto echo_control_factory = absl::make_unique<MockEchoControlFactory>();
219 const auto* echo_control_factory_ptr = echo_control_factory.get();
220
221 std::unique_ptr<AudioProcessing> apm(
222 AudioProcessingBuilder()
223 .SetEchoControlFactory(std::move(echo_control_factory))
224 .Create());
225 apm->gain_control()->Enable(false); // Disable AGC.
226 apm->gain_control()->set_mode(GainControl::Mode::kFixedDigital);
227 webrtc::AudioProcessing::Config apm_config;
228 apm_config.gain_controller2.enabled = false;
229 apm_config.pre_amplifier.enabled = true;
230 apm_config.pre_amplifier.fixed_gain_factor = 1.f;
231 apm->ApplyConfig(apm_config);
232
233 AudioFrame frame;
234 constexpr int16_t kAudioLevel = 10000;
235 constexpr size_t kSampleRateHz = 48000;
236 constexpr size_t kNumChannels = 2;
237 InitializeAudioFrame(kSampleRateHz, kNumChannels, &frame);
238 FillFixedFrame(kAudioLevel, &frame);
239
240 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
241
242 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1);
Fredrik Hernqvistbf47f342019-05-09 10:50:31 +0200243 EXPECT_CALL(*echo_control_mock,
244 ProcessCapture(NotNull(), /*echo_path_change=*/false))
245 .Times(1);
Alessio Bazzicae4498052018-12-17 09:44:06 +0100246 apm->ProcessStream(&frame);
247
248 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1);
Fredrik Hernqvistbf47f342019-05-09 10:50:31 +0200249 EXPECT_CALL(*echo_control_mock,
250 ProcessCapture(NotNull(), /*echo_path_change=*/true))
251 .Times(1);
Alessio Bazzicae4498052018-12-17 09:44:06 +0100252 apm->SetRuntimeSetting(
253 AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
254 apm->ProcessStream(&frame);
255}
256
257TEST(AudioProcessingImplTest,
258 EchoControllerObservesAnalogAgc1EchoPathGainChange) {
259 // Tests that the echo controller observes an echo path gain change when the
260 // AGC1 analog adaptive submodule changes the analog gain.
261 auto echo_control_factory = absl::make_unique<MockEchoControlFactory>();
262 const auto* echo_control_factory_ptr = echo_control_factory.get();
263
264 std::unique_ptr<AudioProcessing> apm(
265 AudioProcessingBuilder()
266 .SetEchoControlFactory(std::move(echo_control_factory))
267 .Create());
268 apm->gain_control()->Enable(true); // Enable AGC.
269 apm->gain_control()->set_mode(GainControl::Mode::kAdaptiveAnalog);
270 webrtc::AudioProcessing::Config apm_config;
271 apm_config.gain_controller2.enabled = false;
272 apm_config.pre_amplifier.enabled = false;
273 apm->ApplyConfig(apm_config);
274
275 AudioFrame frame;
276 constexpr int16_t kAudioLevel = 1000;
277 constexpr size_t kSampleRateHz = 48000;
278 constexpr size_t kNumChannels = 2;
279 InitializeAudioFrame(kSampleRateHz, kNumChannels, &frame);
280 FillFixedFrame(kAudioLevel, &frame);
281
282 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
283
284 const int initial_analog_gain = apm->gain_control()->stream_analog_level();
285 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1);
286 EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), false)).Times(1);
287 apm->ProcessStream(&frame);
288
289 // Force an analog gain change if it did not happen.
290 if (initial_analog_gain == apm->gain_control()->stream_analog_level()) {
291 apm->gain_control()->set_stream_analog_level(initial_analog_gain + 1);
292 }
293
294 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1);
295 EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), true)).Times(1);
296 apm->ProcessStream(&frame);
297}
298
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200299TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) {
300 // Tests that the echo controller observes an echo path gain change when a
301 // playout volume change is reported.
302 auto echo_control_factory = absl::make_unique<MockEchoControlFactory>();
303 const auto* echo_control_factory_ptr = echo_control_factory.get();
304
305 std::unique_ptr<AudioProcessing> apm(
306 AudioProcessingBuilder()
307 .SetEchoControlFactory(std::move(echo_control_factory))
308 .Create());
309 apm->gain_control()->Enable(false); // Disable AGC.
310 apm->gain_control()->set_mode(GainControl::Mode::kFixedDigital);
311
312 AudioFrame frame;
313 constexpr int16_t kAudioLevel = 10000;
314 constexpr size_t kSampleRateHz = 48000;
315 constexpr size_t kNumChannels = 2;
316 InitializeAudioFrame(kSampleRateHz, kNumChannels, &frame);
317 FillFixedFrame(kAudioLevel, &frame);
318
319 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
320
321 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1);
322 EXPECT_CALL(*echo_control_mock,
323 ProcessCapture(NotNull(), /*echo_path_change=*/false))
324 .Times(1);
325 apm->ProcessStream(&frame);
326
327 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1);
328 EXPECT_CALL(*echo_control_mock,
329 ProcessCapture(NotNull(), /*echo_path_change=*/false))
330 .Times(1);
331 apm->SetRuntimeSetting(
332 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
333 apm->ProcessStream(&frame);
334
335 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1);
336 EXPECT_CALL(*echo_control_mock,
337 ProcessCapture(NotNull(), /*echo_path_change=*/false))
338 .Times(1);
339 apm->SetRuntimeSetting(
340 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
341 apm->ProcessStream(&frame);
342
343 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(NotNull())).Times(1);
344 EXPECT_CALL(*echo_control_mock,
345 ProcessCapture(NotNull(), /*echo_path_change=*/true))
346 .Times(1);
347 apm->SetRuntimeSetting(
348 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(100));
349 apm->ProcessStream(&frame);
350}
351
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200352TEST(AudioProcessingImplTest, RenderPreProcessorBeforeEchoDetector) {
353 // Make sure that signal changes caused by a render pre-processing sub-module
354 // take place before any echo detector analysis.
355 rtc::scoped_refptr<TestEchoDetector> test_echo_detector(
356 new rtc::RefCountedObject<TestEchoDetector>());
357 std::unique_ptr<CustomProcessing> test_render_pre_processor(
358 new TestRenderPreProcessor());
359 // Create APM injecting the test echo detector and render pre-processor.
360 std::unique_ptr<AudioProcessing> apm(
361 AudioProcessingBuilder()
362 .SetEchoDetector(test_echo_detector)
363 .SetRenderPreProcessing(std::move(test_render_pre_processor))
364 .Create());
365 webrtc::AudioProcessing::Config apm_config;
366 apm_config.pre_amplifier.enabled = true;
367 apm_config.residual_echo_detector.enabled = true;
368 apm->ApplyConfig(apm_config);
369
370 constexpr int16_t kAudioLevel = 1000;
371 constexpr int kSampleRateHz = 16000;
372 constexpr size_t kNumChannels = 1;
373 AudioFrame frame;
374 InitializeAudioFrame(kSampleRateHz, kNumChannels, &frame);
375
376 constexpr float kAudioLevelFloat = static_cast<float>(kAudioLevel);
377 constexpr float kExpectedPreprocessedAudioLevel =
378 TestRenderPreProcessor::ProcessSample(kAudioLevelFloat);
379 ASSERT_NE(kAudioLevelFloat, kExpectedPreprocessedAudioLevel);
380
381 // Analyze a render stream frame.
382 FillFixedFrame(kAudioLevel, &frame);
383 ASSERT_EQ(AudioProcessing::Error::kNoError,
384 apm->ProcessReverseStream(&frame));
385 // Trigger a call to in EchoDetector::AnalyzeRenderAudio() via
386 // ProcessStream().
387 FillFixedFrame(kAudioLevel, &frame);
388 ASSERT_EQ(AudioProcessing::Error::kNoError, apm->ProcessStream(&frame));
389 // Regardless of how the call to in EchoDetector::AnalyzeRenderAudio() is
390 // triggered, the line below checks that the call has occurred. If not, the
391 // APM implementation may have changed and this test might need to be adapted.
392 ASSERT_TRUE(test_echo_detector->analyze_render_audio_called());
393 // Check that the data read in EchoDetector::AnalyzeRenderAudio() is that
394 // produced by the render pre-processor.
395 EXPECT_EQ(kExpectedPreprocessedAudioLevel,
396 test_echo_detector->last_render_audio_first_sample());
397}
398
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000399} // namespace webrtc