Audio plugin host https://kx.studio/carla
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.

219 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
  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. std::vector<float> inout ((size_t) n << 1), reference ((size_t) n << 1);
  113. std::vector<Complex<float>> frequency (n);
  114. fillRandom (random, inout.data(), n);
  115. zeromem (reference.data(), sizeof (float) * ((size_t) n << 1));
  116. performReferenceFourier (inout.data(), frequency.data(), n, false);
  117. for (size_t i = 0; i < n; ++i)
  118. reference[i] = std::abs (frequency[i]);
  119. for (auto ignoreNegative : { false, true })
  120. {
  121. auto inoutCopy = inout;
  122. fft.performFrequencyOnlyForwardTransform (inoutCopy.data(), ignoreNegative);
  123. auto numMatching = ignoreNegative ? (n / 2) + 1 : n;
  124. u.expect (checkArrayIsSimilar (inoutCopy.data(), reference.data(), numMatching));
  125. }
  126. }
  127. }
  128. };
  129. struct ComplexTest
  130. {
  131. static void run(FFTUnitTest& u)
  132. {
  133. Random random (378272);
  134. for (size_t order = 0; order <= 7; ++order)
  135. {
  136. auto n = (1u << order);
  137. FFT fft ((int) order);
  138. HeapBlock<Complex<float>> input (n), buffer (n), output (n), reference (n);
  139. fillRandom (random, input.getData(), n);
  140. performReferenceFourier (input.getData(), reference.getData(), n, false);
  141. memcpy (buffer.getData(), input.getData(), sizeof (Complex<float>) * n);
  142. fft.perform (buffer.getData(), output.getData(), false);
  143. u.expect (checkArrayIsSimilar (output.getData(), reference.getData(), n));
  144. memcpy (buffer.getData(), reference.getData(), sizeof (Complex<float>) * n);
  145. fft.perform (buffer.getData(), output.getData(), true);
  146. u.expect (checkArrayIsSimilar (output.getData(), input.getData(), n));
  147. }
  148. }
  149. };
  150. template <class TheTest>
  151. void runTestForAllTypes (const char* unitTestName)
  152. {
  153. beginTest (unitTestName);
  154. TheTest::run (*this);
  155. }
  156. void runTest() override
  157. {
  158. runTestForAllTypes<RealTest> ("Real input numbers Test");
  159. runTestForAllTypes<FrequencyOnlyTest> ("Frequency only Test");
  160. runTestForAllTypes<ComplexTest> ("Complex input numbers Test");
  161. }
  162. };
  163. static FFTUnitTest fftUnitTest;
  164. } // namespace dsp
  165. } // namespace juce