Skip to content

Commit 5a0c226

Browse files
committed
major update + bump 307lib
1 parent a253732 commit 5a0c226

File tree

5 files changed

+197
-115
lines changed

5 files changed

+197
-115
lines changed

vccli/AudioAPI.hpp

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -315,27 +315,30 @@ namespace vccli {
315315
EDataFlow defaultDevFlow{ deviceFlowFilter };
316316
if (defaultDevFlow == EDataFlow::eAll)
317317
defaultDevFlow = (defaultDevIsOutput ? EDataFlow::eRender : EDataFlow::eCapture);
318+
318319
deviceEnumerator->GetDefaultAudioEndpoint(defaultDevFlow, ERole::eMultimedia, &dev);
319320
deviceEnumerator->Release();
320321
IAudioEndpointVolume* endpoint{};
321322
dev->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (void**)&endpoint);
322-
const auto& devName{ getDeviceFriendlyName(dev) };
323+
const auto& devName{ str::trim(getDeviceFriendlyName(dev)) };
323324
dev->Release();
324325

325-
return std::make_unique<EndpointVolume>(endpoint, devName, defaultDevFlow);
326-
}
326+
return std::make_unique<EndpointVolume>(endpoint, devName, defaultDevFlow, true);
327+
} // Else we have an actual target ID to find
328+
329+
// Check if we have a valid PID
327330
std::optional<DWORD> target_pid;
328331
if (std::all_of(target_id.begin(), target_id.end(), str::stdpred::isdigit))
329332
target_pid = str::stoul(target_id);
330333

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

335339
UINT count;
336340
devices->GetCount(&count);
337341

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

@@ -345,12 +348,13 @@ namespace vccli {
345348

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

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

@@ -362,6 +366,7 @@ namespace vccli {
362366
IAudioSessionControl2* sessionControl2;
363367
ISimpleAudioVolume* sessionVolumeControl;
364368

369+
// Enumerate all audio sessions on this device:
365370
int sessionCount;
366371
sessionEnumerator->GetCount(&sessionCount);
367372

@@ -376,26 +381,48 @@ namespace vccli {
376381

377382
const auto& pname{ GetProcessNameFrom(pid) };
378383

384+
// Check if this session is a match:
379385
if ((pname.has_value() && target_id_lower == str::tolower(pname.value())) || (target_pid.has_value() && target_pid.value() == pid)) {
380386
sessionControl2->QueryInterface<ISimpleAudioVolume>(&sessionVolumeControl);
381387
sessionControl2->Release();
382388
object = std::make_unique<ApplicationVolume>(sessionVolumeControl, pname.value());
383389
break;
384390
}
385391
}
386-
387392
sessionEnumerator->Release();
388393
}
389-
390394
dev->Release();
391395
}
392396
devices->Release();
393397

394-
if (object == nullptr)
398+
if (object == nullptr) // use a NullVolume struct instead of returning nullptr
395399
object = std::make_unique<NullVolume>(target_id);
396400

