diff --git a/include/boo/audiodev/IAudioVoiceEngine.hpp b/include/boo/audiodev/IAudioVoiceEngine.hpp index 43dda61..5bd69b4 100644 --- a/include/boo/audiodev/IAudioVoiceEngine.hpp +++ b/include/boo/audiodev/IAudioVoiceEngine.hpp @@ -76,6 +76,9 @@ struct IAudioVoiceEngine /** Get list of MIDI input devices found on system */ virtual std::vector> enumerateMIDIInputs() const=0; + /** Query if system supports creating a virtual MIDI input */ + virtual bool supportsVirtualMIDIIn() const=0; + /** Create ad-hoc MIDI in port and register with system */ virtual std::unique_ptr newVirtualMIDIIn(ReceiveFunctor&& receiver)=0; diff --git a/include/boo/audiodev/IMIDIPort.hpp b/include/boo/audiodev/IMIDIPort.hpp index 99f0069..7d8056e 100644 --- a/include/boo/audiodev/IMIDIPort.hpp +++ b/include/boo/audiodev/IMIDIPort.hpp @@ -8,18 +8,20 @@ namespace boo { - +struct IAudioVoiceEngine; using ReceiveFunctor = std::function&&, double time)>; class IMIDIPort { bool m_virtual; protected: - IMIDIPort(bool virt) : m_virtual(virt) {} + IAudioVoiceEngine* m_parent; + IMIDIPort(IAudioVoiceEngine* parent, bool virt) : m_virtual(virt), m_parent(parent) {} public: virtual ~IMIDIPort(); bool isVirtual() const {return m_virtual;} virtual std::string description() const=0; + void _disown() { m_parent = nullptr; } }; class IMIDIReceiver @@ -32,8 +34,8 @@ public: class IMIDIIn : public IMIDIPort, public IMIDIReceiver { protected: - IMIDIIn(bool virt, ReceiveFunctor&& receiver) - : IMIDIPort(virt), IMIDIReceiver(std::move(receiver)) {} + IMIDIIn(IAudioVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver) + : IMIDIPort(parent, virt), IMIDIReceiver(std::move(receiver)) {} public: virtual ~IMIDIIn(); }; @@ -41,7 +43,7 @@ public: class IMIDIOut : public IMIDIPort { protected: - IMIDIOut(bool virt) : IMIDIPort(virt) {} + IMIDIOut(IAudioVoiceEngine* parent, bool virt) : IMIDIPort(parent, virt) {} public: virtual ~IMIDIOut(); virtual size_t send(const void* buf, size_t len) const=0; @@ -50,8 +52,8 @@ public: class IMIDIInOut : public IMIDIPort, public IMIDIReceiver { protected: - IMIDIInOut(bool virt, ReceiveFunctor&& receiver) - : IMIDIPort(virt), IMIDIReceiver(std::move(receiver)) {} + IMIDIInOut(IAudioVoiceEngine* parent, bool virt, ReceiveFunctor&& receiver) + : IMIDIPort(parent, virt), IMIDIReceiver(std::move(receiver)) {} public: virtual ~IMIDIInOut(); virtual size_t send(const void* buf, size_t len) const=0; diff --git a/lib/audiodev/LinuxMidi.hpp b/lib/audiodev/LinuxMidi.hpp index 8faa4b2..9dff328 100644 --- a/lib/audiodev/LinuxMidi.hpp +++ b/lib/audiodev/LinuxMidi.hpp @@ -19,6 +19,30 @@ static inline double TimespecToDouble(struct timespec& ts) struct LinuxMidi : BaseAudioVoiceEngine { + std::unordered_map m_openHandles; + void _addOpenHandle(const char* name, IMIDIPort* port) + { + m_openHandles[name] = port; + } + void _removeOpenHandle(IMIDIPort* port) + { + for (auto it = m_openHandles.begin(); it != m_openHandles.end();) + { + if (it->second == port) + { + it = m_openHandles.erase(it); + continue; + } + ++it; + } + } + + ~LinuxMidi() + { + for (auto& p : m_openHandles) + p.second->_disown(); + } + std::vector> enumerateMIDIInputs() const { std::vector> ret; @@ -49,11 +73,21 @@ struct LinuxMidi : BaseAudioVoiceEngine break; if (device >= 0) { - snd_rawmidi_info_set_device(info, device); - if (snd_rawmidi_info_get_stream(info) != SND_RAWMIDI_STREAM_INPUT) - continue; sprintf(name + strlen(name), ",%d", device); - ret.push_back(std::make_pair(name, snd_rawmidi_info_get_name(info))); + auto search = m_openHandles.find(name); + if (search != m_openHandles.cend()) + { + ret.push_back(std::make_pair(name, search->second->description())); + continue; + } + + snd_rawmidi_t* midi; + if (!snd_rawmidi_open(&midi, nullptr, name, SND_RAWMIDI_NONBLOCK)) + { + snd_rawmidi_info(midi, info); + ret.push_back(std::make_pair(name, snd_rawmidi_info_get_name(info))); + snd_rawmidi_close(midi); + } } } while (device >= 0); @@ -68,6 +102,11 @@ struct LinuxMidi : BaseAudioVoiceEngine return ret; } + bool supportsVirtualMIDIIn() const + { + return true; + } + static void MIDIFreeProc(void* midiStatus) { snd_rawmidi_status_free((snd_rawmidi_status_t*)midiStatus); @@ -112,12 +151,14 @@ struct LinuxMidi : BaseAudioVoiceEngine snd_rawmidi_t* m_midi; std::thread m_midiThread; - MIDIIn(snd_rawmidi_t* midi, bool virt, ReceiveFunctor&& receiver) - : IMIDIIn(virt, std::move(receiver)), m_midi(midi), + MIDIIn(LinuxMidi* parent, snd_rawmidi_t* midi, bool virt, ReceiveFunctor&& receiver) + : IMIDIIn(parent, virt, std::move(receiver)), m_midi(midi), m_midiThread(std::bind(MIDIReceiveProc, m_midi, m_receiver)) {} ~MIDIIn() { + if (m_parent) + static_cast(m_parent)->_removeOpenHandle(this); pthread_cancel(m_midiThread.native_handle()); if (m_midiThread.joinable()) m_midiThread.join(); @@ -137,10 +178,15 @@ struct LinuxMidi : BaseAudioVoiceEngine struct MIDIOut : public IMIDIOut { snd_rawmidi_t* m_midi; - MIDIOut(snd_rawmidi_t* midi, bool virt) - : IMIDIOut(virt), m_midi(midi) {} + MIDIOut(LinuxMidi* parent, snd_rawmidi_t* midi, bool virt) + : IMIDIOut(parent, virt), m_midi(midi) {} - ~MIDIOut() {snd_rawmidi_close(m_midi);} + ~MIDIOut() + { + if (m_parent) + static_cast(m_parent)->_removeOpenHandle(this); + snd_rawmidi_close(m_midi); + } std::string description() const { @@ -163,12 +209,14 @@ struct LinuxMidi : BaseAudioVoiceEngine snd_rawmidi_t* m_midiOut; std::thread m_midiThread; - MIDIInOut(snd_rawmidi_t* midiIn, snd_rawmidi_t* midiOut, bool virt, ReceiveFunctor&& receiver) - : IMIDIInOut(virt, std::move(receiver)), m_midiIn(midiIn), m_midiOut(midiOut), + MIDIInOut(LinuxMidi* parent, snd_rawmidi_t* midiIn, snd_rawmidi_t* midiOut, bool virt, ReceiveFunctor&& receiver) + : IMIDIInOut(parent, virt, std::move(receiver)), m_midiIn(midiIn), m_midiOut(midiOut), m_midiThread(std::bind(MIDIReceiveProc, m_midiIn, m_receiver)) {} ~MIDIInOut() { + if (m_parent) + static_cast(m_parent)->_removeOpenHandle(this); pthread_cancel(m_midiThread.native_handle()); if (m_midiThread.joinable()) m_midiThread.join(); @@ -198,7 +246,7 @@ struct LinuxMidi : BaseAudioVoiceEngine status = snd_rawmidi_open(&midi, nullptr, "virtual", 0); if (status) return {}; - return std::make_unique(midi, true, std::move(receiver)); + return std::make_unique(nullptr, midi, true, std::move(receiver)); } std::unique_ptr newVirtualMIDIOut() @@ -208,7 +256,7 @@ struct LinuxMidi : BaseAudioVoiceEngine status = snd_rawmidi_open(nullptr, &midi, "virtual", 0); if (status) return {}; - return std::make_unique(midi, true); + return std::make_unique(nullptr, midi, true); } std::unique_ptr newVirtualMIDIInOut(ReceiveFunctor&& receiver) @@ -219,7 +267,7 @@ struct LinuxMidi : BaseAudioVoiceEngine status = snd_rawmidi_open(&midiIn, &midiOut, "virtual", 0); if (status) return {}; - return std::make_unique(midiIn, midiOut, true, std::move(receiver)); + return std::make_unique(nullptr, midiIn, midiOut, true, std::move(receiver)); } std::unique_ptr newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) @@ -228,7 +276,9 @@ struct LinuxMidi : BaseAudioVoiceEngine int status = snd_rawmidi_open(&midi, nullptr, name, 0); if (status) return {}; - return std::make_unique(midi, true, std::move(receiver)); + auto ret = std::make_unique(this, midi, true, std::move(receiver)); + _addOpenHandle(name, ret.get()); + return ret; } std::unique_ptr newRealMIDIOut(const char* name) @@ -237,7 +287,9 @@ struct LinuxMidi : BaseAudioVoiceEngine int status = snd_rawmidi_open(nullptr, &midi, name, 0); if (status) return {}; - return std::make_unique(midi, true); + auto ret = std::make_unique(this, midi, true); + _addOpenHandle(name, ret.get()); + return ret; } std::unique_ptr newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) @@ -247,7 +299,9 @@ struct LinuxMidi : BaseAudioVoiceEngine int status = snd_rawmidi_open(&midiIn, &midiOut, name, 0); if (status) return {}; - return std::make_unique(midiIn, midiOut, true, std::move(receiver)); + auto ret = std::make_unique(this, midiIn, midiOut, true, std::move(receiver)); + _addOpenHandle(name, ret.get()); + return ret; } bool useMIDILock() const {return true;} diff --git a/lib/audiodev/WAVOut.cpp b/lib/audiodev/WAVOut.cpp index 7d5fadd..8bb5c5d 100644 --- a/lib/audiodev/WAVOut.cpp +++ b/lib/audiodev/WAVOut.cpp @@ -33,12 +33,17 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine return {}; } + bool supportsVirtualMIDIIn() const + { + return false; + } + boo::ReceiveFunctor* m_midiReceiver = nullptr; struct MIDIIn : public boo::IMIDIIn { - MIDIIn(bool virt, boo::ReceiveFunctor&& receiver) - : IMIDIIn(virt, std::move(receiver)) {} + MIDIIn(WAVOutVoiceEngine* parent, bool virt, boo::ReceiveFunctor&& receiver) + : IMIDIIn(parent, virt, std::move(receiver)) {} std::string description() const { @@ -48,7 +53,7 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine std::unique_ptr newVirtualMIDIIn(boo::ReceiveFunctor&& receiver) { - std::unique_ptr ret = std::make_unique(true, std::move(receiver)); + std::unique_ptr ret = std::make_unique(nullptr, true, std::move(receiver)); m_midiReceiver = &ret->m_receiver; return ret; }