1 /*
2  *  Copyright (c) 2016 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 <numeric>
12 #include <vector>
13 
14 #include "webrtc/base/array_view.h"
15 #include "webrtc/base/random.h"
16 #include "webrtc/modules/audio_processing/audio_buffer.h"
17 #include "webrtc/modules/audio_processing/include/audio_processing.h"
18 #include "webrtc/modules/audio_processing/residual_echo_detector.h"
19 #include "webrtc/modules/audio_processing/test/audio_buffer_tools.h"
20 #include "webrtc/modules/audio_processing/test/performance_timer.h"
21 #include "webrtc/modules/audio_processing/test/simulator_buffers.h"
22 #include "webrtc/system_wrappers/include/clock.h"
23 #include "webrtc/test/gtest.h"
24 #include "webrtc/test/testsupport/perf_test.h"
25 
26 namespace webrtc {
27 namespace {
28 
29 const size_t kNumFramesToProcess = 500;
30 const size_t kProcessingBatchSize = 20;
31 const size_t kWarmupBatchSize = 2 * kProcessingBatchSize;
32 const int kSampleRate = AudioProcessing::kSampleRate48kHz;
33 const int kNumberOfChannels = 1;
34 
FormPerformanceMeasureString(const test::PerformanceTimer & timer)35 std::string FormPerformanceMeasureString(const test::PerformanceTimer& timer) {
36   std::string s = std::to_string(timer.GetDurationAverage());
37   s += ", ";
38   s += std::to_string(timer.GetDurationStandardDeviation());
39   return s;
40 }
41 
RunStandaloneSubmodule()42 void RunStandaloneSubmodule() {
43   test::SimulatorBuffers buffers(
44       kSampleRate, kSampleRate, kSampleRate, kSampleRate, kNumberOfChannels,
45       kNumberOfChannels, kNumberOfChannels, kNumberOfChannels);
46   test::PerformanceTimer timer(kNumFramesToProcess);
47 
48   ResidualEchoDetector echo_detector;
49   echo_detector.Initialize();
50 
51   for (size_t frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) {
52     // The first batch of frames are for warming up, and are not part of the
53     // benchmark. After that the processing time is measured in chunks of
54     // kProcessingBatchSize frames.
55     if (frame_no >= kWarmupBatchSize && frame_no % kProcessingBatchSize == 0) {
56       timer.StartTimer();
57     }
58 
59     buffers.UpdateInputBuffers();
60     echo_detector.AnalyzeRenderAudio(rtc::ArrayView<const float>(
61         buffers.render_input_buffer->split_bands_const_f(0)[kBand0To8kHz],
62         buffers.render_input_buffer->num_frames_per_band()));
63     echo_detector.AnalyzeCaptureAudio(rtc::ArrayView<const float>(
64         buffers.capture_input_buffer->split_bands_const_f(0)[kBand0To8kHz],
65         buffers.capture_input_buffer->num_frames_per_band()));
66 
67     if (frame_no >= kWarmupBatchSize &&
68         frame_no % kProcessingBatchSize == kProcessingBatchSize - 1) {
69       timer.StopTimer();
70     }
71   }
72   webrtc::test::PrintResultMeanAndError(
73       "echo_detector_call_durations", "", "StandaloneEchoDetector",
74       FormPerformanceMeasureString(timer), "us", false);
75 }
76 
RunTogetherWithApm(std::string test_description,bool use_mobile_aec,bool include_default_apm_processing)77 void RunTogetherWithApm(std::string test_description,
78                         bool use_mobile_aec,
79                         bool include_default_apm_processing) {
80   test::SimulatorBuffers buffers(
81       kSampleRate, kSampleRate, kSampleRate, kSampleRate, kNumberOfChannels,
82       kNumberOfChannels, kNumberOfChannels, kNumberOfChannels);
83   test::PerformanceTimer timer(kNumFramesToProcess);
84 
85   webrtc::Config config;
86   AudioProcessing::Config apm_config;
87   if (include_default_apm_processing) {
88     config.Set<DelayAgnostic>(new DelayAgnostic(true));
89     config.Set<ExtendedFilter>(new ExtendedFilter(true));
90   }
91   apm_config.level_controller.enabled = include_default_apm_processing;
92   apm_config.residual_echo_detector.enabled = true;
93 
94   std::unique_ptr<AudioProcessing> apm;
95   apm.reset(AudioProcessing::Create(config));
96   ASSERT_TRUE(apm.get());
97   apm->ApplyConfig(apm_config);
98 
99   ASSERT_EQ(AudioProcessing::kNoError,
100             apm->gain_control()->Enable(include_default_apm_processing));
101   if (use_mobile_aec) {
102     ASSERT_EQ(AudioProcessing::kNoError,
103               apm->echo_cancellation()->Enable(false));
104     ASSERT_EQ(AudioProcessing::kNoError, apm->echo_control_mobile()->Enable(
105                                              include_default_apm_processing));
106   } else {
107     ASSERT_EQ(AudioProcessing::kNoError,
108               apm->echo_cancellation()->Enable(include_default_apm_processing));
109     ASSERT_EQ(AudioProcessing::kNoError,
110               apm->echo_control_mobile()->Enable(false));
111   }
112   ASSERT_EQ(AudioProcessing::kNoError,
113             apm->high_pass_filter()->Enable(include_default_apm_processing));
114   ASSERT_EQ(AudioProcessing::kNoError,
115             apm->noise_suppression()->Enable(include_default_apm_processing));
116   ASSERT_EQ(AudioProcessing::kNoError,
117             apm->voice_detection()->Enable(include_default_apm_processing));
118   ASSERT_EQ(AudioProcessing::kNoError,
119             apm->level_estimator()->Enable(include_default_apm_processing));
120 
121   StreamConfig stream_config(kSampleRate, kNumberOfChannels, false);
122 
123   for (size_t frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) {
124     // The first batch of frames are for warming up, and are not part of the
125     // benchmark. After that the processing time is measured in chunks of
126     // kProcessingBatchSize frames.
127     if (frame_no >= kWarmupBatchSize && frame_no % kProcessingBatchSize == 0) {
128       timer.StartTimer();
129     }
130 
131     buffers.UpdateInputBuffers();
132 
133     ASSERT_EQ(
134         AudioProcessing::kNoError,
135         apm->ProcessReverseStream(&buffers.render_input[0], stream_config,
136                                   stream_config, &buffers.render_output[0]));
137 
138     ASSERT_EQ(AudioProcessing::kNoError, apm->set_stream_delay_ms(0));
139     if (include_default_apm_processing) {
140       apm->gain_control()->set_stream_analog_level(0);
141       if (!use_mobile_aec) {
142         apm->echo_cancellation()->set_stream_drift_samples(0);
143       }
144     }
145     ASSERT_EQ(AudioProcessing::kNoError,
146               apm->ProcessStream(&buffers.capture_input[0], stream_config,
147                                  stream_config, &buffers.capture_output[0]));
148 
149     if (frame_no >= kWarmupBatchSize &&
150         frame_no % kProcessingBatchSize == kProcessingBatchSize - 1) {
151       timer.StopTimer();
152     }
153   }
154 
155   webrtc::test::PrintResultMeanAndError(
156       "echo_detector_call_durations", "_total", test_description,
157       FormPerformanceMeasureString(timer), "us", false);
158 }
159 
160 }  // namespace
161 
TEST(EchoDetectorPerformanceTest,StandaloneProcessing)162 TEST(EchoDetectorPerformanceTest, StandaloneProcessing) {
163   RunStandaloneSubmodule();
164 }
165 
TEST(EchoDetectorPerformanceTest,ProcessingViaApm)166 TEST(EchoDetectorPerformanceTest, ProcessingViaApm) {
167   RunTogetherWithApm("SimpleEchoDetectorViaApm", false, false);
168 }
169 
TEST(EchoDetectorPerformanceTest,InteractionWithDefaultApm)170 TEST(EchoDetectorPerformanceTest, InteractionWithDefaultApm) {
171   RunTogetherWithApm("EchoDetectorAndDefaultDesktopApm", false, true);
172   RunTogetherWithApm("EchoDetectorAndDefaultMobileApm", true, true);
173 }
174 
175 }  // namespace webrtc
176