I'm trying to port some python numpy code to Swift using the Accelerate framework.
In python I write
import numpy as np
frames = np.array([1.0, 2.0, 3.0, 4.0])
fftArray = np.fft.fft(frames, len(frames))
print(fftArray)
And the output is:
[10.+0.j -2.+2.j -2.+0.j -2.-2.j]
So in Swift I'm trying to calculate the FFT like this:
import Foundation
import Accelerate
func fftAnalyzer(frameOfSamples: [Float]) {
// As above, frameOfSamples = [1.0, 2.0, 3.0, 4.0]
let analysisBuffer = frameOfSamples
let frameCount = frameOfSamples.count
var reals = [Float]()
var imags = [Float]()
for (idx, element) in analysisBuffer.enumerated() {
if idx % 2 == 0 {
reals.append(element)
} else {
imags.append(element)
}
}
var complexBuffer = DSPSplitComplex(realp: UnsafeMutablePointer(mutating: reals), imagp: UnsafeMutablePointer(mutating: imags))
let log2Size = Int(log2f(Float(frameCount)))
guard let fftSetup = vDSP_create_fftsetup(vDSP_Length(log2Size), Int32(kFFTRadix2)) else {
return []
}
// Perform a forward FFT
vDSP_fft_zrip(fftSetup, &(complexBuffer), 1, UInt(log2Size), Int32(FFT_FORWARD))
let realFloats = Array(UnsafeBufferPointer(start: complexBuffer.realp, count: Int(frameCount)))
let imaginaryFloats = Array(UnsafeBufferPointer(start: complexBuffer.imagp, count: Int(frameCount)))
print(realFloats)
print(imaginaryFloats)
// Release the setup
vDSP_destroy_fftsetup(fftSetup)
return realFloats
}
The realFloats and imaginaryFloats are printed like so:
[20.0, -4.0, 0.0, 0.0]
[-4.0, 4.0, 0.0, 0.0]
Any ideas on what I should be doing differently?
I'm not good at numpy, but according to the doc, fft
takes complex-input. Then its equivalent would be vDSP_fft_zip
, not vDSP_fft_zrip
.
And your code causes buffer overflow or might cause dangling pointer, with all such things fixed I get this:
func fftAnalyzer(frameOfSamples: [Float]) -> [Float] {
// As above, frameOfSamples = [1.0, 2.0, 3.0, 4.0]
let frameCount = frameOfSamples.count
let reals = UnsafeMutableBufferPointer<Float>.allocate(capacity: frameCount)
defer {reals.deallocate()}
let imags = UnsafeMutableBufferPointer<Float>.allocate(capacity: frameCount)
defer {imags.deallocate()}
_ = reals.initialize(from: frameOfSamples)
imags.initialize(repeating: 0.0)
var complexBuffer = DSPSplitComplex(realp: reals.baseAddress!, imagp: imags.baseAddress!)
let log2Size = Int(log2(Float(frameCount)))
print(log2Size)
guard let fftSetup = vDSP_create_fftsetup(vDSP_Length(log2Size), FFTRadix(kFFTRadix2)) else {
return []
}
defer {vDSP_destroy_fftsetup(fftSetup)}
// Perform a forward FFT
vDSP_fft_zip(fftSetup, &complexBuffer, 1, vDSP_Length(log2Size), FFTDirection(FFT_FORWARD))
let realFloats = Array(reals)
let imaginaryFloats = Array(imags)
print(realFloats)
print(imaginaryFloats)
return realFloats
}
Printed
[10.0, -2.0, -2.0, -2.0] [0.0, 2.0, 0.0, -2.0]