Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(825)

Unified Diff: webrtc/modules/audio_processing/aec3/echo_canceller3_unittest.cc

Issue 2584493002: Added first layer of the echo canceller 3 functionality (Closed)
Patch Set: Changes in response to reviewer comments Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webrtc/modules/audio_processing/aec3/echo_canceller3_unittest.cc
diff --git a/webrtc/modules/audio_processing/aec3/echo_canceller3_unittest.cc b/webrtc/modules/audio_processing/aec3/echo_canceller3_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..12093b1351b7d178b1417939efd7f46cb2aae8bf
--- /dev/null
+++ b/webrtc/modules/audio_processing/aec3/echo_canceller3_unittest.cc
@@ -0,0 +1,831 @@
+/*
+ * Copyright (c) 2016 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 <deque>
+#include <vector>
+
+#include "webrtc/modules/audio_processing/aec3/aec3_constants.h"
+#include "webrtc/modules/audio_processing/aec3/block_framer.h"
+#include "webrtc/modules/audio_processing/aec3/block_processor.h"
+#include "webrtc/modules/audio_processing/aec3/echo_canceller3.h"
+#include "webrtc/modules/audio_processing/aec3/frame_blocker.h"
+#include "webrtc/modules/audio_processing/audio_buffer.h"
+#include "webrtc/test/gtest.h"
+
+namespace webrtc {
+namespace {
+void PopulateInputFrame(size_t frame_length,
hlundin-webrtc 2016/12/20 15:10:35 Please, comment on how the frame is filled. What v
peah-webrtc 2016/12/21 23:13:53 Done.
+ size_t num_bands,
+ size_t frame_index,
+ float* const* frame,
+ int offset) {
+ for (size_t k = 0; k < num_bands; ++k) {
+ for (size_t i = 0; i < frame_length; ++i) {
+ float value = static_cast<int>(frame_index * frame_length + i) + offset;
+ frame[k][i] = (value > 0 ? 5000 * k + value : 0);
+ }
+ }
+}
+
+bool VerifyInputFrame(size_t frame_length,
+ size_t num_bands,
+ size_t frame_index,
+ float* const* frame,
hlundin-webrtc 2016/12/20 15:10:35 More const.
peah-webrtc 2016/12/21 23:13:51 Done.
+ int offset) {
+ float reference_frame_data[num_bands][frame_length];
+ float* reference_frame[num_bands];
+ for (size_t k = 0; k < num_bands; ++k) {
+ reference_frame[k] = &reference_frame_data[k][0];
+ }
+
+ PopulateInputFrame(frame_length, num_bands, frame_index, reference_frame,
+ offset);
+ for (size_t k = 0; k < num_bands; ++k) {
+ for (size_t i = 0; i < frame_length; ++i) {
+ if (reference_frame[k][i] != frame[k][i]) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void FillSubFrameView(
+ const float* const* frame,
+ size_t sub_frame_index,
+ rtc::ArrayView<rtc::ArrayView<const float>> sub_frame_view) {
+ for (size_t k = 0; k < sub_frame_view.size(); ++k) {
+ sub_frame_view[k] = rtc::ArrayView<const float>(
+ &frame[k][sub_frame_index * kSubFrameLength], kSubFrameLength);
+ }
+}
+
+void FillSubFrameView(float* const* frame,
+ size_t sub_frame_index,
+ rtc::ArrayView<rtc::ArrayView<float>> sub_frame_view) {
+ for (size_t k = 0; k < sub_frame_view.size(); ++k) {
+ sub_frame_view[k] = rtc::ArrayView<float>(
+ &frame[k][sub_frame_index * kSubFrameLength], kSubFrameLength);
+ }
+}
+
+// Class for testing that echo leakage is properly reported to the
hlundin-webrtc 2016/12/20 15:10:35 This is not used for the next 400 lines. Define th
peah-webrtc 2016/12/21 23:13:52 Done.
+// BlockProcessor.
+class BlockProcessorEchoLeakageReport : public BlockProcessor {
+ public:
+ explicit BlockProcessorEchoLeakageReport(size_t num_bands);
+ ~BlockProcessorEchoLeakageReport() override;
+
+ void ProcessCapture(bool known_echo_path_change,
+ bool saturated_microphone_signal,
+ std::vector<std::vector<float>>* capture_block) override;
+
+ bool BufferRender(std::vector<std::vector<float>>* block) override;
+
+ void ReportEchoLeakage(bool leakage_detected) override;
+
+ private:
+ float echo_leakage_indicator_ = 0.f;
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorEchoLeakageReport);
+};
+
+BlockProcessorEchoLeakageReport::BlockProcessorEchoLeakageReport(
hlundin-webrtc 2016/12/20 15:10:36 Merge definitions into the class declaration above
peah-webrtc 2016/12/21 23:13:52 Done.
+ size_t num_bands) {}
+
+BlockProcessorEchoLeakageReport::~BlockProcessorEchoLeakageReport() = default;
+
+void BlockProcessorEchoLeakageReport::ProcessCapture(
+ bool known_echo_path_change,
+ bool saturated_microphone_signal,
+ std::vector<std::vector<float>>* capture_block) {
+ for (auto& c : (*capture_block)[0]) {
+ c = echo_leakage_indicator_;
+ }
+}
+
+bool BlockProcessorEchoLeakageReport::BufferRender(
+ std::vector<std::vector<float>>* block) {
+ return false;
+}
+
+void BlockProcessorEchoLeakageReport::ReportEchoLeakage(bool leakage_detected) {
+ echo_leakage_indicator_ = leakage_detected ? 1.f : 0.f;
+}
+
+// Class for testing that echo leakage is properly reported to the
hlundin-webrtc 2016/12/20 15:10:35 Copy-paste failure with the comment.
peah-webrtc 2016/12/21 23:13:52 Done.
+// BlockProcessor.
+class BlockProcessorCaptureSaturationReport : public BlockProcessor {
+ public:
+ explicit BlockProcessorCaptureSaturationReport(size_t num_bands);
+ ~BlockProcessorCaptureSaturationReport() override;
+
+ void ProcessCapture(bool known_echo_path_change,
+ bool saturated_microphone_signal,
+ std::vector<std::vector<float>>* capture_block) override;
+
+ bool BufferRender(std::vector<std::vector<float>>* block) override;
+
+ void ReportEchoLeakage(bool leakage_detected) override;
+
+ private:
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorCaptureSaturationReport);
+};
+
+BlockProcessorCaptureSaturationReport::BlockProcessorCaptureSaturationReport(
+ size_t num_bands) {}
+
+BlockProcessorCaptureSaturationReport::
+ ~BlockProcessorCaptureSaturationReport() = default;
+
+void BlockProcessorCaptureSaturationReport::ProcessCapture(
+ bool known_echo_path_change,
+ bool saturated_microphone_signal,
+ std::vector<std::vector<float>>* capture_block) {
+ for (auto& c : (*capture_block)[0]) {
+ c = saturated_microphone_signal ? 1 : 0;
aleloi 2016/12/21 10:07:45 Nit: 1.f, 0.f for consistency.
peah-webrtc 2016/12/21 23:13:52 Done.
+ }
+}
+
+bool BlockProcessorCaptureSaturationReport::BufferRender(
+ std::vector<std::vector<float>>* block) {
+ return false;
+}
+
+void BlockProcessorCaptureSaturationReport::ReportEchoLeakage(
+ bool leakage_detected) {}
+
+// Class for testing that a known echo path change is properly reported to the
+// BlockProcessor.
+class BlockProcessorKnownEchoPathChangeReport : public BlockProcessor {
+ public:
+ explicit BlockProcessorKnownEchoPathChangeReport(size_t num_bands);
+ ~BlockProcessorKnownEchoPathChangeReport() override;
+
+ void ProcessCapture(bool known_echo_path_change,
+ bool saturated_microphone_signal,
+ std::vector<std::vector<float>>* capture_block) override;
+
+ bool BufferRender(std::vector<std::vector<float>>* block) override;
+
+ void ReportEchoLeakage(bool leakage_detected) override;
+
+ private:
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorKnownEchoPathChangeReport);
+};
+
+BlockProcessorKnownEchoPathChangeReport::
+ BlockProcessorKnownEchoPathChangeReport(size_t num_bands) {}
+
+BlockProcessorKnownEchoPathChangeReport::
+ ~BlockProcessorKnownEchoPathChangeReport() = default;
+
+void BlockProcessorKnownEchoPathChangeReport::ProcessCapture(
+ bool known_echo_path_change,
+ bool saturated_microphone_signal,
+ std::vector<std::vector<float>>* capture_block) {
+ for (auto& c : (*capture_block)[0]) {
+ c = known_echo_path_change ? 1 : 0;
aleloi 2016/12/21 10:07:45 nit: 1.f, 0.f
peah-webrtc 2016/12/21 23:13:52 Done.
+ }
+}
+
+bool BlockProcessorKnownEchoPathChangeReport::BufferRender(
+ std::vector<std::vector<float>>* block) {
+ return false;
+}
+
+void BlockProcessorKnownEchoPathChangeReport::ReportEchoLeakage(
+ bool leakage_detected) {}
+
+// Class for testing that the render data is properly received by the block
+// processor.
+class BlockProcessorRenderRedirect : public BlockProcessor {
hlundin-webrtc 2016/12/20 15:10:35 Move closer to where it is used.
peah-webrtc 2016/12/21 23:13:52 Done.
+ public:
+ explicit BlockProcessorRenderRedirect(size_t num_bands);
+ ~BlockProcessorRenderRedirect() override;
+
+ void ProcessCapture(bool known_echo_path_change,
+ bool saturated_microphone_signal,
+ std::vector<std::vector<float>>* capture_block) override;
+
+ bool BufferRender(std::vector<std::vector<float>>* block) override;
+
+ void ReportEchoLeakage(bool leakage_detected) override;
+
+ private:
+ std::deque<std::vector<std::vector<float>>> received_render_blocks_;
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorRenderRedirect);
+};
+
+BlockProcessorRenderRedirect::BlockProcessorRenderRedirect(size_t num_bands) {}
hlundin-webrtc 2016/12/20 15:10:35 Merge definitions into class declaration.
peah-webrtc 2016/12/21 23:13:52 Done.
+
+BlockProcessorRenderRedirect::~BlockProcessorRenderRedirect() = default;
+
+void BlockProcessorRenderRedirect::ProcessCapture(
+ bool known_echo_path_change,
+ bool saturated_microphone_signal,
+ std::vector<std::vector<float>>* capture_block) {
+ std::vector<std::vector<float>> render_block =
+ received_render_blocks_.front();
+ received_render_blocks_.pop_front();
+ capture_block->swap(render_block);
+}
+
+bool BlockProcessorRenderRedirect::BufferRender(
+ std::vector<std::vector<float>>* block) {
+ received_render_blocks_.push_back(*block);
+ return false;
+}
+
+void BlockProcessorRenderRedirect::ReportEchoLeakage(bool leakage_detected) {}
+
+// Class for testing that the capture data is properly received by the block
+// processor.
+class BlockProcessorTransparentCapture : public BlockProcessor {
ivoc 2016/12/21 14:52:13 Is it me or does this class do nothing? Why is it
peah-webrtc 2016/12/21 23:13:52 It is needed to ensure that the capture signal is
ivoc 2016/12/22 13:38:13 Acknowledged.
peah-webrtc 2017/01/02 08:45:10 Acknowledged.
+ public:
+ explicit BlockProcessorTransparentCapture(size_t num_bands);
+ ~BlockProcessorTransparentCapture() override;
+
+ void ProcessCapture(bool known_echo_path_change,
+ bool saturated_microphone_signal,
+ std::vector<std::vector<float>>* capture_block) override;
+
+ bool BufferRender(std::vector<std::vector<float>>* block) override;
+
+ void ReportEchoLeakage(bool leakage_detected) override;
+
+ private:
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorTransparentCapture);
+};
+
+BlockProcessorTransparentCapture::BlockProcessorTransparentCapture(
hlundin-webrtc 2016/12/20 15:10:35 Merge definitions into class declaration.
peah-webrtc 2016/12/21 23:13:52 Done.
+ size_t num_bands) {}
+
+BlockProcessorTransparentCapture::~BlockProcessorTransparentCapture() = default;
+
+void BlockProcessorTransparentCapture::ProcessCapture(
+ bool known_echo_path_change,
+ bool saturated_microphone_signal,
+ std::vector<std::vector<float>>* capture_block) {}
+
+bool BlockProcessorTransparentCapture::BufferRender(
+ std::vector<std::vector<float>>* block) {
+ return false;
+}
+
+void BlockProcessorTransparentCapture::ReportEchoLeakage(
+ bool leakage_detected) {}
+
+void RunCapturePipelineVerificationTest(int sample_rate_hz) {
hlundin-webrtc 2016/12/20 15:10:35 Write a comment about what the test actually tests
peah-webrtc 2016/12/21 23:13:53 Done.
+ const size_t num_bands = NumBandsForRate(sample_rate_hz);
hlundin-webrtc 2016/12/20 15:10:35 You are duplicating these 10-20 lines an awful lot
peah-webrtc 2016/12/21 23:13:52 I made a tester class that unifies the common part
hlundin-webrtc 2016/12/22 10:23:56 OK. That's _almost_ a test fixture then... :)
peah-webrtc 2017/01/02 08:45:10 :-) Yes, but more explicit.
+ const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160;
+ float input_frame_data[num_bands][frame_length];
+ float* input_frame[num_bands];
+ float output_frame_data[num_bands][frame_length];
+ float* output_frame[num_bands];
+ for (size_t k = 0; k < num_bands; ++k) {
+ input_frame[k] = &input_frame_data[k][0];
+ output_frame[k] = &output_frame_data[k][0];
+ }
+
+ const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100);
+ AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+ AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+
+ EchoCanceller3 aec3(sample_rate_hz, false,
+ new BlockProcessorTransparentCapture(num_bands));
+
+ for (size_t frame_index = 0; frame_index < 20; ++frame_index) {
+ aec3.AnalyzeCapture(&capture_buffer);
+ if (sample_rate_hz > 16000) {
+ capture_buffer.SplitIntoFrequencyBands();
+ render_buffer.SplitIntoFrequencyBands();
+ }
+
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &capture_buffer.split_bands_f(0)[0], 0);
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &render_buffer.split_bands_f(0)[0], 100);
+
+ EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer));
+ aec3.ProcessCapture(&capture_buffer, false);
+ EXPECT_TRUE(VerifyInputFrame(frame_length, num_bands, frame_index,
+ &capture_buffer.split_bands_f(0)[0], -64));
+ }
+}
+
+void RunRenderPipelineVerificationTest(int sample_rate_hz) {
+ const size_t num_bands = NumBandsForRate(sample_rate_hz);
+ const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160;
+ float input_frame_data[num_bands][frame_length];
+ float* input_frame[num_bands];
+ float output_frame_data[num_bands][frame_length];
+ float* output_frame[num_bands];
+ for (size_t k = 0; k < num_bands; ++k) {
+ input_frame[k] = &input_frame_data[k][0];
+ output_frame[k] = &output_frame_data[k][0];
+ }
+
+ const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100);
+ AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+ AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+
+ EchoCanceller3 aec3(sample_rate_hz, false,
+ new BlockProcessorRenderRedirect(num_bands));
+
+ for (size_t frame_index = 0; frame_index < 20; ++frame_index) {
+ aec3.AnalyzeCapture(&capture_buffer);
+ if (sample_rate_hz > 16000) {
+ capture_buffer.SplitIntoFrequencyBands();
+ render_buffer.SplitIntoFrequencyBands();
+ }
+
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &capture_buffer.split_bands_f(0)[0], 100);
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &render_buffer.split_bands_f(0)[0], 0);
+
+ EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer));
+ aec3.ProcessCapture(&capture_buffer, false);
+ EXPECT_TRUE(VerifyInputFrame(frame_length, num_bands, frame_index,
+ &capture_buffer.split_bands_f(0)[0], -64));
+ }
+}
+
+enum class EchoPathTestVariant { kNone, kOneSticky, kOneNonSticky };
ivoc 2016/12/21 14:52:13 Please add a comment to explain what each variant
peah-webrtc 2016/12/21 23:13:52 Done.
+
+void RunEchoPathChangeVerificationTest(
+ int sample_rate_hz,
+ EchoPathTestVariant echo_path_change_test_variant) {
+ const size_t num_bands = NumBandsForRate(sample_rate_hz);
+ const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160;
+ float input_frame_data[num_bands][frame_length];
+ float* input_frame[num_bands];
+ float output_frame_data[num_bands][frame_length];
+ float* output_frame[num_bands];
+ for (size_t k = 0; k < num_bands; ++k) {
+ input_frame[k] = &input_frame_data[k][0];
+ output_frame[k] = &output_frame_data[k][0];
+ }
+
+ const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100);
+ AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+ AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+
+ EchoCanceller3 aec3(sample_rate_hz, false,
+ new BlockProcessorKnownEchoPathChangeReport(num_bands));
hlundin-webrtc 2016/12/20 15:10:36 Use a mock instead to verify that BlockProcessor::
peah-webrtc 2016/12/21 23:13:52 Done.
+
+ for (size_t frame_index = 0; frame_index < 20; ++frame_index) {
+ bool echo_path_change = false;
+ switch (echo_path_change_test_variant) {
+ case EchoPathTestVariant::kNone:
+ break;
+ case EchoPathTestVariant::kOneSticky:
+ echo_path_change = true;
+ break;
+ case EchoPathTestVariant::kOneNonSticky:
+ if (frame_index == 0) {
+ echo_path_change = true;
+ }
+ break;
+ default:
hlundin-webrtc 2016/12/20 15:10:35 You can omit the default case. Since you are switc
peah-webrtc 2016/12/21 23:13:52 Great! Thanks! Done.
+ RTC_NOTREACHED();
+ }
+
+ aec3.AnalyzeCapture(&capture_buffer);
+ if (sample_rate_hz > 16000) {
+ capture_buffer.SplitIntoFrequencyBands();
+ render_buffer.SplitIntoFrequencyBands();
+ }
+
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &capture_buffer.split_bands_f(0)[0], 100);
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &render_buffer.split_bands_f(0)[0], 0);
+
+ EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer));
+ aec3.ProcessCapture(&capture_buffer, echo_path_change);
+
+ switch (echo_path_change_test_variant) {
+ case EchoPathTestVariant::kNone:
+ EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]);
+ break;
+ case EchoPathTestVariant::kOneSticky:
+ if (frame_index > 0) {
+ EXPECT_EQ(1.f, capture_buffer.split_bands_f(0)[0][0]);
+ }
+ break;
+ case EchoPathTestVariant::kOneNonSticky:
+ if (frame_index > 10) {
+ EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]);
+ }
+ break;
+ default:
hlundin-webrtc 2016/12/20 15:10:35 Remove default.
peah-webrtc 2016/12/21 23:13:53 Done.
+ RTC_NOTREACHED();
+ }
+ }
+}
+
+enum class EchoLeakageTestVariant {
ivoc 2016/12/21 14:52:13 Please add a comment here as well to explain the v
peah-webrtc 2016/12/21 23:13:52 Done.
+ kNone,
+ kFalseSticky,
+ kTrueSticky,
+ kTrueNonSticky
+};
+
+void RunEchoLeakageVerificationTest(
+ int sample_rate_hz,
+ EchoLeakageTestVariant leakage_report_variant) {
+ const size_t num_bands = NumBandsForRate(sample_rate_hz);
+ const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160;
+ float input_frame_data[num_bands][frame_length];
+ float* input_frame[num_bands];
+ float output_frame_data[num_bands][frame_length];
+ float* output_frame[num_bands];
+ for (size_t k = 0; k < num_bands; ++k) {
+ input_frame[k] = &input_frame_data[k][0];
+ output_frame[k] = &output_frame_data[k][0];
+ }
+
+ const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100);
+ AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+ AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+
+ EchoCanceller3 aec3(sample_rate_hz, false,
+ new BlockProcessorEchoLeakageReport(num_bands));
hlundin-webrtc 2016/12/20 15:10:35 Rewrite this test to use a mock instead of BlockPr
peah-webrtc 2016/12/21 23:13:52 Done.
+
+ for (size_t frame_index = 0; frame_index < 20; ++frame_index) {
+ switch (leakage_report_variant) {
+ case EchoLeakageTestVariant::kNone:
+ break;
+ case EchoLeakageTestVariant::kFalseSticky:
+ if (frame_index == 0) {
+ aec3.ReportEchoLeakage(false);
+ }
+ break;
+ case EchoLeakageTestVariant::kTrueSticky:
+ if (frame_index == 0) {
+ aec3.ReportEchoLeakage(true);
+ }
+ break;
+ case EchoLeakageTestVariant::kTrueNonSticky:
+ if (frame_index == 0) {
+ aec3.ReportEchoLeakage(true);
+ } else {
+ aec3.ReportEchoLeakage(false);
+ }
+
+ break;
+ default:
hlundin-webrtc 2016/12/20 15:10:35 Remove default.
peah-webrtc 2016/12/21 23:13:52 Done.
+ RTC_NOTREACHED();
+ }
+
+ aec3.AnalyzeCapture(&capture_buffer);
+ if (sample_rate_hz > 16000) {
+ capture_buffer.SplitIntoFrequencyBands();
+ render_buffer.SplitIntoFrequencyBands();
+ }
+
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &capture_buffer.split_bands_f(0)[0], 100);
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &render_buffer.split_bands_f(0)[0], 0);
+
+ EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer));
+ aec3.ProcessCapture(&capture_buffer, false);
+
+ switch (leakage_report_variant) {
+ case EchoLeakageTestVariant::kNone:
+ EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]);
+ break;
+ case EchoLeakageTestVariant::kFalseSticky:
+ EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]);
+ break;
+ case EchoLeakageTestVariant::kTrueSticky:
+ if (frame_index > 0) {
+ EXPECT_EQ(1.f, capture_buffer.split_bands_f(0)[0][0]);
+ }
+ break;
+ case EchoLeakageTestVariant::kTrueNonSticky:
+ if (frame_index == 0) {
+ EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]);
+ } else if (frame_index == 1) {
+ EXPECT_EQ(1.f, capture_buffer.split_bands_f(0)[0][0]);
+ } else if (frame_index > 2) {
+ EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]);
+ }
+ break;
+ default:
hlundin-webrtc 2016/12/20 15:10:36 Remove default.
peah-webrtc 2016/12/21 23:13:52 Done.
+ RTC_NOTREACHED();
+ }
+ }
+}
+
+enum class SaturationTestVariant {
ivoc 2016/12/21 14:52:13 Comment about variants, please.
peah-webrtc 2016/12/21 23:13:53 Done.
+ kNone,
+ kOneNegative,
+ kOnePositive,
+ kNotSticky
+};
+
+void RunCaptureSaturationVerificationTest(
+ int sample_rate_hz,
+ SaturationTestVariant saturation_variant) {
+ const size_t num_bands = NumBandsForRate(sample_rate_hz);
+ const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160;
+ float input_frame_data[num_bands][frame_length];
+ float* input_frame[num_bands];
+ float output_frame_data[num_bands][frame_length];
+ float* output_frame[num_bands];
+ for (size_t k = 0; k < num_bands; ++k) {
+ input_frame[k] = &input_frame_data[k][0];
+ output_frame[k] = &output_frame_data[k][0];
+ }
+
+ const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100);
+ AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+ AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+
+ EchoCanceller3 aec3(sample_rate_hz, false,
+ new BlockProcessorCaptureSaturationReport(num_bands));
hlundin-webrtc 2016/12/20 15:10:35 Use a mock instead to verify that BlockProcessor::
peah-webrtc 2016/12/21 23:13:51 Done.
+
+ for (size_t frame_index = 0; frame_index < 20; ++frame_index) {
+ for (int k = 0; k < samples_per_channel; ++k) {
+ capture_buffer.channels_f()[0][k] = 0.f;
+ }
+ switch (saturation_variant) {
+ case SaturationTestVariant::kNone:
+ break;
+ case SaturationTestVariant::kOneNegative:
+ capture_buffer.channels_f()[0][10] = -32768.f;
+ break;
+ case SaturationTestVariant::kOnePositive:
+ capture_buffer.channels_f()[0][10] = 32767.f;
+ break;
+ case SaturationTestVariant::kNotSticky:
+ if (frame_index < 14) {
+ capture_buffer.channels_f()[0][10] = 32767.f;
+ }
+ break;
+ default:
hlundin-webrtc 2016/12/20 15:10:36 Remove default.
peah-webrtc 2016/12/21 23:13:52 Done.
+ RTC_NOTREACHED();
+ }
+
+ aec3.AnalyzeCapture(&capture_buffer);
+ if (sample_rate_hz > 16000) {
+ capture_buffer.SplitIntoFrequencyBands();
+ render_buffer.SplitIntoFrequencyBands();
+ }
+
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &capture_buffer.split_bands_f(0)[0], 100);
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &render_buffer.split_bands_f(0)[0], 0);
+
+ EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer));
+ aec3.ProcessCapture(&capture_buffer, false);
+
+ switch (saturation_variant) {
+ case SaturationTestVariant::kNone:
+ EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]);
+ break;
+ case SaturationTestVariant::kOneNegative:
+ if (frame_index > 0) {
+ EXPECT_EQ(1.f, capture_buffer.split_bands_f(0)[0][0]);
+ }
+ break;
+ case SaturationTestVariant::kOnePositive:
+ if (frame_index > 0) {
+ EXPECT_EQ(1.f, capture_buffer.split_bands_f(0)[0][0]);
+ }
+ break;
+ case SaturationTestVariant::kNotSticky:
+ if (frame_index > 15) {
+ EXPECT_EQ(0.f, capture_buffer.split_bands_f(0)[0][0]);
+ }
+ break;
+ default:
hlundin-webrtc 2016/12/20 15:10:35 Remove default.
peah-webrtc 2016/12/21 23:13:52 Done.
+ RTC_NOTREACHED();
+ }
+ }
+}
+
+void RunRenderPipelineSwapQueueTest(int sample_rate_hz) {
hlundin-webrtc 2016/12/20 15:10:35 Comment on what this test does.
peah-webrtc 2016/12/21 23:13:51 Done.
+ const size_t num_bands = NumBandsForRate(sample_rate_hz);
+ const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160;
+ float input_frame_data[num_bands][frame_length];
+ float* input_frame[num_bands];
+ float output_frame_data[num_bands][frame_length];
+ float* output_frame[num_bands];
+ for (size_t k = 0; k < num_bands; ++k) {
+ input_frame[k] = &input_frame_data[k][0];
+ output_frame[k] = &output_frame_data[k][0];
+ }
+
+ const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100);
+ AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+ AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+
+ EchoCanceller3 aec3(sample_rate_hz, false,
+ new BlockProcessorRenderRedirect(num_bands));
+
+ const size_t kNumFramesToProcess = 30;
+ for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
+ ++frame_index) {
+ if (sample_rate_hz > 16000) {
+ render_buffer.SplitIntoFrequencyBands();
+ }
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &render_buffer.split_bands_f(0)[0], 0);
+
+ EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer));
+ }
+
+ for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
+ ++frame_index) {
+ aec3.AnalyzeCapture(&capture_buffer);
+ if (sample_rate_hz > 16000) {
+ capture_buffer.SplitIntoFrequencyBands();
+ }
+
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &capture_buffer.split_bands_f(0)[0], 100);
+
+ aec3.ProcessCapture(&capture_buffer, false);
+ EXPECT_TRUE(VerifyInputFrame(frame_length, num_bands, frame_index,
+ &capture_buffer.split_bands_f(0)[0], -64));
+ }
+}
+
+void RunRenderPipelineSwapQueueOverrunReturnValueTest(int sample_rate_hz) {
hlundin-webrtc 2016/12/20 15:10:35 Comment...
peah-webrtc 2016/12/21 23:13:52 Done.
+ const size_t num_bands = NumBandsForRate(sample_rate_hz);
+ const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160;
+ float input_frame_data[num_bands][frame_length];
+ float* input_frame[num_bands];
+ float output_frame_data[num_bands][frame_length];
+ float* output_frame[num_bands];
+ for (size_t k = 0; k < num_bands; ++k) {
+ input_frame[k] = &input_frame_data[k][0];
+ output_frame[k] = &output_frame_data[k][0];
+ }
+
+ const int samples_per_channel = rtc::CheckedDivExact(sample_rate_hz, 100);
+ AudioBuffer capture_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+ AudioBuffer render_buffer(samples_per_channel, 1, samples_per_channel, 1,
+ samples_per_channel);
+
+ EchoCanceller3 aec3(sample_rate_hz, false);
+
+ const size_t kNumFramesToProcess = 30;
+ for (size_t k = 0; k < 2; ++k) {
+ for (size_t frame_index = 0; frame_index < kNumFramesToProcess;
+ ++frame_index) {
+ if (sample_rate_hz > 16000) {
+ render_buffer.SplitIntoFrequencyBands();
+ }
+ PopulateInputFrame(frame_length, num_bands, frame_index,
+ &render_buffer.split_bands_f(0)[0], 0);
+
+ if (k == 0) {
+ EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer));
+ } else {
+ EXPECT_FALSE(aec3.AnalyzeRender(&render_buffer));
+ }
+ }
+ }
+}
+
+void RunBlockerAndFramerTest(int sample_rate_hz) {
hlundin-webrtc 2016/12/20 15:10:35 This looks like unit tests for FrameBlocker and Bl
peah-webrtc 2016/12/21 23:13:51 Done.
+ const size_t num_bands = NumBandsForRate(sample_rate_hz);
+ const size_t frame_length = sample_rate_hz == 8000 ? 80 : 160;
+ const size_t num_sub_frames = frame_length / 80;
+ float input_frame_data[num_bands][frame_length];
+ float* input_frame[num_bands];
+ float output_frame_data[num_bands][frame_length];
+ float* output_frame[num_bands];
+ for (size_t k = 0; k < num_bands; ++k) {
+ input_frame[k] = &input_frame_data[k][0];
+ output_frame[k] = &output_frame_data[k][0];
+ }
+
+ std::vector<std::vector<float>> block(num_bands, std::vector<float>(64, 0.f));
+ FrameBlocker blocker(num_bands);
+ BlockFramer framer(num_bands);
+
+ for (size_t frame_index = 0; frame_index < 20; ++frame_index) {
+ PopulateInputFrame(frame_length, num_bands, frame_index, input_frame, 0);
+ for (size_t sub_frame_index = 0; sub_frame_index < num_sub_frames;
+ ++sub_frame_index) {
+ rtc::ArrayView<float> sub_frame_view_data[num_bands];
+ rtc::ArrayView<const float> sub_frame_const_view_data[num_bands];
+ auto sub_frame_view = rtc::ArrayView<rtc::ArrayView<float>>(
+ &sub_frame_view_data[0], num_bands);
+ auto sub_frame_const_view = rtc::ArrayView<rtc::ArrayView<const float>>(
+ &sub_frame_const_view_data[0], num_bands);
+ FillSubFrameView(output_frame, sub_frame_index, sub_frame_view);
+ FillSubFrameView(input_frame, sub_frame_index, sub_frame_const_view);
+
+ blocker.InsertSubFrameAndExtractBlock(sub_frame_const_view, &block);
+
+ framer.InsertBlockAndExtractSubFrame(block, sub_frame_view);
+ if ((frame_index * num_sub_frames + sub_frame_index + 1) % 4 == 0) {
+ EXPECT_TRUE(blocker.IsBlockAvailable());
+ } else {
+ EXPECT_FALSE(blocker.IsBlockAvailable());
+ }
+ if (blocker.IsBlockAvailable()) {
+ blocker.ExtractBlock(&block);
+ framer.InsertBlock(block);
+ }
+ }
+ EXPECT_TRUE(VerifyInputFrame(frame_length, num_bands, frame_index,
+ output_frame, -64));
+ }
+}
+
+} // namespace
+
+TEST(EchoCanceller3Pipeline, CaptureBitexactness) {
+ for (auto rate : {8000, 16000, 32000, 48000}) {
hlundin-webrtc 2016/12/20 15:10:36 This makes it tricky to know which test case is ac
peah-webrtc 2016/12/21 23:13:52 Good point! I went for SCOPED_TRACE. PTAL
+ RunCapturePipelineVerificationTest(rate);
+ }
+}
+
+TEST(EchoCanceller3Pipeline, RenderBitexactness) {
+ for (auto rate : {8000, 16000, 32000, 48000}) {
+ RunRenderPipelineVerificationTest(rate);
+ }
+}
+
+TEST(EchoCanceller3Pipeline, CaptureSaturation) {
+ auto variants = {
+ SaturationTestVariant::kNone, SaturationTestVariant::kOneNegative,
+ SaturationTestVariant::kOnePositive, SaturationTestVariant::kNotSticky};
+ for (auto rate : {8000, 16000, 32000, 48000}) {
+ for (auto variant : variants) {
+ RunCaptureSaturationVerificationTest(rate, variant);
+ }
+ }
+}
+
+TEST(EchoCanceller3Pipeline, EchoPathChange) {
+ auto variants = {EchoPathTestVariant::kNone, EchoPathTestVariant::kOneSticky,
+ EchoPathTestVariant::kOneNonSticky};
+ for (auto rate : {8000, 16000, 32000, 48000}) {
+ for (auto variant : variants) {
+ RunEchoPathChangeVerificationTest(rate, variant);
+ }
+ }
+}
+
+TEST(EchoCanceller3Pipeline, EchoLeakage) {
+ auto variants = {EchoLeakageTestVariant::kNone,
+ EchoLeakageTestVariant::kFalseSticky,
+ EchoLeakageTestVariant::kTrueSticky,
+ EchoLeakageTestVariant::kTrueNonSticky};
+ for (auto rate : {8000, 16000, 32000, 48000}) {
+ for (auto variant : variants) {
+ RunEchoLeakageVerificationTest(rate, variant);
+ }
+ }
+}
+
+TEST(EchoCanceller3Pipeline, RenderSwapQueue) {
+ for (auto rate : {8000, 16000, 32000, 48000}) {
+ RunRenderPipelineSwapQueueTest(rate);
+ }
+}
+
+TEST(EchoCanceller3Pipeline, RenderSwapQueueOverrunReturnValue) {
+ for (auto rate : {8000, 16000, 32000, 48000}) {
+ RunRenderPipelineSwapQueueOverrunReturnValueTest(rate);
+ }
+}
+
+TEST(EchoCanceller3Pipeline, BlockerAndFramer) {
+ for (auto rate : {8000, 16000, 32000, 48000}) {
+ RunBlockerAndFramerTest(rate);
+ }
+}
+
+} // namespace webrtc

Powered by Google App Engine
This is Rietveld 408576698