// // Fast Fourier transform using PFFFT // // Copyright (C) 2017-2020 Andreas Gustafsson. This file is part of // the Gaborator library source distribution. See the file LICENSE at // the top level of the distribution for license information. // #ifndef _GABORATOR_FFT_PFFFT_H #define _GABORATOR_FFT_PFFFT_H #include #include #include #include #include "pffft.h" // XXX disable in production #ifdef __x86_64__ #define GABORATOR_PFFFT_CHECK_ALIGN(p) assert((((uint64_t)(p)) & 0xF) == 0) #else #define GABORATOR_PFFFT_CHECK_ALIGN(p) do {} while (0) #endif namespace gaborator { template <> struct fft *> { typedef std::complex *I; typedef const std::complex *CONST_I; typedef std::iterator_traits::value_type C; // complex typedef C::value_type T; // float/double fft(unsigned int n_): n(n_) { setup = pffft_new_setup(n, PFFFT_COMPLEX); assert(setup); } ~fft() { pffft_destroy_setup(setup); } unsigned int size() { return n; } // in-place void transform(I a) { pffft_transform_ordered(setup, (float *)a, (float *)a, NULL, PFFFT_FORWARD); } void itransform(I a) { pffft_transform_ordered(setup, (float *)a, (float *)a, NULL, PFFFT_BACKWARD); } // out-of-place void transform(CONST_I in, I out) { GABORATOR_PFFFT_CHECK_ALIGN(in); GABORATOR_PFFFT_CHECK_ALIGN(out); pffft_transform_ordered(setup, (const float *)in, (float *)out, NULL, PFFFT_FORWARD); } void itransform(CONST_I in, I out) { GABORATOR_PFFFT_CHECK_ALIGN(in); GABORATOR_PFFFT_CHECK_ALIGN(out); pffft_transform_ordered(setup, (const float *)in, (float *)out, NULL, PFFFT_BACKWARD); } private: // Size of the transform unsigned int n; PFFFT_Setup *setup; }; // Support transforming std::vector >::iterator template <> struct fft >::iterator>: public fft *> { typedef fft *> base; typedef std::vector >::iterator I; fft(unsigned int n_): fft *>(n_) { } void transform(I a) { base::transform(&(*a)); } void itransform(I a) { base::itransform(&(*a)); } void transform(I in, I out) { base::transform(&(*in), &(*out)); } void itransform(I in, I out) { base::itransform(&(*in), &(*out)); } }; // Use fftpack for double precision #define FFTPACK_DOUBLE_PRECISION 1 #include "fftpack.h" #undef FFTPACK_DOUBLE_PRECISION template <> struct fft *> { typedef std::complex *I; typedef const std::complex *CONST_I; typedef std::iterator_traits::value_type C; // complex typedef C::value_type T; // float/double fft(unsigned int n_): n(n_), wsave(4 * n_ + 15) { cffti(n, wsave.data()); } ~fft() { } unsigned int size() { return n; } // in-place void transform(I a) { cfftf(n, (double *)a, wsave.data()); } void itransform(I a) { cfftb(n, (double *)a, wsave.data()); } // out-of-place void transform(CONST_I in, I out) { std::copy(in, in + n, out); transform(out); } void itransform(CONST_I in, I out) { std::copy(in, in + n, out); itransform(out); } private: // Size of the transform unsigned int n; std::vector wsave; }; // Real FFT template <> struct rfft *> { typedef std::complex *CI; // Complex iterator typedef const std::complex *CONST_CI; typedef typename std::iterator_traits::value_type C; // complex typedef typename C::value_type T; // float/double typedef T *RI; // Real iterator typedef const T *CONST_RI; rfft(unsigned int n_): n(n_) { setup = pffft_new_setup(n, PFFFT_REAL); assert(setup); } ~rfft() { pffft_destroy_setup(setup); } unsigned int size() { return n; } // out-of-place only void transform(CONST_RI in, CI out) { GABORATOR_PFFFT_CHECK_ALIGN(in); GABORATOR_PFFFT_CHECK_ALIGN(out); pffft_transform_ordered(setup, in, (float *) out, NULL, PFFFT_FORWARD); C tmp = out[0]; #if GABORATOR_REAL_FFT_NEGATIVE_FQS for (unsigned int i = 1; i < (n >> 1); i++) out[n - i] = conj(out[i]); #endif out[0] = C(tmp.real(), 0); out[n >> 1] = C(tmp.imag(), 0); } // Note: this temporarily modifies in[0], in spite of the const void itransform(CONST_CI in, RI out) { GABORATOR_PFFFT_CHECK_ALIGN(in); GABORATOR_PFFFT_CHECK_ALIGN(out); C tmp = in[0]; const_cast(in)[0] = C(tmp.real(), in[n >> 1].real()); pffft_transform_ordered(setup, (const float *) in, out, NULL, PFFFT_BACKWARD); const_cast(in)[0] = tmp; } private: // Size of the transform unsigned int n; PFFFT_Setup *setup; }; #undef GABORATOR_PFFFT_CHECK_ALIGN } // namespace #endif