вторник, 6 сентября 2011 г.

iPhone and Audio Sessions issues

There is a strange hard to find bug related to resuming your audio session on iPhone. When you application was interrupted by incoming call sometimes the system won't let you to restore your audio session. This article will describe weird tip.
When i was working on RoboSockets: Link Me Up game for iOS i accidentally found this issue. The game has it's own, written by my, sound engine based on OpenAL. So it was obvious to me, that this is my issue and i have to fix it. After many trials and errors i found, that if my application is interrupted by incoming call for about 1-3 seconds system simply doesn't restore my audio session after my request. I run out through forum and found that i'm not alone. So finally i came to a solution - weird and brutal.

When i start the game i have to init my sound system. The initialization code first of all inits audio session for application:
AudioSessionInitialize(NULL, NULL, InterruptionListenerCallback, NULL);  

The   InterruptionListenerCallback is a callback function that is called by the system when audio session parameters have been changed (read more at Apple docs). It contains following simple code:
void InterruptionListenerCallback(void *data, UInt32 interruptionState) { OpenAL_GetError(__LINE__); if (interruptionState == kAudioSessionBeginInterruption) { AudioSessionSetActive(false); alcMakeContextCurrent(NULL); alcSuspendContext(soundContext); } else if (interruptionState == kAudioSessionEndInterruption) { UInt32 category = kAudioSessionCategory_AmbientSound; AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category); OSStatus error = AudioSessionSetActive(true); int restartTry = 0;   while (error != 0 && ++restartTry < 10) { sleep(1); error = AudioSessionSetActive(true); } if (error == 0) { alcMakeContextCurrent(soundContext); alcProcessContext(soundContext); } } }
 
Do you see that weird while loop? That's the workaround to avoid non resumable audio sessions! I hope this will save you a bit of time. And that's not all, one more tip below.
Few days ago i was preparing new update to the RoboSockets and my tester (lady), reported, the sound doesn't resume when she brings game to the foreground. I said that that is not possible! I spend so much time hunting this bugs and everything worked perfectly for everybody. And than she showed me how to reproduce this bug. I was disappointed. Whether this is iOS bug or mine? So i started to debug game.
RoboSockets is a very gentle game - if you are listening to the music when you launch the game it will turn off all game sounds and you will be unable to switch on\off them. This will happen regardless of whether you just started the game or brought to the foreground from suspended state. There is a small detail about my bug - it will happen only when you close the game start another one with audio and double click on the iPhone button to show the tray with apps. Then you select from tray RoboSockets.
Applications will swap. I called this a "hot swap", don't know how it sounds in Apple terms. And voila! I check for active background audio and the systems responds that the audio is playing right now, so i block all sounds in my game. I quickly found simple solution to this and will share it with you.
- (void)applicationDidBecomeActive:(UIApplication *)application { [glView startAnimation]; if (CheckForActiveBackgroundAudioSession()) { TurnSoundOff(); } [self performSelector:@selector(SoundResumeOnHotSwapAntiBug) withObject:nil afterDelay:1.5]; }
- (void)SoundResumeOnHotSwapAntiBug { if (!CheckForActiveBackgroundAudioSession()) { TurnSoundOn(); } } 

 
The   SoundResumeOnHotSwapAntiBug is called after 1.5 second to verify whether you are really listening to audio or it was just a hot swap.

1 комментарий: