summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/audio_processing/test/debug_dump_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/modules/audio_processing/test/debug_dump_test.cc')
-rw-r--r--third_party/libwebrtc/modules/audio_processing/test/debug_dump_test.cc504
1 files changed, 504 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/audio_processing/test/debug_dump_test.cc b/third_party/libwebrtc/modules/audio_processing/test/debug_dump_test.cc
new file mode 100644
index 0000000000..cded5de217
--- /dev/null
+++ b/third_party/libwebrtc/modules/audio_processing/test/debug_dump_test.cc
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <stddef.h> // size_t
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "api/audio/echo_canceller3_factory.h"
+#include "modules/audio_coding/neteq/tools/resample_input_audio_file.h"
+#include "modules/audio_processing/aec_dump/aec_dump_factory.h"
+#include "modules/audio_processing/test/audio_processing_builder_for_testing.h"
+#include "modules/audio_processing/test/debug_dump_replayer.h"
+#include "modules/audio_processing/test/test_utils.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "test/gtest.h"
+#include "test/testsupport/file_utils.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+
+void MaybeResetBuffer(std::unique_ptr<ChannelBuffer<float>>* buffer,
+ const StreamConfig& config) {
+ auto& buffer_ref = *buffer;
+ if (!buffer_ref.get() || buffer_ref->num_frames() != config.num_frames() ||
+ buffer_ref->num_channels() != config.num_channels()) {
+ buffer_ref.reset(
+ new ChannelBuffer<float>(config.num_frames(), config.num_channels()));
+ }
+}
+
+class DebugDumpGenerator {
+ public:
+ 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);
+
+ // Constructor that uses default input files.
+ explicit DebugDumpGenerator(const AudioProcessing::Config& apm_config);
+
+ ~DebugDumpGenerator();
+
+ // Changes the sample rate of the input audio to the APM.
+ void SetInputRate(int rate_hz);
+
+ // Sets if converts stereo input signal to mono by discarding other channels.
+ void ForceInputMono(bool mono);
+
+ // Changes the sample rate of the reverse audio to the APM.
+ void SetReverseRate(int rate_hz);
+
+ // Sets if converts stereo reverse signal to mono by discarding other
+ // channels.
+ void ForceReverseMono(bool mono);
+
+ // Sets the required sample rate of the APM output.
+ void SetOutputRate(int rate_hz);
+
+ // Sets the required channels of the APM output.
+ void SetOutputChannels(int channels);
+
+ std::string dump_file_name() const { return dump_file_name_; }
+
+ void StartRecording();
+ void Process(size_t num_blocks);
+ void StopRecording();
+ AudioProcessing* apm() const { return apm_.get(); }
+
+ private:
+ static void ReadAndDeinterleave(ResampleInputAudioFile* audio,
+ int channels,
+ const StreamConfig& config,
+ float* const* buffer);
+
+ // APM input/output settings.
+ StreamConfig input_config_;
+ StreamConfig reverse_config_;
+ StreamConfig output_config_;
+
+ // Input file format.
+ const std::string input_file_name_;
+ ResampleInputAudioFile input_audio_;
+ const int input_file_channels_;
+
+ // Reverse file format.
+ const std::string reverse_file_name_;
+ ResampleInputAudioFile reverse_audio_;
+ const int reverse_file_channels_;
+
+ // Buffer for APM input/output.
+ std::unique_ptr<ChannelBuffer<float>> input_;
+ std::unique_ptr<ChannelBuffer<float>> reverse_;
+ std::unique_ptr<ChannelBuffer<float>> output_;
+
+ bool enable_pre_amplifier_;
+
+ TaskQueueForTest worker_queue_;
+ rtc::scoped_refptr<AudioProcessing> apm_;
+
+ const std::string dump_file_name_;
+};
+
+DebugDumpGenerator::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)
+ : input_config_(input_rate_hz, input_channels),
+ reverse_config_(reverse_rate_hz, reverse_channels),
+ output_config_(input_rate_hz, input_channels),
+ input_audio_(input_file_name, input_rate_hz, input_rate_hz),
+ input_file_channels_(input_channels),
+ reverse_audio_(reverse_file_name, reverse_rate_hz, reverse_rate_hz),
+ reverse_file_channels_(reverse_channels),
+ input_(new ChannelBuffer<float>(input_config_.num_frames(),
+ input_config_.num_channels())),
+ reverse_(new ChannelBuffer<float>(reverse_config_.num_frames(),
+ reverse_config_.num_channels())),
+ output_(new ChannelBuffer<float>(output_config_.num_frames(),
+ output_config_.num_channels())),
+ enable_pre_amplifier_(enable_pre_amplifier),
+ worker_queue_("debug_dump_generator_worker_queue"),
+ dump_file_name_(dump_file_name) {
+ AudioProcessingBuilderForTesting apm_builder;
+ apm_ = apm_builder.Create();
+}
+
+DebugDumpGenerator::DebugDumpGenerator(
+ const AudioProcessing::Config& apm_config)
+ : DebugDumpGenerator(ResourcePath("near32_stereo", "pcm"),
+ 32000,
+ 2,
+ ResourcePath("far32_stereo", "pcm"),
+ 32000,
+ 2,
+ TempFilename(OutputPath(), "debug_aec"),
+ apm_config.pre_amplifier.enabled) {
+ apm_->ApplyConfig(apm_config);
+}
+
+DebugDumpGenerator::~DebugDumpGenerator() {
+ remove(dump_file_name_.c_str());
+}
+
+void DebugDumpGenerator::SetInputRate(int rate_hz) {
+ input_audio_.set_output_rate_hz(rate_hz);
+ input_config_.set_sample_rate_hz(rate_hz);
+ MaybeResetBuffer(&input_, input_config_);
+}
+
+void DebugDumpGenerator::ForceInputMono(bool mono) {
+ const int channels = mono ? 1 : input_file_channels_;
+ input_config_.set_num_channels(channels);
+ MaybeResetBuffer(&input_, input_config_);
+}
+
+void DebugDumpGenerator::SetReverseRate(int rate_hz) {
+ reverse_audio_.set_output_rate_hz(rate_hz);
+ reverse_config_.set_sample_rate_hz(rate_hz);
+ MaybeResetBuffer(&reverse_, reverse_config_);
+}
+
+void DebugDumpGenerator::ForceReverseMono(bool mono) {
+ const int channels = mono ? 1 : reverse_file_channels_;
+ reverse_config_.set_num_channels(channels);
+ MaybeResetBuffer(&reverse_, reverse_config_);
+}
+
+void DebugDumpGenerator::SetOutputRate(int rate_hz) {
+ output_config_.set_sample_rate_hz(rate_hz);
+ MaybeResetBuffer(&output_, output_config_);
+}
+
+void DebugDumpGenerator::SetOutputChannels(int channels) {
+ output_config_.set_num_channels(channels);
+ MaybeResetBuffer(&output_, output_config_);
+}
+
+void DebugDumpGenerator::StartRecording() {
+ apm_->AttachAecDump(
+ AecDumpFactory::Create(dump_file_name_.c_str(), -1, &worker_queue_));
+}
+
+void DebugDumpGenerator::Process(size_t num_blocks) {
+ for (size_t i = 0; i < num_blocks; ++i) {
+ ReadAndDeinterleave(&reverse_audio_, reverse_file_channels_,
+ reverse_config_, reverse_->channels());
+ ReadAndDeinterleave(&input_audio_, input_file_channels_, input_config_,
+ input_->channels());
+ RTC_CHECK_EQ(AudioProcessing::kNoError, apm_->set_stream_delay_ms(100));
+ apm_->set_stream_analog_level(100);
+ if (enable_pre_amplifier_) {
+ apm_->SetRuntimeSetting(
+ AudioProcessing::RuntimeSetting::CreateCapturePreGain(1 + i % 10));
+ }
+ apm_->set_stream_key_pressed(i % 10 == 9);
+ RTC_CHECK_EQ(AudioProcessing::kNoError,
+ apm_->ProcessStream(input_->channels(), input_config_,
+ output_config_, output_->channels()));
+
+ RTC_CHECK_EQ(
+ AudioProcessing::kNoError,
+ apm_->ProcessReverseStream(reverse_->channels(), reverse_config_,
+ reverse_config_, reverse_->channels()));
+ }
+}
+
+void DebugDumpGenerator::StopRecording() {
+ apm_->DetachAecDump();
+}
+
+void DebugDumpGenerator::ReadAndDeinterleave(ResampleInputAudioFile* audio,
+ int channels,
+ const StreamConfig& config,
+ float* const* buffer) {
+ const size_t num_frames = config.num_frames();
+ const int out_channels = config.num_channels();
+
+ std::vector<int16_t> signal(channels * num_frames);
+
+ audio->Read(num_frames * channels, &signal[0]);
+
+ // We only allow reducing number of channels by discarding some channels.
+ RTC_CHECK_LE(out_channels, channels);
+ for (int channel = 0; channel < out_channels; ++channel) {
+ for (size_t i = 0; i < num_frames; ++i) {
+ buffer[channel][i] = S16ToFloat(signal[i * channels + channel]);
+ }
+ }
+}
+
+} // namespace
+
+class DebugDumpTest : public ::testing::Test {
+ public:
+ // VerifyDebugDump replays a debug dump using APM and verifies that the result
+ // is bit-exact-identical to the output channel in the dump. This is only
+ // guaranteed if the debug dump is started on the first frame.
+ void VerifyDebugDump(absl::string_view in_filename);
+
+ private:
+ DebugDumpReplayer debug_dump_replayer_;
+};
+
+void DebugDumpTest::VerifyDebugDump(absl::string_view in_filename) {
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(in_filename));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::STREAM) {
+ const audioproc::Stream* msg = &event->stream();
+ const StreamConfig output_config = debug_dump_replayer_.GetOutputConfig();
+ const ChannelBuffer<float>* output = debug_dump_replayer_.GetOutput();
+ // Check that output of APM is bit-exact to the output in the dump.
+ ASSERT_EQ(output_config.num_channels(),
+ static_cast<size_t>(msg->output_channel_size()));
+ ASSERT_EQ(output_config.num_frames() * sizeof(float),
+ msg->output_channel(0).size());
+ for (int i = 0; i < msg->output_channel_size(); ++i) {
+ ASSERT_EQ(0,
+ memcmp(output->channels()[i], msg->output_channel(i).data(),
+ msg->output_channel(i).size()));
+ }
+ }
+ }
+}
+
+TEST_F(DebugDumpTest, SimpleCase) {
+ DebugDumpGenerator generator(/*apm_config=*/{});
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, ChangeInputFormat) {
+ DebugDumpGenerator generator(/*apm_config=*/{});
+
+ generator.StartRecording();
+ generator.Process(100);
+ generator.SetInputRate(48000);
+
+ generator.ForceInputMono(true);
+ // Number of output channel should not be larger than that of input. APM will
+ // fail otherwise.
+ generator.SetOutputChannels(1);
+
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, ChangeReverseFormat) {
+ DebugDumpGenerator generator(/*apm_config=*/{});
+ generator.StartRecording();
+ generator.Process(100);
+ generator.SetReverseRate(48000);
+ generator.ForceReverseMono(true);
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, ChangeOutputFormat) {
+ DebugDumpGenerator generator(/*apm_config=*/{});
+ generator.StartRecording();
+ generator.Process(100);
+ generator.SetOutputRate(48000);
+ generator.SetOutputChannels(1);
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, ToggleAec) {
+ AudioProcessing::Config apm_config;
+ apm_config.echo_canceller.enabled = true;
+ DebugDumpGenerator generator(apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+
+ apm_config.echo_canceller.enabled = false;
+ generator.apm()->ApplyConfig(apm_config);
+
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringInclusive) {
+ AudioProcessing::Config apm_config;
+ apm_config.echo_canceller.enabled = true;
+ apm_config.gain_controller1.analog_gain_controller.enabled = true;
+ apm_config.gain_controller1.analog_gain_controller.startup_min_volume = 0;
+ DebugDumpGenerator generator(apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+
+ DebugDumpReplayer debug_dump_replayer_;
+
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::CONFIG) {
+ const audioproc::Config* msg = &event->config();
+ ASSERT_TRUE(msg->has_experiments_description());
+ EXPECT_PRED_FORMAT2(::testing::IsSubstring, "EchoController",
+ msg->experiments_description().c_str());
+ }
+ }
+}
+
+TEST_F(DebugDumpTest, VerifyCombinedExperimentalStringExclusive) {
+ AudioProcessing::Config apm_config;
+ apm_config.echo_canceller.enabled = true;
+ DebugDumpGenerator generator(apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+
+ DebugDumpReplayer debug_dump_replayer_;
+
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::CONFIG) {
+ const audioproc::Config* msg = &event->config();
+ ASSERT_TRUE(msg->has_experiments_description());
+ EXPECT_PRED_FORMAT2(::testing::IsNotSubstring,
+ "AgcClippingLevelExperiment",
+ msg->experiments_description().c_str());
+ }
+ }
+}
+
+TEST_F(DebugDumpTest, VerifyAec3ExperimentalString) {
+ AudioProcessing::Config apm_config;
+ apm_config.echo_canceller.enabled = true;
+ DebugDumpGenerator generator(apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+
+ DebugDumpReplayer debug_dump_replayer_;
+
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::CONFIG) {
+ const audioproc::Config* msg = &event->config();
+ ASSERT_TRUE(msg->has_experiments_description());
+ EXPECT_PRED_FORMAT2(::testing::IsSubstring, "EchoController",
+ msg->experiments_description().c_str());
+ }
+ }
+}
+
+TEST_F(DebugDumpTest, VerifyEmptyExperimentalString) {
+ DebugDumpGenerator generator(/*apm_config=*/{});
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+
+ DebugDumpReplayer debug_dump_replayer_;
+
+ ASSERT_TRUE(debug_dump_replayer_.SetDumpFile(generator.dump_file_name()));
+
+ while (const absl::optional<audioproc::Event> event =
+ debug_dump_replayer_.GetNextEvent()) {
+ debug_dump_replayer_.RunNextEvent();
+ if (event->type() == audioproc::Event::CONFIG) {
+ const audioproc::Config* msg = &event->config();
+ ASSERT_TRUE(msg->has_experiments_description());
+ EXPECT_EQ(0u, msg->experiments_description().size());
+ }
+ }
+}
+
+// AGC is not supported on Android or iOS.
+#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
+#define MAYBE_ToggleAgc DISABLED_ToggleAgc
+#else
+#define MAYBE_ToggleAgc ToggleAgc
+#endif
+TEST_F(DebugDumpTest, MAYBE_ToggleAgc) {
+ DebugDumpGenerator generator(/*apm_config=*/{});
+ generator.StartRecording();
+ generator.Process(100);
+
+ AudioProcessing::Config apm_config = generator.apm()->GetConfig();
+ apm_config.gain_controller1.enabled = !apm_config.gain_controller1.enabled;
+ generator.apm()->ApplyConfig(apm_config);
+
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, ToggleNs) {
+ DebugDumpGenerator generator(/*apm_config=*/{});
+ generator.StartRecording();
+ generator.Process(100);
+
+ AudioProcessing::Config apm_config = generator.apm()->GetConfig();
+ apm_config.noise_suppression.enabled = !apm_config.noise_suppression.enabled;
+ generator.apm()->ApplyConfig(apm_config);
+
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, TransientSuppressionOn) {
+ DebugDumpGenerator generator(/*apm_config=*/{});
+
+ AudioProcessing::Config apm_config = generator.apm()->GetConfig();
+ apm_config.transient_suppression.enabled = true;
+ generator.apm()->ApplyConfig(apm_config);
+
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+TEST_F(DebugDumpTest, PreAmplifierIsOn) {
+ AudioProcessing::Config apm_config;
+ apm_config.pre_amplifier.enabled = true;
+ DebugDumpGenerator generator(apm_config);
+ generator.StartRecording();
+ generator.Process(100);
+ generator.StopRecording();
+ VerifyDebugDump(generator.dump_file_name());
+}
+
+} // namespace test
+} // namespace webrtc