aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeal(一线灵) <[email protected]>2023-05-31 15:24:11 +0800
committerGitHub <[email protected]>2023-05-31 07:24:11 +0000
commit3caadcf616074c6bedcd45e8af2854379f08e275 (patch)
tree1c82f3e74906393c1ba767c4e6c330190c308c5f
parentcd27f8551dc593cc4fb29e1093ae45a57e6ca58e (diff)
Improve wasapi backend UWP support (#853)
* Improve wasapi, support uwp build * Fix compile errors * [UWP] Support ReadALConfig from app roaming * [UWP] Post disconnect event when default device changed * [UWP] Fix appveyor ci * [WIN32] Default device change notification support * Fix warnings * Add event to notify the app when the default device changes - Event type: AL_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT=0x19A7 - Event callback parameters: void _onALSoftEvent(ALenum eventType, ALuint object, // dataFlow: 0(render), 1(capture) ALuint param, // 0 ALsizei length, // 0 const ALchar* message, // Default device changed:<deviceId> void* userParam); * Fix warnings * Fire default device changed event in mixerProc thread * Fix compile warning * [UWP] Improve cmake * Revert changes * Notify default device change by system event callback * Revert insignificant change * Remove duplicate call
-rw-r--r--.github/workflows/ci.yml10
-rw-r--r--CMakeLists.txt84
-rw-r--r--alc/alconfig.cpp10
-rw-r--r--alc/backends/wasapi.cpp689
-rw-r--r--config.h.in3
-rw-r--r--core/async_event.h1
-rw-r--r--core/helpers.cpp25
-rw-r--r--core/uiddefs.cpp2
-rw-r--r--router/router.cpp4
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);