mirror of
				https://github.com/Ryujinx/SDL.git
				synced 2025-11-04 08:45:00 +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>
 | 
					#include <CoreServices/CoreServices.h>
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#import <AVFoundation/AVFoundation.h>
 | 
					#import <AVFoundation/AVFoundation.h>
 | 
				
			||||||
 | 
					#import <UIKit/UIApplication.h>
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <AudioToolbox/AudioToolbox.h>
 | 
					#include <AudioToolbox/AudioToolbox.h>
 | 
				
			||||||
| 
						 | 
					@ -56,6 +57,9 @@ struct SDL_PrivateAudioData
 | 
				
			||||||
    SDL_atomic_t shutdown;
 | 
					    SDL_atomic_t shutdown;
 | 
				
			||||||
#if MACOSX_COREAUDIO
 | 
					#if MACOSX_COREAUDIO
 | 
				
			||||||
    AudioDeviceID deviceID;
 | 
					    AudioDeviceID deviceID;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    SDL_bool interrupted;
 | 
				
			||||||
 | 
					    CFTypeRef interruption_listener;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -273,10 +273,58 @@ static int open_playback_devices = 0;
 | 
				
			||||||
static int open_capture_devices = 0;
 | 
					static int open_capture_devices = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if !MACOSX_COREAUDIO
 | 
					#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 {
 | 
					    @autoreleasepool {
 | 
				
			||||||
        AVAudioSession *session = [AVAudioSession sharedInstance];
 | 
					        AVAudioSession *session = [AVAudioSession sharedInstance];
 | 
				
			||||||
 | 
					        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
 | 
				
			||||||
        NSString *category;
 | 
					        NSString *category;
 | 
				
			||||||
        NSError *err = nil;
 | 
					        NSError *err = nil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -291,6 +339,12 @@ static BOOL update_audio_session()
 | 
				
			||||||
            category = AVAudioSessionCategoryAmbient;
 | 
					            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 (open_playback_devices + open_capture_devices == 1) {
 | 
				
			||||||
            if (![session setActive:YES error:&err]) {
 | 
					            if (![session setActive:YES error:&err]) {
 | 
				
			||||||
                NSString *desc = err.description;
 | 
					                NSString *desc = err.description;
 | 
				
			||||||
| 
						 | 
					@ -301,10 +355,38 @@ static BOOL update_audio_session()
 | 
				
			||||||
            [session setActive:NO error:nil];
 | 
					            [session setActive:NO error:nil];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (![session setCategory:category error:&err]) {
 | 
					        if (open) {
 | 
				
			||||||
            NSString *desc = err.description;
 | 
					            SDLInterruptionListener *listener = [SDLInterruptionListener new];
 | 
				
			||||||
            SDL_SetError("Could not set Audio Session category: %s", desc.UTF8String);
 | 
					            listener.device = this;
 | 
				
			||||||
            return NO;
 | 
					
 | 
				
			||||||
 | 
					            [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);
 | 
					    AudioObjectRemovePropertyListener(this->hidden->deviceID, &alive_address, device_unplugged, this);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if !MACOSX_COREAUDIO
 | 
				
			||||||
 | 
					    update_audio_session(this, SDL_FALSE);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this->hidden->thread) {
 | 
					    if (this->hidden->thread) {
 | 
				
			||||||
        SDL_AtomicSet(&this->hidden->shutdown, 1);
 | 
					        SDL_AtomicSet(&this->hidden->shutdown, 1);
 | 
				
			||||||
        SDL_WaitThread(this->hidden->thread, NULL);
 | 
					        SDL_WaitThread(this->hidden->thread, NULL);
 | 
				
			||||||
| 
						 | 
					@ -468,10 +554,6 @@ COREAUDIO_CloseDevice(_THIS)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        open_playback_devices--;
 | 
					        open_playback_devices--;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
#if !MACOSX_COREAUDIO
 | 
					 | 
				
			||||||
    update_audio_session();
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if MACOSX_COREAUDIO
 | 
					#if MACOSX_COREAUDIO
 | 
				
			||||||
| 
						 | 
					@ -649,7 +731,7 @@ COREAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if !MACOSX_COREAUDIO
 | 
					#if !MACOSX_COREAUDIO
 | 
				
			||||||
    if (!update_audio_session()) {
 | 
					    if (!update_audio_session(this, SDL_TRUE)) {
 | 
				
			||||||
        return -1;
 | 
					        return -1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue