我有一个迅速的功能,如下所示。存在一个引用此类实例中现有变量的循环。 (fftfilterbankReal是类中的数组)。但是,经过一遍之后,我在代码行“ for in in 0.”中遇到“索引超出范围”的错误。
在调试器中,似乎在该循环的第二次迭代中,“ self”下拉列表下没有变量。
如果我注释掉'vDSP_zvmul(&kernel !, 1,&fft1Input,1,&result,1,vDSP_Length(r.count),1)'行,则循环运行,我可以随时调试并直观地看到调试器中的自变量。
我想让这些变量消失的地方是什么?我已经读过内存分配之类的东西,并且我的类变量是使用'var'声明的,仅此而已,因为它默认情况下应迅速设为Strong。
func convolveInput(realsamples:[Float], imagsamples:[Float]) -> [Float]{
realResult = Array(repeating: [], count: filterbankReal.count)
imagResult = Array(repeating: [], count: filterbankReal.count)
let x = realsamples
let y = imagsamples
var N = x.count
var logN = 16
var fft1Setup = vDSP_create_fftsetup(UInt(logN), FFTRadix(FFT_RADIX2))!
var paddedLength = x.count + filterbankReal.count - 1
var halfPaddedLength = paddedLength/2
var halfKernelLength = kernelLength/2
//setup Complex Buffer 1
var reals = [Float]()
var imags = [Float]()
for i in 0..<x.count{
reals.append(x[i])
imags.append(y[i])
}
var complexBuffer1 = DSPSplitComplex(realp: UnsafeMutablePointer(mutating: reals), imagp: UnsafeMutablePointer(mutating: imags))
//Perform FFT on incoming samples
var re = [Float](repeating:0.0, count: N)
var im = [Float](repeating:0.0, count: N)
var fft1Input = DSPSplitComplex(realp: UnsafeMutablePointer(mutating: re), imagp: UnsafeMutablePointer(mutating: im))
var fftlength = 10
vDSP_fft_zop(fft1Setup, &(complexBuffer1), 1, &fft1Input, 1, UInt(fftlength), Int32(FFT_FORWARD))
//Remove DC from FFT Signal
re.remove(at: 0)
im.remove(at: 0)
for i in 0..<self.fftfilterbankReal.count {
var r:[Float] = self.fftfilterbankReal[i]
var im:[Float] = self.fftfilterbankImag[i]
var kernel:DSPSplitComplex? = DSPSplitComplex(realp: &r, imagp: &im)
var res:Float = 0
var ims:Float = 0
var result:DSPSplitComplex = DSPSplitComplex(realp: &res, imagp: &ims)
vDSP_zvmul(&kernel!, 1, &fft1Input, 1, &result, 1, vDSP_Length(r.count), 1)
self.realResult[i].append(res)
self.imagResult[i].append(ims)
}
答案 0 :(得分:1)
您的代码很好地展示了使用数组和指针时的不良用法。
例如:
var complexBuffer1 = DSPSplitComplex(realp: UnsafeMutablePointer(mutating: reals), imagp: UnsafeMutablePointer(mutating: imags))
或:
var kernel:DSPSplitComplex? = DSPSplitComplex(realp: &r, imagp: &im)
DSPSplitComplex
分别为实部和虚部保留两个指针,并且不复制内容。您不应为此类参数传递Swift数组。
代码中最关键的部分是...
var res:Float = 0
var ims:Float = 0
var result:DSPSplitComplex = DSPSplitComplex(realp: &res, imagp: &ims)
vDSP_zvmul(&kernel!, 1, &fft1Input, 1, &result, 1, vDSP_Length(r.count), 1)
vDSP_zvmul
生成N
(在您的代码N
= vDSP_Length(r.count)
中)复数,因此您需要准备一个可以容纳N
元素的区域。
使用当前代码调用vDSP_zvmul
后,您会破坏整个堆栈内容,这会导致您遇到以下问题:
在调试器中,似乎在该循环的第二次迭代中, 在“自我”下拉菜单下没有任何变量。
您隐藏了代码的许多部分,因此很难猜测您真正想要做什么,但是如果我以更安全的方式重新编写代码,则可能是这样的:
func convolveInput(realsamples:[Float], imagsamples:[Float]) -> [Float]{
realResult = Array(repeating: [], count: filterbankReal.count)
imagResult = Array(repeating: [], count: filterbankReal.count)
let x = realsamples
let y = imagsamples
var N = x.count
var logN = 16
var fft1Setup = vDSP_create_fftsetup(UInt(logN), FFTRadix(FFT_RADIX2))!
var paddedLength = x.count + filterbankReal.count - 1
var halfPaddedLength = paddedLength/2
var halfKernelLength = kernelLength/2
//setup Complex Buffer 1
var reals = UnsafeMutableBufferPointer<Float>.allocate(capacity: x.count)
defer {reals.deallocate()}
var imags = UnsafeMutableBufferPointer<Float>.allocate(capacity: y.count)
defer {imags.deallocate()}
_ = reals.initialize(from: x)
_ = imags.initialize(from: y)
var complexBuffer1 = DSPSplitComplex(realp: reals.baseAddress!, imagp: imags.baseAddress!)
//Perform FFT on incoming samples
var re = UnsafeMutableBufferPointer<Float>.allocate(capacity: N)
defer {re.deallocate()}
var im = UnsafeMutableBufferPointer<Float>.allocate(capacity: N)
defer {im.deallocate()}
var fft1Input = DSPSplitComplex(realp: re.baseAddress!, imagp: im.baseAddress!)
let fftlength = 10
vDSP_fft_zop(fft1Setup, &complexBuffer1, 1, &fft1Input, 1, UInt(fftlength), Int32(FFT_FORWARD))
//Remove DC from FFT Signal
fft1Input = DSPSplitComplex(realp: re.baseAddress!+1, imagp: im.baseAddress!+1)
for i in 0..<self.fftfilterbankReal.count {
self.fftfilterbankReal[i].withUnsafeMutableBufferPointer {rBuf in
self.fftfilterbankImag[i].withUnsafeMutableBufferPointer {imBuf in
var kernel = DSPSplitComplex(realp: rBuf.baseAddress!, imagp: imBuf.baseAddress!)
var res = UnsafeMutableBufferPointer<Float>.allocate(capacity: rBuf.count)
defer {res.deallocate()}
var ims = UnsafeMutableBufferPointer<Float>.allocate(capacity: rBuf.count)
defer {ims.deallocate()}
var result:DSPSplitComplex = DSPSplitComplex(realp: res.baseAddress!, imagp: ims.baseAddress!)
vDSP_zvmul(&kernel, 1, &fft1Input, 1, &result, 1, vDSP_Length(rBuf.count), 1)
//vDSP_zvmul generates `N` complex numbers,
// I do not understand what you really want to do...
self.realResult[i].append(res[0])
self.imagResult[i].append(ims[0])
}
}
}
//...
}
可能还有其他部分需要修复,但是无论如何,请尝试看看会得到什么。
答案 1 :(得分:0)
前言:男孩,筛选所有这些花了我将近2个小时,虽然还算不上完美,但是男孩比这更好了。希望对您有所帮助!
由于Accelerate API是不适合利用Swift功能的C API,因此您的代码遭受了巨大损失。如果您为Accelerate API做一些漂亮的包装,您将获得更具可读性的代码,该包装使您可以将所有“丑陋的东西”塞进很少见或无需编辑的角落。
我通过创建一个新类型ComplexFloatArray
来实现这一点,该类型类似于DSPSplitComplex
,但实际上封装并拥有其缓冲区。这样可以防止DSPSplitComplex
容易受到影响的悬空缓冲区。
使用ComplexFloatArray
类型后,是时候为您使用的Accelerate函数定义一些包装器了。在这种情况下,vDSP_zvmul
和vDSP_fft_zop
。由于C没有元组,因此从C函数返回多个值通常需要使用out-parameters,这些参数在Accelerate框架中普遍使用。我们可以将它们重新设计为具有常规返回类型的Swift函数。这些API非常自然地表示为可在ComplexFloatArray
上运行的实例方法,因此我们将它们放在此处。
此外,您的代码由于依赖于外部状态而变得更加复杂。卷积是一个函数,除了输入(通过参数,而 not 通过实例变量)并返回结果(通过返回值,而 not ),它除了执行输入操作外没有其他原因>通过实例变量)。
import Accelerate
class ComplexFloatArray {
var reals: [Float]
var imaginaries: [Float]
init(reals: [Float], imaginaries: [Float]) {
self.reals = reals
self.imaginaries = imaginaries
}
}
extension ComplexFloatArray { // Core features
var count: Int {
assert(reals.count == imaginaries.count)
return reals.count
}
static let stride = 1
func append(real: Float, imaginary: Float) {
self.reals.append(real)
self.imaginaries.append(imaginary)
}
func useAsDSPSplitComplex<R>(_ closure: (inout DSPSplitComplex) -> R) -> R {
return reals.withUnsafeMutableBufferPointer { realBufferPointer in
return imaginaries.withUnsafeMutableBufferPointer { imaginaryBufferPointer in
var dspSplitComplex = DSPSplitComplex(realp: realBufferPointer.baseAddress!, imagp: imaginaryBufferPointer.baseAddress!)
return closure(&dspSplitComplex)
}
}
}
}
extension ComplexFloatArray { // Convenience utilities
convenience init() {
self.init(reals: [], imaginaries: [])
}
static func zeros(count: Int) -> ComplexFloatArray {
return ComplexFloatArray(reals: Array(repeating: 0, count: count), imaginaries:Array(repeating: 0, count: count))
}
}
extension ComplexFloatArray { // Vector multiplciation extensions
enum ComplexMultiplicationType: Int32 { case normal = 1, conjugate = -1 }
func complexMultiply(
with other: ComplexFloatArray,
multiplicationType: ComplexMultiplicationType = .normal
) -> ComplexFloatArray {
assert(self.count == other.count, "Multiplied vectors must have the same size!")
let result = ComplexFloatArray.zeros(count: self.count)
self.useAsDSPSplitComplex { selfPointer in
other.useAsDSPSplitComplex { otherPointer in
result.useAsDSPSplitComplex { resultPointer in
vDSP_zvmul(
&selfPointer, ComplexFloatArray.stride,
&otherPointer, ComplexFloatArray.stride,
&resultPointer, ComplexFloatArray.stride, vDSP_Length(result.count),
multiplicationType.rawValue)
}
}
}
return result
}
}
extension ComplexFloatArray { // FFT extensions
enum FourierTransformDirection: Int32 { case forward = 1, inverse = -1 }
//TODO: name log2n label better
func outOfPlaceComplexFourierTransform(
setup: FFTSetup,
resultSize: Int,
log2n: UInt,
direction: FourierTransformDirection
) -> ComplexFloatArray {
let result = ComplexFloatArray.zeros(count: resultSize)
self.useAsDSPSplitComplex { selfPointer in
result.useAsDSPSplitComplex{ resultPointer in
vDSP_fft_zop(
setup,
&selfPointer, ComplexFloatArray.stride,
&resultPointer, ComplexFloatArray.stride,
log2n,
direction.rawValue
)
}
}
return result
}
}
extension FFTSetup {
enum FourierTransformRadix: Int32 {
case radix2 = 0, radix3 = 1, radix5 = 2
// Static let constants are only initialized once
// This function's intent to to make sure this enum stays in sync with the raw constants the Accelerate framework uses
static let assertRawValuesAreCorrect: Void = {
func assertRawValue(for actual: FourierTransformRadix, isEqualTo expected: Int) {
assert(actual.rawValue == expected, "\(actual) has a rawValue of \(actual.rawValue), but expected \(expected).")
}
assertRawValue(for: .radix2, isEqualTo: kFFTRadix2)
assertRawValue(for: .radix3, isEqualTo: kFFTRadix3)
assertRawValue(for: .radix5, isEqualTo: kFFTRadix5)
}()
}
init(log2n: Int, _ radix: FourierTransformRadix) {
_ = FourierTransformRadix.assertRawValuesAreCorrect
guard let setup = vDSP_create_fftsetup(vDSP_Length(log2n), FFTRadix(radix.rawValue)) else {
fatalError("vDSP_create_fftsetup(\(log2n), \(radix)) returned nil")
}
self = setup
}
}
struct NameMe {
// I don't know what this is, but if it can somehow be removed,
// the whole convolveInput method could be moved into an extension on ComplexFloatArray.
var fftFilterBank: [ComplexFloatArray]
func convolve(samples: ComplexFloatArray) -> [ComplexFloatArray] {
// TODO: rework reimplement this code to remove the DC from samples, and add it back in
// //Remove DC from FFT Signal
// re.remove(at: 0)
// im.remove(at: 0)
let fftlength: UInt = 10 // Todo: what is this, exactly?
let fft1Input = samples.outOfPlaceComplexFourierTransform( // Rename me to something better
setup: FFTSetup(log2n: 16, .radix2),
resultSize: samples.count,
log2n: fftlength,
direction: .forward
)
return self.fftFilterBank.map { kernel in kernel.complexMultiply(with: fft1Input) }
}
// Stub for compatibility with the old API. Deprecate it and move to the
// `convolve(samples: ComplexFloatArray) -> [ComplexFloatArray]` as soon as possible.
func convolveInput(realsamples: [Float], imagsamples: [Float]) -> [ComplexFloatArray] {
return self.convolve(samples: ComplexFloatArray(reals: realsamples, imaginaries: imagsamples))
}
}
x
,y
,re
,im
之类的名称几乎是无用的沟通能力,几乎应该完全避免。数组是具有“写时复制”的值类型。您只需将它们分配给一个新变量即可复制它们,因此代码如下:
var reals = [Float]()
var imags = [Float]()
for i in 0..<x.count{
reals.append(x[i])
imags.append(y[i])
}
既慢,又在视觉上很麻烦。这可能很简单:let (reals, imags) = (x, y)
。但是同样,这些副本不是必需的(x
和y
也是如此)。删除它们,然后直接使用realsamples
和imagsamples
。
当您发现自己经常将多个数据传递在一起时,这非常有力地表明您应该定义一个新的聚合类型来包装它们。例如,如果要传递两个Array<Float>
来表示复数向量,则应定义一个ComplexVector
类型。这可以让您强制执行不变式(例如,实数值总是与虚值一样多),并添加方便的操作(例如func append(real: Float, imaginary: Float)
,该操作可以同时对这两个数值进行操作,从而确保您永远不会忘记附加到其中一个)数组)。
这里发生了很多事情,因此我无法提前解决每个问题并提前进行解释。我鼓励您花一些时间,通读此书,然后 随时问我任何后续问题。
我怀疑我在重构过程中犯了错误(因为我没有可使用的测试用例),但是该代码具有足够的模块化特性,因此应该很容易隔离,修复和错误。