From 261c06d746471a06fbde0c50b8ac203de6a22665 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 25 Aug 2018 21:56:16 -1000 Subject: [PATCH] Optional teVirtualMIDI integration --- CMakeLists.txt | 9 ++ lib/audiodev/WASAPI.cpp | 188 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 189 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 146acf2..6b2a5d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,15 @@ elseif(WIN32) lib/graphicsdev/VulkanDispatchTable.cpp) endif() + find_file(TE_VIRTUAL_MIDI_H teVirtualMIDI.h PATHS + "$ENV{PROGRAMFILES\(X86\)}/Tobias Erichsen/teVirtualMIDISDK/C-Binding") + if (TE_VIRTUAL_MIDI_H) + message(STATUS "Enabling teVirtualMIDI") + get_filename_component(TE_VIRTUAL_MIDI_DIR ${TE_VIRTUAL_MIDI_H} DIRECTORY) + include_directories(${TE_VIRTUAL_MIDI_DIR}) + add_definitions("-DTE_VIRTUAL_MIDI=1") + endif() + list(APPEND PLAT_SRCS lib/win/ApplicationWin32.cpp lib/win/WindowWin32.cpp diff --git a/lib/audiodev/WASAPI.cpp b/lib/audiodev/WASAPI.cpp index 8c70d77..4b3cea6 100644 --- a/lib/audiodev/WASAPI.cpp +++ b/lib/audiodev/WASAPI.cpp @@ -1,6 +1,7 @@ #include "../win/Win32Common.hpp" #include "AudioVoiceEngine.hpp" #include "logvisor/logvisor.hpp" +#include "boo/IApplication.hpp" #include #include @@ -9,6 +10,20 @@ #include +#ifdef TE_VIRTUAL_MIDI +#include +typedef LPVM_MIDI_PORT (CALLBACK *pfnvirtualMIDICreatePortEx2) +( LPCWSTR portName, LPVM_MIDI_DATA_CB callback, DWORD_PTR dwCallbackInstance, DWORD maxSysexLength, DWORD flags ); +typedef void (CALLBACK *pfnvirtualMIDIClosePort)( LPVM_MIDI_PORT midiPort ); +typedef BOOL (CALLBACK *pfnvirtualMIDISendData)( LPVM_MIDI_PORT midiPort, LPBYTE midiDataBytes, DWORD length ); +typedef LPCWSTR (CALLBACK *pfnvirtualMIDIGetDriverVersion)( PWORD major, PWORD minor, PWORD release, PWORD build ); +static pfnvirtualMIDICreatePortEx2 virtualMIDICreatePortEx2PROC = nullptr; +static pfnvirtualMIDIClosePort virtualMIDIClosePortPROC = nullptr; +static pfnvirtualMIDISendData virtualMIDISendDataPROC = nullptr; +static pfnvirtualMIDIGetDriverVersion virtualMIDIGetDriverVersionPROC = nullptr; +static double PerfFrequency = 0.0; +#endif + #if !WINDOWS_STORE const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); @@ -385,6 +400,20 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine #endif { #if !WINDOWS_STORE +#ifdef TE_VIRTUAL_MIDI + HMODULE virtualMidiModule; + if (!virtualMIDICreatePortEx2PROC && (virtualMidiModule = LoadLibraryW(L"teVirtualMIDI64.dll"))) + { + virtualMIDICreatePortEx2PROC = (pfnvirtualMIDICreatePortEx2)GetProcAddress(virtualMidiModule, "virtualMIDICreatePortEx2"); + virtualMIDIClosePortPROC = (pfnvirtualMIDIClosePort)GetProcAddress(virtualMidiModule, "virtualMIDIClosePort"); + virtualMIDISendDataPROC = (pfnvirtualMIDISendData)GetProcAddress(virtualMidiModule, "virtualMIDISendData"); + virtualMIDIGetDriverVersionPROC = (pfnvirtualMIDIGetDriverVersion)GetProcAddress(virtualMidiModule, "virtualMIDIGetDriverVersion"); + LARGE_INTEGER pf; + QueryPerformanceFrequency(&pf); + PerfFrequency = double(pf.QuadPart); + } +#endif + /* Enumerate default audio device */ if (FAILED(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, @@ -623,9 +652,34 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine bool supportsVirtualMIDIIn() const { +#ifdef TE_VIRTUAL_MIDI + WORD major, minor, release, build; + return virtualMIDIGetDriverVersionPROC && + virtualMIDIGetDriverVersionPROC(&major, &minor, &release, &build) != nullptr; +#else return false; +#endif } +#ifdef TE_VIRTUAL_MIDI + static void CALLBACK VirtualMIDIReceiveProc(LPVM_MIDI_PORT midiPort, + LPBYTE midiDataBytes, + DWORD length, + IMIDIReceiver* dwInstance) + { + std::vector bytes; + bytes.resize(length); + memcpy(&bytes[0], midiDataBytes, length); + + double timestamp; + LARGE_INTEGER perf; + QueryPerformanceCounter(&perf); + timestamp = perf.QuadPart / PerfFrequency; + + dwInstance->m_receiver(std::move(bytes), timestamp); + } +#endif + static void CALLBACK MIDIReceiveProc(HMIDIIN hMidiIn, UINT wMsg, IMIDIReceiver* dwInstance, @@ -640,12 +694,77 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine } } +#ifdef TE_VIRTUAL_MIDI + struct VMIDIIn : public IMIDIIn + { + LPVM_MIDI_PORT m_midi = 0; + + VMIDIIn(WASAPIAudioVoiceEngine* parent, ReceiveFunctor&& receiver) + : IMIDIIn(parent, true, std::move(receiver)) {} + + ~VMIDIIn() + { + virtualMIDIClosePortPROC(m_midi); + } + + std::string description() const + { + return "Virtual MIDI-In"; + } + }; + + struct VMIDIOut : public IMIDIOut + { + LPVM_MIDI_PORT m_midi = 0; + + VMIDIOut(WASAPIAudioVoiceEngine* parent) : IMIDIOut(parent, true) {} + + ~VMIDIOut() + { + virtualMIDIClosePortPROC(m_midi); + } + + std::string description() const + { + return "Virtual MIDI-Out"; + } + + size_t send(const void* buf, size_t len) const + { + return virtualMIDISendDataPROC(m_midi, (LPBYTE)buf, len) ? len : 0; + } + }; + + struct VMIDIInOut : public IMIDIInOut + { + LPVM_MIDI_PORT m_midi = 0; + + VMIDIInOut(WASAPIAudioVoiceEngine* parent, ReceiveFunctor&& receiver) + : IMIDIInOut(parent, true, std::move(receiver)) {} + + ~VMIDIInOut() + { + virtualMIDIClosePortPROC(m_midi); + } + + std::string description() const + { + return "Virtual MIDI-In/Out"; + } + + size_t send(const void* buf, size_t len) const + { + return virtualMIDISendDataPROC(m_midi, (LPBYTE)buf, len) ? len : 0; + } + }; +#endif + struct MIDIIn : public IMIDIIn { HMIDIIN m_midi = 0; - MIDIIn(WASAPIAudioVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver) - : IMIDIIn(parent, virt, std::move(receiver)) {} + MIDIIn(WASAPIAudioVoiceEngine* parent, ReceiveFunctor&& receiver) + : IMIDIIn(parent, false, std::move(receiver)) {} ~MIDIIn() { @@ -675,7 +794,7 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine uint8_t m_buf[512]; MIDIHDR m_hdr = {}; - MIDIOut(WASAPIAudioVoiceEngine* parent, bool virt) : IMIDIOut(parent, virt) {} + MIDIOut(WASAPIAudioVoiceEngine* parent) : IMIDIOut(parent, false) {} void prepare() { @@ -728,8 +847,8 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine uint8_t m_buf[512]; MIDIHDR m_hdr = {}; - MIDIInOut(WASAPIAudioVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver) - : IMIDIInOut(parent, virt, std::move(receiver)) {} + MIDIInOut(WASAPIAudioVoiceEngine* parent, ReceiveFunctor&& receiver) + : IMIDIInOut(parent, false, std::move(receiver)) {} void prepare() { @@ -777,17 +896,70 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine std::unique_ptr newVirtualMIDIIn(ReceiveFunctor&& receiver) { +#ifdef TE_VIRTUAL_MIDI + if (!virtualMIDICreatePortEx2PROC) + return {}; + + std::unique_ptr ret = std::make_unique(this, std::move(receiver)); + if (!ret) + return {}; + + SystemString name = SystemString(APP->getFriendlyName()) + _S(" MIDI-In"); + auto port = virtualMIDICreatePortEx2PROC(name.c_str(), LPVM_MIDI_DATA_CB(VirtualMIDIReceiveProc), + DWORD_PTR(static_cast(ret.get())), 512, + TE_VM_FLAGS_PARSE_RX | TE_VM_FLAGS_INSTANTIATE_RX_ONLY); + if (!port) + return {}; + static_cast(*ret).m_midi = port; + return ret; +#else return {}; +#endif } std::unique_ptr newVirtualMIDIOut() { +#ifdef TE_VIRTUAL_MIDI + if (!virtualMIDICreatePortEx2PROC) + return {}; + + std::unique_ptr ret = std::make_unique(this); + if (!ret) + return {}; + + SystemString name = SystemString(APP->getFriendlyName()) + _S(" MIDI-Out"); + auto port = virtualMIDICreatePortEx2PROC(name.c_str(), nullptr, 0, 512, + TE_VM_FLAGS_PARSE_TX | TE_VM_FLAGS_INSTANTIATE_TX_ONLY); + if (!port) + return {}; + static_cast(*ret).m_midi = port; + return ret; +#else return {}; +#endif } std::unique_ptr newVirtualMIDIInOut(ReceiveFunctor&& receiver) { +#ifdef TE_VIRTUAL_MIDI + if (!virtualMIDICreatePortEx2PROC) + return {}; + + std::unique_ptr ret = std::make_unique(this, std::move(receiver)); + if (!ret) + return {}; + + SystemString name = SystemString(APP->getFriendlyName()) + _S(" MIDI-In/Out"); + auto port = virtualMIDICreatePortEx2PROC(name.c_str(), LPVM_MIDI_DATA_CB(VirtualMIDIReceiveProc), + DWORD_PTR(static_cast(ret.get())), 512, + TE_VM_FLAGS_SUPPORTED); + if (!port) + return {}; + static_cast(*ret).m_midi = port; + return ret; +#else return {}; +#endif } std::unique_ptr newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) @@ -796,7 +968,7 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine return {}; long id = strtol(name + 2, nullptr, 10); - std::unique_ptr ret = std::make_unique(this, false, std::move(receiver)); + std::unique_ptr ret = std::make_unique(this, std::move(receiver)); if (!ret) return {}; @@ -814,7 +986,7 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine return {}; long id = strtol(name + 3, nullptr, 10); - std::unique_ptr ret = std::make_unique(this, false); + std::unique_ptr ret = std::make_unique(this); if (!ret) return {}; @@ -837,7 +1009,7 @@ struct WASAPIAudioVoiceEngine : BaseAudioVoiceEngine long inId = strtol(in + 2, nullptr, 10); long outId = strtol(out + 3, nullptr, 10); - std::unique_ptr ret = std::make_unique(this, false, std::move(receiver)); + std::unique_ptr ret = std::make_unique(this, std::move(receiver)); if (!ret) return {};