Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e528c7de0 | |||
| 03135dbc6e | |||
| 679d624860 | |||
| 55e9f60774 | |||
| e3fcbdb37e | |||
| 1ee9fa348e | |||
| 3684483543 | |||
| 30a1735346 | |||
| 4dba9412fb | |||
| 90bceb9887 | |||
| 6c9ca6e85c | |||
| 82ea2e4803 | |||
| bbe65a2431 | |||
| a229791ade | |||
| be4135523e | |||
| b58ad2a1f8 | |||
| 960fe0d28d | |||
| d827e7e0d8 | |||
| 34071e633f | |||
| d021dff7a6 | |||
| e95f371305 | |||
| 6fb281c3ce | |||
| 3f62b012c3 | |||
| c70a22dd24 | |||
| ccb709fc02 | |||
| 480d769c26 | |||
| 3eac1f5dab | |||
| eda3505f1a | |||
| 44082ac920 | |||
| f2a0898590 | |||
| b707513e49 | |||
| 8acc5d4c9e | |||
| ad5fe4ecbb | |||
| 7cc0d19182 | |||
| 9a5ed7c2b5 | |||
| 5981e05bb1 | |||
| e87c43dc52 |
+23
@@ -0,0 +1,23 @@
|
||||
language: objective-c
|
||||
os: osx
|
||||
osx_image: xcode7.3
|
||||
xcode_sdk: macosx10.11
|
||||
sudo: required
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
install:
|
||||
- sudo xcodebuild -project BGMApp/BGMAppTests/NullAudio/AudioDriverExamples.xcodeproj -target NullAudio DSTROOT="/" install
|
||||
- sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod || sudo killall coreaudiod
|
||||
script:
|
||||
- yes | ./build_and_install.sh
|
||||
- cat build_and_install.log
|
||||
- find */build/Release/*/ -type f -exec md5 {} \;
|
||||
- system_profiler SPAudioDataType
|
||||
- say -a '?'
|
||||
- osascript -e 'tell application "Background Music" to quit'
|
||||
- xcodebuild -workspace BGM.xcworkspace -scheme 'Background Music Device' test
|
||||
- xcodebuild -workspace BGM.xcworkspace -scheme 'Background Music' test
|
||||
- xcodebuild -workspace BGM.xcworkspace -scheme 'BGMXPCHelper' test
|
||||
- yes | ./uninstall.sh
|
||||
- ls /Applications /Library/Audio/Plug-Ins/HAL /usr/local/libexec /Library/Application\ Support/Background\ Music
|
||||
@@ -25,7 +25,6 @@
|
||||
1C1963091BCAF677008A4DF7 /* CAHostTimeBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1963071BCAF677008A4DF7 /* CAHostTimeBase.cpp */; };
|
||||
1C2336DA1BEAB6E7004C1C4E /* BGMMusicPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C2336D91BEAB6E7004C1C4E /* BGMMusicPlayer.m */; };
|
||||
1C2336DF1BEAE10C004C1C4E /* BGMSpotify.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C2336DE1BEAE10C004C1C4E /* BGMSpotify.m */; };
|
||||
1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */; };
|
||||
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C4699461BD5C0E400F78043 /* BGMiTunes.m */; };
|
||||
1C46994E1BD7694C00F78043 /* BGMDeviceControlSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */; };
|
||||
1CB8B33D1BBA75EF000E2DD1 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B33C1BBA75EF000E2DD1 /* AppDelegate.mm */; };
|
||||
@@ -48,12 +47,16 @@
|
||||
276972911CB16008007A2F7C /* safe_install_dir.sh in Resources */ = {isa = PBXBuildFile; fileRef = 276972901CB16008007A2F7C /* safe_install_dir.sh */; };
|
||||
276972921CB1603E007A2F7C /* safe_install_dir.sh in Resources */ = {isa = PBXBuildFile; fileRef = 276972901CB16008007A2F7C /* safe_install_dir.sh */; };
|
||||
277170161CA24D7C00AB34B4 /* BGMXPCListenerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 277170151CA24D7C00AB34B4 /* BGMXPCListenerDelegate.m */; };
|
||||
2783ABB71CE36CDB00F11FD4 /* BGMApps.m in Sources */ = {isa = PBXBuildFile; fileRef = 2783ABB61CE36CDB00F11FD4 /* BGMApps.m */; };
|
||||
2783ABB81CE36CDB00F11FD4 /* BGMApps.m in Sources */ = {isa = PBXBuildFile; fileRef = 2783ABB61CE36CDB00F11FD4 /* BGMApps.m */; };
|
||||
278D71E91CABB6FF00899CF9 /* BGMXPCHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 279597401C996E2000A002FB /* BGMXPCHelperTests.m */; };
|
||||
278D71F61CABBC3B00899CF9 /* BGMXPCHelperTests-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 278D71F51CABBC3B00899CF9 /* BGMXPCHelperTests-Info.plist */; };
|
||||
2795973B1C982E4E00A002FB /* BGMXPCListener.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2795973A1C982E4E00A002FB /* BGMXPCListener.mm */; };
|
||||
27D643BE1C9FB84C00737F6E /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 27D643BB1C9FB84C00737F6E /* Info.plist */; };
|
||||
27D643C01C9FB99200737F6E /* BGMXPCHelperService.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D643BA1C9FB84C00737F6E /* BGMXPCHelperService.m */; };
|
||||
27D643C11C9FB99200737F6E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D643BC1C9FB84C00737F6E /* main.m */; };
|
||||
27DEE37C1D4F8DA300DBFDF1 /* BGMAppVolumes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27DEE37A1D4F8DA300DBFDF1 /* BGMAppVolumes.mm */; };
|
||||
27F7D4901D2483B100821C4B /* BGMDecibel.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F7D48F1D2483B100821C4B /* BGMDecibel.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -113,8 +116,6 @@
|
||||
1C2336DC1BEAB73F004C1C4E /* BGMMusicPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BGMMusicPlayer.h; path = "Music Players/BGMMusicPlayer.h"; sourceTree = "<group>"; };
|
||||
1C2336DD1BEAE10C004C1C4E /* BGMSpotify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMSpotify.h; path = "Music Players/BGMSpotify.h"; sourceTree = "<group>"; };
|
||||
1C2336DE1BEAE10C004C1C4E /* BGMSpotify.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMSpotify.m; path = "Music Players/BGMSpotify.m"; sourceTree = "<group>"; };
|
||||
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMAppVolumes.mm; sourceTree = "<group>"; };
|
||||
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMAppVolumes.h; sourceTree = "<group>"; };
|
||||
1C4699461BD5C0E400F78043 /* BGMiTunes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMiTunes.m; path = "Music Players/BGMiTunes.m"; sourceTree = "<group>"; };
|
||||
1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMDeviceControlSync.cpp; sourceTree = "<group>"; };
|
||||
1C46994D1BD7694C00F78043 /* BGMDeviceControlSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMDeviceControlSync.h; sourceTree = "<group>"; };
|
||||
@@ -160,6 +161,8 @@
|
||||
2771700F1CA0C83B00AB34B4 /* BGM_Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGM_Utils.h; path = ../SharedSource/BGM_Utils.h; sourceTree = "<group>"; };
|
||||
277170141CA24D7C00AB34B4 /* BGMXPCListenerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMXPCListenerDelegate.h; path = BGMXPCHelper/BGMXPCListenerDelegate.h; sourceTree = SOURCE_ROOT; };
|
||||
277170151CA24D7C00AB34B4 /* BGMXPCListenerDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMXPCListenerDelegate.m; path = BGMXPCHelper/BGMXPCListenerDelegate.m; sourceTree = SOURCE_ROOT; };
|
||||
2783ABB51CE36CDB00F11FD4 /* BGMApps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMApps.h; path = "App Volumes/BGMApps.h"; sourceTree = "<group>"; };
|
||||
2783ABB61CE36CDB00F11FD4 /* BGMApps.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMApps.m; path = "App Volumes/BGMApps.m"; sourceTree = "<group>"; };
|
||||
278D71F11CABB6FF00899CF9 /* BGMXPCHelperTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BGMXPCHelperTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
278D71F51CABBC3B00899CF9 /* BGMXPCHelperTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "BGMXPCHelperTests-Info.plist"; sourceTree = "<group>"; };
|
||||
2795970D1C91589B00A002FB /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
|
||||
@@ -173,6 +176,11 @@
|
||||
27D643BB1C9FB84C00737F6E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = BGMXPCHelper/Info.plist; sourceTree = SOURCE_ROOT; };
|
||||
27D643BC1C9FB84C00737F6E /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = BGMXPCHelper/main.m; sourceTree = SOURCE_ROOT; };
|
||||
27D643C41C9FBE5600737F6E /* BGM_TestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGM_TestUtils.h; path = ../SharedSource/BGM_TestUtils.h; sourceTree = "<group>"; };
|
||||
27DEE37A1D4F8DA300DBFDF1 /* BGMAppVolumes.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMAppVolumes.mm; path = "App Volumes/BGMAppVolumes.mm"; sourceTree = "<group>"; };
|
||||
27DEE37B1D4F8DA300DBFDF1 /* BGMAppVolumes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMAppVolumes.h; path = "App Volumes/BGMAppVolumes.h"; sourceTree = "<group>"; };
|
||||
27F7D48E1D2483B100821C4B /* BGMDecibel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMDecibel.h; path = "Music Players/BGMDecibel.h"; sourceTree = "<group>"; };
|
||||
27F7D48F1D2483B100821C4B /* BGMDecibel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMDecibel.m; path = "Music Players/BGMDecibel.m"; sourceTree = "<group>"; };
|
||||
27F7D4911D2484A300821C4B /* Decibel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Decibel.h; path = "Music Players/Decibel.h"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -280,6 +288,8 @@
|
||||
children = (
|
||||
1C2336DC1BEAB73F004C1C4E /* BGMMusicPlayer.h */,
|
||||
1C2336D91BEAB6E7004C1C4E /* BGMMusicPlayer.m */,
|
||||
27F7D48E1D2483B100821C4B /* BGMDecibel.h */,
|
||||
27F7D48F1D2483B100821C4B /* BGMDecibel.m */,
|
||||
1C2336DB1BEAB73F004C1C4E /* BGMiTunes.h */,
|
||||
1C4699461BD5C0E400F78043 /* BGMiTunes.m */,
|
||||
1C2336DD1BEAE10C004C1C4E /* BGMSpotify.h */,
|
||||
@@ -325,8 +335,7 @@
|
||||
children = (
|
||||
1CB8B33B1BBA75EF000E2DD1 /* AppDelegate.h */,
|
||||
1CB8B33C1BBA75EF000E2DD1 /* AppDelegate.mm */,
|
||||
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */,
|
||||
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */,
|
||||
2783ABB01CE35C1D00F11FD4 /* App Volumes */,
|
||||
1CED616A1C316E1A002CAFCF /* BGMAudioDeviceManager.h */,
|
||||
1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */,
|
||||
1C1465B91BCC49D1003AEFE6 /* BGMAutoPauseMusic.h */,
|
||||
@@ -387,6 +396,7 @@
|
||||
27379B841C7C53BE0084A24C /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
27F7D4911D2484A300821C4B /* Decibel.h */,
|
||||
27379B851C7C54870084A24C /* iTunes.h */,
|
||||
27379B861C7C54870084A24C /* Spotify.h */,
|
||||
27379B871C7C552A0084A24C /* VLC.h */,
|
||||
@@ -420,6 +430,17 @@
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2783ABB01CE35C1D00F11FD4 /* App Volumes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
27DEE37B1D4F8DA300DBFDF1 /* BGMAppVolumes.h */,
|
||||
27DEE37A1D4F8DA300DBFDF1 /* BGMAppVolumes.mm */,
|
||||
2783ABB51CE36CDB00F11FD4 /* BGMApps.h */,
|
||||
2783ABB61CE36CDB00F11FD4 /* BGMApps.m */,
|
||||
);
|
||||
name = "App Volumes";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -603,13 +624,15 @@
|
||||
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */,
|
||||
1C1962E41BC94E15008A4DF7 /* CARingBuffer.cpp in Sources */,
|
||||
273F10DF1CC3D0B900C1C6DA /* BGMVox.m in Sources */,
|
||||
27DEE37C1D4F8DA300DBFDF1 /* BGMAppVolumes.mm in Sources */,
|
||||
1CC1DF811BE5068A00FB8FE4 /* CACFArray.cpp in Sources */,
|
||||
2783ABB71CE36CDB00F11FD4 /* BGMApps.m in Sources */,
|
||||
1C0BD0A81BF1B029004F4CF5 /* BGMPreferencesMenu.mm in Sources */,
|
||||
1C1962F41BCABFC5008A4DF7 /* CAHALAudioObject.cpp in Sources */,
|
||||
27F7D4901D2483B100821C4B /* BGMDecibel.m in Sources */,
|
||||
1C2336DA1BEAB6E7004C1C4E /* BGMMusicPlayer.m in Sources */,
|
||||
1C1962F61BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp in Sources */,
|
||||
1CC1DF821BE5068A00FB8FE4 /* CACFDictionary.cpp in Sources */,
|
||||
1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.mm in Sources */,
|
||||
1C0BD0A51BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm in Sources */,
|
||||
1C1963061BCAF468008A4DF7 /* CAMutex.cpp in Sources */,
|
||||
1CE7064C1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm in Sources */,
|
||||
@@ -638,6 +661,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1CB8B3501BBA75F0000E2DD1 /* BGMAppTests.m in Sources */,
|
||||
2783ABB81CE36CDB00F11FD4 /* BGMApps.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -722,6 +746,7 @@
|
||||
"CoreAudio_Debug=1",
|
||||
"CoreAudio_UseSysLog=0",
|
||||
"CoreAudio_StopOnAssert=1",
|
||||
"CoreAudio_ThreadStampMessages=0",
|
||||
);
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
@@ -817,6 +842,7 @@
|
||||
"CoreAudio_Debug=1",
|
||||
"CoreAudio_UseSysLog=0",
|
||||
"CoreAudio_StopOnAssert=1",
|
||||
"CoreAudio_ThreadStampMessages=0",
|
||||
);
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
@@ -876,6 +902,7 @@
|
||||
"CoreAudio_Debug=0",
|
||||
"CoreAudio_UseSysLog=0",
|
||||
"CoreAudio_StopOnAssert=0",
|
||||
"CoreAudio_ThreadStampMessages=0",
|
||||
);
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface BGMAppVolumes : NSObject
|
||||
@interface BGMAppVolumes : NSObject // todo: rename to BGMAppVolumesMenu
|
||||
|
||||
- (id) initWithMenu:(NSMenu*)menu appVolumeView:(NSView*)view audioDevices:(BGMAudioDeviceManager*)audioDevices;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// BGMAppVolumes.m
|
||||
// BGMAppVolumes.mm
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
// BGM Includes
|
||||
#include "BGM_Types.h"
|
||||
#import "BGMApps.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CACFDictionary.h"
|
||||
@@ -57,6 +58,10 @@ static float const kSlidersSnapWithin = 5;
|
||||
forKeyPath:@"runningApplications"
|
||||
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
|
||||
context:nil];
|
||||
|
||||
// todo: delete this
|
||||
NSArray* clients = (__bridge NSArray*)[audioDevices bgmDevice].GetPropertyData_CFType(kBGMClientsAddress);
|
||||
BGMApps* __unused apps = [[BGMApps alloc] initWithClients:clients];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -90,9 +95,6 @@ static float const kSlidersSnapWithin = 5;
|
||||
// TODO: Would it be better to only show apps that are registered as HAL clients?
|
||||
if ([app activationPolicy] != NSApplicationActivationPolicyRegular) continue;
|
||||
|
||||
// Don't show Finder
|
||||
if ([[app bundleIdentifier] isEqualTo:@"com.apple.finder"]) continue;
|
||||
|
||||
#ifndef NS_BLOCK_ASSERTIONS // If assertions are enabled
|
||||
// Count how many apps we should add menu items for so we can check it at the end of the method
|
||||
numApps++;
|
||||
@@ -103,7 +105,7 @@ static float const kSlidersSnapWithin = 5;
|
||||
// Look through the menu item's subviews for the ones we want to set up
|
||||
for (NSView* subview in [[appVolItem view] subviews]) {
|
||||
if ([subview conformsToProtocol:@protocol(BGMAppVolumeSubview)]) {
|
||||
[subview performSelector:@selector(setUpWithApp:context:) withObject:app withObject:self];
|
||||
[(NSView<BGMAppVolumeSubview>*)subview setUpWithApp:app context:self];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +152,7 @@ static float const kSlidersSnapWithin = 5;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: split this method up so it can have a cleaner (less coupled) signature?
|
||||
- (void) setVolumeOfMenuItem:(NSMenuItem*)menuItem fromAppVolumes:(CACFArray&)appVolumes {
|
||||
// Set menuItem's volume slider to the volume of the app in appVolumes that menuItem represents
|
||||
// Leaves menuItem unchanged if it doesn't match any of the apps in appVolumes
|
||||
@@ -214,6 +217,7 @@ static float const kSlidersSnapWithin = 5;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: move this into BGMApps. (it's not ui code.) should be able to remove the audioDevices instance var from this class then
|
||||
- (void) sendVolumeChangeToBGMDevice:(SInt32)newVolume appProcessID:(pid_t)appProcessID appBundleID:(NSString*)appBundleID {
|
||||
CACFDictionary appVolumeChange(true);
|
||||
appVolumeChange.AddSInt32(CFSTR(kBGMAppVolumesKey_ProcessID), appProcessID);
|
||||
@@ -0,0 +1,51 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// BGMApps.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// System Includes
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@interface BGMApp : NSObject
|
||||
|
||||
- (id) initWithName:(NSString*)name;
|
||||
|
||||
@property NSString* name;
|
||||
@property NSImage* icon;
|
||||
@property Float32 volume;
|
||||
//@property ? clients;
|
||||
@property pid_t PID;
|
||||
@property NSString* bundleID;
|
||||
@property NSArray<BGMApp*>* children;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface BGMApps : NSObject
|
||||
|
||||
// todo: comment (takes the data from kAudioDeviceCustomPropertyClients)
|
||||
// todo: seems like this type could be stricter/better
|
||||
- (id) initWithClients:(NSArray<NSDictionary*>*)clients;
|
||||
|
||||
@property (readonly) NSArray<BGMApp*>* apps;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// BGMApps.m
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// todo: need to move any copyright notices around?
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#import "BGMApps.h"
|
||||
|
||||
|
||||
@implementation BGMApps
|
||||
|
||||
- (id) initWithClients:(NSArray<NSDictionary*>*)clients {
|
||||
if ((self = [super init])) {
|
||||
NSLog(@"%@", clients);
|
||||
// todo
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -459,41 +459,32 @@ OSStatus BGMPlayThrough::BGMDeviceListenerProc(AudioObjectID inObjectID,
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::BGMDeviceListenerProc: Got kAudioDevicePropertyDeviceIsRunning notification");
|
||||
|
||||
auto deviceIsRunningHandler = [refCon] {
|
||||
// IsRunning doesn't always return true when IO is starting. Not sure why. But using
|
||||
// RunningSomewhereOtherThanBGMApp instead seems to be working so far.
|
||||
//
|
||||
//if(refCon->mInputDevice->IsRunning())
|
||||
if(RunningSomewhereOtherThanBGMApp(refCon->mInputDevice))
|
||||
// This is dispatched because it can block and
|
||||
// - we might be on a real-time thread, or
|
||||
// - BGMXPCListener::waitForOutputDeviceToStartWithReply might get called on the same thread just
|
||||
// before this and timeout waiting for this to run.
|
||||
//
|
||||
// TODO: We should find a way to do this without dispatching because dispatching isn't real-time safe.
|
||||
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
|
||||
if(refCon->mActive)
|
||||
{
|
||||
#if DEBUG
|
||||
refCon->mToldOutputDeviceToStartAt = mach_absolute_time();
|
||||
#endif
|
||||
refCon->Start();
|
||||
}
|
||||
};
|
||||
|
||||
CAMutex::Tryer stateTrier(refCon->mStateMutex);
|
||||
if(stateTrier.HasLock())
|
||||
{
|
||||
// In the vast majority of cases (when we actually start playthrough here) we get the state lock
|
||||
// and can invoke the handler directly
|
||||
deviceIsRunningHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: This should be rare, but we still shouldn't dispatch on the IO thread because it isn't
|
||||
// real-time safe.
|
||||
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
|
||||
if(refCon->mActive)
|
||||
DebugMsg("BGMPlayThrough::BGMDeviceListenerProc: Handling "
|
||||
"kAudioDevicePropertyDeviceIsRunning notification in dispatched block");
|
||||
CAMutex::Locker stateLocker(refCon->mStateMutex);
|
||||
|
||||
// IsRunning doesn't always return true when IO is starting. Not sure why. But using
|
||||
// RunningSomewhereOtherThanBGMApp instead seems to be working so far.
|
||||
//
|
||||
//if(refCon->mInputDevice->IsRunning())
|
||||
if(RunningSomewhereOtherThanBGMApp(refCon->mInputDevice))
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::BGMDeviceListenerProc: Handling "
|
||||
"kAudioDevicePropertyDeviceIsRunning notification in dispatched block");
|
||||
CAMutex::Locker stateLocker(refCon->mStateMutex);
|
||||
deviceIsRunningHandler();
|
||||
#if DEBUG
|
||||
refCon->mToldOutputDeviceToStartAt = mach_absolute_time();
|
||||
#endif
|
||||
refCon->Start();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -175,8 +175,22 @@
|
||||
}
|
||||
|
||||
- (void) waitForOutputDeviceToStartWithReply:(void (^)(NSError*))reply {
|
||||
OSStatus err = [audioDevices waitForOutputDeviceToStart];
|
||||
NSString* description;
|
||||
OSStatus err;
|
||||
|
||||
try {
|
||||
err = [audioDevices waitForOutputDeviceToStart];
|
||||
} catch (CAException e) {
|
||||
DebugMsg("BGMXPCListener::waitForOutputDeviceToStartWithReply: Caught CAException (%d). Replying kBGMXPC_HardwareError.",
|
||||
e.GetError());
|
||||
err = kBGMXPC_HardwareError;
|
||||
} catch (...) {
|
||||
DebugMsg("BGMXPCListener::waitForOutputDeviceToStartWithReply: Caught unknown exception. Replying kBGMXPC_InternalError.");
|
||||
err = kBGMXPC_InternalError;
|
||||
#if DEBUG
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
|
||||
switch (err) {
|
||||
case noErr:
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// BGMDecibel.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Superclass/Protocol Import
|
||||
#import "BGMMusicPlayer.h"
|
||||
|
||||
|
||||
@interface BGMDecibel : BGMMusicPlayer
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// BGMDecibel.m
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Copyright © 2016 Tanner Hoke
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#import "BGMDecibel.h"
|
||||
|
||||
// Auto-generated Scripting Bridge header
|
||||
#import "Decibel.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
@implementation BGMDecibel
|
||||
|
||||
BGM_MUSIC_PLAYER_DEFAULT_LOAD_METHOD
|
||||
|
||||
+ (NSString*) name {
|
||||
return @"Decibel";
|
||||
}
|
||||
|
||||
- (CFNumberRef) pid {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+ (CFStringRef) bundleID {
|
||||
return CFSTR("org.sbooth.Decibel");
|
||||
}
|
||||
|
||||
- (DecibelApplication* __nullable) decibel {
|
||||
return (DecibelApplication*) self.sbApplication;
|
||||
}
|
||||
|
||||
- (BOOL) isRunning {
|
||||
return self.decibel && [self.decibel isRunning];
|
||||
}
|
||||
|
||||
- (BOOL) pause {
|
||||
// isPlaying checks isRunning, so we don't need to check it here and waste an Apple event
|
||||
BOOL wasPlaying = [self isPlaying];
|
||||
|
||||
if (wasPlaying) {
|
||||
DebugMsg("BGMDecibel::pause: Pausing Decibel");
|
||||
[self.decibel pause];
|
||||
}
|
||||
|
||||
return wasPlaying;
|
||||
}
|
||||
|
||||
- (BOOL) unpause {
|
||||
// isPaused checks isRunning, so we don't need to check it here and waste an Apple event
|
||||
BOOL wasPaused = [self isPaused];
|
||||
|
||||
if (wasPaused) {
|
||||
DebugMsg("BGMDecibel::unpause: Unpausing Decibel");
|
||||
[self.decibel playPause];
|
||||
}
|
||||
|
||||
return wasPaused;
|
||||
}
|
||||
|
||||
- (BOOL) isPlaying {
|
||||
return [self isRunning] && [self.decibel playing];
|
||||
}
|
||||
|
||||
- (BOOL) isPaused {
|
||||
return [self isRunning] && ![self.decibel playing];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
// The music player classes written so far use Scripting Bridge to communicate with the music
|
||||
// player apps (see iTunes.h/Spotify.h) but any other way is fine too.
|
||||
//
|
||||
// If you're not sure what bundle ID the music player uses, install a debug build of BGMDriver
|
||||
// and play something in the music player. The easiest way is to do
|
||||
// build_and_install.sh -d
|
||||
// BGMDriver will log the bundle ID to system.log when it becomes aware of the music player.
|
||||
//
|
||||
|
||||
// System Includes
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -107,7 +107,7 @@ static BGMMusicPlayer* sSelectedMusicPlayer;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) eventDidFail:(const AppleEvent*)event withError:(NSError*)error {
|
||||
- (id __nullable) eventDidFail:(const AppleEvent*)event withError:(NSError*)error {
|
||||
// SBApplicationDelegate method. So far, this just logs the error.
|
||||
|
||||
#if DEBUG
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Decibel.h
|
||||
*
|
||||
* Generated with
|
||||
* sdef /Applications/Decibel.app | sdp -fh --basename Decibel
|
||||
*/
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <ScriptingBridge/ScriptingBridge.h>
|
||||
|
||||
|
||||
@class DecibelApplication, DecibelDocument, DecibelWindow, DecibelApplication, DecibelTrack;
|
||||
|
||||
enum DecibelSaveOptions {
|
||||
DecibelSaveOptionsYes = 'yes ' /* Save the file. */,
|
||||
DecibelSaveOptionsNo = 'no ' /* Do not save the file. */,
|
||||
DecibelSaveOptionsAsk = 'ask ' /* Ask the user whether or not to save the file. */
|
||||
};
|
||||
typedef enum DecibelSaveOptions DecibelSaveOptions;
|
||||
|
||||
enum DecibelPrintingErrorHandling {
|
||||
DecibelPrintingErrorHandlingStandard = 'lwst' /* Standard PostScript error handling */,
|
||||
DecibelPrintingErrorHandlingDetailed = 'lwdt' /* print a detailed report of PostScript errors */
|
||||
};
|
||||
typedef enum DecibelPrintingErrorHandling DecibelPrintingErrorHandling;
|
||||
|
||||
enum DecibelShuffleMode {
|
||||
DecibelShuffleModeOff = 'off ' /* Off */,
|
||||
DecibelShuffleModeTrack = 'trck' /* Track */,
|
||||
DecibelShuffleModeAlbum = 'albm' /* Album */,
|
||||
DecibelShuffleModeArtist = 'arts' /* Artist */
|
||||
};
|
||||
typedef enum DecibelShuffleMode DecibelShuffleMode;
|
||||
|
||||
enum DecibelRepeatMode {
|
||||
DecibelRepeatModeOff = 'off ' /* Off */,
|
||||
DecibelRepeatModeTrack = 'trck' /* Track */,
|
||||
DecibelRepeatModeAlbum = 'albm' /* Album */,
|
||||
DecibelRepeatModeArtist = 'arts' /* Artist */,
|
||||
DecibelRepeatModeAll = 'all ' /* All */
|
||||
};
|
||||
typedef enum DecibelRepeatMode DecibelRepeatMode;
|
||||
|
||||
@protocol DecibelGenericMethods
|
||||
|
||||
- (void) closeSaving:(DecibelSaveOptions)saving savingIn:(NSURL *)savingIn; // Close a document.
|
||||
- (void) saveIn:(NSURL *)in_ as:(id)as; // Save a document.
|
||||
- (void) printWithProperties:(NSDictionary *)withProperties printDialog:(BOOL)printDialog; // Print a document.
|
||||
- (void) delete; // Delete an object.
|
||||
- (void) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties; // Copy an object.
|
||||
- (void) moveTo:(SBObject *)to; // Move an object to a new location.
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Standard Suite
|
||||
*/
|
||||
|
||||
// The application's top-level scripting object.
|
||||
@interface DecibelApplication : SBApplication
|
||||
|
||||
- (SBElementArray<DecibelDocument *> *) documents;
|
||||
- (SBElementArray<DecibelWindow *> *) windows;
|
||||
|
||||
@property (copy, readonly) NSString *name; // The name of the application.
|
||||
@property (readonly) BOOL frontmost; // Is this the active application?
|
||||
@property (copy, readonly) NSString *version; // The version number of the application.
|
||||
|
||||
- (id) open:(id)x; // Open a document.
|
||||
- (void) print:(id)x withProperties:(NSDictionary *)withProperties printDialog:(BOOL)printDialog; // Print a document.
|
||||
- (void) quitSaving:(DecibelSaveOptions)saving; // Quit the application.
|
||||
- (BOOL) exists:(id)x; // Verify that an object exists.
|
||||
- (void) play; // Begin audio playback
|
||||
- (void) pause; // Suspend audio playback
|
||||
- (void) stop; // Stop audio playback
|
||||
- (void) playPause; // Begin or suspend audio playback
|
||||
- (void) seekForward; // Seek forward three seconds
|
||||
- (void) seekBackward; // Seek backward three seconds
|
||||
- (void) playSelection; // Play the selected track, or the first track if more than one are selected
|
||||
- (void) playPreviousTrack; // Play the previous logical track in the playlist
|
||||
- (void) playNextTrack; // Play the next logical track in the playlist
|
||||
- (void) addFile:(NSURL *)x; // Add a file to the playlist
|
||||
- (void) playFile:(NSURL *)x; // Add a file to the playlist and play it
|
||||
- (void) playTrackAtIndex:(NSInteger)x; // Play a track in the playlist
|
||||
- (void) increaseDeviceVolume; // Increase the device volume
|
||||
- (void) decreaseDeviceVolume; // Decrease the device volume
|
||||
- (void) increaseDigitalVolume; // Increase the digital volume
|
||||
- (void) decreaseDigitalVolume; // Decrease the digital volume
|
||||
- (void) clearPlaylist; // Clear the playlist
|
||||
- (void) scramblePlaylist; // Scramble the playlist
|
||||
|
||||
@end
|
||||
|
||||
// A document.
|
||||
@interface DecibelDocument : SBObject <DecibelGenericMethods>
|
||||
|
||||
@property (copy, readonly) NSString *name; // Its name.
|
||||
@property (readonly) BOOL modified; // Has it been modified since the last save?
|
||||
@property (copy, readonly) NSURL *file; // Its location on disk, if it has one.
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// A window.
|
||||
@interface DecibelWindow : SBObject <DecibelGenericMethods>
|
||||
|
||||
@property (copy, readonly) NSString *name; // The title of the window.
|
||||
- (NSInteger) id; // The unique identifier of the window.
|
||||
@property NSInteger index; // The index of the window, ordered front to back.
|
||||
@property NSRect bounds; // The bounding rectangle of the window.
|
||||
@property (readonly) BOOL closeable; // Does the window have a close button?
|
||||
@property (readonly) BOOL miniaturizable; // Does the window have a minimize button?
|
||||
@property BOOL miniaturized; // Is the window minimized right now?
|
||||
@property (readonly) BOOL resizable; // Can the window be resized?
|
||||
@property BOOL visible; // Is the window visible right now?
|
||||
@property (readonly) BOOL zoomable; // Does the window have a zoom button?
|
||||
@property BOOL zoomed; // Is the window zoomed right now?
|
||||
@property (copy, readonly) DecibelDocument *document; // The document whose contents are displayed in the window.
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Decibel Scripting Suite
|
||||
*/
|
||||
|
||||
// The Decibel application class.
|
||||
@interface DecibelApplication (DecibelScriptingSuite)
|
||||
|
||||
- (SBElementArray<DecibelTrack *> *) tracks;
|
||||
|
||||
@property (readonly) BOOL playing; // Is the player currently playing?
|
||||
@property (readonly) BOOL shuffling; // Is the player currently shuffling?
|
||||
@property (readonly) BOOL repeating; // Is the player currently repeating?
|
||||
@property (copy, readonly) DecibelTrack *nowPlaying; // The track that is currently playing?
|
||||
@property double deviceVolume; // The current device volume
|
||||
@property double digitalVolume; // The current digital volume
|
||||
@property double playbackPosition; // The current playback position [0, 1]
|
||||
@property double playbackTime; // The current playback time in seconds
|
||||
@property (readonly) BOOL canPlay; // Is the player currently playing?
|
||||
@property (readonly) BOOL canPlayPreviousTrack; // Is the player currently playing?
|
||||
@property (readonly) BOOL canPlayNextTrack; // Is the player currently playing?
|
||||
@property (readonly) BOOL canAdjustDeviceVolume; // Can the device volume be adjusted?
|
||||
@property DecibelShuffleMode shuffleMode; // Player shuffle mode
|
||||
@property DecibelRepeatMode repeatMode; // Player repeat mode
|
||||
@property (copy, readonly) SBObject *currentPlaylist; // The current playlist
|
||||
|
||||
@end
|
||||
|
||||
// A track in the playlist
|
||||
@interface DecibelTrack : SBObject <DecibelGenericMethods>
|
||||
|
||||
- (NSString *) id; // The track's ID
|
||||
@property (copy, readonly) NSURL *file; // The track's location
|
||||
@property (readonly) double duration; // The track's duration in seconds
|
||||
@property (readonly) double sampleRate; // The track's sample rate in Hz
|
||||
@property (readonly) NSInteger bitDepth; // The bit depth
|
||||
@property (readonly) NSInteger channels; // The track's channels
|
||||
@property (copy) NSString *title; // The track's title
|
||||
@property (copy) NSString *artist; // The track's artist
|
||||
@property (copy) NSString *albumTitle; // The track's album title
|
||||
@property (copy) NSString *albumArtist; // The track's album artist
|
||||
@property NSInteger trackNumber; // The track's track number
|
||||
@property NSInteger trackTotal; // The total number of tracks on the album
|
||||
@property NSInteger discNumber; // The disc number containing the track
|
||||
@property NSInteger discTotal; // The total number of discs (for multidisc albums)
|
||||
@property BOOL partOfACompilation; // Is the track part of a compilation?
|
||||
@property (copy) NSString *genre; // The track's genre
|
||||
@property (copy) NSString *composer; // The track's composer
|
||||
@property (copy) NSString *releaseDate; // The track's release date
|
||||
@property (copy) NSString *ISRC; // The track's ISRC
|
||||
@property (copy) id MCN; // The track's MCN
|
||||
|
||||
- (void) playTrack; // Play a track in the playlist
|
||||
|
||||
@end
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Copyright © 2016 Tanner Hoke
|
||||
//
|
||||
|
||||
// Self Includes
|
||||
@@ -29,6 +30,9 @@
|
||||
|
||||
|
||||
static NSString* const kToggleAutoPauseMusicMenuItemTitleFormat = @"Auto-pause %@";
|
||||
static NSString* const kToggleAutoPauseMusicMenuItemDisabledTooltipFormat = @"%@ doesn't appear to be running.";
|
||||
// Wait time to disable/enable the auto-pause menu item, in seconds
|
||||
static SInt64 const kToggleAutoPauseMusicMenuItemUpdateWaitTime = 1;
|
||||
static float const kMenuItemIconScalingFactor = 1.15f;
|
||||
static NSInteger const kPrefsMenuAutoPauseHeaderTag = 1;
|
||||
|
||||
@@ -39,6 +43,8 @@ static NSInteger const kPrefsMenuAutoPauseHeaderTag = 1;
|
||||
NSArray<NSMenuItem*>* musicPlayerMenuItems;
|
||||
}
|
||||
|
||||
id<NSObject> didLaunchToken, didTerminateToken;
|
||||
|
||||
- (id) initWithPreferencesMenu:(NSMenu*)inPrefsMenu
|
||||
toggleAutoPauseMusicMenuItem:(NSMenuItem*)inToggleAutoPauseMusicMenuItem
|
||||
audioDevices:(BGMAudioDeviceManager*)inAudioDevices {
|
||||
@@ -48,6 +54,7 @@ static NSInteger const kPrefsMenuAutoPauseHeaderTag = 1;
|
||||
audioDevices = inAudioDevices;
|
||||
musicPlayerMenuItems = @[];
|
||||
|
||||
[self initMusicPlayerObservers];
|
||||
[self initSelectedMusicPlayer];
|
||||
[self initMenuSection];
|
||||
[self updateMenuItemTitle];
|
||||
@@ -56,6 +63,35 @@ static NSInteger const kPrefsMenuAutoPauseHeaderTag = 1;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) initMusicPlayerObservers {
|
||||
// Add observers that enable/disable the Auto-pause Music menu item when the music player is launched/terminated.
|
||||
NSNotificationCenter* center = [[NSWorkspace sharedWorkspace] notificationCenter];
|
||||
|
||||
id<NSObject> (^addObserver)(NSString*) = ^(NSString* name) {
|
||||
return [center addObserverForName:name
|
||||
object:nil
|
||||
queue:nil
|
||||
usingBlock:^(NSNotification* note) {
|
||||
NSString* appBundleID = [note.userInfo[NSWorkspaceApplicationKey] bundleIdentifier];
|
||||
NSString* musicPlayerBundleID =
|
||||
(__bridge NSString*)[[[BGMMusicPlayer selectedMusicPlayer] class] bundleID];
|
||||
BOOL isAboutThisMusicPlayer = [appBundleID isEqualToString:musicPlayerBundleID];
|
||||
|
||||
if (isAboutThisMusicPlayer) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
|
||||
kToggleAutoPauseMusicMenuItemUpdateWaitTime * NSEC_PER_SEC),
|
||||
dispatch_get_main_queue(),
|
||||
^{
|
||||
[self updateMenuItemTitle];
|
||||
});
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
didLaunchToken = addObserver(NSWorkspaceDidLaunchApplicationNotification);
|
||||
didTerminateToken = addObserver(NSWorkspaceDidTerminateApplicationNotification);
|
||||
}
|
||||
|
||||
- (void) initSelectedMusicPlayer {
|
||||
// TODO: It would make more sense to either just save the music player setting in the User Defaults (the same way AppDelegate saves
|
||||
// whether auto-pause is enabled) or to send a "musicPlayerID" to the driver, which would only be used by BGMApp. If the latter,
|
||||
@@ -195,10 +231,50 @@ static NSInteger const kPrefsMenuAutoPauseHeaderTag = 1;
|
||||
}
|
||||
|
||||
- (void) updateMenuItemTitle {
|
||||
// Set the title of the Auto-pause Music menu item, including the name of the selected music player
|
||||
// Set the title of the Auto-pause Music menu item, including the name of the selected music player.
|
||||
NSString* musicPlayerName = [[[BGMMusicPlayer selectedMusicPlayer] class] name];
|
||||
NSString* title = [NSString stringWithFormat:kToggleAutoPauseMusicMenuItemTitleFormat, musicPlayerName];
|
||||
[toggleAutoPauseMusicMenuItem setTitle:title];
|
||||
|
||||
// Make the Auto-pause Music menu item appear disabled if the application is not running.
|
||||
//
|
||||
// We don't actually disable it just in case the user decides to disable auto-pause and their music player isn't running. E.g.
|
||||
// someone who only recently installed Background Music and doesn't want to use auto-pause at all.
|
||||
if ([[BGMMusicPlayer selectedMusicPlayer] isRunning]) {
|
||||
toggleAutoPauseMusicMenuItem.attributedTitle = nil;
|
||||
toggleAutoPauseMusicMenuItem.toolTip = nil;
|
||||
} else {
|
||||
// Hardcode the text colour to match disabled menu items. I couldn't figure out a way to do this properly. There's no colour
|
||||
// constant for this, except possibly disabledControlTextColor, which just leaves the text black for me. I also couldn't get
|
||||
// the colours from the built-in NSColorLists.
|
||||
//
|
||||
// TODO: Is it possible to make the tick mark grey as well?
|
||||
BOOL darkMode = [@"Dark" isEqualToString:[[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]];
|
||||
NSDictionary* attributes = @{ NSFontAttributeName: [NSFont menuBarFontOfSize:0], // Default font size
|
||||
NSForegroundColorAttributeName: [NSColor colorWithHue:0
|
||||
saturation:0
|
||||
brightness:(darkMode ? 0.25 : 0.75)
|
||||
alpha:1] };
|
||||
NSAttributedString* pseudoDisabledTitle = [[NSAttributedString alloc] initWithString:[toggleAutoPauseMusicMenuItem title]
|
||||
attributes:attributes];
|
||||
[toggleAutoPauseMusicMenuItem setAttributedTitle:pseudoDisabledTitle];
|
||||
|
||||
toggleAutoPauseMusicMenuItem.toolTip =
|
||||
[NSString stringWithFormat:kToggleAutoPauseMusicMenuItemDisabledTooltipFormat, musicPlayerName];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
// Remove the application launch/termination observers.
|
||||
NSNotificationCenter* center = [[NSWorkspace sharedWorkspace] notificationCenter];
|
||||
|
||||
if (didLaunchToken) {
|
||||
[center removeObserver:didLaunchToken];
|
||||
}
|
||||
|
||||
if (didTerminateToken) {
|
||||
[center removeObserver:didTerminateToken];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,754 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
2D46CA7B17D6ADCA00049D4A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2D46CA7817D6AD8A00049D4A /* Localizable.strings */; };
|
||||
2D46CA7D17D6AF4200049D4A /* DeviceIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 2D46CA7C17D6AF4200049D4A /* DeviceIcon.icns */; };
|
||||
2D47CAA415FEC82B002AAFB5 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2D47CAA215FEC82B002AAFB5 /* Localizable.strings */; };
|
||||
2D4DE41415EDF8D500E96F0D /* CAVolumeCurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D4DE41215EDF8D500E96F0D /* CAVolumeCurve.cpp */; };
|
||||
2D4DE41515EDF8D500E96F0D /* CAVolumeCurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D4DE41315EDF8D500E96F0D /* CAVolumeCurve.h */; };
|
||||
2D616EF415B8C82500D598BD /* NullAudio.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D616EF215B8C82500D598BD /* NullAudio.c */; };
|
||||
2D7477AD1578168D00412279 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D7477AC1578168D00412279 /* CoreFoundation.framework */; };
|
||||
2D76D96115E48B2000FF0F33 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D7477AC1578168D00412279 /* CoreFoundation.framework */; };
|
||||
2D76D97615E48B6400FF0F33 /* SA_PlugIn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D76D97415E48B6400FF0F33 /* SA_PlugIn.cpp */; };
|
||||
2D76D97715E48B6400FF0F33 /* SA_PlugIn.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D76D97515E48B6400FF0F33 /* SA_PlugIn.h */; };
|
||||
2D76D97A15E498EB00FF0F33 /* SA_Object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D76D97815E498EB00FF0F33 /* SA_Object.cpp */; };
|
||||
2D76D97B15E498EB00FF0F33 /* SA_Object.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D76D97915E498EB00FF0F33 /* SA_Object.h */; };
|
||||
2DD7AA0A15EACDE000C67AE1 /* SA_IOKit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA0815EACDDF00C67AE1 /* SA_IOKit.cpp */; };
|
||||
2DD7AA0B15EACDE000C67AE1 /* SA_IOKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA0915EACDDF00C67AE1 /* SA_IOKit.h */; };
|
||||
2DD7AA2315EAFD5100C67AE1 /* CACFArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA0E15EAFD3300C67AE1 /* CACFArray.cpp */; };
|
||||
2DD7AA2415EAFD5100C67AE1 /* CACFArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA0F15EAFD3300C67AE1 /* CACFArray.h */; };
|
||||
2DD7AA2515EAFD5100C67AE1 /* CACFDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1015EAFD3300C67AE1 /* CACFDictionary.cpp */; };
|
||||
2DD7AA2615EAFD5100C67AE1 /* CACFDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1115EAFD3300C67AE1 /* CACFDictionary.h */; };
|
||||
2DD7AA2715EAFD5100C67AE1 /* CACFNumber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1215EAFD3300C67AE1 /* CACFNumber.cpp */; };
|
||||
2DD7AA2815EAFD5100C67AE1 /* CACFNumber.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1315EAFD3300C67AE1 /* CACFNumber.h */; };
|
||||
2DD7AA2915EAFD5100C67AE1 /* CACFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1415EAFD3300C67AE1 /* CACFString.cpp */; };
|
||||
2DD7AA2A15EAFD5100C67AE1 /* CACFString.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1515EAFD3300C67AE1 /* CACFString.h */; };
|
||||
2DD7AA2B15EAFD5100C67AE1 /* CADebugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1615EAFD3300C67AE1 /* CADebugger.cpp */; };
|
||||
2DD7AA2C15EAFD5100C67AE1 /* CADebugger.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1715EAFD3300C67AE1 /* CADebugger.h */; };
|
||||
2DD7AA2D15EAFD5100C67AE1 /* CADebugMacros.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1815EAFD3300C67AE1 /* CADebugMacros.cpp */; };
|
||||
2DD7AA2E15EAFD5100C67AE1 /* CADebugMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1915EAFD3300C67AE1 /* CADebugMacros.h */; };
|
||||
2DD7AA2F15EAFD5100C67AE1 /* CADebugPrintf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1A15EAFD3300C67AE1 /* CADebugPrintf.cpp */; };
|
||||
2DD7AA3015EAFD5100C67AE1 /* CADebugPrintf.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1B15EAFD3300C67AE1 /* CADebugPrintf.h */; };
|
||||
2DD7AA3115EAFD5100C67AE1 /* CAException.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1C15EAFD3300C67AE1 /* CAException.h */; };
|
||||
2DD7AA3215EAFD5100C67AE1 /* CAGuard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1D15EAFD3300C67AE1 /* CAGuard.cpp */; };
|
||||
2DD7AA3315EAFD5100C67AE1 /* CAGuard.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1E15EAFD3300C67AE1 /* CAGuard.h */; };
|
||||
2DD7AA3415EAFD5100C67AE1 /* CAHostTimeBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1F15EAFD3300C67AE1 /* CAHostTimeBase.cpp */; };
|
||||
2DD7AA3515EAFD5100C67AE1 /* CAHostTimeBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA2015EAFD3300C67AE1 /* CAHostTimeBase.h */; };
|
||||
2DD7AA3615EAFD5100C67AE1 /* CAMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA2115EAFD3300C67AE1 /* CAMutex.cpp */; };
|
||||
2DD7AA3715EAFD5100C67AE1 /* CAMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA2215EAFD3300C67AE1 /* CAMutex.h */; };
|
||||
2DD7AA7D15EC20FD00C67AE1 /* CADispatchQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA7B15EC20FD00C67AE1 /* CADispatchQueue.cpp */; };
|
||||
2DD7AA7E15EC20FD00C67AE1 /* CADispatchQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA7C15EC20FD00C67AE1 /* CADispatchQueue.h */; };
|
||||
2DD7AA8015EC3DB800C67AE1 /* CACFObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA7F15EC3DB800C67AE1 /* CACFObject.h */; };
|
||||
2DD7AA9715EC551600C67AE1 /* SA_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA9515EC551500C67AE1 /* SA_Device.cpp */; };
|
||||
2DD7AA9815EC551600C67AE1 /* SA_Device.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA9615EC551600C67AE1 /* SA_Device.h */; };
|
||||
2DD7AA9A15EC572000C67AE1 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD7AA9915EC572000C67AE1 /* IOKit.framework */; };
|
||||
2DED184915C359AC0091BE97 /* SimpleAudioDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DED182515C356BA0091BE97 /* SimpleAudioDriver.cpp */; };
|
||||
2DED184A15C359AC0091BE97 /* SimpleAudioDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DED182615C356BA0091BE97 /* SimpleAudioDriver.h */; };
|
||||
2DED184B15C359AC0091BE97 /* SimpleAudioDriverUserClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DED182915C356BA0091BE97 /* SimpleAudioDriverUserClient.cpp */; };
|
||||
2DED184C15C359AC0091BE97 /* SimpleAudioDriverUserClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DED182A15C356BA0091BE97 /* SimpleAudioDriverUserClient.h */; };
|
||||
2DED184D15C359AC0091BE97 /* SimpleAudioDriverTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DED182B15C356BA0091BE97 /* SimpleAudioDriverTypes.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
2D46CA7917D6AD8A00049D4A /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
2D46CA7C17D6AF4200049D4A /* DeviceIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = DeviceIcon.icns; sourceTree = "<group>"; };
|
||||
2D47CAA315FEC82B002AAFB5 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
2D4DE41215EDF8D500E96F0D /* CAVolumeCurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CAVolumeCurve.cpp; sourceTree = "<group>"; };
|
||||
2D4DE41315EDF8D500E96F0D /* CAVolumeCurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAVolumeCurve.h; sourceTree = "<group>"; };
|
||||
2D616EF115B8C82500D598BD /* NullAudio-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "NullAudio-Info.plist"; sourceTree = "<group>"; };
|
||||
2D616EF215B8C82500D598BD /* NullAudio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = NullAudio.c; sourceTree = "<group>"; };
|
||||
2D7477A91578168D00412279 /* NullAudio.driver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NullAudio.driver; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
2D7477AC1578168D00412279 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
|
||||
2D7477EC157823CF00412279 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
|
||||
2D76D96015E48B2000FF0F33 /* SimpleAudioPlugIn.driver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimpleAudioPlugIn.driver; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
2D76D97415E48B6400FF0F33 /* SA_PlugIn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SA_PlugIn.cpp; sourceTree = "<group>"; };
|
||||
2D76D97515E48B6400FF0F33 /* SA_PlugIn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SA_PlugIn.h; sourceTree = "<group>"; };
|
||||
2D76D97815E498EB00FF0F33 /* SA_Object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SA_Object.cpp; sourceTree = "<group>"; };
|
||||
2D76D97915E498EB00FF0F33 /* SA_Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SA_Object.h; sourceTree = "<group>"; };
|
||||
2D76D98B15E56E4E00FF0F33 /* SA_PlugIn-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SA_PlugIn-Info.plist"; sourceTree = "<group>"; };
|
||||
2DA8FA1515FEAAB000F04B50 /* ReadMe.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReadMe.txt; sourceTree = "<group>"; };
|
||||
2DD7AA0815EACDDF00C67AE1 /* SA_IOKit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SA_IOKit.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA0915EACDDF00C67AE1 /* SA_IOKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SA_IOKit.h; sourceTree = "<group>"; };
|
||||
2DD7AA0E15EAFD3300C67AE1 /* CACFArray.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CACFArray.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA0F15EAFD3300C67AE1 /* CACFArray.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CACFArray.h; sourceTree = "<group>"; };
|
||||
2DD7AA1015EAFD3300C67AE1 /* CACFDictionary.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CACFDictionary.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA1115EAFD3300C67AE1 /* CACFDictionary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CACFDictionary.h; sourceTree = "<group>"; };
|
||||
2DD7AA1215EAFD3300C67AE1 /* CACFNumber.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CACFNumber.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA1315EAFD3300C67AE1 /* CACFNumber.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CACFNumber.h; sourceTree = "<group>"; };
|
||||
2DD7AA1415EAFD3300C67AE1 /* CACFString.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CACFString.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA1515EAFD3300C67AE1 /* CACFString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CACFString.h; sourceTree = "<group>"; };
|
||||
2DD7AA1615EAFD3300C67AE1 /* CADebugger.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CADebugger.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA1715EAFD3300C67AE1 /* CADebugger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CADebugger.h; sourceTree = "<group>"; };
|
||||
2DD7AA1815EAFD3300C67AE1 /* CADebugMacros.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CADebugMacros.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA1915EAFD3300C67AE1 /* CADebugMacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CADebugMacros.h; sourceTree = "<group>"; };
|
||||
2DD7AA1A15EAFD3300C67AE1 /* CADebugPrintf.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CADebugPrintf.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA1B15EAFD3300C67AE1 /* CADebugPrintf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CADebugPrintf.h; sourceTree = "<group>"; };
|
||||
2DD7AA1C15EAFD3300C67AE1 /* CAException.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAException.h; sourceTree = "<group>"; };
|
||||
2DD7AA1D15EAFD3300C67AE1 /* CAGuard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CAGuard.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA1E15EAFD3300C67AE1 /* CAGuard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAGuard.h; sourceTree = "<group>"; };
|
||||
2DD7AA1F15EAFD3300C67AE1 /* CAHostTimeBase.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CAHostTimeBase.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA2015EAFD3300C67AE1 /* CAHostTimeBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAHostTimeBase.h; sourceTree = "<group>"; };
|
||||
2DD7AA2115EAFD3300C67AE1 /* CAMutex.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CAMutex.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA2215EAFD3300C67AE1 /* CAMutex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAMutex.h; sourceTree = "<group>"; };
|
||||
2DD7AA7B15EC20FD00C67AE1 /* CADispatchQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CADispatchQueue.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA7C15EC20FD00C67AE1 /* CADispatchQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CADispatchQueue.h; sourceTree = "<group>"; };
|
||||
2DD7AA7F15EC3DB800C67AE1 /* CACFObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CACFObject.h; sourceTree = "<group>"; };
|
||||
2DD7AA9515EC551500C67AE1 /* SA_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SA_Device.cpp; sourceTree = "<group>"; };
|
||||
2DD7AA9615EC551600C67AE1 /* SA_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SA_Device.h; sourceTree = "<group>"; };
|
||||
2DD7AA9915EC572000C67AE1 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
|
||||
2DED182415C356BA0091BE97 /* SimpleAudioDriver-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SimpleAudioDriver-Info.plist"; sourceTree = "<group>"; };
|
||||
2DED182515C356BA0091BE97 /* SimpleAudioDriver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SimpleAudioDriver.cpp; sourceTree = "<group>"; };
|
||||
2DED182615C356BA0091BE97 /* SimpleAudioDriver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleAudioDriver.h; sourceTree = "<group>"; };
|
||||
2DED182915C356BA0091BE97 /* SimpleAudioDriverUserClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SimpleAudioDriverUserClient.cpp; sourceTree = "<group>"; };
|
||||
2DED182A15C356BA0091BE97 /* SimpleAudioDriverUserClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleAudioDriverUserClient.h; sourceTree = "<group>"; };
|
||||
2DED182B15C356BA0091BE97 /* SimpleAudioDriverTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleAudioDriverTypes.h; sourceTree = "<group>"; };
|
||||
2DED183815C357180091BE97 /* SimpleAudioDriver.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimpleAudioDriver.kext; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
2DED183A15C357180091BE97 /* Kernel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Kernel.framework; path = System/Library/Frameworks/Kernel.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
2D7477A61578168D00412279 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D7477AD1578168D00412279 /* CoreFoundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2D76D95D15E48B2000FF0F33 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D76D96115E48B2000FF0F33 /* CoreFoundation.framework in Frameworks */,
|
||||
2DD7AA9A15EC572000C67AE1 /* IOKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2DED183315C357180091BE97 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
2D616EF015B8C82500D598BD /* NullAudio */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2D46CA7C17D6AF4200049D4A /* DeviceIcon.icns */,
|
||||
2D46CA7817D6AD8A00049D4A /* Localizable.strings */,
|
||||
2D616EF115B8C82500D598BD /* NullAudio-Info.plist */,
|
||||
2D616EF215B8C82500D598BD /* NullAudio.c */,
|
||||
);
|
||||
path = NullAudio;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D74779B1578162B00412279 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2DA8FA1515FEAAB000F04B50 /* ReadMe.txt */,
|
||||
2D616EF015B8C82500D598BD /* NullAudio */,
|
||||
2DED182215C356BA0091BE97 /* SimpleAudio */,
|
||||
2DD7AA0D15EAFD3300C67AE1 /* PublicUtility */,
|
||||
2D7477AB1578168D00412279 /* Frameworks */,
|
||||
2D7477AA1578168D00412279 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D7477AA1578168D00412279 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2D7477A91578168D00412279 /* NullAudio.driver */,
|
||||
2DED183815C357180091BE97 /* SimpleAudioDriver.kext */,
|
||||
2D76D96015E48B2000FF0F33 /* SimpleAudioPlugIn.driver */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D7477AB1578168D00412279 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2D7477EC157823CF00412279 /* CoreAudio.framework */,
|
||||
2D7477AC1578168D00412279 /* CoreFoundation.framework */,
|
||||
2DD7AA9915EC572000C67AE1 /* IOKit.framework */,
|
||||
2DED183A15C357180091BE97 /* Kernel.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2DA8FA7E15FEC6B500F04B50 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2D47CAA215FEC82B002AAFB5 /* Localizable.strings */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2DD7AA0D15EAFD3300C67AE1 /* PublicUtility */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2DD7AA0E15EAFD3300C67AE1 /* CACFArray.cpp */,
|
||||
2DD7AA0F15EAFD3300C67AE1 /* CACFArray.h */,
|
||||
2DD7AA1015EAFD3300C67AE1 /* CACFDictionary.cpp */,
|
||||
2DD7AA1115EAFD3300C67AE1 /* CACFDictionary.h */,
|
||||
2DD7AA1215EAFD3300C67AE1 /* CACFNumber.cpp */,
|
||||
2DD7AA1315EAFD3300C67AE1 /* CACFNumber.h */,
|
||||
2DD7AA7F15EC3DB800C67AE1 /* CACFObject.h */,
|
||||
2DD7AA1415EAFD3300C67AE1 /* CACFString.cpp */,
|
||||
2DD7AA1515EAFD3300C67AE1 /* CACFString.h */,
|
||||
2DD7AA1615EAFD3300C67AE1 /* CADebugger.cpp */,
|
||||
2DD7AA1715EAFD3300C67AE1 /* CADebugger.h */,
|
||||
2DD7AA1815EAFD3300C67AE1 /* CADebugMacros.cpp */,
|
||||
2DD7AA1915EAFD3300C67AE1 /* CADebugMacros.h */,
|
||||
2DD7AA1A15EAFD3300C67AE1 /* CADebugPrintf.cpp */,
|
||||
2DD7AA1B15EAFD3300C67AE1 /* CADebugPrintf.h */,
|
||||
2DD7AA7B15EC20FD00C67AE1 /* CADispatchQueue.cpp */,
|
||||
2DD7AA7C15EC20FD00C67AE1 /* CADispatchQueue.h */,
|
||||
2DD7AA1C15EAFD3300C67AE1 /* CAException.h */,
|
||||
2DD7AA1D15EAFD3300C67AE1 /* CAGuard.cpp */,
|
||||
2DD7AA1E15EAFD3300C67AE1 /* CAGuard.h */,
|
||||
2DD7AA1F15EAFD3300C67AE1 /* CAHostTimeBase.cpp */,
|
||||
2DD7AA2015EAFD3300C67AE1 /* CAHostTimeBase.h */,
|
||||
2DD7AA2115EAFD3300C67AE1 /* CAMutex.cpp */,
|
||||
2DD7AA2215EAFD3300C67AE1 /* CAMutex.h */,
|
||||
2D4DE41215EDF8D500E96F0D /* CAVolumeCurve.cpp */,
|
||||
2D4DE41315EDF8D500E96F0D /* CAVolumeCurve.h */,
|
||||
);
|
||||
path = PublicUtility;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2DED182215C356BA0091BE97 /* SimpleAudio */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2DED182315C356BA0091BE97 /* Driver */,
|
||||
2DED182C15C356BA0091BE97 /* Plug-In */,
|
||||
);
|
||||
path = SimpleAudio;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2DED182315C356BA0091BE97 /* Driver */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2DED182415C356BA0091BE97 /* SimpleAudioDriver-Info.plist */,
|
||||
2DED182515C356BA0091BE97 /* SimpleAudioDriver.cpp */,
|
||||
2DED182615C356BA0091BE97 /* SimpleAudioDriver.h */,
|
||||
2DED182915C356BA0091BE97 /* SimpleAudioDriverUserClient.cpp */,
|
||||
2DED182A15C356BA0091BE97 /* SimpleAudioDriverUserClient.h */,
|
||||
2DED182B15C356BA0091BE97 /* SimpleAudioDriverTypes.h */,
|
||||
);
|
||||
path = Driver;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2DED182C15C356BA0091BE97 /* Plug-In */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2DD7AA9515EC551500C67AE1 /* SA_Device.cpp */,
|
||||
2DD7AA9615EC551600C67AE1 /* SA_Device.h */,
|
||||
2DD7AA0815EACDDF00C67AE1 /* SA_IOKit.cpp */,
|
||||
2DD7AA0915EACDDF00C67AE1 /* SA_IOKit.h */,
|
||||
2D76D97815E498EB00FF0F33 /* SA_Object.cpp */,
|
||||
2D76D97915E498EB00FF0F33 /* SA_Object.h */,
|
||||
2D76D98B15E56E4E00FF0F33 /* SA_PlugIn-Info.plist */,
|
||||
2D76D97415E48B6400FF0F33 /* SA_PlugIn.cpp */,
|
||||
2D76D97515E48B6400FF0F33 /* SA_PlugIn.h */,
|
||||
2DA8FA7E15FEC6B500F04B50 /* Resources */,
|
||||
);
|
||||
path = "Plug-In";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
2D76D95E15E48B2000FF0F33 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D76D97715E48B6400FF0F33 /* SA_PlugIn.h in Headers */,
|
||||
2D76D97B15E498EB00FF0F33 /* SA_Object.h in Headers */,
|
||||
2DD7AA0B15EACDE000C67AE1 /* SA_IOKit.h in Headers */,
|
||||
2DD7AA2415EAFD5100C67AE1 /* CACFArray.h in Headers */,
|
||||
2DD7AA2615EAFD5100C67AE1 /* CACFDictionary.h in Headers */,
|
||||
2DD7AA2815EAFD5100C67AE1 /* CACFNumber.h in Headers */,
|
||||
2DD7AA2A15EAFD5100C67AE1 /* CACFString.h in Headers */,
|
||||
2DD7AA2C15EAFD5100C67AE1 /* CADebugger.h in Headers */,
|
||||
2DD7AA2E15EAFD5100C67AE1 /* CADebugMacros.h in Headers */,
|
||||
2DD7AA3015EAFD5100C67AE1 /* CADebugPrintf.h in Headers */,
|
||||
2DD7AA3115EAFD5100C67AE1 /* CAException.h in Headers */,
|
||||
2DD7AA3315EAFD5100C67AE1 /* CAGuard.h in Headers */,
|
||||
2DD7AA3515EAFD5100C67AE1 /* CAHostTimeBase.h in Headers */,
|
||||
2DD7AA3715EAFD5100C67AE1 /* CAMutex.h in Headers */,
|
||||
2DD7AA7E15EC20FD00C67AE1 /* CADispatchQueue.h in Headers */,
|
||||
2DD7AA8015EC3DB800C67AE1 /* CACFObject.h in Headers */,
|
||||
2DD7AA9815EC551600C67AE1 /* SA_Device.h in Headers */,
|
||||
2D4DE41515EDF8D500E96F0D /* CAVolumeCurve.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2DED183415C357180091BE97 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2DED184A15C359AC0091BE97 /* SimpleAudioDriver.h in Headers */,
|
||||
2DED184C15C359AC0091BE97 /* SimpleAudioDriverUserClient.h in Headers */,
|
||||
2DED184D15C359AC0091BE97 /* SimpleAudioDriverTypes.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
2D7477A81578168D00412279 /* NullAudio */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 2D7477B51578168D00412279 /* Build configuration list for PBXNativeTarget "NullAudio" */;
|
||||
buildPhases = (
|
||||
2D7477A51578168D00412279 /* Sources */,
|
||||
2D7477A61578168D00412279 /* Frameworks */,
|
||||
2D46CA7A17D6ADC500049D4A /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = NullAudio;
|
||||
productName = AudioNULLDriver;
|
||||
productReference = 2D7477A91578168D00412279 /* NullAudio.driver */;
|
||||
productType = "com.apple.product-type.bundle";
|
||||
};
|
||||
2D76D95F15E48B2000FF0F33 /* SimpleAudioPlugIn */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 2D76D97215E48B2000FF0F33 /* Build configuration list for PBXNativeTarget "SimpleAudioPlugIn" */;
|
||||
buildPhases = (
|
||||
2D76D95E15E48B2000FF0F33 /* Headers */,
|
||||
2D76D95C15E48B2000FF0F33 /* Sources */,
|
||||
2D76D95D15E48B2000FF0F33 /* Frameworks */,
|
||||
2DA8FA4B15FEABE000F04B50 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = SimpleAudioPlugIn;
|
||||
productName = SimpleAudioPlugIn;
|
||||
productReference = 2D76D96015E48B2000FF0F33 /* SimpleAudioPlugIn.driver */;
|
||||
productType = "com.apple.product-type.bundle";
|
||||
};
|
||||
2DED183715C357180091BE97 /* SimpleAudioDriver */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 2DED184515C357180091BE97 /* Build configuration list for PBXNativeTarget "SimpleAudioDriver" */;
|
||||
buildPhases = (
|
||||
2DED183415C357180091BE97 /* Headers */,
|
||||
2DED183215C357180091BE97 /* Sources */,
|
||||
2DED183315C357180091BE97 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = SimpleAudioDriver;
|
||||
productName = SimpleAudioDriver;
|
||||
productReference = 2DED183815C357180091BE97 /* SimpleAudioDriver.kext */;
|
||||
productType = "com.apple.product-type.kernel-extension";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
2D74779D1578162B00412279 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0500;
|
||||
};
|
||||
buildConfigurationList = 2D7477A01578162B00412279 /* Build configuration list for PBXProject "AudioDriverExamples" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
English,
|
||||
);
|
||||
mainGroup = 2D74779B1578162B00412279;
|
||||
productRefGroup = 2D7477AA1578168D00412279 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
2D7477A81578168D00412279 /* NullAudio */,
|
||||
2DED183715C357180091BE97 /* SimpleAudioDriver */,
|
||||
2D76D95F15E48B2000FF0F33 /* SimpleAudioPlugIn */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
2D46CA7A17D6ADC500049D4A /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D46CA7B17D6ADCA00049D4A /* Localizable.strings in Resources */,
|
||||
2D46CA7D17D6AF4200049D4A /* DeviceIcon.icns in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2DA8FA4B15FEABE000F04B50 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D47CAA415FEC82B002AAFB5 /* Localizable.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
2D7477A51578168D00412279 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D616EF415B8C82500D598BD /* NullAudio.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2D76D95C15E48B2000FF0F33 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D76D97615E48B6400FF0F33 /* SA_PlugIn.cpp in Sources */,
|
||||
2D76D97A15E498EB00FF0F33 /* SA_Object.cpp in Sources */,
|
||||
2DD7AA0A15EACDE000C67AE1 /* SA_IOKit.cpp in Sources */,
|
||||
2DD7AA2315EAFD5100C67AE1 /* CACFArray.cpp in Sources */,
|
||||
2DD7AA2515EAFD5100C67AE1 /* CACFDictionary.cpp in Sources */,
|
||||
2DD7AA2715EAFD5100C67AE1 /* CACFNumber.cpp in Sources */,
|
||||
2DD7AA2915EAFD5100C67AE1 /* CACFString.cpp in Sources */,
|
||||
2DD7AA2B15EAFD5100C67AE1 /* CADebugger.cpp in Sources */,
|
||||
2DD7AA2D15EAFD5100C67AE1 /* CADebugMacros.cpp in Sources */,
|
||||
2DD7AA2F15EAFD5100C67AE1 /* CADebugPrintf.cpp in Sources */,
|
||||
2DD7AA3215EAFD5100C67AE1 /* CAGuard.cpp in Sources */,
|
||||
2DD7AA3415EAFD5100C67AE1 /* CAHostTimeBase.cpp in Sources */,
|
||||
2DD7AA3615EAFD5100C67AE1 /* CAMutex.cpp in Sources */,
|
||||
2DD7AA7D15EC20FD00C67AE1 /* CADispatchQueue.cpp in Sources */,
|
||||
2DD7AA9715EC551600C67AE1 /* SA_Device.cpp in Sources */,
|
||||
2D4DE41415EDF8D500E96F0D /* CAVolumeCurve.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2DED183215C357180091BE97 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2DED184915C359AC0091BE97 /* SimpleAudioDriver.cpp in Sources */,
|
||||
2DED184B15C359AC0091BE97 /* SimpleAudioDriverUserClient.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
2D46CA7817D6AD8A00049D4A /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
2D46CA7917D6AD8A00049D4A /* English */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D47CAA215FEC82B002AAFB5 /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
2D47CAA315FEC82B002AAFB5 /* English */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
2D7477A21578162B00412279 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_OPTIMIZATION_LEVEL = s;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
2D7477A31578162B00412279 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2D7477A41578164E00412279 /* Debug-Opt */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_OPTIMIZATION_LEVEL = s;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = "Debug-Opt";
|
||||
};
|
||||
2D7477B61578168D00412279 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
"DEBUG=0",
|
||||
);
|
||||
INFOPLIST_FILE = "NullAudio/NullAudio-Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = driver;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
2D7477B71578168D00412279 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
"DEBUG=1",
|
||||
);
|
||||
INFOPLIST_FILE = "NullAudio/NullAudio-Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = driver;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2D7477B81578168D00412279 /* Debug-Opt */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
"DEBUG=1",
|
||||
);
|
||||
INFOPLIST_FILE = "NullAudio/NullAudio-Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = driver;
|
||||
};
|
||||
name = "Debug-Opt";
|
||||
};
|
||||
2D76D96F15E48B2000FF0F33 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_ENABLE_CPP_RTTI = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
"CoreAudio_Debug=0",
|
||||
"CoreAudio_UseSysLog=1",
|
||||
);
|
||||
INFOPLIST_FILE = "SimpleAudio/Plug-In/SA_PlugIn-Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = driver;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
2D76D97015E48B2000FF0F33 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_ENABLE_CPP_RTTI = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
"CoreAudio_Debug=1",
|
||||
"CoreAudio_UseSysLog=1",
|
||||
);
|
||||
INFOPLIST_FILE = "SimpleAudio/Plug-In/SA_PlugIn-Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = driver;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2D76D97115E48B2000FF0F33 /* Debug-Opt */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_ENABLE_CPP_RTTI = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
"CoreAudio_Debug=1",
|
||||
"CoreAudio_UseSysLog=1",
|
||||
);
|
||||
INFOPLIST_FILE = "SimpleAudio/Plug-In/SA_PlugIn-Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = driver;
|
||||
};
|
||||
name = "Debug-Opt";
|
||||
};
|
||||
2DED184615C357180091BE97 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "";
|
||||
INFOPLIST_FILE = "SimpleAudio/Driver/SimpleAudioDriver-Info.plist";
|
||||
INSTALL_PATH = "$(SYSTEM_LIBRARY_DIR)/Extensions";
|
||||
MODULE_NAME = com.apple.audio.SimpleAudioDriver;
|
||||
MODULE_VERSION = 1.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = kext;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
2DED184715C357180091BE97 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
|
||||
INFOPLIST_FILE = "SimpleAudio/Driver/SimpleAudioDriver-Info.plist";
|
||||
INSTALL_PATH = "$(SYSTEM_LIBRARY_DIR)/Extensions";
|
||||
MODULE_NAME = com.apple.audio.SimpleAudioDriver;
|
||||
MODULE_VERSION = 1.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = kext;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2DED184815C357180091BE97 /* Debug-Opt */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
|
||||
INFOPLIST_FILE = "SimpleAudio/Driver/SimpleAudioDriver-Info.plist";
|
||||
INSTALL_PATH = "$(SYSTEM_LIBRARY_DIR)/Extensions";
|
||||
MODULE_NAME = com.apple.audio.SimpleAudioDriver;
|
||||
MODULE_VERSION = 1.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = kext;
|
||||
};
|
||||
name = "Debug-Opt";
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
2D7477A01578162B00412279 /* Build configuration list for PBXProject "AudioDriverExamples" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
2D7477A21578162B00412279 /* Release */,
|
||||
2D7477A31578162B00412279 /* Debug */,
|
||||
2D7477A41578164E00412279 /* Debug-Opt */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
2D7477B51578168D00412279 /* Build configuration list for PBXNativeTarget "NullAudio" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
2D7477B61578168D00412279 /* Release */,
|
||||
2D7477B71578168D00412279 /* Debug */,
|
||||
2D7477B81578168D00412279 /* Debug-Opt */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
2D76D97215E48B2000FF0F33 /* Build configuration list for PBXNativeTarget "SimpleAudioPlugIn" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
2D76D96F15E48B2000FF0F33 /* Release */,
|
||||
2D76D97015E48B2000FF0F33 /* Debug */,
|
||||
2D76D97115E48B2000FF0F33 /* Debug-Opt */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
2DED184515C357180091BE97 /* Build configuration list for PBXNativeTarget "SimpleAudioDriver" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
2DED184615C357180091BE97 /* Release */,
|
||||
2DED184715C357180091BE97 /* Debug */,
|
||||
2DED184815C357180091BE97 /* Debug-Opt */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 2D74779D1578162B00412279 /* Project object */;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.audio.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CFPlugInFactories</key>
|
||||
<dict>
|
||||
<key>99A15A8B-DA3C-42C3-BD5D-D035A0C2377A</key>
|
||||
<string>NullAudio_Create</string>
|
||||
</dict>
|
||||
<key>CFPlugInTypes</key>
|
||||
<dict>
|
||||
<key>443ABAB8-E7B3-491A-B985-BEB9187030DB</key>
|
||||
<array>
|
||||
<string>99A15A8B-DA3C-42C3-BD5D-D035A0C2377A</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
TravisCI's OS X VMs don't have any audio devices installed by default, so we
|
||||
install Apple's NullAudio sample driver before the tests run. Otherwise the
|
||||
tests would fail because BGMApp currently crashes on launch if you don't have
|
||||
any audio devices.
|
||||
|
||||
|
||||
@@ -526,6 +526,7 @@
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
@@ -535,6 +536,7 @@
|
||||
"CoreAudio_Debug=1",
|
||||
"CoreAudio_UseSysLog=1",
|
||||
"CoreAudio_StopOnAssert=1",
|
||||
"CoreAudio_ThreadStampMessages=0",
|
||||
);
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
@@ -542,6 +544,9 @@
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
@@ -560,6 +565,7 @@
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = s;
|
||||
@@ -568,6 +574,7 @@
|
||||
"CoreAudio_Debug=0",
|
||||
"CoreAudio_UseSysLog=1",
|
||||
"CoreAudio_StopOnAssert=0",
|
||||
"CoreAudio_ThreadStampMessages=0",
|
||||
);
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
@@ -575,6 +582,9 @@
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_Device.cpp from Apple's SimpleAudioPlugin sample code. Also uses a few sections from Apple's NullAudio.c
|
||||
// sample code (found in the same sample project).
|
||||
// Based largely on SA_Device.cpp from Apple's SimpleAudioDriver Plug-In sample code. Also uses a few sections from Apple's
|
||||
// NullAudio.c sample code (found in the same sample project).
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
@@ -215,6 +215,8 @@ UInt32 BGM_Device::GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientP
|
||||
|
||||
void BGM_Device::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const
|
||||
{
|
||||
ThrowIfNULL(outData, BGM_RuntimeException(), "BGM_Device::GetPropertyData: !outData");
|
||||
|
||||
if(inObjectID == mObjectID)
|
||||
{
|
||||
Device_GetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData);
|
||||
@@ -235,6 +237,8 @@ void BGM_Device::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, co
|
||||
|
||||
void BGM_Device::SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
|
||||
{
|
||||
ThrowIfNULL(inData, BGM_RuntimeException(), "BGM_Device::SetPropertyData: no data");
|
||||
|
||||
if(inObjectID == mObjectID)
|
||||
{
|
||||
Device_SetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData);
|
||||
@@ -286,6 +290,7 @@ bool BGM_Device::Device_HasProperty(AudioObjectID inObjectID, pid_t inClientPID,
|
||||
case kAudioDeviceCustomPropertyMusicPlayerBundleID:
|
||||
case kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp:
|
||||
case kAudioDeviceCustomPropertyAppVolumes:
|
||||
case kAudioDeviceCustomPropertyClients:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
@@ -338,6 +343,7 @@ bool BGM_Device::Device_IsPropertySettable(AudioObjectID inObjectID, pid_t inCli
|
||||
case kAudioObjectPropertyCustomPropertyInfoList:
|
||||
case kAudioDeviceCustomPropertyDeviceAudibleState:
|
||||
case kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp:
|
||||
case kAudioDeviceCustomPropertyClients:
|
||||
theAnswer = false;
|
||||
break;
|
||||
|
||||
@@ -483,7 +489,7 @@ UInt32 BGM_Device::Device_GetPropertyDataSize(AudioObjectID inObjectID, pid_t in
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyCustomPropertyInfoList:
|
||||
theAnswer = sizeof(AudioServerPlugInCustomPropertyInfo) * 5;
|
||||
theAnswer = sizeof(AudioServerPlugInCustomPropertyInfo) * 6;
|
||||
break;
|
||||
|
||||
case kAudioDeviceCustomPropertyDeviceAudibleState:
|
||||
@@ -503,7 +509,11 @@ UInt32 BGM_Device::Device_GetPropertyDataSize(AudioObjectID inObjectID, pid_t in
|
||||
break;
|
||||
|
||||
case kAudioDeviceCustomPropertyAppVolumes:
|
||||
theAnswer = sizeof(CFPropertyListRef);
|
||||
theAnswer = sizeof(CFArrayRef);
|
||||
break;
|
||||
|
||||
case kAudioDeviceCustomPropertyClients:
|
||||
theAnswer = sizeof(CFArrayRef);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -848,8 +858,7 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
|
||||
// The sample rate is protected by the state lock
|
||||
CAMutex::Locker theStateLocker(mStateMutex);
|
||||
|
||||
// need to lock around fetching the sample rate
|
||||
|
||||
*reinterpret_cast<Float64*>(outData) = _HW_GetSampleRate();
|
||||
outDataSize = sizeof(Float64);
|
||||
}
|
||||
@@ -877,8 +886,14 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
// fill out the return array
|
||||
if(theNumberItemsToFetch > 0)
|
||||
{
|
||||
((AudioValueRange*)outData)[0].mMinimum = 0.0;
|
||||
((AudioValueRange*)outData)[0].mMaximum = DBL_MAX;
|
||||
// 0 would cause divide-by-zero errors in other BGM_Device functions (and
|
||||
// wouldn't make sense anyway).
|
||||
((AudioValueRange*)outData)[0].mMinimum = 1.0;
|
||||
// Just in case DBL_MAX would cause problems in a client for some reason,
|
||||
// use an arbitrary very large number instead. (It wouldn't make sense to
|
||||
// actually set the sample rate this high, but I don't know what a
|
||||
// reasonable maximum would be.)
|
||||
((AudioValueRange*)outData)[0].mMaximum = 1000000000.0;
|
||||
}
|
||||
|
||||
// report how much we wrote
|
||||
@@ -893,7 +908,7 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyPreferredChannelsForStereo:
|
||||
// This property returns which two channesl to use as left/right for stereo
|
||||
// This property returns which two channels to use as left/right for stereo
|
||||
// data by default. Note that the channel numbers are 1-based.
|
||||
ThrowIf(inDataSize < (2 * sizeof(UInt32)), CAException(kAudioHardwareBadPropertySizeError), "BGM_Device::Device_GetPropertyData: not enough space for the return value of kAudioDevicePropertyPreferredChannelsForStereo for the device");
|
||||
((UInt32*)outData)[0] = 1;
|
||||
@@ -950,9 +965,9 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
theNumberItemsToFetch = inDataSize / sizeof(AudioServerPlugInCustomPropertyInfo);
|
||||
|
||||
// clamp it to the number of items we have
|
||||
if(theNumberItemsToFetch > 5)
|
||||
if(theNumberItemsToFetch > 6)
|
||||
{
|
||||
theNumberItemsToFetch = 5;
|
||||
theNumberItemsToFetch = 6;
|
||||
}
|
||||
|
||||
if(theNumberItemsToFetch > 0)
|
||||
@@ -985,6 +1000,12 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
((AudioServerPlugInCustomPropertyInfo*)outData)[4].mPropertyDataType = kAudioServerPlugInCustomPropertyDataTypeCFPropertyList;
|
||||
((AudioServerPlugInCustomPropertyInfo*)outData)[4].mQualifierDataType = kAudioServerPlugInCustomPropertyDataTypeNone;
|
||||
}
|
||||
if(theNumberItemsToFetch > 5)
|
||||
{
|
||||
((AudioServerPlugInCustomPropertyInfo*)outData)[5].mSelector = kAudioDeviceCustomPropertyClients;
|
||||
((AudioServerPlugInCustomPropertyInfo*)outData)[5].mPropertyDataType = kAudioServerPlugInCustomPropertyDataTypeCFPropertyList;
|
||||
((AudioServerPlugInCustomPropertyInfo*)outData)[5].mQualifierDataType = kAudioServerPlugInCustomPropertyDataTypeNone;
|
||||
}
|
||||
outDataSize = theNumberItemsToFetch * sizeof(AudioServerPlugInCustomPropertyInfo);
|
||||
break;
|
||||
|
||||
@@ -1033,6 +1054,15 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
outDataSize = sizeof(CFArrayRef);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioDeviceCustomPropertyClients:
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(CFArrayRef), CAException(kAudioHardwareBadPropertySizeError), "BGM_Device::Device_GetPropertyData: not enough space for the return value of kAudioDeviceCustomPropertyClients for the device");
|
||||
CAMutex::Locker theStateLocker(mStateMutex);
|
||||
*reinterpret_cast<CFArrayRef*>(outData) = mClients.CopyClientPIDsAndBundleIDs().GetCFArray();
|
||||
outDataSize = sizeof(CFArrayRef);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BGM_Object::GetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData);
|
||||
@@ -1049,6 +1079,7 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
ThrowIf(inDataSize < sizeof(Float64), CAException(kAudioHardwareBadPropertySizeError), "BGM_Device::Device_SetPropertyData: wrong size for the data for kAudioDevicePropertyNominalSampleRate");
|
||||
|
||||
// BGMDevice's input and output sample rates are the same, so we just pass the request to Stream_SetPropertyData for one of them
|
||||
// TODO: Set the property for both streams just in case that assumption changes? (As unlikely as that is.)
|
||||
|
||||
Float64 theNewSampleRate = *reinterpret_cast<const Float64*>(inData);
|
||||
|
||||
@@ -1062,7 +1093,7 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
|
||||
theStreamFormat.mSampleRate = theNewSampleRate;
|
||||
|
||||
Stream_SetPropertyData(inObjectID, inClientPID, theStreamPropertyAddress, 0, NULL, theStreamFormatDataSize, &theStreamFormat);
|
||||
Stream_SetPropertyData(kObjectID_Stream_Input, inClientPID, theStreamPropertyAddress, 0, NULL, theStreamFormatDataSize, &theStreamFormat);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1111,15 +1142,17 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
case kAudioDeviceCustomPropertyMusicPlayerBundleID:
|
||||
{
|
||||
ThrowIf(inDataSize < sizeof(CFStringRef), CAException(kAudioHardwareBadPropertySizeError), "BGM_Device::Device_SetPropertyData: wrong size for the data for kAudioDeviceCustomPropertyMusicPlayerBundleID");
|
||||
|
||||
|
||||
CFStringRef theBundleIDRef = *reinterpret_cast<const CFStringRef*>(inData);
|
||||
|
||||
ThrowIfNULL(theBundleIDRef, CAException(kAudioHardwareIllegalOperationError), "BGM_Device::Device_SetPropertyData: kAudioDeviceCustomPropertyMusicPlayerBundleID cannot be set to NULL");
|
||||
ThrowIf(CFGetTypeID(theBundleIDRef) != CFStringGetTypeID(), CAException(kAudioHardwareIllegalOperationError), "BGM_Device::Device_SetPropertyData: CFType given for kAudioDeviceCustomPropertyMusicPlayerBundleID was not a CFString");
|
||||
|
||||
CAMutex::Locker theStateLocker(mStateMutex);
|
||||
|
||||
CFRetain(theBundleIDRef);
|
||||
CACFString bundleID(theBundleIDRef);
|
||||
|
||||
bool propertyWasChanged = mClients.SetMusicPlayer(bundleID);
|
||||
|
||||
if(propertyWasChanged)
|
||||
@@ -1410,8 +1443,9 @@ void BGM_Device::Stream_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
((AudioStreamRangedDescription*)outData)[0].mFormat.mBytesPerFrame = 8;
|
||||
((AudioStreamRangedDescription*)outData)[0].mFormat.mChannelsPerFrame = 2;
|
||||
((AudioStreamRangedDescription*)outData)[0].mFormat.mBitsPerChannel = 32;
|
||||
((AudioStreamRangedDescription*)outData)[0].mSampleRateRange.mMinimum = 0.0;
|
||||
((AudioStreamRangedDescription*)outData)[0].mSampleRateRange.mMaximum = DBL_MAX;
|
||||
// These match kAudioDevicePropertyAvailableNominalSampleRates.
|
||||
((AudioStreamRangedDescription*)outData)[0].mSampleRateRange.mMinimum = 1.0;
|
||||
((AudioStreamRangedDescription*)outData)[0].mSampleRateRange.mMaximum = 1000000000.0;
|
||||
}
|
||||
|
||||
// report how much we wrote
|
||||
@@ -1474,7 +1508,7 @@ void BGM_Device::Stream_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
ThrowIf(theNewFormat->mBytesPerFrame != 8, CAException(kAudioDeviceUnsupportedFormatError), "BGM_Device::Stream_SetPropertyData: unsupported bytes per frame for kAudioStreamPropertyPhysicalFormat");
|
||||
ThrowIf(theNewFormat->mChannelsPerFrame != 2, CAException(kAudioDeviceUnsupportedFormatError), "BGM_Device::Stream_SetPropertyData: unsupported channels per frame for kAudioStreamPropertyPhysicalFormat");
|
||||
ThrowIf(theNewFormat->mBitsPerChannel != 32, CAException(kAudioDeviceUnsupportedFormatError), "BGM_Device::Stream_SetPropertyData: unsupported bits per channel for kAudioStreamPropertyPhysicalFormat");
|
||||
ThrowIf(theNewFormat->mSampleRate < 0.0, //(theNewFormat->mSampleRate != 44100.0) && (theNewFormat->mSampleRate != 48000.0),
|
||||
ThrowIf(theNewFormat->mSampleRate < 1.0, //(theNewFormat->mSampleRate != 44100.0) && (theNewFormat->mSampleRate != 48000.0),
|
||||
CAException(kAudioDeviceUnsupportedFormatError),
|
||||
"BGM_Device::Stream_SetPropertyData: unsupported sample rate for kAudioStreamPropertyPhysicalFormat");
|
||||
|
||||
@@ -1483,7 +1517,6 @@ void BGM_Device::Stream_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
|
||||
CAMutex::Locker theStateLocker(mStateMutex);
|
||||
Float64 theOldSampleRate = _HW_GetSampleRate();
|
||||
|
||||
// make sure that the new value is different than the old value
|
||||
if(theNewSampleRate != theOldSampleRate)
|
||||
{
|
||||
mPendingSampleRate = theNewSampleRate;
|
||||
@@ -1815,51 +1848,58 @@ void BGM_Device::Control_SetPropertyData(AudioObjectID inObjectID, pid_t inClien
|
||||
|
||||
void BGM_Device::StartIO(UInt32 inClientID)
|
||||
{
|
||||
CAMutex::Locker theStateLocker(mStateMutex);
|
||||
bool clientIsBGMApp, bgmAppHasClientRegistered;
|
||||
|
||||
// An overview of the process this function is part of:
|
||||
// - A client starts IO.
|
||||
// - The plugin host (the HAL) calls the StartIO function in BGM_PluginInterface, which calls this function.
|
||||
// - BGMDriver sends a message to BGMApp telling it to start the (real) audio hardware.
|
||||
// - BGMApp starts the hardware and, after the hardware is ready, replies to BGMDriver's message.
|
||||
// - BGMDriver lets the host know that it's ready to do IO by returning from StartIO.
|
||||
|
||||
// Update our client data.
|
||||
//
|
||||
// We add the work to the task queue, rather than doing it here, because BeginIOOperation and EndIOOperation also
|
||||
// add this task to the queue and the updates should be done in order.
|
||||
bool didStartIO = mTaskQueue.QueueSync_StartClientIO(&mClients, inClientID);
|
||||
|
||||
// We only tell the hardware to start if this is the first time IO has been started
|
||||
if(didStartIO)
|
||||
{
|
||||
kern_return_t theError = _HW_StartIO();
|
||||
ThrowIfKernelError(theError,
|
||||
CAException(theError),
|
||||
"BGM_Device::StartIO: Failed to start because of an error calling down to the driver.");
|
||||
{
|
||||
CAMutex::Locker theStateLocker(mStateMutex);
|
||||
|
||||
// We only return from StartIO after BGMApp is ready to pass the audio through to the output device. That way
|
||||
// the HAL doesn't start sending us data before BGMApp can play it, which would mean we'd have to either drop
|
||||
// frames or increase latency.
|
||||
if(!mClients.IsBGMApp(inClientID) && mClients.BGMAppHasClientRegistered())
|
||||
// An overview of the process this function is part of:
|
||||
// - A client starts IO.
|
||||
// - The plugin host (the HAL) calls the StartIO function in BGM_PlugInInterface, which calls this function.
|
||||
// - BGMDriver sends a message to BGMApp telling it to start the (real) audio hardware.
|
||||
// - BGMApp starts the hardware and, after the hardware is ready, replies to BGMDriver's message.
|
||||
// - BGMDriver lets the host know that it's ready to do IO by returning from StartIO.
|
||||
|
||||
// Update our client data.
|
||||
//
|
||||
// We add the work to the task queue, rather than doing it here, because BeginIOOperation and EndIOOperation
|
||||
// also add this task to the queue and the updates should be done in order.
|
||||
bool didStartIO = mTaskQueue.QueueSync_StartClientIO(&mClients, inClientID);
|
||||
|
||||
// We only tell the hardware to start if this is the first time IO has been started.
|
||||
if(didStartIO)
|
||||
{
|
||||
UInt64 theXPCError = WaitForBGMAppToStartOutputDevice();
|
||||
|
||||
if(theXPCError == kBGMXPC_Success)
|
||||
{
|
||||
DebugMsg("BGM_Device::StartIO: Ready for IO.");
|
||||
}
|
||||
else if(theXPCError == kBGMXPC_MessageFailure)
|
||||
{
|
||||
// This most likely means BGMXPCHelper isn't installed or has crashed. IO will probably still work,
|
||||
// but we may drop frames while the audio hardware starts up.
|
||||
DebugMsg("BGM_Device::StartIO: Couldn't reach BGMApp via XPC. Attempting to start IO anyway.");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMsg("BGM_Device::StartIO: BGMApp failed to start the output device. theXPCError=%llu", theXPCError);
|
||||
Throw(CAException(kAudioHardwareUnspecifiedError));
|
||||
}
|
||||
kern_return_t theError = _HW_StartIO();
|
||||
ThrowIfKernelError(theError,
|
||||
CAException(theError),
|
||||
"BGM_Device::StartIO: Failed to start because of an error calling down to the driver.");
|
||||
}
|
||||
|
||||
clientIsBGMApp = mClients.IsBGMApp(inClientID);
|
||||
bgmAppHasClientRegistered = mClients.BGMAppHasClientRegistered();
|
||||
}
|
||||
|
||||
// We only return from StartIO after BGMApp is ready to pass the audio through to the output device. That way
|
||||
// the HAL doesn't start sending us data before BGMApp can play it, which would mean we'd have to either drop
|
||||
// frames or increase latency.
|
||||
if(!clientIsBGMApp && bgmAppHasClientRegistered)
|
||||
{
|
||||
UInt64 theXPCError = WaitForBGMAppToStartOutputDevice();
|
||||
|
||||
if(theXPCError == kBGMXPC_Success)
|
||||
{
|
||||
DebugMsg("BGM_Device::StartIO: Ready for IO.");
|
||||
}
|
||||
else if(theXPCError == kBGMXPC_MessageFailure)
|
||||
{
|
||||
// This most likely means BGMXPCHelper isn't installed or has crashed. IO will probably still work,
|
||||
// but we may drop frames while the audio hardware starts up.
|
||||
DebugMsg("BGM_Device::StartIO: Couldn't reach BGMApp via XPC. Attempting to start IO anyway.");
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMsg("BGM_Device::StartIO: BGMApp failed to start the output device. theXPCError=%llu", theXPCError);
|
||||
Throw(CAException(kAudioHardwareUnspecifiedError));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2268,7 +2308,7 @@ SInt32 BGM_Device::_HW_GetVolumeControlValue(int inObjectID) const
|
||||
return mOutputMasterVolumeControlRawValueShadow;
|
||||
};
|
||||
|
||||
throw (CAException(kAudioHardwareBadObjectError));
|
||||
Throw(CAException(kAudioHardwareBadObjectError));
|
||||
}
|
||||
|
||||
kern_return_t BGM_Device::_HW_SetVolumeControlValue(int inObjectID, SInt32 inNewControlValue)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_Device.h from Apple's SimpleAudioPlugin sample code.
|
||||
// Based largely on SA_Device.h from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_Object.cpp from Apple's SimpleAudioPlugin sample code.
|
||||
// Based largely on SA_Object.cpp from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
// Similarly to BGM_Object.h, this file hasn't been changed much from SA_Object.cpp, except to
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_Object.h from Apple's SimpleAudioPlugin sample code.
|
||||
// Based largely on SA_Object.h from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
// The base class for our classes that represent audio objects. (See AudioServerPlugin.h for a
|
||||
// The base class for our classes that represent audio objects. (See AudioServerPlugIn.h for a
|
||||
// quick explanation of audio objects.)
|
||||
//
|
||||
// This is a stripped down version of SA_Object.h. Unlike the sample code plugin that SA_Object.h
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_PlugIn.cpp from Apple's SimpleAudioPlugin sample code.
|
||||
// Based largely on SA_PlugIn.cpp from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_PlugIn.h from Apple's SimpleAudioPlugin sample code.
|
||||
// Based largely on SA_PlugIn.h from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
|
||||
//
|
||||
// Based largely on SA_PlugIn.cpp from Apple's SimpleAudioPlugin sample code.
|
||||
// Based largely on SA_PlugIn.cpp from Apple's SimpleAudioDriver Plug-In sample code.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
@@ -561,7 +561,8 @@ static OSStatus BGM_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioOb
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_SetPropertyData: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "BGM_SetPropertyData: no address");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "BGM_SetPropertyData: no address");
|
||||
ThrowIfNULL(inData, CAException(kAudioHardwareIllegalOperationError), "BGM_SetPropertyData: no data");
|
||||
|
||||
BGM_Object& theAudioObject = BGM_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
// PublicUtility Includes
|
||||
#include "CAException.h"
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAAtomic.h"
|
||||
|
||||
// System Includes
|
||||
#include <mach/mach_init.h>
|
||||
@@ -80,7 +81,7 @@ BGM_TaskQueue::BGM_TaskQueue()
|
||||
|
||||
// Pre-allocate enough tasks in mNonRealTimeThreadTasksFreeList that the real-time threads should never have to
|
||||
// allocate memory when adding a task to the non-realtime queue.
|
||||
for(int i = 0; i < kNonRealTimeThreadTaskBufferSize; i++)
|
||||
for(UInt32 i = 0; i < kNonRealTimeThreadTaskBufferSize; i++)
|
||||
{
|
||||
BGM_Task* theTask = new BGM_Task;
|
||||
mNonRealTimeThreadTasksFreeList.push_NA(theTask);
|
||||
@@ -207,6 +208,7 @@ UInt64 BGM_TaskQueue::QueueSync(BGM_TaskID inTaskID, bool inRunOnRealtimeThre
|
||||
//
|
||||
// The worker thread signals all threads waiting on this semaphore when it finishes a task. The comments in WorkerThreadProc
|
||||
// explain why we have to check the condition in a loop here.
|
||||
bool didLogTimeoutMessage = false;
|
||||
while(!theTask.IsComplete())
|
||||
{
|
||||
semaphore_t theTaskCompletedSemaphore =
|
||||
@@ -216,10 +218,25 @@ UInt64 BGM_TaskQueue::QueueSync(BGM_TaskID inTaskID, bool inRunOnRealtimeThre
|
||||
theError = semaphore_timedwait(theTaskCompletedSemaphore,
|
||||
(mach_timespec_t){ 0, kRealTimeThreadMaximumComputationNs * 4 });
|
||||
|
||||
if(theError != KERN_OPERATION_TIMED_OUT)
|
||||
if(theError == KERN_OPERATION_TIMED_OUT)
|
||||
{
|
||||
if(!didLogTimeoutMessage && inRunOnRealtimeThread)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::QueueSync: Task %d taking longer than expected.", theTask.GetTaskID());
|
||||
didLogTimeoutMessage = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BGM_Utils::ThrowIfMachError("BGM_TaskQueue::QueueSync", "semaphore_timedwait", theError);
|
||||
}
|
||||
|
||||
CAMemoryBarrier();
|
||||
}
|
||||
|
||||
if(didLogTimeoutMessage)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::QueueSync: Late task %d finished.", theTask.GetTaskID());
|
||||
}
|
||||
|
||||
if(theTask.GetReturnValue() != INT64_MAX)
|
||||
@@ -299,9 +316,9 @@ void* __nullable BGM_TaskQueue::NonRealTimeThreadProc(void* inRefCon)
|
||||
|
||||
void BGM_TaskQueue::WorkerThreadProc(semaphore_t inWorkQueuedSemaphore, semaphore_t inSyncTaskCompletedSemaphore, TAtomicStack<BGM_Task>* inTasks, TAtomicStack2<BGM_Task>* __nullable inFreeList, std::function<bool(BGM_Task*)> inProcessTask)
|
||||
{
|
||||
bool threadShouldStop = false;
|
||||
bool theThreadShouldStop = false;
|
||||
|
||||
while(!threadShouldStop)
|
||||
while(!theThreadShouldStop)
|
||||
{
|
||||
// Wait until a thread signals that it's added tasks to the queue.
|
||||
//
|
||||
@@ -317,18 +334,33 @@ void BGM_TaskQueue::WorkerThreadProc(semaphore_t inWorkQueuedSemaphore, semap
|
||||
BGM_Task* theTask = inTasks->pop_all_reversed();
|
||||
|
||||
while(theTask != NULL &&
|
||||
!threadShouldStop) // Stop processing tasks if we're shutting down
|
||||
!theThreadShouldStop) // Stop processing tasks if we're shutting down
|
||||
{
|
||||
BGM_Task* theNextTask = theTask->mNext;
|
||||
|
||||
BGMAssert(!theTask->IsComplete(),
|
||||
"BGM_TaskQueue::WorkerThreadProc: Cannot process already completed task (ID %d)",
|
||||
theTask->GetTaskID());
|
||||
|
||||
BGMAssert(theTask != theNextTask,
|
||||
"BGM_TaskQueue::WorkerThreadProc: BGM_Task %p (ID %d) was added to %s multiple times. arg1=%llu arg2=%llu",
|
||||
theTask,
|
||||
theTask->GetTaskID(),
|
||||
(inTasks == &mRealTimeThreadTasks ? "mRealTimeThreadTasks" : "mNonRealTimeThreadTasks"),
|
||||
theTask->GetArg1(),
|
||||
theTask->GetArg2());
|
||||
|
||||
// Process the task
|
||||
threadShouldStop = inProcessTask(theTask);
|
||||
|
||||
theTask->MarkCompleted();
|
||||
|
||||
BGM_Task* theNextTask = theTask->next();
|
||||
theThreadShouldStop = inProcessTask(theTask);
|
||||
|
||||
// If the task was queued synchronously, let the thread that queued it know we're finished
|
||||
if(theTask->IsSync())
|
||||
{
|
||||
// Marking the task as completed allows QueueSync to return, which means it's possible for theTask to point to
|
||||
// invalid memory after this point.
|
||||
CAMemoryBarrier();
|
||||
theTask->MarkCompleted();
|
||||
|
||||
// Signal any threads waiting for their task to be processed.
|
||||
//
|
||||
// We use semaphore_signal_all instead of semaphore_signal to avoid a race condition in QueueSync. It's possible
|
||||
@@ -336,7 +368,7 @@ void BGM_TaskQueue::WorkerThreadProc(semaphore_t inWorkQueuedSemaphore, semap
|
||||
// added to the queue. So after each task is completed we have every waiting thread check if it was theirs.
|
||||
//
|
||||
// Note that semaphore_signal_all has an implicit barrier.
|
||||
kern_return_t theError = semaphore_signal_all(inSyncTaskCompletedSemaphore);
|
||||
theError = semaphore_signal_all(inSyncTaskCompletedSemaphore);
|
||||
BGM_Utils::ThrowIfMachError("BGM_TaskQueue::WorkerThreadProc", "semaphore_signal_all", theError);
|
||||
}
|
||||
else if(inFreeList != NULL)
|
||||
@@ -399,6 +431,13 @@ bool BGM_TaskQueue::ProcessNonRealTimeThreadTask(BGM_Task* inTask)
|
||||
bool didStartIO = BGM_ClientTasks::StartIONonRT(theClients, static_cast<UInt32>(inTask->GetArg2()));
|
||||
inTask->SetReturnValue(didStartIO);
|
||||
}
|
||||
// TODO: Catch the other types of exceptions BGM_ClientTasks::StartIONonRT can throw here as well. Set the task's return
|
||||
// value (rather than rethrowing) so the exceptions can be handled if the task was queued sync. Then
|
||||
// QueueSync_StartClientIO can throw some exception and BGM_StartIO can return an appropriate error code to the
|
||||
// HAL, instead of the driver just crashing.
|
||||
//
|
||||
// Do the same for the kBGMTaskStopClientIO case below. And should we set a return value in the catch block for
|
||||
// BGM_InvalidClientException as well, so it can also be rethrown in QueueSync_StartClientIO and then handled?
|
||||
catch(BGM_InvalidClientException)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::ProcessNonRealTimeThreadTask: Ignoring BGM_InvalidClientException thrown by StartIONonRT. %s",
|
||||
|
||||
@@ -71,7 +71,7 @@ private:
|
||||
class BGM_Task
|
||||
{
|
||||
public:
|
||||
BGM_Task(BGM_TaskID inTaskID = kBGMTaskUninitialized, bool inIsSync = false, UInt64 inArg1 = 0, UInt64 inArg2 = 0) : mTaskID(inTaskID), mIsSync(inIsSync), mArg1(inArg1), mArg2(inArg2) { };
|
||||
BGM_Task(BGM_TaskID inTaskID = kBGMTaskUninitialized, bool inIsSync = false, UInt64 inArg1 = 0, UInt64 inArg2 = 0) : mNext(NULL), mTaskID(inTaskID), mIsSync(inIsSync), mArg1(inArg1), mArg2(inArg2) { };
|
||||
|
||||
BGM_TaskID GetTaskID() { return mTaskID; }
|
||||
UInt64 GetArg1() { return mArg1; }
|
||||
@@ -84,7 +84,8 @@ private:
|
||||
bool IsSync() { return mIsSync; }
|
||||
|
||||
// Used by TAtomicStack
|
||||
BGM_Task* __nullable & next() { return mNext; };
|
||||
BGM_Task* __nullable & next() { return mNext; }
|
||||
BGM_Task* __nullable mNext;
|
||||
|
||||
private:
|
||||
BGM_TaskID mTaskID;
|
||||
@@ -93,7 +94,6 @@ private:
|
||||
UInt64 mReturnValue = INT64_MAX;
|
||||
bool mIsComplete = false;
|
||||
bool mIsSync;
|
||||
BGM_Task* __nullable mNext = NULL;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@@ -84,8 +84,8 @@ UInt64 WaitForBGMAppToStartOutputDevice()
|
||||
theConnection.interruptionHandler = failureHandler;
|
||||
theConnection.invalidationHandler = failureHandler;
|
||||
|
||||
// This remote call to BGMXPCHelper will send a reply when the output device is ready to receive IO. Note that we shouldn't trust
|
||||
// the reply string.
|
||||
// This remote call to BGMXPCHelper will send a reply when the output device is ready to receive IO. Note that, for security
|
||||
// reasons, we shouldn't trust the reply object.
|
||||
[[theConnection remoteObjectProxyWithErrorHandler:^(NSError* error) {
|
||||
#if !DEBUG
|
||||
#pragma unused (error)
|
||||
|
||||
@@ -172,6 +172,29 @@ std::vector<BGM_Client> BGM_ClientMap::GetClientsByPID(pid_t inPID) const
|
||||
return theClients;
|
||||
}
|
||||
|
||||
CACFArray BGM_ClientMap::CopyClientPIDsAndBundleIDs() const
|
||||
{
|
||||
// Since this is a read-only, non-real-time operation we can read from the shadow maps to avoid
|
||||
// locking the main maps.
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
CACFArray theClientPIDsAndBundleIDs(false);
|
||||
|
||||
for(auto& theClientEntry : mClientMapShadow)
|
||||
{
|
||||
CACFDictionary theClientDict(false);
|
||||
|
||||
theClientDict.AddSInt32(CFSTR(kBGMClientsKey_ProcessID), theClientEntry.second.mProcessID);
|
||||
// todo: is this a memory leak? (over retaining mBundleID, cause adding it to the dict also retains it) check for the same bug
|
||||
// in CopyClientIntoAppVolumesArray if so.
|
||||
theClientDict.AddString(CFSTR(kBGMClientsKey_BundleID), theClientEntry.second.mBundleID.CopyCFString());
|
||||
|
||||
theClientPIDsAndBundleIDs.AppendDictionary(theClientDict.GetDict());
|
||||
}
|
||||
|
||||
return theClientPIDsAndBundleIDs;
|
||||
}
|
||||
|
||||
#pragma mark Music Player
|
||||
|
||||
void BGM_ClientMap::UpdateMusicPlayerFlags(pid_t inMusicPlayerPID)
|
||||
@@ -213,7 +236,7 @@ void BGM_ClientMap::UpdateMusicPlayerFlagsInShadowMaps(std::function<bool(BGM
|
||||
|
||||
CACFArray BGM_ClientMap::CopyClientRelativeVolumesAsAppVolumes(CAVolumeCurve inVolumeCurve) const
|
||||
{
|
||||
// Since this is a read-only, non-real-time operation, we can read from the shadow maps to avoid
|
||||
// Since this is a read-only, non-real-time operation we can read from the shadow maps to avoid
|
||||
// locking the main maps.
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
|
||||
@@ -101,6 +101,10 @@ private:
|
||||
public:
|
||||
std::vector<BGM_Client> GetClientsByPID(pid_t inPID) const;
|
||||
|
||||
// todo: comment
|
||||
// todo: would it be useful at all to have a similar property that returns past clients instead? probably not...
|
||||
CACFArray CopyClientPIDsAndBundleIDs() const;
|
||||
|
||||
// Set the isMusicPlayer flag for each client. (True if the client has the given bundle ID/PID, false otherwise.)
|
||||
void UpdateMusicPlayerFlags(pid_t inMusicPlayerPID);
|
||||
void UpdateMusicPlayerFlags(CACFString inMusicPlayerBundleID);
|
||||
|
||||
@@ -302,7 +302,7 @@ bool BGM_Clients::SetClientsRelativeVolumes(const CACFArray inAppVolumes)
|
||||
|
||||
// Each element in appVolumes is a CFDictionary containing the process id and/or bundle id of an app, and its
|
||||
// new relative volume
|
||||
for(int i = 0; i < inAppVolumes.GetNumberItems(); i++)
|
||||
for(UInt32 i = 0; i < inAppVolumes.GetNumberItems(); i++)
|
||||
{
|
||||
CACFDictionary theAppVolume(false);
|
||||
inAppVolumes.GetCACFDictionary(i, theAppVolume);
|
||||
|
||||
@@ -108,6 +108,8 @@ public:
|
||||
// Returns true if any clients' relative volumes were changed.
|
||||
bool SetClientsRelativeVolumes(const CACFArray inAppVolumes);
|
||||
|
||||
CACFArray CopyClientPIDsAndBundleIDs() const { return mClientMap.CopyClientPIDsAndBundleIDs(); }
|
||||
|
||||
private:
|
||||
BGM_ClientMap mClientMap;
|
||||
|
||||
|
||||
@@ -26,25 +26,118 @@
|
||||
// Local Includes
|
||||
#include "BGM_TestUtils.h"
|
||||
|
||||
// BGMDriver Includes
|
||||
#include "BGM_Types.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAException.h"
|
||||
|
||||
|
||||
@interface BGM_DeviceTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation BGM_DeviceTests {
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
@implementation BGM_DeviceTests
|
||||
|
||||
- (void) setUp {
|
||||
[super setUp];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
- (void) tearDown {
|
||||
// Reminder: add code here, above the super call
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void) testCustomPropertyMusicPlayerBundleID {
|
||||
BGM_Device& device = BGM_Device::GetInstance();
|
||||
|
||||
// Convenience wrappers
|
||||
auto getBundleID = [&](UInt32 inDataSize = sizeof(CFStringRef)){
|
||||
CFStringRef bundleID = NULL;
|
||||
UInt32 outDataSize;
|
||||
|
||||
device.GetPropertyData(/* inObjectID = */ kObjectID_Device,
|
||||
/* inClientPID = */ 3,
|
||||
/* inAddress = */ kBGMMusicPlayerBundleIDAddress,
|
||||
/* inQualifierDataSize = */ 0,
|
||||
/* inQualifierData = */ NULL,
|
||||
/* inDataSize = */ inDataSize,
|
||||
/* outDataSize = */ outDataSize,
|
||||
/* outData = */ reinterpret_cast<void* __nonnull>(&bundleID));
|
||||
|
||||
// This isn't technically required, but we're unlikely to ever want to return any more/less data from GetPropertyData.
|
||||
XCTAssertEqual(outDataSize, sizeof(CFStringRef));
|
||||
|
||||
return (__bridge_transfer NSString*)bundleID;
|
||||
};
|
||||
|
||||
auto setBundleID = [&](const CFStringRef* __nullable bundleID, UInt32 dataSize = sizeof(CFStringRef)){
|
||||
device.SetPropertyData(/* inObjectID = */ kObjectID_Device,
|
||||
/* inClientPID = */ 1234,
|
||||
/* inAddress = */ kBGMMusicPlayerBundleIDAddress,
|
||||
/* inQualifierDataSize = */ 0,
|
||||
/* inQualifierData = */ NULL,
|
||||
/* inDataSize = */ dataSize,
|
||||
/* inData = */ reinterpret_cast<const void* __nonnull>(bundleID));
|
||||
};
|
||||
|
||||
// Should be set to the empty string by default.
|
||||
XCTAssertEqualObjects(getBundleID(), @"");
|
||||
|
||||
// Should be able to set the property to an arbitrary string. (Purposefully not using CFSTR for this one just in case it
|
||||
// makes a difference.)
|
||||
CFStringRef newID = CFStringCreateWithCString(kCFAllocatorDefault, "test.bundle.ID", kCFStringEncodingUTF8);
|
||||
setBundleID(&newID);
|
||||
CFRelease(newID);
|
||||
XCTAssertEqualObjects(getBundleID(), @"test.bundle.ID");
|
||||
|
||||
// Should be able to set the property back to the empty string.
|
||||
newID = CFSTR("");
|
||||
setBundleID(&newID);
|
||||
XCTAssertEqualObjects(getBundleID(), @"");
|
||||
|
||||
// Arguments should be null-checked.
|
||||
BGMShouldThrow<BGM_RuntimeException>(self, [&](){
|
||||
UInt32 outDataSize;
|
||||
device.GetPropertyData(kObjectID_Device, 0, kBGMMusicPlayerBundleIDAddress, 0, NULL, sizeof(CFStringRef),
|
||||
outDataSize, /* outData = */ reinterpret_cast<void* __nonnull>(NULL));
|
||||
});
|
||||
BGMShouldThrow<BGM_RuntimeException>(self, [&](){
|
||||
setBundleID(NULL);
|
||||
});
|
||||
|
||||
// Invalid data should be rejected.
|
||||
BGMShouldThrow<CAException>(self, [&](){
|
||||
setBundleID((CFStringRef*)&kCFNull);
|
||||
});
|
||||
BGMShouldThrow<CAException>(self, [&](){
|
||||
CFStringRef nullRef = NULL;
|
||||
setBundleID(&nullRef);
|
||||
});
|
||||
BGMShouldThrow<CAException>(self, [&](){
|
||||
CFArrayRef array = (__bridge_retained CFArrayRef)@[ @1, @2 ];
|
||||
setBundleID((CFStringRef*)&array);
|
||||
});
|
||||
|
||||
// Should throw if not given enough space for the return data.
|
||||
BGMShouldThrow<CAException>(self, [&](){
|
||||
getBundleID(/* inDataSize = */ 0);
|
||||
});
|
||||
|
||||
newID = CFSTR("bundle");
|
||||
|
||||
// Passing more data than needed should be fine as long as it starts with a CFStringRef.
|
||||
setBundleID(&newID, sizeof(CFStringRef) * 2);
|
||||
|
||||
// Should throw if not enough data is passed.
|
||||
BGMShouldThrow<CAException>(self, [&](){
|
||||
setBundleID(&newID, sizeof(CFStringRef) - 1);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Performance tests?
|
||||
- (void)testPerformanceExample {
|
||||
- (void) testPerformanceExample {
|
||||
// This is an example of a performance test case.
|
||||
[self measureBlock:^{
|
||||
// Put the code you want to measure the time of here.
|
||||
|
||||
@@ -5,6 +5,19 @@
|
||||
Firstly, thanks for reading this. Pull requests, bug reports, feature requests, etc. are all very welcome (including
|
||||
ones from non-developers).
|
||||
|
||||
For bug reports about `build_and_install.sh`, please include your `build_and_install.log`. (It should be written to the
|
||||
same directory as `build_and_install.sh`.)
|
||||
|
||||
For bug reports about Background Music itself, if you feel like being really helpful, you could reproduce your bug with
|
||||
a debug build and include the relevant logs. But don't feel obligated to. You can build and install a debug build with
|
||||
`./build_and_install.sh -d`.
|
||||
|
||||
BGMDriver and BGMXPCHelper log messages to system.log by default. You can read them in Console.app. BGMApp's logs go to
|
||||
stdout, except errors or warnings that should be visible to users, which go to syslog. To get BGMApp's logs either run
|
||||
it in Xcode or from a terminal (e.g. `$ /Applications/Background\ Music.app/Contents/MacOS/Background\ Music`).
|
||||
|
||||
I'm working on adding logging to release builds and generally making the process easier.
|
||||
|
||||
The code is mostly C++ and Objective-C. But don't worry if you don't know those languages--I don't either. Or Core
|
||||
Audio, for that matter.
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<!-- vim: set tw=120: -->
|
||||
|
||||
# Manual Build and Install
|
||||
|
||||
- Install the virtual audio device `Background Music Device.driver` to `/Library/Audio/Plug-Ins/HAL`.
|
||||
|
||||
```shell
|
||||
sudo xcodebuild -project BGMDriver/BGMDriver.xcodeproj \
|
||||
-target "Background Music Device" \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
DSTROOT="/" \
|
||||
install
|
||||
```
|
||||
- Install the XPC helper.
|
||||
|
||||
```shell
|
||||
sudo xcodebuild -project BGMApp/BGMApp.xcodeproj \
|
||||
-target BGMXPCHelper \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
DSTROOT="/" \
|
||||
INSTALL_PATH="$(BGMApp/BGMXPCHelper/safe_install_dir.sh)" \
|
||||
install
|
||||
```
|
||||
- Install `Background Music.app` to `/Applications` (or wherever).
|
||||
|
||||
```shell
|
||||
xcodebuild -project BGMApp/BGMApp.xcodeproj \
|
||||
-target "Background Music" \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
DSTROOT="/" \
|
||||
install
|
||||
```
|
||||
- Restart `coreaudiod`: <br>
|
||||
(Audio will stop working until the next step, so you might want to pause any running audio apps.)
|
||||
|
||||
```shell
|
||||
sudo launchctl kill -15 system/com.apple.audio.coreaudiod
|
||||
```
|
||||
or, if that fails
|
||||
|
||||
```shell
|
||||
sudo killall coreaudiod
|
||||
```
|
||||
- Run `Background Music.app`.
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
- No restart required to install
|
||||
- Runs entirely in userspace
|
||||
|
||||
**Version 0.1.0**, first release. Probably very buggy. Pretty much only tested on one system. (A MacBook running OS X
|
||||
10.11 using the built-in audio device.)
|
||||
**Version 0.1.0**, first release. Probably very buggy. Not very polished. Pretty much only tested on one system. (A
|
||||
MacBook running OS X 10.11 using the built-in audio device.)
|
||||
|
||||
**Requires OS X 10.10+**. Might work on 10.9, but I haven't tried it.
|
||||
**Requires OS X 10.10+**. Might work on 10.9, but I haven't tried it. Currently unable to build in Xcode 6.
|
||||
|
||||
## Auto-pause music
|
||||
|
||||
@@ -25,10 +25,10 @@ Background Music can pause your music player app when other audio starts playing
|
||||
that when I'm listening to music and pause it to watch a video or something I always forget to unpause it afterwards. So
|
||||
this keeps me from wearing headphones for hours listening to nothing.
|
||||
|
||||
So far iTunes, [Spotify](https://www.spotify.com), [VLC](https://www.videolan.org/vlc/) and
|
||||
[VOX](http://coppertino.com/vox/mac) are supported, but adding support for a music player should only take a few minutes
|
||||
(see `BGMMusicPlayer.h`). If you don't know how to program, or just don't feel like it, create an issue and I'll try to
|
||||
add it for you.
|
||||
So far iTunes, [Spotify](https://www.spotify.com), [Decibel](https://sbooth.org/Decibel/),
|
||||
[VLC](https://www.videolan.org/vlc/) and [VOX](https://coppertino.com/vox/mac) are supported, but adding support for a
|
||||
music player should only take a few minutes (see `BGMMusicPlayer.h`). If you don't know how to program, or just don't
|
||||
feel like it, create an issue and I'll try to add it for you.
|
||||
|
||||
## App volumes
|
||||
|
||||
@@ -47,12 +47,37 @@ Background Music Device. You can create the aggregate device using the Audio MID
|
||||
|
||||
## Install
|
||||
|
||||
No binaries yet, but building should take less than a minute. To build and install everything, clone/download the
|
||||
project and run the `build_and_install.sh` script. Unfortunately, **it won't build if you don't have Xcode installed**
|
||||
because xcodebuild doesn't work on its own anymore.
|
||||
No binaries yet (working on it) but building should take less than a minute.
|
||||
|
||||
The script restarts the system audio process (coreaudiod) at the end of the installation, so you might want to pause any
|
||||
apps playing audio.
|
||||
<table>
|
||||
<tr><td>⚠️</td>
|
||||
<td>Unfortunately, <strong>it won't build if you don't have Xcode installed</strong> because xcodebuild doesn't work on its own anymore. If you don't mind a 4GB download, you can <a href="https://developer.apple.com/xcode/download/">get Xcode here</a>.</td>
|
||||
</tr></table>
|
||||
|
||||
If you're comfortable with it, you can just paste the following at a Terminal prompt.
|
||||
|
||||
[//]: # (Uses /bin/bash instead of just bash on the off chance that someone has a non standard Bash in their $PATH, but)
|
||||
[//]: # (it doesn't do that for Tar or cURL because I'm fairly sure any versions of them should work here. That said,)
|
||||
[//]: # (build_and_install.sh doesn't call things by absolute paths (yet?) anyway.)
|
||||
[//]: # ( )
|
||||
[//]: # (Uses "gzcat - | tar x" instead of "tar xz" because gzcat will also check the file's integrity. (Gzip files)
|
||||
[//]: # (include a checksum.))
|
||||
```shell
|
||||
(set -eo pipefail; URL='https://github.com/kyleneideck/BackgroundMusic/archive/master.tar.gz'; \
|
||||
cd $(mktemp -d); echo Downloading $URL to $(pwd); curl -qfL# $URL | gzcat - | tar x && \
|
||||
/bin/bash BackgroundMusic-master/build_and_install.sh && rm -rf BackgroundMusic-master)
|
||||
```
|
||||
|
||||
Otherwise, to build and install everything, do the following:
|
||||
|
||||
- Clone or [download](https://github.com/kyleneideck/BackgroundMusic/archive/master.zip) the project
|
||||
- If the project is in a zip, unzip it
|
||||
- [Go to the folder of the project](https://github.com/0nn0/terminal-mac-cheatsheet/wiki/Terminal-Cheatsheet-for-Mac-(-basics-)#core-commands) on your terminal
|
||||
- Run the following the command: `/bin/bash build_and_install.sh`.
|
||||
|
||||
The script restarts the system audio process (coreaudiod) at the end of the installation, so you might want to pause any apps playing audio.
|
||||
|
||||
Additional detailed installation instructions can be found on [the Wiki](https://github.com/kyleneideck/BackgroundMusic/wiki/Installation).
|
||||
|
||||
## Uninstall
|
||||
|
||||
@@ -83,7 +108,7 @@ change the default device and then change it back again. Failing that, you might
|
||||
General tab of Skype's preferences.
|
||||
- Plugging in or unplugging headphones when Background Music isn't running can silence system audio. To fix it, go to
|
||||
the Sound section in System Preferences, click the Output tab and change your default output device to something other
|
||||
than Background Music Device.
|
||||
than Background Music Device. Alternatively, you may Option+Click on the Sound icon in the menu bar to select a different output device.
|
||||
|
||||
This happens when OS X remembers that Background Music Device was your default audio device the last time you last
|
||||
used (or didn't use) headphones.
|
||||
@@ -118,6 +143,7 @@ change the default device and then change it back again. Failing that, you might
|
||||
|
||||
### Non-free
|
||||
|
||||
- [Audio Hijack](https://rogueamoeba.com/audiohijack/) - "Capture Audio From Anywhere on Your Mac"
|
||||
- [Volume Mixer For Mac](http://www.volumemixer-app.com/) - "Application specific volume control for Mac OS Yosemite and
|
||||
El Capitan"
|
||||
- [Sound Siphon](http://staticz.com) - System/app audio recording, per-app volumes, system audio equaliser
|
||||
@@ -127,5 +153,3 @@ change the default device and then change it back again. Failing that, you might
|
||||
## License
|
||||
|
||||
GPLv2 or later
|
||||
|
||||
|
||||
|
||||
+55
-34
@@ -23,7 +23,7 @@
|
||||
#ifndef __SharedSource__BGM_Types__
|
||||
#define __SharedSource__BGM_Types__
|
||||
|
||||
#include <CoreAudio/AudioServerPlugin.h>
|
||||
#include <CoreAudio/AudioServerPlugIn.h>
|
||||
|
||||
|
||||
#pragma mark IDs
|
||||
@@ -36,9 +36,9 @@
|
||||
#define kBGMDeviceUID "BGMDevice"
|
||||
#define kBGMDeviceModelUID "BGMDeviceModelUID"
|
||||
|
||||
// The object IDs for the audio objects this driver implements
|
||||
// The object IDs for the audio objects this driver implements.
|
||||
//
|
||||
// Regardless of the wrapped device, this driver always publishes this fixed set of objects. We might need to
|
||||
// BGMDevice always publishes this fixed set of objects (regardless of the wrapped device). We might need to
|
||||
// change that at some point, but so far it hasn't caused any problems and it makes the driver much simpler.
|
||||
enum
|
||||
{
|
||||
@@ -50,12 +50,15 @@ enum
|
||||
kObjectID_Mute_Output_Master = 6
|
||||
};
|
||||
|
||||
#pragma mark Custom properties
|
||||
#pragma mark BGMDevice Custom Properties
|
||||
|
||||
enum
|
||||
{
|
||||
// TODO: Combine the two music player properties
|
||||
|
||||
// Remember to update the values BGM_Device returns for kAudioObjectPropertyCustomPropertyInfoList if you
|
||||
// modify this enum.
|
||||
|
||||
// The process ID of the music player as a CFNumber. Setting this property will also clear the value of
|
||||
// kAudioDeviceCustomPropertyMusicPlayerBundleID. We use 0 to mean unset.
|
||||
//
|
||||
@@ -74,13 +77,17 @@ enum
|
||||
// A CFBoolean similar to kAudioDevicePropertyDeviceIsRunning except it ignores whether IO is running for
|
||||
// BGMApp. This is so BGMApp knows when it can stop doing IO to save CPU.
|
||||
kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp = 'runo',
|
||||
// A CFArray of CFDictionaries that each contain an app's pid, bundle ID and volume relative to other
|
||||
// A CFArray of CFDictionaries that each contain an app's PID, bundle ID and volume relative to other
|
||||
// running apps. See the dictionary keys below for more info.
|
||||
//
|
||||
// Getting this property will only return apps with volumes other than the default. Setting this property
|
||||
// will add new app volumes or replace existing ones, but there's currently no way to delete an app from
|
||||
// the internal collection.
|
||||
kAudioDeviceCustomPropertyAppVolumes = 'apvs'
|
||||
kAudioDeviceCustomPropertyAppVolumes = 'apvs',
|
||||
// todo: send the client ids as well. maybe combine with kAudioDeviceCustomPropertyAppVolumes
|
||||
// A CFArray with one entry for each client of BGMDevice. Each entry is a CFDictionary that contains the
|
||||
// client's PID and, if it has one, the client's bundle ID. See the dictionary keys below for more info.
|
||||
kAudioDeviceCustomPropertyClients = 'clts'
|
||||
};
|
||||
|
||||
// The number of silent/audible frames before BGMDriver will change kAudioDeviceCustomPropertyDeviceAudibleState
|
||||
@@ -104,11 +111,18 @@ enum
|
||||
// the midpoint increases the client's volume and a value less than the midpoint decreases it. A volume curve is
|
||||
// applied to kBGMAppVolumesKey_RelativeVolume when it's first set and then each of the app's samples are multiplied
|
||||
// by it.
|
||||
#define kBGMAppVolumesKey_RelativeVolume "rvol"
|
||||
// The app's pid as a CFNumber. May be omitted if kBGMAppVolumesKey_BundleID is present.
|
||||
#define kBGMAppVolumesKey_ProcessID "pid"
|
||||
#define kBGMAppVolumesKey_RelativeVolume "av_rvol"
|
||||
// The app's process ID as a CFNumber (which wraps a pid_t). May be omitted if kBGMAppVolumesKey_BundleID is present.
|
||||
#define kBGMAppVolumesKey_ProcessID "av_pid"
|
||||
// The app's bundle ID as a CFString. May be omitted if kBGMAppVolumesKey_ProcessID is present.
|
||||
#define kBGMAppVolumesKey_BundleID "bid"
|
||||
#define kBGMAppVolumesKey_BundleID "av_bid"
|
||||
|
||||
// kAudioDeviceCustomPropertyClients keys
|
||||
//
|
||||
// The client's process ID as a CFNumber<pid_t>.
|
||||
#define kBGMClientsKey_ProcessID "c_pid"
|
||||
// The client's bundle ID as a CFString. Omitted if the client doesn't have a bundle ID.
|
||||
#define kBGMClientsKey_BundleID "c_bid"
|
||||
|
||||
// Volume curve range for app volumes
|
||||
#define kAppRelativeVolumeMaxRawValue 100
|
||||
@@ -116,31 +130,9 @@ enum
|
||||
#define kAppRelativeVolumeMinDbValue -96.0f
|
||||
#define kAppRelativeVolumeMaxDbValue 0.0f
|
||||
|
||||
#pragma mark XPC Return Codes
|
||||
#pragma mark BGMDevice Custom Property Addresses
|
||||
|
||||
enum {
|
||||
kBGMXPC_Success,
|
||||
kBGMXPC_MessageFailure,
|
||||
kBGMXPC_Timeout,
|
||||
kBGMXPC_BGMAppStateError,
|
||||
kBGMXPC_HardwareError,
|
||||
kBGMXPC_InternalError
|
||||
};
|
||||
|
||||
#pragma mark Exceptions
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
class BGM_InvalidClientException { };
|
||||
class BGM_InvalidClientPIDException { };
|
||||
class BGM_InvalidClientRelativeVolumeException { };
|
||||
class BGM_DeviceNotSetException { };
|
||||
|
||||
#endif
|
||||
|
||||
#pragma mark Property Addresses
|
||||
|
||||
// For convenience
|
||||
// For convenience.
|
||||
|
||||
static const AudioObjectPropertyAddress kBGMMusicPlayerProcessIDAddress = {
|
||||
kAudioDeviceCustomPropertyMusicPlayerProcessID,
|
||||
@@ -172,5 +164,34 @@ static const AudioObjectPropertyAddress kBGMAppVolumesAddress = {
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress kBGMClientsAddress = {
|
||||
kAudioDeviceCustomPropertyClients,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
#pragma mark XPC Return Codes
|
||||
|
||||
enum {
|
||||
kBGMXPC_Success,
|
||||
kBGMXPC_MessageFailure,
|
||||
kBGMXPC_Timeout,
|
||||
kBGMXPC_BGMAppStateError,
|
||||
kBGMXPC_HardwareError,
|
||||
kBGMXPC_InternalError
|
||||
};
|
||||
|
||||
#pragma mark Exceptions
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
class BGM_InvalidClientException { };
|
||||
class BGM_InvalidClientPIDException { };
|
||||
class BGM_InvalidClientRelativeVolumeException { };
|
||||
class BGM_DeviceNotSetException { };
|
||||
class BGM_RuntimeException { };
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __SharedSource__BGM_Types__ */
|
||||
|
||||
|
||||
@@ -30,6 +30,14 @@
|
||||
// System Includes
|
||||
#include <mach/mach_error.h>
|
||||
|
||||
// The Assert macro from CADebugMacros with support for format strings added.
|
||||
#define BGMAssert(inCondition, inMessage, ...) \
|
||||
if(!(inCondition)) \
|
||||
{ \
|
||||
DebugMsg(inMessage, ## __VA_ARGS__); \
|
||||
__ASSERT_STOP; \
|
||||
}
|
||||
|
||||
|
||||
class BGM_Utils
|
||||
{
|
||||
|
||||
@@ -13,8 +13,6 @@ There are also lots of other TODOs commented around the code.
|
||||
- Should we hide music players in the preferences menu if Launch Services says they aren't installed? (And they aren't
|
||||
running or otherwise obviously present on the system.) If so, should we have a "show all" menu item?
|
||||
|
||||
- Disable the Auto-pause Music menu item if the selected music player isn't running
|
||||
|
||||
- System-wide volume boost
|
||||
|
||||
- Listen for notifications when the output device is removed/disabled and revert to the previous output device
|
||||
@@ -87,9 +85,13 @@ There are also lots of other TODOs commented around the code.
|
||||
- When BGMApp changes the default device to BGMDevice, some apps seem to keep using the previous default device if they
|
||||
were running IO at the time. Not sure if we can do anything about this.
|
||||
|
||||
- Figure out how to test BGMDriver with Address Sanitizer enabled. It isn't working because coreaudiod will try to read
|
||||
files outside of its sandbox.
|
||||
- Figure out how to test BGMDriver with Address Sanitizer enabled. It isn't working because it makes coreaudiod try to
|
||||
read files outside of its sandbox and the system kills it.
|
||||
|
||||
- Crash reporting
|
||||
|
||||
- Test Background Music on a system running OS X on a case-sensitive file system. In
|
||||
[#64](https://github.com/kyleneideck/BackgroundMusic/issues/64), BGMDriver failed to compile and the error suggests it
|
||||
was a case-sensitivity problem. That should be fixed now, but I haven't looked for any runtime bugs.
|
||||
|
||||
|
||||
|
||||
+357
-88
@@ -22,29 +22,68 @@
|
||||
# Copyright © 2016 Kyle Neideck
|
||||
# Copyright © 2016 Nick Jacques
|
||||
#
|
||||
# Builds and installs BGMApp, BGMDriver and BGMXPCHelper. Requires xcodebuild.
|
||||
# Builds and installs BGMApp, BGMDriver and BGMXPCHelper. Requires xcodebuild and Xcode.
|
||||
#
|
||||
|
||||
# Safe mode
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
# General error message
|
||||
# Subshells and function inherit the ERR trap
|
||||
set -o errtrace
|
||||
general_error() {
|
||||
echo "$(tput setaf 1)ERROR$(tput sgr0): Install script failed at line $1. This is probably a" \
|
||||
"bug in the script. Feel free to report it." >&2
|
||||
}
|
||||
trap 'general_error ${LINENO}' ERR
|
||||
|
||||
# Build for release by default.
|
||||
# TODO: Add an option to use the debug configuration?
|
||||
# Go to the project directory.
|
||||
cd "$( dirname "${BASH_SOURCE[0]}" )"
|
||||
|
||||
error_handler() {
|
||||
local LAST_COMMAND="${BASH_COMMAND}" LAST_COMMAND_EXIT_STATUS=$?
|
||||
|
||||
# Log the error.
|
||||
echo "Failure in ${0} at line ${1}. The last command was (probably)" >> ${LOG_FILE}
|
||||
echo " ${LAST_COMMAND}" >> ${LOG_FILE}
|
||||
echo "which exited with status ${LAST_COMMAND_EXIT_STATUS}." >> ${LOG_FILE}
|
||||
echo "Error message: ${ERROR_MSG}" >> ${LOG_FILE}
|
||||
echo >> ${LOG_FILE}
|
||||
|
||||
# Scrub username from log (and also real name just in case).
|
||||
sed -i'tmp' "s/$(whoami)/[username removed]/g" ${LOG_FILE}
|
||||
sed -i'tmp' "s/$(id -F)/[name removed]/g" ${LOG_FILE}
|
||||
rm "${LOG_FILE}tmp"
|
||||
|
||||
# Print an error message.
|
||||
echo "$(tput setaf 9)ERROR$(tput sgr0): Install failed at line $1 with the message:"
|
||||
echo
|
||||
echo -e "${ERROR_MSG}" >&2
|
||||
echo >&2
|
||||
echo "Feel free to report this. If you do, you'll probably want to include the" \
|
||||
"build_and_install.log file from this directory ($(pwd)). But quickly skim through it" \
|
||||
"first to check that it doesn't include any personal information. It shouldn't, but this" \
|
||||
"is alpha software so you never know." >&2
|
||||
echo >&2
|
||||
echo "To try building and installing without this build script, see MANUAL-INSTALL.md." >&2
|
||||
|
||||
# Finish logging debug info if the script fails early.
|
||||
if ! [[ -z ${LOG_DEBUG_INFO_TASK_PID:-} ]]; then
|
||||
wait ${LOG_DEBUG_INFO_TASK_PID}
|
||||
fi
|
||||
}
|
||||
|
||||
# Build for release by default. Use -d for a debug build.
|
||||
CONFIGURATION=Release
|
||||
#CONFIGURATION=Debug
|
||||
|
||||
# The default is to clean before installing because we want the log file to have roughly the same
|
||||
# information after every build.
|
||||
CLEAN=clean
|
||||
|
||||
CONTINUE_ON_ERROR=0
|
||||
|
||||
# Update .gitignore if you change this.
|
||||
LOG_FILE=build_and_install.log
|
||||
|
||||
# Empty the log file
|
||||
echo -n > ${LOG_FILE}
|
||||
|
||||
COREAUDIOD_PLIST="/System/Library/LaunchDaemons/com.apple.audio.coreaudiod.plist"
|
||||
|
||||
# TODO: Should (can?) we use xcodebuild to get these from the Xcode project rather than duplicating
|
||||
@@ -56,6 +95,41 @@ DRIVER_DIR="Background Music Device.driver"
|
||||
XPC_HELPER_PATH="$(BGMApp/BGMXPCHelper/safe_install_dir.sh)"
|
||||
XPC_HELPER_DIR="BGMXPCHelper.xpc"
|
||||
|
||||
GENERAL_ERROR_MSG="Internal script error. Probably a bug in this script."
|
||||
BUILD_FAILED_ERROR_MSG="A build command failed. Probably a compilation error."
|
||||
BGMAPP_FAILED_TO_START_ERROR_MSG="Background Music (${APP_PATH}/${APP_DIR}) didn't seem to start \
|
||||
up. It might just be taking a while.
|
||||
|
||||
If it didn't install correctly, you'll need to open the Sound control panel in System Preferences \
|
||||
and change your output device at least once. Your sound probably won't work until you do. (Or you \
|
||||
restart your computer.)
|
||||
|
||||
If you only have one device, you can create a temporary one by opening \
|
||||
\"/Applications/Utilities/Audio MIDI Setup.app\", clicking the plus button and choosing \"Create \
|
||||
Multi-Output Device\"."
|
||||
ERROR_MSG="${GENERAL_ERROR_MSG}"
|
||||
|
||||
XCODEBUILD="/usr/bin/xcodebuild"
|
||||
if ! [[ -x "${XCODEBUILD}" ]]; then
|
||||
XCODEBUILD=$(which xcodebuild || true)
|
||||
fi
|
||||
# This check is last because it takes 10 seconds or so if it fails.
|
||||
if ! [[ -x "${XCODEBUILD}" ]]; then
|
||||
XCODEBUILD=$(/usr/bin/xcrun --find xcodebuild &2>>${LOG_FILE} || true)
|
||||
fi
|
||||
|
||||
# TODO: Update this when/if Xcode 6 is supported.
|
||||
RECOMMENDED_MIN_XCODE_VERSION=7
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [options]" >&2
|
||||
echo -e "\t-n\tDon't clean before building/installing." >&2
|
||||
echo -e "\t-d\tDebug build. (Release is the default.)" >&2
|
||||
echo -e "\t-c\tContinue on script errors. Might not be safe." >&2
|
||||
echo -e "\t-h\tPrint this usage statement." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
bold_face() {
|
||||
echo $(tput bold)$*$(tput sgr0)
|
||||
}
|
||||
@@ -66,10 +140,15 @@ is_alive() {
|
||||
}
|
||||
|
||||
# Shows a "..." animation until the previous command finishes. Shows an error message and exits the
|
||||
# script if the command fails.
|
||||
# script if the command fails. The return value will be the exit status of the command.
|
||||
#
|
||||
# Takes an optional timeout in seconds. The return value will be the exit status of the command.
|
||||
# Params:
|
||||
# - The error message to show if the previous command fails.
|
||||
# - An optional timeout in seconds.
|
||||
show_spinner() {
|
||||
set +e
|
||||
trap - ERR
|
||||
|
||||
local PREV_COMMAND_PID=$!
|
||||
|
||||
# Get the previous command as a string, with variables resolved. Assumes that if the command has
|
||||
@@ -77,76 +156,233 @@ show_spinner() {
|
||||
# one child.)
|
||||
local CHILD_PID=$(pgrep -P ${PREV_COMMAND_PID} | head -n1 || echo ${PREV_COMMAND_PID})
|
||||
local PREV_COMMAND_STRING=$(ps -o command= ${CHILD_PID})
|
||||
local TIMEOUT=${1:-0}
|
||||
local TIMEOUT=${2:-0}
|
||||
|
||||
(I=1;
|
||||
while (is_alive ${PREV_COMMAND_PID}) && ([[ ${TIMEOUT} -lt 1 ]] || [[ $I -lt ${TIMEOUT} ]]); do
|
||||
printf '.';
|
||||
sleep 1;
|
||||
# Erase after we've printed three dots. (\b is backspace.)
|
||||
[[ $(($I % 3)) -eq 0 ]] && printf '\b\b\b \b\b\b';
|
||||
I=$(($I + 1));
|
||||
done) &
|
||||
exec 3>&1 # Creates an alias so the following subshell can print to stdout.
|
||||
DID_TIMEOUT=$(
|
||||
I=1
|
||||
while (is_alive ${PREV_COMMAND_PID}) && \
|
||||
([[ ${TIMEOUT} -lt 1 ]] || [[ $I -lt ${TIMEOUT} ]])
|
||||
do
|
||||
printf '.' >&3
|
||||
sleep 1
|
||||
# Erase after we've printed three dots. (\b is backspace.)
|
||||
[[ $((I % 3)) -eq 0 ]] && printf '\b\b\b \b\b\b' >&3
|
||||
((I++))
|
||||
done
|
||||
if [[ $I -eq ${TIMEOUT} ]]; then
|
||||
kill ${PREV_COMMAND_PID} >> ${LOG_FILE} 2>&1
|
||||
echo 1
|
||||
else
|
||||
echo 0
|
||||
fi)
|
||||
exec 3<&- # Close the file descriptor.
|
||||
|
||||
set +e
|
||||
wait ${PREV_COMMAND_PID}
|
||||
local EXIT_STATUS=$?
|
||||
set -e
|
||||
|
||||
# Clean up the dots.
|
||||
printf '\b\b\b'
|
||||
printf '\b\b\b \b\b\b'
|
||||
|
||||
# Print an error message if the command fails.
|
||||
# (wait returns 127 if the process has already exited.)
|
||||
if [[ ${EXIT_STATUS} -ne 0 ]] && [[ ${EXIT_STATUS} -ne 127 ]]; then
|
||||
echo "$(tput setaf 1)ERROR$(tput sgr0): Build step failed. See ${LOG_FILE} for details." >&2
|
||||
echo "Failed command:" >&2
|
||||
echo " ${PREV_COMMAND_STRING}" >&2
|
||||
ERROR_MSG="$1"
|
||||
if [[ ${DID_TIMEOUT} -eq 0 ]]; then
|
||||
ERROR_MSG+="\n\nFailed command:
|
||||
${PREV_COMMAND_STRING}"
|
||||
fi
|
||||
|
||||
exit ${EXIT_STATUS}
|
||||
error_handler ${LINENO}
|
||||
|
||||
if [[ ${CONTINUE_ON_ERROR} -eq 0 ]]; then
|
||||
exit ${EXIT_STATUS}
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ${CONTINUE_ON_ERROR} -eq 0 ]]; then
|
||||
set -e
|
||||
trap 'error_handler ${LINENO}' ERR
|
||||
fi
|
||||
|
||||
return ${EXIT_STATUS}
|
||||
}
|
||||
|
||||
# Check for xcodebuild.
|
||||
if [[ "$(which xcodebuild)" == "" ]]; then
|
||||
echo "$(tput setaf 1)ERROR$(tput sgr0): Can't find xcodebuild in your \$PATH." >&2
|
||||
echo >&2
|
||||
echo "If you have Xcode installed, you should be able to install the command line developer" \
|
||||
"tools, including xcodebuild, with" >&2
|
||||
echo " xcode-select --install" >&2
|
||||
echo "If not, you'll need to install Xcode (~9GB), because xcodebuild no longer works without" \
|
||||
"it." >&2
|
||||
parse_options() {
|
||||
while getopts ":ndch" opt; do
|
||||
case $opt in
|
||||
n)
|
||||
CLEAN=""
|
||||
;;
|
||||
d)
|
||||
CONFIGURATION="Debug"
|
||||
;;
|
||||
c)
|
||||
CONTINUE_ON_ERROR=1
|
||||
echo "$(tput setaf 11)WARNING$(tput sgr0): Ignoring errors."
|
||||
set +e
|
||||
trap - ERR
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Disable error handlers.
|
||||
trap - ERR
|
||||
set +e
|
||||
check_xcode() {
|
||||
RETURN=0
|
||||
|
||||
# Check for Xcode.
|
||||
XCODE_PATH=$(which xcode-select > /dev/null && xcode-select --print-path)
|
||||
XCODE_PATH=${XCODE_PATH%/Contents/Developer}
|
||||
|
||||
if [[ "${XCODE_PATH}" == "" ]] && [[ -d /Applications/Xcode.app ]]; then
|
||||
XCODE_PATH="/Applications/Xcode.app"
|
||||
# First, check xcodebuild exists on the system an is an executable.
|
||||
if ! [[ -x "${XCODEBUILD}" ]] || ! xcode-select --print-path &>/dev/null || \
|
||||
! pkgutil --pkg-info=com.apple.pkg.CLTools_Executables &>/dev/null; then
|
||||
RETURN=1
|
||||
fi
|
||||
|
||||
if [[ "${XCODE_PATH}" == "" ]] && [[ -d ~/Applications/Xcode.app ]]; then
|
||||
XCODE_PATH="~/Applications/Xcode.app"
|
||||
# Check that Xcode is installed, not just the command line tools.
|
||||
if ! "${XCODEBUILD}" -version &>/dev/null; then
|
||||
((RETURN+=2))
|
||||
fi
|
||||
|
||||
if [[ "${XCODE_PATH}" != "" ]]; then
|
||||
# Exit with an error message if we couldn't find a working xcodebuild.
|
||||
if [[ ${RETURN} -eq 0 ]] && \
|
||||
# Version check.
|
||||
[[ "$(echo ${XCODE_VERSION} | sed 's/\..*$//g')" -lt ${RECOMMENDED_MIN_XCODE_VERSION} ]]
|
||||
then
|
||||
RETURN=-1
|
||||
fi
|
||||
|
||||
exit ${RETURN}
|
||||
}
|
||||
|
||||
# Expects CHECK_XCODE_TASK_PID to be set.
|
||||
handle_check_xcode_result() {
|
||||
if [[ -z ${HANDLED_CHECK_XCODE_RESULT:-} ]]; then
|
||||
HANDLED_CHECK_XCODE_RESULT=1
|
||||
# Wait for the Xcode checks to finish.
|
||||
set +e
|
||||
trap - ERR
|
||||
wait ${CHECK_XCODE_TASK_PID}
|
||||
CHECK_XCODE_TASK_STATUS=$?
|
||||
trap 'error_handler ${LINENO}' ERR
|
||||
set -e
|
||||
|
||||
# If there was a problem with Xcode/xcodebuild, print the error message and exit.
|
||||
if [[ ${CHECK_XCODE_TASK_STATUS} -ne 0 ]]; then
|
||||
handle_check_xcode_failure ${CHECK_XCODE_TASK_STATUS}
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
handle_check_xcode_failure() {
|
||||
# No command line tools
|
||||
if [[ $1 -eq 1 ]] || [[ $1 -eq 3 ]]; then
|
||||
echo "$(tput setaf 9)ERROR$(tput sgr0): The Xcode Command Line Tools don't seem to be" \
|
||||
"installed on your system." >&2
|
||||
echo >&2
|
||||
echo "If you have Xcode installed, you should be able to install them with" >&2
|
||||
echo " xcode-select --install" >&2
|
||||
echo "If not, you'll need to install Xcode (~9GB), because xcodebuild no longer works" \
|
||||
"without it." >&2
|
||||
echo >&2
|
||||
echo "It looks like you have Xcode installed to ${XCODE_PATH}" >&2
|
||||
fi
|
||||
|
||||
exit 1
|
||||
# No Xcode
|
||||
if [[ $1 -eq 2 ]] || [[ $1 -eq 3 ]]; then
|
||||
echo "$(tput setaf 9)ERROR$(tput sgr0): Unfortunately, Xcode (~9GB) is required to build" \
|
||||
"Background Music, but ${XCODEBUILD} doesn't appear to be usable. You may need to" \
|
||||
"tell the Xcode command line tools where your Xcode is installed to with" >&2
|
||||
echo " xcode-select --switch /The/path/to/your/Xcode.app" >&2
|
||||
echo >&2
|
||||
echo "Output from ${XCODEBUILD}:" >&2
|
||||
|
||||
"${XCODEBUILD}" -version >&2 || true
|
||||
|
||||
echo >&2
|
||||
fi
|
||||
|
||||
if [[ $1 -eq -1 ]]; then
|
||||
# Xcode version is probably too old.
|
||||
echo "$(tput setaf 11)WARNING$(tput sgr0): Your version of Xcode (${XCODE_VERSION}) may" \
|
||||
"not be recent enough to build Background Music." >&2
|
||||
else
|
||||
# Disable error handlers
|
||||
set +e; trap - ERR
|
||||
|
||||
# Look for an Xcode install.
|
||||
echo "Looking for Xcode..." >&2
|
||||
XCODE_PATHS=$(mdfind "kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode' || \
|
||||
kMDItemCFBundleIdentifier == 'com.apple.Xcode'")
|
||||
|
||||
if [[ "${XCODE_PATHS}" != "" ]]; then
|
||||
echo "It looks like you have Xcode installed to" >&2
|
||||
echo "${XCODE_PATHS}" >&2
|
||||
else
|
||||
echo "Not found." >&2
|
||||
fi
|
||||
|
||||
if [[ ${CONTINUE_ON_ERROR} -eq 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
log_debug_info() {
|
||||
# Log some environment details, version numbers, etc. This takes a while, so we do it in the
|
||||
# background.
|
||||
(set +e; trap - ERR
|
||||
echo "Background Music Build Log" >> ${LOG_FILE}
|
||||
echo "----" >> ${LOG_FILE}
|
||||
echo "System details:" >> ${LOG_FILE}
|
||||
|
||||
sw_vers >> ${LOG_FILE} 2>&1
|
||||
# The same as uname -a, except without printing the nodename (for privacy).
|
||||
uname -mrsv >> ${LOG_FILE} 2>&1
|
||||
|
||||
/bin/bash --version >> ${LOG_FILE} 2>&1
|
||||
/usr/bin/env python --version >> ${LOG_FILE} 2>&1
|
||||
|
||||
echo "On git branch: $(git rev-parse --abbrev-ref HEAD 2>&1)" >> ${LOG_FILE}
|
||||
echo "Most recent commit: $(git rev-parse HEAD 2>&1)" \
|
||||
"(\"$(git show -s --format=%s HEAD 2>&1)\")" >> ${LOG_FILE}
|
||||
|
||||
echo "Using xcodebuild: ${XCODEBUILD}" >> ${LOG_FILE}
|
||||
echo "Using BGMXPCHelper path: ${XPC_HELPER_PATH}" >> ${LOG_FILE}
|
||||
|
||||
xcode-select --version >> ${LOG_FILE} 2>&1
|
||||
echo "Xcode path: $(xcode-select --print-path 2>&1)" >> ${LOG_FILE}
|
||||
echo "Xcode version:" >> ${LOG_FILE}
|
||||
xcodebuild -version >> ${LOG_FILE} 2>&1
|
||||
echo "Xcode SDKs:" >> ${LOG_FILE}
|
||||
xcodebuild -showsdks >> ${LOG_FILE} 2>&1
|
||||
xcrun --version >> ${LOG_FILE} 2>&1
|
||||
echo "Clang version:" >> ${LOG_FILE}
|
||||
$(/usr/bin/xcrun --find clang 2>&1) --version >> ${LOG_FILE} 2>&1
|
||||
|
||||
echo "launchctl version: $(launchctl version 2>&1)" >> ${LOG_FILE}
|
||||
echo "----" >> ${LOG_FILE}) &
|
||||
|
||||
LOG_DEBUG_INFO_TASK_PID=$!
|
||||
}
|
||||
|
||||
# Register our handler so we can print a message and clean up if there's an error.
|
||||
trap 'error_handler ${LINENO}' ERR
|
||||
|
||||
parse_options "$@"
|
||||
|
||||
# Warn if running as root.
|
||||
if [[ $(id -u) -eq 0 ]]; then
|
||||
echo "$(tput setaf 11)WARNING$(tput sgr0): This script is not intended to be run as root. Run" \
|
||||
"it normally and it'll sudo when it needs to." >&2
|
||||
fi
|
||||
|
||||
# Go to the project directory.
|
||||
cd "$( dirname "${BASH_SOURCE[0]}" )"
|
||||
|
||||
# Print initial message.
|
||||
echo "$(bold_face About to install Background Music). Please pause all audio, if you can."
|
||||
[[ "${CONFIGURATION}" == "Debug" ]] && echo "Debug build."
|
||||
echo
|
||||
echo "This script will install:"
|
||||
echo " - ${APP_PATH}/${APP_DIR}"
|
||||
@@ -154,6 +390,12 @@ echo " - ${DRIVER_PATH}/${DRIVER_DIR}"
|
||||
echo " - ${XPC_HELPER_PATH}/${XPC_HELPER_DIR}"
|
||||
echo " - /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"
|
||||
echo
|
||||
|
||||
# Make sure Xcode and the command line tools are installed and recent enough.
|
||||
XCODE_VERSION=$(${XCODEBUILD} -version | head -n 1 | awk '{ print $2 }' 2>/dev/null || echo 0)
|
||||
check_xcode &
|
||||
CHECK_XCODE_TASK_PID=$!
|
||||
|
||||
read -p "Continue (y/N)? " CONTINUE_INSTALLATION
|
||||
|
||||
if [[ "${CONTINUE_INSTALLATION}" != "y" ]] && [[ "${CONTINUE_INSTALLATION}" != "Y" ]]; then
|
||||
@@ -161,66 +403,87 @@ if [[ "${CONTINUE_INSTALLATION}" != "y" ]] && [[ "${CONTINUE_INSTALLATION}" != "
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If the check_xcode process has already finished, we can check the result early.
|
||||
if ! is_alive ${CHECK_XCODE_TASK_PID}; then
|
||||
handle_check_xcode_result
|
||||
fi
|
||||
|
||||
# Update the user's sudo timestamp. (Prompts the user for their password.)
|
||||
if ! sudo -v; then
|
||||
# Don't call sudo -v if this is a Travis CI build.
|
||||
if ([[ -z ${TRAVIS:-} ]] || [[ "${TRAVIS}" != true ]]) && ! sudo -v; then
|
||||
echo "ERROR: This script must be run by a user with administrator (sudo) privileges." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
handle_check_xcode_result
|
||||
|
||||
log_debug_info
|
||||
|
||||
# BGMDriver
|
||||
|
||||
echo "[1/3] Installing the virtual audio device $(bold_face ${DRIVER_DIR}) to" \
|
||||
"$(bold_face ${DRIVER_PATH})." \
|
||||
| tee ${LOG_FILE}
|
||||
| tee -a ${LOG_FILE}
|
||||
|
||||
# Disable the -e shell option here so we can handle the error differently.
|
||||
(set +e;
|
||||
sudo xcodebuild -project BGMDriver/BGMDriver.xcodeproj \
|
||||
-target "Background Music Device" \
|
||||
-configuration ${CONFIGURATION} \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
DSTROOT="/" \
|
||||
install >> ${LOG_FILE} 2>&1) &
|
||||
# Disable the -e shell option and error trap for build commands so we can handle errors differently.
|
||||
(set +e; trap - ERR
|
||||
sudo "${XCODEBUILD}" -project BGMDriver/BGMDriver.xcodeproj \
|
||||
-target "Background Music Device" \
|
||||
-configuration ${CONFIGURATION} \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
DSTROOT="/" \
|
||||
${CLEAN} install >> ${LOG_FILE} 2>&1) &
|
||||
|
||||
show_spinner
|
||||
show_spinner "${BUILD_FAILED_ERROR_MSG}"
|
||||
|
||||
# BGMXPCHelper
|
||||
|
||||
echo "[2/3] Installing $(bold_face ${XPC_HELPER_DIR}) to $(bold_face ${XPC_HELPER_PATH})." \
|
||||
| tee -a ${LOG_FILE}
|
||||
|
||||
(set +e;
|
||||
sudo xcodebuild -project BGMApp/BGMApp.xcodeproj \
|
||||
-target BGMXPCHelper \
|
||||
-configuration ${CONFIGURATION} \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
DSTROOT="/" \
|
||||
INSTALL_PATH="${XPC_HELPER_PATH}" \
|
||||
install >> ${LOG_FILE} 2>&1) &
|
||||
(set +e; trap - ERR
|
||||
sudo "${XCODEBUILD}" -project BGMApp/BGMApp.xcodeproj \
|
||||
-target BGMXPCHelper \
|
||||
-configuration ${CONFIGURATION} \
|
||||
OTHER_CFLAGS="-Wno-everything" \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
DSTROOT="/" \
|
||||
INSTALL_PATH="${XPC_HELPER_PATH}" \
|
||||
${CLEAN} install >> ${LOG_FILE} 2>&1) &
|
||||
|
||||
show_spinner
|
||||
show_spinner "${BUILD_FAILED_ERROR_MSG}"
|
||||
|
||||
# BGMApp
|
||||
|
||||
echo "[3/3] Installing $(bold_face ${APP_DIR}) to $(bold_face ${APP_PATH})." \
|
||||
| tee -a ${LOG_FILE}
|
||||
|
||||
(set +e;
|
||||
sudo xcodebuild -project BGMApp/BGMApp.xcodeproj \
|
||||
-target "Background Music" \
|
||||
-configuration ${CONFIGURATION} \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
DSTROOT="/" \
|
||||
install >> ${LOG_FILE} 2>&1) &
|
||||
(set +e; trap - ERR
|
||||
sudo "${XCODEBUILD}" -project BGMApp/BGMApp.xcodeproj \
|
||||
-target "Background Music" \
|
||||
-configuration ${CONFIGURATION} \
|
||||
OTHER_CFLAGS="-Wno-everything" \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
DSTROOT="/" \
|
||||
${CLEAN} install >> ${LOG_FILE} 2>&1) &
|
||||
|
||||
show_spinner
|
||||
show_spinner "${BUILD_FAILED_ERROR_MSG}"
|
||||
|
||||
# Fix Background Music.app owner/group.
|
||||
# (We have to run xcodebuild as root to install BGMXPCHelper because it installs to directories
|
||||
#
|
||||
# We have to run xcodebuild as root to install BGMXPCHelper because it installs to directories
|
||||
# owned by root. But that means the build directory gets created by root, and since BGMApp uses the
|
||||
# same build directory we have to run xcodebuild as root to install BGMApp as well.)
|
||||
# same build directory we have to run xcodebuild as root to install BGMApp as well.
|
||||
#
|
||||
# TODO: Can't we just chown -R the build dir before we install BGMApp? Then we wouldn't have to
|
||||
# install BGMApp as root. (But maybe still handle the unlikely case of APP_PATH not being
|
||||
# user-writable.)
|
||||
sudo chown -R "$(whoami):admin" "${APP_PATH}/${APP_DIR}"
|
||||
|
||||
# Fix the build directories' owner/group. This is mainly so the whole source directory can be
|
||||
# deleted easily after installing.
|
||||
sudo chown -R "$(whoami):admin" "BGMApp/build" "BGMDriver/build"
|
||||
|
||||
# Restart coreaudiod.
|
||||
|
||||
echo "Restarting coreaudiod to load the virtual audio device." \
|
||||
@@ -236,7 +499,7 @@ echo "Restarting coreaudiod to load the virtual audio device." \
|
||||
(sudo launchctl unload "${COREAUDIOD_PLIST}" &>/dev/null && \
|
||||
sudo launchctl load "${COREAUDIOD_PLIST}" &>/dev/null) || \
|
||||
sudo killall coreaudiod &>/dev/null) && \
|
||||
sleep 2
|
||||
sleep 5
|
||||
|
||||
# Invalidate sudo ticket
|
||||
sudo -k
|
||||
@@ -246,13 +509,19 @@ sudo -k
|
||||
# device after restarting coreaudiod and this is the easiest way.
|
||||
echo "Launching Background Music."
|
||||
|
||||
ERROR_MSG="${BGMAPP_FAILED_TO_START_ERROR_MSG}"
|
||||
open "${APP_PATH}/${APP_DIR}"
|
||||
|
||||
# Ignore script errors from this point.
|
||||
set +e
|
||||
trap - ERR
|
||||
|
||||
# Wait up to 5 seconds for Background Music to start.
|
||||
(while ! (ps -Ao ucomm= | grep 'Background Music' > /dev/null); do
|
||||
sleep 1;
|
||||
done) &
|
||||
show_spinner 5
|
||||
(trap 'exit 1' TERM
|
||||
while ! (ps -Ao ucomm= | grep 'Background Music' > /dev/null); do
|
||||
sleep 1
|
||||
done) &
|
||||
show_spinner "${BGMAPP_FAILED_TO_START_ERROR_MSG}" 5
|
||||
|
||||
echo "Done."
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// TODO: Delete this before merging WIP-MultiprocessAppVols into master.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <libproc.h>
|
||||
#include <errno.h>
|
||||
|
||||
extern int responsibility_get_responsible_for_pid(pid_t, int32_t*, uint64_t*, size_t*, char*);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s <pid>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
pid_t pid = (pid_t)atoi(argv[1]);
|
||||
|
||||
{
|
||||
char path_buf[PROC_PIDPATHINFO_MAXSIZE] = "";
|
||||
size_t path_len = sizeof(path_buf);
|
||||
if (proc_pidpath(pid, path_buf, path_len) <= 0) {
|
||||
printf("Couldn't get pid path for pid %d\n", pid);
|
||||
printf("Error %d: %s\n", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
path_buf[path_len - 1] = '\0';
|
||||
printf("Path for process: %s\n", path_buf);
|
||||
}
|
||||
|
||||
{
|
||||
int32_t rpid;
|
||||
uint64_t urpid;
|
||||
char responsible_path_buf[PROC_PIDPATHINFO_MAXSIZE] = "";
|
||||
size_t responsible_path_len = sizeof(responsible_path_buf);
|
||||
if (responsibility_get_responsible_for_pid(pid, &rpid, &urpid, &responsible_path_len, responsible_path_buf) != 0) {
|
||||
printf("Couldn't get responsibility pid for pid %d\n", pid);
|
||||
printf("Error %d: %s\n", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
responsible_path_buf[responsible_path_len - 1] = '\0';
|
||||
printf("Path for responsible process: %s\n", responsible_path_buf);
|
||||
printf("Responsible PID: %d\n", rpid);
|
||||
printf("Responsible unique PID: %llu\n", urpid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+3
-2
@@ -75,8 +75,8 @@ read -p "Continue (y/N)? " user_prompt
|
||||
|
||||
if [ "$user_prompt" == "y" ] || [ "$user_prompt" == "Y" ]; then
|
||||
|
||||
# Ensure that the user can use sudo
|
||||
if ! sudo -v; then
|
||||
# Ensure that the user can use sudo. (But not if this is a Travis CI build, because then it would fail.)
|
||||
if ([[ -z ${TRAVIS:-} ]] || [[ "${TRAVIS}" != true ]]) && ! sudo -v; then
|
||||
echo "ERROR: This script must be run by a user with administrator (sudo) privileges." >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -148,6 +148,7 @@ if [ "$user_prompt" == "y" ] || [ "$user_prompt" == "Y" ]; then
|
||||
# Invalidate sudo ticket
|
||||
sudo -k
|
||||
|
||||
# TODO: What if they only have one audio device?
|
||||
echo -e "\n${bold}Done! Toggle your sound output device in the Sound control panel to complete the uninstall.${normal}"
|
||||
|
||||
osascript -e 'tell application "System Preferences"
|
||||
|
||||
Reference in New Issue
Block a user