From 9b133904473094839feacf610a69875d8b6155e5 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sun, 22 May 2016 19:34:20 -1000 Subject: [PATCH] Integrate Win32 MIDI API --- CMakeLists.txt | 2 +- include/boo/audiodev/IAudioSubmix.hpp | 4 +- lib/audiodev/IAudioMix.hpp | 2 +- lib/audiodev/WASAPI.cpp | 291 +++++++++++++++++++++++++- 4 files changed, 288 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0154cef..1dc6ea9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,7 @@ if(WIN32) list(APPEND _BOO_SYS_DEFINES -DUNICODE -D_UNICODE) - list(APPEND _BOO_SYS_LIBS Winusb opengl32 Setupapi Imm32) + list(APPEND _BOO_SYS_LIBS Winusb opengl32 Setupapi Imm32 Winmm) elseif(APPLE) list(APPEND PLAT_SRCS lib/mac/ApplicationCocoa.mm diff --git a/include/boo/audiodev/IAudioSubmix.hpp b/include/boo/audiodev/IAudioSubmix.hpp index 67afc01..99bacc6 100644 --- a/include/boo/audiodev/IAudioSubmix.hpp +++ b/include/boo/audiodev/IAudioSubmix.hpp @@ -7,8 +7,8 @@ namespace boo { -class IAudioVoice; -class IAudioVoiceCallback; +struct IAudioVoice; +struct IAudioVoiceCallback; struct ChannelMap; struct IAudioSubmixCallback; diff --git a/lib/audiodev/IAudioMix.hpp b/lib/audiodev/IAudioMix.hpp index 19cf641..0c6645c 100644 --- a/lib/audiodev/IAudioMix.hpp +++ b/lib/audiodev/IAudioMix.hpp @@ -5,7 +5,7 @@ namespace boo { -class AudioVoiceEngineMixInfo; +struct AudioVoiceEngineMixInfo; class AudioVoice; class AudioSubmix; diff --git a/lib/audiodev/WASAPI.cpp b/lib/audiodev/WASAPI.cpp index a94cdff..9dd030c 100644 --- a/lib/audiodev/WASAPI.cpp +++ b/lib/audiodev/WASAPI.cpp @@ -4,6 +4,9 @@ #include #include +#include + +#include const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); @@ -138,6 +141,7 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine return; } m_mixInfo.m_sampleRate = pwfx->Format.nSamplesPerSec; + m_5msFrames = (m_mixInfo.m_sampleRate * 5 / 500 + 1) / 2; if (pwfx->Format.wFormatTag == WAVE_FORMAT_PCM || (pwfx->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && pwfx->SubFormat == KSDATAFORMAT_SUBTYPE_PCM)) @@ -251,19 +255,292 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine } } - std::vector> enumerateMIDIDevices() const { return {}; } + std::vector> enumerateMIDIDevices() const + { + std::vector> ret; - std::unique_ptr newVirtualMIDIIn(ReceiveFunctor&& receiver) { return {}; } + UINT numInDevices = midiInGetNumDevs(); + UINT numOutDevices = midiOutGetNumDevs(); + ret.reserve(numInDevices + numOutDevices); - std::unique_ptr newVirtualMIDIOut() { return {}; } + for (UINT i=0 ; i newVirtualMIDIInOut(ReceiveFunctor&& receiver) { return {}; } + MIDIINCAPS caps; + if (FAILED(midiInGetDevCaps(i, &caps, sizeof(caps)))) + continue; - std::unique_ptr newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) { return {}; } +#ifdef UNICODE + int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nullptr, 0, nullptr, nullptr); + std::string strTo(sizeNeeded, 0); + WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, &strTo[0], sizeNeeded, nullptr, nullptr); + ret.push_back(std::make_pair(std::string(name), std::move(strTo))); +#else + ret.push_back(std::make_pair(std::string(name), std::string(caps.szPname))); +#endif + } - std::unique_ptr newRealMIDIOut(const char* name) { return {}; } + for (UINT i=0 ; i newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) { return {}; } + MIDIOUTCAPS caps; + if (FAILED(midiOutGetDevCaps(i, &caps, sizeof(caps)))) + continue; + +#ifdef UNICODE + int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nullptr, 0, nullptr, nullptr); + std::string strTo(sizeNeeded, 0); + WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, &strTo[0], sizeNeeded, nullptr, nullptr); + ret.push_back(std::make_pair(std::string(name), std::move(strTo))); +#else + ret.push_back(std::make_pair(std::string(name), std::string(caps.szPname))); +#endif + } + + return ret; + } + + static void MIDIReceiveProc(HMIDIIN hMidiIn, + UINT wMsg, + IMIDIReceiver* dwInstance, + DWORD_PTR dwParam1, + DWORD_PTR dwParam2) + { + if (wMsg == MIM_DATA) + { + uint8_t (&ptr)[3] = reinterpret_cast(dwParam1); + std::vector bytes(std::cbegin(ptr), std::cend(ptr)); + dwInstance->m_receiver(std::move(bytes)); + } + } + + struct MIDIIn : public IMIDIIn + { + HMIDIIN m_midi = 0; + + MIDIIn(bool virt, ReceiveFunctor&& receiver) + : IMIDIIn(virt, std::move(receiver)) {} + + ~MIDIIn() + { + midiInClose(m_midi); + } + + std::string description() const + { + UINT id = 0; + midiInGetID(m_midi, &id); + MIDIINCAPS caps; + if (FAILED(midiInGetDevCaps(id, &caps, sizeof(caps)))) + return {}; + +#ifdef UNICODE + int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nullptr, 0, nullptr, nullptr); + std::string strTo(sizeNeeded, 0); + WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, &strTo[0], sizeNeeded, nullptr, nullptr); + return strTo; +#else + return caps.szPname; +#endif + } + }; + + struct MIDIOut : public IMIDIOut + { + HMIDIOUT m_midi = 0; + HMIDISTRM m_strm = 0; + uint8_t m_buf[512]; + MIDIHDR m_hdr = {}; + + MIDIOut(bool virt) : IMIDIOut(virt) {} + + void prepare() + { + UINT id = 0; + midiOutGetID(m_midi, &id); + + m_hdr.lpData = reinterpret_cast(m_buf); + m_hdr.dwBufferLength = 512; + m_hdr.dwFlags = MHDR_ISSTRM; + midiOutPrepareHeader(m_midi, &m_hdr, sizeof(m_hdr)); + midiStreamOpen(&m_strm, &id, 1, NULL, NULL, CALLBACK_NULL); + } + + ~MIDIOut() + { + midiStreamClose(m_strm); + midiOutUnprepareHeader(m_midi, &m_hdr, sizeof(m_hdr)); + midiOutClose(m_midi); + } + + std::string description() const + { + UINT id = 0; + midiOutGetID(m_midi, &id); + MIDIOUTCAPS caps; + if (FAILED(midiOutGetDevCaps(id, &caps, sizeof(caps)))) + return {}; + +#ifdef UNICODE + int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nullptr, 0, nullptr, nullptr); + std::string strTo(sizeNeeded, 0); + WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, &strTo[0], sizeNeeded, nullptr, nullptr); + return strTo; +#else + return caps.szPname; +#endif + } + + size_t send(const void* buf, size_t len) const + { + memcpy(((MIDIOut*)this)->m_buf, buf, std::min(len, size_t(512))); + ((MIDIOut*)this)->m_hdr.dwBytesRecorded = len; + midiStreamOut(m_strm, LPMIDIHDR(&m_hdr), sizeof(m_hdr)); + return len; + } + }; + + struct MIDIInOut : public IMIDIInOut + { + HMIDIIN m_midiIn = 0; + HMIDIOUT m_midiOut = 0; + HMIDISTRM m_strm = 0; + uint8_t m_buf[512]; + MIDIHDR m_hdr = {}; + + MIDIInOut(bool virt, ReceiveFunctor&& receiver) + : IMIDIInOut(virt, std::move(receiver)) {} + + void prepare() + { + UINT id = 0; + midiOutGetID(m_midiOut, &id); + + m_hdr.lpData = reinterpret_cast(m_buf); + m_hdr.dwBufferLength = 512; + m_hdr.dwFlags = MHDR_ISSTRM; + midiOutPrepareHeader(m_midiOut, &m_hdr, sizeof(m_hdr)); + midiStreamOpen(&m_strm, &id, 1, NULL, NULL, CALLBACK_NULL); + } + + ~MIDIInOut() + { + midiInClose(m_midiIn); + midiStreamClose(m_strm); + midiOutUnprepareHeader(m_midiOut, &m_hdr, sizeof(m_hdr)); + midiOutClose(m_midiOut); + } + + std::string description() const + { + UINT id = 0; + midiOutGetID(m_midiOut, &id); + MIDIOUTCAPS caps; + if (FAILED(midiOutGetDevCaps(id, &caps, sizeof(caps)))) + return {}; + +#ifdef UNICODE + int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nullptr, 0, nullptr, nullptr); + std::string strTo(sizeNeeded, 0); + WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, &strTo[0], sizeNeeded, nullptr, nullptr); + return strTo; +#else + return caps.szPname; +#endif + } + + size_t send(const void* buf, size_t len) const + { + memcpy(((MIDIOut*)this)->m_buf, buf, std::min(len, size_t(512))); + ((MIDIOut*)this)->m_hdr.dwBytesRecorded = len; + midiStreamOut(m_strm, LPMIDIHDR(&m_hdr), sizeof(m_hdr)); + return len; + } + }; + + unsigned m_midiInCounter = 0; + unsigned m_midiOutCounter = 0; + + std::unique_ptr newVirtualMIDIIn(ReceiveFunctor&& receiver) + { + return {}; + } + + std::unique_ptr newVirtualMIDIOut() + { + return {}; + } + + std::unique_ptr newVirtualMIDIInOut(ReceiveFunctor&& receiver) + { + return {}; + } + + std::unique_ptr newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) + { + if (strcmp(name, "in")) + return {}; + long id = strtol(name + 2, nullptr, 10); + + std::unique_ptr ret = std::make_unique(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 {}; + + return ret; + } + + std::unique_ptr newRealMIDIOut(const char* name) + { + if (strcmp(name, "out")) + return {}; + long id = strtol(name + 3, nullptr, 10); + + std::unique_ptr ret = std::make_unique(false); + if (!ret) + return {}; + + if (FAILED(midiOutOpen(&static_cast(*ret).m_midi, id, NULL, + NULL, CALLBACK_NULL))) + return {}; + + static_cast(*ret).prepare(); + return ret; + } + + std::unique_ptr newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) + { + const char* in = strstr(name, "in"); + const char* out = strstr(name, "out"); + + if (!in || !out) + return {}; + + 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)); + if (!ret) + return {}; + + if (FAILED(midiInOpen(&static_cast(*ret).m_midiIn, inId, DWORD_PTR(MIDIReceiveProc), + DWORD_PTR(static_cast(ret.get())), CALLBACK_FUNCTION))) + return {}; + + if (FAILED(midiOutOpen(&static_cast(*ret).m_midiOut, outId, NULL, + NULL, CALLBACK_NULL))) + return {}; + + static_cast(*ret).prepare(); + return ret; + } }; std::unique_ptr NewAudioVoiceEngine()