From 7613968a01e51cfd36e1d04307d323ec1dab438d Mon Sep 17 00:00:00 2001 From: Yair Chuchem Date: Mon, 25 Mar 2024 15:56:47 +0200 Subject: [PATCH] DRY in FFT engines: legacy spectrum mirroring not part of the engine This makes the FFT engine wrappers simpler, deduplicates code (reducing potential for bugs in wrappers), at the cost of making the FallbackFFT's redundant work done again if the legacy flag is used. --- modules/juce_dsp/frequency/juce_FFT.cpp | 72 +++++++++---------------- 1 file changed, 25 insertions(+), 47 deletions(-) diff --git a/modules/juce_dsp/frequency/juce_FFT.cpp b/modules/juce_dsp/frequency/juce_FFT.cpp index 481b905373..e142badc72 100644 --- a/modules/juce_dsp/frequency/juce_FFT.cpp +++ b/modules/juce_dsp/frequency/juce_FFT.cpp @@ -30,7 +30,7 @@ struct FFT::Instance { virtual ~Instance() = default; virtual void perform (const Complex* input, Complex* output, bool inverse) const noexcept = 0; - virtual void performRealOnlyForwardTransform (float*, bool) const noexcept = 0; + virtual void performRealOnlyForwardTransform (float*) const noexcept = 0; virtual void performRealOnlyInverseTransform (float*) const noexcept = 0; }; @@ -124,7 +124,7 @@ struct FFTFallback final : public FFT::Instance const size_t maxFFTScratchSpaceToAlloca = 256 * 1024; - void performRealOnlyForwardTransform (float* d, bool) const noexcept override + void performRealOnlyForwardTransform (float* d) const noexcept override { if (size == 1) return; @@ -471,7 +471,7 @@ struct AppleFFT final : public FFT::Instance vDSP_vsmul ((float*) output, 1, &factor, (float*) output, 1, static_cast (size << 1)); } - void performRealOnlyForwardTransform (float* inoutData, bool ignoreNegativeFreqs) const noexcept override + void performRealOnlyForwardTransform (float* inoutData) const noexcept override { auto size = (1 << order); auto* inout = reinterpret_cast*> (inoutData); @@ -481,7 +481,12 @@ struct AppleFFT final : public FFT::Instance vDSP_fft_zrip (fftSetup, &splitInOut, 2, order, kFFTDirection_Forward); vDSP_vsmul (inoutData, 1, &forwardNormalisation, inoutData, 1, static_cast (size << 1)); - mirrorResult (inout, ignoreNegativeFreqs); + // Imaginary part of nyquist and DC frequencies are always zero + // so Apple uses the imaginary part of the DC frequency to store + // the real part of the nyquist frequency + auto* out = reinterpret_cast*> (inoutData); + out[size >> 1] = { out[0].imag(), 0.0 }; + out[0] = { out[0].real(), 0.0 }; } void performRealOnlyInverseTransform (float* inoutData) const noexcept override @@ -503,22 +508,6 @@ struct AppleFFT final : public FFT::Instance private: //============================================================================== - void mirrorResult (Complex* out, bool ignoreNegativeFreqs) const noexcept - { - auto size = (1 << order); - auto i = size >> 1; - - // Imaginary part of nyquist and DC frequencies are always zero - // so Apple uses the imaginary part of the DC frequency to store - // the real part of the nyquist frequency - out[i++] = { out[0].imag(), 0.0 }; - out[0] = { out[0].real(), 0.0 }; - - if (! ignoreNegativeFreqs) - for (; i < size; ++i) - out[i] = std::conj (out[size - i]); - } - static DSPSplitComplex toSplitComplex (Complex* data) noexcept { // this assumes that Complex interleaves real and imaginary parts @@ -684,7 +673,7 @@ struct FFTWImpl : public FFT::Instance } } - void performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNegativeFreqs) const noexcept override + void performRealOnlyForwardTransform (float* inputOutputData) const noexcept override { if (order == 0) return; @@ -692,12 +681,6 @@ struct FFTWImpl : public FFT::Instance auto* out = reinterpret_cast*> (inputOutputData); fftw.execute_r2c_fftw (r2c, inputOutputData, out); - - auto size = (1 << order); - - if (! ignoreNegativeFreqs) - for (int i = size >> 1; i < size; ++i) - out[i] = std::conj (out[size - i]); } void performRealOnlyInverseTransform (float* inputOutputData) const noexcept override @@ -784,19 +767,12 @@ struct IntelFFT final : public FFT::Instance DftiComputeForward (c2c, (void*) input, output); } - void performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNegativeFreqs) const noexcept override + void performRealOnlyForwardTransform (float* inputOutputData) const noexcept override { if (order == 0) return; DftiComputeForward (c2r, inputOutputData); - - auto* out = reinterpret_cast*> (inputOutputData); - auto size = (1 << order); - - if (! ignoreNegativeFreqs) - for (int i = size >> 1; i < size; ++i) - out[i] = std::conj (out[size - i]); } void performRealOnlyInverseTransform (float* inputOutputData) const noexcept override @@ -851,19 +827,9 @@ public: } } - void performRealOnlyForwardTransform (float* inoutData, bool ignoreNegativeFreqs) const noexcept override + void performRealOnlyForwardTransform (float* inoutData) const noexcept override { ippsFFTFwd_RToCCS_32f_I (inoutData, real.specPtr, real.workBuf.get()); - - if (order == 0) - return; - - auto* out = reinterpret_cast*> (inoutData); - const auto size = (1 << order); - - if (! ignoreNegativeFreqs) - for (auto i = size >> 1; i < size; ++i) - out[i] = std::conj (out[size - i]); } void performRealOnlyInverseTransform (float* inoutData) const noexcept override @@ -970,7 +936,19 @@ void FFT::perform (const Complex* input, Complex* output, bool inv void FFT::performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNegativeFreqs) const noexcept { if (engine != nullptr) - engine->performRealOnlyForwardTransform (inputOutputData, ignoreNegativeFreqs); + engine->performRealOnlyForwardTransform (inputOutputData); + + if (! ignoreNegativeFreqs && size != 1) + { + // Preserve compatibility with legacy implementation + // where the redundant negative frequencies were also generated. + + auto* out = reinterpret_cast*> (inputOutputData); + + if (! ignoreNegativeFreqs) + for (auto i = size >> 1; i < size; ++i) + out[i] = std::conj (out[size - i]); + } } void FFT::performRealOnlyInverseTransform (float* inputOutputData) const noexcept