Skip to content

Commit

Permalink
major update + bump 307lib
Browse files Browse the repository at this point in the history
  • Loading branch information
radj307 committed Jul 31, 2022
1 parent a253732 commit 5a0c226
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 115 deletions.
2 changes: 1 addition & 1 deletion 307lib
45 changes: 36 additions & 9 deletions vccli/AudioAPI.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,27 +315,30 @@ namespace vccli {
EDataFlow defaultDevFlow{ deviceFlowFilter };
if (defaultDevFlow == EDataFlow::eAll)
defaultDevFlow = (defaultDevIsOutput ? EDataFlow::eRender : EDataFlow::eCapture);

deviceEnumerator->GetDefaultAudioEndpoint(defaultDevFlow, ERole::eMultimedia, &dev);
deviceEnumerator->Release();
IAudioEndpointVolume* endpoint{};
dev->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (void**)&endpoint);
const auto& devName{ getDeviceFriendlyName(dev) };
const auto& devName{ str::trim(getDeviceFriendlyName(dev)) };
dev->Release();

return std::make_unique<EndpointVolume>(endpoint, devName, defaultDevFlow);
}
return std::make_unique<EndpointVolume>(endpoint, devName, defaultDevFlow, true);
} // Else we have an actual target ID to find

// Check if we have a valid PID
std::optional<DWORD> target_pid;
if (std::all_of(target_id.begin(), target_id.end(), str::stdpred::isdigit))
target_pid = str::stoul(target_id);

// Enumerate all devices of the specified I/O type(s):
IMMDeviceCollection* devices;
deviceEnumerator->EnumAudioEndpoints(deviceFlowFilter, ERole::eMultimedia, &devices);
deviceEnumerator->Release();

UINT count;
devices->GetCount(&count);


for (UINT i{ 0u }; object == nullptr && i < count; ++i) {
devices->Item(i, &dev);

Expand All @@ -345,12 +348,13 @@ namespace vccli {

const auto& deviceName{ str::trim(getDeviceFriendlyName(dev)) };

// Check if this device is a match
if (!target_pid.has_value() && (target_id_lower == str::tolower(deviceID) || target_id_lower == str::tolower(deviceName))) {
IAudioEndpointVolume* endpointVolume{};
dev->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (void**)&endpointVolume);
object = std::make_unique<EndpointVolume>(endpointVolume, deviceName, getDeviceDataFlow(dev));
object = std::make_unique<EndpointVolume>(endpointVolume, deviceName, getDeviceDataFlow(dev), isDefaultDevice(dev));
}
else {
else { // Check for matching sessions on this device:
IAudioSessionManager2* mgr{};
dev->Activate(__uuidof(IAudioSessionManager2), 0, NULL, (void**)&mgr);

Expand All @@ -362,6 +366,7 @@ namespace vccli {
IAudioSessionControl2* sessionControl2;
ISimpleAudioVolume* sessionVolumeControl;

// Enumerate all audio sessions on this device:
int sessionCount;
sessionEnumerator->GetCount(&sessionCount);

Expand All @@ -376,26 +381,48 @@ namespace vccli {

const auto& pname{ GetProcessNameFrom(pid) };

// Check if this session is a match:
if ((pname.has_value() && target_id_lower == str::tolower(pname.value())) || (target_pid.has_value() && target_pid.value() == pid)) {
sessionControl2->QueryInterface<ISimpleAudioVolume>(&sessionVolumeControl);
sessionControl2->Release();
object = std::make_unique<ApplicationVolume>(sessionVolumeControl, pname.value());
break;
}
}

sessionEnumerator->Release();
}

dev->Release();
}
devices->Release();

if (object == nullptr)
if (object == nullptr) // use a NullVolume struct instead of returning nullptr
object = std::make_unique<NullVolume>(target_id);

return object;
}

static bool isDefaultDevice(IMMDevice* dev)
{
const auto& devID{ getDeviceID(dev) };
IMMDeviceEnumerator* deviceEnumerator{ getDeviceEnumerator() };
bool isDefault{ false };
IMMDevice* tmp;
std::string tmpID{};
deviceEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eMultimedia, &tmp);
tmpID = getDeviceID(tmp);
tmp->Release();
if (tmpID == devID)
isDefault = true;
else {
deviceEnumerator->GetDefaultAudioEndpoint(EDataFlow::eCapture, ERole::eMultimedia, &tmp);
tmpID = getDeviceID(tmp);
tmp->Release();
if (tmpID == devID)
isDefault = true;
}
deviceEnumerator->Release();
return isDefault;
}
/**
* @brief Resolves the given identifier to a process ID by searching for it in a snapshot.
* @param identifier A process name or process ID.
Expand Down
10 changes: 8 additions & 2 deletions vccli/Volume.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,9 @@ namespace vccli {

struct EndpointVolume : VolumeController<IAudioEndpointVolume> {
EDataFlow flow;
bool isDefault;

EndpointVolume(IAudioEndpointVolume* vol, std::string const& resolved_name, EDataFlow const& flow) : base(vol, resolved_name), flow{ flow } {}
EndpointVolume(IAudioEndpointVolume* vol, std::string const& resolved_name, EDataFlow const& flow, const bool isDefault) : base(vol, resolved_name), flow{ flow }, isDefault{ isDefault } {}

bool getMuted() const override
{
Expand All @@ -140,6 +141,11 @@ namespace vccli {
{
vol->SetMasterVolumeLevelScalar(level, &default_context);
}
constexpr std::optional<std::string> type_name() const override { return{ DataFlowToString(flow) + " Device"}; }
constexpr std::optional<std::string> type_name() const override
{
std::string s{ DataFlowToString(flow) + " Device" };
if (isDefault) s += " (Default)";
return s;
}
};
}
76 changes: 56 additions & 20 deletions vccli/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ namespace vccli {
return str::tolower(std::filesystem::path{ l }.replace_extension().generic_string()) == str::tolower(std::filesystem::path{ r }.replace_extension().generic_string());
}

inline std::optional<std::string> GetProcessNameFrom(DWORD pid)
inline std::optional<std::string> GetProcessNameFrom(DWORD const& pid)
{
if (HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid)) {
DWORD len{ 260 };
Expand All @@ -64,36 +64,62 @@ namespace vccli {
return std::nullopt;
}

inline std::string getDeviceFriendlyName(IMMDevice* dev)
inline std::string getDeviceID(IMMDevice* dev)
{
LPWSTR sbuf;
dev->GetId(&sbuf);
return w_converter.to_bytes(sbuf);
}
/**
* @brief Retrieves the specified property value from the given device's property store.
* @param dev The IMMDevice to retrieve properties from.
* @param pkey The PROPERTYKEY structure to target.
* @returns PROPVARIANT
*/
inline PROPVARIANT getDeviceProperty(IMMDevice* dev, const PROPERTYKEY& pkey)
{
PROPVARIANT pv{};
if (IPropertyStore* properties; dev->OpenPropertyStore(STGM_READ, &properties) == S_OK) {
PROPVARIANT pv;
properties->GetValue(PKEY_DeviceInterface_FriendlyName, &pv);
properties->GetValue(pkey, &pv);
properties->Release();
return w_converter.to_bytes(pv.pwszVal);
}
return{};
return pv;
}
/**
* @brief Retrieve the name of the given device from its properties.
*\n PKEY_DeviceInterface_FriendlyName
* @param dev The IMMDevice to retrieve properties from.
* @returns std::string
*/
inline std::string getDeviceFriendlyName(IMMDevice* dev)
{
return w_converter.to_bytes(getDeviceProperty(dev, PKEY_DeviceInterface_FriendlyName).pwszVal);
}
/**
* @brief Retrieve the name of the given device from its properties.
*\n PKEY_Device_FriendlyName
* @param dev The IMMDevice to retrieve properties from.
* @returns std::string
*/
inline std::string getDeviceName(IMMDevice* dev)
{
if (IPropertyStore* properties; dev->OpenPropertyStore(STGM_READ, &properties) == S_OK) {
PROPVARIANT pv;
properties->GetValue(PKEY_Device_FriendlyName, &pv);
properties->Release();
return w_converter.to_bytes(pv.pwszVal);
}
return{};
return w_converter.to_bytes(getDeviceProperty(dev, PKEY_Device_FriendlyName).pwszVal);
}
/**
* @brief Retrieve the description of the given device from its properties.
*\n PKEY_Device_DeviceDesc
* @param dev The IMMDevice to retrieve properties from.
* @returns std::string
*/
inline std::string getDeviceDesc(IMMDevice* dev)
{
if (IPropertyStore* properties; dev->OpenPropertyStore(STGM_READ, &properties) == S_OK) {
PROPVARIANT pv;
properties->GetValue(PKEY_Device_DeviceDesc, &pv);
properties->Release();
return w_converter.to_bytes(pv.pwszVal);
}
return{};
return w_converter.to_bytes(getDeviceProperty(dev, PKEY_Device_DeviceDesc).pwszVal);
}
/**
* @brief Queries the given device to determine whether it is an input or output device.
* @param dev The IMMDevice to query.
* @returns EDataFlow
*/
inline EDataFlow getDeviceDataFlow(IMMDevice* dev)
{
IMMEndpoint* endpoint;
Expand All @@ -103,6 +129,11 @@ namespace vccli {
endpoint->Release();
return flow;
}
/**
* @brief Convert the given EDataFlow enumeration to a string representation.
* @param dataflow An EDataFlow enum value.
* @returns std::string
*/
inline std::string DataFlowToString(EDataFlow const& dataflow)
{
switch (dataflow) {
Expand All @@ -115,4 +146,9 @@ namespace vccli {
return{};
}
}

inline std::ostream& operator<<(std::ostream& os, const EDataFlow& df)
{
return os << DataFlowToString(df);
}
}
Loading

0 comments on commit 5a0c226

Please sign in to comment.