Resume audio on iOS after phone call or alarm

When a phone call or an alarm triggers on iOS, the application receives
an "audio interruption" and it's up to the application to resume
playback when the interruption ends. I added handling for audio
interruptions same as if the game is focused out and then back in.

(cherry picked from commit 96301e934d)
This commit is contained in:
Ruslan Mustakov 2018-05-07 15:44:07 +07:00 committed by Hein-Pieter van Braam
parent db76c54117
commit ecb4c4268c
4 changed files with 72 additions and 22 deletions

View File

@ -217,13 +217,24 @@ void AudioDriverCoreAudio::start() {
if (!active) { if (!active) {
OSStatus result = AudioOutputUnitStart(audio_unit); OSStatus result = AudioOutputUnitStart(audio_unit);
if (result != noErr) { if (result != noErr) {
ERR_PRINT("AudioOutputUnitStart failed"); ERR_PRINT(("AudioOutputUnitStart failed, code: " + itos(result)).utf8().get_data());
} else { } else {
active = true; active = true;
} }
} }
}; };
void AudioDriverCoreAudio::stop() {
if (active) {
OSStatus result = AudioOutputUnitStop(audio_unit);
if (result != noErr) {
ERR_PRINT(("AudioOutputUnitStop failed, code: " + itos(result)).utf8().get_data());
} else {
active = false;
}
}
}
int AudioDriverCoreAudio::get_mix_rate() const { int AudioDriverCoreAudio::get_mix_rate() const {
return mix_rate; return mix_rate;
}; };

View File

@ -90,6 +90,7 @@ public:
virtual void finish(); virtual void finish();
bool try_lock(); bool try_lock();
void stop();
AudioDriverCoreAudio(); AudioDriverCoreAudio();
~AudioDriverCoreAudio(); ~AudioDriverCoreAudio();

View File

@ -37,6 +37,7 @@
@interface AppDelegate : NSObject <UIApplicationDelegate, GLViewDelegate> { @interface AppDelegate : NSObject <UIApplicationDelegate, GLViewDelegate> {
//@property (strong, nonatomic) UIWindow *window; //@property (strong, nonatomic) UIWindow *window;
ViewController *view_controller; ViewController *view_controller;
bool is_focus_out;
}; };
@property(strong, nonatomic) UIWindow *window; @property(strong, nonatomic) UIWindow *window;

View File

@ -140,6 +140,42 @@ void _ios_add_joystick(GCController *controller, AppDelegate *delegate) {
}; };
} }
static void on_focus_out(ViewController *view_controller, bool *is_focus_out) {
if (!*is_focus_out) {
*is_focus_out = true;
if (OS::get_singleton()->get_main_loop())
OS::get_singleton()->get_main_loop()->notification(
MainLoop::NOTIFICATION_WM_FOCUS_OUT);
[view_controller.view stopAnimation];
if (OS::get_singleton()->native_video_is_playing()) {
OSIPhone::get_singleton()->native_video_focus_out();
}
AudioDriverCoreAudio *audio = dynamic_cast<AudioDriverCoreAudio *>(AudioDriverCoreAudio::get_singleton());
if (audio)
audio->stop();
}
}
static void on_focus_in(ViewController *view_controller, bool *is_focus_out) {
if (*is_focus_out) {
*is_focus_out = false;
if (OS::get_singleton()->get_main_loop())
OS::get_singleton()->get_main_loop()->notification(
MainLoop::NOTIFICATION_WM_FOCUS_IN);
[view_controller.view startAnimation];
if (OSIPhone::get_singleton()->native_video_is_playing()) {
OSIPhone::get_singleton()->native_video_unpause();
}
AudioDriverCoreAudio *audio = dynamic_cast<AudioDriverCoreAudio *>(AudioDriverCoreAudio::get_singleton());
if (audio)
audio->start();
}
}
- (void)controllerWasConnected:(NSNotification *)notification { - (void)controllerWasConnected:(NSNotification *)notification {
// create our dictionary if we don't have one yet // create our dictionary if we don't have one yet
if (ios_joysticks == nil) { if (ios_joysticks == nil) {
@ -569,6 +605,8 @@ static int frame_count = 0;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
CGRect rect = [[UIScreen mainScreen] bounds]; CGRect rect = [[UIScreen mainScreen] bounds];
is_focus_out = false;
[application setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone]; [application setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
// disable idle timer // disable idle timer
// application.idleTimerDisabled = YES; // application.idleTimerDisabled = YES;
@ -628,6 +666,12 @@ static int frame_count = 0;
[self initGameControllers]; [self initGameControllers];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(onAudioInterruption:)
name:AVAudioSessionInterruptionNotification
object:[AVAudioSession sharedInstance]];
// OSIPhone::screen_width = rect.size.width - rect.origin.x; // OSIPhone::screen_width = rect.size.width - rect.origin.x;
// OSIPhone::screen_height = rect.size.height - rect.origin.y; // OSIPhone::screen_height = rect.size.height - rect.origin.y;
@ -639,6 +683,18 @@ static int frame_count = 0;
return TRUE; return TRUE;
}; };
- (void)onAudioInterruption:(NSNotification *)notification {
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
NSLog(@"Audio interruption began");
on_focus_out(view_controller, &is_focus_out);
} else if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]) {
NSLog(@"Audio interruption ended");
on_focus_in(view_controller, &is_focus_out);
}
}
};
- (void)applicationWillTerminate:(UIApplication *)application { - (void)applicationWillTerminate:(UIApplication *)application {
[self deinitGameControllers]; [self deinitGameControllers];
@ -656,14 +712,7 @@ static int frame_count = 0;
- (void)applicationDidEnterBackground:(UIApplication *)application { - (void)applicationDidEnterBackground:(UIApplication *)application {
///@TODO maybe add pause motionManager? and where would we unpause it? ///@TODO maybe add pause motionManager? and where would we unpause it?
if (OS::get_singleton()->get_main_loop()) on_focus_out(view_controller, &is_focus_out);
OS::get_singleton()->get_main_loop()->notification(
MainLoop::NOTIFICATION_WM_FOCUS_OUT);
[view_controller.view stopAnimation];
if (OS::get_singleton()->native_video_is_playing()) {
OSIPhone::get_singleton()->native_video_focus_out();
};
} }
- (void)applicationWillEnterForeground:(UIApplication *)application { - (void)applicationWillEnterForeground:(UIApplication *)application {
@ -678,19 +727,7 @@ static int frame_count = 0;
} }
- (void)applicationDidBecomeActive:(UIApplication *)application { - (void)applicationDidBecomeActive:(UIApplication *)application {
if (OS::get_singleton()->get_main_loop()) on_focus_in(view_controller, &is_focus_out);
OS::get_singleton()->get_main_loop()->notification(
MainLoop::NOTIFICATION_WM_FOCUS_IN);
[view_controller.view
startAnimation]; // FIXME: resume seems to be recommended elsewhere
if (OSIPhone::get_singleton()->native_video_is_playing()) {
OSIPhone::get_singleton()->native_video_unpause();
};
// Fixed audio can not resume if it is interrupted cause by an incoming phone call
if (AudioDriverCoreAudio::get_singleton() != NULL)
AudioDriverCoreAudio::get_singleton()->start();
} }
- (void)dealloc { - (void)dealloc {