1 /*
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
11 #include "modules/audio_processing/audio_processing_impl.h"
12
13 #include <array>
14 #include <memory>
15
16 #include "api/scoped_refptr.h"
17 #include "modules/audio_processing/include/audio_processing.h"
18 #include "modules/audio_processing/optionally_built_submodule_creators.h"
19 #include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
20 #include "modules/audio_processing/test/echo_canceller_test_tools.h"
21 #include "modules/audio_processing/test/echo_control_mock.h"
22 #include "modules/audio_processing/test/test_utils.h"
23 #include "rtc_base/checks.h"
24 #include "rtc_base/random.h"
25 #include "rtc_base/ref_counted_object.h"
26 #include "test/gmock.h"
27 #include "test/gtest.h"
28
29 namespace webrtc {
30 namespace {
31
32 using ::testing::Invoke;
33 using ::testing::NotNull;
34
35 class MockInitialize : public AudioProcessingImpl {
36 public:
MockInitialize(const webrtc::Config & config)37 explicit MockInitialize(const webrtc::Config& config)
38 : AudioProcessingImpl(config) {}
39
40 MOCK_METHOD(int, InitializeLocked, (), (override));
RealInitializeLocked()41 int RealInitializeLocked() RTC_NO_THREAD_SAFETY_ANALYSIS {
42 return AudioProcessingImpl::InitializeLocked();
43 }
44
45 MOCK_METHOD(void, AddRef, (), (const, override));
46 MOCK_METHOD(rtc::RefCountReleaseStatus, Release, (), (const, override));
47 };
48
49 // 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.
56 class MockEchoControlFactory : public EchoControlFactory {
57 public:
MockEchoControlFactory()58 MockEchoControlFactory() : next_mock_(std::make_unique<MockEchoControl>()) {}
59 // Returns a pointer to the next MockEchoControl that this factory creates.
GetNext() const60 MockEchoControl* GetNext() const { return next_mock_.get(); }
Create(int sample_rate_hz,int num_render_channels,int num_capture_channels)61 std::unique_ptr<EchoControl> Create(int sample_rate_hz,
62 int num_render_channels,
63 int num_capture_channels) override {
64 std::unique_ptr<EchoControl> mock = std::move(next_mock_);
65 next_mock_ = std::make_unique<MockEchoControl>();
66 return mock;
67 }
68
69 private:
70 std::unique_ptr<MockEchoControl> next_mock_;
71 };
72
73 // 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.
76 class TestEchoDetector : public EchoDetector {
77 public:
TestEchoDetector()78 TestEchoDetector()
79 : analyze_render_audio_called_(false),
80 last_render_audio_first_sample_(0.f) {}
81 ~TestEchoDetector() override = default;
AnalyzeRenderAudio(rtc::ArrayView<const float> render_audio)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 }
AnalyzeCaptureAudio(rtc::ArrayView<const float> capture_audio)86 void AnalyzeCaptureAudio(rtc::ArrayView<const float> capture_audio) override {
87 }
Initialize(int capture_sample_rate_hz,int num_capture_channels,int render_sample_rate_hz,int num_render_channels)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 {}
GetMetrics() const92 EchoDetector::Metrics GetMetrics() const override { return {}; }
93 // Returns true if AnalyzeRenderAudio() has been called at least once.
analyze_render_audio_called() const94 bool analyze_render_audio_called() const {
95 return analyze_render_audio_called_;
96 }
97 // Returns the first sample of the last analyzed render frame.
last_render_audio_first_sample() const98 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.
110 class TestRenderPreProcessor : public CustomProcessing {
111 public:
112 TestRenderPreProcessor() = default;
113 ~TestRenderPreProcessor() = default;
Initialize(int sample_rate_hz,int num_channels)114 void Initialize(int sample_rate_hz, int num_channels) override {}
Process(AudioBuffer * audio)115 void Process(AudioBuffer* audio) override {
116 for (size_t k = 0; k < audio->num_channels(); ++k) {
117 rtc::ArrayView<float> channel_view(audio->channels()[k],
118 audio->num_frames());
119 std::transform(channel_view.begin(), channel_view.end(),
120 channel_view.begin(), ProcessSample);
121 }
122 }
ToString() const123 std::string ToString() const override { return "TestRenderPreProcessor"; }
SetRuntimeSetting(AudioProcessing::RuntimeSetting setting)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.
ProcessSample(float x)127 static constexpr float ProcessSample(float x) { return 2.f * x; }
128 };
129
130 } // namespace
131
TEST(AudioProcessingImplTest,AudioParameterChangeTriggersInit)132 TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
133 webrtc::Config webrtc_config;
134 MockInitialize mock(webrtc_config);
135 ON_CALL(mock, InitializeLocked())
136 .WillByDefault(Invoke(&mock, &MockInitialize::RealInitializeLocked));
137
138 EXPECT_CALL(mock, InitializeLocked()).Times(1);
139 mock.Initialize();
140
141 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);
146 // Call with the default parameters; there should be an init.
147 EXPECT_CALL(mock, InitializeLocked()).Times(0);
148 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
149 EXPECT_NOERR(
150 mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
151
152 // New sample rate. (Only impacts ProcessStream).
153 config = StreamConfig(32000, 1, /*has_keyboard=*/false);
154 EXPECT_CALL(mock, InitializeLocked()).Times(1);
155 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
156
157 // New number of channels.
158 // TODO(peah): Investigate why this causes 2 inits.
159 config = StreamConfig(32000, 2, /*has_keyboard=*/false);
160 EXPECT_CALL(mock, InitializeLocked()).Times(2);
161 EXPECT_NOERR(mock.ProcessStream(frame.data(), config, config, frame.data()));
162 // ProcessStream sets num_channels_ == num_output_channels.
163 EXPECT_NOERR(
164 mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
165
166 // A new sample rate passed to ProcessReverseStream should cause an init.
167 config = StreamConfig(16000, 2, /*has_keyboard=*/false);
168 EXPECT_CALL(mock, InitializeLocked()).Times(1);
169 EXPECT_NOERR(
170 mock.ProcessReverseStream(frame.data(), config, config, frame.data()));
171 }
172
TEST(AudioProcessingImplTest,UpdateCapturePreGainRuntimeSetting)173 TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
174 std::unique_ptr<AudioProcessing> apm(
175 AudioProcessingBuilderForTesting().Create());
176 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
181 constexpr int kSampleRateHz = 48000;
182 constexpr int16_t kAudioLevel = 10000;
183 constexpr size_t kNumChannels = 2;
184
185 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
186 StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
187 frame.fill(kAudioLevel);
188 apm->ProcessStream(frame.data(), config, config, frame.data());
189 EXPECT_EQ(frame[100], kAudioLevel)
190 << "With factor 1, frame shouldn't be modified.";
191
192 constexpr float kGainFactor = 2.f;
193 apm->SetRuntimeSetting(
194 AudioProcessing::RuntimeSetting::CreateCapturePreGain(kGainFactor));
195
196 // Process for two frames to have time to ramp up gain.
197 for (int i = 0; i < 2; ++i) {
198 frame.fill(kAudioLevel);
199 apm->ProcessStream(frame.data(), config, config, frame.data());
200 }
201 EXPECT_EQ(frame[100], kGainFactor * kAudioLevel)
202 << "Frame should be amplified.";
203 }
204
TEST(AudioProcessingImplTest,EchoControllerObservesPreAmplifierEchoPathGainChange)205 TEST(AudioProcessingImplTest,
206 EchoControllerObservesPreAmplifierEchoPathGainChange) {
207 // Tests that the echo controller observes an echo path gain change when the
208 // pre-amplifier submodule changes the gain.
209 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
210 const auto* echo_control_factory_ptr = echo_control_factory.get();
211
212 std::unique_ptr<AudioProcessing> apm(
213 AudioProcessingBuilderForTesting()
214 .SetEchoControlFactory(std::move(echo_control_factory))
215 .Create());
216 // Disable AGC.
217 webrtc::AudioProcessing::Config apm_config;
218 apm_config.gain_controller1.enabled = false;
219 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
224 constexpr int16_t kAudioLevel = 10000;
225 constexpr size_t kSampleRateHz = 48000;
226 constexpr size_t kNumChannels = 2;
227 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
228 StreamConfig config(kSampleRateHz, kNumChannels, /*has_keyboard=*/false);
229 frame.fill(kAudioLevel);
230
231 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
232
233 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
234 EXPECT_CALL(*echo_control_mock,
235 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
236 .Times(1);
237 apm->ProcessStream(frame.data(), config, config, frame.data());
238
239 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
240 EXPECT_CALL(*echo_control_mock,
241 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
242 .Times(1);
243 apm->SetRuntimeSetting(
244 AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
245 apm->ProcessStream(frame.data(), config, config, frame.data());
246 }
247
TEST(AudioProcessingImplTest,EchoControllerObservesAnalogAgc1EchoPathGainChange)248 TEST(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.
252 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
253 const auto* echo_control_factory_ptr = echo_control_factory.get();
254
255 std::unique_ptr<AudioProcessing> apm(
256 AudioProcessingBuilderForTesting()
257 .SetEchoControlFactory(std::move(echo_control_factory))
258 .Create());
259 webrtc::AudioProcessing::Config apm_config;
260 // Enable AGC1.
261 apm_config.gain_controller1.enabled = true;
262 apm_config.gain_controller1.mode =
263 AudioProcessing::Config::GainController1::kAdaptiveAnalog;
264 apm_config.gain_controller2.enabled = false;
265 apm_config.pre_amplifier.enabled = false;
266 apm->ApplyConfig(apm_config);
267
268 constexpr int16_t kAudioLevel = 1000;
269 constexpr size_t kSampleRateHz = 48000;
270 constexpr size_t kNumChannels = 2;
271 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
272 StreamConfig stream_config(kSampleRateHz, kNumChannels,
273 /*has_keyboard=*/false);
274 frame.fill(kAudioLevel);
275
276 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
277
278 const int initial_analog_gain = apm->recommended_stream_analog_level();
279 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
280 EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), testing::_, false))
281 .Times(1);
282 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
283
284 // Force an analog gain change if it did not happen.
285 if (initial_analog_gain == apm->recommended_stream_analog_level()) {
286 apm->set_stream_analog_level(initial_analog_gain + 1);
287 }
288
289 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
290 EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), testing::_, true))
291 .Times(1);
292 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
293 }
294
TEST(AudioProcessingImplTest,EchoControllerObservesPlayoutVolumeChange)295 TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) {
296 // Tests that the echo controller observes an echo path gain change when a
297 // playout volume change is reported.
298 auto echo_control_factory = std::make_unique<MockEchoControlFactory>();
299 const auto* echo_control_factory_ptr = echo_control_factory.get();
300
301 std::unique_ptr<AudioProcessing> apm(
302 AudioProcessingBuilderForTesting()
303 .SetEchoControlFactory(std::move(echo_control_factory))
304 .Create());
305 // 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);
310
311 constexpr int16_t kAudioLevel = 10000;
312 constexpr size_t kSampleRateHz = 48000;
313 constexpr size_t kNumChannels = 2;
314 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
315 StreamConfig stream_config(kSampleRateHz, kNumChannels,
316 /*has_keyboard=*/false);
317 frame.fill(kAudioLevel);
318
319 MockEchoControl* echo_control_mock = echo_control_factory_ptr->GetNext();
320
321 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
322 EXPECT_CALL(*echo_control_mock,
323 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
324 .Times(1);
325 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
326
327 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
328 EXPECT_CALL(*echo_control_mock,
329 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
330 .Times(1);
331 apm->SetRuntimeSetting(
332 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
333 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
334
335 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
336 EXPECT_CALL(*echo_control_mock,
337 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
338 .Times(1);
339 apm->SetRuntimeSetting(
340 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
341 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
342
343 EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
344 EXPECT_CALL(*echo_control_mock,
345 ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
346 .Times(1);
347 apm->SetRuntimeSetting(
348 AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(100));
349 apm->ProcessStream(frame.data(), stream_config, stream_config, frame.data());
350 }
351
TEST(AudioProcessingImplTest,RenderPreProcessorBeforeEchoDetector)352 TEST(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 AudioProcessingBuilderForTesting()
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 // 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
382 std::array<int16_t, kNumChannels * kSampleRateHz / 100> frame;
383 StreamConfig stream_config(kSampleRateHz, kNumChannels,
384 /*has_keyboard=*/false);
385
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.
392 frame.fill(kAudioLevel);
393 ASSERT_EQ(AudioProcessing::Error::kNoError,
394 apm->ProcessReverseStream(frame.data(), stream_config,
395 stream_config, frame.data()));
396 // Trigger a call to in EchoDetector::AnalyzeRenderAudio() via
397 // ProcessStream().
398 frame.fill(kAudioLevel);
399 ASSERT_EQ(AudioProcessing::Error::kNoError,
400 apm->ProcessStream(frame.data(), stream_config, stream_config,
401 frame.data()));
402 // 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
412 // 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.
TEST(ApmWithSubmodulesExcludedTest,BitexactWithDisabledModules)415 TEST(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.
TEST(ApmWithSubmodulesExcludedTest,ReinitializeTransientSuppressor)464 TEST(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.
TEST(ApmWithSubmodulesExcludedTest,ToggleTransientSuppressor)526 TEST(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 }
574 } // namespace webrtc
575