TheNeil
TheNeil

Reputation: 3752

AVAEInternal / coreaudio.avfaudio Error When Initializing AVAudioSequencer on AVAudioEngine

I'm attempting to create & test a basic audio setup with an AVAudioEngine, AVAudioUnitSampler, and AVAudioSequencer; however, even this most basic version of the code is throwing the errors below, resulting in a complete app crash.

Can anyone advise why this is happening? The cause is specifically the line where the sequencer is initialized (if that's commented out, the error ceases), but I'm unsure why. Is it something to do with the order of steps, or something I've missed here?

It says something about retrieving the outputNode, but I thought AVAudioEngine has one of those initialized by default.

Thank you.

class TestAudioClass {

    private var audioEngine: AVAudioEngine
    private var sampler: AVAudioUnitSampler
    private var sequencer: AVAudioSequencer

    init() {
        self.audioEngine = AVAudioEngine()
        self.sampler = AVAudioUnitSampler()
        self.sequencer = AVAudioSequencer(audioEngine: audioEngine)
    }
}

Thread 1: signal SIGABRT

[avae] AVAEInternal.h:70:_AVAE_Check: required condition is false: [AVAudioEngineGraph.mm:4474:GetDefaultMusicDevice: (outputNode)]

[avae] AVAEInternal.h:103:_AVAE_CheckNoErr: [AVAudioEngineGraph.mm:4309:SetSequence: (err = MusicSequenceSetAudioGraph(inSequence, _seqGraphImpl)): error -1

*** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'error -1'

Upvotes: 1

Views: 1627

Answers (1)

TheNeil
TheNeil

Reputation: 3752

So, after some digging around, I found the solution: Whilst it's true that an AVAudioEngine comes with an outputNode out-of-box, what Apple aren't sufficiently clear on is that this output node doesn't exist immediately after initialization.

In the above code, the sequencer, upon creation, tries to access the output node of the engine, but finds that there's nothing there, triggering the crash.

Before creating the sequencer, you must access the engine's outputNode in some way (either directly, or indirectly via the mainMixerNode). This could just be by calling that property, but a good way to do it is by connecting any relevant nodes to it, such as the AVAudioUnitSampler.

The corrected code is as follows:

class TestAudioClass {

    private var audioEngine: AVAudioEngine
    private var sampler: AVAudioUnitSampler
    private var sequencer: AVAudioSequencer

    init() {
        self.audioEngine = AVAudioEngine()
        self.sampler = AVAudioUnitSampler()
        audioEngine.attach(sampler)
        audioEngine.connect(sampler, to: audioEngine.mainMixerNode, format: nil)
        self.sequencer = AVAudioSequencer(audioEngine: audioEngine)
    }
}

It's also worth noting that if you first access outputNode directly, it won't result in the mixer being attached. As per an Apple staff member here:

"...the default output is created for you automatically either with a mixer attached (access mainMixerNode first) or just by itself (access outputNode first)."

Note: You may still experience similar crashes during deinitialization of this object (such as when performing an unwind segue to another ViewController). Details on how to fix this issue are available here.

Upvotes: 2

Related Questions