I'm very unpleased to have found out the hard way, that
vDSP.FFT<T> where T : vDSP_FourierTransformable
and its associated vDSP_FourierTransformFunctions,
only does real-complex conversion!!!
This is horribly misleading - the only hint that it calls vDSP_fft_zrop() (split-complex real-complex out-of-place) under the hood, not vDSP_fft_zop()[1], is "A 1D single- and double-precision fast Fourier transform." - instead of "a single- and double-precision complex fast Fourier transform". Holy ******* ****.
Just look at how people miss-call this routine. Poor him, realizing he had to log2n+1 lest only the first half of the array was transformed, not understanding why [2].
And poor me, having taken days investigating why a simple Swift overlay vDSP.FFT.transform may execute at 1.4x the speed of vDSP_fft_zopt(out-of-place with temporary buffer)! [3]
[1]: or better, vDSP_fft_zopt with the temp buffer alloc/dealloc taken care of, for us
[2]: for real-complex conversion, say real signal of length 16. log2n is 4 no problem, but then the real and imaginary vectors are both length 8. Also, vDSP_fft only works on integer powers of 2, so he had to choose next integer power of 2 (i.e. 1<<(log2n-1)) instead of plain length for his internal arrays.
[3]: you guessed it. fft_zrop(log2n-1, ...) vs. fft_zop(log2n, ...). Half the problem size lol.
Now we have vDSP.DiscreteFourierTransform, which wraps vDSP_DFT routines and "calls FFT routines when the size allows it", and works too for interleaved complexes. Just go all DFT, right?
if let setup_f = try? vDSP.DiscreteFourierTransform(previous: nil, count: 8, direction: .forward, transformType: .complexComplex, ofType: DSPComplex.self) {
// Done forward transformation
// and scaled the results with 1/N
// How about going reverse?
if let setup_r = try? vDSP.DiscreteFourierTransform(previous: setup_f, count: 8, direction: .inverse, transformType: .complexComplex, ofType: DSPComplex.self) {
// Error: cannot convert vDSP.DiscreteFourierTransform<DSPComplex> to vDSP.DiscreteFourierTransform<Float>
// lolz
}
}
This API appeared in macOS 12. 3 years later nobody have ever noticed. I'm at a loss for words.
Selecting any option will automatically load the page