diff options
-rw-r--r-- | .github/workflows/ci.yml | 10 | ||||
-rw-r--r-- | CMakeLists.txt | 84 | ||||
-rw-r--r-- | alc/alconfig.cpp | 10 | ||||
-rw-r--r-- | alc/backends/wasapi.cpp | 689 | ||||
-rw-r--r-- | config.h.in | 3 | ||||
-rw-r--r-- | core/async_event.h | 1 | ||||
-rw-r--r-- | core/helpers.cpp | 25 | ||||
-rw-r--r-- | core/uiddefs.cpp | 2 | ||||
-rw-r--r-- | router/router.cpp | 4 |
9 files changed, 620 insertions, 208 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a94c760..5309e1f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,16 @@ jobs: build_type: "Debug" } - { + name: "Win64-UWP", + os: windows-latest, + cmake_opts: "-A x64 \ + -DCMAKE_SYSTEM_NAME=WindowsStore \ + \"-DCMAKE_SYSTEM_VERSION=10.0\" \ + -DALSOFT_BUILD_ROUTER=ON \ + -DALSOFT_REQUIRE_WASAPI=ON", + build_type: "Release" + } + - { name: "macOS-Release", os: macos-latest, cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON", diff --git a/CMakeLists.txt b/CMakeLists.txt index 8df4be2a..02bf81b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS") FORCE) endif() endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") + set(ALSOFT_UWP TRUE) endif() set(CMAKE_C_VISIBILITY_PRESET hidden) @@ -148,6 +150,8 @@ set(CPP_DEFS ) # C pre-processor, not C++ set(INC_PATHS ) set(C_FLAGS ) set(LINKER_FLAGS ) +set(LINKER_FLAGS_DEBUG ) +set(LINKER_FLAGS_RELEASE ) set(EXTRA_LIBS ) if(WIN32) @@ -1010,37 +1014,39 @@ endif() # Check Windows-only backends if(WIN32) - # Check MMSystem backend - option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) - option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) - if(ALSOFT_BACKEND_WINMM) - set(HAVE_WINMM 1) - set(BACKENDS "${BACKENDS} WinMM,") - set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h) - # There doesn't seem to be good way to search for winmm.lib for MSVC. - # find_library doesn't find it without being told to look in a specific - # place in the WindowsSDK, but it links anyway. If there ends up being - # Windows targets without this, another means to detect it is needed. - set(EXTRA_LIBS winmm ${EXTRA_LIBS}) - endif() - - # Check DSound backend - option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) - option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) - if(ALSOFT_BACKEND_DSOUND) - check_include_file(dsound.h HAVE_DSOUND_H) - if(DXSDK_DIR) - find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h" - PATHS "${DXSDK_DIR}" PATH_SUFFIXES include - DOC "The DirectSound include directory") + if (NOT ALSOFT_UWP) + # Check MMSystem backend + option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON) + option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF) + if(ALSOFT_BACKEND_WINMM) + set(HAVE_WINMM 1) + set(BACKENDS "${BACKENDS} WinMM,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h) + # There doesn't seem to be good way to search for winmm.lib for MSVC. + # find_library doesn't find it without being told to look in a specific + # place in the WindowsSDK, but it links anyway. If there ends up being + # Windows targets without this, another means to detect it is needed. + set(EXTRA_LIBS winmm ${EXTRA_LIBS}) endif() - if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR) - set(HAVE_DSOUND 1) - set(BACKENDS "${BACKENDS} DirectSound,") - set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h) - if(NOT HAVE_DSOUND_H) - set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR}) + # Check DSound backend + option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON) + option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF) + if(ALSOFT_BACKEND_DSOUND) + check_include_file(dsound.h HAVE_DSOUND_H) + if(DXSDK_DIR) + find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h" + PATHS "${DXSDK_DIR}" PATH_SUFFIXES include + DOC "The DirectSound include directory") + endif() + if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR) + set(HAVE_DSOUND 1) + set(BACKENDS "${BACKENDS} DirectSound,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h) + + if(NOT HAVE_DSOUND_H) + set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR}) + endif() endif() endif() endif() @@ -1054,8 +1060,19 @@ if(WIN32) set(HAVE_WASAPI 1) set(BACKENDS "${BACKENDS} WASAPI,") set(ALC_OBJS ${ALC_OBJS} alc/backends/wasapi.cpp alc/backends/wasapi.h) + if(ALSOFT_UWP) + set_source_files_properties(alc/backends/wasapi.cpp alc/alconfig.cpp PROPERTIES COMPILE_FLAGS /ZW) + endif() endif() endif() + + # Setup properly link flags for UWP + if(ALSOFT_UWP AND HAVE_WASAPI) + # Add compile and link flags required C++/CX + set(C_FLAGS "/Zc:twoPhase-" ${C_FLAGS}) + set(LINKER_FLAGS_DEBUG "/nodefaultlib:vccorlibd /nodefaultlib:msvcrtd vccorlibd.lib msvcrtd.lib ${LINKER_FLAGS_DEBUG}") + set(LINKER_FLAGS_RELEASE "/nodefaultlib:vccorlib /nodefaultlib:msvcrt vccorlib.lib msvcrt.lib ${LINKER_FLAGS_RELEASE}") + endif() endif() if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM) message(FATAL_ERROR "Failed to enabled required WinMM backend") @@ -1399,6 +1416,15 @@ else() endif() target_link_libraries(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + if (WIN32) + set_target_properties(${IMPL_TARGET} PROPERTIES + LINK_FLAGS_DEBUG "${LINKER_FLAGS_DEBUG}" + LINK_FLAGS_RELEASE "${LINKER_FLAGS_RELEASE}" + LINK_FLAGS_MINSIZEREL "${LINKER_FLAGS_RELEASE}" + LINK_FLAGS_RELWITHDEBINFO "${LINKER_FLAGS_RELEASE}" + ) + endif() + if(NOT WIN32 AND NOT APPLE) # FIXME: This doesn't put a dependency on the version script. Changing # the version script will not cause a relink as it should. diff --git a/alc/alconfig.cpp b/alc/alconfig.cpp index 56cad9e0..3c9a9777 100644 --- a/alc/alconfig.cpp +++ b/alc/alconfig.cpp @@ -329,9 +329,14 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha #ifdef _WIN32 void ReadALConfig() { - WCHAR buffer[MAX_PATH]; - if(SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE) != FALSE) { +#if !defined(ALSOFT_UWP) + WCHAR buffer[MAX_PATH]; + if (!SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE)) + return; +#else + auto buffer = Windows::Storage::ApplicationData::Current->RoamingFolder->Path->Data(); +#endif std::string filepath{wstr_to_utf8(buffer)}; filepath += "\\alsoft.ini"; @@ -341,6 +346,7 @@ void ReadALConfig() LoadConfigFromFile(f); } + std::string ppath{GetProcBinary().path}; if(!ppath.empty()) { diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp index d4ad38e2..c8c03e8a 100644 --- a/alc/backends/wasapi.cpp +++ b/alc/backends/wasapi.cpp @@ -58,6 +58,7 @@ #include "albit.h" #include "alc/alconfig.h" +#include "alc/events.h" #include "alnumeric.h" #include "alspan.h" #include "comptr.h" @@ -69,6 +70,14 @@ #include "strutils.h" #include "threads.h" +#if defined(ALSOFT_UWP) +#include <collection.h> +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::Media::Devices; +using namespace Windows::Devices::Enumeration; +using namespace Windows::Media::Devices; +#endif /* Some headers seem to define these as macros for __uuidof, which is annoying * since some headers don't declare them at all. Hopefully the ifdef is enough @@ -80,11 +89,11 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); #endif - +#if !defined(ALSOFT_UWP) DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14); DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0); DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 ); - +#endif namespace { @@ -190,159 +199,504 @@ bool checkName(const al::span<DevMap> list, const std::string &name) std::vector<DevMap> PlaybackDevices; std::vector<DevMap> CaptureDevices; - -using NameGUIDPair = std::pair<std::string,std::string>; -NameGUIDPair get_device_name_and_guid(IMMDevice *device) +#if defined(ALSOFT_UWP) +enum EDataFlow { - static constexpr char UnknownName[]{"Unknown Device Name"}; - static constexpr char UnknownGuid[]{"Unknown Device GUID"}; - std::string name, guid; + eRender = 0, + eCapture = (eRender + 1), + eAll = (eCapture + 1), + EDataFlow_enum_count = (eAll + 1) +}; +#endif - ComPtr<IPropertyStore> ps; - HRESULT hr = device->OpenPropertyStore(STGM_READ, al::out_ptr(ps)); - if(FAILED(hr)) +#if defined(ALSOFT_UWP) +struct DeviceHandle +{ + DeviceHandle& operator=(std::nullptr_t) { - WARN("OpenPropertyStore failed: 0x%08lx\n", hr); - return std::make_pair(UnknownName, UnknownGuid); + value = nullptr; + return *this; } + DeviceInformation^ value{nullptr}; +}; +using EventRegistrationToken = Windows::Foundation::EventRegistrationToken; +#else +using DeviceHandle = ComPtr<IMMDevice>; +using EventRegistrationToken = void*; +#endif - PropVariant pvprop; - hr = ps->GetValue(al::bit_cast<PROPERTYKEY>(DEVPKEY_Device_FriendlyName), pvprop.get()); - if(FAILED(hr)) - { - WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr); - name += UnknownName; +#if defined(ALSOFT_UWP) +struct DeviceHelper final : public IActivateAudioInterfaceCompletionHandler +#else +struct DeviceHelper final : public IMMNotificationClient +#endif +{ +public: + DeviceHelper() + { +#if defined(ALSOFT_UWP) + mActiveClientEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); +#else + HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, + al::out_ptr(mEnumerator)); + if (SUCCEEDED(hr)) + mEnumerator->RegisterEndpointNotificationCallback(this); + else + WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); +#endif } - else if(pvprop->vt == VT_LPWSTR) - name += wstr_to_utf8(pvprop->pwszVal); - else + ~DeviceHelper() { - WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt); - name += UnknownName; +#if defined(ALSOFT_UWP) + if (mActiveClientEvent != nullptr) + CloseHandle(mActiveClientEvent); + mActiveClientEvent = nullptr; +#else + if (mEnumerator) + mEnumerator->UnregisterEndpointNotificationCallback(this); + mEnumerator = nullptr; +#endif } - pvprop.clear(); - hr = ps->GetValue(al::bit_cast<PROPERTYKEY>(PKEY_AudioEndpoint_GUID), pvprop.get()); - if(FAILED(hr)) + /** -------------------------- IUnkonwn ----------------------------- */ + LONG mRefCount{1}; + ULONG STDMETHODCALLTYPE AddRef() override { return InterlockedIncrement(&mRefCount); } + + ULONG STDMETHODCALLTYPE Release() override { - WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr); - guid = UnknownGuid; + ULONG ulRef = InterlockedDecrement(&mRefCount); + if (0 == ulRef) + { + delete this; + } + return ulRef; } - else if(pvprop->vt == VT_LPWSTR) - guid = wstr_to_utf8(pvprop->pwszVal); - else + + HRESULT STDMETHODCALLTYPE QueryInterface(const IID& IId, void** UnknownPtrPtr) override { - WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt); - guid = UnknownGuid; - } + // Three rules of QueryInterface: + // https://docs.microsoft.com/en-us/windows/win32/com/rules-for-implementing-queryinterface + // 1. Objects must have identity. + // 2. The set of interfaces on an object instance must be static. + // 3. It must be possible to query successfully for any interface on an object from any other interface. - return std::make_pair(std::move(name), std::move(guid)); -} + // If ppvObject(the address) is nullptr, then this method returns E_POINTER. + if (!UnknownPtrPtr) + { + return E_POINTER; + } -EndpointFormFactor get_device_formfactor(IMMDevice *device) -{ - ComPtr<IPropertyStore> ps; - HRESULT hr{device->OpenPropertyStore(STGM_READ, al::out_ptr(ps))}; - if(FAILED(hr)) - { - WARN("OpenPropertyStore failed: 0x%08lx\n", hr); - return UnknownFormFactor; + // https://docs.microsoft.com/en-us/windows/win32/com/implementing-reference-counting + // Whenever a client calls a method(or API function), such as QueryInterface, that returns a new interface + // pointer, the method being called is responsible for incrementing the reference count through the returned + // pointer. For example, when a client first creates an object, it receives an interface pointer to an object + // that, from the client's point of view, has a reference count of one. If the client then calls AddRef on the + // interface pointer, the reference count becomes two. The client must call Release twice on the interface + // pointer to drop all of its references to the object. +#if defined(ALSOFT_UWP) + if (IId == __uuidof(IActivateAudioInterfaceCompletionHandler)) + { + *UnknownPtrPtr = (IActivateAudioInterfaceCompletionHandler*)(this); + AddRef(); + return S_OK; + } +#else + if (IId == __uuidof(IMMNotificationClient)) + { + *UnknownPtrPtr = (IMMNotificationClient*)(this); + AddRef(); + return S_OK; + } +#endif + else if (IId == __uuidof(IAgileObject) || IId == __uuidof(IUnknown)) + { + *UnknownPtrPtr = (IUnknown*)(this); + AddRef(); + return S_OK; + } + + // This method returns S_OK if the interface is supported, and E_NOINTERFACE otherwise. + *UnknownPtrPtr = nullptr; + return E_NOINTERFACE; } - EndpointFormFactor formfactor{UnknownFormFactor}; - PropVariant pvform; - hr = ps->GetValue(PKEY_AudioEndpoint_FormFactor, pvform.get()); - if(FAILED(hr)) - WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr); - else if(pvform->vt == VT_UI4) - formfactor = static_cast<EndpointFormFactor>(pvform->ulVal); - else if(pvform->vt != VT_EMPTY) - WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt); - return formfactor; -} +#if defined(ALSOFT_UWP) + /** ----------------------- IActivateAudioInterfaceCompletionHandler ------------ */ + HRESULT ActivateCompleted(IActivateAudioInterfaceAsyncOperation* operation) override + { + HRESULT hrActivateResult = S_OK; + IUnknown* punkAudioInterface = nullptr; + HRESULT hr = operation->GetActivateResult(&hrActivateResult, &punkAudioInterface); + // Check for a successful activation result + if (SUCCEEDED(hr) && SUCCEEDED(hrActivateResult)) + { + if (mPPV) + { + // Get the pointer for the Audio Client + IAudioClient3* audioClient; + punkAudioInterface->QueryInterface(IID_PPV_ARGS(&audioClient)); + *mPPV = audioClient; + } + } -void add_device(IMMDevice *device, const WCHAR *devid, std::vector<DevMap> &list) -{ - for(auto &entry : list) + SetEvent(mActiveClientEvent); + + // Need to return S_OK + return S_OK; + } +#else + /** ----------------------- IMMNotificationClient ------------ */ + STDMETHOD(OnDeviceStateChanged(LPCWSTR /*pwstrDeviceId*/, DWORD /*dwNewState*/)) override { return S_OK; } + STDMETHOD(OnDeviceAdded(LPCWSTR /*pwstrDeviceId*/)) override { return S_OK; } + STDMETHOD(OnDeviceRemoved(LPCWSTR /*pwstrDeviceId*/)) override { return S_OK; } + STDMETHOD(OnPropertyValueChanged(LPCWSTR /*pwstrDeviceId*/, const PROPERTYKEY /*key*/)) override { return S_OK; } + STDMETHOD(OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId)) override { - if(entry.devid == devid) - return; + if (role == eMultimedia && (flow == eRender || flow == eCapture)) + { + std::lock_guard<std::mutex> lck(mDefaultChangeHandlersMtx); + for (auto& handlerItem : mDefaultChangeHandlers) + { + if (handlerItem.second.first == flow) + handlerItem.second.second(pwstrDefaultDeviceId); + } + } + return S_OK; } +#endif - auto name_guid = get_device_name_and_guid(device); + /** -------------------------- DeviceHelper ----------------------------- */ + HRESULT OpenDevice(LPCWSTR devid, EDataFlow flow, DeviceHandle& device) + { +#if !defined(ALSOFT_UWP) + HRESULT hr = E_POINTER; + if (mEnumerator) + { + if (!devid) + hr = mEnumerator->GetDefaultAudioEndpoint(flow, eMultimedia, al::out_ptr(device)); + else + hr = mEnumerator->GetDevice(devid, al::out_ptr(device)); + } + if (FAILED(hr)) + { + return hr; + } +#else + const auto deviceRole = Windows::Media::Devices::AudioDeviceRole::Default; + Platform::String^ devIfPath = + !devid ? (flow == eRender ? MediaDevice::GetDefaultAudioRenderId(deviceRole) : MediaDevice::GetDefaultAudioCaptureId(deviceRole)) + : ref new Platform::String(devid); + + Concurrency::task<DeviceInformation^> createDeviceOp( + DeviceInformation::CreateFromIdAsync(devIfPath, nullptr, DeviceInformationKind::DeviceInterface)); + auto status = createDeviceOp.then([&](DeviceInformation^ deviceInfo) { + device.value = deviceInfo; + }).wait(); + if (status != Concurrency::task_status::completed) + { + return E_NOINTERFACE; + } +#endif + return S_OK; + } - int count{1}; - std::string newname{name_guid.first}; - while(checkName(list, newname)) + HRESULT ActivateAudioClient(_In_ DeviceHandle& device, + void** ppv) { - newname = name_guid.first; - newname += " #"; - newname += std::to_string(++count); +#if !defined(ALSOFT_UWP) + HRESULT hr{device->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, nullptr, ppv)}; +#else + HRESULT hr{ActivateAudioInterface(device.value->Id->Data(), + __uuidof(IAudioClient3), nullptr, ppv)}; +#endif + return hr; } - list.emplace_back(std::move(newname), std::move(name_guid.second), devid); - const DevMap &newentry = list.back(); - TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(), - newentry.endpoint_guid.c_str(), newentry.devid.c_str()); -} + HRESULT probe_devices(EDataFlow flowdir, std::vector<DevMap>& list) + { + std::vector<DevMap>{}.swap(list); -WCHAR *get_device_id(IMMDevice *device) -{ - WCHAR *devid; +#if !defined(ALSOFT_UWP) + ComPtr<IMMDeviceCollection> coll; + HRESULT hr{mEnumerator->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, al::out_ptr(coll))}; + if (FAILED(hr)) + { + ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr); + return hr; + } - const HRESULT hr{device->GetId(&devid)}; - if(FAILED(hr)) - { - ERR("Failed to get device id: %lx\n", hr); - return nullptr; - } + UINT count{0}; + hr = coll->GetCount(&count); + if (SUCCEEDED(hr) && count > 0) + list.reserve(count); - return devid; -} + ComPtr<IMMDevice> device; + hr = mEnumerator->GetDefaultAudioEndpoint(flowdir, eMultimedia, al::out_ptr(device)); + if (SUCCEEDED(hr)) + { + if (WCHAR * devid{get_device_id(device.get())}) + { + add_device(device, devid, list); + CoTaskMemFree(devid); + } + device = nullptr; + } -void probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, std::vector<DevMap> &list) -{ - std::vector<DevMap>{}.swap(list); + for (UINT i{0}; i < count; ++i) + { + hr = coll->Item(i, al::out_ptr(device)); + if (FAILED(hr)) + continue; - ComPtr<IMMDeviceCollection> coll; - HRESULT hr{devenum->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, al::out_ptr(coll))}; - if(FAILED(hr)) - { - ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr); - return; + if (WCHAR * devid{get_device_id(device.get())}) + { + add_device(device, devid, list); + CoTaskMemFree(devid); + } + device = nullptr; + } + + return S_OK; +#else + const auto deviceRole = Windows::Media::Devices::AudioDeviceRole::Default; + auto DefaultAudioId = flowdir == eRender ? MediaDevice::GetDefaultAudioRenderId(deviceRole) + : MediaDevice::GetDefaultAudioCaptureId(deviceRole); + Concurrency::task<DeviceInformation ^> createDefaultOp(DeviceInformation::CreateFromIdAsync(DefaultAudioId, nullptr, DeviceInformationKind::DeviceInterface)); + auto task_status = createDefaultOp + .then([this, &list](DeviceInformation ^ deviceInfo) { + if (deviceInfo) + add_device(DeviceHandle{deviceInfo}, deviceInfo->Id->Data(), list); + }).wait(); + if (task_status != Concurrency::task_group_status::completed) + return E_FAIL; + + // Get the string identifier of the audio renderer + auto AudioSelector = flowdir == eRender ? MediaDevice::GetAudioRenderSelector() : MediaDevice::GetAudioCaptureSelector(); + + // Setup the asynchronous callback + Concurrency::task<DeviceInformationCollection ^> enumOperation( + DeviceInformation::FindAllAsync(AudioSelector, /*PropertyList*/nullptr, DeviceInformationKind::DeviceInterface)); + task_status = enumOperation + .then([this, &list](DeviceInformationCollection ^ DeviceInfoCollection) { + if (DeviceInfoCollection) + { + try + { + auto deviceCount = DeviceInfoCollection->Size; + for (unsigned int i = 0; i < deviceCount; ++i) + { + DeviceInformation ^ deviceInfo = DeviceInfoCollection->GetAt(i); + if (deviceInfo) + add_device(DeviceHandle{deviceInfo}, deviceInfo->Id->Data(), list); + } + } + catch (Platform::Exception ^ e) + { + } + } + }).wait(); + + return task_status == Concurrency::task_group_status::completed ? S_OK : E_FAIL; +#endif } - UINT count{0}; - hr = coll->GetCount(&count); - if(SUCCEEDED(hr) && count > 0) - list.reserve(count); + using NameGUIDPair = std::pair<std::string, std::string>; + static NameGUIDPair get_device_name_and_guid(const DeviceHandle& device) + { +#if !defined(ALSOFT_UWP) + static constexpr char UnknownName[]{"Unknown Device Name"}; + static constexpr char UnknownGuid[]{"Unknown Device GUID"}; + std::string name, guid; - ComPtr<IMMDevice> device; - hr = devenum->GetDefaultAudioEndpoint(flowdir, eMultimedia, al::out_ptr(device)); - if(SUCCEEDED(hr)) + ComPtr<IPropertyStore> ps; + HRESULT hr = device->OpenPropertyStore(STGM_READ, al::out_ptr(ps)); + if (FAILED(hr)) + { + WARN("OpenPropertyStore failed: 0x%08lx\n", hr); + return std::make_pair(UnknownName, UnknownGuid); + } + + PropVariant pvprop; + hr = ps->GetValue(al::bit_cast<PROPERTYKEY>(DEVPKEY_Device_FriendlyName), pvprop.get()); + if (FAILED(hr)) + { + WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr); + name += UnknownName; + } + else if (pvprop->vt == VT_LPWSTR) + name += wstr_to_utf8(pvprop->pwszVal); + else + { + WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt); + name += UnknownName; + } + + pvprop.clear(); + hr = ps->GetValue(al::bit_cast<PROPERTYKEY>(PKEY_AudioEndpoint_GUID), pvprop.get()); + if (FAILED(hr)) + { + WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr); + guid = UnknownGuid; + } + else if (pvprop->vt == VT_LPWSTR) + guid = wstr_to_utf8(pvprop->pwszVal); + else + { + WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt); + guid = UnknownGuid; + } + +#else + auto devInfo = device.value; + std::string name = wstr_to_utf8(devInfo->Name->Data()); + std::string guid; + // devInfo->Id is DeviceInterfacePath: \\?\SWD#MMDEVAPI#{0.0.0.00000000}.{a21c17a0-fc1d-405e-ab5a-b513422b57d1}#{e6327cad-dcec-4949-ae8a-991e976a79d2} + Platform::String ^ devIfPath = devInfo->Id; + auto wcsDevIfPath = devIfPath->Data(); + auto devIdStart = wcsstr(wcsDevIfPath, L"}."); + if (devIdStart) + { + devIdStart += 2; // L"}." + auto devIdStartEnd = wcschr(devIdStart, L'#'); + if (devIdStartEnd) + { + std::wstring wDevId{devIdStart, static_cast<size_t>(devIdStartEnd - devIdStart)}; + guid = wstr_to_utf8(wDevId.c_str()); + std::transform(guid.begin(), guid.end(), guid.begin(), [](char ch) { return static_cast<char>(std::toupper(ch)); }); + } + } +#endif + return std::make_pair(std::move(name), std::move(guid)); + } + + static void add_device(const DeviceHandle& device, const WCHAR* devid, std::vector<DevMap>& list) { - if(WCHAR *devid{get_device_id(device.get())}) + for (auto& entry : list) { - add_device(device.get(), devid, list); - CoTaskMemFree(devid); + if (entry.devid == devid) + return; } + + auto name_guid = get_device_name_and_guid(device); + int count{1}; + std::string newname{name_guid.first}; + while (checkName(list, newname)) + { + newname = name_guid.first; + newname += " #"; + newname += std::to_string(++count); + } + list.emplace_back(std::move(newname), std::move(name_guid.second), devid); + const DevMap& newentry = list.back(); + + TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(), newentry.endpoint_guid.c_str(), + newentry.devid.c_str()); } - for(UINT i{0};i < count;++i) +#if !defined(ALSOFT_UWP) + static WCHAR* get_device_id(IMMDevice* device) { - device = nullptr; - hr = coll->Item(i, al::out_ptr(device)); - if(FAILED(hr)) continue; + WCHAR* devid; - if(WCHAR *devid{get_device_id(device.get())}) + const HRESULT hr{device->GetId(&devid)}; + if (FAILED(hr)) { - add_device(device.get(), devid, list); - CoTaskMemFree(devid); + ERR("Failed to get device id: %lx\n", hr); + return nullptr; } + + return devid; } -} + static EndpointFormFactor get_device_formfactor(IMMDevice* device) + { + ComPtr<IPropertyStore> ps; + HRESULT hr{device->OpenPropertyStore(STGM_READ, al::out_ptr(ps))}; + if (FAILED(hr)) + { + WARN("OpenPropertyStore failed: 0x%08lx\n", hr); + return UnknownFormFactor; + } + + EndpointFormFactor formfactor{UnknownFormFactor}; + PropVariant pvform; + hr = ps->GetValue(PKEY_AudioEndpoint_FormFactor, pvform.get()); + if (FAILED(hr)) + WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr); + else if (pvform->vt == VT_UI4) + formfactor = static_cast<EndpointFormFactor>(pvform->ulVal); + else if (pvform->vt != VT_EMPTY) + WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt); + return formfactor; + } +#endif + + template <typename _Fty> + EventRegistrationToken RegisterDefaultChangeHandler(EDataFlow flow, void* target, _Fty&& cb) + { +#if defined(ALSOFT_UWP) + (void)target; + if (flow == eRender) + return MediaDevice::DefaultAudioRenderDeviceChanged += + ref new TypedEventHandler<Platform::Object ^, DefaultAudioRenderDeviceChangedEventArgs ^>( + [this, cb](Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args) { + if (args->Role == AudioDeviceRole::Default) + cb(args->Id->Data()); + }); + else + return MediaDevice::DefaultAudioCaptureDeviceChanged += + ref new TypedEventHandler<Platform::Object ^, DefaultAudioCaptureDeviceChangedEventArgs ^>( + [this, cb](Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args) { + if (args->Role == AudioDeviceRole::Default) + cb(args->Id->Data()); + }); +#else + std::lock_guard<std::mutex> lck(mDefaultChangeHandlersMtx); + if (mDefaultChangeHandlers.emplace(target, std::make_pair(flow, cb)).second) + return target; + return nullptr; +#endif + } + + void UnregisterDefaultChangeHandler(EventRegistrationToken handler) + { +#if defined(ALSOFT_UWP) + MediaDevice::DefaultAudioRenderDeviceChanged -= handler; +#else + std::lock_guard<std::mutex> lck(mDefaultChangeHandlersMtx); + mDefaultChangeHandlers.erase(handler); +#endif + } +private: +#if defined(ALSOFT_UWP) + HRESULT ActivateAudioInterface(_In_ LPCWSTR deviceInterfacePath, + _In_ REFIID riid, + _In_opt_ PROPVARIANT* activationParams, + void** ppv) + { + IActivateAudioInterfaceAsyncOperation* asyncOp{nullptr}; + mPPV = ppv; + HRESULT hr = ActivateAudioInterfaceAsync(deviceInterfacePath, riid, activationParams, this, &asyncOp); + if (FAILED(hr)) + return hr; + if (asyncOp) + asyncOp->Release(); + + DWORD res{WaitForSingleObjectEx(mActiveClientEvent, 2000, FALSE)}; + if (res != WAIT_OBJECT_0) + ERR("WaitForSingleObjectEx error: 0x%lx\n", res); + return res; + } + + HANDLE mActiveClientEvent{nullptr}; + void** mPPV{nullptr}; +#else + ComPtr<IMMDeviceEnumerator> mEnumerator{nullptr}; + std::mutex mDefaultChangeHandlersMtx; + std::unordered_map<void*, std::pair<EDataFlow,std::function<void(LPCWSTR)>>> mDefaultChangeHandlers; +#endif +}; bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) { @@ -473,6 +827,8 @@ struct WasapiProxy { static std::mutex sThreadLock; static size_t sInitCount; + static ComPtr<DeviceHelper> sDeviceHelper; + std::future<HRESULT> pushMessage(MsgType type, const char *param=nullptr) { std::promise<HRESULT> promise; @@ -544,6 +900,7 @@ std::deque<WasapiProxy::Msg> WasapiProxy::mMsgQueue; std::mutex WasapiProxy::mMsgQueueLock; std::condition_variable WasapiProxy::mMsgQueueCond; std::mutex WasapiProxy::sThreadLock; +ComPtr<DeviceHelper> WasapiProxy::sDeviceHelper; size_t WasapiProxy::sInitCount{0}; int WasapiProxy::messageHandler(std::promise<HRESULT> *promise) @@ -560,6 +917,8 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise) promise->set_value(S_OK); promise = nullptr; + sDeviceHelper.reset(new DeviceHelper()); + TRACE("Starting message loop\n"); while(Msg msg{popMessage()}) { @@ -597,19 +956,12 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise) case MsgType::EnumeratePlayback: case MsgType::EnumerateCapture: { - ComPtr<IMMDeviceEnumerator> devenum; - hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, - IID_IMMDeviceEnumerator, al::out_ptr(devenum)); - if(FAILED(hr)) - msg.mPromise.set_value(hr); - else - { - if(msg.mType == MsgType::EnumeratePlayback) - probe_devices(devenum.get(), eRender, PlaybackDevices); - else if(msg.mType == MsgType::EnumerateCapture) - probe_devices(devenum.get(), eCapture, CaptureDevices); - msg.mPromise.set_value(S_OK); - } + if (msg.mType == MsgType::EnumeratePlayback) + msg.mPromise.set_value(sDeviceHelper->probe_devices(eRender, PlaybackDevices)); + else if (msg.mType == MsgType::EnumerateCapture) + msg.mPromise.set_value(sDeviceHelper->probe_devices(eCapture, CaptureDevices)); + else + msg.mPromise.set_value(E_FAIL); continue; } @@ -625,7 +977,6 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise) return 0; } - struct WasapiPlayback final : public BackendBase, WasapiProxy { WasapiPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~WasapiPlayback() override; @@ -646,7 +997,7 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { ClockLatency getClockLatency() override; HRESULT mOpenStatus{E_FAIL}; - ComPtr<IMMDevice> mMMDev{nullptr}; + DeviceHandle mMMDev{nullptr}; ComPtr<IAudioClient> mClient{nullptr}; ComPtr<IAudioRenderClient> mRender{nullptr}; HANDLE mNotifyEvent{nullptr}; @@ -664,6 +1015,9 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { std::atomic<bool> mKillNow{true}; std::thread mThread; + std::string mDefaultDeviceId; + EventRegistrationToken mDefaultChangeHandler{}; + DEF_NEWDEL(WasapiPlayback) }; @@ -838,33 +1192,29 @@ HRESULT WasapiPlayback::openProxy(const char *name) devid = iter->devid.c_str(); } - ComPtr<IMMDeviceEnumerator> enumerator; - ComPtr<IMMDevice> mmdev; - HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, - IID_IMMDeviceEnumerator, al::out_ptr(enumerator))}; - if(SUCCEEDED(hr)) - { - if(!devid) - hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, al::out_ptr(mmdev)); - else - hr = enumerator->GetDevice(devid, al::out_ptr(mmdev)); - } - if(FAILED(hr)) + HRESULT hr{sDeviceHelper->OpenDevice(devid, eRender, mMMDev)}; + if (FAILED(hr)) { - WARN("Failed to open device \"%s\"\n", name?name:"(default)"); + WARN("Failed to open device \"%s\"\n", name ? name : "(default)"); return hr; } - mClient = nullptr; - mMMDev = std::move(mmdev); - if(name) mDevice->DeviceName = std::string{DevNameHead} + name; - else mDevice->DeviceName = DevNameHead + get_device_name_and_guid(mMMDev.get()).first; + if (name) + mDevice->DeviceName = std::string{DevNameHead} + name; + else + mDevice->DeviceName = DevNameHead + DeviceHelper::get_device_name_and_guid(mMMDev).first; - return hr; + mDefaultChangeHandler = sDeviceHelper->RegisterDefaultChangeHandler(eRender, this, [this](LPCWSTR devid) { + mDefaultDeviceId = wstr_to_utf8(devid); + alc::Event(alc::EventType::DefaultDeviceChanged, (ALCdevice*)mDevice, mDefaultDeviceId); + }); + + return S_OK; } void WasapiPlayback::closeProxy() { + sDeviceHelper->UnregisterDefaultChangeHandler(mDefaultChangeHandler); mClient = nullptr; mMMDev = nullptr; } @@ -881,9 +1231,7 @@ bool WasapiPlayback::reset() HRESULT WasapiPlayback::resetProxy() { mClient = nullptr; - - HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, - al::out_ptr(mClient))}; + HRESULT hr{sDeviceHelper->ActivateAudioClient(mMMDev, al::out_ptr(mClient))}; if(FAILED(hr)) { ERR("Failed to reactivate audio client: 0x%08lx\n", hr); @@ -1154,9 +1502,12 @@ HRESULT WasapiPlayback::resetProxy() } mFormat = OutputType; - const EndpointFormFactor formfactor{get_device_formfactor(mMMDev.get())}; +#if !defined(ALSOFT_UWP) + const EndpointFormFactor formfactor{DeviceHelper::get_device_formfactor(mMMDev.get())}; mDevice->Flags.set(DirectEar, (formfactor == Headphones || formfactor == Headset)); - +#else + mDevice->Flags.set(DirectEar, false); +#endif setDefaultWFXChannelOrder(); hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, @@ -1313,7 +1664,7 @@ struct WasapiCapture final : public BackendBase, WasapiProxy { uint availableSamples() override; HRESULT mOpenStatus{E_FAIL}; - ComPtr<IMMDevice> mMMDev{nullptr}; + DeviceHandle mMMDev{nullptr}; ComPtr<IAudioClient> mClient{nullptr}; ComPtr<IAudioCaptureClient> mCapture{nullptr}; HANDLE mNotifyEvent{nullptr}; @@ -1325,6 +1676,9 @@ struct WasapiCapture final : public BackendBase, WasapiProxy { std::atomic<bool> mKillNow{true}; std::thread mThread; + std::string mDefaultDeviceId; + EventRegistrationToken mDefaultChangeHandler{}; + DEF_NEWDEL(WasapiCapture) }; @@ -1509,31 +1863,28 @@ HRESULT WasapiCapture::openProxy(const char *name) devid = iter->devid.c_str(); } - ComPtr<IMMDeviceEnumerator> enumerator; - HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, - IID_IMMDeviceEnumerator, al::out_ptr(enumerator))}; - if(SUCCEEDED(hr)) - { - if(!devid) - hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, al::out_ptr(mMMDev)); - else - hr = enumerator->GetDevice(devid, al::out_ptr(mMMDev)); - } - if(FAILED(hr)) + HRESULT hr{sDeviceHelper->OpenDevice(devid, eCapture, mMMDev)}; + if (FAILED(hr)) { - WARN("Failed to open device \"%s\"\n", name?name:"(default)"); + WARN("Failed to open device \"%s\"\n", name ? name : "(default)"); return hr; } - mClient = nullptr; - if(name) mDevice->DeviceName = std::string{DevNameHead} + name; - else mDevice->DeviceName = DevNameHead + get_device_name_and_guid(mMMDev.get()).first; + if (name) + mDevice->DeviceName = std::string{ DevNameHead } + name; + else + mDevice->DeviceName = DevNameHead + DeviceHelper::get_device_name_and_guid(mMMDev).first; - return hr; + mDefaultChangeHandler = sDeviceHelper->RegisterDefaultChangeHandler(eCapture, this, [this](LPCWSTR devid) { + mDefaultDeviceId = wstr_to_utf8(devid); + alc::Event(alc::EventType::DefaultDeviceChanged, (ALCdevice*)mDevice, mDefaultDeviceId); + }); + return S_OK; } void WasapiCapture::closeProxy() { + sDeviceHelper->UnregisterDefaultChangeHandler(mDefaultChangeHandler); mClient = nullptr; mMMDev = nullptr; } @@ -1542,8 +1893,7 @@ HRESULT WasapiCapture::resetProxy() { mClient = nullptr; - HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, - al::out_ptr(mClient))}; + HRESULT hr{sDeviceHelper->ActivateAudioClient(mMMDev, al::out_ptr(mClient))}; if(FAILED(hr)) { ERR("Failed to reactivate audio client: 0x%08lx\n", hr); @@ -1904,13 +2254,14 @@ bool WasapiBackendFactory::init() WARN("Failed to initialize COM: 0x%08lx\n", hr); return hr; } - +#if !defined(ALSOFT_UWP) ComPtr<IMMDeviceEnumerator> enumerator; hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, al::out_ptr(enumerator)); if(FAILED(hr)) WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); enumerator = nullptr; +#endif CoUninitialize(); return hr; diff --git a/config.h.in b/config.h.in index 477d8c77..d1cf0395 100644 --- a/config.h.in +++ b/config.h.in @@ -117,3 +117,6 @@ /* Define the installation data directory */ #cmakedefine ALSOFT_INSTALL_DATADIR "@ALSOFT_INSTALL_DATADIR@" + +/* Define whether build alsoft for winuwp */ +#cmakedefine ALSOFT_UWP diff --git a/core/async_event.h b/core/async_event.h index c049fa02..f1ca0c7b 100644 --- a/core/async_event.h +++ b/core/async_event.h @@ -15,7 +15,6 @@ enum class AsyncEnableBits : uint8_t { SourceState, BufferCompleted, Disconnected, - Count }; diff --git a/core/helpers.cpp b/core/helpers.cpp index 58cc74e5..f9de25cf 100644 --- a/core/helpers.cpp +++ b/core/helpers.cpp @@ -3,6 +3,11 @@ #include "helpers.h" +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#endif + #include <algorithm> #include <cerrno> #include <cstdarg> @@ -40,7 +45,7 @@ const PathNamePair &GetProcBinary() { static std::optional<PathNamePair> procbin; if(procbin) return *procbin; - +#if !defined(ALSOFT_UWP) auto fullpath = std::vector<WCHAR>(256); DWORD len{GetModuleFileNameW(nullptr, fullpath.data(), static_cast<DWORD>(fullpath.size()))}; while(len == fullpath.size()) @@ -58,7 +63,16 @@ const PathNamePair &GetProcBinary() fullpath.resize(len); if(fullpath.back() != 0) fullpath.push_back(0); - +#else + auto exePath = __wargv[0]; + if (!exePath) + { + ERR("Failed to get process name: error %lu\n", GetLastError()); + procbin.emplace(); + return *procbin; + } + std::vector<WCHAR> fullpath{exePath, exePath + wcslen(exePath) + 1}; +#endif std::replace(fullpath.begin(), fullpath.end(), '/', '\\'); auto sep = std::find(fullpath.rbegin()+1, fullpath.rend(), '\\'); if(sep != fullpath.rend()) @@ -84,7 +98,7 @@ void DirectorySearch(const char *path, const char *ext, std::vector<std::string> std::wstring wpath{utf8_to_wstr(pathstr.c_str())}; WIN32_FIND_DATAW fdata; - HANDLE hdl{FindFirstFileW(wpath.c_str(), &fdata)}; + HANDLE hdl{FindFirstFileExW(wpath.c_str(), FindExInfoStandard, &fdata, FindExSearchNameMatch, NULL, 0)}; if(hdl == INVALID_HANDLE_VALUE) return; const auto base = results->size(); @@ -97,7 +111,6 @@ void DirectorySearch(const char *path, const char *ext, std::vector<std::string> str += wstr_to_utf8(fdata.cFileName); } while(FindNextFileW(hdl, &fdata)); FindClose(hdl); - const al::span<std::string> newlist{results->data()+base, results->size()-base}; std::sort(newlist.begin(), newlist.end()); for(const auto &name : newlist) @@ -149,6 +162,7 @@ std::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) std::replace(path.begin(), path.end(), '/', '\\'); DirectorySearch(path.c_str(), ext, &results); +#if !defined(ALSOFT_UWP) /* Search the local and global data dirs. */ static const int ids[2]{ CSIDL_APPDATA, CSIDL_COMMON_APPDATA }; for(int id : ids) @@ -165,17 +179,20 @@ std::vector<std::string> SearchDataFiles(const char *ext, const char *subdir) DirectorySearch(path.c_str(), ext, &results); } +#endif return results; } void SetRTPriority(void) { +#if !defined(ALSOFT_UWP) if(RTPrioLevel > 0) { if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) ERR("Failed to set priority level for thread\n"); } +#endif } #else diff --git a/core/uiddefs.cpp b/core/uiddefs.cpp index 244c01a5..833150f5 100644 --- a/core/uiddefs.cpp +++ b/core/uiddefs.cpp @@ -24,7 +24,7 @@ DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2); DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17); -#ifdef HAVE_WASAPI +#if defined(HAVE_WASAPI) && !defined(ALSOFT_UWP) #include <wtypes.h> #include <devpropdef.h> #include <propkeydef.h> diff --git a/router/router.cpp b/router/router.cpp index 3c891053..18ecf9b4 100644 --- a/router/router.cpp +++ b/router/router.cpp @@ -334,7 +334,7 @@ void LoadDriverList(void) TRACE("Got DLL path %ls\n", dll_path); GetCurrentDirectoryW(MAX_PATH, cwd_path); - len = lstrlenW(cwd_path); + len = wcslen(cwd_path); if(len > 0 && (cwd_path[len-1] == '\\' || cwd_path[len-1] == '/')) cwd_path[len-1] = '\0'; TRACE("Got current working directory %ls\n", cwd_path); @@ -343,7 +343,7 @@ void LoadDriverList(void) TRACE("Got proc path %ls\n", proc_path); GetSystemDirectoryW(sys_path, MAX_PATH); - len = lstrlenW(sys_path); + len = wcslen(sys_path); if(len > 0 && (sys_path[len-1] == '\\' || sys_path[len-1] == '/')) sys_path[len-1] = '\0'; TRACE("Got system path %ls\n", sys_path); |