Index: webrtc/common_audio/blocker_unittest.cc |
diff --git a/webrtc/common_audio/blocker_unittest.cc b/webrtc/common_audio/blocker_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..eea3e2516a08db71bbc1760975b4b5ffb7b9a307 |
--- /dev/null |
+++ b/webrtc/common_audio/blocker_unittest.cc |
@@ -0,0 +1,345 @@ |
+/* |
+ * Copyright (c) 2014 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 <memory> |
+ |
+#include "webrtc/common_audio/blocker.h" |
+ |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "webrtc/base/arraysize.h" |
+ |
+namespace { |
+ |
+// Callback Function to add 3 to every sample in the signal. |
+class PlusThreeBlockerCallback : public webrtc::BlockerCallback { |
+ public: |
+ void ProcessBlock(const float* const* input, |
+ size_t num_frames, |
+ size_t num_input_channels, |
+ size_t num_output_channels, |
+ float* const* output) override { |
+ for (size_t i = 0; i < num_output_channels; ++i) { |
+ for (size_t j = 0; j < num_frames; ++j) { |
+ output[i][j] = input[i][j] + 3; |
+ } |
+ } |
+ } |
+}; |
+ |
+// No-op Callback Function. |
+class CopyBlockerCallback : public webrtc::BlockerCallback { |
+ public: |
+ void ProcessBlock(const float* const* input, |
+ size_t num_frames, |
+ size_t num_input_channels, |
+ size_t num_output_channels, |
+ float* const* output) override { |
+ for (size_t i = 0; i < num_output_channels; ++i) { |
+ for (size_t j = 0; j < num_frames; ++j) { |
+ output[i][j] = input[i][j]; |
+ } |
+ } |
+ } |
+}; |
+ |
+} // namespace |
+ |
+namespace webrtc { |
+ |
+// Tests blocking with a window that multiplies the signal by 2, a callback |
+// that adds 3 to each sample in the signal, and different combinations of chunk |
+// size, block size, and shift amount. |
+class BlockerTest : public ::testing::Test { |
+ protected: |
+ void RunTest(Blocker* blocker, |
+ size_t chunk_size, |
+ size_t num_frames, |
+ const float* const* input, |
+ float* const* input_chunk, |
+ float* const* output, |
+ float* const* output_chunk, |
+ size_t num_input_channels, |
+ size_t num_output_channels) { |
+ size_t start = 0; |
+ size_t end = chunk_size - 1; |
+ while (end < num_frames) { |
+ CopyTo(input_chunk, 0, start, num_input_channels, chunk_size, input); |
+ blocker->ProcessChunk(input_chunk, |
+ chunk_size, |
+ num_input_channels, |
+ num_output_channels, |
+ output_chunk); |
+ CopyTo(output, start, 0, num_output_channels, chunk_size, output_chunk); |
+ |
+ start += chunk_size; |
+ end += chunk_size; |
+ } |
+ } |
+ |
+ void ValidateSignalEquality(const float* const* expected, |
+ const float* const* actual, |
+ size_t num_channels, |
+ size_t num_frames) { |
+ for (size_t i = 0; i < num_channels; ++i) { |
+ for (size_t j = 0; j < num_frames; ++j) { |
+ EXPECT_FLOAT_EQ(expected[i][j], actual[i][j]); |
+ } |
+ } |
+ } |
+ |
+ void ValidateInitialDelay(const float* const* output, |
+ size_t num_channels, |
+ size_t num_frames, |
+ size_t initial_delay) { |
+ for (size_t i = 0; i < num_channels; ++i) { |
+ for (size_t j = 0; j < num_frames; ++j) { |
+ if (j < initial_delay) { |
+ EXPECT_FLOAT_EQ(output[i][j], 0.f); |
+ } else { |
+ EXPECT_GT(output[i][j], 0.f); |
+ } |
+ } |
+ } |
+ } |
+ |
+ static void CopyTo(float* const* dst, |
+ size_t start_index_dst, |
+ size_t start_index_src, |
+ size_t num_channels, |
+ size_t num_frames, |
+ const float* const* src) { |
+ for (size_t i = 0; i < num_channels; ++i) { |
+ memcpy(&dst[i][start_index_dst], |
+ &src[i][start_index_src], |
+ num_frames * sizeof(float)); |
+ } |
+ } |
+}; |
+ |
+TEST_F(BlockerTest, TestBlockerMutuallyPrimeChunkandBlockSize) { |
+ const size_t kNumInputChannels = 3; |
+ const size_t kNumOutputChannels = 2; |
+ const size_t kNumFrames = 10; |
+ const size_t kBlockSize = 4; |
+ const size_t kChunkSize = 5; |
+ const size_t kShiftAmount = 2; |
+ |
+ const float kInput[kNumInputChannels][kNumFrames] = { |
+ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, |
+ {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, |
+ {3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; |
+ ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels); |
+ input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput)); |
+ |
+ const float kExpectedOutput[kNumInputChannels][kNumFrames] = { |
+ {6, 6, 12, 20, 20, 20, 20, 20, 20, 20}, |
+ {6, 6, 12, 28, 28, 28, 28, 28, 28, 28}}; |
+ ChannelBuffer<float> expected_output_cb(kNumFrames, kNumInputChannels); |
+ expected_output_cb.SetDataForTesting( |
+ kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput)); |
+ |
+ const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; |
+ |
+ ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels); |
+ ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels); |
+ ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels); |
+ |
+ PlusThreeBlockerCallback callback; |
+ Blocker blocker(kChunkSize, |
+ kBlockSize, |
+ kNumInputChannels, |
+ kNumOutputChannels, |
+ kWindow, |
+ kShiftAmount, |
+ &callback); |
+ |
+ RunTest(&blocker, |
+ kChunkSize, |
+ kNumFrames, |
+ input_cb.channels(), |
+ input_chunk_cb.channels(), |
+ actual_output_cb.channels(), |
+ output_chunk_cb.channels(), |
+ kNumInputChannels, |
+ kNumOutputChannels); |
+ |
+ ValidateSignalEquality(expected_output_cb.channels(), |
+ actual_output_cb.channels(), |
+ kNumOutputChannels, |
+ kNumFrames); |
+} |
+ |
+TEST_F(BlockerTest, TestBlockerMutuallyPrimeShiftAndBlockSize) { |
+ const size_t kNumInputChannels = 3; |
+ const size_t kNumOutputChannels = 2; |
+ const size_t kNumFrames = 12; |
+ const size_t kBlockSize = 4; |
+ const size_t kChunkSize = 6; |
+ const size_t kShiftAmount = 3; |
+ |
+ const float kInput[kNumInputChannels][kNumFrames] = { |
+ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, |
+ {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, |
+ {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; |
+ ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels); |
+ input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput)); |
+ |
+ const float kExpectedOutput[kNumOutputChannels][kNumFrames] = { |
+ {6, 10, 10, 20, 10, 10, 20, 10, 10, 20, 10, 10}, |
+ {6, 14, 14, 28, 14, 14, 28, 14, 14, 28, 14, 14}}; |
+ ChannelBuffer<float> expected_output_cb(kNumFrames, kNumOutputChannels); |
+ expected_output_cb.SetDataForTesting( |
+ kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput)); |
+ |
+ const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; |
+ |
+ ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels); |
+ ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels); |
+ ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels); |
+ |
+ PlusThreeBlockerCallback callback; |
+ Blocker blocker(kChunkSize, |
+ kBlockSize, |
+ kNumInputChannels, |
+ kNumOutputChannels, |
+ kWindow, |
+ kShiftAmount, |
+ &callback); |
+ |
+ RunTest(&blocker, |
+ kChunkSize, |
+ kNumFrames, |
+ input_cb.channels(), |
+ input_chunk_cb.channels(), |
+ actual_output_cb.channels(), |
+ output_chunk_cb.channels(), |
+ kNumInputChannels, |
+ kNumOutputChannels); |
+ |
+ ValidateSignalEquality(expected_output_cb.channels(), |
+ actual_output_cb.channels(), |
+ kNumOutputChannels, |
+ kNumFrames); |
+} |
+ |
+TEST_F(BlockerTest, TestBlockerNoOverlap) { |
+ const size_t kNumInputChannels = 3; |
+ const size_t kNumOutputChannels = 2; |
+ const size_t kNumFrames = 12; |
+ const size_t kBlockSize = 4; |
+ const size_t kChunkSize = 4; |
+ const size_t kShiftAmount = 4; |
+ |
+ const float kInput[kNumInputChannels][kNumFrames] = { |
+ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, |
+ {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, |
+ {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; |
+ ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels); |
+ input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput)); |
+ |
+ const float kExpectedOutput[kNumOutputChannels][kNumFrames] = { |
+ {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, |
+ {14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}}; |
+ ChannelBuffer<float> expected_output_cb(kNumFrames, kNumOutputChannels); |
+ expected_output_cb.SetDataForTesting( |
+ kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput)); |
+ |
+ const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; |
+ |
+ ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels); |
+ ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels); |
+ ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels); |
+ |
+ PlusThreeBlockerCallback callback; |
+ Blocker blocker(kChunkSize, |
+ kBlockSize, |
+ kNumInputChannels, |
+ kNumOutputChannels, |
+ kWindow, |
+ kShiftAmount, |
+ &callback); |
+ |
+ RunTest(&blocker, |
+ kChunkSize, |
+ kNumFrames, |
+ input_cb.channels(), |
+ input_chunk_cb.channels(), |
+ actual_output_cb.channels(), |
+ output_chunk_cb.channels(), |
+ kNumInputChannels, |
+ kNumOutputChannels); |
+ |
+ ValidateSignalEquality(expected_output_cb.channels(), |
+ actual_output_cb.channels(), |
+ kNumOutputChannels, |
+ kNumFrames); |
+} |
+ |
+TEST_F(BlockerTest, InitialDelaysAreMinimum) { |
+ const size_t kNumInputChannels = 3; |
+ const size_t kNumOutputChannels = 2; |
+ const size_t kNumFrames = 1280; |
+ const size_t kChunkSize[] = |
+ {80, 80, 80, 80, 80, 80, 160, 160, 160, 160, 160, 160}; |
+ const size_t kBlockSize[] = |
+ {64, 64, 64, 128, 128, 128, 128, 128, 128, 256, 256, 256}; |
+ const size_t kShiftAmount[] = |
+ {16, 32, 64, 32, 64, 128, 32, 64, 128, 64, 128, 256}; |
+ const size_t kInitialDelay[] = |
+ {48, 48, 48, 112, 112, 112, 96, 96, 96, 224, 224, 224}; |
+ |
+ float input[kNumInputChannels][kNumFrames]; |
+ for (size_t i = 0; i < kNumInputChannels; ++i) { |
+ for (size_t j = 0; j < kNumFrames; ++j) { |
+ input[i][j] = i + 1; |
+ } |
+ } |
+ ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels); |
+ input_cb.SetDataForTesting(input[0], sizeof(input) / sizeof(**input)); |
+ |
+ ChannelBuffer<float> output_cb(kNumFrames, kNumOutputChannels); |
+ |
+ CopyBlockerCallback callback; |
+ |
+ for (size_t i = 0; i < arraysize(kChunkSize); ++i) { |
+ std::unique_ptr<float[]> window(new float[kBlockSize[i]]); |
+ for (size_t j = 0; j < kBlockSize[i]; ++j) { |
+ window[j] = 1.f; |
+ } |
+ |
+ ChannelBuffer<float> input_chunk_cb(kChunkSize[i], kNumInputChannels); |
+ ChannelBuffer<float> output_chunk_cb(kChunkSize[i], kNumOutputChannels); |
+ |
+ Blocker blocker(kChunkSize[i], |
+ kBlockSize[i], |
+ kNumInputChannels, |
+ kNumOutputChannels, |
+ window.get(), |
+ kShiftAmount[i], |
+ &callback); |
+ |
+ RunTest(&blocker, |
+ kChunkSize[i], |
+ kNumFrames, |
+ input_cb.channels(), |
+ input_chunk_cb.channels(), |
+ output_cb.channels(), |
+ output_chunk_cb.channels(), |
+ kNumInputChannels, |
+ kNumOutputChannels); |
+ |
+ ValidateInitialDelay(output_cb.channels(), |
+ kNumOutputChannels, |
+ kNumFrames, |
+ kInitialDelay[i]); |
+ } |
+} |
+ |
+} // namespace webrtc |