swiftvdspunsafemutablepointer

UnsafeMutablePointer<Float> returned by vDSP_fft_zip being overwritten immediately


In Swift 4 I'm creating a DSPSplitComplex to use in vDSP_fft_zip(), but it's being immediately overwritten the next time I create another DSPSplitComplex.

( DSPSplitComplex structure has 2 UnsafeMutablePointer<Float> )

//--Create the DSPSplitComplex    
var A = [Float](repeating:0, count:32); 
var B = [Float](repeating:0, count:32)
var splitComplex1 = DSPSplitComplex(realp: &A, imagp: &B)

//--Perform fft
log2Size = vDSP_Length(log2f(Float(32)))
setupFFT = vDSP_create_fftsetup(log2Size, FFTRadix(FFT_RADIX2))!;
vDSP_fft_zip(setupFFT, & splitComplex1, 1, log2Size, FFTDirection(FFT_FORWARD));

//--Create another DSPSplitComplex    
var C = [Float](repeating:0, count:32); 
var D = [Float](repeating:0, count:32)
var splitComplex2 = DSPSplitComplex(realp: &C, imagp: &D)

I can now see in the debugger that the UnsafeMutablePointer of splitComplex2.realp is the same address as splitComplex1.realp, and consequently anything I do with splitComplex2 overwrites splitComplex1

I guess the clue might be in the title 'unsafe', but if it's actually unusable, then what is the correct strategy for storing the contents of the returned DSPSplitComplex?

I guess creating new [Float] arrays from them is a way to make a permanent copy

let arrary = Array(UnsafeBufferPointer(start: splitComplex1.realp, count: 32))

...but it seems, despite reading the Swift docs, I'm still misunderstanding the point of UnsafeMutablePointer, as why would vDSP_fft_zip() return something that is unusable from the get-go?


Solution

  • Your way of creating DSPSplitComplex is wrong.

    When you pass Array<Float> to UnsafeMutablePointer<Float> using &, the address passed to the function is only valid within the function.

    So, in your code, two addresses passed DSPSplitComplex(realp: &A, imagp: &B) are not valid when the initializer of DSPSplitComplex finished.

    To avoid this sort of behavior, you can try something like this:

    var A = [Float](repeating:0, count:32)
    var B = [Float](repeating:0, count:32)
    A.withUnsafeMutableBufferPointer {aumbp in
        B.withUnsafeMutableBufferPointer {bumbp in
            var splitComplex1 = DSPSplitComplex(realp: aumbp.baseAddress!, imagp: bumbp.baseAddress!)
    
            //--Perform fft
            let log2Size = vDSP_Length(log2f(Float(32)))
            let setupFFT = vDSP_create_fftsetup(log2Size, FFTRadix(FFT_RADIX2))!
            vDSP_fft_zip(setupFFT, &splitComplex1, 1, log2Size, FFTDirection(FFT_FORWARD))
        }
    }
    

    Or else, you can allocate UnsafeMutableBufferPointer<Float>s and use them instead of Array<Float>.