I have an array of Float
(representing audio samples) and I want to turn it into an AVAudioPCMBuffer
so I can pass it to AVAudioFile
's write(from:)
. There's an obvious way (actually not obvious at all, I cribbed it from this gist):
var floats: [Float] = ... // this comes from somewhere else
let audioBuffer = AudioBuffer(mNumberChannels: 1, mDataByteSize: UInt32(floats.count * MemoryLayout<Float>.size), mData: &floats)
var bufferList = AudioBufferList(mNumberBuffers: 1, mBuffers: audioBuffer)
let outputAudioBuffer = AVAudioPCMBuffer(pcmFormat: buffer.format, bufferListNoCopy: &bufferList)!
try self.renderedAudioFile?.write(from: outputAudioBuffer)
This works (I get the audio output I expect) but in Xcode 13.4.1 this gives me a warning on the &floats
: Cannot use inout expression here; argument 'mData' must be a pointer that outlives the call to 'init(mNumberChannels:mDataByteSize:mData:)'
Ok, scope the pointer then:
var floats: [Float] = ... // this comes from somewhere else
try withUnsafeMutablePointer(to: &floats) { bytes in
let audioBuffer = AudioBuffer(mNumberChannels: 1, mDataByteSize: UInt32(bytes.pointee.count * MemoryLayout<Float>.size), mData: bytes)
var bufferList = AudioBufferList(mNumberBuffers: 1, mBuffers: audioBuffer)
let outputAudioBuffer = AVAudioPCMBuffer(pcmFormat: buffer.format, bufferListNoCopy: &bufferList)!
try self.renderedAudioFile?.write(from: outputAudioBuffer)
}
The warning goes away, but now the output is garbage. I really don't understand this as floats.count
and bytes.pointee.count
are the same number. What am I doing wrong?
This solution is somewhat hackish but should work:
By implementing the method in Objective-C you can sidestep the pointer gymnastics needed for AVFAudio in Swift.
Here is a possible Swift solution: