Browse Source

Merge pull request #1646 from cigumo/ios-audio-interruption-handling

adds iOS audio interruption handling and recovery
Alex Szpakowski 4 years ago
parent
commit
73c79d1b73

+ 6 - 0
src/common/ios.h

@@ -78,6 +78,12 @@ bool setAudioMixWithOthers(bool mixEnabled);
  **/
 bool hasBackgroundMusic();
 
+/**
+ * Registers notifications to handle and restore audio interruptions
+ **/
+void initAudioSessionInterruptionHandler();
+void destroyAudioSessionInterruptionHandler();
+    
 /**
  * Gets the area in the window that is safe for UI to render to (not covered by
  * the status bar, notch, etc.)

+ 81 - 0
src/common/ios.mm

@@ -28,6 +28,8 @@
 #import <AudioToolbox/AudioServices.h>
 #import <AVFoundation/AVFoundation.h>
 
+#include "modules/audio/Audio.h"
+
 #include <vector>
 
 #include <SDL_events.h>
@@ -200,6 +202,53 @@ static int dropFileEventFilter(void *userdata, SDL_Event *event)
 	}
 }
 
+@interface LoveAudioInterruptionListener : NSObject
+@end
+
+@implementation LoveAudioInterruptionListener
+
++ (id) shared
+{
+	// thread-safe singleton
+	static dispatch_once_t pred = 0;
+	__strong static id _shared = nil;
+	dispatch_once(&pred, ^{
+			_shared = [[self alloc] init];
+		});
+	return _shared;
+}
+
+- (void)audioSessionInterruption:(NSNotification *)note
+{
+	@synchronized (self) {
+		auto audio = love::Module::getInstance<love::audio::Audio>(love::Module::M_AUDIO);
+		if (!audio) {
+			NSLog(@"LoveAudioInterruptionListener could not get love audio module");
+			return;
+		}
+		NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
+		if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
+			audio->pauseContext();
+		} else {
+			audio->resumeContext();
+		}
+	}
+}
+
+- (void)applicationBecameActive:(NSNotification *)note
+{
+	@synchronized (self) {
+		auto audio = love::Module::getInstance<love::audio::Audio>(love::Module::M_AUDIO);
+		if (!audio) {
+			NSLog(@"ERROR:could not get love audio module");
+			return;
+		}
+		audio->resumeContext();
+	}
+}
+
+@end
+
 namespace love
 {
 namespace ios
@@ -376,6 +425,38 @@ bool hasBackgroundMusic()
 	}
 }
 
+void initAudioSessionInterruptionHandler()
+{
+	@autoreleasepool
+	{
+		AVAudioSession *session = [AVAudioSession sharedInstance];
+		NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
+		[center addObserver:[LoveAudioInterruptionListener shared]
+			   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:[LoveAudioInterruptionListener shared]
+			   selector:@selector(applicationBecameActive:)
+			       name:UIApplicationDidBecomeActiveNotification
+			     object:nil];
+
+		[center addObserver:[LoveAudioInterruptionListener shared]
+			   selector:@selector(applicationBecameActive:)
+			       name:UIApplicationWillEnterForegroundNotification
+			     object:nil];
+	}
+}
+
+void destroyAudioSessionInterruptionHandler()
+{
+	[[NSNotificationCenter defaultCenter] removeObserver:[LoveAudioInterruptionListener shared]];
+}
+
 Rect getSafeArea(SDL_Window *window)
 {
 	@autoreleasepool

+ 6 - 0
src/modules/audio/Audio.h

@@ -291,6 +291,12 @@ public:
 	 **/
 	static bool setMixWithSystem(bool mix);
 
+	/**
+	 * Pause/resume audio context
+	 */
+	virtual void pauseContext() = 0;
+	virtual void resumeContext() = 0;
+
 private:
 
 	static StringMap<DistanceModel, DISTANCE_MAX_ENUM>::Entry distanceModelEntries[];

+ 9 - 0
src/modules/audio/null/Audio.cpp

@@ -203,6 +203,15 @@ bool Audio::isEFXsupported() const
 	return false;
 }
 
+void Audio::pauseContext()
+{
+}
+
+void Audio::resumeContext()
+{
+}
+
+
 } // null
 } // audio
 } // love

+ 3 - 0
src/modules/audio/null/Audio.h

@@ -86,6 +86,9 @@ public:
 	int getMaxSourceEffects() const;
 	bool isEFXsupported() const;
 
+	void pauseContext();
+	void resumeContext();
+
 private:
 	float volume;
 	DistanceModel distanceModel;

+ 23 - 0
src/modules/audio/openal/Audio.cpp

@@ -26,6 +26,10 @@
 #include <cstdlib>
 #include <iostream>
 
+#ifdef LOVE_IOS
+#include "common/ios.h"
+#endif
+
 namespace love
 {
 namespace audio
@@ -189,10 +193,18 @@ Audio::Audio()
 
 	poolThread = new PoolThread(pool);
 	poolThread->start();
+	
+#ifdef LOVE_IOS
+	love::ios::initAudioSessionInterruptionHandler();
+#endif
+        
 }
 
 Audio::~Audio()
 {
+#ifdef LOVE_IOS
+	love::ios::destroyAudioSessionInterruptionHandler();
+#endif
 	poolThread->setFinish();
 	poolThread->wait();
 
@@ -293,6 +305,17 @@ std::vector<love::audio::Source*> Audio::pause()
 	return Source::pause(pool);
 }
 
+void Audio::pauseContext()
+{
+	alcMakeContextCurrent(nullptr);
+}
+
+void Audio::resumeContext()
+{
+	if (context && alcGetCurrentContext() != context)
+		alcMakeContextCurrent(context);
+}
+
 void Audio::setVolume(float volume)
 {
 	alListenerf(AL_GAIN, volume);

+ 2 - 0
src/modules/audio/openal/Audio.h

@@ -95,6 +95,8 @@ public:
 	void pause(love::audio::Source *source);
 	void pause(const std::vector<love::audio::Source*> &sources);
 	std::vector<love::audio::Source*> pause();
+	void pauseContext();
+	void resumeContext();
 	void setVolume(float volume);
 	float getVolume() const;