Home | History | Annotate | Download | only in common_audio
      1 /*
      2  *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/common_audio/lapped_transform.h"
     12 
     13 #include <algorithm>
     14 #include <cmath>
     15 #include <cstring>
     16 
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 using std::complex;
     20 
     21 namespace {
     22 
     23 class NoopCallback : public webrtc::LappedTransform::Callback {
     24  public:
     25   NoopCallback() : block_num_(0) {}
     26 
     27   virtual void ProcessAudioBlock(const complex<float>* const* in_block,
     28                                  size_t in_channels,
     29                                  size_t frames,
     30                                  size_t out_channels,
     31                                  complex<float>* const* out_block) {
     32     RTC_CHECK_EQ(in_channels, out_channels);
     33     for (size_t i = 0; i < out_channels; ++i) {
     34       memcpy(out_block[i], in_block[i], sizeof(**in_block) * frames);
     35     }
     36     ++block_num_;
     37   }
     38 
     39   size_t block_num() {
     40     return block_num_;
     41   }
     42 
     43  private:
     44   size_t block_num_;
     45 };
     46 
     47 class FftCheckerCallback : public webrtc::LappedTransform::Callback {
     48  public:
     49   FftCheckerCallback() : block_num_(0) {}
     50 
     51   virtual void ProcessAudioBlock(const complex<float>* const* in_block,
     52                                  size_t in_channels,
     53                                  size_t frames,
     54                                  size_t out_channels,
     55                                  complex<float>* const* out_block) {
     56     RTC_CHECK_EQ(in_channels, out_channels);
     57 
     58     size_t full_length = (frames - 1) * 2;
     59     ++block_num_;
     60 
     61     if (block_num_ > 0) {
     62       ASSERT_NEAR(in_block[0][0].real(), static_cast<float>(full_length),
     63                   1e-5f);
     64       ASSERT_NEAR(in_block[0][0].imag(), 0.0f, 1e-5f);
     65       for (size_t i = 1; i < frames; ++i) {
     66         ASSERT_NEAR(in_block[0][i].real(), 0.0f, 1e-5f);
     67         ASSERT_NEAR(in_block[0][i].imag(), 0.0f, 1e-5f);
     68       }
     69     }
     70   }
     71 
     72   size_t block_num() {
     73     return block_num_;
     74   }
     75 
     76  private:
     77   size_t block_num_;
     78 };
     79 
     80 void SetFloatArray(float value, int rows, int cols, float* const* array) {
     81   for (int i = 0; i < rows; ++i) {
     82     for (int j = 0; j < cols; ++j) {
     83       array[i][j] = value;
     84     }
     85   }
     86 }
     87 
     88 }  // namespace
     89 
     90 namespace webrtc {
     91 
     92 TEST(LappedTransformTest, Windowless) {
     93   const size_t kChannels = 3;
     94   const size_t kChunkLength = 512;
     95   const size_t kBlockLength = 64;
     96   const size_t kShiftAmount = 64;
     97   NoopCallback noop;
     98 
     99   // Rectangular window.
    100   float window[kBlockLength];
    101   std::fill(window, &window[kBlockLength], 1.0f);
    102 
    103   LappedTransform trans(kChannels, kChannels, kChunkLength, window,
    104                         kBlockLength, kShiftAmount, &noop);
    105   float in_buffer[kChannels][kChunkLength];
    106   float* in_chunk[kChannels];
    107   float out_buffer[kChannels][kChunkLength];
    108   float* out_chunk[kChannels];
    109 
    110   in_chunk[0] = in_buffer[0];
    111   in_chunk[1] = in_buffer[1];
    112   in_chunk[2] = in_buffer[2];
    113   out_chunk[0] = out_buffer[0];
    114   out_chunk[1] = out_buffer[1];
    115   out_chunk[2] = out_buffer[2];
    116   SetFloatArray(2.0f, kChannels, kChunkLength, in_chunk);
    117   SetFloatArray(-1.0f, kChannels, kChunkLength, out_chunk);
    118 
    119   trans.ProcessChunk(in_chunk, out_chunk);
    120 
    121   for (size_t i = 0; i < kChannels; ++i) {
    122     for (size_t j = 0; j < kChunkLength; ++j) {
    123       ASSERT_NEAR(out_chunk[i][j], 2.0f, 1e-5f);
    124     }
    125   }
    126 
    127   ASSERT_EQ(kChunkLength / kBlockLength, noop.block_num());
    128 }
    129 
    130 TEST(LappedTransformTest, IdentityProcessor) {
    131   const size_t kChunkLength = 512;
    132   const size_t kBlockLength = 64;
    133   const size_t kShiftAmount = 32;
    134   NoopCallback noop;
    135 
    136   // Identity window for |overlap = block_size / 2|.
    137   float window[kBlockLength];
    138   std::fill(window, &window[kBlockLength], std::sqrt(0.5f));
    139 
    140   LappedTransform trans(1, 1, kChunkLength, window, kBlockLength, kShiftAmount,
    141                         &noop);
    142   float in_buffer[kChunkLength];
    143   float* in_chunk = in_buffer;
    144   float out_buffer[kChunkLength];
    145   float* out_chunk = out_buffer;
    146 
    147   SetFloatArray(2.0f, 1, kChunkLength, &in_chunk);
    148   SetFloatArray(-1.0f, 1, kChunkLength, &out_chunk);
    149 
    150   trans.ProcessChunk(&in_chunk, &out_chunk);
    151 
    152   for (size_t i = 0; i < kChunkLength; ++i) {
    153     ASSERT_NEAR(out_chunk[i],
    154                 (i < kBlockLength - kShiftAmount) ? 0.0f : 2.0f,
    155                 1e-5f);
    156   }
    157 
    158   ASSERT_EQ(kChunkLength / kShiftAmount, noop.block_num());
    159 }
    160 
    161 TEST(LappedTransformTest, Callbacks) {
    162   const size_t kChunkLength = 512;
    163   const size_t kBlockLength = 64;
    164   FftCheckerCallback call;
    165 
    166   // Rectangular window.
    167   float window[kBlockLength];
    168   std::fill(window, &window[kBlockLength], 1.0f);
    169 
    170   LappedTransform trans(1, 1, kChunkLength, window, kBlockLength,
    171                         kBlockLength, &call);
    172   float in_buffer[kChunkLength];
    173   float* in_chunk = in_buffer;
    174   float out_buffer[kChunkLength];
    175   float* out_chunk = out_buffer;
    176 
    177   SetFloatArray(1.0f, 1, kChunkLength, &in_chunk);
    178   SetFloatArray(-1.0f, 1, kChunkLength, &out_chunk);
    179 
    180   trans.ProcessChunk(&in_chunk, &out_chunk);
    181 
    182   ASSERT_EQ(kChunkLength / kBlockLength, call.block_num());
    183 }
    184 
    185 TEST(LappedTransformTest, chunk_length) {
    186   const size_t kBlockLength = 64;
    187   FftCheckerCallback call;
    188   const float window[kBlockLength] = {};
    189 
    190   // Make sure that chunk_length returns the same value passed to the
    191   // LappedTransform constructor.
    192   {
    193     const size_t kExpectedChunkLength = 512;
    194     const LappedTransform trans(1, 1, kExpectedChunkLength, window,
    195                                 kBlockLength, kBlockLength, &call);
    196 
    197     EXPECT_EQ(kExpectedChunkLength, trans.chunk_length());
    198   }
    199   {
    200     const size_t kExpectedChunkLength = 160;
    201     const LappedTransform trans(1, 1, kExpectedChunkLength, window,
    202                                 kBlockLength, kBlockLength, &call);
    203 
    204     EXPECT_EQ(kExpectedChunkLength, trans.chunk_length());
    205   }
    206 }
    207 
    208 }  // namespace webrtc
    209