/* ============================================================================== This file is part of the JUCE 6 technical preview. Copyright (c) 2017 - ROLI Ltd. You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For this technical preview, this file is not subject to commercial licensing. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { namespace dsp { struct FFTUnitTest : public UnitTest { FFTUnitTest() : UnitTest ("FFT", UnitTestCategories::dsp) {} static void fillRandom (Random& random, Complex* buffer, size_t n) { for (size_t i = 0; i < n; ++i) buffer[i] = Complex ((2.0f * random.nextFloat()) - 1.0f, (2.0f * random.nextFloat()) - 1.0f); } static void fillRandom (Random& random, float* buffer, size_t n) { for (size_t i = 0; i < n; ++i) buffer[i] = (2.0f * random.nextFloat()) - 1.0f; } static Complex freqConvolution (const Complex* in, float freq, size_t n) { Complex sum (0.0, 0.0); for (size_t i = 0; i < n; ++i) sum += in[i] * exp (Complex (0, static_cast (i) * freq)); return sum; } static void performReferenceFourier (const Complex* in, Complex* out, size_t n, bool reverse) { auto base_freq = static_cast (((reverse ? 1.0 : -1.0) * MathConstants::twoPi) / static_cast (n)); for (size_t i = 0; i < n; ++i) out[i] = freqConvolution (in, static_cast(i) * base_freq, n); } static void performReferenceFourier (const float* in, Complex* out, size_t n, bool reverse) { HeapBlock> buffer (n); for (size_t i = 0; i < n; ++i) buffer.getData()[i] = Complex (in[i], 0.0f); float base_freq = static_cast (((reverse ? 1.0 : -1.0) * MathConstants::twoPi) / static_cast (n)); for (size_t i = 0; i < n; ++i) out[i] = freqConvolution (buffer.getData(), static_cast(i) * base_freq, n); } //============================================================================== template static bool checkArrayIsSimilar (Type* a, Type* b, size_t n) noexcept { for (size_t i = 0; i < n; ++i) if (std::abs (a[i] - b[i]) > 1e-3f) return false; return true; } struct RealTest { static void run (FFTUnitTest& u) { Random random (378272); for (size_t order = 0; order <= 8; ++order) { auto n = (1u << order); FFT fft ((int) order); HeapBlock input (n); HeapBlock> reference (n), output (n); fillRandom (random, input.getData(), n); performReferenceFourier (input.getData(), reference.getData(), n, false); // fill only first half with real numbers zeromem (output.getData(), n * sizeof (Complex)); memcpy (reinterpret_cast (output.getData()), input.getData(), n * sizeof (float)); fft.performRealOnlyForwardTransform ((float*) output.getData()); u.expect (checkArrayIsSimilar (reference.getData(), output.getData(), n)); // fill only first half with real numbers zeromem (output.getData(), n * sizeof (Complex)); memcpy (reinterpret_cast (output.getData()), input.getData(), n * sizeof (float)); fft.performRealOnlyForwardTransform ((float*) output.getData(), true); std::fill (reference.getData() + ((n >> 1) + 1), reference.getData() + n, std::complex (0.0f)); u.expect (checkArrayIsSimilar (reference.getData(), output.getData(), (n >> 1) + 1)); memcpy (output.getData(), reference.getData(), n * sizeof (Complex)); fft.performRealOnlyInverseTransform ((float*) output.getData()); u.expect (checkArrayIsSimilar ((float*) output.getData(), input.getData(), n)); } } }; struct FrequencyOnlyTest { static void run(FFTUnitTest& u) { Random random (378272); for (size_t order = 0; order <= 8; ++order) { auto n = (1u << order); FFT fft ((int) order); HeapBlock inout (n << 1), reference (n << 1); HeapBlock> frequency (n); fillRandom (random, inout.getData(), n); zeromem (reference.getData(), sizeof (float) * (n << 1)); performReferenceFourier (inout.getData(), frequency.getData(), n, false); for (size_t i = 0; i < n; ++i) reference.getData()[i] = std::abs (frequency.getData()[i]); fft.performFrequencyOnlyForwardTransform (inout.getData()); u.expect (checkArrayIsSimilar (inout.getData(), reference.getData(), n)); } } }; struct ComplexTest { static void run(FFTUnitTest& u) { Random random (378272); for (size_t order = 0; order <= 7; ++order) { auto n = (1u << order); FFT fft ((int) order); HeapBlock> input (n), buffer (n), output (n), reference (n); fillRandom (random, input.getData(), n); performReferenceFourier (input.getData(), reference.getData(), n, false); memcpy (buffer.getData(), input.getData(), sizeof (Complex) * n); fft.perform (buffer.getData(), output.getData(), false); u.expect (checkArrayIsSimilar (output.getData(), reference.getData(), n)); memcpy (buffer.getData(), reference.getData(), sizeof (Complex) * n); fft.perform (buffer.getData(), output.getData(), true); u.expect (checkArrayIsSimilar (output.getData(), input.getData(), n)); } } }; template void runTestForAllTypes (const char* unitTestName) { beginTest (unitTestName); TheTest::run (*this); } void runTest() override { runTestForAllTypes ("Real input numbers Test"); runTestForAllTypes ("Frequency only Test"); runTestForAllTypes ("Complex input numbers Test"); } }; static FFTUnitTest fftUnitTest; } // namespace dsp } // namespace juce