Search code examples
swift3core-audiounsafemutablepointer

UnsafeMutableBufferPointer slow when using getter


I have an AVAudioEngine with a custom AURenderCallback. When I run the engine, the cpu spikes up and it seems to have to do with the UnsafeMutableBufferPointer.getter and .setter actions:

func performRender(ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>, inTimeStamp: UnsafePointer<AudioTimeStamp>, inBusNumber: UInt32, inNumberFrames: UInt32, ioData: UnsafeMutablePointer<AudioBufferList>?) -> OSStatus {

    let blI = UnsafeMutableAudioBufferListPointer(ioData)

    let numSamples = Int((blI?[0].mDataByteSize)! / UInt32(MemoryLayout<Float32>.stride))

    for input in 0 ..< blI!.count{

        let bI = blI?[input]

        guard let inputData = bI?.mData else {

            //assert(false)

            return kAudioUnitErr_Uninitialized
        }

        let samplesBI = UnsafeMutablePointer<Float32>(inputData.assumingMemoryBound(to: Float32.self))

        let samplesI = UnsafeMutableBufferPointer<Float32>(start: samplesBI, count: numSamples)

        for sampleIndex in 0 ..< samplesI.count {

            samplesI[sampleIndex] *= 0.5
        }

    }

    return noErr
}

Instruments Debug Session

What can cause this inefficient behavior of getting and setting the pointer data? Because it is working on audio samples, the inefficiency results in stutters in the audio signal.


Solution

  • You can skip the UnsafeMutableBufferPointer() call, and index the sample pointer directly:

    let bufferListPtr = UnsafeMutableAudioBufferListPointer(ioData)
    ...
    let mBuffer : AudioBuffer = bufferListPtr[0]
    let count = Int(mBuffer.mDataByteSize) / yourSampleSizeInBytes
    let dataPointer = UnsafeMutableRawPointer(mBuffer.mData)
    if let dptr = dataPointer {
        let sampleArray = dptr.assumingMemoryBound(to: yourSampleType.self)
        for i in 0..<(count) {
            let x = sampleArray[i]
            let y = myModifySample( x )   
            sampleArray[i] =  y 
        }
    }
    

    I use Int16 for mySampleType (size = 2 bytes), but it should work for 4 byte type Float as well.

    Using the raw memory pointer directly might avoid some buffer getter/setter inefficiencies (and also validation, so be careful !).