// // Fast Fourier transform using the Apple vDSP framework // // Copyright (C) 2013-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_VDSP_H #define _GABORATOR_FFT_VDSP_H #include #include #include #include #include #include #include #include #include namespace gaborator { static inline int log2_int_exact(int n) { // n must be a power of two assert(n != 0 && ((n & (n >> 1)) == 0)); int r = 0; for (;;) { n >>= 1; if (n == 0) break; r++; } return r; } template <> struct fft *> { typedef std::complex *I; typedef typename std::iterator_traits::value_type C; // complex typedef typename C::value_type T; // float/double fft(unsigned int n_): n(n_), log2n(log2_int_exact(n)) { setup = vDSP_create_fftsetup(log2n, kFFTRadix2); } ~fft() { vDSP_destroy_fftsetup(setup); } unsigned int size() { return n; } // in-place void transform(I a) { DSPSplitComplex s; // XXX this result in disoptimal alignment s.realp = (float *) a; s.imagp = (float *) a + 1; vDSP_fft_zip(setup, &s, 2, log2n, kFFTDirection_Forward); } void itransform(I a) { DSPSplitComplex s; s.realp = (float *) a; s.imagp = (float *) a + 1; vDSP_fft_zip(setup, &s, 2, log2n, kFFTDirection_Inverse); } // out-of-place // XXX const void transform(I in, I out) { DSPSplitComplex si; si.realp = (float *) in; si.imagp = (float *) in + 1; DSPSplitComplex so; so.realp = (float *) out; so.imagp = (float *) out + 1; vDSP_fft_zop(setup, &si, 2, &so, 2, log2n, kFFTDirection_Forward); } void itransform(I in, I out) { DSPSplitComplex si; si.realp = (float *) in; si.imagp = (float *) in + 1; DSPSplitComplex so; so.realp = (float *) out; so.imagp = (float *) out + 1; vDSP_fft_zop(setup, &si, 2, &so, 2, log2n, kFFTDirection_Inverse); } private: // Size of the transform unsigned int n; unsigned int log2n; FFTSetup setup; }; // 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_), log2n(log2_int_exact(n)) { setup = vDSP_create_fftsetup(log2n, kFFTRadix2); } ~rfft() { vDSP_destroy_fftsetup(setup); } unsigned int size() { return n; } // out-of-place only void transform(CONST_RI in, CI out) { DSPSplitComplex si; si.realp = (float *) in; si.imagp = (float *) in + 1; DSPSplitComplex so; so.realp = (float *) out; so.imagp = (float *) out + 1; vDSP_fft_zrop(setup, &si, 2, &so, 2, log2n, kFFTDirection_Forward); // Undo vDSP scaling for (unsigned int i = 0; i < (n >> 1); i++) out[i] *= (T)0.5; 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); } void itransform(CONST_CI in, RI out) { C tmp = in[0]; const_cast(in)[0] = C(tmp.real(), in[n >> 1].real()); DSPSplitComplex si; si.realp = (float *) in; si.imagp = (float *) in + 1; DSPSplitComplex so; so.realp = (float *) out; so.imagp = (float *) out + 1; vDSP_fft_zrop(setup, &si, 2, &so, 2, log2n, kFFTDirection_Inverse); const_cast(in)[0] = tmp; } private: // Size of the transform unsigned int n; unsigned int log2n; FFTSetup setup; }; // Support transforming std::vector >::iterator template <> struct fft >::iterator>: public fft *> { typedef fft *> base; typedef typename 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)); } }; } // Namespace #endif