mirror of
https://github.com/kyleneideck/BackgroundMusic
synced 2024-11-23 04:33:03 +00:00
Fix BGMApp crashing at launch if BGMDriver isn't installed.
This commit is contained in:
parent
18aa97f055
commit
6117bc285c
2 changed files with 60 additions and 53 deletions
|
@ -17,7 +17,7 @@
|
||||||
// BGMAudioDeviceManager.mm
|
// BGMAudioDeviceManager.mm
|
||||||
// BGMApp
|
// BGMApp
|
||||||
//
|
//
|
||||||
// Copyright © 2016, 2017 Kyle Neideck
|
// Copyright © 2016-2018 Kyle Neideck
|
||||||
//
|
//
|
||||||
|
|
||||||
// Self Include
|
// Self Include
|
||||||
|
@ -35,12 +35,21 @@
|
||||||
// PublicUtility Includes
|
// PublicUtility Includes
|
||||||
#import "CAHALAudioSystemObject.h"
|
#import "CAHALAudioSystemObject.h"
|
||||||
#import "CAAutoDisposer.h"
|
#import "CAAutoDisposer.h"
|
||||||
|
#import "CAAtomic.h"
|
||||||
|
|
||||||
|
|
||||||
#pragma clang assume_nonnull begin
|
#pragma clang assume_nonnull begin
|
||||||
|
|
||||||
@implementation BGMAudioDeviceManager {
|
@implementation BGMAudioDeviceManager {
|
||||||
BGMBackgroundMusicDevice bgmDevice;
|
// This ivar is a pointer so that BGMBackgroundMusicDevice's constructor doesn't get called
|
||||||
|
// during [BGMAudioDeviceManager alloc] when the ivars are initialised. It queries the HAL for
|
||||||
|
// BGMDevice's AudioObject ID, which might throw a CAException, most likely because BGMDevice
|
||||||
|
// isn't installed.
|
||||||
|
//
|
||||||
|
// That would be the only way for [BGMAudioDeviceManager alloc] to throw a CAException, so we
|
||||||
|
// could wrap that call in a try/catch block instead, but it would make the code a bit
|
||||||
|
// confusing.
|
||||||
|
BGMBackgroundMusicDevice* bgmDevice;
|
||||||
BGMAudioDevice outputDevice;
|
BGMAudioDevice outputDevice;
|
||||||
|
|
||||||
BGMDeviceControlSync deviceControlSync;
|
BGMDeviceControlSync deviceControlSync;
|
||||||
|
@ -64,7 +73,7 @@
|
||||||
outputVolumeMenuItem = nil;
|
outputVolumeMenuItem = nil;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bgmDevice = BGMBackgroundMusicDevice();
|
bgmDevice = new BGMBackgroundMusicDevice;
|
||||||
} catch (const CAException& e) {
|
} catch (const CAException& e) {
|
||||||
LogError("BGMAudioDeviceManager::initWithError: BGMDevice not found. (%d)", e.GetError());
|
LogError("BGMAudioDeviceManager::initWithError: BGMDevice not found. (%d)", e.GetError());
|
||||||
|
|
||||||
|
@ -81,11 +90,6 @@
|
||||||
} catch (const CAException& e) {
|
} catch (const CAException& e) {
|
||||||
LogError("BGMAudioDeviceManager::initWithError: failed to init output device (%d)",
|
LogError("BGMAudioDeviceManager::initWithError: failed to init output device (%d)",
|
||||||
e.GetError());
|
e.GetError());
|
||||||
outputDevice.SetObjectID(kAudioObjectUnknown);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outputDevice.GetObjectID() == kAudioObjectUnknown) {
|
|
||||||
LogError("BGMAudioDeviceManager::initWithError: output device not found");
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
*error = [NSError errorWithDomain:@kBGMAppBundleID code:kBGMErrorCode_OutputDeviceNotFound userInfo:nil];
|
*error = [NSError errorWithDomain:@kBGMAppBundleID code:kBGMErrorCode_OutputDeviceNotFound userInfo:nil];
|
||||||
|
@ -99,6 +103,19 @@
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) dealloc {
|
||||||
|
@try {
|
||||||
|
[stateLock lock];
|
||||||
|
|
||||||
|
if (bgmDevice) {
|
||||||
|
delete bgmDevice;
|
||||||
|
bgmDevice = nullptr;
|
||||||
|
}
|
||||||
|
} @finally {
|
||||||
|
[stateLock unlock];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Throws a CAException if it fails to set the output device.
|
// Throws a CAException if it fails to set the output device.
|
||||||
- (void) initOutputDevice {
|
- (void) initOutputDevice {
|
||||||
CAHALAudioSystemObject audioSystem;
|
CAHALAudioSystemObject audioSystem;
|
||||||
|
@ -189,20 +206,13 @@
|
||||||
// kAudioHardwarePropertyDefaultSystemOutputDevice in AudioHardware.h.
|
// kAudioHardwarePropertyDefaultSystemOutputDevice in AudioHardware.h.
|
||||||
|
|
||||||
- (NSError* __nullable) setBGMDeviceAsOSDefault {
|
- (NSError* __nullable) setBGMDeviceAsOSDefault {
|
||||||
// Copy bgmDevice so we can call the HAL without holding stateLock. See startPlayThroughSync.
|
|
||||||
BGMBackgroundMusicDevice bgmDev;
|
|
||||||
|
|
||||||
@try {
|
|
||||||
[stateLock lock];
|
|
||||||
bgmDev = bgmDevice;
|
|
||||||
} @finally {
|
|
||||||
[stateLock unlock];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bgmDev.SetAsOSDefault();
|
// Intentionally avoid taking stateLock before making calls to the HAL. See
|
||||||
|
// startPlayThroughSync.
|
||||||
|
CAMemoryBarrier();
|
||||||
|
bgmDevice->SetAsOSDefault();
|
||||||
} catch (const CAException& e) {
|
} catch (const CAException& e) {
|
||||||
NSLog(@"SetAsOSDefault threw CAException (%d)", e.GetError());
|
BGMLogExceptionIn("BGMAudioDeviceManager::setBGMDeviceAsOSDefault", e);
|
||||||
return [NSError errorWithDomain:@kBGMAppBundleID code:e.GetError() userInfo:nil];
|
return [NSError errorWithDomain:@kBGMAppBundleID code:e.GetError() userInfo:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,13 +221,12 @@
|
||||||
|
|
||||||
- (NSError* __nullable) unsetBGMDeviceAsOSDefault {
|
- (NSError* __nullable) unsetBGMDeviceAsOSDefault {
|
||||||
// Copy the devices so we can call the HAL without holding stateLock. See startPlayThroughSync.
|
// Copy the devices so we can call the HAL without holding stateLock. See startPlayThroughSync.
|
||||||
try {
|
BGMBackgroundMusicDevice* bgmDeviceCopy;
|
||||||
BGMBackgroundMusicDevice bgmDev;
|
|
||||||
AudioDeviceID outputDeviceID;
|
AudioDeviceID outputDeviceID;
|
||||||
|
|
||||||
@try {
|
@try {
|
||||||
[stateLock lock];
|
[stateLock lock];
|
||||||
bgmDev = bgmDevice;
|
bgmDeviceCopy = bgmDevice;
|
||||||
outputDeviceID = outputDevice.GetObjectID();
|
outputDeviceID = outputDevice.GetObjectID();
|
||||||
} @finally {
|
} @finally {
|
||||||
[stateLock unlock];
|
[stateLock unlock];
|
||||||
|
@ -229,7 +238,8 @@
|
||||||
userInfo:nil];
|
userInfo:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
bgmDev.UnsetAsOSDefault(outputDeviceID);
|
try {
|
||||||
|
bgmDeviceCopy->UnsetAsOSDefault(outputDeviceID);
|
||||||
} catch (const CAException& e) {
|
} catch (const CAException& e) {
|
||||||
BGMLogExceptionIn("BGMAudioDeviceManager::unsetBGMDeviceAsOSDefault", e);
|
BGMLogExceptionIn("BGMAudioDeviceManager::unsetBGMDeviceAsOSDefault", e);
|
||||||
return [NSError errorWithDomain:@kBGMAppBundleID code:e.GetError() userInfo:nil];
|
return [NSError errorWithDomain:@kBGMAppBundleID code:e.GetError() userInfo:nil];
|
||||||
|
@ -241,7 +251,7 @@
|
||||||
#pragma mark Accessors
|
#pragma mark Accessors
|
||||||
|
|
||||||
- (BGMBackgroundMusicDevice) bgmDevice {
|
- (BGMBackgroundMusicDevice) bgmDevice {
|
||||||
return bgmDevice;
|
return *bgmDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CAHALAudioDevice) outputDevice {
|
- (CAHALAudioDevice) outputDevice {
|
||||||
|
@ -251,13 +261,13 @@
|
||||||
- (void) setVolume:(SInt32)volume
|
- (void) setVolume:(SInt32)volume
|
||||||
forAppWithProcessID:(pid_t)processID
|
forAppWithProcessID:(pid_t)processID
|
||||||
bundleID:(NSString* __nullable)bundleID {
|
bundleID:(NSString* __nullable)bundleID {
|
||||||
bgmDevice.SetAppVolume(volume, processID, (__bridge_retained CFStringRef)bundleID);
|
bgmDevice->SetAppVolume(volume, processID, (__bridge_retained CFStringRef)bundleID);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setPanPosition:(SInt32)pan
|
- (void) setPanPosition:(SInt32)pan
|
||||||
forAppWithProcessID:(pid_t)processID
|
forAppWithProcessID:(pid_t)processID
|
||||||
bundleID:(NSString* __nullable)bundleID {
|
bundleID:(NSString* __nullable)bundleID {
|
||||||
bgmDevice.SetAppPanPosition(pan, processID, (__bridge_retained CFStringRef)bundleID);
|
bgmDevice->SetAppPanPosition(pan, processID, (__bridge_retained CFStringRef)bundleID);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) isOutputDevice:(AudioObjectID)deviceID {
|
- (BOOL) isOutputDevice:(AudioObjectID)deviceID {
|
||||||
|
@ -315,16 +325,12 @@ forAppWithProcessID:(pid_t)processID
|
||||||
DebugMsg("BGMAudioDeviceManager::setOutputDeviceWithIDImpl: Setting output device. newDeviceID=%u",
|
DebugMsg("BGMAudioDeviceManager::setOutputDeviceWithIDImpl: Setting output device. newDeviceID=%u",
|
||||||
newDeviceID);
|
newDeviceID);
|
||||||
|
|
||||||
AudioDeviceID currentDeviceID = outputDevice.GetObjectID(); // (GetObjectID doesn't throw.)
|
|
||||||
|
|
||||||
@try {
|
@try {
|
||||||
[stateLock lock];
|
[stateLock lock];
|
||||||
|
|
||||||
try {
|
AudioDeviceID currentDeviceID = outputDevice.GetObjectID(); // (Doesn't throw.)
|
||||||
// Re-read the device ID after entering the monitor. (The initial read is because
|
|
||||||
// currentDeviceID is used in the catch blocks.)
|
|
||||||
currentDeviceID = outputDevice.GetObjectID();
|
|
||||||
|
|
||||||
|
try {
|
||||||
if (newDeviceID != currentDeviceID) {
|
if (newDeviceID != currentDeviceID) {
|
||||||
BGMAudioDevice newOutputDevice(newDeviceID);
|
BGMAudioDevice newOutputDevice(newDeviceID);
|
||||||
[self setOutputDeviceForPlaythroughAndControlSync:newOutputDevice];
|
[self setOutputDeviceForPlaythroughAndControlSync:newOutputDevice];
|
||||||
|
@ -348,7 +354,7 @@ forAppWithProcessID:(pid_t)processID
|
||||||
playThrough.StopIfIdle();
|
playThrough.StopIfIdle();
|
||||||
playThrough_UISounds.StopIfIdle();
|
playThrough_UISounds.StopIfIdle();
|
||||||
}
|
}
|
||||||
} catch (CAException e) {
|
} catch (const CAException& e) {
|
||||||
BGMAssert(e.GetError() != kAudioHardwareNoError,
|
BGMAssert(e.GetError() != kAudioHardwareNoError,
|
||||||
"CAException with kAudioHardwareNoError");
|
"CAException with kAudioHardwareNoError");
|
||||||
|
|
||||||
|
@ -377,17 +383,17 @@ forAppWithProcessID:(pid_t)processID
|
||||||
playThrough.Deactivate();
|
playThrough.Deactivate();
|
||||||
playThrough_UISounds.Deactivate();
|
playThrough_UISounds.Deactivate();
|
||||||
|
|
||||||
deviceControlSync.SetDevices(bgmDevice, newOutputDevice);
|
deviceControlSync.SetDevices(*bgmDevice, newOutputDevice);
|
||||||
deviceControlSync.Activate();
|
deviceControlSync.Activate();
|
||||||
|
|
||||||
// Stream audio from BGMDevice to the new output device. This blocks while the old device stops
|
// Stream audio from BGMDevice to the new output device. This blocks while the old device stops
|
||||||
// IO.
|
// IO.
|
||||||
playThrough.SetDevices(&bgmDevice, &newOutputDevice);
|
playThrough.SetDevices(bgmDevice, &newOutputDevice);
|
||||||
playThrough.Activate();
|
playThrough.Activate();
|
||||||
|
|
||||||
// TODO: Support setting different devices as the default output device and the default system
|
// TODO: Support setting different devices as the default output device and the default system
|
||||||
// output device the way OS X does?
|
// output device the way OS X does?
|
||||||
BGMAudioDevice uiSoundsDevice = bgmDevice.GetUISoundsBGMDeviceInstance();
|
BGMAudioDevice uiSoundsDevice = bgmDevice->GetUISoundsBGMDeviceInstance();
|
||||||
playThrough_UISounds.SetDevices(&uiSoundsDevice, &newOutputDevice);
|
playThrough_UISounds.SetDevices(&uiSoundsDevice, &newOutputDevice);
|
||||||
playThrough_UISounds.Activate();
|
playThrough_UISounds.Activate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ BGMBackgroundMusicDevice::BGMBackgroundMusicDevice()
|
||||||
{
|
{
|
||||||
if((GetObjectID() == kAudioObjectUnknown) || (mUISoundsBGMDevice == kAudioObjectUnknown))
|
if((GetObjectID() == kAudioObjectUnknown) || (mUISoundsBGMDevice == kAudioObjectUnknown))
|
||||||
{
|
{
|
||||||
|
LogError("BGMBackgroundMusicDevice::BGMBackgroundMusicDevice: Error getting BGMDevice ID");
|
||||||
Throw(CAException(kAudioHardwareIllegalOperationError));
|
Throw(CAException(kAudioHardwareIllegalOperationError));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue