Ios 苹果FFT给出了不一致的结果
我使用本地Apple类实现了FFT算法。我直接从他们的网站上删除了代码: 尽管如此,当我运行代码时,每次都会提供不同的结果。我创建了一个单元测试,它反复运行,并在单元测试失败时比较结果是否相同。我唯一的猜测是这是一个记忆问题。这是我唯一能想象每次结果都不同的方法Ios 苹果FFT给出了不一致的结果,ios,swift,xcode,fft,Ios,Swift,Xcode,Fft,我使用本地Apple类实现了FFT算法。我直接从他们的网站上删除了代码: 尽管如此,当我运行代码时,每次都会提供不同的结果。我创建了一个单元测试,它反复运行,并在单元测试失败时比较结果是否相同。我唯一的猜测是这是一个记忆问题。这是我唯一能想象每次结果都不同的方法 import Foundation import Accelerate class AppleFFT{ var windowSize = 512 var n = vDSP_Length(512) var ha
import Foundation
import Accelerate
class AppleFFT{
var windowSize = 512
var n = vDSP_Length(512)
var halfN = Int(512 / 2)
var fftSetUp : FFTSetup?
var log2n : vDSP_Length?
init(windowSize: Int){
self.windowSize = windowSize
n = vDSP_Length(windowSize)
halfN = Int(n / 2)
initialize()
}
private init(){
initialize()
}
func initialize(){
log2n = vDSP_Length(log2(Float(n)))
if log2n == nil { return }
fftSetUp = vDSP_create_fftsetup(log2n!, FFTRadix(kFFTRadix2))
}
func process(signal : [Float], n: vDSP_Length) ->DSPSplitComplex{
let window = vDSP.window(ofType: Float.self,
usingSequence: .hanningDenormalized,
count: Int(n),
isHalfWindow: false)
let signal2 = vDSP.multiply(signal, window)
let observed: [DSPComplex] = stride(from: 0, to: Int(n), by: 2).map {
return DSPComplex(real: signal[$0],
imag: signal[$0.advanced(by: 1)])
}
var forwardInputReal = [Float](repeating: 0, count: halfN)
var forwardInputImag = [Float](repeating: 0, count: halfN)
var forwardInput = DSPSplitComplex(realp: &forwardInputReal,
imagp: &forwardInputImag)
vDSP_ctoz(observed, 2,
&forwardInput, 1,
vDSP_Length(halfN))
//Create some empty arrays we can put data into
var forwardOutputReal = [Float](repeating: 0, count: halfN)
var forwardOutputImag = [Float](repeating: 0, count: halfN)
var forwardOutput = DSPSplitComplex(realp: &forwardOutputReal,
imagp: &forwardOutputImag)
//Perform actual fft, placing results in forwardOutput
vDSP_fft_zrop(fftSetUp!,
&forwardInput, 1,
&forwardOutput, 1,
log2n!,
FFTDirection(kFFTDirection_Forward))
//Do cheap analysis to figure out original frequencies
let componentFrequencies = forwardOutputImag.enumerated().filter {
$0.element < -1
}.map {
return $0.offset
}
return forwardOutput
}
}
import XCTest
import Accelerate
class testAppleFFT: XCTestCase {
func testFFTConsistency(){
let signal = genSignalWith(frequencies:[100, 500], numSamples: 512, sampleRate: 44100)
let fft = AppleFFT(windowSize: 512)
let complex1 = fft.process(signal: signal , n: 512)
for i in 0..<10{
print("i = \(i)")
let complex2 = fft.process(signal: signal, n: 512)
var complex1realp = complex1.realp
var complex1imagp = complex1.imagp
var complex2realp = complex2.realp
var complex2imagp = complex2.imagp
for j in 0..<512 {
let r1 = complex1realp.pointee
let i1 = complex1imagp.pointee
let r2 = complex2realp.pointee
let i2 = complex2imagp.pointee
XCTAssert(abs(r1 - r2) < 0.00001)
XCTAssert(abs(i1 - i2) < 0.00001)
if !(abs(r1 - r2) < 0.00001){
print(" error: i: \(i) j: \(j) r1: \(r1) r2: \(r2)")
}
if !(abs(i1 - i2) < 0.00001){
print(" error: index: \(i) i1: \(i1) i2: \(i2)")
}
complex1realp = complex1realp.advanced(by: 1)
complex1imagp = complex1imagp.advanced(by: 1)
complex2realp = complex2realp.advanced(by: 1)
complex2imagp = complex2imagp.advanced(by: 1)
}
}
}
func genSignalWith(frequencies: [Float], numSamples: Int, sampleRate: Float, amplitudes: [Float] = []) -> [Float]{
var sig : [Float] = []
for t in 0..<numSamples{
var sum : Float = 0.0
for i in 0..<frequencies.count{
let f = frequencies[i]
var a : Float = 1.0
if(amplitudes.count > i){
a = amplitudes[i]
}
let thisValue = sin(Float(t) / sampleRate * 2 * .pi * f)
sum += thisValue
}
sig.append(sum)
}
return sig
}
}
<代码>导入基础
进口加速
类AppleFFT{
var windowSize=512
var n=vDSP_长度(512)
var halfN=Int(512/2)
变量fftSetUp:fftSetUp?
变量log2n:vDSP_长度?
初始化(窗口大小:Int){
self.windowSize=windowSize
n=vDSP_长度(窗口大小)
halfN=Int(n/2)
初始化()
}
私有init(){
初始化()
}
func初始化(){
log2n=vDSP_长度(log2(Float(n)))
如果log2n==nil{return}
fftSetUp=vDSP_create_fftSetUp(log2n!,FFTRadix(kFFTRadix2))
}
func进程(信号:[浮点],n:vDSP_长度)->DSPSplitComplex{
让window=vDSP.window(类型:Float.self,
使用序列:。汉宁非规范化,
计数:Int(n),
Ishalf(窗口:false)
让信号2=vDSP.乘法(信号,窗口)
让我们观察:[DSPComplex]=步幅(从:0到:Int(n),由:2)。map{
返回DSPComplex(实数:信号[$0],
图像:信号[$0.高级(由:1)])
}
var forwardInputReal=[Float](重复:0,计数:halfN)
var forwardInputImag=[Float](重复:0,计数:halfN)
var forwardInput=DSPSplitComplex(realp:&forwardInputReal,
imagp:&ForwardInputMag)
vDSP_ctoz(观察到,2,
&前向输入,1,
vDSP_长度(半个)
//创建一些可以将数据放入的空数组
var forwardOutputReal=[Float](重复:0,计数:halfN)
var forwardOutputImag=[Float](重复:0,计数:halfN)
var forwardOutput=DSPSplitComplex(realp:&forwardOutputReal,
imagp:&forwardOutputImag)
//执行实际fft,将结果放入输出
vDSP_fft_zrop(fft设置!,
&前向输入,1,
&前向输出,1,
log2n!,
FFT方向(KFFT方向(U前进))
//进行廉价的分析,找出原始频率
让componentFrequencies=forwardOutputImag.enumerated().filter{
$0.5元<-1
}.地图{
返回$0.5抵销
}
返回转发输出
}
}
导入测试
进口加速
类testAppleFFT:xTestCase{
func testFFTConsistency(){
let signal=genSignalWith(频率:[100500],采样数:512,采样率:44100)
设fft=AppleFFT(窗口大小:512)
设complex1=fft.process(信号:信号,n:512)
对于0..中的i:
不做你想做的事情。它的问题有点微妙,尤其是如果你来自C或C++背景。SWIFT中的数组不像C或C++中的数组;特别是它们在内存中没有固定地址。它们是SWIFT可以选择移动的对象。在SWIFT中工作时,这是很好的,但是有时。当您需要与C函数交互时(特别是您已经注意到,希望在函数调用之间持久化指针的C类型),es会带来麻烦
调用DSPSplitComplex时(realp:&forwardInputReal,…)
,&
隐式创建一个指向forwardInputReal
内存的不可分配指针
,但该指针仅在调用init
期间有效。当您将forwardInput
传递给vDSP_ctoz
时,指针已超出范围,不再有效d、 因此,您正在调用未定义的行为。特别是,编译器可以假定对vDSP_ctoz
的调用不会修改forwardInputReal
或forwardInputImag
的内容,因为函数没有接收到指向其内容的有效指针
解决这一问题的最佳方法是更加明确:
forwardInputReal.withUnsafeMutableBufferPointer { r in
forwardInputImag.withUnsafeMutableBufferPointer { i in
var splitComplex = DSPSplitComplex(realp: r.baseAddress!, imagp: i.baseAddress!)
vDSP_ctoz(observed, 2, &splitComplex, 1, vDSP_Length(halfN))
}
}
// forwardInput[Real,Imag] now contain the de-interleaved data.
// splitComplex is out-of-scope and cannot be used, so the invalid pointers
// are discarded.
有几件事可以让这更容易
首先,有一个诊断程序将为您诊断此错误
其次,我们可以将我展示的小舞蹈包装成一些方便的功能:
/// De-interleave the real and imaginary parts of a complex buffer into two
/// new Float arrays.
func ctoz<T>(_ data: T) -> (real: [Float], imag: [Float])
where T: AccelerateBuffer, T.Element == DSPComplex {
var imag = [Float]()
let real = [Float](unsafeUninitializedCapacity: data.count) { r, n in
imag = [Float](unsafeUninitializedCapacity: data.count) { i, n in
ctoz(data, real: &r, imag: &i)
n = data.count
}
n = data.count
}
return (real, imag)
}
/// De-interleave the real and imaginary parts of a complex buffer into two
/// caller-provided Float buffers.
///
/// - Precondition: data, real, and imag must all have the same length.
func ctoz<T, U, V>(_ data: T, real: inout U, imag: inout V)
where T: AccelerateBuffer, T.Element == DSPComplex,
U: AccelerateMutableBuffer, U.Element == Float,
V: AccelerateMutableBuffer, V.Element == Float
{
precondition(data.count == real.count && data.count == imag.count)
real.withUnsafeMutableBufferPointer { r in
imag.withUnsafeMutableBufferPointer { i in
var split = DSPSplitComplex(realp: r.baseAddress!, imagp: i.baseAddress!)
data.withUnsafeBufferPointer { d in
vDSP_ctoz(d.baseAddress!, 2, &split, 1, vDSP_Length(data.count))
}
}
}
}
甚至:
let (forwardInputReal, forwardInputImag) = ctoz(data)
我将与vDSP团队讨论,看看我们是否能在未来版本的框架中添加类似的内容,这样您就不必自己编写了。是的,您可能正在阅读未初始化的内存。请尝试使用启用了AddressSanitarizer的工具进行编译:几年前的这篇文章有点过时,但对于本规范来说是一个很好的参考国际货币基金组织问题:
var forwardInputReal = [Float](repeating: 0, count: halfN)
var forwardInputImag = [Float](repeating: 0, count: halfN)
ctoz(observed, real: &forwardInputReal, imag: &forwardInputImag)
let (forwardInputReal, forwardInputImag) = ctoz(data)