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