The below code causes a crash with the following errors whenever the object is deinitialized (e.g. when performing an unwind segue back to another ViewController):
required condition is false: [AVAudioEngineGraph.mm:4474:GetDefaultMusicDevice: (outputNode)]
Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: outputNode'
The AVAudioSequencer
is the root of the issue, because the error ceases if this is removed.
How can this crash be avoided?
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)
if let fileURL = Bundle.main.url(forResource: "TestMusic", withExtension: "mid") {
do {
try sequencer.load(from: fileURL, options: AVMusicSequenceLoadOptions())
} catch {
print("Error loading sequencer: \(error.localizedDescription)")
}
}
sequencer.prepareToPlay()
}
}
This crash can be confusing, and may also output no error message whatsoever to the console if the sequencer's content hasn't been loaded yet. Very unhelpful!
The AVAudioSequencer
is indeed the cause of the issue. To fix it, make the sequencer an implicitly unwrapped optional (i.e. add !
to its type) and add explicit instructions to stop & remove it during deinit
, before the rest of the object is deinitialized.
The fixed code is as follows (take note of the deinit
method especially):
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)
if let fileURL = Bundle.main.url(forResource: "TestMusic", withExtension: "mid") {
do {
try sequencer.load(from: fileURL, options: AVMusicSequenceLoadOptions())
} catch {
print("Error loading sequencer: \(error.localizedDescription)")
}
}
sequencer.prepareToPlay()
}
deinit {
sequencer.stop()
sequencer = nil
}
}
Hope this helps!
来源:https://stackoverflow.com/questions/58036260/avaudiosequencer-causes-crash-on-deinit-segue-required-condition-is-false-out