397401
return object;
398402
}
403+
404+
static bool isDefaultDevice(IMMDevice* dev)
405+
{
406+
const auto& devID{ getDeviceID(dev) };
407+
IMMDeviceEnumerator* deviceEnumerator{ getDeviceEnumerator() };
408+
bool isDefault{ false };
409+
IMMDevice* tmp;
410+
std::string tmpID{};
411+
deviceEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eMultimedia, &tmp);
412+
tmpID = getDeviceID(tmp);
413+
tmp->Release();
414+
if (tmpID == devID)
415+
isDefault = true;
416+
else {
417+
deviceEnumerator->GetDefaultAudioEndpoint(EDataFlow::eCapture, ERole::eMultimedia, &tmp);
418+
tmpID = getDeviceID(tmp);
419+
tmp->Release();
420+
if (tmpID == devID)
421+
isDefault = true;
422+
}
423+
deviceEnumerator->Release();
424+
return isDefault;
425+
}
399426
/**
400427
* @brief Resolves the given identifier to a process ID by searching for it in a snapshot.
401428
* @param identifier A process name or process ID.

vccli/Volume.hpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,9 @@ namespace vccli {
116116

117117
struct EndpointVolume : VolumeController<IAudioEndpointVolume> {
118118
EDataFlow flow;
119+
bool isDefault;
119120

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

122123
bool getMuted() const override
123124
{
@@ -140,6 +141,11 @@ namespace vccli {
140141
{
141142
vol->SetMasterVolumeLevelScalar(level, &default_context);
142143
}
143-
constexpr std::optional<std::string> type_name() const override { return{ DataFlowToString(flow) + " Device"}; }
144+
constexpr std::optional<std::string> type_name() const override
145+
{
146+
std::string s{ DataFlowToString(flow) + " Device" };
147+
if (isDefault) s += " (Default)";
148+
return s;
149+
}
144150
};
145151
}

vccli/util.hpp

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ namespace vccli {
4646
return str::tolower(std::filesystem::path{ l }.replace_extension().generic_string()) == str::tolower(std::filesystem::path{ r }.replace_extension().generic_string());
4747
}
4848

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

67-
inline std::string getDeviceFriendlyName(IMMDevice* dev)
67+
inline std::string getDeviceID(IMMDevice* dev)
6868
{
69+
LPWSTR sbuf;
70+
dev->GetId(&sbuf);
71+
return w_converter.to_bytes(sbuf);
72+
}
73+
/**
74+
* @brief Retrieves the specified property value from the given device's property store.
75+
* @param dev The IMMDevice to retrieve properties from.
76+
* @param pkey The PROPERTYKEY structure to target.
77+
* @returns PROPVARIANT
78+
*/
79+
inline PROPVARIANT getDeviceProperty(IMMDevice* dev, const PROPERTYKEY& pkey)
80+
{
81+
PROPVARIANT pv{};
6982
if (IPropertyStore* properties; dev->OpenPropertyStore(STGM_READ, &properties) == S_OK) {
70-
PROPVARIANT pv;
71-
properties->GetValue(PKEY_DeviceInterface_FriendlyName, &pv);
83+
properties->GetValue(pkey, &pv);
7284
properties->Release();
73-
return w_converter.to_bytes(pv.pwszVal);
7485
}
75-
return{};
86+
return pv;
87+
}
88+
/**
89+
* @brief Retrieve the name of the given device from its properties.
90+
*\n PKEY_DeviceInterface_FriendlyName
91+
* @param dev The IMMDevice to retrieve properties from.
92+
* @returns std::string
93+
*/
94+
inline std::string getDeviceFriendlyName(IMMDevice* dev)
95+
{
96+
return w_converter.to_bytes(getDeviceProperty(dev, PKEY_DeviceInterface_FriendlyName).pwszVal);
7697
}
98+
/**
99+
* @brief Retrieve the name of the given device from its properties.
100+
*\n PKEY_Device_FriendlyName
101+
* @param dev The IMMDevice to retrieve properties from.
102+
* @returns std::string
103+
*/
77104
inline std::string getDeviceName(IMMDevice* dev)
78105
{
79-
if (IPropertyStore* properties; dev->OpenPropertyStore(STGM_READ, &properties) == S_OK) {
80-
PROPVARIANT pv;
81-
properties->GetValue(PKEY_Device_FriendlyName, &pv);
82-
properties->Release();
83-
return w_converter.to_bytes(pv.pwszVal);
84-
}
85-
return{};
106+
return w_converter.to_bytes(getDeviceProperty(dev, PKEY_Device_FriendlyName).pwszVal);
86107
}
108+
/**
109+
* @brief Retrieve the description of the given device from its properties.
110+
*\n PKEY_Device_DeviceDesc
111+
* @param dev The IMMDevice to retrieve properties from.
112+
* @returns std::string
113+
*/
87114
inline std::string getDeviceDesc(IMMDevice* dev)
88115
{
89-
if (IPropertyStore* properties; dev->OpenPropertyStore(STGM_READ, &properties) == S_OK) {
90-
PROPVARIANT pv;
91-
properties->GetValue(PKEY_Device_DeviceDesc, &pv);
92-
properties->Release();
93-
return w_converter.to_bytes(pv.pwszVal);
94-
}
95-
return{};
116+
return w_converter.to_bytes(getDeviceProperty(dev, PKEY_Device_DeviceDesc).pwszVal);
96117
}
118+
/**
119+
* @brief Queries the given device to determine whether it is an input or output device.
120+
* @param dev The IMMDevice to query.
121+
* @returns EDataFlow
122+
*/
97123
inline EDataFlow getDeviceDataFlow(IMMDevice* dev)
98124
{
99125
IMMEndpoint* endpoint;
@@ -103,6 +129,11 @@ namespace vccli {
103129
endpoint->Release();
104130
return flow;
105131
}
132+
/**
133+
* @brief Convert the given EDataFlow enumeration to a string representation.
134+
* @param dataflow An EDataFlow enum value.
135+
* @returns std::string
136+
*/
106137
inline std::string DataFlowToString(EDataFlow const& dataflow)
107138
{
108139
switch (dataflow) {
@@ -115,4 +146,9 @@ namespace vccli {
115146
return{};
116147
}
117148
}
149+
150+
inline std::ostream& operator<<(std::ostream& os, const EDataFlow& df)
151+
{
152+
return os << DataFlowToString(df);
153+
}
118154
}

0 commit comments

Comments
 (0)