Это:
var forwardInput = DSPSplitComplex(realp: &forwardInputReal,
imagp: &forwardInputImag)
vDSP_ctoz(observed, 2, &forwardInput, 1, vDSP_Length(halfN))
делает не то, что вы хотите. Проблема с этим немного тонкая, особенно если вы пришли из C или C ++ фона. Массивы в swift не похожи на массивы в C или C ++;в частности, они не имеют фиксированных адресов в памяти. Это объекты, которые Свифт может выбрать для перемещения. Это хорошо, когда вы работаете в Swift, но иногда вызывает боль, когда вам нужно взаимодействовать с функциями C (и особенно с типами C, которые хотят сохранять указатели на вызовы функций, как вы заметили).
Когда вы вызываете DSPSplitComplex(realp: &forwardInputReal, ...)
, &
неявно создает UnsafeMutablePointer<Float>
в памяти forwardInputReal
, но , этот указатель действителен только во время вызова init
. Когда вы передаете forwardInput
в vDSP_ctoz
, указатель уже вышел из области видимости и больше не действителен, поэтому вы вызываете неопределенное поведение. В частности, компилятор может предположить, что вызов 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.
Есть несколько вещей, которые могли бы сделать это проще.
Во-первых, в компилятор Swift приходит изменение , который будет диагностировать эту ошибку для вас.
Во-вторых, мы можем обернуть маленький танец, который я показал, в некоторые вспомогательные функции:
/// 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))
}
}
}
}
С этими определенными вспомогательными функциями вы можете просто сделать:
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)
Я поговорю с командой vDSP и посмотрю, сможем ли мы добавить что-то подобное в Framework для будущего выпуска, поэтомувам не нужно писать это самостоятельно.