1 /*
2  *  Copyright (c) 2017 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 "modules/audio_processing/aec3/aec3_fft.h"
12 
13 #include <algorithm>
14 
15 #include "test/gmock.h"
16 #include "test/gtest.h"
17 
18 namespace webrtc {
19 
20 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
21 
22 // Verifies that the check for non-null input in Fft works.
TEST(Aec3Fft,NullFftInput)23 TEST(Aec3Fft, NullFftInput) {
24   Aec3Fft fft;
25   FftData X;
26   EXPECT_DEATH(fft.Fft(nullptr, &X), "");
27 }
28 
29 // Verifies that the check for non-null input in Fft works.
TEST(Aec3Fft,NullFftOutput)30 TEST(Aec3Fft, NullFftOutput) {
31   Aec3Fft fft;
32   std::array<float, kFftLength> x;
33   EXPECT_DEATH(fft.Fft(&x, nullptr), "");
34 }
35 
36 // Verifies that the check for non-null output in Ifft works.
TEST(Aec3Fft,NullIfftOutput)37 TEST(Aec3Fft, NullIfftOutput) {
38   Aec3Fft fft;
39   FftData X;
40   EXPECT_DEATH(fft.Ifft(X, nullptr), "");
41 }
42 
43 // Verifies that the check for non-null output in ZeroPaddedFft works.
TEST(Aec3Fft,NullZeroPaddedFftOutput)44 TEST(Aec3Fft, NullZeroPaddedFftOutput) {
45   Aec3Fft fft;
46   std::array<float, kFftLengthBy2> x;
47   EXPECT_DEATH(fft.ZeroPaddedFft(x, nullptr), "");
48 }
49 
50 // Verifies that the check for input length in ZeroPaddedFft works.
TEST(Aec3Fft,ZeroPaddedFftWrongInputLength)51 TEST(Aec3Fft, ZeroPaddedFftWrongInputLength) {
52   Aec3Fft fft;
53   FftData X;
54   std::array<float, kFftLengthBy2 - 1> x;
55   EXPECT_DEATH(fft.ZeroPaddedFft(x, &X), "");
56 }
57 
58 // Verifies that the check for non-null output in PaddedFft works.
TEST(Aec3Fft,NullPaddedFftOutput)59 TEST(Aec3Fft, NullPaddedFftOutput) {
60   Aec3Fft fft;
61   std::array<float, kFftLengthBy2> x;
62   std::array<float, kFftLengthBy2> x_old;
63   EXPECT_DEATH(fft.PaddedFft(x, x_old, nullptr), "");
64 }
65 
66 // Verifies that the check for input length in PaddedFft works.
TEST(Aec3Fft,PaddedFftWrongInputLength)67 TEST(Aec3Fft, PaddedFftWrongInputLength) {
68   Aec3Fft fft;
69   FftData X;
70   std::array<float, kFftLengthBy2 - 1> x;
71   std::array<float, kFftLengthBy2> x_old;
72   EXPECT_DEATH(fft.PaddedFft(x, x_old, &X), "");
73 }
74 
75 // Verifies that the check for length in the old value in PaddedFft works.
TEST(Aec3Fft,PaddedFftWrongOldValuesLength)76 TEST(Aec3Fft, PaddedFftWrongOldValuesLength) {
77   Aec3Fft fft;
78   FftData X;
79   std::array<float, kFftLengthBy2> x;
80   std::array<float, kFftLengthBy2 - 1> x_old;
81   EXPECT_DEATH(fft.PaddedFft(x, x_old, &X), "");
82 }
83 
84 #endif
85 
86 // Verifies that Fft works as intended.
TEST(Aec3Fft,Fft)87 TEST(Aec3Fft, Fft) {
88   Aec3Fft fft;
89   FftData X;
90   std::array<float, kFftLength> x;
91   x.fill(0.f);
92   fft.Fft(&x, &X);
93   EXPECT_THAT(X.re, ::testing::Each(0.f));
94   EXPECT_THAT(X.im, ::testing::Each(0.f));
95 
96   x.fill(0.f);
97   x[0] = 1.f;
98   fft.Fft(&x, &X);
99   EXPECT_THAT(X.re, ::testing::Each(1.f));
100   EXPECT_THAT(X.im, ::testing::Each(0.f));
101 
102   x.fill(1.f);
103   fft.Fft(&x, &X);
104   EXPECT_EQ(128.f, X.re[0]);
105   std::for_each(X.re.begin() + 1, X.re.end(),
106                 [](float a) { EXPECT_EQ(0.f, a); });
107   EXPECT_THAT(X.im, ::testing::Each(0.f));
108 }
109 
110 // Verifies that InverseFft works as intended.
TEST(Aec3Fft,Ifft)111 TEST(Aec3Fft, Ifft) {
112   Aec3Fft fft;
113   FftData X;
114   std::array<float, kFftLength> x;
115 
116   X.re.fill(0.f);
117   X.im.fill(0.f);
118   fft.Ifft(X, &x);
119   EXPECT_THAT(x, ::testing::Each(0.f));
120 
121   X.re.fill(1.f);
122   X.im.fill(0.f);
123   fft.Ifft(X, &x);
124   EXPECT_EQ(64.f, x[0]);
125   std::for_each(x.begin() + 1, x.end(), [](float a) { EXPECT_EQ(0.f, a); });
126 
127   X.re.fill(0.f);
128   X.re[0] = 128;
129   X.im.fill(0.f);
130   fft.Ifft(X, &x);
131   EXPECT_THAT(x, ::testing::Each(64.f));
132 }
133 
134 // Verifies that InverseFft and Fft work as intended.
TEST(Aec3Fft,FftAndIfft)135 TEST(Aec3Fft, FftAndIfft) {
136   Aec3Fft fft;
137   FftData X;
138   std::array<float, kFftLength> x;
139   std::array<float, kFftLength> x_ref;
140 
141   int v = 0;
142   for (int k = 0; k < 20; ++k) {
143     for (size_t j = 0; j < x.size(); ++j) {
144       x[j] = v++;
145       x_ref[j] = x[j] * 64.f;
146     }
147     fft.Fft(&x, &X);
148     fft.Ifft(X, &x);
149     for (size_t j = 0; j < x.size(); ++j) {
150       EXPECT_NEAR(x_ref[j], x[j], 0.001f);
151     }
152   }
153 }
154 
155 // Verifies that ZeroPaddedFft work as intended.
TEST(Aec3Fft,ZeroPaddedFft)156 TEST(Aec3Fft, ZeroPaddedFft) {
157   Aec3Fft fft;
158   FftData X;
159   std::array<float, kFftLengthBy2> x_in;
160   std::array<float, kFftLength> x_ref;
161   std::array<float, kFftLength> x_out;
162 
163   int v = 0;
164   x_ref.fill(0.f);
165   for (int k = 0; k < 20; ++k) {
166     for (size_t j = 0; j < x_in.size(); ++j) {
167       x_in[j] = v++;
168       x_ref[j + kFftLengthBy2] = x_in[j] * 64.f;
169     }
170     fft.ZeroPaddedFft(x_in, &X);
171     fft.Ifft(X, &x_out);
172     for (size_t j = 0; j < x_out.size(); ++j) {
173       EXPECT_NEAR(x_ref[j], x_out[j], 0.1f);
174     }
175   }
176 }
177 
178 // Verifies that ZeroPaddedFft work as intended.
TEST(Aec3Fft,PaddedFft)179 TEST(Aec3Fft, PaddedFft) {
180   Aec3Fft fft;
181   FftData X;
182   std::array<float, kFftLengthBy2> x_in;
183   std::array<float, kFftLength> x_out;
184   std::array<float, kFftLengthBy2> x_old;
185   std::array<float, kFftLengthBy2> x_old_ref;
186   std::array<float, kFftLength> x_ref;
187 
188   int v = 0;
189   x_old.fill(0.f);
190   for (int k = 0; k < 20; ++k) {
191     for (size_t j = 0; j < x_in.size(); ++j) {
192       x_in[j] = v++;
193     }
194 
195     std::copy(x_old.begin(), x_old.end(), x_ref.begin());
196     std::copy(x_in.begin(), x_in.end(), x_ref.begin() + kFftLengthBy2);
197     std::copy(x_in.begin(), x_in.end(), x_old_ref.begin());
198     std::for_each(x_ref.begin(), x_ref.end(), [](float& a) { a *= 64.f; });
199 
200     fft.PaddedFft(x_in, x_old, &X);
201     fft.Ifft(X, &x_out);
202 
203     for (size_t j = 0; j < x_out.size(); ++j) {
204       EXPECT_NEAR(x_ref[j], x_out[j], 0.1f);
205     }
206 
207     EXPECT_EQ(x_old_ref, x_old);
208   }
209 }
210 
211 }  // namespace webrtc
212