blob: a3d6f357ec02b4410eaa745b5b682d0876e7b92b [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);
Alessio Bazzica53dd1f32019-03-26 17:27:32 +010091 pffft_wrapper.ForwardTransform(*in_wrapper, out_wrapper.get(),
92 /*ordered=*/false);
Alessio Bazzicacaa499b2019-02-21 17:35:43 +010093 ExpectArrayViewsEquality(out_view, out_wrapper_view);
94
95 // Copy the FFT results into the input buffers to compute the backward FFT.
96 std::copy(out_view.begin(), out_view.end(), in_view.begin());
97 std::copy(out_wrapper_view.begin(), out_wrapper_view.end(),
98 in_wrapper_view.begin());
99
100 // Backward transform.
101 pffft_transform(pffft_status, in, out, scratch, PFFFT_BACKWARD);
Alessio Bazzica53dd1f32019-03-26 17:27:32 +0100102 pffft_wrapper.BackwardTransform(*in_wrapper, out_wrapper.get(),
103 /*ordered=*/false);
Alessio Bazzicacaa499b2019-02-21 17:35:43 +0100104 ExpectArrayViewsEquality(out_view, out_wrapper_view);
105
106 pffft_destroy_setup(pffft_status);
107 pffft_aligned_free(in);
108 pffft_aligned_free(out);
109 pffft_aligned_free(scratch);
110}
111
112} // namespace
113
114TEST(PffftTest, CreateWrapperWithValidSize) {
115 for (size_t fft_size = 0; fft_size < kMaxValidSizeCheck; ++fft_size) {
116 SCOPED_TRACE(fft_size);
117 if (Pffft::IsValidFftSize(fft_size, Pffft::FftType::kReal)) {
118 CreatePffftWrapper(fft_size, Pffft::FftType::kReal);
119 }
120 if (Pffft::IsValidFftSize(fft_size, Pffft::FftType::kComplex)) {
121 CreatePffftWrapper(fft_size, Pffft::FftType::kComplex);
122 }
123 }
124}
125
126#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
Alessio Bazzica9b1288c2019-03-25 09:56:30 +0100127
128class PffftInvalidSizeTest : public testing::Test,
129 public ::testing::WithParamInterface<size_t> {};
130
131TEST_P(PffftInvalidSizeTest, DoNotCreateRealWrapper) {
132 size_t fft_size = GetParam();
133 ASSERT_FALSE(Pffft::IsValidFftSize(fft_size, Pffft::FftType::kReal));
134 EXPECT_DEATH(CreatePffftWrapper(fft_size, Pffft::FftType::kReal), "");
Alessio Bazzicacaa499b2019-02-21 17:35:43 +0100135}
Alessio Bazzica9b1288c2019-03-25 09:56:30 +0100136
137TEST_P(PffftInvalidSizeTest, DoNotCreateComplexWrapper) {
138 size_t fft_size = GetParam();
139 ASSERT_FALSE(Pffft::IsValidFftSize(fft_size, Pffft::FftType::kComplex));
140 EXPECT_DEATH(CreatePffftWrapper(fft_size, Pffft::FftType::kComplex), "");
141}
142
143INSTANTIATE_TEST_SUITE_P(PffftTest,
144 PffftInvalidSizeTest,
145 ::testing::Values(17,
146 33,
147 65,
148 97,
149 129,
150 161,
151 193,
152 257,
153 289,
154 385,
155 481,
156 513,
157 577,
158 641,
159 801,
160 865,
161 1025));
162
Alessio Bazzicacaa499b2019-02-21 17:35:43 +0100163#endif
164
165// TODO(https://crbug.com/webrtc/9577): Enable once SIMD is always enabled.
166TEST(PffftTest, DISABLED_CheckSimd) {
167 EXPECT_TRUE(Pffft::IsSimdEnabled());
168}
169
170TEST(PffftTest, FftBitExactness) {
171 for (int fft_size : kFftSizes) {
172 SCOPED_TRACE(fft_size);
173 if (fft_size != 16) {
174 PffftValidateWrapper(fft_size, /*complex_fft=*/false);
175 }
176 PffftValidateWrapper(fft_size, /*complex_fft=*/true);
177 }
178}
179
180} // namespace test
181} // namespace webrtc