From e9cd6177eb215d57fee31607a2cf946cb8ef5ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20=C3=85hgren?= Date: Tue, 19 May 2020 12:52:08 +0200 Subject: [PATCH] Add ability for audioproc_f to operate on any AudioProcessing object. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 object instead of a std::unique object Bug: webrtc:5298 Change-Id: I70179f19518fc583ad0101bd59c038478a3cc23d Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/175568 Commit-Queue: Per Ã…hgren Reviewed-by: Karl Wiberg Reviewed-by: Sam Zackrisson Cr-Commit-Position: refs/heads/master@{#31319} --- api/test/audioproc_float.cc | 6 ++ api/test/audioproc_float.h | 16 ++++ .../test/aec_dump_based_simulator.cc | 10 ++- .../test/aec_dump_based_simulator.h | 1 + .../test/audio_processing_simulator.cc | 86 +++++++++++-------- .../test/audio_processing_simulator.h | 9 +- .../test/audioproc_float_impl.cc | 68 ++++++++++++--- .../test/audioproc_float_impl.h | 15 ++++ .../test/wav_based_simulator.cc | 22 +++-- .../test/wav_based_simulator.h | 1 + 10 files changed, 168 insertions(+), 66 deletions(-) diff --git a/api/test/audioproc_float.cc b/api/test/audioproc_float.cc index bba9c622a1..c8d7ff7193 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 audio_processing, + int argc, + char* argv[]) { + return AudioprocFloatImpl(std::move(audio_processing), argc, argv); +} + int AudioprocFloat(std::unique_ptr ap_builder, int argc, char* argv[]) { diff --git a/api/test/audioproc_float.h b/api/test/audioproc_float.h index 2625e6ad9a..fec2ad11fa 100644 --- a/api/test/audioproc_float.h +++ b/api/test/audioproc_float.h @@ -19,6 +19,22 @@ namespace webrtc { namespace test { +// 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 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. diff --git a/modules/audio_processing/test/aec_dump_based_simulator.cc b/modules/audio_processing/test/aec_dump_based_simulator.cc index b3b113da44..f5bd6452e3 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 @@ bool VerifyFloatBitExactness(const webrtc::audioproc::Stream& msg, AecDumpBasedSimulator::AecDumpBasedSimulator( const SimulationSettings& settings, + rtc::scoped_refptr audio_processing, std::unique_ptr 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::PrepareReverseProcessStreamCall( } void AecDumpBasedSimulator::Process() { - CreateAudioProcessor(); + ConfigureAudioProcessor(); + if (settings_.artificial_nearend_filename) { std::unique_ptr artificial_nearend_file( new WavReader(settings_.artificial_nearend_filename->c_str())); @@ -237,7 +241,7 @@ void AecDumpBasedSimulator::Process() { 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 ef032d0316..092b82bdbc 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 @@ namespace test { class AecDumpBasedSimulator final : public AudioProcessingSimulator { public: AecDumpBasedSimulator(const SimulationSettings& settings, + rtc::scoped_refptr audio_processing, std::unique_ptr 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 a37a83f1e3..adbc298e9e 100644 --- a/modules/audio_processing/test/audio_processing_simulator.cc +++ b/modules/audio_processing/test/audio_processing_simulator.cc @@ -113,10 +113,10 @@ SimulationSettings::~SimulationSettings() = default; AudioProcessingSimulator::AudioProcessingSimulator( const SimulationSettings& settings, + rtc::scoped_refptr audio_processing, std::unique_ptr ap_builder) : settings_(settings), - ap_builder_(ap_builder ? std::move(ap_builder) - : std::make_unique()), + ap_(std::move(audio_processing)), analog_mic_level_(settings.initial_mic_level), fake_recording_device_( settings.initial_mic_level, @@ -139,6 +139,51 @@ AudioProcessingSimulator::AudioProcessingSimulator( 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 builder = + !!ap_builder ? std::move(ap_builder) + : std::make_unique(); + + // 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(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 @@ void AudioProcessingSimulator::SetupOutput() { ++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 echo_control_factory; if (settings_.use_ts) { apm_config.transient_suppression.enabled = *settings_.use_ts; } @@ -421,29 +464,6 @@ void AudioProcessingSimulator::CreateAudioProcessor() { 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 @@ void AudioProcessingSimulator::CreateAudioProcessor() { *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 fa6efc2842..8579f4b4d0 100644 --- a/modules/audio_processing/test/audio_processing_simulator.h +++ b/modules/audio_processing/test/audio_processing_simulator.h @@ -150,8 +150,8 @@ struct SimulationSettings { // Provides common functionality for performing audioprocessing simulations. class AudioProcessingSimulator { public: - AudioProcessingSimulator(const SimulationSettings& settings, + rtc::scoped_refptr audio_processing, std::unique_ptr ap_builder); virtual ~AudioProcessingSimulator(); @@ -174,8 +174,8 @@ class AudioProcessingSimulator { 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 @@ class AudioProcessingSimulator { int reverse_output_num_channels); const SimulationSettings settings_; - std::unique_ptr ap_; - std::unique_ptr ap_builder_; + rtc::scoped_refptr ap_; std::unique_ptr> in_buf_; std::unique_ptr> out_buf_; diff --git a/modules/audio_processing/test/audioproc_float_impl.cc b/modules/audio_processing/test/audioproc_float_impl.cc index d9a4227eb7..ab395f1018 100644 --- a/modules/audio_processing/test/audioproc_float_impl.cc +++ b/modules/audio_processing/test/audioproc_float_impl.cc @@ -457,7 +457,10 @@ void ReportConditionalErrorAndExit(bool condition, const std::string& message) { } } -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 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) { 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 ap_builder, - int argc, - char* argv[], - absl::string_view input_aecdump, - std::vector* processed_capture_samples) { +int RunSimulation(rtc::scoped_refptr audio_processing, + std::unique_ptr ap_builder, + int argc, + char* argv[], + absl::string_view input_aecdump, + std::vector* processed_capture_samples) { std::vector 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 @@ int AudioprocFloatImpl(std::unique_ptr ap_builder, settings.processed_capture_samples = processed_capture_samples; RTC_CHECK(settings.processed_capture_samples); } - PerformBasicParameterSanityChecks(settings); + PerformBasicParameterSanityChecks(settings, !!audio_processing, !!ap_builder); std::unique_ptr 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 @@ int AudioprocFloatImpl(std::unique_ptr ap_builder, return 0; } +} // namespace + +int AudioprocFloatImpl(rtc::scoped_refptr 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 ap_builder, + int argc, + char* argv[], + absl::string_view input_aecdump, + std::vector* 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 9a9013c644..0687c43a5d 100644 --- a/modules/audio_processing/test/audioproc_float_impl.h +++ b/modules/audio_processing/test/audioproc_float_impl.h @@ -18,6 +18,21 @@ namespace webrtc { namespace test { +// 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. +// 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 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 diff --git a/modules/audio_processing/test/wav_based_simulator.cc b/modules/audio_processing/test/wav_based_simulator.cc index 7179fc3431..75946fb3fa 100644 --- a/modules/audio_processing/test/wav_based_simulator.cc +++ b/modules/audio_processing/test/wav_based_simulator.cc @@ -56,8 +56,18 @@ WavBasedSimulator::GetCustomEventChain(const std::string& filename) { WavBasedSimulator::WavBasedSimulator( const SimulationSettings& settings, + rtc::scoped_refptr audio_processing, std::unique_ptr 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::PrepareReverseProcessStreamCall() { } 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 @@ void WavBasedSimulator::Process() { 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 991f1dbaad..3adbe7022c 100644 --- a/modules/audio_processing/test/wav_based_simulator.h +++ b/modules/audio_processing/test/wav_based_simulator.h @@ -23,6 +23,7 @@ namespace test { class WavBasedSimulator final : public AudioProcessingSimulator { public: WavBasedSimulator(const SimulationSettings& settings, + rtc::scoped_refptr audio_processing, std::unique_ptr ap_builder); ~WavBasedSimulator() override;