Using AudioBufferList with Swift Using AudioBufferList with Swift objective-c objective-c

Using AudioBufferList with Swift


I found this by accident. Oddly the type ahead was actually working with Swift when it suggested UnsafeMutableAudioBufferListPointer. Which you can initialize with an UnsafeMutablePointer argument. This type is a MutableCollectionType and provides subscript and generator access to the contained Audio Buffers.

For example you can set an ABL to silence with the following code

func renderCallback(ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {    let abl = UnsafeMutableAudioBufferListPointer(ioData)    for buffer in abl {        memset(buffer.mData, 0, Int(buffer.mDataByteSize))    }    return noErr}


Edit: Adam Ritenauer's answer is probably the best one now. To expand on it, you can look at the new utility functions/types in the iOS 8.3 Core Audio changes.

UnsafeMutableAudioBufferListPointer can be used to read/access some given data:

struct UnsafeMutableAudioBufferListPointer {    init(_ p: UnsafeMutablePointer<AudioBufferList>)    var count: Int    subscript (index: Int) -> AudioBuffer { get nonmutating set }}

And you can use the extensions on AudioBuffer & AudioBufferList to allocate your own:

extension AudioBufferList {    static func sizeInBytes(maximumBuffers maximumBuffers: Int) -> Int    static func allocate(maximumBuffers maximumBuffers: Int) -> UnsafeMutableAudioBufferListPointer}extension AudioBuffer {    init<Element>(_ typedBuffer: UnsafeMutableBufferPointer<Element>, numberOfChannels: Int)}

Old answer:

This is a bit tricky because AudioBufferList is actually a variable-size struct. This means it's declared as having a single AudioBuffer, but really it has as many as specified by the mNumberBuffers member. This notion doesn't translate very well to Swift, which is why you see var mBuffers: (AudioBuffer).

So the canonical way to access these buffers, and their data, would be using UnsafeArray. The code below provides some ideas, but UnsafePointer and UnsafeArray aren't well documented, so this could be wrong.

// ***WARNING: UNTESTED CODE AHEAD***let foo: UnsafePointer<AudioBufferList> // from elsewhere...// This looks intuitive, but accessing `foo.memory` may be doing a copy.let bufs = UnsafeArray<AudioBuffer>(start: &foo.memory.mBuffers, length: Int(foo.memory.mNumberBuffers))// This is another alternative that should work...let bufsStart = UnsafePointer<AudioBuffer>(UnsafePointer<UInt32>(foo) + 1) // Offset to mBuffers memberlet bufs = UnsafeArray<AudioBuffer>(start: bufsStart, length: Int(foo.memory.mNumberBuffers))// Hopefully this isn't doing a copy, but it shouldn't be too much of a problem anyway.let buf: AudioBuffer = bufs[0] // or you could use a for loop over bufs, etc.typealias MySample = Float32let numSamples = Int(buf.mDataByteSize / UInt32(sizeof(MySample)))let samples = UnsafeArray<MySample>(start: UnsafePointer<MySample>(buf.mData), length: numSamples)// Now use the samples array...

This seems to work in the playground but it's hard for me to test on real audio data. In particular, I'm not 100% sure that using start: &foo.memory.mBuffers will work as expected. (It returns a different pointer from the original, although the data seem to be there.) Give it a shot and report back!

Edit: to debug this, by the way, you can for example:

(lldb) p foo(UnsafePointer<AudioBufferList>) $R1 = (value = Builtin.RawPointer = 0x0000000100700740)(lldb) expr -lc -- ((int*)0x0000000100700740)[0](int) $2 = 42(lldb) expr -lc -- ((int*)0x0000000100700740)[1](int) $3 = 43...


I've found this works OK. abl is an AudioBufferList created from loading a 16bit AIFF audio file.

let mBuffers=abl.memory.mBufferslet data=UnsafePointer<Int16>(mBuffers.mData)let dataArray=UnsafeBufferPointer<Int16>(start:data, count: Int(mBuffers.mDataByteSize)/sizeof(Int16))//checking resulting arraylet count=dataArray.count //this matches the expected number of samples in my casefor i in 0..<count{    print(dataArray[i]) //values look OK in my case    print(" ")}