c-gaborator/lib/gaborator/gaborator/fft_vdsp.h
2022-01-23 17:28:23 +01:00

210 lines
5.2 KiB
C++

//
// Fast Fourier transform using the Apple vDSP framework
//
// Copyright (C) 2013-2021 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 <assert.h>
#include <iterator>
#include <vector>
#include <memory.h>
#include <mach/mach.h>
#include <mach/task.h>
#include <mach/task_info.h>
#include <mach/vm_map.h>
#include <Accelerate/Accelerate.h>
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<std::complex<float> *> {
typedef std::complex<float> *I;
typedef typename std::iterator_traits<I>::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<std::complex<float> *> {
typedef std::complex<float> *CI; // Complex iterator
typedef const std::complex<float> *CONST_CI;
typedef typename std::iterator_traits<CI>::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<CI>(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<CI>(in)[0] = tmp;
}
private:
// Size of the transform
unsigned int n;
unsigned int log2n;
FFTSetup setup;
};
// Support transforming std::vector<std::complex<float>>::iterator
template <>
struct fft<typename std::vector<std::complex<float>>::iterator>:
public fft<std::complex<float> *>
{
typedef fft<std::complex<float> *> base;
typedef typename std::vector<std::complex<float>>::iterator I;
fft(unsigned int n_): fft<std::complex<float> *>(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