Initial commit

This commit is contained in:
Kyle Neideck 2016-02-19 16:25:34 +11:00
commit b17d736382
149 changed files with 30137 additions and 0 deletions

30
.gitignore vendored Normal file
View 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
View 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>

View 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 */;
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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;
}

View 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 */

View 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
}

View 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__) */

View 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>

View file

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "mac",
"filename" : "FermataIcon.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

38
BGMApp/BGMApp/Info.plist Normal file
View 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
View 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.

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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 players 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
View 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 players 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 CDs 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 tracks 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
View 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);
}

View 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

View 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>

Binary file not shown.

View 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__

View 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

View 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

View 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;
}

View 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

View 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;
}

View 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__

View 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);
}

View 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

View 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;
}
}
}

View 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

View 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);
}

View 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

View 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

View 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

View 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
}

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View 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");
}

View 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

View 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];
}
}
}

View 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

View 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;
}

View 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

View 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
}

View 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

View 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();
}
}

View 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__

View 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

View 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

View 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

View 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;
}

View 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

View 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 */;
}

File diff suppressed because it is too large Load diff

View 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__ */

View 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;
};
}

View 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__ */

View 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;
};
}

View 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__ */

View 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;
}

View 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

View 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__ */

View 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

View 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;
}

View 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__ */

View 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;
}

View 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__ */

View 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

View 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__ */

View 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__ */

Some files were not shown because too many files have changed in this diff Show more