1 /*
2 * Copyright (c) 2015 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 <stddef.h> // size_t
12
13 #include <memory>
14 #include <string>
15 #include <vector>
16
17 #include "absl/strings/string_view.h"
18 #include "api/audio/echo_canceller3_factory.h"
19 #include "modules/audio_coding/neteq/tools/resample_input_audio_file.h"
20 #include "modules/audio_processing/aec_dump/aec_dump_factory.h"
21 #include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
22 #include "modules/audio_processing/test/debug_dump_replayer.h"
23 #include "modules/audio_processing/test/test_utils.h"
24 #include "rtc_base/task_queue_for_test.h"
25 #include "test/gtest.h"
26 #include "test/testsupport/file_utils.h"
27
28 namespace webrtc {
29 namespace test {
30
31 namespace {
32
MaybeResetBuffer(std::unique_ptr<ChannelBuffer<float>> * buffer,const StreamConfig & config)33 void MaybeResetBuffer(std::unique_ptr<ChannelBuffer<float>>* buffer,
34 const StreamConfig& config) {
35 auto& buffer_ref = *buffer;
36 if (!buffer_ref.get() || buffer_ref->num_frames() != config.num_frames() ||
37 buffer_ref->num_channels() != config.num_channels()) {
38 buffer_ref.reset(
39 new ChannelBuffer<float>(config.num_frames(), config.num_channels()));
40 }
41 }
42
43 class DebugDumpGenerator {
44 public:
45 DebugDumpGenerator(absl::string_view input_file_name,
46 int input_rate_hz,
47 int input_channels,
48 absl::string_view reverse_file_name,
49 int reverse_rate_hz,
50 int reverse_channels,
51 absl::string_view dump_file_name,
52 bool enable_pre_amplifier);
53
54 // Constructor that uses default input files.
55 explicit DebugDumpGenerator(const AudioProcessing::Config& apm_config);
56
57 ~DebugDumpGenerator();
58
59 // Changes the sample rate of the input audio to the APM.
60 void SetInputRate(int rate_hz);
61
62 // Sets if converts stereo input signal to mono by discarding other channels.
63 void ForceInputMono(bool mono);
64
65 // Changes the sample rate of the reverse audio to the APM.
66 void SetReverseRate(int rate_hz);
67
68 // Sets if converts stereo reverse signal to mono by discarding other
69 // channels.
70 void ForceReverseMono(bool mono);
71
72 // Sets the required sample rate of the APM output.
73 void SetOutputRate(int rate_hz);
74
75 // Sets the required channels of the APM output.
76 void SetOutputChannels(int channels);
77
dump_file_name() const78 std::string dump_file_name() const { return dump_file_name_; }
79
80 void StartRecording();
81 void Process(size_t num_blocks);
82 void StopRecording();
apm() const83 AudioProcessing* apm() const { return apm_.get(); }
84
85 private:
86 static void ReadAndDeinterleave(ResampleInputAudioFile* audio,
87 int channels,
88 const StreamConfig& config,
89 float* const* buffer);
90
91 // APM input/output settings.
92 StreamConfig input_config_;
93 StreamConfig reverse_config_;
94 StreamConfig output_config_;
95
96 // Input file format.
97 const std::string input_file_name_;
98 ResampleInputAudioFile input_audio_;
99 const int input_file_channels_;
100
101 // Reverse file format.
102 const std::string reverse_file_name_;
103 ResampleInputAudioFile reverse_audio_;
104 const int reverse_file_channels_;
105
106 // Buffer for APM input/output.
107 std::unique_ptr<ChannelBuffer<float>> input_;
108 std::unique_ptr<ChannelBuffer<float>> reverse_;
109 std::unique_ptr<ChannelBuffer<float>> output_;
110
111 bool enable_pre_amplifier_;
112
113 TaskQueueForTest worker_queue_;
114 rtc::scoped_refptr<AudioProcessing> apm_;
115
116 const std::string dump_file_name_;
117 };
118
DebugDumpGenerator(absl::string_view input_file_name,int input_rate_hz,int input_channels,absl::string_view reverse_file_name,int reverse_rate_hz,int reverse_channels,absl::string_view dump_file_name,bool enable_pre_amplifier)119 DebugDumpGenerator::DebugDumpGenerator(absl::string_view input_file_name,
120 int input_rate_hz,
121 int input_channels,
122 absl::string_view reverse_file_name,
123 int reverse_rate_hz,
124 int reverse_channels,
125 absl::string_view dump_file_name,
126 bool enable_pre_amplifier)
127 : input_config_(input_rate_hz, input_channels),
128 reverse_config_(reverse_rate_hz, reverse_channels),
129 output_config_(input_rate_hz, input_channels),
130 input_audio_(input_file_name, input_rate_hz, input_rate_hz),
131 input_file_channels_(input_channels),
132 reverse_audio_(reverse_file_name, reverse_rate_hz, reverse_rate_hz),
133 reverse_file_channels_(reverse_channels),
134 input_(new ChannelBuffer<float>(input_config_.num_frames(),
135 input_config_.num_channels())),
136 reverse_(new ChannelBuffer<float>(reverse_config_.num_frames(),
137 reverse_config_.num_channels())),
138 output_(new ChannelBuffer<float>(output_config_.num_frames(),
139 output_config_.num_channels())),
140 enable_pre_amplifier_(enable_pre_amplifier),
141 worker_queue_("debug_dump_generator_worker_queue"),
142 dump_file_name_(dump_file_name) {
143 AudioProcessingBuilderForTesting apm_builder;
144 apm_ = apm_builder.Create();
145 }
146
DebugDumpGenerator(const AudioProcessing::Config & apm_config)147 DebugDumpGenerator::DebugDumpGenerator(
148 const AudioProcessing::Config& apm_config)
149 : DebugDumpGenerator(ResourcePath("near32_stereo", "pcm"),
150 32000,
151 2,
152 ResourcePath("far32_stereo", "pcm"),
153 32000,
154 2,
155 TempFilename(OutputPath(), "debug_aec"),
156 apm_config.pre_amplifier.enabled) {
157 apm_->ApplyConfig(apm_config);
158 }
159
~DebugDumpGenerator()160 DebugDumpGenerator::~DebugDumpGenerator() {
161 remove(dump_file_name_.c_str());
162 }
163
SetInputRate(int rate_hz)164 void DebugDumpGenerator::SetInputRate(int rate_hz) {
165 input_audio_.set_output_rate_hz(rate_hz);
166 input_config_.set_sample_rate_hz(rate_hz);
167 MaybeResetBuffer(&input_, input_config_);
168 }
169
ForceInputMono(bool mono)170 void DebugDumpGenerator::ForceInputMono(bool mono) {
171 const int channels = mono ? 1 : input_file_channels_;
172 input_config_.set_num_channels(channels);
173 MaybeResetBuffer(&input_, input_config_);
174 }
175
SetReverseRate(int rate_hz)176 void DebugDumpGenerator::SetReverseRate(int rate_hz) {
177 reverse_audio_.set_output_rate_hz(rate_hz);
178 reverse_config_.set_sample_rate_hz(rate_hz);
179 MaybeResetBuffer(&reverse_, reverse_config_);
180 }
181
ForceReverseMono(bool mono)182 void DebugDumpGenerator::ForceReverseMono(bool mono) {
183 const int channels = mono ? 1 : reverse_file_channels_;
184 reverse_config_.set_num_channels(channels);
185 MaybeResetBuffer(&reverse_, reverse_config_);
186 }
187
SetOutputRate(int rate_hz)188 void DebugDumpGenerator::SetOutputRate(int rate_hz) {
189 output_config_.set_sample_rate_hz(rate_hz);
190 MaybeResetBuffer(&output_, output_config_);
191 }
192
SetOutputChannels(int channels)193 void DebugDumpGenerator::SetOutputChannels(int channels) {
194 output_config_.set_num_channels(channels);
195 MaybeResetBuffer(&output_, output_config_);
196 }
197
StartRecording()198 void DebugDumpGenerator::StartRecording() {
199 apm_->AttachAecDump(
200 AecDumpFactory::Create(dump_file_name_.c_str(), -1, &worker_queue_));
201 }
202
Process(size_t num_blocks)203 void DebugDumpGenerator::Process(size_t num_blocks) {
204 for (size_t i = 0; i < num_blocks; ++i) {
205 ReadAndDeinterleave(&reverse_audio_, reverse_file_channels_,
206 reverse_config_, reverse_->channels());
207 ReadAndDeinterleave(&input_audio_, input_file_channels_, input_config_,
208 input_->channels());
209 RTC_CHECK_EQ(AudioProcessing::kNoError, apm_->set_stream_delay_ms(100));
210 apm_->set_stream_analog_level(100);
211 if (enable_pre_amplifier_) {
212 apm_->SetRuntimeSetting(
213 AudioProcessing::RuntimeSetting::CreateCapturePreGain(1 + i % 10));
214 }
215 apm_->set_stream_key_pressed(i % 10 == 9);
216 RTC_CHECK_EQ(AudioProcessing::kNoError,
217 apm_->ProcessStream(input_->channels(), input_config_,
218 output_config_, output_->channels()));
219
220 RTC_CHECK_EQ(
221 AudioProcessing::kNoError,
222 apm_->ProcessReverseStream(reverse_->channels(), reverse_config_,
223 reverse_config_, reverse_->channels()));
224 }
225 }
226
StopRecording()227 void DebugDumpGenerator::StopRecording() {
228 apm_->DetachAecDump();
229 }
230
ReadAndDeinterleave(ResampleInputAudioFile * audio,int channels,const StreamConfig & config,float * const * buffer)231 void DebugDumpGenerator::ReadAndDeinterleave(ResampleInputAudioFile* audio,
232 int channels,
233 const StreamConfig& config,
234 float* const* buffer) {
235 const size_t num_frames = config.num_frames();
236 const int out_channels = config.num_channels();
237
238 std::vector<int16_t> signal(channels * num_frames);
239
240 audio->Read(num_frames * channels, &signal[0]);
241
242 // We only allow reducing number of channels by discarding some channels.
243 RTC_CHECK_LE(out_channels, channels);
244 for (int channel = 0; channel < out_channels; ++channel) {
245 for (size_t i = 0; i < num_frames; ++i) {
246 buffer[channel][i] = S16ToFloat(signal[i * channels + channel]);
247 }
248 }
249 }
250
251 } // namespace
252
253 class DebugDumpTest : public ::testing::Test {
254 public:
255 // VerifyDebugDump replays a debug dump using APM and verifies that the result
256 // is bit-exact-identical to the output channel in the dump. This is only
257 // guaranteed if the debug dump is started on the first frame.
258 void VerifyDebugDump(absl::string_view in_filename);
259
260 private:
261 DebugDumpReplayer debug_dump_replayer_;
262 };
263
VerifyDebugDump(absl::string_view in_filename)264 void DebugDumpTest::VerifyDebugDump(absl::string_view in_filename) {
265 ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(in_filename));
266
267 while (const absl::optional<audioproc::Event> event =
268 debug_dump_replayer_.GetNextEvent()) {
269 debug_dump_replayer_.RunNextEvent();
270 if (event->type() == audioproc::Event::STREAM) {
271 const audioproc::Stream* msg = &event->stream();
272 const StreamConfig output_config = debug_dump_replayer_.GetOutputConfig();
273 const ChannelBuffer<float>* output = debug_dump_replayer_.GetOutput();
274 // Check that output of APM is bit-exact to the output in the dump.
275 ASSERT_EQ(output_config.num_channels(),
276 static_cast<size_t>(msg->output_channel_size()));
277 ASSERT_EQ(output_config.num_frames() * sizeof(float),
278 msg->output_channel(0).size());
279 for (int i = 0; i < msg->output_channel_size(); ++i) {
280 ASSERT_EQ(0,
281 memcmp(output->channels()[i], msg->output_channel(i).data(),
282 msg->output_channel(i).size()));
283 }
284 }
285 }
286 }
287
TEST_F(DebugDumpTest,SimpleCase)288 TEST_F(DebugDumpTest, SimpleCase) {
289 DebugDumpGenerator generator(/*apm_config=*/{});
290 generator.StartRecording();
291 generator.Process(100);
292 generator.StopRecording();
293 VerifyDebugDump(generator.dump_file_name());
294 }
295
TEST_F(DebugDumpTest,ChangeInputFormat)296 TEST_F(DebugDumpTest, ChangeInputFormat) {
297 DebugDumpGenerator generator(/*apm_config=*/{});
298
299 generator.StartRecording();
300 generator.Process(100);
301 generator.SetInputRate(48000);
302
303 generator.ForceInputMono(true);
304 // Number of output channel should not be larger than that of input. APM will
305 // fail otherwise.
306 generator.SetOutputChannels(1);
307
308 generator.Process(100);
309 generator.StopRecording();
310 VerifyDebugDump(generator.dump_file_name());
311 }
312
TEST_F(DebugDumpTest,ChangeReverseFormat)313 TEST_F(DebugDumpTest, ChangeReverseFormat) {
314 DebugDumpGenerator generator(/*apm_config=*/{});
315 generator.StartRecording();
316 generator.Process(100);
317 generator.SetReverseRate(48000);
318 generator.ForceReverseMono(true);
319 generator.Process(100);
320 generator.StopRecording();
321 VerifyDebugDump(generator.dump_file_name());
322 }
323
TEST_F(DebugDumpTest,ChangeOutputFormat)324 TEST_F(DebugDumpTest, ChangeOutputFormat) {
325 DebugDumpGenerator generator(/*apm_config=*/{});
326 generator.StartRecording();
327 generator.Process(100);
328 generator.SetOutputRate(48000);
329 generator.SetOutputChannels(1);
330 generator.Process(100);
331 generator.StopRecording();
332 VerifyDebugDump(generator.dump_file_name());
333 }
334
TEST_F(DebugDumpTest,ToggleAec)335 TEST_F(DebugDumpTest, ToggleAec) {
336 AudioProcessing::Config apm_config;
337 apm_config.echo_canceller.enabled = true;
338 DebugDumpGenerator generator(apm_config);
339 generator.StartRecording();
340 generator.Process(100);
341
342 apm_config.echo_canceller.enabled = false;
343 generator.apm()->ApplyConfig(apm_config);
344
345 generator.Process(100);
346 generator.StopRecording();
347 VerifyDebugDump(generator.dump_file_name());
348 }
349
TEST_F(DebugDumpTest,VerifyCombinedExperimentalStringInclusive)350 TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) {
351 AudioProcessing::Config apm_config;
352 apm_config.echo_canceller.enabled = true;
353 apm_config.gain_controller1.analog_gain_controller.enabled = true;
354 apm_config.gain_controller1.analog_gain_controller.startup_min_volume = 0;
355 DebugDumpGenerator generator(apm_config);
356 generator.StartRecording();
357 generator.Process(100);
358 generator.StopRecording();
359
360 DebugDumpReplayer debug_dump_replayer_;
361
362 ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
363
364 while (const absl::optional<audioproc::Event> event =
365 debug_dump_replayer_.GetNextEvent()) {
366 debug_dump_replayer_.RunNextEvent();
367 if (event->type() == audioproc::Event::CONFIG) {
368 const audioproc::Config* msg = &event->config();
369 ASSERT_TRUE(msg->has_experiments_description());
370 EXPECT_PRED_FORMAT2(::testing::IsSubstring, "EchoController",
371 msg->experiments_description().c_str());
372 }
373 }
374 }
375
TEST_F(DebugDumpTest,VerifyCombinedExperimentalStringExclusive)376 TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringExclusive) {
377 AudioProcessing::Config apm_config;
378 apm_config.echo_canceller.enabled = true;
379 DebugDumpGenerator generator(apm_config);
380 generator.StartRecording();
381 generator.Process(100);
382 generator.StopRecording();
383
384 DebugDumpReplayer debug_dump_replayer_;
385
386 ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
387
388 while (const absl::optional<audioproc::Event> event =
389 debug_dump_replayer_.GetNextEvent()) {
390 debug_dump_replayer_.RunNextEvent();
391 if (event->type() == audioproc::Event::CONFIG) {
392 const audioproc::Config* msg = &event->config();
393 ASSERT_TRUE(msg->has_experiments_description());
394 EXPECT_PRED_FORMAT2(::testing::IsNotSubstring,
395 "AgcClippingLevelExperiment",
396 msg->experiments_description().c_str());
397 }
398 }
399 }
400
TEST_F(DebugDumpTest,VerifyAec3ExperimentalString)401 TEST_F(DebugDumpTest, VerifyAec3ExperimentalString) {
402 AudioProcessing::Config apm_config;
403 apm_config.echo_canceller.enabled = true;
404 DebugDumpGenerator generator(apm_config);
405 generator.StartRecording();
406 generator.Process(100);
407 generator.StopRecording();
408
409 DebugDumpReplayer debug_dump_replayer_;
410
411 ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
412
413 while (const absl::optional<audioproc::Event> event =
414 debug_dump_replayer_.GetNextEvent()) {
415 debug_dump_replayer_.RunNextEvent();
416 if (event->type() == audioproc::Event::CONFIG) {
417 const audioproc::Config* msg = &event->config();
418 ASSERT_TRUE(msg->has_experiments_description());
419 EXPECT_PRED_FORMAT2(::testing::IsSubstring, "EchoController",
420 msg->experiments_description().c_str());
421 }
422 }
423 }
424
TEST_F(DebugDumpTest,VerifyEmptyExperimentalString)425 TEST_F(DebugDumpTest, VerifyEmptyExperimentalString) {
426 DebugDumpGenerator generator(/*apm_config=*/{});
427 generator.StartRecording();
428 generator.Process(100);
429 generator.StopRecording();
430
431 DebugDumpReplayer debug_dump_replayer_;
432
433 ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
434
435 while (const absl::optional<audioproc::Event> event =
436 debug_dump_replayer_.GetNextEvent()) {
437 debug_dump_replayer_.RunNextEvent();
438 if (event->type() == audioproc::Event::CONFIG) {
439 const audioproc::Config* msg = &event->config();
440 ASSERT_TRUE(msg->has_experiments_description());
441 EXPECT_EQ(0u, msg->experiments_description().size());
442 }
443 }
444 }
445
446 // AGC is not supported on Android or iOS.
447 #if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
448 #define MAYBE_ToggleAgc DISABLED_ToggleAgc
449 #else
450 #define MAYBE_ToggleAgc ToggleAgc
451 #endif
TEST_F(DebugDumpTest,MAYBE_ToggleAgc)452 TEST_F(DebugDumpTest, MAYBE_ToggleAgc) {
453 DebugDumpGenerator generator(/*apm_config=*/{});
454 generator.StartRecording();
455 generator.Process(100);
456
457 AudioProcessing::Config apm_config = generator.apm()->GetConfig();
458 apm_config.gain_controller1.enabled = !apm_config.gain_controller1.enabled;
459 generator.apm()->ApplyConfig(apm_config);
460
461 generator.Process(100);
462 generator.StopRecording();
463 VerifyDebugDump(generator.dump_file_name());
464 }
465
TEST_F(DebugDumpTest,ToggleNs)466 TEST_F(DebugDumpTest, ToggleNs) {
467 DebugDumpGenerator generator(/*apm_config=*/{});
468 generator.StartRecording();
469 generator.Process(100);
470
471 AudioProcessing::Config apm_config = generator.apm()->GetConfig();
472 apm_config.noise_suppression.enabled = !apm_config.noise_suppression.enabled;
473 generator.apm()->ApplyConfig(apm_config);
474
475 generator.Process(100);
476 generator.StopRecording();
477 VerifyDebugDump(generator.dump_file_name());
478 }
479
TEST_F(DebugDumpTest,TransientSuppressionOn)480 TEST_F(DebugDumpTest, TransientSuppressionOn) {
481 DebugDumpGenerator generator(/*apm_config=*/{});
482
483 AudioProcessing::Config apm_config = generator.apm()->GetConfig();
484 apm_config.transient_suppression.enabled = true;
485 generator.apm()->ApplyConfig(apm_config);
486
487 generator.StartRecording();
488 generator.Process(100);
489 generator.StopRecording();
490 VerifyDebugDump(generator.dump_file_name());
491 }
492
TEST_F(DebugDumpTest,PreAmplifierIsOn)493 TEST_F(DebugDumpTest, PreAmplifierIsOn) {
494 AudioProcessing::Config apm_config;
495 apm_config.pre_amplifier.enabled = true;
496 DebugDumpGenerator generator(apm_config);
497 generator.StartRecording();
498 generator.Process(100);
499 generator.StopRecording();
500 VerifyDebugDump(generator.dump_file_name());
501 }
502
503 } // namespace test
504 } // namespace webrtc
505