Refactor non-UI code out of BGMAppVolumes.

This commit is contained in:
Kyle Neideck 2017-10-28 18:13:08 +11:00
parent fb0740c4c1
commit 1171bee102
No known key found for this signature in database
GPG key ID: CAA8D9B8E39EC18C
25 changed files with 392 additions and 217 deletions

View file

@ -36,7 +36,7 @@
1C3D36721ED90E8600F98E66 /* BGMDeviceControlsList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */; }; 1C3D36721ED90E8600F98E66 /* BGMDeviceControlsList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */; };
1C3D36731ED90E8600F98E66 /* BGMDeviceControlsList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */; }; 1C3D36731ED90E8600F98E66 /* BGMDeviceControlsList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */; };
1C3D36741ED90E8600F98E66 /* BGMDeviceControlsList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */; }; 1C3D36741ED90E8600F98E66 /* BGMDeviceControlsList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */; };
1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */; }; 1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.m */; };
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C4699461BD5C0E400F78043 /* BGMiTunes.m */; }; 1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C4699461BD5C0E400F78043 /* BGMiTunes.m */; };
1C46994E1BD7694C00F78043 /* BGMDeviceControlSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */; }; 1C46994E1BD7694C00F78043 /* BGMDeviceControlSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */; };
1C50FF631EC9F4490031A6EA /* BGMAudioDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CF5423A1EAAEE4300445AD8 /* BGMAudioDevice.cpp */; }; 1C50FF631EC9F4490031A6EA /* BGMAudioDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CF5423A1EAAEE4300445AD8 /* BGMAudioDevice.cpp */; };
@ -65,6 +65,9 @@
1CCC4F4E1E581C40008053E4 /* Mock_CAHALAudioObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CCC4F4C1E581C40008053E4 /* Mock_CAHALAudioObject.cpp */; }; 1CCC4F4E1E581C40008053E4 /* Mock_CAHALAudioObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CCC4F4C1E581C40008053E4 /* Mock_CAHALAudioObject.cpp */; };
1CCC4F621E584100008053E4 /* BGMAppUITests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CCC4F611E584100008053E4 /* BGMAppUITests.mm */; }; 1CCC4F621E584100008053E4 /* BGMAppUITests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CCC4F611E584100008053E4 /* BGMAppUITests.mm */; };
1CD1FD301BDDEAF2004F7E1B /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */; }; 1CD1FD301BDDEAF2004F7E1B /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */; };
1CD410D41F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */; };
1CD410D51F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */; };
1CD410D61F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */; };
1CD989341ECFFC9E0014BBBF /* BGM_Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27FB8C2E1DE468320084DB9D /* BGM_Utils.cpp */; }; 1CD989341ECFFC9E0014BBBF /* BGM_Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27FB8C2E1DE468320084DB9D /* BGM_Utils.cpp */; };
1CD989351ECFFC9E0014BBBF /* CACFArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF7D1BE5068A00FB8FE4 /* CACFArray.cpp */; }; 1CD989351ECFFC9E0014BBBF /* CACFArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF7D1BE5068A00FB8FE4 /* CACFArray.cpp */; };
1CD989361ECFFC9E0014BBBF /* CACFDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF7F1BE5068A00FB8FE4 /* CACFDictionary.cpp */; }; 1CD989361ECFFC9E0014BBBF /* CACFDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CC1DF7F1BE5068A00FB8FE4 /* CACFDictionary.cpp */; };
@ -79,7 +82,7 @@
1CD9893F1ECFFC9E0014BBBF /* CAHALAudioSystemObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962F11BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp */; }; 1CD9893F1ECFFC9E0014BBBF /* CAHALAudioSystemObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962F11BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp */; };
1CD989401ECFFCC50014BBBF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */; }; 1CD989401ECFFCC50014BBBF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */; };
1CD989411ECFFCD10014BBBF /* BGMAppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B33C1BBA75EF000E2DD1 /* BGMAppDelegate.mm */; }; 1CD989411ECFFCD10014BBBF /* BGMAppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B33C1BBA75EF000E2DD1 /* BGMAppDelegate.mm */; };
1CD989421ECFFCFC0014BBBF /* BGMAppVolumes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */; }; 1CD989421ECFFCFC0014BBBF /* BGMAppVolumes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.m */; };
1CD989431ECFFCFC0014BBBF /* BGMAudioDeviceManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */; }; 1CD989431ECFFCFC0014BBBF /* BGMAudioDeviceManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */; };
1CD989441ECFFCFC0014BBBF /* BGMAutoPauseMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C457E51CF2BC2600A6C9A6 /* BGMAutoPauseMenuItem.m */; }; 1CD989441ECFFCFC0014BBBF /* BGMAutoPauseMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C457E51CF2BC2600A6C9A6 /* BGMAutoPauseMenuItem.m */; };
1CD989451ECFFCFC0014BBBF /* BGMAutoPauseMusic.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C1465B71BCC3A73003AEFE6 /* BGMAutoPauseMusic.mm */; }; 1CD989451ECFFCFC0014BBBF /* BGMAutoPauseMusic.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C1465B71BCC3A73003AEFE6 /* BGMAutoPauseMusic.mm */; };
@ -240,7 +243,7 @@
1C2FC31D1EC723A100A76592 /* BGMASOutputDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMASOutputDevice.h; path = Scripting/BGMASOutputDevice.h; sourceTree = "<group>"; }; 1C2FC31D1EC723A100A76592 /* BGMASOutputDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMASOutputDevice.h; path = Scripting/BGMASOutputDevice.h; sourceTree = "<group>"; };
1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMDeviceControlsList.cpp; sourceTree = "<group>"; }; 1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMDeviceControlsList.cpp; sourceTree = "<group>"; };
1C3D36711ED90E8600F98E66 /* BGMDeviceControlsList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMDeviceControlsList.h; sourceTree = "<group>"; }; 1C3D36711ED90E8600F98E66 /* BGMDeviceControlsList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMDeviceControlsList.h; sourceTree = "<group>"; };
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMAppVolumes.mm; sourceTree = "<group>"; }; 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BGMAppVolumes.m; sourceTree = "<group>"; };
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMAppVolumes.h; sourceTree = "<group>"; }; 1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMAppVolumes.h; sourceTree = "<group>"; };
1C4699461BD5C0E400F78043 /* BGMiTunes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMiTunes.m; path = "Music Players/BGMiTunes.m"; sourceTree = "<group>"; }; 1C4699461BD5C0E400F78043 /* BGMiTunes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMiTunes.m; path = "Music Players/BGMiTunes.m"; sourceTree = "<group>"; };
1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMDeviceControlSync.cpp; sourceTree = "<group>"; }; 1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMDeviceControlSync.cpp; sourceTree = "<group>"; };
@ -277,6 +280,8 @@
1CCC4F5F1E5840EF008053E4 /* BGMAppUITests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "BGMAppUITests-Info.plist"; path = "UITests/BGMAppUITests-Info.plist"; sourceTree = "<group>"; }; 1CCC4F5F1E5840EF008053E4 /* BGMAppUITests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "BGMAppUITests-Info.plist"; path = "UITests/BGMAppUITests-Info.plist"; sourceTree = "<group>"; };
1CCC4F611E584100008053E4 /* BGMAppUITests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMAppUITests.mm; path = BGMAppTests/UITests/BGMAppUITests.mm; sourceTree = SOURCE_ROOT; }; 1CCC4F611E584100008053E4 /* BGMAppUITests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMAppUITests.mm; path = BGMAppTests/UITests/BGMAppUITests.mm; sourceTree = SOURCE_ROOT; };
1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
1CD410D21F9EDDAD0070A094 /* BGMAppVolumesController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BGMAppVolumesController.h; sourceTree = "<group>"; };
1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMAppVolumesController.mm; sourceTree = "<group>"; };
1CE7064A1BF1EC0600BFC06D /* BGMOutputDevicePrefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMOutputDevicePrefs.h; path = Preferences/BGMOutputDevicePrefs.h; sourceTree = "<group>"; }; 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>"; }; 1CE7064B1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMOutputDevicePrefs.mm; path = Preferences/BGMOutputDevicePrefs.mm; sourceTree = "<group>"; };
1CEACF4E1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mock_CAHALAudioSystemObject.cpp; path = UnitTests/Mock_CAHALAudioSystemObject.cpp; sourceTree = "<group>"; }; 1CEACF4E1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mock_CAHALAudioSystemObject.cpp; path = UnitTests/Mock_CAHALAudioSystemObject.cpp; sourceTree = "<group>"; };
@ -527,7 +532,9 @@
1C837DD61F6AA1F2004B1E60 /* BGMOutputVolumeMenuItem.h */, 1C837DD61F6AA1F2004B1E60 /* BGMOutputVolumeMenuItem.h */,
1C837DD71F6AA1F2004B1E60 /* BGMOutputVolumeMenuItem.mm */, 1C837DD71F6AA1F2004B1E60 /* BGMOutputVolumeMenuItem.mm */,
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */, 1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */,
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */, 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.m */,
1CD410D21F9EDDAD0070A094 /* BGMAppVolumesController.h */,
1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */,
1CED616A1C316E1A002CAFCF /* BGMAudioDeviceManager.h */, 1CED616A1C316E1A002CAFCF /* BGMAudioDeviceManager.h */,
1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */, 1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */,
1CF5423B1EAAEE4300445AD8 /* BGMAudioDevice.h */, 1CF5423B1EAAEE4300445AD8 /* BGMAudioDevice.h */,
@ -921,6 +928,7 @@
files = ( files = (
1C86DA6A1F91EE3B000C8CCF /* CAPThread.cpp in Sources */, 1C86DA6A1F91EE3B000C8CCF /* CAPThread.cpp in Sources */,
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */, 1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */,
1CD410D41F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */,
1C1962E41BC94E15008A4DF7 /* CARingBuffer.cpp in Sources */, 1C1962E41BC94E15008A4DF7 /* CARingBuffer.cpp in Sources */,
273F10DF1CC3D0B900C1C6DA /* BGMVOX.m in Sources */, 273F10DF1CC3D0B900C1C6DA /* BGMVOX.m in Sources */,
1CC1DF811BE5068A00FB8FE4 /* CACFArray.cpp in Sources */, 1CC1DF811BE5068A00FB8FE4 /* CACFArray.cpp in Sources */,
@ -931,7 +939,7 @@
1C2336DA1BEAB6E7004C1C4E /* BGMMusicPlayer.m in Sources */, 1C2336DA1BEAB6E7004C1C4E /* BGMMusicPlayer.m in Sources */,
1C1962F61BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp in Sources */, 1C1962F61BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp in Sources */,
1CC1DF821BE5068A00FB8FE4 /* CACFDictionary.cpp in Sources */, 1CC1DF821BE5068A00FB8FE4 /* CACFDictionary.cpp in Sources */,
1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.mm in Sources */, 1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.m in Sources */,
1C0BD0A51BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm in Sources */, 1C0BD0A51BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm in Sources */,
1C1963061BCAF468008A4DF7 /* CAMutex.cpp in Sources */, 1C1963061BCAF468008A4DF7 /* CAMutex.cpp in Sources */,
1CE7064C1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm in Sources */, 1CE7064C1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm in Sources */,
@ -974,11 +982,12 @@
files = ( files = (
1CACCF3A1F334447007F86CA /* BGMBackgroundMusicDevice.cpp in Sources */, 1CACCF3A1F334447007F86CA /* BGMBackgroundMusicDevice.cpp in Sources */,
1CC6593D1F91DEB400B0CCDC /* BGMTermination.mm in Sources */, 1CC6593D1F91DEB400B0CCDC /* BGMTermination.mm in Sources */,
1CD410D51F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */,
1CD989571ECFFD250014BBBF /* CAHostTimeBase.cpp in Sources */, 1CD989571ECFFD250014BBBF /* CAHostTimeBase.cpp in Sources */,
1CD989581ECFFD250014BBBF /* CAMutex.cpp in Sources */, 1CD989581ECFFD250014BBBF /* CAMutex.cpp in Sources */,
1CD989591ECFFD250014BBBF /* CAPThread.cpp in Sources */, 1CD989591ECFFD250014BBBF /* CAPThread.cpp in Sources */,
1CD9895A1ECFFD250014BBBF /* CARingBuffer.cpp in Sources */, 1CD9895A1ECFFD250014BBBF /* CARingBuffer.cpp in Sources */,
1CD989421ECFFCFC0014BBBF /* BGMAppVolumes.mm in Sources */, 1CD989421ECFFCFC0014BBBF /* BGMAppVolumes.m in Sources */,
1CD989431ECFFCFC0014BBBF /* BGMAudioDeviceManager.mm in Sources */, 1CD989431ECFFCFC0014BBBF /* BGMAudioDeviceManager.mm in Sources */,
1CD989441ECFFCFC0014BBBF /* BGMAutoPauseMenuItem.m in Sources */, 1CD989441ECFFCFC0014BBBF /* BGMAutoPauseMenuItem.m in Sources */,
1CD989451ECFFCFC0014BBBF /* BGMAutoPauseMusic.mm in Sources */, 1CD989451ECFFCFC0014BBBF /* BGMAutoPauseMusic.mm in Sources */,
@ -1079,6 +1088,7 @@
2743CA031D86D41C0089613B /* BGMScriptingBridge.m in Sources */, 2743CA031D86D41C0089613B /* BGMScriptingBridge.m in Sources */,
1CEACF4F1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp in Sources */, 1CEACF4F1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp in Sources */,
2743CA041D86D41C0089613B /* BGMMusicPlayer.m in Sources */, 2743CA041D86D41C0089613B /* BGMMusicPlayer.m in Sources */,
1CD410D61F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */,
2743CA051D86D41C0089613B /* BGMDecibel.m in Sources */, 2743CA051D86D41C0089613B /* BGMDecibel.m in Sources */,
2743CA061D86D41C0089613B /* BGMSpotify.m in Sources */, 2743CA061D86D41C0089613B /* BGMSpotify.m in Sources */,
2743CA071D86D41C0089613B /* BGMVLC.m in Sources */, 2743CA071D86D41C0089613B /* BGMVLC.m in Sources */,

View file

@ -29,7 +29,7 @@
#import "BGMMusicPlayers.h" #import "BGMMusicPlayers.h"
#import "BGMAutoPauseMusic.h" #import "BGMAutoPauseMusic.h"
#import "BGMAutoPauseMenuItem.h" #import "BGMAutoPauseMenuItem.h"
#import "BGMAppVolumes.h" #import "BGMAppVolumesController.h"
#import "BGMPreferencesMenu.h" #import "BGMPreferencesMenu.h"
#import "BGMXPCListener.h" #import "BGMXPCListener.h"
#import "BGMOutputVolumeMenuItem.h" #import "BGMOutputVolumeMenuItem.h"
@ -55,7 +55,7 @@ static float const kStatusBarIconPadding = 0.25;
BGMAutoPauseMusic* autoPauseMusic; BGMAutoPauseMusic* autoPauseMusic;
BGMAutoPauseMenuItem* autoPauseMenuItem; BGMAutoPauseMenuItem* autoPauseMenuItem;
BGMMusicPlayers* musicPlayers; BGMMusicPlayers* musicPlayers;
BGMAppVolumes* appVolumes; BGMAppVolumesController* appVolumes;
BGMPreferencesMenu* prefsMenu; BGMPreferencesMenu* prefsMenu;
BGMXPCListener* xpcListener; BGMXPCListener* xpcListener;
} }
@ -182,9 +182,9 @@ static float const kStatusBarIconPadding = 0.25;
atIndex:([self.bgmMenu indexOfItemWithTag:kVolumesHeadingMenuItemTag] + 1)]; atIndex:([self.bgmMenu indexOfItemWithTag:kVolumesHeadingMenuItemTag] + 1)];
appVolumes = [[BGMAppVolumes alloc] initWithMenu:self.bgmMenu appVolumes = [[BGMAppVolumesController alloc] initWithMenu:self.bgmMenu
appVolumeView:self.appVolumeView appVolumeView:self.appVolumeView
audioDevices:audioDevices]; audioDevices:audioDevices];
prefsMenu = [[BGMPreferencesMenu alloc] initWithBGMMenu:self.bgmMenu prefsMenu = [[BGMPreferencesMenu alloc] initWithBGMMenu:self.bgmMenu
audioDevices:audioDevices audioDevices:audioDevices

View file

@ -17,7 +17,7 @@
// BGMAppVolumes.h // BGMAppVolumes.h
// BGMApp // BGMApp
// //
// Copyright © 2016 Kyle Neideck // Copyright © 2016, 2017 Kyle Neideck
// //
// Local Includes // Local Includes
@ -26,6 +26,7 @@
// System Includes // System Includes
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#pragma clang assume_nonnull begin
@interface BGMAppVolumes : NSObject @interface BGMAppVolumes : NSObject
@ -33,6 +34,15 @@
appVolumeView:(NSView*)view appVolumeView:(NSView*)view
audioDevices:(BGMAudioDeviceManager*)audioDevices; audioDevices:(BGMAudioDeviceManager*)audioDevices;
// Pass -1 for initialVolume or initialPan to leave the volume/pan at its default level.
- (void) insertMenuItemForApp:(NSRunningApplication*)app
initialVolume:(int)volume
initialPan:(int)pan;
- (void) removeMenuItemForApp:(NSRunningApplication*)app;
- (void) removeAllAppVolumeMenuItems;
@end @end
// Protocol for the UI custom classes // Protocol for the UI custom classes
@ -58,13 +68,16 @@
@interface BGMAVM_VolumeSlider : NSSlider <BGMAppVolumeMenuItemSubview> @interface BGMAVM_VolumeSlider : NSSlider <BGMAppVolumeMenuItemSubview>
- (void) setRelativeVolume:(NSNumber*)relativeVolume; - (void) setRelativeVolume:(int)relativeVolume;
@end @end
@interface BGMAVM_PanSlider : NSSlider <BGMAppVolumeMenuItemSubview> @interface BGMAVM_PanSlider : NSSlider <BGMAppVolumeMenuItemSubview>
- (void) setPanPosition:(NSNumber*)panPosition; - (void) setPanPosition:(int)panPosition;
@end @end
#pragma clang assume_nonnull end

View file

@ -30,9 +30,7 @@
#import "BGMAppDelegate.h" #import "BGMAppDelegate.h"
// PublicUtility Includes // PublicUtility Includes
#import "CACFDictionary.h" #import "CADebugMacros.h"
#import "CACFArray.h"
#import "CACFString.h"
static float const kSlidersSnapWithin = 5; static float const kSlidersSnapWithin = 5;
@ -52,7 +50,9 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
NSInteger numMenuItems; NSInteger numMenuItems;
} }
- (id) initWithMenu:(NSMenu*)menu appVolumeView:(NSView*)view audioDevices:(BGMAudioDeviceManager*)devices { - (id) initWithMenu:(NSMenu*)menu
appVolumeView:(NSView*)view
audioDevices:(BGMAudioDeviceManager*)devices {
if ((self = [super init])) { if ((self = [super init])) {
bgmMenu = menu; bgmMenu = menu;
moreAppsMenu = [[NSMenu alloc] initWithTitle:kMoreAppsMenuTitle]; moreAppsMenu = [[NSMenu alloc] initWithTitle:kMoreAppsMenuTitle];
@ -60,9 +60,6 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
appVolumeViewFullHeight = appVolumeView.frame.size.height; appVolumeViewFullHeight = appVolumeView.frame.size.height;
audioDevices = devices; audioDevices = devices;
numMenuItems = 0; numMenuItems = 0;
// Create the menu items for controlling app volumes
[self insertMenuItemsForApps:[[NSWorkspace sharedWorkspace] runningApplications]];
// Add the More Apps menu to the main menu. // Add the More Apps menu to the main menu.
NSMenuItem* moreAppsMenuItem = NSMenuItem* moreAppsMenuItem =
@ -79,21 +76,11 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
[bgmMenu insertItem:spacer atIndex:[self lastMenuItemIndex]]; [bgmMenu insertItem:spacer atIndex:[self lastMenuItemIndex]];
numMenuItems++; numMenuItems++;
// 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; return self;
} }
- (void) dealloc {
[[NSWorkspace sharedWorkspace] removeObserver:self forKeyPath:@"runningApplications" context:nil];
}
// This method allows the Interface Builder Custom Classes for controls (below) to send their values // This method allows the Interface Builder Custom Classes for controls (below) to send their values
// directly to BGMDevice. Not public to other classes. // directly to BGMDevice. Not public to other classes.
- (BGMAudioDeviceManager*) audioDevices { - (BGMAudioDeviceManager*) audioDevices {
@ -102,37 +89,9 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
#pragma mark UI Modifications #pragma mark UI Modifications
// Adds a volume control menu item for each given app.
- (void) insertMenuItemsForApps:(NSArray<NSRunningApplication*>*)apps {
NSAssert([NSThread isMainThread], @"insertMenuItemsForApps is not thread safe");
// TODO: Handle the C++ exceptions this method can throw. They can cause crashes because this
// method is called in a KVO handler.
// Get the app volumes currently set on the device
CACFArray appVols([audioDevices bgmDevice].GetAppVolumes(), false);
for (NSRunningApplication* app in apps) {
if ([self shouldBeIncludedInMenu:app]) {
[self insertMenuItemForApp:app appVolumesOnDevice:appVols];
}
}
}
- (BOOL) shouldBeIncludedInMenu:(NSRunningApplication*)app {
// Ignore hidden apps and Background Music itself.
// TODO: Would it be better to only show apps that are registered as HAL clients?
BOOL isHidden = app.activationPolicy != NSApplicationActivationPolicyRegular &&
app.activationPolicy != NSApplicationActivationPolicyAccessory;
NSString* bundleID = app.bundleIdentifier;
BOOL isBGMApp = bundleID && [@kBGMAppBundleID isEqualToString:BGMNN(bundleID)];
return !isHidden && !isBGMApp;
}
- (void) insertMenuItemForApp:(NSRunningApplication*)app - (void) insertMenuItemForApp:(NSRunningApplication*)app
appVolumesOnDevice:(const CACFArray&)appVols { initialVolume:(int)volume
initialPan:(int)pan {
NSMenuItem* appVolItem = [self createBlankAppVolumeMenuItem]; NSMenuItem* appVolItem = [self createBlankAppVolumeMenuItem];
// Look through the menu item's subviews for the ones we want to set up // Look through the menu item's subviews for the ones we want to set up
@ -148,7 +107,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
appVolItem.representedObject = app; appVolItem.representedObject = app;
// Set the slider to the volume for this app if we got one from the driver // Set the slider to the volume for this app if we got one from the driver
[self setVolumeOfMenuItem:appVolItem fromAppVolumes:appVols]; [self setVolumeOfMenuItem:appVolItem relativeVolume:volume panPosition:pan];
// NSMenuItem didn't implement NSAccessibility before OS X SDK 10.12. // NSMenuItem didn't implement NSAccessibility before OS X SDK 10.12.
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 // MAC_OS_X_VERSION_10_12 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 // MAC_OS_X_VERSION_10_12
@ -180,6 +139,19 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
return menuItem; return menuItem;
} }
- (void) setVolumeOfMenuItem:(NSMenuItem*)menuItem relativeVolume:(int)volume panPosition:(int)pan {
// Update the sliders.
for (NSView* subview in menuItem.view.subviews) {
if (volume != -1 && [subview isKindOfClass:[BGMAVM_VolumeSlider class]]) {
[(BGMAVM_VolumeSlider*)subview setRelativeVolume:volume];
}
if (pan != -1 && [subview isKindOfClass:[BGMAVM_PanSlider class]]) {
[(BGMAVM_PanSlider*)subview setPanPosition:pan];
}
}
}
- (NSInteger) firstMenuItemIndex { - (NSInteger) firstMenuItemIndex {
return [self lastMenuItemIndex] - numMenuItems + 1; return [self lastMenuItemIndex] - numMenuItems + 1;
} }
@ -188,81 +160,34 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
return [bgmMenu indexOfItemWithTag:kSeparatorBelowVolumesMenuItemTag] - 1; return [bgmMenu indexOfItemWithTag:kSeparatorBelowVolumesMenuItemTag] - 1;
} }
- (void) removeMenuItemsForApps:(NSArray<NSRunningApplication*>*)apps { - (void) removeMenuItemForApp:(NSRunningApplication*)app {
NSAssert([NSThread isMainThread], @"removeMenuItemsForApps is not thread safe");
// Subtract two extra positions to skip the More Apps menu and the spacer menu item above it. // Subtract two extra positions to skip the More Apps menu and the spacer menu item above it.
NSInteger lastAppVolumeMenuItemIndex = [self lastMenuItemIndex] - 2; NSInteger lastAppVolumeMenuItemIndex = [self lastMenuItemIndex] - 2;
// Check each app volume menu item, removing the items that control one of the given apps // Check each app volume menu item and remove the item that controls the given app.
for (NSRunningApplication* appToBeRemoved in apps) {
BOOL didRemoveItem = NO;
// Look through the main menu. // Look through the main menu.
for (NSInteger i = [self firstMenuItemIndex]; i <= lastAppVolumeMenuItemIndex; i++) { for (NSInteger i = [self firstMenuItemIndex]; i <= lastAppVolumeMenuItemIndex; i++) {
NSMenuItem* item = [bgmMenu itemAtIndex:i]; NSMenuItem* item = [bgmMenu itemAtIndex:i];
NSRunningApplication* itemApp = item.representedObject; NSRunningApplication* itemApp = item.representedObject;
BGMAssert(itemApp, "!itemApp for %s", item.title.UTF8String); BGMAssert(itemApp, "!itemApp for %s", item.title.UTF8String);
if ([itemApp isEqual:appToBeRemoved]) { if ([itemApp isEqual:app]) {
[bgmMenu removeItem:item]; [bgmMenu removeItem:item];
numMenuItems--; numMenuItems--;
return;
didRemoveItem = YES;
break;
}
}
// Look through the More Apps menu.
if (!didRemoveItem) {
for (NSInteger i = 0; i < [moreAppsMenu numberOfItems]; i++) {
NSMenuItem* item = [moreAppsMenu itemAtIndex:i];
NSRunningApplication* itemApp = item.representedObject;
BGMAssert(itemApp, "!itemApp for %s", item.title.UTF8String);
if ([itemApp isEqual:appToBeRemoved]) {
[moreAppsMenu removeItem:item];
break;
}
}
} }
} }
}
- (void) setVolumeOfMenuItem:(NSMenuItem*)menuItem fromAppVolumes:(const CACFArray&)appVolumes { // Look through the More Apps menu.
// Set menuItem's volume slider to the volume of the app in appVolumes that menuItem represents for (NSInteger i = 0; i < [moreAppsMenu numberOfItems]; i++) {
// Leaves menuItem unchanged if it doesn't match any of the apps in appVolumes NSMenuItem* item = [moreAppsMenu itemAtIndex:i];
NSRunningApplication* representedApp = menuItem.representedObject; NSRunningApplication* itemApp = item.representedObject;
BGMAssert(itemApp, "!itemApp for %s", item.title.UTF8String);
for (UInt32 i = 0; i < appVolumes.GetNumberItems(); i++) {
CACFDictionary appVolume(false); if ([itemApp isEqual:app]) {
appVolumes.GetCACFDictionary(i, appVolume); [moreAppsMenu removeItem:item];
return;
// 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);
CFTypeRef panPosition;
appVolume.GetCFType(CFSTR(kBGMAppVolumesKey_PanPosition), panPosition);
// Update the slider
for (NSView* subview in menuItem.view.subviews) {
if ([subview respondsToSelector:@selector(setRelativeVolume:)]) {
[subview performSelector:@selector(setRelativeVolume:) withObject:(__bridge NSNumber*)relativeVolume];
}
if ([subview respondsToSelector:@selector(setPanPosition:)]) {
[subview performSelector:@selector(setPanPosition:) withObject:(__bridge NSNumber*)panPosition];
}
}
} }
} }
} }
@ -281,8 +206,9 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
#if DEBUG #if DEBUG
const char* appName = [((NSRunningApplication*)menuItem.representedObject).localizedName UTF8String]; const char* appName = [((NSRunningApplication*)menuItem.representedObject).localizedName UTF8String];
#endif #endif
auto nearEnough = [](CGFloat x, CGFloat y) { // Shouldn't be necessary, but just in case. // Using this function (instead of just ==) shouldn't be necessary, but just in case.
BOOL(^nearEnough)(CGFloat x, CGFloat y) = ^BOOL(CGFloat x, CGFloat y) {
return fabs(x - y) < 0.01; // We don't need much precision. return fabs(x - y) < 0.01; // We don't need much precision.
}; };
@ -293,7 +219,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
BGMAssert(nearEnough(height, appVolumeViewFullHeight), "Extra controls were already hidden"); BGMAssert(nearEnough(height, appVolumeViewFullHeight), "Extra controls were already hidden");
// Make the menu item shorter to hide the extra controls. Keep the width unchanged. // Make the menu item shorter to hide the extra controls. Keep the width unchanged.
menuItem.view.frameSize = { width, kAppVolumeViewInitialHeight }; menuItem.view.frameSize = NSMakeSize(width, kAppVolumeViewInitialHeight);
// Turn the button upside down so the arrowhead points down. // Turn the button upside down so the arrowhead points down.
button.frameCenterRotation = 180.0; button.frameCenterRotation = 180.0;
// Move the button up slightly so it aligns with the volume slider. // Move the button up slightly so it aligns with the volume slider.
@ -315,7 +241,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
BGMAssert(nearEnough(height, kAppVolumeViewInitialHeight), "Extra controls were already shown"); BGMAssert(nearEnough(height, kAppVolumeViewInitialHeight), "Extra controls were already shown");
// Make the menu item taller to show the extra controls. Keep the width unchanged. // Make the menu item taller to show the extra controls. Keep the width unchanged.
menuItem.view.frameSize = { width, appVolumeViewFullHeight }; menuItem.view.frameSize = NSMakeSize(width, appVolumeViewFullHeight);
// Turn the button rightside up so the arrowhead points up. // Turn the button rightside up so the arrowhead points up.
button.frameCenterRotation = 0.0; button.frameCenterRotation = 0.0;
// Move the button down slightly, back to it's original position. // Move the button down slightly, back to it's original position.
@ -328,38 +254,16 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
} }
} }
#pragma mark KVO - (void) removeAllAppVolumeMenuItems {
// Remove all of the menu items this class adds to the menu except for the last two, which are
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context // the More Apps menu item and the invisible spacer above it.
{ while (numMenuItems > 2) {
#pragma unused (object, context) [bgmMenu removeItemAtIndex:[self firstMenuItemIndex]];
numMenuItems--;
// KVO callback for the apps currently running on the system. Adds/removes the associated menu items.
if ([keyPath isEqualToString:@"runningApplications"]) {
NSArray<NSRunningApplication*>* newApps = change[NSKeyValueChangeNewKey];
NSArray<NSRunningApplication*>* oldApps = change[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;
}
} }
// The More Apps menu only contains app volume menu items, so we can just remove everything.
[moreAppsMenu removeAllItems];
} }
@end @end
@ -429,7 +333,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
@implementation BGMAVM_VolumeSlider { @implementation BGMAVM_VolumeSlider {
// Will be set to -1 for apps without a pid // Will be set to -1 for apps without a pid
pid_t appProcessID; pid_t appProcessID;
NSString* appBundleID; NSString* __nullable appBundleID;
BGMAppVolumes* context; BGMAppVolumes* context;
} }
@ -459,14 +363,14 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
// changes how the slider looks. // changes how the slider looks.
- (void) snap { - (void) snap {
// Snap to the 50% point. // Snap to the 50% point.
float midPoint = static_cast<float>((self.maxValue + self.minValue) / 2); float midPoint = (float)((self.maxValue + self.minValue) / 2);
if (self.floatValue > (midPoint - kSlidersSnapWithin) && self.floatValue < (midPoint + kSlidersSnapWithin)) { if (self.floatValue > (midPoint - kSlidersSnapWithin) && self.floatValue < (midPoint + kSlidersSnapWithin)) {
self.floatValue = midPoint; self.floatValue = midPoint;
} }
} }
- (void) setRelativeVolume:(NSNumber*)relativeVolume { - (void) setRelativeVolume:(int)relativeVolume {
self.intValue = relativeVolume.intValue; self.intValue = relativeVolume;
[self snap]; [self snap];
} }
@ -479,9 +383,9 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
// The values from our sliders are in // The values from our sliders are in
// [kAppRelativeVolumeMinRawValue, kAppRelativeVolumeMaxRawValue] already. // [kAppRelativeVolumeMinRawValue, kAppRelativeVolumeMaxRawValue] already.
context.audioDevices.bgmDevice.SetAppVolume(self.intValue, [context.audioDevices setVolume:self.intValue
appProcessID, forAppWithProcessID:appProcessID
(__bridge_retained CFStringRef)appBundleID); bundleID:appBundleID];
} }
@end @end
@ -489,7 +393,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
@implementation BGMAVM_PanSlider { @implementation BGMAVM_PanSlider {
// Will be set to -1 for apps without a pid // Will be set to -1 for apps without a pid
pid_t appProcessID; pid_t appProcessID;
NSString* appBundleID; NSString* __nullable appBundleID;
BGMAppVolumes* context; BGMAppVolumes* context;
} }
@ -515,8 +419,8 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
} }
} }
- (void) setPanPosition:(NSNumber *)panPosition { - (void) setPanPosition:(int)panPosition {
self.intValue = panPosition.intValue; self.intValue = panPosition;
} }
- (void) appPanPositionChanged { - (void) appPanPositionChanged {
@ -525,9 +429,9 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
DebugMsg("BGMAppVolumes::appPanPositionChanged: App pan position for %s changed to %d", appBundleID.UTF8String, self.intValue); DebugMsg("BGMAppVolumes::appPanPositionChanged: App pan position for %s changed to %d", appBundleID.UTF8String, self.intValue);
// The values from our sliders are in [kAppPanLeftRawValue, kAppPanRightRawValue] already. // The values from our sliders are in [kAppPanLeftRawValue, kAppPanRightRawValue] already.
context.audioDevices.bgmDevice.SetAppPanPosition(self.intValue, [context.audioDevices setPanPosition:self.intValue
appProcessID, forAppWithProcessID:appProcessID
(__bridge_retained CFStringRef)appBundleID); bundleID:appBundleID];
} }
@end @end

