/* * 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 "webrtc/common_audio/lapped_transform.h" #include #include #include #include "testing/gtest/include/gtest/gtest.h" using std::complex; namespace { class NoopCallback : public webrtc::LappedTransform::Callback { public: NoopCallback() : block_num_(0) {} virtual void ProcessAudioBlock(const complex* const* in_block, int in_channels, size_t frames, int out_channels, complex* const* out_block) { RTC_CHECK_EQ(in_channels, out_channels); for (int i = 0; i < out_channels; ++i) { memcpy(out_block[i], in_block[i], sizeof(**in_block) * frames); } ++block_num_; } int block_num() { return block_num_; } private: int block_num_; }; class FftCheckerCallback : public webrtc::LappedTransform::Callback { public: FftCheckerCallback() : block_num_(0) {} virtual void ProcessAudioBlock(const complex* const* in_block, int in_channels, size_t frames, int out_channels, complex* const* out_block) { RTC_CHECK_EQ(in_channels, out_channels); size_t full_length = (frames - 1) * 2; ++block_num_; if (block_num_ > 0) { ASSERT_NEAR(in_block[0][0].real(), static_cast(full_length), 1e-5f); ASSERT_NEAR(in_block[0][0].imag(), 0.0f, 1e-5f); for (size_t i = 1; i < frames; ++i) { ASSERT_NEAR(in_block[0][i].real(), 0.0f, 1e-5f); ASSERT_NEAR(in_block[0][i].imag(), 0.0f, 1e-5f); } } } int block_num() { return block_num_; } private: int block_num_; }; void SetFloatArray(float value, int rows, int cols, float* const* array) { for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { array[i][j] = value; } } } } // namespace namespace webrtc { TEST(LappedTransformTest, Windowless) { const int kChannels = 3; const int kChunkLength = 512; const int kBlockLength = 64; const int kShiftAmount = 64; NoopCallback noop; // Rectangular window. float window[kBlockLength]; std::fill(window, &window[kBlockLength], 1.0f); LappedTransform trans(kChannels, kChannels, kChunkLength, window, kBlockLength, kShiftAmount, &noop); float in_buffer[kChannels][kChunkLength]; float* in_chunk[kChannels]; float out_buffer[kChannels][kChunkLength]; float* out_chunk[kChannels]; in_chunk[0] = in_buffer[0]; in_chunk[1] = in_buffer[1]; in_chunk[2] = in_buffer[2]; out_chunk[0] = out_buffer[0]; out_chunk[1] = out_buffer[1]; out_chunk[2] = out_buffer[2]; SetFloatArray(2.0f, kChannels, kChunkLength, in_chunk); SetFloatArray(-1.0f, kChannels, kChunkLength, out_chunk); trans.ProcessChunk(in_chunk, out_chunk); for (int i = 0; i < kChannels; ++i) { for (int j = 0; j < kChunkLength; ++j) { ASSERT_NEAR(out_chunk[i][j], 2.0f, 1e-5f); } } ASSERT_EQ(kChunkLength / kBlockLength, noop.block_num()); } TEST(LappedTransformTest, IdentityProcessor) { const int kChunkLength = 512; const int kBlockLength = 64; const int kShiftAmount = 32; NoopCallback noop; // Identity window for |overlap = block_size / 2|. float window[kBlockLength]; std::fill(window, &window[kBlockLength], std::sqrt(0.5f)); LappedTransform trans(1, 1, kChunkLength, window, kBlockLength, kShiftAmount, &noop); float in_buffer[kChunkLength]; float* in_chunk = in_buffer; float out_buffer[kChunkLength]; float* out_chunk = out_buffer; SetFloatArray(2.0f, 1, kChunkLength, &in_chunk); SetFloatArray(-1.0f, 1, kChunkLength, &out_chunk); trans.ProcessChunk(&in_chunk, &out_chunk); for (int i = 0; i < kChunkLength; ++i) { ASSERT_NEAR(out_chunk[i], (i < kBlockLength - kShiftAmount) ? 0.0f : 2.0f, 1e-5f); } ASSERT_EQ(kChunkLength / kShiftAmount, noop.block_num()); } TEST(LappedTransformTest, Callbacks) { const int kChunkLength = 512; const int kBlockLength = 64; FftCheckerCallback call; // Rectangular window. float window[kBlockLength]; std::fill(window, &window[kBlockLength], 1.0f); LappedTransform trans(1, 1, kChunkLength, window, kBlockLength, kBlockLength, &call); float in_buffer[kChunkLength]; float* in_chunk = in_buffer; float out_buffer[kChunkLength]; float* out_chunk = out_buffer; SetFloatArray(1.0f, 1, kChunkLength, &in_chunk); SetFloatArray(-1.0f, 1, kChunkLength, &out_chunk); trans.ProcessChunk(&in_chunk, &out_chunk); ASSERT_EQ(kChunkLength / kBlockLength, call.block_num()); } TEST(LappedTransformTest, chunk_length) { const int kBlockLength = 64; FftCheckerCallback call; const float window[kBlockLength] = {}; // Make sure that chunk_length returns the same value passed to the // LappedTransform constructor. { const size_t kExpectedChunkLength = 512; const LappedTransform trans(1, 1, kExpectedChunkLength, window, kBlockLength, kBlockLength, &call); EXPECT_EQ(kExpectedChunkLength, trans.chunk_length()); } { const size_t kExpectedChunkLength = 160; const LappedTransform trans(1, 1, kExpectedChunkLength, window, kBlockLength, kBlockLength, &call); EXPECT_EQ(kExpectedChunkLength, trans.chunk_length()); } } } // namespace webrtc