mirror of
				https://github.com/Ryujinx/SDL.git
				synced 2025-11-04 02:54:55 +00:00 
			
		
		
		
	Handle audio interruptions on iOS/tvOS. Fixes bugs 2569 and 2960.
This commit is contained in:
		
							parent
							
								
									8f8f225b3f
								
							
						
					
					
						commit
						f0fca2880f
					
				| 
						 | 
				
			
			@ -34,6 +34,7 @@
 | 
			
		|||
#include <CoreServices/CoreServices.h>
 | 
			
		||||
#else
 | 
			
		||||
#import <AVFoundation/AVFoundation.h>
 | 
			
		||||
#import <UIKit/UIApplication.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <AudioToolbox/AudioToolbox.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +57,9 @@ struct SDL_PrivateAudioData
 | 
			
		|||
    SDL_atomic_t shutdown;
 | 
			
		||||
#if MACOSX_COREAUDIO
 | 
			
		||||
    AudioDeviceID deviceID;
 | 
			
		||||
#else
 | 
			
		||||
    SDL_bool interrupted;
 | 
			
		||||
    CFTypeRef interruption_listener;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -273,10 +273,58 @@ static int open_playback_devices = 0;
 | 
			
		|||
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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -441,6 +523,10 @@ COREAUDIO_CloseDevice(_THIS)
 | 
			
		|||
    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);
 | 
			
		||||
| 
						 | 
				
			
			@ -468,10 +554,6 @@ COREAUDIO_CloseDevice(_THIS)
 | 
			
		|||
    } else {
 | 
			
		||||
        open_playback_devices--;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if !MACOSX_COREAUDIO
 | 
			
		||||
    update_audio_session();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if MACOSX_COREAUDIO
 | 
			
		||||
| 
						 | 
				
			
			@ -649,7 +731,7 @@ COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
#if !MACOSX_COREAUDIO
 | 
			
		||||
    if (!update_audio_session()) {
 | 
			
		||||
    if (!update_audio_session(this, SDL_TRUE)) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue