mirror of https://github.com/AxioDL/boo.git
Functor-based MIDI command receiving
This commit is contained in:
parent
7756fcaf76
commit
df61d6678b
|
@ -194,6 +194,7 @@ add_library(boo
|
||||||
lib/audiodev/MIDIEncoder.cpp
|
lib/audiodev/MIDIEncoder.cpp
|
||||||
lib/audiodev/MIDIDecoder.cpp
|
lib/audiodev/MIDIDecoder.cpp
|
||||||
lib/audiodev/MIDICommon.hpp
|
lib/audiodev/MIDICommon.hpp
|
||||||
|
lib/audiodev/MIDICommon.cpp
|
||||||
include/boo/inputdev/IHIDListener.hpp
|
include/boo/inputdev/IHIDListener.hpp
|
||||||
include/boo/IGraphicsContext.hpp
|
include/boo/IGraphicsContext.hpp
|
||||||
include/boo/graphicsdev/IGraphicsDataFactory.hpp
|
include/boo/graphicsdev/IGraphicsDataFactory.hpp
|
||||||
|
|
|
@ -48,22 +48,22 @@ struct IAudioVoiceEngine
|
||||||
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() const=0;
|
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices() const=0;
|
||||||
|
|
||||||
/** Create ad-hoc MIDI in port and register with system */
|
/** Create ad-hoc MIDI in port and register with system */
|
||||||
virtual std::unique_ptr<IMIDIIn> newVirtualMIDIIn()=0;
|
virtual std::unique_ptr<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver)=0;
|
||||||
|
|
||||||
/** Create ad-hoc MIDI out port and register with system */
|
/** Create ad-hoc MIDI out port and register with system */
|
||||||
virtual std::unique_ptr<IMIDIOut> newVirtualMIDIOut()=0;
|
virtual std::unique_ptr<IMIDIOut> newVirtualMIDIOut()=0;
|
||||||
|
|
||||||
/** Create ad-hoc MIDI in/out port and register with system */
|
/** Create ad-hoc MIDI in/out port and register with system */
|
||||||
virtual std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut()=0;
|
virtual std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut(ReceiveFunctor&& receiver)=0;
|
||||||
|
|
||||||
/** Open named MIDI in port, name format depends on OS */
|
/** Open named MIDI in port, name format depends on OS */
|
||||||
virtual std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name)=0;
|
virtual std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name, ReceiveFunctor&& receiver)=0;
|
||||||
|
|
||||||
/** Open named MIDI out port, name format depends on OS */
|
/** Open named MIDI out port, name format depends on OS */
|
||||||
virtual std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name)=0;
|
virtual std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name)=0;
|
||||||
|
|
||||||
/** Open named MIDI in/out port, name format depends on OS */
|
/** Open named MIDI in/out port, name format depends on OS */
|
||||||
virtual std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name)=0;
|
virtual std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver)=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Construct host platform's voice engine */
|
/** Construct host platform's voice engine */
|
||||||
|
|
|
@ -3,35 +3,53 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
|
||||||
|
using ReceiveFunctor = std::function<void(std::vector<uint8_t>&&)>;
|
||||||
|
|
||||||
class IMIDIPort
|
class IMIDIPort
|
||||||
{
|
{
|
||||||
|
bool m_virtual;
|
||||||
|
protected:
|
||||||
|
IMIDIPort(bool virt) : m_virtual(virt) {}
|
||||||
public:
|
public:
|
||||||
virtual ~IMIDIPort() = default;
|
virtual ~IMIDIPort();
|
||||||
virtual bool isVirtual() const=0;
|
bool isVirtual() const {return m_virtual;}
|
||||||
virtual std::string description() const=0;
|
virtual std::string description() const=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IMIDIIn : public IMIDIPort
|
class IMIDIIn : public IMIDIPort
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
|
ReceiveFunctor m_receiver;
|
||||||
|
IMIDIIn(bool virt, ReceiveFunctor&& receiver)
|
||||||
|
: IMIDIPort(virt), m_receiver(std::move(receiver)) {}
|
||||||
public:
|
public:
|
||||||
virtual size_t receive(void* buf, size_t len) const=0;
|
virtual ~IMIDIIn();
|
||||||
};
|
};
|
||||||
|
|
||||||
class IMIDIOut : public IMIDIPort
|
class IMIDIOut : public IMIDIPort
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
|
IMIDIOut(bool virt) : IMIDIPort(virt) {}
|
||||||
public:
|
public:
|
||||||
|
virtual ~IMIDIOut();
|
||||||
virtual size_t send(const void* buf, size_t len) const=0;
|
virtual size_t send(const void* buf, size_t len) const=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IMIDIInOut : public IMIDIPort
|
class IMIDIInOut : public IMIDIPort
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
|
ReceiveFunctor m_receiver;
|
||||||
|
IMIDIInOut(bool virt, ReceiveFunctor&& receiver)
|
||||||
|
: IMIDIPort(virt), m_receiver(std::move(receiver)) {}
|
||||||
public:
|
public:
|
||||||
|
virtual ~IMIDIInOut();
|
||||||
virtual size_t send(const void* buf, size_t len) const=0;
|
virtual size_t send(const void* buf, size_t len) const=0;
|
||||||
virtual size_t receive(void* buf, size_t len) const=0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "boo/audiodev/IMIDIReader.hpp"
|
#include "boo/audiodev/IMIDIReader.hpp"
|
||||||
#include "boo/audiodev/IMIDIPort.hpp"
|
#include "boo/audiodev/IMIDIPort.hpp"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
@ -12,19 +13,14 @@ class MIDIDecoder
|
||||||
{
|
{
|
||||||
IMIDIReader& m_out;
|
IMIDIReader& m_out;
|
||||||
uint8_t m_status = 0;
|
uint8_t m_status = 0;
|
||||||
|
bool _readContinuedValue(std::vector<uint8_t>::const_iterator& it,
|
||||||
struct ReadController
|
std::vector<uint8_t>::const_iterator end,
|
||||||
{
|
uint32_t& valOut);
|
||||||
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);
|
|
||||||
public:
|
public:
|
||||||
MIDIDecoder(IMIDIIn& in, IMIDIReader& out) : m_readControl(in), m_out(out) {}
|
MIDIDecoder(IMIDIReader& out) : m_out(out) {}
|
||||||
bool receiveBytes();
|
std::vector<uint8_t>::const_iterator
|
||||||
|
receiveBytes(std::vector<uint8_t>::const_iterator begin,
|
||||||
|
std::vector<uint8_t>::const_iterator end);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <thread>
|
||||||
#include "AudioVoiceEngine.hpp"
|
#include "AudioVoiceEngine.hpp"
|
||||||
#include "logvisor/logvisor.hpp"
|
#include "logvisor/logvisor.hpp"
|
||||||
|
|
||||||
|
@ -56,15 +57,15 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
return AudioChannelSet::Stereo;
|
return AudioChannelSet::Stereo;
|
||||||
|
|
||||||
static const std::array<AudioChannelSet, 4> testSets =
|
static const std::array<AudioChannelSet, 4> testSets =
|
||||||
{AudioChannelSet::Surround71, AudioChannelSet::Surround51,
|
{{AudioChannelSet::Surround71, AudioChannelSet::Surround51,
|
||||||
AudioChannelSet::Quad, AudioChannelSet::Stereo};
|
AudioChannelSet::Quad, AudioChannelSet::Stereo}};
|
||||||
for (AudioChannelSet set : testSets)
|
for (AudioChannelSet set : testSets)
|
||||||
{
|
{
|
||||||
for (snd_pcm_chmap_query_t** chmap = chmaps ; *chmap != nullptr ; ++chmap)
|
for (snd_pcm_chmap_query_t** chmap = chmaps ; *chmap != nullptr ; ++chmap)
|
||||||
{
|
{
|
||||||
snd_pcm_chmap_t* chm = &(*chmap)->map;
|
snd_pcm_chmap_t* chm = &(*chmap)->map;
|
||||||
uint64_t chBits = 0;
|
uint64_t chBits = 0;
|
||||||
for (int c=0 ; c<chm->channels ; ++c)
|
for (unsigned c=0 ; c<chm->channels ; ++c)
|
||||||
chBits |= 1 << chm->pos[c];
|
chBits |= 1 << chm->pos[c];
|
||||||
|
|
||||||
switch (set)
|
switch (set)
|
||||||
|
@ -204,7 +205,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
{
|
{
|
||||||
snd_pcm_chmap_t* chm = &(*chmap)->map;
|
snd_pcm_chmap_t* chm = &(*chmap)->map;
|
||||||
uint64_t chBits = 0;
|
uint64_t chBits = 0;
|
||||||
for (int c=0 ; c<chm->channels ; ++c)
|
for (unsigned c=0 ; c<chm->channels ; ++c)
|
||||||
chBits |= 1 << chm->pos[c];
|
chBits |= 1 << chm->pos[c];
|
||||||
|
|
||||||
bool good = false;
|
bool good = false;
|
||||||
|
@ -246,7 +247,7 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
chmapOut.m_channelCount = chCount;
|
chmapOut.m_channelCount = chCount;
|
||||||
for (int c=0 ; c<foundChmap->channels ; ++c)
|
for (unsigned c=0 ; c<foundChmap->channels ; ++c)
|
||||||
chmapOut.m_channels[c] = AudioChannel(foundChmap->pos[c] - 3);
|
chmapOut.m_channels[c] = AudioChannel(foundChmap->pos[c] - 3);
|
||||||
snd_pcm_set_chmap(m_pcm, foundChmap);
|
snd_pcm_set_chmap(m_pcm, foundChmap);
|
||||||
snd_pcm_free_chmaps(chmaps);
|
snd_pcm_free_chmaps(chmaps);
|
||||||
|
@ -364,15 +365,41 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
return ret;
|
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<uint8_t>(std::cbegin(buf), std::cbegin(buf) + rdBytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct MIDIIn : public IMIDIIn
|
struct MIDIIn : public IMIDIIn
|
||||||
{
|
{
|
||||||
|
bool m_midiRunning = true;
|
||||||
snd_rawmidi_t* m_midi;
|
snd_rawmidi_t* m_midi;
|
||||||
bool m_virtual;
|
std::thread m_midiThread;
|
||||||
MIDIIn(snd_rawmidi_t* midi, bool virt)
|
|
||||||
: m_midi(midi), m_virtual(virt) {}
|
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
|
std::string description() const
|
||||||
{
|
{
|
||||||
snd_rawmidi_info_t* info;
|
snd_rawmidi_info_t* info;
|
||||||
|
@ -381,21 +408,16 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
std::string ret = snd_rawmidi_info_get_name(info);
|
std::string ret = snd_rawmidi_info_get_name(info);
|
||||||
return ret;
|
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
|
struct MIDIOut : public IMIDIOut
|
||||||
{
|
{
|
||||||
snd_rawmidi_t* m_midi;
|
snd_rawmidi_t* m_midi;
|
||||||
bool m_virtual;
|
|
||||||
MIDIOut(snd_rawmidi_t* midi, bool virt)
|
MIDIOut(snd_rawmidi_t* midi, bool virt)
|
||||||
: m_midi(midi), m_virtual(virt) {}
|
: IMIDIOut(virt), m_midi(midi) {}
|
||||||
|
|
||||||
~MIDIOut() {snd_rawmidi_close(m_midi);}
|
~MIDIOut() {snd_rawmidi_close(m_midi);}
|
||||||
bool isVirtual() const {return m_virtual;}
|
|
||||||
std::string description() const
|
std::string description() const
|
||||||
{
|
{
|
||||||
snd_rawmidi_info_t* info;
|
snd_rawmidi_info_t* info;
|
||||||
|
@ -404,26 +426,33 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
std::string ret = snd_rawmidi_info_get_name(info);
|
std::string ret = snd_rawmidi_info_get_name(info);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t send(const void* buf, size_t len) const
|
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
|
struct MIDIInOut : public IMIDIInOut
|
||||||
{
|
{
|
||||||
|
bool m_midiRunning = true;
|
||||||
snd_rawmidi_t* m_midiIn;
|
snd_rawmidi_t* m_midiIn;
|
||||||
snd_rawmidi_t* m_midiOut;
|
snd_rawmidi_t* m_midiOut;
|
||||||
bool m_virtual;
|
std::thread m_midiThread;
|
||||||
MIDIInOut(snd_rawmidi_t* midiIn, snd_rawmidi_t* midiOut, bool virt)
|
|
||||||
: m_midiIn(midiIn), m_midiOut(midiOut), m_virtual(virt) {}
|
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()
|
~MIDIInOut()
|
||||||
{
|
{
|
||||||
|
m_midiRunning = false;
|
||||||
|
if (m_midiThread.joinable())
|
||||||
|
m_midiThread.join();
|
||||||
snd_rawmidi_close(m_midiIn);
|
snd_rawmidi_close(m_midiIn);
|
||||||
snd_rawmidi_close(m_midiOut);
|
snd_rawmidi_close(m_midiOut);
|
||||||
}
|
}
|
||||||
bool isVirtual() const {return m_virtual;}
|
|
||||||
std::string description() const
|
std::string description() const
|
||||||
{
|
{
|
||||||
snd_rawmidi_info_t* info;
|
snd_rawmidi_info_t* info;
|
||||||
|
@ -432,58 +461,55 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
std::string ret = snd_rawmidi_info_get_name(info);
|
std::string ret = snd_rawmidi_info_get_name(info);
|
||||||
return ret;
|
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
|
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<IMIDIIn> newVirtualMIDIIn()
|
std::unique_ptr<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
snd_rawmidi_t* midi;
|
snd_rawmidi_t* midi;
|
||||||
status = snd_rawmidi_open(&midi, nullptr, "virtual", SND_RAWMIDI_NONBLOCK);
|
status = snd_rawmidi_open(&midi, nullptr, "virtual", 0);
|
||||||
if (status)
|
if (status)
|
||||||
return {};
|
return {};
|
||||||
return std::make_unique<MIDIIn>(midi, true);
|
return std::make_unique<MIDIIn>(midi, true, std::move(receiver));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IMIDIOut> newVirtualMIDIOut()
|
std::unique_ptr<IMIDIOut> newVirtualMIDIOut()
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
snd_rawmidi_t* midi;
|
snd_rawmidi_t* midi;
|
||||||
status = snd_rawmidi_open(nullptr, &midi, "virtual", SND_RAWMIDI_NONBLOCK);
|
status = snd_rawmidi_open(nullptr, &midi, "virtual", 0);
|
||||||
if (status)
|
if (status)
|
||||||
return {};
|
return {};
|
||||||
return std::make_unique<MIDIOut>(midi, true);
|
return std::make_unique<MIDIOut>(midi, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut()
|
std::unique_ptr<IMIDIInOut> newVirtualMIDIInOut(ReceiveFunctor&& receiver)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
snd_rawmidi_t* midiIn;
|
snd_rawmidi_t* midiIn;
|
||||||
snd_rawmidi_t* midiOut;
|
snd_rawmidi_t* midiOut;
|
||||||
status = snd_rawmidi_open(&midiIn, &midiOut, "virtual", SND_RAWMIDI_NONBLOCK);
|
status = snd_rawmidi_open(&midiIn, &midiOut, "virtual", 0);
|
||||||
if (status)
|
if (status)
|
||||||
return {};
|
return {};
|
||||||
return std::make_unique<MIDIInOut>(midiIn, midiOut, true);
|
return std::make_unique<MIDIInOut>(midiIn, midiOut, true, std::move(receiver));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name)
|
std::unique_ptr<IMIDIIn> newRealMIDIIn(const char* name, ReceiveFunctor&& receiver)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
char path[128];
|
char path[128];
|
||||||
snprintf(path, 128, "hw:%s", name);
|
snprintf(path, 128, "hw:%s", name);
|
||||||
|
|
||||||
snd_rawmidi_t* midi;
|
snd_rawmidi_t* midi;
|
||||||
status = snd_rawmidi_open(&midi, nullptr, path, SND_RAWMIDI_NONBLOCK);
|
status = snd_rawmidi_open(&midi, nullptr, path, 0);
|
||||||
if (status)
|
if (status)
|
||||||
return {};
|
return {};
|
||||||
return std::make_unique<MIDIIn>(midi, true);
|
return std::make_unique<MIDIIn>(midi, true, std::move(receiver));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name)
|
std::unique_ptr<IMIDIOut> newRealMIDIOut(const char* name)
|
||||||
|
@ -493,13 +519,13 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
snprintf(path, 128, "hw:%s", name);
|
snprintf(path, 128, "hw:%s", name);
|
||||||
|
|
||||||
snd_rawmidi_t* midi;
|
snd_rawmidi_t* midi;
|
||||||
status = snd_rawmidi_open(nullptr, &midi, path, SND_RAWMIDI_NONBLOCK);
|
status = snd_rawmidi_open(nullptr, &midi, path, 0);
|
||||||
if (status)
|
if (status)
|
||||||
return {};
|
return {};
|
||||||
return std::make_unique<MIDIOut>(midi, true);
|
return std::make_unique<MIDIOut>(midi, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name)
|
std::unique_ptr<IMIDIInOut> newRealMIDIInOut(const char* name, ReceiveFunctor&& receiver)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
char path[128];
|
char path[128];
|
||||||
|
@ -507,10 +533,10 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
|
|
||||||
snd_rawmidi_t* midiIn;
|
snd_rawmidi_t* midiIn;
|
||||||
snd_rawmidi_t* midiOut;
|
snd_rawmidi_t* midiOut;
|
||||||
status = snd_rawmidi_open(&midiIn, &midiOut, path, SND_RAWMIDI_NONBLOCK);
|
status = snd_rawmidi_open(&midiIn, &midiOut, path, 0);
|
||||||
if (status)
|
if (status)
|
||||||
return {};
|
return {};
|
||||||
return std::make_unique<MIDIInOut>(midiIn, midiOut, true);
|
return std::make_unique<MIDIInOut>(midiIn, midiOut, true, std::move(receiver));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include "MIDICommon.hpp"
|
||||||
|
#include "boo/audiodev/IMIDIPort.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
IMIDIPort::~IMIDIPort() {}
|
||||||
|
IMIDIIn::~IMIDIIn() {}
|
||||||
|
IMIDIOut::~IMIDIOut() {}
|
||||||
|
IMIDIInOut::~IMIDIInOut() {}
|
||||||
|
|
||||||
|
}
|
|
@ -7,112 +7,118 @@ namespace boo
|
||||||
|
|
||||||
static inline uint8_t clamp7(uint8_t val) {return std::max(0, std::min(127, int(val)));}
|
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<uint8_t>::const_iterator& it,
|
||||||
|
std::vector<uint8_t>::const_iterator end,
|
||||||
|
uint32_t& valOut)
|
||||||
{
|
{
|
||||||
return m_in.receive(&a, 1) != 0;
|
uint8_t a = *it++;
|
||||||
}
|
valOut = a & 0x7f;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (a & 0x80)
|
if (a & 0x80)
|
||||||
{
|
{
|
||||||
ret <<= 7;
|
if (it == end)
|
||||||
bool good = m_readControl.readByte(a);
|
|
||||||
if (!good)
|
|
||||||
return ret;
|
|
||||||
ret |= a & 0x7f;
|
|
||||||
|
|
||||||
if (a & 0x80)
|
|
||||||
{
|
|
||||||
ret <<= 7;
|
|
||||||
good = m_readControl.readByte(a);
|
|
||||||
if (!good)
|
|
||||||
return ret;
|
|
||||||
ret |= a & 0x7f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MIDIDecoder::receiveBytes()
|
|
||||||
{
|
|
||||||
uint8_t a, b;
|
|
||||||
bool good = m_readControl.read2Bytes(a, b);
|
|
||||||
if (!good)
|
|
||||||
return false;
|
return false;
|
||||||
|
valOut <<= 7;
|
||||||
|
a = *it++;
|
||||||
|
valOut |= a & 0x7f;
|
||||||
|
|
||||||
|
if (a & 0x80)
|
||||||
|
{
|
||||||
|
if (it == end)
|
||||||
|
return false;
|
||||||
|
valOut <<= 7;
|
||||||
|
a = *it++;
|
||||||
|
valOut |= a & 0x7f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t>::const_iterator
|
||||||
|
MIDIDecoder::receiveBytes(std::vector<uint8_t>::const_iterator begin,
|
||||||
|
std::vector<uint8_t>::const_iterator end)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t>::const_iterator it = begin;
|
||||||
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
|
||||||
|
uint8_t a = *it++;
|
||||||
|
uint8_t b;
|
||||||
if (a & 0x80)
|
if (a & 0x80)
|
||||||
m_status = a;
|
m_status = a;
|
||||||
else
|
|
||||||
b = a;
|
|
||||||
|
|
||||||
uint8_t chan = m_status & 0xf;
|
uint8_t chan = m_status & 0xf;
|
||||||
switch (Status(m_status & 0xf0))
|
switch (Status(m_status & 0xf0))
|
||||||
{
|
{
|
||||||
case Status::NoteOff:
|
case Status::NoteOff:
|
||||||
{
|
{
|
||||||
good = m_readControl.read2Bytes(a, b);
|
if (it == end)
|
||||||
if (!good)
|
return begin;
|
||||||
return false;
|
a = *it++;
|
||||||
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
b = *it++;
|
||||||
m_out.noteOff(chan, clamp7(a), clamp7(b));
|
m_out.noteOff(chan, clamp7(a), clamp7(b));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::NoteOn:
|
case Status::NoteOn:
|
||||||
{
|
{
|
||||||
good = m_readControl.read2Bytes(a, b);
|
if (it == end)
|
||||||
if (!good)
|
return begin;
|
||||||
return false;
|
a = *it++;
|
||||||
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
b = *it++;
|
||||||
m_out.noteOn(chan, clamp7(a), clamp7(b));
|
m_out.noteOn(chan, clamp7(a), clamp7(b));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::NotePressure:
|
case Status::NotePressure:
|
||||||
{
|
{
|
||||||
good = m_readControl.read2Bytes(a, b);
|
if (it == end)
|
||||||
if (!good)
|
return begin;
|
||||||
return false;
|
a = *it++;
|
||||||
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
b = *it++;
|
||||||
m_out.notePressure(chan, clamp7(a), clamp7(b));
|
m_out.notePressure(chan, clamp7(a), clamp7(b));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::ControlChange:
|
case Status::ControlChange:
|
||||||
{
|
{
|
||||||
good = m_readControl.read2Bytes(a, b);
|
if (it == end)
|
||||||
if (!good)
|
return begin;
|
||||||
return false;
|
a = *it++;
|
||||||
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
b = *it++;
|
||||||
m_out.controlChange(chan, clamp7(a), clamp7(b));
|
m_out.controlChange(chan, clamp7(a), clamp7(b));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::ProgramChange:
|
case Status::ProgramChange:
|
||||||
{
|
{
|
||||||
m_out.programChange(chan, clamp7(b));
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
a = *it++;
|
||||||
|
m_out.programChange(chan, clamp7(a));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::ChannelPressure:
|
case Status::ChannelPressure:
|
||||||
{
|
{
|
||||||
m_out.channelPressure(chan, clamp7(b));
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
a = *it++;
|
||||||
|
m_out.channelPressure(chan, clamp7(a));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::PitchBend:
|
case Status::PitchBend:
|
||||||
{
|
{
|
||||||
good = m_readControl.read2Bytes(a, b);
|
if (it == end)
|
||||||
if (!good)
|
return begin;
|
||||||
return false;
|
a = *it++;
|
||||||
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
b = *it++;
|
||||||
m_out.pitchBend(chan, clamp7(b) * 128 + clamp7(a));
|
m_out.pitchBend(chan, clamp7(b) * 128 + clamp7(a));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -122,32 +128,37 @@ bool MIDIDecoder::receiveBytes()
|
||||||
{
|
{
|
||||||
case Status::SysEx:
|
case Status::SysEx:
|
||||||
{
|
{
|
||||||
uint32_t len = _readContinuedValue(a);
|
uint32_t len;
|
||||||
std::unique_ptr<uint8_t[]> buf(new uint8_t[len]);
|
if (!_readContinuedValue(it, end, len) || end - it < len)
|
||||||
if (!m_readControl.readBuffer(buf.get(), len))
|
return begin;
|
||||||
return false;
|
m_out.sysex(&*it, len);
|
||||||
m_out.sysex(buf.get(), len);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::TimecodeQuarterFrame:
|
case Status::TimecodeQuarterFrame:
|
||||||
{
|
{
|
||||||
good = m_readControl.read2Bytes(a, b);
|
if (it == end)
|
||||||
if (!good)
|
return begin;
|
||||||
return false;
|
a = *it++;
|
||||||
m_out.timeCodeQuarterFrame(a >> 4 & 0x7, a & 0xf);
|
m_out.timeCodeQuarterFrame(a >> 4 & 0x7, a & 0xf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::SongPositionPointer:
|
case Status::SongPositionPointer:
|
||||||
{
|
{
|
||||||
good = m_readControl.read2Bytes(a, b);
|
if (it == end)
|
||||||
if (!good)
|
return begin;
|
||||||
return false;
|
a = *it++;
|
||||||
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
b = *it++;
|
||||||
m_out.songPositionPointer(clamp7(b) * 128 + clamp7(a));
|
m_out.songPositionPointer(clamp7(b) * 128 + clamp7(a));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::SongSelect:
|
case Status::SongSelect:
|
||||||
{
|
{
|
||||||
m_out.songSelect(clamp7(b));
|
if (it == end)
|
||||||
|
return begin;
|
||||||
|
a = *it++;
|
||||||
|
m_out.songSelect(clamp7(a));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Status::TuneRequest:
|
case Status::TuneRequest:
|
||||||
|
@ -175,7 +186,7 @@ bool MIDIDecoder::receiveBytes()
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue