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:
NoopCallback()25   NoopCallback() : block_num_(0) {}
26 
ProcessAudioBlock(const complex<float> * const * in_block,size_t in_channels,size_t frames,size_t out_channels,complex<float> * const * out_block)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 
block_num()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:
FftCheckerCallback()49   FftCheckerCallback() : block_num_(0) {}
50 
ProcessAudioBlock(const complex<float> * const * in_block,size_t in_channels,size_t frames,size_t out_channels,complex<float> * const * out_block)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 
block_num()72   size_t block_num() {
73     return block_num_;
74   }
75 
76  private:
77   size_t block_num_;
78 };
79 
SetFloatArray(float value,int rows,int cols,float * const * array)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 
TEST(LappedTransformTest,Windowless)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 
TEST(LappedTransformTest,IdentityProcessor)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 
TEST(LappedTransformTest,Callbacks)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 
TEST(LappedTransformTest,chunk_length)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