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