Reputation: 6308
I'm playing game sounds using OpenAL, and bg music using standard AV. Recently i've found that after the incoming call all openal sounds don't work while bg music is still playing. If I force stop app and start again sounds appear again. Do smbd happen to know what's happening to openal during/after the incoming call?
Upvotes: 4
Views: 1478
Reputation: 10887
I had a hard time figuring this out so wanted to add my answer here. This is all specifically in Xamarin, but I suspect it applies generally and is similar to @Tertium's answer
You can prevent iOS from interrupting your audio in some situations (e.g., getting a phone call but declining it), using AVAudioSession.SharedInstance().SetPrefersNoInterruptionsFromSystemAlerts(true, out NSError err);
You will still be interrupted in some situations (e.g., you accept a phone call). To catch these you must AVAudioSession.Notifications.ObserveInteruption(myAudioInterruptionHandler);
when you launch your app.
Inside this handler, you can determine if you're shutting down or coming back like so:
void myAudioInterruptionHandler(object sender, AVAudioSessionInterruptionEventArgs args) {
args.Notification.UserInfo.TryGetValue(
new NSString("AVAudioSessionInterruptionTypeKey"),
out NSObject typeKey
);
bool isBeginningInterruption = (typeKey.ToString() == "1");
// ...
}
When the interruption begins, stop whatever audio is playing (you'll need to handle this on your own based on your app, but probably by calling AL.SourceStop
on everything).
Then, critically,
ContextHandle audioContextHandle = Alc.GetCurrentContext();
Alc.MakeContextCurrent(ContextHandle.Zero);
If you don't do this right away, iOS will fry your ALC context and you are doomed. Note that if you have a handler for AudioRouteChanged
this is too late, you must do it in the AudioInterruption
handler.
When you're coming back from the interruption, first reboot your iOS audio session:
AVAudioSession.SharedInstance().SetActive(true);
You may also need to reset your preferred input (I think this step is optional if you always use the default input) AVAudioSession.SharedInstance().SetPreferredInput(Input, out NSError err)
Then restore your context
Alc.MakeContextCurrent(audioContextHandle);
Upvotes: 0
Reputation: 6308
Ok, it seems I've found a solution. I'm using obj-c sound manager, so I just added beginInterruption and endInterruption delegate methods of AVAudioSession (and AVAudioPlayer) to my class.
beginInterruption looks like:
alcMakeContextCurrent(NULL);
and endInterruption looks something like:
NSError * audioSessionError = NULL;
[audioSession setCategory:soundCategory error:&audioSessionError];
if (audioSessionError)
{
Log(@"ERROR - SoundManager: Unable to set the audio session category");
return;
}
// Set the audio session state to true and report any errors
audioSessionError = NULL;
[audioSession setActive:YES error:&audioSessionError];
if (audioSessionError)
{
Log(@"ERROR - SoundManager: Unable to set the audio session state to YES with error %d.", (int) result);
return;
}
//music players handling
bool plays = false;
if (musicPlayer[currentPlayer] != nil)
plays = [musicPlayer[currentPlayer] isPlaying];
if (musicPlayer[currentPlayer] != nil && !plays)
[musicPlayer[currentPlayer] play];
alcMakeContextCurrent(context);
Yes, this works if you're using only openAL sounds. But to play long tracks you should use AVAudioPlayer. But here's the Apple magic again! If you play music along with OpenAL sounds something odd happens. Cancel the incoming call and AVAudioSessionDelegate::endInterruption with AVAudioPlayerDelegate::audioPlayerEndInterruption will never called. Only beginInterruption, not the end. Even AppDelegate::applicationWillEnterForeground will not be called, and app just don't know that we've returned.
But the good news is that you can call your endInterruption in AppDelegate::applicationDidBecomeActive method, and openAL context will be restored. And this works!
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if (MySoundMngr != nil)
{
[MySoundMngr endInterruption];
}
// Restart any tasks that were paused and so on....
}
Upvotes: 2