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
Nsoperation and Nsoperationqueue Working Thread VS Main Thread
Receiving Push Notifications While in Background
Core Data: Do Child Contexts Ever Get Permanent Objectids for Newly Inserted Objects
Passing Data to Apple Watch App
How to Get Download Progress in Afnetworking 2.0
Gradient Polyline with Mapkit iOS
Example for Login Screen Modally Based on Storyboard
What Does Deployment Target Mean
Ios6 Udid - What Advantages Does Identifierforvendor Have Over Identifierforadvertising
How to Mask a Square Image into an Image with Round Corners in iOS
iOS Enterprise Distribution Through Ota
How to Use Namespaces in Swift
Why Maskstobounds = Yes Prevents Calayer Shadow
How to Fetch All Contacts Record in iOS 9 Using Contacts Framework
Designing Inside a Scrollview in Xcode 4.2 with Storyboards