CoreAudio callback and AudioBufferList references

Thanks to some help from here I have setup code for using an AudioConverter. I have setup a Playground to test further. I can do


var _buffer:Array<Float> = [Float](count: Int(numOutputPackets), repeatedValue: 0)
    var audioBufferList = AudioBufferList(
        mNumberBuffers: 1,
        mBuffers: AudioBuffer(
            mNumberChannels: 1,
            mDataByteSize: givenSize,
            mData: &_buffer
        )
    )
withUnsafeMutablePointers(&soundData, &audioBufferList, &_buffer) { soundPointer, audioBufferPtr, bufferPtr in
        err = AudioConverterFillComplexBuffer(converter, fillComplexCallback, soundPointer , &numOutputPackets, audioBufferPtr, nil)
}


where soundData is a float array populated with audio samples. The audiosamples are generated and the soundData array is filled as epxected. in my callback function defined as


func fillComplexCallback(myConverter: AudioConverterRef, packetNumber: UnsafeMutablePointer<UInt32>, ioData: UnsafeMutablePointer<AudioBufferList>, aspd: UnsafeMutablePointer<UnsafeMutablePointer<AudioStreamPacketDescription>>, userInfo: UnsafeMutablePointer<Void>) -> OSStatus


I can access the soundData object like

var soundData:Array<Float> = UnsafeMutablePointer<Array<Float>>(userInfo).memory


That works as expected. The soundData within the callback function is filled with the right samples. Yet the AudioBufferList is always uninitalized or empty. I access it from its memory directly and I get a valid struct which I can access its AudioBuffer. The AudioBuffer has 0 for mDataSize and a nil reference for the data.


var abl = UnsafeMutablePointer<AudioBufferList>(ioData).memory


The audioconvertfillcomplexbuffer call does also give back a size error. After returning from the callback the original AudioBufferList seems "initialized" and is empty as well.


The code base on Obj-C works well. So my guess is the AudioBufferList is causing the problems. Any ideas?


I can supply a playground setup to run exactly the described problem.


Thanks,

Volker

Answered by Volker in 32790022

In addition to the above findings I can report that if I give the callback a copy of the AudioBufferList with userInfoData, I can make the AudioConverter run through fully without error. It seems that solves it. So for reference i add the vital code parts:


My callback and a struct that goes in as user info (both defined in a global context)

struct audioIO {
    var pos: UInt32 = 0
    var srcBuffer: Array<Float>
    var srcBufferSize: UInt32 = 0
    var srcSizePerPacket: UInt32 = 4
    var numPacketsPerRead:UInt32 = 0
    var maxPacketsInSound: UInt32 = 0
    var abl: AudioBufferList
}

func fillComplexCallback(myConverter: AudioConverterRef, packetNumber: UnsafeMutablePointer<UInt32>, ioData: UnsafeMutablePointer<AudioBufferList>, aspd: UnsafeMutablePointer<UnsafeMutablePointer<AudioStreamPacketDescription>>, userInfo: UnsafeMutablePointer<Void>) -> OSStatus {

    let myAIO = UnsafeMutablePointer<audioIO>(userInfo).memory
    if (packetNumber.memory > myAIO.numPacketsPerRead) {
        packetNumber.memory = myAIO.numPacketsPerRead
    }

    if (packetNumber.memory +  myAIO.pos > myAIO.maxPacketsInSound)
    {
        packetNumber.memory = myAIO.maxPacketsInSound - myAIO.pos
    }

    let soundData:Array<Float> = myAIO.srcBuffer
    let _buffer: Array<Float> = Array(soundData[Int(myAIO.pos)..<Int(myAIO.pos+packetNumber.memory)])
    let outByteSize = packetNumber.memory * 4
    UnsafeMutablePointer<audioIO>(userInfo).memory.pos = myAIO.pos + packetNumber.memory
    var abl = myAIO.abl

    abl.mBuffers.mDataByteSize = outByteSize
    abl.mBuffers.mNumberChannels = 1
    abl.mBuffers.mData = UnsafeMutablePointer<Void>(_buffer)
    UnsafeMutablePointer<AudioBufferList>(ioData).memory = abl

    return 0
}



An thats how I call the audioconverter - some variables are left out:


