diff --git a/modules/juce_dsp/frequency/juce_FFT.cpp b/modules/juce_dsp/frequency/juce_FFT.cpp index 08ab182574..f252a9dd65 100644 --- a/modules/juce_dsp/frequency/juce_FFT.cpp +++ b/modules/juce_dsp/frequency/juce_FFT.cpp @@ -29,7 +29,7 @@ struct FFT::Instance { virtual ~Instance() {} virtual void perform (const Complex* input, Complex* output, bool inverse) const noexcept = 0; - virtual void performRealOnlyForwardTransform (float*) const noexcept = 0; + virtual void performRealOnlyForwardTransform (float*, bool) const noexcept = 0; virtual void performRealOnlyInverseTransform (float*) const noexcept = 0; }; @@ -131,7 +131,7 @@ struct FFTFallback : public FFT::Instance const size_t maxFFTScratchSpaceToAlloca = 256 * 1024; - void performRealOnlyForwardTransform (float* d) const noexcept override + void performRealOnlyForwardTransform (float* d, bool) const noexcept override { if (size == 1) return; @@ -180,7 +180,12 @@ struct FFTFallback : public FFT::Instance void performRealOnlyInverseTransform (Complex* scratch, float* d) const noexcept { - perform (reinterpret_cast*> (d), scratch, true); + auto* input = reinterpret_cast*> (d); + + for (auto i = size >> 1; i < size; ++i) + input[i] = std::conj (input[size - i]); + + perform (input, scratch, true); for (int i = 0; i < size; ++i) { @@ -464,7 +469,7 @@ struct AppleFFT : public FFT::Instance vDSP_vsmul ((float*) output, 1, &factor, (float*) output, 1, static_cast (size << 1)); } - void performRealOnlyForwardTransform (float* inoutData) const noexcept override + void performRealOnlyForwardTransform (float* inoutData, bool ignoreNegativeFreqs) const noexcept override { auto size = (1 << order); auto* inout = reinterpret_cast*> (inoutData); @@ -473,7 +478,8 @@ struct AppleFFT : public FFT::Instance inoutData[size] = 0.0f; vDSP_fft_zrip (fftSetup, &splitInOut, 2, order, kFFTDirection_Forward); vDSP_vsmul (inoutData, 1, &forwardNormalisation, inoutData, 1, static_cast (size << 1)); - mirrorResult (inout); + + mirrorResult (inout, ignoreNegativeFreqs); } void performRealOnlyInverseTransform (float* inoutData) const noexcept override @@ -495,7 +501,7 @@ struct AppleFFT : public FFT::Instance private: //============================================================================== - void mirrorResult (Complex* out) const noexcept + void mirrorResult (Complex* out, bool ignoreNegativeFreqs) const noexcept { auto size = (1 << order); auto i = size >> 1; @@ -506,8 +512,9 @@ private: out[i++] = { out[0].imag(), 0.0 }; out[0] = { out[0].real(), 0.0 }; - for (; i < size; ++i) - out[i] = std::conj (out[size - i]); + if (! ignoreNegativeFreqs) + for (; i < size; ++i) + out[i] = std::conj (out[size - i]); } static DSPSplitComplex toSplitComplex (Complex* data) noexcept @@ -671,7 +678,7 @@ struct FFTWImpl : public FFT::Instance } } - void performRealOnlyForwardTransform (float* inputOutputData) const noexcept override + void performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNegativeFreqs) const noexcept override { if (order == 0) return; @@ -682,8 +689,9 @@ struct FFTWImpl : public FFT::Instance auto size = (1 << order); - for (auto i = size >> 1; i < size; ++i) - out[i] = std::conj (out[size - i]); + if (! ignoreNegativeFreqs) + for (auto i = size >> 1; i < size; ++i) + out[i] = std::conj (out[size - i]); } void performRealOnlyInverseTransform (float* inputOutputData) const noexcept override @@ -761,7 +769,7 @@ struct IntelFFT : public FFT::Instance DftiComputeForward (c2c, (void*) input, output); } - void performRealOnlyForwardTransform (float* inputOutputData) const noexcept override + void performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNegativeFreqs) const noexcept override { if (order == 0) return; @@ -771,8 +779,9 @@ struct IntelFFT : public FFT::Instance auto* out = reinterpret_cast*> (inputOutputData); auto size = (1 << order); - for (auto i = size >> 1; i < size; ++i) - out[i] = std::conj (out[size - i]); + if (! ignoreNegativeFreqs) + for (auto i = size >> 1; i < size; ++i) + out[i] = std::conj (out[size - i]); } void performRealOnlyInverseTransform (float* inputOutputData) const noexcept override @@ -802,10 +811,10 @@ void FFT::perform (const Complex* input, Complex* output, bool inv engine->perform (input, output, inverse); } -void FFT::performRealOnlyForwardTransform (float* inputOutputData) const noexcept +void FFT::performRealOnlyForwardTransform (float* inputOutputData, bool ignoreNeagtiveFreqs) const noexcept { if (engine != nullptr) - engine->performRealOnlyForwardTransform (inputOutputData); + engine->performRealOnlyForwardTransform (inputOutputData, ignoreNeagtiveFreqs); } void FFT::performRealOnlyInverseTransform (float* inputOutputData) const noexcept diff --git a/modules/juce_dsp/frequency/juce_FFT.h b/modules/juce_dsp/frequency/juce_FFT.h index a93a57fde8..6b0ece8955 100644 --- a/modules/juce_dsp/frequency/juce_FFT.h +++ b/modules/juce_dsp/frequency/juce_FFT.h @@ -56,17 +56,31 @@ public: /** Performs an in-place forward transform on a block of real data. + As the coefficients of the negative frequences (frequencies higher than + N/2 or pi) are the complex conjugate of their positive counterparts, + it may not be necessary to calculate them for your particular application. + You can use dontCalculateNegativeFrequencies to let the FFT + engine know that you do not plan on using them. Note that this is only a + hint: some FFT engines (currently only the Fallback engine), will still + calculate the negative frequencies even if dontCalculateNegativeFrequencies + is true. + The size of the array passed in must be 2 * getSize(), and the first half - should contain your raw input sample data. On return, the array will contain - size complex real + imaginary parts data interleaved, and can be passed to + should contain your raw input sample data. On return, if + dontCalculateNegativeFrequencies is false, the array will contain size + complex real + imaginary parts data interleaved. If + dontCalculateNegativeFrequencies is true, the array will contain at least + (size / 2) + 1 complex numbers. Both outputs can be passed to performRealOnlyInverseTransform() in order to convert it back to reals. */ - void performRealOnlyForwardTransform (float* inputOutputData) const noexcept; + void performRealOnlyForwardTransform (float* inputOutputData, + bool dontCalculateNegativeFrequencies = false) const noexcept; /** Performs a reverse operation to data created in performRealOnlyForwardTransform(). - The size of the array passed in must be 2 * getSize(), containing size complex - real and imaginary parts interleaved numbers. On return, the first half of the + Although performRealOnlyInverseTransform will only use the first ((size / 2) + 1) + complex numbers, the size of the array passed in must still be 2 * getSize(), as some + FFT engines require the extra space for the calculation. On return, the first half of the array will contain the reconstituted samples. */ void performRealOnlyInverseTransform (float* inputOutputData) const noexcept; diff --git a/modules/juce_dsp/frequency/juce_FFT_test.cpp b/modules/juce_dsp/frequency/juce_FFT_test.cpp index 1377754efb..056560dc97 100644 --- a/modules/juce_dsp/frequency/juce_FFT_test.cpp +++ b/modules/juce_dsp/frequency/juce_FFT_test.cpp @@ -113,6 +113,14 @@ struct FFTUnitTest : public UnitTest 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));