mirror of
https://github.com/kyleneideck/BackgroundMusic
synced 2024-09-20 14:31:56 +00:00
Merge branch 'pan' of https://github.com/rakslice/BackgroundMusic into rakslice-pan
This commit is contained in:
commit
a62fae6fd1
11 changed files with 376 additions and 56 deletions
|
@ -41,6 +41,12 @@
|
|||
|
||||
@end
|
||||
|
||||
@protocol BGMAppPanSubview <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>
|
||||
|
@ -55,3 +61,14 @@
|
|||
|
||||
@end
|
||||
|
||||
@interface BGMAVM_PanSlider : NSSlider <BGMAppPanSubview>
|
||||
|
||||
- (void) setPanPosition:(NSNumber*)panPosition;
|
||||
|
||||
@end
|
||||
|
||||
@interface BGMAVM_PanSliderCell : NSSliderCell
|
||||
|
||||
- (void)drawBarInside:(NSRect)rect flipped:(BOOL)flipped;
|
||||
|
||||
@end
|
||||
|
|
|
@ -102,6 +102,9 @@ static float const kSlidersSnapWithin = 5;
|
|||
if ([subview conformsToProtocol:@protocol(BGMAppVolumeSubview)]) {
|
||||
[subview performSelector:@selector(setUpWithApp:context:) withObject:app withObject:self];
|
||||
}
|
||||
if ([subview conformsToProtocol:@protocol(BGMAppPanSubview)]) {
|
||||
[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
|
||||
|
@ -169,11 +172,17 @@ static float const kSlidersSnapWithin = 5;
|
|||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -224,6 +233,19 @@ static float const kSlidersSnapWithin = 5;
|
|||
[audioDevices bgmDevice].SetPropertyData_CFType(kBGMAppVolumesAddress, appVolumeChanges.AsPropertyList());
|
||||
}
|
||||
|
||||
- (void) sendPanPositionChangeToBGMDevice:(SInt32)newPanPosition 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 [kAppPanLeftRawValue, kAppPanRightRawValue] already
|
||||
appVolumeChange.AddSInt32(CFSTR(kBGMAppVolumesKey_PanPosition), newPanPosition);
|
||||
|
||||
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
|
||||
|
@ -271,7 +293,7 @@ static float const kSlidersSnapWithin = 5;
|
|||
|
||||
- (void) snap {
|
||||
// Snap to the 50% point
|
||||
float midPoint = static_cast<float>((self.maxValue - self.minValue) / 2);
|
||||
float midPoint = static_cast<float>((self.maxValue + self.minValue) / 2);
|
||||
if (self.floatValue > (midPoint - kSlidersSnapWithin) && self.floatValue < (midPoint + kSlidersSnapWithin)) {
|
||||
self.floatValue = midPoint;
|
||||
}
|
||||
|
@ -294,3 +316,82 @@ static float const kSlidersSnapWithin = 5;
|
|||
|
||||
@end
|
||||
|
||||
@implementation BGMAVM_PanSlider {
|
||||
// Will be set to -1 for apps without a pid
|
||||
pid_t appProcessID;
|
||||
NSString* appBundleID;
|
||||
BGMAppVolumes* context;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder: coder];
|
||||
|
||||
if(self) {
|
||||
NSSliderCell * oldCell = [self cell];
|
||||
|
||||
BGMAVM_PanSliderCell *cell = [[BGMAVM_PanSliderCell alloc] init];
|
||||
|
||||
cell.minValue = oldCell.minValue;
|
||||
cell.maxValue = oldCell.maxValue;
|
||||
cell.intValue = oldCell.intValue;
|
||||
cell.controlSize = oldCell.controlSize;
|
||||
|
||||
[self setCell:cell];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) setUpWithApp:(NSRunningApplication*)app context:(BGMAppVolumes*)ctx {
|
||||
context = ctx;
|
||||
|
||||
self.target = self;
|
||||
self.action = @selector(appPanPositionChanged);
|
||||
|
||||
appProcessID = app.processIdentifier;
|
||||
appBundleID = app.bundleIdentifier;
|
||||
|
||||
self.minValue = kAppPanLeftRawValue;
|
||||
self.maxValue = kAppPanRightRawValue;
|
||||
}
|
||||
|
||||
- (void) snap {
|
||||
// Snap to the center point
|
||||
float midPoint = static_cast<float>((self.maxValue + self.minValue) / 2);
|
||||
if (self.floatValue > (midPoint - 2 * kSlidersSnapWithin) && self.floatValue < (midPoint + 2 * kSlidersSnapWithin)) {
|
||||
self.floatValue = midPoint;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setPanPosition:(NSNumber *)panPosition {
|
||||
self.intValue = panPosition.intValue;
|
||||
[self snap];
|
||||
}
|
||||
|
||||
- (void) appPanPositionChanged {
|
||||
// TODO: This (sending updates to the driver) should probably be rate-limited. It uses a fair bit of CPU for me.
|
||||
|
||||
DebugMsg("BGMAppVolumes::appPanPositionChanged: App pan position for %s changed to %d", appBundleID.UTF8String, self.intValue);
|
||||
|
||||
[self snap];
|
||||
|
||||
[context sendPanPositionChangeToBGMDevice:self.intValue appProcessID:appProcessID appBundleID:appBundleID];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation BGMAVM_PanSliderCell
|
||||
|
||||
- (void)drawBarInside:(NSRect)rect flipped:(BOOL)flipped {
|
||||
// Custom slider cell to get rid of the level highlight
|
||||
|
||||
// Just run the stock method with the values swapped to get it to do what we want
|
||||
auto savedValue = self.doubleValue;
|
||||
self.doubleValue = self.minValue;
|
||||
[super drawBarInside:rect flipped:flipped];
|
||||
self.doubleValue = savedValue;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<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"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="355" 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">
|
||||
|
@ -83,6 +83,11 @@
|
|||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<sliderCell key="cell" controlSize="small" continuous="YES" state="on" alignment="left" maxValue="100" doubleValue="50" tickMarkPosition="above" sliderType="linear" id="Jmg-df-9Xl"/>
|
||||
</slider>
|
||||
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2mh-uO-kOV" customClass="BGMAVM_PanSlider">
|
||||
<rect key="frame" x="261" y="2" width="74" height="15"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<sliderCell key="cell" controlSize="small" continuous="YES" state="on" alignment="left" minValue="-100" maxValue="100" tickMarkPosition="above" sliderType="linear" id="ccM-Mt-93g"/>
|
||||
</slider>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="81" y="-111"/>
|
||||
</customView>
|
||||
|
|
|
@ -2092,6 +2092,31 @@ void BGM_Device::ApplyClientRelativeVolume(UInt32 inClientID, UInt32 inIOBufferF
|
|||
Float32* theBuffer = reinterpret_cast<Float32*>(ioBuffer);
|
||||
Float32 theRelativeVolume = mClients.GetClientRelativeVolumeRT(inClientID);
|
||||
|
||||
auto thePanPositionInt = mClients.GetClientPanPositionRT(inClientID);
|
||||
Float32 thePanPosition = ((Float32)thePanPositionInt)/100.0f;
|
||||
|
||||
// TODO precompute matrix coefficients w/ volume and do everything in one pass
|
||||
|
||||
// Apply balance w/ crossover to the frames in the buffer.
|
||||
// Expect samples interleaved, starting with left
|
||||
if (thePanPosition > 0.0f) {
|
||||
for (UInt32 i = 0; i < inIOBufferFrameSize * 2; i += 2) {
|
||||
auto L = i;
|
||||
auto R = i + 1;
|
||||
|
||||
theBuffer[R] = theBuffer[R] + theBuffer[L] * thePanPosition;
|
||||
theBuffer[L] = theBuffer[L] * (1 - thePanPosition);
|
||||
}
|
||||
} else if (thePanPosition < 0.0f) {
|
||||
for (UInt32 i = 0; i < inIOBufferFrameSize * 2; i += 2) {
|
||||
auto L = i;
|
||||
auto R = i + 1;
|
||||
|
||||
theBuffer[L] = theBuffer[L] + theBuffer[R] * (-thePanPosition);
|
||||
theBuffer[R] = theBuffer[R] * (1 + thePanPosition);
|
||||
}
|
||||
}
|
||||
|
||||
if(theRelativeVolume != 1.)
|
||||
{
|
||||
for(UInt32 i = 0; i < inIOBufferFrameSize * 2; i++)
|
||||
|
|
|
@ -48,5 +48,6 @@ void BGM_Client::Copy(const BGM_Client& inClient)
|
|||
mDoingIO = inClient.mDoingIO;
|
||||
mIsMusicPlayer = inClient.mIsMusicPlayer;
|
||||
mRelativeVolume = inClient.mRelativeVolume;
|
||||
mPanPosition = inClient.mPanPosition;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,9 @@ public:
|
|||
// mRelativeVolumeCurve is applied this this value when it's set.
|
||||
Float32 mRelativeVolume = 1.0;
|
||||
|
||||
// The client's pan position, in the range [-100, 100] where -100 is left and 100 is right
|
||||
SInt32 mPanPosition = 0;
|
||||
|
||||
};
|
||||
|
||||
#pragma clang assume_nonnull end
|
||||
|
|
|
@ -37,14 +37,16 @@ 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
|
||||
// If this client has been a client in the past (and has a bundle ID), copy its previous audio settings
|
||||
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",
|
||||
DebugMsg("BGM_ClientMap::AddClient: Found previous volume %f and pan %d for client %u",
|
||||
pastClientItr->second.mRelativeVolume,
|
||||
pastClientItr->second.mPanPosition,
|
||||
inClient.mClientID);
|
||||
inClient.mRelativeVolume = pastClientItr->second.mRelativeVolume;
|
||||
inClient.mPanPosition = pastClientItr->second.mPanPosition;
|
||||
}
|
||||
|
||||
// Add the new client to the shadow maps
|
||||
|
@ -234,8 +236,8 @@ CACFArray BGM_ClientMap::CopyClientRelativeVolumesAsAppVolumes(CAVolumeCurve i
|
|||
|
||||
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)
|
||||
// Only include clients set to a non-default volume or pan
|
||||
if(inClient.mRelativeVolume != 1.0 || inClient.mPanPosition != 0)
|
||||
{
|
||||
CACFDictionary theAppVolume(false);
|
||||
|
||||
|
@ -244,6 +246,8 @@ void BGM_ClientMap::CopyClientIntoAppVolumesArray(BGM_Client inClient, CAVolu
|
|||
// Reverse the volume conversion from SetClientsRelativeVolumes
|
||||
theAppVolume.AddSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume),
|
||||
inVolumeCurve.ConvertScalarToRaw(inClient.mRelativeVolume / 4));
|
||||
theAppVolume.AddSInt32(CFSTR(kBGMAppVolumesKey_PanPosition),
|
||||
inClient.mPanPosition);
|
||||
|
||||
ioAppVolumes.AppendDictionary(theAppVolume.GetDict());
|
||||
}
|
||||
|
@ -251,28 +255,75 @@ void BGM_ClientMap::CopyClientIntoAppVolumesArray(BGM_Client inClient, CAVolu
|
|||
|
||||
// TODO: Combine the SetClientsRelativeVolume methods? Their code is very similar.
|
||||
|
||||
bool BGM_ClientMap::SetClientsRelativeVolume(pid_t inAppPID, Float32 inRelativeVolume)
|
||||
template <typename T>
|
||||
std::vector<BGM_Client*> * _Nullable GetClientsFromMap(std::map<T, std::vector<BGM_Client*>> & map, T key) {
|
||||
auto theClientItr = map.find(key);
|
||||
if(theClientItr != map.end()) {
|
||||
return &theClientItr->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<BGM_Client*> * _Nullable BGM_ClientMap::GetClients(pid_t inAppPid) {
|
||||
return GetClientsFromMap(mClientMapByPIDShadow, inAppPid);
|
||||
}
|
||||
|
||||
std::vector<BGM_Client*> * _Nullable BGM_ClientMap::GetClients(CACFString inAppBundleID) {
|
||||
return GetClientsFromMap(mClientMapByBundleIDShadow, inAppBundleID);
|
||||
}
|
||||
|
||||
void ShowSetRelativeVolumeMessage(pid_t inAppPID, BGM_Client* theClient);
|
||||
void ShowSetRelativeVolumeMessage(CACFString inAppBundleID, BGM_Client* theClient);
|
||||
|
||||
void ShowSetRelativeVolumeMessage(pid_t inAppPID, BGM_Client* theClient) {
|
||||
(void)inAppPID;
|
||||
(void)theClient;
|
||||
DebugMsg("BGM_ClientMap::SetClientsRelativeVolume: Set volume %f for client %u by pid (%d)",
|
||||
theClient->mRelativeVolume,
|
||||
theClient->mClientID,
|
||||
inAppPID);
|
||||
}
|
||||
|
||||
void ShowSetRelativeVolumeMessage(CACFString inAppBundleID, BGM_Client* theClient) {
|
||||
(void)inAppBundleID;
|
||||
(void)theClient;
|
||||
DebugMsg("BGM_ClientMap::SetClientsRelativeVolume: Set volume %f for client %u by bundle ID (%s)",
|
||||
theClient->mRelativeVolume,
|
||||
theClient->mClientID,
|
||||
CFStringGetCStringPtr(inAppBundleID.GetCFString(), kCFStringEncodingUTF8));
|
||||
}
|
||||
|
||||
// Template method declarations are running into LLVM bug 23987
|
||||
// TODO: template these.
|
||||
|
||||
//bool BGM_ClientMap::SetClientsRelativeVolume(pid_t inAppPID, Float32 inRelativeVolume) {
|
||||
// return SetClientsRelativeVolumeT<pid_t>(inAppPID, inRelativeVolume);
|
||||
//}
|
||||
|
||||
//bool BGM_ClientMap::SetClientsRelativeVolume(CACFString inAppBundleID, Float32 inRelativeVolume) {
|
||||
// return SetClientsRelativeVolumeT<CACFString>(inAppBundleID, inRelativeVolume)
|
||||
//}
|
||||
|
||||
//template <typename T>
|
||||
//bool BGM_ClientMap::SetClientsRelativeVolume(T searchKey, Float32 inRelativeVolume)
|
||||
|
||||
bool BGM_ClientMap::SetClientsRelativeVolume(pid_t searchKey, 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);
|
||||
// Look up the clients for the key and update their volumes
|
||||
|
||||
if(theClientItr != mClientMapByPIDShadow.end())
|
||||
auto theClients = GetClients(searchKey);
|
||||
if(theClients != nullptr)
|
||||
{
|
||||
std::vector<BGM_Client*> theClients = theClientItr->second;
|
||||
|
||||
for(BGM_Client* theClient : theClients)
|
||||
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);
|
||||
ShowSetRelativeVolumeMessage(searchKey, theClient);
|
||||
|
||||
didChangeVolume = true;
|
||||
}
|
||||
|
@ -286,29 +337,23 @@ bool BGM_ClientMap::SetClientsRelativeVolume(pid_t inAppPID, Float32 inRelati
|
|||
return didChangeVolume;
|
||||
}
|
||||
|
||||
|
||||
bool BGM_ClientMap::SetClientsRelativeVolume(CACFString inAppBundleID, Float32 inRelativeVolume)
|
||||
bool BGM_ClientMap::SetClientsRelativeVolume(CACFString searchKey, 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);
|
||||
// Look up the clients for the key and update their volumes
|
||||
|
||||
if(theClientItr != mClientMapByBundleIDShadow.end())
|
||||
auto theClients = GetClients(searchKey);
|
||||
if(theClients != nullptr)
|
||||
{
|
||||
std::vector<BGM_Client*> theClients = theClientItr->second;
|
||||
|
||||
for(BGM_Client* theClient : theClients)
|
||||
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));
|
||||
ShowSetRelativeVolumeMessage(searchKey, theClient);
|
||||
|
||||
didChangeVolume = true;
|
||||
}
|
||||
|
@ -322,6 +367,62 @@ bool BGM_ClientMap::SetClientsRelativeVolume(CACFString inAppBundleID, Float3
|
|||
return didChangeVolume;
|
||||
}
|
||||
|
||||
bool BGM_ClientMap::SetClientsPanPosition(pid_t searchKey, SInt32 inPanPosition)
|
||||
{
|
||||
bool didChangePanPosition = false;
|
||||
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
auto theSetPansInShadowMapsFunc = [&] {
|
||||
// Look up the clients for the key and update their pan positions
|
||||
auto theClients = GetClients(searchKey);
|
||||
if(theClients != nullptr) {
|
||||
for(auto theClient: *theClients) {
|
||||
theClient->mPanPosition = inPanPosition;
|
||||
|
||||
// ShowSetPanPositionsMessage(searchKey, theClient)
|
||||
|
||||
didChangePanPosition = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
theSetPansInShadowMapsFunc();
|
||||
SwapInShadowMaps();
|
||||
theSetPansInShadowMapsFunc();
|
||||
|
||||
return didChangePanPosition;
|
||||
|
||||
}
|
||||
|
||||
bool BGM_ClientMap::SetClientsPanPosition(CACFString searchKey, SInt32 inPanPosition)
|
||||
{
|
||||
bool didChangePanPosition = false;
|
||||
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
||||
auto theSetPansInShadowMapsFunc = [&] {
|
||||
// Look up the clients for the key and update their pan positions
|
||||
auto theClients = GetClients(searchKey);
|
||||
if(theClients != nullptr) {
|
||||
for(auto theClient: *theClients) {
|
||||
theClient->mPanPosition = inPanPosition;
|
||||
|
||||
// ShowSetPanPositionsMessage(searchKey, theClient)
|
||||
|
||||
didChangePanPosition = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
theSetPansInShadowMapsFunc();
|
||||
SwapInShadowMaps();
|
||||
theSetPansInShadowMapsFunc();
|
||||
|
||||
return didChangePanPosition;
|
||||
|
||||
}
|
||||
|
||||
void BGM_ClientMap::UpdateClientIOStateNonRT(UInt32 inClientID, bool inDoingIO)
|
||||
{
|
||||
CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex);
|
||||
|
|
|
@ -118,11 +118,23 @@ 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.
|
||||
// Returns true if a client for 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.
|
||||
// Returns true if a client for bundle ID inAppBundleID was found and its relative volume changed.
|
||||
bool SetClientsRelativeVolume(CACFString inAppBundleID, Float32 inRelativeVolume);
|
||||
|
||||
// Using the template function hits LLVM Bug 23987
|
||||
// TODO Switch to template function
|
||||
|
||||
// Returns true if a client for the key was found and its relative volume changed.
|
||||
//template <typename T>
|
||||
//bool SetClientsRelativeVolume(T _Null_unspecified key, Float32 inRelativeVolume);
|
||||
|
||||
// Returns true if a client for PID inAppPID was found and its relative volume changed.
|
||||
bool SetClientsPanPosition(pid_t inAppPID, SInt32 inPanPosition);
|
||||
// Returns true if a client for bundle ID inAppBundleID was found and its relative volume changed.
|
||||
bool SetClientsPanPosition(CACFString inAppBundleID, SInt32 inPanPosition);
|
||||
|
||||
void StartIONonRT(UInt32 inClientID) { UpdateClientIOStateNonRT(inClientID, true); }
|
||||
void StopIONonRT(UInt32 inClientID) { UpdateClientIOStateNonRT(inClientID, false); }
|
||||
|
||||
|
@ -136,6 +148,11 @@ private:
|
|||
// mutex must be locked when calling this method.
|
||||
void SwapInShadowMapsRT();
|
||||
|
||||
// Client lookup for PID inAppPID
|
||||
std::vector<BGM_Client*> * _Nullable GetClients(pid_t inAppPid);
|
||||
// Client lookup for bundle ID inAppBundleID
|
||||
std::vector<BGM_Client*> * _Nullable GetClients(CACFString inAppBundleID);
|
||||
|
||||
private:
|
||||
BGM_TaskQueue* mTaskQueue;
|
||||
|
||||
|
|
|
@ -296,6 +296,13 @@ Float32 BGM_Clients::GetClientRelativeVolumeRT(UInt32 inClientID) const
|
|||
return (didGetClient ? theClient.mRelativeVolume : 1.0);
|
||||
}
|
||||
|
||||
SInt32 BGM_Clients::GetClientPanPositionRT(UInt32 inClientID) const
|
||||
{
|
||||
BGM_Client theClient;
|
||||
bool didGetClient = mClientMap.GetClientRT(inClientID, &theClient);
|
||||
return (didGetClient ? theClient.mPanPosition : kAppPanCenterRawValue);
|
||||
}
|
||||
|
||||
bool BGM_Clients::SetClientsRelativeVolumes(const CACFArray inAppVolumes)
|
||||
{
|
||||
bool didChangeAppVolumes = false;
|
||||
|
@ -320,21 +327,21 @@ bool BGM_Clients::SetClientsRelativeVolumes(const CACFArray inAppVolumes)
|
|||
BGM_InvalidClientRelativeVolumeException(),
|
||||
"BGM_Clients::SetClientsRelativeVolumes: App volume was sent without PID or bundle ID for app");
|
||||
|
||||
Float32 theRelativeVolume;
|
||||
bool didGetVolume;
|
||||
{
|
||||
SInt32 theRawRelativeVolume;
|
||||
bool didGetVolume = theAppVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume), theRawRelativeVolume);
|
||||
didGetVolume = theAppVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume), theRawRelativeVolume);
|
||||
|
||||
ThrowIf(!didGetVolume || theRawRelativeVolume < kAppRelativeVolumeMinRawValue || theRawRelativeVolume > kAppRelativeVolumeMaxRawValue,
|
||||
if (didGetVolume) {
|
||||
ThrowIf(didGetVolume && (theRawRelativeVolume < kAppRelativeVolumeMinRawValue || theRawRelativeVolume > kAppRelativeVolumeMaxRawValue),
|
||||
BGM_InvalidClientRelativeVolumeException(),
|
||||
"BGM_Clients::SetClientsRelativeVolumes: Relative volume for app missing or out of valid range");
|
||||
"BGM_Clients::SetClientsRelativeVolumes: Relative volume for app out of valid range");
|
||||
|
||||
// Apply the volume curve to the raw volume
|
||||
//
|
||||
// mRelativeVolumeCurve uses the default kPow2Over1Curve transfer function, so we also multiply by 4 to
|
||||
// keep the middle volume equal to 1 (meaning apps' volumes are unchanged by default).
|
||||
theRelativeVolume = mRelativeVolumeCurve.ConvertRawToScalar(theRawRelativeVolume) * 4;
|
||||
}
|
||||
Float32 theRelativeVolume = mRelativeVolumeCurve.ConvertRawToScalar(theRawRelativeVolume) * 4;
|
||||
|
||||
// Try to update the client's volume, first by PID and then, if that fails, by bundle ID
|
||||
//
|
||||
|
@ -352,6 +359,38 @@ bool BGM_Clients::SetClientsRelativeVolumes(const CACFArray inAppVolumes)
|
|||
// TODO: The app isn't currently a client, so we should add it to the past clients map, or update its
|
||||
// past volume if it's already in there.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool didGetPanPosition;
|
||||
{
|
||||
SInt32 thePanPosition;
|
||||
didGetPanPosition = theAppVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_PanPosition), thePanPosition);
|
||||
if (didGetPanPosition) {
|
||||
ThrowIf(didGetPanPosition && (thePanPosition < kAppPanLeftRawValue || thePanPosition > kAppPanRightRawValue),
|
||||
BGM_InvalidClientPanPositionException(),
|
||||
"BGM_Clients::SetClientsRelativeVolumes: Pan position for app out of valid range");
|
||||
|
||||
if(mClientMap.SetClientsPanPosition(theAppPID, thePanPosition))
|
||||
{
|
||||
didChangeAppVolumes = true;
|
||||
}
|
||||
else if(mClientMap.SetClientsPanPosition(theAppBundleID, thePanPosition))
|
||||
{
|
||||
didChangeAppVolumes = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: The app isn't currently a client, so we should add it to the past clients map, or update its
|
||||
// past pan position if it's already in there.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThrowIf(!didGetVolume && !didGetPanPosition,
|
||||
BGM_InvalidClientRelativeVolumeException(),
|
||||
"BGM_Clients::SetClientsRelativeVolumes: No volume or pan position in request");
|
||||
}
|
||||
|
||||
return didChangeAppVolumes;
|
||||
|
|
|
@ -95,6 +95,7 @@ public:
|
|||
bool IsMusicPlayerRT(const UInt32 inClientID) const;
|
||||
|
||||
Float32 GetClientRelativeVolumeRT(UInt32 inClientID) const;
|
||||
SInt32 GetClientPanPositionRT(UInt32 inClientID) const;
|
||||
|
||||
// Copies the current and past clients into an array in the format expected for
|
||||
// kAudioDeviceCustomPropertyAppVolumes. (Except that CACFArray and CACFDictionary are used instead
|
||||
|
@ -102,7 +103,8 @@ public:
|
|||
CACFArray CopyClientRelativeVolumesAsAppVolumes() const { return mClientMap.CopyClientRelativeVolumesAsAppVolumes(mRelativeVolumeCurve); };
|
||||
|
||||
// inAppVolumes is an array of dicts with the keys kBGMAppVolumesKey_ProcessID,
|
||||
// kBGMAppVolumesKey_BundleID and kBGMAppVolumesKey_RelativeVolume. This method finds the client for
|
||||
// kBGMAppVolumesKey_BundleID and optionally kBGMAppVolumesKey_RelativeVolume and
|
||||
// kBGMAppVolumesKey_PanPosition. This method finds the client for
|
||||
// each app by PID or bundle ID, sets the volume and applies mRelativeVolumeCurve to it.
|
||||
//
|
||||
// Returns true if any clients' relative volumes were changed.
|
||||
|
|
|
@ -110,6 +110,9 @@ enum
|
|||
// 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"
|
||||
// A CFNumber<SInt32> between kAppPanLeftRawValue and kAppPanRightRawValue. A negative value has a higher proportion of left channel,
|
||||
// and a positive value has a higher proportion of right channel.
|
||||
#define kBGMAppVolumesKey_PanPosition "ppos"
|
||||
// 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.
|
||||
|
@ -121,6 +124,11 @@ enum
|
|||
#define kAppRelativeVolumeMinDbValue -96.0f
|
||||
#define kAppRelativeVolumeMaxDbValue 0.0f
|
||||
|
||||
// Pan position values
|
||||
#define kAppPanLeftRawValue -100
|
||||
#define kAppPanCenterRawValue 0
|
||||
#define kAppPanRightRawValue 100
|
||||
|
||||
#pragma mark BGMDevice Custom Property Addresses
|
||||
|
||||
// For convenience.
|
||||
|
@ -173,6 +181,7 @@ enum {
|
|||
class BGM_InvalidClientException { };
|
||||
class BGM_InvalidClientPIDException { };
|
||||
class BGM_InvalidClientRelativeVolumeException { };
|
||||
class BGM_InvalidClientPanPositionException { };
|
||||
class BGM_DeviceNotSetException { };
|
||||
class BGM_RuntimeException { };
|
||||
|
||||
|
|
Loading…
Reference in a new issue