ALSA MIDI fixes

This commit is contained in:
Jack Andersen 2018-08-18 14:28:00 -10:00
parent 5e58e989a8
commit 168fb3f516
4 changed files with 91 additions and 27 deletions

View File

@ -76,6 +76,9 @@ struct IAudioVoiceEngine
/** Get list of MIDI input devices found on system */
virtual std::vector<std::pair<std::string, std::string>> 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<IMIDIIn> newVirtualMIDIIn(ReceiveFunctor&& receiver)=0;

View File

@ -8,18 +8,20 @@
namespace boo
{
struct IAudioVoiceEngine;
using ReceiveFunctor = std::function<void(std::vector<uint8_t>&&, 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;

View File

@ -19,6 +19,30 @@ static inline double TimespecToDouble(struct timespec& ts)
struct LinuxMidi : BaseAudioVoiceEngine
{
std::unordered_map<std::string, IMIDIPort*> 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<std::pair<std::string, std::string>> enumerateMIDIInputs() const
{
std::vector<std::pair<std::string, std::string>> 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<LinuxMidi*>(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<LinuxMidi*>(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<LinuxMidi*>(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<MIDIIn>(midi, true, std::move(receiver));
return std::make_unique<MIDIIn>(nullptr, midi, true, std::move(receiver));
}
std::unique_ptr<IMIDIOut> newVirtualMIDIOut()
@ -208,7 +256,7 @@ struct LinuxMidi : BaseAudioVoiceEngine
status = snd_rawmidi_open(nullptr, &midi, "virtual", 0);
if (status)
return {};
return std::make_unique<MIDIOut>(midi, true);
return std::make_unique<MIDIOut>(nullptr, midi, true);
}
std::unique_ptr<IMIDIInOut> 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<MIDIInOut>(midiIn, midiOut, true, std::move(receiver));
return std::make_unique<MIDIInOut>(nullptr, midiIn, midiOut, true, std::move(receiver));
}
std::unique_ptr<IMIDIIn> 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<MIDIIn>(midi, true, std::move(receiver));
auto ret = std::make_unique<MIDIIn>(this, midi, true, std::move(receiver));
_addOpenHandle(name, ret.get());
return ret;
}
std::unique_ptr<IMIDIOut> 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<MIDIOut>(midi, true);
auto ret = std::make_unique<MIDIOut>(this, midi, true);
_addOpenHandle(name, ret.get());
return ret;
}
std::unique_ptr<IMIDIInOut> 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<MIDIInOut>(midiIn, midiOut, true, std::move(receiver));
auto ret = std::make_unique<MIDIInOut>(this, midiIn, midiOut, true, std::move(receiver));
_addOpenHandle(name, ret.get());
return ret;
}
bool useMIDILock() const {return true;}

View File

@ -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<boo::IMIDIIn> newVirtualMIDIIn(boo::ReceiveFunctor&& receiver)
{
std::unique_ptr<boo::IMIDIIn> ret = std::make_unique<MIDIIn>(true, std::move(receiver));
std::unique_ptr<boo::IMIDIIn> ret = std::make_unique<MIDIIn>(nullptr, true, std::move(receiver));
m_midiReceiver = &ret->m_receiver;
return ret;
}