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