|  | /* | 
|  | *  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 <algorithm> | 
|  | #include <cmath> | 
|  | #include <cstring> | 
|  |  | 
|  | #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<float>* const* in_block, | 
|  | size_t in_channels, | 
|  | size_t frames, | 
|  | size_t out_channels, | 
|  | complex<float>* const* out_block) { | 
|  | RTC_CHECK_EQ(in_channels, out_channels); | 
|  | for (size_t i = 0; i < out_channels; ++i) { | 
|  | memcpy(out_block[i], in_block[i], sizeof(**in_block) * frames); | 
|  | } | 
|  | ++block_num_; | 
|  | } | 
|  |  | 
|  | size_t block_num() { | 
|  | return block_num_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | size_t block_num_; | 
|  | }; | 
|  |  | 
|  | class FftCheckerCallback : public webrtc::LappedTransform::Callback { | 
|  | public: | 
|  | FftCheckerCallback() : block_num_(0) {} | 
|  |  | 
|  | virtual void ProcessAudioBlock(const complex<float>* const* in_block, | 
|  | size_t in_channels, | 
|  | size_t frames, | 
|  | size_t out_channels, | 
|  | complex<float>* 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<float>(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); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t block_num() { | 
|  | return block_num_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | size_t 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 size_t kChannels = 3; | 
|  | const size_t kChunkLength = 512; | 
|  | const size_t kBlockLength = 64; | 
|  | const size_t 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 (size_t i = 0; i < kChannels; ++i) { | 
|  | for (size_t 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 size_t kChunkLength = 512; | 
|  | const size_t kBlockLength = 64; | 
|  | const size_t 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 (size_t 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 size_t kChunkLength = 512; | 
|  | const size_t 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 size_t 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 |