Skip to content

Commit

Permalink
Handle audio interruptions on iOS/tvOS. Fixes bugs 2569 and 2960.
Browse files Browse the repository at this point in the history
  • Loading branch information
slime73 committed Sep 18, 2016
1 parent 8f8f225 commit f0fca28
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 10 deletions.
4 changes: 4 additions & 0 deletions src/audio/coreaudio/SDL_coreaudio.h
Expand Up @@ -34,6 +34,7 @@
#include <CoreServices/CoreServices.h>
#else
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIApplication.h>
#endif

#include <AudioToolbox/AudioToolbox.h>
Expand All @@ -56,6 +57,9 @@ struct SDL_PrivateAudioData
SDL_atomic_t shutdown;
#if MACOSX_COREAUDIO
AudioDeviceID deviceID;
#else
SDL_bool interrupted;
CFTypeRef interruption_listener;
#endif
};

Expand Down
102 changes: 92 additions & 10 deletions src/audio/coreaudio/SDL_coreaudio.m
Expand Up @@ -273,10 +273,58 @@
static int open_capture_devices = 0;

#if !MACOSX_COREAUDIO
static BOOL update_audio_session()

static void interruption_begin(_THIS)
{
if (this != NULL && this->hidden->audioQueue != NULL) {
this->hidden->interrupted = SDL_TRUE;
AudioQueuePause(this->hidden->audioQueue);
}
}

static void interruption_end(_THIS)
{
if (this != NULL && this->hidden != NULL && this->hidden->audioQueue != NULL
&& this->hidden->interrupted) {
this->hidden->interrupted = SDL_FALSE;
AudioQueueStart(this->hidden->audioQueue, NULL);
}
}

@interface SDLInterruptionListener : NSObject

@property (nonatomic, assign) SDL_AudioDevice *device;

@end

@implementation SDLInterruptionListener

- (void)audioSessionInterruption:(NSNotification *)note
{
@synchronized (self) {
NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
interruption_begin(self.device);
} else {
interruption_end(self.device);
}
}
}

- (void)applicationBecameActive:(NSNotification *)note
{
@synchronized (self) {
interruption_end(self.device);
}
}

@end

static BOOL update_audio_session(_THIS, SDL_bool open)
{
@autoreleasepool {
AVAudioSession *session = [AVAudioSession sharedInstance];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
NSString *category;
NSError *err = nil;

Expand All @@ -291,6 +339,12 @@ static BOOL update_audio_session()
category = AVAudioSessionCategoryAmbient;
}

if (![session setCategory:category error:&err]) {
NSString *desc = err.description;
SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
return NO;
}

if (open_playback_devices + open_capture_devices == 1) {
if (![session setActive:YES error:&err]) {
NSString *desc = err.description;
Expand All @@ -301,10 +355,38 @@ static BOOL update_audio_session()
[session setActive:NO error:nil];
}

if (![session setCategory:category error:&err]) {
NSString *desc = err.description;
SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
return NO;
if (open) {
SDLInterruptionListener *listener = [SDLInterruptionListener new];
listener.device = this;

[center addObserver:listener
selector:@selector(audioSessionInterruption:)
name:AVAudioSessionInterruptionNotification
object:session];

/* An interruption end notification is not guaranteed to be sent if
we were previously interrupted... resuming if needed when the app
becomes active seems to be the way to go. */
[center addObserver:listener
selector:@selector(applicationBecameActive:)
name:UIApplicationDidBecomeActiveNotification
object:session];

[center addObserver:listener
selector:@selector(applicationBecameActive:)
name:UIApplicationWillEnterForegroundNotification
object:session];

this->hidden->interruption_listener = CFBridgingRetain(listener);
} else {
if (this->hidden->interruption_listener != NULL) {
SDLInterruptionListener *listener = nil;
listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
@synchronized (listener) {
listener.device = NULL;
}
[center removeObserver:listener];
}
}
}

Expand Down Expand Up @@ -441,6 +523,10 @@ static BOOL update_audio_session()
AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
#endif

#if !MACOSX_COREAUDIO
update_audio_session(this, SDL_FALSE);
#endif

if (this->hidden->thread) {
SDL_AtomicSet(&this->hidden->shutdown, 1);
SDL_WaitThread(this->hidden->thread, NULL);
Expand Down Expand Up @@ -468,10 +554,6 @@ static BOOL update_audio_session()
} else {
open_playback_devices--;
}

#if !MACOSX_COREAUDIO
update_audio_session();
#endif
}

#if MACOSX_COREAUDIO
Expand Down Expand Up @@ -649,7 +731,7 @@ static BOOL update_audio_session()
}

#if !MACOSX_COREAUDIO
if (!update_audio_session()) {
if (!update_audio_session(this, SDL_TRUE)) {
return -1;
}
#endif
Expand Down

0 comments on commit f0fca28

Please sign in to comment.