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.

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