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