aboutsummaryrefslogtreecommitdiffstats
path: root/alc/backends/wasapi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'alc/backends/wasapi.cpp')
-rw-r--r--alc/backends/wasapi.cpp262
1 files changed, 130 insertions, 132 deletions
diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp
index 0786a7d7..b594aebe 100644
--- a/alc/backends/wasapi.cpp
+++ b/alc/backends/wasapi.cpp
@@ -238,10 +238,9 @@ struct DevMap {
bool checkName(const al::vector<DevMap> &list, const std::string &name)
{
- return std::find_if(list.cbegin(), list.cend(),
- [&name](const DevMap &entry) -> bool
- { return entry.name == name; }
- ) != list.cend();
+ auto match_name = [&name](const DevMap &entry) -> bool
+ { return entry.name == name; };
+ return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
}
al::vector<DevMap> PlaybackDevices;
@@ -297,26 +296,26 @@ NameGUIDPair get_device_name_and_guid(IMMDevice *device)
return std::make_pair(std::move(name), std::move(guid));
}
-void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
+EndpointFormFactor get_device_formfactor(IMMDevice *device)
{
ComPtr<IPropertyStore> ps;
- HRESULT hr = device->OpenPropertyStore(STGM_READ, ps.getPtr());
+ HRESULT hr{device->OpenPropertyStore(STGM_READ, ps.getPtr())};
if(FAILED(hr))
{
WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
- return;
+ return UnknownFormFactor;
}
+ EndpointFormFactor formfactor{UnknownFormFactor};
PropVariant pvform;
- hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_FormFactor), pvform.get());
+ 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)
- *formfactor = UnknownFormFactor;
- else
+ formfactor = static_cast<EndpointFormFactor>(pvform->ulVal);
+ else if(pvform->vt != VT_EMPTY)
WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt);
+ return formfactor;
}
@@ -484,6 +483,7 @@ void TraceFormat(const char *msg, const WAVEFORMATEX *format)
enum class MsgType {
OpenDevice,
+ ReopenDevice,
ResetDevice,
StartDevice,
StopDevice,
@@ -497,6 +497,7 @@ enum class MsgType {
constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{
"Open Device",
+ "Reopen Device",
"Reset Device",
"Start Device",
"Stop Device",
@@ -511,7 +512,7 @@ constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{
struct WasapiProxy {
virtual ~WasapiProxy() = default;
- virtual HRESULT openProxy() = 0;
+ virtual HRESULT openProxy(const char *name) = 0;
virtual void closeProxy() = 0;
virtual HRESULT resetProxy() = 0;
@@ -521,19 +522,20 @@ struct WasapiProxy {
struct Msg {
MsgType mType;
WasapiProxy *mProxy;
+ const char *mParam;
std::promise<HRESULT> mPromise;
};
static std::deque<Msg> mMsgQueue;
static std::mutex mMsgQueueLock;
static std::condition_variable mMsgQueueCond;
- std::future<HRESULT> pushMessage(MsgType type)
+ std::future<HRESULT> pushMessage(MsgType type, const char *param=nullptr)
{
std::promise<HRESULT> promise;
std::future<HRESULT> future{promise.get_future()};
{
std::lock_guard<std::mutex> _{mMsgQueueLock};
- mMsgQueue.emplace_back(Msg{type, this, std::move(promise)});
+ mMsgQueue.emplace_back(Msg{type, this, param, std::move(promise)});
}
mMsgQueueCond.notify_one();
return future;
@@ -545,7 +547,7 @@ struct WasapiProxy {
std::future<HRESULT> future{promise.get_future()};
{
std::lock_guard<std::mutex> _{mMsgQueueLock};
- mMsgQueue.emplace_back(Msg{type, nullptr, std::move(promise)});
+ mMsgQueue.emplace_back(Msg{type, nullptr, nullptr, std::move(promise)});
}
mMsgQueueCond.notify_one();
return future;
@@ -600,9 +602,9 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
Msg msg;
while(popMessage(msg))
{
- TRACE("Got message \"%s\" (0x%04x, this=%p)\n",
+ TRACE("Got message \"%s\" (0x%04x, this=%p, param=%p)\n",
MessageStr[static_cast<size_t>(msg.mType)], static_cast<uint>(msg.mType),
- decltype(std::declval<void*>()){msg.mProxy});
+ static_cast<void*>(msg.mProxy), static_cast<const void*>(msg.mParam));
switch(msg.mType)
{
@@ -611,7 +613,7 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
if(++deviceCount == 1)
hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if(SUCCEEDED(hr))
- hr = msg.mProxy->openProxy();
+ hr = msg.mProxy->openProxy(msg.mParam);
msg.mPromise.set_value(hr);
if(FAILED(hr))
@@ -621,6 +623,11 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
}
continue;
+ case MsgType::ReopenDevice:
+ hr = msg.mProxy->openProxy(msg.mParam);
+ msg.mPromise.set_value(hr);
+ continue;
+
case MsgType::ResetDevice:
hr = msg.mProxy->resetProxy();
msg.mPromise.set_value(hr);
@@ -688,7 +695,7 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy {
int mixerProc();
void open(const char *name) override;
- HRESULT openProxy() override;
+ HRESULT openProxy(const char *name) override;
void closeProxy() override;
bool reset() override;
@@ -700,8 +707,6 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy {
ClockLatency getClockLatency() override;
- std::wstring mDevId;
-
HRESULT mOpenStatus{E_FAIL};
ComPtr<IMMDevice> mMMDev{nullptr};
ComPtr<IAudioClient> mClient{nullptr};
@@ -796,83 +801,86 @@ void WasapiPlayback::open(const char *name)
{
HRESULT hr{S_OK};
- mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
- if(mNotifyEvent == nullptr)
+ if(!mNotifyEvent)
{
- ERR("Failed to create notify events: %lu\n", GetLastError());
- hr = E_FAIL;
- }
-
- if(SUCCEEDED(hr))
- {
- if(name)
+ mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
+ if(mNotifyEvent == nullptr)
{
- if(PlaybackDevices.empty())
- pushMessage(MsgType::EnumeratePlayback).wait();
-
+ ERR("Failed to create notify events: %lu\n", GetLastError());
hr = E_FAIL;
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name || entry.endpoint_guid == name; });
- if(iter == PlaybackDevices.cend())
- {
- const std::wstring wname{utf8_to_wstr(name)};
- iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
- [&wname](const DevMap &entry) -> bool
- { return entry.devid == wname; });
- }
- if(iter == PlaybackDevices.cend())
- WARN("Failed to find device name matching \"%s\"\n", name);
- else
- {
- mDevId = iter->devid;
- mDevice->DeviceName = iter->name;
- hr = S_OK;
- }
}
}
if(SUCCEEDED(hr))
- hr = pushMessage(MsgType::OpenDevice).get();
- mOpenStatus = hr;
-
- if(FAILED(hr))
{
- if(mNotifyEvent != nullptr)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
+ if(name && PlaybackDevices.empty())
+ pushMessage(MsgType::EnumeratePlayback).wait();
- mDevId.clear();
+ if(SUCCEEDED(mOpenStatus))
+ hr = pushMessage(MsgType::ReopenDevice, name).get();
+ else
+ {
+ hr = pushMessage(MsgType::OpenDevice, name).get();
+ mOpenStatus = hr;
+ }
+ }
+ if(FAILED(hr))
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
hr};
- }
}
-HRESULT WasapiPlayback::openProxy()
+HRESULT WasapiPlayback::openProxy(const char *name)
{
+ const wchar_t *devid{nullptr};
+ if(name)
+ {
+ auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
+ [name](const DevMap &entry) -> bool
+ { return entry.name == name || entry.endpoint_guid == name; });
+ if(iter == PlaybackDevices.cend())
+ {
+ const std::wstring wname{utf8_to_wstr(name)};
+ iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
+ [&wname](const DevMap &entry) -> bool
+ { return entry.devid == wname; });
+ }
+ if(iter == PlaybackDevices.cend())
+ {
+ WARN("Failed to find device name matching \"%s\"\n", name);
+ return E_FAIL;
+ }
+ name = iter->name.c_str();
+ devid = iter->devid.c_str();
+ }
+
void *ptr;
HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
IID_IMMDeviceEnumerator, &ptr)};
- if(SUCCEEDED(hr))
+ ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
+ ComPtr<IMMDevice> mmdev;
+ if(!devid)
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mmdev.getPtr());
+ else
+ hr = enumerator->GetDevice(devid, mmdev.getPtr());
+ enumerator = nullptr;
+ if(FAILED(hr))
{
- ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
- if(mDevId.empty())
- hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mMMDev.getPtr());
- else
- hr = enumerator->GetDevice(mDevId.c_str(), mMMDev.getPtr());
+ WARN("Failed to open device \"%s\"\n", name?name:"(default)");
+ return hr;
}
- if(SUCCEEDED(hr))
- hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
- if(SUCCEEDED(hr))
+
+ hr = mmdev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
+ if(FAILED(hr))
{
- mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
- if(mDevice->DeviceName.empty())
- mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
+ WARN("Failed to activate device \"%s\"\n", name?name:"(default)");
+ return hr;
}
- if(FAILED(hr))
- mMMDev = nullptr;
+ mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
+ mMMDev = std::move(mmdev);
+ if(name) mDevice->DeviceName = name;
+ else mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
return hr;
}
@@ -1105,8 +1113,7 @@ HRESULT WasapiPlayback::resetProxy()
}
mFrameStep = OutputType.Format.nChannels;
- EndpointFormFactor formfactor{UnknownFormFactor};
- get_device_formfactor(mMMDev.get(), &formfactor);
+ const EndpointFormFactor formfactor{get_device_formfactor(mMMDev.get())};
mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo
&& (formfactor == Headphones || formfactor == Headset));
@@ -1226,7 +1233,7 @@ struct WasapiCapture final : public BackendBase, WasapiProxy {
int recordProc();
void open(const char *name) override;
- HRESULT openProxy() override;
+ HRESULT openProxy(const char *name) override;
void closeProxy() override;
HRESULT resetProxy() override;
@@ -1238,8 +1245,6 @@ struct WasapiCapture final : public BackendBase, WasapiProxy {
void captureSamples(al::byte *buffer, uint samples) override;
uint availableSamples() override;
- std::wstring mDevId;
-
HRESULT mOpenStatus{E_FAIL};
ComPtr<IMMDevice> mMMDev{nullptr};
ComPtr<IAudioClient> mClient{nullptr};
@@ -1373,48 +1378,15 @@ void WasapiCapture::open(const char *name)
if(SUCCEEDED(hr))
{
- if(name)
- {
- if(CaptureDevices.empty())
- pushMessage(MsgType::EnumerateCapture).wait();
-
- hr = E_FAIL;
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [name](const DevMap &entry) -> bool
- { return entry.name == name || entry.endpoint_guid == name; });
- if(iter == CaptureDevices.cend())
- {
- const std::wstring wname{utf8_to_wstr(name)};
- iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
- [&wname](const DevMap &entry) -> bool
- { return entry.devid == wname; });
- }
- if(iter == CaptureDevices.cend())
- WARN("Failed to find device name matching \"%s\"\n", name);
- else
- {
- mDevId = iter->devid;
- mDevice->DeviceName = iter->name;
- hr = S_OK;
- }
- }
+ if(name && CaptureDevices.empty())
+ pushMessage(MsgType::EnumerateCapture).wait();
+ hr = pushMessage(MsgType::OpenDevice, name).get();
}
-
- if(SUCCEEDED(hr))
- hr = pushMessage(MsgType::OpenDevice).get();
mOpenStatus = hr;
if(FAILED(hr))
- {
- if(mNotifyEvent != nullptr)
- CloseHandle(mNotifyEvent);
- mNotifyEvent = nullptr;
-
- mDevId.clear();
-
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
hr};
- }
hr = pushMessage(MsgType::ResetDevice).get();
if(FAILED(hr))
@@ -1425,30 +1397,56 @@ void WasapiCapture::open(const char *name)
}
}
-HRESULT WasapiCapture::openProxy()
+HRESULT WasapiCapture::openProxy(const char *name)
{
+ const wchar_t *devid{nullptr};
+ if(name)
+ {
+ auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
+ [name](const DevMap &entry) -> bool
+ { return entry.name == name || entry.endpoint_guid == name; });
+ if(iter == CaptureDevices.cend())
+ {
+ const std::wstring wname{utf8_to_wstr(name)};
+ iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
+ [&wname](const DevMap &entry) -> bool
+ { return entry.devid == wname; });
+ }
+ if(iter == CaptureDevices.cend())
+ {
+ WARN("Failed to find device name matching \"%s\"\n", name);
+ return E_FAIL;
+ }
+ name = iter->name.c_str();
+ devid = iter->devid.c_str();
+ }
+
void *ptr;
HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
IID_IMMDeviceEnumerator, &ptr)};
- if(SUCCEEDED(hr))
- {
- ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
- if(mDevId.empty())
- hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, mMMDev.getPtr());
- else
- hr = enumerator->GetDevice(mDevId.c_str(), mMMDev.getPtr());
- }
- if(SUCCEEDED(hr))
- hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
- if(SUCCEEDED(hr))
+ ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
+ if(!devid)
+ hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, mMMDev.getPtr());
+ else
+ hr = enumerator->GetDevice(devid, mMMDev.getPtr());
+ enumerator = nullptr;
+ if(FAILED(hr))
{
- mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
- if(mDevice->DeviceName.empty())
- mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
+ WARN("Failed to open device \"%s\"\n", name?name:"(default)");
+ return hr;
}
+ hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
if(FAILED(hr))
+ {
+ WARN("Failed to activate device \"%s\"\n", name?name:"(default)");
mMMDev = nullptr;
+ return hr;
+ }
+
+ mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
+ if(name) mDevice->DeviceName = name;
+ else mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
return hr;
}