|  | /* | 
|  | *  Copyright (c) 2014 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/test/audioproc_float_impl.h" | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <cstdio> | 
|  | #include <cstdlib> | 
|  | #include <fstream> | 
|  | #include <iostream> | 
|  | #include <memory> | 
|  | #include <optional> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/base/nullability.h" | 
|  | #include "absl/flags/flag.h" | 
|  | #include "absl/flags/parse.h" | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "api/audio/audio_processing.h" | 
|  | #include "api/audio/builtin_audio_processing_builder.h" | 
|  | #include "api/audio/echo_canceller3_config.h" | 
|  | #include "api/audio/echo_canceller3_factory.h" | 
|  | #include "api/audio/echo_detector_creator.h" | 
|  | #include "api/environment/environment.h" | 
|  | #include "api/environment/environment_factory.h" | 
|  | #include "api/field_trials.h" | 
|  | #include "api/scoped_refptr.h" | 
|  | #include "common_audio/wav_file.h" | 
|  | #include "modules/audio_processing/test/aec_dump_based_simulator.h" | 
|  | #include "modules/audio_processing/test/audio_processing_simulator.h" | 
|  | #include "modules/audio_processing/test/echo_canceller3_config_json.h" | 
|  | #include "modules/audio_processing/test/wav_based_simulator.h" | 
|  | #include "rtc_base/checks.h" | 
|  |  | 
|  | constexpr int kParameterNotSpecifiedValue = -10000; | 
|  |  | 
|  | ABSL_FLAG(std::string, dump_input, "", "Aec dump input filename"); | 
|  | ABSL_FLAG(std::string, dump_output, "", "Aec dump output filename"); | 
|  | ABSL_FLAG(std::string, i, "", "Forward stream input wav filename"); | 
|  | ABSL_FLAG(std::string, o, "", "Forward stream output wav filename"); | 
|  | ABSL_FLAG(std::string, ri, "", "Reverse stream input wav filename"); | 
|  | ABSL_FLAG(std::string, ro, "", "Reverse stream output wav filename"); | 
|  | ABSL_FLAG(std::string, | 
|  | artificial_nearend, | 
|  | "", | 
|  | "Artificial nearend wav filename"); | 
|  | ABSL_FLAG(std::string, linear_aec_output, "", "Linear AEC output wav filename"); | 
|  | ABSL_FLAG(int, | 
|  | output_num_channels, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Number of forward stream output channels"); | 
|  | ABSL_FLAG(int, | 
|  | reverse_output_num_channels, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Number of Reverse stream output channels"); | 
|  | ABSL_FLAG(int, | 
|  | output_sample_rate_hz, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Forward stream output sample rate in Hz"); | 
|  | ABSL_FLAG(int, | 
|  | reverse_output_sample_rate_hz, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Reverse stream output sample rate in Hz"); | 
|  | ABSL_FLAG(bool, | 
|  | fixed_interface, | 
|  | false, | 
|  | "Use the fixed interface when operating on wav files"); | 
|  | ABSL_FLAG(int, | 
|  | aec, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the echo canceller"); | 
|  | ABSL_FLAG(int, | 
|  | aecm, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the mobile echo controller"); | 
|  | ABSL_FLAG(int, | 
|  | ed, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the residual echo detector"); | 
|  | ABSL_FLAG(std::string, | 
|  | ed_graph, | 
|  | "", | 
|  | "Output filename for graph of echo likelihood"); | 
|  | ABSL_FLAG(int, | 
|  | agc, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the AGC"); | 
|  | ABSL_FLAG(int, | 
|  | agc2, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the AGC2"); | 
|  | ABSL_FLAG(int, | 
|  | pre_amplifier, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate(0) the pre amplifier"); | 
|  | ABSL_FLAG( | 
|  | int, | 
|  | capture_level_adjustment, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate(0) the capture level adjustment functionality"); | 
|  | ABSL_FLAG(int, | 
|  | analog_mic_gain_emulation, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate(0) the analog mic gain emulation in the " | 
|  | "production (non-test) code."); | 
|  | ABSL_FLAG(int, | 
|  | hpf, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the high-pass filter"); | 
|  | ABSL_FLAG(int, | 
|  | ns, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the noise suppressor"); | 
|  | ABSL_FLAG(int, | 
|  | ts, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the transient suppressor"); | 
|  | ABSL_FLAG(int, | 
|  | analog_agc, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the analog AGC"); | 
|  | ABSL_FLAG(bool, | 
|  | all_default, | 
|  | false, | 
|  | "Activate all of the default components (will be overridden by any " | 
|  | "other settings)"); | 
|  | ABSL_FLAG(int, | 
|  | analog_agc_use_digital_adaptive_controller, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) digital adaptation in AGC1. " | 
|  | "Digital adaptation is active by default."); | 
|  | ABSL_FLAG(int, | 
|  | agc_mode, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Specify the AGC mode (0-2)"); | 
|  | ABSL_FLAG(int, | 
|  | agc_target_level, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Specify the AGC target level (0-31)"); | 
|  | ABSL_FLAG(int, | 
|  | agc_limiter, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the level estimator"); | 
|  | ABSL_FLAG(int, | 
|  | agc_compression_gain, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Specify the AGC compression gain (0-90)"); | 
|  | ABSL_FLAG(int, | 
|  | agc2_enable_adaptive_gain, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the AGC2 adaptive gain"); | 
|  | ABSL_FLAG(float, | 
|  | agc2_fixed_gain_db, | 
|  | kParameterNotSpecifiedValue, | 
|  | "AGC2 fixed gain (dB) to apply"); | 
|  | ABSL_FLAG(int, | 
|  | agc2_enable_input_volume_controller, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) the AGC2 input volume adjustments"); | 
|  | ABSL_FLAG(float, | 
|  | pre_amplifier_gain_factor, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Pre-amplifier gain factor (linear) to apply"); | 
|  | ABSL_FLAG(float, | 
|  | pre_gain_factor, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Pre-gain factor (linear) to apply in the capture level adjustment"); | 
|  | ABSL_FLAG(float, | 
|  | post_gain_factor, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Post-gain factor (linear) to apply in the capture level adjustment"); | 
|  | ABSL_FLAG(float, | 
|  | analog_mic_gain_emulation_initial_level, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Emulated analog mic level to apply initially in the production " | 
|  | "(non-test) code."); | 
|  | ABSL_FLAG(int, | 
|  | ns_level, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Specify the NS level (0-3)"); | 
|  | ABSL_FLAG(int, | 
|  | ns_analysis_on_linear_aec_output, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Specifies whether the noise suppression analysis is done on the " | 
|  | "linear AEC output"); | 
|  | ABSL_FLAG(int, | 
|  | maximum_internal_processing_rate, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Set a maximum internal processing rate (32000 or 48000) to override " | 
|  | "the default rate"); | 
|  | ABSL_FLAG(int, | 
|  | stream_delay, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Specify the stream delay in ms to use"); | 
|  | ABSL_FLAG(int, | 
|  | use_stream_delay, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) reporting the stream delay"); | 
|  | ABSL_FLAG(int, | 
|  | stream_drift_samples, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Specify the number of stream drift samples to use"); | 
|  | ABSL_FLAG(int, | 
|  | initial_mic_level, | 
|  | 100, | 
|  | "Initial mic level (0-255) for the analog mic gain simulation in the " | 
|  | "test code"); | 
|  | ABSL_FLAG(int, | 
|  | simulate_mic_gain, | 
|  | 0, | 
|  | "Activate (1) or deactivate(0) the analog mic gain simulation in the " | 
|  | "test code"); | 
|  | ABSL_FLAG(int, | 
|  | multi_channel_render, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) multi-channel render processing in " | 
|  | "APM pipeline"); | 
|  | ABSL_FLAG(int, | 
|  | multi_channel_capture, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Activate (1) or deactivate (0) multi-channel capture processing in " | 
|  | "APM pipeline"); | 
|  | ABSL_FLAG(int, | 
|  | simulated_mic_kind, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Specify which microphone kind to use for microphone simulation"); | 
|  | ABSL_FLAG(int, | 
|  | override_key_pressed, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Always set to true (1) or to false (0) the key press state. If " | 
|  | "unspecified, false is set with Wav files or, with AEC dumps, the " | 
|  | "recorded event is used."); | 
|  | ABSL_FLAG(int, | 
|  | frame_for_sending_capture_output_used_false, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Capture frame index for sending a runtime setting for that the " | 
|  | "capture output is not used."); | 
|  | ABSL_FLAG(int, | 
|  | frame_for_sending_capture_output_used_true, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Capture frame index for sending a runtime setting for that the " | 
|  | "capture output is used."); | 
|  | ABSL_FLAG(bool, performance_report, false, "Report the APM performance "); | 
|  | ABSL_FLAG(std::string, | 
|  | performance_report_output_file, | 
|  | "", | 
|  | "Generate a CSV file with the API call durations"); | 
|  | ABSL_FLAG(bool, verbose, false, "Produce verbose output"); | 
|  | ABSL_FLAG(bool, | 
|  | quiet, | 
|  | false, | 
|  | "Avoid producing information about the progress."); | 
|  | ABSL_FLAG(bool, | 
|  | bitexactness_report, | 
|  | false, | 
|  | "Report bitexactness for aec dump result reproduction"); | 
|  | ABSL_FLAG(bool, | 
|  | discard_settings_in_aecdump, | 
|  | false, | 
|  | "Discard any config settings specified in the aec dump"); | 
|  | ABSL_FLAG(bool, | 
|  | store_intermediate_output, | 
|  | false, | 
|  | "Creates new output files after each init"); | 
|  | ABSL_FLAG(std::string, | 
|  | custom_call_order_file, | 
|  | "", | 
|  | "Custom process API call order file"); | 
|  | ABSL_FLAG(std::string, | 
|  | output_custom_call_order_file, | 
|  | "", | 
|  | "Generate custom process API call order file from AEC dump"); | 
|  | ABSL_FLAG(bool, | 
|  | print_aec_parameter_values, | 
|  | false, | 
|  | "Print parameter values used in AEC in JSON-format"); | 
|  | ABSL_FLAG(std::string, | 
|  | aec_settings, | 
|  | "", | 
|  | "File in JSON-format with custom AEC settings"); | 
|  | ABSL_FLAG(bool, | 
|  | dump_data, | 
|  | false, | 
|  | "Dump internal data during the call (requires build flag)"); | 
|  | ABSL_FLAG(std::string, | 
|  | dump_data_output_dir, | 
|  | "", | 
|  | "Internal data dump output directory"); | 
|  | ABSL_FLAG(int, | 
|  | dump_set_to_use, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Specifies the dump set to use (if not all the dump sets will " | 
|  | "be used"); | 
|  | ABSL_FLAG(bool, | 
|  | analyze, | 
|  | false, | 
|  | "Only analyze the call setup behavior (no processing)"); | 
|  | ABSL_FLAG(float, | 
|  | dump_start_seconds, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Start of when to dump data (seconds)."); | 
|  | ABSL_FLAG(float, | 
|  | dump_end_seconds, | 
|  | kParameterNotSpecifiedValue, | 
|  | "End of when to dump data (seconds)."); | 
|  | ABSL_FLAG(int, | 
|  | dump_start_frame, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Start of when to dump data (frames)."); | 
|  | ABSL_FLAG(int, | 
|  | dump_end_frame, | 
|  | kParameterNotSpecifiedValue, | 
|  | "End of when to dump data (frames)."); | 
|  | ABSL_FLAG(int, | 
|  | init_to_process, | 
|  | kParameterNotSpecifiedValue, | 
|  | "Init index to process."); | 
|  |  | 
|  | ABSL_FLAG(bool, | 
|  | float_wav_output, | 
|  | false, | 
|  | "Produce floating point wav output files."); | 
|  |  | 
|  | ABSL_FLAG(std::string, | 
|  | force_fieldtrials, | 
|  | "", | 
|  | "Field trials control experimental feature code which can be forced. " | 
|  | "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/" | 
|  | " will assign the group Enable to field trial WebRTC-FooFeature."); | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | const char kUsageDescription[] = | 
|  | "Usage: audioproc_f [options] -i <input.wav>\n" | 
|  | "                   or\n" | 
|  | "       audioproc_f [options] -dump_input <aec_dump>\n" | 
|  | "\n\n" | 
|  | "Command-line tool to simulate a call using the audio " | 
|  | "processing module, either based on wav files or " | 
|  | "protobuf debug dump recordings.\n"; | 
|  |  | 
|  | void SetSettingIfSpecified(absl::string_view value, | 
|  | std::optional<std::string>* parameter) { | 
|  | if (value.compare("") != 0) { | 
|  | *parameter = std::string(value); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SetSettingIfSpecified(int value, std::optional<int>* parameter) { | 
|  | if (value != kParameterNotSpecifiedValue) { | 
|  | *parameter = value; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SetSettingIfSpecified(float value, std::optional<float>* parameter) { | 
|  | constexpr float kFloatParameterNotSpecifiedValue = | 
|  | kParameterNotSpecifiedValue; | 
|  | if (value != kFloatParameterNotSpecifiedValue) { | 
|  | *parameter = value; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SetSettingIfFlagSet(int32_t flag, std::optional<bool>* parameter) { | 
|  | if (flag == 0) { | 
|  | *parameter = false; | 
|  | } else if (flag == 1) { | 
|  | *parameter = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | SimulationSettings CreateSettings() { | 
|  | SimulationSettings settings; | 
|  | if (absl::GetFlag(FLAGS_all_default)) { | 
|  | settings.use_ts = true; | 
|  | settings.use_analog_agc = true; | 
|  | settings.use_ns = true; | 
|  | settings.use_hpf = true; | 
|  | settings.use_agc = true; | 
|  | settings.use_agc2 = false; | 
|  | settings.use_pre_amplifier = false; | 
|  | settings.use_aec = true; | 
|  | settings.use_aecm = false; | 
|  | settings.use_ed = false; | 
|  | } | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_input), | 
|  | &settings.aec_dump_input_filename); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_output), | 
|  | &settings.aec_dump_output_filename); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_i), &settings.input_filename); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_o), &settings.output_filename); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_ri), | 
|  | &settings.reverse_input_filename); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_ro), | 
|  | &settings.reverse_output_filename); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_artificial_nearend), | 
|  | &settings.artificial_nearend_filename); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_linear_aec_output), | 
|  | &settings.linear_aec_output_filename); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_output_num_channels), | 
|  | &settings.output_num_channels); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_reverse_output_num_channels), | 
|  | &settings.reverse_output_num_channels); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_output_sample_rate_hz), | 
|  | &settings.output_sample_rate_hz); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_reverse_output_sample_rate_hz), | 
|  | &settings.reverse_output_sample_rate_hz); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_aec), &settings.use_aec); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_aecm), &settings.use_aecm); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_ed), &settings.use_ed); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_ed_graph), | 
|  | &settings.ed_graph_output_filename); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_agc), &settings.use_agc); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_agc2), &settings.use_agc2); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_pre_amplifier), | 
|  | &settings.use_pre_amplifier); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_capture_level_adjustment), | 
|  | &settings.use_capture_level_adjustment); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_analog_mic_gain_emulation), | 
|  | &settings.use_analog_mic_gain_emulation); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_hpf), &settings.use_hpf); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_ns), &settings.use_ns); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_ts), &settings.use_ts); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_analog_agc), | 
|  | &settings.use_analog_agc); | 
|  | SetSettingIfFlagSet( | 
|  | absl::GetFlag(FLAGS_analog_agc_use_digital_adaptive_controller), | 
|  | &settings.analog_agc_use_digital_adaptive_controller); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_agc_mode), &settings.agc_mode); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_agc_target_level), | 
|  | &settings.agc_target_level); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_agc_limiter), | 
|  | &settings.use_agc_limiter); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_agc_compression_gain), | 
|  | &settings.agc_compression_gain); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_agc2_enable_adaptive_gain), | 
|  | &settings.agc2_use_adaptive_gain); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_agc2_fixed_gain_db), | 
|  | &settings.agc2_fixed_gain_db); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_agc2_enable_input_volume_controller), | 
|  | &settings.agc2_use_input_volume_controller); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_pre_amplifier_gain_factor), | 
|  | &settings.pre_amplifier_gain_factor); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_pre_gain_factor), | 
|  | &settings.pre_gain_factor); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_post_gain_factor), | 
|  | &settings.post_gain_factor); | 
|  | SetSettingIfSpecified( | 
|  | absl::GetFlag(FLAGS_analog_mic_gain_emulation_initial_level), | 
|  | &settings.analog_mic_gain_emulation_initial_level); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_ns_level), &settings.ns_level); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_ns_analysis_on_linear_aec_output), | 
|  | &settings.ns_analysis_on_linear_aec_output); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_maximum_internal_processing_rate), | 
|  | &settings.maximum_internal_processing_rate); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_stream_delay), | 
|  | &settings.stream_delay); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_use_stream_delay), | 
|  | &settings.use_stream_delay); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_custom_call_order_file), | 
|  | &settings.call_order_input_filename); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_output_custom_call_order_file), | 
|  | &settings.call_order_output_filename); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_aec_settings), | 
|  | &settings.aec_settings_filename); | 
|  | settings.initial_mic_level = absl::GetFlag(FLAGS_initial_mic_level); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_multi_channel_render), | 
|  | &settings.multi_channel_render); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_multi_channel_capture), | 
|  | &settings.multi_channel_capture); | 
|  | settings.simulate_mic_gain = absl::GetFlag(FLAGS_simulate_mic_gain); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_simulated_mic_kind), | 
|  | &settings.simulated_mic_kind); | 
|  | SetSettingIfFlagSet(absl::GetFlag(FLAGS_override_key_pressed), | 
|  | &settings.override_key_pressed); | 
|  | SetSettingIfSpecified( | 
|  | absl::GetFlag(FLAGS_frame_for_sending_capture_output_used_false), | 
|  | &settings.frame_for_sending_capture_output_used_false); | 
|  | SetSettingIfSpecified( | 
|  | absl::GetFlag(FLAGS_frame_for_sending_capture_output_used_true), | 
|  | &settings.frame_for_sending_capture_output_used_true); | 
|  | settings.report_performance = absl::GetFlag(FLAGS_performance_report); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_performance_report_output_file), | 
|  | &settings.performance_report_output_filename); | 
|  | settings.use_verbose_logging = absl::GetFlag(FLAGS_verbose); | 
|  | settings.use_quiet_output = absl::GetFlag(FLAGS_quiet); | 
|  | settings.report_bitexactness = absl::GetFlag(FLAGS_bitexactness_report); | 
|  | settings.discard_all_settings_in_aecdump = | 
|  | absl::GetFlag(FLAGS_discard_settings_in_aecdump); | 
|  | settings.fixed_interface = absl::GetFlag(FLAGS_fixed_interface); | 
|  | settings.store_intermediate_output = | 
|  | absl::GetFlag(FLAGS_store_intermediate_output); | 
|  | settings.print_aec_parameter_values = | 
|  | absl::GetFlag(FLAGS_print_aec_parameter_values); | 
|  | settings.dump_internal_data = absl::GetFlag(FLAGS_dump_data); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_data_output_dir), | 
|  | &settings.dump_internal_data_output_dir); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_set_to_use), | 
|  | &settings.dump_set_to_use); | 
|  | settings.wav_output_format = absl::GetFlag(FLAGS_float_wav_output) | 
|  | ? WavFile::SampleFormat::kFloat | 
|  | : WavFile::SampleFormat::kInt16; | 
|  |  | 
|  | settings.analysis_only = absl::GetFlag(FLAGS_analyze); | 
|  |  | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_start_frame), | 
|  | &settings.dump_start_frame); | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_end_frame), | 
|  | &settings.dump_end_frame); | 
|  |  | 
|  | constexpr int kFramesPerSecond = 100; | 
|  | std::optional<float> start_seconds; | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_start_seconds), | 
|  | &start_seconds); | 
|  | if (start_seconds) { | 
|  | settings.dump_start_frame = *start_seconds * kFramesPerSecond; | 
|  | } | 
|  |  | 
|  | std::optional<float> end_seconds; | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_dump_end_seconds), &end_seconds); | 
|  | if (end_seconds) { | 
|  | settings.dump_end_frame = *end_seconds * kFramesPerSecond; | 
|  | } | 
|  |  | 
|  | SetSettingIfSpecified(absl::GetFlag(FLAGS_init_to_process), | 
|  | &settings.init_to_process); | 
|  |  | 
|  | return settings; | 
|  | } | 
|  |  | 
|  | void ReportConditionalErrorAndExit(bool condition, absl::string_view message) { | 
|  | if (condition) { | 
|  | std::cerr << message << std::endl; | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | void PerformBasicParameterSanityChecks(const SimulationSettings& settings) { | 
|  | if (settings.input_filename || settings.reverse_input_filename) { | 
|  | ReportConditionalErrorAndExit( | 
|  | !!settings.aec_dump_input_filename, | 
|  | "Error: The aec dump file cannot be specified " | 
|  | "together with input wav files!\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | !!settings.aec_dump_input_string, | 
|  | "Error: The aec dump input string cannot be specified " | 
|  | "together with input wav files!\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit(!!settings.artificial_nearend_filename, | 
|  | "Error: The artificial nearend cannot be " | 
|  | "specified together with input wav files!\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit(!settings.input_filename, | 
|  | "Error: When operating at wav files, the " | 
|  | "input wav filename must be " | 
|  | "specified!\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.reverse_output_filename && !settings.reverse_input_filename, | 
|  | "Error: When operating at wav files, the reverse input wav filename " | 
|  | "must be specified if the reverse output wav filename is specified!\n"); | 
|  | } else { | 
|  | ReportConditionalErrorAndExit( | 
|  | !settings.aec_dump_input_filename && !settings.aec_dump_input_string, | 
|  | "Error: Either the aec dump input file, the wav " | 
|  | "input file or the aec dump input string must be specified!\n"); | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.aec_dump_input_filename && settings.aec_dump_input_string, | 
|  | "Error: The aec dump input file cannot be specified together with the " | 
|  | "aec dump input string!\n"); | 
|  | } | 
|  |  | 
|  | ReportConditionalErrorAndExit(settings.use_aec && !(*settings.use_aec) && | 
|  | settings.linear_aec_output_filename, | 
|  | "Error: The linear AEC ouput filename cannot " | 
|  | "be specified without the AEC being active"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.use_aec && *settings.use_aec && settings.use_aecm && | 
|  | *settings.use_aecm, | 
|  | "Error: The AEC and the AECM cannot be activated at the same time!\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.output_sample_rate_hz && *settings.output_sample_rate_hz <= 0, | 
|  | "Error: --output_sample_rate_hz must be positive!\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.reverse_output_sample_rate_hz && | 
|  | settings.output_sample_rate_hz && | 
|  | *settings.output_sample_rate_hz <= 0, | 
|  | "Error: --reverse_output_sample_rate_hz must be positive!\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.output_num_channels && *settings.output_num_channels <= 0, | 
|  | "Error: --output_num_channels must be positive!\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.reverse_output_num_channels && | 
|  | *settings.reverse_output_num_channels <= 0, | 
|  | "Error: --reverse_output_num_channels must be positive!\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.agc_target_level && ((*settings.agc_target_level) < 0 || | 
|  | (*settings.agc_target_level) > 31), | 
|  | "Error: --agc_target_level must be specified between 0 and 31.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.agc_compression_gain && ((*settings.agc_compression_gain) < 0 || | 
|  | (*settings.agc_compression_gain) > 90), | 
|  | "Error: --agc_compression_gain must be specified between 0 and 90.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.agc2_fixed_gain_db && ((*settings.agc2_fixed_gain_db) < 0 || | 
|  | (*settings.agc2_fixed_gain_db) > 90), | 
|  | "Error: --agc2_fixed_gain_db must be specified between 0 and 90.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.ns_level && | 
|  | ((*settings.ns_level) < 0 || (*settings.ns_level) > 3), | 
|  | "Error: --ns_level must be specified between 0 and 3.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.report_bitexactness && !settings.aec_dump_input_filename, | 
|  | "Error: --bitexactness_report can only be used when operating on an " | 
|  | "aecdump\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.call_order_input_filename && settings.aec_dump_input_filename, | 
|  | "Error: --custom_call_order_file cannot be used when operating on an " | 
|  | "aecdump\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | (settings.initial_mic_level < 0 || settings.initial_mic_level > 255), | 
|  | "Error: --initial_mic_level must be specified between 0 and 255.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.simulated_mic_kind && !settings.simulate_mic_gain, | 
|  | "Error: --simulated_mic_kind cannot be specified when mic simulation is " | 
|  | "disabled\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | !settings.simulated_mic_kind && settings.simulate_mic_gain, | 
|  | "Error: --simulated_mic_kind must be specified when mic simulation is " | 
|  | "enabled\n"); | 
|  |  | 
|  | // TODO(bugs.webrtc.org/7494): Document how the two settings below differ. | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.simulate_mic_gain && settings.use_analog_mic_gain_emulation, | 
|  | "Error: --simulate_mic_gain and --use_analog_mic_gain_emulation cannot " | 
|  | "be enabled at the same time\n"); | 
|  |  | 
|  | auto valid_wav_name = [](absl::string_view wav_file_name) { | 
|  | if (wav_file_name.size() < 5) { | 
|  | return false; | 
|  | } | 
|  | if ((wav_file_name.compare(wav_file_name.size() - 4, 4, ".wav") == 0) || | 
|  | (wav_file_name.compare(wav_file_name.size() - 4, 4, ".WAV") == 0)) { | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | }; | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.input_filename && (!valid_wav_name(*settings.input_filename)), | 
|  | "Error: --i must be a valid .wav file name.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.output_filename && (!valid_wav_name(*settings.output_filename)), | 
|  | "Error: --o must be a valid .wav file name.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.reverse_input_filename && | 
|  | (!valid_wav_name(*settings.reverse_input_filename)), | 
|  | "Error: --ri must be a valid .wav file name.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.reverse_output_filename && | 
|  | (!valid_wav_name(*settings.reverse_output_filename)), | 
|  | "Error: --ro must be a valid .wav file name.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.artificial_nearend_filename && | 
|  | !valid_wav_name(*settings.artificial_nearend_filename), | 
|  | "Error: --artifical_nearend must be a valid .wav file name.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.linear_aec_output_filename && | 
|  | (!valid_wav_name(*settings.linear_aec_output_filename)), | 
|  | "Error: --linear_aec_output must be a valid .wav file name.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | WEBRTC_APM_DEBUG_DUMP == 0 && settings.dump_internal_data, | 
|  | "Error: --dump_data cannot be set without proper build support.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit(settings.init_to_process && | 
|  | *settings.init_to_process != 1 && | 
|  | !settings.aec_dump_input_filename, | 
|  | "Error: --init_to_process must be set to 1 for " | 
|  | "wav-file based simulations.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | !settings.init_to_process && | 
|  | (settings.dump_start_frame || settings.dump_end_frame), | 
|  | "Error: --init_to_process must be set when specifying a start and/or end " | 
|  | "frame for when to dump internal data.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | !settings.dump_internal_data && | 
|  | settings.dump_internal_data_output_dir.has_value(), | 
|  | "Error: --dump_data_output_dir cannot be set without --dump_data.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | !settings.aec_dump_input_filename && | 
|  | settings.call_order_output_filename.has_value(), | 
|  | "Error: --output_custom_call_order_file needs an AEC dump input file.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | (!settings.use_pre_amplifier || !(*settings.use_pre_amplifier)) && | 
|  | settings.pre_amplifier_gain_factor.has_value(), | 
|  | "Error: --pre_amplifier_gain_factor needs --pre_amplifier to be " | 
|  | "specified and set.\n"); | 
|  | } | 
|  |  | 
|  | void CheckSettingsForBuiltinBuilderAreUnused( | 
|  | const SimulationSettings& settings) { | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.aec_settings_filename.has_value(), | 
|  | "Error: The aec_settings_filename cannot be specified when a " | 
|  | "pre-constructed audio processing object is provided.\n"); | 
|  |  | 
|  | ReportConditionalErrorAndExit( | 
|  | settings.print_aec_parameter_values, | 
|  | "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) { | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Helper for reading JSON from a file and parsing it to an AEC3 configuration. | 
|  | EchoCanceller3Config ReadAec3ConfigFromJsonFile(absl::string_view filename) { | 
|  | std::string json_string; | 
|  | std::string s; | 
|  | std::ifstream f(std::string(filename).c_str()); | 
|  | if (f.fail()) { | 
|  | std::cout << "Failed to open the file " << filename << std::endl; | 
|  | RTC_CHECK_NOTREACHED(); | 
|  | } | 
|  | while (std::getline(f, s)) { | 
|  | json_string += s; | 
|  | } | 
|  |  | 
|  | bool parsing_successful; | 
|  | EchoCanceller3Config cfg; | 
|  | Aec3ConfigFromJsonString(json_string, &cfg, &parsing_successful); | 
|  | if (!parsing_successful) { | 
|  | std::cout << "Parsing of json string failed: " << std::endl | 
|  | << json_string << std::endl; | 
|  | RTC_CHECK_NOTREACHED(); | 
|  | } | 
|  | RTC_CHECK(EchoCanceller3Config::Validate(&cfg)); | 
|  |  | 
|  | return cfg; | 
|  | } | 
|  |  | 
|  | void SetDependencies(const SimulationSettings& settings, | 
|  | BuiltinAudioProcessingBuilder& builder) { | 
|  | // Create and set an EchoCanceller3Factory if needed. | 
|  | if (settings.use_aec && *settings.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; | 
|  | } | 
|  |  | 
|  | builder.SetEchoControlFactory(std::make_unique<EchoCanceller3Factory>(cfg)); | 
|  | } | 
|  |  | 
|  | if (settings.use_ed && *settings.use_ed) { | 
|  | builder.SetEchoDetector(CreateEchoDetector()); | 
|  | } | 
|  | } | 
|  |  | 
|  | int RunSimulation( | 
|  | absl_nonnull std::unique_ptr<AudioProcessingBuilderInterface> ap_builder, | 
|  | bool builtin_builder_provided, | 
|  | int argc, | 
|  | char* argv[]) { | 
|  | std::vector<char*> args = absl::ParseCommandLine(argc, argv); | 
|  | if (args.size() != 1) { | 
|  | printf("%s", kUsageDescription); | 
|  | return 1; | 
|  | } | 
|  | FieldTrials field_trials(absl::GetFlag(FLAGS_force_fieldtrials)); | 
|  | const Environment env = CreateEnvironment(&field_trials); | 
|  |  | 
|  | SimulationSettings settings = CreateSettings(); | 
|  | PerformBasicParameterSanityChecks(settings); | 
|  | if (builtin_builder_provided) { | 
|  | SetDependencies(settings, | 
|  | static_cast<BuiltinAudioProcessingBuilder&>(*ap_builder)); | 
|  | } else { | 
|  | CheckSettingsForBuiltinBuilderAreUnused(settings); | 
|  | } | 
|  | scoped_refptr<AudioProcessing> audio_processing = ap_builder->Build(env); | 
|  | RTC_CHECK(audio_processing); | 
|  |  | 
|  | std::unique_ptr<AudioProcessingSimulator> processor; | 
|  | if (settings.aec_dump_input_filename || settings.aec_dump_input_string) { | 
|  | processor = std::make_unique<AecDumpBasedSimulator>( | 
|  | settings, std::move(audio_processing)); | 
|  | } else { | 
|  | processor = std::make_unique<WavBasedSimulator>( | 
|  | settings, std::move(audio_processing)); | 
|  | } | 
|  |  | 
|  | if (settings.analysis_only) { | 
|  | processor->Analyze(); | 
|  | } else { | 
|  | processor->Process(); | 
|  | } | 
|  |  | 
|  | if (settings.report_performance) { | 
|  | processor->GetApiCallStatistics().PrintReport(); | 
|  | } | 
|  | if (settings.performance_report_output_filename) { | 
|  | processor->GetApiCallStatistics().WriteReportToFile( | 
|  | *settings.performance_report_output_filename); | 
|  | } | 
|  |  | 
|  | if (settings.report_bitexactness && settings.aec_dump_input_filename) { | 
|  | if (processor->OutputWasBitexact()) { | 
|  | std::cout << "The processing was bitexact."; | 
|  | } else { | 
|  | std::cout << "The processing was not bitexact."; | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | int AudioprocFloatImpl( | 
|  | absl_nonnull std::unique_ptr<BuiltinAudioProcessingBuilder> ap_builder, | 
|  | int argc, | 
|  | char* argv[]) { | 
|  | return RunSimulation(std::move(ap_builder), /*builtin_builder_provided=*/true, | 
|  | argc, argv); | 
|  | } | 
|  |  | 
|  | int AudioprocFloatImpl( | 
|  | absl_nonnull std::unique_ptr<AudioProcessingBuilderInterface> ap_builder, | 
|  | int argc, | 
|  | char* argv[]) { | 
|  | return RunSimulation(std::move(ap_builder), | 
|  | /*builtin_builder_provided=*/false, argc, argv); | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  | }  // namespace webrtc |