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