View file

@ -0,0 +1,41 @@
// 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/>.
//
// BGMAppVolumesController.h
// BGMApp
//
// Copyright © 2017 Kyle Neideck
//
// Local Includes
#import "BGMAudioDeviceManager.h"
// System Includes
#import <Cocoa/Cocoa.h>
#pragma clang assume_nonnull begin
@interface BGMAppVolumesController : NSObject
- (id) initWithMenu:(NSMenu*)menu
appVolumeView:(NSView*)view
audioDevices:(BGMAudioDeviceManager*)audioDevices;
@end
#pragma clang assume_nonnull end

View file

@ -0,0 +1,195 @@
// 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/>.
//
// BGMAppVolumesController.mm
// BGMApp
//
// Copyright © 2017 Kyle Neideck
// Copyright © 2017 Andrew Tonner
//
// Self Include
#import "BGMAppVolumesController.h"
// Local Includes
#import "BGM_Types.h"
#import "BGM_Utils.h"
#import "BGMAppVolumes.h"
// PublicUtility Includes
#import "CACFArray.h"
#import "CACFDictionary.h"
#import "CACFString.h"
#pragma clang assume_nonnull begin
typedef struct BGMAppVolumeAndPan {
int volume;
int pan;
} BGMAppVolumeAndPan;
@implementation BGMAppVolumesController {
// The App Volumes UI.
BGMAppVolumes* appVolumes;
BGMAudioDeviceManager* audioDevices;
}
- (id) initWithMenu:(NSMenu*)menu
appVolumeView:(NSView*)view
audioDevices:(BGMAudioDeviceManager*)devices {
if ((self = [super init])) {
audioDevices = devices;
appVolumes = [[BGMAppVolumes alloc] initWithMenu:menu
appVolumeView:view
audioDevices:devices];
// Create the menu items for controlling app volumes.
NSArray<NSRunningApplication*>* apps = [[NSWorkspace sharedWorkspace] runningApplications];
[self insertMenuItemsForApps:apps];
// Register for notifications when the user opens or closes apps, so we can update the menu.
auto opts = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[[NSWorkspace sharedWorkspace] addObserver:self
forKeyPath:@"runningApplications"
options:opts
context:nil];
}
return self;
}
- (void) dealloc {
[[NSWorkspace sharedWorkspace] removeObserver:self
forKeyPath:@"runningApplications"
context:nil];
}
// Adds a volume control menu item for each given app.
- (void) insertMenuItemsForApps:(NSArray<NSRunningApplication*>*)apps {
NSAssert([NSThread isMainThread], @"insertMenuItemsForApps is not thread safe");
// TODO: Handle the C++ exceptions this method can throw. They can cause crashes because this
// method is called in a KVO handler.
// Get the app volumes currently set on the device
CACFArray volumesFromBGMDevice([audioDevices bgmDevice].GetAppVolumes(), false);
for (NSRunningApplication* app in apps) {
if ([self shouldBeIncludedInMenu:app]) {
BGMAppVolumeAndPan initial = [self getVolumeAndPanForApp:app
fromVolumes:volumesFromBGMDevice];
[appVolumes insertMenuItemForApp:app
initialVolume:initial.volume
initialPan:initial.pan];
}
}
}
- (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication*)app
fromVolumes:(const CACFArray&)volumes {
BGMAppVolumeAndPan volumeAndPan = {
.volume = -1,
.pan = -1
};
for (UInt32 i = 0; i < volumes.GetNumberItems(); i++) {
CACFDictionary appVolume(false);
volumes.GetCACFDictionary(i, appVolume);
// Match the app to the volume/pan 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 ((app.processIdentifier == pid) ||
[app.bundleIdentifier isEqualToString:(__bridge NSString*)bundleID.GetCFString()]) {
// Found a match, so read the volume and pan.
appVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume), volumeAndPan.volume);
appVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_PanPosition), volumeAndPan.pan);
break;
}
}
return volumeAndPan;
}
- (BOOL) shouldBeIncludedInMenu:(NSRunningApplication*)app {
// Ignore hidden apps and Background Music itself.
// TODO: Would it be better to only show apps that are registered as HAL clients?
BOOL isHidden = app.activationPolicy != NSApplicationActivationPolicyRegular &&
app.activationPolicy != NSApplicationActivationPolicyAccessory;
NSString* bundleID = app.bundleIdentifier;
BOOL isBGMApp = bundleID && [@kBGMAppBundleID isEqualToString:BGMNN(bundleID)];
return !isHidden && !isBGMApp;
}
- (void) removeMenuItemsForApps:(NSArray<NSRunningApplication*>*)apps {
NSAssert([NSThread isMainThread], @"removeMenuItemsForApps is not thread safe");
for (NSRunningApplication* app in apps) {
[appVolumes removeMenuItemForApp:app];
}
}
#pragma mark KVO
- (void) observeValueForKeyPath:(NSString* __nullable)keyPath
ofObject:(id __nullable)object
change:(NSDictionary* __nullable)change
context:(void* __nullable)context
{
#pragma unused (object, context)
// KVO callback for the apps currently running on the system. Adds/removes the associated menu
// items.
if (keyPath && change && [keyPath isEqualToString:@"runningApplications"]) {
NSArray<NSRunningApplication*>* newApps = change[NSKeyValueChangeNewKey];
NSArray<NSRunningApplication*>* oldApps = change[NSKeyValueChangeOldKey];
int changeKind = [change[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:
[appVolumes removeAllAppVolumeMenuItems];
[self insertMenuItemsForApps:newApps];
break;
}
}
}
@end
#pragma clang assume_nonnull end

View file

@ -26,7 +26,7 @@
#if defined(__cplusplus) #if defined(__cplusplus)
// Local Includes // Local Includes
#include "BGMBackgroundMusicDevice.h" #import "BGMBackgroundMusicDevice.h"
// PublicUtility Includes // PublicUtility Includes
#import "CAHALAudioDevice.h" #import "CAHALAudioDevice.h"
@ -40,9 +40,9 @@
#pragma clang assume_nonnull begin #pragma clang assume_nonnull begin
const int kBGMErrorCode_BGMDeviceNotFound = 1; static const int kBGMErrorCode_BGMDeviceNotFound = 1;
const int kBGMErrorCode_OutputDeviceNotFound = 2; static const int kBGMErrorCode_OutputDeviceNotFound = 2;
const int kBGMErrorCode_ReturningEarly = 3; static const int kBGMErrorCode_ReturningEarly = 3;
@interface BGMAudioDeviceManager : NSObject @interface BGMAudioDeviceManager : NSObject
@ -62,6 +62,13 @@ const int kBGMErrorCode_ReturningEarly = 3;
- (CAHALAudioDevice) outputDevice; - (CAHALAudioDevice) outputDevice;
#endif #endif
- (void) setVolume:(SInt32)volume
forAppWithProcessID:(pid_t)processID
bundleID:(NSString* __nullable)bundleID;
- (void) setPanPosition:(SInt32)pan
forAppWithProcessID:(pid_t)processID
bundleID:(NSString* __nullable)bundleID;
- (BOOL) isOutputDevice:(AudioObjectID)deviceID; - (BOOL) isOutputDevice:(AudioObjectID)deviceID;
- (BOOL) isOutputDataSource:(UInt32)dataSourceID; - (BOOL) isOutputDataSource:(UInt32)dataSourceID;

View file

@ -36,6 +36,8 @@
#include "CAAutoDisposer.h" #include "CAAutoDisposer.h"
#pragma clang assume_nonnull begin
@implementation BGMAudioDeviceManager { @implementation BGMAudioDeviceManager {
BGMBackgroundMusicDevice bgmDevice; BGMBackgroundMusicDevice bgmDevice;
BGMAudioDevice outputDevice; BGMAudioDevice outputDevice;
@ -52,7 +54,7 @@
#pragma mark Construction/Destruction #pragma mark Construction/Destruction
- (instancetype) initWithError:(NSError**)error { - (instancetype) initWithError:(NSError** __nullable)error {
if ((self = [super init])) { if ((self = [super init])) {
stateLock = [NSRecursiveLock new]; stateLock = [NSRecursiveLock new];
bgmXPCHelperConnection = nil; bgmXPCHelperConnection = nil;
@ -238,6 +240,18 @@
return outputDevice; return outputDevice;
} }
- (void) setVolume:(SInt32)volume
forAppWithProcessID:(pid_t)processID
bundleID:(NSString* __nullable)bundleID {
bgmDevice.SetAppVolume(volume, processID, (__bridge_retained CFStringRef)bundleID);
}
- (void) setPanPosition:(SInt32)pan
forAppWithProcessID:(pid_t)processID
bundleID:(NSString* __nullable)bundleID {
bgmDevice.SetAppPanPosition(pan, processID, (__bridge_retained CFStringRef)bundleID);
}
- (BOOL) isOutputDevice:(AudioObjectID)deviceID { - (BOOL) isOutputDevice:(AudioObjectID)deviceID {
@try { @try {
[stateLock lock]; [stateLock lock];
@ -503,3 +517,6 @@
@end @end
#pragma clang assume_nonnull end

View file

@ -149,7 +149,7 @@ void BGMBackgroundMusicDevice::SetAppVolume(SInt32 inVolume,
void BGMBackgroundMusicDevice::SetAppPanPosition(SInt32 inPanPosition, void BGMBackgroundMusicDevice::SetAppPanPosition(SInt32 inPanPosition,
pid_t inAppProcessID, pid_t inAppProcessID,
CFStringRef inAppBundleID) CFStringRef __nullable inAppBundleID)
{ {
BGMAssert((kAppPanLeftRawValue <= inPanPosition) && (inPanPosition <= kAppPanRightRawValue), BGMAssert((kAppPanLeftRawValue <= inPanPosition) && (inPanPosition <= kAppPanRightRawValue),
"BGMBackgroundMusicDevice::SetAppPanPosition: Pan position out of bounds"); "BGMBackgroundMusicDevice::SetAppPanPosition: Pan position out of bounds");
@ -167,7 +167,7 @@ void BGMBackgroundMusicDevice::SetAppPanPosition(SInt32 inPanPosition,
void BGMBackgroundMusicDevice::SendAppVolumeOrPanToBGMDevice(SInt32 inNewValue, void BGMBackgroundMusicDevice::SendAppVolumeOrPanToBGMDevice(SInt32 inNewValue,
CFStringRef inVolumeTypeKey, CFStringRef inVolumeTypeKey,
pid_t inAppProcessID, pid_t inAppProcessID,
CFStringRef inAppBundleID) CFStringRef __nullable inAppBundleID)
{ {
CACFArray appVolumeChanges(true); CACFArray appVolumeChanges(true);

View file

@ -118,13 +118,13 @@ public:
*/ */
void SetAppPanPosition(SInt32 inPanPosition, void SetAppPanPosition(SInt32 inPanPosition,
pid_t inAppProcessID, pid_t inAppProcessID,
CFStringRef inAppBundleID); CFStringRef __nullable inAppBundleID);
private: private:
void SendAppVolumeOrPanToBGMDevice(SInt32 inNewValue, void SendAppVolumeOrPanToBGMDevice(SInt32 inNewValue,
CFStringRef inVolumeTypeKey, CFStringRef inVolumeTypeKey,
pid_t inAppProcessID, pid_t inAppProcessID,
CFStringRef inAppBundleID); CFStringRef __nullable inAppBundleID);
static std::vector<CACFString> static std::vector<CACFString>
ResponsibleBundleIDsOf(CACFString inParentBundleID); ResponsibleBundleIDsOf(CACFString inParentBundleID);

View file

@ -31,8 +31,6 @@
#import "BGMScriptingBridge.h" #import "BGMScriptingBridge.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"

View file

@ -30,8 +30,6 @@
#import "BGMScriptingBridge.h" #import "BGMScriptingBridge.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"

View file

@ -24,8 +24,6 @@
#import "BGMMusicPlayer.h" #import "BGMMusicPlayer.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"

View file

@ -24,8 +24,6 @@
#import "BGMScriptingBridge.h" #import "BGMScriptingBridge.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"

View file

@ -33,8 +33,6 @@
#import "BGMScriptingBridge.h" #import "BGMScriptingBridge.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"

View file

@ -31,8 +31,6 @@
#import "BGMScriptingBridge.h" #import "BGMScriptingBridge.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"

View file

@ -30,8 +30,6 @@
#import "BGMScriptingBridge.h" #import "BGMScriptingBridge.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"

View file

@ -30,8 +30,6 @@
#import "BGMScriptingBridge.h" #import "BGMScriptingBridge.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"

View file

@ -27,8 +27,6 @@
#import "BGM_Types.h" #import "BGM_Types.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"

View file

@ -27,8 +27,6 @@
#import "BGMXPCHelperService.h" #import "BGMXPCHelperService.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"
// System Includes // System Includes

View file

@ -48,8 +48,6 @@
#import "BGMXPCListenerDelegate.h" #import "BGMXPCListenerDelegate.h"
// PublicUtility Includes // PublicUtility Includes
#undef CoreAudio_ThreadStampMessages
#define CoreAudio_ThreadStampMessages 0 // Requires C++
#include "CADebugMacros.h" #include "CADebugMacros.h"
// System Includes // System Includes

View file

@ -57,7 +57,7 @@
- (void) testStartOutputDeviceWithoutBGMAppConnected { - (void) testStartOutputDeviceWithoutBGMAppConnected {
dispatch_semaphore_t replySemaphore = dispatch_semaphore_create(0); dispatch_semaphore_t replySemaphore = dispatch_semaphore_create(0);
// Unregister BGMXPCHelper's connection to BGMApp in case BGMApp didn't shutdown cleanly the last time it ran. // Unregister BGMXPCHelper's connection to BGMApp in case BGMApp didn't shutdown cleanly the last time it ran.
[[connection remoteObjectProxy] unregisterAsBGMApp]; [[connection remoteObjectProxy] unregisterAsBGMApp];
@ -68,8 +68,9 @@
dispatch_semaphore_signal(replySemaphore); dispatch_semaphore_signal(replySemaphore);
} forUISoundsDevice:NO]; } forUISoundsDevice:NO];
if (0 != dispatch_semaphore_wait(replySemaphore, dispatch_time(DISPATCH_TIME_NOW, kStartIOTimeoutNsec))) { // Very long timeout to make it less likely to fail on Travis CI when there's high contention.
if (0 != dispatch_semaphore_wait(replySemaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * 60 * NSEC_PER_SEC))) {
XCTFail(@"Timed out waiting for BGMXPCHelper"); XCTFail(@"Timed out waiting for BGMXPCHelper");
} }
} }

View file

@ -61,9 +61,18 @@
// CADebugger // CADebugger
//============================================================================= //=============================================================================
// BGM edit: Added extern "C" so CADebugger (and headers that include it) can be used in Obj-C.
#ifdef __cplusplus
extern "C" {
#endif
#if TARGET_API_MAC_OSX #if TARGET_API_MAC_OSX
extern bool CAIsDebuggerAttached(void); extern bool CAIsDebuggerAttached(void);
#endif #endif
extern void CADebuggerStop(void); extern void CADebuggerStop(void);
#ifdef __cplusplus
}
#endif
#endif #endif

View file

@ -68,7 +68,7 @@ void LogError(const char *fmt, ...)
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
// BGM edit: vprintf leaves args in an undefined state, which can cause a crash in // BGM edit: vprintf leaves args in an undefined state, which can cause a crash in
// vsyslog. Also added CADebuggerStop(). Original code commented out below. // vsyslog. Also added __ASSERT_STOP. Original code commented out below.
//#if DEBUG //#if DEBUG
// vprintf(fmt, args); // vprintf(fmt, args);
//#endif //#endif
@ -83,7 +83,7 @@ void LogError(const char *fmt, ...)
vsyslog(LOG_ERR, fmt, args); vsyslog(LOG_ERR, fmt, args);
#endif #endif
#if DEBUG #if DEBUG
CADebuggerStop(); __ASSERT_STOP;
#endif #endif
// BGM edit end // BGM edit end
va_end(args); va_end(args);
@ -94,7 +94,7 @@ void LogWarning(const char *fmt, ...)
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
// BGM edit: vprintf leaves args in an undefined state, which can cause a crash in // BGM edit: vprintf leaves args in an undefined state, which can cause a crash in
// vsyslog. Also added CADebuggerStop(). Original code commented out below. // vsyslog. Also added __ASSERT_STOP. Original code commented out below.
//#if DEBUG //#if DEBUG
// vprintf(fmt, args); // vprintf(fmt, args);
//#endif //#endif
@ -109,7 +109,7 @@ void LogWarning(const char *fmt, ...)
vsyslog(LOG_WARNING, fmt, args); vsyslog(LOG_WARNING, fmt, args);
#endif #endif
#if DEBUG #if DEBUG
//CADebuggerStop(); // TODO: Add a toggle for this to the project file (under "Preprocessor Macros"). Default to off. //__ASSERT_STOP; // TODO: Add a toggle for this to the project file (under "Preprocessor Macros"). Default to off.
#endif #endif
// BGM edit end // BGM edit end
va_end(args); va_end(args);

View file

@ -131,7 +131,7 @@
#pragma mark C Utility Functions #pragma mark C Utility Functions
dispatch_queue_t BGMGetDispatchQueue_PriorityUserInteractive(); dispatch_queue_t BGMGetDispatchQueue_PriorityUserInteractive(void);
#if defined(__cplusplus) #if defined(__cplusplus)