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.

208 lines
7.2KB

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