blob: 71352bc65abd815c9e58c29033ae2544f154f5a5 [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"
Alessio Bazzicad2b97402018-08-09 14:23:11 +020017#include "modules/audio_processing/include/audio_processing.h"
Sam Zackrissonb37e59d2020-04-27 08:39:33 +020018#include "modules/audio_processing/optionally_built_submodule_creators.h"
Per Åhgrencc73ed32020-04-26 23:56:17 +020019#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
Sam Zackrissonb37e59d2020-04-27 08:39:33 +020020#include "modules/audio_processing/test/echo_canceller_test_tools.h"
Alessio Bazzicae4498052018-12-17 09:44:06 +010021#include "modules/audio_processing/test/echo_control_mock.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "modules/audio_processing/test/test_utils.h"
Alessio Bazzicae4498052018-12-17 09:44:06 +010023#include "rtc_base/checks.h"
Sam Zackrissonb37e59d2020-04-27 08:39:33 +020024#include "rtc_base/random.h"
Steve Anton10542f22019-01-11 09:11:00 -080025#include "rtc_base/ref_counted_object.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026#include "test/gmock.h"
27#include "test/gtest.h"
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000028
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000029namespace webrtc {
peaha9cc40b2017-06-29 08:32:09 -070030namespace {
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000031
Alessio Bazzicae4498052018-12-17 09:44:06 +010032using ::testing::Invoke;
33using ::testing::NotNull;
34
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000035class MockInitialize : public AudioProcessingImpl {
36 public:
peah88ac8532016-09-12 16:47:25 -070037 explicit MockInitialize(const webrtc::Config& config)
38 : AudioProcessingImpl(config) {}
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000039
andrew@webrtc.orge84978f2014-01-25 02:09:06 +000040 MOCK_METHOD0(InitializeLocked, int());
danilchap56359be2017-09-07 07:53:45 -070041 int RealInitializeLocked() RTC_NO_THREAD_SAFETY_ANALYSIS {
pbos@webrtc.org788acd12014-12-15 09:41:24 +000042 return AudioProcessingImpl::InitializeLocked();
43 }
peaha9cc40b2017-06-29 08:32:09 -070044
Niels Möller6f72f562017-10-19 13:15:17 +020045 MOCK_CONST_METHOD0(AddRef, void());
46 MOCK_CONST_METHOD0(Release, rtc::RefCountReleaseStatus());
andrew@webrtc.org60730cf2014-01-07 17:45:09 +000047};
48
Alessio Bazzicae4498052018-12-17 09:44:06 +010049// Creates MockEchoControl instances and provides a raw pointer access to
50// the next created one. The raw pointer is meant to be used with gmock.
51// Returning a pointer of the next created MockEchoControl instance is necessary
52// for the following reasons: (i) gmock expectations must be set before any call
53// occurs, (ii) APM is initialized the first time that
54// AudioProcessingImpl::ProcessStream() is called and the initialization leads
55// to the creation of a new EchoControl object.
56class MockEchoControlFactory : public EchoControlFactory {
57 public:
Mirko Bonadei317a1f02019-09-17 17:06:18 +020058 MockEchoControlFactory() : next_mock_(std::make_unique<MockEchoControl>()) {}
Alessio Bazzicae4498052018-12-17 09:44:06 +010059 // Returns a pointer to the next MockEchoControl that this factory creates.
60 MockEchoControl* GetNext() const { return next_mock_.get(); }
Per Åhgren4e5c7092019-11-01 20:44:11 +010061 std::unique_ptr<EchoControl> Create(int sample_rate_hz,
62 int num_render_channels,
63 int num_capture_channels) override {
Alessio Bazzicae4498052018-12-17 09:44:06 +010064 std::unique_ptr<EchoControl> mock = std::move(next_mock_);
Mirko Bonadei317a1f02019-09-17 17:06:18 +020065 next_mock_ = std::make_unique<MockEchoControl>();
Alessio Bazzicae4498052018-12-17 09:44:06 +010066 return mock;
67 }
68
69 private:
70 std::unique_ptr<MockEchoControl> next_mock_;
71};
72
Alessio Bazzicad2b97402018-08-09 14:23:11 +020073// Mocks EchoDetector and records the first samples of the last analyzed render
74// stream frame. Used to check what data is read by an EchoDetector
75// implementation injected into an APM.
76class TestEchoDetector : public EchoDetector {
77 public:
78 TestEchoDetector()
79 : analyze_render_audio_called_(false),
80 last_render_audio_first_sample_(0.f) {}
81 ~TestEchoDetector() override = default;
82 void AnalyzeRenderAudio(rtc::ArrayView<const float> render_audio) override {
83 last_render_audio_first_sample_ = render_audio[0];
84 analyze_render_audio_called_ = true;
85 }
86 void AnalyzeCaptureAudio(rtc::ArrayView<const float> capture_audio) override {
87 }
88 void Initialize(int capture_sample_rate_hz,
89 int num_capture_channels,
90 int render_sample_rate_hz,
91 int num_render_channels) override {}
92 EchoDetector::Metrics GetMetrics() const override { return {}; }
93 // Returns true if AnalyzeRenderAudio() has been called at least once.
94 bool analyze_render_audio_called() const {
95 return analyze_render_audio_called_;
96 }
97 // Returns the first sample of the last analyzed render frame.
98 float last_render_audio_first_sample() const {
99 return last_render_audio_first_sample_;
100 }
101
102 private:
103 bool analyze_render_audio_called_;
104 float last_render_audio_first_sample_;
105};
106
107// Mocks CustomProcessing and applies ProcessSample() to all the samples.
108// Meant to be injected into an APM to modify samples in a known and detectable
109// way.
110class TestRenderPreProcessor : public CustomProcessing {
111 public:
112 TestRenderPreProcessor() = default;
113 ~TestRenderPreProcessor() = default;
114 void Initialize(int sample_rate_hz, int num_channels) override {}
115 void Process(AudioBuffer* audio) override {
116 for (size_t k = 0; k < audio->num_channels(); ++k) {
Per Åhgrend47941e2019-08-22 11:51:13 +0200117 rtc::ArrayView<float> channel_view(audio->channels()[k],
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200118 audio->num_frames());
119 std::transform(channel_view.begin(), channel_view.end(),
120 channel_view.begin(), ProcessSample);
121 }
Mirko Bonadeic4dd7302019-02-25 09:12:02 +0100122 }
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200123 std::string ToString() const override { return "TestRenderPreProcessor"; }
124 void SetRuntimeSetting(AudioProcessing::RuntimeSetting setting) override {}
125 // Modifies a sample. This member is used in Process() to modify a frame and
126 // it is publicly visible to enable tests.
127 static constexpr float ProcessSample(float x) { return 2.f * x; }
128};
129
peaha9cc40b2017-06-29 08:32:09 -0700130} // namespace
131
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000132TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
Per Åhgren2507f8c2020-03-19 12:33:29 +0100133 webrtc::Config webrtc_config;
134 MockInitialize mock(webrtc_config);
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000135 ON_CALL(mock, InitializeLocked())
136 .WillByDefault(Invoke(&mock, &MockInitialize::RealInitializeLocked));
137
138 EXPECT_CALL(mock, InitializeLocked()).Times(1);
139 mock.Initialize();
140
Per Åhgren2507f8c2020-03-19 12:33:29 +0100141 constexpr size_t kMaxSampleRateHz = 32000;
142 constexpr size_t kMaxNumChannels = 2;
143 std::array<int16_t, kMaxNumChannels * kMaxSampleRateHz / 100> frame;
144 frame.fill(0);
145 StreamConfig config(16000, 1, /*has_keyboard=*/false);
peah2ace3f92016-09-10 04:42:27 -0700146 // Call with the default parameters; there should be an init.
Per Åhgren4bdced52017-06-27 16:00:38 +0200147 EXPECT_CALL(mock, InitializeLocked()).Times(0);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100148 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
Per Åhgren2507f8c2020-03-19 12:33:29 +0100149 EXPECT_NOERR(
150 mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000151
152 // New sample rate. (Only impacts ProcessStream).
Per Åhgren2507f8c2020-03-19 12:33:29 +0100153 config = StreamConfig(32000, 1, /*has_keyboard=*/false);
Yves Gerey665174f2018-06-19 15:03:05 +0200154 EXPECT_CALL(mock, InitializeLocked()).Times(1);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100155 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000156
157 // New number of channels.
peah2ace3f92016-09-10 04:42:27 -0700158 // TODO(peah): Investigate why this causes 2 inits.
Per Åhgren2507f8c2020-03-19 12:33:29 +0100159 config = StreamConfig(32000, 2, /*has_keyboard=*/false);
Yves Gerey665174f2018-06-19 15:03:05 +0200160 EXPECT_CALL(mock, InitializeLocked()).Times(2);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100161 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000162 // ProcessStream sets num_channels_ == num_output_channels.
Per Åhgren2507f8c2020-03-19 12:33:29 +0100163 EXPECT_NOERR(
164 mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000165
aluebsb0319552016-03-17 20:39:53 -0700166 // A new sample rate passed to ProcessReverseStream should cause an init.
Per Åhgren2507f8c2020-03-19 12:33:29 +0100167 config = StreamConfig(16000, 2, /*has_keyboard=*/false);
Alex Luebs5b830fe2016-03-08 17:52:52 +0100168 EXPECT_CALL(mock, InitializeLocked()).Times(1);
Per Åhgren2507f8c2020-03-19 12:33:29 +0100169 EXPECT_NOERR(
170 mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000171}
172
Alessio Bazzicac054e782018-04-16 12:10:09 +0200173TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
Per Åhgrencc73ed32020-04-26 23:56:17 +0200174 std::unique_ptr<AudioProcessing> apm(
175 AudioProcessingBuilderForTesting().Create());
Alex Loikob5c9a792018-04-16 16:31:22 +0200176 webrtc::AudioProcessing::Config apm_config;
177 apm_config.pre_amplifier.enabled = true;
178 apm_config.pre_amplifier.fixed_gain_factor = 1.f;
179 apm->ApplyConfig(apm_config);
180
Per Åhgren2507f8c2020-03-19 12:33:29 +0100181 constexpr int kSampleRateHz = 48000;
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200182 constexpr int16_t kAudioLevel = 10000;
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200183 constexpr size_t kNumChannels = 2;
Alex Loikob5c9a792018-04-16 16:31:22 +0200184
Per Åhgren2507f8c2020-03-19 12:33:29 +0100185 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
186 StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
187 frame.fill(kAudioLevel);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100188 apm->ProcessStream(frame.data(), config, config, frame.data());
Per Åhgren2507f8c2020-03-19 12:33:29 +0100189 EXPECT_EQ(frame[100], kAudioLevel)
Alex Loikob5c9a792018-04-16 16:31:22 +0200190 << "With factor 1, frame shouldn't be modified.";
191
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200192 constexpr float kGainFactor = 2.f;
Alex Loikob5c9a792018-04-16 16:31:22 +0200193 apm->SetRuntimeSetting(
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200194 AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor));
Alex Loikob5c9a792018-04-16 16:31:22 +0200195
196 // Process for two frames to have time to ramp up gain.
197 for (int i = 0; i < 2; ++i) {
Per Åhgren2507f8c2020-03-19 12:33:29 +0100198 frame.fill(kAudioLevel);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100199 apm->ProcessStream(frame.data(), config, config, frame.data());
Alex Loikob5c9a792018-04-16 16:31:22 +0200200 }
Per Åhgren2507f8c2020-03-19 12:33:29 +0100201 EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
Alex Loikob5c9a792018-04-16 16:31:22 +0200202 << "Frame should be amplified.";
Alessio Bazzicac054e782018-04-16 12:10:09 +0200203}
204
Alessio Bazzicae4498052018-12-17 09:44:06 +0100205TEST(AudioProcessingImplTest,
206 EchoControllerObservesPreAmplifierEchoPathGainChange) {
207 // Tests that the echo controller observes an echo path gain change when the
208 // pre-amplifier submodule changes the gain.
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200209 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
Alessio Bazzicae4498052018-12-17 09:44:06 +0100210 const auto* echo_control_factory_ptr = echo_control_factory.get();
211
212 std::unique_ptr<AudioProcessing> apm(
Per Åhgrencc73ed32020-04-26 23:56:17 +0200213 AudioProcessingBuilderForTesting()
Alessio Bazzicae4498052018-12-17 09:44:06 +0100214 .SetEchoControlFactory(std::move(echo_control_factory))
215 .Create());
Sam Zackrisson41478c72019-10-15 10:10:26 +0200216 // Disable AGC.
Alessio Bazzicae4498052018-12-17 09:44:06 +0100217 webrtc::AudioProcessing::Config apm_config;
Sam Zackrisson41478c72019-10-15 10:10:26 +0200218 apm_config.gain_controller1.enabled = false;
Alessio Bazzicae4498052018-12-17 09:44:06 +0100219 apm_config.gain_controller2.enabled = false;
220 apm_config.pre_amplifier.enabled = true;
221 apm_config.pre_amplifier.fixed_gain_factor = 1.f;
222 apm->ApplyConfig(apm_config);
223
Alessio Bazzicae4498052018-12-17 09:44:06 +0100224 constexpr int16_t kAudioLevel = 10000;
225 constexpr size_t kSampleRateHz = 48000;
226 constexpr size_t kNumChannels = 2;
Per Åhgren2507f8c2020-03-19 12:33:29 +0100227 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
228 StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
229 frame.fill(kAudioLevel);
Alessio Bazzicae4498052018-12-17 09:44:06 +0100230
231 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
232
Per Åhgren0aefbf02019-08-23 21:29:17 +0200233 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistbf47f342019-05-09 10:50:31 +0200234 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100235 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
Fredrik Hernqvistbf47f342019-05-09 10:50:31 +0200236 .Times(1);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100237 apm->ProcessStream(frame.data(), config, config, frame.data());
Alessio Bazzicae4498052018-12-17 09:44:06 +0100238
Per Åhgren0aefbf02019-08-23 21:29:17 +0200239 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistbf47f342019-05-09 10:50:31 +0200240 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100241 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
Fredrik Hernqvistbf47f342019-05-09 10:50:31 +0200242 .Times(1);
Alessio Bazzicae4498052018-12-17 09:44:06 +0100243 apm->SetRuntimeSetting(
244 AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
Per Åhgrendc5522b2020-03-19 14:55:58 +0100245 apm->ProcessStream(frame.data(), config, config, frame.data());
Alessio Bazzicae4498052018-12-17 09:44:06 +0100246}
247
248TEST(AudioProcessingImplTest,
249 EchoControllerObservesAnalogAgc1EchoPathGainChange) {
250 // Tests that the echo controller observes an echo path gain change when the
251 // AGC1 analog adaptive submodule changes the analog gain.
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200252 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
Alessio Bazzicae4498052018-12-17 09:44:06 +0100253 const auto* echo_control_factory_ptr = echo_control_factory.get();
254
255 std::unique_ptr<AudioProcessing> apm(
Per Åhgrencc73ed32020-04-26 23:56:17 +0200256 AudioProcessingBuilderForTesting()
Alessio Bazzicae4498052018-12-17 09:44:06 +0100257 .SetEchoControlFactory(std::move(echo_control_factory))
258 .Create());
Alessio Bazzicae4498052018-12-17 09:44:06 +0100259 webrtc::AudioProcessing::Config apm_config;
Sam Zackrisson41478c72019-10-15 10:10:26 +0200260 // Enable AGC1.
261 apm_config.gain_controller1.enabled = true;
262 apm_config.gain_controller1.mode =
263 AudioProcessing::Config::GainController1::kAdaptiveAnalog;
Alessio Bazzicae4498052018-12-17 09:44:06 +0100264 apm_config.gain_controller2.enabled = false;
265 apm_config.pre_amplifier.enabled = false;
266 apm->ApplyConfig(apm_config);
267
Alessio Bazzicae4498052018-12-17 09:44:06 +0100268 constexpr int16_t kAudioLevel = 1000;
269 constexpr size_t kSampleRateHz = 48000;
270 constexpr size_t kNumChannels = 2;
Per Åhgren2507f8c2020-03-19 12:33:29 +0100271 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
272 StreamConfig stream_config(kSampleRateHz, kNumChannels,
273 /*has_keyboard=*/false);
274 frame.fill(kAudioLevel);
Alessio Bazzicae4498052018-12-17 09:44:06 +0100275
276 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
277
Sam Zackrisson41478c72019-10-15 10:10:26 +0200278 const int initial_analog_gain = apm->recommended_stream_analog_level();
Per Åhgren0aefbf02019-08-23 21:29:17 +0200279 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100280 EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), testing::_, false))
281 .Times(1);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100282 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Alessio Bazzicae4498052018-12-17 09:44:06 +0100283
284 // Force an analog gain change if it did not happen.
Sam Zackrisson41478c72019-10-15 10:10:26 +0200285 if (initial_analog_gain == apm->recommended_stream_analog_level()) {
286 apm->set_stream_analog_level(initial_analog_gain + 1);
Alessio Bazzicae4498052018-12-17 09:44:06 +0100287 }
288
Per Åhgren0aefbf02019-08-23 21:29:17 +0200289 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100290 EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), testing::_, true))
291 .Times(1);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100292 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Alessio Bazzicae4498052018-12-17 09:44:06 +0100293}
294
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200295TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) {
296 // Tests that the echo controller observes an echo path gain change when a
297 // playout volume change is reported.
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200298 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200299 const auto* echo_control_factory_ptr = echo_control_factory.get();
300
301 std::unique_ptr<AudioProcessing> apm(
Per Åhgrencc73ed32020-04-26 23:56:17 +0200302 AudioProcessingBuilderForTesting()
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200303 .SetEchoControlFactory(std::move(echo_control_factory))
304 .Create());
Sam Zackrisson41478c72019-10-15 10:10:26 +0200305 // Disable AGC.
306 webrtc::AudioProcessing::Config apm_config;
307 apm_config.gain_controller1.enabled = false;
308 apm_config.gain_controller2.enabled = false;
309 apm->ApplyConfig(apm_config);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200310
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200311 constexpr int16_t kAudioLevel = 10000;
312 constexpr size_t kSampleRateHz = 48000;
313 constexpr size_t kNumChannels = 2;
Per Åhgren2507f8c2020-03-19 12:33:29 +0100314 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
315 StreamConfig stream_config(kSampleRateHz, kNumChannels,
316 /*has_keyboard=*/false);
317 frame.fill(kAudioLevel);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200318
319 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
320
Per Åhgren0aefbf02019-08-23 21:29:17 +0200321 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200322 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100323 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200324 .Times(1);
Per Åhgrendc5522b2020-03-19 14:55:58 +0100325 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200326
Per Åhgren0aefbf02019-08-23 21:29:17 +0200327 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200328 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100329 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200330 .Times(1);
331 apm->SetRuntimeSetting(
332 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
Per Åhgrendc5522b2020-03-19 14:55:58 +0100333 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200334
Per Åhgren0aefbf02019-08-23 21:29:17 +0200335 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200336 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100337 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200338 .Times(1);
339 apm->SetRuntimeSetting(
340 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
Per Åhgrendc5522b2020-03-19 14:55:58 +0100341 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200342
Per Åhgren0aefbf02019-08-23 21:29:17 +0200343 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200344 EXPECT_CALL(*echo_control_mock,
Per Åhgrenc20a19c2019-11-13 11:12:29 +0100345 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200346 .Times(1);
347 apm->SetRuntimeSetting(
348 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(100));
Per Åhgrendc5522b2020-03-19 14:55:58 +0100349 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
Fredrik Hernqvistca362852019-05-10 15:50:02 +0200350}
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(
Per Åhgrencc73ed32020-04-26 23:56:17 +0200361 AudioProcessingBuilderForTesting()
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200362 .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;
Sam Zackrisson12e319a2020-01-03 14:54:20 +0100373 // Explicitly initialize APM to ensure no render frames are discarded.
374 const ProcessingConfig processing_config = {{
375 {kSampleRateHz, kNumChannels, /*has_keyboard=*/false},
376 {kSampleRateHz, kNumChannels, /*has_keyboard=*/false},
377 {kSampleRateHz, kNumChannels, /*has_keyboard=*/false},
378 {kSampleRateHz, kNumChannels, /*has_keyboard=*/false},
379 }};
380 apm->Initialize(processing_config);
381
Per Åhgren2507f8c2020-03-19 12:33:29 +0100382 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
383 StreamConfig stream_config(kSampleRateHz, kNumChannels,
384 /*has_keyboard=*/false);
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200385
386 constexpr float kAudioLevelFloat = static_cast<float>(kAudioLevel);
387 constexpr float kExpectedPreprocessedAudioLevel =
388 TestRenderPreProcessor::ProcessSample(kAudioLevelFloat);
389 ASSERT_NE(kAudioLevelFloat, kExpectedPreprocessedAudioLevel);
390
391 // Analyze a render stream frame.
Per Åhgren2507f8c2020-03-19 12:33:29 +0100392 frame.fill(kAudioLevel);
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200393 ASSERT_EQ(AudioProcessing::Error::kNoError,
Per Åhgren2507f8c2020-03-19 12:33:29 +0100394 apm->ProcessReverseStream(frame.data(), stream_config,
395 stream_config, frame.data()));
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200396 // Trigger a call to in EchoDetector::AnalyzeRenderAudio() via
397 // ProcessStream().
Per Åhgren2507f8c2020-03-19 12:33:29 +0100398 frame.fill(kAudioLevel);
399 ASSERT_EQ(AudioProcessing::Error::kNoError,
400 apm->ProcessStream(frame.data(), stream_config, stream_config,
Per Åhgrendc5522b2020-03-19 14:55:58 +0100401 frame.data()));
Alessio Bazzicad2b97402018-08-09 14:23:11 +0200402 // Regardless of how the call to in EchoDetector::AnalyzeRenderAudio() is
403 // triggered, the line below checks that the call has occurred. If not, the
404 // APM implementation may have changed and this test might need to be adapted.
405 ASSERT_TRUE(test_echo_detector->analyze_render_audio_called());
406 // Check that the data read in EchoDetector::AnalyzeRenderAudio() is that
407 // produced by the render pre-processor.
408 EXPECT_EQ(kExpectedPreprocessedAudioLevel,
409 test_echo_detector->last_render_audio_first_sample());
410}
411
Sam Zackrissonb37e59d2020-04-27 08:39:33 +0200412// Disabling build-optional submodules and trying to enable them via the APM
413// config should be bit-exact with running APM with said submodules disabled.
414// This mainly tests that SetCreateOptionalSubmodulesForTesting has an effect.
415TEST(ApmWithSubmodulesExcludedTest, BitexactWithDisabledModules) {
416 rtc::scoped_refptr<AudioProcessingImpl> apm =
417 new rtc::RefCountedObject<AudioProcessingImpl>(webrtc::Config());
418 ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
419
420 ApmSubmoduleCreationOverrides overrides;
421 overrides.transient_suppression = true;
422 apm->OverrideSubmoduleCreationForTesting(overrides);
423
424 AudioProcessing::Config apm_config = apm->GetConfig();
425 apm_config.transient_suppression.enabled = true;
426 apm->ApplyConfig(apm_config);
427
428 rtc::scoped_refptr<AudioProcessing> apm_reference =
429 AudioProcessingBuilder().Create();
430 apm_config = apm_reference->GetConfig();
431 apm_config.transient_suppression.enabled = false;
432 apm_reference->ApplyConfig(apm_config);
433
434 constexpr int kSampleRateHz = 16000;
435 constexpr int kNumChannels = 1;
436 std::array<float, kSampleRateHz / 100> buffer;
437 std::array<float, kSampleRateHz / 100> buffer_reference;
438 float* channel_pointers[] = {buffer.data()};
439 float* channel_pointers_reference[] = {buffer_reference.data()};
440 StreamConfig stream_config(/*sample_rate_hz=*/kSampleRateHz,
441 /*num_channels=*/kNumChannels,
442 /*has_keyboard=*/false);
443 Random random_generator(2341U);
444 constexpr int kFramesToProcessPerConfiguration = 10;
445
446 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
447 RandomizeSampleVector(&random_generator, buffer);
448 std::copy(buffer.begin(), buffer.end(), buffer_reference.begin());
449 ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
450 channel_pointers),
451 kNoErr);
452 ASSERT_EQ(
453 apm_reference->ProcessStream(channel_pointers_reference, stream_config,
454 stream_config, channel_pointers_reference),
455 kNoErr);
456 for (int j = 0; j < kSampleRateHz / 100; ++j) {
457 EXPECT_EQ(buffer[j], buffer_reference[j]);
458 }
459 }
460}
461
462// Disable transient suppressor creation and run APM in ways that should trigger
463// calls to the transient suppressor API.
464TEST(ApmWithSubmodulesExcludedTest, ReinitializeTransientSuppressor) {
465 rtc::scoped_refptr<AudioProcessingImpl> apm =
466 new rtc::RefCountedObject<AudioProcessingImpl>(webrtc::Config());
467 ASSERT_EQ(apm->Initialize(), kNoErr);
468
469 ApmSubmoduleCreationOverrides overrides;
470 overrides.transient_suppression = true;
471 apm->OverrideSubmoduleCreationForTesting(overrides);
472
473 AudioProcessing::Config config = apm->GetConfig();
474 config.transient_suppression.enabled = true;
475 apm->ApplyConfig(config);
476 // 960 samples per frame: 10 ms of <= 48 kHz audio with <= 2 channels.
477 float buffer[960];
478 float* channel_pointers[] = {&buffer[0], &buffer[480]};
479 Random random_generator(2341U);
480 constexpr int kFramesToProcessPerConfiguration = 3;
481
482 StreamConfig initial_stream_config(/*sample_rate_hz=*/16000,
483 /*num_channels=*/1,
484 /*has_keyboard=*/false);
485 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
486 RandomizeSampleVector(&random_generator, buffer);
487 EXPECT_EQ(apm->ProcessStream(channel_pointers, initial_stream_config,
488 initial_stream_config, channel_pointers),
489 kNoErr);
490 }
491
492 StreamConfig stereo_stream_config(/*sample_rate_hz=*/16000,
493 /*num_channels=*/2,
494 /*has_keyboard=*/false);
495 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
496 RandomizeSampleVector(&random_generator, buffer);
497 EXPECT_EQ(apm->ProcessStream(channel_pointers, stereo_stream_config,
498 stereo_stream_config, channel_pointers),
499 kNoErr);
500 }
501
502 StreamConfig high_sample_rate_stream_config(/*sample_rate_hz=*/48000,
503 /*num_channels=*/1,
504 /*has_keyboard=*/false);
505 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
506 RandomizeSampleVector(&random_generator, buffer);
507 EXPECT_EQ(
508 apm->ProcessStream(channel_pointers, high_sample_rate_stream_config,
509 high_sample_rate_stream_config, channel_pointers),
510 kNoErr);
511 }
512
513 StreamConfig keyboard_stream_config(/*sample_rate_hz=*/16000,
514 /*num_channels=*/1,
515 /*has_keyboard=*/true);
516 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
517 RandomizeSampleVector(&random_generator, buffer);
518 EXPECT_EQ(apm->ProcessStream(channel_pointers, keyboard_stream_config,
519 keyboard_stream_config, channel_pointers),
520 kNoErr);
521 }
522}
523
524// Disable transient suppressor creation and run APM in ways that should trigger
525// calls to the transient suppressor API.
526TEST(ApmWithSubmodulesExcludedTest, ToggleTransientSuppressor) {
527 rtc::scoped_refptr<AudioProcessingImpl> apm =
528 new rtc::RefCountedObject<AudioProcessingImpl>(webrtc::Config());
529 ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
530
531 ApmSubmoduleCreationOverrides overrides;
532 overrides.transient_suppression = true;
533 apm->OverrideSubmoduleCreationForTesting(overrides);
534
535 // 960 samples per frame: 10 ms of <= 48 kHz audio with <= 2 channels.
536 float buffer[960];
537 float* channel_pointers[] = {&buffer[0], &buffer[480]};
538 Random random_generator(2341U);
539 constexpr int kFramesToProcessPerConfiguration = 3;
540 StreamConfig stream_config(/*sample_rate_hz=*/16000,
541 /*num_channels=*/1,
542 /*has_keyboard=*/false);
543
544 AudioProcessing::Config config = apm->GetConfig();
545 config.transient_suppression.enabled = true;
546 apm->ApplyConfig(config);
547 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
548 RandomizeSampleVector(&random_generator, buffer);
549 EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
550 channel_pointers),
551 kNoErr);
552 }
553
554 config = apm->GetConfig();
555 config.transient_suppression.enabled = false;
556 apm->ApplyConfig(config);
557 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
558 RandomizeSampleVector(&random_generator, buffer);
559 EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
560 channel_pointers),
561 kNoErr);
562 }
563
564 config = apm->GetConfig();
565 config.transient_suppression.enabled = true;
566 apm->ApplyConfig(config);
567 for (int i = 0; i < kFramesToProcessPerConfiguration; ++i) {
568 RandomizeSampleVector(&random_generator, buffer);
569 EXPECT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
570 channel_pointers),
571 kNoErr);
572 }
573}
andrew@webrtc.org60730cf2014-01-07 17:45:09 +0000574} // namespace webrtc