diff --git a/src/common/ios.h b/src/common/ios.h index 8e436d94b..7c103f9c2 100644 --- a/src/common/ios.h +++ b/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.) diff --git a/src/common/ios.mm b/src/common/ios.mm index 648b56c61..095f570b1 100644 --- a/src/common/ios.mm +++ b/src/common/ios.mm @@ -28,6 +28,8 @@ #import #import +#include "modules/audio/Audio.h" + #include #include @@ -200,6 +202,56 @@ 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::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::Module::M_AUDIO); + if (!audio) + { + NSLog(@"ERROR:could not get love audio module"); + return; + } + audio->resumeContext(); + } +} + +@end // LoveAudioInterruptionListener + namespace love { namespace ios @@ -380,6 +432,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 diff --git a/src/modules/audio/Audio.h b/src/modules/audio/Audio.h index 7a72882be..d2bfc4132 100644 --- a/src/modules/audio/Audio.h +++ b/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::Entry distanceModelEntries[]; diff --git a/src/modules/audio/null/Audio.cpp b/src/modules/audio/null/Audio.cpp index f64d6cf7e..8fa9eee8d 100644 --- a/src/modules/audio/null/Audio.cpp +++ b/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 diff --git a/src/modules/audio/null/Audio.h b/src/modules/audio/null/Audio.h index cecc2f7dc..367c25bad 100644 --- a/src/modules/audio/null/Audio.h +++ b/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; diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index 75a23d54f..d62a272ac 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -26,6 +26,10 @@ #include #include +#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 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); diff --git a/src/modules/audio/openal/Audio.h b/src/modules/audio/openal/Audio.h index 18fcae6d2..75e6a26bc 100644 --- a/src/modules/audio/openal/Audio.h +++ b/src/modules/audio/openal/Audio.h @@ -95,6 +95,8 @@ public: void pause(love::audio::Source *source); void pause(const std::vector &sources); std::vector pause(); + void pauseContext(); + void resumeContext(); void setVolume(float volume); float getVolume() const; diff --git a/src/modules/graphics/opengl/StreamBuffer.cpp b/src/modules/graphics/opengl/StreamBuffer.cpp index 966fa2a1f..672b3f3df 100644 --- a/src/modules/graphics/opengl/StreamBuffer.cpp +++ b/src/modules/graphics/opengl/StreamBuffer.cpp @@ -499,7 +499,7 @@ love::graphics::StreamBuffer *CreateStreamBuffer(BufferType mode, size_t size) { // AMD's pinned memory seems to be faster than persistent mapping, // on AMD GPUs. - if (GLAD_AMD_pinned_memory) + if (GLAD_AMD_pinned_memory && gl.getVendor() == OpenGL::VENDOR_AMD) { try {