xref: /aosp_15_r20/external/webrtc/modules/audio_processing/test/audio_processing_simulator.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "modules/audio_processing/test/audio_processing_simulator.h"
12 
13 #include <algorithm>
14 #include <fstream>
15 #include <iostream>
16 #include <memory>
17 #include <string>
18 #include <utility>
19 #include <vector>
20 
21 #include "absl/strings/string_view.h"
22 #include "api/audio/echo_canceller3_config_json.h"
23 #include "api/audio/echo_canceller3_factory.h"
24 #include "api/audio/echo_detector_creator.h"
25 #include "modules/audio_processing/aec_dump/aec_dump_factory.h"
26 #include "modules/audio_processing/echo_control_mobile_impl.h"
27 #include "modules/audio_processing/include/audio_processing.h"
28 #include "modules/audio_processing/logging/apm_data_dumper.h"
29 #include "modules/audio_processing/test/fake_recording_device.h"
30 #include "rtc_base/checks.h"
31 #include "rtc_base/logging.h"
32 #include "rtc_base/strings/json.h"
33 #include "rtc_base/strings/string_builder.h"
34 
35 namespace webrtc {
36 namespace test {
37 namespace {
38 // Helper for reading JSON from a file and parsing it to an AEC3 configuration.
ReadAec3ConfigFromJsonFile(absl::string_view filename)39 EchoCanceller3Config ReadAec3ConfigFromJsonFile(absl::string_view filename) {
40   std::string json_string;
41   std::string s;
42   std::ifstream f(std::string(filename).c_str());
43   if (f.fail()) {
44     std::cout << "Failed to open the file " << filename << std::endl;
45     RTC_CHECK_NOTREACHED();
46   }
47   while (std::getline(f, s)) {
48     json_string += s;
49   }
50 
51   bool parsing_successful;
52   EchoCanceller3Config cfg;
53   Aec3ConfigFromJsonString(json_string, &cfg, &parsing_successful);
54   if (!parsing_successful) {
55     std::cout << "Parsing of json string failed: " << std::endl
56               << json_string << std::endl;
57     RTC_CHECK_NOTREACHED();
58   }
59   RTC_CHECK(EchoCanceller3Config::Validate(&cfg));
60 
61   return cfg;
62 }
63 
GetIndexedOutputWavFilename(absl::string_view wav_name,int counter)64 std::string GetIndexedOutputWavFilename(absl::string_view wav_name,
65                                         int counter) {
66   rtc::StringBuilder ss;
67   ss << wav_name.substr(0, wav_name.size() - 4) << "_" << counter
68      << wav_name.substr(wav_name.size() - 4);
69   return ss.Release();
70 }
71 
WriteEchoLikelihoodGraphFileHeader(std::ofstream * output_file)72 void WriteEchoLikelihoodGraphFileHeader(std::ofstream* output_file) {
73   (*output_file) << "import numpy as np" << std::endl
74                  << "import matplotlib.pyplot as plt" << std::endl
75                  << "y = np.array([";
76 }
77 
WriteEchoLikelihoodGraphFileFooter(std::ofstream * output_file)78 void WriteEchoLikelihoodGraphFileFooter(std::ofstream* output_file) {
79   (*output_file) << "])" << std::endl
80                  << "if __name__ == '__main__':" << std::endl
81                  << "  x = np.arange(len(y))*.01" << std::endl
82                  << "  plt.plot(x, y)" << std::endl
83                  << "  plt.ylabel('Echo likelihood')" << std::endl
84                  << "  plt.xlabel('Time (s)')" << std::endl
85                  << "  plt.show()" << std::endl;
86 }
87 
88 // RAII class for execution time measurement. Updates the provided
89 // ApiCallStatistics based on the time between ScopedTimer creation and
90 // leaving the enclosing scope.
91 class ScopedTimer {
92  public:
ScopedTimer(ApiCallStatistics * api_call_statistics,ApiCallStatistics::CallType call_type)93   ScopedTimer(ApiCallStatistics* api_call_statistics,
94               ApiCallStatistics::CallType call_type)
95       : start_time_(rtc::TimeNanos()),
96         call_type_(call_type),
97         api_call_statistics_(api_call_statistics) {}
98 
~ScopedTimer()99   ~ScopedTimer() {
100     api_call_statistics_->Add(rtc::TimeNanos() - start_time_, call_type_);
101   }
102 
103  private:
104   const int64_t start_time_;
105   const ApiCallStatistics::CallType call_type_;
106   ApiCallStatistics* const api_call_statistics_;
107 };
108 
109 }  // namespace
110 
111 SimulationSettings::SimulationSettings() = default;
112 SimulationSettings::SimulationSettings(const SimulationSettings&) = default;
113 SimulationSettings::~SimulationSettings() = default;
114 
AudioProcessingSimulator(const SimulationSettings & settings,rtc::scoped_refptr<AudioProcessing> audio_processing,std::unique_ptr<AudioProcessingBuilder> ap_builder)115 AudioProcessingSimulator::AudioProcessingSimulator(
116     const SimulationSettings& settings,
117     rtc::scoped_refptr<AudioProcessing> audio_processing,
118     std::unique_ptr<AudioProcessingBuilder> ap_builder)
119     : settings_(settings),
120       ap_(std::move(audio_processing)),
121       applied_input_volume_(settings.initial_mic_level),
122       fake_recording_device_(
123           settings.initial_mic_level,
124           settings_.simulate_mic_gain ? *settings.simulated_mic_kind : 0),
125       worker_queue_("file_writer_task_queue") {
126   RTC_CHECK(!settings_.dump_internal_data || WEBRTC_APM_DEBUG_DUMP == 1);
127   if (settings_.dump_start_frame || settings_.dump_end_frame) {
128     ApmDataDumper::SetActivated(!settings_.dump_start_frame);
129   } else {
130     ApmDataDumper::SetActivated(settings_.dump_internal_data);
131   }
132 
133   if (settings_.dump_set_to_use) {
134     ApmDataDumper::SetDumpSetToUse(*settings_.dump_set_to_use);
135   }
136 
137   if (settings_.dump_internal_data_output_dir.has_value()) {
138     ApmDataDumper::SetOutputDirectory(
139         settings_.dump_internal_data_output_dir.value());
140   }
141 
142   if (settings_.ed_graph_output_filename &&
143       !settings_.ed_graph_output_filename->empty()) {
144     residual_echo_likelihood_graph_writer_.open(
145         *settings_.ed_graph_output_filename);
146     RTC_CHECK(residual_echo_likelihood_graph_writer_.is_open());
147     WriteEchoLikelihoodGraphFileHeader(&residual_echo_likelihood_graph_writer_);
148   }
149 
150   if (settings_.simulate_mic_gain)
151     RTC_LOG(LS_VERBOSE) << "Simulating analog mic gain";
152 
153   // Create the audio processing object.
154   RTC_CHECK(!(ap_ && ap_builder))
155       << "The AudioProcessing and the AudioProcessingBuilder cannot both be "
156          "specified at the same time.";
157 
158   if (ap_) {
159     RTC_CHECK(!settings_.aec_settings_filename);
160     RTC_CHECK(!settings_.print_aec_parameter_values);
161   } else {
162     // Use specied builder if such is provided, otherwise create a new builder.
163     std::unique_ptr<AudioProcessingBuilder> builder =
164         !!ap_builder ? std::move(ap_builder)
165                      : std::make_unique<AudioProcessingBuilder>();
166 
167     // Create and set an EchoCanceller3Factory if needed.
168     const bool use_aec = settings_.use_aec && *settings_.use_aec;
169     if (use_aec) {
170       EchoCanceller3Config cfg;
171       if (settings_.aec_settings_filename) {
172         if (settings_.use_verbose_logging) {
173           std::cout << "Reading AEC Parameters from JSON input." << std::endl;
174         }
175         cfg = ReadAec3ConfigFromJsonFile(*settings_.aec_settings_filename);
176       }
177 
178       if (settings_.linear_aec_output_filename) {
179         cfg.filter.export_linear_aec_output = true;
180       }
181 
182       if (settings_.print_aec_parameter_values) {
183         if (!settings_.use_quiet_output) {
184           std::cout << "AEC settings:" << std::endl;
185         }
186         std::cout << Aec3ConfigToJsonString(cfg) << std::endl;
187       }
188 
189       auto echo_control_factory = std::make_unique<EchoCanceller3Factory>(cfg);
190       builder->SetEchoControlFactory(std::move(echo_control_factory));
191     }
192 
193     if (settings_.use_ed && *settings.use_ed) {
194       builder->SetEchoDetector(CreateEchoDetector());
195     }
196 
197     // Create an audio processing object.
198     ap_ = builder->Create();
199     RTC_CHECK(ap_);
200   }
201 }
202 
~AudioProcessingSimulator()203 AudioProcessingSimulator::~AudioProcessingSimulator() {
204   if (residual_echo_likelihood_graph_writer_.is_open()) {
205     WriteEchoLikelihoodGraphFileFooter(&residual_echo_likelihood_graph_writer_);
206     residual_echo_likelihood_graph_writer_.close();
207   }
208 }
209 
ProcessStream(bool fixed_interface)210 void AudioProcessingSimulator::ProcessStream(bool fixed_interface) {
211   // Optionally simulate the input volume.
212   if (settings_.simulate_mic_gain) {
213     RTC_DCHECK(!settings_.use_analog_mic_gain_emulation);
214     // Set the input volume to simulate.
215     fake_recording_device_.SetMicLevel(applied_input_volume_);
216 
217     if (settings_.aec_dump_input_filename &&
218         aec_dump_applied_input_level_.has_value()) {
219       // For AEC dumps, use the applied input level, if recorded, to "virtually
220       // restore" the capture signal level before the input volume was applied.
221       fake_recording_device_.SetUndoMicLevel(*aec_dump_applied_input_level_);
222     }
223 
224     // Apply the input volume.
225     if (fixed_interface) {
226       fake_recording_device_.SimulateAnalogGain(fwd_frame_.data);
227     } else {
228       fake_recording_device_.SimulateAnalogGain(in_buf_.get());
229     }
230   }
231 
232   // Let APM know which input volume was applied.
233   // Keep track of whether `set_stream_analog_level()` is called.
234   bool applied_input_volume_set = false;
235   if (settings_.simulate_mic_gain) {
236     // When the input volume is simulated, use the volume applied for
237     // simulation.
238     ap_->set_stream_analog_level(fake_recording_device_.MicLevel());
239     applied_input_volume_set = true;
240   } else if (!settings_.use_analog_mic_gain_emulation) {
241     // Ignore the recommended input volume stored in `applied_input_volume_` and
242     // instead notify APM with the recorded input volume (if available).
243     if (settings_.aec_dump_input_filename &&
244         aec_dump_applied_input_level_.has_value()) {
245       // The actually applied input volume is available in the AEC dump.
246       ap_->set_stream_analog_level(*aec_dump_applied_input_level_);
247       applied_input_volume_set = true;
248     } else if (!settings_.aec_dump_input_filename) {
249       // Wav files do not include any information about the actually applied
250       // input volume. Hence, use the recommended input volume stored in
251       // `applied_input_volume_`.
252       ap_->set_stream_analog_level(applied_input_volume_);
253       applied_input_volume_set = true;
254     }
255   }
256 
257   // Post any scheduled runtime settings.
258   if (settings_.frame_for_sending_capture_output_used_false &&
259       *settings_.frame_for_sending_capture_output_used_false ==
260           static_cast<int>(num_process_stream_calls_)) {
261     ap_->PostRuntimeSetting(
262         AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(false));
263   }
264   if (settings_.frame_for_sending_capture_output_used_true &&
265       *settings_.frame_for_sending_capture_output_used_true ==
266           static_cast<int>(num_process_stream_calls_)) {
267     ap_->PostRuntimeSetting(
268         AudioProcessing::RuntimeSetting::CreateCaptureOutputUsedSetting(true));
269   }
270 
271   // Process the current audio frame.
272   if (fixed_interface) {
273     {
274       const auto st = ScopedTimer(&api_call_statistics_,
275                                   ApiCallStatistics::CallType::kCapture);
276       RTC_CHECK_EQ(
277           AudioProcessing::kNoError,
278           ap_->ProcessStream(fwd_frame_.data.data(), fwd_frame_.config,
279                              fwd_frame_.config, fwd_frame_.data.data()));
280     }
281     fwd_frame_.CopyTo(out_buf_.get());
282   } else {
283     const auto st = ScopedTimer(&api_call_statistics_,
284                                 ApiCallStatistics::CallType::kCapture);
285     RTC_CHECK_EQ(AudioProcessing::kNoError,
286                  ap_->ProcessStream(in_buf_->channels(), in_config_,
287                                     out_config_, out_buf_->channels()));
288   }
289 
290   // Retrieve the recommended input volume only if `set_stream_analog_level()`
291   // has been called to stick to the APM API contract.
292   if (applied_input_volume_set) {
293     applied_input_volume_ = ap_->recommended_stream_analog_level();
294   }
295 
296   if (buffer_memory_writer_) {
297     RTC_CHECK(!buffer_file_writer_);
298     buffer_memory_writer_->Write(*out_buf_);
299   } else if (buffer_file_writer_) {
300     RTC_CHECK(!buffer_memory_writer_);
301     buffer_file_writer_->Write(*out_buf_);
302   }
303 
304   if (linear_aec_output_file_writer_) {
305     bool output_available = ap_->GetLinearAecOutput(linear_aec_output_buf_);
306     RTC_CHECK(output_available);
307     RTC_CHECK_GT(linear_aec_output_buf_.size(), 0);
308     RTC_CHECK_EQ(linear_aec_output_buf_[0].size(), 160);
309     for (size_t k = 0; k < linear_aec_output_buf_[0].size(); ++k) {
310       for (size_t ch = 0; ch < linear_aec_output_buf_.size(); ++ch) {
311         RTC_CHECK_EQ(linear_aec_output_buf_[ch].size(), 160);
312         float sample = FloatToFloatS16(linear_aec_output_buf_[ch][k]);
313         linear_aec_output_file_writer_->WriteSamples(&sample, 1);
314       }
315     }
316   }
317 
318   if (residual_echo_likelihood_graph_writer_.is_open()) {
319     auto stats = ap_->GetStatistics();
320     residual_echo_likelihood_graph_writer_
321         << stats.residual_echo_likelihood.value_or(-1.f) << ", ";
322   }
323 
324   ++num_process_stream_calls_;
325 }
326 
ProcessReverseStream(bool fixed_interface)327 void AudioProcessingSimulator::ProcessReverseStream(bool fixed_interface) {
328   if (fixed_interface) {
329     {
330       const auto st = ScopedTimer(&api_call_statistics_,
331                                   ApiCallStatistics::CallType::kRender);
332       RTC_CHECK_EQ(
333           AudioProcessing::kNoError,
334           ap_->ProcessReverseStream(rev_frame_.data.data(), rev_frame_.config,
335                                     rev_frame_.config, rev_frame_.data.data()));
336     }
337     rev_frame_.CopyTo(reverse_out_buf_.get());
338   } else {
339     const auto st = ScopedTimer(&api_call_statistics_,
340                                 ApiCallStatistics::CallType::kRender);
341     RTC_CHECK_EQ(AudioProcessing::kNoError,
342                  ap_->ProcessReverseStream(
343                      reverse_in_buf_->channels(), reverse_in_config_,
344                      reverse_out_config_, reverse_out_buf_->channels()));
345   }
346 
347   if (reverse_buffer_file_writer_) {
348     reverse_buffer_file_writer_->Write(*reverse_out_buf_);
349   }
350 
351   ++num_reverse_process_stream_calls_;
352 }
353 
SetupBuffersConfigsOutputs(int input_sample_rate_hz,int output_sample_rate_hz,int reverse_input_sample_rate_hz,int reverse_output_sample_rate_hz,int input_num_channels,int output_num_channels,int reverse_input_num_channels,int reverse_output_num_channels)354 void AudioProcessingSimulator::SetupBuffersConfigsOutputs(
355     int input_sample_rate_hz,
356     int output_sample_rate_hz,
357     int reverse_input_sample_rate_hz,
358     int reverse_output_sample_rate_hz,
359     int input_num_channels,
360     int output_num_channels,
361     int reverse_input_num_channels,
362     int reverse_output_num_channels) {
363   in_config_ = StreamConfig(input_sample_rate_hz, input_num_channels);
364   in_buf_.reset(new ChannelBuffer<float>(
365       rtc::CheckedDivExact(input_sample_rate_hz, kChunksPerSecond),
366       input_num_channels));
367 
368   reverse_in_config_ =
369       StreamConfig(reverse_input_sample_rate_hz, reverse_input_num_channels);
370   reverse_in_buf_.reset(new ChannelBuffer<float>(
371       rtc::CheckedDivExact(reverse_input_sample_rate_hz, kChunksPerSecond),
372       reverse_input_num_channels));
373 
374   out_config_ = StreamConfig(output_sample_rate_hz, output_num_channels);
375   out_buf_.reset(new ChannelBuffer<float>(
376       rtc::CheckedDivExact(output_sample_rate_hz, kChunksPerSecond),
377       output_num_channels));
378 
379   reverse_out_config_ =
380       StreamConfig(reverse_output_sample_rate_hz, reverse_output_num_channels);
381   reverse_out_buf_.reset(new ChannelBuffer<float>(
382       rtc::CheckedDivExact(reverse_output_sample_rate_hz, kChunksPerSecond),
383       reverse_output_num_channels));
384 
385   fwd_frame_.SetFormat(input_sample_rate_hz, input_num_channels);
386   rev_frame_.SetFormat(reverse_input_sample_rate_hz,
387                        reverse_input_num_channels);
388 
389   if (settings_.use_verbose_logging) {
390     rtc::LogMessage::LogToDebug(rtc::LS_VERBOSE);
391 
392     std::cout << "Sample rates:" << std::endl;
393     std::cout << " Forward input: " << input_sample_rate_hz << std::endl;
394     std::cout << " Forward output: " << output_sample_rate_hz << std::endl;
395     std::cout << " Reverse input: " << reverse_input_sample_rate_hz
396               << std::endl;
397     std::cout << " Reverse output: " << reverse_output_sample_rate_hz
398               << std::endl;
399     std::cout << "Number of channels: " << std::endl;
400     std::cout << " Forward input: " << input_num_channels << std::endl;
401     std::cout << " Forward output: " << output_num_channels << std::endl;
402     std::cout << " Reverse input: " << reverse_input_num_channels << std::endl;
403     std::cout << " Reverse output: " << reverse_output_num_channels
404               << std::endl;
405   }
406 
407   SetupOutput();
408 }
409 
SelectivelyToggleDataDumping(int init_index,int capture_frames_since_init) const410 void AudioProcessingSimulator::SelectivelyToggleDataDumping(
411     int init_index,
412     int capture_frames_since_init) const {
413   if (!(settings_.dump_start_frame || settings_.dump_end_frame)) {
414     return;
415   }
416 
417   if (settings_.init_to_process && *settings_.init_to_process != init_index) {
418     return;
419   }
420 
421   if (settings_.dump_start_frame &&
422       *settings_.dump_start_frame == capture_frames_since_init) {
423     ApmDataDumper::SetActivated(true);
424   }
425 
426   if (settings_.dump_end_frame &&
427       *settings_.dump_end_frame == capture_frames_since_init) {
428     ApmDataDumper::SetActivated(false);
429   }
430 }
431 
SetupOutput()432 void AudioProcessingSimulator::SetupOutput() {
433   if (settings_.output_filename) {
434     std::string filename;
435     if (settings_.store_intermediate_output) {
436       filename = GetIndexedOutputWavFilename(*settings_.output_filename,
437                                              output_reset_counter_);
438     } else {
439       filename = *settings_.output_filename;
440     }
441 
442     std::unique_ptr<WavWriter> out_file(
443         new WavWriter(filename, out_config_.sample_rate_hz(),
444                       static_cast<size_t>(out_config_.num_channels()),
445                       settings_.wav_output_format));
446     buffer_file_writer_.reset(new ChannelBufferWavWriter(std::move(out_file)));
447   } else if (settings_.aec_dump_input_string.has_value()) {
448     buffer_memory_writer_ = std::make_unique<ChannelBufferVectorWriter>(
449         settings_.processed_capture_samples);
450   }
451 
452   if (settings_.linear_aec_output_filename) {
453     std::string filename;
454     if (settings_.store_intermediate_output) {
455       filename = GetIndexedOutputWavFilename(
456           *settings_.linear_aec_output_filename, output_reset_counter_);
457     } else {
458       filename = *settings_.linear_aec_output_filename;
459     }
460 
461     linear_aec_output_file_writer_.reset(
462         new WavWriter(filename, 16000, out_config_.num_channels(),
463                       settings_.wav_output_format));
464 
465     linear_aec_output_buf_.resize(out_config_.num_channels());
466   }
467 
468   if (settings_.reverse_output_filename) {
469     std::string filename;
470     if (settings_.store_intermediate_output) {
471       filename = GetIndexedOutputWavFilename(*settings_.reverse_output_filename,
472                                              output_reset_counter_);
473     } else {
474       filename = *settings_.reverse_output_filename;
475     }
476 
477     std::unique_ptr<WavWriter> reverse_out_file(
478         new WavWriter(filename, reverse_out_config_.sample_rate_hz(),
479                       static_cast<size_t>(reverse_out_config_.num_channels()),
480                       settings_.wav_output_format));
481     reverse_buffer_file_writer_.reset(
482         new ChannelBufferWavWriter(std::move(reverse_out_file)));
483   }
484 
485   ++output_reset_counter_;
486 }
487 
DetachAecDump()488 void AudioProcessingSimulator::DetachAecDump() {
489   if (settings_.aec_dump_output_filename) {
490     ap_->DetachAecDump();
491   }
492 }
493 
ConfigureAudioProcessor()494 void AudioProcessingSimulator::ConfigureAudioProcessor() {
495   AudioProcessing::Config apm_config;
496   if (settings_.use_ts) {
497     apm_config.transient_suppression.enabled = *settings_.use_ts != 0;
498   }
499   if (settings_.multi_channel_render) {
500     apm_config.pipeline.multi_channel_render = *settings_.multi_channel_render;
501   }
502 
503   if (settings_.multi_channel_capture) {
504     apm_config.pipeline.multi_channel_capture =
505         *settings_.multi_channel_capture;
506   }
507 
508   if (settings_.use_agc2) {
509     apm_config.gain_controller2.enabled = *settings_.use_agc2;
510     if (settings_.agc2_fixed_gain_db) {
511       apm_config.gain_controller2.fixed_digital.gain_db =
512           *settings_.agc2_fixed_gain_db;
513     }
514     if (settings_.agc2_use_adaptive_gain) {
515       apm_config.gain_controller2.adaptive_digital.enabled =
516           *settings_.agc2_use_adaptive_gain;
517     }
518   }
519   if (settings_.use_pre_amplifier) {
520     apm_config.pre_amplifier.enabled = *settings_.use_pre_amplifier;
521     if (settings_.pre_amplifier_gain_factor) {
522       apm_config.pre_amplifier.fixed_gain_factor =
523           *settings_.pre_amplifier_gain_factor;
524     }
525   }
526 
527   if (settings_.use_analog_mic_gain_emulation) {
528     if (*settings_.use_analog_mic_gain_emulation) {
529       apm_config.capture_level_adjustment.enabled = true;
530       apm_config.capture_level_adjustment.analog_mic_gain_emulation.enabled =
531           true;
532     } else {
533       apm_config.capture_level_adjustment.analog_mic_gain_emulation.enabled =
534           false;
535     }
536   }
537   if (settings_.analog_mic_gain_emulation_initial_level) {
538     apm_config.capture_level_adjustment.analog_mic_gain_emulation
539         .initial_level = *settings_.analog_mic_gain_emulation_initial_level;
540   }
541 
542   if (settings_.use_capture_level_adjustment) {
543     apm_config.capture_level_adjustment.enabled =
544         *settings_.use_capture_level_adjustment;
545   }
546   if (settings_.pre_gain_factor) {
547     apm_config.capture_level_adjustment.pre_gain_factor =
548         *settings_.pre_gain_factor;
549   }
550   if (settings_.post_gain_factor) {
551     apm_config.capture_level_adjustment.post_gain_factor =
552         *settings_.post_gain_factor;
553   }
554 
555   const bool use_aec = settings_.use_aec && *settings_.use_aec;
556   const bool use_aecm = settings_.use_aecm && *settings_.use_aecm;
557   if (use_aec || use_aecm) {
558     apm_config.echo_canceller.enabled = true;
559     apm_config.echo_canceller.mobile_mode = use_aecm;
560   }
561   apm_config.echo_canceller.export_linear_aec_output =
562       !!settings_.linear_aec_output_filename;
563 
564   if (settings_.use_hpf) {
565     apm_config.high_pass_filter.enabled = *settings_.use_hpf;
566   }
567 
568   if (settings_.use_agc) {
569     apm_config.gain_controller1.enabled = *settings_.use_agc;
570   }
571   if (settings_.agc_mode) {
572     apm_config.gain_controller1.mode =
573         static_cast<webrtc::AudioProcessing::Config::GainController1::Mode>(
574             *settings_.agc_mode);
575   }
576   if (settings_.use_agc_limiter) {
577     apm_config.gain_controller1.enable_limiter = *settings_.use_agc_limiter;
578   }
579   if (settings_.agc_target_level) {
580     apm_config.gain_controller1.target_level_dbfs = *settings_.agc_target_level;
581   }
582   if (settings_.agc_compression_gain) {
583     apm_config.gain_controller1.compression_gain_db =
584         *settings_.agc_compression_gain;
585   }
586   if (settings_.use_analog_agc) {
587     apm_config.gain_controller1.analog_gain_controller.enabled =
588         *settings_.use_analog_agc;
589   }
590   if (settings_.analog_agc_use_digital_adaptive_controller) {
591     apm_config.gain_controller1.analog_gain_controller.enable_digital_adaptive =
592         *settings_.analog_agc_use_digital_adaptive_controller;
593   }
594 
595   if (settings_.maximum_internal_processing_rate) {
596     apm_config.pipeline.maximum_internal_processing_rate =
597         *settings_.maximum_internal_processing_rate;
598   }
599 
600   if (settings_.use_ns) {
601     apm_config.noise_suppression.enabled = *settings_.use_ns;
602   }
603   if (settings_.ns_level) {
604     const int level = *settings_.ns_level;
605     RTC_CHECK_GE(level, 0);
606     RTC_CHECK_LE(level, 3);
607     apm_config.noise_suppression.level =
608         static_cast<AudioProcessing::Config::NoiseSuppression::Level>(level);
609   }
610   if (settings_.ns_analysis_on_linear_aec_output) {
611     apm_config.noise_suppression.analyze_linear_aec_output_when_available =
612         *settings_.ns_analysis_on_linear_aec_output;
613   }
614 
615   ap_->ApplyConfig(apm_config);
616 
617   if (settings_.use_ts) {
618     // Default to key pressed if activating the transient suppressor with
619     // continuous key events.
620     ap_->set_stream_key_pressed(*settings_.use_ts == 2);
621   }
622 
623   if (settings_.aec_dump_output_filename) {
624     ap_->AttachAecDump(AecDumpFactory::Create(
625         *settings_.aec_dump_output_filename, -1, &worker_queue_));
626   }
627 }
628 
629 }  // namespace test
630 }  // namespace webrtc
631