Files
fastfetch/src/detection/sound/sound_windows.cpp
T

123 lines
4.2 KiB
C++
Raw Normal View History

2023-01-25 17:17:31 +08:00
extern "C" {
2026-03-29 09:28:49 +08:00
#include "sound.h"
#include "common/windows/com.h"
2023-01-25 17:17:31 +08:00
}
2026-03-25 16:17:20 +08:00
#include "common/windows/unicode.hpp"
#include "common/windows/variant.hpp"
2023-01-25 17:17:31 +08:00
#include <initguid.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <functiondiscoverykeys_devpkey.h>
2026-03-29 09:28:49 +08:00
static void ffCoTaskMemFreeWrapper(void* pptr) {
2026-07-10 18:08:04 +08:00
assert(pptr != nullptr);
2026-03-29 09:28:49 +08:00
void* ptr = *(void**) pptr;
if (ptr) {
CoTaskMemFree(ptr);
}
2025-03-31 16:38:58 +08:00
}
2026-04-09 16:20:59 +08:00
#define FF_COTASK_AUTO_FREE FF_A_CLEANUP(ffCoTaskMemFreeWrapper)
2025-03-31 16:38:58 +08:00
2026-04-14 10:41:18 +08:00
static const char* detectSoundDevice(FFlist* devices /* List of FFSoundDevice */, IMMDevice* immDevice, LPWSTR mainDeviceId) {
2026-07-10 18:08:04 +08:00
LPWSTR FF_COTASK_AUTO_FREE immDeviceId = nullptr;
2026-04-14 10:41:18 +08:00
if (FAILED(immDevice->GetId(&immDeviceId))) {
return "immDevice->GetId() failed";
}
2026-07-10 18:08:04 +08:00
IPropertyStore* FF_AUTO_RELEASE_COM_OBJECT immPropStore = nullptr;
2026-04-14 10:41:18 +08:00
if (FAILED(immDevice->OpenPropertyStore(STGM_READ, &immPropStore))) {
return "immDevice->OpenPropertyStore() failed";
}
DWORD immState;
if (FAILED(immDevice->GetState(&immState))) {
return "immDevice->GetState() failed";
}
FFSoundDevice* device = FF_LIST_ADD(FFSoundDevice, *devices);
2026-04-14 14:05:36 +08:00
device->type = (FFSoundType) ((!mainDeviceId || wcscmp(immDeviceId, mainDeviceId) == 0 ? FF_SOUND_TYPE_MAIN : FF_SOUND_TYPE_NONE) |
((immState & DEVICE_STATE_ACTIVE) ? FF_SOUND_TYPE_ACTIVE : FF_SOUND_TYPE_NONE));
2026-04-14 10:41:18 +08:00
device->volume = FF_SOUND_VOLUME_UNKNOWN;
ffStrbufInitWS(&device->identifier, immDeviceId);
ffStrbufInit(&device->name);
ffStrbufInitStatic(&device->platformApi, "Core Audio APIs");
{
FFPropVariant friendlyName;
if (SUCCEEDED(immPropStore->GetValue(PKEY_Device_FriendlyName, &friendlyName))) {
ffStrbufSetWSV(&device->name, friendlyName.get<std::wstring_view>());
} else if (SUCCEEDED(immPropStore->GetValue(PKEY_Device_DeviceDesc, &friendlyName))) {
ffStrbufSetWSV(&device->name, friendlyName.get<std::wstring_view>());
} else {
ffStrbufSetStatic(&device->name, "Unknown Device");
}
}
2026-07-10 18:08:04 +08:00
IAudioEndpointVolume* FF_AUTO_RELEASE_COM_OBJECT immEndpointVolume = nullptr;
if (SUCCEEDED(immDevice->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, nullptr, (void**) &immEndpointVolume))) {
2026-04-14 10:41:18 +08:00
BOOL muted;
if (FAILED(immEndpointVolume->GetMute(&muted)) || !muted) {
FLOAT volume;
if (SUCCEEDED(immEndpointVolume->GetMasterVolumeLevelScalar(&volume))) {
device->volume = (uint8_t) (volume * 100 + 0.5);
}
}
}
2026-07-10 18:08:04 +08:00
return nullptr;
2026-04-14 10:41:18 +08:00
}
const char* ffDetectSound(FFSoundOptions* options, FFlist* devices /* List of FFSoundDevice */) {
2023-01-25 17:17:31 +08:00
const char* error = ffInitCom();
2026-03-29 09:28:49 +08:00
if (error) {
2023-01-25 17:17:31 +08:00
return error;
2026-03-29 09:28:49 +08:00
}
2023-01-25 17:17:31 +08:00
2026-07-10 18:08:04 +08:00
IMMDeviceEnumerator* FF_AUTO_RELEASE_COM_OBJECT pEnum = nullptr;
2023-01-25 17:17:31 +08:00
2026-07-10 18:08:04 +08:00
if (FAILED(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pEnum)))) {
2024-02-02 15:34:21 +08:00
return "CoCreateInstance(CLSID_MMDeviceEnumerator) failed";
2026-03-29 09:28:49 +08:00
}
2023-01-25 17:17:31 +08:00
2026-07-10 18:08:04 +08:00
LPWSTR FF_COTASK_AUTO_FREE mainDeviceId = nullptr;
2023-01-25 17:17:31 +08:00
{
2026-07-10 18:08:04 +08:00
IMMDevice* FF_AUTO_RELEASE_COM_OBJECT pDefaultDevice = nullptr;
2023-01-25 17:17:31 +08:00
2026-03-29 09:28:49 +08:00
if (FAILED(pEnum->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDefaultDevice))) {
2023-01-25 17:17:31 +08:00
return "GetDefaultAudioEndpoint() failed";
2026-03-29 09:28:49 +08:00
}
2023-01-25 17:17:31 +08:00
2026-04-14 10:41:18 +08:00
if (options->soundType & FF_SOUND_TYPE_MAIN) {
2026-07-10 18:08:04 +08:00
return detectSoundDevice(devices, pDefaultDevice, nullptr);
2026-04-14 10:41:18 +08:00
}
2026-03-29 09:28:49 +08:00
if (FAILED(pDefaultDevice->GetId(&mainDeviceId))) {
2023-01-25 17:17:31 +08:00
return "pDefaultDevice->GetId() failed";
2026-03-29 09:28:49 +08:00
}
2023-01-25 17:17:31 +08:00
}
2026-07-10 18:08:04 +08:00
IMMDeviceCollection* FF_AUTO_RELEASE_COM_OBJECT pDevices = nullptr;
2023-01-25 17:17:31 +08:00
2026-04-14 10:41:18 +08:00
if (FAILED(pEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE | (options->soundType & FF_SOUND_TYPE_ACTIVE ? 0 : DEVICE_STATE_DISABLED), &pDevices))) {
2023-01-25 17:17:31 +08:00
return "EnumAudioEndpoints() failed";
2026-03-29 09:28:49 +08:00
}
2023-01-25 17:17:31 +08:00
uint32_t deviceCount;
2026-03-29 09:28:49 +08:00
if (FAILED(pDevices->GetCount(&deviceCount))) {
2023-01-25 17:17:31 +08:00
return "pDevices->GetCount() failed";
2026-03-29 09:28:49 +08:00
}
2023-01-25 17:17:31 +08:00
2026-03-29 09:28:49 +08:00
for (uint32_t deviceIdx = 0; deviceIdx < deviceCount; ++deviceIdx) {
2026-07-10 18:08:04 +08:00
IMMDevice* FF_AUTO_RELEASE_COM_OBJECT immDevice = nullptr;
2026-03-29 09:28:49 +08:00
if (FAILED(pDevices->Item(deviceIdx, &immDevice))) {
2023-01-25 17:17:31 +08:00
continue;
2026-03-29 09:28:49 +08:00
}
2023-01-25 17:17:31 +08:00
2026-04-14 10:41:18 +08:00
detectSoundDevice(devices, immDevice, mainDeviceId);
2023-01-25 17:17:31 +08:00
}
2026-07-10 18:08:04 +08:00
return nullptr;
2023-01-25 17:17:31 +08:00
}