Connect multiple AVAudioMixerNodes to AVAudioEngine
Asked Answered
E

0

7

I'm having a crash when I try connecting two AVAudioMixerNodes to the microphone input of AVAudioEngine. I couldn't find documentation online how to manage two or more nodes to the same input.

What is the proper way to handle that case where you have multiple node listening to the microphone?

I'm calling those three method of after the other on the background thread:

    private func setupSession() {
        let session = AVAudioSession.sharedInstance()
        do {
            try session.setCategory(.playAndRecord, options: [.mixWithOthers])
            if session.isInputGainSettable {
                try session.setInputGain(0.2)
            }
            try session.setActive(true, options: .notifyOthersOnDeactivation)
        } catch let error as NSError {
            LoggerManager.shared.error(error: error, message: "Error while setting up AVAudioSession Category/Active status")
        }
    }

    private func setupMixerNodes() {
        analyzerNode = AVAudioMixerNode()
        analyzerNode.volume = 0

        volumeNode = AVAudioMixerNode()
        volumeNode.volume = 0

        engine.attach(analyzerNode)
        engine.attach(volumeNode)
    }

    private func makeConnections() {
        /* input microphone */
        let inputNode = engine.inputNode

        let inputFormat = inputNode.outputFormat(forBus: 0)
        let analyzerConnectionPoint = AVAudioConnectionPoint(node: analyzerNode, bus: 0)
        let volumeConnectionPoint = AVAudioConnectionPoint(node: volumeNode, bus: 0)
        engine.connect(inputNode, to: [analyzerConnectionPoint, volumeConnectionPoint], fromBus: 0, format: inputFormat)

        let mainMixerNode = engine.mainMixerNode
        let mixerFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: inputFormat.sampleRate, channels: 1, interleaved: false)

        engine.connect(analyzerNode, to: mainMixerNode, fromBus: 0, toBus: 0, format: mixerFormat)
        engine.connect(volumeNode, to: mainMixerNode, fromBus: 0, toBus: 1, format: mixerFormat)
    }

and I get this crash:

 AURemoteIO.cpp:1128  failed: -10851 (enable 1, outf< 2 ch,      0 Hz, Float32, deinterleaved> inf< 2 ch,      0 Hz, Float32, deinterleaved>)
2022-03-08 12:59:57.950612-0500 SnoreLabo[2914:456147] [avae]            AVAEInternal.h:76    required condition is false: [AVAudioEngineGraph.mm:2401:ConnectMultipleOutputs: (IsFormatSampleRateAndChannelCountValid(format))]
2022-03-08 12:59:58.030789-0500 SnoreLabo[2914:456147] *** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: IsFormatSampleRateAndChannelCountValid(format)'
*** First throw call stack:
(0x18273ed3c 0x199aa36a8 0x1828085bc 0x1ddf44eac 0x1ddf9b020 0x1de01bfa0 0x1de018328 0x104d4db1c 0x104d4e1f8 0x104ed4f6c 0x104bcdbe8 0x10dc00718 0x10dc01f94 0x10dc13edc 0x10dc146fc 0x1dcc4ae48 0x1dcc4a9f0)
libc++abi: terminating with uncaught exception of type NSException
dyld4 config: DYLD_LIBRARY_PATH=/usr/lib/system/introspection DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib
*** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: IsFormatSampleRateAndChannelCountValid(format)'
terminating with uncaught exception of type NSException

EDIT

I ended up using this:

print("Current category: \(AVAudioSession.sharedInstance().category)")
engine.connect(inputNode, to: [analyzerConnectionPoint, volumeConnectionPoint], fromBus: 0, format: inputFormat)

But I still get the same crash from time to time... And the category is properly set to .playAndRecord when it crash so that's not the issue...

Epicycle answered 8/3, 2022 at 18:39 Comment(4)
In the AVAudioEngine documentation they refer to the "audio processing chain", not "tree". This leads me to believe that it's designed to be connected serially. Have you tried connecting Input -> Analyzer -> Volume -> Mixer?Mimesis
@Mimesis But it works 99% of the time, and they do have a method to connect multiple… why would they provide one if it was a chainEpicycle
Definitely a head scratcher. I noticed that the format described in the first line of your crash is a bit odd, and definitely not an input format (it would have only one channel). How about passing nil for the format when you perform the connections?Mimesis
@Mimesis I tried passing nil and it still doesn't work. The crash occurs 100% if I access engine.inputNode from outside the thread i'm running the makeConnections(), I'm still confused why accessing the microphone from a different thread would cause issue thoEpicycle

© 2022 - 2024 — McMap. All rights reserved.