Jer*_*hoy 3 fft ios accelerate-framework vdsp swift
我应该如何使用 Accelerate 框架在 iOS 上的 Swift 中计算真实信号的 FFT?
Apple 的 Accelerate 框架似乎提供了有效计算信号 FFT 的功能。
不幸的是,Internet 上可用的大多数示例(如Swift-FFT-Example和TempiFFT )在进行广泛测试并调用 Objective C API 时会崩溃。
在苹果文档回答了许多问题,但也导致了一些人(这是一块强制?为什么我需要这个调用转换?)。
很少有线程通过具体示例来解决 FFT 的各个方面。值得注意的是,在 Swift 中使用 Accelerate 的 FFT,Swift中的DFT 结果与 MATLAB和FFT 计算错误 - Swift 的结果不同。他们都没有直接解决“从 0 开始做这件事的正确方法是什么”的问题?
我花了一天时间才弄清楚如何正确地做到这一点,所以我希望这个帖子可以清楚地解释您应该如何使用 Apple 的 FFT,展示要避免的陷阱,并帮助开发人员节省宝贵的时间时间。
TL ; DR:如果您需要一个有效的实现来复制过去,这里是一个要点。
快速傅立叶变换是一种算法,它采用时域中的信号——在规则的、通常很小的时间间隔内进行的测量集合——并将其转换为表示到相位域中的信号(频率的集合) )。能够表达由变换丢失的信号随时间的变化(变换是可逆的,这意味着通过计算 FFT 不会丢失任何信息,您可以应用 IFFT 来获取原始信号),但我们能够区分信号包含的频率。这通常用于显示您在各种硬件和 YouTube 视频上收听的音乐的频谱图。
FFT 适用于复数。如果您不知道它们是什么,让我们假设它是半径和角度的组合。二维平面上的每个点有一个复数。实数(通常的浮点数)可以看作是一条线上的位置(左边为负,右边为正)。
Nb:FFT(FFT(FFT(FFT(X))) = X(最多一个常数,取决于您的 FFT 实现)。
通常,您要计算音频信号小窗口的 FFT。为了这个例子,我们将采用一个小的 1024 个样本窗口。您还希望使用 2 的幂,否则事情会变得更加困难。
var signal: [Float] // Array of length 1024
Run Code Online (Sandbox Code Playgroud)
首先,您需要为计算初始化一些常量。
// The length of the input
length = vDSP_Length(signal.count)
// The power of two of two times the length of the input.
// Do not forget this factor 2.
log2n = vDSP_Length(ceil(log2(Float(length * 2))))
// Create the instance of the FFT class which allow computing FFT of complex vector with length
// up to `length`.
fftSetup = vDSP.FFT(log2n: log2n, radix: .radix2, ofType: DSPSplitComplex.self)!
Run Code Online (Sandbox Code Playgroud)
按照苹果的文档,我们首先需要创建一个复杂的数组作为我们的输入。不要被教程误导。您通常想要的是将您的信号复制为输入的实部,并将复数部分保持为空。
// Input / Output arrays
var forwardInputReal = [Float](signal) // Copy the signal here
var forwardInputImag = [Float](repeating: 0, count: Int(length))
var forwardOutputReal = [Float](repeating: 0, count: Int(length))
var forwardOutputImag = [Float](repeating: 0, count: Int(length))
Run Code Online (Sandbox Code Playgroud)
请注意,FFT 函数不允许同时使用相同的 splitComplex 作为输入和输出。如果您遇到崩溃,这可能是原因。这就是我们定义输入和输出的原因。
现在,我们必须小心并“锁定”指向这四个数组的指针,如文档示例中所示。如果您只是将其&forwardInputReal用作 的参数DSPSplitComplex,则指针可能会在下一行失效,并且您可能会遇到应用程序偶发性崩溃的情况。
forwardInputReal.withUnsafeMutableBufferPointer { forwardInputRealPtr in
forwardInputImag.withUnsafeMutableBufferPointer { forwardInputImagPtr in
forwardOutputReal.withUnsafeMutableBufferPointer { forwardOutputRealPtr in
fforwardOutputImag.withUnsafeMutableBufferPointer { forwardOutputImagPtr in
// Input
let forwardInput = DSPSplitComplex(realp: forwardInputRealPtr.baseAddress!, imagp: forwardInputImagPtr.baseAddress!)
// Output
var forwardOutput = DSPSplitComplex(realp: forwardOutputRealPtr.baseAddress!, imagp: forwardOutputImagPtr.baseAddress!)
// FFT call goes here
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,压轴台词:呼叫您的 fft:
fftSetup.forward(input: forwardInput, output: &forwardOutput)
Run Code Online (Sandbox Code Playgroud)
您的 FFT 结果现在可以在forwardOutputReal和 中使用forwardOutputImag。
如果您只需要每个频率的幅度,而不关心实部和虚部,您可以在输入和输出旁边声明一个额外的数组:
var magnitudes = [Float](repeating: 0, count: Int(length))
Run Code Online (Sandbox Code Playgroud)
在您的 fft 计算每个“bin”的幅度之后立即添加:
vDSP.absolute(forwardOutput, result: &magnitudes)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1812 次 |
| 最近记录: |