Using a matrix mixer node it's possible. There are other ways but I am not aware of a simpler solution. A matrix mixer is a mixer that allows to connect any input to any output. So it can be used to connect left and right input channels to one output channel only.
Gist
Its usage is a bit complicated so I have a gist to make it more convenient.
Here are some parts anyway.
Instantiation
let matrixUnitDescription = AudioComponentDescription(
componentType: kAudioUnitType_Mixer,
componentSubType: kAudioUnitSubType_MatrixMixer,
componentManufacturer: kAudioUnitManufacturer_Apple,
componentFlags: 0,
componentFlagsMask: 0
)
let matrixMixerNode = try await AVAudioUnit.instantiate(with: matrixUnitDescription)
Connect
You can then connect your player node to this mixer node, and connect the mixer node to the main mixer node (or another node before that if you prefer).
engine.connect(playerNode, to: matrixMixerNode, format: nil)
engine.connect(matrixMixerNode, to: engine.mainMixerNode, format: nil)
Then comes the trick part. When the engine is started, set the general, input and output volumes:
// global
AudioUnitSetParameter(
matrixMixerNode.audioUnit,
kMatrixMixerParam_Volume,
kAudioUnitScope_Global,
0xFFFF_FFFF,
1,
0
)
// inputs
for inputChannelIndex in 0..<matrixMixerNode.inputFormat(forBus: 0).channelCount {
AudioUnitSetParameter(
matrixMixerNode.audioUnit,
kMatrixMixerParam_Volume,
kAudioUnitScope_Input,
inputChannelIndex,
1,
0
)
}
// outputs
for outputChannelIndex in 0..<matrixMixerNode.outputFormat(forBus: 0).channelCount {
AudioUnitSetParameter(
matrixMixerNode.audioUnit,
kMatrixMixerParam_Volume,
kAudioUnitScope_Output,
outputChannelIndex,
volume,
0
)
}
And finally you can set the volume for a connexion between an input channel and an output channel. So, to transform a stereo signal to mono:
let leftCrossPoint = UInt32((0 << 16) | 0)
AudioUnitSetParameter(
matrixMixerNode.audioUnit,
kMatrixMixerParam_Volume,
kAudioUnitScope_Global,
leftCrossPoint,
1,
0
)
let rightCrossPoint = UInt32((1 << 16) | 0)
AudioUnitSetParameter(
matrixMixerNode.audioUnit,
kMatrixMixerParam_Volume,
kAudioUnitScope_Global,
rightCrossPoint,
1,
0
)
It's a lot of boiler plate, and here the volume was always 1 but it can also be adjusted. That's why the gist could be helpful if you need further details.
Links
Anyway, here are the links I used to understand how to use this audio unit: