1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-02-01 03:10:06 +00:00

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.
This commit is contained in:
Yair Chuchem 2024-03-25 15:56:47 +02:00
parent fe00442335
commit 7613968a01

View file

@ -30,7 +30,7 @@ struct FFT::Instance
{
virtual ~Instance() = default;
virtual void perform (const Complex<float>* input, Complex<float>* 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_t> (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<Complex<float>*> (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_t> (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<Complex<float>*> (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<float>* 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<float>* 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<Complex<float>*> (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<Complex<float>*> (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<Complex<float>*> (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<float>* input, Complex<float>* 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<Complex<float>*> (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