Swift: How to convert AVAudioEngine's stereo output to mono
Asked Answered
R

1

3

I´m trying to convert the output of my AVAudioEngine from stereo to mono. I want to be able to send the converted mono signal to the left or right headphone or speaker using AVAudioPlayerNode's pan property.

engine.connect(audioFilePlayer[i], to: timePitchControl[i], format: audioFile[i].processingFormat)
engine.connect(timePitchControl[i], to: engine.mainMixerNode, format: audioFile[i].processingFormat)

Is there a way to convert the output of the engine (AVAudioEngine -> engine.mainMixerNode) to a mono signal in real time?

Razor answered 31/5, 2021 at 22:29 Comment(0)
G
0

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:

Gamelan answered 16/9, 2023 at 17:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.