blob: 8ec2208f984874ae2c51ac5b2d4a4d2052001e1c [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)
125TEST(PffftTest, DoNotCreateWrapperWithInvalidSize) {
126 for (size_t fft_size = 0; fft_size < kMaxValidSizeCheck; ++fft_size) {
127 SCOPED_TRACE(fft_size);
128 if (!Pffft::IsValidFftSize(fft_size, Pffft::FftType::kReal)) {
129 EXPECT_DEATH(CreatePffftWrapper(fft_size, Pffft::FftType::kReal), "");
130 }
131 if (!Pffft::IsValidFftSize(fft_size, Pffft::FftType::kComplex)) {
132 EXPECT_DEATH(CreatePffftWrapper(fft_size, Pffft::FftType::kComplex), "");
133 }
134 }
135}
136#endif
137
138// TODO(https://crbug.com/webrtc/9577): Enable once SIMD is always enabled.
139TEST(PffftTest, DISABLED_CheckSimd) {
140 EXPECT_TRUE(Pffft::IsSimdEnabled());
141}
142
143TEST(PffftTest, FftBitExactness) {
144 for (int fft_size : kFftSizes) {
145 SCOPED_TRACE(fft_size);
146 if (fft_size != 16) {
147 PffftValidateWrapper(fft_size, /*complex_fft=*/false);
148 }
149 PffftValidateWrapper(fft_size, /*complex_fft=*/true);
150 }
151}
152
153} // namespace test
154} // namespace webrtc