diff --git a/CMakeLists.txt b/CMakeLists.txt index 5469d00..cf0c60e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,6 +194,7 @@ add_library(boo lib/audiodev/MIDIEncoder.cpp lib/audiodev/MIDIDecoder.cpp lib/audiodev/MIDICommon.hpp + lib/audiodev/MIDICommon.cpp include/boo/inputdev/IHIDListener.hpp include/boo/IGraphicsContext.hpp include/boo/graphicsdev/IGraphicsDataFactory.hpp diff --git a/include/boo/audiodev/IAudioVoiceEngine.hpp b/include/boo/audiodev/IAudioVoiceEngine.hpp index 7987cb1..736b43d 100644 --- a/include/boo/audiodev/IAudioVoiceEngine.hpp +++ b/include/boo/audiodev/IAudioVoiceEngine.hpp @@ -48,22 +48,22 @@ struct IAudioVoiceEngine virtual std::vector> enumerateMIDIDevices() const=0; /** Create ad-hoc MIDI in port and register with system */ - virtual std::unique_ptr newVirtualMIDIIn()=0; + virtual std::unique_ptr newVirtualMIDIIn(ReceiveFunctor&& receiver)=0; /** Create ad-hoc MIDI out port and register with system */ virtual std::unique_ptr newVirtualMIDIOut()=0; /** Create ad-hoc MIDI in/out port and register with system */ - virtual std::unique_ptr newVirtualMIDIInOut()=0; + virtual std::unique_ptr newVirtualMIDIInOut(ReceiveFunctor&& receiver)=0; /** Open named MIDI in port, name format depends on OS */ - virtual std::unique_ptr newRealMIDIIn(const char* name)=0; + virtual std::unique_ptr newRealMIDIIn(const char* name, ReceiveFunctor&& receiver)=0; /** Open named MIDI out port, name format depends on OS */ virtual std::unique_ptr newRealMIDIOut(const char* name)=0; /** Open named MIDI in/out port, name format depends on OS */ - virtual std::unique_ptr newRealMIDIInOut(const char* name)=0; + virtual std::unique_ptr newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver)=0; }; /** Construct host platform's voice engine */ diff --git a/include/boo/audiodev/IMIDIPort.hpp b/include/boo/audiodev/IMIDIPort.hpp index 1c3b7c6..2077918 100644 --- a/include/boo/audiodev/IMIDIPort.hpp +++ b/include/boo/audiodev/IMIDIPort.hpp @@ -3,35 +3,53 @@ #include #include +#include +#include namespace boo { +using ReceiveFunctor = std::function&&)>; + class IMIDIPort { + bool m_virtual; +protected: + IMIDIPort(bool virt) : m_virtual(virt) {} public: - virtual ~IMIDIPort() = default; - virtual bool isVirtual() const=0; + virtual ~IMIDIPort(); + bool isVirtual() const {return m_virtual;} virtual std::string description() const=0; }; class IMIDIIn : public IMIDIPort { +protected: + ReceiveFunctor m_receiver; + IMIDIIn(bool virt, ReceiveFunctor&& receiver) + : IMIDIPort(virt), m_receiver(std::move(receiver)) {} public: - virtual size_t receive(void* buf, size_t len) const=0; + virtual ~IMIDIIn(); }; class IMIDIOut : public IMIDIPort { +protected: + IMIDIOut(bool virt) : IMIDIPort(virt) {} public: + virtual ~IMIDIOut(); virtual size_t send(const void* buf, size_t len) const=0; }; class IMIDIInOut : public IMIDIPort { +protected: + ReceiveFunctor m_receiver; + IMIDIInOut(bool virt, ReceiveFunctor&& receiver) + : IMIDIPort(virt), m_receiver(std::move(receiver)) {} public: + virtual ~IMIDIInOut(); virtual size_t send(const void* buf, size_t len) const=0; - virtual size_t receive(void* buf, size_t len) const=0; }; } diff --git a/include/boo/audiodev/MIDIDecoder.hpp b/include/boo/audiodev/MIDIDecoder.hpp index e36da82..1b277f0 100644 --- a/include/boo/audiodev/MIDIDecoder.hpp +++ b/include/boo/audiodev/MIDIDecoder.hpp @@ -4,6 +4,7 @@ #include "boo/audiodev/IMIDIReader.hpp" #include "boo/audiodev/IMIDIPort.hpp" #include +#include namespace boo { @@ -12,19 +13,14 @@ class MIDIDecoder { IMIDIReader& m_out; uint8_t m_status = 0; - - struct ReadController - { - IMIDIIn& m_in; - bool readByte(uint8_t& a); - bool read2Bytes(uint8_t& a, uint8_t& b); - bool readBuffer(void* buf, size_t len); - ReadController(IMIDIIn& in) : m_in(in) {} - } m_readControl; - uint32_t _readContinuedValue(uint8_t a); + bool _readContinuedValue(std::vector::const_iterator& it, + std::vector::const_iterator end, + uint32_t& valOut); public: - MIDIDecoder(IMIDIIn& in, IMIDIReader& out) : m_readControl(in), m_out(out) {} - bool receiveBytes(); + MIDIDecoder(IMIDIReader& out) : m_out(out) {} + std::vector::const_iterator + receiveBytes(std::vector::const_iterator begin, + std::vector::const_iterator end); }; } diff --git a/lib/audiodev/ALSA.cpp b/lib/audiodev/ALSA.cpp index 343bec2..d8ec2ff 100644 --- a/lib/audiodev/ALSA.cpp +++ b/lib/audiodev/ALSA.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "AudioVoiceEngine.hpp" #include "logvisor/logvisor.hpp" @@ -56,15 +57,15 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine return AudioChannelSet::Stereo; static const std::array testSets = - {AudioChannelSet::Surround71, AudioChannelSet::Surround51, - AudioChannelSet::Quad, AudioChannelSet::Stereo}; + {{AudioChannelSet::Surround71, AudioChannelSet::Surround51, + AudioChannelSet::Quad, AudioChannelSet::Stereo}}; for (AudioChannelSet set : testSets) { for (snd_pcm_chmap_query_t** chmap = chmaps ; *chmap != nullptr ; ++chmap) { snd_pcm_chmap_t* chm = &(*chmap)->map; uint64_t chBits = 0; - for (int c=0 ; cchannels ; ++c) + for (unsigned c=0 ; cchannels ; ++c) chBits |= 1 << chm->pos[c]; switch (set) @@ -204,7 +205,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine { snd_pcm_chmap_t* chm = &(*chmap)->map; uint64_t chBits = 0; - for (int c=0 ; cchannels ; ++c) + for (unsigned c=0 ; cchannels ; ++c) chBits |= 1 << chm->pos[c]; bool good = false; @@ -246,7 +247,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine return; } chmapOut.m_channelCount = chCount; - for (int c=0 ; cchannels ; ++c) + for (unsigned c=0 ; cchannels ; ++c) chmapOut.m_channels[c] = AudioChannel(foundChmap->pos[c] - 3); snd_pcm_set_chmap(m_pcm, foundChmap); snd_pcm_free_chmaps(chmaps); @@ -364,15 +365,41 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine return ret; } + static void MIDIReceiveProc(snd_rawmidi_t* midi, const ReceiveFunctor& receiver, bool& running) + { + uint8_t buf[512]; + while (running) + { + int rdBytes = snd_rawmidi_read(midi, buf, 512); + if (rdBytes < 0) + { + Log.report(logvisor::Error, "MIDI connection lost"); + running = false; + break; + } + + receiver(std::vector(std::cbegin(buf), std::cbegin(buf) + rdBytes)); + } + } + struct MIDIIn : public IMIDIIn { + bool m_midiRunning = true; snd_rawmidi_t* m_midi; - bool m_virtual; - MIDIIn(snd_rawmidi_t* midi, bool virt) - : m_midi(midi), m_virtual(virt) {} + std::thread m_midiThread; + + MIDIIn(snd_rawmidi_t* midi, bool virt, ReceiveFunctor&& receiver) + : IMIDIIn(virt, std::move(receiver)), m_midi(midi), + m_midiThread(std::bind(MIDIReceiveProc, m_midi, m_receiver, m_midiRunning)) {} + + ~MIDIIn() + { + m_midiRunning = false; + if (m_midiThread.joinable()) + m_midiThread.join(); + snd_rawmidi_close(m_midi); + } - ~MIDIIn() {snd_rawmidi_close(m_midi);} - bool isVirtual() const {return m_virtual;} std::string description() const { snd_rawmidi_info_t* info; @@ -381,21 +408,16 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine std::string ret = snd_rawmidi_info_get_name(info); return ret; } - size_t receive(void* buf, size_t len) const - { - return std::max(0l, snd_rawmidi_read(m_midi, buf, len)); - } }; struct MIDIOut : public IMIDIOut { snd_rawmidi_t* m_midi; - bool m_virtual; MIDIOut(snd_rawmidi_t* midi, bool virt) - : m_midi(midi), m_virtual(virt) {} + : IMIDIOut(virt), m_midi(midi) {} ~MIDIOut() {snd_rawmidi_close(m_midi);} - bool isVirtual() const {return m_virtual;} + std::string description() const { snd_rawmidi_info_t* info; @@ -404,26 +426,33 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine std::string ret = snd_rawmidi_info_get_name(info); return ret; } + size_t send(const void* buf, size_t len) const { - return std::max(0l, snd_rawmidi_write(m_midi, buf, len)); + return size_t(std::max(0l, snd_rawmidi_write(m_midi, buf, len))); } }; struct MIDIInOut : public IMIDIInOut { + bool m_midiRunning = true; snd_rawmidi_t* m_midiIn; snd_rawmidi_t* m_midiOut; - bool m_virtual; - MIDIInOut(snd_rawmidi_t* midiIn, snd_rawmidi_t* midiOut, bool virt) - : m_midiIn(midiIn), m_midiOut(midiOut), m_virtual(virt) {} + 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), + m_midiThread(std::bind(MIDIReceiveProc, m_midiIn, m_receiver, m_midiRunning)) {} ~MIDIInOut() { + m_midiRunning = false; + if (m_midiThread.joinable()) + m_midiThread.join(); snd_rawmidi_close(m_midiIn); snd_rawmidi_close(m_midiOut); } - bool isVirtual() const {return m_virtual;} + std::string description() const { snd_rawmidi_info_t* info; @@ -432,58 +461,55 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine std::string ret = snd_rawmidi_info_get_name(info); return ret; } - size_t receive(void* buf, size_t len) const - { - return std::max(0l, snd_rawmidi_read(m_midiOut, buf, len)); - } + size_t send(const void* buf, size_t len) const { - return std::max(0l, snd_rawmidi_write(m_midiOut, buf, len)); + return size_t(std::max(0l, snd_rawmidi_write(m_midiOut, buf, len))); } }; - std::unique_ptr newVirtualMIDIIn() + std::unique_ptr newVirtualMIDIIn(ReceiveFunctor&& receiver) { int status; snd_rawmidi_t* midi; - status = snd_rawmidi_open(&midi, nullptr, "virtual", SND_RAWMIDI_NONBLOCK); + status = snd_rawmidi_open(&midi, nullptr, "virtual", 0); if (status) return {}; - return std::make_unique(midi, true); + return std::make_unique(midi, true, std::move(receiver)); } std::unique_ptr newVirtualMIDIOut() { int status; snd_rawmidi_t* midi; - status = snd_rawmidi_open(nullptr, &midi, "virtual", SND_RAWMIDI_NONBLOCK); + status = snd_rawmidi_open(nullptr, &midi, "virtual", 0); if (status) return {}; return std::make_unique(midi, true); } - std::unique_ptr newVirtualMIDIInOut() + std::unique_ptr newVirtualMIDIInOut(ReceiveFunctor&& receiver) { int status; snd_rawmidi_t* midiIn; snd_rawmidi_t* midiOut; - status = snd_rawmidi_open(&midiIn, &midiOut, "virtual", SND_RAWMIDI_NONBLOCK); + status = snd_rawmidi_open(&midiIn, &midiOut, "virtual", 0); if (status) return {}; - return std::make_unique(midiIn, midiOut, true); + return std::make_unique(midiIn, midiOut, true, std::move(receiver)); } - std::unique_ptr newRealMIDIIn(const char* name) + std::unique_ptr newRealMIDIIn(const char* name, ReceiveFunctor&& receiver) { int status; char path[128]; snprintf(path, 128, "hw:%s", name); snd_rawmidi_t* midi; - status = snd_rawmidi_open(&midi, nullptr, path, SND_RAWMIDI_NONBLOCK); + status = snd_rawmidi_open(&midi, nullptr, path, 0); if (status) return {}; - return std::make_unique(midi, true); + return std::make_unique(midi, true, std::move(receiver)); } std::unique_ptr newRealMIDIOut(const char* name) @@ -493,13 +519,13 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine snprintf(path, 128, "hw:%s", name); snd_rawmidi_t* midi; - status = snd_rawmidi_open(nullptr, &midi, path, SND_RAWMIDI_NONBLOCK); + status = snd_rawmidi_open(nullptr, &midi, path, 0); if (status) return {}; return std::make_unique(midi, true); } - std::unique_ptr newRealMIDIInOut(const char* name) + std::unique_ptr newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver) { int status; char path[128]; @@ -507,10 +533,10 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine snd_rawmidi_t* midiIn; snd_rawmidi_t* midiOut; - status = snd_rawmidi_open(&midiIn, &midiOut, path, SND_RAWMIDI_NONBLOCK); + status = snd_rawmidi_open(&midiIn, &midiOut, path, 0); if (status) return {}; - return std::make_unique(midiIn, midiOut, true); + return std::make_unique(midiIn, midiOut, true, std::move(receiver)); } }; diff --git a/lib/audiodev/MIDICommon.cpp b/lib/audiodev/MIDICommon.cpp new file mode 100644 index 0000000..d8cd072 --- /dev/null +++ b/lib/audiodev/MIDICommon.cpp @@ -0,0 +1,12 @@ +#include "MIDICommon.hpp" +#include "boo/audiodev/IMIDIPort.hpp" + +namespace boo +{ + +IMIDIPort::~IMIDIPort() {} +IMIDIIn::~IMIDIIn() {} +IMIDIOut::~IMIDIOut() {} +IMIDIInOut::~IMIDIInOut() {} + +} diff --git a/lib/audiodev/MIDIDecoder.cpp b/lib/audiodev/MIDIDecoder.cpp index 71d9899..5f026c1 100644 --- a/lib/audiodev/MIDIDecoder.cpp +++ b/lib/audiodev/MIDIDecoder.cpp @@ -7,112 +7,118 @@ namespace boo static inline uint8_t clamp7(uint8_t val) {return std::max(0, std::min(127, int(val)));} -bool MIDIDecoder::ReadController::readByte(uint8_t& a) +bool MIDIDecoder::_readContinuedValue(std::vector::const_iterator& it, + std::vector::const_iterator end, + uint32_t& valOut) { - return m_in.receive(&a, 1) != 0; -} - -bool MIDIDecoder::ReadController::read2Bytes(uint8_t& a, uint8_t& b) -{ - uint8_t buf[2]; - int len = m_in.receive(buf, 2); - a = buf[0]; - b = buf[1]; - return len > 1; -} - -bool MIDIDecoder::ReadController::readBuffer(void* buf, size_t len) -{ - return m_in.receive(buf, len) == len; -} - -uint32_t MIDIDecoder::_readContinuedValue(uint8_t a) -{ - uint32_t ret = a & 0x7f; + uint8_t a = *it++; + valOut = a & 0x7f; if (a & 0x80) { - ret <<= 7; - bool good = m_readControl.readByte(a); - if (!good) - return ret; - ret |= a & 0x7f; + if (it == end) + return false; + valOut <<= 7; + a = *it++; + valOut |= a & 0x7f; if (a & 0x80) { - ret <<= 7; - good = m_readControl.readByte(a); - if (!good) - return ret; - ret |= a & 0x7f; + if (it == end) + return false; + valOut <<= 7; + a = *it++; + valOut |= a & 0x7f; } } - return ret; + return true; } -bool MIDIDecoder::receiveBytes() +std::vector::const_iterator +MIDIDecoder::receiveBytes(std::vector::const_iterator begin, + std::vector::const_iterator end) { - uint8_t a, b; - bool good = m_readControl.read2Bytes(a, b); - if (!good) - return false; + std::vector::const_iterator it = begin; + if (it == end) + return begin; + uint8_t a = *it++; + uint8_t b; if (a & 0x80) m_status = a; - else - b = a; uint8_t chan = m_status & 0xf; switch (Status(m_status & 0xf0)) { case Status::NoteOff: { - good = m_readControl.read2Bytes(a, b); - if (!good) - return false; + if (it == end) + return begin; + a = *it++; + if (it == end) + return begin; + b = *it++; m_out.noteOff(chan, clamp7(a), clamp7(b)); break; } case Status::NoteOn: { - good = m_readControl.read2Bytes(a, b); - if (!good) - return false; + if (it == end) + return begin; + a = *it++; + if (it == end) + return begin; + b = *it++; m_out.noteOn(chan, clamp7(a), clamp7(b)); break; } case Status::NotePressure: { - good = m_readControl.read2Bytes(a, b); - if (!good) - return false; + if (it == end) + return begin; + a = *it++; + if (it == end) + return begin; + b = *it++; m_out.notePressure(chan, clamp7(a), clamp7(b)); break; } case Status::ControlChange: { - good = m_readControl.read2Bytes(a, b); - if (!good) - return false; + if (it == end) + return begin; + a = *it++; + if (it == end) + return begin; + b = *it++; m_out.controlChange(chan, clamp7(a), clamp7(b)); break; } case Status::ProgramChange: { - m_out.programChange(chan, clamp7(b)); + if (it == end) + return begin; + a = *it++; + m_out.programChange(chan, clamp7(a)); break; } case Status::ChannelPressure: { - m_out.channelPressure(chan, clamp7(b)); + if (it == end) + return begin; + a = *it++; + m_out.channelPressure(chan, clamp7(a)); break; } case Status::PitchBend: { - good = m_readControl.read2Bytes(a, b); - if (!good) - return false; + if (it == end) + return begin; + a = *it++; + if (it == end) + return begin; + b = *it++; m_out.pitchBend(chan, clamp7(b) * 128 + clamp7(a)); break; } @@ -122,32 +128,37 @@ bool MIDIDecoder::receiveBytes() { case Status::SysEx: { - uint32_t len = _readContinuedValue(a); - std::unique_ptr buf(new uint8_t[len]); - if (!m_readControl.readBuffer(buf.get(), len)) - return false; - m_out.sysex(buf.get(), len); + uint32_t len; + if (!_readContinuedValue(it, end, len) || end - it < len) + return begin; + m_out.sysex(&*it, len); break; } case Status::TimecodeQuarterFrame: { - good = m_readControl.read2Bytes(a, b); - if (!good) - return false; + if (it == end) + return begin; + a = *it++; m_out.timeCodeQuarterFrame(a >> 4 & 0x7, a & 0xf); break; } case Status::SongPositionPointer: { - good = m_readControl.read2Bytes(a, b); - if (!good) - return false; + if (it == end) + return begin; + a = *it++; + if (it == end) + return begin; + b = *it++; m_out.songPositionPointer(clamp7(b) * 128 + clamp7(a)); break; } case Status::SongSelect: { - m_out.songSelect(clamp7(b)); + if (it == end) + return begin; + a = *it++; + m_out.songSelect(clamp7(a)); break; } case Status::TuneRequest: @@ -175,7 +186,7 @@ bool MIDIDecoder::receiveBytes() default: break; } - return true; + return it; } }