Ios 如何使用苹果&x27;s在swift中加速框架,以便计算实际信号的FFT?

Ios 如何使用苹果&x27;s在swift中加速框架,以便计算实际信号的FFT?,ios,swift,fft,accelerate-framework,vdsp,Ios,Swift,Fft,Accelerate Framework,Vdsp,我应该如何在iOS上使用Accelerate框架计算Swift中真实信号的FFT 网络上的可用示例 苹果的加速框架似乎提供了有效计算信号FFT的功能 不幸的是,互联网上的大多数示例,如和,如果经过广泛测试并调用Objective C API,就会崩溃 答案很多,但也引出了其他一些问题(这篇文章是强制性的吗?为什么我需要这个电话来转换?) 堆栈溢出上的线程 很少有线程用具体的例子来处理FFT的各个方面。值得注意的是,以及。 它们都没有直接解决“从0开始,正确的方法是什么”的问题 我花了一天的时间才

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

网络上的可用示例 苹果的加速框架似乎提供了有效计算信号FFT的功能

不幸的是,互联网上的大多数示例,如和,如果经过广泛测试并调用Objective C API,就会崩溃

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

堆栈溢出上的线程 很少有线程用具体的例子来处理FFT的各个方面。值得注意的是,以及。 它们都没有直接解决“从0开始,正确的方法是什么”的问题


我花了一天的时间才弄明白如何正确地使用它,所以我希望这篇文章能够清楚地解释你应该如何使用苹果的FFT,说明应该避免哪些陷阱,并帮助开发人员节省宝贵的时间。

TL;DR:如果您需要一个工作实现来复制过去

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

快速傅立叶变换(FFT)的工作原理是。如果你不知道它们是什么,让我们假设它是半径和角度的组合。二维平面上的每个点都有一个复数。实数(通常的浮点数)可以看作是一条线上的一个位置(左边是负数,右边是正数)

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

如何计算实际信号的FFT。 通常,您需要计算音频信号的一个小窗口的FFT。为了便于示例,我们将使用一个小的1024个采样窗口。您还希望使用2的幂,否则事情会变得有点困难

var信号:[Float]//长度为1024的数组
首先,需要为计算初始化一些常量

//输入的长度
长度=vDSP_长度(信号计数)
//输入长度的两倍中的两倍的功率。
//不要忘记这个因素2。
log2n=vDSP_长度(ceil(log2(Float(长度*2)))
//创建FFT类的实例,该类允许计算具有长度的复向量的FFT
//最长为“长度”。
fftSetup=vDSP.FFT(log2n:log2n,基数:.radix2,类型:DSPSplitComplex.self)!
根据苹果的文档,我们首先需要创建一个复杂的数组,作为我们的输入。 不要被教程误导。你通常想要的是将信号复制为输入的真实部分,并将复杂部分保持为空

//输入/输出数组
var forwardInputReal=[Float](信号)//在此处复制信号
var forwardInputImag=[Float](重复:0,计数:Int(长度))
var forwardOutputReal=[Float](重复:0,计数:Int(长度))
var forwardOutputImag=[Float](重复:0,计数:Int(长度))
请注意,FFT函数不允许同时使用与输入和输出相同的splitComplex。如果遇到崩溃,这可能是原因。这就是我们同时定义输入和输出的原因

现在,我们必须小心并“锁定”指向这四个数组的指针,如文档示例所示。如果您只是简单地使用
&forwardInputReal
作为
DSPSplitComplex
的参数,指针可能会在下一行失效,您的应用程序可能会偶尔崩溃

forwardInputReal.WithUnsafemtableBufferPointer{forwardInputRealPtr in
ForwardInputMag.WithUnsafemtableBufferPointer{ForwardInputInputMagPtr in
forwardOutputReal.WithUnsafemtableBufferPointer{forwardOutputRealPtr in
fforwardOutputImag.WithUnsafemtableBufferPointer{forwardOutputImagPtr in
//输入
让forwardInput=DSPSplitComplex(realp:forwardInputRealPtr.baseAddress!,imagp:ForwardInputMagPtr.baseAddress!)
//输出
var forwardOutput=DSPSplitComplex(realp:forwardOutputRealPtr.baseAddress!,imagp:forwardOutputImagPtr.baseAddress!)
//FFT呼叫在这里
}
}
}
}
现在,最后一行:对fft的调用:

fftSetup.forward(输入:forwardInput,输出:&forwardOutput)
FFT的结果现在可以在
forwardOutputReal
forwardOutputImag
中找到

如果只需要每个频率的振幅,而不关心实部和虚部,则可以在输入和输出旁边声明一个附加数组:

var大小=[Float](重复:0,计数:Int(长度))
在fft计算每个“仓”的振幅后立即添加:

vDSP.absolute(正向输出、结果和大小)

取决于您是否需要FFT“快速”。如果您需要性能(最大触发器或最小延迟),那么方法是手动管理内存(分配和释放不安全的可变指针)并使用C API。这将允许使用相同的原始内存进行FFT输入和输出,从而减少CPU和内存层次结构上的第一级和第二级数据缓存溢出和未命中。也可能减少内存副本。您能否详细说明如何做