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