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.5KB

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