@@ -313,32 +313,6 @@ static void COREAUDIO_DetectDevices(SDL_AudioDevice **default_playback, SDL_Audi
313313
314314static bool session_active = false ;
315315
316- static bool PauseOneAudioDevice (SDL_AudioDevice *device, void *userdata)
317- {
318- if (device->hidden && device->hidden ->audioQueue && !device->hidden ->interrupted ) {
319- AudioQueuePause (device->hidden ->audioQueue );
320- }
321- return false ; // keep enumerating devices until we've paused them all.
322- }
323-
324- static void PauseAudioDevices (void )
325- {
326- (void ) SDL_FindPhysicalAudioDeviceByCallback (PauseOneAudioDevice, NULL );
327- }
328-
329- static bool ResumeOneAudioDevice (SDL_AudioDevice *device, void *userdata)
330- {
331- if (device->hidden && device->hidden ->audioQueue && !device->hidden ->interrupted ) {
332- AudioQueueStart (device->hidden ->audioQueue , NULL );
333- }
334- return false ; // keep enumerating devices until we've resumed them all.
335- }
336-
337- static void ResumeAudioDevices (void )
338- {
339- (void ) SDL_FindPhysicalAudioDeviceByCallback (ResumeOneAudioDevice, NULL );
340- }
341-
342316static void InterruptionBegin (SDL_AudioDevice *device)
343317{
344318 if (device != NULL && device->hidden != NULL && device->hidden ->audioQueue != NULL ) {
@@ -383,40 +357,22 @@ - (void)applicationBecameActive:(NSNotification *)note
383357
384358@end
385359
386- typedef struct
387- {
388- int playback;
389- int recording;
390- } CountOpenAudioDevicesData;
391360
392- static bool CountOpenAudioDevices (SDL_AudioDevice *device, void *userdata)
393- {
394- CountOpenAudioDevicesData *data = (CountOpenAudioDevicesData *) userdata;
395- if (device->hidden != NULL ) { // assume it's open if hidden != NULL
396- if (device->recording ) {
397- data->recording ++;
398- } else {
399- data->playback ++;
400- }
401- }
402- return false ; // keep enumerating until all devices have been checked.
403- }
361+ static SDL_AtomicInt open_playback_device_count;
362+ static SDL_AtomicInt open_recording_device_count;
404363
405- static bool UpdateAudioSession (SDL_AudioDevice *device, bool open, bool allow_playandrecord)
364+ static bool UpdateAudioSession (SDL_AudioDevice *device, bool allow_playandrecord)
406365{
407366 @autoreleasepool {
408367 AVAudioSession *session = [AVAudioSession sharedInstance ];
409- NSNotificationCenter *center = [NSNotificationCenter defaultCenter ];
410-
411368 NSString *category = AVAudioSessionCategoryPlayback;
412369 NSString *mode = AVAudioSessionModeDefault;
413370 NSUInteger options = AVAudioSessionCategoryOptionMixWithOthers;
414371 NSError *err = nil ;
415372 const char *hint;
416373
417- CountOpenAudioDevicesData data;
418- SDL_zero (data);
419- (void ) SDL_FindPhysicalAudioDeviceByCallback (CountOpenAudioDevices, &data);
374+ const int opened_for_playback = SDL_GetAtomicInt (&open_playback_device_count);
375+ const int opened_for_recording = SDL_GetAtomicInt (&open_recording_device_count);
420376
421377 hint = SDL_GetHint (SDL_HINT_AUDIO_CATEGORY);
422378 if (hint) {
@@ -436,14 +392,14 @@ static bool UpdateAudioSession(SDL_AudioDevice *device, bool open, bool allow_pl
436392 category = AVAudioSessionCategoryPlayAndRecord;
437393 }
438394 }
439- } else if (data. playback && data. recording ) {
395+ } else if (opened_for_playback && opened_for_recording ) {
440396 if (allow_playandrecord) {
441397 category = AVAudioSessionCategoryPlayAndRecord;
442398 } else {
443399 // We already failed play and record with AVAudioSessionErrorCodeResourceNotAvailable
444400 return false ;
445401 }
446- } else if (data. recording ) {
402+ } else if (opened_for_recording ) {
447403 category = AVAudioSessionCategoryRecord;
448404 }
449405
@@ -469,72 +425,12 @@ static bool UpdateAudioSession(SDL_AudioDevice *device, bool open, bool allow_pl
469425 }
470426
471427 if (![session.category isEqualToString: category] || session.categoryOptions != options) {
472- // Stop the current session so we don't interrupt other application audio
473- PauseAudioDevices ();
474- [session setActive: NO error: nil ];
475- session_active = false ;
476-
477428 if (![session setCategory: category mode: mode options: options error: &err]) {
478429 NSString *desc = err.description ;
479430 SDL_SetError (" Could not set Audio Session category: %s " , desc.UTF8String );
480431 return false ;
481432 }
482433 }
483-
484- if ((data.playback || data.recording ) && !session_active) {
485- if (![session setActive: YES error: &err]) {
486- if ([err code ] == AVAudioSessionErrorCodeResourceNotAvailable &&
487- category == AVAudioSessionCategoryPlayAndRecord) {
488- if (UpdateAudioSession (device, open, false )) {
489- return true ;
490- } else {
491- return SDL_SetError (" Could not activate Audio Session: Resource not available" );
492- }
493- }
494-
495- NSString *desc = err.description ;
496- return SDL_SetError (" Could not activate Audio Session: %s " , desc.UTF8String );
497- }
498- session_active = true ;
499- ResumeAudioDevices ();
500- } else if (!data.playback && !data.recording && session_active) {
501- PauseAudioDevices ();
502- [session setActive: NO error: nil ];
503- session_active = false ;
504- }
505-
506- if (open) {
507- SDLInterruptionListener *listener = [SDLInterruptionListener new ];
508- listener.device = device;
509-
510- [center addObserver: listener
511- selector: @selector (audioSessionInterruption: )
512- name: AVAudioSessionInterruptionNotification
513- object: session];
514-
515- /* An interruption end notification is not guaranteed to be sent if
516- we were previously interrupted... resuming if needed when the app
517- becomes active seems to be the way to go. */
518- // Note: object: below needs to be nil, as otherwise it filters by the object, and session doesn't send foreground / active notifications.
519- [center addObserver: listener
520- selector: @selector (applicationBecameActive: )
521- name: UIApplicationDidBecomeActiveNotification
522- object: nil ];
523-
524- [center addObserver: listener
525- selector: @selector (applicationBecameActive: )
526- name: UIApplicationWillEnterForegroundNotification
527- object: nil ];
528-
529- device->hidden ->interruption_listener = CFBridgingRetain (listener);
530- } else {
531- SDLInterruptionListener *listener = nil ;
532- listener = (SDLInterruptionListener *)CFBridgingRelease (device->hidden ->interruption_listener );
533- [center removeObserver: listener];
534- @synchronized (listener) {
535- listener.device = NULL ;
536- }
537- }
538434 }
539435
540436 return true ;
@@ -640,7 +536,14 @@ static void COREAUDIO_CloseDevice(SDL_AudioDevice *device)
640536 }
641537
642538 #ifndef MACOSX_COREAUDIO
643- UpdateAudioSession (device, false , true );
539+ if (device->hidden ->interruption_listener ) {
540+ SDLInterruptionListener *listener = (SDLInterruptionListener *)CFBridgingRelease (device->hidden ->interruption_listener );
541+ device->hidden ->interruption_listener = NULL ;
542+ [[NSNotificationCenter defaultCenter ] removeObserver: listener];
543+ @synchronized (listener) {
544+ listener.device = NULL ;
545+ }
546+ }
644547 #endif
645548
646549 if (device->hidden ->ready_semaphore ) {
@@ -651,6 +554,18 @@ static void COREAUDIO_CloseDevice(SDL_AudioDevice *device)
651554 SDL_free (device->hidden ->audioBuffer );
652555 SDL_free (device->hidden ->thread_error );
653556 SDL_free (device->hidden );
557+
558+ #ifndef MACOSX_COREAUDIO
559+ SDL_AtomicDecRef (device->recording ? &open_recording_device_count : &open_playback_device_count);
560+
561+ SDL_assert (SDL_GetAtomicInt (&open_playback_device_count) >= 0 );
562+ SDL_assert (SDL_GetAtomicInt (&open_recording_device_count) >= 0 );
563+
564+ if (session_active && !SDL_GetAtomicInt (&open_playback_device_count) && !SDL_GetAtomicInt (&open_recording_device_count)) {
565+ [[AVAudioSession sharedInstance ] setActive: NO error: nil ];
566+ session_active = false ;
567+ }
568+ #endif
654569}
655570
656571#ifdef MACOSX_COREAUDIO
@@ -899,26 +814,32 @@ static bool COREAUDIO_OpenDevice(SDL_AudioDevice *device)
899814 }
900815
901816 #ifndef MACOSX_COREAUDIO
902- if (!UpdateAudioSession (device, true , true )) {
817+ SDL_AtomicIncRef (device->recording ? &open_recording_device_count : &open_playback_device_count);
818+ if (!UpdateAudioSession (device, true )) {
903819 return false ;
904820 }
905821
822+ AVAudioSession *session = [AVAudioSession sharedInstance ];
906823 // Stop CoreAudio from doing expensive audio rate conversion
907- @autoreleasepool {
908- AVAudioSession *session = [AVAudioSession sharedInstance ];
909- [session setPreferredSampleRate: device->spec.freq error: nil ];
910- device->spec .freq = (int )session.sampleRate ;
911- #ifdef SDL_PLATFORM_TVOS
912- if (device->recording ) {
913- [session setPreferredInputNumberOfChannels: device->spec.channels error: nil ];
914- device->spec .channels = (int )session.preferredInputNumberOfChannels ;
915- } else {
916- [session setPreferredOutputNumberOfChannels: device->spec.channels error: nil ];
917- device->spec .channels = (int )session.preferredOutputNumberOfChannels ;
824+ [session setPreferredSampleRate: device->spec.freq error: nil ];
825+ device->spec .freq = (int )session.sampleRate ;
826+ #ifdef SDL_PLATFORM_TVOS
827+ if (device->recording ) {
828+ [session setPreferredInputNumberOfChannels: device->spec.channels error: nil ];
829+ device->spec .channels = (int )session.preferredInputNumberOfChannels ;
830+ } else {
831+ [session setPreferredOutputNumberOfChannels: device->spec.channels error: nil ];
832+ device->spec .channels = (int )session.preferredOutputNumberOfChannels ;
833+ }
834+ #else
835+ // Calling setPreferredOutputNumberOfChannels seems to break audio output on iOS
836+ #endif // SDL_PLATFORM_TVOS
837+
838+ if (!session_active) {
839+ if (![session setActive: YES error: &err]) {
840+ return SDL_SetError (" Could not activate Audio Session: %s " , err.description .UTF8String );
918841 }
919- #else
920- // Calling setPreferredOutputNumberOfChannels seems to break audio output on iOS
921- #endif // SDL_PLATFORM_TVOS
842+ session_active = true ;
922843 }
923844 #endif
924845
@@ -992,13 +913,40 @@ static bool COREAUDIO_OpenDevice(SDL_AudioDevice *device)
992913 SDL_DestroySemaphore (device->hidden ->ready_semaphore );
993914 device->hidden ->ready_semaphore = NULL ;
994915
995- if (( device->hidden ->thread != NULL ) && (device-> hidden -> thread_error != NULL ) ) {
916+ if (device->hidden ->thread_error != NULL ) {
996917 SDL_WaitThread (device->hidden ->thread , NULL );
997918 device->hidden ->thread = NULL ;
998919 return SDL_SetError (" %s " , device->hidden ->thread_error );
999920 }
1000921
1001- return (device->hidden ->thread != NULL );
922+ #ifndef MACOSX_COREAUDIO
923+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter ];
924+ SDLInterruptionListener *listener = [SDLInterruptionListener new ];
925+ listener.device = device;
926+
927+ [center addObserver: listener
928+ selector: @selector (audioSessionInterruption: )
929+ name: AVAudioSessionInterruptionNotification
930+ object: session];
931+
932+ /* An interruption end notification is not guaranteed to be sent if
933+ we were previously interrupted... resuming if needed when the app
934+ becomes active seems to be the way to go. */
935+ // Note: object: below needs to be nil, as otherwise it filters by the object, and session doesn't send foreground / active notifications.
936+ [center addObserver: listener
937+ selector: @selector (applicationBecameActive: )
938+ name: UIApplicationDidBecomeActiveNotification
939+ object: nil ];
940+
941+ [center addObserver: listener
942+ selector: @selector (applicationBecameActive: )
943+ name: UIApplicationWillEnterForegroundNotification
944+ object: nil ];
945+
946+ device->hidden ->interruption_listener = CFBridgingRetain (listener);
947+ #endif
948+
949+ return true ;
1002950}
1003951
1004952static void COREAUDIO_DeinitializeStart (void )
0 commit comments