text: /*
   *  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 "modules/audio_processing/audio_processing_impl.h"
  
  #include <algorithm>
  #include <memory>
  #include <vector>
  
  #include "api/array_view.h"
  #include "modules/audio_processing/test/test_utils.h"
  #include "modules/include/module_common_types.h"
  #include "rtc_base/criticalsection.h"
  #include "rtc_base/event.h"
  #include "rtc_base/platform_thread.h"
  #include "rtc_base/random.h"
  #include "system_wrappers/include/sleep.h"
  #include "test/gtest.h"
  
  namespace webrtc {
  
  namespace {
  
  class AudioProcessingImplLockTest;
  
  // Type of the render thread APM API call to use in the test.
  enum class RenderApiImpl {
    ProcessReverseStreamImpl1,
    ProcessReverseStreamImpl2,
    AnalyzeReverseStreamImpl
  };
  
  // Type of the capture thread APM API call to use in the test.
  enum class CaptureApiImpl {
    ProcessStreamImpl1,
    ProcessStreamImpl2,
    ProcessStreamImpl3
  };
  
  // The runtime parameter setting scheme to use in the test.
  enum class RuntimeParameterSettingScheme {
    SparseStreamMetadataChangeScheme,
    ExtremeStreamMetadataChangeScheme,
    FixedMonoStreamMetadataScheme,
    FixedStereoStreamMetadataScheme
  };
  
  // Variant of echo canceller settings to use in the test.
  enum class AecType {
    BasicWebRtcAecSettings,
    AecTurnedOff,
    BasicWebRtcAecSettingsWithExtentedFilter,
    BasicWebRtcAecSettingsWithDelayAgnosticAec,
    BasicWebRtcAecSettingsWithAecMobile
  };
  
  // Thread-safe random number generator wrapper.
  class RandomGenerator {
   public:
    RandomGenerator() : rand_gen_(42U) {}
  
    int RandInt(int min, int max) {
      rtc::CritScope cs(&crit_);
      return rand_gen_.Rand(min, max);
    }
  
    int RandInt(int max) {
      rtc::CritScope cs(&crit_);
      return rand_gen_.Rand(max);
    }
  
    float RandFloat() {
      rtc::CritScope cs(&crit_);
      return rand_gen_.Rand<float>();
    }
  
   private:
    rtc::CriticalSection crit_;
    Random rand_gen_ RTC_GUARDED_BY(crit_);
  };
  
  // Variables related to the audio data and formats.
  struct AudioFrameData {
    explicit AudioFrameData(int 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];
    }
  
    AudioFrame frame;
    std::vector<float*> output_frame;
    std::vector<float> output_frame_channels;
    AudioProcessing::ChannelLayout output_channel_layout =
        AudioProcessing::ChannelLayout::kMono;
    int input_sample_rate_hz = 16000;
    int input_number_of_channels = -1;
    std::vector<float*> input_frame;
    std::vector<float> input_framechannels;
    AudioProcessing::ChannelLayout input_channel_layout =
        AudioProcessing::ChannelLayout::kMono;
    int output_sample_rate_hz = 16000;
    int output_number_of_channels = -1;
    StreamConfig input_stream_config;
    StreamConfig output_stream_config;
    int input_samples_per_channel = -1;
    int output_samples_per_channel = -1;
  };
  
  // The configuration for the test.
  struct TestConfig {
    // Test case generator for the test configurations to use in the brief tests.
    static std::vector<TestConfig> GenerateBriefTestConfigs() {
      std::vector<TestConfig> test_configs;
      AecType aec_types[] = {AecType::BasicWebRtcAecSettingsWithDelayAgnosticAec,
                             AecType::BasicWebRtcAecSettingsWithAecMobile};
      for (auto aec_type : aec_types) {
        TestConfig test_config;
        test_config.aec_type = aec_type;
  
        test_config.min_number_of_calls = 300;
  
        // Perform tests only with the extreme runtime parameter setting scheme.
        test_config.runtime_parameter_setting_scheme =
            RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme;
  
        // Only test 16 kHz for this test suite.
        test_config.initial_sample_rate_hz = 16000;
  
        // Create test config for the second processing API function set.
        test_config.render_api_function =
            RenderApiImpl::ProcessReverseStreamImpl2;
        test_config.capture_api_function = CaptureApiImpl::ProcessStreamImpl2;
  
        // Create test config for the first processing API function set.
        test_configs.push_back(test_config);
        test_config.render_api_function =
            RenderApiImpl::AnalyzeReverseStreamImpl;
        test_config.capture_api_function = CaptureApiImpl::ProcessStreamImpl3;
        test_configs.push_back(test_config);
      }
  
      // Return the created test configurations.
      return test_configs;
    }
  
    // Test case generator for the test configurations to use in the extensive
    // tests.
    static std::vector<TestConfig> GenerateExtensiveTestConfigs() {
      // Lambda functions for the test config generation.
      auto add_processing_apis = [](TestConfig test_config) {
        struct AllowedApiCallCombinations {
          RenderApiImpl render_api;
          CaptureApiImpl capture_api;
        };
  
        const AllowedApiCallCombinations api_calls[] = {
            {RenderApiImpl::ProcessReverseStreamImpl1,
             CaptureApiImpl::ProcessStreamImpl1},
            {RenderApiImpl::ProcessReverseStreamImpl2,
             CaptureApiImpl::ProcessStreamImpl2},
            {RenderApiImpl::ProcessReverseStreamImpl2,
             CaptureApiImpl::ProcessStreamImpl3},
            {RenderApiImpl::AnalyzeReverseStreamImpl,
             CaptureApiImpl::ProcessStreamImpl2},
            {RenderApiImpl::AnalyzeReverseStreamImpl,
             CaptureApiImpl::ProcessStreamImpl3}};
        std::vector<TestConfig> out;
        for (auto api_call : api_calls) {
          test_config.render_api_function = api_call.render_api;
          test_config.capture_api_function = api_call.capture_api;
          out.push_back(test_config);
        }
        return out;
      };
  
      auto add_aec_settings = [](const std::vector<TestConfig>& in) {
        std::vector<TestConfig> out;
        AecType aec_types[] = {
            AecType::BasicWebRtcAecSettings, AecType::AecTurnedOff,
            AecType::BasicWebRtcAecSettingsWithExtentedFilter,
            AecType::BasicWebRtcAecSettingsWithDelayAgnosticAec,
            AecType::BasicWebRtcAecSettingsWithAecMobile};
        for (auto test_config : in) {
          // Due to a VisualStudio 2015 compiler issue, the internal loop
          // variable here cannot override a previously defined name.
          // In other words "type" cannot be named "aec_type" here.
          // https://connect.microsoft.com/VisualStudio/feedback/details/2291755
          for (auto type : aec_types) {
            test_config.aec_type = type;
            out.push_back(test_config);
          }
        }
        return out;
      };
  
      auto add_settings_scheme = [](const std::vector<TestConfig>& in) {
        std::vector<TestConfig> out;
        RuntimeParameterSettingScheme schemes[] = {
            RuntimeParameterSettingScheme::SparseStreamMetadataChangeScheme,
            RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme,
            RuntimeParameterSettingScheme::FixedMonoStreamMetadataScheme,
            RuntimeParameterSettingScheme::FixedStereoStreamMetadataScheme};
  
        for (auto test_config : in) {
          for (auto scheme : schemes) {
            test_config.runtime_parameter_setting_scheme = scheme;
            out.push_back(test_config);
          }
        }
        return out;
      };
  
      auto add_sample_rates = [](const std::vector<TestConfig>& in) {
        const int sample_rates[] = {8000, 16000, 32000, 48000};
  
        std::vector<TestConfig> out;
        for (auto test_config : in) {
          auto available_rates =
              (test_config.aec_type ==
                       AecType::BasicWebRtcAecSettingsWithAecMobile
                   ? rtc::ArrayView<const int>(sample_rates, 2)
                   : rtc::ArrayView<const int>(sample_rates));
  
          for (auto rate : available_rates) {
            test_config.initial_sample_rate_hz = rate;
            out.push_back(test_config);
          }
        }
        return out;
      };
  
      // Generate test configurations of the relevant combinations of the
      // parameters to
      // test.
      TestConfig test_config;
      test_config.min_number_of_calls = 10000;
      return add_sample_rates(add_settings_scheme(
          add_aec_settings(add_processing_apis(test_config))));
    }
  
    RenderApiImpl render_api_function = RenderApiImpl::ProcessReverseStreamImpl2;
    CaptureApiImpl capture_api_function = CaptureApiImpl::ProcessStreamImpl2;
    RuntimeParameterSettingScheme runtime_parameter_setting_scheme =
        RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme;
    int initial_sample_rate_hz = 16000;
    AecType aec_type = AecType::BasicWebRtcAecSettingsWithDelayAgnosticAec;
    int min_number_of_calls = 300;
  };
  
  // Handler for the frame counters.
  class FrameCounters {
   public:
    void IncreaseRenderCounter() {
      rtc::CritScope cs(&crit_);
      render_count++;
    }
  
    void IncreaseCaptureCounter() {
      rtc::CritScope cs(&crit_);
      capture_count++;
    }
  
    int GetCaptureCounter() const {
      rtc::CritScope cs(&crit_);
      return capture_count;
    }
  
    int GetRenderCounter() const {
      rtc::CritScope cs(&crit_);
      return render_count;
    }
  
    int CaptureMinusRenderCounters() const {
      rtc::CritScope cs(&crit_);
      return capture_count - render_count;
    }
  
    int RenderMinusCaptureCounters() const {
      return -CaptureMinusRenderCounters();
    }
  
    bool BothCountersExceedeThreshold(int threshold) {
      rtc::CritScope cs(&crit_);
      return (render_count > threshold && capture_count > threshold);
    }
  
   private:
    rtc::CriticalSection crit_;
    int render_count RTC_GUARDED_BY(crit_) = 0;
    int capture_count RTC_GUARDED_BY(crit_) = 0;
  };
  
  // Class for handling the capture side processing.
  class CaptureProcessor {
   public:
    CaptureProcessor(int max_frame_size,
                     RandomGenerator* rand_gen,
                     rtc::Event* render_call_event,
                     rtc::Event* capture_call_event,
                     FrameCounters* shared_counters_state,
                     AudioProcessingImplLockTest* test_framework,
                     TestConfig* test_config,
                     AudioProcessing* apm);
    bool Process();
  
   private:
    static const int kMaxCallDifference = 10;
    static const float kCaptureInputFloatLevel;
    static const int kCaptureInputFixLevel = 1024;
  
    void PrepareFrame();
    void CallApmCaptureSide();
    void ApplyRuntimeSettingScheme();
  
    RandomGenerator* const rand_gen_ = nullptr;
    rtc::Event* const render_call_event_ = nullptr;
    rtc::Event* const capture_call_event_ = nullptr;
    FrameCounters* const frame_counters_ = nullptr;
    AudioProcessingImplLockTest* const test_ = nullptr;
    const TestConfig* const test_config_ = nullptr;
    AudioProcessing* const apm_ = nullptr;
    AudioFrameData frame_data_;
  };
  
  // Class for handling the stats processing.
  class StatsProcessor {
   public:
    StatsProcessor(RandomGenerator* rand_gen,
                   TestConfig* test_config,
                   AudioProcessing* apm);
    bool Process();
  
   private:
    RandomGenerator* rand_gen_ = nullptr;
    TestConfig* test_config_ = nullptr;
    AudioProcessing* apm_ = nullptr;
  };
  
  // Class for handling the render side processing.
  class RenderProcessor {
   public:
    RenderProcessor(int max_frame_size,
                    RandomGenerator* rand_gen,
                    rtc::Event* render_call_event,
                    rtc::Event* capture_call_event,
                    FrameCounters* shared_counters_state,
                    AudioProcessingImplLockTest* test_framework,
                    TestConfig* test_config,
                    AudioProcessing* apm);
    bool Process();
  
   private:
    static const int kMaxCallDifference = 10;
    static const int kRenderInputFixLevel = 16384;
    static const float kRenderInputFloatLevel;
  
    void PrepareFrame();
    void CallApmRenderSide();
    void ApplyRuntimeSettingScheme();
  
    RandomGenerator* const rand_gen_ = nullptr;
    rtc::Event* const render_call_event_ = nullptr;
    rtc::Event* const capture_call_event_ = nullptr;
    FrameCounters* const frame_counters_ = nullptr;
    AudioProcessingImplLockTest* const test_ = nullptr;
    const TestConfig* const test_config_ = nullptr;
    AudioProcessing* const apm_ = nullptr;
    AudioFrameData frame_data_;
    bool first_render_call_ = true;
  };
  
  class AudioProcessingImplLockTest
      : public ::testing::TestWithParam<TestConfig> {
   public:
    AudioProcessingImplLockTest();
    bool RunTest();
    bool MaybeEndTest();
  
   private:
    static const int kTestTimeOutLimit = 10 * 60 * 1000;
    static const int kMaxFrameSize = 480;
  
    // ::testing::TestWithParam<> implementation
    void SetUp() override;
    void TearDown() override;
  
    // Thread callback for the render thread
    static bool RenderProcessorThreadFunc(void* context) {
      return reinterpret_cast<AudioProcessingImplLockTest*>(context)
          ->render_thread_state_.Process();
    }
  
    // Thread callback for the capture thread
    static bool CaptureProcessorThreadFunc(void* context) {
      return reinterpret_cast<AudioProcessingImplLockTest*>(context)
          ->capture_thread_state_.Process();
    }
  
    // Thread callback for the stats thread
    static bool StatsProcessorThreadFunc(void* context) {
      return reinterpret_cast<AudioProcessingImplLockTest*>(context)
          ->stats_thread_state_.Process();
    }
  
    // Tests whether all the required render and capture side calls have been
    // done.
    bool TestDone() {
      return frame_counters_.BothCountersExceedeThreshold(
          test_config_.min_number_of_calls);
    }
  
    // Start the threads used in the test.
    void StartThreads() {
      render_thread_.Start();
      render_thread_.SetPriority(rtc::kRealtimePriority);
      capture_thread_.Start();
      capture_thread_.SetPriority(rtc::kRealtimePriority);
      stats_thread_.Start();
      stats_thread_.SetPriority(rtc::kNormalPriority);
    }
  
    // Event handlers for the test.
    rtc::Event test_complete_;
    rtc::Event render_call_event_;
    rtc::Event capture_call_event_;
  
    // Thread related variables.
    rtc::PlatformThread render_thread_;
    rtc::PlatformThread capture_thread_;
    rtc::PlatformThread stats_thread_;
    mutable RandomGenerator rand_gen_;
  
    std::unique_ptr<AudioProcessing> apm_;
    TestConfig test_config_;
    FrameCounters frame_counters_;
    RenderProcessor render_thread_state_;
    CaptureProcessor capture_thread_state_;
    StatsProcessor stats_thread_state_;
  };
  
  // Sleeps a random time between 0 and max_sleep milliseconds.
  void SleepRandomMs(int max_sleep, RandomGenerator* rand_gen) {
    int sleeptime = rand_gen->RandInt(0, max_sleep);
    SleepMs(sleeptime);
  }
  
  // Populates a float audio frame with random data.
  void PopulateAudioFrame(float** frame,
                          float amplitude,
                          size_t num_channels,
                          size_t samples_per_channel,
                          RandomGenerator* rand_gen) {
    for (size_t ch = 0; ch < num_channels; ch++) {
      for (size_t k = 0; k < samples_per_channel; k++) {
        // Store random 16 bit quantized float number between +-amplitude.
        frame[ch][k] = amplitude * (2 * rand_gen->RandFloat() - 1);
      }
    }
  }
  
  // Populates an audioframe frame of AudioFrame type with random data.
  void PopulateAudioFrame(AudioFrame* frame,
                          int16_t amplitude,
                          RandomGenerator* rand_gen) {
    ASSERT_GT(amplitude, 0);
    ASSERT_LE(amplitude, 32767);
    int16_t* frame_data = frame->mutable_data();
    for (size_t ch = 0; ch < frame->num_channels_; ch++) {
      for (size_t k = 0; k < frame->samples_per_channel_; k++) {
        // Store random 16 bit number between -(amplitude+1) and
        // amplitude.
        frame_data[k * ch] =
            rand_gen->RandInt(2 * amplitude + 1) - amplitude - 1;
      }
    }
  }
  
  AudioProcessingImplLockTest::AudioProcessingImplLockTest()
      : test_complete_(false, false),
        render_call_event_(false, false),
        capture_call_event_(false, false),
        render_thread_(RenderProcessorThreadFunc, this, "render"),
        capture_thread_(CaptureProcessorThreadFunc, this, "capture"),
        stats_thread_(StatsProcessorThreadFunc, this, "stats"),
        apm_(AudioProcessingBuilder().Create()),
        render_thread_state_(kMaxFrameSize,
                             &rand_gen_,
                             &render_call_event_,
                             &capture_call_event_,
                             &frame_counters_,
                             this,
                             &test_config_,
                             apm_.get()),
        capture_thread_state_(kMaxFrameSize,
                              &rand_gen_,
                              &render_call_event_,
                              &capture_call_event_,
                              &frame_counters_,
                              this,
                              &test_config_,
                              apm_.get()),
        stats_thread_state_(&rand_gen_, &test_config_, apm_.get()) {}
  
  // Run the test with a timeout.
  bool AudioProcessingImplLockTest::RunTest() {
    StartThreads();
    return test_complete_.Wait(kTestTimeOutLimit);
  }
  
  bool AudioProcessingImplLockTest::MaybeEndTest() {
    if (HasFatalFailure() || TestDone()) {
      test_complete_.Set();
      return true;
    }
    return false;
  }
  
  // Setup of test and APM.
  void AudioProcessingImplLockTest::SetUp() {
    test_config_ = static_cast<TestConfig>(GetParam());
  
    ASSERT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true));
    ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
  
    ASSERT_EQ(apm_->kNoError,
              apm_->gain_control()->set_mode(GainControl::kAdaptiveDigital));
    ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
  
    ASSERT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true));
    ASSERT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true));
  
    Config config;
    if (test_config_.aec_type == AecType::AecTurnedOff) {
      ASSERT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(false));
      ASSERT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(false));
    } else if (test_config_.aec_type ==
               AecType::BasicWebRtcAecSettingsWithAecMobile) {
      ASSERT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true));
      ASSERT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(false));
    } else {
      ASSERT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(false));
      ASSERT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true));
      ASSERT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_metrics(true));
      ASSERT_EQ(apm_->kNoError,
                apm_->echo_cancellation()->enable_delay_logging(true));
  
      config.Set<ExtendedFilter>(
          new ExtendedFilter(test_config_.aec_type ==
                             AecType::BasicWebRtcAecSettingsWithExtentedFilter));
  
      config.Set<DelayAgnostic>(
          new DelayAgnostic(test_config_.aec_type ==
                            AecType::BasicWebRtcAecSettingsWithDelayAgnosticAec));
  
      apm_->SetExtraOptions(config);
    }
  }
  
  void AudioProcessingImplLockTest::TearDown() {
    render_call_event_.Set();
    capture_call_event_.Set();
    render_thread_.Stop();
    capture_thread_.Stop();
    stats_thread_.Stop();
  }
  
  StatsProcessor::StatsProcessor(RandomGenerator* rand_gen,
                                 TestConfig* test_config,
                                 AudioProcessing* apm)
      : rand_gen_(rand_gen), test_config_(test_config), apm_(apm) {}
  
  // Implements the callback functionality for the statistics
  // collection thread.
  bool StatsProcessor::Process() {
    SleepRandomMs(100, rand_gen_);
  
    EXPECT_EQ(apm_->echo_cancellation()->is_enabled(),
              ((test_config_->aec_type != AecType::AecTurnedOff) &&
               (test_config_->aec_type !=
                AecType::BasicWebRtcAecSettingsWithAecMobile)));
    apm_->echo_cancellation()->stream_drift_samples();
    EXPECT_EQ(apm_->echo_control_mobile()->is_enabled(),
              (test_config_->aec_type != AecType::AecTurnedOff) &&
                  (test_config_->aec_type ==
                   AecType::BasicWebRtcAecSettingsWithAecMobile));
    EXPECT_TRUE(apm_->gain_control()->is_enabled());
    EXPECT_TRUE(apm_->noise_suppression()->is_enabled());
  
    // The below return values are not testable.
    apm_->noise_suppression()->speech_probability();
    apm_->voice_detection()->is_enabled();
  
    return true;
  }
  
  const float CaptureProcessor::kCaptureInputFloatLevel = 0.03125f;
  
  CaptureProcessor::CaptureProcessor(int max_frame_size,
                                     RandomGenerator* rand_gen,
                                     rtc::Event* render_call_event,
                                     rtc::Event* capture_call_event,
                                     FrameCounters* shared_counters_state,
                                     AudioProcessingImplLockTest* test_framework,
                                     TestConfig* test_config,
                                     AudioProcessing* apm)
      : rand_gen_(rand_gen),
        render_call_event_(render_call_event),
        capture_call_event_(capture_call_event),
        frame_counters_(shared_counters_state),
        test_(test_framework),
        test_config_(test_config),
        apm_(apm),
        frame_data_(max_frame_size) {}
  
  // Implements the callback functionality for the capture thread.
  bool CaptureProcessor::Process() {
    // Sleep a random time to simulate thread jitter.
    SleepRandomMs(3, rand_gen_);
  
    // Check whether the test is done.
    if (test_->MaybeEndTest()) {
      return false;
    }
  
    // Ensure that the number of render and capture calls do not
    // differ too much.
    if (frame_counters_->CaptureMinusRenderCounters() > kMaxCallDifference) {
      render_call_event_->Wait(rtc::Event::kForever);
    }
  
    // Apply any specified capture side APM non-processing runtime calls.
    ApplyRuntimeSettingScheme();
  
    // Apply the capture side processing call.
    CallApmCaptureSide();
  
    // Increase the number of capture-side calls.
    frame_counters_->IncreaseCaptureCounter();
  
    // Flag to the render thread that another capture API call has occurred
    // by triggering this threads call event.
    capture_call_event_->Set();
  
    return true;
  }
  
  // Prepares a frame with relevant audio data and metadata.
  void CaptureProcessor::PrepareFrame() {
    // Restrict to a common fixed sample rate if the AudioFrame
    // interface is used.
    if (test_config_->capture_api_function ==
        CaptureApiImpl::ProcessStreamImpl1) {
      frame_data_.input_sample_rate_hz = test_config_->initial_sample_rate_hz;
      frame_data_.output_sample_rate_hz = test_config_->initial_sample_rate_hz;
    }
  
    // Prepare the audioframe data and metadata.
    frame_data_.input_samples_per_channel =
        frame_data_.input_sample_rate_hz * AudioProcessing::kChunkSizeMs / 1000;
    frame_data_.frame.sample_rate_hz_ = frame_data_.input_sample_rate_hz;
    frame_data_.frame.num_channels_ = frame_data_.input_number_of_channels;
    frame_data_.frame.samples_per_channel_ =
        frame_data_.input_samples_per_channel;
    PopulateAudioFrame(&frame_data_.frame, kCaptureInputFixLevel, rand_gen_);
  
    // Prepare the float audio input data and metadata.
    frame_data_.input_stream_config.set_sample_rate_hz(
        frame_data_.input_sample_rate_hz);
    frame_data_.input_stream_config.set_num_channels(
        frame_data_.input_number_of_channels);
    frame_data_.input_stream_config.set_has_keyboard(false);
    PopulateAudioFrame(&frame_data_.input_frame[0], kCaptureInputFloatLevel,
                       frame_data_.input_number_of_channels,
                       frame_data_.input_samples_per_channel, rand_gen_);
    frame_data_.input_channel_layout =
        (frame_data_.input_number_of_channels == 1
             ? AudioProcessing::ChannelLayout::kMono
             : AudioProcessing::ChannelLayout::kStereo);
  
    // Prepare the float audio output data and metadata.
    frame_data_.output_samples_per_channel =
        frame_data_.output_sample_rate_hz * AudioProcessing::kChunkSizeMs / 1000;
    frame_data_.output_stream_config.set_sample_rate_hz(
        frame_data_.output_sample_rate_hz);
    frame_data_.output_stream_config.set_num_channels(
        frame_data_.output_number_of_channels);
    frame_data_.output_stream_config.set_has_keyboard(false);
    frame_data_.output_channel_layout =
        (frame_data_.output_number_of_channels == 1
             ? AudioProcessing::ChannelLayout::kMono
             : AudioProcessing::ChannelLayout::kStereo);
  }
  
  // Applies the capture side processing API call.
  void CaptureProcessor::CallApmCaptureSide() {
    // Prepare a proper capture side processing API call input.
    PrepareFrame();
  
    // Set the stream delay.
    apm_->set_stream_delay_ms(30);
  
    // Set the analog level.
    apm_->gain_control()->set_stream_analog_level(80);
  
    // Call the specified capture side API processing method.
    int result = AudioProcessing::kNoError;
    switch (test_config_->capture_api_function) {
      case CaptureApiImpl::ProcessStreamImpl1:
        result = apm_->ProcessStream(&frame_data_.frame);
        break;
      case CaptureApiImpl::ProcessStreamImpl2:
        result = apm_->ProcessStream(
            &frame_data_.input_frame[0], frame_data_.input_samples_per_channel,
            frame_data_.input_sample_rate_hz, frame_data_.input_channel_layout,
            frame_data_.output_sample_rate_hz, frame_data_.output_channel_layout,
            &frame_data_.output_frame[0]);
        break;
      case CaptureApiImpl::ProcessStreamImpl3:
        result = apm_->ProcessStream(
            &frame_data_.input_frame[0], frame_data_.input_stream_config,
            frame_data_.output_stream_config, &frame_data_.output_frame[0]);
        break;
      default:
        FAIL();
    }
  
    // Retrieve the new analog level.
    apm_->gain_control()->stream_analog_level();
  
    // Check the return code for error.
    ASSERT_EQ(AudioProcessing::kNoError, result);
  }
  
  // Applies any runtime capture APM API calls and audio stream characteristics
  // specified by the scheme for the test.
  void CaptureProcessor::ApplyRuntimeSettingScheme() {
    const int capture_count_local = frame_counters_->GetCaptureCounter();
  
    // Update the number of channels and sample rates for the input and output.
    // Note that the counts frequencies for when to set parameters
    // are set using prime numbers in order to ensure that the
    // permutation scheme in the parameter setting changes.
    switch (test_config_->runtime_parameter_setting_scheme) {
      case RuntimeParameterSettingScheme::SparseStreamMetadataChangeScheme:
        if (capture_count_local == 0)
          frame_data_.input_sample_rate_hz = 16000;
        else if (capture_count_local % 11 == 0)
          frame_data_.input_sample_rate_hz = 32000;
        else if (capture_count_local % 73 == 0)
          frame_data_.input_sample_rate_hz = 48000;
        else if (capture_count_local % 89 == 0)
          frame_data_.input_sample_rate_hz = 16000;
        else if (capture_count_local % 97 == 0)
          frame_data_.input_sample_rate_hz = 8000;
  
        if (capture_count_local == 0)
          frame_data_.input_number_of_channels = 1;
        else if (capture_count_local % 4 == 0)
          frame_data_.input_number_of_channels =
              (frame_data_.input_number_of_channels == 1 ? 2 : 1);
  
        if (capture_count_local == 0)
          frame_data_.output_sample_rate_hz = 16000;
        else if (capture_count_local % 5 == 0)
          frame_data_.output_sample_rate_hz = 32000;
        else if (capture_count_local % 47 == 0)
          frame_data_.output_sample_rate_hz = 48000;
        else if (capture_count_local % 53 == 0)
          frame_data_.output_sample_rate_hz = 16000;
        else if (capture_count_local % 71 == 0)
          frame_data_.output_sample_rate_hz = 8000;
  
        if (capture_count_local == 0)
          frame_data_.output_number_of_channels = 1;
        else if (capture_count_local % 8 == 0)
          frame_data_.output_number_of_channels =
              (frame_data_.output_number_of_channels == 1 ? 2 : 1);
        break;
      case RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme:
        if (capture_count_local % 2 == 0) {
          frame_data_.input_number_of_channels = 1;
          frame_data_.input_sample_rate_hz = 16000;
          frame_data_.output_number_of_channels = 1;
          frame_data_.output_sample_rate_hz = 16000;
        } else {
          frame_data_.input_number_of_channels =
              (frame_data_.input_number_of_channels == 1 ? 2 : 1);
          if (frame_data_.input_sample_rate_hz == 8000)
            frame_data_.input_sample_rate_hz = 16000;
          else if (frame_data_.input_sample_rate_hz == 16000)
            frame_data_.input_sample_rate_hz = 32000;
          else if (frame_data_.input_sample_rate_hz == 32000)
            frame_data_.input_sample_rate_hz = 48000;
          else if (frame_data_.input_sample_rate_hz == 48000)
            frame_data_.input_sample_rate_hz = 8000;
  
          frame_data_.output_number_of_channels =
              (frame_data_.output_number_of_channels == 1 ? 2 : 1);
          if (frame_data_.output_sample_rate_hz == 8000)
            frame_data_.output_sample_rate_hz = 16000;
          else if (frame_data_.output_sample_rate_hz == 16000)
            frame_data_.output_sample_rate_hz = 32000;
          else if (frame_data_.output_sample_rate_hz == 32000)
            frame_data_.output_sample_rate_hz = 48000;
          else if (frame_data_.output_sample_rate_hz == 48000)
            frame_data_.output_sample_rate_hz = 8000;
        }
        break;
      case RuntimeParameterSettingScheme::FixedMonoStreamMetadataScheme:
        if (capture_count_local == 0) {
          frame_data_.input_sample_rate_hz = 16000;
          frame_data_.input_number_of_channels = 1;
          frame_data_.output_sample_rate_hz = 16000;
          frame_data_.output_number_of_channels = 1;
        }
        break;
      case RuntimeParameterSettingScheme::FixedStereoStreamMetadataScheme:
        if (capture_count_local == 0) {
          frame_data_.input_sample_rate_hz = 16000;
          frame_data_.input_number_of_channels = 2;
          frame_data_.output_sample_rate_hz = 16000;
          frame_data_.output_number_of_channels = 2;
        }
        break;
      default:
        FAIL();
    }
  
    // Call any specified runtime APM setter and
    // getter calls.
    switch (test_config_->runtime_parameter_setting_scheme) {
      case RuntimeParameterSettingScheme::SparseStreamMetadataChangeScheme:
      case RuntimeParameterSettingScheme::FixedMonoStreamMetadataScheme:
        break;
      case RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme:
      case RuntimeParameterSettingScheme::FixedStereoStreamMetadataScheme:
        if (capture_count_local % 2 == 0) {
          ASSERT_EQ(AudioProcessing::Error::kNoError,
                    apm_->set_stream_delay_ms(30));
          apm_->set_stream_key_pressed(true);
          apm_->set_delay_offset_ms(15);
          EXPECT_EQ(apm_->delay_offset_ms(), 15);
        } else {
          ASSERT_EQ(AudioProcessing::Error::kNoError,
                    apm_->set_stream_delay_ms(50));
          apm_->set_stream_key_pressed(false);
          apm_->set_delay_offset_ms(20);
          EXPECT_EQ(apm_->delay_offset_ms(), 20);
          apm_->delay_offset_ms();
        }
        break;
      default:
        FAIL();
    }
  
    // Restric the number of output channels not to exceed
    // the number of input channels.
    frame_data_.output_number_of_channels =
        std::min(frame_data_.output_number_of_channels,
                 frame_data_.input_number_of_channels);
  }
  
  const float RenderProcessor::kRenderInputFloatLevel = 0.5f;
  
  RenderProcessor::RenderProcessor(int max_frame_size,
                                   RandomGenerator* rand_gen,
                                   rtc::Event* render_call_event,
                                   rtc::Event* capture_call_event,
                                   FrameCounters* shared_counters_state,
                                   AudioProcessingImplLockTest* test_framework,
                                   TestConfig* test_config,
                                   AudioProcessing* apm)
      : rand_gen_(rand_gen),
        render_call_event_(render_call_event),
        capture_call_event_(capture_call_event),
        frame_counters_(shared_counters_state),
        test_(test_framework),
        test_config_(test_config),
        apm_(apm),
        frame_data_(max_frame_size) {}
  
  // Implements the callback functionality for the render thread.
  bool RenderProcessor::Process() {
    // Conditional wait to ensure that a capture call has been done
    // before the first render call is performed (implicitly
    // required by the APM API).
    if (first_render_call_) {
      capture_call_event_->Wait(rtc::Event::kForever);
      first_render_call_ = false;
    }
  
    // Sleep a random time to simulate thread jitter.
    SleepRandomMs(3, rand_gen_);
  
    // Check whether the test is done.
    if (test_->MaybeEndTest()) {
      return false;
    }
  
    // Ensure that the number of render and capture calls do not
    // differ too much.
    if (frame_counters_->RenderMinusCaptureCounters() > kMaxCallDifference) {
      capture_call_event_->Wait(rtc::Event::kForever);
    }
  
    // Apply any specified render side APM non-processing runtime calls.
    ApplyRuntimeSettingScheme();
  
    // Apply the render side processing call.
    CallApmRenderSide();
  
    // Increase the number of render-side calls.
    frame_counters_->IncreaseRenderCounter();
  
    // Flag to the capture thread that another render API call has occurred
    // by triggering this threads call event.
    render_call_event_->Set();
    return true;
  }
  
  // Prepares the render side frame and the accompanying metadata
  // with the appropriate information.
  void RenderProcessor::PrepareFrame() {
    // Restrict to a common fixed sample rate if the AudioFrame interface is
    // used.
    if ((test_config_->render_api_function ==
         RenderApiImpl::ProcessReverseStreamImpl1) ||
        (test_config_->aec_type !=
         AecType::BasicWebRtcAecSettingsWithAecMobile)) {
      frame_data_.input_sample_rate_hz = test_config_->initial_sample_rate_hz;
      frame_data_.output_sample_rate_hz = test_config_->initial_sample_rate_hz;
    }
  
    // Prepare the audioframe data and metadata
    frame_data_.input_samples_per_channel =
        frame_data_.input_sample_rate_hz * AudioProcessing::kChunkSizeMs / 1000;
    frame_data_.frame.sample_rate_hz_ = frame_data_.input_sample_rate_hz;
    frame_data_.frame.num_channels_ = frame_data_.input_number_of_channels;
    frame_data_.frame.samples_per_channel_ =
        frame_data_.input_samples_per_channel;
    PopulateAudioFrame(&frame_data_.frame, kRenderInputFixLevel, rand_gen_);
  
    // Prepare the float audio input data and metadata.
    frame_data_.input_stream_config.set_sample_rate_hz(
        frame_data_.input_sample_rate_hz);
    frame_data_.input_stream_config.set_num_channels(
        frame_data_.input_number_of_channels);
    frame_data_.input_stream_config.set_has_keyboard(false);
    PopulateAudioFrame(&frame_data_.input_frame[0], kRenderInputFloatLevel,
                       frame_data_.input_number_of_channels,
                       frame_data_.input_samples_per_channel, rand_gen_);
    frame_data_.input_channel_layout =
        (frame_data_.input_number_of_channels == 1
             ? AudioProcessing::ChannelLayout::kMono
             : AudioProcessing::ChannelLayout::kStereo);
  
    // Prepare the float audio output data and metadata.
    frame_data_.output_samples_per_channel =
        frame_data_.output_sample_rate_hz * AudioProcessing::kChunkSizeMs / 1000;
    frame_data_.output_stream_config.set_sample_rate_hz(
        frame_data_.output_sample_rate_hz);
    frame_data_.output_stream_config.set_num_channels(
        frame_data_.output_number_of_channels);
    frame_data_.output_stream_config.set_has_keyboard(false);
    frame_data_.output_channel_layout =
        (frame_data_.output_number_of_channels == 1
             ? AudioProcessing::ChannelLayout::kMono
             : AudioProcessing::ChannelLayout::kStereo);
  }
  
  // Makes the render side processing API call.
  void RenderProcessor::CallApmRenderSide() {
    // Prepare a proper render side processing API call input.
    PrepareFrame();
  
    // Call the specified render side API processing method.
    int result = AudioProcessing::kNoError;
    switch (test_config_->render_api_function) {
      case RenderApiImpl::ProcessReverseStreamImpl1:
        result = apm_->ProcessReverseStream(&frame_data_.frame);
        break;
      case RenderApiImpl::ProcessReverseStreamImpl2:
        result = apm_->ProcessReverseStream(
            &frame_data_.input_frame[0], frame_data_.input_stream_config,
            frame_data_.output_stream_config, &frame_data_.output_frame[0]);
        break;
      case RenderApiImpl::AnalyzeReverseStreamImpl:
        result = apm_->AnalyzeReverseStream(
            &frame_data_.input_frame[0], frame_data_.input_samples_per_channel,
            frame_data_.input_sample_rate_hz, frame_data_.input_channel_layout);
        break;
      default:
        FAIL();
    }
  
    // Check the return code for error.
    ASSERT_EQ(AudioProcessing::kNoError, result);
  }
  
  // Applies any render capture side APM API calls and audio stream
  // characteristics
  // specified by the scheme for the test.
  void RenderProcessor::ApplyRuntimeSettingScheme() {
    const int render_count_local = frame_counters_->GetRenderCounter();
  
    // Update the number of channels and sample rates for the input and output.
    // Note that the counts frequencies for when to set parameters
    // are set using prime numbers in order to ensure that the
    // permutation scheme in the parameter setting changes.
    switch (test_config_->runtime_parameter_setting_scheme) {
      case RuntimeParameterSettingScheme::SparseStreamMetadataChangeScheme:
        if (render_count_local == 0)
          frame_data_.input_sample_rate_hz = 16000;
        else if (render_count_local % 47 == 0)
          frame_data_.input_sample_rate_hz = 32000;
        else if (render_count_local % 71 == 0)
          frame_data_.input_sample_rate_hz = 48000;
        else if (render_count_local % 79 == 0)
          frame_data_.input_sample_rate_hz = 16000;
        else if (render_count_local % 83 == 0)
          frame_data_.input_sample_rate_hz = 8000;
  
        if (render_count_local == 0)
          frame_data_.input_number_of_channels = 1;
        else if (render_count_local % 4 == 0)
          frame_data_.input_number_of_channels =
              (frame_data_.input_number_of_channels == 1 ? 2 : 1);
  
        if (render_count_local == 0)
          frame_data_.output_sample_rate_hz = 16000;
        else if (render_count_local % 17 == 0)
          frame_data_.output_sample_rate_hz = 32000;
        else if (render_count_local % 19 == 0)
          frame_data_.output_sample_rate_hz = 48000;
        else if (render_count_local % 29 == 0)
          frame_data_.output_sample_rate_hz = 16000;
        else if (render_count_local % 61 == 0)
          frame_data_.output_sample_rate_hz = 8000;
  
        if (render_count_local == 0)
          frame_data_.output_number_of_channels = 1;
        else if (render_count_local % 8 == 0)
          frame_data_.output_number_of_channels =
              (frame_data_.output_number_of_channels == 1 ? 2 : 1);
        break;
      case RuntimeParameterSettingScheme::ExtremeStreamMetadataChangeScheme:
        if (render_count_local == 0) {
          frame_data_.input_number_of_channels = 1;
          frame_data_.input_sample_rate_hz = 16000;
          frame_data_.output_number_of_channels = 1;
          frame_data_.output_sample_rate_hz = 16000;
        } else {
          frame_data_.input_number_of_channels =
              (frame_data_.input_number_of_channels == 1 ? 2 : 1);
          if (frame_data_.input_sample_rate_hz == 8000)
            frame_data_.input_sample_rate_hz = 16000;
          else if (frame_data_.input_sample_rate_hz == 16000)
            frame_data_.input_sample_rate_hz = 32000;
          else if (frame_data_.input_sample_rate_hz == 32000)
            frame_data_.input_sample_rate_hz = 48000;
          else if (frame_data_.input_sample_rate_hz == 48000)
            frame_data_.input_sample_rate_hz = 8000;
  
          frame_data_.output_number_of_channels =
              (frame_data_.output_number_of_channels == 1 ? 2 : 1);
          if (frame_data_.output_sample_rate_hz == 8000)
            frame_data_.output_sample_rate_hz = 16000;
          else if (frame_data_.output_sample_rate_hz == 16000)
            frame_data_.output_sample_rate_hz = 32000;
          else if (frame_data_.output_sample_rate_hz == 32000)
            frame_data_.output_sample_rate_hz = 48000;
          else if (frame_data_.output_sample_rate_hz == 48000)
            frame_data_.output_sample_rate_hz = 8000;
        }
        break;
      case RuntimeParameterSettingScheme::FixedMonoStreamMetadataScheme:
        if (render_count_local == 0) {
          frame_data_.input_sample_rate_hz = 16000;
          frame_data_.input_number_of_channels = 1;
          frame_data_.output_sample_rate_hz = 16000;
          frame_data_.output_number_of_channels = 1;
        }
        break;
      case RuntimeParameterSettingScheme::FixedStereoStreamMetadataScheme:
        if (render_count_local == 0) {
          frame_data_.input_sample_rate_hz = 16000;
          frame_data_.input_number_of_channels = 2;
          frame_data_.output_sample_rate_hz = 16000;
          frame_data_.output_number_of_channels = 2;
        }
        break;
      default:
        FAIL();
    }
  
    // Restric the number of output channels not to exceed
    // the number of input channels.
    frame_data_.output_number_of_channels =
        std::min(frame_data_.output_number_of_channels,
                 frame_data_.input_number_of_channels);
  }
  
  }  // anonymous namespace
  
  TEST_P(AudioProcessingImplLockTest, LockTest) {
    // Run test and verify that it did not time out.
    ASSERT_TRUE(RunTest());
  }
  
  // Instantiate tests from the extreme test configuration set.
  INSTANTIATE_TEST_CASE_P(
      DISABLED_AudioProcessingImplLockExtensive,
      AudioProcessingImplLockTest,
      ::testing::ValuesIn(TestConfig::GenerateExtensiveTestConfigs()));
  
  INSTANTIATE_TEST_CASE_P(
      AudioProcessingImplLockBrief,
      AudioProcessingImplLockTest,
      ::testing::ValuesIn(TestConfig::GenerateBriefTestConfigs()));
  
  }  // namespace webrtc
  