Add ability for audioproc_f to operate on any AudioProcessing object.

This CL extends the WebRTC testing API to allow audioproc_f -based
testing using a pre-created AudioProcessing object. This is an
important feature to allow testing any AudioProcessing objects
that are injected into WebRTC.

Beyond adding this, the CL also changes the simulation code to
operate on a scoped_refptr<AudioProcessing> object instead of a
std::unique<AudioProcessing> object

Bug: webrtc:5298
Change-Id: I70179f19518fc583ad0101bd59c038478a3cc23d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/175568
Commit-Queue: Per Åhgren <peah@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Sam Zackrisson <saza@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31319}
diff --git a/api/test/audioproc_float.cc b/api/test/audioproc_float.cc
index bba9c62..c8d7ff7 100644
--- a/api/test/audioproc_float.cc
+++ b/api/test/audioproc_float.cc
@@ -17,6 +17,12 @@
 namespace webrtc {
 namespace test {
 
+int AudioprocFloat(rtc::scoped_refptr<AudioProcessing> audio_processing,
+                   int argc,
+                   char* argv[]) {
+  return AudioprocFloatImpl(std::move(audio_processing), argc, argv);
+}
+
 int AudioprocFloat(std::unique_ptr<AudioProcessingBuilder> ap_builder,
                    int argc,
                    char* argv[]) {
diff --git a/api/test/audioproc_float.h b/api/test/audioproc_float.h
index 2625e6a..fec2ad1 100644
--- a/api/test/audioproc_float.h
+++ b/api/test/audioproc_float.h
@@ -22,6 +22,22 @@
 // This is an interface for the audio processing simulation utility. This
 // utility can be used to simulate the audioprocessing module using a recording
 // (either an AEC dump or wav files), and generate the output as a wav file.
+// Any audio_processing object specified in the input is used for the
+// simulation. The optional |audio_processing| object provides the
+// AudioProcessing instance that is used during the simulation. Note that when
+// the audio_processing object is specified all functionality that relies on
+// using the AudioProcessingBuilder is deactivated, since the AudioProcessing
+// object is already created and the builder is not used in the simulation. It
+// is needed to pass the command line flags as |argc| and |argv|, so these can
+// be interpreted properly by the utility. To see a list of all supported
+// command line flags, run the executable with the '--help' flag.
+int AudioprocFloat(rtc::scoped_refptr<AudioProcessing> audio_processing,
+                   int argc,
+                   char* argv[]);
+
+// This is an interface for the audio processing simulation utility. This
+// utility can be used to simulate the audioprocessing module using a recording
+// (either an AEC dump or wav files), and generate the output as a wav file.
 // The |ap_builder| object will be used to create the AudioProcessing instance
 // that is used during the simulation. The |ap_builder| supports setting of
 // injectable components, which will be passed on to the created AudioProcessing
diff --git a/modules/audio_processing/test/aec_dump_based_simulator.cc b/modules/audio_processing/test/aec_dump_based_simulator.cc
index b3b113d..f5bd645 100644
--- a/modules/audio_processing/test/aec_dump_based_simulator.cc
+++ b/modules/audio_processing/test/aec_dump_based_simulator.cc
@@ -66,8 +66,11 @@
 
 AecDumpBasedSimulator::AecDumpBasedSimulator(
     const SimulationSettings& settings,
+    rtc::scoped_refptr<AudioProcessing> audio_processing,
     std::unique_ptr<AudioProcessingBuilder> ap_builder)
-    : AudioProcessingSimulator(settings, std::move(ap_builder)) {
+    : AudioProcessingSimulator(settings,
+                               std::move(audio_processing),
+                               std::move(ap_builder)) {
   MaybeOpenCallOrderFile();
 }
 
@@ -206,7 +209,8 @@
 }
 
 void AecDumpBasedSimulator::Process() {
-  CreateAudioProcessor();
+  ConfigureAudioProcessor();
+
   if (settings_.artificial_nearend_filename) {
     std::unique_ptr<WavReader> artificial_nearend_file(
         new WavReader(settings_.artificial_nearend_filename->c_str()));
@@ -237,7 +241,7 @@
     fclose(dump_input_file_);
   }
 
-  DestroyAudioProcessor();
+  DetachAecDump();
 }
 
 void AecDumpBasedSimulator::HandleEvent(
diff --git a/modules/audio_processing/test/aec_dump_based_simulator.h b/modules/audio_processing/test/aec_dump_based_simulator.h
index ef032d0..092b82b 100644
--- a/modules/audio_processing/test/aec_dump_based_simulator.h
+++ b/modules/audio_processing/test/aec_dump_based_simulator.h
@@ -33,6 +33,7 @@
 class AecDumpBasedSimulator final : public AudioProcessingSimulator {
  public:
   AecDumpBasedSimulator(const SimulationSettings& settings,
+                        rtc::scoped_refptr<AudioProcessing> audio_processing,
                         std::unique_ptr<AudioProcessingBuilder> ap_builder);
   ~AecDumpBasedSimulator() override;
 
diff --git a/modules/audio_processing/test/audio_processing_simulator.cc b/modules/audio_processing/test/audio_processing_simulator.cc
index a37a83f..adbc298 100644
--- a/modules/audio_processing/test/audio_processing_simulator.cc
+++ b/modules/audio_processing/test/audio_processing_simulator.cc
@@ -113,10 +113,10 @@
 
 AudioProcessingSimulator::AudioProcessingSimulator(
     const SimulationSettings& settings,
+    rtc::scoped_refptr<AudioProcessing> audio_processing,
     std::unique_ptr<AudioProcessingBuilder> ap_builder)
     : settings_(settings),
-      ap_builder_(ap_builder ? std::move(ap_builder)
-                             : std::make_unique<AudioProcessingBuilder>()),
+      ap_(std::move(audio_processing)),
       analog_mic_level_(settings.initial_mic_level),
       fake_recording_device_(
           settings.initial_mic_level,
@@ -139,6 +139,51 @@
 
   if (settings_.simulate_mic_gain)
     RTC_LOG(LS_VERBOSE) << "Simulating analog mic gain";
+
+  // Create the audio processing object.
+  RTC_CHECK(!(ap_ && ap_builder))
+      << "The AudioProcessing and the AudioProcessingBuilder cannot both be "
+         "specified at the same time.";
+
+  if (ap_) {
+    RTC_CHECK(!settings_.aec_settings_filename);
+    RTC_CHECK(!settings_.print_aec_parameter_values);
+  } else {
+    // Use specied builder if such is provided, otherwise create a new builder.
+    std::unique_ptr<AudioProcessingBuilder> builder =
+        !!ap_builder ? std::move(ap_builder)
+                     : std::make_unique<AudioProcessingBuilder>();
+
+    // Create and set an EchoCanceller3Factory if needed.
+    const bool use_aec = settings_.use_aec && *settings_.use_aec;
+    if (use_aec) {
+      EchoCanceller3Config cfg;
+      if (settings_.aec_settings_filename) {
+        if (settings_.use_verbose_logging) {
+          std::cout << "Reading AEC Parameters from JSON input." << std::endl;
+        }
+        cfg = ReadAec3ConfigFromJsonFile(*settings_.aec_settings_filename);
+      }
+
+      if (settings_.linear_aec_output_filename) {
+        cfg.filter.export_linear_aec_output = true;
+      }
+
+      if (settings_.print_aec_parameter_values) {
+        if (!settings_.use_quiet_output) {
+          std::cout << "AEC settings:" << std::endl;
+        }
+        std::cout << Aec3ConfigToJsonString(cfg) << std::endl;
+      }
+
+      auto echo_control_factory = std::make_unique<EchoCanceller3Factory>(cfg);
+      builder->SetEchoControlFactory(std::move(echo_control_factory));
+    }
+
+    // Create an audio processing object.
+    ap_ = builder->Create();
+    RTC_CHECK(ap_);
+  }
 }
 
 AudioProcessingSimulator::~AudioProcessingSimulator() {
@@ -369,16 +414,14 @@
   ++output_reset_counter_;
 }
 
-void AudioProcessingSimulator::DestroyAudioProcessor() {
+void AudioProcessingSimulator::DetachAecDump() {
   if (settings_.aec_dump_output_filename) {
     ap_->DetachAecDump();
   }
 }
 
-void AudioProcessingSimulator::CreateAudioProcessor() {
-  Config config;
+void AudioProcessingSimulator::ConfigureAudioProcessor() {
   AudioProcessing::Config apm_config;
-  std::unique_ptr<EchoControlFactory> echo_control_factory;
   if (settings_.use_ts) {
     apm_config.transient_suppression.enabled = *settings_.use_ts;
   }
@@ -421,29 +464,6 @@
   apm_config.echo_canceller.export_linear_aec_output =
       !!settings_.linear_aec_output_filename;
 
-  if (use_aec) {
-    EchoCanceller3Config cfg;
-    if (settings_.aec_settings_filename) {
-      if (settings_.use_verbose_logging) {
-        std::cout << "Reading AEC Parameters from JSON input." << std::endl;
-      }
-      cfg = ReadAec3ConfigFromJsonFile(*settings_.aec_settings_filename);
-    }
-
-    if (settings_.linear_aec_output_filename) {
-      cfg.filter.export_linear_aec_output = true;
-    }
-
-    echo_control_factory.reset(new EchoCanceller3Factory(cfg));
-
-    if (settings_.print_aec_parameter_values) {
-      if (!settings_.use_quiet_output) {
-        std::cout << "AEC settings:" << std::endl;
-      }
-      std::cout << Aec3ConfigToJsonString(cfg) << std::endl;
-    }
-  }
-
   if (settings_.use_hpf) {
     apm_config.high_pass_filter.enabled = *settings_.use_hpf;
   }
@@ -512,14 +532,6 @@
         *settings_.ns_analysis_on_linear_aec_output;
   }
 
-  RTC_CHECK(ap_builder_);
-  if (echo_control_factory) {
-    ap_builder_->SetEchoControlFactory(std::move(echo_control_factory));
-  }
-  ap_.reset((*ap_builder_).Create(config));
-
-  RTC_CHECK(ap_);
-
   ap_->ApplyConfig(apm_config);
 
   if (settings_.use_ts) {
diff --git a/modules/audio_processing/test/audio_processing_simulator.h b/modules/audio_processing/test/audio_processing_simulator.h
index fa6efc2..8579f4b 100644
--- a/modules/audio_processing/test/audio_processing_simulator.h
+++ b/modules/audio_processing/test/audio_processing_simulator.h
@@ -150,8 +150,8 @@
 // Provides common functionality for performing audioprocessing simulations.
 class AudioProcessingSimulator {
  public:
-
   AudioProcessingSimulator(const SimulationSettings& settings,
+                           rtc::scoped_refptr<AudioProcessing> audio_processing,
                            std::unique_ptr<AudioProcessingBuilder> ap_builder);
   virtual ~AudioProcessingSimulator();
 
@@ -174,8 +174,8 @@
  protected:
   void ProcessStream(bool fixed_interface);
   void ProcessReverseStream(bool fixed_interface);
-  void CreateAudioProcessor();
-  void DestroyAudioProcessor();
+  void ConfigureAudioProcessor();
+  void DetachAecDump();
   void SetupBuffersConfigsOutputs(int input_sample_rate_hz,
                                   int output_sample_rate_hz,
                                   int reverse_input_sample_rate_hz,
@@ -186,8 +186,7 @@
                                   int reverse_output_num_channels);
 
   const SimulationSettings settings_;
-  std::unique_ptr<AudioProcessing> ap_;
-  std::unique_ptr<AudioProcessingBuilder> ap_builder_;
+  rtc::scoped_refptr<AudioProcessing> ap_;
 
   std::unique_ptr<ChannelBuffer<float>> in_buf_;
   std::unique_ptr<ChannelBuffer<float>> out_buf_;
diff --git a/modules/audio_processing/test/audioproc_float_impl.cc b/modules/audio_processing/test/audioproc_float_impl.cc
index d9a4227..ab395f1 100644
--- a/modules/audio_processing/test/audioproc_float_impl.cc
+++ b/modules/audio_processing/test/audioproc_float_impl.cc
@@ -457,7 +457,10 @@
   }
 }
 
-void PerformBasicParameterSanityChecks(const SimulationSettings& settings) {
+void PerformBasicParameterSanityChecks(
+    const SimulationSettings& settings,
+    bool pre_constructed_ap_provided,
+    bool pre_constructed_ap_builder_provided) {
   if (settings.input_filename || settings.reverse_input_filename) {
     ReportConditionalErrorAndExit(
         !!settings.aec_dump_input_filename,
@@ -624,21 +627,41 @@
           settings.pre_amplifier_gain_factor.has_value(),
       "Error: --pre_amplifier_gain_factor needs --pre_amplifier to be "
       "specified and set.\n");
+
+  ReportConditionalErrorAndExit(
+      pre_constructed_ap_provided && pre_constructed_ap_builder_provided,
+      "Error: The AudioProcessing and the AudioProcessingBuilder cannot both "
+      "be specified at the same time.\n");
+
+  ReportConditionalErrorAndExit(
+      settings.aec_settings_filename && pre_constructed_ap_provided,
+      "Error: The aec_settings_filename cannot be specified when a "
+      "pre-constructed audio processing object is provided.\n");
+
+  ReportConditionalErrorAndExit(
+      settings.aec_settings_filename && pre_constructed_ap_provided,
+      "Error: The print_aec_parameter_values cannot be set when a "
+      "pre-constructed audio processing object is provided.\n");
+
+  if (settings.linear_aec_output_filename && pre_constructed_ap_provided) {
+    std::cout << "Warning: For the linear AEC output to be stored, this must "
+                 "be configured in the AEC that is part of the provided "
+                 "AudioProcessing object."
+              << std::endl;
+  }
 }
 
-}  // namespace
-
-int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
-                       int argc,
-                       char* argv[],
-                       absl::string_view input_aecdump,
-                       std::vector<float>* processed_capture_samples) {
+int RunSimulation(rtc::scoped_refptr<AudioProcessing> audio_processing,
+                  std::unique_ptr<AudioProcessingBuilder> ap_builder,
+                  int argc,
+                  char* argv[],
+                  absl::string_view input_aecdump,
+                  std::vector<float>* processed_capture_samples) {
   std::vector<char*> args = absl::ParseCommandLine(argc, argv);
   if (args.size() != 1) {
     printf("%s", kUsageDescription);
     return 1;
   }
-
   // InitFieldTrialsFromString stores the char*, so the char array must
   // outlive the application.
   const std::string field_trials = absl::GetFlag(FLAGS_force_fieldtrials);
@@ -650,13 +673,15 @@
     settings.processed_capture_samples = processed_capture_samples;
     RTC_CHECK(settings.processed_capture_samples);
   }
-  PerformBasicParameterSanityChecks(settings);
+  PerformBasicParameterSanityChecks(settings, !!audio_processing, !!ap_builder);
   std::unique_ptr<AudioProcessingSimulator> processor;
 
   if (settings.aec_dump_input_filename || settings.aec_dump_input_string) {
-    processor.reset(new AecDumpBasedSimulator(settings, std::move(ap_builder)));
+    processor.reset(new AecDumpBasedSimulator(
+        settings, std::move(audio_processing), std::move(ap_builder)));
   } else {
-    processor.reset(new WavBasedSimulator(settings, std::move(ap_builder)));
+    processor.reset(new WavBasedSimulator(settings, std::move(audio_processing),
+                                          std::move(ap_builder)));
   }
 
   processor->Process();
@@ -680,5 +705,24 @@
   return 0;
 }
 
+}  // namespace
+
+int AudioprocFloatImpl(rtc::scoped_refptr<AudioProcessing> audio_processing,
+                       int argc,
+                       char* argv[]) {
+  return RunSimulation(
+      std::move(audio_processing), /*ap_builder=*/nullptr, argc, argv,
+      /*input_aecdump=*/"", /*processed_capture_samples=*/nullptr);
+}
+
+int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
+                       int argc,
+                       char* argv[],
+                       absl::string_view input_aecdump,
+                       std::vector<float>* processed_capture_samples) {
+  return RunSimulation(/*audio_processing=*/nullptr, std::move(ap_builder),
+                       argc, argv, input_aecdump, processed_capture_samples);
+}
+
 }  // namespace test
 }  // namespace webrtc
diff --git a/modules/audio_processing/test/audioproc_float_impl.h b/modules/audio_processing/test/audioproc_float_impl.h
index 9a9013c..0687c43 100644
--- a/modules/audio_processing/test/audioproc_float_impl.h
+++ b/modules/audio_processing/test/audioproc_float_impl.h
@@ -24,6 +24,21 @@
 // via the |argv| argument. Pass |processed_capture_samples| to write in it the
 // samples processed on the capture side; if |processed_capture_samples| is not
 // passed, the output file can optionally be specified via the |argv| argument.
+// Any audio_processing object specified in the input is used for the
+// simulation. Note that when the audio_processing object is specified all
+// functionality that relies on using the internal builder is deactivated,
+// since the AudioProcessing object is already created and the builder is not
+// used in the simulation.
+int AudioprocFloatImpl(rtc::scoped_refptr<AudioProcessing> audio_processing,
+                       int argc,
+                       char* argv[]);
+
+// This function implements the audio processing simulation utility. Pass
+// |input_aecdump| to provide the content of an AEC dump file as a string; if
+// |input_aecdump| is not passed, a WAV or AEC input dump file must be specified
+// via the |argv| argument. Pass |processed_capture_samples| to write in it the
+// samples processed on the capture side; if |processed_capture_samples| is not
+// passed, the output file can optionally be specified via the |argv| argument.
 int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
                        int argc,
                        char* argv[],
diff --git a/modules/audio_processing/test/wav_based_simulator.cc b/modules/audio_processing/test/wav_based_simulator.cc
index 7179fc3..75946fb 100644
--- a/modules/audio_processing/test/wav_based_simulator.cc
+++ b/modules/audio_processing/test/wav_based_simulator.cc
@@ -56,8 +56,18 @@
 
 WavBasedSimulator::WavBasedSimulator(
     const SimulationSettings& settings,
+    rtc::scoped_refptr<AudioProcessing> audio_processing,
     std::unique_ptr<AudioProcessingBuilder> ap_builder)
-    : AudioProcessingSimulator(settings, std::move(ap_builder)) {}
+    : AudioProcessingSimulator(settings,
+                               std::move(audio_processing),
+                               std::move(ap_builder)) {
+  if (settings_.call_order_input_filename) {
+    call_chain_ = WavBasedSimulator::GetCustomEventChain(
+        *settings_.call_order_input_filename);
+  } else {
+    call_chain_ = WavBasedSimulator::GetDefaultEventChain();
+  }
+}
 
 WavBasedSimulator::~WavBasedSimulator() = default;
 
@@ -89,13 +99,7 @@
 }
 
 void WavBasedSimulator::Process() {
-  if (settings_.call_order_input_filename) {
-    call_chain_ = WavBasedSimulator::GetCustomEventChain(
-        *settings_.call_order_input_filename);
-  } else {
-    call_chain_ = WavBasedSimulator::GetDefaultEventChain();
-  }
-  CreateAudioProcessor();
+  ConfigureAudioProcessor();
 
   Initialize();
 
@@ -120,7 +124,7 @@
     call_chain_index = (call_chain_index + 1) % call_chain_.size();
   }
 
-  DestroyAudioProcessor();
+  DetachAecDump();
 }
 
 bool WavBasedSimulator::HandleProcessStreamCall() {
diff --git a/modules/audio_processing/test/wav_based_simulator.h b/modules/audio_processing/test/wav_based_simulator.h
index 991f1db..3adbe70 100644
--- a/modules/audio_processing/test/wav_based_simulator.h
+++ b/modules/audio_processing/test/wav_based_simulator.h
@@ -23,6 +23,7 @@
 class WavBasedSimulator final : public AudioProcessingSimulator {
  public:
   WavBasedSimulator(const SimulationSettings& settings,
+                    rtc::scoped_refptr<AudioProcessing> audio_processing,
                     std::unique_ptr<AudioProcessingBuilder> ap_builder);
   ~WavBasedSimulator() override;