| Index: webrtc/common_audio/lapped_transform_unittest.cc | 
| diff --git a/webrtc/common_audio/lapped_transform_unittest.cc b/webrtc/common_audio/lapped_transform_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..a78488e326d9b0759955bb8a9b3941ecd0057235 | 
| --- /dev/null | 
| +++ b/webrtc/common_audio/lapped_transform_unittest.cc | 
| @@ -0,0 +1,208 @@ | 
| +/* | 
| + *  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 | 
|  |