|  | /*
  ==============================================================================
   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.
   JUCE is an open source library subject to commercial or open-source
   licensing.
   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).
   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy
   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).
   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", "DSP") {}
    static void fillRandom (Random& random, Complex<float>* buffer, size_t n)
    {
        for (size_t i = 0; i < n; ++i)
            buffer[i] = Complex<float> ((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<float> freqConvolution (const Complex<float>* in, float freq, size_t n)
    {
        Complex<float> sum (0.0, 0.0);
        for (size_t i = 0; i < n; ++i)
            sum += in[i] * exp (Complex<float> (0, static_cast<float> (i) * freq));
        return sum;
    }
    static void performReferenceFourier (const Complex<float>* in, Complex<float>* out,
                                         size_t n, bool reverve)
    {
        float base_freq = static_cast<float>(((reverve ? 1.0 : -1.0) * 2.0 * double_Pi)
                                              / static_cast<float> (n));
        for (size_t i = 0; i < n; ++i)
            out[i] = freqConvolution (in, static_cast<float>(i) * base_freq, n);
    }
    static void performReferenceFourier (const float* in, Complex<float>* out,
                                         size_t n, bool reverve)
    {
        HeapBlock<Complex<float>> buffer (n);
        for (size_t i = 0; i < n; ++i)
            buffer.getData()[i] = Complex<float> (in[i], 0.0f);
        float base_freq = static_cast<float>(((reverve ? 1.0 : -1.0) * 2.0 * double_Pi)
                                             / static_cast<float> (n));
        for (size_t i = 0; i < n; ++i)
            out[i] = freqConvolution (buffer.getData(), static_cast<float>(i) * base_freq, n);
    }
    //==============================================================================
    template <typename Type>
    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<float> input (n);
                HeapBlock<Complex<float>> 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<float>));
                memcpy (reinterpret_cast<float*> (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<float>));
                memcpy (reinterpret_cast<float*> (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<float> (0.0f));
                u.expect (checkArrayIsSimilar (reference.getData(), output.getData(), (n >> 1) + 1));
                memcpy (output.getData(), reference.getData(), n * sizeof (Complex<float>));
                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<float> inout (n << 1), reference (n << 1);
                HeapBlock<Complex<float>> 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<Complex<float>> 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<float>) * n);
                fft.perform (buffer.getData(), output.getData(), false);
                u.expect (checkArrayIsSimilar (output.getData(), reference.getData(), n));
                memcpy (buffer.getData(), reference.getData(), sizeof (Complex<float>) * n);
                fft.perform (buffer.getData(), output.getData(), true);
                u.expect (checkArrayIsSimilar (output.getData(), input.getData(), n));
            }
        }
    };
    template <class TheTest>
    void runTestForAllTypes (const char* unitTestName)
    {
        beginTest (unitTestName);
        TheTest::run (*this);
    }
    void runTest() override
    {
        runTestForAllTypes<RealTest> ("Real input numbers Test");
        runTestForAllTypes<FrequencyOnlyTest> ("Frequency only Test");
        runTestForAllTypes<ComplexTest> ("Complex input numbers Test");
    }
};
static FFTUnitTest fftUnitTest;
} // namespace dsp
} // namespace juce
 |