Compare commits

...

2 Commits

Author SHA1 Message Date
Kyle Neideck 3e528c7de0 Disable warnings in build_and_install.sh. 2017-06-21 08:31:56 +10:00
Kyle Neideck 03135dbc6e WIP: Add BGMDriver property for client PIDs and bundle IDs
The idea is to have BGMApp group all of an app's clients/subprocesses
together (the way Activity Monitor does if you set it to "app processes,
hierarchically") when setting app volumes.

For issue #42. (See that issue for much more detail.)
2016-08-02 01:04:12 +10:00
13 changed files with 257 additions and 25 deletions
+22 -6
View File
@@ -25,7 +25,6 @@
1C1963091BCAF677008A4DF7 /* CAHostTimeBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1963071BCAF677008A4DF7 /* CAHostTimeBase.cpp */; };
1C2336DA1BEAB6E7004C1C4E /* BGMMusicPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C2336D91BEAB6E7004C1C4E /* BGMMusicPlayer.m */; };
1C2336DF1BEAE10C004C1C4E /* BGMSpotify.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C2336DE1BEAE10C004C1C4E /* BGMSpotify.m */; };
1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */; };
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C4699461BD5C0E400F78043 /* BGMiTunes.m */; };
1C46994E1BD7694C00F78043 /* BGMDeviceControlSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */; };
1CB8B33D1BBA75EF000E2DD1 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B33C1BBA75EF000E2DD1 /* AppDelegate.mm */; };
@@ -48,12 +47,15 @@
276972911CB16008007A2F7C /* safe_install_dir.sh in Resources */ = {isa = PBXBuildFile; fileRef = 276972901CB16008007A2F7C /* safe_install_dir.sh */; };
276972921CB1603E007A2F7C /* safe_install_dir.sh in Resources */ = {isa = PBXBuildFile; fileRef = 276972901CB16008007A2F7C /* safe_install_dir.sh */; };
277170161CA24D7C00AB34B4 /* BGMXPCListenerDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 277170151CA24D7C00AB34B4 /* BGMXPCListenerDelegate.m */; };
2783ABB71CE36CDB00F11FD4 /* BGMApps.m in Sources */ = {isa = PBXBuildFile; fileRef = 2783ABB61CE36CDB00F11FD4 /* BGMApps.m */; };
2783ABB81CE36CDB00F11FD4 /* BGMApps.m in Sources */ = {isa = PBXBuildFile; fileRef = 2783ABB61CE36CDB00F11FD4 /* BGMApps.m */; };
278D71E91CABB6FF00899CF9 /* BGMXPCHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 279597401C996E2000A002FB /* BGMXPCHelperTests.m */; };
278D71F61CABBC3B00899CF9 /* BGMXPCHelperTests-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 278D71F51CABBC3B00899CF9 /* BGMXPCHelperTests-Info.plist */; };
2795973B1C982E4E00A002FB /* BGMXPCListener.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2795973A1C982E4E00A002FB /* BGMXPCListener.mm */; };
27D643BE1C9FB84C00737F6E /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 27D643BB1C9FB84C00737F6E /* Info.plist */; };
27D643C01C9FB99200737F6E /* BGMXPCHelperService.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D643BA1C9FB84C00737F6E /* BGMXPCHelperService.m */; };
27D643C11C9FB99200737F6E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 27D643BC1C9FB84C00737F6E /* main.m */; };
27DEE37C1D4F8DA300DBFDF1 /* BGMAppVolumes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27DEE37A1D4F8DA300DBFDF1 /* BGMAppVolumes.mm */; };
27F7D4901D2483B100821C4B /* BGMDecibel.m in Sources */ = {isa = PBXBuildFile; fileRef = 27F7D48F1D2483B100821C4B /* BGMDecibel.m */; };
/* End PBXBuildFile section */
@@ -114,8 +116,6 @@
1C2336DC1BEAB73F004C1C4E /* BGMMusicPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BGMMusicPlayer.h; path = "Music Players/BGMMusicPlayer.h"; sourceTree = "<group>"; };
1C2336DD1BEAE10C004C1C4E /* BGMSpotify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMSpotify.h; path = "Music Players/BGMSpotify.h"; sourceTree = "<group>"; };
1C2336DE1BEAE10C004C1C4E /* BGMSpotify.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMSpotify.m; path = "Music Players/BGMSpotify.m"; sourceTree = "<group>"; };
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMAppVolumes.mm; sourceTree = "<group>"; };
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMAppVolumes.h; sourceTree = "<group>"; };
1C4699461BD5C0E400F78043 /* BGMiTunes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMiTunes.m; path = "Music Players/BGMiTunes.m"; sourceTree = "<group>"; };
1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMDeviceControlSync.cpp; sourceTree = "<group>"; };
1C46994D1BD7694C00F78043 /* BGMDeviceControlSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMDeviceControlSync.h; sourceTree = "<group>"; };
@@ -161,6 +161,8 @@
2771700F1CA0C83B00AB34B4 /* BGM_Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGM_Utils.h; path = ../SharedSource/BGM_Utils.h; sourceTree = "<group>"; };
277170141CA24D7C00AB34B4 /* BGMXPCListenerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMXPCListenerDelegate.h; path = BGMXPCHelper/BGMXPCListenerDelegate.h; sourceTree = SOURCE_ROOT; };
277170151CA24D7C00AB34B4 /* BGMXPCListenerDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMXPCListenerDelegate.m; path = BGMXPCHelper/BGMXPCListenerDelegate.m; sourceTree = SOURCE_ROOT; };
2783ABB51CE36CDB00F11FD4 /* BGMApps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMApps.h; path = "App Volumes/BGMApps.h"; sourceTree = "<group>"; };
2783ABB61CE36CDB00F11FD4 /* BGMApps.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMApps.m; path = "App Volumes/BGMApps.m"; sourceTree = "<group>"; };
278D71F11CABB6FF00899CF9 /* BGMXPCHelperTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BGMXPCHelperTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
278D71F51CABBC3B00899CF9 /* BGMXPCHelperTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "BGMXPCHelperTests-Info.plist"; sourceTree = "<group>"; };
2795970D1C91589B00A002FB /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
@@ -174,6 +176,8 @@
27D643BB1C9FB84C00737F6E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = BGMXPCHelper/Info.plist; sourceTree = SOURCE_ROOT; };
27D643BC1C9FB84C00737F6E /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = BGMXPCHelper/main.m; sourceTree = SOURCE_ROOT; };
27D643C41C9FBE5600737F6E /* BGM_TestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGM_TestUtils.h; path = ../SharedSource/BGM_TestUtils.h; sourceTree = "<group>"; };
27DEE37A1D4F8DA300DBFDF1 /* BGMAppVolumes.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMAppVolumes.mm; path = "App Volumes/BGMAppVolumes.mm"; sourceTree = "<group>"; };
27DEE37B1D4F8DA300DBFDF1 /* BGMAppVolumes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMAppVolumes.h; path = "App Volumes/BGMAppVolumes.h"; sourceTree = "<group>"; };
27F7D48E1D2483B100821C4B /* BGMDecibel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMDecibel.h; path = "Music Players/BGMDecibel.h"; sourceTree = "<group>"; };
27F7D48F1D2483B100821C4B /* BGMDecibel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMDecibel.m; path = "Music Players/BGMDecibel.m"; sourceTree = "<group>"; };
27F7D4911D2484A300821C4B /* Decibel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Decibel.h; path = "Music Players/Decibel.h"; sourceTree = "<group>"; };
@@ -331,8 +335,7 @@
children = (
1CB8B33B1BBA75EF000E2DD1 /* AppDelegate.h */,
1CB8B33C1BBA75EF000E2DD1 /* AppDelegate.mm */,
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */,
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.mm */,
2783ABB01CE35C1D00F11FD4 /* App Volumes */,
1CED616A1C316E1A002CAFCF /* BGMAudioDeviceManager.h */,
1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */,
1C1465B91BCC49D1003AEFE6 /* BGMAutoPauseMusic.h */,
@@ -427,6 +430,17 @@
name = "Supporting Files";
sourceTree = "<group>";
};
2783ABB01CE35C1D00F11FD4 /* App Volumes */ = {
isa = PBXGroup;
children = (
27DEE37B1D4F8DA300DBFDF1 /* BGMAppVolumes.h */,
27DEE37A1D4F8DA300DBFDF1 /* BGMAppVolumes.mm */,
2783ABB51CE36CDB00F11FD4 /* BGMApps.h */,
2783ABB61CE36CDB00F11FD4 /* BGMApps.m */,
);
name = "App Volumes";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -610,14 +624,15 @@
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */,
1C1962E41BC94E15008A4DF7 /* CARingBuffer.cpp in Sources */,
273F10DF1CC3D0B900C1C6DA /* BGMVox.m in Sources */,
27DEE37C1D4F8DA300DBFDF1 /* BGMAppVolumes.mm in Sources */,
1CC1DF811BE5068A00FB8FE4 /* CACFArray.cpp in Sources */,
2783ABB71CE36CDB00F11FD4 /* BGMApps.m in Sources */,
1C0BD0A81BF1B029004F4CF5 /* BGMPreferencesMenu.mm in Sources */,
1C1962F41BCABFC5008A4DF7 /* CAHALAudioObject.cpp in Sources */,
27F7D4901D2483B100821C4B /* BGMDecibel.m in Sources */,
1C2336DA1BEAB6E7004C1C4E /* BGMMusicPlayer.m in Sources */,
1C1962F61BCABFC5008A4DF7 /* CAHALAudioSystemObject.cpp in Sources */,
1CC1DF821BE5068A00FB8FE4 /* CACFDictionary.cpp in Sources */,
1C3DB4891BE0885A00EC8160 /* BGMAppVolumes.mm in Sources */,
1C0BD0A51BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm in Sources */,
1C1963061BCAF468008A4DF7 /* CAMutex.cpp in Sources */,
1CE7064C1BF1EC0600BFC06D /* BGMOutputDevicePrefs.mm in Sources */,
@@ -646,6 +661,7 @@
buildActionMask = 2147483647;
files = (
1CB8B3501BBA75F0000E2DD1 /* BGMAppTests.m in Sources */,
2783ABB81CE36CDB00F11FD4 /* BGMApps.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -27,7 +27,7 @@
#import <Cocoa/Cocoa.h>
@interface BGMAppVolumes : NSObject
@interface BGMAppVolumes : NSObject // todo: rename to BGMAppVolumesMenu
- (id) initWithMenu:(NSMenu*)menu appVolumeView:(NSView*)view audioDevices:(BGMAudioDeviceManager*)audioDevices;
@@ -14,7 +14,7 @@
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMAppVolumes.m
// BGMAppVolumes.mm
// BGMApp
//
// Copyright © 2016 Kyle Neideck
@@ -25,6 +25,7 @@
// BGM Includes
#include "BGM_Types.h"
#import "BGMApps.h"
// PublicUtility Includes
#include "CACFDictionary.h"
@@ -57,6 +58,10 @@ static float const kSlidersSnapWithin = 5;
forKeyPath:@"runningApplications"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:nil];
// todo: delete this
NSArray* clients = (__bridge NSArray*)[audioDevices bgmDevice].GetPropertyData_CFType(kBGMClientsAddress);
BGMApps* __unused apps = [[BGMApps alloc] initWithClients:clients];
}
return self;
@@ -90,9 +95,6 @@ static float const kSlidersSnapWithin = 5;
// TODO: Would it be better to only show apps that are registered as HAL clients?
if ([app activationPolicy] != NSApplicationActivationPolicyRegular) continue;
// Don't show Finder
if ([[app bundleIdentifier] isEqualTo:@"com.apple.finder"]) continue;
#ifndef NS_BLOCK_ASSERTIONS // If assertions are enabled
// Count how many apps we should add menu items for so we can check it at the end of the method
numApps++;
@@ -103,7 +105,7 @@ static float const kSlidersSnapWithin = 5;
// Look through the menu item's subviews for the ones we want to set up
for (NSView* subview in [[appVolItem view] subviews]) {
if ([subview conformsToProtocol:@protocol(BGMAppVolumeSubview)]) {
[subview performSelector:@selector(setUpWithApp:context:) withObject:app withObject:self];
[(NSView<BGMAppVolumeSubview>*)subview setUpWithApp:app context:self];
}
}
@@ -150,6 +152,7 @@ static float const kSlidersSnapWithin = 5;
}
}
// todo: split this method up so it can have a cleaner (less coupled) signature?
- (void) setVolumeOfMenuItem:(NSMenuItem*)menuItem fromAppVolumes:(CACFArray&)appVolumes {
// Set menuItem's volume slider to the volume of the app in appVolumes that menuItem represents
// Leaves menuItem unchanged if it doesn't match any of the apps in appVolumes
@@ -214,6 +217,7 @@ static float const kSlidersSnapWithin = 5;
}
}
// todo: move this into BGMApps. (it's not ui code.) should be able to remove the audioDevices instance var from this class then
- (void) sendVolumeChangeToBGMDevice:(SInt32)newVolume appProcessID:(pid_t)appProcessID appBundleID:(NSString*)appBundleID {
CACFDictionary appVolumeChange(true);
appVolumeChange.AddSInt32(CFSTR(kBGMAppVolumesKey_ProcessID), appProcessID);
+51
View File
@@ -0,0 +1,51 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMApps.h
// BGMApp
//
// Copyright © 2016 Kyle Neideck
//
// System Includes
#import <Foundation/Foundation.h>
@interface BGMApp : NSObject
- (id) initWithName:(NSString*)name;
@property NSString* name;
@property NSImage* icon;
@property Float32 volume;
//@property ? clients;
@property pid_t PID;
@property NSString* bundleID;
@property NSArray<BGMApp*>* children;
@end
@interface BGMApps : NSObject
// todo: comment (takes the data from kAudioDeviceCustomPropertyClients)
// todo: seems like this type could be stricter/better
- (id) initWithClients:(NSArray<NSDictionary*>*)clients;
@property (readonly) NSArray<BGMApp*>* apps;
@end
+40
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/>.
//
// BGMApps.m
// BGMApp
//
// Copyright © 2016 Kyle Neideck
// todo: need to move any copyright notices around?
//
// Self Include
#import "BGMApps.h"
@implementation BGMApps
- (id) initWithClients:(NSArray<NSDictionary*>*)clients {
if ((self = [super init])) {
NSLog(@"%@", clients);
// todo
}
return self;
}
@end
+1 -1
View File
@@ -107,7 +107,7 @@ static BGMMusicPlayer* sSelectedMusicPlayer;
return self;
}
- (id) eventDidFail:(const AppleEvent*)event withError:(NSError*)error {
- (id __nullable) eventDidFail:(const AppleEvent*)event withError:(NSError*)error {
// SBApplicationDelegate method. So far, this just logs the error.
#if DEBUG
+26 -5
View File
@@ -290,6 +290,7 @@ bool BGM_Device::Device_HasProperty(AudioObjectID inObjectID, pid_t inClientPID,
case kAudioDeviceCustomPropertyMusicPlayerBundleID:
case kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp:
case kAudioDeviceCustomPropertyAppVolumes:
case kAudioDeviceCustomPropertyClients:
theAnswer = true;
break;
@@ -342,6 +343,7 @@ bool BGM_Device::Device_IsPropertySettable(AudioObjectID inObjectID, pid_t inCli
case kAudioObjectPropertyCustomPropertyInfoList:
case kAudioDeviceCustomPropertyDeviceAudibleState:
case kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp:
case kAudioDeviceCustomPropertyClients:
theAnswer = false;
break;
@@ -487,7 +489,7 @@ UInt32 BGM_Device::Device_GetPropertyDataSize(AudioObjectID inObjectID, pid_t in
break;
case kAudioObjectPropertyCustomPropertyInfoList:
theAnswer = sizeof(AudioServerPlugInCustomPropertyInfo) * 5;
theAnswer = sizeof(AudioServerPlugInCustomPropertyInfo) * 6;
break;
case kAudioDeviceCustomPropertyDeviceAudibleState:
@@ -507,7 +509,11 @@ UInt32 BGM_Device::Device_GetPropertyDataSize(AudioObjectID inObjectID, pid_t in
break;
case kAudioDeviceCustomPropertyAppVolumes:
theAnswer = sizeof(CFPropertyListRef);
theAnswer = sizeof(CFArrayRef);
break;
case kAudioDeviceCustomPropertyClients:
theAnswer = sizeof(CFArrayRef);
break;
default:
@@ -902,7 +908,7 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
break;
case kAudioDevicePropertyPreferredChannelsForStereo:
// This property returns which two channesl to use as left/right for stereo
// This property returns which two channels to use as left/right for stereo
// data by default. Note that the channel numbers are 1-based.
ThrowIf(inDataSize < (2 * sizeof(UInt32)), CAException(kAudioHardwareBadPropertySizeError), "BGM_Device::Device_GetPropertyData: not enough space for the return value of kAudioDevicePropertyPreferredChannelsForStereo for the device");
((UInt32*)outData)[0] = 1;
@@ -959,9 +965,9 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
theNumberItemsToFetch = inDataSize / sizeof(AudioServerPlugInCustomPropertyInfo);
// clamp it to the number of items we have
if(theNumberItemsToFetch > 5)
if(theNumberItemsToFetch > 6)
{
theNumberItemsToFetch = 5;
theNumberItemsToFetch = 6;
}
if(theNumberItemsToFetch > 0)
@@ -994,6 +1000,12 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
((AudioServerPlugInCustomPropertyInfo*)outData)[4].mPropertyDataType = kAudioServerPlugInCustomPropertyDataTypeCFPropertyList;
((AudioServerPlugInCustomPropertyInfo*)outData)[4].mQualifierDataType = kAudioServerPlugInCustomPropertyDataTypeNone;
}
if(theNumberItemsToFetch > 5)
{
((AudioServerPlugInCustomPropertyInfo*)outData)[5].mSelector = kAudioDeviceCustomPropertyClients;
((AudioServerPlugInCustomPropertyInfo*)outData)[5].mPropertyDataType = kAudioServerPlugInCustomPropertyDataTypeCFPropertyList;
((AudioServerPlugInCustomPropertyInfo*)outData)[5].mQualifierDataType = kAudioServerPlugInCustomPropertyDataTypeNone;
}
outDataSize = theNumberItemsToFetch * sizeof(AudioServerPlugInCustomPropertyInfo);
break;
@@ -1042,6 +1054,15 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient
outDataSize = sizeof(CFArrayRef);
}
break;
case kAudioDeviceCustomPropertyClients:
{
ThrowIf(inDataSize < sizeof(CFArrayRef), CAException(kAudioHardwareBadPropertySizeError), "BGM_Device::Device_GetPropertyData: not enough space for the return value of kAudioDeviceCustomPropertyClients for the device");
CAMutex::Locker theStateLocker(mStateMutex);
*reinterpret_cast<CFArrayRef*>(outData) = mClients.CopyClientPIDsAndBundleIDs().GetCFArray();
outDataSize = sizeof(CFArrayRef);
}
break;
default:
BGM_Object::GetPropertyData(inObjectID, inClientPID, inAddress, inQualifierDataSize, inQualifierData, inDataSize, outDataSize, outData);
@@ -172,6 +172,29 @@ std::vector<BGM_Client> BGM_ClientMap::GetClientsByPID(pid_t inPID) const
return theClients;
}
CACFArray BGM_ClientMap::CopyClientPIDsAndBundleIDs() const
{
// Since this is a read-only, non-real-time operation we can read from the shadow maps to avoid
// locking the main maps.
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
CACFArray theClientPIDsAndBundleIDs(false);
for(auto& theClientEntry : mClientMapShadow)
{
CACFDictionary theClientDict(false);
theClientDict.AddSInt32(CFSTR(kBGMClientsKey_ProcessID), theClientEntry.second.mProcessID);
// todo: is this a memory leak? (over retaining mBundleID, cause adding it to the dict also retains it) check for the same bug
// in CopyClientIntoAppVolumesArray if so.
theClientDict.AddString(CFSTR(kBGMClientsKey_BundleID), theClientEntry.second.mBundleID.CopyCFString());
theClientPIDsAndBundleIDs.AppendDictionary(theClientDict.GetDict());
}
return theClientPIDsAndBundleIDs;
}
#pragma mark Music Player
void BGM_ClientMap::UpdateMusicPlayerFlags(pid_t inMusicPlayerPID)
@@ -213,7 +236,7 @@ void BGM_ClientMap::UpdateMusicPlayerFlagsInShadowMaps(std::function<bool(BGM
CACFArray BGM_ClientMap::CopyClientRelativeVolumesAsAppVolumes(CAVolumeCurve inVolumeCurve) const
{
// Since this is a read-only, non-real-time operation, we can read from the shadow maps to avoid
// Since this is a read-only, non-real-time operation we can read from the shadow maps to avoid
// locking the main maps.
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
@@ -101,6 +101,10 @@ private:
public:
std::vector<BGM_Client> GetClientsByPID(pid_t inPID) const;
// todo: comment
// todo: would it be useful at all to have a similar property that returns past clients instead? probably not...
CACFArray CopyClientPIDsAndBundleIDs() const;
// Set the isMusicPlayer flag for each client. (True if the client has the given bundle ID/PID, false otherwise.)
void UpdateMusicPlayerFlags(pid_t inMusicPlayerPID);
void UpdateMusicPlayerFlags(CACFString inMusicPlayerBundleID);
@@ -108,6 +108,8 @@ public:
// Returns true if any clients' relative volumes were changed.
bool SetClientsRelativeVolumes(const CACFArray inAppVolumes);
CACFArray CopyClientPIDsAndBundleIDs() const { return mClientMap.CopyClientPIDsAndBundleIDs(); }
private:
BGM_ClientMap mClientMap;
+26 -6
View File
@@ -56,6 +56,9 @@ enum
{
// TODO: Combine the two music player properties
// Remember to update the values BGM_Device returns for kAudioObjectPropertyCustomPropertyInfoList if you
// modify this enum.
// The process ID of the music player as a CFNumber. Setting this property will also clear the value of
// kAudioDeviceCustomPropertyMusicPlayerBundleID. We use 0 to mean unset.
//
@@ -74,13 +77,17 @@ enum
// A CFBoolean similar to kAudioDevicePropertyDeviceIsRunning except it ignores whether IO is running for
// BGMApp. This is so BGMApp knows when it can stop doing IO to save CPU.
kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp = 'runo',
// A CFArray of CFDictionaries that each contain an app's pid, bundle ID and volume relative to other
// A CFArray of CFDictionaries that each contain an app's PID, bundle ID and volume relative to other
// running apps. See the dictionary keys below for more info.
//
// Getting this property will only return apps with volumes other than the default. Setting this property
// will add new app volumes or replace existing ones, but there's currently no way to delete an app from
// the internal collection.
kAudioDeviceCustomPropertyAppVolumes = 'apvs'
kAudioDeviceCustomPropertyAppVolumes = 'apvs',
// todo: send the client ids as well. maybe combine with kAudioDeviceCustomPropertyAppVolumes
// A CFArray with one entry for each client of BGMDevice. Each entry is a CFDictionary that contains the
// client's PID and, if it has one, the client's bundle ID. See the dictionary keys below for more info.
kAudioDeviceCustomPropertyClients = 'clts'
};
// The number of silent/audible frames before BGMDriver will change kAudioDeviceCustomPropertyDeviceAudibleState
@@ -104,11 +111,18 @@ enum
// the midpoint increases the client's volume and a value less than the midpoint decreases it. A volume curve is
// applied to kBGMAppVolumesKey_RelativeVolume when it's first set and then each of the app's samples are multiplied
// by it.
#define kBGMAppVolumesKey_RelativeVolume "rvol"
// The app's pid as a CFNumber. May be omitted if kBGMAppVolumesKey_BundleID is present.
#define kBGMAppVolumesKey_ProcessID "pid"
#define kBGMAppVolumesKey_RelativeVolume "av_rvol"
// The app's process ID as a CFNumber (which wraps a pid_t). May be omitted if kBGMAppVolumesKey_BundleID is present.
#define kBGMAppVolumesKey_ProcessID "av_pid"
// The app's bundle ID as a CFString. May be omitted if kBGMAppVolumesKey_ProcessID is present.
#define kBGMAppVolumesKey_BundleID "bid"
#define kBGMAppVolumesKey_BundleID "av_bid"
// kAudioDeviceCustomPropertyClients keys
//
// The client's process ID as a CFNumber<pid_t>.
#define kBGMClientsKey_ProcessID "c_pid"
// The client's bundle ID as a CFString. Omitted if the client doesn't have a bundle ID.
#define kBGMClientsKey_BundleID "c_bid"
// Volume curve range for app volumes
#define kAppRelativeVolumeMaxRawValue 100
@@ -150,6 +164,12 @@ static const AudioObjectPropertyAddress kBGMAppVolumesAddress = {
kAudioObjectPropertyElementMaster
};
static const AudioObjectPropertyAddress kBGMClientsAddress = {
kAudioDeviceCustomPropertyClients,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
#pragma mark XPC Return Codes
enum {
+2
View File
@@ -445,6 +445,7 @@ echo "[2/3] Installing $(bold_face ${XPC_HELPER_DIR}) to $(bold_face ${XPC_HELPE
sudo "${XCODEBUILD}" -project BGMApp/BGMApp.xcodeproj \
-target BGMXPCHelper \
-configuration ${CONFIGURATION} \
OTHER_CFLAGS="-Wno-everything" \
RUN_CLANG_STATIC_ANALYZER=0 \
DSTROOT="/" \
INSTALL_PATH="${XPC_HELPER_PATH}" \
@@ -461,6 +462,7 @@ echo "[3/3] Installing $(bold_face ${APP_DIR}) to $(bold_face ${APP_PATH})." \
sudo "${XCODEBUILD}" -project BGMApp/BGMApp.xcodeproj \
-target "Background Music" \
-configuration ${CONFIGURATION} \
OTHER_CFLAGS="-Wno-everything" \
RUN_CLANG_STATIC_ANALYZER=0 \
DSTROOT="/" \
${CLEAN} install >> ${LOG_FILE} 2>&1) &
+49
View File
@@ -0,0 +1,49 @@
// TODO: Delete this before merging WIP-MultiprocessAppVols into master.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <libproc.h>
#include <errno.h>
extern int responsibility_get_responsible_for_pid(pid_t, int32_t*, uint64_t*, size_t*, char*);
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Usage: %s <pid>\n", argv[0]);
return -1;
}
pid_t pid = (pid_t)atoi(argv[1]);
{
char path_buf[PROC_PIDPATHINFO_MAXSIZE] = "";
size_t path_len = sizeof(path_buf);
if (proc_pidpath(pid, path_buf, path_len) <= 0) {
printf("Couldn't get pid path for pid %d\n", pid);
printf("Error %d: %s\n", errno, strerror(errno));
return -1;
}
path_buf[path_len - 1] = '\0';
printf("Path for process: %s\n", path_buf);
}
{
int32_t rpid;
uint64_t urpid;
char responsible_path_buf[PROC_PIDPATHINFO_MAXSIZE] = "";
size_t responsible_path_len = sizeof(responsible_path_buf);
if (responsibility_get_responsible_for_pid(pid, &rpid, &urpid, &responsible_path_len, responsible_path_buf) != 0) {
printf("Couldn't get responsibility pid for pid %d\n", pid);
printf("Error %d: %s\n", errno, strerror(errno));
return -1;
}
responsible_path_buf[responsible_path_len - 1] = '\0';
printf("Path for responsible process: %s\n", responsible_path_buf);
printf("Responsible PID: %d\n", rpid);
printf("Responsible unique PID: %llu\n", urpid);
}
return 0;
}