OpenShot Audio Library | OpenShotAudio  0.6.0
juce_FFT_test.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11  Agreement and JUCE Privacy Policy.
12 
13  End User License Agreement: www.juce.com/juce-7-licence
14  Privacy Policy: www.juce.com/juce-privacy-policy
15 
16  Or: You may also use this code under the terms of the GPL v3 (see
17  www.gnu.org/licenses).
18 
19  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21  DISCLAIMED.
22 
23  ==============================================================================
24 */
25 
26 namespace juce::dsp
27 {
28 
29 struct FFTUnitTest final : public UnitTest
30 {
31  FFTUnitTest()
32  : UnitTest ("FFT", UnitTestCategories::dsp)
33  {}
34 
35  static void fillRandom (Random& random, Complex<float>* buffer, size_t n)
36  {
37  for (size_t i = 0; i < n; ++i)
38  buffer[i] = Complex<float> ((2.0f * random.nextFloat()) - 1.0f,
39  (2.0f * random.nextFloat()) - 1.0f);
40  }
41 
42  static void fillRandom (Random& random, float* buffer, size_t n)
43  {
44  for (size_t i = 0; i < n; ++i)
45  buffer[i] = (2.0f * random.nextFloat()) - 1.0f;
46  }
47 
48  static Complex<float> freqConvolution (const Complex<float>* in, float freq, size_t n)
49  {
50  Complex<float> sum (0.0, 0.0);
51  for (size_t i = 0; i < n; ++i)
52  sum += in[i] * exp (Complex<float> (0, static_cast<float> (i) * freq));
53 
54  return sum;
55  }
56 
57  static void performReferenceFourier (const Complex<float>* in, Complex<float>* out,
58  size_t n, bool reverse)
59  {
60  auto base_freq = static_cast<float> (((reverse ? 1.0 : -1.0) * MathConstants<double>::twoPi)
61  / static_cast<float> (n));
62 
63  for (size_t i = 0; i < n; ++i)
64  out[i] = freqConvolution (in, static_cast<float> (i) * base_freq, n);
65  }
66 
67  static void performReferenceFourier (const float* in, Complex<float>* out,
68  size_t n, bool reverse)
69  {
70  HeapBlock<Complex<float>> buffer (n);
71 
72  for (size_t i = 0; i < n; ++i)
73  buffer.getData()[i] = Complex<float> (in[i], 0.0f);
74 
75  float base_freq = static_cast<float> (((reverse ? 1.0 : -1.0) * MathConstants<double>::twoPi)
76  / static_cast<float> (n));
77 
78  for (size_t i = 0; i < n; ++i)
79  out[i] = freqConvolution (buffer.getData(), static_cast<float> (i) * base_freq, n);
80  }
81 
82 
83  //==============================================================================
84  template <typename Type>
85  static bool checkArrayIsSimilar (Type* a, Type* b, size_t n) noexcept
86  {
87  for (size_t i = 0; i < n; ++i)
88  if (std::abs (a[i] - b[i]) > 1e-3f)
89  return false;
90 
91  return true;
92  }
93 
94  struct RealTest
95  {
96  static void run (FFTUnitTest& u)
97  {
98  Random random (378272);
99 
100  for (size_t order = 0; order <= 8; ++order)
101  {
102  auto n = (1u << order);
103 
104  FFT fft ((int) order);
105 
106  HeapBlock<float> input (n);
107  HeapBlock<Complex<float>> reference (n), output (n);
108 
109  fillRandom (random, input.getData(), n);
110  performReferenceFourier (input.getData(), reference.getData(), n, false);
111 
112  // fill only first half with real numbers
113  zeromem (output.getData(), n * sizeof (Complex<float>));
114  memcpy (reinterpret_cast<float*> (output.getData()), input.getData(), n * sizeof (float));
115 
116  fft.performRealOnlyForwardTransform ((float*) output.getData());
117  u.expect (checkArrayIsSimilar (reference.getData(), output.getData(), n));
118 
119  // fill only first half with real numbers
120  zeromem (output.getData(), n * sizeof (Complex<float>));
121  memcpy (reinterpret_cast<float*> (output.getData()), input.getData(), n * sizeof (float));
122 
123  fft.performRealOnlyForwardTransform ((float*) output.getData(), true);
124  std::fill (reference.getData() + ((n >> 1) + 1), reference.getData() + n, std::complex<float> (0.0f));
125  u.expect (checkArrayIsSimilar (reference.getData(), output.getData(), (n >> 1) + 1));
126 
127  memcpy (output.getData(), reference.getData(), n * sizeof (Complex<float>));
128  fft.performRealOnlyInverseTransform ((float*) output.getData());
129  u.expect (checkArrayIsSimilar ((float*) output.getData(), input.getData(), n));
130  }
131  }
132  };
133 
134  struct FrequencyOnlyTest
135  {
136  static void run (FFTUnitTest& u)
137  {
138  Random random (378272);
139  for (size_t order = 0; order <= 8; ++order)
140  {
141  auto n = (1u << order);
142 
143  FFT fft ((int) order);
144 
145  std::vector<float> inout ((size_t) n << 1), reference ((size_t) n << 1);
146  std::vector<Complex<float>> frequency (n);
147 
148  fillRandom (random, inout.data(), n);
149  zeromem (reference.data(), sizeof (float) * ((size_t) n << 1));
150  performReferenceFourier (inout.data(), frequency.data(), n, false);
151 
152  for (size_t i = 0; i < n; ++i)
153  reference[i] = std::abs (frequency[i]);
154 
155  for (auto ignoreNegative : { false, true })
156  {
157  auto inoutCopy = inout;
158  fft.performFrequencyOnlyForwardTransform (inoutCopy.data(), ignoreNegative);
159  auto numMatching = ignoreNegative ? (n / 2) + 1 : n;
160  u.expect (checkArrayIsSimilar (inoutCopy.data(), reference.data(), numMatching));
161  }
162  }
163  }
164  };
165 
166  struct ComplexTest
167  {
168  static void run (FFTUnitTest& u)
169  {
170  Random random (378272);
171 
172  for (size_t order = 0; order <= 7; ++order)
173  {
174  auto n = (1u << order);
175 
176  FFT fft ((int) order);
177 
178  HeapBlock<Complex<float>> input (n), buffer (n), output (n), reference (n);
179 
180  fillRandom (random, input.getData(), n);
181  performReferenceFourier (input.getData(), reference.getData(), n, false);
182 
183  memcpy (buffer.getData(), input.getData(), sizeof (Complex<float>) * n);
184  fft.perform (buffer.getData(), output.getData(), false);
185 
186  u.expect (checkArrayIsSimilar (output.getData(), reference.getData(), n));
187 
188  memcpy (buffer.getData(), reference.getData(), sizeof (Complex<float>) * n);
189  fft.perform (buffer.getData(), output.getData(), true);
190 
191 
192  u.expect (checkArrayIsSimilar (output.getData(), input.getData(), n));
193  }
194  }
195  };
196 
197  template <class TheTest>
198  void runTestForAllTypes (const char* unitTestName)
199  {
200  beginTest (unitTestName);
201 
202  TheTest::run (*this);
203  }
204 
205  void runTest() override
206  {
207  runTestForAllTypes<RealTest> ("Real input numbers Test");
208  runTestForAllTypes<FrequencyOnlyTest> ("Frequency only Test");
209  runTestForAllTypes<ComplexTest> ("Complex input numbers Test");
210  }
211 };
212 
213 static FFTUnitTest fftUnitTest;
214 
215 } // namespace juce::dsp
UnitTest(const String &name, const String &category=String())
void beginTest(const String &testName)
static constexpr FloatType twoPi