Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Crash in AVAudioEngine.start() even though it's wrapped in do/catch

I have the following code to (re)start AVAudioEngine wired up to AVAudioEngineConfigurationChangeNotification:

   do {
       try self.engine.start()
   } catch {
       DDLogError("could not start sound engine")
       self.soundEnabled = false
       return
   }

self.engine is defined as

private let engine = AVAudioEngine()

However, I frequently receive crash reports via Crashlytics saying

Fatal Exception: com.apple.coreaudio.avfaudio error 561015905

on the line containing the try self.engine.start().

561015905 is AVAudioSessionErrorCodeCannotStartPlaying and from what I understand, this should be a NSError error code, not an exception, which should be caught by my blank catch in the code above. Still, the app just seems to crash at that point. What am I missing?

I know there are situations where the app wakes up in the background where this error can occur and I would be fine with that, as long as I can somehow catch it happening, as I thought I could with do/catch.

like image 342
Mike Avatar asked Dec 14 '15 13:12

Mike


2 Answers

Xcode Version 9.2 (9C40b) + Swift 4 : I know this question is a little old, however, I was having the same crashing issues with audioEngine.start() even though in a do/try/catch and also receiving the following from Crashalytics:

Fatal Exception: com.apple.coreaudio.avfaudio error 561015905

S1LENT WARRIOR's sleep(1) "hack" worked in some cases, but not all (specifically with AVAudioEngineConfigurationChangeNotification selector).

Finally, I used Obj-C exception handling to really catch the error so no crash occurs, from this very useful post by freytag (big thanks!):

Catching NSException in Swift

Now, after implementing the ObjC .h and .m files and the bridging header, I do:

do {
    try ObjC.catchException {
        try! self.audioEngine.start()
    }
}
catch {
    print("An error occurred: \(error)")
}

You can test this by screwing up the engine initialization (eg don't .attach or .connect anything) and no crash... only:

2018-01-06 10:01:48.890801+0700 XXXXXX[16389:3367770] [avae] AVAEInternal.h:70:_AVAE_Check: required condition is false: [AVAudioEngineGraph.mm:1209:Initialize: (inputNode != nullptr || outputNode != nullptr)] An error occurred: Error Domain=com.apple.coreaudio.avfaudio Code=0 "(null)"

Make sure you check the audioEngine is running before using it, something like:

func play(soundName: String) {
    if !audioEngine.isRunning {
        return
    }
    // play sound
 }

OK, so you get no sound but it is a "graceful fail".

Seems ridiculous that in Swift you can't properly catch an exception, and ok if you're going to make some exceptions not catchable then at least provide a method to test first, something like audioEngine.areYouConfiguredProperly(). Oh hang on there is this method (in Obj-C) [AVAudioEngine startAndReturnError:] but someone decided to wrap it with the startEngine() function and do away with all useful functionality... doh.

like image 120
Christian Cerri Avatar answered Nov 02 '22 00:11

Christian Cerri


I encountered the same error when handling AVAudioSessionInterruption notification.
In my case, the error was arising when i tried to start AVAudioEngine after the interruption ended.
After detailed testing and debugging for a while, i noticed the app wasn't crashing if i introduce a debugger breakpoint before audioEngine.prepare() or audioEngine.start().
So i added sleep(1) before audioEngine.start() and my app stopped crashing!

I know its not a very elegant solution but still hope this may help someone else!

like image 4
S1LENT WARRIOR Avatar answered Nov 02 '22 02:11

S1LENT WARRIOR