From 3adc13d68bc92bc30ac6b015c025fb5092579329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abd=C3=BClkadir?= <141135041+abdulkadiryugruk@users.noreply.github.com> Date: Wed, 24 Dec 2025 20:19:14 +0300 Subject: [PATCH 1/6] Fix Discord/Brave volume control Fix Discord/Brave volume control & crashes on macOS Tahoe --- BGMApp/BGMApp/BGMAppVolumes.m | 11 ++- BGMApp/BGMApp/BGMAppVolumesController.mm | 87 +++++++++++++++++++++--- 2 files changed, 86 insertions(+), 12 deletions(-) diff --git a/BGMApp/BGMApp/BGMAppVolumes.m b/BGMApp/BGMApp/BGMAppVolumes.m index 34b16b52..ada03c4c 100644 --- a/BGMApp/BGMApp/BGMAppVolumes.m +++ b/BGMApp/BGMApp/BGMAppVolumes.m @@ -117,13 +117,22 @@ - (void) insertMenuItemForApp:(NSRunningApplication*)app } #endif + // --- GÜNCELLENEN KISIM --- + // Discord veya Brave helper'larını ana menüye zorla + BOOL isTargetApp = [app.localizedName containsString:@"Discord"] || + [app.bundleIdentifier containsString:@"Discord"] || + [app.localizedName containsString:@"Brave"] || + [app.bundleIdentifier containsString:@"Brave"]; + // Add the menu item to its menu. - if (app.activationPolicy == NSApplicationActivationPolicyRegular) { + if (app.activationPolicy == NSApplicationActivationPolicyRegular || isTargetApp) { [bgmMenu insertItem:appVolItem atIndex:[self firstMenuItemIndex]]; numMenuItems++; } else if (app.activationPolicy == NSApplicationActivationPolicyAccessory) { [moreAppsMenu insertItem:appVolItem atIndex:0]; } + // -------------------------- + // --- DEĞİŞTİRİLEN KISIM BİTİŞ --- } - (NSMenuItem*) getMenuItemForApp:(NSRunningApplication*)app { diff --git a/BGMApp/BGMApp/BGMAppVolumesController.mm b/BGMApp/BGMApp/BGMAppVolumesController.mm index 82821170..1dbdc065 100644 --- a/BGMApp/BGMApp/BGMAppVolumesController.mm +++ b/BGMApp/BGMApp/BGMAppVolumesController.mm @@ -146,11 +146,24 @@ - (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication*)app } - (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 *appName = app.localizedName; + + if ([appName rangeOfString:@"Discord" options:NSCaseInsensitiveSearch].location != NSNotFound || + [appName rangeOfString:@"Brave" options:NSCaseInsensitiveSearch].location != NSNotFound) { + + if ([appName rangeOfString:@"Helper" options:NSCaseInsensitiveSearch].location != NSNotFound || + [appName rangeOfString:@"Renderer" options:NSCaseInsensitiveSearch].location != NSNotFound || + [appName rangeOfString:@"GPU" options:NSCaseInsensitiveSearch].location != NSNotFound || + [appName rangeOfString:@"Plugin" options:NSCaseInsensitiveSearch].location != NSNotFound) { + isHidden = YES; + } else { + isHidden = NO; + } + } + NSString* bundleID = app.bundleIdentifier; BOOL isBGMApp = bundleID && [@kBGMAppBundleID isEqualToString:BGMNN(bundleID)]; @@ -170,20 +183,21 @@ - (void) removeMenuItemsForApps:(NSArray*)apps { - (void) setVolume:(SInt32)volume forAppWithProcessID:(pid_t)processID bundleID:(NSString* __nullable)bundleID { - // Update the app's volume. + audioDevices.bgmDevice.SetAppVolume(volume, processID, (__bridge_retained CFStringRef)bundleID); - // If this volume is for FaceTime, set the volume for the avconferenced process as well. This - // works around FaceTime not playing its own audio. It plays UI sounds through - // systemsoundserverd and call audio through avconferenced. - // - // This isn't ideal because other apps might play audio through avconferenced, but I don't see a - // good way we could find out which app is actually playing the audio. We could probably figure - // it out from reading avconferenced's logs, at least, if it turns out to be important. See - // https://github.com/kyleneideck/BackgroundMusic/issues/139. if ([bundleID isEqual:@"com.apple.FaceTime"]) { [self setAvconferencedVolume:volume]; } + + if (bundleID) { + if ([bundleID rangeOfString:@"Brave" options:NSCaseInsensitiveSearch].location != NSNotFound) { + [self setVolumeForAllProcessesLike:@"Brave" volume:volume]; + } + else if ([bundleID rangeOfString:@"Discord" options:NSCaseInsensitiveSearch].location != NSNotFound) { + [self setVolumeForAllProcessesLike:@"Discord" volume:volume]; + } + } } - (void) setAvconferencedVolume:(SInt32)volume { @@ -253,6 +267,57 @@ - (void) observeValueForKeyPath:(NSString* __nullable)keyPath } } +- (void) setVolumeForAllProcessesLike:(NSString*)searchName volume:(SInt32)volume { + pid_t pids[4096]; + + int bytesReturned = proc_listallpids(pids, sizeof(pids)); + if (bytesReturned <= 0) return; + + int procCount = bytesReturned / sizeof(pid_t); + if (procCount > 4096) procCount = 4096; + + char path[PROC_PIDPATHINFO_MAXSIZE]; + char name[PROC_PIDPATHINFO_MAXSIZE]; + + NSString *safeBundleIDStr = @"com.bgm.safe.helper"; + CFStringRef safeBundleID = (__bridge CFStringRef)safeBundleIDStr; + + @autoreleasepool { + for (int i = 0; i < procCount; i++) { + pid_t pid = pids[i]; + + if (pid < 100) continue; + + memset(path, 0, sizeof(path)); + memset(name, 0, sizeof(name)); + + if (proc_pidpath(pid, path, sizeof(path)) <= 0) continue; + proc_name(pid, name, sizeof(name)); + + NSString *procPath = nil; + NSString *procName = nil; + + @try { + if (path[0] != '\0') procPath = [NSString stringWithUTF8String:path]; + if (name[0] != '\0') procName = [NSString stringWithUTF8String:name]; + } @catch (...) { + continue; + } + + BOOL match = NO; + if (procPath && [procPath rangeOfString:searchName options:NSCaseInsensitiveSearch].location != NSNotFound) match = YES; + else if (procName && [procName rangeOfString:searchName options:NSCaseInsensitiveSearch].location != NSNotFound) match = YES; + + if (match) { + try { + audioDevices.bgmDevice.SetAppVolume(volume, pid, safeBundleID); + } catch (...) { + } + } + } + } +} + @end #pragma clang assume_nonnull end From 1989b7891fb9d3ebf9c8ae744a195f7f9c3db4cb Mon Sep 17 00:00:00 2001 From: Kyle Neideck Date: Sun, 28 Dec 2025 11:39:32 +1100 Subject: [PATCH 2/6] Revert "Fix Discord/Brave volume control" This reverts commit 3adc13d68bc92bc30ac6b015c025fb5092579329. --- BGMApp/BGMApp/BGMAppVolumes.m | 11 +-- BGMApp/BGMApp/BGMAppVolumesController.mm | 87 +++--------------------- 2 files changed, 12 insertions(+), 86 deletions(-) diff --git a/BGMApp/BGMApp/BGMAppVolumes.m b/BGMApp/BGMApp/BGMAppVolumes.m index ada03c4c..34b16b52 100644 --- a/BGMApp/BGMApp/BGMAppVolumes.m +++ b/BGMApp/BGMApp/BGMAppVolumes.m @@ -117,22 +117,13 @@ - (void) insertMenuItemForApp:(NSRunningApplication*)app } #endif - // --- GÜNCELLENEN KISIM --- - // Discord veya Brave helper'larını ana menüye zorla - BOOL isTargetApp = [app.localizedName containsString:@"Discord"] || - [app.bundleIdentifier containsString:@"Discord"] || - [app.localizedName containsString:@"Brave"] || - [app.bundleIdentifier containsString:@"Brave"]; - // Add the menu item to its menu. - if (app.activationPolicy == NSApplicationActivationPolicyRegular || isTargetApp) { + if (app.activationPolicy == NSApplicationActivationPolicyRegular) { [bgmMenu insertItem:appVolItem atIndex:[self firstMenuItemIndex]]; numMenuItems++; } else if (app.activationPolicy == NSApplicationActivationPolicyAccessory) { [moreAppsMenu insertItem:appVolItem atIndex:0]; } - // -------------------------- - // --- DEĞİŞTİRİLEN KISIM BİTİŞ --- } - (NSMenuItem*) getMenuItemForApp:(NSRunningApplication*)app { diff --git a/BGMApp/BGMApp/BGMAppVolumesController.mm b/BGMApp/BGMApp/BGMAppVolumesController.mm index 1dbdc065..82821170 100644 --- a/BGMApp/BGMApp/BGMAppVolumesController.mm +++ b/BGMApp/BGMApp/BGMAppVolumesController.mm @@ -146,24 +146,11 @@ - (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication*)app } - (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 *appName = app.localizedName; - - if ([appName rangeOfString:@"Discord" options:NSCaseInsensitiveSearch].location != NSNotFound || - [appName rangeOfString:@"Brave" options:NSCaseInsensitiveSearch].location != NSNotFound) { - - if ([appName rangeOfString:@"Helper" options:NSCaseInsensitiveSearch].location != NSNotFound || - [appName rangeOfString:@"Renderer" options:NSCaseInsensitiveSearch].location != NSNotFound || - [appName rangeOfString:@"GPU" options:NSCaseInsensitiveSearch].location != NSNotFound || - [appName rangeOfString:@"Plugin" options:NSCaseInsensitiveSearch].location != NSNotFound) { - isHidden = YES; - } else { - isHidden = NO; - } - } - NSString* bundleID = app.bundleIdentifier; BOOL isBGMApp = bundleID && [@kBGMAppBundleID isEqualToString:BGMNN(bundleID)]; @@ -183,21 +170,20 @@ - (void) removeMenuItemsForApps:(NSArray*)apps { - (void) setVolume:(SInt32)volume forAppWithProcessID:(pid_t)processID bundleID:(NSString* __nullable)bundleID { - + // Update the app's volume. audioDevices.bgmDevice.SetAppVolume(volume, processID, (__bridge_retained CFStringRef)bundleID); + // If this volume is for FaceTime, set the volume for the avconferenced process as well. This + // works around FaceTime not playing its own audio. It plays UI sounds through + // systemsoundserverd and call audio through avconferenced. + // + // This isn't ideal because other apps might play audio through avconferenced, but I don't see a + // good way we could find out which app is actually playing the audio. We could probably figure + // it out from reading avconferenced's logs, at least, if it turns out to be important. See + // https://github.com/kyleneideck/BackgroundMusic/issues/139. if ([bundleID isEqual:@"com.apple.FaceTime"]) { [self setAvconferencedVolume:volume]; } - - if (bundleID) { - if ([bundleID rangeOfString:@"Brave" options:NSCaseInsensitiveSearch].location != NSNotFound) { - [self setVolumeForAllProcessesLike:@"Brave" volume:volume]; - } - else if ([bundleID rangeOfString:@"Discord" options:NSCaseInsensitiveSearch].location != NSNotFound) { - [self setVolumeForAllProcessesLike:@"Discord" volume:volume]; - } - } } - (void) setAvconferencedVolume:(SInt32)volume { @@ -267,57 +253,6 @@ - (void) observeValueForKeyPath:(NSString* __nullable)keyPath } } -- (void) setVolumeForAllProcessesLike:(NSString*)searchName volume:(SInt32)volume { - pid_t pids[4096]; - - int bytesReturned = proc_listallpids(pids, sizeof(pids)); - if (bytesReturned <= 0) return; - - int procCount = bytesReturned / sizeof(pid_t); - if (procCount > 4096) procCount = 4096; - - char path[PROC_PIDPATHINFO_MAXSIZE]; - char name[PROC_PIDPATHINFO_MAXSIZE]; - - NSString *safeBundleIDStr = @"com.bgm.safe.helper"; - CFStringRef safeBundleID = (__bridge CFStringRef)safeBundleIDStr; - - @autoreleasepool { - for (int i = 0; i < procCount; i++) { - pid_t pid = pids[i]; - - if (pid < 100) continue; - - memset(path, 0, sizeof(path)); - memset(name, 0, sizeof(name)); - - if (proc_pidpath(pid, path, sizeof(path)) <= 0) continue; - proc_name(pid, name, sizeof(name)); - - NSString *procPath = nil; - NSString *procName = nil; - - @try { - if (path[0] != '\0') procPath = [NSString stringWithUTF8String:path]; - if (name[0] != '\0') procName = [NSString stringWithUTF8String:name]; - } @catch (...) { - continue; - } - - BOOL match = NO; - if (procPath && [procPath rangeOfString:searchName options:NSCaseInsensitiveSearch].location != NSNotFound) match = YES; - else if (procName && [procName rangeOfString:searchName options:NSCaseInsensitiveSearch].location != NSNotFound) match = YES; - - if (match) { - try { - audioDevices.bgmDevice.SetAppVolume(volume, pid, safeBundleID); - } catch (...) { - } - } - } - } -} - @end #pragma clang assume_nonnull end From d17ad6a89cca17116dee0520bbd1b533c6ec54dc Mon Sep 17 00:00:00 2001 From: Kyle Neideck Date: Sun, 28 Dec 2025 11:50:00 +1100 Subject: [PATCH 3/6] Add Brave Browser and more Discord IDs to the bundle ID list. --- BGMApp/BGMApp/BGMBackgroundMusicDevice.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/BGMApp/BGMApp/BGMBackgroundMusicDevice.cpp b/BGMApp/BGMApp/BGMBackgroundMusicDevice.cpp index b44f83cb..e0daa18b 100644 --- a/BGMApp/BGMApp/BGMBackgroundMusicDevice.cpp +++ b/BGMApp/BGMApp/BGMBackgroundMusicDevice.cpp @@ -237,7 +237,14 @@ BGMBackgroundMusicDevice::ResponsibleBundleIDsOf(CACFString inParentBundleID) { "hu.mplayerhq.mplayerosx.extended", { "ch.sttz.mplayerosx.extended.binaries.officialsvn" } }, // Discord - { "com.hnc.Discord", { "com.hnc.Discord.helper" } }, + { "com.hnc.Discord", + { "com.hnc.Discord.helper", + "com.hnc.Discord.helper.Renderer", + "com.hnc.Discord.helper.Plugin" } }, + // Brave + { "com.brave.Browser", + { "com.brave.Browser.helper", + "com.brave.Browser.helper.plugin" } }, // Skype { "com.skype.skype", { "com.skype.skype.Helper" } }, // Google Chrome From 478c1fe191fbf7758415e096804c210751c43a67 Mon Sep 17 00:00:00 2001 From: Kyle Neideck Date: Tue, 30 Dec 2025 01:00:28 +1100 Subject: [PATCH 4/6] Fix unit test compilation. --- .../BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp b/BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp index 3e1f0954..3107f48c 100644 --- a/BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp +++ b/BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp @@ -17,7 +17,7 @@ // MockAudioObjects.cpp // BGMAppUnitTests // -// Copyright © 2020 Kyle Neideck +// Copyright © 2020, 2025 Kyle Neideck // // Self Include @@ -26,6 +26,9 @@ // PublicUtility Includes #include "CACFString.h" +// STL Includes +#include + // static MockAudioObjects::MockDeviceMap MockAudioObjects::sDevices; @@ -87,9 +90,9 @@ std::shared_ptr MockAudioObjects::GetAudioDevice(CFStringRef in { // Convert inUID to a std::string. UInt32 uidCStringLen = CACFString::GetStringByteLength(inUID) + 1; - char uidCString[uidCStringLen]; - CACFString::GetCString(inUID, uidCString, uidCStringLen); - std::string uid = std::string(uidCString); + std::vector uidCString(uidCStringLen); + CACFString::GetCString(inUID, uidCString.data(), uidCStringLen); + std::string uid = std::string(uidCString.data()); return GetAudioDevice(uid); } From ebdb7f2038fcae39fcc199ac88d982e0e47210a4 Mon Sep 17 00:00:00 2001 From: Kyle Neideck Date: Tue, 30 Dec 2025 01:02:04 +1100 Subject: [PATCH 5/6] Fix BGMDriver crash when an app volume is set with no bundle ID. BGMApp was setting the `kAudioDeviceCustomPropertyAppVolumes` property with only a process ID in the CFDictionary when the app had no bundle ID. BGMDriver would then cause a null deref in `BGM_ClientMap::GetClients`. See #839. --- BGMDriver/BGMDriver/BGM_Device.cpp | 75 ++++++++++++++++++- .../BGMDriver/DeviceClients/BGM_ClientMap.cpp | 15 ++-- .../BGMDriver/DeviceClients/BGM_ClientMap.h | 4 +- .../BGMDriver/DeviceClients/BGM_Clients.cpp | 24 ++---- 4 files changed, 93 insertions(+), 25 deletions(-) diff --git a/BGMDriver/BGMDriver/BGM_Device.cpp b/BGMDriver/BGMDriver/BGM_Device.cpp index 480e2688..8cecf36e 100644 --- a/BGMDriver/BGMDriver/BGM_Device.cpp +++ b/BGMDriver/BGMDriver/BGM_Device.cpp @@ -17,7 +17,7 @@ // BGM_Device.cpp // BGMDriver // -// Copyright © 2016, 2017, 2019 Kyle Neideck +// Copyright © 2016, 2017, 2019, 2025 Kyle Neideck // Copyright © 2017 Andrew Tonner // Copyright © 2019 Gordon Childs // Copyright © 2020 Aleksey Yurkevich @@ -40,6 +40,7 @@ #include "CADispatchQueue.h" #include "CAException.h" #include "CACFArray.h" +#include "CACFDictionary.h" #include "CACFString.h" #include "CADebugMacros.h" #include "CAHostTimeBase.h" @@ -1001,6 +1002,74 @@ void BGM_Device::Device_GetPropertyData(AudioObjectID inObjectID, pid_t inClient }; } +// Validates inAppVolumes for the kAudioDeviceCustomPropertyAppVolumes property as described in +// BGM_Types.h. Throws CAException(kAudioHardwareIllegalOperationError) if invalid. +static void ValidateAppVolumesProperty(const CACFArray& inAppVolumes) +{ + UInt32 theCount = inAppVolumes.GetNumberItems(); + + for(UInt32 i = 0; i < theCount; i++) + { + // Each element must be a CFDictionary. + CFTypeRef theElement = nullptr; + bool didGetValue = inAppVolumes.GetCFType(i, theElement); + ThrowIf(!didGetValue || theElement == nullptr, + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: Could not get element from array"); + ThrowIf(CFGetTypeID(theElement) != CFDictionaryGetTypeID(), + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: Element is not a CFDictionary"); + + CACFDictionary theDict(static_cast(theElement), false); + + // Check for ProcessID. Must be a CFNumber if present. + CFTypeRef thePIDValue = nullptr; + bool hasPID = theDict.GetCFType(CFSTR(kBGMAppVolumesKey_ProcessID), thePIDValue); + if(hasPID && thePIDValue != nullptr) + { + ThrowIf(CFGetTypeID(thePIDValue) != CFNumberGetTypeID(), + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: ProcessID is not a CFNumber"); + } + + // Check for BundleID. Must be a CFString if present. + CFTypeRef theBundleIDValue = nullptr; + bool hasBundleID = theDict.GetCFType(CFSTR(kBGMAppVolumesKey_BundleID), theBundleIDValue); + if(hasBundleID && theBundleIDValue != nullptr) + { + ThrowIf(CFGetTypeID(theBundleIDValue) != CFStringGetTypeID(), + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: BundleID is not a CFString"); + } + + // At least one of ProcessID or BundleID must be present. + ThrowIf(!hasPID && !hasBundleID, + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: Neither ProcessID nor BundleID present"); + + // Check RelativeVolume. Must be a CFNumber in [0, 100] if present. + SInt32 theVolume; + bool hasVolume = theDict.GetSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume), theVolume); + if(hasVolume) + { + ThrowIf(theVolume < kAppRelativeVolumeMinRawValue || + theVolume > kAppRelativeVolumeMaxRawValue, + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: RelativeVolume out of range"); + } + + // Check PanPosition. Must be a CFNumber in [-100, 100] if present. + SInt32 thePan; + bool hasPan = theDict.GetSInt32(CFSTR(kBGMAppVolumesKey_PanPosition), thePan); + if(hasPan) + { + ThrowIf(thePan < kAppPanLeftRawValue || thePan > kAppPanRightRawValue, + CAException(kAudioHardwareIllegalOperationError), + "BGM_Device::ValidateAppVolumesProperty: PanPosition out of range"); + } + } +} + void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData) { switch(inAddress.mSelector) @@ -1092,6 +1161,8 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient CACFArray array(arrayRef, false); + ValidateAppVolumesProperty(array); + bool propertyWasChanged = false; CAMutex::Locker theStateLocker(mStateMutex); @@ -1104,7 +1175,7 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient { Throw(CAException(kAudioHardwareIllegalOperationError)); } - + if(propertyWasChanged) { // Send notification diff --git a/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.cpp b/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.cpp index 14828bf4..e6717cc9 100644 --- a/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.cpp +++ b/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.cpp @@ -17,7 +17,7 @@ // BGM_ClientMap.cpp // BGMDriver // -// Copyright © 2016, 2017, 2019 Kyle Neideck +// Copyright © 2016, 2017, 2019, 2025 Kyle Neideck // Copyright © 2017 Andrew Tonner // @@ -264,6 +264,9 @@ std::vector * _Nullable BGM_ClientMap::GetClients(pid_t inAppPid) { } std::vector * _Nullable BGM_ClientMap::GetClients(CACFString inAppBundleID) { + if(!inAppBundleID.IsValid()) { + return nullptr; + } return GetClientsFromMap(mClientMapByBundleIDShadow, inAppBundleID); } @@ -335,12 +338,12 @@ bool BGM_ClientMap::SetClientsRelativeVolume(pid_t searchKey, Float32 inRelative bool BGM_ClientMap::SetClientsRelativeVolume(CACFString searchKey, Float32 inRelativeVolume) { bool didChangeVolume = false; - + CAMutex::Locker theShadowMapsLocker(mShadowMapsMutex); - + auto theSetVolumesInShadowMapsFunc = [&] { // Look up the clients for the key and update their volumes - + auto theClients = GetClients(searchKey); if(theClients != nullptr) { @@ -389,9 +392,9 @@ bool BGM_ClientMap::SetClientsPanPosition(pid_t searchKey, SInt32 inPanPosition) 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); diff --git a/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.h b/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.h index 6f4c3e98..1ae1a9ef 100644 --- a/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.h +++ b/BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.h @@ -17,7 +17,7 @@ // BGM_ClientMap.h // BGMDriver // -// Copyright © 2016 Kyle Neideck +// Copyright © 2016, 2025 Kyle Neideck // #ifndef __BGMDriver__BGM_ClientMap__ @@ -131,11 +131,13 @@ class BGM_ClientMap // 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 for bundle ID inAppBundleID was found and its relative volume changed. + // inAppBundleID may contain a null CFStringRef, in which case it returns false. bool SetClientsRelativeVolume(CACFString inAppBundleID, Float32 inRelativeVolume); // Returns true if a client for PID inAppPID was found and its pan position changed. bool SetClientsPanPosition(pid_t inAppPID, SInt32 inPanPosition); // Returns true if a client for bundle ID inAppBundleID was found and its pan position changed. + // inAppBundleID may contain a null CFStringRef, in which case it returns false. bool SetClientsPanPosition(CACFString inAppBundleID, SInt32 inPanPosition); void StartIONonRT(UInt32 inClientID) { UpdateClientIOStateNonRT(inClientID, true); } diff --git a/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp b/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp index 60e1de94..010dc2d7 100644 --- a/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp +++ b/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp @@ -17,7 +17,7 @@ // BGM_Clients.cpp // BGMDriver // -// Copyright © 2016, 2017, 2019 Kyle Neideck +// Copyright © 2016, 2017, 2019, 2025 Kyle Neideck // Copyright © 2017 Andrew Tonner // @@ -26,6 +26,7 @@ // Local Includes #include "BGM_Types.h" +#include "BGM_Utils.h" #include "BGM_PlugIn.h" // PublicUtility Includes @@ -324,26 +325,21 @@ bool BGM_Clients::SetClientsRelativeVolumes(const CACFArray inAppVolumes) // Get the app's PID from the dict pid_t theAppPID; bool didFindPID = theAppVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_ProcessID), theAppPID); - + // Get the app's bundle ID from the dict CACFString theAppBundleID; theAppBundleID.DontAllowRelease(); theAppVolume.GetCACFString(CFSTR(kBGMAppVolumesKey_BundleID), theAppBundleID); - - ThrowIf(!didFindPID && !theAppBundleID.IsValid(), - BGM_InvalidClientRelativeVolumeException(), - "BGM_Clients::SetClientsRelativeVolumes: App volume was sent without PID or bundle ID for app"); - + + BGMAssert(didFindPID || theAppBundleID.IsValid(), + "BGM_Clients::SetClientsRelativeVolumes: No PID or bundle ID"); + bool didGetVolume; { SInt32 theRawRelativeVolume; didGetVolume = theAppVolume.GetSInt32(CFSTR(kBGMAppVolumesKey_RelativeVolume), theRawRelativeVolume); - + if (didGetVolume) { - ThrowIf(didGetVolume && (theRawRelativeVolume < kAppRelativeVolumeMinRawValue || theRawRelativeVolume > kAppRelativeVolumeMaxRawValue), - BGM_InvalidClientRelativeVolumeException(), - "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 @@ -372,10 +368,6 @@ bool BGM_Clients::SetClientsRelativeVolumes(const CACFArray inAppVolumes) 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; From f3633510437789b566ec70f01a329abb67658aa6 Mon Sep 17 00:00:00 2001 From: Kyle Neideck Date: Tue, 30 Dec 2025 01:17:51 +1100 Subject: [PATCH 6/6] Fix BGMDriver release builds. --- BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp b/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp index 010dc2d7..62330939 100644 --- a/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp +++ b/BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp @@ -333,6 +333,7 @@ bool BGM_Clients::SetClientsRelativeVolumes(const CACFArray inAppVolumes) BGMAssert(didFindPID || theAppBundleID.IsValid(), "BGM_Clients::SetClientsRelativeVolumes: No PID or bundle ID"); + (void)didFindPID; // Suppress unused variable warning in release builds. bool didGetVolume; {