The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
7.7KB

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