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