ios - 如何快速使用苹果的 Accelerate 框架来计算真实信号的 FFT?

标签 ios swift fft accelerate-framework vdsp

我应该如何使用 Accelerate 框架在 iOS 上的 Swift 中计算真实信号的 FFT?

网络上的可用示例

Apple 的 Accelerate 框架似乎提供了有效计算信号 FFT 的功能。

不幸的是,互联网上可用的大多数示例,例如 Swift-FFT-ExampleTempiFFT , 如果广泛测试并调用 Objective C API 会崩溃。

Apple documentation回答了许多问题,但也引出了其他一些问题(这篇文章是强制性的吗?为什么我需要这个电话来转换?)。

Stack Overflow 上的线程

很少有线程通过具体示例来解决 FFT 的各个方面。值得注意的是FFT Using Accelerate In Swift , DFT result in Swift is different than that of MATLABFFT Calculating incorrectly - Swift .
他们都没有直接解决“从 0 开始做这件事的正确方法是什么”的问题?

我花了一天时间才弄清楚如何正确地做到这一点,所以我希望这个帖子可以清楚地解释您应该如何使用 Apple 的 FFT,展示要避免的陷阱,并帮助开发人员节省宝贵的时间时间。

最佳答案

TL ; DR :如果您需要一个有效的实现来复制过去 here is a gist .

什么是FFT?

快速傅立叶变换是一种算法,它采用时域中的信号——在规则的、通常很小的时间间隔内进行的测量集合——并将其转换为表示到相位域中的信号(频率的集合) )。
能够表达由变换丢失的信号随时间的变化(变换是可逆的,这意味着通过计算 FFT 不会丢失任何信息,您可以应用 IFFT 来获取原始信号),但我们能够区分信号包含的频率。这通常用于显示您在各种硬件和 YouTube 视频上收听的音乐的频谱图。

FFT 适用于 complexe numbers .如果您不知道它们是什么,让我们假设它是半径和角度的组合。二维平面上的每个点有一个复数。实数(通常的浮点数)可以看作是一条线上的一个位置(左边是负数,右边是正数)。

注意:FFT(FFT(FFT(FFT(X))) = X(最多一个常数,取决于您的 FFT 实现)。

如何计算真实信号的 FFT。

通常您想计算音频信号的小窗口的 F​​FT。为了这个例子,我们将采用一个小的 1024 个样本窗口。您还希望使用 2 的幂,否则事情会变得更加困难。

var signal: [Float] // Array of length 1024

首先,您需要为计算初始化一些常量。

// 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)!

按照苹果的文档,我们首先需要创建一个复杂的数组作为我们的输入。
不要被教程误导。您通常想要的是将您的信号复制为输入的实部,并将复数部分保持为空。

// 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))

注意,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
          }
        }
      }
    }

现在,压轴台词:调用您的 fft:

fftSetup.forward(input: forwardInput, output: &forwardOutput)

您的 FFT 结果现在可在 forwardOutputReal 中获得和 forwardOutputImag .

如果你只想要每个频率的幅度,而不关心实部和虚部,你可以在输入和输出旁边声明一个额外的数组:

var magnitudes = [Float](repeating: 0, count: Int(length))

在您的 fft 计算每个“bin”的幅度之后立即添加:

vDSP.absolute(forwardOutput, result: &magnitudes)

关于ios - 如何快速使用苹果的 Accelerate 框架来计算真实信号的 FFT?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60120842/

相关文章:

android - 您可以将应用作为应用内购买来销售吗?

iOS:循环依赖调用其他类中的方法

ios - .ondelete 带有部分的 SwiftUI 列表

ios - Swift 数组未附加到

ios - 如何在 textView 滚动期间隐藏 UIButton

python - 从音频脉冲中获取二进制数据

iphone - 在 EKEventStore 中检索具有特定标题的 EKEvent

ios - 使用 AWS ios SDK 在 S3 上进行 MultipartUpload 上传

java - 如何用梳状滤波器检测基频?

c++ - 将 FMA 指令用于 FFT 算法