AVAudioPlayerNode multichannel audio control

836 views Asked by At

I have successfully used AVAudioPlayerNode to play stereo and mono files. I would like to use files with 3+ channels (surround files) and be able to route the audio in a non-linear way. For instance, I could assign file channel 0 to output channel 2, and file channel 4 to output channel 1.

The number of outputs of the audio interface will be unknown (2-40), and this is why I need to be able to allow the user to route the audio as they see fit. And the solution in WWDC 2015 507 of having the user change the routing in Audio Midi Setup is not a viable solution.

There's only 1 possibility that I can think of (and I am open to others): creating one player per channel, and loading each with only one channels' worth of buffers similar to this post. But even by the posters admission, there are issues.

So I'm looking for a way to copy each channel of a file into an AudioBuffer like:

let file = try AVAudioFile(forReading: audioURL)
let fullBuffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, 
                                  frameCapacity: AVAudioFrameCount(file.length))

try file.read(into: fullBuffer)

// channel 0
let buffer0 = AVAudioPCMBuffer(pcmFormat: file.processingFormat,
                               frameCapacity: AVAudioFrameCount(file.length))

// this doesn't work, unable to get fullBuffer channel and copy
// error on subscripting mBuffers
buffer0.audioBufferList.pointee.mBuffers.mData = fullBuffer.audioBufferList.pointee.mBuffers[0].mData

// repeat above buffer code for each channel from the fullBuffer
1

There are 1 answers

0
GW.Rodriguez On

I was able to figure it out, so here's the code to make it work. Note: the code below separates a stereo (2 channel) file. This could easily be expanded to handle an unknown number of channels.

let file = try AVAudioFile(forReading: audioURL)

let formatL = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: file.processingFormat.sampleRate, channels: 1, interleaved: false)
let formatR = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: file.processingFormat.sampleRate, channels: 1, interleaved: 

let fullBuffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: AVAudioFrameCount(file.length))
let bufferLeft = AVAudioPCMBuffer(pcmFormat: formatL, frameCapacity: AVAudioFrameCount(file.length))
let bufferRight = AVAudioPCMBuffer(pcmFormat: formatR, frameCapacity: AVAudioFrameCount(file.length))

try file.read(into: fullBuffer)
bufferLeft.frameLength = fullBuffer.frameLength
bufferRight.frameLength = fullBuffer.frameLength

for i in 0..<Int(file.length) {
    bufferLeft.floatChannelData![0][i] = fullBuffer.floatChannelData![0][i]
    bufferRight.floatChannelData![0][i] = fullBuffer.floatChannelData![1][i]
}