I have a bridging function in Swift, one of whose arguments in C is AudioBufferList *
. In Swift this generates an UnsafePointer<AudioBufferList>
. I've manage to deference the pointer by calling audioData[0]
(is there a better way?). But I'm struggling with the next 2 tiers down: the .mBuffers
array of AudioBuffer
's and their void *
/ UnsafePointer<()>
.mData
members.
In C it would simply be
Float32 *audioData = (Float 32*)abl->mBuffers[0]->mData;
output = audioData[sampleNum]...
In Swift the first odd thing is that it won't let me access the elements of mBuffers
but is perfectly happy when I access it as a property. In other words, this works and even has correct data (for the first member of mBuffers
I presume)...
println(abl[0].mBuffers.mNumberChannels) // But .mBuffers should be an []!
Second, it let's me print out .mData
subscripts but the value is always ()
println(abl[0].mBuffers.mData[10]) // Prints '()'
I've tried various casting ops and accessing with multiple indices but to no avail...any ideas?
Here are the C and Swift definitions for AudioBufferList
and AudioBuffer
for convenience...
// C
struct AudioBufferList
{
UInt32 mNumberBuffers;
AudioBuffer mBuffers[1]; // this is a variable length array of mNumberBuffers elements
// ...and a bit more for c++
}
struct AudioBuffer
{
UInt32 mNumberChannels;
UInt32 mDataByteSize;
void* mData;
};
...
// SWIFT
struct AudioBufferList {
var mNumberBuffers: UInt32
var mBuffers: (AudioBuffer)
}
struct AudioBuffer {
var mNumberChannels: UInt32
var mDataByteSize: UInt32
var mData: UnsafePointer<()>
}
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 member
let 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 = Float32
let 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
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With