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