diff options
Diffstat (limited to 'alc/backends/wasapi.cpp')
-rw-r--r-- | alc/backends/wasapi.cpp | 262 |
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; } |