xref: /aosp_15_r20/external/webrtc/modules/audio_processing/test/debug_dump_test.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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