Initial commit
30
.gitignore
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
.DS_Store
|
||||
.*.swp
|
||||
/BGMDriver/BGMDriver/quick_install.conf
|
||||
|
||||
# Everything below is from https://github.com/github/gitignore/blob/master/Objective-C.gitignore
|
||||
|
||||
## Build generated
|
||||
build/
|
||||
DerivedData
|
||||
|
||||
## Various settings
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
|
||||
## Other
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
*.xcuserstate
|
||||
*.xcscmblueprint
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
25
BGM.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:BGMDriver/BGMDriver.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:BGMApp/BGMApp.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:README.md">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:TODO.md">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:CONTRIBUTING.md">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:LICENSE">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Images">
|
||||
</FileRef>
|
||||
</Workspace>
|
808
BGMApp/BGMApp.xcodeproj/project.pbxproj
Normal file
|
@ -0,0 +1,808 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1C0BD0A51BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C0BD0A41BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm */; };
|
||||
1C0BD0A81BF1B029004F4CF5 /* BGMPreferencesMenu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C0BD0A71BF1B029004F4CF5 /* BGMPreferencesMenu.mm */; };
|
||||
1C1108AE1C444F01003F625E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1CB8B3421BBA75EF000E2DD1 /* MainMenu.xib */; };
|
||||
1C1465B81BCC3A73003AEFE6 /* BGMAutoPauseMusic.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C1465B71BCC3A73003AEFE6 /* BGMAutoPauseMusic.mm */; };
|
||||
1C1962E41BC94E15008A4DF7 /* CARingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962E21BC94E15008A4DF7 /* CARingBuffer.cpp */; };
|
||||
1C1962E71BC94E91008A4DF7 /* BGMPlayThrough.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962E51BC94E91008A4DF7 /* BGMPlayThrough.cpp */; };
|
||||
1C1962F31BCABFC5008A4DF7 /* CAHALAudioDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962EB1BCABFC5008A4DF7 /* CAHALAudioDevice.cpp */; };
|
||||
1C1962F41BCABFC5008A4DF7 /* CAHALAudioObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962ED1BCABFC5008A4DF7 /* CAHALAudioObject.cpp */; };
|
||||
1C1962F51BCABFC5008A4DF7 /* CAHALAudioStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962EF1BCABFC5008A4DF7 /* CAHALAudioStream.cpp */; };
|
||||
1C1962F61BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962F11BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp */; };
|
||||
1C1962FA1BCAC061008A4DF7 /* CADebugMacros.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962F71BCAC061008A4DF7 /* CADebugMacros.cpp */; };
|
||||
1C1962FD1BCAC0C3008A4DF7 /* CADebugPrintf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962FB1BCAC0C3008A4DF7 /* CADebugPrintf.cpp */; };
|
||||
1C1963011BCAC0F6008A4DF7 /* CACFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962FF1BCAC0F6008A4DF7 /* CACFString.cpp */; };
|
||||
1C1963031BCAC160008A4DF7 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C1963021BCAC160008A4DF7 /* CoreAudio.framework */; };
|
||||
1C1963061BCAF468008A4DF7 /* CAMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1963041BCAF468008A4DF7 /* CAMutex.cpp */; };
|
||||
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 */; };
|
||||
1CB8B33F1BBA75EF000E2DD1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B33E1BBA75EF000E2DD1 /* main.m */; };
|
||||
1CB8B3501BBA75F0000E2DD1 /* BGMAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B34F1BBA75F0000E2DD1 /* BGMAppTests.m */; };
|
||||
1CC1DF811BE5068A00FB8FE4 /* CACFArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF7D1BE5068A00FB8FE4 /* CACFArray.cpp */; };
|
||||
1CC1DF821BE5068A00FB8FE4 /* CACFDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF7F1BE5068A00FB8FE4 /* CACFDictionary.cpp */; };
|
||||
1CC1DF911BE5891300FB8FE4 /* CADebugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF8F1BE5891300FB8FE4 /* CADebugger.cpp */; };
|
||||
1CC1DF961BE8607700FB8FE4 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1CC1DF951BE8607700FB8FE4 /* Images.xcassets */; };
|
||||
1CD1FD301BDDEAF2004F7E1B /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */; };
|
||||
1CE7064C1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CE7064B1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm */; };
|
||||
1CED61691C3081C2002CAFCF /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 1CED61681C3081C2002CAFCF /* LICENSE */; };
|
||||
1CED616C1C316E1A002CAFCF /* BGMAudioDeviceManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */; };
|
||||
271677BA1C6CBDFA0080B0A2 /* CACFNumber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 271677B81C6CBDFA0080B0A2 /* CACFNumber.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
1CB8B34A1BBA75F0000E2DD1 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 1CB8B32E1BBA75EF000E2DD1 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 1CB8B3351BBA75EF000E2DD1;
|
||||
remoteInfo = BGMApp;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1C0BD0A31BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMAutoPauseMusicPrefs.h; path = Preferences/BGMAutoPauseMusicPrefs.h; sourceTree = "<group>"; };
|
||||
1C0BD0A41BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMAutoPauseMusicPrefs.mm; path = Preferences/BGMAutoPauseMusicPrefs.mm; sourceTree = "<group>"; };
|
||||
1C0BD0A61BF1B029004F4CF5 /* BGMPreferencesMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMPreferencesMenu.h; path = Preferences/BGMPreferencesMenu.h; sourceTree = "<group>"; };
|
||||
1C0BD0A71BF1B029004F4CF5 /* BGMPreferencesMenu.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMPreferencesMenu.mm; path = Preferences/BGMPreferencesMenu.mm; sourceTree = "<group>"; };
|
||||
1C1465B71BCC3A73003AEFE6 /* BGMAutoPauseMusic.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMAutoPauseMusic.mm; sourceTree = "<group>"; };
|
||||
1C1465B91BCC49D1003AEFE6 /* BGMAutoPauseMusic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMAutoPauseMusic.h; sourceTree = "<group>"; };
|
||||
1C1962E21BC94E15008A4DF7 /* CARingBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CARingBuffer.cpp; path = PublicUtility/CARingBuffer.cpp; sourceTree = "<group>"; };
|
||||
1C1962E31BC94E15008A4DF7 /* CARingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CARingBuffer.h; path = PublicUtility/CARingBuffer.h; sourceTree = "<group>"; };
|
||||
1C1962E51BC94E91008A4DF7 /* BGMPlayThrough.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMPlayThrough.cpp; sourceTree = "<group>"; };
|
||||
1C1962E61BC94E91008A4DF7 /* BGMPlayThrough.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMPlayThrough.h; sourceTree = "<group>"; };
|
||||
1C1962E81BC95301008A4DF7 /* CAAtomic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAtomic.h; path = PublicUtility/CAAtomic.h; sourceTree = "<group>"; };
|
||||
1C1962E91BC95301008A4DF7 /* CAAutoDisposer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAutoDisposer.h; path = PublicUtility/CAAutoDisposer.h; sourceTree = "<group>"; };
|
||||
1C1962EA1BC95301008A4DF7 /* CABitOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CABitOperations.h; path = PublicUtility/CABitOperations.h; sourceTree = "<group>"; };
|
||||
1C1962EB1BCABFC5008A4DF7 /* CAHALAudioDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAHALAudioDevice.cpp; path = PublicUtility/CAHALAudioDevice.cpp; sourceTree = "<group>"; };
|
||||
1C1962EC1BCABFC5008A4DF7 /* CAHALAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAHALAudioDevice.h; path = PublicUtility/CAHALAudioDevice.h; sourceTree = "<group>"; };
|
||||
1C1962ED1BCABFC5008A4DF7 /* CAHALAudioObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAHALAudioObject.cpp; path = PublicUtility/CAHALAudioObject.cpp; sourceTree = "<group>"; };
|
||||
1C1962EE1BCABFC5008A4DF7 /* CAHALAudioObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAHALAudioObject.h; path = PublicUtility/CAHALAudioObject.h; sourceTree = "<group>"; };
|
||||
1C1962EF1BCABFC5008A4DF7 /* CAHALAudioStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAHALAudioStream.cpp; path = PublicUtility/CAHALAudioStream.cpp; sourceTree = "<group>"; };
|
||||
1C1962F01BCABFC5008A4DF7 /* CAHALAudioStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAHALAudioStream.h; path = PublicUtility/CAHALAudioStream.h; sourceTree = "<group>"; };
|
||||
1C1962F11BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAHALAudioSystemObject.cpp; path = PublicUtility/CAHALAudioSystemObject.cpp; sourceTree = "<group>"; };
|
||||
1C1962F21BCABFC5008A4DF7 /* CAHALAudioSystemObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAHALAudioSystemObject.h; path = PublicUtility/CAHALAudioSystemObject.h; sourceTree = "<group>"; };
|
||||
1C1962F71BCAC061008A4DF7 /* CADebugMacros.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CADebugMacros.cpp; path = PublicUtility/CADebugMacros.cpp; sourceTree = "<group>"; };
|
||||
1C1962F81BCAC061008A4DF7 /* CADebugMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CADebugMacros.h; path = PublicUtility/CADebugMacros.h; sourceTree = "<group>"; };
|
||||
1C1962F91BCAC061008A4DF7 /* CAPropertyAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAPropertyAddress.h; path = PublicUtility/CAPropertyAddress.h; sourceTree = "<group>"; };
|
||||
1C1962FB1BCAC0C3008A4DF7 /* CADebugPrintf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CADebugPrintf.cpp; path = PublicUtility/CADebugPrintf.cpp; sourceTree = "<group>"; };
|
||||
1C1962FC1BCAC0C3008A4DF7 /* CADebugPrintf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CADebugPrintf.h; path = PublicUtility/CADebugPrintf.h; sourceTree = "<group>"; };
|
||||
1C1962FE1BCAC0DB008A4DF7 /* CAException.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CAException.h; path = PublicUtility/CAException.h; sourceTree = "<group>"; };
|
||||
1C1962FF1BCAC0F6008A4DF7 /* CACFString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CACFString.cpp; path = PublicUtility/CACFString.cpp; sourceTree = "<group>"; };
|
||||
1C1963001BCAC0F6008A4DF7 /* CACFString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CACFString.h; path = PublicUtility/CACFString.h; sourceTree = "<group>"; };
|
||||
1C1963021BCAC160008A4DF7 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
|
||||
1C1963041BCAF468008A4DF7 /* CAMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAMutex.cpp; path = PublicUtility/CAMutex.cpp; sourceTree = "<group>"; };
|
||||
1C1963051BCAF468008A4DF7 /* CAMutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMutex.h; path = PublicUtility/CAMutex.h; sourceTree = "<group>"; };
|
||||
1C1963071BCAF677008A4DF7 /* CAHostTimeBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAHostTimeBase.cpp; path = PublicUtility/CAHostTimeBase.cpp; sourceTree = "<group>"; };
|
||||
1C1963081BCAF677008A4DF7 /* CAHostTimeBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAHostTimeBase.h; path = PublicUtility/CAHostTimeBase.h; sourceTree = "<group>"; };
|
||||
1C2336D91BEAB6E7004C1C4E /* BGMMusicPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMMusicPlayer.m; path = "Music Players/BGMMusicPlayer.m"; sourceTree = "<group>"; };
|
||||
1C2336DB1BEAB73F004C1C4E /* BGMiTunes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BGMiTunes.h; path = "Music Players/BGMiTunes.h"; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
1C4699411BD5BA9E00F78043 /* BGM_Types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGM_Types.h; path = ../BGMDriver/BGMDriver/BGM_Types.h; sourceTree = "<group>"; };
|
||||
1C4699461BD5C0E400F78043 /* BGMiTunes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMiTunes.m; path = "Music Players/BGMiTunes.m"; sourceTree = "<group>"; };
|
||||
1C46994B1BD5E70D00F78043 /* iTunes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTunes.h; 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>"; };
|
||||
1C6F8E1D1BEB1406000D8108 /* Spotify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Spotify.h; sourceTree = "<group>"; };
|
||||
1C8034C21BDAFD5700668E00 /* CAPThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAPThread.cpp; path = PublicUtility/CAPThread.cpp; sourceTree = "<group>"; };
|
||||
1C8034C31BDAFD5700668E00 /* CAPThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAPThread.h; path = PublicUtility/CAPThread.h; sourceTree = "<group>"; };
|
||||
1CB8B3361BBA75EF000E2DD1 /* Background Music.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Background Music.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1CB8B33A1BBA75EF000E2DD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
1CB8B33B1BBA75EF000E2DD1 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
1CB8B33C1BBA75EF000E2DD1 /* AppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = "<group>"; };
|
||||
1CB8B33E1BBA75EF000E2DD1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
1CB8B3431BBA75EF000E2DD1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
1CB8B3491BBA75F0000E2DD1 /* BGMAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BGMAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1CB8B34E1BBA75F0000E2DD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
1CB8B34F1BBA75F0000E2DD1 /* BGMAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BGMAppTests.m; sourceTree = "<group>"; };
|
||||
1CC1DF7D1BE5068A00FB8FE4 /* CACFArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CACFArray.cpp; path = PublicUtility/CACFArray.cpp; sourceTree = "<group>"; };
|
||||
1CC1DF7E1BE5068A00FB8FE4 /* CACFArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CACFArray.h; path = PublicUtility/CACFArray.h; sourceTree = "<group>"; };
|
||||
1CC1DF7F1BE5068A00FB8FE4 /* CACFDictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CACFDictionary.cpp; path = PublicUtility/CACFDictionary.cpp; sourceTree = "<group>"; };
|
||||
1CC1DF801BE5068A00FB8FE4 /* CACFDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CACFDictionary.h; path = PublicUtility/CACFDictionary.h; sourceTree = "<group>"; };
|
||||
1CC1DF8F1BE5891300FB8FE4 /* CADebugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CADebugger.cpp; path = PublicUtility/CADebugger.cpp; sourceTree = "<group>"; };
|
||||
1CC1DF901BE5891300FB8FE4 /* CADebugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CADebugger.h; path = PublicUtility/CADebugger.h; sourceTree = "<group>"; };
|
||||
1CC1DF951BE8607700FB8FE4 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
1CE7064A1BF1EC0600BFC06D /* BGMOutputDevicePrefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMOutputDevicePrefs.h; path = Preferences/BGMOutputDevicePrefs.h; sourceTree = "<group>"; };
|
||||
1CE7064B1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMOutputDevicePrefs.mm; path = Preferences/BGMOutputDevicePrefs.mm; sourceTree = "<group>"; };
|
||||
1CED61681C3081C2002CAFCF /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
|
||||
1CED616A1C316E1A002CAFCF /* BGMAudioDeviceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMAudioDeviceManager.h; sourceTree = "<group>"; };
|
||||
1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMAudioDeviceManager.mm; sourceTree = "<group>"; };
|
||||
1CF69BA41BCFF59C009B5D1F /* BGMApp.profdata */ = {isa = PBXFileReference; lastKnownFileType = file; path = BGMApp.profdata; sourceTree = "<group>"; };
|
||||
271677B81C6CBDFA0080B0A2 /* CACFNumber.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CACFNumber.cpp; path = PublicUtility/CACFNumber.cpp; sourceTree = "<group>"; };
|
||||
271677B91C6CBDFA0080B0A2 /* CACFNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CACFNumber.h; path = PublicUtility/CACFNumber.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
1CB8B3331BBA75EF000E2DD1 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1CD1FD301BDDEAF2004F7E1B /* AudioToolbox.framework in Frameworks */,
|
||||
1C1963031BCAC160008A4DF7 /* CoreAudio.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
1CB8B3461BBA75F0000E2DD1 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
1C0BD0A21BF1A827004F4CF5 /* Preferences Menu */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1C0BD0A61BF1B029004F4CF5 /* BGMPreferencesMenu.h */,
|
||||
1C0BD0A71BF1B029004F4CF5 /* BGMPreferencesMenu.mm */,
|
||||
1C0BD0A31BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.h */,
|
||||
1C0BD0A41BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm */,
|
||||
1CE7064A1BF1EC0600BFC06D /* BGMOutputDevicePrefs.h */,
|
||||
1CE7064B1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm */,
|
||||
);
|
||||
name = "Preferences Menu";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1C1962E11BC94DDF008A4DF7 /* PublicUtility */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1C1962E81BC95301008A4DF7 /* CAAtomic.h */,
|
||||
1C1962E91BC95301008A4DF7 /* CAAutoDisposer.h */,
|
||||
1C1962EA1BC95301008A4DF7 /* CABitOperations.h */,
|
||||
1CC1DF7D1BE5068A00FB8FE4 /* CACFArray.cpp */,
|
||||
1CC1DF7E1BE5068A00FB8FE4 /* CACFArray.h */,
|
||||
1CC1DF7F1BE5068A00FB8FE4 /* CACFDictionary.cpp */,
|
||||
1CC1DF801BE5068A00FB8FE4 /* CACFDictionary.h */,
|
||||
271677B81C6CBDFA0080B0A2 /* CACFNumber.cpp */,
|
||||
271677B91C6CBDFA0080B0A2 /* CACFNumber.h */,
|
||||
1C1962FF1BCAC0F6008A4DF7 /* CACFString.cpp */,
|
||||
1C1963001BCAC0F6008A4DF7 /* CACFString.h */,
|
||||
1CC1DF8F1BE5891300FB8FE4 /* CADebugger.cpp */,
|
||||
1CC1DF901BE5891300FB8FE4 /* CADebugger.h */,
|
||||
1C1962F71BCAC061008A4DF7 /* CADebugMacros.cpp */,
|
||||
1C1962F81BCAC061008A4DF7 /* CADebugMacros.h */,
|
||||
1C1962FB1BCAC0C3008A4DF7 /* CADebugPrintf.cpp */,
|
||||
1C1962FC1BCAC0C3008A4DF7 /* CADebugPrintf.h */,
|
||||
1C1962FE1BCAC0DB008A4DF7 /* CAException.h */,
|
||||
1C1962EB1BCABFC5008A4DF7 /* CAHALAudioDevice.cpp */,
|
||||
1C1962EC1BCABFC5008A4DF7 /* CAHALAudioDevice.h */,
|
||||
1C1962ED1BCABFC5008A4DF7 /* CAHALAudioObject.cpp */,
|
||||
1C1962EE1BCABFC5008A4DF7 /* CAHALAudioObject.h */,
|
||||
1C1962EF1BCABFC5008A4DF7 /* CAHALAudioStream.cpp */,
|
||||
1C1962F01BCABFC5008A4DF7 /* CAHALAudioStream.h */,
|
||||
1C1962F11BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp */,
|
||||
1C1962F21BCABFC5008A4DF7 /* CAHALAudioSystemObject.h */,
|
||||
1C1963071BCAF677008A4DF7 /* CAHostTimeBase.cpp */,
|
||||
1C1963081BCAF677008A4DF7 /* CAHostTimeBase.h */,
|
||||
1C1963041BCAF468008A4DF7 /* CAMutex.cpp */,
|
||||
1C1963051BCAF468008A4DF7 /* CAMutex.h */,
|
||||
1C1962F91BCAC061008A4DF7 /* CAPropertyAddress.h */,
|
||||
1C8034C21BDAFD5700668E00 /* CAPThread.cpp */,
|
||||
1C8034C31BDAFD5700668E00 /* CAPThread.h */,
|
||||
1C1962E21BC94E15008A4DF7 /* CARingBuffer.cpp */,
|
||||
1C1962E31BC94E15008A4DF7 /* CARingBuffer.h */,
|
||||
);
|
||||
name = PublicUtility;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1C4699401BD5BA1700F78043 /* BGMCommon */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1C4699411BD5BA9E00F78043 /* BGM_Types.h */,
|
||||
);
|
||||
name = BGMCommon;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1C4699451BD5BF2E00F78043 /* Music Players */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1C2336DC1BEAB73F004C1C4E /* BGMMusicPlayer.h */,
|
||||
1C2336D91BEAB6E7004C1C4E /* BGMMusicPlayer.m */,
|
||||
1C2336DB1BEAB73F004C1C4E /* BGMiTunes.h */,
|
||||
1C4699461BD5C0E400F78043 /* BGMiTunes.m */,
|
||||
1C2336DD1BEAE10C004C1C4E /* BGMSpotify.h */,
|
||||
1C2336DE1BEAE10C004C1C4E /* BGMSpotify.m */,
|
||||
);
|
||||
name = "Music Players";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CB8B32D1BBA75EF000E2DD1 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CB8B3381BBA75EF000E2DD1 /* BGMApp */,
|
||||
1CB8B34C1BBA75F0000E2DD1 /* BGMAppTests */,
|
||||
1C4699401BD5BA1700F78043 /* BGMCommon */,
|
||||
1C1962E11BC94DDF008A4DF7 /* PublicUtility */,
|
||||
1CB8B3371BBA75EF000E2DD1 /* Products */,
|
||||
1CF69BA31BCFF59C009B5D1F /* OptimizationProfiles */,
|
||||
1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */,
|
||||
1C1963021BCAC160008A4DF7 /* CoreAudio.framework */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CB8B3371BBA75EF000E2DD1 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CB8B3361BBA75EF000E2DD1 /* Background Music.app */,
|
||||
1CB8B3491BBA75F0000E2DD1 /* BGMAppTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CB8B3381BBA75EF000E2DD1 /* BGMApp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CB8B33B1BBA75EF000E2DD1 /* AppDelegate.h */,
|
||||
1CB8B33C1BBA75EF000E2DD1 /* AppDelegate.mm */,
|
||||
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */,
|
||||
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */,
|
||||
1CED616A1C316E1A002CAFCF /* BGMAudioDeviceManager.h */,
|
||||
1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */,
|
||||
1C1465B91BCC49D1003AEFE6 /* BGMAutoPauseMusic.h */,
|
||||
1C1465B71BCC3A73003AEFE6 /* BGMAutoPauseMusic.mm */,
|
||||
1C4699451BD5BF2E00F78043 /* Music Players */,
|
||||
1C0BD0A21BF1A827004F4CF5 /* Preferences Menu */,
|
||||
1C46994D1BD7694C00F78043 /* BGMDeviceControlSync.h */,
|
||||
1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */,
|
||||
1C1962E61BC94E91008A4DF7 /* BGMPlayThrough.h */,
|
||||
1C1962E51BC94E91008A4DF7 /* BGMPlayThrough.cpp */,
|
||||
1CB8B3421BBA75EF000E2DD1 /* MainMenu.xib */,
|
||||
1CB8B3391BBA75EF000E2DD1 /* Supporting Files */,
|
||||
);
|
||||
path = BGMApp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CB8B3391BBA75EF000E2DD1 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CED61681C3081C2002CAFCF /* LICENSE */,
|
||||
1CC1DF951BE8607700FB8FE4 /* Images.xcassets */,
|
||||
1CB8B33A1BBA75EF000E2DD1 /* Info.plist */,
|
||||
1C46994B1BD5E70D00F78043 /* iTunes.h */,
|
||||
1C6F8E1D1BEB1406000D8108 /* Spotify.h */,
|
||||
1CB8B33E1BBA75EF000E2DD1 /* main.m */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CB8B34C1BBA75F0000E2DD1 /* BGMAppTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CB8B34F1BBA75F0000E2DD1 /* BGMAppTests.m */,
|
||||
1CB8B34D1BBA75F0000E2DD1 /* Supporting Files */,
|
||||
);
|
||||
path = BGMAppTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CB8B34D1BBA75F0000E2DD1 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CB8B34E1BBA75F0000E2DD1 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CF69BA31BCFF59C009B5D1F /* OptimizationProfiles */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CF69BA41BCFF59C009B5D1F /* BGMApp.profdata */,
|
||||
);
|
||||
path = OptimizationProfiles;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
1CB8B3351BBA75EF000E2DD1 /* Background Music */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1CB8B3531BBA75F0000E2DD1 /* Build configuration list for PBXNativeTarget "Background Music" */;
|
||||
buildPhases = (
|
||||
1CB8B3321BBA75EF000E2DD1 /* Sources */,
|
||||
1CB8B3331BBA75EF000E2DD1 /* Frameworks */,
|
||||
1CB8B3341BBA75EF000E2DD1 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Background Music";
|
||||
productName = BGMApp;
|
||||
productReference = 1CB8B3361BBA75EF000E2DD1 /* Background Music.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
1CB8B3481BBA75F0000E2DD1 /* BGMAppTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1CB8B3561BBA75F0000E2DD1 /* Build configuration list for PBXNativeTarget "BGMAppTests" */;
|
||||
buildPhases = (
|
||||
1CB8B3451BBA75F0000E2DD1 /* Sources */,
|
||||
1CB8B3461BBA75F0000E2DD1 /* Frameworks */,
|
||||
1CB8B3471BBA75F0000E2DD1 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
1CB8B34B1BBA75F0000E2DD1 /* PBXTargetDependency */,
|
||||
);
|
||||
name = BGMAppTests;
|
||||
productName = BGMAppTests;
|
||||
productReference = 1CB8B3491BBA75F0000E2DD1 /* BGMAppTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
1CB8B32E1BBA75EF000E2DD1 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
CLASSPREFIX = BGM;
|
||||
LastUpgradeCheck = 0700;
|
||||
ORGANIZATIONNAME = "Kyle Neideck";
|
||||
TargetAttributes = {
|
||||
1CB8B3351BBA75EF000E2DD1 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
};
|
||||
1CB8B3481BBA75F0000E2DD1 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
TestTargetID = 1CB8B3351BBA75EF000E2DD1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 1CB8B3311BBA75EF000E2DD1 /* Build configuration list for PBXProject "BGMApp" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 1CB8B32D1BBA75EF000E2DD1;
|
||||
productRefGroup = 1CB8B3371BBA75EF000E2DD1 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
1CB8B3351BBA75EF000E2DD1 /* Background Music */,
|
||||
1CB8B3481BBA75F0000E2DD1 /* BGMAppTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
1CB8B3341BBA75EF000E2DD1 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1CED61691C3081C2002CAFCF /* LICENSE in Resources */,
|
||||
1CC1DF961BE8607700FB8FE4 /* Images.xcassets in Resources */,
|
||||
1C1108AE1C444F01003F625E /* MainMenu.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
1CB8B3471BBA75F0000E2DD1 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
1CB8B3321BBA75EF000E2DD1 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */,
|
||||
1C1962E41BC94E15008A4DF7 /* CARingBuffer.cpp in Sources */,
|
||||
1CC1DF811BE5068A00FB8FE4 /* CACFArray.cpp in Sources */,
|
||||
1C0BD0A81BF1B029004F4CF5 /* BGMPreferencesMenu.mm in Sources */,
|
||||
1C1962F41BCABFC5008A4DF7 /* CAHALAudioObject.cpp 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 */,
|
||||
1C1962F51BCABFC5008A4DF7 /* CAHALAudioStream.cpp in Sources */,
|
||||
1C46994E1BD7694C00F78043 /* BGMDeviceControlSync.cpp in Sources */,
|
||||
1C1963091BCAF677008A4DF7 /* CAHostTimeBase.cpp in Sources */,
|
||||
1CB8B33F1BBA75EF000E2DD1 /* main.m in Sources */,
|
||||
1CB8B33D1BBA75EF000E2DD1 /* AppDelegate.mm in Sources */,
|
||||
271677BA1C6CBDFA0080B0A2 /* CACFNumber.cpp in Sources */,
|
||||
1C2336DF1BEAE10C004C1C4E /* BGMSpotify.m in Sources */,
|
||||
1CED616C1C316E1A002CAFCF /* BGMAudioDeviceManager.mm in Sources */,
|
||||
1C1962FD1BCAC0C3008A4DF7 /* CADebugPrintf.cpp in Sources */,
|
||||
1C1963011BCAC0F6008A4DF7 /* CACFString.cpp in Sources */,
|
||||
1C1962E71BC94E91008A4DF7 /* BGMPlayThrough.cpp in Sources */,
|
||||
1C1962FA1BCAC061008A4DF7 /* CADebugMacros.cpp in Sources */,
|
||||
1C1962F31BCABFC5008A4DF7 /* CAHALAudioDevice.cpp in Sources */,
|
||||
1CC1DF911BE5891300FB8FE4 /* CADebugger.cpp in Sources */,
|
||||
1C1465B81BCC3A73003AEFE6 /* BGMAutoPauseMusic.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
1CB8B3451BBA75F0000E2DD1 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1CB8B3501BBA75F0000E2DD1 /* BGMAppTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
1CB8B34B1BBA75F0000E2DD1 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 1CB8B3351BBA75EF000E2DD1 /* Background Music */;
|
||||
targetProxy = 1CB8B34A1BBA75F0000E2DD1 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
1CB8B3421BBA75EF000E2DD1 /* MainMenu.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
1CB8B3431BBA75EF000E2DD1 /* Base */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
1C4603C51C05963C00150F9B /* DebugOpt */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_ASSIGN_ENUM = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_NS_ASSERTIONS = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 3;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"CoreAudio_Debug=1",
|
||||
"CoreAudio_UseSysLog=0",
|
||||
"CoreAudio_StopOnAssert=1",
|
||||
"CoreAudio_ThreadStampMessages=1",
|
||||
);
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_UNROLL_LOOPS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_STRICT_SELECTOR_MATCH = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = DebugOpt;
|
||||
};
|
||||
1C4603C61C05963C00150F9B /* DebugOpt */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_USE_OPTIMIZATION_PROFILE = NO;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_UNROLL_LOOPS = YES;
|
||||
GCC_WARN_STRICT_SELECTOR_MATCH = YES;
|
||||
INFOPLIST_FILE = BGMApp/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
LLVM_LTO = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGMApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WARNING_CFLAGS = "";
|
||||
};
|
||||
name = DebugOpt;
|
||||
};
|
||||
1C4603C71C05963C00150F9B /* DebugOpt */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = BGMAppTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.bearisdriving.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Background Music.app/Contents/MacOS/Background Music";
|
||||
};
|
||||
name = DebugOpt;
|
||||
};
|
||||
1CB8B3511BBA75F0000E2DD1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_ASSIGN_ENUM = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"CoreAudio_Debug=1",
|
||||
"CoreAudio_UseSysLog=0",
|
||||
"CoreAudio_ThreadStampMessages=1",
|
||||
"CoreAudio_StopOnAssert=1",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_STRICT_SELECTOR_MATCH = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1CB8B3521BBA75F0000E2DD1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_ASSIGN_ENUM = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 3;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=0",
|
||||
"CoreAudio_Debug=0",
|
||||
"CoreAudio_UseSysLog=0",
|
||||
"CoreAudio_StopOnAssert=0",
|
||||
);
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_UNROLL_LOOPS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_STRICT_SELECTOR_MATCH = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1CB8B3541BBA75F0000E2DD1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_USE_OPTIMIZATION_PROFILE = NO;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_UNROLL_LOOPS = YES;
|
||||
GCC_WARN_STRICT_SELECTOR_MATCH = YES;
|
||||
INFOPLIST_FILE = BGMApp/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
LLVM_LTO = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGMApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WARNING_CFLAGS = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1CB8B3551BBA75F0000E2DD1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_USE_OPTIMIZATION_PROFILE = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_UNROLL_LOOPS = YES;
|
||||
GCC_WARN_STRICT_SELECTOR_MATCH = YES;
|
||||
INFOPLIST_FILE = BGMApp/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
LLVM_LTO = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGMApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WARNING_CFLAGS = "-Wno-profile-instr-out-of-date";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1CB8B3571BBA75F0000E2DD1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = BGMAppTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.bearisdriving.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Background Music.app/Contents/MacOS/Background Music";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1CB8B3581BBA75F0000E2DD1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = BGMAppTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.bearisdriving.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Background Music.app/Contents/MacOS/Background Music";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
1CB8B3311BBA75EF000E2DD1 /* Build configuration list for PBXProject "BGMApp" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1CB8B3511BBA75F0000E2DD1 /* Debug */,
|
||||
1CB8B3521BBA75F0000E2DD1 /* Release */,
|
||||
1C4603C51C05963C00150F9B /* DebugOpt */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1CB8B3531BBA75F0000E2DD1 /* Build configuration list for PBXNativeTarget "Background Music" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1CB8B3541BBA75F0000E2DD1 /* Debug */,
|
||||
1CB8B3551BBA75F0000E2DD1 /* Release */,
|
||||
1C4603C61C05963C00150F9B /* DebugOpt */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1CB8B3561BBA75F0000E2DD1 /* Build configuration list for PBXNativeTarget "BGMAppTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1CB8B3571BBA75F0000E2DD1 /* Debug */,
|
||||
1CB8B3581BBA75F0000E2DD1 /* Release */,
|
||||
1C4603C71C05963C00150F9B /* DebugOpt */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 1CB8B32E1BBA75EF000E2DD1 /* Project object */;
|
||||
}
|
37
BGMApp/BGMApp/AppDelegate.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// AppDelegate.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// Sets up and tears down the app.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||
|
||||
@property (weak) IBOutlet NSMenu* bgmMenu;
|
||||
@property (weak) IBOutlet NSMenuItem* autoPauseMenuItem;
|
||||
@property (weak) IBOutlet NSView* appVolumeView;
|
||||
@property (weak) IBOutlet NSPanel* aboutPanel;
|
||||
@property (unsafe_unretained) IBOutlet NSTextView *aboutPanelLicenseView;
|
||||
|
||||
@end
|
||||
|
144
BGMApp/BGMApp/AppDelegate.mm
Normal file
|
@ -0,0 +1,144 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// AppDelegate.mm
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Includes
|
||||
#import "AppDelegate.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
#import "BGMAudioDeviceManager.h"
|
||||
#import "BGMAutoPauseMusic.h"
|
||||
#import "BGMAppVolumes.h"
|
||||
#import "BGMPreferencesMenu.h"
|
||||
|
||||
|
||||
static float const kStatusBarIconPadding = 0.25;
|
||||
|
||||
@implementation AppDelegate {
|
||||
// The button in the system status bar (the bar with volume, battery, clock, etc.) to show the main menu
|
||||
// for the app. These are called "menu bar extras" in the Human Interface Guidelines.
|
||||
NSStatusItem* statusBarItem;
|
||||
|
||||
BGMAutoPauseMusic* autoPauseMusic;
|
||||
BGMAppVolumes* appVolumes;
|
||||
BGMAudioDeviceManager* audioDevices;
|
||||
BGMPreferencesMenu* prefsMenu;
|
||||
}
|
||||
|
||||
- (void) awakeFromNib {
|
||||
// Set up the status bar item
|
||||
statusBarItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
|
||||
|
||||
// Set the icon
|
||||
NSImage* icon = [NSImage imageNamed:@"FermataIcon"];
|
||||
if (icon != nil) {
|
||||
CGFloat lengthMinusPadding = [[statusBarItem button] frame].size.height * (1 - kStatusBarIconPadding);
|
||||
[icon setSize:NSMakeSize(lengthMinusPadding, lengthMinusPadding)];
|
||||
// Make the icon a "template image" so it gets drawn colour-inverted when it's highlighted or the status
|
||||
// bar's in dark mode
|
||||
[icon setTemplate:YES];
|
||||
statusBarItem.button.image = icon;
|
||||
} else {
|
||||
// If our icon is missing for some reason, fallback to a fermata character (1D110)
|
||||
statusBarItem.button.title = @"𝄐";
|
||||
}
|
||||
|
||||
// Set the main menu
|
||||
statusBarItem.menu = self.bgmMenu;
|
||||
}
|
||||
|
||||
- (void) applicationDidFinishLaunching:(NSNotification*)aNotification {
|
||||
#pragma unused (aNotification)
|
||||
|
||||
// Coordinates the audio devices (BGMDevice and the output device): manages playthrough, volume/mute controls, etc.
|
||||
NSError* err;
|
||||
audioDevices = [[BGMAudioDeviceManager alloc] initWithError:&err];
|
||||
if (audioDevices == nil) {
|
||||
[self showDeviceNotFoundErrorMessageAndExit:err.code];
|
||||
}
|
||||
[audioDevices setBGMDeviceAsOSDefault];
|
||||
|
||||
// Register the preference defaults. These are the preferences/state that only apply to BGMApp. The others are
|
||||
// persisted on BGMDriver.
|
||||
NSDictionary* appDefaults = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
|
||||
forKey:@"AutoPauseMusicEnabled"];
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
|
||||
|
||||
autoPauseMusic = [[BGMAutoPauseMusic alloc] initWithAudioDevices:audioDevices];
|
||||
|
||||
// Enable auto-pausing music if it's enabled in the user's preferences (which it is by default)
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AutoPauseMusicEnabled"]) {
|
||||
[self toggleAutoPauseMusic:self];
|
||||
}
|
||||
|
||||
appVolumes = [[BGMAppVolumes alloc] initWithMenu:[self bgmMenu]
|
||||
appVolumeView:[self appVolumeView]
|
||||
audioDevices:audioDevices];
|
||||
|
||||
prefsMenu = [[BGMPreferencesMenu alloc] initWithbgmMenu:[self bgmMenu]
|
||||
audioDevices:audioDevices
|
||||
aboutPanel:[self aboutPanel]
|
||||
aboutPanelLicenseView:[self aboutPanelLicenseView]];
|
||||
}
|
||||
|
||||
- (void) showDeviceNotFoundErrorMessageAndExit:(NSInteger)code {
|
||||
// Show an error dialog and exit if either BGMDevice wasn't found on the system or we couldn't find any output devices
|
||||
|
||||
NSAlert* alert = [NSAlert new];
|
||||
|
||||
if (code == kBGMErrorCode_BGMDeviceNotFound) {
|
||||
// TODO: Check whether the driver files are in /Library/Audio/Plug-Ins/HAL and offer to install them if not. Also,
|
||||
// it would be nice if we could restart coreaudiod automatically (using launchd).
|
||||
[alert setMessageText:@"Could not find the Background Music virtual audio device."];
|
||||
[alert setInformativeText:@"Make sure you've installed Background Music.driver to /Library/Audio/Plug-Ins/HAL and restarted coreaudiod (e.g. \"sudo killall coreaudiod\")."];
|
||||
} else if(code == kBGMErrorCode_OutputDeviceNotFound) {
|
||||
[alert setMessageText:@"Could not find an audio output device."];
|
||||
[alert setInformativeText:@"If you do have one installed, this is probably a bug. Sorry about that. Feel free to file an issue on GitHub."];
|
||||
}
|
||||
|
||||
[alert runModal];
|
||||
[NSApp terminate:self];
|
||||
}
|
||||
|
||||
- (void) applicationWillTerminate:(NSNotification*)aNotification {
|
||||
#pragma unused (aNotification)
|
||||
[audioDevices unsetBGMDeviceAsOSDefault];
|
||||
}
|
||||
|
||||
- (IBAction) toggleAutoPauseMusic:(id)sender {
|
||||
#pragma unused (sender)
|
||||
|
||||
if (self.autoPauseMenuItem.state == NSOnState) {
|
||||
self.autoPauseMenuItem.state = NSOffState;
|
||||
[autoPauseMusic disable];
|
||||
} else {
|
||||
self.autoPauseMenuItem.state = NSOnState;
|
||||
[autoPauseMusic enable];
|
||||
}
|
||||
|
||||
// Persist the change in the user's preferences
|
||||
[[NSUserDefaults standardUserDefaults] setBool:(self.autoPauseMenuItem.state == NSOnState)
|
||||
forKey:@"AutoPauseMusicEnabled"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
57
BGMApp/BGMApp/BGMAppVolumes.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMAppVolumes.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Local Includes
|
||||
#import "BGMAudioDeviceManager.h"
|
||||
|
||||
// System Includes
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface BGMAppVolumes : NSObject
|
||||
|
||||
- (id) initWithMenu:(NSMenu*)menu appVolumeView:(NSView*)view audioDevices:(BGMAudioDeviceManager*)audioDevices;
|
||||
|
||||
@end
|
||||
|
||||
// Protocol for the UI custom classes
|
||||
|
||||
@protocol BGMAppVolumeSubview <NSObject>
|
||||
|
||||
- (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx;
|
||||
|
||||
@end
|
||||
|
||||
// Custom classes for the UI elements in the app volume menu items
|
||||
|
||||
@interface BGMAVM_AppIcon : NSImageView <BGMAppVolumeSubview>
|
||||
@end
|
||||
|
||||
@interface BGMAVM_AppNameLabel : NSTextField <BGMAppVolumeSubview>
|
||||
@end
|
||||
|
||||
@interface BGMAVM_VolumeSlider : NSSlider <BGMAppVolumeSubview>
|
||||
|
||||
- (void) setRelativeVolume:(NSNumber*)relativeVolume;
|
||||
|
||||
@end
|
||||
|
298
BGMApp/BGMApp/BGMAppVolumes.mm
Normal file
|
@ -0,0 +1,298 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMAppVolumes.m
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#import "BGMAppVolumes.h"
|
||||
|
||||
// BGM Includes
|
||||
#include "BGM_Types.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CACFDictionary.h"
|
||||
#include "CACFArray.h"
|
||||
#include "CACFString.h"
|
||||
|
||||
|
||||
static NSInteger const kAppVolumesMenuItemTag = 3;
|
||||
static NSInteger const kSeparatorBelowAppVolumesMenuItemTag = 4;
|
||||
|
||||
static float const kSlidersSnapWithin = 5;
|
||||
|
||||
@implementation BGMAppVolumes {
|
||||
NSMenu* bgmMenu;
|
||||
NSView* appVolumeView;
|
||||
BGMAudioDeviceManager* audioDevices;
|
||||
}
|
||||
|
||||
- (id) initWithMenu:(NSMenu*)menu appVolumeView:(NSView*)view audioDevices:(BGMAudioDeviceManager*)devices {
|
||||
if ((self = [super init])) {
|
||||
bgmMenu = menu;
|
||||
appVolumeView = view;
|
||||
audioDevices = devices;
|
||||
|
||||
// Create the menu items for controlling app volumes
|
||||
[self insertMenuItemsForApps:[[NSWorkspace sharedWorkspace] runningApplications]];
|
||||
|
||||
// Register for notifications when the user opens or closes apps, so we can update the menu
|
||||
[[NSWorkspace sharedWorkspace] addObserver:self
|
||||
forKeyPath:@"runningApplications"
|
||||
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
|
||||
context:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
[[NSWorkspace sharedWorkspace] removeObserver:self forKeyPath:@"runningApplications" context:nil];
|
||||
}
|
||||
|
||||
- (void) insertMenuItemsForApps:(NSArray<NSRunningApplication*>*)apps {
|
||||
NSAssert([NSThread isMainThread], @"insertMenuItemsForApps is not thread safe");
|
||||
|
||||
#ifndef NS_BLOCK_ASSERTIONS // If assertions are enabled
|
||||
NSInteger numMenuItemsBeforeInsert =
|
||||
[bgmMenu indexOfItemWithTag:kSeparatorBelowAppVolumesMenuItemTag] - [bgmMenu indexOfItemWithTag:kAppVolumesMenuItemTag] - 1;
|
||||
NSUInteger numApps = 0;
|
||||
#endif
|
||||
|
||||
// Create a blank menu item to copy as a template
|
||||
NSMenuItem* blankItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
[blankItem setView:appVolumeView];
|
||||
|
||||
// Get the app volumes currently set on the device
|
||||
CACFArray appVolumesOnDevice((CFArrayRef)[audioDevices bgmDevice].GetPropertyData_CFType(kBGMAppVolumesAddress), false);
|
||||
|
||||
NSInteger index = [bgmMenu indexOfItemWithTag:kAppVolumesMenuItemTag] + 1;
|
||||
|
||||
// Add a volume-control menu item for each app
|
||||
for (NSRunningApplication* app in apps) {
|
||||
// Only show apps that appear in the dock (at first)
|
||||
// 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++;
|
||||
#endif
|
||||
|
||||
NSMenuItem* appVolItem = [blankItem copy];
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
// Store the NSRunningApplication object with the menu item so when the app closes we can find the item to remove it
|
||||
[appVolItem setRepresentedObject:app];
|
||||
|
||||
// Set the slider to the volume for this app if we got one from the driver
|
||||
[self setVolumeOfMenuItem:appVolItem fromAppVolumes:appVolumesOnDevice];
|
||||
|
||||
[bgmMenu insertItem:appVolItem atIndex:index];
|
||||
}
|
||||
|
||||
#ifndef NS_BLOCK_ASSERTIONS // If assertions are enabled
|
||||
NSInteger numMenuItemsAfterInsert =
|
||||
[bgmMenu indexOfItemWithTag:kSeparatorBelowAppVolumesMenuItemTag] - [bgmMenu indexOfItemWithTag:kAppVolumesMenuItemTag] - 1;
|
||||
NSAssert3(numMenuItemsAfterInsert == (numMenuItemsBeforeInsert + numApps),
|
||||
@"Did not add the expected number of menu items. numMenuItemsBeforeInsert=%ld numMenuItemsAfterInsert=%ld numAppsToAdd=%lu",
|
||||
(long)numMenuItemsBeforeInsert,
|
||||
(long)numMenuItemsAfterInsert,
|
||||
(unsigned long)numApps);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void) removeMenuItemsForApps:(NSArray<NSRunningApplication*>*)apps {
|
||||
NSAssert([NSThread isMainThread], @"removeMenuItemsForApps is not thread safe");
|
||||
|
||||
NSInteger firstItemIndex = [bgmMenu indexOfItemWithTag:kAppVolumesMenuItemTag] + 1;
|
||||
NSInteger lastItemIndex = [bgmMenu indexOfItemWithTag:kSeparatorBelowAppVolumesMenuItemTag] - 1;
|
||||
|
||||
// Check each app volume menu item, removing the items that control one of the given apps
|
||||
for (NSInteger i = firstItemIndex; i <= lastItemIndex; i++) {
|
||||
NSMenuItem* item = [bgmMenu itemAtIndex:i];
|
||||
|
||||
for (NSRunningApplication* appToBeRemoved in apps) {
|
||||
NSRunningApplication* itemApp = [item representedObject];
|
||||
|
||||
if ([itemApp isEqual:appToBeRemoved]) {
|
||||
[bgmMenu removeItem:item];
|
||||
// Correct i to account for the item we removed, since we're editing the menu in place
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (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
|
||||
NSRunningApplication* representedApp = [menuItem representedObject];
|
||||
|
||||
for (UInt32 i = 0; i < appVolumes.GetNumberItems(); i++) {
|
||||
CACFDictionary appVolume(false);
|
||||
appVolumes.GetCACFDictionary(i, appVolume);
|
||||
|
||||
// Match the app to the menu item by pid or bundle id
|
||||
CACFString bundleID;
|
||||
bundleID.DontAllowRelease();
|
||||
appVolume.GetCACFString(CFSTR(kBGMAppVolumesKey_BundleID), bundleID);
|
||||
|
||||
pid_t pid;
|
||||
appVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_ProcessID), pid);
|
||||
|
||||
if ([representedApp processIdentifier] == pid ||
|
||||
[[representedApp bundleIdentifier] isEqualToString:(__bridge NSString*)bundleID.GetCFString()]) {
|
||||
CFTypeRef relativeVolume;
|
||||
appVolume.GetCFType(CFSTR(kBGMAppVolumesKey_RelativeVolume), relativeVolume);
|
||||
|
||||
// Update the slider
|
||||
for (NSView* subview in [[menuItem view] subviews]) {
|
||||
if ([subview respondsToSelector:@selector(setRelativeVolume:)]) {
|
||||
[subview performSelector:@selector(setRelativeVolume:) withObject:(__bridge NSNumber*)relativeVolume];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
#pragma unused (object, context)
|
||||
|
||||
// KVO callback for the apps currently running on the system. Adds/removes the associated menu items.
|
||||
if ([keyPath isEqualToString:@"runningApplications"]) {
|
||||
NSArray<NSRunningApplication*>* newApps = [change objectForKey:NSKeyValueChangeNewKey];
|
||||
NSArray<NSRunningApplication*>* oldApps = [change objectForKey:NSKeyValueChangeOldKey];
|
||||
|
||||
int changeKind = [[change valueForKey:NSKeyValueChangeKindKey] intValue];
|
||||
switch (changeKind) {
|
||||
case NSKeyValueChangeInsertion:
|
||||
[self insertMenuItemsForApps:newApps];
|
||||
break;
|
||||
|
||||
case NSKeyValueChangeRemoval:
|
||||
[self removeMenuItemsForApps:oldApps];
|
||||
break;
|
||||
|
||||
case NSKeyValueChangeReplacement:
|
||||
[self removeMenuItemsForApps:oldApps];
|
||||
[self insertMenuItemsForApps:newApps];
|
||||
break;
|
||||
|
||||
case NSKeyValueChangeSetting:
|
||||
[bgmMenu removeAllItems];
|
||||
[self insertMenuItemsForApps:newApps];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) sendVolumeChangeToBGMDevice:(SInt32)newVolume appProcessID:(pid_t)appProcessID appBundleID:(NSString*)appBundleID {
|
||||
CACFDictionary appVolumeChange(true);
|
||||
appVolumeChange.AddSInt32(CFSTR(kBGMAppVolumesKey_ProcessID), appProcessID);
|
||||
appVolumeChange.AddString(CFSTR(kBGMAppVolumesKey_BundleID), (__bridge CFStringRef)appBundleID);
|
||||
// The values from our sliders are in [kAppRelativeVolumeMinRawValue, kAppRelativeVolumeMaxRawValue] already
|
||||
appVolumeChange.AddSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume), newVolume);
|
||||
|
||||
CACFArray appVolumeChanges(true);
|
||||
appVolumeChanges.AppendDictionary(appVolumeChange.GetDict());
|
||||
|
||||
[audioDevices bgmDevice].SetPropertyData_CFType(kBGMAppVolumesAddress, appVolumeChanges.AsPropertyList());
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// Custom classes for the UI elements in the app volume menu items
|
||||
|
||||
@implementation BGMAVM_AppIcon
|
||||
|
||||
- (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx {
|
||||
#pragma unused (ctx)
|
||||
|
||||
[self setImage:[app icon]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation BGMAVM_AppNameLabel
|
||||
|
||||
- (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx {
|
||||
#pragma unused (ctx)
|
||||
|
||||
[self setStringValue:[app localizedName]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation BGMAVM_VolumeSlider {
|
||||
// Will be set to -1 for apps without a pid
|
||||
pid_t appProcessID;
|
||||
NSString* appBundleID;
|
||||
BGMAppVolumes* context;
|
||||
}
|
||||
|
||||
- (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx {
|
||||
context = ctx;
|
||||
|
||||
[self setTarget:self];
|
||||
[self setAction:@selector(appVolumeChanged)];
|
||||
|
||||
appProcessID = [app processIdentifier];
|
||||
appBundleID = [app bundleIdentifier];
|
||||
|
||||
[self setMaxValue:kAppRelativeVolumeMaxRawValue];
|
||||
[self setMinValue:kAppRelativeVolumeMinRawValue];
|
||||
}
|
||||
|
||||
- (void) snap {
|
||||
// Snap to the 50% point
|
||||
float midPoint = static_cast<float>(([self maxValue] - [self minValue]) / 2);
|
||||
if ([self floatValue] > (midPoint - kSlidersSnapWithin) && [self floatValue] < (midPoint + kSlidersSnapWithin)) {
|
||||
[self setFloatValue:midPoint];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setRelativeVolume:(NSNumber*)relativeVolume {
|
||||
[self setIntValue:[relativeVolume intValue]];
|
||||
[self snap];
|
||||
}
|
||||
|
||||
- (void) appVolumeChanged {
|
||||
// TODO: This (sending updates to the driver) should probably be rate-limited. It uses a fair bit of CPU for me.
|
||||
|
||||
DebugMsg("BGMAppVolumes::appVolumeChanged: App volume for %s changed to %d", [appBundleID UTF8String], [self intValue]);
|
||||
|
||||
[self snap];
|
||||
|
||||
[context sendVolumeChangeToBGMDevice:[self intValue] appProcessID:appProcessID appBundleID:appBundleID];
|
||||
}
|
||||
|
||||
@end
|
||||
|
53
BGMApp/BGMApp/BGMAudioDeviceManager.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMAudioDeviceManager.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// Manages the BGMDevice and the output device. Sets the system's current default device as the output device on init, then
|
||||
// starts playthrough and mirroring the devices' controls. The output device can be changed but the BGMDevice is fixed.
|
||||
//
|
||||
|
||||
// System Includes
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <CoreAudio/AudioHardwareBase.h>
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAHALAudioDevice.h"
|
||||
|
||||
|
||||
extern int const kBGMErrorCode_BGMDeviceNotFound;
|
||||
extern int const kBGMErrorCode_OutputDeviceNotFound;
|
||||
|
||||
@interface BGMAudioDeviceManager : NSObject
|
||||
|
||||
- (id) initWithError:(NSError**)error;
|
||||
|
||||
// Set BGMDevice as the default audio device for all processes
|
||||
- (void) setBGMDeviceAsOSDefault;
|
||||
// Replace BGMDevice as the default device with the output device
|
||||
- (void) unsetBGMDeviceAsOSDefault;
|
||||
|
||||
- (CAHALAudioDevice) bgmDevice;
|
||||
|
||||
- (BOOL) isOutputDevice:(AudioObjectID)deviceID;
|
||||
// Returns NO if the output device couldn't be changed and has been reverted
|
||||
- (BOOL) setOutputDeviceWithID:(AudioObjectID)deviceID revertOnFailure:(BOOL)revertOnFailure;
|
||||
|
||||
@end
|
||||
|
215
BGMApp/BGMApp/BGMAudioDeviceManager.mm
Normal file
|
@ -0,0 +1,215 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMAudioDeviceManager.mm
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#import "BGMAudioDeviceManager.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
#include "BGMDeviceControlSync.h"
|
||||
#include "BGMPlayThrough.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAHALAudioSystemObject.h"
|
||||
#include "CAAutoDisposer.h"
|
||||
|
||||
// System Includes
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
int const kBGMErrorCode_BGMDeviceNotFound = 0;
|
||||
int const kBGMErrorCode_OutputDeviceNotFound = 1;
|
||||
|
||||
// Hack/workaround that adds a default constructor to CAHALAudioDevice so we don't have to use pointers for the instance variables
|
||||
class BGMAudioDevice : public CAHALAudioDevice {
|
||||
using CAHALAudioDevice::CAHALAudioDevice;
|
||||
public:
|
||||
BGMAudioDevice() : CAHALAudioDevice(kAudioDeviceUnknown) { }
|
||||
};
|
||||
|
||||
@implementation BGMAudioDeviceManager {
|
||||
BGMAudioDevice bgmDevice;
|
||||
BGMAudioDevice outputDevice;
|
||||
BGMDeviceControlSync deviceControlSync;
|
||||
BGMPlayThrough playThrough;
|
||||
}
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
- (id) initWithError:(NSError**)error {
|
||||
if ((self = [super init])) {
|
||||
bgmDevice = BGMAudioDevice(CFSTR(kBGMDeviceUID));
|
||||
|
||||
if (bgmDevice.GetObjectID() == kAudioObjectUnknown) {
|
||||
DebugMsg("BGMAudioDeviceManager::initWithError: BGMDevice not found");
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:@"com.bearisdriving.BGMApp" code:kBGMErrorCode_BGMDeviceNotFound userInfo:nil];
|
||||
}
|
||||
self = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
[self initOutputDevice];
|
||||
|
||||
|
||||
if (outputDevice.GetObjectID() == kAudioDeviceUnknown) {
|
||||
DebugMsg("BGMAudioDeviceManager::initWithError: output device not found");
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:@"com.bearisdriving.BGMApp" code:kBGMErrorCode_OutputDeviceNotFound userInfo:nil];
|
||||
}
|
||||
self = nil;
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) initOutputDevice {
|
||||
CAHALAudioSystemObject audioSystem;
|
||||
// outputDevice = BGMAudioDevice(CFSTR("AppleHDAEngineOutput:1B,0,1,1:0"));
|
||||
AudioObjectID defaultDeviceID = audioSystem.GetDefaultAudioDevice(false, false);
|
||||
if (defaultDeviceID == bgmDevice.GetObjectID()) {
|
||||
// TODO: If BGMDevice is already the default (because BGMApp didn't shutdown properly or it was set manually)
|
||||
// we should temporarily disable BGMDevice so we can find out what the previous default was.
|
||||
|
||||
// For now, just pick the device with the lowest latency
|
||||
UInt32 numDevices = audioSystem.GetNumberAudioDevices();
|
||||
if (numDevices > 0) {
|
||||
SInt32 minLatencyDeviceIdx = -1;
|
||||
UInt32 minLatency = UINT32_MAX;
|
||||
CAAutoArrayDelete<AudioObjectID> devices(numDevices);
|
||||
audioSystem.GetAudioDevices(numDevices, devices);
|
||||
|
||||
for (UInt32 i = 0; i < numDevices; i++) {
|
||||
BGMAudioDevice device(devices[i]);
|
||||
|
||||
BOOL isBGMDevice = device.GetObjectID() == bgmDevice.GetObjectID();
|
||||
BOOL hasOutputChannels = device.GetTotalNumberChannels(/* inIsInput = */ false) > 0;
|
||||
|
||||
if (!isBGMDevice && hasOutputChannels) {
|
||||
if (minLatencyDeviceIdx == -1) {
|
||||
// First, look for any device other than BGMDevice
|
||||
minLatencyDeviceIdx = i;
|
||||
} else if (device.GetLatency(false) < minLatency) {
|
||||
// Then compare the devices by their latencies
|
||||
minLatencyDeviceIdx = i;
|
||||
minLatency = device.GetLatency(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[self setOutputDeviceWithID:devices[minLatencyDeviceIdx] revertOnFailure:NO];
|
||||
}
|
||||
} else {
|
||||
[self setOutputDeviceWithID:defaultDeviceID revertOnFailure:NO];
|
||||
}
|
||||
|
||||
assert(outputDevice.GetObjectID() != bgmDevice.GetObjectID());
|
||||
|
||||
// Log message
|
||||
if (outputDevice.GetObjectID() == kAudioDeviceUnknown) {
|
||||
CFStringRef outputDeviceUID = outputDevice.CopyDeviceUID();
|
||||
DebugMsg("BGMAudioDeviceManager::initDevices: Set output device to %s",
|
||||
CFStringGetCStringPtr(outputDeviceUID, kCFStringEncodingUTF8));
|
||||
CFRelease(outputDeviceUID);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Systemwide Default Device
|
||||
|
||||
- (void) setBGMDeviceAsOSDefault {
|
||||
CAHALAudioSystemObject audioSystem;
|
||||
|
||||
if (audioSystem.GetDefaultAudioDevice(false, true) == outputDevice.GetObjectID()) {
|
||||
// The default system device was the same as the default device, so change that as well
|
||||
audioSystem.SetDefaultAudioDevice(false, true, bgmDevice.GetObjectID());
|
||||
}
|
||||
|
||||
audioSystem.SetDefaultAudioDevice(false, false, bgmDevice.GetObjectID());
|
||||
}
|
||||
|
||||
- (void) unsetBGMDeviceAsOSDefault {
|
||||
CAHALAudioSystemObject audioSystem;
|
||||
|
||||
if (audioSystem.GetDefaultAudioDevice(false, true) == bgmDevice.GetObjectID()) {
|
||||
// We changed the system output device to BGMDevice, which we only do if it initially matches the
|
||||
// default output device, so change it back
|
||||
audioSystem.SetDefaultAudioDevice(false, true, outputDevice.GetObjectID());
|
||||
}
|
||||
|
||||
if (audioSystem.GetDefaultAudioDevice(false, false) == bgmDevice.GetObjectID()) {
|
||||
audioSystem.SetDefaultAudioDevice(false, false, outputDevice.GetObjectID());
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Accessors
|
||||
|
||||
- (CAHALAudioDevice) bgmDevice {
|
||||
return bgmDevice;
|
||||
}
|
||||
|
||||
- (BOOL) isOutputDevice:(AudioObjectID)deviceID {
|
||||
return deviceID == outputDevice.GetObjectID();
|
||||
}
|
||||
|
||||
- (BOOL) setOutputDeviceWithID:(AudioObjectID)deviceID revertOnFailure:(BOOL)revertOnFailure {
|
||||
DebugMsg("BGMAudioDeviceManager::setOutputDeviceWithID: Setting output device. deviceID=%u", deviceID);
|
||||
|
||||
// Set up playthrough and control sync
|
||||
BGMAudioDevice newOutputDevice(deviceID);
|
||||
try {
|
||||
// Mirror changes in BGMDevice's controls to the new output device's
|
||||
deviceControlSync = BGMDeviceControlSync(bgmDevice, newOutputDevice);
|
||||
|
||||
// Stream audio from BGMDevice to the output device
|
||||
//
|
||||
// TODO: Should this be done async? Some output devices take a long time to start IO (e.g. AirPlay) and I
|
||||
// assume this blocks the main thread. Haven't tried it to check, though.
|
||||
playThrough = BGMPlayThrough(bgmDevice, newOutputDevice);
|
||||
|
||||
// Start playthrough because audio might be playing
|
||||
playThrough.Start();
|
||||
} catch (CAException e) {
|
||||
// Using LogWarning from PublicUtility instead of NSLog here crashes from a bad access. Not sure why.
|
||||
NSLog(@"BGMAudioDeviceManager::setOutputDeviceWithID: Couldn't set device with ID %u as output device. %s %s%d.",
|
||||
newOutputDevice.GetObjectID(),
|
||||
(revertOnFailure ? "Will attempt to revert to the previous device." : ""),
|
||||
"Error: ", e.GetError());
|
||||
|
||||
if (revertOnFailure) {
|
||||
// Try to reactivate the original device listener and playthrough
|
||||
[self setOutputDeviceWithID:outputDevice.GetObjectID() revertOnFailure:NO];
|
||||
return NO;
|
||||
} else {
|
||||
// TODO: Handle in callers. (Maybe show an error dialog and try to set the original default device as the output device.)
|
||||
Throw(e);
|
||||
}
|
||||
}
|
||||
|
||||
outputDevice = BGMAudioDevice(deviceID);
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
40
BGMApp/BGMApp/BGMAutoPauseMusic.h
Normal file
|
@ -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/>.
|
||||
|
||||
//
|
||||
// BGMAutoPauseMusic.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// When enabled, BGMAutoPauseMusic listens for notifications from BGMDevice to tell when music is playing and
|
||||
// pauses the music player if other audio starts.
|
||||
//
|
||||
|
||||
// Local Includes
|
||||
#import "BGMAudioDeviceManager.h"
|
||||
|
||||
// System Includes
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@interface BGMAutoPauseMusic : NSObject
|
||||
|
||||
- (id) initWithAudioDevices:(BGMAudioDeviceManager*)inAudioDevices;
|
||||
- (void) enable;
|
||||
- (void) disable;
|
||||
|
||||
@end
|
||||
|
187
BGMApp/BGMApp/BGMAutoPauseMusic.mm
Normal file
|
@ -0,0 +1,187 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMAutoPauseMusic.m
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#import "BGMAutoPauseMusic.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
#import "BGMMusicPlayer.h"
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
|
||||
// How long to wait before pausing/unpausing. This is so short sounds can play without awkwardly causing a short period of silence,
|
||||
// and other audio can have short periods of silence without causing music to play and quickly pause again. Of course, it's a
|
||||
// trade-off against how long the music will overlap the other audio before it gets paused and how long the music will stay paused
|
||||
// after a sound that was only slightly longer than the pause delay.
|
||||
//
|
||||
// TODO: Make these settable in advanced settings?
|
||||
static int const kPauseDelayMSecs = 1500;
|
||||
static int const kUnpauseDelayMSecs = 3000;
|
||||
|
||||
@implementation BGMAutoPauseMusic {
|
||||
BOOL enabled;
|
||||
BGMAudioDeviceManager* audioDevices;
|
||||
dispatch_queue_t listenerQueue;
|
||||
// Have to keep track of the listener block we add so we can remove it later
|
||||
AudioObjectPropertyListenerBlock listenerBlock;
|
||||
// True if BGMApp has paused musicPlayer and hasn't unpaused it yet. (Will be out of sync with the music player app if the user
|
||||
// has unpaused it themselves.)
|
||||
BOOL wePaused;
|
||||
// The times, in absolute time, that the BGMDevice last changed its audible state to silent...
|
||||
UInt64 wentSilent;
|
||||
// ...and to audible
|
||||
UInt64 wentAudible;
|
||||
dispatch_queue_t pauseUnpauseMusicQueue;
|
||||
}
|
||||
|
||||
- (id) initWithAudioDevices:(BGMAudioDeviceManager*)inAudioDevices {
|
||||
if ((self = [super init])) {
|
||||
enabled = NO;
|
||||
audioDevices = inAudioDevices;
|
||||
wePaused = NO;
|
||||
|
||||
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0);
|
||||
listenerQueue = dispatch_queue_create("com.bearisdriving.BGMAutoPauseMusic.Listener", attr);
|
||||
pauseUnpauseMusicQueue = dispatch_queue_create("com.bearisdriving.BGMAutoPauseMusic.PauseUnpauseMusic", attr);
|
||||
|
||||
[self initListenerBlock];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
[self disable];
|
||||
}
|
||||
|
||||
- (void) initListenerBlock {
|
||||
// To avoid retain cycle
|
||||
__unsafe_unretained BGMAutoPauseMusic* weakSelf = self;
|
||||
|
||||
listenerBlock = ^(UInt32 inNumberAddresses, const AudioObjectPropertyAddress * _Nonnull inAddresses) {
|
||||
// inAddresses "may contain addresses for properties for which the listener is not signed up to receive notifications",
|
||||
// so we have to check them all
|
||||
for (int i = 0; i < inNumberAddresses; i++) {
|
||||
if (inAddresses[i].mSelector == kAudioDeviceCustomPropertyDeviceAudibleState) {
|
||||
SInt32 audibleState = [weakSelf deviceAudibleState];
|
||||
|
||||
#if DEBUG
|
||||
const char audibleStateStr[5] = CA4CCToCString(audibleState);
|
||||
DebugMsg("BGMAutoPauseMusic::initListenerBlock: kAudioDeviceCustomPropertyDeviceAudibleState property changed to '%s'",
|
||||
audibleStateStr);
|
||||
#endif
|
||||
|
||||
if (audibleState == kBGMDeviceIsAudible) {
|
||||
[weakSelf queuePauseBlock];
|
||||
} else if (audibleState == kBGMDeviceIsSilent) {
|
||||
[weakSelf queueUnpauseBlock];
|
||||
} else if (audibleState == kBGMDeviceIsSilentExceptMusic) {
|
||||
// If we pause the music player and then the user unpauses it before the other audio stops, we need to set
|
||||
// wePaused to false at some point before the other audio starts again so we know we should pause
|
||||
wePaused = NO;
|
||||
}
|
||||
// TODO: Add a fourth audible state, something like "AudibleAndMusicPlaying", and check it here to
|
||||
// handle the user unpausing and then repausing music while also playing other audio?
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
- (SInt32) deviceAudibleState {
|
||||
SInt32 audibleState;
|
||||
CFNumberRef audibleStateRef = static_cast<CFNumberRef>([audioDevices bgmDevice].GetPropertyData_CFType(kBGMAudibleStateAddress));
|
||||
CFNumberGetValue(audibleStateRef, kCFNumberSInt32Type, &audibleState);
|
||||
CFRelease(audibleStateRef);
|
||||
return audibleState;
|
||||
}
|
||||
|
||||
- (void) queuePauseBlock {
|
||||
UInt64 now = mach_absolute_time();
|
||||
wentAudible = now;
|
||||
UInt64 startedPauseDelay = now;
|
||||
|
||||
DebugMsg("BGMAutoPauseMusic::queuePauseBlock: Dispatching pause block at %llu", now);
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kPauseDelayMSecs * NSEC_PER_MSEC),
|
||||
pauseUnpauseMusicQueue,
|
||||
^{
|
||||
BOOL stillAudible = [self deviceAudibleState] == kBGMDeviceIsAudible;
|
||||
|
||||
DebugMsg("BGMAutoPauseMusic::queuePauseBlock: Running pause block dispatched at %llu.%s wentAudible=%llu",
|
||||
startedPauseDelay,
|
||||
stillAudible ? "" : " Not pausing because the device isn't audible.",
|
||||
wentAudible);
|
||||
|
||||
// Pause if this is the most recent pause block and the device is still audible, which means the audible
|
||||
// state hasn't changed since this block was queued. Also set wePaused to true if the player wasn't
|
||||
// already paused.
|
||||
if (!wePaused && startedPauseDelay == wentAudible && stillAudible) {
|
||||
wePaused = [[BGMMusicPlayer selectedMusicPlayer] pause] || wePaused;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void) queueUnpauseBlock {
|
||||
UInt64 now = mach_absolute_time();
|
||||
wentSilent = now;
|
||||
UInt64 startedUnpauseDelay = now;
|
||||
|
||||
DebugMsg("BGMAutoPauseMusic::queueUnpauseBlock: Dispatched unpause block at %llu", now);
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kUnpauseDelayMSecs * NSEC_PER_MSEC),
|
||||
pauseUnpauseMusicQueue,
|
||||
^{
|
||||
BOOL stillSilent = [self deviceAudibleState] == kBGMDeviceIsSilent;
|
||||
|
||||
DebugMsg("BGMAutoPauseMusic::queueUnpauseBlock: Running unpause block dispatched at %llu.%s%s wentSilent=%llu",
|
||||
startedUnpauseDelay,
|
||||
wePaused ? "" : " Not unpausing because we weren't the one who paused.",
|
||||
stillSilent ? "" : " Not unpausing because the device isn't silent.",
|
||||
wentSilent);
|
||||
|
||||
// Unpause if we were the one who paused. Also check that this is the most recent unpause block and the
|
||||
// device is still silent, which means the audible state hasn't changed since this block was queued.
|
||||
if (wePaused && startedUnpauseDelay == wentSilent && stillSilent) {
|
||||
wePaused = NO;
|
||||
[[BGMMusicPlayer selectedMusicPlayer] unpause];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void) enable {
|
||||
if (!enabled) {
|
||||
[audioDevices bgmDevice].AddPropertyListenerBlock(kBGMAudibleStateAddress, listenerQueue, listenerBlock);
|
||||
enabled = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) disable {
|
||||
if (enabled) {
|
||||
[audioDevices bgmDevice].RemovePropertyListenerBlock(kBGMAudibleStateAddress, listenerQueue, listenerBlock);
|
||||
enabled = NO;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
355
BGMApp/BGMApp/BGMDeviceControlSync.cpp
Normal file
|
@ -0,0 +1,355 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMDeviceControlSync.cpp
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "BGMDeviceControlSync.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
|
||||
// System Includes
|
||||
#include <AudioToolbox/AudioServices.h>
|
||||
|
||||
|
||||
// AudioObjectPropertyElement docs: "Elements are numbered sequentially where 0 represents the master element."
|
||||
static const AudioObjectPropertyElement kMasterChannel = 0;
|
||||
|
||||
static const AudioObjectPropertyAddress kMutePropertyAddress =
|
||||
{ kAudioDevicePropertyMute, kAudioObjectPropertyScopeOutput, kAudioObjectPropertyElementMaster };
|
||||
|
||||
static const AudioObjectPropertyAddress kVolumePropertyAddress =
|
||||
{ kAudioDevicePropertyVolumeScalar, kAudioObjectPropertyScopeOutput, kAudioObjectPropertyElementMaster };
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
BGMDeviceControlSync::BGMDeviceControlSync(CAHALAudioDevice inBGMDevice, CAHALAudioDevice inOutputDevice)
|
||||
:
|
||||
mBGMDevice(inBGMDevice),
|
||||
mOutputDevice(inOutputDevice)
|
||||
{
|
||||
Activate();
|
||||
}
|
||||
|
||||
BGMDeviceControlSync::~BGMDeviceControlSync()
|
||||
{
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
void BGMDeviceControlSync::Activate()
|
||||
{
|
||||
ThrowIf((mBGMDevice.GetObjectID() == kAudioDeviceUnknown || mOutputDevice.GetObjectID() == kAudioDeviceUnknown),
|
||||
BGM_DeviceNotSetException(),
|
||||
"BGMDeviceControlSync::Activate: Both the output device and BGMDevice must be set to start synchronizing their controls");
|
||||
|
||||
if(!mActive)
|
||||
{
|
||||
// Register listeners for volume and mute values
|
||||
mBGMDevice.AddPropertyListener(kVolumePropertyAddress, &BGMDeviceControlSync::BGMDeviceListenerProc, this);
|
||||
|
||||
try
|
||||
{
|
||||
mBGMDevice.AddPropertyListener(kMutePropertyAddress, &BGMDeviceControlSync::BGMDeviceListenerProc, this);
|
||||
}
|
||||
catch(CAException)
|
||||
{
|
||||
mBGMDevice.RemovePropertyListener(kVolumePropertyAddress, &BGMDeviceControlSync::BGMDeviceListenerProc, this);
|
||||
throw;
|
||||
}
|
||||
|
||||
// Init BGMDevice controls to match output device
|
||||
CopyVolume(mOutputDevice, mBGMDevice, kAudioObjectPropertyScopeOutput);
|
||||
CopyMute(mOutputDevice, mBGMDevice, kAudioObjectPropertyScopeOutput);
|
||||
|
||||
mActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
void BGMDeviceControlSync::Deactivate()
|
||||
{
|
||||
if(mActive && mBGMDevice.GetObjectID() != kAudioDeviceUnknown)
|
||||
{
|
||||
// Unregister listeners
|
||||
mBGMDevice.RemovePropertyListener(kVolumePropertyAddress, &BGMDeviceControlSync::BGMDeviceListenerProc, this);
|
||||
mBGMDevice.RemovePropertyListener(kMutePropertyAddress, &BGMDeviceControlSync::BGMDeviceListenerProc, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BGMDeviceControlSync::Swap(BGMDeviceControlSync& inDeviceControlSync)
|
||||
{
|
||||
mBGMDevice = inDeviceControlSync.mBGMDevice;
|
||||
mOutputDevice = inDeviceControlSync.mOutputDevice;
|
||||
inDeviceControlSync.Deactivate();
|
||||
Activate();
|
||||
}
|
||||
|
||||
#pragma mark Get/Set Control Values
|
||||
|
||||
// static
|
||||
void BGMDeviceControlSync::CopyMute(CAHALAudioDevice inFromDevice, CAHALAudioDevice inToDevice, AudioObjectPropertyScope inScope)
|
||||
{
|
||||
// TODO: Support for devices that have per-channel mute controls but no master mute control
|
||||
bool toHasSettableMasterMute = inToDevice.HasMuteControl(inScope, kMasterChannel) && inToDevice.MuteControlIsSettable(inScope, kMasterChannel);
|
||||
if(toHasSettableMasterMute && inFromDevice.HasMuteControl(inScope, kMasterChannel))
|
||||
{
|
||||
inToDevice.SetMuteControlValue(inScope,
|
||||
kMasterChannel,
|
||||
inFromDevice.GetMuteControlValue(inScope, kMasterChannel));
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void BGMDeviceControlSync::CopyVolume(CAHALAudioDevice inFromDevice, CAHALAudioDevice inToDevice, AudioObjectPropertyScope inScope)
|
||||
{
|
||||
// Get the volume of the from device
|
||||
bool didGetFromVolume = false;
|
||||
Float32 fromVolume = FLT_MIN;
|
||||
|
||||
if(inFromDevice.HasVolumeControl(inScope, kMasterChannel))
|
||||
{
|
||||
fromVolume = inFromDevice.GetVolumeControlScalarValue(inScope, kMasterChannel);
|
||||
didGetFromVolume = true;
|
||||
}
|
||||
|
||||
// Use the average channel volume of the from device if it has no master volume
|
||||
if(!didGetFromVolume)
|
||||
{
|
||||
UInt32 fromNumChannels = inFromDevice.GetTotalNumberChannels(inScope == kAudioObjectPropertyScopeInput);
|
||||
fromVolume = 0;
|
||||
|
||||
for(UInt32 channel = 1; channel <= fromNumChannels; channel++)
|
||||
{
|
||||
if(inFromDevice.HasVolumeControl(inScope, channel))
|
||||
{
|
||||
fromVolume += inFromDevice.GetVolumeControlScalarValue(inScope, channel);
|
||||
didGetFromVolume = true;
|
||||
}
|
||||
}
|
||||
|
||||
fromVolume /= fromNumChannels;
|
||||
}
|
||||
|
||||
// Set the volume of the to device
|
||||
if(didGetFromVolume && fromVolume != FLT_MIN)
|
||||
{
|
||||
bool didSetVolume = false;
|
||||
|
||||
try
|
||||
{
|
||||
didSetVolume = SetMasterVolumeScalar(inToDevice, inScope, fromVolume);
|
||||
}
|
||||
catch(CAException e)
|
||||
{
|
||||
OSStatus err = e.GetError();
|
||||
char err4CC[5] = CA4CCToCString(err);
|
||||
CFStringRef uid = inToDevice.CopyDeviceUID();
|
||||
LogWarning("BGMDeviceControlSync::CopyVolume: CAException '%s' trying to set master volume of %s", err4CC, uid);
|
||||
CFRelease(uid);
|
||||
}
|
||||
|
||||
if(!didSetVolume)
|
||||
{
|
||||
// Couldn't find a master volume control to set, so try to find a virtual one
|
||||
Float32 fromVirtualMasterVolume;
|
||||
bool success = GetVirtualMasterVolume(inFromDevice, inScope, fromVirtualMasterVolume);
|
||||
if(success)
|
||||
{
|
||||
didSetVolume = SetVirtualMasterVolume(inToDevice, inScope, fromVirtualMasterVolume);
|
||||
}
|
||||
}
|
||||
|
||||
if(!didSetVolume)
|
||||
{
|
||||
// Couldn't set a master or virtual master volume, so as a fallback try to set each channel individually
|
||||
UInt32 numChannels = inToDevice.GetTotalNumberChannels(inScope == kAudioObjectPropertyScopeInput);
|
||||
for(UInt32 channel = 1; channel <= numChannels; channel++)
|
||||
{
|
||||
if(inToDevice.HasVolumeControl(inScope, channel) && inToDevice.VolumeControlIsSettable(inScope, channel))
|
||||
{
|
||||
inToDevice.SetVolumeControlScalarValue(inScope, channel, fromVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool BGMDeviceControlSync::SetMasterVolumeScalar(CAHALAudioDevice inDevice, AudioObjectPropertyScope inScope, Float32 inVolume)
|
||||
{
|
||||
bool hasSettableMasterVolume =
|
||||
inDevice.HasVolumeControl(inScope, kMasterChannel) && inDevice.VolumeControlIsSettable(inScope, kMasterChannel);
|
||||
|
||||
if(hasSettableMasterVolume)
|
||||
{
|
||||
inDevice.SetVolumeControlScalarValue(inScope, kMasterChannel, inVolume);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool BGMDeviceControlSync::GetVirtualMasterVolume(CAHALAudioDevice inDevice, AudioObjectPropertyScope inScope, Float32& outVirtualMasterVolume)
|
||||
{
|
||||
AudioObjectPropertyAddress virtualMasterVolumeAddress =
|
||||
{ kAudioHardwareServiceDeviceProperty_VirtualMasterVolume, inScope, kAudioObjectPropertyElementMaster };
|
||||
|
||||
if(!AudioHardwareServiceHasProperty(inDevice.GetObjectID(), &virtualMasterVolumeAddress))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UInt32 virtualMasterVolumePropertySize = sizeof(Float32);
|
||||
return kAudioServicesNoError == AHSGetPropertyData(inDevice.GetObjectID(),
|
||||
&virtualMasterVolumeAddress,
|
||||
&virtualMasterVolumePropertySize,
|
||||
&outVirtualMasterVolume);
|
||||
}
|
||||
|
||||
// static
|
||||
bool BGMDeviceControlSync::SetVirtualMasterVolume(CAHALAudioDevice inDevice, AudioObjectPropertyScope inScope, Float32 inVolume)
|
||||
{
|
||||
// TODO: For me, setting the virtual master volume sets all the device's channels to the same volume, meaning you can't
|
||||
// keep any channels quieter than the others. The expected behaviour is to scale the channel volumes
|
||||
// proportionally. So to do this properly I think we'd have to store BGMDevice's previous volume and calculate
|
||||
// each channel's new volume from its current volume and the distance between BGMDevice's old and new volumes.
|
||||
//
|
||||
// The docs kAudioHardwareServiceDeviceProperty_VirtualMasterVolume for say
|
||||
// "If the device has individual channel volume controls, this property will apply to those identified by the
|
||||
// device's preferred multi-channel layout (or preferred stereo pair if the device is stereo only). Note that
|
||||
// this control maintains the relative balance between all the channels it affects.
|
||||
// so I'm not sure why that's not working here. As a workaround we take the to device's (virtual master) balance
|
||||
// before changing the volume and set it back after, but of course that'll only work for stereo devices.
|
||||
|
||||
bool didSetVolume = false;
|
||||
AudioObjectPropertyAddress virtualMasterVolumeAddress =
|
||||
{ kAudioHardwareServiceDeviceProperty_VirtualMasterVolume, inScope, kAudioObjectPropertyElementMaster };
|
||||
bool hasVirtualMasterVolume = AudioHardwareServiceHasProperty(inDevice.GetObjectID(), &virtualMasterVolumeAddress);
|
||||
|
||||
Boolean virtualMasterVolumeIsSettable;
|
||||
OSStatus err = AudioHardwareServiceIsPropertySettable(inDevice.GetObjectID(), &virtualMasterVolumeAddress, &virtualMasterVolumeIsSettable);
|
||||
virtualMasterVolumeIsSettable &= (err == kAudioServicesNoError);
|
||||
|
||||
if(hasVirtualMasterVolume && virtualMasterVolumeIsSettable)
|
||||
{
|
||||
// Not sure why, but setting the virtual master volume sets all channels to the same volume. As a workaround, we store
|
||||
// the current balance here so we can reset it after setting the volume.
|
||||
Float32 virtualMasterBalance;
|
||||
bool didGetVirtualMasterBalance = GetVirtualMasterBalance(inDevice, inScope, virtualMasterBalance);
|
||||
|
||||
didSetVolume = kAudioServicesNoError == AHSSetPropertyData(inDevice.GetObjectID(), &virtualMasterVolumeAddress, sizeof(Float32), &inVolume);
|
||||
|
||||
// Reset the balance
|
||||
AudioObjectPropertyAddress virtualMasterBalanceAddress =
|
||||
{ kAudioHardwareServiceDeviceProperty_VirtualMasterBalance, inScope, kAudioObjectPropertyElementMaster };
|
||||
|
||||
if(didSetVolume && didGetVirtualMasterBalance && AudioHardwareServiceHasProperty(inDevice.GetObjectID(), &virtualMasterBalanceAddress))
|
||||
{
|
||||
Boolean balanceIsSettable;
|
||||
err = AudioHardwareServiceIsPropertySettable(inDevice.GetObjectID(), &virtualMasterBalanceAddress, &balanceIsSettable);
|
||||
if(err == kAudioServicesNoError && balanceIsSettable)
|
||||
{
|
||||
AHSSetPropertyData(inDevice.GetObjectID(), &virtualMasterBalanceAddress, sizeof(Float32), &virtualMasterBalance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return didSetVolume;
|
||||
}
|
||||
|
||||
// static
|
||||
bool BGMDeviceControlSync::GetVirtualMasterBalance(CAHALAudioDevice inDevice, AudioObjectPropertyScope inScope, Float32& outVirtualMasterBalance)
|
||||
{
|
||||
AudioObjectPropertyAddress virtualMasterBalanceAddress =
|
||||
{ kAudioHardwareServiceDeviceProperty_VirtualMasterBalance, inScope, kAudioObjectPropertyElementMaster };
|
||||
|
||||
if(!AudioHardwareServiceHasProperty(inDevice.GetObjectID(), &virtualMasterBalanceAddress))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UInt32 virtualMasterVolumePropertySize = sizeof(Float32);
|
||||
return kAudioServicesNoError == AHSGetPropertyData(inDevice.GetObjectID(),
|
||||
&virtualMasterBalanceAddress,
|
||||
&virtualMasterVolumePropertySize,
|
||||
&outVirtualMasterBalance);
|
||||
}
|
||||
|
||||
// static
|
||||
OSStatus BGMDeviceControlSync::AHSGetPropertyData(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32* ioDataSize, void* outData)
|
||||
{
|
||||
// The docs for AudioHardwareServiceGetPropertyData specifically allow passing NULL for inQualifierData as we do here,
|
||||
// but it's declared in an assume_nonnull section so we have to disable the warning here. I'm not sure why inQualifierData
|
||||
// isn't __nullable. I'm assuming it's either a backwards compatibility thing or just a bug.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnonnull"
|
||||
// The non-depreciated version of this (and the setter below) doesn't seem to support devices other than the default
|
||||
return AudioHardwareServiceGetPropertyData(inObjectID, inAddress, 0, NULL, ioDataSize, outData);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
// static
|
||||
OSStatus BGMDeviceControlSync::AHSSetPropertyData(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inDataSize, const void* inData)
|
||||
{
|
||||
// See the explanation about these pragmas in AHSGetPropertyData
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnonnull"
|
||||
return AudioHardwareServiceSetPropertyData(inObjectID, inAddress, 0, NULL, inDataSize, inData);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
#pragma mark Listener
|
||||
|
||||
// static
|
||||
OSStatus BGMDeviceControlSync::BGMDeviceListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress* __nonnull inAddresses, void* __nullable inClientData)
|
||||
{
|
||||
// refCon (reference context) is the instance that registered this listener proc
|
||||
BGMDeviceControlSync* refCon = static_cast<BGMDeviceControlSync*>(inClientData);
|
||||
|
||||
ThrowIf(inObjectID != refCon->mBGMDevice.GetObjectID(),
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"BGMDeviceControlSync::BGMDeviceListenerProc: notified about audio object other than BGMDevice");
|
||||
|
||||
for(int i = 0; i < inNumberAddresses; i++)
|
||||
{
|
||||
AudioObjectPropertyScope scope = inAddresses[i].mScope;
|
||||
|
||||
switch(inAddresses[i].mSelector)
|
||||
{
|
||||
case kAudioDevicePropertyVolumeScalar:
|
||||
// Update the output device
|
||||
CopyVolume(refCon->mBGMDevice, refCon->mOutputDevice, scope);
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyMute:
|
||||
// Update the output device. Note that this also runs when you change the volume (on BGMDevice)
|
||||
CopyMute(refCon->mBGMDevice, refCon->mOutputDevice, scope);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
81
BGMApp/BGMApp/BGMDeviceControlSync.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMDeviceControlSync.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// Listens for notifications that BGMDevice's controls (just volume and mute currently) have changed value, and
|
||||
// copies the new values to the output device.
|
||||
//
|
||||
|
||||
#ifndef BGMDeviceControlSync_h
|
||||
#define BGMDeviceControlSync_h
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAHALAudioDevice.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
class BGMDeviceControlSync
|
||||
{
|
||||
|
||||
public:
|
||||
BGMDeviceControlSync(CAHALAudioDevice inBGMDevice, CAHALAudioDevice inOutputDevice);
|
||||
~BGMDeviceControlSync();
|
||||
// Disallow copying
|
||||
BGMDeviceControlSync(const BGMDeviceControlSync&) = delete;
|
||||
BGMDeviceControlSync& operator=(const BGMDeviceControlSync&) = delete;
|
||||
// Move constructor/assignment
|
||||
BGMDeviceControlSync(BGMDeviceControlSync&& inDeviceControlSync) { Swap(inDeviceControlSync); }
|
||||
BGMDeviceControlSync& operator=(BGMDeviceControlSync&& inDeviceControlSync) { Swap(inDeviceControlSync); return *this; }
|
||||
|
||||
#ifdef __OBJC__
|
||||
// Only intended as a convenience for Objective-C instance vars
|
||||
BGMDeviceControlSync() { };
|
||||
#endif
|
||||
|
||||
private:
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
void Swap(BGMDeviceControlSync& inDeviceControlSync);
|
||||
|
||||
static void CopyMute(CAHALAudioDevice inFromDevice, CAHALAudioDevice inToDevice, AudioObjectPropertyScope inScope);
|
||||
static void CopyVolume(CAHALAudioDevice inFromDevice, CAHALAudioDevice inToDevice, AudioObjectPropertyScope inScope);
|
||||
|
||||
static bool SetMasterVolumeScalar(CAHALAudioDevice inDevice, AudioObjectPropertyScope inScope, Float32 inVolume);
|
||||
static bool GetVirtualMasterVolume(CAHALAudioDevice inDevice, AudioObjectPropertyScope inScope, Float32& outVirtualMasterVolume);
|
||||
static bool SetVirtualMasterVolume(CAHALAudioDevice inDevice, AudioObjectPropertyScope inScope, Float32 inVolume);
|
||||
static bool GetVirtualMasterBalance(CAHALAudioDevice inDevice, AudioObjectPropertyScope inScope, Float32& outVirtualMasterBalance);
|
||||
|
||||
static OSStatus AHSGetPropertyData(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32* ioDataSize, void* outData);
|
||||
static OSStatus AHSSetPropertyData(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inDataSize, const void* inData);
|
||||
|
||||
static OSStatus BGMDeviceListenerProc(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress* inAddresses, void* __nullable inClientData);
|
||||
|
||||
private:
|
||||
bool mActive = false;
|
||||
CAHALAudioDevice mBGMDevice = CAHALAudioDevice(kAudioDeviceUnknown);
|
||||
CAHALAudioDevice mOutputDevice = CAHALAudioDevice(kAudioDeviceUnknown);
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* BGMDeviceControlSync_h */
|
||||
|
662
BGMApp/BGMApp/BGMPlayThrough.cpp
Normal file
|
@ -0,0 +1,662 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMPlayThrough.cpp
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "BGMPlayThrough.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAAtomic.h"
|
||||
|
||||
// STL Includes
|
||||
#include <algorithm> // For std::max
|
||||
|
||||
// System Includes
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
static const AudioObjectPropertyAddress kDeviceIsRunningAddress = {
|
||||
kAudioDevicePropertyDeviceIsRunning,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress kProcessorOverloadAddress = {
|
||||
kAudioDeviceProcessorOverload,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
BGMPlayThrough::BGMPlayThrough(CAHALAudioDevice inInputDevice, CAHALAudioDevice inOutputDevice)
|
||||
:
|
||||
mInputDevice(inInputDevice),
|
||||
mOutputDevice(inOutputDevice)
|
||||
{
|
||||
AllocateBuffer();
|
||||
}
|
||||
|
||||
BGMPlayThrough::~BGMPlayThrough()
|
||||
{
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
void BGMPlayThrough::Swap(BGMPlayThrough &inPlayThrough)
|
||||
{
|
||||
CAMutex::Locker stateLocker(mStateMutex);
|
||||
|
||||
bool wasPlayingThrough = inPlayThrough.mPlayingThrough;
|
||||
|
||||
Deactivate();
|
||||
|
||||
mInputDevice = inPlayThrough.mInputDevice;
|
||||
mOutputDevice = inPlayThrough.mOutputDevice;
|
||||
|
||||
AllocateBuffer();
|
||||
|
||||
inPlayThrough.Deactivate();
|
||||
|
||||
if(wasPlayingThrough)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
void BGMPlayThrough::Activate()
|
||||
{
|
||||
CAMutex::Locker stateLocker(mStateMutex);
|
||||
|
||||
if(!mActive)
|
||||
{
|
||||
CreateIOProcs();
|
||||
|
||||
// Start the output device. (See the comments in PauseIfIdle for an explanation.)
|
||||
mOutputDevice.StartIOProc(NULL);
|
||||
|
||||
if(IsBGMDevice(mInputDevice))
|
||||
{
|
||||
// Set BGMDevice sample rate to match the output device
|
||||
Float64 outputSampleRate = mOutputDevice.GetNominalSampleRate();
|
||||
mInputDevice.SetNominalSampleRate(outputSampleRate);
|
||||
|
||||
// Set BGMDevice IO buffer size to match the output device
|
||||
mInputDevice.SetNominalSampleRate(outputSampleRate);
|
||||
|
||||
// Register for notifications from BGMDevice
|
||||
mInputDevice.AddPropertyListener(kDeviceIsRunningAddress, &BGMPlayThrough::BGMDeviceListenerProc, this);
|
||||
mInputDevice.AddPropertyListener(kProcessorOverloadAddress, &BGMPlayThrough::BGMDeviceListenerProc, this);
|
||||
mInputDevice.AddPropertyListener(kBGMRunningSomewhereOtherThanBGMAppAddress,
|
||||
&BGMPlayThrough::BGMDeviceListenerProc,
|
||||
this);
|
||||
}
|
||||
|
||||
mActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
void BGMPlayThrough::Deactivate()
|
||||
{
|
||||
CAMutex::Locker stateLocker(mStateMutex);
|
||||
|
||||
if(mActive)
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::Deactivate: Deactivating playthrough");
|
||||
|
||||
if(IsBGMDevice(mInputDevice))
|
||||
{
|
||||
// Unregister notification listeners
|
||||
mInputDevice.RemovePropertyListener(kDeviceIsRunningAddress, &BGMPlayThrough::BGMDeviceListenerProc, this);
|
||||
mInputDevice.RemovePropertyListener(kProcessorOverloadAddress, &BGMPlayThrough::BGMDeviceListenerProc, this);
|
||||
mInputDevice.RemovePropertyListener(kBGMRunningSomewhereOtherThanBGMAppAddress,
|
||||
&BGMPlayThrough::BGMDeviceListenerProc,
|
||||
this);
|
||||
}
|
||||
|
||||
if(!mPlayingThrough)
|
||||
{
|
||||
// Balance the StartIOProc(NULL) call in Activate() to stop the output device. (See the docs for
|
||||
// AudioDeviceStart in AudioHardware.h)
|
||||
mOutputDevice.StopIOProc(NULL);
|
||||
}
|
||||
|
||||
DestroyIOProcs();
|
||||
|
||||
mActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
void BGMPlayThrough::AllocateBuffer()
|
||||
{
|
||||
// Allocate the ring buffer that will hold the data passing between the devices
|
||||
UInt32 numberStreams = 1;
|
||||
AudioStreamBasicDescription outputFormat[1];
|
||||
mOutputDevice.GetCurrentVirtualFormats(false, numberStreams, outputFormat);
|
||||
|
||||
if(numberStreams < 1)
|
||||
{
|
||||
Throw(CAException(kAudioHardwareUnsupportedOperationError));
|
||||
}
|
||||
|
||||
// The calculation for the size of the buffer is from Apple's CAPlayThrough.cpp sample code
|
||||
//
|
||||
// TODO: Test playthrough with hardware with more than 2 channels per frame, a sample (virtual) format other than
|
||||
// 32-bit floats and/or an IO buffer size other than 512 frames
|
||||
mBuffer.Allocate(outputFormat[0].mChannelsPerFrame,
|
||||
outputFormat[0].mBytesPerFrame,
|
||||
mOutputDevice.GetIOBufferSize() * 20);
|
||||
}
|
||||
|
||||
// static
|
||||
bool BGMPlayThrough::IsBGMDevice(CAHALAudioDevice inDevice)
|
||||
{
|
||||
CFStringRef uid = inDevice.CopyDeviceUID();
|
||||
bool isBGMDevice = CFEqual(uid, CFSTR(kBGMDeviceUID));
|
||||
CFRelease(uid);
|
||||
return isBGMDevice;
|
||||
}
|
||||
|
||||
void BGMPlayThrough::CreateIOProcs()
|
||||
{
|
||||
Assert(!mPlayingThrough, "BGMPlayThrough::CreateIOProcs: Tried to create IOProcs when playthrough was already running");
|
||||
|
||||
if(mInputDevice.IsAlive() && mOutputDevice.IsAlive())
|
||||
{
|
||||
mInputDeviceIOProcID = mInputDevice.CreateIOProcID(&BGMPlayThrough::InputDeviceIOProc, this);
|
||||
mOutputDeviceIOProcID = mOutputDevice.CreateIOProcID(&BGMPlayThrough::OutputDeviceIOProc, this);
|
||||
|
||||
Assert(mInputDeviceIOProcID != NULL && mOutputDeviceIOProcID != NULL,
|
||||
"BGMPlayThrough::CreateIOProcs: Null IOProc ID returned by CreateIOProcID");
|
||||
|
||||
// TODO: Try using SetIOCycleUsage to reduce latency? Our IOProcs don't really do anything except copy a small
|
||||
// buffer. According to this, Jack OS X considered it:
|
||||
// https://lists.apple.com/archives/coreaudio-api/2008/Mar/msg00043.html but from a quick look at their
|
||||
// code, I don't think they ended up using it.
|
||||
// mInputDevice->SetIOCycleUsage(0.01f);
|
||||
// mOutputDevice->SetIOCycleUsage(0.01f);
|
||||
}
|
||||
}
|
||||
|
||||
void BGMPlayThrough::DestroyIOProcs()
|
||||
{
|
||||
Pause();
|
||||
|
||||
if(mInputDeviceIOProcID != NULL)
|
||||
{
|
||||
mInputDevice.DestroyIOProcID(mInputDeviceIOProcID);
|
||||
mInputDeviceIOProcID = NULL;
|
||||
}
|
||||
|
||||
if(mOutputDeviceIOProcID != NULL)
|
||||
{
|
||||
mOutputDevice.DestroyIOProcID(mOutputDeviceIOProcID);
|
||||
mOutputDeviceIOProcID = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Control Playthrough
|
||||
|
||||
OSStatus BGMPlayThrough::Start()
|
||||
{
|
||||
CAMutex::Locker stateLocker(mStateMutex);
|
||||
|
||||
if(!mPlayingThrough && mInputDevice.IsAlive() && mOutputDevice.IsAlive())
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::Start: Starting playthrough");
|
||||
|
||||
// Set up IOProcs and listeners if they aren't already
|
||||
Activate();
|
||||
|
||||
// Just in case Pause() didn't reset these for some reason
|
||||
mInputDeviceIOProcShouldStop = false;
|
||||
mOutputDeviceIOProcShouldStop = false;
|
||||
CAMemoryBarrier();
|
||||
|
||||
// Start our IOProcs
|
||||
mInputDevice.StartIOProc(mInputDeviceIOProcID);
|
||||
// mOutputDevice.SetIOBufferSize(512);
|
||||
mOutputDevice.StartIOProc(mOutputDeviceIOProcID);
|
||||
|
||||
mPlayingThrough = true;
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
OSStatus BGMPlayThrough::Pause()
|
||||
{
|
||||
CAMutex::Locker stateLocker(mStateMutex);
|
||||
|
||||
if(mActive && mPlayingThrough)
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::Pause: Pausing playthrough");
|
||||
|
||||
if(mInputDevice.IsAlive())
|
||||
{
|
||||
mInputDeviceIOProcShouldStop = true;
|
||||
}
|
||||
if(mOutputDevice.IsAlive())
|
||||
{
|
||||
mOutputDeviceIOProcShouldStop = true;
|
||||
}
|
||||
|
||||
// Wait for the IOProcs to stop themselves, with a timeout of about two IO cycles. This is so the IOProcs don't get called
|
||||
// after the BGMPlayThrough instance (pointed to by the client data they get from the HAL) is deallocated.
|
||||
//
|
||||
// From Jeff Moore on the Core Audio mailing list:
|
||||
// Note that there is no guarantee about how many times your IOProc might get called after AudioDeviceStop() returns
|
||||
// when you make the call from outside of your IOProc. However, if you call AudioDeviceStop() from inside your IOProc,
|
||||
// you do get the guarantee that your IOProc will not get called again after the IOProc has returned.
|
||||
UInt64 totalWaitNs = 0;
|
||||
Float64 expectedInputCycleNs = mInputDevice.GetIOBufferSize() * (1 / mInputDevice.GetNominalSampleRate()) * NSEC_PER_SEC;
|
||||
Float64 expectedOutputCycleNs = mOutputDevice.GetIOBufferSize() * (1 / mOutputDevice.GetNominalSampleRate()) * NSEC_PER_SEC;
|
||||
UInt64 expectedMaxCycleNs = static_cast<UInt64>(std::max(expectedInputCycleNs, expectedOutputCycleNs));
|
||||
while((mInputDeviceIOProcShouldStop || mOutputDeviceIOProcShouldStop) && totalWaitNs < 2 * expectedMaxCycleNs)
|
||||
{
|
||||
// TODO: If playthrough is started again while we're waiting in this loop we could drop frames. Wait on a semaphore
|
||||
// instead of sleeping? That way Start() could also signal it, before waiting on the state mutex, as a way of
|
||||
// cancelling the pause operation.
|
||||
struct timespec rmtp;
|
||||
int err = nanosleep((const struct timespec[]){{0, NSEC_PER_MSEC}}, &rmtp);
|
||||
totalWaitNs += NSEC_PER_MSEC - (err == -1 ? rmtp.tv_nsec : 0);
|
||||
CAMemoryBarrier();
|
||||
}
|
||||
|
||||
// Clean up if the IOProcs didn't stop themselves
|
||||
if(mInputDeviceIOProcShouldStop && mInputDeviceIOProcID != NULL)
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::Pause: The input IOProc didn't stop itself in time. Stopping it from outside of the IO thread.");
|
||||
mInputDevice.StopIOProc(mInputDeviceIOProcID);
|
||||
mInputDeviceIOProcShouldStop = false;
|
||||
}
|
||||
if(mOutputDeviceIOProcShouldStop && mOutputDeviceIOProcID != NULL)
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::Pause: The output IOProc didn't stop itself in time. Stopping it from outside of the IO thread.");
|
||||
mOutputDevice.StopIOProc(mOutputDeviceIOProcID);
|
||||
mOutputDeviceIOProcShouldStop = false;
|
||||
}
|
||||
|
||||
/*
|
||||
// Increase the size of the IO buffers while idle to save CPU
|
||||
UInt32 unusedMin, max;
|
||||
mOutputDevice->GetIOBufferSizeRange(unusedMin, max);
|
||||
DebugMsg("BGMPlayThrough::Pause: Set output device to its max IO buffer size: %u", max);
|
||||
mOutputDevice->SetIOBufferSize(max);
|
||||
*/
|
||||
|
||||
mPlayingThrough = false;
|
||||
}
|
||||
|
||||
mFirstInputSampleTime = -1;
|
||||
mLastInputSampleTime = -1;
|
||||
mLastOutputSampleTime = -1;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
void BGMPlayThrough::PauseIfIdle()
|
||||
{
|
||||
// To save CPU time, we pause playthrough when no clients are doing IO. On my system, this reduces coreaudiod's CPU
|
||||
// use from around 5% to 2.5% and BGMApp's from around 0.6% to 0.3%. If this isn't working for you, a client might be
|
||||
// running IO without being audible. VLC does that when you have a file paused, for example.
|
||||
//
|
||||
// The driver for Apple's built-in audio uses a fair bit of CPU doing some DSP on all audio playing through the
|
||||
// built-in speakers. So this might not have much effect if you're using headphones or different audio hardware.
|
||||
//
|
||||
// To minimize latency, rather than stopping IO on the hardware completely, we just stop our IOProcs. (See Pause().)
|
||||
// We can start them again quickly enough that this doesn't add latency, though I'm not sure the HAL guarantees that.
|
||||
// This function should at least be called with real-time priority because we call it when we get the
|
||||
// kAudioDevicePropertyDeviceIsRunning notification. From Jeff Moore on the Core Audio mailing list:
|
||||
// "Only kAudioDevicePropertyDeviceIsRunning comes from the IO thread, but it happens at a relatively safe time
|
||||
// - either before the HAL starts tracking time or after it is finished.
|
||||
//
|
||||
// Another option is to increase the size of the IO buffers on the output device (with SetIOBufferSize()), which uses
|
||||
// less CPU at the cost of higher latency. I also tried increasing the size of the IO buffers while playthrough is
|
||||
// paused but changing them back before starting playthrough. It was around 4-5 times faster than restarting IO for
|
||||
// me, but it still roughly doubled latency compared to the current solution. And of course that's all completely
|
||||
// hardware-dependent.
|
||||
//
|
||||
// TODO: I think the correct way to handle this is for BGMDriver's StartIO function to tell BGMApp to start IO on the
|
||||
// output device, and not return until BGMApp replies that IO has started. We should probably use XPC to send
|
||||
// those messages rather than HAL notifications. With this solution we'd add no more latency than with the
|
||||
// current solution (which is no more than if we run the hardware constantly -- the latency comes from needing
|
||||
// an extra output buffer) and we wouldn't have to waste any CPU time when audio is idle.
|
||||
|
||||
CAMutex::Locker stateLocker(mStateMutex);
|
||||
|
||||
Assert(IsBGMDevice(mInputDevice),
|
||||
"BGMDevice not set as input device. PauseIfIdle can't tell if other devices are idle");
|
||||
|
||||
if(!RunningSomewhereOtherThanBGMApp(mInputDevice))
|
||||
{
|
||||
mLastNotifiedIOStoppedOnBGMDevice = mach_absolute_time();
|
||||
|
||||
// Wait a bit before pausing playthrough
|
||||
//
|
||||
// This stops us from starting and stopping IO too rapidly, which wastes CPU, and gives BGMDriver time to update
|
||||
// kAudioDeviceCustomPropertyDeviceAudibleState, which it can only do while IO is running. (The wait duration is
|
||||
// more or less arbitrary, except that it has to be longer than kDeviceAudibleStateMinChangedFramesForUpdate.)
|
||||
|
||||
// 1 / sample rate = seconds per frame
|
||||
Float64 nsecPerFrame = (1.0 / mInputDevice.GetNominalSampleRate()) * NSEC_PER_SEC;
|
||||
UInt64 waitNsec = static_cast<UInt64>(20 * kDeviceAudibleStateMinChangedFramesForUpdate * nsecPerFrame);
|
||||
UInt64 queuedAt = mLastNotifiedIOStoppedOnBGMDevice;
|
||||
|
||||
DebugMsg("BGMPlayThrough::PauseIfIdle: Will dispatch pause-if-idle block in %llu ns. %s%llu",
|
||||
waitNsec,
|
||||
"queuedAt=", queuedAt);
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, waitNsec),
|
||||
dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0),
|
||||
^{
|
||||
// Check the BGMPlayThrough instance hasn't been destructed since it queued this block
|
||||
if(mActive)
|
||||
{
|
||||
// The "2" is just to avoid shadowing the other locker
|
||||
CAMutex::Locker stateLocker2(mStateMutex);
|
||||
|
||||
// Don't Pause playthrough if IO has started running again or if
|
||||
// kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp has changed since
|
||||
// this block was queued
|
||||
if(mPlayingThrough && !RunningSomewhereOtherThanBGMApp(mInputDevice)
|
||||
&& queuedAt == mLastNotifiedIOStoppedOnBGMDevice)
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::PauseIfIdle: BGMDevice is only running IO for BGMApp. %s",
|
||||
"Pausing playthrough.");
|
||||
Pause();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark BGMDevice Listener
|
||||
|
||||
// TODO: Listen for changes to the sample rate and IO buffer size of the output device and update the input device to match
|
||||
|
||||
// static
|
||||
OSStatus BGMPlayThrough::BGMDeviceListenerProc(AudioObjectID inObjectID,
|
||||
UInt32 inNumberAddresses,
|
||||
const AudioObjectPropertyAddress* __nonnull inAddresses,
|
||||
void* __nullable inClientData)
|
||||
{
|
||||
// refCon (reference context) is the instance that registered the listener proc
|
||||
BGMPlayThrough* refCon = static_cast<BGMPlayThrough*>(inClientData);
|
||||
|
||||
// If the input device isn't BGMDevice, this listener proc shouldn't be registered
|
||||
ThrowIf(inObjectID != refCon->mInputDevice.GetObjectID(),
|
||||
CAException(kAudioHardwareBadObjectError),
|
||||
"BGMPlayThrough::BGMDeviceListenerProc: notified about audio object other than BGMDevice");
|
||||
|
||||
for(int i = 0; i < inNumberAddresses; i++)
|
||||
{
|
||||
switch(inAddresses[i].mSelector)
|
||||
{
|
||||
case kAudioDeviceProcessorOverload:
|
||||
DebugMsg("BGMPlayThrough::BGMDeviceListenerProc: WARNING! Got kAudioDeviceProcessorOverload notification");
|
||||
LogWarning("Background Music: CPU overload on Background Music Device");
|
||||
break;
|
||||
|
||||
// Start playthrough when a client starts IO on BGMDevice and pause when BGMApp (i.e. playthrough itself) is
|
||||
// the only client left doing IO.
|
||||
//
|
||||
// These cases are dispatched to avoid causing deadlocks by triggering one of the following notifications in
|
||||
// the process of handling one. Deadlocks could happen if these were handled synchronously when:
|
||||
// - the first ListenerProc call takes the state mutex, then requests some data from the HAL and waits
|
||||
// for it to return,
|
||||
// - the request triggers the HAL to send notifications, which it sends on a different thread,
|
||||
// - the HAL waits for the second ListenerProc call to return before it returns the data requested by the
|
||||
// first ListenerProc call, and
|
||||
// - the second ListenerProc call waits for the first to unlock the state mutex.
|
||||
|
||||
case kAudioDevicePropertyDeviceIsRunning: // Received on the IO thread before our IOProc is called
|
||||
{
|
||||
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))
|
||||
{
|
||||
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
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
|
||||
if(refCon->mActive)
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::BGMDeviceListenerProc: Handling %s",
|
||||
"kAudioDevicePropertyDeviceIsRunning notification in dispatched block");
|
||||
CAMutex::Locker stateLocker(refCon->mStateMutex);
|
||||
deviceIsRunningHandler();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp:
|
||||
DebugMsg("BGMPlayThrough::BGMDeviceListenerProc: Got %s",
|
||||
"kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp notification");
|
||||
|
||||
// These notifications don't need to be handled very quickly, so we can always dispatch
|
||||
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
|
||||
if(refCon->mActive)
|
||||
{
|
||||
refCon->PauseIfIdle();
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// static
|
||||
bool BGMPlayThrough::RunningSomewhereOtherThanBGMApp(const CAHALAudioDevice inBGMDevice)
|
||||
{
|
||||
return CFBooleanGetValue(
|
||||
static_cast<CFBooleanRef>(
|
||||
inBGMDevice.GetPropertyData_CFType(kBGMRunningSomewhereOtherThanBGMAppAddress)));
|
||||
}
|
||||
|
||||
#pragma mark IOProcs
|
||||
|
||||
// Note that the IOProcs will very likely not run on the same thread and that they intentionally don't lock any mutexes.
|
||||
|
||||
// static
|
||||
OSStatus BGMPlayThrough::InputDeviceIOProc(AudioObjectID inDevice,
|
||||
const AudioTimeStamp* inNow,
|
||||
const AudioBufferList* inInputData,
|
||||
const AudioTimeStamp* inInputTime,
|
||||
AudioBufferList* outOutputData,
|
||||
const AudioTimeStamp* inOutputTime,
|
||||
void* __nullable inClientData)
|
||||
{
|
||||
#pragma unused (inDevice, inNow, outOutputData, inOutputTime)
|
||||
|
||||
// refCon (reference context) is the instance that created the IOProc
|
||||
BGMPlayThrough* refCon = static_cast<BGMPlayThrough*>(inClientData);
|
||||
|
||||
// Stop this IOProc if the main thread has told us to
|
||||
if(refCon->mInputDeviceIOProcShouldStop)
|
||||
{
|
||||
refCon->mInputDevice.StopIOProc(refCon->mInputDeviceIOProcID);
|
||||
CAMemoryBarrier();
|
||||
refCon->mInputDeviceIOProcShouldStop = false;
|
||||
return noErr;
|
||||
}
|
||||
|
||||
if(refCon->mFirstInputSampleTime == -1)
|
||||
{
|
||||
refCon->mFirstInputSampleTime = inInputTime->mSampleTime;
|
||||
}
|
||||
|
||||
UInt32 framesToStore = inInputData->mBuffers[0].mDataByteSize / (SizeOf32(Float32) * 2);
|
||||
|
||||
CARingBufferError err = refCon->mBuffer.Store(inInputData,
|
||||
framesToStore,
|
||||
static_cast<CARingBuffer::SampleTime>(inInputTime->mSampleTime));
|
||||
|
||||
HandleRingBufferError(err, "InputDeviceIOProc", "mBuffer.Store");
|
||||
|
||||
CAMemoryBarrier();
|
||||
refCon->mLastInputSampleTime = inInputTime->mSampleTime;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
// static
|
||||
OSStatus BGMPlayThrough::OutputDeviceIOProc(AudioObjectID inDevice,
|
||||
const AudioTimeStamp* inNow,
|
||||
const AudioBufferList* inInputData,
|
||||
const AudioTimeStamp* inInputTime,
|
||||
AudioBufferList* outOutputData,
|
||||
const AudioTimeStamp* inOutputTime,
|
||||
void* __nullable inClientData)
|
||||
{
|
||||
#pragma unused (inDevice, inNow, inInputData, inInputTime, inOutputTime)
|
||||
|
||||
// refCon (reference context) is the instance that created the IOProc
|
||||
BGMPlayThrough* refCon = static_cast<BGMPlayThrough*>(inClientData);
|
||||
|
||||
// Stop this IOProc if the main thread has told us to
|
||||
if(refCon->mOutputDeviceIOProcShouldStop)
|
||||
{
|
||||
refCon->mOutputDevice.StopIOProc(refCon->mOutputDeviceIOProcID);
|
||||
CAMemoryBarrier();
|
||||
refCon->mOutputDeviceIOProcShouldStop = false;
|
||||
return noErr;
|
||||
}
|
||||
|
||||
// Return early if we don't have any data to output yet
|
||||
if(refCon->mLastInputSampleTime == -1)
|
||||
{
|
||||
// TODO: Write silence to outOutputData here
|
||||
return noErr;
|
||||
}
|
||||
|
||||
// If this is the first time this IOProc has been called since starting playthrough...
|
||||
if(refCon->mLastOutputSampleTime == -1)
|
||||
{
|
||||
// Calculate the number of frames between the read and write heads
|
||||
refCon->mInToOutSampleOffset = inOutputTime->mSampleTime - refCon->mLastInputSampleTime;
|
||||
|
||||
// Log if we dropped frames
|
||||
if(refCon->mFirstInputSampleTime != refCon->mLastInputSampleTime)
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::OutputDeviceIOProc: Dropped %f frames before output started. %s%f %s%f",
|
||||
(refCon->mLastInputSampleTime - refCon->mFirstInputSampleTime),
|
||||
"mFirstInputSampleTime=", refCon->mFirstInputSampleTime,
|
||||
"mLastInputSampleTime=", refCon->mLastInputSampleTime);
|
||||
}
|
||||
}
|
||||
|
||||
CARingBuffer::SampleTime readHeadSampleTime =
|
||||
static_cast<CARingBuffer::SampleTime>(inOutputTime->mSampleTime - refCon->mInToOutSampleOffset);
|
||||
CARingBuffer::SampleTime lastInputSampleTime =
|
||||
static_cast<CARingBuffer::SampleTime>(refCon->mLastInputSampleTime);
|
||||
|
||||
UInt32 framesToOutput = outOutputData->mBuffers[0].mDataByteSize / (SizeOf32(Float32) * 2);
|
||||
|
||||
// Very occasionally (at least for me) our read head gets ahead of input, i.e. we haven't received any new input since
|
||||
// this IOProc was last called, and we have to recalculate its position. I figure this might be caused by clock drift
|
||||
// but I'm really not sure. It also happens if the input or output sample times are restarted from zero.
|
||||
//
|
||||
// We also recalculate the offset if the read head is outside of the ring buffer. This happens for example when you plug
|
||||
// in or unplug headphones, which causes the output sample times to be restarted from zero.
|
||||
//
|
||||
// The vast majority of the time, just using lastInputSampleTime as the read head time instead of the one we calculate
|
||||
// would work fine (and would also account for the above).
|
||||
SInt64 bufferStartTime, bufferEndTime;
|
||||
CARingBufferError err = refCon->mBuffer.GetTimeBounds(bufferStartTime, bufferEndTime);
|
||||
bool outOfBounds = false;
|
||||
if(err == kCARingBufferError_OK)
|
||||
{
|
||||
outOfBounds = (readHeadSampleTime < bufferStartTime) || (readHeadSampleTime - framesToOutput > bufferEndTime);
|
||||
}
|
||||
if(lastInputSampleTime < readHeadSampleTime || outOfBounds)
|
||||
{
|
||||
DebugMsg("BGMPlayThrough::OutputDeviceIOProc: No input samples ready at output sample time. %s%lld %s%lld %s%f",
|
||||
"lastInputSampleTime=", lastInputSampleTime,
|
||||
"readHeadSampleTime=", readHeadSampleTime,
|
||||
"mInToOutSampleOffset=", refCon->mInToOutSampleOffset);
|
||||
|
||||
// Recalculate the in-to-out offset and read head
|
||||
refCon->mInToOutSampleOffset = inOutputTime->mSampleTime - lastInputSampleTime;
|
||||
readHeadSampleTime = static_cast<CARingBuffer::SampleTime>(inOutputTime->mSampleTime - refCon->mInToOutSampleOffset);
|
||||
}
|
||||
|
||||
// Copy the frames from the ring buffer
|
||||
err = refCon->mBuffer.Fetch(outOutputData, framesToOutput, readHeadSampleTime);
|
||||
|
||||
HandleRingBufferError(err, "OutputDeviceIOProc", "mBuffer.Fetch");
|
||||
|
||||
refCon->mLastOutputSampleTime = inOutputTime->mSampleTime;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
// static
|
||||
void BGMPlayThrough::HandleRingBufferError(CARingBufferError inErr,
|
||||
const char* inMethodName,
|
||||
const char* inCallReturningErr)
|
||||
{
|
||||
#if DEBUG
|
||||
if(inErr != kCARingBufferError_OK)
|
||||
{
|
||||
const char* errStr = (inErr == kCARingBufferError_TooMuch ? "kCARingBufferError_TooMuch" :
|
||||
(inErr == kCARingBufferError_CPUOverload ? "kCARingBufferError_CPUOverload" : "unknown error"));
|
||||
|
||||
DebugMsg("BGMPlayThrough::%s: %s returned %s (%d)", inMethodName, inCallReturningErr, errStr, inErr);
|
||||
|
||||
// kCARingBufferError_CPUOverload wouldn't mean we have a bug, but I think kCARingBufferError_TooMuch would
|
||||
if(inErr != kCARingBufferError_CPUOverload)
|
||||
{
|
||||
Throw(CAException(inErr));
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Not sure what we should do to handle these errors in release builds, if anything.
|
||||
// TODO: There's code in Apple's CAPlayThrough.cpp sample code that handles them. (Look for "kCARingBufferError_OK"
|
||||
// around line 707.) Should be easy enough to use, but it's more complicated that just directly copying it.
|
||||
#pragma unused (inErr, inMethodName, inCallReturningErr)
|
||||
#endif
|
||||
}
|
||||
|
140
BGMApp/BGMApp/BGMPlayThrough.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMPlayThrough.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// Reads audio from an input device and immediately writes it to an output device. We currently use this class with the input
|
||||
// device always set to BGMDevice and the output device set to the one selected in the preferences menu.
|
||||
//
|
||||
// Apple's CAPlayThrough sample code (https://developer.apple.com/library/mac/samplecode/CAPlayThrough/Introduction/Intro.html)
|
||||
// has a similar class, but I couldn't get it fast enough to use here. Soundflower also has a similar class
|
||||
// (https://github.com/mattingalls/Soundflower/blob/master/SoundflowerBed/AudioThruEngine.h) that seems to be based on Apple
|
||||
// sample code from 2004. This class's main addition is pausing playthrough when idle to save CPU.
|
||||
//
|
||||
// This class will hopefully not be needed after CoreAudio's aggregate devices get support for controls, which is planned for
|
||||
// a future release.
|
||||
//
|
||||
|
||||
#ifndef __BGMApp__BGMPlayThrough__
|
||||
#define __BGMApp__BGMPlayThrough__
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CARingBuffer.h"
|
||||
#include "CAHALAudioDevice.h"
|
||||
#include "CAMutex.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
class BGMPlayThrough
|
||||
{
|
||||
|
||||
public:
|
||||
BGMPlayThrough(CAHALAudioDevice inInputDevice, CAHALAudioDevice inOutputDevice);
|
||||
~BGMPlayThrough();
|
||||
// Disallow copying
|
||||
BGMPlayThrough(const BGMPlayThrough&) = delete;
|
||||
BGMPlayThrough& operator=(const BGMPlayThrough&) = delete;
|
||||
// Move constructor/assignment
|
||||
BGMPlayThrough(BGMPlayThrough&& inPlayThrough) { Swap(inPlayThrough); }
|
||||
BGMPlayThrough& operator=(BGMPlayThrough&& inPlayThrough) { Swap(inPlayThrough); return *this; }
|
||||
|
||||
#ifdef __OBJC__
|
||||
// Only intended as a convenience for Objective-C instance vars
|
||||
BGMPlayThrough() { }
|
||||
#endif
|
||||
|
||||
private:
|
||||
void Swap(BGMPlayThrough& inPlayThrough);
|
||||
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
|
||||
void AllocateBuffer();
|
||||
|
||||
static bool IsBGMDevice(CAHALAudioDevice inDevice);
|
||||
|
||||
void CreateIOProcs();
|
||||
void DestroyIOProcs();
|
||||
|
||||
public:
|
||||
OSStatus Start();
|
||||
|
||||
private:
|
||||
OSStatus Pause();
|
||||
void PauseIfIdle();
|
||||
static OSStatus BGMDeviceListenerProc(AudioObjectID inObjectID,
|
||||
UInt32 inNumberAddresses,
|
||||
const AudioObjectPropertyAddress* inAddresses,
|
||||
void* __nullable inClientData);
|
||||
static bool RunningSomewhereOtherThanBGMApp(const CAHALAudioDevice inBGMDevice);
|
||||
|
||||
static OSStatus InputDeviceIOProc(AudioObjectID inDevice,
|
||||
const AudioTimeStamp* inNow,
|
||||
const AudioBufferList* inInputData,
|
||||
const AudioTimeStamp* inInputTime,
|
||||
AudioBufferList* outOutputData,
|
||||
const AudioTimeStamp* inOutputTime,
|
||||
void* __nullable inClientData);
|
||||
static OSStatus OutputDeviceIOProc(AudioObjectID inDevice,
|
||||
const AudioTimeStamp* inNow,
|
||||
const AudioBufferList* inInputData,
|
||||
const AudioTimeStamp* inInputTime,
|
||||
AudioBufferList* outOutputData,
|
||||
const AudioTimeStamp* inOutputTime,
|
||||
void* __nullable inClientData);
|
||||
static void HandleRingBufferError(CARingBufferError err,
|
||||
const char* methodName,
|
||||
const char* callReturningErr);
|
||||
|
||||
private:
|
||||
CARingBuffer mBuffer;
|
||||
|
||||
AudioDeviceIOProcID mInputDeviceIOProcID;
|
||||
AudioDeviceIOProcID mOutputDeviceIOProcID;
|
||||
|
||||
CAHALAudioDevice mInputDevice = CAHALAudioDevice(kAudioDeviceUnknown);
|
||||
CAHALAudioDevice mOutputDevice = CAHALAudioDevice(kAudioDeviceUnknown);
|
||||
|
||||
CAMutex mStateMutex = CAMutex("State mutex");
|
||||
|
||||
bool mActive = false;
|
||||
bool mPlayingThrough = false;
|
||||
|
||||
UInt64 mLastNotifiedIOStoppedOnBGMDevice;
|
||||
|
||||
bool mInputDeviceIOProcShouldStop = false;
|
||||
bool mOutputDeviceIOProcShouldStop = false;
|
||||
|
||||
// IOProc vars. (Should only be used inside IOProcs.)
|
||||
|
||||
// The earliest/latest sample times seen by the IOProcs since starting playthrough. -1 for unset.
|
||||
Float64 mFirstInputSampleTime = -1;
|
||||
Float64 mLastInputSampleTime = -1;
|
||||
Float64 mLastOutputSampleTime = -1;
|
||||
|
||||
// Subtract this from the output time to get the input time
|
||||
Float64 mInToOutSampleOffset;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* defined(__BGMApp__BGMPlayThrough__) */
|
||||
|
197
BGMApp/BGMApp/Base.lproj/MainMenu.xib
Normal file
|
@ -0,0 +1,197 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="14F1509" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
|
||||
<connections>
|
||||
<outlet property="aboutPanel" destination="Cf4-3V-gl1" id="cgo-Hw-rE2"/>
|
||||
<outlet property="aboutPanelLicenseView" destination="LSG-PF-cl8" id="mbu-kv-Jfc"/>
|
||||
<outlet property="appVolumeView" destination="MWB-XH-kFI" id="eFA-RN-VMC"/>
|
||||
<outlet property="autoPauseMenuItem" destination="nHv-T8-1nb" id="skN-ap-dre"/>
|
||||
<outlet property="bgmMenu" destination="8AN-nh-rEe" id="UWn-BX-eLy"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<menu id="8AN-nh-rEe" userLabel="BGM Menu">
|
||||
<items>
|
||||
<menuItem title="Auto-pause Music" tag="2" id="nHv-T8-1nb">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutoPauseMusic:" target="Voe-Tx-rLC" id="fL3-wA-voV"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="ZGd-Pq-YeA"/>
|
||||
<menuItem title="App Volumes" tag="3" enabled="NO" id="8PP-wA-Pae">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" tag="4" id="rkf-nx-H2J"/>
|
||||
<menuItem title="Preferences" tag="1" id="BHb-uh-9Zm">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Preferences" id="Img-Dh-cpU">
|
||||
<items>
|
||||
<menuItem title="Auto-pause" tag="1" enabled="NO" id="2aT-t7-HGW">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="nb1-jq-97L"/>
|
||||
<menuItem title="Output Device" tag="2" enabled="NO" id="chk-9C-pab">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="D3z-zv-JrJ"/>
|
||||
<menuItem title="About Background Music" tag="3" id="R45-Vo-Eto">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Quit Background Music" id="Nj2-gJ-DhW">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="terminate:" target="-1" id="seH-Fc-tXb"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
<point key="canvasLocation" x="-184" y="-69.5"/>
|
||||
</menu>
|
||||
<customView id="MWB-XH-kFI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="264" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField identifier="AppName" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Xmd-bg-huG" customClass="BGMAVM_AppNameLabel">
|
||||
<rect key="frame" x="58" y="4" width="115" height="14"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="left" title="App name here" usesSingleLineMode="YES" id="ZHF-ZW-Oqg">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<imageView identifier="AppIcon" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="W04-iT-IUw" customClass="BGMAVM_AppIcon">
|
||||
<rect key="frame" x="36" y="3" width="16" height="16"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="6QQ-oO-HxF"/>
|
||||
</imageView>
|
||||
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="I1l-Ci-4md" customClass="BGMAVM_VolumeSlider">
|
||||
<rect key="frame" x="179" y="2" width="74" height="17"/>
|
||||
<sliderCell key="cell" controlSize="small" continuous="YES" state="on" alignment="left" maxValue="100" doubleValue="50" tickMarkPosition="above" sliderType="linear" id="Jmg-df-9Xl"/>
|
||||
</slider>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="81" y="-111"/>
|
||||
</customView>
|
||||
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="Cf4-3V-gl1" customClass="NSPanel">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" topStrut="YES"/>
|
||||
<rect key="contentRect" x="248" y="350" width="748" height="293"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
|
||||
<view key="contentView" id="HlB-hX-Y0Y">
|
||||
<rect key="frame" x="0.0" y="0.0" width="748" height="293"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r51-dd-LGP">
|
||||
<rect key="frame" x="18" y="78" width="240" height="22"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Background Music" id="Dw2-nu-eBQ">
|
||||
<font key="font" size="18" name=".HelveticaNeueDeskInterface-Regular"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" tag="1" translatesAutoresizingMaskIntoConstraints="NO" id="ekc-h0-I43">
|
||||
<rect key="frame" x="18" y="53" width="240" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Version 0.1.0" id="FDH-7l-wFf">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L5P-Lw-aCd">
|
||||
<rect key="frame" x="299" y="256" width="270" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="Licensed under GPLv2 or any later version." id="ETh-En-bzX">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<box horizontalHuggingPriority="750" fixedFrame="YES" title="Box" boxType="separator" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="Zc9-gs-X8C">
|
||||
<rect key="frame" x="264" y="71" width="5" height="150"/>
|
||||
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
|
||||
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<font key="titleFont" metaFont="system"/>
|
||||
</box>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" tag="2" translatesAutoresizingMaskIntoConstraints="NO" id="Vy4-dv-jQB">
|
||||
<rect key="frame" x="18" y="28" width="240" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Copyright © 2016 Kyle Neideck" placeholderString="" id="ctF-95-uVu">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<imageView wantsLayer="YES" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Tui-Hf-FLv">
|
||||
<rect key="frame" x="63" y="108" width="150" height="150"/>
|
||||
<shadow key="shadow">
|
||||
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</shadow>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyUpOrDown" image="FermataIcon" id="dBU-ZS-ZzA"/>
|
||||
</imageView>
|
||||
<imageView wantsLayer="YES" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="R1R-Rd-xPC">
|
||||
<rect key="frame" x="63" y="108" width="150" height="150"/>
|
||||
<shadow key="shadow">
|
||||
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</shadow>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyUpOrDown" image="FermataIcon" id="1VP-dU-RCe"/>
|
||||
</imageView>
|
||||
<scrollView fixedFrame="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eqz-ap-PAC">
|
||||
<rect key="frame" x="301" y="28" width="427" height="220"/>
|
||||
<clipView key="contentView" ambiguous="YES" id="Cdb-RA-YK0">
|
||||
<rect key="frame" x="1" y="1" width="425" height="218"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textView ambiguous="YES" editable="NO" importsGraphics="NO" findStyle="panel" continuousSpellChecking="YES" allowsUndo="YES" usesRuler="YES" usesFontPanel="YES" verticallyResizable="YES" allowsNonContiguousLayout="YES" quoteSubstitution="YES" dashSubstitution="YES" smartInsertDelete="YES" id="LSG-PF-cl8">
|
||||
<rect key="frame" x="0.0" y="0.0" width="425" height="218"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<size key="minSize" width="425" height="218"/>
|
||||
<size key="maxSize" width="477" height="10000000"/>
|
||||
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<size key="minSize" width="425" height="218"/>
|
||||
<size key="maxSize" width="477" height="10000000"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="3jV-aP-AB7">
|
||||
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" verticalHuggingPriority="750" horizontal="NO" id="qCC-lY-zQ6">
|
||||
<rect key="frame" x="410" y="1" width="16" height="218"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
</view>
|
||||
<point key="canvasLocation" x="101" y="211.5"/>
|
||||
</window>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" id="IoN-sN-cCx">
|
||||
<rect key="frame" x="0.0" y="0.0" width="471" height="180"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="mini" sendsActionOnEndEditing="YES" drawsBackground="YES" id="Ay8-8n-FHi">
|
||||
<font key="font" size="14" name="Courier"/>
|
||||
<string key="title">// The version and copyright strings are replaced with
// the values from Info.plist in the setup code. These
// values are just placeholders/fallbacks.
//
// The icon image was being drawn with about 50%
|
||||
// opacity and I couldn't figure out why. The only
// solution I found was to layer two copies of the icon
// on top of each other and add a black shadow (with no
// offset) to both.
</string>
|
||||
<color key="textColor" red="0.11543657067200695" green="0.4338699494949495" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" red="0.99215692281723022" green="0.9960784912109375" blue="0.9960784912109375" alpha="1" colorSpace="deviceRGB"/>
|
||||
</textFieldCell>
|
||||
<point key="canvasLocation" x="101.5" y="496"/>
|
||||
</textField>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="FermataIcon" width="284" height="284"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "appicon_16.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "appicon_32.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "appicon_32.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "appicon_64.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "appicon_128.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "appicon_256.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "appicon_256.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "appicon_512.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "appicon_512.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "appicon_1024.png",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 31 KiB |
BIN
BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_128.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_16.png
Normal file
After Width: | Height: | Size: 540 B |
BIN
BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_256.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_32.png
Normal file
After Width: | Height: | Size: 837 B |
BIN
BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_512.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
BGMApp/BGMApp/Images.xcassets/AppIcon.appiconset/appicon_64.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
6
BGMApp/BGMApp/Images.xcassets/Contents.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
12
BGMApp/BGMApp/Images.xcassets/FermataIcon.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"filename" : "FermataIcon.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
BGMApp/BGMApp/Images.xcassets/FermataIcon.imageset/FermataIcon.pdf
vendored
Normal file
38
BGMApp/BGMApp/Info.plist
Normal file
|
@ -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>AudioHardwarePowerHint</key>
|
||||
<string>None</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2016 Kyle Neideck</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
339
BGMApp/BGMApp/LICENSE
Normal file
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
106
BGMApp/BGMApp/Music Players/BGMMusicPlayer.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMMusicPlayer.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// The base class and protocol for music player apps. Also holds the state of the currently
|
||||
// selected music player.
|
||||
//
|
||||
// To add support for a music player, create a subclass of BGMMusicPlayerBase that implements
|
||||
// BGMMusicPlayerProtocol. BGMSpotify will probably be the most useful example.
|
||||
//
|
||||
// Include the BGM_MUSIC_PLAYER_DEFAULT_LOAD_METHOD macro somewhere in the @implementation block.
|
||||
// You might also want to override the icon method if the default implementation from
|
||||
// BGMMusicPlayerBase doesn't work.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define BGM_MUSIC_PLAYER_ADD_SELF_TO_CLASSES_LIST \
|
||||
[BGMMusicPlayerBase addToMusicPlayerClasses:[self class]];
|
||||
|
||||
#define BGM_MUSIC_PLAYER_DEFAULT_LOAD_METHOD \
|
||||
+ (void) load { \
|
||||
BGM_MUSIC_PLAYER_ADD_SELF_TO_CLASSES_LIST \
|
||||
}
|
||||
|
||||
// Forward declarations (just for the typedef)
|
||||
@class BGMMusicPlayerBase;
|
||||
@protocol BGMMusicPlayerProtocol;
|
||||
|
||||
typedef BGMMusicPlayerBase<BGMMusicPlayerProtocol> BGMMusicPlayer;
|
||||
|
||||
@protocol BGMMusicPlayerProtocol
|
||||
|
||||
@optional
|
||||
// Subclasses usually won't need to implement these unless the music player has no bundle ID.
|
||||
+ (id) initWithPID:(pid_t)pid;
|
||||
+ (id) initWithPIDFromNSNumber:(NSNumber*)pid;
|
||||
+ (id) initWithPIDFromCFNumber:(CFNumberRef)pid;
|
||||
// The pid of each instance of the music player app currently running
|
||||
+ (NSArray<NSNumber*>*) pidsOfRunningInstances;
|
||||
|
||||
@required
|
||||
// The name of the music player, to be used in the UI
|
||||
+ (NSString*) name;
|
||||
|
||||
// The refs returned by the bundleID and pid methods don't need to be released by users, but may be
|
||||
// released by the class/instance at some point (get rule applies).
|
||||
+ (CFStringRef __nullable) bundleID;
|
||||
// Subclasses will usually always return NULL unless they implement the optional methods above.
|
||||
- (CFNumberRef __nullable) pid;
|
||||
|
||||
- (BOOL) isRunning;
|
||||
|
||||
// Pause the music player. Does nothing if the music player is already paused or isn't running.
|
||||
// Returns YES if the music player is paused now but wasn't before, returns NO otherwise.
|
||||
- (BOOL) pause;
|
||||
|
||||
// Unpause the music player. Does nothing if the music player is already playing or isn't running.
|
||||
// Returns YES if the music player is playing now but wasn't before, returns NO otherwise.
|
||||
- (BOOL) unpause;
|
||||
|
||||
- (BOOL) isPlaying;
|
||||
|
||||
- (BOOL) isPaused;
|
||||
|
||||
@end
|
||||
|
||||
@interface BGMMusicPlayerBase : NSObject
|
||||
|
||||
+ (NSArray*) musicPlayerClasses;
|
||||
+ (void) addToMusicPlayerClasses:(Class)musicPlayerClass;
|
||||
|
||||
// The music player currently selected in the preferences menu. (There's no real reason for this to be
|
||||
// global or in this class. I was just trying it out of curiosity.)
|
||||
+ (BGMMusicPlayer*) selectedMusicPlayer;
|
||||
+ (void) setSelectedMusicPlayer:(BGMMusicPlayer*)musicPlayer;
|
||||
|
||||
+ (NSImage* __nullable) icon;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
67
BGMApp/BGMApp/Music Players/BGMMusicPlayer.m
Normal file
|
@ -0,0 +1,67 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMMusicPlayer.m
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
#import "BGMMusicPlayer.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@implementation BGMMusicPlayerBase
|
||||
|
||||
// A array of the subclasses of BGMMusicPlayer
|
||||
static NSArray* sMusicPlayerClasses;
|
||||
|
||||
// The user-selected music player. One of BGMMusicPlayer's subclasses declares itself the default music player by
|
||||
// setting this to an instance of itself in its load method.
|
||||
static BGMMusicPlayer* sSelectedMusicPlayer;
|
||||
|
||||
// Load-time static initializer
|
||||
+ (void) load {
|
||||
sMusicPlayerClasses = @[];
|
||||
}
|
||||
|
||||
+ (void) addToMusicPlayerClasses:(Class)musicPlayerClass {
|
||||
sMusicPlayerClasses = [sMusicPlayerClasses arrayByAddingObject:musicPlayerClass];
|
||||
}
|
||||
|
||||
+ (NSArray*) musicPlayerClasses {
|
||||
return sMusicPlayerClasses;
|
||||
}
|
||||
|
||||
+ (BGMMusicPlayer*) selectedMusicPlayer {
|
||||
NSAssert(sSelectedMusicPlayer != nil, @"One of BGMMusicPlayer's subclasses should set itself as the default \
|
||||
music player (i.e. set sSelectedMusicPlayer) in its initialize method");
|
||||
return sSelectedMusicPlayer;
|
||||
}
|
||||
|
||||
+ (void) setSelectedMusicPlayer:(BGMMusicPlayer*)musicPlayer {
|
||||
sSelectedMusicPlayer = musicPlayer;
|
||||
}
|
||||
|
||||
+ (NSImage*) icon {
|
||||
NSString* bundleID = (__bridge NSString*)[(id<BGMMusicPlayerProtocol>)self bundleID];
|
||||
NSString* bundlePath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:bundleID];
|
||||
return bundlePath == nil ? nil : [[NSWorkspace sharedWorkspace] iconForFile:bundlePath];
|
||||
}
|
||||
|
||||
@end
|
||||
|
30
BGMApp/BGMApp/Music Players/BGMSpotify.h
Normal file
|
@ -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/>.
|
||||
|
||||
//
|
||||
// BGMSpotify.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Superclass/Protocol Import
|
||||
#import "BGMMusicPlayer.h"
|
||||
|
||||
|
||||
@interface BGMSpotify : BGMMusicPlayer
|
||||
|
||||
@end
|
||||
|
101
BGMApp/BGMApp/Music Players/BGMSpotify.m
Normal file
|
@ -0,0 +1,101 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMSpotify.m
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// Spotify's AppleScript API looks to have been designed to match iTunes', so this file is basically
|
||||
// just s/iTunes/Spotify/ on BGMiTunes.m
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#import "BGMSpotify.h"
|
||||
|
||||
// Auto-generated Scripting Bridge header
|
||||
#import "Spotify.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
@implementation BGMSpotify {
|
||||
SpotifyApplication* spotify;
|
||||
}
|
||||
|
||||
BGM_MUSIC_PLAYER_DEFAULT_LOAD_METHOD
|
||||
|
||||
+ (NSString*) name {
|
||||
return @"Spotify";
|
||||
}
|
||||
|
||||
- (id) init {
|
||||
if ((self = [super init])) {
|
||||
spotify = [SBApplication applicationWithBundleIdentifier:(__bridge NSString*)[[self class] bundleID]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CFNumberRef) pid {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+ (CFStringRef) bundleID {
|
||||
return CFSTR("com.spotify.client");
|
||||
}
|
||||
|
||||
- (BOOL) isRunning {
|
||||
return [spotify 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("BGMSpotify::pause: Pausing Spotify");
|
||||
[spotify 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("BGMSpotify::unpause: Unpausing Spotify");
|
||||
[spotify playpause];
|
||||
}
|
||||
|
||||
return wasPaused;
|
||||
}
|
||||
|
||||
- (BOOL) isPlaying {
|
||||
return [self isRunning] && [spotify playerState] == SpotifyEPlSPlaying;
|
||||
}
|
||||
|
||||
- (BOOL) isPaused {
|
||||
return [self isRunning] && [spotify playerState] == SpotifyEPlSPaused;
|
||||
}
|
||||
|
||||
@end
|
||||
|
31
BGMApp/BGMApp/Music Players/BGMiTunes.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMiTunes.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Superclass/Protocol Import
|
||||
#import "BGMMusicPlayer.h"
|
||||
|
||||
|
||||
@interface BGMiTunes : BGMMusicPlayer
|
||||
|
||||
@end
|
||||
|
||||
|
103
BGMApp/BGMApp/Music Players/BGMiTunes.m
Normal file
|
@ -0,0 +1,103 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMiTunes.m
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#import "BGMiTunes.h"
|
||||
|
||||
// Auto-generated Scripting Bridge header
|
||||
#import "iTunes.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#undef CoreAudio_ThreadStampMessages
|
||||
#define CoreAudio_ThreadStampMessages 0 // Requires C++
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
@implementation BGMiTunes {
|
||||
iTunesApplication* iTunes;
|
||||
}
|
||||
|
||||
+ (void) load {
|
||||
BGM_MUSIC_PLAYER_ADD_SELF_TO_CLASSES_LIST
|
||||
|
||||
// iTunes is selected as the music player when the user hasn't changed the setting yet
|
||||
[self setSelectedMusicPlayer:[BGMiTunes new]];
|
||||
}
|
||||
|
||||
+ (NSString*) name {
|
||||
return @"iTunes";
|
||||
}
|
||||
|
||||
- (id) init {
|
||||
if ((self = [super init])) {
|
||||
iTunes = [SBApplication applicationWithBundleIdentifier:(__bridge NSString*)[[self class] bundleID]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CFNumberRef) pid {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+ (CFStringRef) bundleID {
|
||||
return CFSTR("com.apple.iTunes");
|
||||
}
|
||||
|
||||
- (BOOL) isRunning {
|
||||
return [iTunes 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("BGMiTunes::pause: Pausing iTunes");
|
||||
[iTunes 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("BGMiTunes::unpause: Unpausing iTunes");
|
||||
[iTunes playpause];
|
||||
}
|
||||
|
||||
return wasPaused;
|
||||
}
|
||||
|
||||
- (BOOL) isPlaying {
|
||||
return [self isRunning] && [iTunes playerState] == iTunesEPlSPlaying;
|
||||
}
|
||||
|
||||
- (BOOL) isPaused {
|
||||
return [self isRunning] && [iTunes playerState] == iTunesEPlSPaused;
|
||||
}
|
||||
|
||||
@end
|
||||
|
40
BGMApp/BGMApp/Preferences/BGMAutoPauseMusicPrefs.h
Normal file
|
@ -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/>.
|
||||
|
||||
//
|
||||
// BGMAutoPauseMusicPrefs.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "BGMAudioDeviceManager.h"
|
||||
|
||||
// System Includes
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
|
||||
@interface BGMAutoPauseMusicPrefs : NSObject
|
||||
|
||||
// Note that toggleAutoPauseMusicMenuItem is the item in the main menu that enables/disables auto-pausing, rather than the
|
||||
// disabled "Auto-pause" menu item in the preferences menu that acts as a section heading. This class updates the text of
|
||||
// toggleAutoPauseMusicMenuItem when the user changes the music player.
|
||||
- (id) initWithPreferencesMenu:(NSMenu*)inPrefsMenu
|
||||
toggleAutoPauseMusicMenuItem:(NSMenuItem*)inToggleAutoPauseMusicMenuItem
|
||||
audioDevices:(BGMAudioDeviceManager*)inAudioDevices;
|
||||
|
||||
@end
|
||||
|
205
BGMApp/BGMApp/Preferences/BGMAutoPauseMusicPrefs.mm
Normal file
|
@ -0,0 +1,205 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMAutoPauseMusicPrefs.mm
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Includes
|
||||
#import "BGMAutoPauseMusicPrefs.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
#import "BGMMusicPlayer.h"
|
||||
|
||||
|
||||
static NSString* const kToggleAutoPauseMusicMenuItemTitleFormat = @"Auto-pause %@";
|
||||
static float const kMenuItemIconScalingFactor = 1.15f;
|
||||
static NSInteger const kPrefsMenuAutoPauseHeaderTag = 1;
|
||||
|
||||
@implementation BGMAutoPauseMusicPrefs {
|
||||
BGMAudioDeviceManager* audioDevices;
|
||||
NSMenuItem* toggleAutoPauseMusicMenuItem;
|
||||
NSMenu* prefsMenu;
|
||||
NSArray<NSMenuItem*>* musicPlayerMenuItems;
|
||||
}
|
||||
|
||||
- (id) initWithPreferencesMenu:(NSMenu*)inPrefsMenu
|
||||
toggleAutoPauseMusicMenuItem:(NSMenuItem*)inToggleAutoPauseMusicMenuItem
|
||||
audioDevices:(BGMAudioDeviceManager*)inAudioDevices {
|
||||
if ((self = [super init])) {
|
||||
prefsMenu = inPrefsMenu;
|
||||
toggleAutoPauseMusicMenuItem = inToggleAutoPauseMusicMenuItem;
|
||||
audioDevices = inAudioDevices;
|
||||
musicPlayerMenuItems = @[];
|
||||
|
||||
[self initSelectedMusicPlayer];
|
||||
[self initMenuSection];
|
||||
[self updateMenuItemTitle];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (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,
|
||||
// we might as well save the auto-pause setting on the driver as well just so all the settings are saved in the same place.
|
||||
|
||||
// Get the currently selected music player from the driver and update the global in BGMMusicPlayerBase
|
||||
|
||||
// The bundle ID and PID set on the driver
|
||||
CFNumberRef selectedPID = static_cast<CFNumberRef>([audioDevices bgmDevice].GetPropertyData_CFType(kBGMMusicPlayerProcessIDAddress));
|
||||
CFStringRef selectedBundleID = [audioDevices bgmDevice].GetPropertyData_CFString(kBGMMusicPlayerBundleIDAddress);
|
||||
|
||||
DebugMsg("BGMAutoPauseMusicPrefs::initSelectedMusicPlayer: Music player on BGMDriver: bundleID=%s PID=%s",
|
||||
selectedBundleID == NULL ? "null" : CFStringGetCStringPtr(selectedBundleID, kCFStringEncodingUTF8),
|
||||
selectedPID == NULL ? "null" : [[(__bridge NSNumber*)selectedPID stringValue] UTF8String]);
|
||||
|
||||
// If no music player is set on the driver, set it to the one set in the app and return
|
||||
if ((selectedBundleID == NULL || CFEqual(selectedBundleID, CFSTR(""))) &&
|
||||
(selectedPID == NULL || [(__bridge NSNumber*)selectedPID intValue] < 1)) {
|
||||
[self updateBGMDevice];
|
||||
return;
|
||||
}
|
||||
|
||||
// The IDs set in the app, which will be updated if they don't match the values from the driver
|
||||
CFNumberRef selectedPIDInBGMApp = [[BGMMusicPlayerBase selectedMusicPlayer] pid];
|
||||
CFStringRef selectedBundleIDInBGMApp = [[[BGMMusicPlayerBase selectedMusicPlayer] class] bundleID];
|
||||
|
||||
// Return early if the music player selected in the app already matches the driver
|
||||
if ((selectedPID != NULL && selectedPIDInBGMApp != NULL && CFEqual(selectedPID, selectedPIDInBGMApp)) ||
|
||||
(selectedBundleID != NULL && selectedBundleIDInBGMApp != NULL && CFEqual(selectedBundleID, selectedBundleIDInBGMApp))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check each selectable music player
|
||||
for (Class mpClass in [BGMMusicPlayerBase musicPlayerClasses]) {
|
||||
// Look for a running instance of the music player by PID
|
||||
if (selectedPID != NULL &&
|
||||
[mpClass respondsToSelector:@selector(pidsOfRunningInstances)] &&
|
||||
[mpClass respondsToSelector:@selector(initWithPIDFromNSNumber:)]) {
|
||||
NSArray<NSNumber*>* mpPIDs = [mpClass pidsOfRunningInstances];
|
||||
for (NSNumber* mpPID in mpPIDs) {
|
||||
if (CFEqual((__bridge CFNumberRef)mpPID, selectedPID)) {
|
||||
DebugMsg("BGMAutoPauseMusicPrefs::initSelectedMusicPlayer: Selected music player on driver was %s (found by pid)",
|
||||
[[mpClass name] UTF8String]);
|
||||
[BGMMusicPlayerBase setSelectedMusicPlayer:[[mpClass alloc] initWithPIDFromNSNumber:mpPID]];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check by bundle ID
|
||||
CFStringRef mpBundleID = [mpClass bundleID];
|
||||
if (selectedBundleID != NULL &&
|
||||
mpBundleID != NULL &&
|
||||
CFEqual(mpBundleID, selectedBundleID)) {
|
||||
// Found the selected music player. Update the app to match the driver and return.
|
||||
DebugMsg("BGMAutoPauseMusicPrefs::initSelectedMusicPlayer: Selected music player on driver was %s",
|
||||
[[mpClass name] UTF8String]);
|
||||
|
||||
[BGMMusicPlayerBase setSelectedMusicPlayer:[mpClass new]];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) initMenuSection {
|
||||
// Add the menu items related to auto-pausing music to the settings submenu
|
||||
|
||||
// The index to start inserting music player menu items at
|
||||
NSInteger musicPlayerItemsIndex = [prefsMenu indexOfItemWithTag:kPrefsMenuAutoPauseHeaderTag] + 1;
|
||||
|
||||
// Insert the options to change the music player app
|
||||
for (Class musicPlayerClass in [BGMMusicPlayerBase musicPlayerClasses]) {
|
||||
NSMenuItem* menuItem = [prefsMenu insertItemWithTitle:[musicPlayerClass name]
|
||||
action:@selector(handleMusicPlayerChange:)
|
||||
keyEquivalent:@""
|
||||
atIndex:musicPlayerItemsIndex];
|
||||
|
||||
musicPlayerMenuItems = [musicPlayerMenuItems arrayByAddingObject:menuItem];
|
||||
|
||||
// Create an instance for this music player and associate it with the menu item
|
||||
[menuItem setRepresentedObject:[musicPlayerClass new]];
|
||||
|
||||
// Show the default music player as selected
|
||||
if (musicPlayerClass == [[BGMMusicPlayerBase selectedMusicPlayer] class]) {
|
||||
[menuItem setState:NSOnState];
|
||||
}
|
||||
|
||||
// Set the item's icon
|
||||
NSImage* icon = [musicPlayerClass icon];
|
||||
if (icon == nil) {
|
||||
// Set a blank icon so the text lines up
|
||||
icon = [NSImage new];
|
||||
}
|
||||
// Size the icon relative to the size of the item's text
|
||||
CGFloat length = [[NSFont menuBarFontOfSize:0] pointSize] * kMenuItemIconScalingFactor;
|
||||
[icon setSize:NSMakeSize(length, length)];
|
||||
[menuItem setImage:icon];
|
||||
|
||||
[menuItem setTarget:self];
|
||||
[menuItem setIndentationLevel:1];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) handleMusicPlayerChange:(NSMenuItem*)sender {
|
||||
// Set the new music player as the selected music player
|
||||
BGMMusicPlayer* musicPlayer = [sender representedObject];
|
||||
assert(musicPlayer != nil);
|
||||
[BGMMusicPlayerBase setSelectedMusicPlayer:musicPlayer];
|
||||
|
||||
// Select/Deselect the menu items
|
||||
for (NSMenuItem* item in musicPlayerMenuItems) {
|
||||
BOOL isNewlySelectedMusicPlayer = item == sender;
|
||||
[item setState:(isNewlySelectedMusicPlayer ? NSOnState : NSOffState)];
|
||||
}
|
||||
|
||||
[self updateMenuItemTitle];
|
||||
[self updateBGMDevice];
|
||||
}
|
||||
|
||||
- (void) updateBGMDevice {
|
||||
// Send the music player's PID or bundle ID to the driver
|
||||
|
||||
DebugMsg("BGMAutoPauseMusicPrefs::updateBGMDevice: Setting the music player to %s on the driver",
|
||||
[[[[BGMMusicPlayer selectedMusicPlayer] class] name] UTF8String]);
|
||||
|
||||
CFNumberRef __nullable pid = [[BGMMusicPlayer selectedMusicPlayer] pid];
|
||||
|
||||
if (pid != NULL) {
|
||||
[audioDevices bgmDevice].SetPropertyData_CFType(kBGMMusicPlayerProcessIDAddress, pid);
|
||||
} else {
|
||||
CFStringRef __nullable bundleID = [[[BGMMusicPlayer selectedMusicPlayer] class] bundleID];
|
||||
|
||||
if (bundleID != NULL) {
|
||||
[audioDevices bgmDevice].SetPropertyData_CFString(kBGMMusicPlayerBundleIDAddress, bundleID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) updateMenuItemTitle {
|
||||
// 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];
|
||||
}
|
||||
|
||||
@end
|
||||
|
36
BGMApp/BGMApp/Preferences/BGMOutputDevicePrefs.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMOutputDevicePrefs.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Local Includes
|
||||
#import "BGMAudioDeviceManager.h"
|
||||
|
||||
// System Includes
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
|
||||
@interface BGMOutputDevicePrefs : NSObject
|
||||
|
||||
- (id) initWithAudioDevices:(BGMAudioDeviceManager*)inAudioDevices;
|
||||
- (void) populatePreferencesMenu:(NSMenu*)prefsMenu;
|
||||
|
||||
@end
|
||||
|
107
BGMApp/BGMApp/Preferences/BGMOutputDevicePrefs.mm
Normal file
|
@ -0,0 +1,107 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMOutputDevicePrefs.mm
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#import "BGMOutputDevicePrefs.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAHALAudioSystemObject.h"
|
||||
#include "CAHALAudioDevice.h"
|
||||
#include "CAAutoDisposer.h"
|
||||
|
||||
|
||||
static NSInteger const kOutputDeviceMenuItemTag = 2;
|
||||
|
||||
@implementation BGMOutputDevicePrefs {
|
||||
BGMAudioDeviceManager* audioDevices;
|
||||
NSMutableArray<NSMenuItem*>* outputDeviceMenuItems;
|
||||
}
|
||||
|
||||
- (id) initWithAudioDevices:(BGMAudioDeviceManager*)inAudioDevices {
|
||||
if ((self = [super init])) {
|
||||
audioDevices = inAudioDevices;
|
||||
outputDeviceMenuItems = [NSMutableArray new];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) populatePreferencesMenu:(NSMenu*)prefsMenu {
|
||||
// Remove existing menu items
|
||||
for (NSMenuItem* item in outputDeviceMenuItems) {
|
||||
[prefsMenu removeItem:item];
|
||||
}
|
||||
[outputDeviceMenuItems removeAllObjects];
|
||||
|
||||
// Insert menu items after the item for the "Output Device" heading
|
||||
const NSInteger menuItemsIdx = [prefsMenu indexOfItemWithTag:kOutputDeviceMenuItemTag] + 1;
|
||||
|
||||
// Add a menu item for each output device
|
||||
CAHALAudioSystemObject audioSystem;
|
||||
UInt32 numDevices = audioSystem.GetNumberAudioDevices();
|
||||
if (numDevices > 0) {
|
||||
CAAutoArrayDelete<AudioObjectID> devices(numDevices);
|
||||
audioSystem.GetAudioDevices(numDevices, devices);
|
||||
|
||||
for (UInt32 i = 0; i < numDevices; i++) {
|
||||
CAHALAudioDevice device(devices[i]);
|
||||
BOOL hasOutputChannels = device.GetTotalNumberChannels(/* inIsInput = */ false) > 0;
|
||||
|
||||
if (device.GetObjectID() != [audioDevices bgmDevice].GetObjectID() && hasOutputChannels) {
|
||||
NSString* deviceName = CFBridgingRelease(device.CopyName());
|
||||
NSMenuItem* item = [[NSMenuItem alloc] initWithTitle:deviceName
|
||||
action:@selector(outputDeviceWasChanged:)
|
||||
keyEquivalent:@""];
|
||||
|
||||
BOOL isSelected = [audioDevices isOutputDevice:device.GetObjectID()];
|
||||
[item setState:(isSelected ? NSOnState : NSOffState)];
|
||||
[item setTarget:self];
|
||||
[item setIndentationLevel:1];
|
||||
[item setRepresentedObject:[NSNumber numberWithUnsignedInt:device.GetObjectID()]];
|
||||
|
||||
[prefsMenu insertItem:item atIndex:menuItemsIdx];
|
||||
|
||||
[outputDeviceMenuItems addObject:item];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) outputDeviceWasChanged:(NSMenuItem*)menuItem {
|
||||
BOOL success = [audioDevices setOutputDeviceWithID:[[menuItem representedObject] unsignedIntValue] revertOnFailure:YES];
|
||||
|
||||
if (!success) {
|
||||
// Couldn't change the output device, so show a warning and change the menu selection back
|
||||
NSAlert* alert = [NSAlert new];
|
||||
NSString* deviceName = [menuItem title];
|
||||
[alert setMessageText:[NSString stringWithFormat:@"Failed to set %@ as the output device", deviceName]];
|
||||
[alert setInformativeText:@"This is probably a bug. Feel free to report it."];
|
||||
[alert runModal];
|
||||
|
||||
[menuItem setState:NSOffState];
|
||||
|
||||
// TODO: Reselect previous menu item
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
45
BGMApp/BGMApp/Preferences/BGMPreferencesMenu.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMPreferencesMenu.h
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// Handles the preferences menu UI. The user's preference changes are often passed directly to the driver rather
|
||||
// than to other BGMApp classes.
|
||||
//
|
||||
|
||||
// Local Includes
|
||||
#import "BGMAudioDeviceManager.h"
|
||||
|
||||
// System Includes
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface BGMPreferencesMenu : NSObject <NSMenuDelegate>
|
||||
|
||||
- (id) initWithbgmMenu:(NSMenu*)inBGMMenu
|
||||
audioDevices:(BGMAudioDeviceManager*)inAudioDevices
|
||||
aboutPanel:(NSPanel*)inAboutPanel
|
||||
aboutPanelLicenseView:(NSTextView*)inAboutPanelLicenseView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
131
BGMApp/BGMApp/Preferences/BGMPreferencesMenu.mm
Normal file
|
@ -0,0 +1,131 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMPreferencesMenu.mm
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#import "BGMPreferencesMenu.h"
|
||||
|
||||
// Local Includes
|
||||
#import "BGMAutoPauseMusicPrefs.h"
|
||||
#import "BGMOutputDevicePrefs.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// Interface Builder tags
|
||||
static NSInteger const kToggleAutoPauseMusicMenuItemTag = 2;
|
||||
|
||||
static NSInteger const kPreferencesMenuItemTag = 1;
|
||||
static NSInteger const kAboutPanelMenuItemTag = 3;
|
||||
|
||||
static NSInteger const kAboutPanelVersionLabelTag = 1;
|
||||
static NSInteger const kAboutPanelCopyrightLabelTag = 2;
|
||||
|
||||
@implementation BGMPreferencesMenu {
|
||||
BGMAudioDeviceManager* audioDevices;
|
||||
|
||||
// Menu sections
|
||||
BGMAutoPauseMusicPrefs* autoPauseMusicPrefs;
|
||||
BGMOutputDevicePrefs* outputDevicePrefs;
|
||||
|
||||
// About Background Music window
|
||||
NSPanel* aboutPanel;
|
||||
NSTextField* aboutPanelVersionLabel;
|
||||
NSTextField* aboutPanelCopyrightLabel;
|
||||
NSTextView* aboutPanelLicenceView;
|
||||
}
|
||||
|
||||
- (id) initWithbgmMenu:(NSMenu*)inBGMMenu
|
||||
audioDevices:(BGMAudioDeviceManager*)inAudioDevices
|
||||
aboutPanel:(NSPanel*)inAboutPanel
|
||||
aboutPanelLicenseView:(NSTextView*)inAboutPanelLicenseView {
|
||||
if ((self = [super init])) {
|
||||
audioDevices = inAudioDevices;
|
||||
aboutPanel = inAboutPanel;
|
||||
|
||||
aboutPanelVersionLabel = [[aboutPanel contentView] viewWithTag:kAboutPanelVersionLabelTag];
|
||||
aboutPanelCopyrightLabel = [[aboutPanel contentView] viewWithTag:kAboutPanelCopyrightLabelTag];
|
||||
aboutPanelLicenceView = inAboutPanelLicenseView;
|
||||
|
||||
NSMenu* prefsMenu = [[inBGMMenu itemWithTag:kPreferencesMenuItemTag] submenu];
|
||||
[prefsMenu setDelegate:self];
|
||||
|
||||
NSMenuItem* toggleAutoPauseMusicMenuItem = [inBGMMenu itemWithTag:kToggleAutoPauseMusicMenuItemTag];
|
||||
autoPauseMusicPrefs = [[BGMAutoPauseMusicPrefs alloc] initWithPreferencesMenu:prefsMenu
|
||||
toggleAutoPauseMusicMenuItem:toggleAutoPauseMusicMenuItem
|
||||
audioDevices:audioDevices];
|
||||
|
||||
outputDevicePrefs = [[BGMOutputDevicePrefs alloc] initWithAudioDevices:audioDevices];
|
||||
|
||||
// Set up the "About Background Music" menu item
|
||||
NSMenuItem* aboutMenuItem = [prefsMenu itemWithTag:kAboutPanelMenuItemTag];
|
||||
[aboutMenuItem setTarget:self];
|
||||
[aboutMenuItem setAction:@selector(showAboutPanel)];
|
||||
|
||||
[self initAboutPanel];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) initAboutPanel {
|
||||
// Set up the About Background Music window
|
||||
|
||||
NSBundle* bundle = [NSBundle mainBundle];
|
||||
|
||||
if (bundle == nil) {
|
||||
LogWarning("Background Music: BGMPreferencesMenu::initAboutPanel: Could not find main bundle");
|
||||
} else {
|
||||
// Version number label
|
||||
NSString* version = [[bundle infoDictionary] objectForKey:@"CFBundleShortVersionString"];
|
||||
[aboutPanelVersionLabel setStringValue:[NSString stringWithFormat:@"Version %@", version]];
|
||||
|
||||
// Copyright notice label
|
||||
NSString* copyrightNotice = [[bundle infoDictionary] objectForKey:@"NSHumanReadableCopyright"];
|
||||
[aboutPanelCopyrightLabel setStringValue:copyrightNotice];
|
||||
|
||||
// Load the text of the license into the text view
|
||||
NSString* licensePath = [bundle pathForResource:@"LICENSE" ofType:nil];
|
||||
NSError* err;
|
||||
NSString* licenseStr = [NSString stringWithContentsOfFile:licensePath encoding:NSASCIIStringEncoding error:&err];
|
||||
if (err != nil || [licenseStr isEqualToString:@""]) {
|
||||
NSLog(@"Error loading license file: %@", err);
|
||||
licenseStr = @"Error: could not open license file.";
|
||||
}
|
||||
[aboutPanelLicenceView setString:licenseStr];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) menuNeedsUpdate:(NSMenu*)menu {
|
||||
[outputDevicePrefs populatePreferencesMenu:menu];
|
||||
}
|
||||
|
||||
- (void) showAboutPanel {
|
||||
DebugMsg("BGMPreferencesMenu::showAboutPanel: Opening \"About Background Music\" panel");
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
[aboutPanel setIsVisible:YES];
|
||||
[aboutPanel makeKeyAndOrderFront:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
82
BGMApp/BGMApp/Spotify.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Spotify.h
|
||||
*
|
||||
* Generated with
|
||||
* sdef /Applications/Spotify.app | sdp -fh --basename Spotify
|
||||
*/
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <ScriptingBridge/ScriptingBridge.h>
|
||||
|
||||
|
||||
@class SpotifyApplication, SpotifyTrack, SpotifyApplication;
|
||||
|
||||
enum SpotifyEPlS {
|
||||
SpotifyEPlSStopped = 'kPSS',
|
||||
SpotifyEPlSPlaying = 'kPSP',
|
||||
SpotifyEPlSPaused = 'kPSp'
|
||||
};
|
||||
typedef enum SpotifyEPlS SpotifyEPlS;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Spotify Suite
|
||||
*/
|
||||
|
||||
// The Spotify application.
|
||||
@interface SpotifyApplication : SBApplication
|
||||
|
||||
@property (copy, readonly) SpotifyTrack *currentTrack; // The current playing track.
|
||||
@property NSInteger soundVolume; // The sound output volume (0 = minimum, 100 = maximum)
|
||||
@property (readonly) SpotifyEPlS playerState; // Is Spotify stopped, paused, or playing?
|
||||
@property double playerPosition; // The player’s position within the currently playing track in seconds.
|
||||
@property (readonly) BOOL repeatingEnabled; // Is repeating enabled in the current playback context?
|
||||
@property BOOL repeating; // Is repeating on or off?
|
||||
@property (readonly) BOOL shufflingEnabled; // Is shuffling enabled in the current playback context?
|
||||
@property BOOL shuffling; // Is shuffling on or off?
|
||||
|
||||
- (void) nextTrack; // Skip to the next track.
|
||||
- (void) previousTrack; // Skip to the previous track.
|
||||
- (void) playpause; // Toggle play/pause.
|
||||
- (void) pause; // Pause playback.
|
||||
- (void) play; // Resume playback.
|
||||
- (void) playTrack:(NSString *)x inContext:(NSString *)inContext; // Start playback of a track in the given context.
|
||||
|
||||
@end
|
||||
|
||||
// A Spotify track.
|
||||
@interface SpotifyTrack : SBObject
|
||||
|
||||
@property (copy, readonly) NSString *artist; // The artist of the track.
|
||||
@property (copy, readonly) NSString *album; // The album of the track.
|
||||
@property (readonly) NSInteger discNumber; // The disc number of the track.
|
||||
@property (readonly) NSInteger duration; // The length of the track in seconds.
|
||||
@property (readonly) NSInteger playedCount; // The number of times this track has been played.
|
||||
@property (readonly) NSInteger trackNumber; // The index of the track in its album.
|
||||
@property (readonly) BOOL starred; // Is the track starred?
|
||||
@property (readonly) NSInteger popularity; // How popular is this track? 0-100
|
||||
- (NSString *) id; // The ID of the item.
|
||||
@property (copy, readonly) NSString *name; // The name of the track.
|
||||
@property (copy, readonly) NSImage *artwork; // The track's album cover.
|
||||
@property (copy, readonly) NSString *albumArtist; // That album artist of the track.
|
||||
@property (copy) NSString *spotifyUrl; // The URL of the track.
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Standard Suite
|
||||
*/
|
||||
|
||||
// The application's top level scripting object.
|
||||
@interface SpotifyApplication (StandardSuite)
|
||||
|
||||
@property (copy, readonly) NSString *name; // The name of the application.
|
||||
@property (readonly) BOOL frontmost; // Is this the frontmost (active) application?
|
||||
@property (copy, readonly) NSString *version; // The version of the application.
|
||||
|
||||
@end
|
||||
|
528
BGMApp/BGMApp/iTunes.h
Normal file
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
* iTunes.h
|
||||
*
|
||||
* Generated with
|
||||
* sdef /Applications/iTunes.app | sdp -fh --basename iTunes
|
||||
*/
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <ScriptingBridge/ScriptingBridge.h>
|
||||
|
||||
|
||||
@class iTunesPrintSettings, iTunesApplication, iTunesItem, iTunesAirPlayDevice, iTunesArtwork, iTunesEncoder, iTunesEQPreset, iTunesPlaylist, iTunesAudioCDPlaylist, iTunesLibraryPlaylist, iTunesRadioTunerPlaylist, iTunesSource, iTunesTrack, iTunesAudioCDTrack, iTunesFileTrack, iTunesSharedTrack, iTunesURLTrack, iTunesUserPlaylist, iTunesFolderPlaylist, iTunesVisual, iTunesWindow, iTunesBrowserWindow, iTunesEQWindow, iTunesPlaylistWindow;
|
||||
|
||||
enum iTunesEKnd {
|
||||
iTunesEKndTrackListing = 'kTrk' /* a basic listing of tracks within a playlist */,
|
||||
iTunesEKndAlbumListing = 'kAlb' /* a listing of a playlist grouped by album */,
|
||||
iTunesEKndCdInsert = 'kCDi' /* a printout of the playlist for jewel case inserts */
|
||||
};
|
||||
typedef enum iTunesEKnd iTunesEKnd;
|
||||
|
||||
enum iTunesEnum {
|
||||
iTunesEnumStandard = 'lwst' /* Standard PostScript error handling */,
|
||||
iTunesEnumDetailed = 'lwdt' /* print a detailed report of PostScript errors */
|
||||
};
|
||||
typedef enum iTunesEnum iTunesEnum;
|
||||
|
||||
enum iTunesEPlS {
|
||||
iTunesEPlSStopped = 'kPSS',
|
||||
iTunesEPlSPlaying = 'kPSP',
|
||||
iTunesEPlSPaused = 'kPSp',
|
||||
iTunesEPlSFastForwarding = 'kPSF',
|
||||
iTunesEPlSRewinding = 'kPSR'
|
||||
};
|
||||
typedef enum iTunesEPlS iTunesEPlS;
|
||||
|
||||
enum iTunesERpt {
|
||||
iTunesERptOff = 'kRpO',
|
||||
iTunesERptOne = 'kRp1',
|
||||
iTunesERptAll = 'kAll'
|
||||
};
|
||||
typedef enum iTunesERpt iTunesERpt;
|
||||
|
||||
enum iTunesEVSz {
|
||||
iTunesEVSzSmall = 'kVSS',
|
||||
iTunesEVSzMedium = 'kVSM',
|
||||
iTunesEVSzLarge = 'kVSL'
|
||||
};
|
||||
typedef enum iTunesEVSz iTunesEVSz;
|
||||
|
||||
enum iTunesESrc {
|
||||
iTunesESrcLibrary = 'kLib',
|
||||
iTunesESrcIPod = 'kPod',
|
||||
iTunesESrcAudioCD = 'kACD',
|
||||
iTunesESrcMP3CD = 'kMCD',
|
||||
iTunesESrcRadioTuner = 'kTun',
|
||||
iTunesESrcSharedLibrary = 'kShd',
|
||||
iTunesESrcUnknown = 'kUnk'
|
||||
};
|
||||
typedef enum iTunesESrc iTunesESrc;
|
||||
|
||||
enum iTunesESrA {
|
||||
iTunesESrAAlbums = 'kSrL' /* albums only */,
|
||||
iTunesESrAAll = 'kAll' /* all text fields */,
|
||||
iTunesESrAArtists = 'kSrR' /* artists only */,
|
||||
iTunesESrAComposers = 'kSrC' /* composers only */,
|
||||
iTunesESrADisplayed = 'kSrV' /* visible text fields */,
|
||||
iTunesESrASongs = 'kSrS' /* song names only */
|
||||
};
|
||||
typedef enum iTunesESrA iTunesESrA;
|
||||
|
||||
enum iTunesESpK {
|
||||
iTunesESpKNone = 'kNon',
|
||||
iTunesESpKBooks = 'kSpA',
|
||||
iTunesESpKFolder = 'kSpF',
|
||||
iTunesESpKGenius = 'kSpG',
|
||||
iTunesESpKITunesU = 'kSpU',
|
||||
iTunesESpKLibrary = 'kSpL',
|
||||
iTunesESpKMovies = 'kSpI',
|
||||
iTunesESpKMusic = 'kSpZ',
|
||||
iTunesESpKPodcasts = 'kSpP',
|
||||
iTunesESpKPurchasedMusic = 'kSpM',
|
||||
iTunesESpKTVShows = 'kSpT'
|
||||
};
|
||||
typedef enum iTunesESpK iTunesESpK;
|
||||
|
||||
enum iTunesEVdK {
|
||||
iTunesEVdKNone = 'kNon' /* not a video or unknown video kind */,
|
||||
iTunesEVdKHomeVideo = 'kVdH' /* home video track */,
|
||||
iTunesEVdKMovie = 'kVdM' /* movie track */,
|
||||
iTunesEVdKMusicVideo = 'kVdV' /* music video track */,
|
||||
iTunesEVdKTVShow = 'kVdT' /* TV show track */
|
||||
};
|
||||
typedef enum iTunesEVdK iTunesEVdK;
|
||||
|
||||
enum iTunesERtK {
|
||||
iTunesERtKUser = 'kRtU' /* user-specified rating */,
|
||||
iTunesERtKComputed = 'kRtC' /* iTunes-computed rating */
|
||||
};
|
||||
typedef enum iTunesERtK iTunesERtK;
|
||||
|
||||
enum iTunesEAPD {
|
||||
iTunesEAPDComputer = 'kAPC',
|
||||
iTunesEAPDAirPortExpress = 'kAPX',
|
||||
iTunesEAPDAppleTV = 'kAPT',
|
||||
iTunesEAPDAirPlayDevice = 'kAPO',
|
||||
iTunesEAPDUnknown = 'kAPU'
|
||||
};
|
||||
typedef enum iTunesEAPD iTunesEAPD;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Standard Suite
|
||||
*/
|
||||
|
||||
@interface iTunesPrintSettings : SBObject
|
||||
|
||||
@property (readonly) NSInteger copies; // the number of copies of a document to be printed
|
||||
@property (readonly) BOOL collating; // Should printed copies be collated?
|
||||
@property (readonly) NSInteger startingPage; // the first page of the document to be printed
|
||||
@property (readonly) NSInteger endingPage; // the last page of the document to be printed
|
||||
@property (readonly) NSInteger pagesAcross; // number of logical pages laid across a physical page
|
||||
@property (readonly) NSInteger pagesDown; // number of logical pages laid out down a physical page
|
||||
@property (readonly) iTunesEnum errorHandling; // how errors are handled
|
||||
@property (copy, readonly) NSDate *requestedPrintTime; // the time at which the desktop printer should print the document
|
||||
@property (copy, readonly) NSArray *printerFeatures; // printer specific options
|
||||
@property (copy, readonly) NSString *faxNumber; // for fax number
|
||||
@property (copy, readonly) NSString *targetPrinter; // for target printer
|
||||
|
||||
- (void) printPrintDialog:(BOOL)printDialog withProperties:(iTunesPrintSettings *)withProperties kind:(iTunesEKnd)kind theme:(NSString *)theme; // Print the specified object(s)
|
||||
- (void) close; // Close an object
|
||||
- (void) delete; // Delete an element from an object
|
||||
- (SBObject *) duplicateTo:(SBObject *)to; // Duplicate one or more object(s)
|
||||
- (BOOL) exists; // Verify if an object exists
|
||||
- (void) open; // open the specified object(s)
|
||||
- (void) playOnce:(BOOL)once; // play the current track or the specified track or file.
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* iTunes Suite
|
||||
*/
|
||||
|
||||
// The application program
|
||||
@interface iTunesApplication : SBApplication
|
||||
|
||||
- (SBElementArray *) AirPlayDevices;
|
||||
- (SBElementArray *) browserWindows;
|
||||
- (SBElementArray *) encoders;
|
||||
- (SBElementArray *) EQPresets;
|
||||
- (SBElementArray *) EQWindows;
|
||||
- (SBElementArray *) playlistWindows;
|
||||
- (SBElementArray *) sources;
|
||||
- (SBElementArray *) visuals;
|
||||
- (SBElementArray *) windows;
|
||||
|
||||
@property (readonly) BOOL AirPlayEnabled; // is AirPlay currently enabled?
|
||||
@property (readonly) BOOL converting; // is a track currently being converted?
|
||||
@property (copy) NSArray *currentAirPlayDevices; // the currently selected AirPlay device(s)
|
||||
@property (copy) iTunesEncoder *currentEncoder; // the currently selected encoder (MP3, AIFF, WAV, etc.)
|
||||
@property (copy) iTunesEQPreset *currentEQPreset; // the currently selected equalizer preset
|
||||
@property (copy, readonly) iTunesPlaylist *currentPlaylist; // the playlist containing the currently targeted track
|
||||
@property (copy, readonly) NSString *currentStreamTitle; // the name of the current song in the playing stream (provided by streaming server)
|
||||
@property (copy, readonly) NSString *currentStreamURL; // the URL of the playing stream or streaming web site (provided by streaming server)
|
||||
@property (copy, readonly) iTunesTrack *currentTrack; // the current targeted track
|
||||
@property (copy) iTunesVisual *currentVisual; // the currently selected visual plug-in
|
||||
@property BOOL EQEnabled; // is the equalizer enabled?
|
||||
@property BOOL fixedIndexing; // true if all AppleScript track indices should be independent of the play order of the owning playlist.
|
||||
@property BOOL frontmost; // is iTunes the frontmost application?
|
||||
@property BOOL fullScreen; // are visuals displayed using the entire screen?
|
||||
@property (copy, readonly) NSString *name; // the name of the application
|
||||
@property BOOL mute; // has the sound output been muted?
|
||||
@property double playerPosition; // the player’s position within the currently playing track in seconds.
|
||||
@property (readonly) iTunesEPlS playerState; // is iTunes stopped, paused, or playing?
|
||||
@property (copy, readonly) SBObject *selection; // the selection visible to the user
|
||||
@property NSInteger soundVolume; // the sound output volume (0 = minimum, 100 = maximum)
|
||||
@property (copy, readonly) NSString *version; // the version of iTunes
|
||||
@property BOOL visualsEnabled; // are visuals currently being displayed?
|
||||
@property iTunesEVSz visualSize; // the size of the displayed visual
|
||||
@property (copy, readonly) NSString *iAdIdentifier; // the iAd identifier
|
||||
|
||||
- (void) printPrintDialog:(BOOL)printDialog withProperties:(iTunesPrintSettings *)withProperties kind:(iTunesEKnd)kind theme:(NSString *)theme; // Print the specified object(s)
|
||||
- (void) run; // run iTunes
|
||||
- (void) quit; // quit iTunes
|
||||
- (iTunesTrack *) add:(NSArray *)x to:(SBObject *)to; // add one or more files to a playlist
|
||||
- (void) backTrack; // reposition to beginning of current track or go to previous track if already at start of current track
|
||||
- (iTunesTrack *) convert:(NSArray *)x; // convert one or more files or tracks
|
||||
- (void) fastForward; // skip forward in a playing track
|
||||
- (void) nextTrack; // advance to the next track in the current playlist
|
||||
- (void) pause; // pause playback
|
||||
- (void) playOnce:(BOOL)once; // play the current track or the specified track or file.
|
||||
- (void) playpause; // toggle the playing/paused state of the current track
|
||||
- (void) previousTrack; // return to the previous track in the current playlist
|
||||
- (void) resume; // disable fast forward/rewind and resume playback, if playing.
|
||||
- (void) rewind; // skip backwards in a playing track
|
||||
- (void) stop; // stop playback
|
||||
- (void) update; // update the specified iPod
|
||||
- (void) eject; // eject the specified iPod
|
||||
- (void) subscribe:(NSString *)x; // subscribe to a podcast feed
|
||||
- (void) updateAllPodcasts; // update all subscribed podcast feeds
|
||||
- (void) updatePodcast; // update podcast feed
|
||||
- (void) openLocation:(NSString *)x; // Opens a Music Store or audio stream URL
|
||||
|
||||
@end
|
||||
|
||||
// an item
|
||||
@interface iTunesItem : SBObject
|
||||
|
||||
@property (copy, readonly) SBObject *container; // the container of the item
|
||||
- (NSInteger) id; // the id of the item
|
||||
@property (readonly) NSInteger index; // The index of the item in internal application order.
|
||||
@property (copy) NSString *name; // the name of the item
|
||||
@property (copy, readonly) NSString *persistentID; // the id of the item as a hexadecimal string. This id does not change over time.
|
||||
@property (copy) NSDictionary *properties; // every property of the item
|
||||
|
||||
- (void) printPrintDialog:(BOOL)printDialog withProperties:(iTunesPrintSettings *)withProperties kind:(iTunesEKnd)kind theme:(NSString *)theme; // Print the specified object(s)
|
||||
- (void) close; // Close an object
|
||||
- (void) delete; // Delete an element from an object
|
||||
- (SBObject *) duplicateTo:(SBObject *)to; // Duplicate one or more object(s)
|
||||
- (BOOL) exists; // Verify if an object exists
|
||||
- (void) open; // open the specified object(s)
|
||||
- (void) playOnce:(BOOL)once; // play the current track or the specified track or file.
|
||||
- (void) reveal; // reveal and select a track or playlist
|
||||
|
||||
@end
|
||||
|
||||
// an AirPlay device
|
||||
@interface iTunesAirPlayDevice : iTunesItem
|
||||
|
||||
@property (readonly) BOOL active; // is the device currently being played to?
|
||||
@property (readonly) BOOL available; // is the device currently available?
|
||||
@property (readonly) iTunesEAPD kind; // the kind of the device
|
||||
@property (copy, readonly) NSString *networkAddress; // the network (MAC) address of the device
|
||||
- (BOOL) protected; // is the device password- or passcode-protected?
|
||||
@property BOOL selected; // is the device currently selected?
|
||||
@property (readonly) BOOL supportsAudio; // does the device support audio playback?
|
||||
@property (readonly) BOOL supportsVideo; // does the device support video playback?
|
||||
@property NSInteger soundVolume; // the output volume for the device (0 = minimum, 100 = maximum)
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// a piece of art within a track
|
||||
@interface iTunesArtwork : iTunesItem
|
||||
|
||||
@property (copy) NSImage *data; // data for this artwork, in the form of a picture
|
||||
@property (copy) NSString *objectDescription; // description of artwork as a string
|
||||
@property (readonly) BOOL downloaded; // was this artwork downloaded by iTunes?
|
||||
@property (copy, readonly) NSNumber *format; // the data format for this piece of artwork
|
||||
@property NSInteger kind; // kind or purpose of this piece of artwork
|
||||
@property (copy) NSData *rawData; // data for this artwork, in original format
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// converts a track to a specific file format
|
||||
@interface iTunesEncoder : iTunesItem
|
||||
|
||||
@property (copy, readonly) NSString *format; // the data format created by the encoder
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// equalizer preset configuration
|
||||
@interface iTunesEQPreset : iTunesItem
|
||||
|
||||
@property double band1; // the equalizer 32 Hz band level (-12.0 dB to +12.0 dB)
|
||||
@property double band2; // the equalizer 64 Hz band level (-12.0 dB to +12.0 dB)
|
||||
@property double band3; // the equalizer 125 Hz band level (-12.0 dB to +12.0 dB)
|
||||
@property double band4; // the equalizer 250 Hz band level (-12.0 dB to +12.0 dB)
|
||||
@property double band5; // the equalizer 500 Hz band level (-12.0 dB to +12.0 dB)
|
||||
@property double band6; // the equalizer 1 kHz band level (-12.0 dB to +12.0 dB)
|
||||
@property double band7; // the equalizer 2 kHz band level (-12.0 dB to +12.0 dB)
|
||||
@property double band8; // the equalizer 4 kHz band level (-12.0 dB to +12.0 dB)
|
||||
@property double band9; // the equalizer 8 kHz band level (-12.0 dB to +12.0 dB)
|
||||
@property double band10; // the equalizer 16 kHz band level (-12.0 dB to +12.0 dB)
|
||||
@property (readonly) BOOL modifiable; // can this preset be modified?
|
||||
@property double preamp; // the equalizer preamp level (-12.0 dB to +12.0 dB)
|
||||
@property BOOL updateTracks; // should tracks which refer to this preset be updated when the preset is renamed or deleted?
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// a list of songs/streams
|
||||
@interface iTunesPlaylist : iTunesItem
|
||||
|
||||
- (SBElementArray *) tracks;
|
||||
|
||||
@property (readonly) NSInteger duration; // the total length of all songs (in seconds)
|
||||
@property (copy) NSString *name; // the name of the playlist
|
||||
@property BOOL loved; // is this plalist loved?
|
||||
@property (copy, readonly) iTunesPlaylist *parent; // folder which contains this playlist (if any)
|
||||
@property BOOL shuffle; // play the songs in this playlist in random order?
|
||||
@property (readonly) NSInteger size; // the total size of all songs (in bytes)
|
||||
@property iTunesERpt songRepeat; // playback repeat mode
|
||||
@property (readonly) iTunesESpK specialKind; // special playlist kind
|
||||
@property (copy, readonly) NSString *time; // the length of all songs in MM:SS format
|
||||
@property (readonly) BOOL visible; // is this playlist visible in the Source list?
|
||||
|
||||
- (void) moveTo:(SBObject *)to; // Move playlist(s) to a new location
|
||||
- (iTunesTrack *) searchFor:(NSString *)for_ only:(iTunesESrA)only; // search a playlist for tracks matching the search string. Identical to entering search text in the Search field in iTunes.
|
||||
|
||||
@end
|
||||
|
||||
// a playlist representing an audio CD
|
||||
@interface iTunesAudioCDPlaylist : iTunesPlaylist
|
||||
|
||||
- (SBElementArray *) audioCDTracks;
|
||||
|
||||
@property (copy) NSString *artist; // the artist of the CD
|
||||
@property BOOL compilation; // is this CD a compilation album?
|
||||
@property (copy) NSString *composer; // the composer of the CD
|
||||
@property NSInteger discCount; // the total number of discs in this CD’s album
|
||||
@property NSInteger discNumber; // the index of this CD disc in the source album
|
||||
@property (copy) NSString *genre; // the genre of the CD
|
||||
@property NSInteger year; // the year the album was recorded/released
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// the master music library playlist
|
||||
@interface iTunesLibraryPlaylist : iTunesPlaylist
|
||||
|
||||
- (SBElementArray *) fileTracks;
|
||||
- (SBElementArray *) URLTracks;
|
||||
- (SBElementArray *) sharedTracks;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// the radio tuner playlist
|
||||
@interface iTunesRadioTunerPlaylist : iTunesPlaylist
|
||||
|
||||
- (SBElementArray *) URLTracks;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// a music source (music library, CD, device, etc.)
|
||||
@interface iTunesSource : iTunesItem
|
||||
|
||||
- (SBElementArray *) audioCDPlaylists;
|
||||
- (SBElementArray *) libraryPlaylists;
|
||||
- (SBElementArray *) playlists;
|
||||
- (SBElementArray *) radioTunerPlaylists;
|
||||
- (SBElementArray *) userPlaylists;
|
||||
|
||||
@property (readonly) long long capacity; // the total size of the source if it has a fixed size
|
||||
@property (readonly) long long freeSpace; // the free space on the source if it has a fixed size
|
||||
@property (readonly) iTunesESrc kind;
|
||||
|
||||
- (void) update; // update the specified iPod
|
||||
- (void) eject; // eject the specified iPod
|
||||
|
||||
@end
|
||||
|
||||
// playable audio source
|
||||
@interface iTunesTrack : iTunesItem
|
||||
|
||||
- (SBElementArray *) artworks;
|
||||
|
||||
@property (copy) NSString *album; // the album name of the track
|
||||
@property (copy) NSString *albumArtist; // the album artist of the track
|
||||
@property BOOL albumLoved; // is the album for this track loved?
|
||||
@property NSInteger albumRating; // the rating of the album for this track (0 to 100)
|
||||
@property (readonly) iTunesERtK albumRatingKind; // the rating kind of the album rating for this track
|
||||
@property (copy) NSString *artist; // the artist/source of the track
|
||||
@property (readonly) NSInteger bitRate; // the bit rate of the track (in kbps)
|
||||
@property double bookmark; // the bookmark time of the track in seconds
|
||||
@property BOOL bookmarkable; // is the playback position for this track remembered?
|
||||
@property NSInteger bpm; // the tempo of this track in beats per minute
|
||||
@property (copy) NSString *category; // the category of the track
|
||||
@property (copy) NSString *comment; // freeform notes about the track
|
||||
@property BOOL compilation; // is this track from a compilation album?
|
||||
@property (copy) NSString *composer; // the composer of the track
|
||||
@property (readonly) NSInteger databaseID; // the common, unique ID for this track. If two tracks in different playlists have the same database ID, they are sharing the same data.
|
||||
@property (copy, readonly) NSDate *dateAdded; // the date the track was added to the playlist
|
||||
@property (copy) NSString *objectDescription; // the description of the track
|
||||
@property NSInteger discCount; // the total number of discs in the source album
|
||||
@property NSInteger discNumber; // the index of the disc containing this track on the source album
|
||||
@property (readonly) double duration; // the length of the track in seconds
|
||||
@property BOOL enabled; // is this track checked for playback?
|
||||
@property (copy) NSString *episodeID; // the episode ID of the track
|
||||
@property NSInteger episodeNumber; // the episode number of the track
|
||||
@property (copy) NSString *EQ; // the name of the EQ preset of the track
|
||||
@property double finish; // the stop time of the track in seconds
|
||||
@property BOOL gapless; // is this track from a gapless album?
|
||||
@property (copy) NSString *genre; // the music/audio genre (category) of the track
|
||||
@property (copy) NSString *grouping; // the grouping (piece) of the track. Generally used to denote movements within a classical work.
|
||||
@property (readonly) BOOL iTunesU; // is this track an iTunes U episode?
|
||||
@property (copy, readonly) NSString *kind; // a text description of the track
|
||||
@property (copy) NSString *longDescription;
|
||||
@property BOOL loved; // is this track loved?
|
||||
@property (copy) NSString *lyrics; // the lyrics of the track
|
||||
@property (copy, readonly) NSDate *modificationDate; // the modification date of the content of this track
|
||||
@property NSInteger playedCount; // number of times this track has been played
|
||||
@property (copy) NSDate *playedDate; // the date and time this track was last played
|
||||
@property (readonly) BOOL podcast; // is this track a podcast episode?
|
||||
@property NSInteger rating; // the rating of this track (0 to 100)
|
||||
@property (readonly) iTunesERtK ratingKind; // the rating kind of this track
|
||||
@property (copy, readonly) NSDate *releaseDate; // the release date of this track
|
||||
@property (readonly) NSInteger sampleRate; // the sample rate of the track (in Hz)
|
||||
@property NSInteger seasonNumber; // the season number of the track
|
||||
@property BOOL shufflable; // is this track included when shuffling?
|
||||
@property NSInteger skippedCount; // number of times this track has been skipped
|
||||
@property (copy) NSDate *skippedDate; // the date and time this track was last skipped
|
||||
@property (copy) NSString *show; // the show name of the track
|
||||
@property (copy) NSString *sortAlbum; // override string to use for the track when sorting by album
|
||||
@property (copy) NSString *sortArtist; // override string to use for the track when sorting by artist
|
||||
@property (copy) NSString *sortAlbumArtist; // override string to use for the track when sorting by album artist
|
||||
@property (copy) NSString *sortName; // override string to use for the track when sorting by name
|
||||
@property (copy) NSString *sortComposer; // override string to use for the track when sorting by composer
|
||||
@property (copy) NSString *sortShow; // override string to use for the track when sorting by show name
|
||||
@property (readonly) long long size; // the size of the track (in bytes)
|
||||
@property double start; // the start time of the track in seconds
|
||||
@property (copy, readonly) NSString *time; // the length of the track in MM:SS format
|
||||
@property NSInteger trackCount; // the total number of tracks on the source album
|
||||
@property NSInteger trackNumber; // the index of the track on the source album
|
||||
@property BOOL unplayed; // is this track unplayed?
|
||||
@property iTunesEVdK videoKind; // kind of video track
|
||||
@property NSInteger volumeAdjustment; // relative volume adjustment of the track (-100% to 100%)
|
||||
@property NSInteger year; // the year the track was recorded/released
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// a track on an audio CD
|
||||
@interface iTunesAudioCDTrack : iTunesTrack
|
||||
|
||||
@property (copy, readonly) NSURL *location; // the location of the file represented by this track
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// a track representing an audio file (MP3, AIFF, etc.)
|
||||
@interface iTunesFileTrack : iTunesTrack
|
||||
|
||||
@property (copy) NSURL *location; // the location of the file represented by this track
|
||||
|
||||
- (void) refresh; // update file track information from the current information in the track’s file
|
||||
|
||||
@end
|
||||
|
||||
// a track residing in a shared library
|
||||
@interface iTunesSharedTrack : iTunesTrack
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// a track representing a network stream
|
||||
@interface iTunesURLTrack : iTunesTrack
|
||||
|
||||
@property (copy) NSString *address; // the URL for this track
|
||||
|
||||
- (void) download; // download podcast episode
|
||||
|
||||
@end
|
||||
|
||||
// custom playlists created by the user
|
||||
@interface iTunesUserPlaylist : iTunesPlaylist
|
||||
|
||||
- (SBElementArray *) fileTracks;
|
||||
- (SBElementArray *) URLTracks;
|
||||
- (SBElementArray *) sharedTracks;
|
||||
|
||||
@property BOOL shared; // is this playlist shared?
|
||||
@property (readonly) BOOL smart; // is this a Smart Playlist?
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// a folder that contains other playlists
|
||||
@interface iTunesFolderPlaylist : iTunesUserPlaylist
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// a visual plug-in
|
||||
@interface iTunesVisual : iTunesItem
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// any window
|
||||
@interface iTunesWindow : iTunesItem
|
||||
|
||||
@property NSRect bounds; // the boundary rectangle for the window
|
||||
@property (readonly) BOOL closeable; // does the window have a close box?
|
||||
@property (readonly) BOOL collapseable; // does the window have a collapse (windowshade) box?
|
||||
@property BOOL collapsed; // is the window collapsed?
|
||||
@property NSPoint position; // the upper left position of the window
|
||||
@property (readonly) BOOL resizable; // is the window resizable?
|
||||
@property BOOL visible; // is the window visible?
|
||||
@property (readonly) BOOL zoomable; // is the window zoomable?
|
||||
@property BOOL zoomed; // is the window zoomed?
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// the main iTunes window
|
||||
@interface iTunesBrowserWindow : iTunesWindow
|
||||
|
||||
@property BOOL minimized; // is the small player visible?
|
||||
@property (copy, readonly) SBObject *selection; // the selected songs
|
||||
@property (copy) iTunesPlaylist *view; // the playlist currently displayed in the window
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// the iTunes equalizer window
|
||||
@interface iTunesEQWindow : iTunesWindow
|
||||
|
||||
@property BOOL minimized; // is the small EQ window visible?
|
||||
|
||||
|
||||
@end
|
||||
|
||||
// a sub-window showing a single playlist
|
||||
@interface iTunesPlaylistWindow : iTunesWindow
|
||||
|
||||
@property (copy, readonly) SBObject *selection; // the selected songs
|
||||
@property (copy, readonly) iTunesPlaylist *view; // the playlist displayed in the window
|
||||
|
||||
|
||||
@end
|
||||
|
29
BGMApp/BGMApp/main.m
Normal file
|
@ -0,0 +1,29 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// main.m
|
||||
// BGMApp
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
return NSApplicationMain(argc, argv);
|
||||
}
|
||||
|
58
BGMApp/BGMAppTests/BGMAppTests.m
Normal file
|
@ -0,0 +1,58 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGMAppTests.m
|
||||
// BGMAppTests
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
|
||||
@interface BGMAppTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation BGMAppTests
|
||||
|
||||
// TODO: More than no tests
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testExample {
|
||||
// This is an example of a functional test case.
|
||||
XCTAssert(YES, @"Pass");
|
||||
}
|
||||
|
||||
- (void)testPerformanceExample {
|
||||
// This is an example of a performance test case.
|
||||
[self measureBlock:^{
|
||||
// Put the code you want to measure the time of here.
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
24
BGMApp/BGMAppTests/Info.plist
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?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>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</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</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
BIN
BGMApp/OptimizationProfiles/BGMApp.profdata
Normal file
305
BGMApp/PublicUtility/CAAtomic.h
Normal file
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
File: CAAtomic.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
/*
|
||||
This file implements all Atomic operations using Interlocked functions specified in
|
||||
Winbase.h
|
||||
NOTE: According to Microsoft documentation, all Interlocked functions generates a
|
||||
full barrier.
|
||||
On Windows:
|
||||
As the Interlocked functions returns the Old value, Extra checks and operations
|
||||
are made after the atomic operation to return value consistent with OSX counterparts.
|
||||
*/
|
||||
#ifndef __CAAtomic_h__
|
||||
#define __CAAtomic_h__
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
#include <windows.h>
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_InterlockedOr)
|
||||
#pragma intrinsic(_InterlockedAnd)
|
||||
#else
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#include <libkern/OSAtomic.h>
|
||||
#endif
|
||||
|
||||
inline void CAMemoryBarrier()
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
MemoryBarrier();
|
||||
#else
|
||||
OSMemoryBarrier();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicAdd32Barrier(SInt32 theAmt, volatile SInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
long lRetVal = InterlockedExchangeAdd((volatile long*)theValue, theAmt);
|
||||
// InterlockedExchangeAdd returns the original value which differs from OSX version.
|
||||
// At this point the addition would have occured and hence returning the new value
|
||||
// to keep it sync with OSX.
|
||||
return lRetVal + theAmt;
|
||||
#else
|
||||
return OSAtomicAdd32Barrier(theAmt, (volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicOr32Barrier(UInt32 theMask, volatile UInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
// InterlockedAnd macro is not defined in x86 platform, and hence using the intrinsic
|
||||
// function instead.
|
||||
long j = _InterlockedOr((volatile long*)theValue, theMask);
|
||||
// _InterlockedOr returns the original value which differs from OSX version.
|
||||
// Returning the new value similar to OSX
|
||||
return (SInt32)(j | theMask);
|
||||
#else
|
||||
return OSAtomicOr32Barrier(theMask, (volatile uint32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicAnd32Barrier(UInt32 theMask, volatile UInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
// InterlockedAnd macro is not defined in x86 platform, and hence using the intrinsic
|
||||
// function instead.
|
||||
long j = _InterlockedAnd((volatile long*)theValue, theMask);
|
||||
// _InterlockedAnd returns the original value which differs from OSX version.
|
||||
// Returning the new value similar to OSX
|
||||
return (SInt32)(j & theMask);
|
||||
#else
|
||||
return OSAtomicAnd32Barrier(theMask, (volatile uint32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool CAAtomicCompareAndSwap32Barrier(SInt32 oldValue, SInt32 newValue, volatile SInt32 *theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
// InterlockedCompareExchange returns the old value. But we need to return bool value.
|
||||
long lRetVal = InterlockedCompareExchange((volatile long*)theValue, newValue, oldValue);
|
||||
// Hence we check if the new value is set and if it is we return true else false.
|
||||
// If theValue is equal to oldValue then the swap happens. Otherwise swap doesn't happen.
|
||||
return (oldValue == lRetVal);
|
||||
#else
|
||||
return OSAtomicCompareAndSwap32Barrier(oldValue, newValue, (volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline SInt32 CAAtomicIncrement32(volatile SInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
return (SInt32)InterlockedIncrement((volatile long*)theValue);
|
||||
#else
|
||||
return OSAtomicIncrement32((volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicDecrement32(volatile SInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
return (SInt32)InterlockedDecrement((volatile long*)theValue);
|
||||
#else
|
||||
return OSAtomicDecrement32((volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicIncrement32Barrier(volatile SInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
return CAAtomicIncrement32(theValue);
|
||||
#else
|
||||
return OSAtomicIncrement32Barrier((volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline SInt32 CAAtomicDecrement32Barrier(volatile SInt32* theValue)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
return CAAtomicDecrement32(theValue);
|
||||
#else
|
||||
return OSAtomicDecrement32Barrier((volatile int32_t *)theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool CAAtomicTestAndClearBarrier(int bitToClear, void* theAddress)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
BOOL bOldVal = InterlockedBitTestAndReset((long*)theAddress, bitToClear);
|
||||
return (bOldVal ? true : false);
|
||||
#else
|
||||
return OSAtomicTestAndClearBarrier(bitToClear, (volatile void *)theAddress);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool CAAtomicTestAndClear(int bitToClear, void* theAddress)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
BOOL bOldVal = CAAtomicTestAndClearBarrier(bitToClear, (long*)theAddress);
|
||||
return (bOldVal ? true : false);
|
||||
#else
|
||||
return OSAtomicTestAndClear(bitToClear, (volatile void *)theAddress);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool CAAtomicTestAndSetBarrier(int bitToSet, void* theAddress)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
BOOL bOldVal = InterlockedBitTestAndSet((long*)theAddress, bitToSet);
|
||||
return (bOldVal ? true : false);
|
||||
#else
|
||||
return OSAtomicTestAndSetBarrier(bitToSet, (volatile void *)theAddress);
|
||||
#endif
|
||||
}
|
||||
|
||||
// int32_t flavors -- for C++ only since we can't overload in C
|
||||
// CFBase.h defines SInt32 as signed int which is similar to int32_t. If CFBase.h is included, then
|
||||
// this will generate redefinition error. But on Mac, CFBase.h, still includes MacTypes.h where
|
||||
// SInt32 is defined as signed long so this would work there.
|
||||
// So in order to fix the redefinition errors, we define these functions only if MacTypes.h is included.
|
||||
#if defined(__cplusplus) && defined(__MACTYPES__) && !__LP64__
|
||||
inline int32_t CAAtomicAdd32Barrier(int32_t theAmt, volatile int32_t* theValue)
|
||||
{
|
||||
return CAAtomicAdd32Barrier(theAmt, (volatile SInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicOr32Barrier(uint32_t theMask, volatile uint32_t* theValue)
|
||||
{
|
||||
return CAAtomicOr32Barrier(theMask, (volatile UInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicAnd32Barrier(uint32_t theMask, volatile uint32_t* theValue)
|
||||
{
|
||||
return CAAtomicAnd32Barrier(theMask, (volatile UInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline bool CAAtomicCompareAndSwap32Barrier(int32_t oldValue, int32_t newValue, volatile int32_t *theValue)
|
||||
{
|
||||
return CAAtomicCompareAndSwap32Barrier(oldValue, newValue, (volatile SInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicIncrement32(volatile int32_t* theValue)
|
||||
{
|
||||
return CAAtomicIncrement32((volatile SInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicDecrement32(volatile int32_t* theValue)
|
||||
{
|
||||
return CAAtomicDecrement32((volatile SInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicIncrement32Barrier(volatile int32_t* theValue)
|
||||
{
|
||||
return CAAtomicIncrement32Barrier((volatile SInt32 *)theValue);
|
||||
}
|
||||
|
||||
inline int32_t CAAtomicDecrement32Barrier(volatile int32_t* theValue)
|
||||
{
|
||||
return CAAtomicDecrement32Barrier((volatile SInt32 *)theValue);
|
||||
}
|
||||
#endif // __cplusplus && !__LP64__
|
||||
|
||||
#if __LP64__
|
||||
inline bool CAAtomicCompareAndSwap64Barrier( int64_t __oldValue, int64_t __newValue, volatile int64_t *__theValue )
|
||||
{
|
||||
return OSAtomicCompareAndSwap64Barrier(__oldValue, __newValue, __theValue);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool CAAtomicCompareAndSwapPtrBarrier(void *__oldValue, void *__newValue, volatile void ** __theValue)
|
||||
{
|
||||
#if __LP64__
|
||||
return CAAtomicCompareAndSwap64Barrier((int64_t)__oldValue, (int64_t)__newValue, (int64_t *)__theValue);
|
||||
#else
|
||||
return CAAtomicCompareAndSwap32Barrier((int32_t)__oldValue, (int32_t)__newValue, (int32_t *)__theValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Spinlocks. These use memory barriers as required to synchronize access to shared
|
||||
* memory protected by the lock. The lock operation spins, but employs various strategies
|
||||
* to back off if the lock is held, making it immune to most priority-inversion livelocks.
|
||||
* The try operation immediately returns false if the lock was held, true if it took the
|
||||
* lock. The convention is that unlocked is zero, locked is nonzero.
|
||||
*/
|
||||
#define CA_SPINLOCK_INIT 0
|
||||
|
||||
typedef int32_t CASpinLock;
|
||||
|
||||
bool CASpinLockTry( volatile CASpinLock *__lock );
|
||||
void CASpinLockLock( volatile CASpinLock *__lock );
|
||||
void CASpinLockUnlock( volatile CASpinLock *__lock );
|
||||
|
||||
inline void CASpinLockLock( volatile CASpinLock *__lock )
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
OSSpinLockLock(__lock);
|
||||
#else
|
||||
while (CAAtomicTestAndSetBarrier(0, (void*)__lock))
|
||||
usleep(1000); // ???
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void CASpinLockUnlock( volatile CASpinLock *__lock )
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
OSSpinLockUnlock(__lock);
|
||||
#else
|
||||
CAAtomicTestAndClearBarrier(0, (void*)__lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool CASpinLockTry( volatile CASpinLock *__lock )
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
return OSSpinLockTry(__lock);
|
||||
#else
|
||||
return (CAAtomicTestAndSetBarrier(0, (void*)__lock) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#endif // __CAAtomic_h__
|
508
BGMApp/PublicUtility/CAAutoDisposer.h
Normal file
|
@ -0,0 +1,508 @@
|
|||
/*
|
||||
File: CAAutoDisposer.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAPtr_h__)
|
||||
#define __CAPtr_h__
|
||||
|
||||
#include <stdlib.h> // for malloc
|
||||
#include <new> // for bad_alloc
|
||||
#include <string.h> // for memset
|
||||
|
||||
inline void* CA_malloc(size_t size)
|
||||
{
|
||||
void* p = malloc(size);
|
||||
if (!p && size) throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void* CA_realloc(void* old, size_t size)
|
||||
{
|
||||
#if TARGET_OS_WIN32
|
||||
void* p = realloc(old, size);
|
||||
#else
|
||||
void* p = reallocf(old, size); // reallocf ensures the old pointer is freed if memory is full (p is NULL).
|
||||
#endif
|
||||
if (!p && size) throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifndef UINTPTR_MAX
|
||||
#if __LP64__
|
||||
#define UINTPTR_MAX 18446744073709551615ULL
|
||||
#else
|
||||
#define UINTPTR_MAX 4294967295U
|
||||
#endif
|
||||
#endif
|
||||
|
||||
inline void* CA_calloc(size_t n, size_t size)
|
||||
{
|
||||
// ensure that multiplication will not overflow
|
||||
if (n && UINTPTR_MAX / n < size) throw std::bad_alloc();
|
||||
|
||||
size_t nsize = n*size;
|
||||
void* p = malloc(nsize);
|
||||
if (!p && nsize) throw std::bad_alloc();
|
||||
|
||||
memset(p, 0, nsize);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
// helper class for automatic conversions
|
||||
template <typename T>
|
||||
struct CAPtrRef
|
||||
{
|
||||
T* ptr_;
|
||||
|
||||
explicit CAPtrRef(T* ptr) : ptr_(ptr) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class CAAutoFree
|
||||
{
|
||||
private:
|
||||
T* ptr_;
|
||||
|
||||
public:
|
||||
|
||||
CAAutoFree() : ptr_(0) {}
|
||||
|
||||
explicit CAAutoFree(T* ptr) : ptr_(ptr) {}
|
||||
|
||||
template<typename U>
|
||||
CAAutoFree(CAAutoFree<U>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
// C++ std says: a template constructor is never a copy constructor
|
||||
CAAutoFree(CAAutoFree<T>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
CAAutoFree(size_t n, bool clear = false)
|
||||
// this becomes an ambiguous call if n == 0
|
||||
: ptr_(0)
|
||||
{
|
||||
size_t maxItems = ~size_t(0) / sizeof(T);
|
||||
if (n > maxItems)
|
||||
throw std::bad_alloc();
|
||||
|
||||
ptr_ = static_cast<T*>(clear ? CA_calloc(n, sizeof(T)) : CA_malloc(n * sizeof(T)));
|
||||
}
|
||||
|
||||
~CAAutoFree() { free(); }
|
||||
|
||||
void alloc(size_t numItems, bool clear = false)
|
||||
{
|
||||
size_t maxItems = ~size_t(0) / sizeof(T);
|
||||
if (numItems > maxItems) throw std::bad_alloc();
|
||||
|
||||
free();
|
||||
ptr_ = static_cast<T*>(clear ? CA_calloc(numItems, sizeof(T)) : CA_malloc(numItems * sizeof(T)));
|
||||
}
|
||||
|
||||
void allocBytes(size_t numBytes, bool clear = false)
|
||||
{
|
||||
free();
|
||||
ptr_ = static_cast<T*>(clear ? CA_calloc(1, numBytes) : CA_malloc(numBytes));
|
||||
}
|
||||
|
||||
void reallocBytes(size_t numBytes)
|
||||
{
|
||||
ptr_ = static_cast<T*>(CA_realloc(ptr_, numBytes));
|
||||
}
|
||||
|
||||
void reallocItems(size_t numItems)
|
||||
{
|
||||
size_t maxItems = ~size_t(0) / sizeof(T);
|
||||
if (numItems > maxItems) throw std::bad_alloc();
|
||||
|
||||
ptr_ = static_cast<T*>(CA_realloc(ptr_, numItems * sizeof(T)));
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CAAutoFree& operator=(CAAutoFree<U>& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoFree& operator=(CAAutoFree& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoFree& operator=(T* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CAAutoFree& operator=(U* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
|
||||
T* operator()() const { return ptr_; }
|
||||
T* get() const { return ptr_; }
|
||||
operator T*() const { return ptr_; }
|
||||
|
||||
bool operator==(CAAutoFree const& that) const { return ptr_ == that.ptr_; }
|
||||
bool operator!=(CAAutoFree const& that) const { return ptr_ != that.ptr_; }
|
||||
bool operator==(T* ptr) const { return ptr_ == ptr; }
|
||||
bool operator!=(T* ptr) const { return ptr_ != ptr; }
|
||||
|
||||
T* release()
|
||||
{
|
||||
// release ownership
|
||||
T* result = ptr_;
|
||||
ptr_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void set(T* ptr)
|
||||
{
|
||||
if (ptr != ptr_)
|
||||
{
|
||||
::free(ptr_);
|
||||
ptr_ = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void free()
|
||||
{
|
||||
set(0);
|
||||
}
|
||||
|
||||
|
||||
// automatic conversions to allow assignment from results of functions.
|
||||
// hard to explain. see auto_ptr implementation and/or Josuttis' STL book.
|
||||
CAAutoFree(CAPtrRef<T> ref) : ptr_(ref.ptr_) { }
|
||||
|
||||
CAAutoFree& operator=(CAPtrRef<T> ref)
|
||||
{
|
||||
set(ref.ptr_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
operator CAPtrRef<U>()
|
||||
{ return CAPtrRef<U>(release()); }
|
||||
|
||||
template<typename U>
|
||||
operator CAAutoFree<U>()
|
||||
{ return CAAutoFree<U>(release()); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
class CAAutoDelete
|
||||
{
|
||||
private:
|
||||
T* ptr_;
|
||||
|
||||
public:
|
||||
CAAutoDelete() : ptr_(0) {}
|
||||
|
||||
explicit CAAutoDelete(T* ptr) : ptr_(ptr) {}
|
||||
|
||||
template<typename U>
|
||||
CAAutoDelete(CAAutoDelete<U>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
// C++ std says: a template constructor is never a copy constructor
|
||||
CAAutoDelete(CAAutoDelete<T>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
~CAAutoDelete() { free(); }
|
||||
|
||||
template <typename U>
|
||||
CAAutoDelete& operator=(CAAutoDelete<U>& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoDelete& operator=(CAAutoDelete& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoDelete& operator=(T* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CAAutoDelete& operator=(U* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
|
||||
T* operator()() const { return ptr_; }
|
||||
T* get() const { return ptr_; }
|
||||
operator T*() const { return ptr_; }
|
||||
|
||||
bool operator==(CAAutoDelete const& that) const { return ptr_ == that.ptr_; }
|
||||
bool operator!=(CAAutoDelete const& that) const { return ptr_ != that.ptr_; }
|
||||
bool operator==(T* ptr) const { return ptr_ == ptr; }
|
||||
bool operator!=(T* ptr) const { return ptr_ != ptr; }
|
||||
|
||||
T* release()
|
||||
{
|
||||
// release ownership
|
||||
T* result = ptr_;
|
||||
ptr_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void set(T* ptr)
|
||||
{
|
||||
if (ptr != ptr_)
|
||||
{
|
||||
delete ptr_;
|
||||
ptr_ = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void free()
|
||||
{
|
||||
set(0);
|
||||
}
|
||||
|
||||
|
||||
// automatic conversions to allow assignment from results of functions.
|
||||
// hard to explain. see auto_ptr implementation and/or Josuttis' STL book.
|
||||
CAAutoDelete(CAPtrRef<T> ref) : ptr_(ref.ptr_) { }
|
||||
|
||||
CAAutoDelete& operator=(CAPtrRef<T> ref)
|
||||
{
|
||||
set(ref.ptr_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
operator CAPtrRef<U>()
|
||||
{ return CAPtrRef<U>(release()); }
|
||||
|
||||
template<typename U>
|
||||
operator CAAutoFree<U>()
|
||||
{ return CAAutoFree<U>(release()); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
class CAAutoArrayDelete
|
||||
{
|
||||
private:
|
||||
T* ptr_;
|
||||
|
||||
public:
|
||||
CAAutoArrayDelete() : ptr_(0) {}
|
||||
|
||||
explicit CAAutoArrayDelete(T* ptr) : ptr_(ptr) {}
|
||||
|
||||
template<typename U>
|
||||
CAAutoArrayDelete(CAAutoArrayDelete<U>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
// C++ std says: a template constructor is never a copy constructor
|
||||
CAAutoArrayDelete(CAAutoArrayDelete<T>& that) : ptr_(that.release()) {} // take ownership
|
||||
|
||||
// this becomes an ambiguous call if n == 0
|
||||
CAAutoArrayDelete(size_t n) : ptr_(new T[n]) {}
|
||||
|
||||
~CAAutoArrayDelete() { free(); }
|
||||
|
||||
void alloc(size_t numItems)
|
||||
{
|
||||
free();
|
||||
ptr_ = new T [numItems];
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CAAutoArrayDelete& operator=(CAAutoArrayDelete<U>& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoArrayDelete& operator=(CAAutoArrayDelete& that)
|
||||
{
|
||||
set(that.release()); // take ownership
|
||||
return *this;
|
||||
}
|
||||
|
||||
CAAutoArrayDelete& operator=(T* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
CAAutoArrayDelete& operator=(U* ptr)
|
||||
{
|
||||
set(ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*() const { return *ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
|
||||
T* operator()() const { return ptr_; }
|
||||
T* get() const { return ptr_; }
|
||||
operator T*() const { return ptr_; }
|
||||
|
||||
bool operator==(CAAutoArrayDelete const& that) const { return ptr_ == that.ptr_; }
|
||||
bool operator!=(CAAutoArrayDelete const& that) const { return ptr_ != that.ptr_; }
|
||||
bool operator==(T* ptr) const { return ptr_ == ptr; }
|
||||
bool operator!=(T* ptr) const { return ptr_ != ptr; }
|
||||
|
||||
T* release()
|
||||
{
|
||||
// release ownership
|
||||
T* result = ptr_;
|
||||
ptr_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void set(T* ptr)
|
||||
{
|
||||
if (ptr != ptr_)
|
||||
{
|
||||
delete [] ptr_;
|
||||
ptr_ = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void free()
|
||||
{
|
||||
set(0);
|
||||
}
|
||||
|
||||
|
||||
// automatic conversions to allow assignment from results of functions.
|
||||
// hard to explain. see auto_ptr implementation and/or Josuttis' STL book.
|
||||
CAAutoArrayDelete(CAPtrRef<T> ref) : ptr_(ref.ptr_) { }
|
||||
|
||||
CAAutoArrayDelete& operator=(CAPtrRef<T> ref)
|
||||
{
|
||||
set(ref.ptr_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
operator CAPtrRef<U>()
|
||||
{ return CAPtrRef<U>(release()); }
|
||||
|
||||
template<typename U>
|
||||
operator CAAutoArrayDelete<U>()
|
||||
{ return CAAutoFree<U>(release()); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// convenience function
|
||||
template <typename T>
|
||||
void free(CAAutoFree<T>& p)
|
||||
{
|
||||
p.free();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if 0
|
||||
// example program showing ownership transfer
|
||||
|
||||
CAAutoFree<char> source()
|
||||
{
|
||||
// source allocates and returns ownership to the caller.
|
||||
const char* str = "this is a test";
|
||||
size_t size = strlen(str) + 1;
|
||||
CAAutoFree<char> captr(size, false);
|
||||
strlcpy(captr(), str, size);
|
||||
printf("source %08X %08X '%s'\n", &captr, captr(), captr());
|
||||
return captr;
|
||||
}
|
||||
|
||||
void user(CAAutoFree<char> const& captr)
|
||||
{
|
||||
// passed by const reference. user can access the pointer but does not take ownership.
|
||||
printf("user: %08X %08X '%s'\n", &captr, captr(), captr());
|
||||
}
|
||||
|
||||
void sink(CAAutoFree<char> captr)
|
||||
{
|
||||
// passed by value. sink takes ownership and frees the pointer on return.
|
||||
printf("sink: %08X %08X '%s'\n", &captr, captr(), captr());
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, char * const argv[])
|
||||
{
|
||||
|
||||
CAAutoFree<char> captr(source());
|
||||
printf("main captr A %08X %08X\n", &captr, captr());
|
||||
user(captr);
|
||||
sink(captr);
|
||||
printf("main captr B %08X %08X\n", &captr, captr());
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
206
BGMApp/PublicUtility/CABitOperations.h
Normal file
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
File: CABitOperations.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#ifndef _CABitOperations_h_
|
||||
#define _CABitOperations_h_
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
//#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h>
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#else
|
||||
// #include <MacTypes.h>
|
||||
#include "CFBase.h"
|
||||
#endif
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
// return whether a number is a power of two
|
||||
inline UInt32 IsPowerOfTwo(UInt32 x)
|
||||
{
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
// count the leading zeros in a word
|
||||
// Metrowerks Codewarrior. powerpc native count leading zeros instruction:
|
||||
// I think it's safe to remove this ...
|
||||
//#define CountLeadingZeroes(x) ((int)__cntlzw((unsigned int)x))
|
||||
|
||||
inline UInt32 CountLeadingZeroes(UInt32 arg)
|
||||
{
|
||||
// GNUC / LLVM have a builtin
|
||||
#if defined(__GNUC__) || defined(__llvm___)
|
||||
#if (TARGET_CPU_X86 || TARGET_CPU_X86_64)
|
||||
if (arg == 0) return 32;
|
||||
#endif // TARGET_CPU_X86 || TARGET_CPU_X86_64
|
||||
return __builtin_clz(arg);
|
||||
#elif TARGET_OS_WIN32
|
||||
UInt32 tmp;
|
||||
__asm{
|
||||
bsr eax, arg
|
||||
mov ecx, 63
|
||||
cmovz eax, ecx
|
||||
xor eax, 31
|
||||
mov tmp, eax // this moves the result in tmp to return.
|
||||
}
|
||||
return tmp;
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif // defined(__GNUC__)
|
||||
}
|
||||
// Alias (with different spelling)
|
||||
#define CountLeadingZeros CountLeadingZeroes
|
||||
|
||||
inline UInt32 CountLeadingZeroesLong(UInt64 arg)
|
||||
{
|
||||
// GNUC / LLVM have a builtin
|
||||
#if defined(__GNUC__) || defined(__llvm___)
|
||||
#if (TARGET_CPU_X86 || TARGET_CPU_X86_64)
|
||||
if (arg == 0) return 64;
|
||||
#endif // TARGET_CPU_X86 || TARGET_CPU_X86_64
|
||||
return __builtin_clzll(arg);
|
||||
#elif TARGET_OS_WIN32
|
||||
UInt32 x = CountLeadingZeroes((UInt32)(arg >> 32));
|
||||
if(x < 32)
|
||||
return x;
|
||||
else
|
||||
return 32+CountLeadingZeroes((UInt32)arg);
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif // defined(__GNUC__)
|
||||
}
|
||||
#define CountLeadingZerosLong CountLeadingZeroesLong
|
||||
|
||||
// count trailing zeroes
|
||||
inline UInt32 CountTrailingZeroes(UInt32 x)
|
||||
{
|
||||
return 32 - CountLeadingZeroes(~x & (x-1));
|
||||
}
|
||||
|
||||
// count leading ones
|
||||
inline UInt32 CountLeadingOnes(UInt32 x)
|
||||
{
|
||||
return CountLeadingZeroes(~x);
|
||||
}
|
||||
|
||||
// count trailing ones
|
||||
inline UInt32 CountTrailingOnes(UInt32 x)
|
||||
{
|
||||
return 32 - CountLeadingZeroes(x & (~x-1));
|
||||
}
|
||||
|
||||
// number of bits required to represent x.
|
||||
inline UInt32 NumBits(UInt32 x)
|
||||
{
|
||||
return 32 - CountLeadingZeroes(x);
|
||||
}
|
||||
|
||||
// base 2 log of next power of two greater or equal to x
|
||||
inline UInt32 Log2Ceil(UInt32 x)
|
||||
{
|
||||
return 32 - CountLeadingZeroes(x - 1);
|
||||
}
|
||||
|
||||
// base 2 log of next power of two less or equal to x
|
||||
inline UInt32 Log2Floor(UInt32 x)
|
||||
{
|
||||
return 32 - CountLeadingZeroes(x) - 1;
|
||||
}
|
||||
|
||||
// next power of two greater or equal to x
|
||||
inline UInt32 NextPowerOfTwo(UInt32 x)
|
||||
{
|
||||
return 1 << Log2Ceil(x);
|
||||
}
|
||||
|
||||
// counting the one bits in a word
|
||||
inline UInt32 CountOnes(UInt32 x)
|
||||
{
|
||||
// secret magic algorithm for counting bits in a word.
|
||||
x = x - ((x >> 1) & 0x55555555);
|
||||
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
|
||||
return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
|
||||
}
|
||||
|
||||
// counting the zero bits in a word
|
||||
inline UInt32 CountZeroes(UInt32 x)
|
||||
{
|
||||
return CountOnes(~x);
|
||||
}
|
||||
|
||||
// return the bit position (0..31) of the least significant bit
|
||||
inline UInt32 LSBitPos(UInt32 x)
|
||||
{
|
||||
return CountTrailingZeroes(x & -(SInt32)x);
|
||||
}
|
||||
|
||||
// isolate the least significant bit
|
||||
inline UInt32 LSBit(UInt32 x)
|
||||
{
|
||||
return x & -(SInt32)x;
|
||||
}
|
||||
|
||||
// return the bit position (0..31) of the most significant bit
|
||||
inline UInt32 MSBitPos(UInt32 x)
|
||||
{
|
||||
return 31 - CountLeadingZeroes(x);
|
||||
}
|
||||
|
||||
// isolate the most significant bit
|
||||
inline UInt32 MSBit(UInt32 x)
|
||||
{
|
||||
return 1 << MSBitPos(x);
|
||||
}
|
||||
|
||||
// Division optimized for power of 2 denominators
|
||||
inline UInt32 DivInt(UInt32 numerator, UInt32 denominator)
|
||||
{
|
||||
if(IsPowerOfTwo(denominator))
|
||||
return numerator >> (31 - CountLeadingZeroes(denominator));
|
||||
else
|
||||
return numerator/denominator;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
821
BGMApp/PublicUtility/CACFArray.cpp
Normal file
|
@ -0,0 +1,821 @@
|
|||
/*
|
||||
File: CACFArray.cpp
|
||||
Abstract: CACFArray.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CACFArray.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CACFDictionary.h"
|
||||
#include "CACFNumber.h"
|
||||
#include "CACFString.h"
|
||||
|
||||
//=============================================================================
|
||||
// CACFArray
|
||||
//=============================================================================
|
||||
|
||||
bool CACFArray::HasItem(const void* inItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
if(mCFArray != NULL)
|
||||
{
|
||||
CFRange theRange = { 0, CFArrayGetCount(mCFArray)};
|
||||
theAnswer = CFArrayContainsValue(mCFArray, theRange, inItem);
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetIndexOfItem(const void* inItem, UInt32& outIndex) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
outIndex = 0;
|
||||
if(mCFArray != NULL)
|
||||
{
|
||||
CFRange theRange = { 0, CFArrayGetCount(mCFArray)};
|
||||
CFIndex theIndex = CFArrayGetFirstIndexOfValue(mCFArray, theRange, inItem);
|
||||
if(theIndex != -1)
|
||||
{
|
||||
theAnswer = true;
|
||||
outIndex = ToUInt32(theIndex);
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetBool(UInt32 inIndex, bool& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inIndex, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFBooleanGetTypeID()))
|
||||
{
|
||||
outValue = CFBooleanGetValue(static_cast<CFBooleanRef>(theValue));
|
||||
theAnswer = true;
|
||||
}
|
||||
else if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
SInt32 theNumericValue = 0;
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &theNumericValue);
|
||||
outValue = theNumericValue != 0;
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetSInt32(UInt32 inIndex, SInt32& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberSInt32Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetUInt32(UInt32 inIndex, UInt32& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberSInt32Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetSInt64(UInt32 inIndex, SInt64& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberSInt64Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetUInt64(UInt32 inIndex, UInt64& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberSInt64Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetFloat32(UInt32 inIndex, Float32& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberFloat32Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetFloat64(UInt32 inIndex, Float64& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theItem), kCFNumberFloat64Type, &outItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::Get4CC(UInt32 inIndex, UInt32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inIndex, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
else if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
CFStringRef theString = static_cast<CFStringRef>(theValue);
|
||||
if(CFStringGetLength(theString) == 4)
|
||||
{
|
||||
char theCString[5];
|
||||
CFStringGetCString(theString, theCString, 5, kCFStringEncodingASCII);
|
||||
outValue = CFSwapInt32BigToHost(*reinterpret_cast<UInt32*>(theCString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetString(UInt32 inIndex, CFStringRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFStringGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFStringRef>(theItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetArray(UInt32 inIndex, CFArrayRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFArrayGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFArrayRef>(theItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetDictionary(UInt32 inIndex, CFDictionaryRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFDictionaryGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFDictionaryRef>(theItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetData(UInt32 inIndex, CFDataRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFDataGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFDataRef>(theItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetUUID(UInt32 inIndex, CFUUIDRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFUUIDGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFUUIDRef>(theItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::GetCFType(UInt32 inIndex, CFTypeRef& outItem) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && (inIndex < GetNumberItems()))
|
||||
{
|
||||
outItem = CFArrayGetValueAtIndex(mCFArray, static_cast<CFIndex>(inIndex));
|
||||
theAnswer = outItem != NULL;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CACFArray::GetCACFString(UInt32 inIndex, CACFString& outItem) const
|
||||
{
|
||||
outItem = static_cast<CFStringRef>(NULL);
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFStringGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFStringRef>(theItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CACFArray::GetCACFArray(UInt32 inIndex, CACFArray& outItem) const
|
||||
{
|
||||
outItem = static_cast<CFArrayRef>(NULL);
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFArrayGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFArrayRef>(theItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CACFArray::GetCACFDictionary(UInt32 inIndex, CACFDictionary& outItem) const
|
||||
{
|
||||
outItem = static_cast<CFDictionaryRef>(NULL);
|
||||
CFTypeRef theItem = NULL;
|
||||
if(GetCFType(inIndex, theItem))
|
||||
{
|
||||
if((theItem != NULL) && (CFGetTypeID(theItem) == CFDictionaryGetTypeID()))
|
||||
{
|
||||
outItem = static_cast<CFDictionaryRef>(theItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CACFArray::AppendBool(bool inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFBoolean theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFBoolean());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendSInt32(SInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendUInt32(UInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendSInt64(SInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendUInt64(UInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendFloat32(Float32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendFloat64(Float64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = AppendCFType(theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::AppendString(const CFStringRef inItem)
|
||||
{
|
||||
return AppendCFType(inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::AppendArray(const CFArrayRef inItem)
|
||||
{
|
||||
return AppendCFType(inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::AppendDictionary(const CFDictionaryRef inItem)
|
||||
{
|
||||
return AppendCFType(inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::AppendData(const CFDataRef inItem)
|
||||
{
|
||||
return AppendCFType(inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::AppendCFType(const CFTypeRef inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CFArrayAppendValue(mCFArray, inItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertBool(UInt32 inIndex, bool inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFBoolean theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFBoolean());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertSInt32(UInt32 inIndex, SInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertUInt32(UInt32 inIndex, UInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertSInt64(UInt32 inIndex, SInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertUInt64(UInt32 inIndex, UInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertFloat32(UInt32 inIndex, Float32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertFloat64(UInt32 inIndex, Float64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = InsertCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::InsertString(UInt32 inIndex, const CFStringRef inItem)
|
||||
{
|
||||
return InsertCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::InsertArray(UInt32 inIndex, const CFArrayRef inItem)
|
||||
{
|
||||
return InsertCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::InsertDictionary(UInt32 inIndex, const CFDictionaryRef inItem)
|
||||
{
|
||||
return InsertCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::InsertData(UInt32 inIndex, const CFDataRef inItem)
|
||||
{
|
||||
return InsertCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::InsertCFType(UInt32 inIndex, const CFTypeRef inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable)
|
||||
{
|
||||
if(inIndex < GetNumberItems())
|
||||
{
|
||||
CFArrayInsertValueAtIndex(mCFArray, static_cast<CFIndex>(inIndex), inItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
CFArrayAppendValue(mCFArray, inItem);
|
||||
}
|
||||
theAnswer = true;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetBool(UInt32 inIndex, bool inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFBoolean theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFBoolean());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetSInt32(UInt32 inIndex, SInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetUInt32(UInt32 inIndex, UInt32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetSInt64(UInt32 inIndex, SInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetUInt64(UInt32 inIndex, UInt64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetFloat32(UInt32 inIndex, Float32 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetFloat64(UInt32 inIndex, Float64 inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CACFNumber theItem(inItem);
|
||||
if(theItem.IsValid())
|
||||
{
|
||||
theAnswer = SetCFType(inIndex, theItem.GetCFNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFArray::SetString(UInt32 inIndex, const CFStringRef inItem)
|
||||
{
|
||||
return SetCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::SetArray(UInt32 inIndex, const CFArrayRef inItem)
|
||||
{
|
||||
return SetCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::SetDictionary(UInt32 inIndex, const CFDictionaryRef inItem)
|
||||
{
|
||||
return SetCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::SetData(UInt32 inIndex, const CFDataRef inItem)
|
||||
{
|
||||
return SetCFType(inIndex, inItem);
|
||||
}
|
||||
|
||||
bool CACFArray::SetCFType(UInt32 inIndex, const CFTypeRef inItem)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if((mCFArray != NULL) && mMutable && (inIndex <= GetNumberItems()))
|
||||
{
|
||||
CFArraySetValueAtIndex(mCFArray, static_cast<CFIndex>(inIndex), inItem);
|
||||
theAnswer = true;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
195
BGMApp/PublicUtility/CACFArray.h
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
File: CACFArray.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CACFArray_h__)
|
||||
#define __CACFArray_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
// System Includes
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#include <CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
//=============================================================================
|
||||
// Types
|
||||
//=============================================================================
|
||||
|
||||
class CACFDictionary;
|
||||
class CACFString;
|
||||
|
||||
//=============================================================================
|
||||
// CACFArray
|
||||
//=============================================================================
|
||||
|
||||
class CACFArray
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CACFArray() : mCFArray(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)), mRelease(true), mMutable(true) {}
|
||||
explicit CACFArray(bool inRelease) : mCFArray(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)), mRelease(inRelease), mMutable(true) {}
|
||||
CACFArray(UInt32 inMaxNumberItems, bool inRelease) : mCFArray(CFArrayCreateMutable(NULL, static_cast<CFIndex>(inMaxNumberItems), &kCFTypeArrayCallBacks)), mRelease(inRelease), mMutable(true) {}
|
||||
CACFArray(CFArrayRef inCFArray, bool inRelease) : mCFArray(const_cast<CFMutableArrayRef>(inCFArray)), mRelease(inRelease), mMutable(false) {}
|
||||
CACFArray(CFMutableArrayRef inCFArray, bool inRelease) : mCFArray(inCFArray), mRelease(inRelease), mMutable(true) {}
|
||||
CACFArray(const CACFArray& inArray) : mCFArray(inArray.mCFArray), mRelease(inArray.mRelease), mMutable(inArray.mMutable) { Retain(); }
|
||||
CACFArray& operator=(const CACFArray& inArray) { Release(); mCFArray = inArray.mCFArray; mRelease = inArray.mRelease; mMutable = inArray.mMutable; Retain(); return *this; }
|
||||
CACFArray& operator=(CFArrayRef inCFArray) { Release(); mCFArray = const_cast<CFMutableArrayRef>(inCFArray); mMutable = false; Retain(); return *this; }
|
||||
CACFArray& operator=(CFMutableArrayRef inCFArray) { Release(); mCFArray = inCFArray; mMutable = true; Retain(); return *this; }
|
||||
~CACFArray() { Release(); }
|
||||
|
||||
private:
|
||||
void Retain() { if(mRelease && (mCFArray != NULL)) { CFRetain(mCFArray); } }
|
||||
void Release() { if(mRelease && (mCFArray != NULL)) { CFRelease(mCFArray); } }
|
||||
|
||||
// Attributes
|
||||
public:
|
||||
bool IsValid() const { return mCFArray != NULL; }
|
||||
bool IsMutable() const { return mMutable; }
|
||||
bool CanModify() const { return mMutable && (mCFArray != NULL); }
|
||||
|
||||
bool WillRelease() const { return mRelease; }
|
||||
void ShouldRelease(bool inRelease) { mRelease = inRelease; }
|
||||
|
||||
CFTypeID GetTypeID() const { return CFGetTypeID(mCFArray); }
|
||||
|
||||
CFArrayRef GetCFArray() const { return mCFArray; }
|
||||
CFArrayRef CopyCFArray() const { if(mCFArray != NULL) { CFRetain(mCFArray); } return mCFArray; }
|
||||
|
||||
CFMutableArrayRef GetCFMutableArray() const { return mCFArray; }
|
||||
CFMutableArrayRef CopyCFMutableArray() const { if(mCFArray != NULL) { CFRetain(mCFArray); } return mCFArray; }
|
||||
CFPropertyListRef AsPropertyList() const { return mCFArray; }
|
||||
|
||||
void SetCFMutableArrayFromCopy(CFArrayRef inArray, bool inRelease = true) { Release(); mCFArray = CFArrayCreateMutableCopy(NULL, 0, inArray); mMutable = true; mRelease = inRelease; }
|
||||
|
||||
// Item Operations
|
||||
public:
|
||||
UInt32 GetNumberItems() const { UInt32 theAnswer = 0; if(mCFArray != NULL) { theAnswer = ToUInt32(CFArrayGetCount(mCFArray)); } return theAnswer; }
|
||||
bool HasItem(const void* inItem) const;
|
||||
void RemoveItem(const void* inItem) { UInt32 theIndex; if(CanModify() && GetIndexOfItem(inItem, theIndex)) { RemoveItemAtIndex(theIndex); } }
|
||||
bool GetIndexOfItem(const void* inItem, UInt32& outIndex) const;
|
||||
void RemoveItemAtIndex(UInt32 inIndex) { if(CanModify()) { CFArrayRemoveValueAtIndex(mCFArray, static_cast<CFIndex>(inIndex)); } }
|
||||
void Clear() { if(CanModify()) { CFArrayRemoveAllValues(mCFArray); } }
|
||||
void Sort(CFComparatorFunction inCompareFunction) { if(CanModify()) { CFRange theRange = { 0, CFArrayGetCount(mCFArray) }; CFArraySortValues(mCFArray, theRange, inCompareFunction, NULL); } }
|
||||
void SortNumbers() { Sort((CFComparatorFunction)CFNumberCompare); }
|
||||
void SortStrings() { Sort((CFComparatorFunction)CFStringCompare); }
|
||||
|
||||
bool GetBool(UInt32 inIndex, bool& outValue) const;
|
||||
bool GetSInt32(UInt32 inIndex, SInt32& outItem) const;
|
||||
bool GetUInt32(UInt32 inIndex, UInt32& outItem) const;
|
||||
bool GetSInt64(UInt32 inIndex, SInt64& outItem) const;
|
||||
bool GetUInt64(UInt32 inIndex, UInt64& outItem) const;
|
||||
bool GetFloat32(UInt32 inIndex, Float32& outItem) const;
|
||||
bool GetFloat64(UInt32 inIndex, Float64& outItem) const;
|
||||
bool Get4CC(UInt32 inIndex, UInt32& outValue) const;
|
||||
bool GetString(UInt32 inIndex, CFStringRef& outItem) const;
|
||||
bool GetArray(UInt32 inIndex, CFArrayRef& outItem) const;
|
||||
bool GetDictionary(UInt32 inIndex, CFDictionaryRef& outItem) const;
|
||||
bool GetData(UInt32 inIndex, CFDataRef& outItem) const;
|
||||
bool GetUUID(UInt32 inIndex, CFUUIDRef& outItem) const;
|
||||
bool GetCFType(UInt32 inIndex, CFTypeRef& outItem) const;
|
||||
|
||||
void GetCACFString(UInt32 inIndex, CACFString& outItem) const;
|
||||
void GetCACFArray(UInt32 inIndex, CACFArray& outItem) const;
|
||||
void GetCACFDictionary(UInt32 inIndex, CACFDictionary& outItem) const;
|
||||
|
||||
bool AppendBool(bool inItem);
|
||||
bool AppendSInt32(SInt32 inItem);
|
||||
bool AppendUInt32(UInt32 inItem);
|
||||
bool AppendSInt64(SInt64 inItem);
|
||||
bool AppendUInt64(UInt64 inItem);
|
||||
bool AppendFloat32(Float32 inItem);
|
||||
bool AppendFloat64(Float64 inItem);
|
||||
bool AppendString(const CFStringRef inItem);
|
||||
bool AppendArray(const CFArrayRef inItem);
|
||||
bool AppendDictionary(const CFDictionaryRef inItem);
|
||||
bool AppendData(const CFDataRef inItem);
|
||||
bool AppendCFType(const CFTypeRef inItem);
|
||||
|
||||
bool InsertBool(UInt32 inIndex, bool inItem);
|
||||
bool InsertSInt32(UInt32 inIndex, SInt32 inItem);
|
||||
bool InsertUInt32(UInt32 inIndex, UInt32 inItem);
|
||||
bool InsertSInt64(UInt32 inIndex, SInt64 inItem);
|
||||
bool InsertUInt64(UInt32 inIndex, UInt64 inItem);
|
||||
bool InsertFloat32(UInt32 inIndex, Float32 inItem);
|
||||
bool InsertFloat64(UInt32 inIndex, Float64 inItem);
|
||||
bool InsertString(UInt32 inIndex, const CFStringRef inItem);
|
||||
bool InsertArray(UInt32 inIndex, const CFArrayRef inItem);
|
||||
bool InsertDictionary(UInt32 inIndex, const CFDictionaryRef inItem);
|
||||
bool InsertData(UInt32 inIndex, const CFDataRef inItem);
|
||||
bool InsertCFType(UInt32 inIndex, const CFTypeRef inItem);
|
||||
|
||||
bool SetBool(UInt32 inIndex, bool inItem);
|
||||
bool SetSInt32(UInt32 inIndex, SInt32 inItem);
|
||||
bool SetUInt32(UInt32 inIndex, UInt32 inItem);
|
||||
bool SetSInt64(UInt32 inIndex, SInt64 inItem);
|
||||
bool SetUInt64(UInt32 inIndex, UInt64 inItem);
|
||||
bool SetFloat32(UInt32 inIndex, Float32 inItem);
|
||||
bool SetFloat64(UInt32 inIndex, Float64 inItem);
|
||||
bool SetString(UInt32 inIndex, const CFStringRef inItem);
|
||||
bool SetArray(UInt32 inIndex, const CFArrayRef inItem);
|
||||
bool SetDictionary(UInt32 inIndex, const CFDictionaryRef inItem);
|
||||
bool SetData(UInt32 inIndex, const CFDataRef inItem);
|
||||
bool SetCFType(UInt32 inIndex, const CFTypeRef inItem);
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
CFMutableArrayRef mCFArray;
|
||||
bool mRelease;
|
||||
bool mMutable;
|
||||
|
||||
CACFArray(const void*); // prevent accidental instantiation with a pointer via bool constructor
|
||||
};
|
||||
|
||||
#endif
|
581
BGMApp/PublicUtility/CACFDictionary.cpp
Normal file
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
File: CACFDictionary.cpp
|
||||
Abstract: CACFDictionary.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CACFDictionary.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CACFArray.h"
|
||||
#include "CACFNumber.h"
|
||||
#include "CACFString.h"
|
||||
|
||||
//=============================================================================
|
||||
// CACFDictionary
|
||||
//=============================================================================
|
||||
|
||||
bool CACFDictionary::HasKey(const CFStringRef inKey) const
|
||||
{
|
||||
return CFDictionaryContainsKey(mCFDictionary, inKey) != 0;
|
||||
}
|
||||
|
||||
UInt32 CACFDictionary::Size () const
|
||||
{
|
||||
return mCFDictionary ? ToUInt32(CFDictionaryGetCount(mCFDictionary)) : 0;
|
||||
}
|
||||
|
||||
void CACFDictionary::GetKeys (const void **keys) const
|
||||
{
|
||||
CFDictionaryGetKeysAndValues(mCFDictionary, keys, NULL);
|
||||
}
|
||||
|
||||
void CACFDictionary::GetKeysAndValues (const void **keys, const void **values) const
|
||||
{
|
||||
CFDictionaryGetKeysAndValues(mCFDictionary, keys, values);
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetBool(const CFStringRef inKey, bool& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFBooleanGetTypeID()))
|
||||
{
|
||||
outValue = CFBooleanGetValue(static_cast<CFBooleanRef>(theValue));
|
||||
theAnswer = true;
|
||||
}
|
||||
else if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
SInt32 theNumericValue = 0;
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &theNumericValue);
|
||||
outValue = theNumericValue != 0;
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetSInt32(const CFStringRef inKey, SInt32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetUInt32(const CFStringRef inKey, UInt32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetSInt64(const CFStringRef inKey, SInt64& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt64Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetUInt64(const CFStringRef inKey, UInt64& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt64Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetFloat32FromString(const CFStringRef inKey, Float32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<Float32>(CFStringGetDoubleValue(static_cast<CFStringRef>(theValue)));
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetUInt32FromString(const CFStringRef inKey, UInt32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
outValue = CFStringGetIntValue(static_cast<CFStringRef>(theValue));
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetFloat32(const CFStringRef inKey, Float32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberFloat32Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetFloat64(const CFStringRef inKey, Float64& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberFloat64Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetFixed32(const CFStringRef inKey, Float32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
SInt32 theFixed32 = 0;
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &theFixed32);
|
||||
|
||||
// this is a 16.16 value so convert it to a float
|
||||
Float32 theSign = theFixed32 < 0 ? -1.0f : 1.0f;
|
||||
theFixed32 *= (SInt32)theSign;
|
||||
Float32 theWholePart = (theFixed32 & 0x7FFF0000) >> 16;
|
||||
Float32 theFractPart = theFixed32 & 0x0000FFFF;
|
||||
theFractPart /= 65536.0f;
|
||||
outValue = theSign * (theWholePart + theFractPart);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetFixed64(const CFStringRef inKey, Float64& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
SInt64 theFixed64 = 0;
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt64Type, &theFixed64);
|
||||
outValue = static_cast<Float64>(theFixed64 >> 32);
|
||||
outValue += static_cast<Float64>(theFixed64 & 0x00000000FFFFFFFFLL) / static_cast<Float64>(0x0000000100000000LL);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::Get4CC(const CFStringRef inKey, UInt32& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFNumberGetTypeID()))
|
||||
{
|
||||
CFNumberGetValue(static_cast<CFNumberRef>(theValue), kCFNumberSInt32Type, &outValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
else if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
CFStringRef theString = static_cast<CFStringRef>(theValue);
|
||||
if(CFStringGetLength(theString) == 4)
|
||||
{
|
||||
char theCString[5];
|
||||
CFStringGetCString(theString, theCString, 5, kCFStringEncodingASCII);
|
||||
outValue = CFSwapInt32BigToHost(*reinterpret_cast<UInt32*>(theCString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetString(const CFStringRef inKey, CFStringRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFStringRef>(theValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetArray(const CFStringRef inKey, CFArrayRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFArrayGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFArrayRef>(theValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetDictionary(const CFStringRef inKey, CFDictionaryRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFDictionaryGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFDictionaryRef>(theValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetData(const CFStringRef inKey, CFDataRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFDataGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFDataRef>(theValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetCFType(const CFStringRef inKey, CFTypeRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if(mCFDictionary != NULL)
|
||||
{
|
||||
outValue = CFDictionaryGetValue(mCFDictionary, inKey);
|
||||
theAnswer = (outValue != NULL);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetURL(const CFStringRef inKey, CFURLRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFURLGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFURLRef>(theValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::GetCFTypeWithCStringKey(const char* inKey, CFTypeRef& outValue) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if(mCFDictionary != NULL)
|
||||
{
|
||||
CACFString theKey(inKey);
|
||||
if(theKey.IsValid())
|
||||
{
|
||||
theAnswer = GetCFType(theKey.GetCFString(), outValue);
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CACFDictionary::GetCACFString(const CFStringRef inKey, CACFString& outValue) const
|
||||
{
|
||||
outValue = static_cast<CFStringRef>(NULL);
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFStringGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFStringRef>(theValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CACFDictionary::GetCACFArray(const CFStringRef inKey, CACFArray& outValue) const
|
||||
{
|
||||
outValue = static_cast<CFArrayRef>(NULL);
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFArrayGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFArrayRef>(theValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CACFDictionary::GetCACFDictionary(const CFStringRef inKey, CACFDictionary& outValue) const
|
||||
{
|
||||
outValue = static_cast<CFDictionaryRef>(NULL);
|
||||
CFTypeRef theValue = NULL;
|
||||
if(GetCFType(inKey, theValue))
|
||||
{
|
||||
if((theValue != NULL) && (CFGetTypeID(theValue) == CFDictionaryGetTypeID()))
|
||||
{
|
||||
outValue = static_cast<CFDictionaryRef>(theValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddBool(const CFStringRef inKey, bool inValue)
|
||||
{
|
||||
CACFBoolean theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFBoolean());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddSInt32(const CFStringRef inKey, SInt32 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddUInt32(const CFStringRef inKey, UInt32 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddSInt64(const CFStringRef inKey, SInt64 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddUInt64(const CFStringRef inKey, UInt64 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddFloat32(const CFStringRef inKey, Float32 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddFloat64(const CFStringRef inKey, Float64 inValue)
|
||||
{
|
||||
CACFNumber theValue(inValue);
|
||||
return AddCFType(inKey, theValue.GetCFNumber());
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddNumber(const CFStringRef inKey, const CFNumberRef inValue)
|
||||
{
|
||||
return AddCFType(inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddString(const CFStringRef inKey, const CFStringRef inValue)
|
||||
{
|
||||
return AddCFType(inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddArray(const CFStringRef inKey, const CFArrayRef inValue)
|
||||
{
|
||||
return AddCFType(inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddDictionary(const CFStringRef inKey, const CFDictionaryRef inValue)
|
||||
{
|
||||
return AddCFType(inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddData(const CFStringRef inKey, const CFDataRef inValue)
|
||||
{
|
||||
return AddCFType(inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddURL(const CFStringRef inKey, const CFURLRef inValue)
|
||||
{
|
||||
return AddCFType (inKey, inValue);
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddCFTypeWithCStringKey(const char* inKey, const CFTypeRef inValue)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if (inKey)
|
||||
{
|
||||
CACFString theKey(inKey);
|
||||
if(theKey.IsValid())
|
||||
{
|
||||
theAnswer = AddCFType(theKey.GetCFString(), inValue);
|
||||
}
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddCString(const CFStringRef inKey, const char* inValue)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if (inValue)
|
||||
{
|
||||
CACFString theValue(inValue);
|
||||
if(theValue.IsValid())
|
||||
{
|
||||
theAnswer = AddCFType(inKey, theValue.GetCFString());
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CACFDictionary::AddCFType(const CFStringRef inKey, const CFTypeRef inValue)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
if(mMutable && (mCFDictionary != NULL) && inValue)
|
||||
{
|
||||
CFDictionarySetValue(mCFDictionary, inKey, inValue);
|
||||
theAnswer = true;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
176
BGMApp/PublicUtility/CACFDictionary.h
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
File: CACFDictionary.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CACFDictionary_h__)
|
||||
#define __CACFDictionary_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
// System Includes
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#else
|
||||
#include <CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Types
|
||||
//=============================================================================
|
||||
|
||||
class CACFArray;
|
||||
class CACFString;
|
||||
|
||||
//=============================================================================
|
||||
// CACFDictionary
|
||||
//=============================================================================
|
||||
|
||||
class CACFDictionary
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CACFDictionary() : mCFDictionary(CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)), mRelease(true), mMutable(true) {}
|
||||
explicit CACFDictionary(bool inRelease) : mCFDictionary(CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)), mRelease(inRelease), mMutable(true) {}
|
||||
CACFDictionary(CFDictionaryRef inCFDictionary, bool inRelease) : mCFDictionary(const_cast<CFMutableDictionaryRef>(inCFDictionary)), mRelease(inRelease), mMutable(false) {}
|
||||
CACFDictionary(CFMutableDictionaryRef inCFDictionary, bool inRelease) : mCFDictionary(inCFDictionary), mRelease(inRelease), mMutable(true) {}
|
||||
CACFDictionary(const CACFDictionary& inDictionary) : mCFDictionary(inDictionary.mCFDictionary), mRelease(inDictionary.mRelease), mMutable(inDictionary.mMutable) { Retain(); }
|
||||
CACFDictionary& operator=(const CACFDictionary& inDictionary) { Release(); mCFDictionary = inDictionary.mCFDictionary; mRelease = inDictionary.mRelease; mMutable = inDictionary.mMutable; Retain(); return *this; }
|
||||
CACFDictionary& operator=(CFDictionaryRef inDictionary) { Release(); mCFDictionary = const_cast<CFMutableDictionaryRef>(inDictionary); mMutable = false; Retain(); return *this; }
|
||||
CACFDictionary& operator=(CFMutableDictionaryRef inDictionary) { Release(); mCFDictionary = inDictionary; mMutable = true; Retain(); return *this; }
|
||||
~CACFDictionary() { Release(); }
|
||||
|
||||
private:
|
||||
void Retain() { if(mRelease && (mCFDictionary != NULL)) { CFRetain(mCFDictionary); } }
|
||||
void Release() { if(mRelease && (mCFDictionary != NULL)) { CFRelease(mCFDictionary); } }
|
||||
|
||||
// Attributes
|
||||
public:
|
||||
bool IsValid() const { return mCFDictionary != NULL; }
|
||||
bool IsMutable() const { return mMutable;}
|
||||
bool CanModify() const { return mMutable && (mCFDictionary != NULL); }
|
||||
|
||||
bool WillRelease() const { return mRelease; }
|
||||
void ShouldRelease(bool inRelease) { mRelease = inRelease; }
|
||||
|
||||
CFDictionaryRef GetDict() const { return mCFDictionary; }
|
||||
CFDictionaryRef GetCFDictionary() const { return mCFDictionary; }
|
||||
CFDictionaryRef CopyCFDictionary() const { if(mCFDictionary != NULL) { CFRetain(mCFDictionary); } return mCFDictionary; }
|
||||
|
||||
CFMutableDictionaryRef GetMutableDict() { return mCFDictionary; }
|
||||
CFMutableDictionaryRef GetCFMutableDictionary() const { return mCFDictionary; }
|
||||
CFMutableDictionaryRef CopyCFMutableDictionary() const { if(mCFDictionary != NULL) { CFRetain(mCFDictionary); } return mCFDictionary; }
|
||||
void SetCFMutableDictionaryFromCopy(CFDictionaryRef inDictionary, bool inRelease = true) { Release(); mCFDictionary = CFDictionaryCreateMutableCopy(NULL, 0, inDictionary); mMutable = true; mRelease = inRelease; }
|
||||
void SetCFMutableDictionaryToEmpty(bool inRelease = true) { Release(); mCFDictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); mMutable = true; mRelease = inRelease; }
|
||||
|
||||
CFPropertyListRef AsPropertyList() const { return mCFDictionary; }
|
||||
OSStatus GetDictIfMutable(CFMutableDictionaryRef& outDict) const { OSStatus theAnswer = -1; if(mMutable) { outDict = mCFDictionary; theAnswer = 0; } return theAnswer; }
|
||||
|
||||
// Item Operations
|
||||
public:
|
||||
bool HasKey(const CFStringRef inKey) const;
|
||||
UInt32 Size() const;
|
||||
void GetKeys(const void** keys) const;
|
||||
void GetKeysAndValues (const void **keys, const void **values) const;
|
||||
|
||||
bool GetBool(const CFStringRef inKey, bool& outValue) const;
|
||||
bool GetSInt32(const CFStringRef inKey, SInt32& outValue) const;
|
||||
bool GetUInt32(const CFStringRef inKey, UInt32& outValue) const;
|
||||
bool GetUInt32FromString(const CFStringRef inKey, UInt32& outValue) const;
|
||||
bool GetSInt64(const CFStringRef inKey, SInt64& outValue) const;
|
||||
bool GetUInt64(const CFStringRef inKey, UInt64& outValue) const;
|
||||
bool GetFloat32(const CFStringRef inKey, Float32& outValue) const;
|
||||
bool GetFloat32FromString(const CFStringRef inKey, Float32& outValue) const;
|
||||
bool GetFloat64(const CFStringRef inKey, Float64& outValue) const;
|
||||
bool GetFixed32(const CFStringRef inKey, Float32& outValue) const;
|
||||
bool GetFixed64(const CFStringRef inKey, Float64& outValue) const;
|
||||
bool Get4CC(const CFStringRef inKey, UInt32& outValue) const;
|
||||
bool GetString(const CFStringRef inKey, CFStringRef& outValue) const;
|
||||
bool GetArray(const CFStringRef inKey, CFArrayRef& outValue) const;
|
||||
bool GetDictionary(const CFStringRef inKey, CFDictionaryRef& outValue) const;
|
||||
bool GetData(const CFStringRef inKey, CFDataRef& outValue) const;
|
||||
bool GetCFType(const CFStringRef inKey, CFTypeRef& outValue) const;
|
||||
bool GetURL(const CFStringRef inKey, CFURLRef& outValue) const;
|
||||
bool GetCFTypeWithCStringKey(const char* inKey, CFTypeRef& outValue) const;
|
||||
|
||||
void GetCACFString(const CFStringRef inKey, CACFString& outItem) const;
|
||||
void GetCACFArray(const CFStringRef inKey, CACFArray& outItem) const;
|
||||
void GetCACFDictionary(const CFStringRef inKey, CACFDictionary& outItem) const;
|
||||
|
||||
bool AddBool(const CFStringRef inKey, bool inValue);
|
||||
bool AddSInt32(const CFStringRef inKey, SInt32 inValue);
|
||||
bool AddUInt32(const CFStringRef inKey, UInt32 inValue);
|
||||
bool AddSInt64(const CFStringRef inKey, SInt64 inValue);
|
||||
bool AddUInt64(const CFStringRef inKey, UInt64 inValue);
|
||||
bool AddFloat32(const CFStringRef inKey, Float32 inValue);
|
||||
bool AddFloat64(const CFStringRef inKey, Float64 inValue);
|
||||
bool AddNumber(const CFStringRef inKey, const CFNumberRef inValue);
|
||||
bool AddString(const CFStringRef inKey, const CFStringRef inValue);
|
||||
bool AddArray(const CFStringRef inKey, const CFArrayRef inValue);
|
||||
bool AddDictionary(const CFStringRef inKey, const CFDictionaryRef inValue);
|
||||
bool AddData(const CFStringRef inKey, const CFDataRef inValue);
|
||||
bool AddCFType(const CFStringRef inKey, const CFTypeRef inValue);
|
||||
bool AddURL(const CFStringRef inKey, const CFURLRef inValue);
|
||||
|
||||
bool AddCFTypeWithCStringKey(const char* inKey, const CFTypeRef inValue);
|
||||
bool AddCString(const CFStringRef inKey, const char* inValue);
|
||||
|
||||
void RemoveKey(const CFStringRef inKey) { if(CanModify()) { CFDictionaryRemoveValue(mCFDictionary, inKey); } }
|
||||
void Clear() { if(CanModify()) { CFDictionaryRemoveAllValues(mCFDictionary); } }
|
||||
|
||||
void Show() { CFShow(mCFDictionary); }
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
CFMutableDictionaryRef mCFDictionary;
|
||||
bool mRelease;
|
||||
bool mMutable;
|
||||
|
||||
CACFDictionary(const void*); // prevent accidental instantiation with a pointer via bool constructor
|
||||
};
|
||||
|
||||
#endif //__CACFDictionary_h__
|
83
BGMApp/PublicUtility/CACFNumber.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
File: CACFNumber.cpp
|
||||
Abstract: CACFNumber.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CACFNumber.h"
|
||||
|
||||
//=============================================================================
|
||||
// CACFNumber
|
||||
//=============================================================================
|
||||
|
||||
Float32 CACFNumber::GetFixed32() const
|
||||
{
|
||||
SInt32 theFixedValue = GetSInt32();
|
||||
|
||||
// this is a 16.16 value so convert it to a float
|
||||
Float32 theSign = theFixedValue < 0 ? -1.0f : 1.0f;
|
||||
theFixedValue *= (SInt32)theSign;
|
||||
Float32 theWholePart = (theFixedValue & 0x7FFF0000) >> 16;
|
||||
Float32 theFractPart = theFixedValue & 0x0000FFFF;
|
||||
theFractPart /= 65536.0f;
|
||||
|
||||
return theSign * (theWholePart + theFractPart);
|
||||
}
|
||||
|
||||
Float64 CACFNumber::GetFixed64() const
|
||||
{
|
||||
SInt64 theFixedValue = GetSInt64();
|
||||
|
||||
// this is a 32.32 value so convert it to a double
|
||||
Float64 theSign = theFixedValue < 0 ? -1.0 : 1.0;
|
||||
theFixedValue *= (SInt64)theSign;
|
||||
Float64 theWholePart = (theFixedValue & 0x7FFFFFFF00000000LL) >> 32;
|
||||
Float64 theFractPart = theFixedValue & 0x00000000FFFFFFFFLL;
|
||||
theFractPart /= 4294967296.0;
|
||||
|
||||
return theSign * (theWholePart + theFractPart);
|
||||
}
|
151
BGMApp/PublicUtility/CACFNumber.h
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
File: CACFNumber.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CACFNumber_h__)
|
||||
#define __CACFNumber_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <CoreFoundation/CFNumber.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#include <CFNumber.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CACFBoolean
|
||||
//=============================================================================
|
||||
|
||||
class CACFBoolean
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
explicit CACFBoolean(CFBooleanRef inCFBoolean) : mCFBoolean(inCFBoolean), mWillRelease(true) {}
|
||||
CACFBoolean(CFBooleanRef inCFBoolean, bool inWillRelease) : mCFBoolean(inCFBoolean), mWillRelease(inWillRelease) {}
|
||||
explicit CACFBoolean(bool inValue) : mCFBoolean(inValue ? kCFBooleanTrue : kCFBooleanFalse), mWillRelease(true) { Retain(); }
|
||||
~CACFBoolean() { Release(); }
|
||||
CACFBoolean(const CACFBoolean& inBoolean) : mCFBoolean(inBoolean.mCFBoolean), mWillRelease(inBoolean.mWillRelease) { Retain(); }
|
||||
CACFBoolean& operator=(const CACFBoolean& inBoolean) { Release(); mCFBoolean = inBoolean.mCFBoolean; mWillRelease = inBoolean.mWillRelease; Retain(); return *this; }
|
||||
CACFBoolean& operator=(CFBooleanRef inCFBoolean) { Release(); mCFBoolean = inCFBoolean; mWillRelease = true; return *this; }
|
||||
|
||||
private:
|
||||
void Retain() { if(mWillRelease && (mCFBoolean != NULL)) { CFRetain(mCFBoolean); } }
|
||||
void Release() { if(mWillRelease && (mCFBoolean != NULL)) { CFRelease(mCFBoolean); } }
|
||||
|
||||
CFBooleanRef mCFBoolean;
|
||||
bool mWillRelease;
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void AllowRelease() { mWillRelease = true; }
|
||||
void DontAllowRelease() { mWillRelease = false; }
|
||||
bool IsValid() { return mCFBoolean != NULL; }
|
||||
|
||||
// Value Access
|
||||
public:
|
||||
CFBooleanRef GetCFBoolean() const { return mCFBoolean; }
|
||||
CFBooleanRef CopyCFBoolean() const { if(mCFBoolean != NULL) { CFRetain(mCFBoolean); } return mCFBoolean; }
|
||||
|
||||
bool GetBoolean() const { bool theAnswer = false; if(mCFBoolean != NULL) { theAnswer = CFEqual(mCFBoolean, kCFBooleanTrue); } return theAnswer; }
|
||||
|
||||
CACFBoolean(const void*); // prevent accidental instantiation with a pointer via bool constructor
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// CACFNumber
|
||||
//=============================================================================
|
||||
|
||||
class CACFNumber
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
explicit CACFNumber(CFNumberRef inCFNumber) : mCFNumber(inCFNumber), mWillRelease(true) {}
|
||||
CACFNumber(CFNumberRef inCFNumber, bool inWillRelease) : mCFNumber(inCFNumber), mWillRelease(inWillRelease) {}
|
||||
CACFNumber(SInt32 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberSInt32Type, &inValue)), mWillRelease(true) {}
|
||||
CACFNumber(UInt32 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberSInt32Type, &inValue)), mWillRelease(true) {}
|
||||
CACFNumber(SInt64 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberSInt64Type, &inValue)), mWillRelease(true) {}
|
||||
CACFNumber(UInt64 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberSInt64Type, &inValue)), mWillRelease(true) {}
|
||||
CACFNumber(Float32 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberFloat32Type, &inValue)), mWillRelease(true) {}
|
||||
CACFNumber(Float64 inValue) : mCFNumber(CFNumberCreate(NULL, kCFNumberFloat64Type, &inValue)), mWillRelease(true) {}
|
||||
~CACFNumber() { Release(); }
|
||||
CACFNumber(const CACFNumber& inNumber) : mCFNumber(inNumber.mCFNumber), mWillRelease(inNumber.mWillRelease) { Retain(); }
|
||||
CACFNumber& operator=(const CACFNumber& inNumber) { Release(); mCFNumber = inNumber.mCFNumber; mWillRelease = inNumber.mWillRelease; Retain(); return *this; }
|
||||
CACFNumber& operator=(CFNumberRef inCFNumber) { Release(); mCFNumber = inCFNumber; mWillRelease = true; return *this; }
|
||||
|
||||
private:
|
||||
void Retain() { if(mWillRelease && (mCFNumber != NULL)) { CFRetain(mCFNumber); } }
|
||||
void Release() { if(mWillRelease && (mCFNumber != NULL)) { CFRelease(mCFNumber); } }
|
||||
|
||||
CFNumberRef mCFNumber;
|
||||
bool mWillRelease;
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void AllowRelease() { mWillRelease = true; }
|
||||
void DontAllowRelease() { mWillRelease = false; }
|
||||
bool IsValid() const { return mCFNumber != NULL; }
|
||||
|
||||
// Value Access
|
||||
public:
|
||||
CFNumberRef GetCFNumber() const { return mCFNumber; }
|
||||
CFNumberRef CopyCFNumber() const { if(mCFNumber != NULL) { CFRetain(mCFNumber); } return mCFNumber; }
|
||||
|
||||
SInt8 GetSInt8() const { SInt8 theAnswer = 0; if(mCFNumber != NULL) { CFNumberGetValue(mCFNumber, kCFNumberSInt8Type, &theAnswer); } return theAnswer; }
|
||||
SInt32 GetSInt32() const { SInt32 theAnswer = 0; if(mCFNumber != NULL) { CFNumberGetValue(mCFNumber, kCFNumberSInt32Type, &theAnswer); } return theAnswer; }
|
||||
UInt32 GetUInt32() const { UInt32 theAnswer = 0; if(mCFNumber != NULL) { CFNumberGetValue(mCFNumber, kCFNumberSInt32Type, &theAnswer); } return theAnswer; }
|
||||
Float32 GetFloat32() const { Float32 theAnswer = 0.0f; if(mCFNumber != NULL) { CFNumberGetValue(mCFNumber, kCFNumberFloat32Type, &theAnswer); } return theAnswer; }
|
||||
Float32 GetFixed32() const;
|
||||
Float64 GetFixed64() const;
|
||||
SInt64 GetSInt64() const { SInt64 theAnswer = 0; if(mCFNumber != NULL) { CFNumberGetValue(mCFNumber, kCFNumberSInt64Type, &theAnswer); } return theAnswer; }
|
||||
|
||||
CACFNumber(const void*); // prevent accidental instantiation with a pointer via bool constructor
|
||||
};
|
||||
|
||||
#endif
|
110
BGMApp/PublicUtility/CACFString.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
File: CACFString.cpp
|
||||
Abstract: CACFString.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CACFString.h"
|
||||
|
||||
//=============================================================================
|
||||
// CACFString
|
||||
//=============================================================================
|
||||
|
||||
UInt32 CACFString::GetStringByteLength(CFStringRef inCFString, CFStringEncoding inEncoding)
|
||||
{
|
||||
CFIndex theAnswer = 0;
|
||||
|
||||
if(inCFString != NULL)
|
||||
{
|
||||
CFRange theRange = { 0, CFStringGetLength(inCFString) };
|
||||
CFStringGetBytes(inCFString, theRange, inEncoding, 0, false, NULL, 0x7FFFFFFF, &theAnswer);
|
||||
}
|
||||
|
||||
return UInt32(theAnswer);
|
||||
}
|
||||
|
||||
void CACFString::GetCString(CFStringRef inCFString, char* outString, UInt32& ioStringSize, CFStringEncoding inEncoding)
|
||||
{
|
||||
if(ioStringSize > 0)
|
||||
{
|
||||
if(inCFString != NULL)
|
||||
{
|
||||
CFIndex theLength = 0;
|
||||
CFRange theRange = { 0, CFStringGetLength(inCFString) };
|
||||
CFStringGetBytes(inCFString, theRange, inEncoding, 0, false, (UInt8*)outString, static_cast<CFIndex>(ioStringSize - 1), &theLength);
|
||||
outString[theLength] = 0;
|
||||
ioStringSize = ToUInt32(theLength) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
outString[0] = 0;
|
||||
ioStringSize = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CACFString::GetUnicodeString(CFStringRef inCFString, UInt16* outString, UInt32& ioStringSize)
|
||||
{
|
||||
if(ioStringSize > 0)
|
||||
{
|
||||
if(inCFString != NULL)
|
||||
{
|
||||
CFRange theStringRange = { 0, CFStringGetLength(inCFString) };
|
||||
if(static_cast<UInt32>(theStringRange.length) > ioStringSize)
|
||||
{
|
||||
theStringRange.length = static_cast<CFIndex>(ioStringSize);
|
||||
}
|
||||
CFStringGetCharacters(inCFString, theStringRange, outString);
|
||||
ioStringSize = ToUInt32(theStringRange.length);
|
||||
}
|
||||
else
|
||||
{
|
||||
outString[0] = 0;
|
||||
ioStringSize = 0;
|
||||
}
|
||||
}
|
||||
}
|
196
BGMApp/PublicUtility/CACFString.h
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
File: CACFString.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CACFString_h__)
|
||||
#define __CACFString_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <CoreFoundation/CFString.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#include <CFString.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CACFString
|
||||
//
|
||||
// Notes
|
||||
// - Using the AssignWithoutRetain() method will fool the static analyzer into thinking that the
|
||||
// CFString being assigned will be leaked. This is because the static analyzer is not smart
|
||||
// enough to understand that the destructor will release the object.
|
||||
//=============================================================================
|
||||
|
||||
class CACFString
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CACFString() : mCFString(NULL), mWillRelease(true) {}
|
||||
explicit CACFString(CFStringRef inCFString) : mCFString(inCFString), mWillRelease(true) {}
|
||||
CACFString(const char* inCString) : mCFString(CFStringCreateWithCString(NULL, inCString, kCFStringEncodingASCII)), mWillRelease(true) {}
|
||||
CACFString(CFStringRef inCFString, bool inWillRelease) : mCFString(inCFString), mWillRelease(inWillRelease) {}
|
||||
CACFString(const char* inCString, bool inWillRelease) : mCFString(CFStringCreateWithCString(NULL, inCString, kCFStringEncodingASCII)), mWillRelease(inWillRelease) {}
|
||||
CACFString(const char* inCString, CFStringEncoding inCStringEncoding, bool inWillRelease = true) : mCFString(CFStringCreateWithCString(NULL, inCString, inCStringEncoding)), mWillRelease(inWillRelease) {}
|
||||
~CACFString() { Release(); }
|
||||
CACFString(const CACFString& inString) : mCFString(inString.mCFString), mWillRelease(inString.mWillRelease) { Retain(); }
|
||||
CACFString& operator=(const CACFString& inString) { if (inString.mCFString != mCFString) { Release(); mCFString = inString.mCFString; mWillRelease = inString.mWillRelease; Retain(); } return *this; }
|
||||
CACFString& operator=(CFStringRef inCFString) { if (inCFString != mCFString) { Release(); mCFString = inCFString; } mWillRelease = true; Retain(); return *this; }
|
||||
void AssignWithoutRetain(CFStringRef inCFString) { if (inCFString != mCFString) { Release(); mCFString = inCFString; } mWillRelease = true; }
|
||||
|
||||
private:
|
||||
void Retain() { if(mWillRelease && (mCFString != NULL)) { CFRetain(mCFString); } }
|
||||
void Release() { if(mWillRelease && (mCFString != NULL)) { CFRelease(mCFString); } }
|
||||
|
||||
CFStringRef mCFString;
|
||||
bool mWillRelease;
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void AllowRelease() { mWillRelease = true; }
|
||||
void DontAllowRelease() { mWillRelease = false; }
|
||||
bool IsValid() const { return mCFString != NULL; }
|
||||
bool IsEqualTo(CFStringRef inString) const { bool theAnswer = false; if(mCFString != NULL) { theAnswer = CFStringCompare(inString, mCFString, 0) == kCFCompareEqualTo; } return theAnswer; }
|
||||
bool StartsWith(CFStringRef inString) const { bool theAnswer = false; if(mCFString != NULL) { theAnswer = CFStringHasPrefix(mCFString, inString); } return theAnswer; }
|
||||
bool EndsWith(CFStringRef inString) const { bool theAnswer = false; if(mCFString != NULL) { theAnswer = CFStringHasSuffix(mCFString, inString); } return theAnswer; }
|
||||
|
||||
// Value Access
|
||||
public:
|
||||
CFStringRef GetCFString() const { return mCFString; }
|
||||
CFStringRef CopyCFString() const { if(mCFString != NULL) { CFRetain(mCFString); } return mCFString; }
|
||||
const CFStringRef* GetPointerToStorage() const { return &mCFString; }
|
||||
CFStringRef& GetStorage() { Release(); mWillRelease = true; return mCFString; }
|
||||
UInt32 GetLength() const { UInt32 theAnswer = 0; if(mCFString != NULL) { theAnswer = ToUInt32(CFStringGetLength(mCFString)); } return theAnswer; }
|
||||
UInt32 GetByteLength(CFStringEncoding inEncoding = kCFStringEncodingUTF8) const { UInt32 theAnswer = 0; if(mCFString != NULL) { theAnswer = GetStringByteLength(mCFString, inEncoding); } return theAnswer; }
|
||||
void GetCString(char* outString, UInt32& ioStringSize, CFStringEncoding inEncoding = kCFStringEncodingUTF8) const { GetCString(mCFString, outString, ioStringSize, inEncoding); }
|
||||
void GetUnicodeString(UInt16* outString, UInt32& ioStringSize) const { GetUnicodeString(mCFString, outString, ioStringSize); }
|
||||
SInt32 GetAsInteger() { return GetAsInteger(mCFString); }
|
||||
Float64 GetAsFloat64() { return GetAsFloat64(mCFString); }
|
||||
|
||||
static UInt32 GetStringLength(CFStringRef inCFString) { UInt32 theAnswer = 0; if(inCFString != NULL) { theAnswer = ToUInt32(CFStringGetLength(inCFString)); } return theAnswer; }
|
||||
static UInt32 GetStringByteLength(CFStringRef inCFString, CFStringEncoding inEncoding = kCFStringEncodingUTF8);
|
||||
static void GetCString(CFStringRef inCFString, char* outString, UInt32& ioStringSize, CFStringEncoding inEncoding = kCFStringEncodingUTF8);
|
||||
static void GetUnicodeString(CFStringRef inCFString, UInt16* outString, UInt32& ioStringSize);
|
||||
static SInt32 GetAsInteger(CFStringRef inCFString) { SInt32 theAnswer = 0; if(inCFString != NULL){ theAnswer = CFStringGetIntValue(inCFString); } return theAnswer; }
|
||||
static Float64 GetAsFloat64(CFStringRef inCFString) { Float64 theAnswer = 0; if(inCFString != NULL){ theAnswer = CFStringGetDoubleValue(inCFString); } return theAnswer; }
|
||||
|
||||
};
|
||||
|
||||
inline bool operator<(const CACFString& x, const CACFString& y) { return CFStringCompare(x.GetCFString(), y.GetCFString(), 0) == kCFCompareLessThan; }
|
||||
inline bool operator==(const CACFString& x, const CACFString& y) { return CFStringCompare(x.GetCFString(), y.GetCFString(), 0) == kCFCompareEqualTo; }
|
||||
inline bool operator!=(const CACFString& x, const CACFString& y) { return !(x == y); }
|
||||
inline bool operator<=(const CACFString& x, const CACFString& y) { return (x < y) || (x == y); }
|
||||
inline bool operator>=(const CACFString& x, const CACFString& y) { return !(x < y); }
|
||||
inline bool operator>(const CACFString& x, const CACFString& y) { return !((x < y) || (x == y)); }
|
||||
|
||||
inline bool operator<(const CACFString& x, CFStringRef y) { return CFStringCompare(x.GetCFString(), y, 0) == kCFCompareLessThan; }
|
||||
inline bool operator==(const CACFString& x, CFStringRef y) { return CFStringCompare(x.GetCFString(), y, 0) == kCFCompareEqualTo; }
|
||||
inline bool operator!=(const CACFString& x, CFStringRef y) { return !(x == y); }
|
||||
inline bool operator<=(const CACFString& x, CFStringRef y) { return (x < y) || (x == y); }
|
||||
inline bool operator>=(const CACFString& x, CFStringRef y) { return !(x < y); }
|
||||
inline bool operator>(const CACFString& x, CFStringRef y) { return !((x < y) || (x == y)); }
|
||||
|
||||
inline bool operator<(CFStringRef x, const CACFString& y) { return CFStringCompare(x, y.GetCFString(), 0) == kCFCompareLessThan; }
|
||||
inline bool operator==(CFStringRef x, const CACFString& y) { return CFStringCompare(x, y.GetCFString(), 0) == kCFCompareEqualTo; }
|
||||
inline bool operator!=(CFStringRef x, const CACFString& y) { return !(x == y); }
|
||||
inline bool operator<=(CFStringRef x, const CACFString& y) { return (x < y) || (x == y); }
|
||||
inline bool operator>=(CFStringRef x, const CACFString& y) { return !(x < y); }
|
||||
inline bool operator>(CFStringRef x, const CACFString& y) { return !((x < y) || (x == y)); }
|
||||
|
||||
//=============================================================================
|
||||
// CACFMutableString
|
||||
//=============================================================================
|
||||
|
||||
class CACFMutableString
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CACFMutableString() : mCFMutableString(NULL), mWillRelease(true) {}
|
||||
CACFMutableString(CFMutableStringRef inCFMutableString, bool inWillRelease = true) : mCFMutableString(inCFMutableString), mWillRelease(inWillRelease) {}
|
||||
CACFMutableString(CFStringRef inStringToCopy, bool /*inMakeCopy*/, bool inWillRelease = true) : mCFMutableString(CFStringCreateMutableCopy(NULL, 0, inStringToCopy)), mWillRelease(inWillRelease) {}
|
||||
CACFMutableString(const char* inCString, bool inWillRelease = true) : mCFMutableString(NULL), mWillRelease(inWillRelease) { CACFString theString(inCString); mCFMutableString = CFStringCreateMutableCopy(NULL, 0, theString.GetCFString()); }
|
||||
CACFMutableString(const char* inCString, CFStringEncoding inCStringEncoding, bool inWillRelease = true) : mCFMutableString(NULL), mWillRelease(inWillRelease) { CACFString theString(inCString, inCStringEncoding); mCFMutableString = CFStringCreateMutableCopy(NULL, 0, theString.GetCFString()); }
|
||||
~CACFMutableString() { Release(); }
|
||||
CACFMutableString(const CACFMutableString& inString) : mCFMutableString(inString.mCFMutableString), mWillRelease(inString.mWillRelease) { Retain(); }
|
||||
CACFMutableString& operator=(const CACFMutableString& inString) { Release(); mCFMutableString = inString.mCFMutableString; mWillRelease = inString.mWillRelease; Retain(); return *this; }
|
||||
CACFMutableString& operator=(CFMutableStringRef inCFMutableString) { Release(); mCFMutableString = inCFMutableString; mWillRelease = true; return *this; }
|
||||
|
||||
private:
|
||||
void Retain() { if(mWillRelease && (mCFMutableString != NULL)) { CFRetain(mCFMutableString); } }
|
||||
void Release() { if(mWillRelease && (mCFMutableString != NULL)) { CFRelease(mCFMutableString); } }
|
||||
|
||||
CFMutableStringRef mCFMutableString;
|
||||
bool mWillRelease;
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void AllowRelease() { mWillRelease = true; }
|
||||
void DontAllowRelease() { mWillRelease = false; }
|
||||
bool IsValid() { return mCFMutableString != NULL; }
|
||||
bool IsEqualTo(CFStringRef inString) const { bool theAnswer = false; if(mCFMutableString != NULL) { theAnswer = CFStringCompare(inString, mCFMutableString, 0) == kCFCompareEqualTo; } return theAnswer; }
|
||||
bool StartsWith(CFStringRef inString) const { bool theAnswer = false; if(mCFMutableString != NULL) { theAnswer = CFStringHasPrefix(mCFMutableString, inString); } return theAnswer; }
|
||||
bool EndsWith(CFStringRef inString) const { bool theAnswer = false; if(mCFMutableString != NULL) { theAnswer = CFStringHasSuffix(mCFMutableString, inString); } return theAnswer; }
|
||||
void Append(CFStringRef inString) { if(mCFMutableString != NULL) { CFStringAppend(mCFMutableString, inString); } }
|
||||
|
||||
// Value Access
|
||||
public:
|
||||
CFMutableStringRef GetCFMutableString() const { return mCFMutableString; }
|
||||
CFMutableStringRef CopyCFMutableString() const { if(mCFMutableString != NULL) { CFRetain(mCFMutableString); } return mCFMutableString; }
|
||||
UInt32 GetLength() const { UInt32 theAnswer = 0; if(mCFMutableString != NULL) { theAnswer = ToUInt32(CFStringGetLength(mCFMutableString)); } return theAnswer; }
|
||||
UInt32 GetByteLength(CFStringEncoding inEncoding = kCFStringEncodingUTF8) const { UInt32 theAnswer = 0; if(mCFMutableString != NULL) { theAnswer = CACFString::GetStringByteLength(mCFMutableString, inEncoding); } return theAnswer; }
|
||||
void GetCString(char* outString, UInt32& ioStringSize, CFStringEncoding inEncoding = kCFStringEncodingUTF8) const { CACFString::GetCString(mCFMutableString, outString, ioStringSize, inEncoding); }
|
||||
void GetUnicodeString(UInt16* outString, UInt32& ioStringSize) const { CACFString::GetUnicodeString(mCFMutableString, outString, ioStringSize); }
|
||||
SInt32 GetAsInteger() { return CACFString::GetAsInteger(mCFMutableString); }
|
||||
Float64 GetAsFloat64() { return CACFString::GetAsFloat64(mCFMutableString); }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
90
BGMApp/PublicUtility/CADebugMacros.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
File: CADebugMacros.cpp
|
||||
Abstract: CADebugMacros.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#include "CADebugMacros.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#if TARGET_API_MAC_OSX
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
#include <stdio.h>
|
||||
|
||||
void DebugPrint(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
void LogError(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
#if DEBUG
|
||||
vprintf(fmt, args);
|
||||
#endif
|
||||
#if TARGET_API_MAC_OSX
|
||||
vsyslog(LOG_ERR, fmt, args);
|
||||
#endif
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void LogWarning(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
#if DEBUG
|
||||
vprintf(fmt, args);
|
||||
#endif
|
||||
#if TARGET_API_MAC_OSX
|
||||
vsyslog(LOG_WARNING, fmt, args);
|
||||
#endif
|
||||
va_end(args);
|
||||
}
|
581
BGMApp/PublicUtility/CADebugMacros.h
Normal file
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
File: CADebugMacros.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CADebugMacros_h__)
|
||||
#define __CADebugMacros_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include "CoreAudioTypes.h"
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CADebugMacros
|
||||
//=============================================================================
|
||||
|
||||
//#define CoreAudio_StopOnFailure 1
|
||||
//#define CoreAudio_TimeStampMessages 1
|
||||
//#define CoreAudio_ThreadStampMessages 1
|
||||
//#define CoreAudio_FlushDebugMessages 1
|
||||
|
||||
#if TARGET_RT_BIG_ENDIAN
|
||||
#define CA4CCToCString(the4CC) { ((char*)&the4CC)[0], ((char*)&the4CC)[1], ((char*)&the4CC)[2], ((char*)&the4CC)[3], 0 }
|
||||
#define CACopy4CCToCString(theCString, the4CC) { theCString[0] = ((char*)&the4CC)[0]; theCString[1] = ((char*)&the4CC)[1]; theCString[2] = ((char*)&the4CC)[2]; theCString[3] = ((char*)&the4CC)[3]; theCString[4] = 0; }
|
||||
#else
|
||||
#define CA4CCToCString(the4CC) { ((char*)&the4CC)[3], ((char*)&the4CC)[2], ((char*)&the4CC)[1], ((char*)&the4CC)[0], 0 }
|
||||
#define CACopy4CCToCString(theCString, the4CC) { theCString[0] = ((char*)&the4CC)[3]; theCString[1] = ((char*)&the4CC)[2]; theCString[2] = ((char*)&the4CC)[1]; theCString[3] = ((char*)&the4CC)[0]; theCString[4] = 0; }
|
||||
#endif
|
||||
|
||||
// This is a macro that does a sizeof and casts the result to a UInt32. This is useful for all the
|
||||
// places where -wshorten64-32 catches assigning a sizeof expression to a UInt32.
|
||||
// For want of a better place to park this, we'll park it here.
|
||||
#define SizeOf32(X) ((UInt32)sizeof(X))
|
||||
|
||||
// This is a macro that does a offsetof and casts the result to a UInt32. This is useful for all the
|
||||
// places where -wshorten64-32 catches assigning an offsetof expression to a UInt32.
|
||||
// For want of a better place to park this, we'll park it here.
|
||||
#define OffsetOf32(X, Y) ((UInt32)offsetof(X, Y))
|
||||
|
||||
// This macro casts the expression to a UInt32. It is called out specially to allow us to track casts
|
||||
// that have been added purely to avert -wshorten64-32 warnings on 64 bit platforms.
|
||||
// For want of a better place to park this, we'll park it here.
|
||||
#define ToUInt32(X) ((UInt32)(X))
|
||||
#define ToSInt32(X) ((SInt32)(X))
|
||||
|
||||
#pragma mark Basic Definitions
|
||||
|
||||
#if DEBUG || CoreAudio_Debug
|
||||
// can be used to break into debugger immediately, also see CADebugger
|
||||
#define BusError() { long* p=NULL; *p=0; }
|
||||
|
||||
// basic debugging print routines
|
||||
#if TARGET_OS_MAC && !TARGET_API_MAC_CARBON
|
||||
extern void DebugStr(const unsigned char* debuggerMsg);
|
||||
#define DebugMessage(msg) DebugStr("\p"msg)
|
||||
#define DebugMessageN1(msg, N1)
|
||||
#define DebugMessageN2(msg, N1, N2)
|
||||
#define DebugMessageN3(msg, N1, N2, N3)
|
||||
#else
|
||||
#include "CADebugPrintf.h"
|
||||
|
||||
#if (CoreAudio_FlushDebugMessages && !CoreAudio_UseSysLog) || defined(CoreAudio_UseSideFile)
|
||||
#define FlushRtn ,fflush(DebugPrintfFile)
|
||||
#else
|
||||
#define FlushRtn
|
||||
#endif
|
||||
|
||||
#if CoreAudio_ThreadStampMessages
|
||||
#include <pthread.h>
|
||||
#include "CAHostTimeBase.h"
|
||||
#if TARGET_RT_64_BIT
|
||||
#define DebugPrintfThreadIDFormat "%16p"
|
||||
#else
|
||||
#define DebugPrintfThreadIDFormat "%8p"
|
||||
#endif
|
||||
#define DebugMsg(inFormat, ...) DebugPrintf("%17qd: " DebugPrintfThreadIDFormat " " inFormat, CAHostTimeBase::GetCurrentTimeInNanos(), pthread_self(), ## __VA_ARGS__) FlushRtn
|
||||
#elif CoreAudio_TimeStampMessages
|
||||
#include "CAHostTimeBase.h"
|
||||
#define DebugMsg(inFormat, ...) DebugPrintf("%17qd: " inFormat, CAHostTimeBase::GetCurrentTimeInNanos(), ## __VA_ARGS__) FlushRtn
|
||||
#else
|
||||
#define DebugMsg(inFormat, ...) DebugPrintf(inFormat, ## __VA_ARGS__) FlushRtn
|
||||
#endif
|
||||
#endif
|
||||
void DebugPrint(const char *fmt, ...); // can be used like printf
|
||||
#ifndef DEBUGPRINT
|
||||
#define DEBUGPRINT(msg) DebugPrint msg // have to double-parenthesize arglist (see Debugging.h)
|
||||
#endif
|
||||
#if VERBOSE
|
||||
#define vprint(msg) DEBUGPRINT(msg)
|
||||
#else
|
||||
#define vprint(msg)
|
||||
#endif
|
||||
|
||||
// Original macro keeps its function of turning on and off use of CADebuggerStop() for both asserts and throws.
|
||||
// For backwards compat, it overrides any setting of the two sub-macros.
|
||||
#if CoreAudio_StopOnFailure
|
||||
#include "CADebugger.h"
|
||||
#undef CoreAudio_StopOnAssert
|
||||
#define CoreAudio_StopOnAssert 1
|
||||
#undef CoreAudio_StopOnThrow
|
||||
#define CoreAudio_StopOnThrow 1
|
||||
#define STOP CADebuggerStop()
|
||||
#else
|
||||
#define STOP
|
||||
#endif
|
||||
|
||||
#if CoreAudio_StopOnAssert
|
||||
#if !CoreAudio_StopOnFailure
|
||||
#include "CADebugger.h"
|
||||
#define STOP
|
||||
#endif
|
||||
#define __ASSERT_STOP CADebuggerStop()
|
||||
#else
|
||||
#define __ASSERT_STOP
|
||||
#endif
|
||||
|
||||
#if CoreAudio_StopOnThrow
|
||||
#if !CoreAudio_StopOnFailure
|
||||
#include "CADebugger.h"
|
||||
#define STOP
|
||||
#endif
|
||||
#define __THROW_STOP CADebuggerStop()
|
||||
#else
|
||||
#define __THROW_STOP
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define DebugMsg(inFormat, ...)
|
||||
#ifndef DEBUGPRINT
|
||||
#define DEBUGPRINT(msg)
|
||||
#endif
|
||||
#define vprint(msg)
|
||||
#define STOP
|
||||
#define __ASSERT_STOP
|
||||
#define __THROW_STOP
|
||||
#endif
|
||||
|
||||
// Old-style numbered DebugMessage calls are implemented in terms of DebugMsg() now
|
||||
#define DebugMessage(msg) DebugMsg(msg)
|
||||
#define DebugMessageN1(msg, N1) DebugMsg(msg, N1)
|
||||
#define DebugMessageN2(msg, N1, N2) DebugMsg(msg, N1, N2)
|
||||
#define DebugMessageN3(msg, N1, N2, N3) DebugMsg(msg, N1, N2, N3)
|
||||
#define DebugMessageN4(msg, N1, N2, N3, N4) DebugMsg(msg, N1, N2, N3, N4)
|
||||
#define DebugMessageN5(msg, N1, N2, N3, N4, N5) DebugMsg(msg, N1, N2, N3, N4, N5)
|
||||
#define DebugMessageN6(msg, N1, N2, N3, N4, N5, N6) DebugMsg(msg, N1, N2, N3, N4, N5, N6)
|
||||
#define DebugMessageN7(msg, N1, N2, N3, N4, N5, N6, N7) DebugMsg(msg, N1, N2, N3, N4, N5, N6, N7)
|
||||
#define DebugMessageN8(msg, N1, N2, N3, N4, N5, N6, N7, N8) DebugMsg(msg, N1, N2, N3, N4, N5, N6, N7, N8)
|
||||
#define DebugMessageN9(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9) DebugMsg(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9)
|
||||
|
||||
void LogError(const char *fmt, ...); // writes to syslog (and stderr if debugging)
|
||||
void LogWarning(const char *fmt, ...); // writes to syslog (and stderr if debugging)
|
||||
|
||||
#define NO_ACTION (void)0
|
||||
|
||||
#if DEBUG || CoreAudio_Debug
|
||||
|
||||
#pragma mark Debug Macros
|
||||
|
||||
#define Assert(inCondition, inMessage) \
|
||||
if(!(inCondition)) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
__ASSERT_STOP; \
|
||||
}
|
||||
|
||||
#define AssertFileLine(inCondition, inMessage) \
|
||||
if(!(inCondition)) \
|
||||
{ \
|
||||
DebugMessageN3("%s, line %d: %s", __FILE__, __LINE__, inMessage); \
|
||||
__ASSERT_STOP; \
|
||||
}
|
||||
|
||||
#define AssertNoError(inError, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
char __4CC[5] = CA4CCToCString(__Err); \
|
||||
DebugMessageN2(inMessage ", Error: %d (%s)", (int)__Err, __4CC); \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define AssertNoKernelError(inError, inMessage) \
|
||||
{ \
|
||||
unsigned int __Err = (unsigned int)(inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
DebugMessageN1(inMessage ", Error: 0x%X", __Err); \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define AssertNotNULL(inPtr, inMessage) \
|
||||
{ \
|
||||
if((inPtr) == NULL) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIf(inCondition, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
STOP; \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailWithAction(inCondition, inAction, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfNULL(inPointer, inAction, inHandler, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfKernelError(inKernelError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
unsigned int __Err = (inKernelError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
DebugMessageN1(inMessage ", Error: 0x%X", __Err); \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIfError(inError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
char __4CC[5] = CA4CCToCString(__Err); \
|
||||
DebugMessageN2(inMessage ", Error: %ld (%s)", (long int)__Err, __4CC); \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIfNoMessage(inCondition, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailWithActionNoMessage(inCondition, inAction, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfNULLNoMessage(inPointer, inAction, inHandler, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfKernelErrorNoMessage(inKernelError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
unsigned int __Err = (inKernelError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIfErrorNoMessage(inError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
#define Throw(inException) __THROW_STOP; throw (inException)
|
||||
|
||||
#define ThrowIf(inCondition, inException, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#define ThrowIfNULL(inPointer, inException, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
DebugMessage(inMessage); \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#define ThrowIfKernelError(inKernelError, inException, inMessage) \
|
||||
{ \
|
||||
int __Err = (inKernelError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
DebugMessageN1(inMessage ", Error: 0x%X", __Err); \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ThrowIfError(inError, inException, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
char __4CC[5] = CA4CCToCString(__Err); \
|
||||
DebugMessageN2(inMessage ", Error: %d (%s)", (int)__Err, __4CC); \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
#define ThrowIfWinError(inError, inException, inMessage) \
|
||||
{ \
|
||||
HRESULT __Err = (inError); \
|
||||
if(FAILED(__Err)) \
|
||||
{ \
|
||||
DebugMessageN2(inMessage ", Code: %d, Facility: 0x%X", HRESULT_CODE(__Err), HRESULT_FACILITY(__Err)); \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SubclassResponsibility(inMethodName, inException) \
|
||||
{ \
|
||||
DebugMessage(inMethodName": Subclasses must implement this method"); \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#else
|
||||
|
||||
#pragma mark Release Macros
|
||||
|
||||
#define Assert(inCondition, inMessage) \
|
||||
if(!(inCondition)) \
|
||||
{ \
|
||||
__ASSERT_STOP; \
|
||||
}
|
||||
|
||||
#define AssertFileLine(inCondition, inMessage) Assert(inCondition, inMessage)
|
||||
|
||||
#define AssertNoError(inError, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define AssertNoKernelError(inError, inMessage) \
|
||||
{ \
|
||||
unsigned int __Err = (unsigned int)(inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define AssertNotNULL(inPtr, inMessage) \
|
||||
{ \
|
||||
if((inPtr) == NULL) \
|
||||
{ \
|
||||
__ASSERT_STOP; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIf(inCondition, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailWithAction(inCondition, inAction, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfNULL(inPointer, inAction, inHandler, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfKernelError(inKernelError, inAction, inHandler, inMessage) \
|
||||
if((inKernelError) != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfError(inError, inAction, inHandler, inMessage) \
|
||||
if((inError) != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfNoMessage(inCondition, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailWithActionNoMessage(inCondition, inAction, inHandler, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfNULLNoMessage(inPointer, inAction, inHandler, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
}
|
||||
|
||||
#define FailIfKernelErrorNoMessage(inKernelError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
unsigned int __Err = (inKernelError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FailIfErrorNoMessage(inError, inAction, inHandler, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
STOP; \
|
||||
{ inAction; } \
|
||||
goto inHandler; \
|
||||
} \
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
#define Throw(inException) __THROW_STOP; throw (inException)
|
||||
|
||||
#define ThrowIf(inCondition, inException, inMessage) \
|
||||
if(inCondition) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#define ThrowIfNULL(inPointer, inException, inMessage) \
|
||||
if((inPointer) == NULL) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#define ThrowIfKernelError(inKernelError, inException, inMessage) \
|
||||
{ \
|
||||
int __Err = (inKernelError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ThrowIfError(inError, inException, inMessage) \
|
||||
{ \
|
||||
SInt32 __Err = (inError); \
|
||||
if(__Err != 0) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
#define ThrowIfWinError(inError, inException, inMessage) \
|
||||
{ \
|
||||
HRESULT __Err = (inError); \
|
||||
if(FAILED(__Err)) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SubclassResponsibility(inMethodName, inException) \
|
||||
{ \
|
||||
Throw(inException); \
|
||||
}
|
||||
|
||||
#endif // defined(__cplusplus)
|
||||
|
||||
#endif // DEBUG || CoreAudio_Debug
|
||||
|
||||
#endif
|
89
BGMApp/PublicUtility/CADebugPrintf.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
File: CADebugPrintf.cpp
|
||||
Abstract: CADebugPrintf.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CADebugPrintf.h"
|
||||
|
||||
#if DEBUG || CoreAudio_Debug
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <Windows.h>
|
||||
extern "C"
|
||||
int CAWin32DebugPrintf(char* inFormat, ...)
|
||||
{
|
||||
char theMessage[1024];
|
||||
va_list theArguments;
|
||||
va_start(theArguments, inFormat);
|
||||
_vsnprintf(theMessage, 1024, inFormat, theArguments);
|
||||
va_end(theArguments);
|
||||
OutputDebugString(theMessage);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CoreAudio_UseSideFile)
|
||||
#include <unistd.h>
|
||||
FILE* sDebugPrintfSideFile = NULL;
|
||||
extern "C"
|
||||
void OpenDebugPrintfSideFile()
|
||||
{
|
||||
if(sDebugPrintfSideFile == NULL)
|
||||
{
|
||||
char theFileName[1024];
|
||||
snprintf(theFileName, sizeof(theFileName), CoreAudio_UseSideFile, getpid());
|
||||
sDebugPrintfSideFile = fopen(theFileName, "a+");
|
||||
DebugPrintfRtn(DebugPrintfFileComma "\n------------------------------\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
115
BGMApp/PublicUtility/CADebugPrintf.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
File: CADebugPrintf.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CADebugPrintf_h__)
|
||||
#define __CADebugPrintf_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include "CoreAudioTypes.h"
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Macros to redirect debugging output to various logging services
|
||||
//=============================================================================
|
||||
|
||||
//#define CoreAudio_UseSysLog 1
|
||||
//#define CoreAudio_UseSideFile "/CoreAudio-%d.txt"
|
||||
|
||||
#if DEBUG || CoreAudio_Debug
|
||||
|
||||
#if TARGET_OS_WIN32
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
#endif
|
||||
extern int CAWin32DebugPrintf(char* inFormat, ...);
|
||||
#define DebugPrintfRtn CAWin32DebugPrintf
|
||||
#define DebugPrintfFile
|
||||
#define DebugPrintfLineEnding "\n"
|
||||
#define DebugPrintfFileComma
|
||||
#else
|
||||
#if CoreAudio_UseSysLog
|
||||
#include <sys/syslog.h>
|
||||
#define DebugPrintfRtn syslog
|
||||
#define DebugPrintfFile LOG_NOTICE
|
||||
#define DebugPrintfLineEnding ""
|
||||
#define DebugPrintfFileComma DebugPrintfFile,
|
||||
#elif defined(CoreAudio_UseSideFile)
|
||||
#include <stdio.h>
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
#endif
|
||||
void OpenDebugPrintfSideFile();
|
||||
extern FILE* sDebugPrintfSideFile;
|
||||
#define DebugPrintfRtn fprintf
|
||||
#define DebugPrintfFile ((sDebugPrintfSideFile != NULL) ? sDebugPrintfSideFile : stderr)
|
||||
#define DebugPrintfLineEnding "\n"
|
||||
#define DebugPrintfFileComma DebugPrintfFile,
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#define DebugPrintfRtn fprintf
|
||||
#define DebugPrintfFile stderr
|
||||
#define DebugPrintfLineEnding "\n"
|
||||
#define DebugPrintfFileComma DebugPrintfFile,
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define DebugPrintf(inFormat, ...) DebugPrintfRtn(DebugPrintfFileComma inFormat DebugPrintfLineEnding, ## __VA_ARGS__)
|
||||
#else
|
||||
#define DebugPrintfRtn
|
||||
#define DebugPrintfFile
|
||||
#define DebugPrintfLineEnding
|
||||
#define DebugPrintfFileComma
|
||||
#define DebugPrintf(inFormat, ...)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
103
BGMApp/PublicUtility/CADebugger.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
File: CADebugger.cpp
|
||||
Abstract: CADebugger.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CADebugger.h"
|
||||
|
||||
//=============================================================================
|
||||
// CADebugger
|
||||
//=============================================================================
|
||||
|
||||
#if TARGET_API_MAC_OSX
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
bool CAIsDebuggerAttached(void)
|
||||
{
|
||||
int mib[4];
|
||||
struct kinfo_proc info;
|
||||
size_t size;
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = getpid();
|
||||
size = sizeof(info);
|
||||
info.kp_proc.p_flag = 0;
|
||||
|
||||
sysctl(mib, 4, &info, &size, NULL, 0);
|
||||
|
||||
return (info.kp_proc.p_flag & P_TRACED) == P_TRACED;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CADebuggerStop(void)
|
||||
{
|
||||
#if CoreAudio_Debug
|
||||
#if TARGET_API_MAC_OSX
|
||||
if(CAIsDebuggerAttached())
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
asm("int3");
|
||||
#else
|
||||
__builtin_trap();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
abort();
|
||||
}
|
||||
#else
|
||||
__debugbreak();
|
||||
#endif
|
||||
#endif
|
||||
}
|
69
BGMApp/PublicUtility/CADebugger.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
File: CADebugger.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CADebugger_h__)
|
||||
#define __CADebugger_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CADebugger
|
||||
//=============================================================================
|
||||
|
||||
#if TARGET_API_MAC_OSX
|
||||
extern bool CAIsDebuggerAttached(void);
|
||||
#endif
|
||||
extern void CADebuggerStop(void);
|
||||
|
||||
#endif
|
83
BGMApp/PublicUtility/CAException.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
File: CAException.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAException_h__)
|
||||
#define __CAException_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include "CoreAudioTypes.h"
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CAException
|
||||
//=============================================================================
|
||||
|
||||
class CAException
|
||||
{
|
||||
|
||||
public:
|
||||
CAException(OSStatus inError) : mError(inError) {}
|
||||
CAException(const CAException& inException) : mError(inException.mError) {}
|
||||
CAException& operator=(const CAException& inException) { mError = inException.mError; return *this; }
|
||||
~CAException() {}
|
||||
|
||||
OSStatus GetError() const { return mError; }
|
||||
|
||||
protected:
|
||||
OSStatus mError;
|
||||
};
|
||||
|
||||
#define CATry try{
|
||||
#define CACatch } catch(...) {}
|
||||
#define CASwallowException(inExpression) try { inExpression; } catch(...) {}
|
||||
|
||||
#endif
|
1156
BGMApp/PublicUtility/CAHALAudioDevice.cpp
Normal file
238
BGMApp/PublicUtility/CAHALAudioDevice.h
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
File: CAHALAudioDevice.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAHALAudioDevice_h__)
|
||||
#define __CAHALAudioDevice_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Super Class Includes
|
||||
#include "CAHALAudioObject.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
|
||||
//==================================================================================================
|
||||
// CAHALAudioDevice
|
||||
//==================================================================================================
|
||||
|
||||
class CAHALAudioDevice
|
||||
:
|
||||
public CAHALAudioObject
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAHALAudioDevice(AudioObjectID inAudioDevice);
|
||||
CAHALAudioDevice(CFStringRef inUID);
|
||||
virtual ~CAHALAudioDevice();
|
||||
|
||||
// General Stuff
|
||||
public:
|
||||
CFStringRef CopyDeviceUID() const;
|
||||
bool HasModelUID() const;
|
||||
CFStringRef CopyModelUID() const;
|
||||
CFStringRef CopyConfigurationApplicationBundleID() const;
|
||||
CFURLRef CopyIconLocation() const;
|
||||
UInt32 GetTransportType() const;
|
||||
bool CanBeDefaultDevice(bool inIsInput, bool inIsSystem) const;
|
||||
bool HasDevicePlugInStatus() const;
|
||||
OSStatus GetDevicePlugInStatus() const;
|
||||
bool IsAlive() const;
|
||||
bool IsHidden() const;
|
||||
pid_t GetHogModeOwner() const;
|
||||
bool IsHogModeSettable() const;
|
||||
bool TakeHogMode();
|
||||
void ReleaseHogMode();
|
||||
bool HasPreferredStereoChannels(bool inIsInput) const;
|
||||
void GetPreferredStereoChannels(bool inIsInput, UInt32& outLeft, UInt32& outRight) const;
|
||||
void SetPreferredStereoChannels(bool inIsInput, UInt32 inLeft, UInt32 inRight);
|
||||
bool HasPreferredChannelLayout(bool inIsInput) const;
|
||||
void GetPreferredChannelLayout(bool inIsInput, AudioChannelLayout& outChannelLayout) const;
|
||||
void SetPreferredStereoChannels(bool inIsInput, AudioChannelLayout& inChannelLayout);
|
||||
UInt32 GetNumberRelatedAudioDevices() const;
|
||||
void GetRelatedAudioDevices(UInt32& ioNumberRelatedDevices, AudioObjectID* outRelatedDevices) const;
|
||||
AudioObjectID GetRelatedAudioDeviceByIndex(UInt32 inIndex) const;
|
||||
|
||||
// Stream Stuff
|
||||
public:
|
||||
UInt32 GetNumberStreams(bool inIsInput) const;
|
||||
void GetStreams(bool inIsInput, UInt32& ioNumberStreams, AudioObjectID* outStreamList) const;
|
||||
AudioObjectID GetStreamByIndex(bool inIsInput, UInt32 inIndex) const;
|
||||
UInt32 GetTotalNumberChannels(bool inIsInput) const;
|
||||
void GetCurrentVirtualFormats(bool inIsInput, UInt32& ioNumberStreams, AudioStreamBasicDescription* outFormats) const;
|
||||
void GetCurrentPhysicalFormats(bool inIsInput, UInt32& ioNumberStreams, AudioStreamBasicDescription* outFormats) const;
|
||||
|
||||
// IO Stuff
|
||||
public:
|
||||
bool IsRunning() const;
|
||||
bool IsRunningSomewhere() const;
|
||||
UInt32 GetLatency(bool inIsInput) const;
|
||||
UInt32 GetSafetyOffset(bool inIsInput) const;
|
||||
bool HasClockDomain() const;
|
||||
UInt32 GetClockDomain() const;
|
||||
Float64 GetActualSampleRate() const;
|
||||
Float64 GetNominalSampleRate() const;
|
||||
void SetNominalSampleRate(Float64 inSampleRate);
|
||||
UInt32 GetNumberAvailableNominalSampleRateRanges() const;
|
||||
void GetAvailableNominalSampleRateRanges(UInt32& ioNumberRanges, AudioValueRange* outRanges) const;
|
||||
void GetAvailableNominalSampleRateRangeByIndex(UInt32 inIndex, Float64& outMinimum, Float64& outMaximum) const;
|
||||
bool IsValidNominalSampleRate(Float64 inSampleRate) const;
|
||||
bool IsIOBufferSizeSettable() const;
|
||||
UInt32 GetIOBufferSize() const;
|
||||
void SetIOBufferSize(UInt32 inBufferSize);
|
||||
bool UsesVariableIOBufferSizes() const;
|
||||
UInt32 GetMaximumVariableIOBufferSize() const;
|
||||
bool HasIOBufferSizeRange() const;
|
||||
void GetIOBufferSizeRange(UInt32& outMinimum, UInt32& outMaximum) const;
|
||||
AudioDeviceIOProcID CreateIOProcID(AudioDeviceIOProc inIOProc, void* inClientData);
|
||||
AudioDeviceIOProcID CreateIOProcIDWithBlock(dispatch_queue_t inDispatchQueue, AudioDeviceIOBlock inIOBlock);
|
||||
void DestroyIOProcID(AudioDeviceIOProcID inIOProcID);
|
||||
void StartIOProc(AudioDeviceIOProcID inIOProcID);
|
||||
void StartIOProcAtTime(AudioDeviceIOProcID inIOProcID, AudioTimeStamp& ioStartTime, bool inIsInput, bool inIgnoreHardware);
|
||||
void StopIOProc(AudioDeviceIOProcID inIOProcID);
|
||||
void GetIOProcStreamUsage(AudioDeviceIOProcID inIOProcID, bool inIsInput, bool* outStreamUsage) const;
|
||||
void SetIOProcStreamUsage(AudioDeviceIOProcID inIOProcID, bool inIsInput, const bool* inStreamUsage);
|
||||
Float32 GetIOCycleUsage() const;
|
||||
void SetIOCycleUsage(Float32 inValue);
|
||||
|
||||
// Time Operations
|
||||
public:
|
||||
void GetCurrentTime(AudioTimeStamp& outTime);
|
||||
void TranslateTime(const AudioTimeStamp& inTime, AudioTimeStamp& outTime);
|
||||
void GetNearestStartTime(AudioTimeStamp& ioTime, bool inIsInput, bool inIgnoreHardware);
|
||||
|
||||
// Controls
|
||||
public:
|
||||
bool HasVolumeControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool VolumeControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
Float32 GetVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
Float32 GetVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void SetVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue);
|
||||
void SetVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue);
|
||||
Float32 GetVolumeControlScalarForDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const;
|
||||
Float32 GetVolumeControlDecibelForScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const;
|
||||
|
||||
bool HasSubVolumeControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool SubVolumeControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
Float32 GetSubVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
Float32 GetSubVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void SetSubVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue);
|
||||
void SetSubVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue);
|
||||
Float32 GetSubVolumeControlScalarForDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const;
|
||||
Float32 GetSubVolumeControlDecibelForScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const;
|
||||
|
||||
bool HasMuteControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool MuteControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool GetMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void SetMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue);
|
||||
|
||||
bool HasSoloControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool SoloControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool GetSoloControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void SetSoloControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue);
|
||||
|
||||
bool HasStereoPanControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool StereoPanControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
Float32 GetStereoPanControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void SetStereoPanControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue);
|
||||
void GetStereoPanControlChannels(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32& outLeftChannel, UInt32& outRightChannel) const;
|
||||
|
||||
bool HasJackControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool GetJackControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
|
||||
bool HasSubMuteControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool SubMuteControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool GetSubMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void SetSubMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue);
|
||||
|
||||
bool HasiSubOwnerControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool iSubOwnerControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool GetiSubOwnerControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void SetiSubOwnerControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue);
|
||||
|
||||
bool HasDataSourceControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool DataSourceControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
UInt32 GetCurrentDataSourceID(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void SetCurrentDataSourceByID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID);
|
||||
UInt32 GetNumberAvailableDataSources(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void GetAvailableDataSources(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32& ioNumberSources, UInt32* outSources) const;
|
||||
UInt32 GetAvailableDataSourceByIndex(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inIndex) const;
|
||||
CFStringRef CopyDataSourceNameForID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID) const;
|
||||
|
||||
bool HasDataDestinationControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
bool DataDestinationControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
UInt32 GetCurrentDataDestinationID(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void SetCurrentDataDestinationByID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID);
|
||||
UInt32 GetNumberAvailableDataDestinations(AudioObjectPropertyScope inScope, UInt32 inChannel) const;
|
||||
void GetAvailableDataDestinations(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32& ioNumberDestinations, UInt32* outDestinations) const;
|
||||
UInt32 GetAvailableDataDestinationByIndex(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inIndex) const;
|
||||
CFStringRef CopyDataDestinationNameForID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID) const;
|
||||
|
||||
bool HasClockSourceControl() const;
|
||||
bool ClockSourceControlIsSettable() const;
|
||||
UInt32 GetCurrentClockSourceID() const;
|
||||
void SetCurrentClockSourceByID(UInt32 inID);
|
||||
UInt32 GetNumberAvailableClockSources() const;
|
||||
void GetAvailableClockSources(UInt32& ioNumberSources, UInt32* outSources) const;
|
||||
UInt32 GetAvailableClockSourceByIndex(UInt32 inIndex) const;
|
||||
CFStringRef CopyClockSourceNameForID(UInt32 inID) const;
|
||||
UInt32 GetClockSourceKindForID(UInt32 inID) const;
|
||||
|
||||
};
|
||||
|
||||
inline AudioDeviceIOProcID CAHALAudioDevice::CreateIOProcIDWithBlock(dispatch_queue_t inDispatchQueue, AudioDeviceIOBlock inIOBlock)
|
||||
{
|
||||
AudioDeviceIOProcID theAnswer = NULL;
|
||||
OSStatus theError = AudioDeviceCreateIOProcIDWithBlock(&theAnswer, mObjectID, inDispatchQueue, inIOBlock);
|
||||
ThrowIfError(theError, CAException(theError), "CAHALAudioDevice::CreateIOProcIDWithBlock: got an error creating the IOProc ID");
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#endif
|
370
BGMApp/PublicUtility/CAHALAudioObject.cpp
Normal file
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
File: CAHALAudioObject.cpp
|
||||
Abstract: CAHALAudioObject.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CAHALAudioObject.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAAutoDisposer.h"
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
#include "CAPropertyAddress.h"
|
||||
|
||||
//==================================================================================================
|
||||
// CAHALAudioObject
|
||||
//==================================================================================================
|
||||
|
||||
CAHALAudioObject::CAHALAudioObject(AudioObjectID inObjectID)
|
||||
:
|
||||
mObjectID(inObjectID)
|
||||
{
|
||||
}
|
||||
|
||||
CAHALAudioObject::~CAHALAudioObject()
|
||||
{
|
||||
}
|
||||
|
||||
AudioObjectID CAHALAudioObject::GetObjectID() const
|
||||
{
|
||||
return mObjectID;
|
||||
}
|
||||
|
||||
void CAHALAudioObject::SetObjectID(AudioObjectID inObjectID)
|
||||
{
|
||||
mObjectID = inObjectID;
|
||||
}
|
||||
|
||||
AudioClassID CAHALAudioObject::GetClassID() const
|
||||
{
|
||||
// set up the return value
|
||||
AudioClassID theAnswer = 0;
|
||||
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyClass);
|
||||
|
||||
// make sure the property exists
|
||||
if(HasProperty(theAddress))
|
||||
{
|
||||
UInt32 theSize = sizeof(AudioClassID);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
AudioObjectID CAHALAudioObject::GetOwnerObjectID() const
|
||||
{
|
||||
// set up the return value
|
||||
AudioObjectID theAnswer = 0;
|
||||
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyOwner);
|
||||
|
||||
// make sure the property exists
|
||||
if(HasProperty(theAddress))
|
||||
{
|
||||
// get the property data
|
||||
UInt32 theSize = sizeof(AudioObjectID);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
CFStringRef CAHALAudioObject::CopyOwningPlugInBundleID() const
|
||||
{
|
||||
// set up the return value
|
||||
CFStringRef theAnswer = NULL;
|
||||
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyCreator);
|
||||
|
||||
// make sure the property exists
|
||||
if(HasProperty(theAddress))
|
||||
{
|
||||
// get the property data
|
||||
UInt32 theSize = sizeof(CFStringRef);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
CFStringRef CAHALAudioObject::CopyName() const
|
||||
{
|
||||
// set up the return value
|
||||
CFStringRef theAnswer = NULL;
|
||||
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyName);
|
||||
|
||||
// make sure the property exists
|
||||
if(HasProperty(theAddress))
|
||||
{
|
||||
// get the property data
|
||||
UInt32 theSize = sizeof(CFStringRef);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
CFStringRef CAHALAudioObject::CopyManufacturer() const
|
||||
{
|
||||
// set up the return value
|
||||
CFStringRef theAnswer = NULL;
|
||||
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyManufacturer);
|
||||
|
||||
// make sure the property exists
|
||||
if(HasProperty(theAddress))
|
||||
{
|
||||
// get the property data
|
||||
UInt32 theSize = sizeof(CFStringRef);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
CFStringRef CAHALAudioObject::CopyNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const
|
||||
{
|
||||
// set up the return value
|
||||
CFStringRef theAnswer = NULL;
|
||||
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyElementName, inScope, inElement);
|
||||
|
||||
// make sure the property exists
|
||||
if(HasProperty(theAddress))
|
||||
{
|
||||
// get the property data
|
||||
UInt32 theSize = sizeof(CFStringRef);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
CFStringRef CAHALAudioObject::CopyCategoryNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const
|
||||
{
|
||||
// set up the return value
|
||||
CFStringRef theAnswer = NULL;
|
||||
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyElementCategoryName, inScope, inElement);
|
||||
|
||||
// make sure the property exists
|
||||
if(HasProperty(theAddress))
|
||||
{
|
||||
// get the property data
|
||||
UInt32 theSize = sizeof(CFStringRef);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
CFStringRef CAHALAudioObject::CopyNumberNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const
|
||||
{
|
||||
// set up the return value
|
||||
CFStringRef theAnswer = NULL;
|
||||
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyElementNumberName, inScope, inElement);
|
||||
|
||||
// make sure the property exists
|
||||
if(HasProperty(theAddress))
|
||||
{
|
||||
// get the property data
|
||||
UInt32 theSize = sizeof(CFStringRef);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CAHALAudioObject::ObjectExists(AudioObjectID inObjectID)
|
||||
{
|
||||
Boolean isSettable;
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyClass);
|
||||
return (inObjectID == 0) || (AudioObjectIsPropertySettable(inObjectID, &theAddress, &isSettable) != 0);
|
||||
}
|
||||
|
||||
UInt32 CAHALAudioObject::GetNumberOwnedObjects(AudioClassID inClass) const
|
||||
{
|
||||
// set up the return value
|
||||
UInt32 theAnswer = 0;
|
||||
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyOwnedObjects);
|
||||
|
||||
// figure out the qualifier
|
||||
UInt32 theQualifierSize = 0;
|
||||
void* theQualifierData = NULL;
|
||||
if(inClass != 0)
|
||||
{
|
||||
theQualifierSize = sizeof(AudioObjectID);
|
||||
theQualifierData = &inClass;
|
||||
}
|
||||
|
||||
// get the property data size
|
||||
theAnswer = GetPropertyDataSize(theAddress, theQualifierSize, theQualifierData);
|
||||
|
||||
// calculate the number of object IDs
|
||||
theAnswer /= SizeOf32(AudioObjectID);
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CAHALAudioObject::GetAllOwnedObjects(AudioClassID inClass, UInt32& ioNumberObjects, AudioObjectID* ioObjectIDs) const
|
||||
{
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyOwnedObjects);
|
||||
|
||||
// figure out the qualifier
|
||||
UInt32 theQualifierSize = 0;
|
||||
void* theQualifierData = NULL;
|
||||
if(inClass != 0)
|
||||
{
|
||||
theQualifierSize = sizeof(AudioObjectID);
|
||||
theQualifierData = &inClass;
|
||||
}
|
||||
|
||||
// get the property data
|
||||
UInt32 theDataSize = ioNumberObjects * SizeOf32(AudioClassID);
|
||||
GetPropertyData(theAddress, theQualifierSize, theQualifierData, theDataSize, ioObjectIDs);
|
||||
|
||||
// set the number of object IDs being returned
|
||||
ioNumberObjects = theDataSize / SizeOf32(AudioObjectID);
|
||||
}
|
||||
|
||||
AudioObjectID CAHALAudioObject::GetOwnedObjectByIndex(AudioClassID inClass, UInt32 inIndex)
|
||||
{
|
||||
// set up the property address
|
||||
CAPropertyAddress theAddress(kAudioObjectPropertyOwnedObjects);
|
||||
|
||||
// figure out the qualifier
|
||||
UInt32 theQualifierSize = 0;
|
||||
void* theQualifierData = NULL;
|
||||
if(inClass != 0)
|
||||
{
|
||||
theQualifierSize = sizeof(AudioObjectID);
|
||||
theQualifierData = &inClass;
|
||||
}
|
||||
|
||||
// figure out how much space to allocate
|
||||
UInt32 theDataSize = GetPropertyDataSize(theAddress, theQualifierSize, theQualifierData);
|
||||
UInt32 theNumberObjectIDs = theDataSize / SizeOf32(AudioObjectID);
|
||||
|
||||
// set up the return value
|
||||
AudioObjectID theAnswer = 0;
|
||||
|
||||
// maker sure the index is in range
|
||||
if(inIndex < theNumberObjectIDs)
|
||||
{
|
||||
// allocate it
|
||||
CAAutoArrayDelete<AudioObjectID> theObjectList(theDataSize / sizeof(AudioObjectID));
|
||||
|
||||
// get the property data
|
||||
GetPropertyData(theAddress, theQualifierSize, theQualifierData, theDataSize, theObjectList);
|
||||
|
||||
// get the return value
|
||||
theAnswer = theObjectList[inIndex];
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CAHALAudioObject::HasProperty(const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
return AudioObjectHasProperty(mObjectID, &inAddress);
|
||||
}
|
||||
|
||||
bool CAHALAudioObject::IsPropertySettable(const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
Boolean isSettable = false;
|
||||
OSStatus theError = AudioObjectIsPropertySettable(mObjectID, &inAddress, &isSettable);
|
||||
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::IsPropertySettable: got an error getting info about a property");
|
||||
return isSettable != 0;
|
||||
}
|
||||
|
||||
UInt32 CAHALAudioObject::GetPropertyDataSize(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const
|
||||
{
|
||||
UInt32 theDataSize = 0;
|
||||
OSStatus theError = AudioObjectGetPropertyDataSize(mObjectID, &inAddress, inQualifierDataSize, inQualifierData, &theDataSize);
|
||||
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::GetPropertyDataSize: got an error getting the property data size");
|
||||
return theDataSize;
|
||||
}
|
||||
|
||||
void CAHALAudioObject::GetPropertyData(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32& ioDataSize, void* outData) const
|
||||
{
|
||||
OSStatus theError = AudioObjectGetPropertyData(mObjectID, &inAddress, inQualifierDataSize, inQualifierData, &ioDataSize, outData);
|
||||
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::GetPropertyData: got an error getting the property data");
|
||||
}
|
||||
|
||||
void CAHALAudioObject::SetPropertyData(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
|
||||
{
|
||||
OSStatus theError = AudioObjectSetPropertyData(mObjectID, &inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData);
|
||||
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::SetPropertyData: got an error setting the property data");
|
||||
}
|
||||
|
||||
void CAHALAudioObject::AddPropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData)
|
||||
{
|
||||
OSStatus theError = AudioObjectAddPropertyListener(mObjectID, &inAddress, inListenerProc, inClientData);
|
||||
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::AddPropertyListener: got an error adding a property listener");
|
||||
}
|
||||
|
||||
void CAHALAudioObject::RemovePropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData)
|
||||
{
|
||||
OSStatus theError = AudioObjectRemovePropertyListener(mObjectID, &inAddress, inListenerProc, inClientData);
|
||||
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::RemovePropertyListener: got an error removing a property listener");
|
||||
}
|
155
BGMApp/PublicUtility/CAHALAudioObject.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
File: CAHALAudioObject.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAHALAudioObject_h__)
|
||||
#define __CAHALAudioObject_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
|
||||
// System Includes
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#else
|
||||
#include <CoreAudio.h>
|
||||
#include <CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
//==================================================================================================
|
||||
// CAHALAudioObject
|
||||
//==================================================================================================
|
||||
|
||||
class CAHALAudioObject
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAHALAudioObject(AudioObjectID inObjectID);
|
||||
virtual ~CAHALAudioObject();
|
||||
|
||||
// Attributes
|
||||
public:
|
||||
AudioObjectID GetObjectID() const;
|
||||
void SetObjectID(AudioObjectID inObjectID);
|
||||
AudioClassID GetClassID() const;
|
||||
AudioObjectID GetOwnerObjectID() const;
|
||||
CFStringRef CopyOwningPlugInBundleID() const;
|
||||
CFStringRef CopyName() const;
|
||||
CFStringRef CopyManufacturer() const;
|
||||
CFStringRef CopyNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const;
|
||||
CFStringRef CopyCategoryNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const;
|
||||
CFStringRef CopyNumberNameForElement(AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) const;
|
||||
|
||||
static bool ObjectExists(AudioObjectID inObjectID);
|
||||
|
||||
// Owned Objects
|
||||
public:
|
||||
UInt32 GetNumberOwnedObjects(AudioClassID inClass) const;
|
||||
void GetAllOwnedObjects(AudioClassID inClass, UInt32& ioNumberObjects, AudioObjectID* ioObjectIDs) const;
|
||||
AudioObjectID GetOwnedObjectByIndex(AudioClassID inClass, UInt32 inIndex);
|
||||
|
||||
// Property Operations
|
||||
public:
|
||||
bool HasProperty(const AudioObjectPropertyAddress& inAddress) const;
|
||||
bool IsPropertySettable(const AudioObjectPropertyAddress& inAddress) const;
|
||||
UInt32 GetPropertyDataSize(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const;
|
||||
|
||||
void GetPropertyData(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32& ioDataSize, void* outData) const;
|
||||
void SetPropertyData(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
|
||||
|
||||
UInt32 GetPropertyData_UInt32(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { UInt32 theAnswer = 0; UInt32 theDataSize = SizeOf32(UInt32); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &theAnswer); return theAnswer; }
|
||||
void SetPropertyData_UInt32(const AudioObjectPropertyAddress& inAddress, UInt32 inValue, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(UInt32), &inValue); }
|
||||
|
||||
Float32 GetPropertyData_Float32(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { Float32 theAnswer = 0; UInt32 theDataSize = SizeOf32(Float32); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &theAnswer); return theAnswer; }
|
||||
void SetPropertyData_Float32(const AudioObjectPropertyAddress& inAddress, Float32 inValue, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(Float32), &inValue); }
|
||||
|
||||
Float64 GetPropertyData_Float64(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { Float64 theAnswer = 0; UInt32 theDataSize = SizeOf32(Float64); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &theAnswer); return theAnswer; }
|
||||
void SetPropertyData_Float64(const AudioObjectPropertyAddress& inAddress, Float64 inValue, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(Float64), &inValue); }
|
||||
|
||||
CFTypeRef GetPropertyData_CFType(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { CFTypeRef theAnswer = NULL; UInt32 theDataSize = SizeOf32(CFTypeRef); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &theAnswer); return theAnswer; }
|
||||
void SetPropertyData_CFType(const AudioObjectPropertyAddress& inAddress, CFTypeRef inValue, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(CFTypeRef), &inValue); }
|
||||
|
||||
CFStringRef GetPropertyData_CFString(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { CFStringRef theAnswer = NULL; UInt32 theDataSize = SizeOf32(CFStringRef); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &theAnswer); return theAnswer; }
|
||||
void SetPropertyData_CFString(const AudioObjectPropertyAddress& inAddress, CFStringRef inValue, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(CFStringRef), &inValue); }
|
||||
|
||||
template <class T> void GetPropertyData_Struct(const AudioObjectPropertyAddress& inAddress, T& outStruct, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { UInt32 theDataSize = SizeOf32(T); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, &outStruct); }
|
||||
template <class T> void SetPropertyData_Struct(const AudioObjectPropertyAddress& inAddress, T& inStruct, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, SizeOf32(T), &inStruct); }
|
||||
|
||||
template <class T> UInt32 GetPropertyData_ArraySize(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { return GetPropertyDataSize(inAddress, inQualifierDataSize, inQualifierData) / SizeOf32(T); }
|
||||
template <class T> void GetPropertyData_Array(const AudioObjectPropertyAddress& inAddress, UInt32& ioNumberItems, T* outArray, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) const { UInt32 theDataSize = ioNumberItems * SizeOf32(T); GetPropertyData(inAddress, inQualifierDataSize, inQualifierData, theDataSize, outArray); ioNumberItems = theDataSize / SizeOf32(T); }
|
||||
template <class T> void SetPropertyData_Array(const AudioObjectPropertyAddress& inAddress, UInt32 inNumberItems, T* inArray, UInt32 inQualifierDataSize = 0, const void* inQualifierData = NULL) { SetPropertyData(inAddress, inQualifierDataSize, inQualifierData, inNumberItems * SizeOf32(T), inArray); }
|
||||
|
||||
void AddPropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData);
|
||||
void RemovePropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData);
|
||||
|
||||
void AddPropertyListenerBlock(const AudioObjectPropertyAddress& inAddress, dispatch_queue_t inDispatchQueue, AudioObjectPropertyListenerBlock inListenerBlock);
|
||||
void RemovePropertyListenerBlock(const AudioObjectPropertyAddress& inAddress, dispatch_queue_t inDispatchQueue, AudioObjectPropertyListenerBlock inListenerBlock);
|
||||
|
||||
// Implementation
|
||||
protected:
|
||||
AudioObjectID mObjectID;
|
||||
|
||||
};
|
||||
|
||||
inline void CAHALAudioObject::AddPropertyListenerBlock(const AudioObjectPropertyAddress& inAddress, dispatch_queue_t inDispatchQueue, AudioObjectPropertyListenerBlock inListenerBlock)
|
||||
{
|
||||
OSStatus theError = AudioObjectAddPropertyListenerBlock(mObjectID, &inAddress, inDispatchQueue, inListenerBlock);
|
||||
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::AddPropertyListenerBlock: got an error adding a property listener");
|
||||
}
|
||||
|
||||
inline void CAHALAudioObject::RemovePropertyListenerBlock(const AudioObjectPropertyAddress& inAddress, dispatch_queue_t inDispatchQueue, AudioObjectPropertyListenerBlock inListenerBlock)
|
||||
{
|
||||
OSStatus theError = AudioObjectRemovePropertyListenerBlock(mObjectID, &inAddress, inDispatchQueue, inListenerBlock);
|
||||
ThrowIfError(theError, CAException(theError), "CAHALAudioObject::RemovePropertyListener: got an error removing a property listener");
|
||||
}
|
||||
|
||||
#endif
|
182
BGMApp/PublicUtility/CAHALAudioStream.cpp
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
File: CAHALAudioStream.cpp
|
||||
Abstract: CAHALAudioStream.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CAHALAudioStream.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAAutoDisposer.h"
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
#include "CAPropertyAddress.h"
|
||||
|
||||
//==================================================================================================
|
||||
// CAHALAudioStream
|
||||
//==================================================================================================
|
||||
|
||||
CAHALAudioStream::CAHALAudioStream(AudioObjectID inAudioStream)
|
||||
:
|
||||
CAHALAudioObject(inAudioStream)
|
||||
{
|
||||
}
|
||||
|
||||
CAHALAudioStream::~CAHALAudioStream()
|
||||
{
|
||||
}
|
||||
|
||||
UInt32 CAHALAudioStream::GetDirection() const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyDirection);
|
||||
return GetPropertyData_UInt32(theAddress, 0, NULL);
|
||||
}
|
||||
|
||||
UInt32 CAHALAudioStream::GetTerminalType() const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyTerminalType);
|
||||
return GetPropertyData_UInt32(theAddress, 0, NULL);
|
||||
}
|
||||
|
||||
UInt32 CAHALAudioStream::GetStartingChannel() const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyStartingChannel);
|
||||
return GetPropertyData_UInt32(theAddress, 0, NULL);
|
||||
}
|
||||
|
||||
UInt32 CAHALAudioStream::GetLatency() const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyLatency);
|
||||
return GetPropertyData_UInt32(theAddress, 0, NULL);
|
||||
}
|
||||
|
||||
void CAHALAudioStream::GetCurrentVirtualFormat(AudioStreamBasicDescription& outFormat) const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyVirtualFormat);
|
||||
UInt32 theSize = sizeof(AudioStreamBasicDescription);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &outFormat);
|
||||
}
|
||||
|
||||
void CAHALAudioStream::SetCurrentVirtualFormat(const AudioStreamBasicDescription& inFormat)
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyVirtualFormat);
|
||||
SetPropertyData(theAddress, 0, NULL, sizeof(AudioStreamBasicDescription), &inFormat);
|
||||
}
|
||||
|
||||
UInt32 CAHALAudioStream::GetNumberAvailableVirtualFormats() const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyAvailableVirtualFormats);
|
||||
UInt32 theAnswer = GetPropertyDataSize(theAddress, 0, NULL);
|
||||
theAnswer /= SizeOf32(AudioStreamRangedDescription);
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CAHALAudioStream::GetAvailableVirtualFormats(UInt32& ioNumberFormats, AudioStreamRangedDescription* outFormats) const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyAvailableVirtualFormats);
|
||||
UInt32 theSize = ioNumberFormats * SizeOf32(AudioStreamRangedDescription);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, outFormats);
|
||||
ioNumberFormats = theSize / SizeOf32(AudioStreamRangedDescription);
|
||||
}
|
||||
|
||||
void CAHALAudioStream::GetAvailableVirtualFormatByIndex(UInt32 inIndex, AudioStreamRangedDescription& outFormat) const
|
||||
{
|
||||
UInt32 theNumberFormats = GetNumberAvailableVirtualFormats();
|
||||
if((theNumberFormats > 0) && (inIndex < theNumberFormats))
|
||||
{
|
||||
CAAutoArrayDelete<AudioStreamRangedDescription> theFormats(theNumberFormats);
|
||||
GetAvailableVirtualFormats(theNumberFormats, theFormats);
|
||||
if((theNumberFormats > 0) && (inIndex < theNumberFormats))
|
||||
{
|
||||
outFormat = theFormats[inIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CAHALAudioStream::GetCurrentPhysicalFormat(AudioStreamBasicDescription& outFormat) const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyPhysicalFormat);
|
||||
UInt32 theSize = sizeof(AudioStreamBasicDescription);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &outFormat);
|
||||
}
|
||||
|
||||
void CAHALAudioStream::SetCurrentPhysicalFormat(const AudioStreamBasicDescription& inFormat)
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyPhysicalFormat);
|
||||
SetPropertyData(theAddress, 0, NULL, sizeof(AudioStreamBasicDescription), &inFormat);
|
||||
}
|
||||
|
||||
UInt32 CAHALAudioStream::GetNumberAvailablePhysicalFormats() const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyAvailablePhysicalFormats);
|
||||
UInt32 theAnswer = GetPropertyDataSize(theAddress, 0, NULL);
|
||||
theAnswer /= SizeOf32(AudioStreamRangedDescription);
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CAHALAudioStream::GetAvailablePhysicalFormats(UInt32& ioNumberFormats, AudioStreamRangedDescription* outFormats) const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioStreamPropertyAvailablePhysicalFormats);
|
||||
UInt32 theSize = ioNumberFormats * SizeOf32(AudioStreamRangedDescription);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, outFormats);
|
||||
ioNumberFormats = theSize / SizeOf32(AudioStreamRangedDescription);
|
||||
}
|
||||
|
||||
void CAHALAudioStream::GetAvailablePhysicalFormatByIndex(UInt32 inIndex, AudioStreamRangedDescription& outFormat) const
|
||||
{
|
||||
UInt32 theNumberFormats = GetNumberAvailablePhysicalFormats();
|
||||
if((theNumberFormats > 0) && (inIndex < theNumberFormats))
|
||||
{
|
||||
CAAutoArrayDelete<AudioStreamRangedDescription> theFormats(theNumberFormats);
|
||||
GetAvailablePhysicalFormats(theNumberFormats, theFormats);
|
||||
if((theNumberFormats > 0) && (inIndex < theNumberFormats))
|
||||
{
|
||||
outFormat = theFormats[inIndex];
|
||||
}
|
||||
}
|
||||
}
|
94
BGMApp/PublicUtility/CAHALAudioStream.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
File: CAHALAudioStream.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAHALAudioStream_h__)
|
||||
#define __CAHALAudioStream_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Super Class Includes
|
||||
#include "CAHALAudioObject.h"
|
||||
|
||||
//==================================================================================================
|
||||
// CAHALAudioStream
|
||||
//==================================================================================================
|
||||
|
||||
class CAHALAudioStream
|
||||
:
|
||||
public CAHALAudioObject
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAHALAudioStream(AudioObjectID inAudioStream);
|
||||
virtual ~CAHALAudioStream();
|
||||
|
||||
// Attributes
|
||||
public:
|
||||
UInt32 GetDirection() const;
|
||||
UInt32 GetTerminalType() const;
|
||||
UInt32 GetStartingChannel() const;
|
||||
UInt32 GetLatency() const;
|
||||
|
||||
// Format Info
|
||||
public:
|
||||
void GetCurrentVirtualFormat(AudioStreamBasicDescription& outFormat) const;
|
||||
void SetCurrentVirtualFormat(const AudioStreamBasicDescription& inFormat);
|
||||
UInt32 GetNumberAvailableVirtualFormats() const;
|
||||
void GetAvailableVirtualFormats(UInt32& ioNumberFormats, AudioStreamRangedDescription* outFormats) const;
|
||||
void GetAvailableVirtualFormatByIndex(UInt32 inIndex, AudioStreamRangedDescription& outFormat) const;
|
||||
|
||||
void GetCurrentPhysicalFormat(AudioStreamBasicDescription& outFormat) const;
|
||||
void SetCurrentPhysicalFormat(const AudioStreamBasicDescription& inFormat);
|
||||
UInt32 GetNumberAvailablePhysicalFormats() const;
|
||||
void GetAvailablePhysicalFormats(UInt32& ioNumberFormats, AudioStreamRangedDescription* outFormats) const;
|
||||
void GetAvailablePhysicalFormatByIndex(UInt32 inIndex, AudioStreamRangedDescription& outFormat) const;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
181
BGMApp/PublicUtility/CAHALAudioSystemObject.cpp
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
File: CAHALAudioSystemObject.cpp
|
||||
Abstract: CAHALAudioSystemObject.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CAHALAudioSystemObject.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAAutoDisposer.h"
|
||||
#include "CACFString.h"
|
||||
#include "CAHALAudioDevice.h"
|
||||
#include "CAPropertyAddress.h"
|
||||
|
||||
//==================================================================================================
|
||||
// CAHALAudioSystemObject
|
||||
//==================================================================================================
|
||||
|
||||
CAHALAudioSystemObject::CAHALAudioSystemObject()
|
||||
:
|
||||
CAHALAudioObject(kAudioObjectSystemObject)
|
||||
{
|
||||
}
|
||||
|
||||
CAHALAudioSystemObject::~CAHALAudioSystemObject()
|
||||
{
|
||||
}
|
||||
|
||||
UInt32 CAHALAudioSystemObject::GetNumberAudioDevices() const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioHardwarePropertyDevices);
|
||||
UInt32 theAnswer = GetPropertyDataSize(theAddress, 0, NULL);
|
||||
theAnswer /= SizeOf32(AudioObjectID);
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CAHALAudioSystemObject::GetAudioDevices(UInt32& ioNumberAudioDevices, AudioObjectID* outAudioDevices) const
|
||||
{
|
||||
CAPropertyAddress theAddress(kAudioHardwarePropertyDevices);
|
||||
UInt32 theSize = ioNumberAudioDevices * SizeOf32(AudioObjectID);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, outAudioDevices);
|
||||
ioNumberAudioDevices = theSize / SizeOf32(AudioObjectID);
|
||||
}
|
||||
|
||||
AudioObjectID CAHALAudioSystemObject::GetAudioDeviceAtIndex(UInt32 inIndex) const
|
||||
{
|
||||
AudioObjectID theAnswer = kAudioObjectUnknown;
|
||||
UInt32 theNumberDevices = GetNumberAudioDevices();
|
||||
if((theNumberDevices > 0) && (inIndex < theNumberDevices))
|
||||
{
|
||||
CAAutoArrayDelete<AudioObjectID> theDeviceList(theNumberDevices);
|
||||
GetAudioDevices(theNumberDevices, theDeviceList);
|
||||
if((theNumberDevices > 0) && (inIndex < theNumberDevices))
|
||||
{
|
||||
theAnswer = theDeviceList[inIndex];
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
AudioObjectID CAHALAudioSystemObject::GetAudioDeviceForUID(CFStringRef inUID) const
|
||||
{
|
||||
AudioObjectID theAnswer = kAudioObjectUnknown;
|
||||
AudioValueTranslation theValue = { &inUID, sizeof(CFStringRef), &theAnswer, sizeof(AudioObjectID) };
|
||||
CAPropertyAddress theAddress(kAudioHardwarePropertyDeviceForUID);
|
||||
UInt32 theSize = sizeof(AudioValueTranslation);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theValue);
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CAHALAudioSystemObject::LogBasicDeviceInfo()
|
||||
{
|
||||
UInt32 theNumberDevices = GetNumberAudioDevices();
|
||||
CAAutoArrayDelete<AudioObjectID> theDeviceList(theNumberDevices);
|
||||
GetAudioDevices(theNumberDevices, theDeviceList);
|
||||
DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: %d devices", (int)theNumberDevices);
|
||||
for(UInt32 theDeviceIndex = 0; theDeviceIndex < theNumberDevices; ++theDeviceIndex)
|
||||
{
|
||||
char theCString[256];
|
||||
UInt32 theCStringSize = sizeof(theCString);
|
||||
DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: Device %d", (int)theDeviceIndex);
|
||||
|
||||
CAHALAudioDevice theDevice(theDeviceList[theDeviceIndex]);
|
||||
DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: Object ID: %d", (int)theDeviceList[theDeviceIndex]);
|
||||
|
||||
CACFString theDeviceName(theDevice.CopyName());
|
||||
theCStringSize = sizeof(theCString);
|
||||
theDeviceName.GetCString(theCString, theCStringSize);
|
||||
DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: Name: %s", theCString);
|
||||
|
||||
CACFString theDeviceUID(theDevice.CopyDeviceUID());
|
||||
theCStringSize = sizeof(theCString);
|
||||
theDeviceUID.GetCString(theCString, theCStringSize);
|
||||
DebugMessageN1("CAHALAudioSystemObject::LogBasicDeviceInfo: UID: %s", theCString);
|
||||
}
|
||||
}
|
||||
|
||||
static inline AudioObjectPropertySelector CAHALAudioSystemObject_CalculateDefaultDeviceProperySelector(bool inIsInput, bool inIsSystem)
|
||||
{
|
||||
AudioObjectPropertySelector theAnswer = kAudioHardwarePropertyDefaultOutputDevice;
|
||||
if(inIsInput)
|
||||
{
|
||||
theAnswer = kAudioHardwarePropertyDefaultInputDevice;
|
||||
}
|
||||
else if(inIsSystem)
|
||||
{
|
||||
theAnswer = kAudioHardwarePropertyDefaultSystemOutputDevice;
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
AudioObjectID CAHALAudioSystemObject::GetDefaultAudioDevice(bool inIsInput, bool inIsSystem) const
|
||||
{
|
||||
AudioObjectID theAnswer = kAudioObjectUnknown;
|
||||
CAPropertyAddress theAddress(CAHALAudioSystemObject_CalculateDefaultDeviceProperySelector(inIsInput, inIsSystem));
|
||||
UInt32 theSize = sizeof(AudioObjectID);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theAnswer);
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CAHALAudioSystemObject::SetDefaultAudioDevice(bool inIsInput, bool inIsSystem, AudioObjectID inNewDefaultDevice)
|
||||
{
|
||||
CAPropertyAddress theAddress(CAHALAudioSystemObject_CalculateDefaultDeviceProperySelector(inIsInput, inIsSystem));
|
||||
UInt32 theSize = sizeof(AudioObjectID);
|
||||
SetPropertyData(theAddress, 0, NULL, theSize, &inNewDefaultDevice);
|
||||
}
|
||||
|
||||
AudioObjectID CAHALAudioSystemObject::GetAudioPlugInForBundleID(CFStringRef inUID) const
|
||||
{
|
||||
AudioObjectID theAnswer = kAudioObjectUnknown;
|
||||
AudioValueTranslation theValue = { &inUID, sizeof(CFStringRef), &theAnswer, sizeof(AudioObjectID) };
|
||||
CAPropertyAddress theAddress(kAudioHardwarePropertyPlugInForBundleID);
|
||||
UInt32 theSize = sizeof(AudioValueTranslation);
|
||||
GetPropertyData(theAddress, 0, NULL, theSize, &theValue);
|
||||
return theAnswer;
|
||||
}
|
90
BGMApp/PublicUtility/CAHALAudioSystemObject.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
File: CAHALAudioSystemObject.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAHALAudioSystemObject_h__)
|
||||
#define __CAHALAudioSystemObject_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Super Class Includes
|
||||
#include "CAHALAudioObject.h"
|
||||
|
||||
//==================================================================================================
|
||||
// CAHALAudioSystemObject
|
||||
//==================================================================================================
|
||||
|
||||
class CAHALAudioSystemObject
|
||||
:
|
||||
public CAHALAudioObject
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAHALAudioSystemObject();
|
||||
virtual ~CAHALAudioSystemObject();
|
||||
|
||||
// Audio Device List Management
|
||||
public:
|
||||
UInt32 GetNumberAudioDevices() const;
|
||||
void GetAudioDevices(UInt32& ioNumberAudioDevices, AudioObjectID* outAudioDevices) const;
|
||||
AudioObjectID GetAudioDeviceAtIndex(UInt32 inIndex) const;
|
||||
AudioObjectID GetAudioDeviceForUID(CFStringRef inUID) const;
|
||||
void LogBasicDeviceInfo();
|
||||
|
||||
// Default Device Management
|
||||
public:
|
||||
AudioObjectID GetDefaultAudioDevice(bool inIsInput, bool inIsSystem) const;
|
||||
void SetDefaultAudioDevice(bool inIsInput, bool inIsSystem, AudioObjectID inNewDefaultDevice);
|
||||
|
||||
// PlugIns
|
||||
public:
|
||||
AudioObjectID GetAudioPlugInForBundleID(CFStringRef inBundleID) const;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
99
BGMApp/PublicUtility/CAHostTimeBase.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
File: CAHostTimeBase.cpp
|
||||
Abstract: CAHostTimeBase.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#include "CAHostTimeBase.h"
|
||||
|
||||
Float64 CAHostTimeBase::sFrequency = 0;
|
||||
Float64 CAHostTimeBase::sInverseFrequency = 0;
|
||||
UInt32 CAHostTimeBase::sMinDelta = 0;
|
||||
UInt32 CAHostTimeBase::sToNanosNumerator = 0;
|
||||
UInt32 CAHostTimeBase::sToNanosDenominator = 0;
|
||||
pthread_once_t CAHostTimeBase::sIsInited = PTHREAD_ONCE_INIT;
|
||||
#if Track_Host_TimeBase
|
||||
UInt64 CAHostTimeBase::sLastTime = 0;
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// CAHostTimeBase
|
||||
//
|
||||
// This class provides platform independent access to the host's time base.
|
||||
//=============================================================================
|
||||
|
||||
void CAHostTimeBase::Initialize()
|
||||
{
|
||||
// get the info about Absolute time
|
||||
#if TARGET_OS_MAC
|
||||
struct mach_timebase_info theTimeBaseInfo;
|
||||
mach_timebase_info(&theTimeBaseInfo);
|
||||
sMinDelta = 1;
|
||||
sToNanosNumerator = theTimeBaseInfo.numer;
|
||||
sToNanosDenominator = theTimeBaseInfo.denom;
|
||||
|
||||
// the frequency of that clock is: (sToNanosDenominator / sToNanosNumerator) * 10^9
|
||||
sFrequency = static_cast<Float64>(sToNanosDenominator) / static_cast<Float64>(sToNanosNumerator);
|
||||
sFrequency *= 1000000000.0;
|
||||
#elif TARGET_OS_WIN32
|
||||
LARGE_INTEGER theFrequency;
|
||||
QueryPerformanceFrequency(&theFrequency);
|
||||
sMinDelta = 1;
|
||||
sToNanosNumerator = 1000000000ULL;
|
||||
sToNanosDenominator = *((UInt64*)&theFrequency);
|
||||
sFrequency = static_cast<Float64>(*((UInt64*)&theFrequency));
|
||||
#endif
|
||||
sInverseFrequency = 1.0 / sFrequency;
|
||||
|
||||
#if Log_Host_Time_Base_Parameters
|
||||
DebugPrintf("Host Time Base Parameters");
|
||||
DebugPrintf(" Minimum Delta: %lu", (unsigned long)sMinDelta);
|
||||
DebugPrintf(" Frequency: %f", sFrequency);
|
||||
DebugPrintf(" To Nanos Numerator: %lu", (unsigned long)sToNanosNumerator);
|
||||
DebugPrintf(" To Nanos Denominator: %lu", (unsigned long)sToNanosDenominator);
|
||||
#endif
|
||||
}
|
234
BGMApp/PublicUtility/CAHostTimeBase.h
Normal file
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
File: CAHostTimeBase.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAHostTimeBase_h__)
|
||||
#define __CAHostTimeBase_h__
|
||||
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
#include <mach/mach_time.h>
|
||||
#include <pthread.h>
|
||||
#elif TARGET_OS_WIN32
|
||||
#include <windows.h>
|
||||
#include "WinPThreadDefs.h"
|
||||
#else
|
||||
#error Unsupported operating system
|
||||
#endif
|
||||
|
||||
#include "CADebugPrintf.h"
|
||||
|
||||
//=============================================================================
|
||||
// CAHostTimeBase
|
||||
//
|
||||
// This class provides platform independent access to the host's time base.
|
||||
//=============================================================================
|
||||
|
||||
#if CoreAudio_Debug
|
||||
// #define Log_Host_Time_Base_Parameters 1
|
||||
// #define Track_Host_TimeBase 1
|
||||
#endif
|
||||
|
||||
class CAHostTimeBase
|
||||
{
|
||||
|
||||
public:
|
||||
static UInt64 ConvertToNanos(UInt64 inHostTime);
|
||||
static UInt64 ConvertFromNanos(UInt64 inNanos);
|
||||
|
||||
static UInt64 GetTheCurrentTime();
|
||||
#if TARGET_OS_MAC
|
||||
static UInt64 GetCurrentTime() { return GetTheCurrentTime(); }
|
||||
#endif
|
||||
static UInt64 GetCurrentTimeInNanos();
|
||||
|
||||
static Float64 GetFrequency() { pthread_once(&sIsInited, Initialize); return sFrequency; }
|
||||
static Float64 GetInverseFrequency() { pthread_once(&sIsInited, Initialize); return sInverseFrequency; }
|
||||
static UInt32 GetMinimumDelta() { pthread_once(&sIsInited, Initialize); return sMinDelta; }
|
||||
|
||||
static UInt64 AbsoluteHostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime);
|
||||
static SInt64 HostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime);
|
||||
|
||||
static UInt64 MultiplyByRatio(UInt64 inMuliplicand, UInt32 inNumerator, UInt32 inDenominator);
|
||||
|
||||
private:
|
||||
static void Initialize();
|
||||
|
||||
static pthread_once_t sIsInited;
|
||||
|
||||
static Float64 sFrequency;
|
||||
static Float64 sInverseFrequency;
|
||||
static UInt32 sMinDelta;
|
||||
static UInt32 sToNanosNumerator;
|
||||
static UInt32 sToNanosDenominator;
|
||||
#if Track_Host_TimeBase
|
||||
static UInt64 sLastTime;
|
||||
#endif
|
||||
};
|
||||
|
||||
inline UInt64 CAHostTimeBase::GetTheCurrentTime()
|
||||
{
|
||||
UInt64 theTime = 0;
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
theTime = mach_absolute_time();
|
||||
#elif TARGET_OS_WIN32
|
||||
LARGE_INTEGER theValue;
|
||||
QueryPerformanceCounter(&theValue);
|
||||
theTime = *((UInt64*)&theValue);
|
||||
#endif
|
||||
|
||||
#if Track_Host_TimeBase
|
||||
if(sLastTime != 0)
|
||||
{
|
||||
if(theTime <= sLastTime)
|
||||
{
|
||||
DebugPrintf("CAHostTimeBase::GetTheCurrentTime: the current time is earlier than the last time, now: %qd, then: %qd", theTime, sLastTime);
|
||||
}
|
||||
sLastTime = theTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
sLastTime = theTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
return theTime;
|
||||
}
|
||||
|
||||
inline UInt64 CAHostTimeBase::ConvertToNanos(UInt64 inHostTime)
|
||||
{
|
||||
pthread_once(&sIsInited, Initialize);
|
||||
|
||||
UInt64 theAnswer = MultiplyByRatio(inHostTime, sToNanosNumerator, sToNanosDenominator);
|
||||
#if CoreAudio_Debug
|
||||
if(((sToNanosNumerator > sToNanosDenominator) && (theAnswer < inHostTime)) || ((sToNanosDenominator > sToNanosNumerator) && (theAnswer > inHostTime)))
|
||||
{
|
||||
DebugPrintf("CAHostTimeBase::ConvertToNanos: The conversion wrapped");
|
||||
}
|
||||
#endif
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline UInt64 CAHostTimeBase::ConvertFromNanos(UInt64 inNanos)
|
||||
{
|
||||
pthread_once(&sIsInited, Initialize);
|
||||
|
||||
UInt64 theAnswer = MultiplyByRatio(inNanos, sToNanosDenominator, sToNanosNumerator);
|
||||
#if CoreAudio_Debug
|
||||
if(((sToNanosDenominator > sToNanosNumerator) && (theAnswer < inNanos)) || ((sToNanosNumerator > sToNanosDenominator) && (theAnswer > inNanos)))
|
||||
{
|
||||
DebugPrintf("CAHostTimeBase::ConvertFromNanos: The conversion wrapped");
|
||||
}
|
||||
#endif
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline UInt64 CAHostTimeBase::GetCurrentTimeInNanos()
|
||||
{
|
||||
return ConvertToNanos(GetTheCurrentTime());
|
||||
}
|
||||
|
||||
inline UInt64 CAHostTimeBase::AbsoluteHostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime)
|
||||
{
|
||||
UInt64 theAnswer;
|
||||
|
||||
if(inStartTime <= inEndTime)
|
||||
{
|
||||
theAnswer = inEndTime - inStartTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = inStartTime - inEndTime;
|
||||
}
|
||||
|
||||
return ConvertToNanos(theAnswer);
|
||||
}
|
||||
|
||||
inline SInt64 CAHostTimeBase::HostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime)
|
||||
{
|
||||
SInt64 theAnswer;
|
||||
SInt64 theSign = 1;
|
||||
|
||||
if(inStartTime <= inEndTime)
|
||||
{
|
||||
theAnswer = static_cast<SInt64>(inEndTime - inStartTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = static_cast<SInt64>(inStartTime - inEndTime);
|
||||
theSign = -1;
|
||||
}
|
||||
|
||||
return theSign * static_cast<SInt64>(ConvertToNanos(static_cast<UInt64>(theAnswer)));
|
||||
}
|
||||
|
||||
inline UInt64 CAHostTimeBase::MultiplyByRatio(UInt64 inMuliplicand, UInt32 inNumerator, UInt32 inDenominator)
|
||||
{
|
||||
#if TARGET_OS_MAC && TARGET_RT_64_BIT
|
||||
__uint128_t theAnswer = inMuliplicand;
|
||||
#else
|
||||
long double theAnswer = inMuliplicand;
|
||||
#endif
|
||||
if(inNumerator != inDenominator)
|
||||
{
|
||||
theAnswer *= inNumerator;
|
||||
theAnswer /= inDenominator;
|
||||
}
|
||||
return static_cast<UInt64>(theAnswer);
|
||||
}
|
||||
|
||||
#endif
|
345
BGMApp/PublicUtility/CAMutex.cpp
Normal file
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
File: CAMutex.cpp
|
||||
Abstract: CAMutex.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CAMutex.h"
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
#include "CAHostTimeBase.h"
|
||||
|
||||
//==================================================================================================
|
||||
// Logging
|
||||
//==================================================================================================
|
||||
|
||||
#if CoreAudio_Debug
|
||||
// #define Log_Ownership 1
|
||||
// #define Log_Errors 1
|
||||
// #define Log_LongLatencies 1
|
||||
// #define LongLatencyThreshholdNS 1000000ULL // nanoseconds
|
||||
#endif
|
||||
|
||||
//==================================================================================================
|
||||
// CAMutex
|
||||
//==================================================================================================
|
||||
|
||||
CAMutex::CAMutex(const char* inName)
|
||||
:
|
||||
mName(inName),
|
||||
mOwner(0)
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
OSStatus theError = pthread_mutex_init(&mMutex, NULL);
|
||||
ThrowIf(theError != 0, CAException(theError), "CAMutex::CAMutex: Could not init the mutex");
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::CAMutex: creating %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
|
||||
#endif
|
||||
#elif TARGET_OS_WIN32
|
||||
mMutex = CreateMutex(NULL, false, NULL);
|
||||
ThrowIfNULL(mMutex, CAException(GetLastError()), "CAMutex::CAMutex: could not create the mutex.");
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::CAMutex: creating %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
CAMutex::~CAMutex()
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::~CAMutex: destroying %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
|
||||
#endif
|
||||
pthread_mutex_destroy(&mMutex);
|
||||
#elif TARGET_OS_WIN32
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::~CAMutex: destroying %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), mName, mOwner);
|
||||
#endif
|
||||
if(mMutex != NULL)
|
||||
{
|
||||
CloseHandle(mMutex);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CAMutex::Lock()
|
||||
{
|
||||
bool theAnswer = false;
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
pthread_t theCurrentThread = pthread_self();
|
||||
if(!pthread_equal(theCurrentThread, mOwner))
|
||||
{
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Lock: thread %p is locking %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
|
||||
#endif
|
||||
|
||||
#if Log_LongLatencies
|
||||
UInt64 lockTryTime = CAHostTimeBase::GetCurrentTimeInNanos();
|
||||
#endif
|
||||
|
||||
OSStatus theError = pthread_mutex_lock(&mMutex);
|
||||
ThrowIf(theError != 0, CAException(theError), "CAMutex::Lock: Could not lock the mutex");
|
||||
mOwner = theCurrentThread;
|
||||
theAnswer = true;
|
||||
|
||||
#if Log_LongLatencies
|
||||
UInt64 lockAcquireTime = CAHostTimeBase::GetCurrentTimeInNanos();
|
||||
if (lockAcquireTime - lockTryTime >= LongLatencyThresholdNS)
|
||||
DebugPrintfRtn(DebugPrintfFileComma "Thread %p took %.6fs to acquire the lock %s\n", theCurrentThread, (lockAcquireTime - lockTryTime) * 1.0e-9 /* nanos to seconds */, mName);
|
||||
#endif
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Lock: thread %p has locked %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(mOwner != GetCurrentThreadId())
|
||||
{
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Lock: thread %lu is locking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
|
||||
OSStatus theError = WaitForSingleObject(mMutex, INFINITE);
|
||||
ThrowIfError(theError, CAException(theError), "CAMutex::Lock: could not lock the mutex");
|
||||
mOwner = GetCurrentThreadId();
|
||||
theAnswer = true;
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Lock: thread %lu has locked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void CAMutex::Unlock()
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
if(pthread_equal(pthread_self(), mOwner))
|
||||
{
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Unlock: thread %p is unlocking %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner);
|
||||
#endif
|
||||
|
||||
mOwner = 0;
|
||||
OSStatus theError = pthread_mutex_unlock(&mMutex);
|
||||
ThrowIf(theError != 0, CAException(theError), "CAMutex::Unlock: Could not unlock the mutex");
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Unlock: thread %p has unlocked %s, owner: %p\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), pthread_self(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMessage("CAMutex::Unlock: A thread is attempting to unlock a Mutex it doesn't own");
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(mOwner == GetCurrentThreadId())
|
||||
{
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Unlock: thread %lu is unlocking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
|
||||
mOwner = 0;
|
||||
bool wasReleased = ReleaseMutex(mMutex);
|
||||
ThrowIf(!wasReleased, CAException(GetLastError()), "CAMutex::Unlock: Could not unlock the mutex");
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Unlock: thread %lu has unlocked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMessage("CAMutex::Unlock: A thread is attempting to unlock a Mutex it doesn't own");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CAMutex::Try(bool& outWasLocked)
|
||||
{
|
||||
bool theAnswer = false;
|
||||
outWasLocked = false;
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
pthread_t theCurrentThread = pthread_self();
|
||||
if(!pthread_equal(theCurrentThread, mOwner))
|
||||
{
|
||||
// this means the current thread doesn't already own the lock
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Try: thread %p is try-locking %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
|
||||
#endif
|
||||
|
||||
// go ahead and call trylock to see if we can lock it.
|
||||
int theError = pthread_mutex_trylock(&mMutex);
|
||||
if(theError == 0)
|
||||
{
|
||||
// return value of 0 means we successfully locked the lock
|
||||
mOwner = theCurrentThread;
|
||||
theAnswer = true;
|
||||
outWasLocked = true;
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Try: thread %p has locked %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else if(theError == EBUSY)
|
||||
{
|
||||
// return value of EBUSY means that the lock was already locked by another thread
|
||||
theAnswer = false;
|
||||
outWasLocked = false;
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%p %.4f: CAMutex::Try: thread %p failed to lock %s, owner: %p\n", theCurrentThread, ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), theCurrentThread, mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// any other return value means something really bad happenned
|
||||
ThrowIfError(theError, CAException(theError), "CAMutex::Try: call to pthread_mutex_trylock failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this means the current thread already owns the lock
|
||||
theAnswer = true;
|
||||
outWasLocked = false;
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(mOwner != GetCurrentThreadId())
|
||||
{
|
||||
// this means the current thread doesn't own the lock
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Try: thread %lu is try-locking %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
|
||||
// try to acquire the mutex
|
||||
OSStatus theError = WaitForSingleObject(mMutex, 0);
|
||||
if(theError == WAIT_OBJECT_0)
|
||||
{
|
||||
// this means we successfully locked the lock
|
||||
mOwner = GetCurrentThreadId();
|
||||
theAnswer = true;
|
||||
outWasLocked = true;
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Try: thread %lu has locked %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else if(theError == WAIT_TIMEOUT)
|
||||
{
|
||||
// this means that the lock was already locked by another thread
|
||||
theAnswer = false;
|
||||
outWasLocked = false;
|
||||
|
||||
#if Log_Ownership
|
||||
DebugPrintfRtn(DebugPrintfFileComma "%lu %.4f: CAMutex::Try: thread %lu failed to lock %s, owner: %lu\n", GetCurrentThreadId(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), GetCurrentThreadId(), mName, mOwner);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// any other return value means something really bad happenned
|
||||
ThrowIfError(theError, CAException(GetLastError()), "CAMutex::Try: call to lock the mutex failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this means the current thread already owns the lock
|
||||
theAnswer = true;
|
||||
outWasLocked = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool CAMutex::IsFree() const
|
||||
{
|
||||
return mOwner == 0;
|
||||
}
|
||||
|
||||
bool CAMutex::IsOwnedByCurrentThread() const
|
||||
{
|
||||
bool theAnswer = true;
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
theAnswer = pthread_equal(pthread_self(), mOwner);
|
||||
#elif TARGET_OS_WIN32
|
||||
theAnswer = (mOwner == GetCurrentThreadId());
|
||||
#endif
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
|
||||
CAMutex::Unlocker::Unlocker(CAMutex& inMutex)
|
||||
: mMutex(inMutex),
|
||||
mNeedsLock(false)
|
||||
{
|
||||
Assert(mMutex.IsOwnedByCurrentThread(), "Major problem: Unlocker attempted to unlock a mutex not owned by the current thread!");
|
||||
|
||||
mMutex.Unlock();
|
||||
mNeedsLock = true;
|
||||
}
|
||||
|
||||
CAMutex::Unlocker::~Unlocker()
|
||||
{
|
||||
if(mNeedsLock)
|
||||
{
|
||||
mMutex.Lock();
|
||||
}
|
||||
}
|
163
BGMApp/PublicUtility/CAMutex.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
File: CAMutex.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#ifndef __CAMutex_h__
|
||||
#define __CAMutex_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// System Includes
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
#include <pthread.h>
|
||||
#elif TARGET_OS_WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#error Unsupported operating system
|
||||
#endif
|
||||
|
||||
//==================================================================================================
|
||||
// A recursive mutex.
|
||||
//==================================================================================================
|
||||
|
||||
class CAMutex
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAMutex(const char* inName);
|
||||
virtual ~CAMutex();
|
||||
|
||||
// Actions
|
||||
public:
|
||||
virtual bool Lock();
|
||||
virtual void Unlock();
|
||||
virtual bool Try(bool& outWasLocked); // returns true if lock is free, false if not
|
||||
|
||||
virtual bool IsFree() const;
|
||||
virtual bool IsOwnedByCurrentThread() const;
|
||||
|
||||
// Implementation
|
||||
protected:
|
||||
const char* mName;
|
||||
#if TARGET_OS_MAC
|
||||
pthread_t mOwner;
|
||||
pthread_mutex_t mMutex;
|
||||
#elif TARGET_OS_WIN32
|
||||
UInt32 mOwner;
|
||||
HANDLE mMutex;
|
||||
#endif
|
||||
|
||||
// Helper class to manage taking and releasing recursively
|
||||
public:
|
||||
class Locker
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
Locker(CAMutex& inMutex) : mMutex(&inMutex), mNeedsRelease(false) { mNeedsRelease = mMutex->Lock(); }
|
||||
Locker(CAMutex* inMutex) : mMutex(inMutex), mNeedsRelease(false) { mNeedsRelease = (mMutex != NULL && mMutex->Lock()); }
|
||||
// in this case the mutex can be null
|
||||
~Locker() { if(mNeedsRelease) { mMutex->Unlock(); } }
|
||||
|
||||
|
||||
private:
|
||||
Locker(const Locker&);
|
||||
Locker& operator=(const Locker&);
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
CAMutex* mMutex;
|
||||
bool mNeedsRelease;
|
||||
|
||||
};
|
||||
|
||||
// Unlocker
|
||||
class Unlocker
|
||||
{
|
||||
public:
|
||||
Unlocker(CAMutex& inMutex);
|
||||
~Unlocker();
|
||||
|
||||
private:
|
||||
CAMutex& mMutex;
|
||||
bool mNeedsLock;
|
||||
|
||||
// Hidden definitions of copy ctor, assignment operator
|
||||
Unlocker(const Unlocker& copy); // Not implemented
|
||||
Unlocker& operator=(const Unlocker& copy); // Not implemented
|
||||
};
|
||||
|
||||
// you can use this with Try - if you take the lock in try, pass in the outWasLocked var
|
||||
class Tryer {
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
Tryer (CAMutex &mutex) : mMutex(mutex), mNeedsRelease(false), mHasLock(false) { mHasLock = mMutex.Try (mNeedsRelease); }
|
||||
~Tryer () { if (mNeedsRelease) mMutex.Unlock(); }
|
||||
|
||||
bool HasLock () const { return mHasLock; }
|
||||
|
||||
private:
|
||||
Tryer(const Tryer&);
|
||||
Tryer& operator=(const Tryer&);
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
CAMutex & mMutex;
|
||||
bool mNeedsRelease;
|
||||
bool mHasLock;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif // __CAMutex_h__
|
450
BGMApp/PublicUtility/CAPThread.cpp
Normal file
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
File: CAPThread.cpp
|
||||
Abstract: CAPThread.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
//=============================================================================
|
||||
// Includes
|
||||
//=============================================================================
|
||||
|
||||
// Self Include
|
||||
#include "CAPThread.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
|
||||
// System Includes
|
||||
#if TARGET_OS_MAC
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
// Standard Library Includes
|
||||
#include <stdio.h>
|
||||
|
||||
//==================================================================================================
|
||||
// CAPThread
|
||||
//==================================================================================================
|
||||
|
||||
// returns the thread's priority as it was last set by the API
|
||||
#define CAPTHREAD_SET_PRIORITY 0
|
||||
// returns the thread's priority as it was last scheduled by the Kernel
|
||||
#define CAPTHREAD_SCHEDULED_PRIORITY 1
|
||||
|
||||
//#define Log_SetPriority 1
|
||||
|
||||
CAPThread::CAPThread(ThreadRoutine inThreadRoutine, void* inParameter, UInt32 inPriority, bool inFixedPriority, bool inAutoDelete, const char* inThreadName)
|
||||
:
|
||||
#if TARGET_OS_MAC
|
||||
mPThread(0),
|
||||
mSpawningThreadPriority(getScheduledPriority(pthread_self(), CAPTHREAD_SET_PRIORITY)),
|
||||
#elif TARGET_OS_WIN32
|
||||
mThreadHandle(NULL),
|
||||
mThreadID(0),
|
||||
#endif
|
||||
mThreadRoutine(inThreadRoutine),
|
||||
mThreadParameter(inParameter),
|
||||
mPriority(inPriority),
|
||||
mPeriod(0),
|
||||
mComputation(0),
|
||||
mConstraint(0),
|
||||
mIsPreemptible(true),
|
||||
mTimeConstraintSet(false),
|
||||
mFixedPriority(inFixedPriority),
|
||||
mAutoDelete(inAutoDelete)
|
||||
{
|
||||
if(inThreadName != NULL)
|
||||
{
|
||||
strlcpy(mThreadName, inThreadName, kMaxThreadNameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(mThreadName, 0, kMaxThreadNameLength);
|
||||
}
|
||||
}
|
||||
|
||||
CAPThread::CAPThread(ThreadRoutine inThreadRoutine, void* inParameter, UInt32 inPeriod, UInt32 inComputation, UInt32 inConstraint, bool inIsPreemptible, bool inAutoDelete, const char* inThreadName)
|
||||
:
|
||||
#if TARGET_OS_MAC
|
||||
mPThread(0),
|
||||
mSpawningThreadPriority(getScheduledPriority(pthread_self(), CAPTHREAD_SET_PRIORITY)),
|
||||
#elif TARGET_OS_WIN32
|
||||
mThreadHandle(NULL),
|
||||
mThreadID(0),
|
||||
#endif
|
||||
mThreadRoutine(inThreadRoutine),
|
||||
mThreadParameter(inParameter),
|
||||
mPriority(kDefaultThreadPriority),
|
||||
mPeriod(inPeriod),
|
||||
mComputation(inComputation),
|
||||
mConstraint(inConstraint),
|
||||
mIsPreemptible(inIsPreemptible),
|
||||
mTimeConstraintSet(true),
|
||||
mFixedPriority(false),
|
||||
mAutoDelete(inAutoDelete)
|
||||
{
|
||||
if(inThreadName != NULL)
|
||||
{
|
||||
strlcpy(mThreadName, inThreadName, kMaxThreadNameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(mThreadName, 0, kMaxThreadNameLength);
|
||||
}
|
||||
}
|
||||
|
||||
CAPThread::~CAPThread()
|
||||
{
|
||||
}
|
||||
|
||||
UInt32 CAPThread::GetScheduledPriority()
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
return CAPThread::getScheduledPriority( mPThread, CAPTHREAD_SCHEDULED_PRIORITY );
|
||||
#elif TARGET_OS_WIN32
|
||||
UInt32 theAnswer = 0;
|
||||
if(mThreadHandle != NULL)
|
||||
{
|
||||
theAnswer = GetThreadPriority(mThreadHandle);
|
||||
}
|
||||
return theAnswer;
|
||||
#endif
|
||||
}
|
||||
|
||||
UInt32 CAPThread::GetScheduledPriority(NativeThread thread)
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
return getScheduledPriority( thread, CAPTHREAD_SCHEDULED_PRIORITY );
|
||||
#elif TARGET_OS_WIN32
|
||||
return 0; // ???
|
||||
#endif
|
||||
}
|
||||
|
||||
void CAPThread::SetPriority(UInt32 inPriority, bool inFixedPriority)
|
||||
{
|
||||
mPriority = inPriority;
|
||||
mTimeConstraintSet = false;
|
||||
mFixedPriority = inFixedPriority;
|
||||
#if TARGET_OS_MAC
|
||||
if(mPThread != 0)
|
||||
{
|
||||
SetPriority(mPThread, mPriority, mFixedPriority);
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(mThreadID != NULL)
|
||||
{
|
||||
SetPriority(mThreadID, mPriority, mFixedPriority);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CAPThread::SetPriority(NativeThread inThread, UInt32 inPriority, bool inFixedPriority)
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
if(inThread != 0)
|
||||
{
|
||||
kern_return_t theError = 0;
|
||||
|
||||
// set whether or not this is a fixed priority thread
|
||||
if (inFixedPriority)
|
||||
{
|
||||
thread_extended_policy_data_t theFixedPolicy = { false };
|
||||
theError = thread_policy_set(pthread_mach_thread_np(inThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
|
||||
AssertNoKernelError(theError, "CAPThread::SetPriority: failed to set the fixed-priority policy");
|
||||
}
|
||||
|
||||
// set the thread's absolute priority which is relative to the priority on which thread_policy_set() is called
|
||||
UInt32 theCurrentThreadPriority = getScheduledPriority(pthread_self(), CAPTHREAD_SET_PRIORITY);
|
||||
thread_precedence_policy_data_t thePrecedencePolicy = { static_cast<integer_t>(inPriority - theCurrentThreadPriority) };
|
||||
theError = thread_policy_set(pthread_mach_thread_np(inThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
|
||||
AssertNoKernelError(theError, "CAPThread::SetPriority: failed to set the precedence policy");
|
||||
|
||||
#if Log_SetPriority
|
||||
DebugMessageN4("CAPThread::SetPriority: requsted: %lu spawning: %lu current: %lu assigned: %d", mPriority, mSpawningThreadPriority, theCurrentThreadPriority, thePrecedencePolicy.importance);
|
||||
#endif
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(inThread != NULL)
|
||||
{
|
||||
HANDLE hThread = OpenThread(NULL, FALSE, inThread);
|
||||
if(hThread != NULL) {
|
||||
SetThreadPriority(hThread, inPriority);
|
||||
CloseHandle(hThread);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CAPThread::SetTimeConstraints(UInt32 inPeriod, UInt32 inComputation, UInt32 inConstraint, bool inIsPreemptible)
|
||||
{
|
||||
mPeriod = inPeriod;
|
||||
mComputation = inComputation;
|
||||
mConstraint = inConstraint;
|
||||
mIsPreemptible = inIsPreemptible;
|
||||
mTimeConstraintSet = true;
|
||||
#if TARGET_OS_MAC
|
||||
if(mPThread != 0)
|
||||
{
|
||||
thread_time_constraint_policy_data_t thePolicy;
|
||||
thePolicy.period = mPeriod;
|
||||
thePolicy.computation = mComputation;
|
||||
thePolicy.constraint = mConstraint;
|
||||
thePolicy.preemptible = mIsPreemptible;
|
||||
AssertNoError(thread_policy_set(pthread_mach_thread_np(mPThread), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&thePolicy, THREAD_TIME_CONSTRAINT_POLICY_COUNT), "CAPThread::SetTimeConstraints: thread_policy_set failed");
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
if(mThreadHandle != NULL)
|
||||
{
|
||||
SetThreadPriority(mThreadHandle, THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CAPThread::Start()
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
Assert(mPThread == 0, "CAPThread::Start: can't start because the thread is already running");
|
||||
if(mPThread == 0)
|
||||
{
|
||||
OSStatus theResult;
|
||||
pthread_attr_t theThreadAttributes;
|
||||
|
||||
theResult = pthread_attr_init(&theThreadAttributes);
|
||||
ThrowIf(theResult != 0, CAException(theResult), "CAPThread::Start: Thread attributes could not be created.");
|
||||
|
||||
theResult = pthread_attr_setdetachstate(&theThreadAttributes, PTHREAD_CREATE_DETACHED);
|
||||
ThrowIf(theResult != 0, CAException(theResult), "CAPThread::Start: A thread could not be created in the detached state.");
|
||||
|
||||
theResult = pthread_create(&mPThread, &theThreadAttributes, (ThreadRoutine)CAPThread::Entry, this);
|
||||
ThrowIf(theResult != 0 || !mPThread, CAException(theResult), "CAPThread::Start: Could not create a thread.");
|
||||
|
||||
pthread_attr_destroy(&theThreadAttributes);
|
||||
|
||||
}
|
||||
#elif TARGET_OS_WIN32
|
||||
Assert(mThreadID == 0, "CAPThread::Start: can't start because the thread is already running");
|
||||
if(mThreadID == 0)
|
||||
{
|
||||
// clean up the existing thread handle
|
||||
if(mThreadHandle != NULL)
|
||||
{
|
||||
CloseHandle(mThreadHandle);
|
||||
mThreadHandle = NULL;
|
||||
}
|
||||
|
||||
// create a new thread
|
||||
mThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Entry, this, 0, &mThreadID);
|
||||
ThrowIf(mThreadHandle == NULL, CAException(GetLastError()), "CAPThread::Start: Could not create a thread.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
|
||||
void* CAPThread::Entry(CAPThread* inCAPThread)
|
||||
{
|
||||
void* theAnswer = NULL;
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
inCAPThread->mPThread = pthread_self();
|
||||
#elif TARGET_OS_WIN32
|
||||
// do we need to do something here?
|
||||
#endif
|
||||
|
||||
#if !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
|
||||
if(inCAPThread->mThreadName[0] != 0)
|
||||
{
|
||||
pthread_setname_np(inCAPThread->mThreadName);
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
if(inCAPThread->mTimeConstraintSet)
|
||||
{
|
||||
inCAPThread->SetTimeConstraints(inCAPThread->mPeriod, inCAPThread->mComputation, inCAPThread->mConstraint, inCAPThread->mIsPreemptible);
|
||||
}
|
||||
else
|
||||
{
|
||||
inCAPThread->SetPriority(inCAPThread->mPriority, inCAPThread->mFixedPriority);
|
||||
}
|
||||
|
||||
if(inCAPThread->mThreadRoutine != NULL)
|
||||
{
|
||||
theAnswer = inCAPThread->mThreadRoutine(inCAPThread->mThreadParameter);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// what should be done here?
|
||||
}
|
||||
inCAPThread->mPThread = 0;
|
||||
if (inCAPThread->mAutoDelete)
|
||||
delete inCAPThread;
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 CAPThread::getScheduledPriority(pthread_t inThread, int inPriorityKind)
|
||||
{
|
||||
thread_basic_info_data_t threadInfo;
|
||||
policy_info_data_t thePolicyInfo;
|
||||
unsigned int count;
|
||||
|
||||
if (inThread == NULL)
|
||||
return 0;
|
||||
|
||||
// get basic info
|
||||
count = THREAD_BASIC_INFO_COUNT;
|
||||
thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (thread_info_t)&threadInfo, &count);
|
||||
|
||||
switch (threadInfo.policy) {
|
||||
case POLICY_TIMESHARE:
|
||||
count = POLICY_TIMESHARE_INFO_COUNT;
|
||||
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (thread_info_t)&(thePolicyInfo.ts), &count);
|
||||
if (inPriorityKind == CAPTHREAD_SCHEDULED_PRIORITY) {
|
||||
return static_cast<UInt32>(thePolicyInfo.ts.cur_priority);
|
||||
}
|
||||
return static_cast<UInt32>(thePolicyInfo.ts.base_priority);
|
||||
break;
|
||||
|
||||
case POLICY_FIFO:
|
||||
count = POLICY_FIFO_INFO_COUNT;
|
||||
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (thread_info_t)&(thePolicyInfo.fifo), &count);
|
||||
if ( (thePolicyInfo.fifo.depressed) && (inPriorityKind == CAPTHREAD_SCHEDULED_PRIORITY) ) {
|
||||
return static_cast<UInt32>(thePolicyInfo.fifo.depress_priority);
|
||||
}
|
||||
return static_cast<UInt32>(thePolicyInfo.fifo.base_priority);
|
||||
break;
|
||||
|
||||
case POLICY_RR:
|
||||
count = POLICY_RR_INFO_COUNT;
|
||||
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (thread_info_t)&(thePolicyInfo.rr), &count);
|
||||
if ( (thePolicyInfo.rr.depressed) && (inPriorityKind == CAPTHREAD_SCHEDULED_PRIORITY) ) {
|
||||
return static_cast<UInt32>(thePolicyInfo.rr.depress_priority);
|
||||
}
|
||||
return static_cast<UInt32>(thePolicyInfo.rr.base_priority);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#elif TARGET_OS_WIN32
|
||||
|
||||
UInt32 WINAPI CAPThread::Entry(CAPThread* inCAPThread)
|
||||
{
|
||||
UInt32 theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if(inCAPThread->mTimeConstraintSet)
|
||||
{
|
||||
inCAPThread->SetTimeConstraints(inCAPThread->mPeriod, inCAPThread->mComputation, inCAPThread->mConstraint, inCAPThread->mIsPreemptible);
|
||||
}
|
||||
else
|
||||
{
|
||||
inCAPThread->SetPriority(inCAPThread->mPriority, inCAPThread->mFixedPriority);
|
||||
}
|
||||
|
||||
if(inCAPThread->mThreadRoutine != NULL)
|
||||
{
|
||||
theAnswer = reinterpret_cast<UInt32>(inCAPThread->mThreadRoutine(inCAPThread->mThreadParameter));
|
||||
}
|
||||
inCAPThread->mThreadID = 0;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// what should be done here?
|
||||
}
|
||||
CloseHandle(inCAPThread->mThreadHandle);
|
||||
inCAPThread->mThreadHandle = NULL;
|
||||
if (inCAPThread->mAutoDelete)
|
||||
delete inCAPThread;
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
Boolean CompareAndSwap(UInt32 inOldValue, UInt32 inNewValue, UInt32* inOldValuePtr)
|
||||
{
|
||||
return InterlockedCompareExchange((volatile LONG*)inOldValuePtr, inNewValue, inOldValue) == inOldValue;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CAPThread::SetName(const char* inThreadName)
|
||||
{
|
||||
if(inThreadName != NULL)
|
||||
{
|
||||
strlcpy(mThreadName, inThreadName, kMaxThreadNameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(mThreadName, 0, kMaxThreadNameLength);
|
||||
}
|
||||
}
|
||||
|
||||
#if CoreAudio_Debug
|
||||
void CAPThread::DebugPriority(const char *label)
|
||||
{
|
||||
#if !TARGET_OS_WIN32
|
||||
if (mTimeConstraintSet)
|
||||
printf("CAPThread::%s %p: pri=<time constraint>, spawning pri=%d, scheduled pri=%d\n", label, this,
|
||||
(int)mSpawningThreadPriority, (mPThread != NULL) ? (int)GetScheduledPriority() : -1);
|
||||
else
|
||||
printf("CAPThread::%s %p: pri=%d%s, spawning pri=%d, scheduled pri=%d\n", label, this, (int)mPriority, mFixedPriority ? " fixed" : "",
|
||||
(int)mSpawningThreadPriority, (mPThread != NULL) ? (int)GetScheduledPriority() : -1);
|
||||
#else
|
||||
if (mTimeConstraintSet)
|
||||
{
|
||||
printf("CAPThread::%s %p: pri=<time constraint>, spawning pri=%d, scheduled pri=%d\n", label, this,
|
||||
(int)mPriority, (mThreadHandle != NULL) ? (int)GetScheduledPriority() : -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("CAPThread::%s %p: pri=%d%s, spawning pri=%d, scheduled pri=%d\n", label, this, (int)mPriority, mFixedPriority ? " fixed" : "",
|
||||
(int)mPriority, (mThreadHandle != NULL) ? (int)GetScheduledPriority() : -1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
191
BGMApp/PublicUtility/CAPThread.h
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
File: CAPThread.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAPThread_h__)
|
||||
#define __CAPThread_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// System Includes
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#else
|
||||
#include <CFBase.h>
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#elif TARGET_OS_WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#error Unsupported operating system
|
||||
#endif
|
||||
|
||||
//==================================================================================================
|
||||
// CAPThread
|
||||
//
|
||||
// This class wraps a pthread and a Win32 thread.
|
||||
// caution: long-running fixed priority threads can make the system unresponsive
|
||||
//==================================================================================================
|
||||
|
||||
class CAPThread
|
||||
{
|
||||
|
||||
// Types
|
||||
public:
|
||||
typedef void* (*ThreadRoutine)(void* inParameter);
|
||||
|
||||
// Constants
|
||||
public:
|
||||
enum
|
||||
{
|
||||
#if TARGET_OS_MAC
|
||||
kMinThreadPriority = 1,
|
||||
kMaxThreadPriority = 63,
|
||||
kDefaultThreadPriority = 31,
|
||||
kMaxThreadNameLength = 64
|
||||
#elif TARGET_OS_WIN32
|
||||
kMinThreadPriority = 1,
|
||||
kMaxThreadPriority = 31,
|
||||
kDefaultThreadPriority = THREAD_PRIORITY_NORMAL,
|
||||
kMaxThreadNameLength = 256
|
||||
#endif
|
||||
};
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAPThread(ThreadRoutine inThreadRoutine, void* inParameter, UInt32 inPriority = kDefaultThreadPriority, bool inFixedPriority=false, bool inAutoDelete=false, const char* inThreadName = NULL);
|
||||
CAPThread(ThreadRoutine inThreadRoutine, void* inParameter, UInt32 inPeriod, UInt32 inComputation, UInt32 inConstraint, bool inIsPreemptible, bool inAutoDelete=false, const char* inThreadName = NULL);
|
||||
virtual ~CAPThread();
|
||||
|
||||
// Properties
|
||||
public:
|
||||
#if TARGET_OS_MAC
|
||||
typedef pthread_t NativeThread;
|
||||
|
||||
NativeThread GetNativeThread() { return mPThread; }
|
||||
static NativeThread GetCurrentThread() { return pthread_self(); }
|
||||
static bool IsNativeThreadsEqual(NativeThread a, NativeThread b) { return (a==b); }
|
||||
|
||||
bool operator==(NativeThread b) { return pthread_equal(mPThread,b); }
|
||||
|
||||
pthread_t GetPThread() const { return mPThread; }
|
||||
bool IsCurrentThread() const { return (0 != mPThread) && (pthread_self() == mPThread); }
|
||||
bool IsRunning() const { return 0 != mPThread; }
|
||||
static UInt32 getScheduledPriority(pthread_t inThread, int inPriorityKind);
|
||||
#elif TARGET_OS_WIN32
|
||||
typedef unsigned long NativeThread;
|
||||
|
||||
NativeThread GetNativeThread() { return mThreadID; }
|
||||
static NativeThread GetCurrentThread() { return GetCurrentThreadId(); }
|
||||
static bool IsNativeThreadsEqual(NativeThread a, NativeThread b) { return (a==b); }
|
||||
|
||||
bool operator ==(NativeThread b) { return (mThreadID==b); }
|
||||
|
||||
HANDLE GetThreadHandle() const { return mThreadHandle; }
|
||||
UInt32 GetThreadID() const { return mThreadID; }
|
||||
bool IsCurrentThread() const { return (0 != mThreadID) && (GetCurrentThreadId() == mThreadID); }
|
||||
bool IsRunning() const { return 0 != mThreadID; }
|
||||
#endif
|
||||
|
||||
bool IsTimeShareThread() const { return !mTimeConstraintSet; }
|
||||
bool IsTimeConstraintThread() const { return mTimeConstraintSet; }
|
||||
|
||||
UInt32 GetPriority() const { return mPriority; }
|
||||
UInt32 GetScheduledPriority();
|
||||
static UInt32 GetScheduledPriority(NativeThread thread);
|
||||
void SetPriority(UInt32 inPriority, bool inFixedPriority=false);
|
||||
static void SetPriority(NativeThread inThread, UInt32 inPriority, bool inFixedPriority = false);
|
||||
|
||||
void GetTimeConstraints(UInt32& outPeriod, UInt32& outComputation, UInt32& outConstraint, bool& outIsPreemptible) const { outPeriod = mPeriod; outComputation = mComputation; outConstraint = mConstraint; outIsPreemptible = mIsPreemptible; }
|
||||
void SetTimeConstraints(UInt32 inPeriod, UInt32 inComputation, UInt32 inConstraint, bool inIsPreemptible);
|
||||
void ClearTimeConstraints() { SetPriority(mPriority); }
|
||||
|
||||
bool WillAutoDelete() const { return mAutoDelete; }
|
||||
void SetAutoDelete(bool b) { mAutoDelete = b; }
|
||||
|
||||
void SetName(const char* inThreadName);
|
||||
|
||||
#if CoreAudio_Debug
|
||||
void DebugPriority(const char *label);
|
||||
#endif
|
||||
|
||||
// Actions
|
||||
public:
|
||||
virtual void Start();
|
||||
|
||||
// Implementation
|
||||
protected:
|
||||
#if TARGET_OS_MAC
|
||||
static void* Entry(CAPThread* inCAPThread);
|
||||
#elif TARGET_OS_WIN32
|
||||
static UInt32 WINAPI Entry(CAPThread* inCAPThread);
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
pthread_t mPThread;
|
||||
UInt32 mSpawningThreadPriority;
|
||||
#elif TARGET_OS_WIN32
|
||||
HANDLE mThreadHandle;
|
||||
unsigned long mThreadID;
|
||||
#endif
|
||||
ThreadRoutine mThreadRoutine;
|
||||
void* mThreadParameter;
|
||||
char mThreadName[kMaxThreadNameLength];
|
||||
UInt32 mPriority;
|
||||
UInt32 mPeriod;
|
||||
UInt32 mComputation;
|
||||
UInt32 mConstraint;
|
||||
bool mIsPreemptible;
|
||||
bool mTimeConstraintSet;
|
||||
bool mFixedPriority;
|
||||
bool mAutoDelete; // delete self when thread terminates
|
||||
};
|
||||
|
||||
#endif
|
312
BGMApp/PublicUtility/CAPropertyAddress.h
Normal file
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
File: CAPropertyAddress.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__CAPropertyAddress_h__)
|
||||
#define __CAPropertyAddress_h__
|
||||
|
||||
//==================================================================================================
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
|
||||
// Standard Library Includes
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
//==================================================================================================
|
||||
// CAPropertyAddress
|
||||
//
|
||||
// CAPropertyAddress extends the AudioObjectPropertyAddress structure to C++ including constructors
|
||||
// and other utility operations. Note that there is no defined operator< or operator== because the
|
||||
// presence of wildcards for the fields make comparisons ambiguous without specifying whether or
|
||||
// not to take the wildcards into account. Consequently, if you want to use this struct in an STL
|
||||
// data structure, you'll need to specify the approriate function object explicitly in the template
|
||||
// declaration.
|
||||
//==================================================================================================
|
||||
|
||||
struct CAPropertyAddress
|
||||
:
|
||||
public AudioObjectPropertyAddress
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAPropertyAddress() : AudioObjectPropertyAddress() { mSelector = 0; mScope = kAudioObjectPropertyScopeGlobal; mElement = kAudioObjectPropertyElementMaster; }
|
||||
CAPropertyAddress(AudioObjectPropertySelector inSelector) : AudioObjectPropertyAddress() { mSelector = inSelector; mScope = kAudioObjectPropertyScopeGlobal; mElement = kAudioObjectPropertyElementMaster; }
|
||||
CAPropertyAddress(AudioObjectPropertySelector inSelector, AudioObjectPropertyScope inScope) : AudioObjectPropertyAddress() { mSelector = inSelector; mScope = inScope; mElement = kAudioObjectPropertyElementMaster; }
|
||||
CAPropertyAddress(AudioObjectPropertySelector inSelector, AudioObjectPropertyScope inScope, AudioObjectPropertyElement inElement) : AudioObjectPropertyAddress() { mSelector = inSelector; mScope = inScope; mElement = inElement; }
|
||||
CAPropertyAddress(const AudioObjectPropertyAddress& inAddress) : AudioObjectPropertyAddress(inAddress){}
|
||||
CAPropertyAddress(const CAPropertyAddress& inAddress) : AudioObjectPropertyAddress(inAddress){}
|
||||
CAPropertyAddress& operator=(const AudioObjectPropertyAddress& inAddress) { AudioObjectPropertyAddress::operator=(inAddress); return *this; }
|
||||
CAPropertyAddress& operator=(const CAPropertyAddress& inAddress) { AudioObjectPropertyAddress::operator=(inAddress); return *this; }
|
||||
|
||||
// Operations
|
||||
public:
|
||||
static bool IsSameAddress(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) { return (inAddress1.mScope == inAddress2.mScope) && (inAddress1.mSelector == inAddress2.mSelector) && (inAddress1.mElement == inAddress2.mElement); }
|
||||
static bool IsLessThanAddress(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) { bool theAnswer = false; if(inAddress1.mScope != inAddress2.mScope) { theAnswer = inAddress1.mScope < inAddress2.mScope; } else if(inAddress1.mSelector != inAddress2.mSelector) { theAnswer = inAddress1.mSelector < inAddress2.mSelector; } else { theAnswer = inAddress1.mElement < inAddress2.mElement; } return theAnswer; }
|
||||
static bool IsCongruentSelector(AudioObjectPropertySelector inSelector1, AudioObjectPropertySelector inSelector2) { return (inSelector1 == inSelector2) || (inSelector1 == kAudioObjectPropertySelectorWildcard) || (inSelector2 == kAudioObjectPropertySelectorWildcard); }
|
||||
static bool IsCongruentScope(AudioObjectPropertyScope inScope1, AudioObjectPropertyScope inScope2) { return (inScope1 == inScope2) || (inScope1 == kAudioObjectPropertyScopeWildcard) || (inScope2 == kAudioObjectPropertyScopeWildcard); }
|
||||
static bool IsCongruentElement(AudioObjectPropertyElement inElement1, AudioObjectPropertyElement inElement2) { return (inElement1 == inElement2) || (inElement1 == kAudioObjectPropertyElementWildcard) || (inElement2 == kAudioObjectPropertyElementWildcard); }
|
||||
static bool IsCongruentAddress(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) { return IsCongruentScope(inAddress1.mScope, inAddress2.mScope) && IsCongruentSelector(inAddress1.mSelector, inAddress2.mSelector) && IsCongruentElement(inAddress1.mElement, inAddress2.mElement); }
|
||||
static bool IsCongruentLessThanAddress(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) { bool theAnswer = false; if(!IsCongruentScope(inAddress1.mScope, inAddress2.mScope)) { theAnswer = inAddress1.mScope < inAddress2.mScope; } else if(!IsCongruentSelector(inAddress1.mSelector, inAddress2.mSelector)) { theAnswer = inAddress1.mSelector < inAddress2.mSelector; } else if(!IsCongruentElement(inAddress1.mElement, inAddress2.mElement)) { theAnswer = inAddress1.mElement < inAddress2.mElement; } return theAnswer; }
|
||||
|
||||
// STL Helpers
|
||||
public:
|
||||
struct EqualTo : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
|
||||
{
|
||||
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsSameAddress(inAddress1, inAddress2); }
|
||||
};
|
||||
|
||||
struct LessThan : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
|
||||
{
|
||||
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsLessThanAddress(inAddress1, inAddress2); }
|
||||
};
|
||||
|
||||
struct CongruentEqualTo : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
|
||||
{
|
||||
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsCongruentAddress(inAddress1, inAddress2); }
|
||||
};
|
||||
|
||||
struct CongruentLessThan : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
|
||||
{
|
||||
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsCongruentLessThanAddress(inAddress1, inAddress2); }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
// CAPropertyAddressList
|
||||
//
|
||||
// An auto-resizing array of CAPropertyAddress structures.
|
||||
//==================================================================================================
|
||||
|
||||
class CAPropertyAddressList
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAPropertyAddressList() : mAddressList(), mToken(NULL) {}
|
||||
explicit CAPropertyAddressList(void* inToken) : mAddressList(), mToken(inToken) {}
|
||||
explicit CAPropertyAddressList(uintptr_t inToken) : mAddressList(), mToken(reinterpret_cast<void*>(inToken)) {}
|
||||
CAPropertyAddressList(const CAPropertyAddressList& inAddressList) : mAddressList(inAddressList.mAddressList), mToken(inAddressList.mToken) {}
|
||||
CAPropertyAddressList& operator=(const CAPropertyAddressList& inAddressList) { mAddressList = inAddressList.mAddressList; mToken = inAddressList.mToken; return *this; }
|
||||
~CAPropertyAddressList() {}
|
||||
|
||||
// Operations
|
||||
public:
|
||||
void* GetToken() const { return mToken; }
|
||||
void SetToken(void* inToken) { mToken = inToken; }
|
||||
|
||||
uintptr_t GetIntToken() const { return reinterpret_cast<uintptr_t>(mToken); }
|
||||
void SetIntToken(uintptr_t inToken) { mToken = reinterpret_cast<void*>(inToken); }
|
||||
|
||||
AudioObjectID GetAudioObjectIDToken() const { return static_cast<AudioObjectID>(reinterpret_cast<uintptr_t>(mToken)); }
|
||||
|
||||
bool IsEmpty() const { return mAddressList.empty(); }
|
||||
UInt32 GetNumberItems() const { return ToUInt32(mAddressList.size()); }
|
||||
void GetItemByIndex(UInt32 inIndex, AudioObjectPropertyAddress& outAddress) const { if(inIndex < mAddressList.size()) { outAddress = mAddressList.at(inIndex); } }
|
||||
const AudioObjectPropertyAddress* GetItems() const { return &(*mAddressList.begin()); }
|
||||
AudioObjectPropertyAddress* GetItems() { return &(*mAddressList.begin()); }
|
||||
|
||||
bool HasItem(const AudioObjectPropertyAddress& inAddress) const { AddressList::const_iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), std::bind1st(CAPropertyAddress::CongruentEqualTo(), inAddress)); return theIterator != mAddressList.end(); }
|
||||
bool HasExactItem(const AudioObjectPropertyAddress& inAddress) const { AddressList::const_iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), std::bind1st(CAPropertyAddress::EqualTo(), inAddress)); return theIterator != mAddressList.end(); }
|
||||
|
||||
void AppendItem(const AudioObjectPropertyAddress& inAddress) { mAddressList.push_back(inAddress); }
|
||||
void AppendUniqueItem(const AudioObjectPropertyAddress& inAddress) { if(!HasItem(inAddress)) { mAddressList.push_back(inAddress); } }
|
||||
void AppendUniqueExactItem(const AudioObjectPropertyAddress& inAddress) { if(!HasExactItem(inAddress)) { mAddressList.push_back(inAddress); } }
|
||||
void InsertItemAtIndex(UInt32 inIndex, const AudioObjectPropertyAddress& inAddress) { if(inIndex < mAddressList.size()) { AddressList::iterator theIterator = mAddressList.begin(); std::advance(theIterator, static_cast<int>(inIndex)); mAddressList.insert(theIterator, inAddress); } else { mAddressList.push_back(inAddress); } }
|
||||
void EraseExactItem(const AudioObjectPropertyAddress& inAddress) { AddressList::iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), std::bind1st(CAPropertyAddress::EqualTo(), inAddress)); if(theIterator != mAddressList.end()) { mAddressList.erase(theIterator); } }
|
||||
void EraseItemAtIndex(UInt32 inIndex) { if(inIndex < mAddressList.size()) { AddressList::iterator theIterator = mAddressList.begin(); std::advance(theIterator, static_cast<int>(inIndex)); mAddressList.erase(theIterator); } }
|
||||
void EraseAllItems() { mAddressList.clear(); }
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
typedef std::vector<CAPropertyAddress> AddressList;
|
||||
|
||||
AddressList mAddressList;
|
||||
void* mToken;
|
||||
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
// CAPropertyAddressListVector
|
||||
//
|
||||
// An auto-resizing array of CAPropertyAddressList objects.
|
||||
//==================================================================================================
|
||||
|
||||
class CAPropertyAddressListVector
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
CAPropertyAddressListVector() : mAddressListVector() {}
|
||||
CAPropertyAddressListVector(const CAPropertyAddressListVector& inAddressListVector) : mAddressListVector(inAddressListVector.mAddressListVector) {}
|
||||
CAPropertyAddressListVector& operator=(const CAPropertyAddressListVector& inAddressListVector) { mAddressListVector = inAddressListVector.mAddressListVector; return *this; }
|
||||
~CAPropertyAddressListVector() {}
|
||||
|
||||
// Operations
|
||||
public:
|
||||
bool IsEmpty() const { return mAddressListVector.empty(); }
|
||||
bool HasAnyNonEmptyItems() const;
|
||||
bool HasAnyItemsWithAddress(const AudioObjectPropertyAddress& inAddress) const;
|
||||
bool HasAnyItemsWithExactAddress(const AudioObjectPropertyAddress& inAddress) const;
|
||||
|
||||
UInt32 GetNumberItems() const { return ToUInt32(mAddressListVector.size()); }
|
||||
const CAPropertyAddressList& GetItemByIndex(UInt32 inIndex) const { return mAddressListVector.at(inIndex); }
|
||||
CAPropertyAddressList& GetItemByIndex(UInt32 inIndex) { return mAddressListVector.at(inIndex); }
|
||||
const CAPropertyAddressList* GetItemByToken(void* inToken) const;
|
||||
CAPropertyAddressList* GetItemByToken(void* inToken);
|
||||
const CAPropertyAddressList* GetItemByIntToken(uintptr_t inToken) const;
|
||||
CAPropertyAddressList* GetItemByIntToken(uintptr_t inToken);
|
||||
|
||||
void AppendItem(const CAPropertyAddressList& inAddressList) { mAddressListVector.push_back(inAddressList); }
|
||||
void EraseAllItems() { mAddressListVector.clear(); }
|
||||
|
||||
// Implementation
|
||||
private:
|
||||
typedef std::vector<CAPropertyAddressList> AddressListVector;
|
||||
|
||||
AddressListVector mAddressListVector;
|
||||
|
||||
};
|
||||
|
||||
inline bool CAPropertyAddressListVector::HasAnyNonEmptyItems() const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
for(AddressListVector::const_iterator theIterator = mAddressListVector.begin(); !theAnswer && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
theAnswer = !theIterator->IsEmpty();
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline bool CAPropertyAddressListVector::HasAnyItemsWithAddress(const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
for(AddressListVector::const_iterator theIterator = mAddressListVector.begin(); !theAnswer && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
theAnswer = theIterator->HasItem(inAddress);
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline bool CAPropertyAddressListVector::HasAnyItemsWithExactAddress(const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
for(AddressListVector::const_iterator theIterator = mAddressListVector.begin(); !theAnswer && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
theAnswer = theIterator->HasExactItem(inAddress);
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline const CAPropertyAddressList* CAPropertyAddressListVector::GetItemByToken(void* inToken) const
|
||||
{
|
||||
const CAPropertyAddressList* theAnswer = NULL;
|
||||
bool wasFound = false;
|
||||
for(AddressListVector::const_iterator theIterator = mAddressListVector.begin(); !wasFound && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
if(theIterator->GetToken() == inToken)
|
||||
{
|
||||
wasFound = true;
|
||||
theAnswer = &(*theIterator);
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline CAPropertyAddressList* CAPropertyAddressListVector::GetItemByToken(void* inToken)
|
||||
{
|
||||
CAPropertyAddressList* theAnswer = NULL;
|
||||
bool wasFound = false;
|
||||
for(AddressListVector::iterator theIterator = mAddressListVector.begin(); !wasFound && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
if(theIterator->GetToken() == inToken)
|
||||
{
|
||||
wasFound = true;
|
||||
theAnswer = &(*theIterator);
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline const CAPropertyAddressList* CAPropertyAddressListVector::GetItemByIntToken(uintptr_t inToken) const
|
||||
{
|
||||
const CAPropertyAddressList* theAnswer = NULL;
|
||||
bool wasFound = false;
|
||||
for(AddressListVector::const_iterator theIterator = mAddressListVector.begin(); !wasFound && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
if(theIterator->GetIntToken() == inToken)
|
||||
{
|
||||
wasFound = true;
|
||||
theAnswer = &(*theIterator);
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
inline CAPropertyAddressList* CAPropertyAddressListVector::GetItemByIntToken(uintptr_t inToken)
|
||||
{
|
||||
CAPropertyAddressList* theAnswer = NULL;
|
||||
bool wasFound = false;
|
||||
for(AddressListVector::iterator theIterator = mAddressListVector.begin(); !wasFound && (theIterator != mAddressListVector.end()); ++theIterator)
|
||||
{
|
||||
if(theIterator->GetIntToken() == inToken)
|
||||
{
|
||||
wasFound = true;
|
||||
theAnswer = &(*theIterator);
|
||||
}
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#endif
|
319
BGMApp/PublicUtility/CARingBuffer.cpp
Normal file
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
File: CARingBuffer.cpp
|
||||
Abstract: CARingBuffer.h
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#include "CARingBuffer.h"
|
||||
#include "CABitOperations.h"
|
||||
#include "CAAutoDisposer.h"
|
||||
#include "CAAtomic.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <libkern/OSAtomic.h>
|
||||
|
||||
CARingBuffer::CARingBuffer() :
|
||||
mBuffers(NULL), mNumberChannels(0), mCapacityFrames(0), mCapacityBytes(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CARingBuffer::~CARingBuffer()
|
||||
{
|
||||
Deallocate();
|
||||
}
|
||||
|
||||
|
||||
void CARingBuffer::Allocate(int nChannels, UInt32 bytesPerFrame, UInt32 capacityFrames)
|
||||
{
|
||||
Deallocate();
|
||||
|
||||
capacityFrames = NextPowerOfTwo(capacityFrames);
|
||||
|
||||
mNumberChannels = nChannels;
|
||||
mBytesPerFrame = bytesPerFrame;
|
||||
mCapacityFrames = capacityFrames;
|
||||
mCapacityFramesMask = capacityFrames - 1;
|
||||
mCapacityBytes = bytesPerFrame * capacityFrames;
|
||||
|
||||
// put everything in one memory allocation, first the pointers, then the deinterleaved channels
|
||||
UInt32 allocSize = (mCapacityBytes + sizeof(Byte *)) * nChannels;
|
||||
Byte *p = (Byte *)CA_malloc(allocSize);
|
||||
memset(p, 0, allocSize);
|
||||
mBuffers = (Byte **)p;
|
||||
p += nChannels * sizeof(Byte *);
|
||||
for (int i = 0; i < nChannels; ++i) {
|
||||
mBuffers[i] = p;
|
||||
p += mCapacityBytes;
|
||||
}
|
||||
|
||||
for (UInt32 i = 0; i<kGeneralRingTimeBoundsQueueSize; ++i)
|
||||
{
|
||||
mTimeBoundsQueue[i].mStartTime = 0;
|
||||
mTimeBoundsQueue[i].mEndTime = 0;
|
||||
mTimeBoundsQueue[i].mUpdateCounter = 0;
|
||||
}
|
||||
mTimeBoundsQueuePtr = 0;
|
||||
}
|
||||
|
||||
void CARingBuffer::Deallocate()
|
||||
{
|
||||
if (mBuffers) {
|
||||
free(mBuffers);
|
||||
mBuffers = NULL;
|
||||
}
|
||||
mNumberChannels = 0;
|
||||
mCapacityBytes = 0;
|
||||
mCapacityFrames = 0;
|
||||
}
|
||||
|
||||
inline void ZeroRange(Byte **buffers, int nchannels, int offset, int nbytes)
|
||||
{
|
||||
while (--nchannels >= 0) {
|
||||
memset(*buffers + offset, 0, nbytes);
|
||||
++buffers;
|
||||
}
|
||||
}
|
||||
|
||||
inline void StoreABL(Byte **buffers, int destOffset, const AudioBufferList *abl, int srcOffset, int nbytes)
|
||||
{
|
||||
int nchannels = abl->mNumberBuffers;
|
||||
const AudioBuffer *src = abl->mBuffers;
|
||||
while (--nchannels >= 0) {
|
||||
if (srcOffset > (int)src->mDataByteSize) continue;
|
||||
memcpy(*buffers + destOffset, (Byte *)src->mData + srcOffset, std::min(nbytes, (int)src->mDataByteSize - srcOffset));
|
||||
++buffers;
|
||||
++src;
|
||||
}
|
||||
}
|
||||
|
||||
inline void FetchABL(AudioBufferList *abl, int destOffset, Byte **buffers, int srcOffset, int nbytes)
|
||||
{
|
||||
int nchannels = abl->mNumberBuffers;
|
||||
AudioBuffer *dest = abl->mBuffers;
|
||||
while (--nchannels >= 0) {
|
||||
if (destOffset > (int)dest->mDataByteSize) continue;
|
||||
memcpy((Byte *)dest->mData + destOffset, *buffers + srcOffset, std::min(nbytes, (int)dest->mDataByteSize - destOffset));
|
||||
++buffers;
|
||||
++dest;
|
||||
}
|
||||
}
|
||||
|
||||
inline void ZeroABL(AudioBufferList *abl, int destOffset, int nbytes)
|
||||
{
|
||||
int nBuffers = abl->mNumberBuffers;
|
||||
AudioBuffer *dest = abl->mBuffers;
|
||||
while (--nBuffers >= 0) {
|
||||
if (destOffset > (int)dest->mDataByteSize) continue;
|
||||
memset((Byte *)dest->mData + destOffset, 0, std::min(nbytes, (int)dest->mDataByteSize - destOffset));
|
||||
++dest;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CARingBufferError CARingBuffer::Store(const AudioBufferList *abl, UInt32 framesToWrite, SampleTime startWrite)
|
||||
{
|
||||
if (framesToWrite == 0)
|
||||
return kCARingBufferError_OK;
|
||||
|
||||
if (framesToWrite > mCapacityFrames)
|
||||
return kCARingBufferError_TooMuch; // too big!
|
||||
|
||||
SampleTime endWrite = startWrite + framesToWrite;
|
||||
|
||||
if (startWrite < EndTime()) {
|
||||
// going backwards, throw everything out
|
||||
SetTimeBounds(startWrite, startWrite);
|
||||
} else if (endWrite - StartTime() <= mCapacityFrames) {
|
||||
// the buffer has not yet wrapped and will not need to
|
||||
} else {
|
||||
// advance the start time past the region we are about to overwrite
|
||||
SampleTime newStart = endWrite - mCapacityFrames; // one buffer of time behind where we're writing
|
||||
SampleTime newEnd = std::max(newStart, EndTime());
|
||||
SetTimeBounds(newStart, newEnd);
|
||||
}
|
||||
|
||||
// write the new frames
|
||||
Byte **buffers = mBuffers;
|
||||
int nchannels = mNumberChannels;
|
||||
int offset0, offset1, nbytes;
|
||||
SampleTime curEnd = EndTime();
|
||||
|
||||
if (startWrite > curEnd) {
|
||||
// we are skipping some samples, so zero the range we are skipping
|
||||
offset0 = FrameOffset(curEnd);
|
||||
offset1 = FrameOffset(startWrite);
|
||||
if (offset0 < offset1)
|
||||
ZeroRange(buffers, nchannels, offset0, offset1 - offset0);
|
||||
else {
|
||||
ZeroRange(buffers, nchannels, offset0, mCapacityBytes - offset0);
|
||||
ZeroRange(buffers, nchannels, 0, offset1);
|
||||
}
|
||||
offset0 = offset1;
|
||||
} else {
|
||||
offset0 = FrameOffset(startWrite);
|
||||
}
|
||||
|
||||
offset1 = FrameOffset(endWrite);
|
||||
if (offset0 < offset1)
|
||||
StoreABL(buffers, offset0, abl, 0, offset1 - offset0);
|
||||
else {
|
||||
nbytes = mCapacityBytes - offset0;
|
||||
StoreABL(buffers, offset0, abl, 0, nbytes);
|
||||
StoreABL(buffers, 0, abl, nbytes, offset1);
|
||||
}
|
||||
|
||||
// now update the end time
|
||||
SetTimeBounds(StartTime(), endWrite);
|
||||
|
||||
return kCARingBufferError_OK; // success
|
||||
}
|
||||
|
||||
void CARingBuffer::SetTimeBounds(SampleTime startTime, SampleTime endTime)
|
||||
{
|
||||
UInt32 nextPtr = mTimeBoundsQueuePtr + 1;
|
||||
UInt32 index = nextPtr & kGeneralRingTimeBoundsQueueMask;
|
||||
|
||||
mTimeBoundsQueue[index].mStartTime = startTime;
|
||||
mTimeBoundsQueue[index].mEndTime = endTime;
|
||||
mTimeBoundsQueue[index].mUpdateCounter = nextPtr;
|
||||
CAAtomicCompareAndSwap32Barrier(mTimeBoundsQueuePtr, mTimeBoundsQueuePtr + 1, (SInt32*)&mTimeBoundsQueuePtr);
|
||||
}
|
||||
|
||||
CARingBufferError CARingBuffer::GetTimeBounds(SampleTime &startTime, SampleTime &endTime)
|
||||
{
|
||||
for (int i=0; i<8; ++i) // fail after a few tries.
|
||||
{
|
||||
UInt32 curPtr = mTimeBoundsQueuePtr;
|
||||
UInt32 index = curPtr & kGeneralRingTimeBoundsQueueMask;
|
||||
CARingBuffer::TimeBounds* bounds = mTimeBoundsQueue + index;
|
||||
|
||||
startTime = bounds->mStartTime;
|
||||
endTime = bounds->mEndTime;
|
||||
UInt32 newPtr = bounds->mUpdateCounter;
|
||||
|
||||
if (newPtr == curPtr)
|
||||
return kCARingBufferError_OK;
|
||||
}
|
||||
return kCARingBufferError_CPUOverload;
|
||||
}
|
||||
|
||||
CARingBufferError CARingBuffer::ClipTimeBounds(SampleTime& startRead, SampleTime& endRead)
|
||||
{
|
||||
SampleTime startTime, endTime;
|
||||
|
||||
CARingBufferError err = GetTimeBounds(startTime, endTime);
|
||||
if (err) return err;
|
||||
|
||||
if (startRead > endTime || endRead < startTime) {
|
||||
endRead = startRead;
|
||||
return kCARingBufferError_OK;
|
||||
}
|
||||
|
||||
startRead = std::max(startRead, startTime);
|
||||
endRead = std::min(endRead, endTime);
|
||||
endRead = std::max(endRead, startRead);
|
||||
|
||||
return kCARingBufferError_OK; // success
|
||||
}
|
||||
|
||||
CARingBufferError CARingBuffer::Fetch(AudioBufferList *abl, UInt32 nFrames, SampleTime startRead)
|
||||
{
|
||||
if (nFrames == 0)
|
||||
return kCARingBufferError_OK;
|
||||
|
||||
startRead = std::max(0LL, startRead);
|
||||
|
||||
SampleTime endRead = startRead + nFrames;
|
||||
|
||||
SampleTime startRead0 = startRead;
|
||||
SampleTime endRead0 = endRead;
|
||||
|
||||
CARingBufferError err = ClipTimeBounds(startRead, endRead);
|
||||
if (err) return err;
|
||||
|
||||
if (startRead == endRead) {
|
||||
ZeroABL(abl, 0, nFrames * mBytesPerFrame);
|
||||
return kCARingBufferError_OK;
|
||||
}
|
||||
|
||||
SInt32 byteSize = (SInt32)((endRead - startRead) * mBytesPerFrame);
|
||||
|
||||
SInt32 destStartByteOffset = std::max((SInt32)0, (SInt32)((startRead - startRead0) * mBytesPerFrame));
|
||||
|
||||
if (destStartByteOffset > 0) {
|
||||
ZeroABL(abl, 0, std::min((SInt32)(nFrames * mBytesPerFrame), destStartByteOffset));
|
||||
}
|
||||
|
||||
SInt32 destEndSize = std::max((SInt32)0, (SInt32)(endRead0 - endRead));
|
||||
if (destEndSize > 0) {
|
||||
ZeroABL(abl, destStartByteOffset + byteSize, destEndSize * mBytesPerFrame);
|
||||
}
|
||||
|
||||
Byte **buffers = mBuffers;
|
||||
int offset0 = FrameOffset(startRead);
|
||||
int offset1 = FrameOffset(endRead);
|
||||
int nbytes;
|
||||
|
||||
if (offset0 < offset1) {
|
||||
nbytes = offset1 - offset0;
|
||||
FetchABL(abl, destStartByteOffset, buffers, offset0, nbytes);
|
||||
} else {
|
||||
nbytes = mCapacityBytes - offset0;
|
||||
FetchABL(abl, destStartByteOffset, buffers, offset0, nbytes);
|
||||
FetchABL(abl, destStartByteOffset + nbytes, buffers, 0, offset1);
|
||||
nbytes += offset1;
|
||||
}
|
||||
|
||||
int nchannels = abl->mNumberBuffers;
|
||||
AudioBuffer *dest = abl->mBuffers;
|
||||
while (--nchannels >= 0)
|
||||
{
|
||||
dest->mDataByteSize = nbytes;
|
||||
dest++;
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
126
BGMApp/PublicUtility/CARingBuffer.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
File: CARingBuffer.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
Version: 1.1
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
|
||||
Inc. ("Apple") in consideration of your agreement to the following
|
||||
terms, and your use, installation, modification or redistribution of
|
||||
this Apple software constitutes acceptance of these terms. If you do
|
||||
not agree with these terms, please do not use, install, modify or
|
||||
redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc. may
|
||||
be used to endorse or promote products derived from the Apple Software
|
||||
without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or
|
||||
implied, are granted by Apple herein, including but not limited to any
|
||||
patent rights that may be infringed by your derivative works or by other
|
||||
works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
|
||||
*/
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#else
|
||||
#include <CoreAudioTypes.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef CARingBuffer_Header
|
||||
#define CARingBuffer_Header
|
||||
|
||||
enum {
|
||||
kCARingBufferError_OK = 0,
|
||||
kCARingBufferError_TooMuch = 3, // fetch start time is earlier than buffer start time and fetch end time is later than buffer end time
|
||||
kCARingBufferError_CPUOverload = 4 // the reader is unable to get enough CPU cycles to capture a consistent snapshot of the time bounds
|
||||
};
|
||||
|
||||
typedef SInt32 CARingBufferError;
|
||||
|
||||
const UInt32 kGeneralRingTimeBoundsQueueSize = 32;
|
||||
const UInt32 kGeneralRingTimeBoundsQueueMask = kGeneralRingTimeBoundsQueueSize - 1;
|
||||
|
||||
class CARingBuffer {
|
||||
public:
|
||||
typedef SInt64 SampleTime;
|
||||
|
||||
CARingBuffer();
|
||||
~CARingBuffer();
|
||||
|
||||
void Allocate(int nChannels, UInt32 bytesPerFrame, UInt32 capacityFrames);
|
||||
// capacityFrames will be rounded up to a power of 2
|
||||
void Deallocate();
|
||||
|
||||
CARingBufferError Store(const AudioBufferList *abl, UInt32 nFrames, SampleTime frameNumber);
|
||||
// Copy nFrames of data into the ring buffer at the specified sample time.
|
||||
// The sample time should normally increase sequentially, though gaps
|
||||
// are filled with zeroes. A sufficiently large gap effectively empties
|
||||
// the buffer before storing the new data.
|
||||
|
||||
// If frameNumber is less than the previous frame number, the behavior is undefined.
|
||||
|
||||
// Return false for failure (buffer not large enough).
|
||||
|
||||
CARingBufferError Fetch(AudioBufferList *abl, UInt32 nFrames, SampleTime frameNumber);
|
||||
// will alter mNumDataBytes of the buffers
|
||||
|
||||
CARingBufferError GetTimeBounds(SampleTime &startTime, SampleTime &endTime);
|
||||
|
||||
protected:
|
||||
|
||||
int FrameOffset(SampleTime frameNumber) { return (frameNumber & mCapacityFramesMask) * mBytesPerFrame; }
|
||||
|
||||
CARingBufferError ClipTimeBounds(SampleTime& startRead, SampleTime& endRead);
|
||||
|
||||
// these should only be called from Store.
|
||||
SampleTime StartTime() const { return mTimeBoundsQueue[mTimeBoundsQueuePtr & kGeneralRingTimeBoundsQueueMask].mStartTime; }
|
||||
SampleTime EndTime() const { return mTimeBoundsQueue[mTimeBoundsQueuePtr & kGeneralRingTimeBoundsQueueMask].mEndTime; }
|
||||
void SetTimeBounds(SampleTime startTime, SampleTime endTime);
|
||||
|
||||
protected:
|
||||
Byte ** mBuffers; // allocated in one chunk of memory
|
||||
int mNumberChannels;
|
||||
UInt32 mBytesPerFrame; // within one deinterleaved channel
|
||||
UInt32 mCapacityFrames; // per channel, must be a power of 2
|
||||
UInt32 mCapacityFramesMask;
|
||||
UInt32 mCapacityBytes; // per channel
|
||||
|
||||
// range of valid sample time in the buffer
|
||||
typedef struct {
|
||||
volatile SampleTime mStartTime;
|
||||
volatile SampleTime mEndTime;
|
||||
volatile UInt32 mUpdateCounter;
|
||||
} TimeBounds;
|
||||
|
||||
CARingBuffer::TimeBounds mTimeBoundsQueue[kGeneralRingTimeBoundsQueueSize];
|
||||
UInt32 mTimeBoundsQueuePtr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
678
BGMDriver/BGMDriver.xcodeproj/project.pbxproj
Normal file
|
@ -0,0 +1,678 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1C0CB6B91C642C600084C15A /* BGM_Client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C0CB6B01C642C600084C15A /* BGM_Client.cpp */; };
|
||||
1C0CB6BA1C642C600084C15A /* BGM_ClientMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C0CB6B21C642C600084C15A /* BGM_ClientMap.cpp */; };
|
||||
1C0CB6BB1C642C600084C15A /* BGM_Clients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C0CB6B41C642C600084C15A /* BGM_Clients.cpp */; };
|
||||
1C305D9D1BE294B5004EBB91 /* CACFNumber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C305D9B1BE294B5004EBB91 /* CACFNumber.cpp */; };
|
||||
1C30A69F1C1E98F000C05AA5 /* CAMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3841BBCEFE8000E2DD1 /* CAMutex.cpp */; };
|
||||
1C38210E1C4A163A00A0C8C6 /* BGM_TaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C38210C1C4A163A00A0C8C6 /* BGM_TaskQueue.cpp */; };
|
||||
1C3821111C4A18DE00A0C8C6 /* CAPThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C38210F1C4A18DE00A0C8C6 /* CAPThread.cpp */; };
|
||||
1C3DB4871BE063C500EC8160 /* BGM_DeviceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4861BE063C500EC8160 /* BGM_DeviceTests.mm */; };
|
||||
1C8034DD1BDD073B00668E00 /* BGM_ClientsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C8034DC1BDD073B00668E00 /* BGM_ClientsTests.mm */; };
|
||||
1CB8B36E1BBBD541000E2DD1 /* BGM_PlugInInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B36D1BBBD541000E2DD1 /* BGM_PlugInInterface.cpp */; };
|
||||
1CB8B3731BBBD8A4000E2DD1 /* CADebugMacros.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3701BBBD8A4000E2DD1 /* CADebugMacros.cpp */; };
|
||||
1CB8B3761BBBD924000E2DD1 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB8B3741BBBD924000E2DD1 /* CoreAudio.framework */; };
|
||||
1CB8B3771BBBD924000E2DD1 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB8B3751BBBD924000E2DD1 /* CoreFoundation.framework */; };
|
||||
1CB8B37A1BBBDFA2000E2DD1 /* CADebugPrintf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3781BBBDFA2000E2DD1 /* CADebugPrintf.cpp */; };
|
||||
1CB8B37D1BBCCF62000E2DD1 /* BGM_PlugIn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B37B1BBCCF62000E2DD1 /* BGM_PlugIn.cpp */; };
|
||||
1CB8B3801BBCCF87000E2DD1 /* BGM_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B37E1BBCCF87000E2DD1 /* BGM_Device.cpp */; };
|
||||
1CB8B3831BBCE7B5000E2DD1 /* BGM_Object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3811BBCE7B5000E2DD1 /* BGM_Object.cpp */; };
|
||||
1CB8B3861BBCEFE8000E2DD1 /* CAMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3841BBCEFE8000E2DD1 /* CAMutex.cpp */; };
|
||||
1CB8B3891BBCF08A000E2DD1 /* CAHostTimeBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3871BBCF08A000E2DD1 /* CAHostTimeBase.cpp */; };
|
||||
1CB8B38E1BBCF4A9000E2DD1 /* CACFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B38A1BBCF4A9000E2DD1 /* CACFString.cpp */; };
|
||||
1CB8B38F1BBCF4A9000E2DD1 /* CAVolumeCurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B38C1BBCF4A9000E2DD1 /* CAVolumeCurve.cpp */; };
|
||||
1CB8B3921BBCF50A000E2DD1 /* BGM_WrappedAudioEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3901BBCF50A000E2DD1 /* BGM_WrappedAudioEngine.cpp */; };
|
||||
1CB8B3951BBD2418000E2DD1 /* CADispatchQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3931BBD2418000E2DD1 /* CADispatchQueue.cpp */; };
|
||||
1CBB322C1BDD3A3000C9BD55 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB8B3741BBBD924000E2DD1 /* CoreAudio.framework */; };
|
||||
1CC1DF891BE558B000FB8FE4 /* CADebugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF871BE558B000FB8FE4 /* CADebugger.cpp */; };
|
||||
1CC1DF8A1BE5703B00FB8FE4 /* CACFArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CE3E68F1BE2683900167F5D /* CACFArray.cpp */; };
|
||||
1CC1DF8B1BE5703B00FB8FE4 /* CACFDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CE3E68C1BE263CA00167F5D /* CACFDictionary.cpp */; };
|
||||
1CC1DF8D1BE5705700FB8FE4 /* CACFDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CE3E68C1BE263CA00167F5D /* CACFDictionary.cpp */; };
|
||||
1CC1DF8E1BE5706C00FB8FE4 /* CACFArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CE3E68F1BE2683900167F5D /* CACFArray.cpp */; };
|
||||
1CC1DF931BE7B79500FB8FE4 /* CADebugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF871BE558B000FB8FE4 /* CADebugger.cpp */; };
|
||||
1CC1DF941BE7B79500FB8FE4 /* CAVolumeCurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B38C1BBCF4A9000E2DD1 /* CAVolumeCurve.cpp */; };
|
||||
1CC1DF9E1BE94AA200FB8FE4 /* DeviceIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 1CC1DF9D1BE94AA200FB8FE4 /* DeviceIcon.icns */; };
|
||||
27379B821C76D62D0084A24C /* CADebugMacros.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3701BBBD8A4000E2DD1 /* CADebugMacros.cpp */; };
|
||||
27379B831C76D62D0084A24C /* CADebugPrintf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3781BBBDFA2000E2DD1 /* CADebugPrintf.cpp */; };
|
||||
277EE6591C7269910037F1EE /* BGM_ClientMapTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 277EE6581C7269910037F1EE /* BGM_ClientMapTests.mm */; };
|
||||
277EE65A1C728C630037F1EE /* BGM_Client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C0CB6B01C642C600084C15A /* BGM_Client.cpp */; };
|
||||
277EE65B1C728C630037F1EE /* BGM_ClientMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C0CB6B21C642C600084C15A /* BGM_ClientMap.cpp */; };
|
||||
277EE65C1C728C630037F1EE /* BGM_Clients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C0CB6B41C642C600084C15A /* BGM_Clients.cpp */; };
|
||||
277EE65D1C728C630037F1EE /* BGM_TaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C38210C1C4A163A00A0C8C6 /* BGM_TaskQueue.cpp */; };
|
||||
277EE65E1C728C9D0037F1EE /* BGM_PlugIn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B37B1BBCCF62000E2DD1 /* BGM_PlugIn.cpp */; };
|
||||
277EE65F1C728C9D0037F1EE /* CAPThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C38210F1C4A18DE00A0C8C6 /* CAPThread.cpp */; };
|
||||
277EE6601C728CA70037F1EE /* BGM_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B37E1BBCCF87000E2DD1 /* BGM_Device.cpp */; };
|
||||
277EE6611C728CAE0037F1EE /* BGM_Object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3811BBCE7B5000E2DD1 /* BGM_Object.cpp */; };
|
||||
277EE6621C728CC70037F1EE /* BGM_WrappedAudioEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3901BBCF50A000E2DD1 /* BGM_WrappedAudioEngine.cpp */; };
|
||||
277EE6631C728CC70037F1EE /* CADispatchQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3931BBD2418000E2DD1 /* CADispatchQueue.cpp */; };
|
||||
277EE6641C728CDB0037F1EE /* CACFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B38A1BBCF4A9000E2DD1 /* CACFString.cpp */; };
|
||||
277EE6651C728CDB0037F1EE /* CAHostTimeBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3871BBCF08A000E2DD1 /* CAHostTimeBase.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1C0CB6A61C4E06C00084C15A /* CAAtomicStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAtomicStack.h; path = PublicUtility/CAAtomicStack.h; sourceTree = "<group>"; };
|
||||
1C0CB6A71C4E06F70084C15A /* CAAtomic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAtomic.h; path = PublicUtility/CAAtomic.h; sourceTree = "<group>"; };
|
||||
1C0CB6A91C50A3AF0084C15A /* CAAutoDisposer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAutoDisposer.h; path = PublicUtility/CAAutoDisposer.h; sourceTree = "<group>"; };
|
||||
1C0CB6B01C642C600084C15A /* BGM_Client.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_Client.cpp; sourceTree = "<group>"; };
|
||||
1C0CB6B11C642C600084C15A /* BGM_Client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_Client.h; sourceTree = "<group>"; };
|
||||
1C0CB6B21C642C600084C15A /* BGM_ClientMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_ClientMap.cpp; sourceTree = "<group>"; };
|
||||
1C0CB6B31C642C600084C15A /* BGM_ClientMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_ClientMap.h; sourceTree = "<group>"; };
|
||||
1C0CB6B41C642C600084C15A /* BGM_Clients.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_Clients.cpp; sourceTree = "<group>"; };
|
||||
1C0CB6B51C642C600084C15A /* BGM_Clients.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_Clients.h; sourceTree = "<group>"; };
|
||||
1C0CB6B81C642C600084C15A /* BGM_ClientTasks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_ClientTasks.h; sourceTree = "<group>"; };
|
||||
1C305D9B1BE294B5004EBB91 /* CACFNumber.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CACFNumber.cpp; path = PublicUtility/CACFNumber.cpp; sourceTree = "<group>"; };
|
||||
1C305D9C1BE294B5004EBB91 /* CACFNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CACFNumber.h; path = PublicUtility/CACFNumber.h; sourceTree = "<group>"; };
|
||||
1C38210C1C4A163A00A0C8C6 /* BGM_TaskQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_TaskQueue.cpp; sourceTree = "<group>"; };
|
||||
1C38210D1C4A163A00A0C8C6 /* BGM_TaskQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_TaskQueue.h; sourceTree = "<group>"; };
|
||||
1C38210F1C4A18DE00A0C8C6 /* CAPThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAPThread.cpp; path = PublicUtility/CAPThread.cpp; sourceTree = "<group>"; };
|
||||
1C3821101C4A18DE00A0C8C6 /* CAPThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAPThread.h; path = PublicUtility/CAPThread.h; sourceTree = "<group>"; };
|
||||
1C3DB4861BE063C500EC8160 /* BGM_DeviceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGM_DeviceTests.mm; sourceTree = "<group>"; };
|
||||
1C8034DA1BDD073B00668E00 /* BGMDriverTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BGMDriverTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1C8034DC1BDD073B00668E00 /* BGM_ClientsTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BGM_ClientsTests.mm; sourceTree = "<group>"; };
|
||||
1C8034DE1BDD073B00668E00 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
1CB8B3641BBBB78D000E2DD1 /* Background Music Device.driver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Background Music Device.driver"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1CB8B3681BBBB78D000E2DD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
1CB8B36D1BBBD541000E2DD1 /* BGM_PlugInInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_PlugInInterface.cpp; sourceTree = "<group>"; };
|
||||
1CB8B3701BBBD8A4000E2DD1 /* CADebugMacros.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CADebugMacros.cpp; path = PublicUtility/CADebugMacros.cpp; sourceTree = "<group>"; };
|
||||
1CB8B3711BBBD8A4000E2DD1 /* CADebugMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CADebugMacros.h; path = PublicUtility/CADebugMacros.h; sourceTree = "<group>"; };
|
||||
1CB8B3721BBBD8A4000E2DD1 /* CAException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAException.h; path = PublicUtility/CAException.h; sourceTree = "<group>"; };
|
||||
1CB8B3741BBBD924000E2DD1 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
|
||||
1CB8B3751BBBD924000E2DD1 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
|
||||
1CB8B3781BBBDFA2000E2DD1 /* CADebugPrintf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CADebugPrintf.cpp; path = PublicUtility/CADebugPrintf.cpp; sourceTree = "<group>"; };
|
||||
1CB8B3791BBBDFA2000E2DD1 /* CADebugPrintf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CADebugPrintf.h; path = PublicUtility/CADebugPrintf.h; sourceTree = "<group>"; };
|
||||
1CB8B37B1BBCCF62000E2DD1 /* BGM_PlugIn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_PlugIn.cpp; sourceTree = "<group>"; };
|
||||
1CB8B37C1BBCCF62000E2DD1 /* BGM_PlugIn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_PlugIn.h; sourceTree = "<group>"; };
|
||||
1CB8B37E1BBCCF87000E2DD1 /* BGM_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_Device.cpp; sourceTree = "<group>"; };
|
||||
1CB8B37F1BBCCF87000E2DD1 /* BGM_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_Device.h; sourceTree = "<group>"; };
|
||||
1CB8B3811BBCE7B5000E2DD1 /* BGM_Object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_Object.cpp; sourceTree = "<group>"; };
|
||||
1CB8B3821BBCE7B5000E2DD1 /* BGM_Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_Object.h; sourceTree = "<group>"; };
|
||||
1CB8B3841BBCEFE8000E2DD1 /* CAMutex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAMutex.cpp; path = PublicUtility/CAMutex.cpp; sourceTree = "<group>"; };
|
||||
1CB8B3851BBCEFE8000E2DD1 /* CAMutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAMutex.h; path = PublicUtility/CAMutex.h; sourceTree = "<group>"; };
|
||||
1CB8B3871BBCF08A000E2DD1 /* CAHostTimeBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAHostTimeBase.cpp; path = PublicUtility/CAHostTimeBase.cpp; sourceTree = "<group>"; };
|
||||
1CB8B3881BBCF08A000E2DD1 /* CAHostTimeBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAHostTimeBase.h; path = PublicUtility/CAHostTimeBase.h; sourceTree = "<group>"; };
|
||||
1CB8B38A1BBCF4A9000E2DD1 /* CACFString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CACFString.cpp; path = PublicUtility/CACFString.cpp; sourceTree = "<group>"; };
|
||||
1CB8B38B1BBCF4A9000E2DD1 /* CACFString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CACFString.h; path = PublicUtility/CACFString.h; sourceTree = "<group>"; };
|
||||
1CB8B38C1BBCF4A9000E2DD1 /* CAVolumeCurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAVolumeCurve.cpp; path = PublicUtility/CAVolumeCurve.cpp; sourceTree = "<group>"; };
|
||||
1CB8B38D1BBCF4A9000E2DD1 /* CAVolumeCurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAVolumeCurve.h; path = PublicUtility/CAVolumeCurve.h; sourceTree = "<group>"; };
|
||||
1CB8B3901BBCF50A000E2DD1 /* BGM_WrappedAudioEngine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_WrappedAudioEngine.cpp; sourceTree = "<group>"; };
|
||||
1CB8B3911BBCF50A000E2DD1 /* BGM_WrappedAudioEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_WrappedAudioEngine.h; sourceTree = "<group>"; };
|
||||
1CB8B3931BBD2418000E2DD1 /* CADispatchQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CADispatchQueue.cpp; path = PublicUtility/CADispatchQueue.cpp; sourceTree = "<group>"; };
|
||||
1CB8B3941BBD2418000E2DD1 /* CADispatchQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CADispatchQueue.h; path = PublicUtility/CADispatchQueue.h; sourceTree = "<group>"; };
|
||||
1CB8B3961BBD26D2000E2DD1 /* BGM_Types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BGM_Types.h; sourceTree = "<group>"; };
|
||||
1CC1DF871BE558B000FB8FE4 /* CADebugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CADebugger.cpp; path = PublicUtility/CADebugger.cpp; sourceTree = "<group>"; };
|
||||
1CC1DF881BE558B000FB8FE4 /* CADebugger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CADebugger.h; path = PublicUtility/CADebugger.h; sourceTree = "<group>"; };
|
||||
1CC1DF991BE865C000FB8FE4 /* quick_install.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = quick_install.sh; sourceTree = "<group>"; };
|
||||
1CC1DF9D1BE94AA200FB8FE4 /* DeviceIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = DeviceIcon.icns; sourceTree = "<group>"; };
|
||||
1CD1FD311BE0364A004F7E1B /* BGM_TestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_TestUtils.h; sourceTree = "<group>"; };
|
||||
1CE3E68C1BE263CA00167F5D /* CACFDictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CACFDictionary.cpp; path = PublicUtility/CACFDictionary.cpp; sourceTree = "<group>"; };
|
||||
1CE3E68D1BE263CA00167F5D /* CACFDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CACFDictionary.h; path = PublicUtility/CACFDictionary.h; sourceTree = "<group>"; };
|
||||
1CE3E68F1BE2683900167F5D /* CACFArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CACFArray.cpp; path = PublicUtility/CACFArray.cpp; sourceTree = "<group>"; };
|
||||
1CE3E6901BE2683900167F5D /* CACFArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CACFArray.h; path = PublicUtility/CACFArray.h; sourceTree = "<group>"; };
|
||||
277EE6581C7269910037F1EE /* BGM_ClientMapTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGM_ClientMapTests.mm; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
1C8034D71BDD073B00668E00 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1CBB322C1BDD3A3000C9BD55 /* CoreAudio.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
1CB8B3611BBBB78D000E2DD1 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1CB8B3761BBBD924000E2DD1 /* CoreAudio.framework in Frameworks */,
|
||||
1CB8B3771BBBD924000E2DD1 /* CoreFoundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
1C0CB6AF1C642C600084C15A /* Device Clients */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1C0CB6B11C642C600084C15A /* BGM_Client.h */,
|
||||
1C0CB6B01C642C600084C15A /* BGM_Client.cpp */,
|
||||
1C0CB6B31C642C600084C15A /* BGM_ClientMap.h */,
|
||||
1C0CB6B21C642C600084C15A /* BGM_ClientMap.cpp */,
|
||||
1C0CB6B51C642C600084C15A /* BGM_Clients.h */,
|
||||
1C0CB6B41C642C600084C15A /* BGM_Clients.cpp */,
|
||||
1C0CB6B81C642C600084C15A /* BGM_ClientTasks.h */,
|
||||
);
|
||||
name = "Device Clients";
|
||||
path = DeviceClients;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1C8034DB1BDD073B00668E00 /* BGMDriverTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CD1FD311BE0364A004F7E1B /* BGM_TestUtils.h */,
|
||||
1C8034DC1BDD073B00668E00 /* BGM_ClientsTests.mm */,
|
||||
277EE6581C7269910037F1EE /* BGM_ClientMapTests.mm */,
|
||||
1C3DB4861BE063C500EC8160 /* BGM_DeviceTests.mm */,
|
||||
1C8034DE1BDD073B00668E00 /* Info.plist */,
|
||||
);
|
||||
path = BGMDriverTests;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
1CB8B3591BBBB69C000E2DD1 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CB8B3661BBBB78D000E2DD1 /* BGMDriver */,
|
||||
1C8034DB1BDD073B00668E00 /* BGMDriverTests */,
|
||||
1CB8B36F1BBBD7AE000E2DD1 /* PublicUtility */,
|
||||
1CB8B3651BBBB78D000E2DD1 /* Products */,
|
||||
1CB8B3741BBBD924000E2DD1 /* CoreAudio.framework */,
|
||||
1CB8B3751BBBD924000E2DD1 /* CoreFoundation.framework */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CB8B3651BBBB78D000E2DD1 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CB8B3641BBBB78D000E2DD1 /* Background Music Device.driver */,
|
||||
1C8034DA1BDD073B00668E00 /* BGMDriverTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CB8B3661BBBB78D000E2DD1 /* BGMDriver */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CB8B3961BBD26D2000E2DD1 /* BGM_Types.h */,
|
||||
1CB8B36D1BBBD541000E2DD1 /* BGM_PlugInInterface.cpp */,
|
||||
1CB8B37C1BBCCF62000E2DD1 /* BGM_PlugIn.h */,
|
||||
1CB8B37B1BBCCF62000E2DD1 /* BGM_PlugIn.cpp */,
|
||||
1CB8B3821BBCE7B5000E2DD1 /* BGM_Object.h */,
|
||||
1CB8B3811BBCE7B5000E2DD1 /* BGM_Object.cpp */,
|
||||
1CB8B37F1BBCCF87000E2DD1 /* BGM_Device.h */,
|
||||
1CB8B37E1BBCCF87000E2DD1 /* BGM_Device.cpp */,
|
||||
1C0CB6AF1C642C600084C15A /* Device Clients */,
|
||||
1C38210D1C4A163A00A0C8C6 /* BGM_TaskQueue.h */,
|
||||
1C38210C1C4A163A00A0C8C6 /* BGM_TaskQueue.cpp */,
|
||||
1CB8B3911BBCF50A000E2DD1 /* BGM_WrappedAudioEngine.h */,
|
||||
1CB8B3901BBCF50A000E2DD1 /* BGM_WrappedAudioEngine.cpp */,
|
||||
1CB8B3671BBBB78D000E2DD1 /* Supporting Files */,
|
||||
);
|
||||
path = BGMDriver;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CB8B3671BBBB78D000E2DD1 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1CC1DF9D1BE94AA200FB8FE4 /* DeviceIcon.icns */,
|
||||
1CC1DF991BE865C000FB8FE4 /* quick_install.sh */,
|
||||
1CB8B3681BBBB78D000E2DD1 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1CB8B36F1BBBD7AE000E2DD1 /* PublicUtility */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1C0CB6A71C4E06F70084C15A /* CAAtomic.h */,
|
||||
1C0CB6A61C4E06C00084C15A /* CAAtomicStack.h */,
|
||||
1C0CB6A91C50A3AF0084C15A /* CAAutoDisposer.h */,
|
||||
1CE3E68F1BE2683900167F5D /* CACFArray.cpp */,
|
||||
1CE3E6901BE2683900167F5D /* CACFArray.h */,
|
||||
1CE3E68C1BE263CA00167F5D /* CACFDictionary.cpp */,
|
||||
1CE3E68D1BE263CA00167F5D /* CACFDictionary.h */,
|
||||
1C305D9B1BE294B5004EBB91 /* CACFNumber.cpp */,
|
||||
1C305D9C1BE294B5004EBB91 /* CACFNumber.h */,
|
||||
1CB8B38A1BBCF4A9000E2DD1 /* CACFString.cpp */,
|
||||
1CB8B38B1BBCF4A9000E2DD1 /* CACFString.h */,
|
||||
1CC1DF871BE558B000FB8FE4 /* CADebugger.cpp */,
|
||||
1CC1DF881BE558B000FB8FE4 /* CADebugger.h */,
|
||||
1CB8B3701BBBD8A4000E2DD1 /* CADebugMacros.cpp */,
|
||||
1CB8B3711BBBD8A4000E2DD1 /* CADebugMacros.h */,
|
||||
1CB8B3781BBBDFA2000E2DD1 /* CADebugPrintf.cpp */,
|
||||
1CB8B3791BBBDFA2000E2DD1 /* CADebugPrintf.h */,
|
||||
1CB8B3931BBD2418000E2DD1 /* CADispatchQueue.cpp */,
|
||||
1CB8B3941BBD2418000E2DD1 /* CADispatchQueue.h */,
|
||||
1CB8B3721BBBD8A4000E2DD1 /* CAException.h */,
|
||||
1CB8B3871BBCF08A000E2DD1 /* CAHostTimeBase.cpp */,
|
||||
1CB8B3881BBCF08A000E2DD1 /* CAHostTimeBase.h */,
|
||||
1CB8B3841BBCEFE8000E2DD1 /* CAMutex.cpp */,
|
||||
1CB8B3851BBCEFE8000E2DD1 /* CAMutex.h */,
|
||||
1C38210F1C4A18DE00A0C8C6 /* CAPThread.cpp */,
|
||||
1C3821101C4A18DE00A0C8C6 /* CAPThread.h */,
|
||||
1CB8B38C1BBCF4A9000E2DD1 /* CAVolumeCurve.cpp */,
|
||||
1CB8B38D1BBCF4A9000E2DD1 /* CAVolumeCurve.h */,
|
||||
);
|
||||
name = PublicUtility;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
1C8034D91BDD073B00668E00 /* BGMDriverTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1C8034DF1BDD073B00668E00 /* Build configuration list for PBXNativeTarget "BGMDriverTests" */;
|
||||
buildPhases = (
|
||||
1C8034D61BDD073B00668E00 /* Sources */,
|
||||
1C8034D71BDD073B00668E00 /* Frameworks */,
|
||||
1C8034D81BDD073B00668E00 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = BGMDriverTests;
|
||||
productName = BGMDriverTests;
|
||||
productReference = 1C8034DA1BDD073B00668E00 /* BGMDriverTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
1CB8B3631BBBB78D000E2DD1 /* Background Music Device */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1CB8B3691BBBB78D000E2DD1 /* Build configuration list for PBXNativeTarget "Background Music Device" */;
|
||||
buildPhases = (
|
||||
1CB8B3601BBBB78D000E2DD1 /* Sources */,
|
||||
1CB8B3611BBBB78D000E2DD1 /* Frameworks */,
|
||||
1CB8B3621BBBB78D000E2DD1 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Background Music Device";
|
||||
productName = BGMDriver;
|
||||
productReference = 1CB8B3641BBBB78D000E2DD1 /* Background Music Device.driver */;
|
||||
productType = "com.apple.product-type.bundle";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
1CB8B35A1BBBB69C000E2DD1 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
CLASSPREFIX = BGM_;
|
||||
LastUpgradeCheck = 0700;
|
||||
ORGANIZATIONNAME = "Kyle Neideck";
|
||||
TargetAttributes = {
|
||||
1C8034D91BDD073B00668E00 = {
|
||||
CreatedOnToolsVersion = 7.0.1;
|
||||
};
|
||||
1CB8B3631BBBB78D000E2DD1 = {
|
||||
CreatedOnToolsVersion = 6.4;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 1CB8B35D1BBBB69C000E2DD1 /* Build configuration list for PBXProject "BGMDriver" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 1CB8B3591BBBB69C000E2DD1;
|
||||
productRefGroup = 1CB8B3651BBBB78D000E2DD1 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
1CB8B3631BBBB78D000E2DD1 /* Background Music Device */,
|
||||
1C8034D91BDD073B00668E00 /* BGMDriverTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
1C8034D81BDD073B00668E00 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
1CB8B3621BBBB78D000E2DD1 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1CC1DF9E1BE94AA200FB8FE4 /* DeviceIcon.icns in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
1C8034D61BDD073B00668E00 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
27379B821C76D62D0084A24C /* CADebugMacros.cpp in Sources */,
|
||||
27379B831C76D62D0084A24C /* CADebugPrintf.cpp in Sources */,
|
||||
277EE6641C728CDB0037F1EE /* CACFString.cpp in Sources */,
|
||||
277EE6651C728CDB0037F1EE /* CAHostTimeBase.cpp in Sources */,
|
||||
277EE6621C728CC70037F1EE /* BGM_WrappedAudioEngine.cpp in Sources */,
|
||||
277EE6631C728CC70037F1EE /* CADispatchQueue.cpp in Sources */,
|
||||
277EE6611C728CAE0037F1EE /* BGM_Object.cpp in Sources */,
|
||||
277EE6601C728CA70037F1EE /* BGM_Device.cpp in Sources */,
|
||||
277EE65E1C728C9D0037F1EE /* BGM_PlugIn.cpp in Sources */,
|
||||
277EE65F1C728C9D0037F1EE /* CAPThread.cpp in Sources */,
|
||||
277EE65A1C728C630037F1EE /* BGM_Client.cpp in Sources */,
|
||||
277EE65B1C728C630037F1EE /* BGM_ClientMap.cpp in Sources */,
|
||||
277EE65C1C728C630037F1EE /* BGM_Clients.cpp in Sources */,
|
||||
277EE65D1C728C630037F1EE /* BGM_TaskQueue.cpp in Sources */,
|
||||
1C30A69F1C1E98F000C05AA5 /* CAMutex.cpp in Sources */,
|
||||
1CC1DF931BE7B79500FB8FE4 /* CADebugger.cpp in Sources */,
|
||||
1CC1DF941BE7B79500FB8FE4 /* CAVolumeCurve.cpp in Sources */,
|
||||
1CC1DF8E1BE5706C00FB8FE4 /* CACFArray.cpp in Sources */,
|
||||
277EE6591C7269910037F1EE /* BGM_ClientMapTests.mm in Sources */,
|
||||
1CC1DF8D1BE5705700FB8FE4 /* CACFDictionary.cpp in Sources */,
|
||||
1C3DB4871BE063C500EC8160 /* BGM_DeviceTests.mm in Sources */,
|
||||
1C8034DD1BDD073B00668E00 /* BGM_ClientsTests.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
1CB8B3601BBBB78D000E2DD1 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1CC1DF8A1BE5703B00FB8FE4 /* CACFArray.cpp in Sources */,
|
||||
1CC1DF8B1BE5703B00FB8FE4 /* CACFDictionary.cpp in Sources */,
|
||||
1CB8B3801BBCCF87000E2DD1 /* BGM_Device.cpp in Sources */,
|
||||
1CB8B3951BBD2418000E2DD1 /* CADispatchQueue.cpp in Sources */,
|
||||
1CB8B37A1BBBDFA2000E2DD1 /* CADebugPrintf.cpp in Sources */,
|
||||
1C305D9D1BE294B5004EBB91 /* CACFNumber.cpp in Sources */,
|
||||
1CB8B3731BBBD8A4000E2DD1 /* CADebugMacros.cpp in Sources */,
|
||||
1C0CB6B91C642C600084C15A /* BGM_Client.cpp in Sources */,
|
||||
1C3821111C4A18DE00A0C8C6 /* CAPThread.cpp in Sources */,
|
||||
1CB8B38E1BBCF4A9000E2DD1 /* CACFString.cpp in Sources */,
|
||||
1CB8B3921BBCF50A000E2DD1 /* BGM_WrappedAudioEngine.cpp in Sources */,
|
||||
1CB8B36E1BBBD541000E2DD1 /* BGM_PlugInInterface.cpp in Sources */,
|
||||
1CB8B37D1BBCCF62000E2DD1 /* BGM_PlugIn.cpp in Sources */,
|
||||
1C0CB6BA1C642C600084C15A /* BGM_ClientMap.cpp in Sources */,
|
||||
1CB8B3831BBCE7B5000E2DD1 /* BGM_Object.cpp in Sources */,
|
||||
1CC1DF891BE558B000FB8FE4 /* CADebugger.cpp in Sources */,
|
||||
1CB8B3861BBCEFE8000E2DD1 /* CAMutex.cpp in Sources */,
|
||||
1CB8B3891BBCF08A000E2DD1 /* CAHostTimeBase.cpp in Sources */,
|
||||
1CB8B38F1BBCF4A9000E2DD1 /* CAVolumeCurve.cpp in Sources */,
|
||||
1C38210E1C4A163A00A0C8C6 /* BGM_TaskQueue.cpp in Sources */,
|
||||
1C0CB6BB1C642C600084C15A /* BGM_Clients.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
1C8034E01BDD073B00668E00 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = BGMDriverTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGMDriverTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1C8034E11BDD073B00668E00 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = c11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = BGMDriverTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGMDriverTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1CB8B35E1BBBB69C000E2DD1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_ASSIGN_ENUM = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"CoreAudio_Debug=1",
|
||||
"CoreAudio_UseSysLog=1",
|
||||
"CoreAudio_StopOnAssert=1",
|
||||
);
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1CB8B35F1BBBB69C000E2DD1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_WARN_ASSIGN_ENUM = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 3;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=0",
|
||||
"CoreAudio_Debug=0",
|
||||
"CoreAudio_UseSysLog=1",
|
||||
"CoreAudio_StopOnAssert=0",
|
||||
);
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1CB8B36A1BBBB78D000E2DD1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = "compiler-default";
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_CPP_RTTI = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = BGMDriver/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGMDriver;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = NO;
|
||||
WRAPPER_EXTENSION = driver;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1CB8B36B1BBBB78D000E2DD1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = "compiler-default";
|
||||
GCC_ENABLE_CPP_RTTI = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INFOPLIST_FILE = BGMDriver/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGMDriver;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = NO;
|
||||
WRAPPER_EXTENSION = driver;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
1C8034DF1BDD073B00668E00 /* Build configuration list for PBXNativeTarget "BGMDriverTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1C8034E01BDD073B00668E00 /* Debug */,
|
||||
1C8034E11BDD073B00668E00 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1CB8B35D1BBBB69C000E2DD1 /* Build configuration list for PBXProject "BGMDriver" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1CB8B35E1BBBB69C000E2DD1 /* Debug */,
|
||||
1CB8B35F1BBBB69C000E2DD1 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1CB8B3691BBBB78D000E2DD1 /* Build configuration list for PBXNativeTarget "Background Music Device" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1CB8B36A1BBBB78D000E2DD1 /* Debug */,
|
||||
1CB8B36B1BBBB78D000E2DD1 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 1CB8B35A1BBBB69C000E2DD1 /* Project object */;
|
||||
}
|
2342
BGMDriver/BGMDriver/BGM_Device.cpp
Normal file
224
BGMDriver/BGMDriver/BGM_Device.h
Normal file
|
@ -0,0 +1,224 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_Device.h
|
||||
// BGMDriver
|
||||
//
|
||||
// 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.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
#ifndef __BGMDriver__BGM_Device__
|
||||
#define __BGMDriver__BGM_Device__
|
||||
|
||||
// SuperClass Includes
|
||||
#include "BGM_Object.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
#include "BGM_WrappedAudioEngine.h"
|
||||
#include "BGM_Clients.h"
|
||||
#include "BGM_TaskQueue.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CACFString.h"
|
||||
#include "CAMutex.h"
|
||||
#include "CAVolumeCurve.h"
|
||||
|
||||
|
||||
class BGM_Device
|
||||
:
|
||||
public BGM_Object
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
public:
|
||||
static BGM_Device& GetInstance();
|
||||
|
||||
private:
|
||||
static void StaticInitializer();
|
||||
|
||||
protected:
|
||||
BGM_Device();
|
||||
virtual ~BGM_Device();
|
||||
|
||||
virtual void Activate();
|
||||
virtual void Deactivate();
|
||||
|
||||
private:
|
||||
void InitLoopback();
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
public:
|
||||
virtual bool HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual bool IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData) const;
|
||||
virtual void GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* __nonnull outData) const;
|
||||
virtual void SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, const void* __nonnull inData);
|
||||
|
||||
#pragma mark Device Property Operations
|
||||
|
||||
private:
|
||||
bool Device_HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
bool Device_IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
UInt32 Device_GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData) const;
|
||||
void Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* __nonnull outData) const;
|
||||
void Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, const void* __nonnull inData);
|
||||
|
||||
#pragma mark Stream Property Operations
|
||||
|
||||
private:
|
||||
bool Stream_HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
bool Stream_IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
UInt32 Stream_GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData) const;
|
||||
void Stream_GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* __nonnull outData) const;
|
||||
void Stream_SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, const void* __nonnull inData);
|
||||
|
||||
#pragma mark Control Property Operations
|
||||
|
||||
private:
|
||||
bool Control_HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
bool Control_IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
UInt32 Control_GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData) const;
|
||||
void Control_GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* __nonnull outData) const;
|
||||
void Control_SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* __nullable inQualifierData, UInt32 inDataSize, const void* __nonnull inData);
|
||||
|
||||
#pragma mark IO Operations
|
||||
|
||||
public:
|
||||
void StartIO(UInt32 inClientID);
|
||||
void StopIO(UInt32 inClientID);
|
||||
|
||||
void GetZeroTimeStamp(Float64& outSampleTime, UInt64& outHostTime, UInt64& outSeed);
|
||||
|
||||
void WillDoIOOperation(UInt32 inOperationID, bool& outWillDo, bool& outWillDoInPlace) const;
|
||||
void BeginIOOperation(UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo& inIOCycleInfo, UInt32 inClientID);
|
||||
void DoIOOperation(AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo& inIOCycleInfo, void* __nonnull ioMainBuffer, void* __nullable ioSecondaryBuffer);
|
||||
void EndIOOperation(UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo& inIOCycleInfo, UInt32 inClientID);
|
||||
|
||||
private:
|
||||
void ReadInputData(UInt32 inIOBufferFrameSize, Float64 inSampleTime, void* __nonnull outBuffer);
|
||||
void WriteOutputData(UInt32 inIOBufferFrameSize, Float64 inSampleTime, const void* __nonnull inBuffer);
|
||||
void ApplyClientRelativeVolume(UInt32 inClientID, UInt32 inIOBufferFrameSize, void* __nonnull inBuffer) const;
|
||||
bool BufferIsAudible(UInt32 inIOBufferFrameSize, const void* __nonnull inBuffer);
|
||||
void UpdateAudibleStateSampleTimes_PreMix(UInt32 inClientID, UInt32 inIOBufferFrameSize, Float64 inOutputSampleTime, const void* __nonnull inBuffer);
|
||||
void UpdateAudibleStateSampleTimes_PostMix(UInt32 inIOBufferFrameSize, Float64 inOutputSampleTime, const void* __nonnull inBuffer);
|
||||
void UpdateDeviceAudibleState(UInt32 inIOBufferFrameSize, Float64 inOutputSampleTime);
|
||||
|
||||
#pragma mark Hardware Accessors
|
||||
|
||||
private:
|
||||
void _HW_Open();
|
||||
void _HW_Close();
|
||||
kern_return_t _HW_StartIO();
|
||||
void _HW_StopIO();
|
||||
Float64 _HW_GetSampleRate() const;
|
||||
kern_return_t _HW_SetSampleRate(Float64 inNewSampleRate);
|
||||
UInt32 _HW_GetRingBufferFrameSize() const;
|
||||
SInt32 _HW_GetVolumeControlValue(int inObjectID) const;
|
||||
kern_return_t _HW_SetVolumeControlValue(int inObjectID, SInt32 inNewControlValue);
|
||||
UInt32 _HW_GetMuteControlValue(AudioObjectID inObjectID) const;
|
||||
kern_return_t _HW_SetMuteControlValue(AudioObjectID inObjectID, UInt32 inValue);
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
public:
|
||||
CFStringRef __nonnull CopyDeviceUID() const { return CFSTR(kBGMDeviceUID); }
|
||||
void AddClient(const AudioServerPlugInClientInfo* __nonnull inClientInfo);
|
||||
void RemoveClient(const AudioServerPlugInClientInfo* __nonnull inClientInfo);
|
||||
void PerformConfigChange(UInt64 inChangeAction, void* __nullable inChangeInfo);
|
||||
void AbortConfigChange(UInt64 inChangeAction, void* __nullable inChangeInfo);
|
||||
|
||||
private:
|
||||
static pthread_once_t sStaticInitializer;
|
||||
static BGM_Device* __nonnull sInstance;
|
||||
|
||||
#define kDeviceName "Background Music Device"
|
||||
#define kDeviceManufacturerName "Background Music contributors"
|
||||
|
||||
enum
|
||||
{
|
||||
kNumberOfSubObjects = 4,
|
||||
kNumberOfInputSubObjects = 1,
|
||||
kNumberOfOutputSubObjects = 3,
|
||||
|
||||
kNumberOfStreams = 2,
|
||||
kNumberOfInputStreams = 1,
|
||||
kNumberOfOutputStreams = 1,
|
||||
|
||||
kNumberOfControls = 2
|
||||
};
|
||||
|
||||
CAMutex mStateMutex;
|
||||
CAMutex mIOMutex;
|
||||
|
||||
UInt64 mSampleRateShadow; // Currently unused
|
||||
const Float64 kSampleRateDefault = 44100.0;
|
||||
// Before we can change sample rate, the host has to stop the device. The new sample rate is stored here while it does.
|
||||
Float64 mPendingSampleRate;
|
||||
|
||||
BGM_WrappedAudioEngine* __nullable mWrappedAudioEngine;
|
||||
|
||||
BGM_TaskQueue mTaskQueue;
|
||||
|
||||
BGM_Clients mClients;
|
||||
|
||||
#define kLoopbackRingBufferFrameSize 16384
|
||||
Float64 mLoopbackSampleRate;
|
||||
Float32 mLoopbackRingBuffer[kLoopbackRingBufferFrameSize * 2];
|
||||
// TODO: a comment explaining why we need a clock for loopback-only mode
|
||||
struct {
|
||||
Float64 hostTicksPerFrame = 0.0;
|
||||
UInt64 numberTimeStamps = 0;
|
||||
UInt64 anchorHostTime = 0;
|
||||
} mLoopbackTime;
|
||||
|
||||
bool mInputStreamIsActive;
|
||||
bool mOutputStreamIsActive;
|
||||
|
||||
SInt32 mDeviceAudibleState;
|
||||
struct
|
||||
{
|
||||
Float64 latestAudibleNonMusic;
|
||||
Float64 latestSilent;
|
||||
Float64 latestAudibleMusic;
|
||||
Float64 latestSilentMusic;
|
||||
} mAudibleStateSampleTimes;
|
||||
|
||||
// This volume range will be used when the BGMDevice isn't wrapping another device (or we fail to
|
||||
// get the range of the wrapped device for some reason).
|
||||
#define kDefaultMinRawVolumeValue 0
|
||||
#define kDefaultMaxRawVolumeValue 96
|
||||
#define kDefaultMinDbVolumeValue -96.0f
|
||||
#define kDefaultMaxDbVolumeValue 0.0f
|
||||
|
||||
SInt32 mOutputMasterVolumeControlRawValueShadow;
|
||||
SInt32 mOutputMasterMinRawVolumeShadow;
|
||||
SInt32 mOutputMasterMaxRawVolumeShadow;
|
||||
Float32 mOutputMasterMinDbVolumeShadow;
|
||||
Float32 mOutputMasterMaxDbVolumeShadow;
|
||||
CAVolumeCurve mVolumeCurve;
|
||||
UInt32 mOutputMuteValueShadow;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __BGMDriver__BGM_Device__ */
|
||||
|
188
BGMDriver/BGMDriver/BGM_Object.cpp
Normal file
|
@ -0,0 +1,188 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_Object.cpp
|
||||
// BGMDriver
|
||||
//
|
||||
// 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.
|
||||
// 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
|
||||
// remove the SA_ObjectMap class.
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "BGM_Object.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
|
||||
|
||||
//==================================================================================================
|
||||
#pragma mark -
|
||||
#pragma mark BGM_Object
|
||||
//==================================================================================================
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
BGM_Object::BGM_Object(AudioObjectID inObjectID, AudioClassID inClassID, AudioClassID inBaseClassID, AudioObjectID inOwnerObjectID)
|
||||
:
|
||||
mObjectID(inObjectID),
|
||||
mClassID(inClassID),
|
||||
mBaseClassID(inBaseClassID),
|
||||
mOwnerObjectID(inOwnerObjectID),
|
||||
mIsActive(false)
|
||||
{
|
||||
}
|
||||
|
||||
void BGM_Object::Activate()
|
||||
{
|
||||
mIsActive = true;
|
||||
}
|
||||
|
||||
void BGM_Object::Deactivate()
|
||||
{
|
||||
mIsActive = false;
|
||||
}
|
||||
|
||||
BGM_Object::~BGM_Object()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
bool BGM_Object::HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
#pragma unused(inObjectID, inClientPID)
|
||||
|
||||
bool theAnswer = false;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyBaseClass:
|
||||
case kAudioObjectPropertyClass:
|
||||
case kAudioObjectPropertyOwner:
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
theAnswer = true;
|
||||
break;
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool BGM_Object::IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
#pragma unused(inObjectID, inClientPID)
|
||||
|
||||
bool theAnswer = false;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyBaseClass:
|
||||
case kAudioObjectPropertyClass:
|
||||
case kAudioObjectPropertyOwner:
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
theAnswer = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
Throw(CAException(kAudioHardwareUnknownPropertyError));
|
||||
break;
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 BGM_Object::GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const
|
||||
{
|
||||
#pragma unused(inObjectID, inClientPID, inQualifierDataSize, inQualifierData)
|
||||
|
||||
UInt32 theAnswer = 0;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyBaseClass:
|
||||
case kAudioObjectPropertyClass:
|
||||
theAnswer = sizeof(AudioClassID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwner:
|
||||
theAnswer = sizeof(AudioObjectID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
theAnswer = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
Throw(CAException(kAudioHardwareUnknownPropertyError));
|
||||
break;
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void BGM_Object::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const
|
||||
{
|
||||
#pragma unused(inObjectID, inClientPID, inQualifierDataSize, inQualifierData)
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyBaseClass:
|
||||
// This is the AudioClassID of the base class of this object. This is an invariant.
|
||||
ThrowIf(inDataSize < sizeof(AudioClassID), CAException(kAudioHardwareBadPropertySizeError), "BGM_Object::GetPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass");
|
||||
*reinterpret_cast<AudioClassID*>(outData) = mBaseClassID;
|
||||
outDataSize = sizeof(AudioClassID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyClass:
|
||||
// This is the AudioClassID of the class of this object. This is an invariant.
|
||||
ThrowIf(inDataSize < sizeof(AudioClassID), CAException(kAudioHardwareBadPropertySizeError), "BGM_Object::GetPropertyData: not enough space for the return value of kAudioObjectPropertyClass");
|
||||
*reinterpret_cast<AudioClassID*>(outData) = mClassID;
|
||||
outDataSize = sizeof(AudioClassID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwner:
|
||||
// The AudioObjectID of the object that owns this object. This is an invariant.
|
||||
ThrowIf(inDataSize < sizeof(AudioObjectID), CAException(kAudioHardwareBadPropertySizeError), "BGM_Object::GetPropertyData: not enough space for the return value of kAudioObjectPropertyOwner");
|
||||
*reinterpret_cast<AudioClassID*>(outData) = mOwnerObjectID;
|
||||
outDataSize = sizeof(AudioObjectID);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
// This is an array of AudioObjectIDs for the objects owned by this object. By default,
|
||||
// objects don't own any other objects. This is an invariant by default, but an object
|
||||
// that can contain other objects will likely need to do some synchronization to access
|
||||
// this property.
|
||||
outDataSize = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
Throw(CAException(kAudioHardwareUnknownPropertyError));
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void BGM_Object::SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
|
||||
{
|
||||
#pragma unused(inObjectID, inClientPID, inQualifierDataSize, inQualifierData, inDataSize, inData)
|
||||
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
default:
|
||||
Throw(CAException(kAudioHardwareUnknownPropertyError));
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
162
BGMDriver/BGMDriver/BGM_Object.h
Normal file
|
@ -0,0 +1,162 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_Object.h
|
||||
// BGMDriver
|
||||
//
|
||||
// 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.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
// 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
|
||||
// is from, we have a fixed set of audio objects. So we've removed the code that handled
|
||||
// growing/shrinking that set.
|
||||
//
|
||||
|
||||
#ifndef __BGMDriver__BGM_Object__
|
||||
#define __BGMDriver__BGM_Object__
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioServerPlugIn.h>
|
||||
|
||||
|
||||
// TODO: Update the large comment below to reflect what was removed from SA_Object.h? It hasn't
|
||||
// been changed at all so far, except to replace "SA_Object" with "BGM_Object".
|
||||
|
||||
//==================================================================================================
|
||||
// BGM_Object
|
||||
//
|
||||
// This is the base class for objects managed by BGM_ObjectMap. It's only job is to ensure that
|
||||
// objects of this type have the proper external semantics for a reference counted object. This
|
||||
// means that the desctructor is protected so that these objects cannot be deleted directly. Also,
|
||||
// these objects many not make a copy of another object or be assigned from another object. Note
|
||||
// that the reference count of the object is tracked and owned by the BGM_ObjectMap.
|
||||
//
|
||||
// These objects provide RTTI information tied to the constants describing the HAL's API class
|
||||
// hierarchy as described in the headers. The class ID and base class IDs passed in to the
|
||||
// constructor must operate with the semantics described in AudioObjectBase.h where the base class
|
||||
// has to always be one of the standard classes. The class ID can be a custom value or a standard
|
||||
// value however. If it is a standard value, the base class should be the proper standard base
|
||||
// class. So for example, a standard volume control object will say that it's class is
|
||||
// kAudioVolumeControlClassID and that its base class is kAudioLevelControlClassID. In the case of
|
||||
// a custom boolean control, it would say that it's class is a custom value like 'MYBL' and that
|
||||
// its base class is kAudioBooleanControlClassID.
|
||||
//
|
||||
// Subclasses of this class must implement Activate(). This method is called after an object has
|
||||
// been constructed and inserted into the object map. Until Activate() is called, a constructed
|
||||
// object may not do anything active such as sending/receiving notifications or creating other
|
||||
// objects. Active operations may be performed in the Activate() method proper however. Note that
|
||||
// Activate() is called prior to any references to the object being handed out. As such, it does
|
||||
// not need to worry about being thread safe while Activate() is in progress.
|
||||
//
|
||||
// Subclasses of this class must also implement Deactivate(). This method is called when the object
|
||||
// is at the end of it's lifecycle. Once Deactivate() has been called, the object may no longer
|
||||
// perform active opertions, including Deactivating other objects. This is based on the notion that
|
||||
// all the objects have a definite point at which they are considered dead to the outside world.
|
||||
// For example, an AudioDevice object is dead if it's hardware is unplugged. The point of death is
|
||||
// the notification the owner of the device gets to signal that it has been unplugged. Note that it
|
||||
// is both normal and expected that a dead object might still have outstanding references. Thus, an
|
||||
// object has to put in some care to do the right thing when these zombie references are used. The
|
||||
// best thing to do is to just have those queries return appropriate errors.
|
||||
//
|
||||
// Deactivate() itself needs to be thread safe with respect to other operations taking place on the
|
||||
// object. This also means taking care to handle the Deactivation of owned objects. For example, an
|
||||
// AudioDevice object will almost always own one or more AudioStream objects. If the stream is in a
|
||||
// separate lock domain from it's owning device, then the device has to be very careful about how
|
||||
// it deactivates the stream such that it doesn't try to lock the stream's lock while holding the
|
||||
// device's lock which will inevitably lead to a deadlock situation. There are two reasonable
|
||||
// approaches to dealing with this kind of situation. The first is to just not get into it by
|
||||
// making the device share a lock domain with all it's owned objects like streams and controls. The
|
||||
// other approach is to use dispatch queues to make the work of Deactivating owned objects take
|
||||
// place outside of the device's lock domain. For example, if the device needs to deactivate a
|
||||
// stream, it can remove the stream from any tracking in the device object and then dispatch
|
||||
// asynchronously the Deactivate() call on the stream and the release of the reference the device
|
||||
// has on the stream.
|
||||
//
|
||||
// Note that both Activate() and Deactivate() are called by objects at large. Typically,
|
||||
// Activate() is called by the creator of the object, usually right after the object has been
|
||||
// allocated. Deactivate() will usually be called by the owner of the object upon recognizing that
|
||||
// the object is dead to the outside world. Going back to the example of an AudioDevice getting
|
||||
// unplugged, the Deactivate() method will be called by whomever receives the notification about
|
||||
// the hardware going away, which is often the owner of the object.
|
||||
//
|
||||
// This class also defines methods to implement the portion of the
|
||||
// AudioServerPlugInDriverInterface that deals with properties. The five methods all have the same
|
||||
// basic arguments and semantics. The class also provides the implementation for
|
||||
// the minimum required properties for all AudioObjects. There is a detailed commentary about each
|
||||
// specific property in the GetPropertyData() method.
|
||||
//
|
||||
// It is important that a thread retain and hold a reference while it is using an BGM_Object and
|
||||
// that the reference be released promptly when the thread is finished using the object. By
|
||||
// assuming this, a BGM_Object can minimize the amount of locking it needs to do. In particular,
|
||||
// purely static or invariant data can be handled without any locking at all.
|
||||
//==================================================================================================
|
||||
|
||||
class BGM_Object
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
public:
|
||||
BGM_Object(AudioObjectID inObjectID, AudioClassID inClassID, AudioClassID inBaseClassID, AudioObjectID inOwnerObjectID);
|
||||
|
||||
virtual void Activate();
|
||||
virtual void Deactivate();
|
||||
|
||||
protected:
|
||||
virtual ~BGM_Object();
|
||||
|
||||
private:
|
||||
BGM_Object(const BGM_Object&);
|
||||
BGM_Object& operator=(const BGM_Object&);
|
||||
|
||||
#pragma mark Attributes
|
||||
|
||||
public:
|
||||
AudioObjectID GetObjectID() const { return mObjectID; }
|
||||
void* GetObjectIDAsPtr() const { uintptr_t thePtr = mObjectID; return reinterpret_cast<void*>(thePtr); }
|
||||
AudioClassID GetClassID() const { return mClassID; }
|
||||
AudioClassID GetBaseClassID() const { return mBaseClassID; }
|
||||
AudioObjectID GetOwnerObjectID() const { return mOwnerObjectID; }
|
||||
bool IsActive() const { return mIsActive; }
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
public:
|
||||
virtual bool HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual bool IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const;
|
||||
virtual void GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const;
|
||||
virtual void SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
protected:
|
||||
AudioObjectID mObjectID;
|
||||
AudioClassID mClassID;
|
||||
AudioClassID mBaseClassID;
|
||||
AudioObjectID mOwnerObjectID;
|
||||
bool mIsActive;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __BGMDriver__BGM_Object__ */
|
||||
|
220
BGMDriver/BGMDriver/BGM_PlugIn.cpp
Normal file
|
@ -0,0 +1,220 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_PlugIn.cpp
|
||||
// BGMDriver
|
||||
//
|
||||
// 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.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "BGM_PlugIn.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
#include "BGM_Device.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAMutex.h"
|
||||
#include "CAException.h"
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
pthread_once_t BGM_PlugIn::sStaticInitializer = PTHREAD_ONCE_INIT;
|
||||
BGM_PlugIn* BGM_PlugIn::sInstance = NULL;
|
||||
AudioServerPlugInHostRef BGM_PlugIn::sHost = NULL;
|
||||
|
||||
BGM_PlugIn& BGM_PlugIn::GetInstance()
|
||||
{
|
||||
pthread_once(&sStaticInitializer, StaticInitializer);
|
||||
return *sInstance;
|
||||
}
|
||||
|
||||
void BGM_PlugIn::StaticInitializer()
|
||||
{
|
||||
try
|
||||
{
|
||||
sInstance = new BGM_PlugIn;
|
||||
sInstance->Activate();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
DebugMsg("BGM_PlugIn::StaticInitializer: failed to create the plug-in");
|
||||
delete sInstance;
|
||||
sInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BGM_PlugIn::BGM_PlugIn()
|
||||
:
|
||||
BGM_Object(kAudioObjectPlugInObject, kAudioPlugInClassID, kAudioObjectClassID, 0),
|
||||
mMutex("BGM_PlugIn")
|
||||
{
|
||||
}
|
||||
|
||||
BGM_PlugIn::~BGM_PlugIn()
|
||||
{
|
||||
}
|
||||
|
||||
void BGM_PlugIn::Deactivate()
|
||||
{
|
||||
CAMutex::Locker theLocker(mMutex);
|
||||
BGM_Object::Deactivate();
|
||||
// TODO:
|
||||
//_RemoveAllDevices();
|
||||
}
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
bool BGM_PlugIn::HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
case kAudioPlugInPropertyDeviceList:
|
||||
case kAudioPlugInPropertyTranslateUIDToDevice:
|
||||
case kAudioPlugInPropertyResourceBundle:
|
||||
theAnswer = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = BGM_Object::HasProperty(inObjectID, inClientPID, inAddress);
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
bool BGM_PlugIn::IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const
|
||||
{
|
||||
bool theAnswer = false;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
case kAudioPlugInPropertyDeviceList:
|
||||
case kAudioPlugInPropertyTranslateUIDToDevice:
|
||||
case kAudioPlugInPropertyResourceBundle:
|
||||
theAnswer = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = BGM_Object::IsPropertySettable(inObjectID, inClientPID, inAddress);
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
UInt32 BGM_PlugIn::GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const
|
||||
{
|
||||
UInt32 theAnswer = 0;
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
theAnswer = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
case kAudioPlugInPropertyDeviceList:
|
||||
theAnswer = sizeof(AudioObjectID);
|
||||
break;
|
||||
|
||||
case kAudioPlugInPropertyTranslateUIDToDevice:
|
||||
theAnswer = sizeof(AudioObjectID);
|
||||
break;
|
||||
|
||||
case kAudioPlugInPropertyResourceBundle:
|
||||
theAnswer = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
default:
|
||||
theAnswer = BGM_Object::GetPropertyDataSize(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData);
|
||||
};
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
void BGM_PlugIn::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const
|
||||
{
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
case kAudioObjectPropertyManufacturer:
|
||||
// This is the human readable name of the maker of the plug-in.
|
||||
ThrowIf(inDataSize < sizeof(CFStringRef), CAException(kAudioHardwareBadPropertySizeError), "BGM_PlugIn::GetPropertyData: not enough space for the return value of kAudioObjectPropertyManufacturer");
|
||||
*reinterpret_cast<CFStringRef*>(outData) = CFSTR("Background Music contributors");
|
||||
outDataSize = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
case kAudioPlugInPropertyDeviceList:
|
||||
{
|
||||
// This plug-in object only owns the device
|
||||
AudioObjectID* theReturnedDeviceList = reinterpret_cast<AudioObjectID*>(outData);
|
||||
if(inDataSize >= sizeof(AudioObjectID))
|
||||
{
|
||||
theReturnedDeviceList[0] = kObjectID_Device;
|
||||
|
||||
// say how much we returned
|
||||
outDataSize = sizeof(AudioObjectID);
|
||||
}
|
||||
else
|
||||
{
|
||||
outDataSize = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioPlugInPropertyTranslateUIDToDevice:
|
||||
{
|
||||
// This property translates the UID passed in the qualifier as a CFString into the
|
||||
// AudioObjectID for the device the UID refers to or kAudioObjectUnknown if no device
|
||||
// has the UID.
|
||||
ThrowIf(inQualifierDataSize < sizeof(CFStringRef), CAException(kAudioHardwareBadPropertySizeError), "BGM_PlugIn::GetPropertyData: the qualifier size is too small for kAudioPlugInPropertyTranslateUIDToDevice");
|
||||
ThrowIf(inDataSize < sizeof(AudioObjectID), CAException(kAudioHardwareBadPropertySizeError), "BGM_PlugIn::GetPropertyData: not enough space for the return value of kAudioPlugInPropertyTranslateUIDToDevice");
|
||||
CFStringRef theUID = *reinterpret_cast<const CFStringRef*>(inQualifierData);
|
||||
*reinterpret_cast<AudioObjectID*>(outData) =
|
||||
CFEqual(theUID, BGM_Device::GetInstance().CopyDeviceUID()) ? kObjectID_Device : kAudioObjectUnknown;
|
||||
outDataSize = sizeof(AudioObjectID);
|
||||
}
|
||||
break;
|
||||
|
||||
case kAudioPlugInPropertyResourceBundle:
|
||||
// The resource bundle is a path relative to the path of the plug-in's bundle.
|
||||
// To specify that the plug-in bundle itself should be used, we just return the
|
||||
// empty string.
|
||||
ThrowIf(inDataSize < sizeof(AudioObjectID), CAException(kAudioHardwareBadPropertySizeError), "BGM_GetPlugInPropertyData: not enough space for the return value of kAudioPlugInPropertyResourceBundle");
|
||||
*reinterpret_cast<CFStringRef*>(outData) = CFSTR("");
|
||||
outDataSize = sizeof(CFStringRef);
|
||||
break;
|
||||
|
||||
default:
|
||||
BGM_Object::GetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void BGM_PlugIn::SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
|
||||
{
|
||||
switch(inAddress.mSelector)
|
||||
{
|
||||
default:
|
||||
BGM_Object::SetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
87
BGMDriver/BGMDriver/BGM_PlugIn.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_PlugIn.h
|
||||
// BGMDriver
|
||||
//
|
||||
// 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.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
#ifndef __BGMDriver__BGM_PlugIn__
|
||||
#define __BGMDriver__BGM_PlugIn__
|
||||
|
||||
// SuperClass Includes
|
||||
#include "BGM_Object.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAMutex.h"
|
||||
|
||||
|
||||
class BGM_PlugIn
|
||||
:
|
||||
public BGM_Object
|
||||
{
|
||||
|
||||
#pragma mark Construction/Destruction
|
||||
|
||||
public:
|
||||
static BGM_PlugIn& GetInstance();
|
||||
|
||||
protected:
|
||||
BGM_PlugIn();
|
||||
virtual ~BGM_PlugIn();
|
||||
virtual void Deactivate();
|
||||
|
||||
private:
|
||||
static void StaticInitializer();
|
||||
|
||||
#pragma mark Host Access
|
||||
|
||||
public:
|
||||
static void SetHost(AudioServerPlugInHostRef inHost) { sHost = inHost; }
|
||||
|
||||
static void Host_PropertiesChanged(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[]) { if(sHost != NULL) { sHost->PropertiesChanged(sHost, inObjectID, inNumberAddresses, inAddresses); } }
|
||||
static void Host_RequestDeviceConfigurationChange(AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo) { if(sHost != NULL) { sHost->RequestDeviceConfigurationChange(sHost, inDeviceObjectID, inChangeAction, inChangeInfo); } }
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
public:
|
||||
virtual bool HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual bool IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
|
||||
virtual UInt32 GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const;
|
||||
virtual void GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const;
|
||||
virtual void SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
|
||||
|
||||
#pragma mark Implementation
|
||||
|
||||
public:
|
||||
const CFStringRef GetBundleID() const { return CFSTR("com.bearisdriving.BGMDriver"); }
|
||||
|
||||
private:
|
||||
CAMutex mMutex;
|
||||
|
||||
static pthread_once_t sStaticInitializer;
|
||||
static BGM_PlugIn* sInstance;
|
||||
static AudioServerPlugInHostRef sHost;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __BGMDriver__BGM_PlugIn__ */
|
||||
|
812
BGMDriver/BGMDriver/BGM_PlugInInterface.cpp
Normal file
|
@ -0,0 +1,812 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_PlugInInterface.cpp
|
||||
// BGMDriver
|
||||
//
|
||||
// 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.
|
||||
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
|
||||
//
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioServerPlugIn.h>
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CADebugMacros.h"
|
||||
#include "CAException.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
#include "BGM_Object.h"
|
||||
#include "BGM_PlugIn.h"
|
||||
#include "BGM_Device.h"
|
||||
|
||||
|
||||
#pragma mark COM Prototypes
|
||||
|
||||
// Entry points for the COM methods
|
||||
extern "C" void* BGM_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID);
|
||||
static HRESULT BGM_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInterface);
|
||||
static ULONG BGM_AddRef(void* inDriver);
|
||||
static ULONG BGM_Release(void* inDriver);
|
||||
static OSStatus BGM_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerPlugInHostRef inHost);
|
||||
static OSStatus BGM_CreateDevice(AudioServerPlugInDriverRef inDriver, CFDictionaryRef inDescription, const AudioServerPlugInClientInfo* inClientInfo, AudioObjectID* outDeviceObjectID);
|
||||
static OSStatus BGM_DestroyDevice(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID);
|
||||
static OSStatus BGM_AddDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo);
|
||||
static OSStatus BGM_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo);
|
||||
static OSStatus BGM_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo);
|
||||
static OSStatus BGM_AbortDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo);
|
||||
static Boolean BGM_HasProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress);
|
||||
static OSStatus BGM_IsPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable);
|
||||
static OSStatus BGM_GetPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
|
||||
static OSStatus BGM_GetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData);
|
||||
static OSStatus BGM_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
|
||||
static OSStatus BGM_StartIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID);
|
||||
static OSStatus BGM_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID);
|
||||
static OSStatus BGM_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, Float64* outSampleTime, UInt64* outHostTime, UInt64* outSeed);
|
||||
static OSStatus BGM_WillDoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, Boolean* outWillDo, Boolean* outWillDoInPlace);
|
||||
static OSStatus BGM_BeginIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo);
|
||||
static OSStatus BGM_DoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo, void* ioMainBuffer, void* ioSecondaryBuffer);
|
||||
static OSStatus BGM_EndIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo);
|
||||
|
||||
#pragma mark The COM Interface
|
||||
|
||||
static AudioServerPlugInDriverInterface gAudioServerPlugInDriverInterface =
|
||||
{
|
||||
NULL,
|
||||
BGM_QueryInterface,
|
||||
BGM_AddRef,
|
||||
BGM_Release,
|
||||
BGM_Initialize,
|
||||
BGM_CreateDevice,
|
||||
BGM_DestroyDevice,
|
||||
BGM_AddDeviceClient,
|
||||
BGM_RemoveDeviceClient,
|
||||
BGM_PerformDeviceConfigurationChange,
|
||||
BGM_AbortDeviceConfigurationChange,
|
||||
BGM_HasProperty,
|
||||
BGM_IsPropertySettable,
|
||||
BGM_GetPropertyDataSize,
|
||||
BGM_GetPropertyData,
|
||||
BGM_SetPropertyData,
|
||||
BGM_StartIO,
|
||||
BGM_StopIO,
|
||||
BGM_GetZeroTimeStamp,
|
||||
BGM_WillDoIOOperation,
|
||||
BGM_BeginIOOperation,
|
||||
BGM_DoIOOperation,
|
||||
BGM_EndIOOperation
|
||||
};
|
||||
static AudioServerPlugInDriverInterface* gAudioServerPlugInDriverInterfacePtr = &gAudioServerPlugInDriverInterface;
|
||||
static AudioServerPlugInDriverRef gAudioServerPlugInDriverRef = &gAudioServerPlugInDriverInterfacePtr;
|
||||
static UInt32 gAudioServerPlugInDriverRefCount = 1;
|
||||
|
||||
static BGM_Object& BGM_LookUpOwnerObject(AudioObjectID inObjectID)
|
||||
{
|
||||
switch(inObjectID)
|
||||
{
|
||||
case kObjectID_PlugIn:
|
||||
return BGM_PlugIn::GetInstance();
|
||||
|
||||
case kObjectID_Device:
|
||||
case kObjectID_Stream_Input:
|
||||
case kObjectID_Stream_Output:
|
||||
case kObjectID_Volume_Output_Master:
|
||||
case kObjectID_Mute_Output_Master:
|
||||
return BGM_Device::GetInstance();
|
||||
}
|
||||
|
||||
DebugMsg("BGM_LookUpOwnerObject: unknown object");
|
||||
throw (CAException(kAudioHardwareBadObjectError));
|
||||
}
|
||||
|
||||
#pragma mark Factory
|
||||
|
||||
extern "C"
|
||||
void* BGM_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID)
|
||||
{
|
||||
// This is the CFPlugIn factory function. Its job is to create the implementation for the given
|
||||
// type provided that the type is supported. Because this driver is simple and all its
|
||||
// initialization is handled via static initalization when the bundle is loaded, all that
|
||||
// needs to be done is to return the AudioServerPlugInDriverRef that points to the driver's
|
||||
// interface. A more complicated driver would create any base line objects it needs to satisfy
|
||||
// the IUnknown methods that are used to discover that actual interface to talk to the driver.
|
||||
// The majority of the driver's initilization should be handled in the Initialize() method of
|
||||
// the driver's AudioServerPlugInDriverInterface.
|
||||
|
||||
#pragma unused(inAllocator)
|
||||
|
||||
void* theAnswer = NULL;
|
||||
if(CFEqual(inRequestedTypeUUID, kAudioServerPlugInTypeUUID))
|
||||
{
|
||||
theAnswer = gAudioServerPlugInDriverRef;
|
||||
|
||||
BGM_PlugIn::GetInstance();
|
||||
}
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark Inheritence
|
||||
|
||||
static HRESULT BGM_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInterface)
|
||||
{
|
||||
// This function is called by the HAL to get the interface to talk to the plug-in through.
|
||||
// AudioServerPlugIns are required to support the IUnknown interface and the
|
||||
// AudioServerPlugInDriverInterface. As it happens, all interfaces must also provide the
|
||||
// IUnknown interface, so we can always just return the single interface we made with
|
||||
// gAudioServerPlugInDriverInterfacePtr regardless of which one is asked for.
|
||||
|
||||
HRESULT theAnswer = 0;
|
||||
CFUUIDRef theRequestedUUID = NULL;
|
||||
|
||||
try
|
||||
{
|
||||
// validate the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_QueryInterface: bad driver reference");
|
||||
ThrowIfNULL(outInterface, CAException(kAudioHardwareIllegalOperationError), "BGM_QueryInterface: no place to store the returned interface");
|
||||
|
||||
// make a CFUUIDRef from inUUID
|
||||
theRequestedUUID = CFUUIDCreateFromUUIDBytes(NULL, inUUID);
|
||||
ThrowIf(theRequestedUUID == NULL, CAException(kAudioHardwareIllegalOperationError), "BGM_QueryInterface: failed to create the CFUUIDRef");
|
||||
|
||||
// AudioServerPlugIns only support two interfaces, IUnknown (which has to be supported by all
|
||||
// CFPlugIns and AudioServerPlugInDriverInterface (which is the actual interface the HAL will
|
||||
// use).
|
||||
ThrowIf(!CFEqual(theRequestedUUID, IUnknownUUID) && !CFEqual(theRequestedUUID, kAudioServerPlugInDriverInterfaceUUID), CAException(E_NOINTERFACE), "BGM_QueryInterface: requested interface is unsupported");
|
||||
ThrowIf(gAudioServerPlugInDriverRefCount == UINT32_MAX, CAException(E_NOINTERFACE), "BGM_QueryInterface: the ref count is maxxed out");
|
||||
|
||||
// do the work
|
||||
++gAudioServerPlugInDriverRefCount;
|
||||
*outInterface = gAudioServerPlugInDriverRef;
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
if(theRequestedUUID != NULL)
|
||||
{
|
||||
CFRelease(theRequestedUUID);
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static ULONG BGM_AddRef(void* inDriver)
|
||||
{
|
||||
// This call returns the resulting reference count after the increment.
|
||||
|
||||
ULONG theAnswer = 0;
|
||||
|
||||
// check the arguments
|
||||
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "BGM_AddRef: bad driver reference");
|
||||
FailIf(gAudioServerPlugInDriverRefCount == UINT32_MAX, Done, "BGM_AddRef: out of references");
|
||||
|
||||
// increment the refcount
|
||||
++gAudioServerPlugInDriverRefCount;
|
||||
theAnswer = gAudioServerPlugInDriverRefCount;
|
||||
|
||||
Done:
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static ULONG BGM_Release(void* inDriver)
|
||||
{
|
||||
// This call returns the resulting reference count after the decrement.
|
||||
|
||||
ULONG theAnswer = 0;
|
||||
|
||||
// check the arguments
|
||||
FailIf(inDriver != gAudioServerPlugInDriverRef, Done, "BGM_Release: bad driver reference");
|
||||
FailIf(gAudioServerPlugInDriverRefCount == UINT32_MAX, Done, "BGM_Release: out of references");
|
||||
|
||||
// decrement the refcount
|
||||
// Note that we don't do anything special if the refcount goes to zero as the HAL
|
||||
// will never fully release a plug-in it opens. We keep managing the refcount so that
|
||||
// the API semantics are correct though.
|
||||
--gAudioServerPlugInDriverRefCount;
|
||||
theAnswer = gAudioServerPlugInDriverRefCount;
|
||||
|
||||
Done:
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark Basic Operations
|
||||
|
||||
static OSStatus BGM_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerPlugInHostRef inHost)
|
||||
{
|
||||
// The job of this method is, as the name implies, to get the driver initialized. One specific
|
||||
// thing that needs to be done is to store the AudioServerPlugInHostRef so that it can be used
|
||||
// later. Note that when this call returns, the HAL will scan the various lists the driver
|
||||
// maintains (such as the device list) to get the inital set of objects the driver is
|
||||
// publishing. So, there is no need to notifiy the HAL about any objects created as part of the
|
||||
// execution of this method.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_Initialize: bad driver reference");
|
||||
|
||||
// store the AudioServerPlugInHostRef
|
||||
BGM_PlugIn::GetInstance().SetHost(inHost);
|
||||
|
||||
// Init/activate the device
|
||||
BGM_Device::GetInstance();
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_CreateDevice(AudioServerPlugInDriverRef inDriver, CFDictionaryRef inDescription, const AudioServerPlugInClientInfo* inClientInfo, AudioObjectID* outDeviceObjectID)
|
||||
{
|
||||
// This method is used to tell a driver that implements the Transport Manager semantics to
|
||||
// create an AudioEndpointDevice from a set of AudioEndpoints. Since this driver is not a
|
||||
// Transport Manager, we just return kAudioHardwareUnsupportedOperationError.
|
||||
|
||||
#pragma unused(inDriver, inDescription, inClientInfo, outDeviceObjectID)
|
||||
|
||||
return kAudioHardwareUnsupportedOperationError;
|
||||
}
|
||||
|
||||
static OSStatus BGM_DestroyDevice(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID)
|
||||
{
|
||||
// This method is used to tell a driver that implements the Transport Manager semantics to
|
||||
// destroy an AudioEndpointDevice. Since this driver is not a Transport Manager, we just check
|
||||
// the arguments and return kAudioHardwareUnsupportedOperationError.
|
||||
|
||||
#pragma unused(inDriver, inDeviceObjectID)
|
||||
|
||||
return kAudioHardwareUnsupportedOperationError;
|
||||
}
|
||||
|
||||
static OSStatus BGM_AddDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo)
|
||||
{
|
||||
// This method is used to inform the driver about a new client that is using the given device.
|
||||
// This allows the device to act differently depending on who the client is.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_AddDeviceClient: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_AddDeviceClient: unknown device");
|
||||
|
||||
// inform the device
|
||||
BGM_Device::GetInstance().AddClient(inClientInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(BGM_InvalidClientException)
|
||||
{
|
||||
theAnswer = kAudioHardwareIllegalOperationError;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, const AudioServerPlugInClientInfo* inClientInfo)
|
||||
{
|
||||
// This method is used to inform the driver about a client that is no longer using the given
|
||||
// device.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_RemoveDeviceClient: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_RemoveDeviceClient: unknown device");
|
||||
|
||||
// inform the device
|
||||
BGM_Device::GetInstance().RemoveClient(inClientInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(BGM_InvalidClientException)
|
||||
{
|
||||
theAnswer = kAudioHardwareIllegalOperationError;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo)
|
||||
{
|
||||
// This method is called to tell the device that it can perform the configuation change that it
|
||||
// had requested via a call to the host method, RequestDeviceConfigurationChange(). The
|
||||
// arguments, inChangeAction and inChangeInfo are the same as what was passed to
|
||||
// RequestDeviceConfigurationChange().
|
||||
//
|
||||
// The HAL guarantees that IO will be stopped while this method is in progress. The HAL will
|
||||
// also handle figuring out exactly what changed for the non-control related properties. This
|
||||
// means that the only notifications that would need to be sent here would be for either
|
||||
// custom properties the HAL doesn't know about or for controls.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_PerformDeviceConfigurationChange: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_PerformDeviceConfigurationChange: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
BGM_Device::GetInstance().PerformConfigChange(inChangeAction, inChangeInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_AbortDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo)
|
||||
{
|
||||
// This method is called to tell the driver that a request for a config change has been denied.
|
||||
// This provides the driver an opportunity to clean up any state associated with the request.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_PerformDeviceConfigurationChange: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_PerformDeviceConfigurationChange: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
BGM_Device::GetInstance().AbortConfigChange(inChangeAction, inChangeInfo);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark Property Operations
|
||||
|
||||
static Boolean BGM_HasProperty(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress)
|
||||
{
|
||||
// This method returns whether or not the given object has the given property.
|
||||
|
||||
Boolean theAnswer = false;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_HasProperty: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "BGM_HasProperty: no address");
|
||||
|
||||
theAnswer = BGM_LookUpOwnerObject(inObjectID).HasProperty(inObjectID, inClientProcessID, *inAddress);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = false;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_IsPropertySettable(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, Boolean* outIsSettable)
|
||||
{
|
||||
// This method returns whether or not the given property on the object can have its value
|
||||
// changed.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_IsPropertySettable: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "BGM_IsPropertySettable: no address");
|
||||
ThrowIfNULL(outIsSettable, CAException(kAudioHardwareIllegalOperationError), "BGM_IsPropertySettable: no place to put the return value");
|
||||
|
||||
BGM_Object& theAudioObject = BGM_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
*outIsSettable = theAudioObject.IsPropertySettable(inObjectID, inClientProcessID, *inAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_GetPropertyDataSize(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize)
|
||||
{
|
||||
// This method returns the byte size of the property's data.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_GetPropertyDataSize: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "BGM_GetPropertyDataSize: no address");
|
||||
ThrowIfNULL(outDataSize, CAException(kAudioHardwareIllegalOperationError), "BGM_GetPropertyDataSize: no place to put the return value");
|
||||
|
||||
BGM_Object& theAudioObject = BGM_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
*outDataSize = theAudioObject.GetPropertyDataSize(inObjectID, inClientProcessID, *inAddress, inQualifierDataSize, inQualifierData);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_GetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32* outDataSize, void* outData)
|
||||
{
|
||||
// This method fetches the data for a given property
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_GetPropertyData: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "BGM_GetPropertyData: no address");
|
||||
ThrowIfNULL(outDataSize, CAException(kAudioHardwareIllegalOperationError), "BGM_GetPropertyData: no place to put the return value size");
|
||||
ThrowIfNULL(outData, CAException(kAudioHardwareIllegalOperationError), "BGM_GetPropertyData: no place to put the return value");
|
||||
|
||||
BGM_Object& theAudioObject = BGM_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
theAudioObject.GetPropertyData(inObjectID, inClientProcessID, *inAddress, inQualifierDataSize, inQualifierData, inDataSize, *outDataSize, outData);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_SetPropertyData(AudioServerPlugInDriverRef inDriver, AudioObjectID inObjectID, pid_t inClientProcessID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
|
||||
{
|
||||
// This method changes the value of the given property
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_SetPropertyData: bad driver reference");
|
||||
ThrowIfNULL(inAddress, CAException(kAudioHardwareIllegalOperationError), "BGM_SetPropertyData: no address");
|
||||
|
||||
BGM_Object& theAudioObject = BGM_LookUpOwnerObject(inObjectID);
|
||||
if(theAudioObject.HasProperty(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
if(theAudioObject.IsPropertySettable(inObjectID, inClientProcessID, *inAddress))
|
||||
{
|
||||
theAudioObject.SetPropertyData(inObjectID, inClientProcessID, *inAddress, inQualifierDataSize, inQualifierData, inDataSize, inData);
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnsupportedOperationError;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
theAnswer = kAudioHardwareUnknownPropertyError;
|
||||
}
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
#pragma mark IO Operations
|
||||
|
||||
static OSStatus BGM_StartIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID)
|
||||
{
|
||||
// This call tells the device that IO is starting for the given client. When this routine
|
||||
// returns, the device's clock is running and it is ready to have data read/written. It is
|
||||
// important to note that multiple clients can have IO running on the device at the same time.
|
||||
// So, work only needs to be done when the first client starts. All subsequent starts simply
|
||||
// increment the counter.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_StartIO: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_StartIO: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
BGM_Device::GetInstance().StartIO(inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID)
|
||||
{
|
||||
// This call tells the device that the client has stopped IO. The driver can stop the hardware
|
||||
// once all clients have stopped.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_StopIO: bad driver reference");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_StopIO: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
BGM_Device::GetInstance().StopIO(inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_GetZeroTimeStamp(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, Float64* outSampleTime, UInt64* outHostTime, UInt64* outSeed)
|
||||
{
|
||||
// This method returns the current zero time stamp for the device. The HAL models the timing of
|
||||
// a device as a series of time stamps that relate the sample time to a host time. The zero
|
||||
// time stamps are spaced such that the sample times are the value of
|
||||
// kAudioDevicePropertyZeroTimeStampPeriod apart. This is often modeled using a ring buffer
|
||||
// where the zero time stamp is updated when wrapping around the ring buffer.
|
||||
|
||||
#pragma unused(inClientID)
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_GetZeroTimeStamp: bad driver reference");
|
||||
ThrowIfNULL(outSampleTime, CAException(kAudioHardwareIllegalOperationError), "BGM_GetZeroTimeStamp: no place to put the sample time");
|
||||
ThrowIfNULL(outHostTime, CAException(kAudioHardwareIllegalOperationError), "BGM_GetZeroTimeStamp: no place to put the host time");
|
||||
ThrowIfNULL(outSeed, CAException(kAudioHardwareIllegalOperationError), "BGM_GetZeroTimeStamp: no place to put the seed");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_GetZeroTimeStamp: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
BGM_Device::GetInstance().GetZeroTimeStamp(*outSampleTime, *outHostTime, *outSeed);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_WillDoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, Boolean* outWillDo, Boolean* outWillDoInPlace)
|
||||
{
|
||||
// This method returns whether or not the device will do a given IO operation.
|
||||
|
||||
#pragma unused(inClientID)
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_WillDoIOOperation: bad driver reference");
|
||||
ThrowIfNULL(outWillDo, CAException(kAudioHardwareIllegalOperationError), "BGM_WillDoIOOperation: no place to put the will-do return value");
|
||||
ThrowIfNULL(outWillDoInPlace, CAException(kAudioHardwareIllegalOperationError), "BGM_WillDoIOOperation: no place to put the in-place return value");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_WillDoIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
bool willDo = false;
|
||||
bool willDoInPlace = false;
|
||||
BGM_Device::GetInstance().WillDoIOOperation(inOperationID, willDo, willDoInPlace);
|
||||
|
||||
// set the return values
|
||||
*outWillDo = willDo;
|
||||
*outWillDoInPlace = willDoInPlace;
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_BeginIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo)
|
||||
{
|
||||
// This is called at the beginning of an IO operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_BeginIOOperation: bad driver reference");
|
||||
ThrowIfNULL(inIOCycleInfo, CAException(kAudioHardwareIllegalOperationError), "BGM_BeginIOOperation: no cycle info");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_BeginIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
BGM_Device::GetInstance().BeginIOOperation(inOperationID, inIOBufferFrameSize, *inIOCycleInfo, inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_DoIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, AudioObjectID inStreamObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo, void* ioMainBuffer, void* ioSecondaryBuffer)
|
||||
{
|
||||
// This is called to actually perform a given operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_EndIOOperation: bad driver reference");
|
||||
ThrowIfNULL(inIOCycleInfo, CAException(kAudioHardwareIllegalOperationError), "BGM_EndIOOperation: no cycle info");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_EndIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
BGM_Device::GetInstance().DoIOOperation(inStreamObjectID, inClientID, inOperationID, inIOBufferFrameSize, *inIOCycleInfo, ioMainBuffer, ioSecondaryBuffer);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
||||
static OSStatus BGM_EndIOOperation(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID, UInt32 inOperationID, UInt32 inIOBufferFrameSize, const AudioServerPlugInIOCycleInfo* inIOCycleInfo)
|
||||
{
|
||||
// This is called at the end of an IO operation.
|
||||
|
||||
OSStatus theAnswer = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// check the arguments
|
||||
ThrowIf(inDriver != gAudioServerPlugInDriverRef, CAException(kAudioHardwareBadObjectError), "BGM_EndIOOperation: bad driver reference");
|
||||
ThrowIfNULL(inIOCycleInfo, CAException(kAudioHardwareIllegalOperationError), "BGM_EndIOOperation: no cycle info");
|
||||
ThrowIf(inDeviceObjectID != kObjectID_Device, CAException(kAudioHardwareBadObjectError), "BGM_EndIOOperation: unknown device");
|
||||
|
||||
// tell the device to do the work
|
||||
BGM_Device::GetInstance().EndIOOperation(inOperationID, inIOBufferFrameSize, *inIOCycleInfo, inClientID);
|
||||
}
|
||||
catch(const CAException& inException)
|
||||
{
|
||||
theAnswer = inException.GetError();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
theAnswer = kAudioHardwareUnspecifiedError;
|
||||
}
|
||||
|
||||
return theAnswer;
|
||||
}
|
||||
|
461
BGMDriver/BGMDriver/BGM_TaskQueue.cpp
Normal file
|
@ -0,0 +1,461 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_TaskQueue.cpp
|
||||
// BGMDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "BGM_TaskQueue.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
#include "BGM_PlugIn.h"
|
||||
#include "BGM_Clients.h"
|
||||
#include "BGM_ClientMap.h"
|
||||
#include "BGM_ClientTasks.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAException.h"
|
||||
#include "CADebugMacros.h"
|
||||
|
||||
// System Includes
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_error.h>
|
||||
#include <mach/semaphore.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach/task.h>
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
#pragma mark Construction/destruction
|
||||
|
||||
BGM_TaskQueue::BGM_TaskQueue()
|
||||
:
|
||||
// The inline documentation for thread_time_constraint_policy.period says "A value of 0 indicates that there is no
|
||||
// inherent periodicity in the computation". So I figure setting the period to 0 means the scheduler will take as long
|
||||
// as it wants to wake our real-time thread, which is fine for us, but once it has only other real-time threads can
|
||||
// preempt us. (And that's only if they won't make our computation take longer than kRealTimeThreadMaximumComputationNs).
|
||||
mRealTimeThread(&BGM_TaskQueue::RealTimeThreadProc,
|
||||
this,
|
||||
/* inPeriod = */ 0,
|
||||
NanosToAbsoluteTime(kRealTimeThreadNominalComputationNs),
|
||||
NanosToAbsoluteTime(kRealTimeThreadMaximumComputationNs),
|
||||
/* inIsPreemptible = */ true),
|
||||
mNonRealTimeThread(&BGM_TaskQueue::NonRealTimeThreadProc, this)
|
||||
{
|
||||
// Init the semaphores
|
||||
auto createSemaphore = [] () {
|
||||
semaphore_t theSemaphore;
|
||||
kern_return_t theError = semaphore_create(mach_task_self(), &theSemaphore, SYNC_POLICY_FIFO, 0);
|
||||
|
||||
// Check errors
|
||||
ThrowIfMachError("BGM_TaskQueue", "semaphore_create", theError);
|
||||
|
||||
ThrowIf(theSemaphore == SEMAPHORE_NULL,
|
||||
CAException(kAudioHardwareUnspecifiedError),
|
||||
"BGM_TaskQueue::BGM_TaskQueue: Could not create semaphore");
|
||||
|
||||
return theSemaphore;
|
||||
};
|
||||
|
||||
mRealTimeThreadWorkQueuedSemaphore = createSemaphore();
|
||||
mNonRealTimeThreadWorkQueuedSemaphore = createSemaphore();
|
||||
mRealTimeThreadSyncTaskCompletedSemaphore = createSemaphore();
|
||||
mNonRealTimeThreadSyncTaskCompletedSemaphore = createSemaphore();
|
||||
|
||||
// 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++)
|
||||
{
|
||||
BGM_Task* theTask = new BGM_Task;
|
||||
mNonRealTimeThreadTasksFreeList.push_NA(theTask);
|
||||
}
|
||||
|
||||
// Start the worker threads
|
||||
mRealTimeThread.Start();
|
||||
mNonRealTimeThread.Start();
|
||||
}
|
||||
|
||||
BGM_TaskQueue::~BGM_TaskQueue()
|
||||
{
|
||||
// Join the worker threads
|
||||
QueueSync(kBGMTaskStopWorkerThread, /* inRunOnRealtimeThread = */ true);
|
||||
QueueSync(kBGMTaskStopWorkerThread, /* inRunOnRealtimeThread = */ false);
|
||||
|
||||
// Destroy the semaphores
|
||||
auto destroySemaphore = [] (semaphore_t inSemaphore) {
|
||||
kern_return_t theError = semaphore_destroy(mach_task_self(), inSemaphore);
|
||||
|
||||
// Check errors
|
||||
ThrowIfMachError("~BGM_TaskQueue", "semaphore_destroy", theError);
|
||||
};
|
||||
|
||||
destroySemaphore(mRealTimeThreadWorkQueuedSemaphore);
|
||||
destroySemaphore(mNonRealTimeThreadWorkQueuedSemaphore);
|
||||
destroySemaphore(mRealTimeThreadSyncTaskCompletedSemaphore);
|
||||
destroySemaphore(mNonRealTimeThreadSyncTaskCompletedSemaphore);
|
||||
|
||||
BGM_Task* theTask;
|
||||
|
||||
// Delete the tasks in the non-realtime tasks free list
|
||||
while((theTask = mNonRealTimeThreadTasksFreeList.pop_atomic()) != NULL)
|
||||
{
|
||||
delete theTask;
|
||||
}
|
||||
|
||||
// Delete any tasks left on the non-realtime queue that need to be
|
||||
while((theTask = mNonRealTimeThreadTasks.pop_atomic()) != NULL)
|
||||
{
|
||||
if(!theTask->IsSync())
|
||||
{
|
||||
delete theTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
UInt32 BGM_TaskQueue::NanosToAbsoluteTime(UInt32 inNanos)
|
||||
{
|
||||
// Converts a duration from nanoseconds to absolute time (i.e. number of bus cycles). Used for calculating
|
||||
// the real-time thread's time constraint policy.
|
||||
|
||||
mach_timebase_info_data_t theTimebaseInfo;
|
||||
mach_timebase_info(&theTimebaseInfo);
|
||||
|
||||
Float64 theTicksPerNs = static_cast<Float64>(theTimebaseInfo.denom) / theTimebaseInfo.numer;
|
||||
return static_cast<UInt32>(inNanos * theTicksPerNs);
|
||||
}
|
||||
|
||||
#pragma mark Task queueing
|
||||
|
||||
void BGM_TaskQueue::QueueSync_SwapClientShadowMaps(BGM_ClientMap* inClientMap)
|
||||
{
|
||||
// TODO: Is there any reason to use uintptr_t when we pass pointers to tasks like this? I can't think of any
|
||||
// reason for a system to have (non-function) pointers larger than 64-bit, so I figure they should fit.
|
||||
//
|
||||
// From http://en.cppreference.com/w/cpp/language/reinterpret_cast:
|
||||
// "A pointer converted to an integer of sufficient size and back to the same pointer type is guaranteed
|
||||
// to have its original value [...]"
|
||||
QueueSync(kBGMTaskSwapClientShadowMaps, /* inRunOnRealtimeThread = */ true, reinterpret_cast<UInt64>(inClientMap));
|
||||
}
|
||||
|
||||
void BGM_TaskQueue::QueueAsync_SendPropertyNotification(AudioObjectPropertySelector inProperty)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::QueueAsync_SendPropertyNotification: Queueing property notification. inProperty=%u", inProperty);
|
||||
BGM_Task theTask(kBGMTaskSendPropertyNotification, /* inIsSync = */ false, inProperty);
|
||||
QueueOnNonRealtimeThread(theTask);
|
||||
}
|
||||
|
||||
bool BGM_TaskQueue::Queue_UpdateClientIOState(bool inSync, BGM_Clients* inClients, UInt32 inClientID, bool inDoingIO)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::Queue_UpdateClientIOState: Queueing %s %s",
|
||||
(inDoingIO ? "kBGMTaskStartClientIO" : "kBGMTaskStopClientIO"),
|
||||
(inSync ? "synchronously" : "asynchronously"));
|
||||
|
||||
BGM_TaskID theTaskID = (inDoingIO ? kBGMTaskStartClientIO : kBGMTaskStopClientIO);
|
||||
UInt64 theClientsPtrArg = reinterpret_cast<UInt64>(inClients);
|
||||
UInt64 theClientIDTaskArg = static_cast<UInt64>(inClientID);
|
||||
|
||||
if(inSync)
|
||||
{
|
||||
return QueueSync(theTaskID, false, theClientsPtrArg, theClientIDTaskArg);
|
||||
}
|
||||
else
|
||||
{
|
||||
BGM_Task theTask(theTaskID, /* inIsSync = */ false, theClientsPtrArg, theClientIDTaskArg);
|
||||
QueueOnNonRealtimeThread(theTask);
|
||||
|
||||
// This method's return value isn't used when queueing async, because we can't know what it should be yet.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
UInt64 BGM_TaskQueue::QueueSync(BGM_TaskID inTaskID, bool inRunOnRealtimeThread, UInt64 inTaskArg1, UInt64 inTaskArg2)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::QueueSync: Queueing task synchronously to be processed on the %s thread. inTaskID=%d inTaskArg1=%llu inTaskArg2=%llu",
|
||||
(inRunOnRealtimeThread ? "realtime" : "non-realtime"),
|
||||
inTaskID,
|
||||
inTaskArg1,
|
||||
inTaskArg2);
|
||||
|
||||
// Create the task
|
||||
BGM_Task theTask(inTaskID, /* inIsSync = */ true, inTaskArg1, inTaskArg2);
|
||||
|
||||
// Add the task to the queue
|
||||
TAtomicStack<BGM_Task>& theTasks = (inRunOnRealtimeThread ? mRealTimeThreadTasks : mNonRealTimeThreadTasks);
|
||||
theTasks.push_atomic(&theTask);
|
||||
|
||||
// Wake the worker thread so it'll process the task. (Note that semaphore_signal has an implicit barrier.)
|
||||
kern_return_t theError = semaphore_signal(inRunOnRealtimeThread ? mRealTimeThreadWorkQueuedSemaphore : mNonRealTimeThreadWorkQueuedSemaphore);
|
||||
ThrowIfMachError("QueueSync", "semaphore_signal", theError);
|
||||
|
||||
// Wait until the task has been processed.
|
||||
//
|
||||
// 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.
|
||||
while(!theTask.IsComplete())
|
||||
{
|
||||
semaphore_t theTaskCompletedSemaphore =
|
||||
inRunOnRealtimeThread ? mRealTimeThreadSyncTaskCompletedSemaphore : mNonRealTimeThreadSyncTaskCompletedSemaphore;
|
||||
// TODO: Because the worker threads use semaphore_signal_all instead of semaphore_signal, a thread can miss the signal if
|
||||
// it isn't waiting at the right time. Using a timeout for now as a temporary fix so threads don't get stuck here.
|
||||
theError = semaphore_timedwait(theTaskCompletedSemaphore,
|
||||
(mach_timespec_t){ 0, kRealTimeThreadMaximumComputationNs * 4 });
|
||||
|
||||
if(theError != KERN_OPERATION_TIMED_OUT)
|
||||
{
|
||||
ThrowIfMachError("QueueSync", "semaphore_timedwait", theError);
|
||||
}
|
||||
}
|
||||
|
||||
if(theTask.GetReturnValue() != INT64_MAX)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::QueueSync: Task %d returned %llu.", theTask.GetTaskID(), theTask.GetReturnValue());
|
||||
}
|
||||
|
||||
return theTask.GetReturnValue();
|
||||
}
|
||||
|
||||
void BGM_TaskQueue::QueueOnNonRealtimeThread(BGM_Task inTask)
|
||||
{
|
||||
// Add the task to our task list
|
||||
BGM_Task* freeTask = mNonRealTimeThreadTasksFreeList.pop_atomic();
|
||||
|
||||
if(freeTask == NULL)
|
||||
{
|
||||
LogWarning("BGM_TaskQueue::QueueOnNonRealtimeThread: No pre-allocated tasks left in the free list. Allocating new task.");
|
||||
freeTask = new BGM_Task;
|
||||
}
|
||||
|
||||
*freeTask = inTask;
|
||||
|
||||
mNonRealTimeThreadTasks.push_atomic(freeTask);
|
||||
|
||||
// Signal the worker thread to process the task. (Note that semaphore_signal has an implicit barrier.)
|
||||
kern_return_t theError = semaphore_signal(mNonRealTimeThreadWorkQueuedSemaphore);
|
||||
ThrowIfMachError("QueueOnNonRealtimeThread", "semaphore_signal", theError);
|
||||
}
|
||||
|
||||
#pragma mark Worker threads
|
||||
|
||||
void BGM_TaskQueue::AssertCurrentThreadIsRTWorkerThread(const char* inCallerMethodName)
|
||||
{
|
||||
#if DEBUG // This Assert macro always checks the condition, even in release builds if the compiler doesn't optimise it away
|
||||
if(!mRealTimeThread.IsCurrentThread())
|
||||
{
|
||||
DebugMsg("%s should only be called on the realtime worker thread.", inCallerMethodName);
|
||||
__ASSERT_STOP; // TODO: Figure out a better way to assert with a formatted message
|
||||
}
|
||||
|
||||
Assert(mRealTimeThread.IsTimeConstraintThread(), "mRealTimeThread should be in a time-constraint priority band.");
|
||||
#else
|
||||
#pragma unused (inCallerMethodName)
|
||||
#endif
|
||||
}
|
||||
|
||||
//static
|
||||
void* __nullable BGM_TaskQueue::RealTimeThreadProc(void* inRefCon)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::RealTimeThreadProc: The realtime worker thread has started");
|
||||
|
||||
BGM_TaskQueue* refCon = static_cast<BGM_TaskQueue*>(inRefCon);
|
||||
refCon->WorkerThreadProc(refCon->mRealTimeThreadWorkQueuedSemaphore,
|
||||
refCon->mRealTimeThreadSyncTaskCompletedSemaphore,
|
||||
&refCon->mRealTimeThreadTasks,
|
||||
NULL,
|
||||
[&] (BGM_Task* inTask) { return refCon->ProcessRealTimeThreadTask(inTask); });
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//static
|
||||
void* __nullable BGM_TaskQueue::NonRealTimeThreadProc(void* inRefCon)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::NonRealTimeThreadProc: The non-realtime worker thread has started");
|
||||
|
||||
BGM_TaskQueue* refCon = static_cast<BGM_TaskQueue*>(inRefCon);
|
||||
refCon->WorkerThreadProc(refCon->mNonRealTimeThreadWorkQueuedSemaphore,
|
||||
refCon->mNonRealTimeThreadSyncTaskCompletedSemaphore,
|
||||
&refCon->mNonRealTimeThreadTasks,
|
||||
&refCon->mNonRealTimeThreadTasksFreeList,
|
||||
[&] (BGM_Task* inTask) { return refCon->ProcessNonRealTimeThreadTask(inTask); });
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
while(!threadShouldStop)
|
||||
{
|
||||
// Wait until a thread signals that it's added tasks to the queue.
|
||||
//
|
||||
// Note that we don't have to hold any lock before waiting. If the semaphore is signalled before we begin waiting we'll
|
||||
// still get the signal after we do.
|
||||
kern_return_t theError = semaphore_wait(inWorkQueuedSemaphore);
|
||||
ThrowIfMachError("WorkerThreadProc", "semaphore_wait", theError);
|
||||
|
||||
// Fetch the tasks from the queue.
|
||||
//
|
||||
// The tasks need to be processed in the order they were added to the queue. Since pop_all_reversed is atomic, other threads
|
||||
// can't add new tasks while we're reading, which would mix up the order.
|
||||
BGM_Task* theTask = inTasks->pop_all_reversed();
|
||||
|
||||
while(theTask != NULL &&
|
||||
!threadShouldStop) // Stop processing tasks if we're shutting down
|
||||
{
|
||||
// Process the task
|
||||
threadShouldStop = inProcessTask(theTask);
|
||||
|
||||
theTask->MarkCompleted();
|
||||
|
||||
BGM_Task* theNextTask = theTask->next();
|
||||
|
||||
// If the task was queued synchronously, let the thread that queued it know we're finished
|
||||
if(theTask->IsSync())
|
||||
{
|
||||
// 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
|
||||
// for threads calling QueueSync to wait on the semaphore in an order different to the order of the tasks they just
|
||||
// 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);
|
||||
ThrowIfMachError("WorkerThreadProc", "semaphore_signal_all", theError);
|
||||
}
|
||||
else if(inFreeList != NULL)
|
||||
{
|
||||
// After completing an async task, move it to the free list so the memory can be reused
|
||||
inFreeList->push_atomic(theTask);
|
||||
}
|
||||
|
||||
theTask = theNextTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BGM_TaskQueue::ProcessRealTimeThreadTask(BGM_Task* inTask)
|
||||
{
|
||||
AssertCurrentThreadIsRTWorkerThread("BGM_TaskQueue::ProcessRealTimeThreadTask");
|
||||
|
||||
switch(inTask->GetTaskID())
|
||||
{
|
||||
case kBGMTaskStopWorkerThread:
|
||||
DebugMsg("BGM_TaskQueue::ProcessRealTimeThreadTask: Stopping");
|
||||
// Return that the thread should stop itself
|
||||
return true;
|
||||
|
||||
case kBGMTaskSwapClientShadowMaps:
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::ProcessRealTimeThreadTask: Swapping the shadow maps in BGM_ClientMap");
|
||||
BGM_ClientMap* theClientMap = reinterpret_cast<BGM_ClientMap*>(inTask->GetArg1());
|
||||
BGM_ClientTasks::SwapInShadowMapsRT(theClientMap);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Assert(false, "BGM_TaskQueue::ProcessRealTimeThreadTask: Unexpected task ID");
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BGM_TaskQueue::ProcessNonRealTimeThreadTask(BGM_Task* inTask)
|
||||
{
|
||||
#if DEBUG // This Assert macro always checks the condition, if for some reason the compiler doesn't optimise it away, even in release builds
|
||||
Assert(mNonRealTimeThread.IsCurrentThread(), "ProcessNonRealTimeThreadTask should only be called on the non-realtime worker thread.");
|
||||
Assert(mNonRealTimeThread.IsTimeShareThread(), "mNonRealTimeThread should not be in a time-constraint priority band.");
|
||||
#endif
|
||||
|
||||
switch(inTask->GetTaskID())
|
||||
{
|
||||
case kBGMTaskStopWorkerThread:
|
||||
DebugMsg("BGM_TaskQueue::ProcessNonRealTimeThreadTask: Stopping");
|
||||
// Return that the thread should stop itself
|
||||
return true;
|
||||
|
||||
case kBGMTaskStartClientIO:
|
||||
DebugMsg("BGM_TaskQueue::ProcessNonRealTimeThreadTask: Processing kBGMTaskStartClientIO");
|
||||
try
|
||||
{
|
||||
BGM_Clients* theClients = reinterpret_cast<BGM_Clients*>(inTask->GetArg1());
|
||||
bool didStartIO = BGM_ClientTasks::StartIONonRT(theClients, static_cast<UInt32>(inTask->GetArg2()));
|
||||
inTask->SetReturnValue(didStartIO);
|
||||
}
|
||||
catch(BGM_InvalidClientException)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::ProcessNonRealTimeThreadTask: Ignoring BGM_InvalidClientException thrown by StartIONonRT. %s",
|
||||
"It's possible the client was removed before this task was processed.");
|
||||
}
|
||||
break;
|
||||
|
||||
case kBGMTaskStopClientIO:
|
||||
DebugMsg("BGM_TaskQueue::ProcessNonRealTimeThreadTask: Processing kBGMTaskStopClientIO");
|
||||
try
|
||||
{
|
||||
BGM_Clients* theClients = reinterpret_cast<BGM_Clients*>(inTask->GetArg1());
|
||||
bool didStopIO = BGM_ClientTasks::StopIONonRT(theClients, static_cast<UInt32>(inTask->GetArg2()));
|
||||
inTask->SetReturnValue(didStopIO);
|
||||
}
|
||||
catch(BGM_InvalidClientException)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::ProcessNonRealTimeThreadTask: Ignoring BGM_InvalidClientException thrown by StopIONonRT. %s",
|
||||
"It's possible the client was removed before this task was processed.");
|
||||
}
|
||||
break;
|
||||
|
||||
case kBGMTaskSendPropertyNotification:
|
||||
DebugMsg("BGM_TaskQueue::ProcessNonRealTimeThreadTask: Processing kBGMTaskSendPropertyNotification");
|
||||
{
|
||||
AudioObjectPropertyAddress thePropertyAddress[] = {
|
||||
{ static_cast<UInt32>(inTask->GetArg1()), kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster } };
|
||||
BGM_PlugIn::Host_PropertiesChanged(kObjectID_Device, 1, thePropertyAddress);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Assert(false, "BGM_TaskQueue::ProcessNonRealTimeThreadTask: Unexpected task ID");
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
void BGM_TaskQueue::ThrowIfMachError(const char* inCallingMethod, const char* inErrorReturnedBy, kern_return_t inError)
|
||||
{
|
||||
// We use this instead of ThrowIfKernErr when the error corresponds to a Mach error string we'd like to log
|
||||
|
||||
#if !DEBUG
|
||||
#pragma unused (inCallingMethod, inErrorReturnedBy)
|
||||
#endif
|
||||
|
||||
if(inError != KERN_SUCCESS)
|
||||
{
|
||||
DebugMsg("BGM_TaskQueue::%s: %s returned an error (%d): %s", inCallingMethod, inErrorReturnedBy, inError, mach_error_string(inError));
|
||||
Throw(CAException(inError));
|
||||
}
|
||||
}
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
196
BGMDriver/BGMDriver/BGM_TaskQueue.h
Normal file
|
@ -0,0 +1,196 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_TaskQueue.h
|
||||
// BGMDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
#ifndef __BGMDriver__BGM_TaskQueue__
|
||||
#define __BGMDriver__BGM_TaskQueue__
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAPThread.h"
|
||||
#include "CAAtomicStack.h"
|
||||
|
||||
// STL Includes
|
||||
#include <functional>
|
||||
|
||||
// System Includes
|
||||
#include <mach/semaphore.h>
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
|
||||
|
||||
// Forward declarations
|
||||
class BGM_Clients;
|
||||
class BGM_ClientMap;
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
//==================================================================================================
|
||||
// BGM_TaskQueue
|
||||
//
|
||||
// This class has two worker threads, one with real-time priority and one with default priority,
|
||||
// that tasks can be dispatched to. The two main use cases are dispatching work from a real-time
|
||||
// thread to be done async, and dispatching work from a non-real-time thread that needs to run on
|
||||
// a real-time thread to avoid priority inversions.
|
||||
//==================================================================================================
|
||||
|
||||
class BGM_TaskQueue
|
||||
{
|
||||
|
||||
private:
|
||||
enum BGM_TaskID {
|
||||
kBGMTaskUninitialized,
|
||||
kBGMTaskStopWorkerThread,
|
||||
|
||||
// Realtime thread only
|
||||
kBGMTaskSwapClientShadowMaps,
|
||||
|
||||
// Non-realtime thread only
|
||||
kBGMTaskStartClientIO,
|
||||
kBGMTaskStopClientIO,
|
||||
kBGMTaskSendPropertyNotification
|
||||
};
|
||||
|
||||
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_TaskID GetTaskID() { return mTaskID; }
|
||||
UInt64 GetArg1() { return mArg1; }
|
||||
UInt64 GetArg2() { return mArg2; }
|
||||
UInt64 GetReturnValue() { return mReturnValue; }
|
||||
void SetReturnValue(UInt64 inReturnValue) { mReturnValue = inReturnValue; }
|
||||
bool IsComplete() { return mIsComplete; }
|
||||
void MarkCompleted() { mIsComplete = true; }
|
||||
// True if the thread that queued this task is blocking until the task is completed
|
||||
bool IsSync() { return mIsSync; }
|
||||
|
||||
// Used by TAtomicStack
|
||||
BGM_Task* __nullable & next() { return mNext; };
|
||||
|
||||
private:
|
||||
BGM_TaskID mTaskID;
|
||||
UInt64 mArg1;
|
||||
UInt64 mArg2;
|
||||
UInt64 mReturnValue = INT64_MAX;
|
||||
bool mIsComplete = false;
|
||||
bool mIsSync;
|
||||
BGM_Task* __nullable mNext = NULL;
|
||||
};
|
||||
|
||||
public:
|
||||
BGM_TaskQueue();
|
||||
~BGM_TaskQueue();
|
||||
// Disallow copying
|
||||
BGM_TaskQueue(const BGM_TaskQueue&) = delete;
|
||||
BGM_TaskQueue& operator=(const BGM_TaskQueue&) = delete;
|
||||
|
||||
private:
|
||||
static UInt32 NanosToAbsoluteTime(UInt32 inNanos);
|
||||
|
||||
public:
|
||||
void QueueSync_SwapClientShadowMaps(BGM_ClientMap* inClientMap);
|
||||
|
||||
// Sends a property changed notification to the BGMDevice host. Assumes the scope and element are kAudioObjectPropertyScopeGlobal and
|
||||
// kAudioObjectPropertyElementMaster because currently those are the only ones we use.
|
||||
void QueueAsync_SendPropertyNotification(AudioObjectPropertySelector inProperty);
|
||||
|
||||
// Set/unset a client's is-doing-IO flag
|
||||
|
||||
inline bool QueueSync_StartClientIO(BGM_Clients* inClients, UInt32 inClientID) { return Queue_UpdateClientIOState(true, inClients, inClientID, true); }
|
||||
inline bool QueueSync_StopClientIO(BGM_Clients* inClients, UInt32 inClientID) { return Queue_UpdateClientIOState(true, inClients, inClientID, false); }
|
||||
|
||||
inline void QueueAsync_StartClientIO(BGM_Clients* inClients, UInt32 inClientID) { Queue_UpdateClientIOState(false, inClients, inClientID, true); }
|
||||
inline void QueueAsync_StopClientIO(BGM_Clients* inClients, UInt32 inClientID) { Queue_UpdateClientIOState(false, inClients, inClientID, false); }
|
||||
|
||||
private:
|
||||
bool Queue_UpdateClientIOState(bool inSync, BGM_Clients* inClients, UInt32 inClientID, bool inDoingIO);
|
||||
|
||||
UInt64 QueueSync(BGM_TaskID inTaskID, bool inRunOnRealtimeThread, UInt64 inTaskArg1 = 0, UInt64 inTaskArg2 = 0);
|
||||
|
||||
void QueueOnNonRealtimeThread(BGM_Task inTask);
|
||||
|
||||
public:
|
||||
void AssertCurrentThreadIsRTWorkerThread(const char* inCallerMethodName);
|
||||
|
||||
private:
|
||||
static void* __nullable RealTimeThreadProc(void* inRefCon);
|
||||
static void* __nullable NonRealTimeThreadProc(void* inRefCon);
|
||||
|
||||
void WorkerThreadProc(semaphore_t inWorkQueuedSemaphore, semaphore_t inSyncTaskCompletedSemaphore, TAtomicStack<BGM_Task>* inTasks, TAtomicStack2<BGM_Task>* __nullable inFreeList, std::function<bool(BGM_Task*)> inProcessTask);
|
||||
|
||||
// These return true when the thread should be stopped
|
||||
bool ProcessRealTimeThreadTask(BGM_Task* inTask);
|
||||
bool ProcessNonRealTimeThreadTask(BGM_Task* inTask);
|
||||
|
||||
static void ThrowIfMachError(const char* inCallingMethod, const char* inErrorReturnedBy, kern_return_t inError);
|
||||
|
||||
private:
|
||||
// The worker threads that perform the queued tasks
|
||||
CAPThread mRealTimeThread;
|
||||
CAPThread mNonRealTimeThread;
|
||||
|
||||
// The approximate amount of time we'll need whenever our real-time thread is scheduled. This is currently just
|
||||
// set to the minimum (see sched_prim.c) because our real-time tasks do very little work.
|
||||
//
|
||||
// TODO: Would it be better to specify these in absolute time, which would make them relative to the system's bus
|
||||
// speed? Or even calculate them from the system's CPU/RAM speed? Note that none of our tasks actually have
|
||||
// a deadline (though that might change). They just have to run with real-time priority to avoid causing
|
||||
// priority inversions on the IO thread.
|
||||
static const UInt32 kRealTimeThreadNominalComputationNs = 50 * NSEC_PER_USEC;
|
||||
// The maximum amount of time the real-time thread can take to finish its computation after being scheduled.
|
||||
static const UInt32 kRealTimeThreadMaximumComputationNs = 60 * NSEC_PER_USEC;
|
||||
|
||||
// We use Mach semaphores for communication with the worker threads because signalling them is real-time safe.
|
||||
|
||||
// Signalled to tell the worker threads when there are tasks for them process.
|
||||
semaphore_t mRealTimeThreadWorkQueuedSemaphore;
|
||||
semaphore_t mNonRealTimeThreadWorkQueuedSemaphore;
|
||||
// Signalled when a worker thread completes a task, if the thread that queued that task is blocking on it.
|
||||
semaphore_t mRealTimeThreadSyncTaskCompletedSemaphore;
|
||||
semaphore_t mNonRealTimeThreadSyncTaskCompletedSemaphore;
|
||||
|
||||
// When a task is queued we add it to one of these, depending on which worker thread it will run on. Using
|
||||
// TAtomicStack lets us safely add and remove tasks on real-time threads.
|
||||
//
|
||||
// We use TAtomicStack rather than TAtomicStack2 because we need pop_all_reversed() to make sure we process the
|
||||
// tasks in order.
|
||||
TAtomicStack<BGM_Task> mRealTimeThreadTasks;
|
||||
TAtomicStack<BGM_Task> mNonRealTimeThreadTasks;
|
||||
|
||||
// The number of tasks to pre-allocate and add to the non-realtime task free list. Should be large enough that
|
||||
// the free list is never emptied. (At least not while IO could be running.)
|
||||
static const UInt32 kNonRealTimeThreadTaskBufferSize = 512;
|
||||
// Realtime threads can't safely allocate memory, so when they queue a task the memory for it comes from this
|
||||
// free list. We pre-allocate as many tasks as they should ever need in the constructor. (But if the free list
|
||||
// runs out of tasks somehow the realtime thread will allocate a new one.)
|
||||
//
|
||||
// There's a similar free list used in Apple's CAThreadSafeList.h.
|
||||
//
|
||||
// We can use TAtomicStack2 instead of TAtomicStack because we never call pop_all on the free list.
|
||||
TAtomicStack2<BGM_Task> mNonRealTimeThreadTasksFreeList;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* __BGMDriver__BGM_TaskQueue__ */
|
||||
|
162
BGMDriver/BGMDriver/BGM_Types.h
Normal file
|
@ -0,0 +1,162 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_Types.h
|
||||
// BGMDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// Shared with the BGMApp project.
|
||||
//
|
||||
|
||||
#ifndef BGMDriver_BGM_Types_h
|
||||
#define BGMDriver_BGM_Types_h
|
||||
|
||||
#include <CoreAudio/AudioServerPlugin.h>
|
||||
|
||||
|
||||
#pragma mark IDs
|
||||
|
||||
// TODO: Change these and the other defines to const strings?
|
||||
#define kBGMDeviceBundleID "com.bearisdriving.BGMDevice"
|
||||
#define kBGMAppBundleID "com.bearisdriving.BGMApp"
|
||||
|
||||
#define kBGMDeviceUID "BGMDevice"
|
||||
#define kBGMDeviceModelUID "BGMDeviceModelUID"
|
||||
|
||||
// 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
|
||||
// change that at some point, but so far it hasn't caused any problems and it makes the driver much simpler.
|
||||
enum
|
||||
{
|
||||
kObjectID_PlugIn = kAudioObjectPlugInObject,
|
||||
kObjectID_Device = 2,
|
||||
kObjectID_Stream_Input = 3,
|
||||
kObjectID_Stream_Output = 4,
|
||||
kObjectID_Volume_Output_Master = 5,
|
||||
kObjectID_Mute_Output_Master = 6
|
||||
};
|
||||
|
||||
#pragma mark Custom properties
|
||||
|
||||
enum
|
||||
{
|
||||
// TODO: Combine the two music player properties
|
||||
|
||||
// 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.
|
||||
//
|
||||
// There is currently no way for a client to tell whether the process it has set as the music player is a
|
||||
// client of the BGMDevice.
|
||||
kAudioDeviceCustomPropertyMusicPlayerProcessID = 'mppi',
|
||||
// The music player's bundle ID as a CFString (UTF8), or the empty string if it's unset/null. Setting this
|
||||
// property will also clear the value of kAudioDeviceCustomPropertyMusicPlayerProcessID.
|
||||
kAudioDeviceCustomPropertyMusicPlayerBundleID = 'mpbi',
|
||||
// A CFNumber that specifies whether the device is silent, playing only music (i.e. the client set as the
|
||||
// music player is the only client playing audio) or audible. See enum values below. This property is only
|
||||
// updated after the audible state has been different for kDeviceAudibleStateMinChangedFramesForUpdate
|
||||
// consecutive frames. (To avoid excessive CPU use if for some reason the audible state starts changing
|
||||
// very often.)
|
||||
kAudioDeviceCustomPropertyDeviceAudibleState = 'daud',
|
||||
// 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
|
||||
// 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'
|
||||
};
|
||||
|
||||
// The number of silent/audible frames before BGMDriver will change kAudioDeviceCustomPropertyDeviceAudibleState
|
||||
#define kDeviceAudibleStateMinChangedFramesForUpdate (2 << 12)
|
||||
|
||||
enum
|
||||
{
|
||||
// kAudioDeviceCustomPropertyDeviceAudibleState values
|
||||
//
|
||||
// No audio is playing on the device's streams (regardless of whether IO is running or not)
|
||||
kBGMDeviceIsSilent = 'silt',
|
||||
// The client whose bundle ID matches the current value of kCustomAudioDevicePropertyMusicPlayerBundleID is the
|
||||
// only audible client
|
||||
kBGMDeviceIsSilentExceptMusic = 'olym',
|
||||
kBGMDeviceIsAudible = 'audi'
|
||||
};
|
||||
|
||||
// kAudioDeviceCustomPropertyAppVolumes keys
|
||||
//
|
||||
// A CFNumber<SInt32> between kAppRelativeVolumeMinRawValue and kAppRelativeVolumeMaxRawValue. A value greater than
|
||||
// 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"
|
||||
// The app's bundle ID as a CFString. May be omitted if kBGMAppVolumesKey_ProcessID is present.
|
||||
#define kBGMAppVolumesKey_BundleID "bid"
|
||||
|
||||
// Volume curve range for app volumes
|
||||
#define kAppRelativeVolumeMaxRawValue 100
|
||||
#define kAppRelativeVolumeMinRawValue 0
|
||||
#define kAppRelativeVolumeMinDbValue -96.0f
|
||||
#define kAppRelativeVolumeMaxDbValue 0.0f
|
||||
|
||||
#pragma mark Exceptions
|
||||
|
||||
class BGM_InvalidClientException { };
|
||||
class BGM_InvalidClientPIDException { };
|
||||
class BGM_InvalidClientRelativeVolumeException { };
|
||||
class BGM_DeviceNotSetException { };
|
||||
|
||||
#pragma mark Property Addresses
|
||||
|
||||
// For convenience
|
||||
|
||||
static const AudioObjectPropertyAddress kBGMMusicPlayerProcessIDAddress = {
|
||||
kAudioDeviceCustomPropertyMusicPlayerProcessID,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress kBGMMusicPlayerBundleIDAddress = {
|
||||
kAudioDeviceCustomPropertyMusicPlayerBundleID,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress kBGMAudibleStateAddress = {
|
||||
kAudioDeviceCustomPropertyDeviceAudibleState,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress kBGMRunningSomewhereOtherThanBGMAppAddress = {
|
||||
kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
static const AudioObjectPropertyAddress kBGMAppVolumesAddress = {
|
||||
kAudioDeviceCustomPropertyAppVolumes,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
#endif
|
||||
|
45
BGMDriver/BGMDriver/BGM_WrappedAudioEngine.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_WrappedAudioEngine.cpp
|
||||
// BGMDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "BGM_WrappedAudioEngine.h"
|
||||
|
||||
|
||||
// TODO: Register to be notified when the IO Registry values for these change so we can cache them
|
||||
|
||||
UInt64 BGM_WrappedAudioEngine::GetSampleRate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
kern_return_t BGM_WrappedAudioEngine::SetSampleRate(Float64 inNewSampleRate)
|
||||
{
|
||||
#pragma unused (inNewSampleRate)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UInt32 BGM_WrappedAudioEngine::GetSampleBufferFrameSize() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
48
BGMDriver/BGMDriver/BGM_WrappedAudioEngine.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_WrappedAudioEngine.h
|
||||
// BGMDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// The plan for this is to allow devices with IOAudioEngine drivers to be used as the output device
|
||||
// directly from BGMDriver, rather than going through BGMApp. That way we get roughly the same CPU
|
||||
// usage and latency as normal, and don't need to worry about pausing BGMApp's IO when no clients
|
||||
// are doing IO. It also lets BGMDriver mostly continue working without BGMApp running. I've written
|
||||
// a very experimental version that mostly works but the code needs a lot of clean up so I haven't
|
||||
// added it to this project yet.
|
||||
//
|
||||
|
||||
#ifndef __BGMDriver__BGM_WrappedAudioEngine__
|
||||
#define __BGMDriver__BGM_WrappedAudioEngine__
|
||||
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <mach/kern_return.h>
|
||||
|
||||
|
||||
class BGM_WrappedAudioEngine
|
||||
{
|
||||
|
||||
public:
|
||||
UInt64 GetSampleRate() const;
|
||||
kern_return_t SetSampleRate(Float64 inNewSampleRate);
|
||||
UInt32 GetSampleBufferFrameSize() const;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __BGMDriver__BGM_WrappedAudioEngine__ */
|
||||
|
52
BGMDriver/BGMDriver/DeviceClients/BGM_Client.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_Client.cpp
|
||||
// BGMDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "BGM_Client.h"
|
||||
|
||||
|
||||
BGM_Client::BGM_Client(const AudioServerPlugInClientInfo* inClientInfo)
|
||||
:
|
||||
mClientID(inClientInfo->mClientID),
|
||||
mProcessID(inClientInfo->mProcessID),
|
||||
mBundleID(inClientInfo->mBundleID),
|
||||
mIsNativeEndian(inClientInfo->mIsNativeEndian)
|
||||
{
|
||||
// The bundle ID ref we were passed is only valid until our plugin returns control to the HAL, so we need to retain
|
||||
// it. (CACFString will handle the rest of its ownership/destruction.)
|
||||
if(inClientInfo->mBundleID != NULL)
|
||||
{
|
||||
CFRetain(inClientInfo->mBundleID);
|
||||
}
|
||||
}
|
||||
|
||||
void BGM_Client::Copy(const BGM_Client& inClient)
|
||||
{
|
||||
mClientID = inClient.mClientID;
|
||||
mProcessID = inClient.mProcessID;
|
||||
mBundleID = inClient.mBundleID;
|
||||
mIsNativeEndian = inClient.mIsNativeEndian;
|
||||
mDoingIO = inClient.mDoingIO;
|
||||
mIsMusicPlayer = inClient.mIsMusicPlayer;
|
||||
mRelativeVolume = inClient.mRelativeVolume;
|
||||
}
|
||||
|
80
BGMDriver/BGMDriver/DeviceClients/BGM_Client.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_Client.h
|
||||
// BGMDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
#ifndef __BGMDriver__BGM_Client__
|
||||
#define __BGMDriver__BGM_Client__
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CACFString.h"
|
||||
|
||||
// System Includes
|
||||
#include <CoreAudio/AudioServerPlugIn.h>
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
//==================================================================================================
|
||||
// BGM_Client
|
||||
//
|
||||
// Client meaning a client (of the host) of the BGMDevice, i.e. an app registered with the HAL,
|
||||
// generally so it can do IO at some point.
|
||||
//==================================================================================================
|
||||
|
||||
class BGM_Client
|
||||
{
|
||||
|
||||
public:
|
||||
BGM_Client() = default;
|
||||
BGM_Client(const AudioServerPlugInClientInfo* inClientInfo);
|
||||
~BGM_Client() = default;
|
||||
BGM_Client(const BGM_Client& inClient) { Copy(inClient); };
|
||||
BGM_Client& operator=(const BGM_Client& inClient) { Copy(inClient); return *this; }
|
||||
|
||||
private:
|
||||
void Copy(const BGM_Client& inClient);
|
||||
|
||||
public:
|
||||
// These fields are duplicated from AudioServerPlugInClientInfo (except the mBundleID CFStringRef is
|
||||
// wrapped in a CACFString here).
|
||||
UInt32 mClientID;
|
||||
pid_t mProcessID;
|
||||
Boolean mIsNativeEndian = true;
|
||||
CACFString mBundleID;
|
||||
|
||||
// Becomes true when the client triggers the plugin host to call StartIO or to begin
|
||||
// kAudioServerPlugInIOOperationThread, and false again on StopIO or when
|
||||
// kAudioServerPlugInIOOperationThread ends
|
||||
bool mDoingIO = false;
|
||||
|
||||
// True if BGMApp has set this client as belonging to the music player app
|
||||
bool mIsMusicPlayer = false;
|
||||
|
||||
// The client's volume relative to other clients. In the range [0.0, 4.0], defaults to 1.0 (unchanged).
|
||||
// mRelativeVolumeCurve is applied this this value when it's set.
|
||||
Float32 mRelativeVolume = 1.0;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* __BGMDriver__BGM_Client__ */
|
||||
|
359
BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.cpp
Normal file
|
@ -0,0 +1,359 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_ClientMap.cpp
|
||||
// BGMDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
// Self Include
|
||||
#include "BGM_ClientMap.h"
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Types.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CACFDictionary.h"
|
||||
#include "CAException.h"
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
void BGM_ClientMap::AddClient(BGM_Client inClient)
|
||||
{
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
// If this client has been a client in the past (and has a bundle ID), copy its previous relative volume
|
||||
auto pastClientItr = inClient.mBundleID.IsValid() ? mPastClientMap.find(inClient.mBundleID) : mPastClientMap.end();
|
||||
if(pastClientItr != mPastClientMap.end())
|
||||
{
|
||||
DebugMsg("BGM_ClientMap::AddClient: Found previous volume %f for client %u",
|
||||
pastClientItr->second.mRelativeVolume,
|
||||
inClient.mClientID);
|
||||
inClient.mRelativeVolume = pastClientItr->second.mRelativeVolume;
|
||||
}
|
||||
|
||||
// Add the new client to the shadow maps
|
||||
AddClientToShadowMaps(inClient);
|
||||
|
||||
// Swap the maps with their shadow maps
|
||||
SwapInShadowMaps();
|
||||
|
||||
// The shadow maps (which were the main maps until we swapped them) are now missing the new client. Add it again to
|
||||
// keep the sets of maps identical.
|
||||
AddClientToShadowMaps(inClient);
|
||||
|
||||
// Remove the client from the past clients map (if it was in there)
|
||||
if(inClient.mBundleID.IsValid())
|
||||
{
|
||||
mPastClientMap.erase(inClient.mBundleID);
|
||||
}
|
||||
}
|
||||
|
||||
void BGM_ClientMap::AddClientToShadowMaps(BGM_Client inClient)
|
||||
{
|
||||
ThrowIf(mClientMapShadow.count(inClient.mClientID) != 0,
|
||||
BGM_InvalidClientException(),
|
||||
"BGM_ClientMap::AddClientToShadowMaps: Tried to add client whose client ID was already in use");
|
||||
|
||||
// Add to the client ID shadow map
|
||||
mClientMapShadow[inClient.mClientID] = inClient;
|
||||
|
||||
// Get a reference to the client in the map so we can add it to the pointer maps
|
||||
BGM_Client& clientInMap = mClientMapShadow.at(inClient.mClientID);
|
||||
|
||||
// Add to the PID shadow map
|
||||
mClientMapByPIDShadow[inClient.mProcessID].push_back(&clientInMap);
|
||||
|
||||
// Add to the bundle ID shadow map
|
||||
if(inClient.mBundleID.IsValid())
|
||||
{
|
||||
mClientMapByBundleIDShadow[inClient.mBundleID].push_back(&clientInMap);
|
||||
}
|
||||
}
|
||||
|
||||
BGM_Client BGM_ClientMap::RemoveClient(UInt32 inClientID)
|
||||
{
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
auto theClientItr = mClientMapShadow.find(inClientID);
|
||||
|
||||
// Removing a client that was never added is an error
|
||||
ThrowIf(theClientItr == mClientMapShadow.end(),
|
||||
BGM_InvalidClientException(),
|
||||
"BGM_ClientMap::RemoveClient: Could not find client to be removed");
|
||||
|
||||
BGM_Client theClient = theClientItr->second;
|
||||
|
||||
// Insert the client into the past clients map
|
||||
if(theClient.mBundleID.IsValid())
|
||||
{
|
||||
mPastClientMap[theClient.mBundleID] = theClient;
|
||||
}
|
||||
|
||||
// Remove the client from the shadow maps
|
||||
mClientMapShadow.erase(theClientItr);
|
||||
mClientMapByPIDShadow.erase(theClient.mProcessID);
|
||||
if(theClient.mBundleID.IsValid())
|
||||
{
|
||||
mClientMapByBundleID.erase(theClient.mBundleID);
|
||||
}
|
||||
|
||||
// Swap the maps with their shadow maps
|
||||
SwapInShadowMaps();
|
||||
|
||||
// Erase the client again so the maps and their shadow maps are kept identical
|
||||
mClientMapShadow.erase(inClientID);
|
||||
mClientMapByPIDShadow.erase(theClient.mProcessID);
|
||||
if(theClient.mBundleID.IsValid())
|
||||
{
|
||||
mClientMapByBundleID.erase(theClient.mBundleID);
|
||||
}
|
||||
|
||||
return theClient;
|
||||
}
|
||||
|
||||
bool BGM_ClientMap::GetClientRT(UInt32 inClientID, BGM_Client* outClient) const
|
||||
{
|
||||
CAMutex::Locker theMapsLocker(mMapsMutex);
|
||||
return GetClient(mClientMap, inClientID, outClient);
|
||||
}
|
||||
|
||||
bool BGM_ClientMap::GetClientNonRT(UInt32 inClientID, BGM_Client* outClient) const
|
||||
{
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
return GetClient(mClientMapShadow, inClientID, outClient);
|
||||
}
|
||||
|
||||
//static
|
||||
bool BGM_ClientMap::GetClient(const std::map<UInt32, BGM_Client>& inClientMap, UInt32 inClientID, BGM_Client* outClient)
|
||||
{
|
||||
auto theClientItr = inClientMap.find(inClientID);
|
||||
|
||||
if(theClientItr != inClientMap.end())
|
||||
{
|
||||
*outClient = theClientItr->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<BGM_Client> BGM_ClientMap::GetClientsByPID(pid_t inPID) const
|
||||
{
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
std::vector<BGM_Client> theClients;
|
||||
|
||||
auto theMapItr = mClientMapByPIDShadow.find(inPID);
|
||||
if(theMapItr != mClientMapByPIDShadow.end())
|
||||
{
|
||||
// Found clients with the PID, so copy them into the return vector
|
||||
for(auto& theClientPtrsItr : theMapItr->second)
|
||||
{
|
||||
theClients.push_back(*theClientPtrsItr);
|
||||
}
|
||||
}
|
||||
|
||||
return theClients;
|
||||
}
|
||||
|
||||
#pragma mark Music Player
|
||||
|
||||
void BGM_ClientMap::UpdateMusicPlayerFlags(pid_t inMusicPlayerPID)
|
||||
{
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
auto theIsMusicPlayerTest = [&] (BGM_Client theClient) {
|
||||
return (theClient.mProcessID == inMusicPlayerPID);
|
||||
};
|
||||
|
||||
UpdateMusicPlayerFlagsInShadowMaps(theIsMusicPlayerTest);
|
||||
SwapInShadowMaps();
|
||||
UpdateMusicPlayerFlagsInShadowMaps(theIsMusicPlayerTest);
|
||||
}
|
||||
|
||||
void BGM_ClientMap::UpdateMusicPlayerFlags(CACFString inMusicPlayerBundleID)
|
||||
{
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
auto theIsMusicPlayerTest = [&] (BGM_Client theClient) {
|
||||
return (theClient.mBundleID.IsValid() && theClient.mBundleID == inMusicPlayerBundleID);
|
||||
};
|
||||
|
||||
UpdateMusicPlayerFlagsInShadowMaps(theIsMusicPlayerTest);
|
||||
SwapInShadowMaps();
|
||||
UpdateMusicPlayerFlagsInShadowMaps(theIsMusicPlayerTest);
|
||||
}
|
||||
|
||||
void BGM_ClientMap::UpdateMusicPlayerFlagsInShadowMaps(std::function<bool(BGM_Client)> inIsMusicPlayerTest)
|
||||
{
|
||||
for(auto& theItr : mClientMapShadow)
|
||||
{
|
||||
BGM_Client& theClient = theItr.second;
|
||||
theClient.mIsMusicPlayer = inIsMusicPlayerTest(theClient);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark App Volumes
|
||||
|
||||
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
|
||||
// locking the main maps.
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
CACFArray theAppVolumes(false);
|
||||
|
||||
for(auto& theClientEntry : mClientMapShadow)
|
||||
{
|
||||
CopyClientIntoAppVolumesArray(theClientEntry.second, inVolumeCurve, theAppVolumes);
|
||||
}
|
||||
|
||||
for(auto& thePastClientEntry : mPastClientMap)
|
||||
{
|
||||
CopyClientIntoAppVolumesArray(thePastClientEntry.second, inVolumeCurve, theAppVolumes);
|
||||
}
|
||||
|
||||
return theAppVolumes;
|
||||
}
|
||||
|
||||
void BGM_ClientMap::CopyClientIntoAppVolumesArray(BGM_Client inClient, CAVolumeCurve inVolumeCurve, CACFArray& ioAppVolumes) const
|
||||
{
|
||||
// Only include clients set to a non-default volume
|
||||
if(inClient.mRelativeVolume != 1.0)
|
||||
{
|
||||
CACFDictionary theAppVolume(false);
|
||||
|
||||
theAppVolume.AddSInt32(CFSTR(kBGMAppVolumesKey_ProcessID), inClient.mProcessID);
|
||||
theAppVolume.AddString(CFSTR(kBGMAppVolumesKey_BundleID), inClient.mBundleID.CopyCFString());
|
||||
// Reverse the volume conversion from SetClientsRelativeVolumes
|
||||
theAppVolume.AddSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume),
|
||||
inVolumeCurve.ConvertScalarToRaw(inClient.mRelativeVolume / 4));
|
||||
|
||||
ioAppVolumes.AppendDictionary(theAppVolume.GetDict());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Combine the SetClientsRelativeVolume methods? Their code is very similar.
|
||||
|
||||
bool BGM_ClientMap::SetClientsRelativeVolume(pid_t inAppPID, Float32 inRelativeVolume)
|
||||
{
|
||||
bool didChangeVolume = false;
|
||||
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
auto theSetVolumesInShadowMapsFunc = [&] {
|
||||
// Look up the clients for the PID and update their volumes
|
||||
auto theClientItr = mClientMapByPIDShadow.find(inAppPID);
|
||||
|
||||
if(theClientItr != mClientMapByPIDShadow.end())
|
||||
{
|
||||
std::vector<BGM_Client*> theClients = theClientItr->second;
|
||||
|
||||
for(BGM_Client* theClient : theClients)
|
||||
{
|
||||
theClient->mRelativeVolume = inRelativeVolume;
|
||||
|
||||
DebugMsg("BGM_ClientMap::SetClientsRelativeVolume: Set volume %f for client %u by pid (%d)",
|
||||
theClient->mRelativeVolume,
|
||||
theClient->mClientID,
|
||||
theClient->mProcessID);
|
||||
|
||||
didChangeVolume = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
theSetVolumesInShadowMapsFunc();
|
||||
SwapInShadowMaps();
|
||||
theSetVolumesInShadowMapsFunc();
|
||||
|
||||
return didChangeVolume;
|
||||
}
|
||||
|
||||
|
||||
bool BGM_ClientMap::SetClientsRelativeVolume(CACFString inAppBundleID, Float32 inRelativeVolume)
|
||||
{
|
||||
bool didChangeVolume = false;
|
||||
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
auto theSetVolumesInShadowMapsFunc = [&] {
|
||||
// Look up the clients for the bundle ID and update their volumes
|
||||
auto theClientItr = mClientMapByBundleIDShadow.find(inAppBundleID);
|
||||
|
||||
if(theClientItr != mClientMapByBundleIDShadow.end())
|
||||
{
|
||||
std::vector<BGM_Client*> theClients = theClientItr->second;
|
||||
|
||||
for(BGM_Client* theClient : theClients)
|
||||
{
|
||||
theClient->mRelativeVolume = inRelativeVolume;
|
||||
|
||||
DebugMsg("BGM_ClientMap::SetClientsRelativeVolume: Set volume %f for client %u by bundle ID (%s)",
|
||||
theClient->mRelativeVolume,
|
||||
theClient->mClientID,
|
||||
CFStringGetCStringPtr(inAppBundleID.GetCFString(), kCFStringEncodingUTF8));
|
||||
|
||||
didChangeVolume = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
theSetVolumesInShadowMapsFunc();
|
||||
SwapInShadowMaps();
|
||||
theSetVolumesInShadowMapsFunc();
|
||||
|
||||
return didChangeVolume;
|
||||
}
|
||||
|
||||
void BGM_ClientMap::UpdateClientIOStateNonRT(UInt32 inClientID, bool inDoingIO)
|
||||
{
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
mClientMapShadow[inClientID].mDoingIO = inDoingIO;
|
||||
SwapInShadowMaps();
|
||||
mClientMapShadow[inClientID].mDoingIO = inDoingIO;
|
||||
}
|
||||
|
||||
void BGM_ClientMap::SwapInShadowMaps()
|
||||
{
|
||||
mTaskQueue->QueueSync_SwapClientShadowMaps(this);
|
||||
}
|
||||
|
||||
void BGM_ClientMap::SwapInShadowMapsRT()
|
||||
{
|
||||
#if DEBUG
|
||||
// This method should only be called by the realtime worker thread in BGM_TaskQueue. The only safe way to call it is on a realtime
|
||||
// thread while a non-realtime thread is holding the shadow maps mutex. (These assertions assume that the realtime worker thread is
|
||||
// the only thread we'll call this on, but we could decide to change that at some point.)
|
||||
mTaskQueue->AssertCurrentThreadIsRTWorkerThread("BGM_ClientMap::SwapInShadowMapsRT");
|
||||
|
||||
Assert(!mShadowMapsMutex.IsFree(), "Can't swap in the shadow maps while the shadow maps mutex is free");
|
||||
Assert(!mShadowMapsMutex.IsOwnedByCurrentThread(), "The shadow maps mutex should not be held by a realtime thread");
|
||||
#endif
|
||||
|
||||
CAMutex::Locker theMapsLocker(mMapsMutex);
|
||||
|
||||
mClientMap.swap(mClientMapShadow);
|
||||
mClientMapByPID.swap(mClientMapByPIDShadow);
|
||||
mClientMapByBundleID.swap(mClientMapByBundleIDShadow);
|
||||
}
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
174
BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.h
Normal file
|
@ -0,0 +1,174 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_ClientMap.h
|
||||
// BGMDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
|
||||
#ifndef __BGMDriver__BGM_ClientMap__
|
||||
#define __BGMDriver__BGM_ClientMap__
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Client.h"
|
||||
#include "BGM_TaskQueue.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAMutex.h"
|
||||
#include "CACFString.h"
|
||||
#include "CACFArray.h"
|
||||
#include "CAVolumeCurve.h"
|
||||
|
||||
// STL Includes
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
|
||||
// Forward Declarations
|
||||
class BGM_ClientTasks;
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
//==================================================================================================
|
||||
// BGM_ClientMap
|
||||
//
|
||||
// This class stores the clients (BGM_Client) that have been registered with BGMDevice by the HAL.
|
||||
// It also maintains maps from clients' PIDs and bundle IDs to the clients. When a client is
|
||||
// removed by the HAL we add it to a map of past clients to keep track of settings specific to that
|
||||
// client. (Currently only the client's volume.)
|
||||
//
|
||||
// Since the maps are read from during IO, this class has to to be real-time safe when accessing
|
||||
// them. So each map has an identical "shadow" map, which we use to buffer updates.
|
||||
//
|
||||
// To update the clients we lock the shadow maps, modify them, have BGM_TaskQueue's real-time
|
||||
// thread swap them with the main maps, and then repeat the modification to keep both sets of maps
|
||||
// identical. We have to swap the maps on a real-time thread so we can take the main maps' lock
|
||||
// without risking priority inversion, but this way the actual work doesn't need to be real-time
|
||||
// safe.
|
||||
//
|
||||
// Methods that only read from the maps and are called on non-real-time threads will just read
|
||||
// from the shadow maps because it's easier.
|
||||
//
|
||||
// Methods whose names end with "RT" and "NonRT" can only safely be called from real-time and
|
||||
// non-real-time threads respectively. (Methods with neither are most likely non-RT.)
|
||||
//==================================================================================================
|
||||
|
||||
class BGM_ClientMap
|
||||
{
|
||||
|
||||
friend class BGM_ClientTasks;
|
||||
|
||||
typedef std::vector<BGM_Client*> BGM_ClientPtrList;
|
||||
|
||||
public:
|
||||
BGM_ClientMap(BGM_TaskQueue* inTaskQueue) : mTaskQueue(inTaskQueue), mMapsMutex("Maps mutex"), mShadowMapsMutex("Shadow maps mutex") { };
|
||||
|
||||
void AddClient(BGM_Client inClient);
|
||||
|
||||
private:
|
||||
void AddClientToShadowMaps(BGM_Client inClient);
|
||||
|
||||
public:
|
||||
// Returns the removed client
|
||||
BGM_Client RemoveClient(UInt32 inClientID);
|
||||
|
||||
// These methods are functionally identical except that GetClientRT must only be called from real-time threads and GetClientNonRT
|
||||
// must only be called from non-real-time threads. Both return true if a client was found.
|
||||
bool GetClientRT(UInt32 inClientID, BGM_Client* outClient) const;
|
||||
bool GetClientNonRT(UInt32 inClientID, BGM_Client* outClient) const;
|
||||
|
||||
private:
|
||||
static bool GetClient(const std::map<UInt32, BGM_Client>& inClientMap,
|
||||
UInt32 inClientID,
|
||||
BGM_Client* outClient);
|
||||
|
||||
public:
|
||||
std::vector<BGM_Client> GetClientsByPID(pid_t inPID) 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);
|
||||
|
||||
private:
|
||||
void UpdateMusicPlayerFlagsInShadowMaps(std::function<bool(BGM_Client)> inIsMusicPlayerTest);
|
||||
|
||||
public:
|
||||
// Copies the current and past clients into an array in the format expected for
|
||||
// kAudioDeviceCustomPropertyAppVolumes. (Except that CACFArray and CACFDictionary are used instead
|
||||
// of unwrapped CFArray and CFDictionary refs.)
|
||||
CACFArray CopyClientRelativeVolumesAsAppVolumes(CAVolumeCurve inVolumeCurve) const;
|
||||
|
||||
private:
|
||||
void CopyClientIntoAppVolumesArray(BGM_Client inClient, CAVolumeCurve inVolumeCurve, CACFArray& ioAppVolumes) const;
|
||||
|
||||
public:
|
||||
// Returns true if a client with PID inAppPID was found and its relative volume changed.
|
||||
bool SetClientsRelativeVolume(pid_t inAppPID, Float32 inRelativeVolume);
|
||||
// Returns true if a client with bundle ID inAppBundleID was found and its relative volume changed.
|
||||
bool SetClientsRelativeVolume(CACFString inAppBundleID, Float32 inRelativeVolume);
|
||||
|
||||
void StartIONonRT(UInt32 inClientID) { UpdateClientIOStateNonRT(inClientID, true); }
|
||||
void StopIONonRT(UInt32 inClientID) { UpdateClientIOStateNonRT(inClientID, false); }
|
||||
|
||||
private:
|
||||
void UpdateClientIOStateNonRT(UInt32 inClientID, bool inDoingIO);
|
||||
|
||||
// Has a real-time thread call SwapInShadowMapsRT. (Synchronously queues the call as a task on mTaskQueue.)
|
||||
// The shadow maps mutex must be locked when calling this method.
|
||||
void SwapInShadowMaps();
|
||||
// Note that this method is called by BGM_TaskQueue through the BGM_ClientTasks interface. The shadow maps
|
||||
// mutex must be locked when calling this method.
|
||||
void SwapInShadowMapsRT();
|
||||
|
||||
private:
|
||||
BGM_TaskQueue* mTaskQueue;
|
||||
|
||||
// Must be held to access mClientMap or mClientMapByPID. Code that runs while holding this mutex needs
|
||||
// to be real-time safe. Should probably not be held for most operations on mClientMapByBundleID because,
|
||||
// as far as I can tell, code that works with CFStrings is unlikely to be real-time safe.
|
||||
CAMutex mMapsMutex;
|
||||
// Should only be locked by non-real-time threads. Should not be released until the maps have been
|
||||
// made identical to their shadow maps.
|
||||
CAMutex mShadowMapsMutex;
|
||||
|
||||
// The clients currently registered with BGMDevice. Indexed by client ID.
|
||||
std::map<UInt32, BGM_Client> mClientMap;
|
||||
// We keep this in sync with mClientMap so it can be modified outside of real-time safe sections and
|
||||
// then swapped in on a real-time thread, which is safe.
|
||||
std::map<UInt32, BGM_Client> mClientMapShadow;
|
||||
|
||||
// These maps hold lists of pointers to clients in mClientMap/mClientMapShadow. Lists because a process
|
||||
// can have multiple clients and clients can have the same bundle ID.
|
||||
|
||||
std::map<pid_t, BGM_ClientPtrList> mClientMapByPID;
|
||||
std::map<pid_t, BGM_ClientPtrList> mClientMapByPIDShadow;
|
||||
|
||||
std::map<CACFString, BGM_ClientPtrList> mClientMapByBundleID;
|
||||
std::map<CACFString, BGM_ClientPtrList> mClientMapByBundleIDShadow;
|
||||
|
||||
// Clients are added to mPastClientMap after they're removed so we can restore settings specific to them
|
||||
// if they get added again.
|
||||
std::map<CACFString, BGM_Client> mPastClientMap;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* __BGMDriver__BGM_ClientMap__ */
|
||||
|
56
BGMDriver/BGMDriver/DeviceClients/BGM_ClientTasks.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
// 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/>.
|
||||
|
||||
//
|
||||
// BGM_ClientTasks.h
|
||||
// BGMDriver
|
||||
//
|
||||
// Copyright © 2016 Kyle Neideck
|
||||
//
|
||||
// The interface between the client classes (BGM_Client, BGM_Clients and BGM_ClientMap) and BGM_TaskQueue.
|
||||
//
|
||||
|
||||
#ifndef __BGMDriver__BGM_ClientTasks__
|
||||
#define __BGMDriver__BGM_ClientTasks__
|
||||
|
||||
// Local Includes
|
||||
#include "BGM_Clients.h"
|
||||
#include "BGM_ClientMap.h"
|
||||
|
||||
|
||||
// Forward Declarations
|
||||
class BGM_TaskQueue;
|
||||
|
||||
|
||||
#pragma clang assume_nonnull begin
|
||||
|
||||
class BGM_ClientTasks
|
||||
{
|
||||
|
||||
friend class BGM_TaskQueue;
|
||||
|
||||
private:
|
||||
|
||||
static bool StartIONonRT(BGM_Clients* inClients, UInt32 inClientID) { return inClients->StartIONonRT(inClientID); }
|
||||
static bool StopIONonRT(BGM_Clients* inClients, UInt32 inClientID) { return inClients->StopIONonRT(inClientID); }
|
||||
|
||||
static void SwapInShadowMapsRT(BGM_ClientMap* inClientMap) { inClientMap->SwapInShadowMapsRT(); }
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
||||
#endif /* __BGMDriver__BGM_ClientTasks__ */
|
||||
|