var givenSize: UInt32 = 32768
var packetsPerRead: UInt32 = 32768 / 4
var numOutputPackets = givenSize / outputSizePerPacket
var buffer = [Float]()
var _buffer:Array<Float> = [Float](count: Int(numOutputPackets), repeatedValue: 0)
var audioBufferList = AudioBufferList(mNumberBuffers: 1, mBuffers: AudioBuffer( mNumberChannels: 1, mDataByteSize: givenSize, mData: &_buffer))
var myAudioIO = audioIO(pos: UInt32(0), srcBuffer: soundData, srcBufferSize: givenSize, srcSizePerPacket: UInt32(4), numPacketsPerRead: packetsPerRead, maxPacketsInSound: UInt32(sampleCount), abl: audioBufferList)
var outPos: UInt32 = 0
while 1==1 {
  
    withUnsafeMutablePointers(&myAudioIO, &audioBufferList, &_buffer) { soundPointer, audioBufferPtr, bufferPtr in
        err = AudioConverterFillComplexBuffer(converter, fillComplexCallback, soundPointer , &numOutputPackets, audioBufferPtr, nil)
    }
  
    if err != 0 {
        print("Audio error 4 \(err)")
        break
    }
    if (numOutputPackets < 1) {
        print("finished")
        break;
    }
    buffer += Array(_buffer[0..<Int(numOutputPackets)])
    outPos += numOutputPackets
}


volker

Accepted Answer

In addition to the above findings I can report that if I give the callback a copy of the AudioBufferList with userInfoData, I can make the AudioConverter run through fully without error. It seems that solves it. So for reference i add the vital code parts:


My callback and a struct that goes in as user info (both defined in a global context)

struct audioIO {
    var pos: UInt32 = 0
    var srcBuffer: Array<Float>
    var srcBufferSize: UInt32 = 0
    var srcSizePerPacket: UInt32 = 4
    var numPacketsPerRead:UInt32 = 0
    var maxPacketsInSound: UInt32 = 0
    var abl: AudioBufferList
}

func fillComplexCallback(myConverter: AudioConverterRef, packetNumber: UnsafeMutablePointer<UInt32>, ioData: UnsafeMutablePointer<AudioBufferList>, aspd: UnsafeMutablePointer<UnsafeMutablePointer<AudioStreamPacketDescription>>, userInfo: UnsafeMutablePointer<Void>) -> OSStatus {

    let myAIO = UnsafeMutablePointer<audioIO>(userInfo).memory
    if (packetNumber.memory > myAIO.numPacketsPerRead) {
        packetNumber.memory = myAIO.numPacketsPerRead
    }

    if (packetNumber.memory +  myAIO.pos > myAIO.maxPacketsInSound)
    {
        packetNumber.memory = myAIO.maxPacketsInSound - myAIO.pos
    }

    let soundData:Array<Float> = myAIO.srcBuffer
    let _buffer: Array<Float> = Array(soundData[Int(myAIO.pos)..<Int(myAIO.pos+packetNumber.memory)])
    let outByteSize = packetNumber.memory * 4
    UnsafeMutablePointer<audioIO>(userInfo).memory.pos = myAIO.pos + packetNumber.memory
    var abl = myAIO.abl

    abl.mBuffers.mDataByteSize = outByteSize
    abl.mBuffers.mNumberChannels = 1
    abl.mBuffers.mData = UnsafeMutablePointer<Void>(_buffer)
    UnsafeMutablePointer<AudioBufferList>(ioData).memory = abl

    return 0
}



An thats how I call the audioconverter - some variables are left out:


var givenSize: UInt32 = 32768
var packetsPerRead: UInt32 = 32768 / 4
var numOutputPackets = givenSize / outputSizePerPacket
var buffer = [Float]()
var _buffer:Array<Float> = [Float](count: Int(numOutputPackets), repeatedValue: 0)
var audioBufferList = AudioBufferList(mNumberBuffers: 1, mBuffers: AudioBuffer( mNumberChannels: 1, mDataByteSize: givenSize, mData: &_buffer))
var myAudioIO = audioIO(pos: UInt32(0), srcBuffer: soundData, srcBufferSize: givenSize, srcSizePerPacket: UInt32(4), numPacketsPerRead: packetsPerRead, maxPacketsInSound: UInt32(sampleCount), abl: audioBufferList)
var outPos: UInt32 = 0
while 1==1 {
  
    withUnsafeMutablePointers(&myAudioIO, &audioBufferList, &_buffer) { soundPointer, audioBufferPtr, bufferPtr in
        err = AudioConverterFillComplexBuffer(converter, fillComplexCallback, soundPointer , &numOutputPackets, audioBufferPtr, nil)
    }
  
    if err != 0 {
        print("Audio error 4 \(err)")
        break
    }
    if (numOutputPackets < 1) {
        print("finished")
        break;
    }
    buffer += Array(_buffer[0..<Int(numOutputPackets)])
    outPos += numOutputPackets
}


volker

CoreAudio callback and AudioBufferList references
 
 
Q