Edit: From what I found out, starting the AVAudioEngine back up is causing the hang for 3 seconds after I enter back into my app and press the play button.
I am building a music player app with AVAudioEngine. When I press the play button in my app, it takes about 800ms to 3s of hang for audio to resume playing from my app (Main thread is blocked???). Is there a correct way to handle taking over the audio session of other apps? I couldn't find any topics here on stack over flow regarding this or even in apple docs.
Most of the interruption related docs are related to Siri or alarm and not related to music playing from other apps.
Here is part of my AudioManager class.
My app is a music player that plays songs through both the speaker and bluetooth headphones like AirPods.
class AudioPlaybackManager {
init() {
audioEngine.attach(playerNode)
audioEngine.attach(eqNode)
audioEngine.attach(pitchNode)
audioEngine.attach(reverbNode)
audioEngine.connect(playerNode, to: eqNode, format: audioFile?.processingFormat)
audioEngine.connect(eqNode, to: pitchNode, format: audioFile?.processingFormat)
audioEngine.connect(pitchNode, to: reverbNode, format: audioFile?.processingFormat)
audioEngine.connect(reverbNode, to: audioEngine.mainMixerNode, format: audioFile?.processingFormat)
try? audioEngine.start()
}
func enableBackgroundPlay() {
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(.playback, mode: .default)
try session.setActive(true)
} catch {
print(error.localizedDescription)
}
}
func setupNotifications() {
// Get the default notification center instance.
let nc = NotificationCenter.default
nc.addObserver(self,
selector: #selector(handleInterruption),
name: AVAudioSession.interruptionNotification,
object: AVAudioSession.sharedInstance())
}
@objc func handleInterruption(notification: Notification) {
guard let userInfo = notification.userInfo,
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
// Switch over the interruption type.
switch type {
case .began:
// An interruption began. Update the UI as necessary.
print("Interruption Begun")
musicPlayerControlsManager.isPlaying = false
playerNode.pause()
case .ended:
// An interruption ended. Resume playback, if appropriate.
guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else { return }
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
print("Interruption Ended.")
if options.contains(.shouldResume) {
// An interruption ended. Resume playback.
musicPlayerControlsManager.isPlaying = false
playerNode.play()
} else {
// An interruption ended. Don't resume playback.
}
default: ()
}
}
}