| /* | 
 |  *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 | #include <math.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <atomic> | 
 | #include <memory> | 
 | #include <vector> | 
 |  | 
 | #include "absl/strings/string_view.h" | 
 | #include "api/array_view.h" | 
 | #include "api/audio/builtin_audio_processing_builder.h" | 
 | #include "api/environment/environment_factory.h" | 
 | #include "api/numerics/samples_stats_counter.h" | 
 | #include "api/test/metrics/global_metrics_logger_and_exporter.h" | 
 | #include "api/test/metrics/metric.h" | 
 | #include "modules/audio_processing/audio_processing_impl.h" | 
 | #include "modules/audio_processing/test/test_utils.h" | 
 | #include "rtc_base/event.h" | 
 | #include "rtc_base/numerics/safe_conversions.h" | 
 | #include "rtc_base/platform_thread.h" | 
 | #include "rtc_base/random.h" | 
 | #include "system_wrappers/include/clock.h" | 
 | #include "test/gtest.h" | 
 |  | 
 | namespace webrtc { | 
 | namespace { | 
 |  | 
 | using test::GetGlobalMetricsLogger; | 
 | using test::ImprovementDirection; | 
 | using test::Metric; | 
 | using test::Unit; | 
 |  | 
 | class CallSimulator; | 
 |  | 
 | // Type of the render thread APM API call to use in the test. | 
 | enum class ProcessorType { kRender, kCapture }; | 
 |  | 
 | // Variant of APM processing settings to use in the test. | 
 | enum class SettingsType { | 
 |   kDefaultApmDesktop, | 
 |   kDefaultApmMobile, | 
 |   kAllSubmodulesTurnedOff, | 
 |   kDefaultApmDesktopWithoutDelayAgnostic, | 
 |   kDefaultApmDesktopWithoutExtendedFilter | 
 | }; | 
 |  | 
 | // Variables related to the audio data and formats. | 
 | struct AudioFrameData { | 
 |   explicit AudioFrameData(size_t max_frame_size) { | 
 |     // Set up the two-dimensional arrays needed for the APM API calls. | 
 |     input_framechannels.resize(2 * max_frame_size); | 
 |     input_frame.resize(2); | 
 |     input_frame[0] = &input_framechannels[0]; | 
 |     input_frame[1] = &input_framechannels[max_frame_size]; | 
 |  | 
 |     output_frame_channels.resize(2 * max_frame_size); | 
 |     output_frame.resize(2); | 
 |     output_frame[0] = &output_frame_channels[0]; | 
 |     output_frame[1] = &output_frame_channels[max_frame_size]; | 
 |   } | 
 |  | 
 |   std::vector<float> output_frame_channels; | 
 |   std::vector<float*> output_frame; | 
 |   std::vector<float> input_framechannels; | 
 |   std::vector<float*> input_frame; | 
 |   StreamConfig input_stream_config; | 
 |   StreamConfig output_stream_config; | 
 | }; | 
 |  | 
 | // The configuration for the test. | 
 | struct SimulationConfig { | 
 |   SimulationConfig(int sample_rate_hz, SettingsType simulation_settings) | 
 |       : sample_rate_hz(sample_rate_hz), | 
 |         simulation_settings(simulation_settings) {} | 
 |  | 
 |   static std::vector<SimulationConfig> GenerateSimulationConfigs() { | 
 |     std::vector<SimulationConfig> simulation_configs; | 
 | #ifndef WEBRTC_ANDROID | 
 |     const SettingsType desktop_settings[] = { | 
 |         SettingsType::kDefaultApmDesktop, SettingsType::kAllSubmodulesTurnedOff, | 
 |         SettingsType::kDefaultApmDesktopWithoutDelayAgnostic, | 
 |         SettingsType::kDefaultApmDesktopWithoutExtendedFilter}; | 
 |  | 
 |     const int desktop_sample_rates[] = {8000, 16000, 32000, 48000}; | 
 |  | 
 |     for (auto sample_rate : desktop_sample_rates) { | 
 |       for (auto settings : desktop_settings) { | 
 |         simulation_configs.push_back(SimulationConfig(sample_rate, settings)); | 
 |       } | 
 |     } | 
 | #endif | 
 |  | 
 |     const SettingsType mobile_settings[] = {SettingsType::kDefaultApmMobile}; | 
 |  | 
 |     const int mobile_sample_rates[] = {8000, 16000}; | 
 |  | 
 |     for (auto sample_rate : mobile_sample_rates) { | 
 |       for (auto settings : mobile_settings) { | 
 |         simulation_configs.push_back(SimulationConfig(sample_rate, settings)); | 
 |       } | 
 |     } | 
 |  | 
 |     return simulation_configs; | 
 |   } | 
 |  | 
 |   std::string SettingsDescription() const { | 
 |     std::string description; | 
 |     switch (simulation_settings) { | 
 |       case SettingsType::kDefaultApmMobile: | 
 |         description = "DefaultApmMobile"; | 
 |         break; | 
 |       case SettingsType::kDefaultApmDesktop: | 
 |         description = "DefaultApmDesktop"; | 
 |         break; | 
 |       case SettingsType::kAllSubmodulesTurnedOff: | 
 |         description = "AllSubmodulesOff"; | 
 |         break; | 
 |       case SettingsType::kDefaultApmDesktopWithoutDelayAgnostic: | 
 |         description = "DefaultApmDesktopWithoutDelayAgnostic"; | 
 |         break; | 
 |       case SettingsType::kDefaultApmDesktopWithoutExtendedFilter: | 
 |         description = "DefaultApmDesktopWithoutExtendedFilter"; | 
 |         break; | 
 |     } | 
 |     return description; | 
 |   } | 
 |  | 
 |   int sample_rate_hz = 16000; | 
 |   SettingsType simulation_settings = SettingsType::kDefaultApmDesktop; | 
 | }; | 
 |  | 
 | // Handler for the frame counters. | 
 | class FrameCounters { | 
 |  public: | 
 |   void IncreaseRenderCounter() { render_count_.fetch_add(1); } | 
 |  | 
 |   void IncreaseCaptureCounter() { capture_count_.fetch_add(1); } | 
 |  | 
 |   int CaptureMinusRenderCounters() const { | 
 |     // The return value will be approximate, but that's good enough since | 
 |     // by the time we return the value, it's not guaranteed to be correct | 
 |     // anyway. | 
 |     return capture_count_.load(std::memory_order_acquire) - | 
 |            render_count_.load(std::memory_order_acquire); | 
 |   } | 
 |  | 
 |   int RenderMinusCaptureCounters() const { | 
 |     return -CaptureMinusRenderCounters(); | 
 |   } | 
 |  | 
 |   bool BothCountersExceedeThreshold(int threshold) const { | 
 |     // TODO(tommi): We could use an event to signal this so that we don't need | 
 |     // to be polling from the main thread and possibly steal cycles. | 
 |     const int capture_count = capture_count_.load(std::memory_order_acquire); | 
 |     const int render_count = render_count_.load(std::memory_order_acquire); | 
 |     return (render_count > threshold && capture_count > threshold); | 
 |   } | 
 |  | 
 |  private: | 
 |   std::atomic<int> render_count_{0}; | 
 |   std::atomic<int> capture_count_{0}; | 
 | }; | 
 |  | 
 | // Class that represents a flag that can only be raised. | 
 | class LockedFlag { | 
 |  public: | 
 |   bool get_flag() const { return flag_.load(std::memory_order_acquire); } | 
 |  | 
 |   void set_flag() { | 
 |     if (!get_flag()) { | 
 |       // read-only operation to avoid affecting the cache-line. | 
 |       int zero = 0; | 
 |       flag_.compare_exchange_strong(zero, 1); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   std::atomic<int> flag_{0}; | 
 | }; | 
 |  | 
 | // Parent class for the thread processors. | 
 | class TimedThreadApiProcessor { | 
 |  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) | 
 |       : rand_gen_(rand_gen), | 
 |         frame_counters_(shared_counters_state), | 
 |         capture_call_checker_(capture_call_checker), | 
 |         test_(test_framework), | 
 |         simulation_config_(simulation_config), | 
 |         apm_(apm), | 
 |         frame_data_(kMaxFrameSize), | 
 |         clock_(Clock::GetRealTimeClock()), | 
 |         num_durations_to_store_(num_durations_to_store), | 
 |         api_call_durations_(num_durations_to_store_ - kNumInitializationFrames), | 
 |         samples_count_(0), | 
 |         input_level_(input_level), | 
 |         processor_type_(processor_type), | 
 |         num_channels_(num_channels) {} | 
 |  | 
 |   // Implements the callback functionality for the threads. | 
 |   bool Process(); | 
 |  | 
 |   // Method for printing out the simulation statistics. | 
 |   void print_processor_statistics(absl::string_view processor_name) const { | 
 |     const std::string modifier = "_api_call_duration"; | 
 |  | 
 |     const std::string sample_rate_name = | 
 |         "_" + std::to_string(simulation_config_->sample_rate_hz) + "Hz"; | 
 |  | 
 |     GetGlobalMetricsLogger()->LogMetric( | 
 |         "apm_timing" + sample_rate_name, processor_name, api_call_durations_, | 
 |         Unit::kMilliseconds, ImprovementDirection::kNeitherIsBetter); | 
 |   } | 
 |  | 
 |   void AddDuration(int64_t duration) { | 
 |     if (samples_count_ >= kNumInitializationFrames && | 
 |         samples_count_ < num_durations_to_store_) { | 
 |       api_call_durations_.AddSample(duration); | 
 |     } | 
 |     samples_count_++; | 
 |   } | 
 |  | 
 |  private: | 
 |   static const int kMaxCallDifference = 10; | 
 |   static const int kMaxFrameSize = 480; | 
 |   static const int kNumInitializationFrames = 5; | 
 |  | 
 |   int ProcessCapture() { | 
 |     // Set the stream delay. | 
 |     apm_->set_stream_delay_ms(30); | 
 |  | 
 |     // Call and time the specified capture side API processing method. | 
 |     const int64_t start_time = clock_->TimeInMicroseconds(); | 
 |     const int result = apm_->ProcessStream( | 
 |         &frame_data_.input_frame[0], frame_data_.input_stream_config, | 
 |         frame_data_.output_stream_config, &frame_data_.output_frame[0]); | 
 |     const int64_t end_time = clock_->TimeInMicroseconds(); | 
 |  | 
 |     frame_counters_->IncreaseCaptureCounter(); | 
 |  | 
 |     AddDuration(end_time - start_time); | 
 |  | 
 |     if (first_process_call_) { | 
 |       // Flag that the capture side has been called at least once | 
 |       // (needed to ensure that a capture call has been done | 
 |       // before the first render call is performed (implicitly | 
 |       // required by the APM API). | 
 |       capture_call_checker_->set_flag(); | 
 |       first_process_call_ = false; | 
 |     } | 
 |     return result; | 
 |   } | 
 |  | 
 |   bool ReadyToProcessCapture() { | 
 |     return (frame_counters_->CaptureMinusRenderCounters() <= | 
 |             kMaxCallDifference); | 
 |   } | 
 |  | 
 |   int ProcessRender() { | 
 |     // Call and time the specified render side API processing method. | 
 |     const int64_t start_time = clock_->TimeInMicroseconds(); | 
 |     const int result = apm_->ProcessReverseStream( | 
 |         &frame_data_.input_frame[0], frame_data_.input_stream_config, | 
 |         frame_data_.output_stream_config, &frame_data_.output_frame[0]); | 
 |     const int64_t end_time = clock_->TimeInMicroseconds(); | 
 |     frame_counters_->IncreaseRenderCounter(); | 
 |  | 
 |     AddDuration(end_time - start_time); | 
 |  | 
 |     return result; | 
 |   } | 
 |  | 
 |   bool ReadyToProcessRender() { | 
 |     // Do not process until at least one capture call has been done. | 
 |     // (implicitly required by the APM API). | 
 |     if (first_process_call_ && !capture_call_checker_->get_flag()) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     // Ensure that the number of render and capture calls do not differ too | 
 |     // much. | 
 |     if (frame_counters_->RenderMinusCaptureCounters() > kMaxCallDifference) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     first_process_call_ = false; | 
 |     return true; | 
 |   } | 
 |  | 
 |   void PrepareFrame() { | 
 |     // Lambda function for populating a float multichannel audio frame | 
 |     // with random data. | 
 |     auto populate_audio_frame = [](float amplitude, size_t num_channels, | 
 |                                    size_t samples_per_channel, Random* rand_gen, | 
 |                                    float** frame) { | 
 |       for (size_t ch = 0; ch < num_channels; ch++) { | 
 |         for (size_t k = 0; k < samples_per_channel; k++) { | 
 |           // Store random float number with a value between +-amplitude. | 
 |           frame[ch][k] = amplitude * (2 * rand_gen->Rand<float>() - 1); | 
 |         } | 
 |       } | 
 |     }; | 
 |  | 
 |     // Prepare the audio input data and metadata. | 
 |     frame_data_.input_stream_config.set_sample_rate_hz( | 
 |         simulation_config_->sample_rate_hz); | 
 |     frame_data_.input_stream_config.set_num_channels(num_channels_); | 
 |     populate_audio_frame(input_level_, num_channels_, | 
 |                          (simulation_config_->sample_rate_hz * | 
 |                           AudioProcessing::kChunkSizeMs / 1000), | 
 |                          rand_gen_, &frame_data_.input_frame[0]); | 
 |  | 
 |     // Prepare the float audio output data and metadata. | 
 |     frame_data_.output_stream_config.set_sample_rate_hz( | 
 |         simulation_config_->sample_rate_hz); | 
 |     frame_data_.output_stream_config.set_num_channels(1); | 
 |   } | 
 |  | 
 |   bool ReadyToProcess() { | 
 |     switch (processor_type_) { | 
 |       case ProcessorType::kRender: | 
 |         return ReadyToProcessRender(); | 
 |  | 
 |       case ProcessorType::kCapture: | 
 |         return ReadyToProcessCapture(); | 
 |     } | 
 |  | 
 |     // Should not be reached, but the return statement is needed for the code to | 
 |     // build successfully on Android. | 
 |     RTC_DCHECK_NOTREACHED(); | 
 |     return false; | 
 |   } | 
 |  | 
 |   Random* rand_gen_ = nullptr; | 
 |   FrameCounters* frame_counters_ = nullptr; | 
 |   LockedFlag* capture_call_checker_ = nullptr; | 
 |   CallSimulator* test_ = nullptr; | 
 |   const SimulationConfig* const simulation_config_ = nullptr; | 
 |   AudioProcessing* apm_ = nullptr; | 
 |   AudioFrameData frame_data_; | 
 |   Clock* clock_; | 
 |   const size_t num_durations_to_store_; | 
 |   SamplesStatsCounter api_call_durations_; | 
 |   size_t samples_count_ = 0; | 
 |   const float input_level_; | 
 |   bool first_process_call_ = true; | 
 |   const ProcessorType processor_type_; | 
 |   const int num_channels_ = 1; | 
 | }; | 
 |  | 
 | // Class for managing the test simulation. | 
 | class CallSimulator : public ::testing::TestWithParam<SimulationConfig> { | 
 |  public: | 
 |   CallSimulator() | 
 |       : rand_gen_(42U), | 
 |         simulation_config_(static_cast<SimulationConfig>(GetParam())) {} | 
 |  | 
 |   // Run the call simulation with a timeout. | 
 |   bool Run() { | 
 |     StartThreads(); | 
 |  | 
 |     bool result = test_complete_.Wait(kTestTimeout); | 
 |  | 
 |     StopThreads(); | 
 |  | 
 |     render_thread_state_->print_processor_statistics( | 
 |         simulation_config_.SettingsDescription() + "_render"); | 
 |     capture_thread_state_->print_processor_statistics( | 
 |         simulation_config_.SettingsDescription() + "_capture"); | 
 |  | 
 |     return result; | 
 |   } | 
 |  | 
 |   // Tests whether all the required render and capture side calls have been | 
 |   // done. | 
 |   bool MaybeEndTest() { | 
 |     if (frame_counters_.BothCountersExceedeThreshold(kMinNumFramesToProcess)) { | 
 |       test_complete_.Set(); | 
 |       return true; | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |  private: | 
 |   static const float kCaptureInputFloatLevel; | 
 |   static const float kRenderInputFloatLevel; | 
 |   static const int kMinNumFramesToProcess = 150; | 
 |   static constexpr TimeDelta kTestTimeout = | 
 |       TimeDelta::Millis(3 * 10 * kMinNumFramesToProcess); | 
 |  | 
 |   // Stop all running threads. | 
 |   void StopThreads() { | 
 |     render_thread_.Finalize(); | 
 |     capture_thread_.Finalize(); | 
 |   } | 
 |  | 
 |   // Simulator and APM setup. | 
 |   void SetUp() override { | 
 |     // Lambda function for setting the default APM runtime settings for desktop. | 
 |     auto set_default_desktop_apm_runtime_settings = [](AudioProcessing* apm) { | 
 |       AudioProcessing::Config apm_config = apm->GetConfig(); | 
 |       apm_config.echo_canceller.enabled = true; | 
 |       apm_config.echo_canceller.mobile_mode = false; | 
 |       apm_config.noise_suppression.enabled = true; | 
 |       apm_config.gain_controller1.enabled = true; | 
 |       apm_config.gain_controller1.mode = | 
 |           AudioProcessing::Config::GainController1::kAdaptiveDigital; | 
 |       apm->ApplyConfig(apm_config); | 
 |     }; | 
 |  | 
 |     // Lambda function for setting the default APM runtime settings for mobile. | 
 |     auto set_default_mobile_apm_runtime_settings = [](AudioProcessing* apm) { | 
 |       AudioProcessing::Config apm_config = apm->GetConfig(); | 
 |       apm_config.echo_canceller.enabled = true; | 
 |       apm_config.echo_canceller.mobile_mode = true; | 
 |       apm_config.noise_suppression.enabled = true; | 
 |       apm_config.gain_controller1.mode = | 
 |           AudioProcessing::Config::GainController1::kAdaptiveDigital; | 
 |       apm->ApplyConfig(apm_config); | 
 |     }; | 
 |  | 
 |     // Lambda function for turning off all of the APM runtime settings | 
 |     // submodules. | 
 |     auto turn_off_default_apm_runtime_settings = [](AudioProcessing* apm) { | 
 |       AudioProcessing::Config apm_config = apm->GetConfig(); | 
 |       apm_config.echo_canceller.enabled = false; | 
 |       apm_config.gain_controller1.enabled = false; | 
 |       apm_config.noise_suppression.enabled = false; | 
 |       apm->ApplyConfig(apm_config); | 
 |     }; | 
 |  | 
 |     int num_capture_channels = 1; | 
 |     switch (simulation_config_.simulation_settings) { | 
 |       case SettingsType::kDefaultApmMobile: { | 
 |         apm_ = BuiltinAudioProcessingBuilder().Build(CreateEnvironment()); | 
 |         ASSERT_TRUE(!!apm_); | 
 |         set_default_mobile_apm_runtime_settings(apm_.get()); | 
 |         break; | 
 |       } | 
 |       case SettingsType::kDefaultApmDesktop: { | 
 |         apm_ = BuiltinAudioProcessingBuilder().Build(CreateEnvironment()); | 
 |         ASSERT_TRUE(!!apm_); | 
 |         set_default_desktop_apm_runtime_settings(apm_.get()); | 
 |         break; | 
 |       } | 
 |       case SettingsType::kAllSubmodulesTurnedOff: { | 
 |         apm_ = BuiltinAudioProcessingBuilder().Build(CreateEnvironment()); | 
 |         ASSERT_TRUE(!!apm_); | 
 |         turn_off_default_apm_runtime_settings(apm_.get()); | 
 |         break; | 
 |       } | 
 |       case SettingsType::kDefaultApmDesktopWithoutDelayAgnostic: { | 
 |         apm_ = BuiltinAudioProcessingBuilder().Build(CreateEnvironment()); | 
 |         ASSERT_TRUE(!!apm_); | 
 |         set_default_desktop_apm_runtime_settings(apm_.get()); | 
 |         break; | 
 |       } | 
 |       case SettingsType::kDefaultApmDesktopWithoutExtendedFilter: { | 
 |         apm_ = BuiltinAudioProcessingBuilder().Build(CreateEnvironment()); | 
 |         ASSERT_TRUE(!!apm_); | 
 |         set_default_desktop_apm_runtime_settings(apm_.get()); | 
 |         break; | 
 |       } | 
 |     } | 
 |  | 
 |     render_thread_state_.reset(new TimedThreadApiProcessor( | 
 |         ProcessorType::kRender, &rand_gen_, &frame_counters_, | 
 |         &capture_call_checker_, this, &simulation_config_, apm_.get(), | 
 |         kMinNumFramesToProcess, kRenderInputFloatLevel, 1)); | 
 |     capture_thread_state_.reset(new TimedThreadApiProcessor( | 
 |         ProcessorType::kCapture, &rand_gen_, &frame_counters_, | 
 |         &capture_call_checker_, this, &simulation_config_, apm_.get(), | 
 |         kMinNumFramesToProcess, kCaptureInputFloatLevel, num_capture_channels)); | 
 |   } | 
 |  | 
 |   // Start the threads used in the test. | 
 |   void StartThreads() { | 
 |     const auto attributes = | 
 |         ThreadAttributes().SetPriority(ThreadPriority::kRealtime); | 
 |     render_thread_ = PlatformThread::SpawnJoinable( | 
 |         [this] { | 
 |           while (render_thread_state_->Process()) { | 
 |           } | 
 |         }, | 
 |         "render", attributes); | 
 |     capture_thread_ = PlatformThread::SpawnJoinable( | 
 |         [this] { | 
 |           while (capture_thread_state_->Process()) { | 
 |           } | 
 |         }, | 
 |         "capture", attributes); | 
 |   } | 
 |  | 
 |   // Event handler for the test. | 
 |   Event test_complete_; | 
 |  | 
 |   // Thread related variables. | 
 |   Random rand_gen_; | 
 |  | 
 |   scoped_refptr<AudioProcessing> apm_; | 
 |   const SimulationConfig simulation_config_; | 
 |   FrameCounters frame_counters_; | 
 |   LockedFlag capture_call_checker_; | 
 |   std::unique_ptr<TimedThreadApiProcessor> render_thread_state_; | 
 |   std::unique_ptr<TimedThreadApiProcessor> capture_thread_state_; | 
 |   PlatformThread render_thread_; | 
 |   PlatformThread capture_thread_; | 
 | }; | 
 |  | 
 | // Implements the callback functionality for the threads. | 
 | bool TimedThreadApiProcessor::Process() { | 
 |   PrepareFrame(); | 
 |  | 
 |   // Wait in a spinlock manner until it is ok to start processing. | 
 |   // Note that SleepMs is not applicable since it only allows sleeping | 
 |   // on a millisecond basis which is too long. | 
 |   // TODO(tommi): This loop may affect the performance of the test that it's | 
 |   // meant to measure.  See if we could use events instead to signal readiness. | 
 |   while (!ReadyToProcess()) { | 
 |   } | 
 |  | 
 |   int result = AudioProcessing::kNoError; | 
 |   switch (processor_type_) { | 
 |     case ProcessorType::kRender: | 
 |       result = ProcessRender(); | 
 |       break; | 
 |     case ProcessorType::kCapture: | 
 |       result = ProcessCapture(); | 
 |       break; | 
 |   } | 
 |  | 
 |   EXPECT_EQ(result, AudioProcessing::kNoError); | 
 |  | 
 |   return !test_->MaybeEndTest(); | 
 | } | 
 |  | 
 | const float CallSimulator::kRenderInputFloatLevel = 0.5f; | 
 | const float CallSimulator::kCaptureInputFloatLevel = 0.03125f; | 
 | }  // anonymous namespace | 
 |  | 
 | TEST_P(CallSimulator, ApiCallDurationTest) { | 
 |   // Run test and verify that it did not time out. | 
 |   EXPECT_TRUE(Run()); | 
 | } | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     AudioProcessingPerformanceTest, | 
 |     CallSimulator, | 
 |     ::testing::ValuesIn(SimulationConfig::GenerateSimulationConfigs())); | 
 |  | 
 | }  // namespace webrtc |