Using Audiobufferlist with Swift

Using AudioBufferList with Swift

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
...

How to access multiple buffers in UnsafePointer AudioBufferList (non-mutable)

To access each AudioBuffer in an AudioBufferList in Swift, you may choose two ways:

  • Do some pointer operations yourself and calculate each address of AudioBuffer.
  • Use UnsafeMutableAudioBufferListPointer forcefully.

If you could find a right sample code before UnsafeMutableAudioBufferListPointer was introduced in Swift, the first way might be your option, but as for now, it's hard to find one.


To use UnsafeMutableAudioBufferListPointer forcefully, you may need to convert UnsafePointer<AudioBufferList> to UnsafeMutablePointer<AudioBufferList>, which is not too difficult:

            let audioBufferListPtr = UnsafeMutableAudioBufferListPointer(UnsafeMutablePointer(mutating: audioBufferList))

I believe you know how to access buffers once you get UnsafeMutableAudioBufferListPointer.

Caution: Even if type conversion from UnsafePointer<AudioBufferList> to UnsafeMutablePointer<AudioBufferList> is easily made, the actual region passed through audioBufferList is not mutable. You may need extra care not to mutate such region.

How do I turn an array of Float into an AVAudioPCMBuffer?

This solution is somewhat hackish but should work:

@implementation AVAudioFile (FloatArrayWriting)

- (BOOL)writeFloatArray:(const float *)data count:(NSInteger)count format:(AVAudioFormat *)format error:(NSError **)error
{
NSParameterAssert(data != NULL);
NSParameterAssert(count >= 0);
NSParameterAssert(format != nil);

AudioBufferList abl;
abl.mNumberBuffers = 1;
abl.mBuffers[0].mData = (void *)data;
abl.mBuffers[0].mNumberChannels = 1;
abl.mBuffers[0].mDataByteSize = count * sizeof(float);

AVAudioPCMBuffer *buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format bufferListNoCopy:&abl deallocator:NULL];
return [self writeFromBuffer:buffer error:error];
}

@end

By implementing the method in Objective-C you can sidestep the pointer gymnastics needed for AVFAudio in Swift.

Here is a possible Swift solution:

try floats.withUnsafeMutableBufferPointer { umrbp in
let audioBuffer = AudioBuffer(mNumberChannels: 1, mDataByteSize: UInt32(umrbp.count * MemoryLayout<Float>.size), mData: umrbp.baseAddress)
var bufferList = AudioBufferList(mNumberBuffers: 1, mBuffers: audioBuffer)
let outputAudioBuffer = AVAudioPCMBuffer(pcmFormat: buffer.format, bufferListNoCopy: &bufferList)!
try self.renderedAudioFile?.write(from: outputAudioBuffer)
}

How to convert UnsafeMutablePointer AudioBufferList to AudioBuffer in Swift?

Simply use memory property of any UnsafeMutablePointer<> to access it's raw memory. SO your code should look something like this.

var audioBufferListPtr = UnsafeMutableAudioBufferListPointer(ioData).unsafeMutablePointer.memory
for i in 0 ..< Int(inBufferNumber) {
var buffer: AudioBuffer = audioBufferListPtr.mBuffers
}

N.B. Swift is a quickly evolving language. So this code may change in future.

Using AudioBufferList with Swift once again

You can copy data into a byte or Int16 array, element by element, in a "for" loop. Swift might optimize this loop reasonably well compared to memset, such that processor data cache effects end up dominating the performance.

Extracting data from AudioBufferList in Swift 3

If you un-converted your code to Swift 2, it does not work. You may have modified too much when converting your code to Swift 3.

Try this:

var monoSamples = [Float]()
let ptr = bufferList.mBuffers.mData?.assumingMemoryBound(to: Float.self)
monoSamples.append(contentsOf: UnsafeBufferPointer(start: ptr, count: Int(inNumberFrames)))

UnsafeMutableAudioBufferListPointer allocation before calling AudioObjectGetPropertyData

to correctly allocate UnsafeMutablePointer<AudioBufferList> from the number of bytes that is returned by AudioObjectGetPropertyDataSize()

You should not allocate UnsafeMutablePointer<AudioBufferList>, but allocate raw bytes of the exact size and cast it to UnsafeMutablePointer<AudioBufferList>.

Some thing like this:

        let propData = UnsafeMutableRawPointer.allocate(byteCount: Int(propsize), alignment: MemoryLayout<AudioBufferList>.alignment)
result = AudioObjectGetPropertyData(self.id, &address, 0, nil, &propsize, propData);
if (result != 0) {
return false
}
let bufferList = propData.assumingMemoryBound(to: AudioBufferList.self)


Related Topics



Leave a reply



Submit