1 /*
2  *  Copyright (c) 2015 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 #include "webrtc/modules/audio_processing/audio_processing_impl.h"
11 
12 #include <math.h>
13 
14 #include <algorithm>
15 #include <memory>
16 #include <vector>
17 
18 #include "webrtc/base/array_view.h"
19 #include "webrtc/base/atomicops.h"
20 #include "webrtc/base/platform_thread.h"
21 #include "webrtc/base/random.h"
22 #include "webrtc/base/safe_conversions.h"
23 #include "webrtc/config.h"
24 #include "webrtc/modules/audio_processing/test/test_utils.h"
25 #include "webrtc/modules/include/module_common_types.h"
26 #include "webrtc/system_wrappers/include/clock.h"
27 #include "webrtc/system_wrappers/include/event_wrapper.h"
28 #include "webrtc/system_wrappers/include/sleep.h"
29 #include "webrtc/test/gtest.h"
30 #include "webrtc/test/testsupport/perf_test.h"
31 
32 // Check to verify that the define for the intelligibility enhancer is properly
33 // set.
34 #if !defined(WEBRTC_INTELLIGIBILITY_ENHANCER) || \
35     (WEBRTC_INTELLIGIBILITY_ENHANCER != 0 &&     \
36      WEBRTC_INTELLIGIBILITY_ENHANCER != 1)
37 #error "Set WEBRTC_INTELLIGIBILITY_ENHANCER to either 0 or 1"
38 #endif
39 
40 namespace webrtc {
41 
42 namespace {
43 
44 static const bool kPrintAllDurations = false;
45 
46 class CallSimulator;
47 
48 // Type of the render thread APM API call to use in the test.
49 enum class ProcessorType { kRender, kCapture };
50 
51 // Variant of APM processing settings to use in the test.
52 enum class SettingsType {
53   kDefaultApmDesktop,
54   kDefaultApmMobile,
55   kDefaultApmDesktopAndBeamformer,
56   kDefaultApmDesktopAndIntelligibilityEnhancer,
57   kAllSubmodulesTurnedOff,
58   kDefaultApmDesktopWithoutDelayAgnostic,
59   kDefaultApmDesktopWithoutExtendedFilter
60 };
61 
62 // Variables related to the audio data and formats.
63 struct AudioFrameData {
AudioFrameDatawebrtc::__anon83da69590111::AudioFrameData64   explicit AudioFrameData(size_t max_frame_size) {
65     // Set up the two-dimensional arrays needed for the APM API calls.
66     input_framechannels.resize(2 * max_frame_size);
67     input_frame.resize(2);
68     input_frame[0] = &input_framechannels[0];
69     input_frame[1] = &input_framechannels[max_frame_size];
70 
71     output_frame_channels.resize(2 * max_frame_size);
72     output_frame.resize(2);
73     output_frame[0] = &output_frame_channels[0];
74     output_frame[1] = &output_frame_channels[max_frame_size];
75   }
76 
77   std::vector<float> output_frame_channels;
78   std::vector<float*> output_frame;
79   std::vector<float> input_framechannels;
80   std::vector<float*> input_frame;
81   StreamConfig input_stream_config;
82   StreamConfig output_stream_config;
83 };
84 
85 // The configuration for the test.
86 struct SimulationConfig {
SimulationConfigwebrtc::__anon83da69590111::SimulationConfig87   SimulationConfig(int sample_rate_hz, SettingsType simulation_settings)
88       : sample_rate_hz(sample_rate_hz),
89         simulation_settings(simulation_settings) {}
90 
GenerateSimulationConfigswebrtc::__anon83da69590111::SimulationConfig91   static std::vector<SimulationConfig> GenerateSimulationConfigs() {
92     std::vector<SimulationConfig> simulation_configs;
93 #ifndef WEBRTC_ANDROID
94     const SettingsType desktop_settings[] = {
95         SettingsType::kDefaultApmDesktop, SettingsType::kAllSubmodulesTurnedOff,
96         SettingsType::kDefaultApmDesktopWithoutDelayAgnostic,
97         SettingsType::kDefaultApmDesktopWithoutExtendedFilter};
98 
99     const int desktop_sample_rates[] = {8000, 16000, 32000, 48000};
100 
101     for (auto sample_rate : desktop_sample_rates) {
102       for (auto settings : desktop_settings) {
103         simulation_configs.push_back(SimulationConfig(sample_rate, settings));
104       }
105     }
106 
107 #if WEBRTC_INTELLIGIBILITY_ENHANCER == 1
108     const SettingsType intelligibility_enhancer_settings[] = {
109         SettingsType::kDefaultApmDesktopAndIntelligibilityEnhancer};
110 
111     const int intelligibility_enhancer_sample_rates[] = {8000, 16000, 32000,
112                                                          48000};
113 
114     for (auto sample_rate : intelligibility_enhancer_sample_rates) {
115       for (auto settings : intelligibility_enhancer_settings) {
116         simulation_configs.push_back(SimulationConfig(sample_rate, settings));
117       }
118     }
119 #endif
120 
121     const SettingsType beamformer_settings[] = {
122         SettingsType::kDefaultApmDesktopAndBeamformer};
123 
124     const int beamformer_sample_rates[] = {8000, 16000, 32000, 48000};
125 
126     for (auto sample_rate : beamformer_sample_rates) {
127       for (auto settings : beamformer_settings) {
128         simulation_configs.push_back(SimulationConfig(sample_rate, settings));
129       }
130     }
131 #endif
132 
133     const SettingsType mobile_settings[] = {SettingsType::kDefaultApmMobile};
134 
135     const int mobile_sample_rates[] = {8000, 16000};
136 
137     for (auto sample_rate : mobile_sample_rates) {
138       for (auto settings : mobile_settings) {
139         simulation_configs.push_back(SimulationConfig(sample_rate, settings));
140       }
141     }
142 
143     return simulation_configs;
144   }
145 
SettingsDescriptionwebrtc::__anon83da69590111::SimulationConfig146   std::string SettingsDescription() const {
147     std::string description;
148     switch (simulation_settings) {
149       case SettingsType::kDefaultApmMobile:
150         description = "DefaultApmMobile";
151         break;
152       case SettingsType::kDefaultApmDesktop:
153         description = "DefaultApmDesktop";
154         break;
155       case SettingsType::kDefaultApmDesktopAndBeamformer:
156         description = "DefaultApmDesktopAndBeamformer";
157         break;
158       case SettingsType::kDefaultApmDesktopAndIntelligibilityEnhancer:
159         description = "DefaultApmDesktopAndIntelligibilityEnhancer";
160         break;
161       case SettingsType::kAllSubmodulesTurnedOff:
162         description = "AllSubmodulesOff";
163         break;
164       case SettingsType::kDefaultApmDesktopWithoutDelayAgnostic:
165         description = "DefaultApmDesktopWithoutDelayAgnostic";
166         break;
167       case SettingsType::kDefaultApmDesktopWithoutExtendedFilter:
168         description = "DefaultApmDesktopWithoutExtendedFilter";
169         break;
170     }
171     return description;
172   }
173 
174   int sample_rate_hz = 16000;
175   SettingsType simulation_settings = SettingsType::kDefaultApmDesktop;
176 };
177 
178 // Handler for the frame counters.
179 class FrameCounters {
180  public:
IncreaseRenderCounter()181   void IncreaseRenderCounter() {
182     rtc::AtomicOps::Increment(&render_count_);
183   }
184 
IncreaseCaptureCounter()185   void IncreaseCaptureCounter() {
186     rtc::AtomicOps::Increment(&capture_count_);
187   }
188 
CaptureMinusRenderCounters() const189   int CaptureMinusRenderCounters() const {
190     // The return value will be approximate, but that's good enough since
191     // by the time we return the value, it's not guaranteed to be correct
192     // anyway.
193     return rtc::AtomicOps::AcquireLoad(&capture_count_) -
194            rtc::AtomicOps::AcquireLoad(&render_count_);
195   }
196 
RenderMinusCaptureCounters() const197   int RenderMinusCaptureCounters() const {
198     return -CaptureMinusRenderCounters();
199   }
200 
BothCountersExceedeThreshold(int threshold) const201   bool BothCountersExceedeThreshold(int threshold) const {
202     // TODO(tommi): We could use an event to signal this so that we don't need
203     // to be polling from the main thread and possibly steal cycles.
204     const int capture_count = rtc::AtomicOps::AcquireLoad(&capture_count_);
205     const int render_count = rtc::AtomicOps::AcquireLoad(&render_count_);
206     return (render_count > threshold && capture_count > threshold);
207   }
208 
209  private:
210   int render_count_ = 0;
211   int capture_count_ = 0;
212 };
213 
214 // Class that represents a flag that can only be raised.
215 class LockedFlag {
216  public:
get_flag() const217   bool get_flag() const {
218     return rtc::AtomicOps::AcquireLoad(&flag_);
219   }
220 
set_flag()221   void set_flag() {
222     if (!get_flag())  // read-only operation to avoid affecting the cache-line.
223       rtc::AtomicOps::CompareAndSwap(&flag_, 0, 1);
224   }
225 
226  private:
227   int flag_ = 0;
228 };
229 
230 // Parent class for the thread processors.
231 class TimedThreadApiProcessor {
232  public:
TimedThreadApiProcessor(ProcessorType processor_type,Random * rand_gen,FrameCounters * shared_counters_state,LockedFlag * capture_call_checker,CallSimulator * test_framework,const SimulationConfig * simulation_config,AudioProcessing * apm,int num_durations_to_store,float input_level,int num_channels)233   TimedThreadApiProcessor(ProcessorType processor_type,
234                           Random* rand_gen,
235                           FrameCounters* shared_counters_state,
236                           LockedFlag* capture_call_checker,
237                           CallSimulator* test_framework,
238                           const SimulationConfig* simulation_config,
239                           AudioProcessing* apm,
240                           int num_durations_to_store,
241                           float input_level,
242                           int num_channels)
243       : rand_gen_(rand_gen),
244         frame_counters_(shared_counters_state),
245         capture_call_checker_(capture_call_checker),
246         test_(test_framework),
247         simulation_config_(simulation_config),
248         apm_(apm),
249         frame_data_(kMaxFrameSize),
250         clock_(webrtc::Clock::GetRealTimeClock()),
251         num_durations_to_store_(num_durations_to_store),
252         input_level_(input_level),
253         processor_type_(processor_type),
254         num_channels_(num_channels) {
255     api_call_durations_.reserve(num_durations_to_store_);
256   }
257 
258   // Implements the callback functionality for the threads.
259   bool Process();
260 
261   // Method for printing out the simulation statistics.
print_processor_statistics(std::string processor_name) const262   void print_processor_statistics(std::string processor_name) const {
263     const std::string modifier = "_api_call_duration";
264 
265     // Lambda function for creating a test printout string.
266     auto create_mean_and_std_string = [](int64_t average,
267                                          int64_t standard_dev) {
268       std::string s = std::to_string(average);
269       s += ", ";
270       s += std::to_string(standard_dev);
271       return s;
272     };
273 
274     const std::string sample_rate_name =
275         "_" + std::to_string(simulation_config_->sample_rate_hz) + "Hz";
276 
277     webrtc::test::PrintResultMeanAndError(
278         "apm_timing", sample_rate_name, processor_name,
279         create_mean_and_std_string(GetDurationAverage(),
280                                    GetDurationStandardDeviation()),
281         "us", false);
282 
283     if (kPrintAllDurations) {
284       std::string value_string = "";
285       for (int64_t duration : api_call_durations_) {
286         value_string += std::to_string(duration) + ",";
287       }
288       webrtc::test::PrintResultList("apm_call_durations", sample_rate_name,
289                                     processor_name, value_string, "us", false);
290     }
291   }
292 
AddDuration(int64_t duration)293   void AddDuration(int64_t duration) {
294     if (api_call_durations_.size() < num_durations_to_store_) {
295       api_call_durations_.push_back(duration);
296     }
297   }
298 
299  private:
300   static const int kMaxCallDifference = 10;
301   static const int kMaxFrameSize = 480;
302   static const int kNumInitializationFrames = 5;
303 
GetDurationStandardDeviation() const304   int64_t GetDurationStandardDeviation() const {
305     double variance = 0;
306     const int64_t average_duration = GetDurationAverage();
307     for (size_t k = kNumInitializationFrames; k < api_call_durations_.size();
308          k++) {
309       int64_t tmp = api_call_durations_[k] - average_duration;
310       variance += static_cast<double>(tmp * tmp);
311     }
312     const int denominator = rtc::checked_cast<int>(api_call_durations_.size()) -
313                             kNumInitializationFrames;
314     return (denominator > 0
315                 ? rtc::checked_cast<int64_t>(sqrt(variance / denominator))
316                 : -1);
317   }
318 
GetDurationAverage() const319   int64_t GetDurationAverage() const {
320     int64_t average_duration = 0;
321     for (size_t k = kNumInitializationFrames; k < api_call_durations_.size();
322          k++) {
323       average_duration += api_call_durations_[k];
324     }
325     const int denominator = rtc::checked_cast<int>(api_call_durations_.size()) -
326                             kNumInitializationFrames;
327     return (denominator > 0 ? average_duration / denominator : -1);
328   }
329 
ProcessCapture()330   int ProcessCapture() {
331     // Set the stream delay.
332     apm_->set_stream_delay_ms(30);
333 
334     // Call and time the specified capture side API processing method.
335     const int64_t start_time = clock_->TimeInMicroseconds();
336     const int result = apm_->ProcessStream(
337         &frame_data_.input_frame[0], frame_data_.input_stream_config,
338         frame_data_.output_stream_config, &frame_data_.output_frame[0]);
339     const int64_t end_time = clock_->TimeInMicroseconds();
340 
341     frame_counters_->IncreaseCaptureCounter();
342 
343     AddDuration(end_time - start_time);
344 
345     if (first_process_call_) {
346       // Flag that the capture side has been called at least once
347       // (needed to ensure that a capture call has been done
348       // before the first render call is performed (implicitly
349       // required by the APM API).
350       capture_call_checker_->set_flag();
351       first_process_call_ = false;
352     }
353     return result;
354   }
355 
ReadyToProcessCapture()356   bool ReadyToProcessCapture() {
357     return (frame_counters_->CaptureMinusRenderCounters() <=
358             kMaxCallDifference);
359   }
360 
ProcessRender()361   int ProcessRender() {
362     // Call and time the specified render side API processing method.
363     const int64_t start_time = clock_->TimeInMicroseconds();
364     const int result = apm_->ProcessReverseStream(
365         &frame_data_.input_frame[0], frame_data_.input_stream_config,
366         frame_data_.output_stream_config, &frame_data_.output_frame[0]);
367     const int64_t end_time = clock_->TimeInMicroseconds();
368     frame_counters_->IncreaseRenderCounter();
369 
370     AddDuration(end_time - start_time);
371 
372     return result;
373   }
374 
ReadyToProcessRender()375   bool ReadyToProcessRender() {
376     // Do not process until at least one capture call has been done.
377     // (implicitly required by the APM API).
378     if (first_process_call_ && !capture_call_checker_->get_flag()) {
379       return false;
380     }
381 
382     // Ensure that the number of render and capture calls do not differ too
383     // much.
384     if (frame_counters_->RenderMinusCaptureCounters() > kMaxCallDifference) {
385       return false;
386     }
387 
388     first_process_call_ = false;
389     return true;
390   }
391 
PrepareFrame()392   void PrepareFrame() {
393     // Lambda function for populating a float multichannel audio frame
394     // with random data.
395     auto populate_audio_frame = [](float amplitude, size_t num_channels,
396                                    size_t samples_per_channel, Random* rand_gen,
397                                    float** frame) {
398       for (size_t ch = 0; ch < num_channels; ch++) {
399         for (size_t k = 0; k < samples_per_channel; k++) {
400           // Store random float number with a value between +-amplitude.
401           frame[ch][k] = amplitude * (2 * rand_gen->Rand<float>() - 1);
402         }
403       }
404     };
405 
406     // Prepare the audio input data and metadata.
407     frame_data_.input_stream_config.set_sample_rate_hz(
408         simulation_config_->sample_rate_hz);
409     frame_data_.input_stream_config.set_num_channels(num_channels_);
410     frame_data_.input_stream_config.set_has_keyboard(false);
411     populate_audio_frame(input_level_, num_channels_,
412                          (simulation_config_->sample_rate_hz *
413                           AudioProcessing::kChunkSizeMs / 1000),
414                          rand_gen_, &frame_data_.input_frame[0]);
415 
416     // Prepare the float audio output data and metadata.
417     frame_data_.output_stream_config.set_sample_rate_hz(
418         simulation_config_->sample_rate_hz);
419     frame_data_.output_stream_config.set_num_channels(1);
420     frame_data_.output_stream_config.set_has_keyboard(false);
421   }
422 
ReadyToProcess()423   bool ReadyToProcess() {
424     switch (processor_type_) {
425       case ProcessorType::kRender:
426         return ReadyToProcessRender();
427 
428       case ProcessorType::kCapture:
429         return ReadyToProcessCapture();
430     }
431 
432     // Should not be reached, but the return statement is needed for the code to
433     // build successfully on Android.
434     RTC_NOTREACHED();
435     return false;
436   }
437 
438   Random* rand_gen_ = nullptr;
439   FrameCounters* frame_counters_ = nullptr;
440   LockedFlag* capture_call_checker_ = nullptr;
441   CallSimulator* test_ = nullptr;
442   const SimulationConfig* const simulation_config_ = nullptr;
443   AudioProcessing* apm_ = nullptr;
444   AudioFrameData frame_data_;
445   webrtc::Clock* clock_;
446   const size_t num_durations_to_store_;
447   std::vector<int64_t> api_call_durations_;
448   const float input_level_;
449   bool first_process_call_ = true;
450   const ProcessorType processor_type_;
451   const int num_channels_ = 1;
452 };
453 
454 // Class for managing the test simulation.
455 class CallSimulator : public ::testing::TestWithParam<SimulationConfig> {
456  public:
CallSimulator()457   CallSimulator()
458       : test_complete_(EventWrapper::Create()),
459         render_thread_(
460             new rtc::PlatformThread(RenderProcessorThreadFunc, this, "render")),
461         capture_thread_(new rtc::PlatformThread(CaptureProcessorThreadFunc,
462                                                 this,
463                                                 "capture")),
464         rand_gen_(42U),
465         simulation_config_(static_cast<SimulationConfig>(GetParam())) {}
466 
467   // Run the call simulation with a timeout.
Run()468   EventTypeWrapper Run() {
469     StartThreads();
470 
471     EventTypeWrapper result = test_complete_->Wait(kTestTimeout);
472 
473     StopThreads();
474 
475     render_thread_state_->print_processor_statistics(
476         simulation_config_.SettingsDescription() + "_render");
477     capture_thread_state_->print_processor_statistics(
478         simulation_config_.SettingsDescription() + "_capture");
479 
480     return result;
481   }
482 
483   // Tests whether all the required render and capture side calls have been
484   // done.
MaybeEndTest()485   bool MaybeEndTest() {
486     if (frame_counters_.BothCountersExceedeThreshold(kMinNumFramesToProcess)) {
487       test_complete_->Set();
488       return true;
489     }
490     return false;
491   }
492 
493  private:
494   static const float kCaptureInputFloatLevel;
495   static const float kRenderInputFloatLevel;
496   static const int kMinNumFramesToProcess = 150;
497   static const int32_t kTestTimeout = 3 * 10 * kMinNumFramesToProcess;
498 
499   // ::testing::TestWithParam<> implementation.
TearDown()500   void TearDown() override { StopThreads(); }
501 
502   // Stop all running threads.
StopThreads()503   void StopThreads() {
504     render_thread_->Stop();
505     capture_thread_->Stop();
506   }
507 
508   // Simulator and APM setup.
SetUp()509   void SetUp() override {
510     // Lambda function for setting the default APM runtime settings for desktop.
511     auto set_default_desktop_apm_runtime_settings = [](AudioProcessing* apm) {
512       ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(true));
513       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
514       ASSERT_EQ(apm->kNoError,
515                 apm->gain_control()->set_mode(GainControl::kAdaptiveDigital));
516       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
517       ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
518       ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
519       ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(false));
520       ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(true));
521       ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->enable_metrics(true));
522       ASSERT_EQ(apm->kNoError,
523                 apm->echo_cancellation()->enable_delay_logging(true));
524     };
525 
526     // Lambda function for setting the default APM runtime settings for mobile.
527     auto set_default_mobile_apm_runtime_settings = [](AudioProcessing* apm) {
528       ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(true));
529       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
530       ASSERT_EQ(apm->kNoError,
531                 apm->gain_control()->set_mode(GainControl::kAdaptiveDigital));
532       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
533       ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(true));
534       ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(true));
535       ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
536       ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(false));
537     };
538 
539     // Lambda function for turning off all of the APM runtime settings
540     // submodules.
541     auto turn_off_default_apm_runtime_settings = [](AudioProcessing* apm) {
542       ASSERT_EQ(apm->kNoError, apm->level_estimator()->Enable(false));
543       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(false));
544       ASSERT_EQ(apm->kNoError,
545                 apm->gain_control()->set_mode(GainControl::kAdaptiveDigital));
546       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(false));
547       ASSERT_EQ(apm->kNoError, apm->noise_suppression()->Enable(false));
548       ASSERT_EQ(apm->kNoError, apm->voice_detection()->Enable(false));
549       ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(false));
550       ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->Enable(false));
551       ASSERT_EQ(apm->kNoError, apm->echo_cancellation()->enable_metrics(false));
552       ASSERT_EQ(apm->kNoError,
553                 apm->echo_cancellation()->enable_delay_logging(false));
554     };
555 
556     // Lambda function for adding default desktop APM settings to a config.
557     auto add_default_desktop_config = [](Config* config) {
558       config->Set<ExtendedFilter>(new ExtendedFilter(true));
559       config->Set<DelayAgnostic>(new DelayAgnostic(true));
560     };
561 
562     // Lambda function for adding beamformer settings to a config.
563     auto add_beamformer_config = [](Config* config) {
564       const size_t num_mics = 2;
565       const std::vector<Point> array_geometry =
566           ParseArrayGeometry("0 0 0 0.05 0 0", num_mics);
567       RTC_CHECK_EQ(array_geometry.size(), num_mics);
568 
569       config->Set<Beamforming>(
570           new Beamforming(true, array_geometry,
571                           SphericalPointf(DegreesToRadians(90), 0.f, 1.f)));
572     };
573 
574     int num_capture_channels = 1;
575     switch (simulation_config_.simulation_settings) {
576       case SettingsType::kDefaultApmMobile: {
577         apm_.reset(AudioProcessingImpl::Create());
578         ASSERT_TRUE(!!apm_);
579         set_default_mobile_apm_runtime_settings(apm_.get());
580         break;
581       }
582       case SettingsType::kDefaultApmDesktop: {
583         Config config;
584         add_default_desktop_config(&config);
585         apm_.reset(AudioProcessingImpl::Create(config));
586         ASSERT_TRUE(!!apm_);
587         set_default_desktop_apm_runtime_settings(apm_.get());
588         apm_->SetExtraOptions(config);
589         break;
590       }
591       case SettingsType::kDefaultApmDesktopAndBeamformer: {
592         Config config;
593         add_beamformer_config(&config);
594         add_default_desktop_config(&config);
595         apm_.reset(AudioProcessingImpl::Create(config));
596         ASSERT_TRUE(!!apm_);
597         set_default_desktop_apm_runtime_settings(apm_.get());
598         apm_->SetExtraOptions(config);
599         num_capture_channels = 2;
600         break;
601       }
602       case SettingsType::kDefaultApmDesktopAndIntelligibilityEnhancer: {
603         Config config;
604         config.Set<Intelligibility>(new Intelligibility(true));
605         add_default_desktop_config(&config);
606         apm_.reset(AudioProcessingImpl::Create(config));
607         ASSERT_TRUE(!!apm_);
608         set_default_desktop_apm_runtime_settings(apm_.get());
609         apm_->SetExtraOptions(config);
610         break;
611       }
612       case SettingsType::kAllSubmodulesTurnedOff: {
613         apm_.reset(AudioProcessingImpl::Create());
614         ASSERT_TRUE(!!apm_);
615         turn_off_default_apm_runtime_settings(apm_.get());
616         break;
617       }
618       case SettingsType::kDefaultApmDesktopWithoutDelayAgnostic: {
619         Config config;
620         config.Set<ExtendedFilter>(new ExtendedFilter(true));
621         config.Set<DelayAgnostic>(new DelayAgnostic(false));
622         apm_.reset(AudioProcessingImpl::Create(config));
623         ASSERT_TRUE(!!apm_);
624         set_default_desktop_apm_runtime_settings(apm_.get());
625         apm_->SetExtraOptions(config);
626         break;
627       }
628       case SettingsType::kDefaultApmDesktopWithoutExtendedFilter: {
629         Config config;
630         config.Set<ExtendedFilter>(new ExtendedFilter(false));
631         config.Set<DelayAgnostic>(new DelayAgnostic(true));
632         apm_.reset(AudioProcessingImpl::Create(config));
633         ASSERT_TRUE(!!apm_);
634         set_default_desktop_apm_runtime_settings(apm_.get());
635         apm_->SetExtraOptions(config);
636         break;
637       }
638     }
639 
640     render_thread_state_.reset(new TimedThreadApiProcessor(
641         ProcessorType::kRender, &rand_gen_, &frame_counters_,
642         &capture_call_checker_, this, &simulation_config_, apm_.get(),
643         kMinNumFramesToProcess, kRenderInputFloatLevel, 1));
644     capture_thread_state_.reset(new TimedThreadApiProcessor(
645         ProcessorType::kCapture, &rand_gen_, &frame_counters_,
646         &capture_call_checker_, this, &simulation_config_, apm_.get(),
647         kMinNumFramesToProcess, kCaptureInputFloatLevel, num_capture_channels));
648   }
649 
650   // Thread callback for the render thread.
RenderProcessorThreadFunc(void * context)651   static bool RenderProcessorThreadFunc(void* context) {
652     return reinterpret_cast<CallSimulator*>(context)
653         ->render_thread_state_->Process();
654   }
655 
656   // Thread callback for the capture thread.
CaptureProcessorThreadFunc(void * context)657   static bool CaptureProcessorThreadFunc(void* context) {
658     return reinterpret_cast<CallSimulator*>(context)
659         ->capture_thread_state_->Process();
660   }
661 
662   // Start the threads used in the test.
StartThreads()663   void StartThreads() {
664     ASSERT_NO_FATAL_FAILURE(render_thread_->Start());
665     render_thread_->SetPriority(rtc::kRealtimePriority);
666     ASSERT_NO_FATAL_FAILURE(capture_thread_->Start());
667     capture_thread_->SetPriority(rtc::kRealtimePriority);
668   }
669 
670   // Event handler for the test.
671   const std::unique_ptr<EventWrapper> test_complete_;
672 
673   // Thread related variables.
674   std::unique_ptr<rtc::PlatformThread> render_thread_;
675   std::unique_ptr<rtc::PlatformThread> capture_thread_;
676   Random rand_gen_;
677 
678   std::unique_ptr<AudioProcessing> apm_;
679   const SimulationConfig simulation_config_;
680   FrameCounters frame_counters_;
681   LockedFlag capture_call_checker_;
682   std::unique_ptr<TimedThreadApiProcessor> render_thread_state_;
683   std::unique_ptr<TimedThreadApiProcessor> capture_thread_state_;
684 };
685 
686 // Implements the callback functionality for the threads.
Process()687 bool TimedThreadApiProcessor::Process() {
688   PrepareFrame();
689 
690   // Wait in a spinlock manner until it is ok to start processing.
691   // Note that SleepMs is not applicable since it only allows sleeping
692   // on a millisecond basis which is too long.
693   // TODO(tommi): This loop may affect the performance of the test that it's
694   // meant to measure.  See if we could use events instead to signal readiness.
695   while (!ReadyToProcess()) {
696   }
697 
698   int result = AudioProcessing::kNoError;
699   switch (processor_type_) {
700     case ProcessorType::kRender:
701       result = ProcessRender();
702       break;
703     case ProcessorType::kCapture:
704       result = ProcessCapture();
705       break;
706   }
707 
708   EXPECT_EQ(result, AudioProcessing::kNoError);
709 
710   return !test_->MaybeEndTest();
711 }
712 
713 const float CallSimulator::kRenderInputFloatLevel = 0.5f;
714 const float CallSimulator::kCaptureInputFloatLevel = 0.03125f;
715 }  // anonymous namespace
716 
TEST_P(CallSimulator,ApiCallDurationTest)717 TEST_P(CallSimulator, ApiCallDurationTest) {
718   // Run test and verify that it did not time out.
719   EXPECT_EQ(kEventSignaled, Run());
720 }
721 
722 INSTANTIATE_TEST_CASE_P(
723     AudioProcessingPerformanceTest,
724     CallSimulator,
725     ::testing::ValuesIn(SimulationConfig::GenerateSimulationConfigs()));
726 
727 }  // namespace webrtc
728