FFT计算错误 - Swift

3

我正在尝试进行快速傅里叶变换。我基于Surge进行计算,但是我无法得到正确的结果。当我对1000赫兹的声音进行fft时,结果看起来像这样:FFT using surge。而当我使用Python进行相同的处理时,结果看起来更加正确:python code。Python代码如下:

import numpy as np
import scipy.io.wavfile
import numpy.fft
import matplotlib.pyplot as plt

FILENAME = 'beep.wav'

fs, data = scipy.io.wavfile.read(FILENAME)
data = data[:801]
spacing = 1 / float(fs)
freq = numpy.fft.rfft(data)
freq_power = np.abs(freq)
a = 1 / (2 * spacing)
b = (len(data) + 1) // 2
freq_axis = np.linspace(0, a, b)
plt.plot(freq_axis, freq_power)
plt.show()

Swift代码看起来像这样:
import Accelerate
public func sqrt(x: [Float]) -> [Float] {
var results = [Float](count: x.count, repeatedValue: 0.0)
vvsqrtf(&results, x, [Int32(x.count)])

return results
}

public func fft(input: [Float]) -> [Float] {
var real = [Float](input)
var imaginary = [Float](count: input.count, repeatedValue: 0.0)
var splitComplex = DSPSplitComplex(realp: &real, imagp: &imaginary)

let length = vDSP_Length(floor(log2(Float(input.count))))
let radix = FFTRadix(kFFTRadix2)
let weights = vDSP_create_fftsetup(length, radix)
println(weights)
vDSP_fft_zip(weights, &splitComplex, 1, 8, FFTDirection(FFT_FORWARD))

var magnitudes = [Float](count: input.count, repeatedValue: 0.0)
vDSP_zvmags(&splitComplex, 1, &magnitudes, 1, vDSP_Length(input.count))

var normalizedMagnitudes = [Float](count: input.count, repeatedValue: 0.0)
vDSP_vsmul(sqrt(magnitudes), 1, [2.0 / Float(input.count)], &normalizedMagnitudes, 1, vDSP_Length(input.count))
vDSP_destroy_fftsetup(weights)

return normalizedMagnitudes
}

再次强调,Swift代码是导致意外结果的代码。我做错了什么?


你没有展示出在Swift中读取wave文件的等效方法。你是否验证过(通过打印输出等方式)你在Swift fft中输入的数据和长度与你从Python的wave文件读取得到的数据类似? - hotpaw2
你如何解决这个问题? - Santos Ramón
我也使用Swift Surge库执行FFT,与仅使用MATLAB时得到的结果不同。 - SwiftMatt
1个回答

1

看起来您正在使用带有 Accelerate 框架的 Swift 浮点数组,但您可能需要使用 UnsafeMutablePointer<Float> 类型来分配您的向量,因为 Accelerate 框架是一个 Objective-C 框架。以下是一个示例。

public func sqrt(x: [Float]) -> [Float] {
    // convert swift array to C vector
    var temp = UnsafeMutablePointer<Float>.alloc(x.count)
    for (var i=0;i<x.count;i++) {
        temp[i] = x[i];
    }
    var count = UnsafeMutablePointer<Int32>.alloc(1)
    count[0] = Int32(x.count)
    vvsqrtf(temp, temp, count)
    // convert C vector to swift array
    var results = [Float](count: x.count, repeatedValue: 0.0)
    for (var i=0;i<x.count;i++) {
        results[i] = temp[i];
    }
    // Free memory
    count.dealloc(1)
    temp.dealloc(x.count)
    return results
}

为了提高性能,最好在整个代码中使用 UnsafeMutablePointer<Float> 类型来处理数据向量,而不是在函数调用中反复转换,就像我在这个示例中所做的那样。此外,您还应该保存您的 FFT 设置并重复使用以获得更好的性能。
由于您正在使用 vDSP FFT,您可能还会喜欢 vDSP_zvabs API,该 API 从 FFT 结果中计算出分贝值的幅度。
最后,请务必阅读有关加速框架 FFT API 的数据打包和缩放的链接。 https://developer.apple.com/library/mac/documentation/Performance/Conceptual/vDSP_Programming_Guide/UsingFourierTransforms/UsingFourierTransforms.html 为了提高性能,vDSP API 不输出最明显的比例值(因为您无疑会在其他地方对数据进行缩放),并且将一些额外的数据打包到一些 FFT 点中。

按照惯例,Swift在for循环后不使用括号。 - SwiftMatt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接