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.

215 lines
7.5KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - 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 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-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
  19. {
  20. namespace dsp
  21. {
  22. struct FFTUnitTest : public UnitTest
  23. {
  24. FFTUnitTest()
  25. : UnitTest ("FFT", UnitTestCategories::dsp)
  26. {}
  27. static void fillRandom (Random& random, Complex<float>* buffer, size_t n)
  28. {
  29. for (size_t i = 0; i < n; ++i)
  30. buffer[i] = Complex<float> ((2.0f * random.nextFloat()) - 1.0f,
  31. (2.0f * random.nextFloat()) - 1.0f);
  32. }
  33. static void fillRandom (Random& random, float* buffer, size_t n)
  34. {
  35. for (size_t i = 0; i < n; ++i)
  36. buffer[i] = (2.0f * random.nextFloat()) - 1.0f;
  37. }
  38. static Complex<float> freqConvolution (const Complex<float>* in, float freq, size_t n)
  39. {
  40. Complex<float> sum (0.0, 0.0);
  41. for (size_t i = 0; i < n; ++i)
  42. sum += in[i] * exp (Complex<float> (0, static_cast<float> (i) * freq));
  43. return sum;
  44. }
  45. static void performReferenceFourier (const Complex<float>* in, Complex<float>* out,
  46. size_t n, bool reverse)
  47. {
  48. auto base_freq = static_cast<float> (((reverse ? 1.0 : -1.0) * MathConstants<double>::twoPi)
  49. / static_cast<float> (n));
  50. for (size_t i = 0; i < n; ++i)
  51. out[i] = freqConvolution (in, static_cast<float>(i) * base_freq, n);
  52. }
  53. static void performReferenceFourier (const float* in, Complex<float>* out,
  54. size_t n, bool reverse)
  55. {
  56. HeapBlock<Complex<float>> buffer (n);
  57. for (size_t i = 0; i < n; ++i)
  58. buffer.getData()[i] = Complex<float> (in[i], 0.0f);
  59. float base_freq = static_cast<float> (((reverse ? 1.0 : -1.0) * MathConstants<double>::twoPi)
  60. / static_cast<float> (n));
  61. for (size_t i = 0; i < n; ++i)
  62. out[i] = freqConvolution (buffer.getData(), static_cast<float>(i) * base_freq, n);
  63. }
  64. //==============================================================================
  65. template <typename Type>
  66. static bool checkArrayIsSimilar (Type* a, Type* b, size_t n) noexcept
  67. {
  68. for (size_t i = 0; i < n; ++i)
  69. if (std::abs (a[i] - b[i]) > 1e-3f)
  70. return false;
  71. return true;
  72. }
  73. struct RealTest
  74. {
  75. static void run (FFTUnitTest& u)
  76. {
  77. Random random (378272);
  78. for (size_t order = 0; order <= 8; ++order)
  79. {
  80. auto n = (1u << order);
  81. FFT fft ((int) order);
  82. HeapBlock<float> input (n);
  83. HeapBlock<Complex<float>> reference (n), output (n);
  84. fillRandom (random, input.getData(), n);
  85. performReferenceFourier (input.getData(), reference.getData(), n, false);
  86. // fill only first half with real numbers
  87. zeromem (output.getData(), n * sizeof (Complex<float>));
  88. memcpy (reinterpret_cast<float*> (output.getData()), input.getData(), n * sizeof (float));
  89. fft.performRealOnlyForwardTransform ((float*) output.getData());
  90. u.expect (checkArrayIsSimilar (reference.getData(), output.getData(), n));
  91. // fill only first half with real numbers
  92. zeromem (output.getData(), n * sizeof (Complex<float>));
  93. memcpy (reinterpret_cast<float*> (output.getData()), input.getData(), n * sizeof (float));
  94. fft.performRealOnlyForwardTransform ((float*) output.getData(), true);
  95. std::fill (reference.getData() + ((n >> 1) + 1), reference.getData() + n, std::complex<float> (0.0f));
  96. u.expect (checkArrayIsSimilar (reference.getData(), output.getData(), (n >> 1) + 1));
  97. memcpy (output.getData(), reference.getData(), n * sizeof (Complex<float>));
  98. fft.performRealOnlyInverseTransform ((float*) output.getData());
  99. u.expect (checkArrayIsSimilar ((float*) output.getData(), input.getData(), n));
  100. }
  101. }
  102. };
  103. struct FrequencyOnlyTest
  104. {
  105. static void run(FFTUnitTest& u)
  106. {
  107. Random random (378272);
  108. for (size_t order = 0; order <= 8; ++order)
  109. {
  110. auto n = (1u << order);
  111. FFT fft ((int) order);
  112. HeapBlock<float> inout (n << 1), reference (n << 1);
  113. HeapBlock<Complex<float>> frequency (n);
  114. fillRandom (random, inout.getData(), n);
  115. zeromem (reference.getData(), sizeof (float) * (n << 1));
  116. performReferenceFourier (inout.getData(), frequency.getData(), n, false);
  117. for (size_t i = 0; i < n; ++i)
  118. reference.getData()[i] = std::abs (frequency.getData()[i]);
  119. fft.performFrequencyOnlyForwardTransform (inout.getData());
  120. u.expect (checkArrayIsSimilar (inout.getData(), reference.getData(), n));
  121. }
  122. }
  123. };
  124. struct ComplexTest
  125. {
  126. static void run(FFTUnitTest& u)
  127. {
  128. Random random (378272);
  129. for (size_t order = 0; order <= 7; ++order)
  130. {
  131. auto n = (1u << order);
  132. FFT fft ((int) order);
  133. HeapBlock<Complex<float>> input (n), buffer (n), output (n), reference (n);
  134. fillRandom (random, input.getData(), n);
  135. performReferenceFourier (input.getData(), reference.getData(), n, false);
  136. memcpy (buffer.getData(), input.getData(), sizeof (Complex<float>) * n);
  137. fft.perform (buffer.getData(), output.getData(), false);
  138. u.expect (checkArrayIsSimilar (output.getData(), reference.getData(), n));
  139. memcpy (buffer.getData(), reference.getData(), sizeof (Complex<float>) * n);
  140. fft.perform (buffer.getData(), output.getData(), true);
  141. u.expect (checkArrayIsSimilar (output.getData(), input.getData(), n));
  142. }
  143. }
  144. };
  145. template <class TheTest>
  146. void runTestForAllTypes (const char* unitTestName)
  147. {
  148. beginTest (unitTestName);
  149. TheTest::run (*this);
  150. }
  151. void runTest() override
  152. {
  153. runTestForAllTypes<RealTest> ("Real input numbers Test");
  154. runTestForAllTypes<FrequencyOnlyTest> ("Frequency only Test");
  155. runTestForAllTypes<ComplexTest> ("Complex input numbers Test");
  156. }
  157. };
  158. static FFTUnitTest fftUnitTest;
  159. } // namespace dsp
  160. } // namespace juce