From afbc7d894f5c1917d6fb022438d4058c7074e0e8 Mon Sep 17 00:00:00 2001 From: loki Date: Fri, 24 Jan 2020 22:34:09 +0100 Subject: [PATCH] Created tool to identify different audio-sinks --- sunshine/platform/windows_wasapi.cpp | 6 - tools/CMakeLists.txt | 11 +- tools/audio.cpp | 250 +++++++++++++++++++++++++++ 3 files changed, 260 insertions(+), 7 deletions(-) create mode 100644 tools/audio.cpp diff --git a/sunshine/platform/windows_wasapi.cpp b/sunshine/platform/windows_wasapi.cpp index 6a1d8cd1..ca105fd9 100644 --- a/sunshine/platform/windows_wasapi.cpp +++ b/sunshine/platform/windows_wasapi.cpp @@ -2,12 +2,6 @@ // Created by loki on 1/12/20. // -// Acquire definition of CreateEventEx -//#if _WIN32_WINNT < 0x0600 -//#undef _WIN32_WINNT -//#define _WIN32_WINNT 0x0600 -//#endif - #include #include #include diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index f55c67d9..ced99407 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -9,4 +9,13 @@ set_target_properties(dxgi-info PROPERTIES CXX_STANDARD 17) target_link_libraries(dxgi-info ${CMAKE_THREAD_LIBS_INIT} dxgi) -target_compile_options(dxgi-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS}) \ No newline at end of file +target_compile_options(dxgi-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS}) + +add_executable(audio-info audio.cpp) +set_target_properties(audio-info PROPERTIES CXX_STANDARD 17) +target_link_libraries(audio-info + ${CMAKE_THREAD_LIBS_INIT} + ksuser + windowsapp + ) +target_compile_options(audio-info PRIVATE ${SUNSHINE_COMPILE_OPTIONS}) \ No newline at end of file diff --git a/tools/audio.cpp b/tools/audio.cpp new file mode 100644 index 00000000..41b3d286 --- /dev/null +++ b/tools/audio.cpp @@ -0,0 +1,250 @@ +// +// Created by loki on 1/24/20. +// + +#include +#include +#include + +#include + +#define INITGUID +#include +#undef INITGUID + +#include + +#include "sunshine/utility.h" + +DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName, 0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 2); + +using namespace std::literals; +const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); +const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); +const IID IID_IAudioClient = __uuidof(IAudioClient); +const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); + +int device_state_filter = DEVICE_STATE_ACTIVE; +namespace audio { +template +void Release(T *p) { + p->Release(); +} + +template +void co_task_free(T *p) { + CoTaskMemFree((LPVOID)p); +} + +using device_enum_t = util::safe_ptr>; +using collection_t = util::safe_ptr>; +using prop_t = util::safe_ptr>; +using device_t = util::safe_ptr>; +using audio_client_t = util::safe_ptr>; +using audio_capture_t = util::safe_ptr>; +using wave_format_t = util::safe_ptr>; + +using wstring_t = util::safe_ptr>; + +using handle_t = util::safe_ptr_v2; + +class prop_var_t { +public: + prop_var_t() { + PropVariantInit(&prop); + } + + ~prop_var_t() { + PropVariantClear(&prop); + } + + PROPVARIANT prop; +}; + +const wchar_t *no_null(const wchar_t *str) { + return str ? str : L"Unknown"; +} +void print_device(device_t &device) { + HRESULT status; + + audio::wstring_t::pointer wstring_p {}; + DWORD device_state; + + device->GetState(&device_state); + device->GetId(&wstring_p); + audio::wstring_t wstring { wstring_p }; + + audio::prop_t::pointer prop_p {}; + device->OpenPropertyStore(STGM_READ, &prop_p); + audio::prop_t prop { prop_p }; + + prop_var_t adapter_friendly_name; + prop_var_t device_friendly_name; + prop_var_t device_desc; + + prop->GetValue(PKEY_Device_FriendlyName, &device_friendly_name.prop); + prop->GetValue(PKEY_DeviceInterface_FriendlyName, &adapter_friendly_name.prop); + prop->GetValue(PKEY_Device_DeviceDesc, &device_desc.prop); + + if(!(device_state & device_state_filter)) { + return; + } + + std::wstring device_state_string = L"Unknown"s; + switch(device_state) { + case DEVICE_STATE_ACTIVE: + device_state_string = L"Active"s; + break; + case DEVICE_STATE_DISABLED: + device_state_string = L"Disabled"s; + break; + case DEVICE_STATE_UNPLUGGED: + device_state_string = L"Unplugged"s; + break; + case DEVICE_STATE_NOTPRESENT: + device_state_string = L"Not present"s; + break; + } + + std::wcout + << L"===== Device ====="sv << std::endl + << L"Device ID : "sv << wstring.get() << std::endl + << L"Device name : "sv << no_null((LPWSTR)device_friendly_name.prop.pszVal) << std::endl + << L"Adapter name : "sv << no_null((LPWSTR)adapter_friendly_name.prop.pszVal) << std::endl + << L"Device description : "sv << no_null((LPWSTR)device_desc.prop.pszVal) << std::endl + << L"Device state : "sv << device_state_string << std::endl << std::endl; + + if(device_state != DEVICE_STATE_ACTIVE) { + return; + } + + // Ensure WaveFromat is compatible + audio_client_t::pointer audio_client_p{}; + status = device->Activate( + IID_IAudioClient, + CLSCTX_ALL, + nullptr, + (void **) &audio_client_p); + audio_client_t audio_client { audio_client_p }; + + if (FAILED(status)) { + std::cout << "Couldn't activate Device: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + + return; + } + + wave_format_t::pointer wave_format_p{}; + status = audio_client->GetMixFormat(&wave_format_p); + wave_format_t wave_format { wave_format_p }; + + if (FAILED(status)) { + std::cout << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + + return; + } + + switch(wave_format->wFormatTag) { + case WAVE_FORMAT_PCM: + break; + case WAVE_FORMAT_IEEE_FLOAT: + break; + case WAVE_FORMAT_EXTENSIBLE: { + auto wave_ex = (PWAVEFORMATEXTENSIBLE) wave_format.get(); + if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { + break; + } + + std::cout << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']' << std::endl; + } + default: + std::cout << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']' << std::endl; + }; +} +} + +void print_help() { + std::cout + << "==== Help ===="sv << std::endl + << "Usage:"sv << std::endl + << " audio-info [Active|Disabled|Unplugged|Not-Present]" << std::endl; +} + +int main(int argc, char *argv[]) { + if(argc > 1) { + device_state_filter = 0; + } + + for(auto x = 1; x < argc; ++x) { + for(auto p = argv[x]; *p != '\0'; ++p) { + if(*p == ' ') { + *p = '-'; + + continue; + } + + *p = std::tolower(*p); + } + + if(argv[x] == "active"sv) { + device_state_filter |= DEVICE_STATE_ACTIVE; + } + else if(argv[x] == "disabled"sv) { + device_state_filter |= DEVICE_STATE_DISABLED; + } + else if(argv[x] == "unplugged"sv) { + device_state_filter |= DEVICE_STATE_UNPLUGGED; + } + else if(argv[x] == "not-present"sv) { + device_state_filter |= DEVICE_STATE_NOTPRESENT; + } + else { + print_help(); + return 2; + } + } + + Windows::Foundation::Initialize(RO_INIT_MULTITHREADED); + + HRESULT status; + + audio::device_enum_t::pointer device_enum_p{}; + status = CoCreateInstance( + CLSID_MMDeviceEnumerator, + nullptr, + CLSCTX_ALL, + IID_IMMDeviceEnumerator, + (void **) &device_enum_p); + audio::device_enum_t device_enum { device_enum_p }; + + if (FAILED(status)) { + std::cout << "Couldn't create Device Enumerator: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + + return -1; + } + + audio::collection_t::pointer collection_p {}; + status = device_enum->EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL, &collection_p); + audio::collection_t collection { collection_p }; + + if (FAILED(status)) { + std::cout << "Couldn't enumerate: [0x"sv << util::hex(status).to_string_view() << ']' << std::endl; + + return -1; + } + + UINT count; + collection->GetCount(&count); + + std::cout << "====== Found "sv << count << " potential audio devices ======"sv << std::endl; + for(auto x = 0; x < count; ++x) { + audio::device_t::pointer device_p {}; + collection->Item(x, &device_p); + audio::device_t device { device_p }; + + audio::print_device(device); + } + + return 0; +} \ No newline at end of file