21

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<()>
}
Hari Honor
  • 8,677
  • 8
  • 51
  • 54

4 Answers4

23

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
}
Adam Ritenauer
  • 3,151
  • 4
  • 33
  • 40
  • Nice, this seems to be fairly new. – jtbandes Sep 27 '15 at 01:04
  • 1
    Question: I have an `UnsafePointer` but the UnsafeMutableAudioBufferListPointer required a `UnsafeMutablePointer`. Do you know how to convert it? – Georg Nov 05 '15 at 10:31
  • 1
    Setting to silence is one thing, but producing a programmatically generated sound seems to be something entirely different. I'm generating a pure tone using a sine wave and cannot figure out the proper way to convert from the floating-point amplitude to the Int32 that memset requires. All I get is horrible crackles. Any ideas? – Ash Nov 21 '15 at 10:47
  • @Georg you want `UnsafeMutablePointer(mutating: myImmutableAudoBufferList)` – Idr Apr 24 '20 at 13:56
10

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
...
jtbandes
  • 115,675
  • 35
  • 233
  • 266
  • Hi, haven't had a chance to try this out in my project but just wanted to say quickly that `reinterpretCast` might help too... – Hari Honor Jul 22 '14 at 09:10
  • That's a good point, but I'm not sure how to use it here. It's undocumented so I don't know if it acts on the same location in memory, copies things around, or what... – jtbandes Jul 22 '14 at 15:48
  • thx. decided to give you the credit even if Adam's is better :) – Hari Honor Mar 30 '17 at 08:24
1

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

let mBuffers=abl.memory.mBuffers

let data=UnsafePointer<Int16>(mBuffers.mData)
let dataArray=UnsafeBufferPointer<Int16>(start:data, count: Int(mBuffers.mDataByteSize)/sizeof(Int16))

//checking resulting array
let count=dataArray.count //this matches the expected number of samples in my case
for i in 0..<count
{
    print(dataArray[i]) //values look OK in my case
    print(" ")
}
macduff
  • 1,583
  • 1
  • 9
  • 9
1

This works for me with Swift 1.2

        var ddata: NSData
        buf = AudioBuffer(mNumberChannels: 1, mDataByteSize: numberOfFrames * UInt32(sizeof(Float32)), mData: &ddata)
        var audioBuffers = AudioBufferList(mNumberBuffers: 1, mBuffers: buf!)
Kendrick Ledet
  • 673
  • 6
  • 9