diff --git a/lib/audiodev/WASAPI.cpp b/lib/audiodev/WASAPI.cpp index e430b33..8c70d77 100644 --- a/lib/audiodev/WASAPI.cpp +++ b/lib/audiodev/WASAPI.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -35,6 +36,7 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine #endif ComPtr m_audClient; ComPtr m_renderClient; + std::string m_sinkName; size_t m_curBufFrame = 0; std::vector m_5msBuffer; @@ -136,11 +138,14 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine void _buildAudioRenderClient() { #if !WINDOWS_STORE - if (FAILED(m_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &m_device))) + if (!m_device) { - Log.report(logvisor::Error, L"unable to obtain default audio device"); - m_device.Reset(); - return; + if (FAILED(m_enumerator->GetDevice(MBSTWCS(m_sinkName.c_str()).c_str(), &m_device))) + { + Log.report(logvisor::Error, "unable to obtain audio device %s", m_sinkName.c_str()); + m_device.Reset(); + return; + } } if (FAILED(m_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, &m_audClient))) @@ -396,6 +401,17 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine return; } + if (FAILED(m_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &m_device))) + { + Log.report(logvisor::Error, L"unable to obtain default audio device"); + m_device.Reset(); + return; + } + LPWSTR sinkName = nullptr; + m_device->GetId(&sinkName); + m_sinkName = WCSTMBS(sinkName); + CoTaskMemFree(sinkName); + _buildAudioRenderClient(); #else auto deviceIdStr = MediaDevice::GetDefaultAudioRenderId(Windows::Media::Devices::AudioDeviceRole::Default); @@ -418,12 +434,7 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine if (m_mixInfo.m_sampleFormat != oldFmt) Log.report(logvisor::Fatal, L"audio device sample format changed, boo doesn't support this!!"); - if (m_voiceHead) - for (AudioVoice& vox : *m_voiceHead) - vox._resetSampleRate(vox.m_sampleRateIn); - if (m_submixHead) - for (AudioSubmix& smx : *m_submixHead) - smx._resetOutputSampleRate(); + _resetSampleRate(); } void pumpAndMixVoices() @@ -443,7 +454,10 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine Log.report(logvisor::Fatal, L"unable to setup AudioRenderClient"); if (m_rebuild) + { + m_device.Reset(); _rebuildAudioRenderClient(); + } HRESULT res; if (!m_started) @@ -511,14 +525,64 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine } } + std::string getCurrentAudioOutput() const + { + return m_sinkName; + } + + bool setCurrentAudioOutput(const char* name) + { + ComPtr newDevice; + if (FAILED(m_enumerator->GetDevice(MBSTWCS(name).c_str(), &newDevice))) + { + Log.report(logvisor::Error, "unable to obtain audio device %s", name); + return false; + } + m_device = newDevice; + m_sinkName = name; + _rebuildAudioRenderClient(); + return true; + } + + std::vector> enumerateAudioOutputs() const + { + std::vector> ret; + + ComPtr collection; + if (FAILED(m_enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection))) + { + Log.report(logvisor::Error, L"unable to enumerate audio outputs"); + return ret; + } + + UINT count = 0; + collection->GetCount(&count); + for (UINT i = 0; i < count; ++i) + { + ComPtr device; + collection->Item(i, &device); + LPWSTR devName; + device->GetId(&devName); + ComPtr props; + device->OpenPropertyStore(STGM_READ, &props); + PROPVARIANT val = {}; + props->GetValue(PKEY_Device_FriendlyName, &val); + std::string friendlyName; + if (val.vt == VT_LPWSTR) + friendlyName = WCSTMBS(val.pwszVal); + ret.emplace_back(WCSTMBS(devName), std::move(friendlyName)); + } + + return ret; + } + #if !WINDOWS_STORE - std::vector> enumerateMIDIDevices() const + std::vector> enumerateMIDIInputs() const { std::vector> ret; UINT numInDevices = midiInGetNumDevs(); - UINT numOutDevices = midiOutGetNumDevs(); - ret.reserve(numInDevices + numOutDevices); + ret.reserve(numInDevices); for (UINT i=0 ; i newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) { - if (strcmp(name, "in")) + if (strncmp(name, "in", 2)) return {}; long id = strtol(name + 2, nullptr, 10); - std::unique_ptr ret = std::make_unique(false, std::move(receiver)); + std::unique_ptr ret = std::make_unique(this, false, std::move(receiver)); if (!ret) return {}; if (FAILED(midiInOpen(&static_cast(*ret).m_midi, id, DWORD_PTR(MIDIReceiveProc), DWORD_PTR(static_cast(ret.get())), CALLBACK_FUNCTION))) return {}; + midiInStart(static_cast(*ret).m_midi); return ret; } std::unique_ptr newRealMIDIOut(const char* name) { - if (strcmp(name, "out")) + if (strncmp(name, "out", 3)) return {}; long id = strtol(name + 3, nullptr, 10); - std::unique_ptr ret = std::make_unique(false); + std::unique_ptr ret = std::make_unique(this, false); if (!ret) return {}; @@ -765,13 +837,14 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine long inId = strtol(in + 2, nullptr, 10); long outId = strtol(out + 3, nullptr, 10); - std::unique_ptr ret = std::make_unique(false, std::move(receiver)); + std::unique_ptr ret = std::make_unique(this, false, std::move(receiver)); if (!ret) return {}; if (FAILED(midiInOpen(&static_cast(*ret).m_midiIn, inId, DWORD_PTR(MIDIReceiveProc), DWORD_PTR(static_cast(ret.get())), CALLBACK_FUNCTION))) return {}; + midiInStart(static_cast(*ret).m_midiIn); if (FAILED(midiOutOpen(&static_cast(*ret).m_midiOut, outId, NULL, NULL, CALLBACK_NULL))) diff --git a/lib/win/WinCommon.hpp b/lib/win/WinCommon.hpp index 92e781e..ad4dc07 100644 --- a/lib/win/WinCommon.hpp +++ b/lib/win/WinCommon.hpp @@ -125,4 +125,12 @@ static inline std::string WCSTMBS(const wchar_t* wstr) return strTo; } +static inline std::wstring MBSTWCS(const char* str) +{ + int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0) - 1; + std::wstring strTo(sizeNeeded, 0); + MultiByteToWideChar(CP_UTF8, 0, str, -1, &strTo[0], sizeNeeded); + return strTo; +} + #endif // BOO_WINCOMMON_HPP