Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion BGMApp/BGMApp/BGMBackgroundMusicDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 7 additions & 4 deletions BGMApp/BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// MockAudioObjects.cpp
// BGMAppUnitTests
//
// Copyright © 2020 Kyle Neideck
// Copyright © 2020, 2025 Kyle Neideck
//

// Self Include
Expand All @@ -26,6 +26,9 @@
// PublicUtility Includes
#include "CACFString.h"

// STL Includes
#include <vector>


// static
MockAudioObjects::MockDeviceMap MockAudioObjects::sDevices;
Expand Down Expand Up @@ -87,9 +90,9 @@ std::shared_ptr<MockAudioDevice> 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<char> uidCString(uidCStringLen);
CACFString::GetCString(inUID, uidCString.data(), uidCStringLen);
std::string uid = std::string(uidCString.data());

return GetAudioDevice(uid);
}
Expand Down
75 changes: 73 additions & 2 deletions BGMDriver/BGMDriver/BGM_Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand Down Expand Up @@ -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<CFDictionaryRef>(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)
Expand Down Expand Up @@ -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);
Expand All @@ -1104,7 +1175,7 @@ void BGM_Device::Device_SetPropertyData(AudioObjectID inObjectID, pid_t inClient
{
Throw(CAException(kAudioHardwareIllegalOperationError));
}

if(propertyWasChanged)
{
// Send notification
Expand Down
15 changes: 9 additions & 6 deletions BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//

Expand Down Expand Up @@ -264,6 +264,9 @@ std::vector<BGM_Client*> * _Nullable BGM_ClientMap::GetClients(pid_t inAppPid) {
}

std::vector<BGM_Client*> * _Nullable BGM_ClientMap::GetClients(CACFString inAppBundleID) {
if(!inAppBundleID.IsValid()) {
return nullptr;
}
return GetClientsFromMap(mClientMapByBundleIDShadow, inAppBundleID);
}

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion BGMDriver/BGMDriver/DeviceClients/BGM_ClientMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// BGM_ClientMap.h
// BGMDriver
//
// Copyright © 2016 Kyle Neideck
// Copyright © 2016, 2025 Kyle Neideck
//

#ifndef __BGMDriver__BGM_ClientMap__
Expand Down Expand Up @@ -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); }
Expand Down
25 changes: 9 additions & 16 deletions BGMDriver/BGMDriver/DeviceClients/BGM_Clients.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//

Expand All @@ -26,6 +26,7 @@

// Local Includes
#include "BGM_Types.h"
#include "BGM_Utils.h"
#include "BGM_PlugIn.h"

// PublicUtility Includes
Expand Down Expand Up @@ -324,26 +325,22 @@ 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");
(void)didFindPID; // Suppress unused variable warning in release builds.

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
Expand Down Expand Up @@ -372,10 +369,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;
Expand Down