blob: 70c5ce52f23733190a0160707ff33b0244a2d299 [file] [log] [blame]
Alessio Bazzicacaa499b2019-02-21 17:35:43 +01001/*
2 * Copyright (c) 2019 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/utility/pffft_wrapper.h"
12
13#include <algorithm>
14#include <cstdlib>
15#include <memory>
16
17#include "test/gtest.h"
18#include "third_party/pffft/src/pffft.h"
19
20namespace webrtc {
21namespace test {
22namespace {
23
24constexpr size_t kMaxValidSizeCheck = 1024;
25
26static constexpr int kFftSizes[] = {
27 16, 32, 64, 96, 128, 160, 192, 256, 288, 384, 5 * 96, 512,
28 576, 5 * 128, 800, 864, 1024, 2048, 2592, 4000, 4096, 12000, 36864};
29
30void CreatePffftWrapper(size_t fft_size, Pffft::FftType fft_type) {
31 Pffft pffft_wrapper(fft_size, fft_type);
32}
33
34float* AllocateScratchBuffer(size_t fft_size, bool complex_fft) {
35 return static_cast<float*>(
36 pffft_aligned_malloc(fft_size * (complex_fft ? 2 : 1) * sizeof(float)));
37}
38
39double frand() {
40 return std::rand() / static_cast<double>(RAND_MAX);
41}
42
43void ExpectArrayViewsEquality(rtc::ArrayView<const float> a,
44 rtc::ArrayView<const float> b) {
45 ASSERT_EQ(a.size(), b.size());
46 for (size_t i = 0; i < a.size(); ++i) {
47 SCOPED_TRACE(i);
48 EXPECT_EQ(a[i], b[i]);
49 }
50}
51
52// Compares the output of the PFFFT C++ wrapper to that of the C PFFFT.
53// Bit-exactness is expected.
54void PffftValidateWrapper(size_t fft_size, bool complex_fft) {
55 // Always use the same seed to avoid flakiness.
56 std::srand(0);
57
58 // Init PFFFT.
59 PFFFT_Setup* pffft_status =
60 pffft_new_setup(fft_size, complex_fft ? PFFFT_COMPLEX : PFFFT_REAL);
61 ASSERT_TRUE(pffft_status) << "FFT size (" << fft_size << ") not supported.";
62 size_t num_floats = fft_size * (complex_fft ? 2 : 1);
63 int num_bytes = static_cast<int>(num_floats) * sizeof(float);
64 float* in = static_cast<float*>(pffft_aligned_malloc(num_bytes));
65 float* out = static_cast<float*>(pffft_aligned_malloc(num_bytes));
66 float* scratch = AllocateScratchBuffer(fft_size, complex_fft);
67
68 // Init PFFFT C++ wrapper.
69 Pffft::FftType fft_type =
70 complex_fft ? Pffft::FftType::kComplex : Pffft::FftType::kReal;
71 ASSERT_TRUE(Pffft::IsValidFftSize(fft_size, fft_type));
72 Pffft pffft_wrapper(fft_size, fft_type);
73 auto in_wrapper = pffft_wrapper.CreateBuffer();
74 auto out_wrapper = pffft_wrapper.CreateBuffer();
75
76 // Input and output buffers views.
77 rtc::ArrayView<float> in_view(in, num_floats);
78 rtc::ArrayView<float> out_view(out, num_floats);
79 auto in_wrapper_view = in_wrapper->GetView();
80 EXPECT_EQ(in_wrapper_view.size(), num_floats);
81 auto out_wrapper_view = out_wrapper->GetConstView();
82 EXPECT_EQ(out_wrapper_view.size(), num_floats);
83
84 // Random input data.
85 for (size_t i = 0; i < num_floats; ++i) {
86 in_wrapper_view[i] = in[i] = static_cast<float>(frand() * 2.0 - 1.0);
87 }
88
89 // Forward transform.
90 pffft_transform(pffft_status, in, out, scratch, PFFFT_FORWARD);
91 pffft_wrapper.ForwardTransform(*in_wrapper, out_wrapper.get());
92 ExpectArrayViewsEquality(out_view, out_wrapper_view);
93
94 // Copy the FFT results into the input buffers to compute the backward FFT.
95 std::copy(out_view.begin(), out_view.end(), in_view.begin());
96 std::copy(out_wrapper_view.begin(), out_wrapper_view.end(),
97 in_wrapper_view.begin());
98
99 // Backward transform.
100 pffft_transform(pffft_status, in, out, scratch, PFFFT_BACKWARD);
101 pffft_wrapper.BackwardTransform(*in_wrapper, out_wrapper.get());
102 ExpectArrayViewsEquality(out_view, out_wrapper_view);
103
104 pffft_destroy_setup(pffft_status);
105 pffft_aligned_free(in);
106 pffft_aligned_free(out);
107 pffft_aligned_free(scratch);
108}
109
110} // namespace
111
112TEST(PffftTest, CreateWrapperWithValidSize) {
113 for (size_t fft_size = 0; fft_size < kMaxValidSizeCheck; ++fft_size) {
114 SCOPED_TRACE(fft_size);
115 if (Pffft::IsValidFftSize(fft_size, Pffft::FftType::kReal)) {
116 CreatePffftWrapper(fft_size, Pffft::FftType::kReal);
117 }
118 if (Pffft::IsValidFftSize(fft_size, Pffft::FftType::kComplex)) {
119 CreatePffftWrapper(fft_size, Pffft::FftType::kComplex);
120 }
121 }
122}
123
124#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
Alessio Bazzica9b1288c2019-03-25 09:56:30 +0100125
126class PffftInvalidSizeTest : public testing::Test,
127 public ::testing::WithParamInterface<size_t> {};
128
129TEST_P(PffftInvalidSizeTest, DoNotCreateRealWrapper) {
130 size_t fft_size = GetParam();
131 ASSERT_FALSE(Pffft::IsValidFftSize(fft_size, Pffft::FftType::kReal));
132 EXPECT_DEATH(CreatePffftWrapper(fft_size, Pffft::FftType::kReal), "");
Alessio Bazzicacaa499b2019-02-21 17:35:43 +0100133}
Alessio Bazzica9b1288c2019-03-25 09:56:30 +0100134
135TEST_P(PffftInvalidSizeTest, DoNotCreateComplexWrapper) {
136 size_t fft_size = GetParam();
137 ASSERT_FALSE(Pffft::IsValidFftSize(fft_size, Pffft::FftType::kComplex));
138 EXPECT_DEATH(CreatePffftWrapper(fft_size, Pffft::FftType::kComplex), "");
139}
140
141INSTANTIATE_TEST_SUITE_P(PffftTest,
142 PffftInvalidSizeTest,
143 ::testing::Values(17,
144 33,
145 65,
146 97,
147 129,
148 161,
149 193,
150 257,
151 289,
152 385,
153 481,
154 513,
155 577,
156 641,
157 801,
158 865,
159 1025));
160
Alessio Bazzicacaa499b2019-02-21 17:35:43 +0100161#endif
162
163// TODO(https://crbug.com/webrtc/9577): Enable once SIMD is always enabled.
164TEST(PffftTest, DISABLED_CheckSimd) {
165 EXPECT_TRUE(Pffft::IsSimdEnabled());
166}
167
168TEST(PffftTest, FftBitExactness) {
169 for (int fft_size : kFftSizes) {
170 SCOPED_TRACE(fft_size);
171 if (fft_size != 16) {
172 PffftValidateWrapper(fft_size, /*complex_fft=*/false);
173 }
174 PffftValidateWrapper(fft_size, /*complex_fft=*/true);
175 }
176}
177
178} // namespace test
179} // namespace webrtc