Dynamic ALSA MIDI I/O device discovery and re/connect

This commit is contained in:
Robin Gareus 2018-12-23 21:48:07 +01:00
parent 2d87af1988
commit 79e247e00a
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
2 changed files with 152 additions and 2 deletions

View File

@ -71,6 +71,7 @@ AlsaAudioBackend::AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info)
, _n_outputs (0)
, _systemic_audio_input_latency (0)
, _systemic_audio_output_latency (0)
, _midi_device_thread_active (false)
, _dsp_load (0)
, _processed_samples (0)
, _port_change_flag (false)
@ -947,6 +948,8 @@ AlsaAudioBackend::_start (bool for_latency_measurement)
return ProcessThreadStartError;
}
_midi_device_thread_active = listen_for_midi_device_changes ();
#if 1
if (NULL != getenv ("ALSAEXT")) {
boost::char_separator<char> sep (";");
@ -992,6 +995,8 @@ AlsaAudioBackend::stop ()
return -1;
}
stop_listen_for_midi_device_changes ();
while (!_rmidi_out.empty ()) {
AlsaMidiIO *m = _rmidi_out.back ();
m->stop();
@ -1386,6 +1391,142 @@ AlsaAudioBackend::register_system_audio_ports()
return 0;
}
void
AlsaAudioBackend::auto_update_midi_devices ()
{
std::map<std::string, std::string> devices;
if (_midi_driver_option == _("ALSA raw devices")) {
get_alsa_rawmidi_device_names (devices);
} else if (_midi_driver_option == _("ALSA sequencer")) {
get_alsa_sequencer_names (devices);
} else {
return;
}
/* find new devices */
for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
if (_midi_devices.find (i->first) != _midi_devices.end()) {
continue;
}
printf ("NEW MIDI DEVICE %s", i->first.c_str());
_midi_devices[i->first] = new AlsaMidiDeviceInfo (false);
set_midi_device_enabled (i->first, true);
}
for (std::map<std::string, struct AlsaMidiDeviceInfo*>::iterator i = _midi_devices.begin (); i != _midi_devices.end(); ) {
if (devices.find (i->first) != devices.end()) {
++i;
continue;
}
printf ("REMOVE MIDI DEVICE %s", i->first.c_str());
set_midi_device_enabled (i->first, false);
i = _midi_devices.erase (i);
}
}
void*
AlsaAudioBackend::_midi_device_thread (void* arg)
{
AlsaAudioBackend* self = static_cast<AlsaAudioBackend*>(arg);
self->midi_device_thread ();
pthread_exit (0);
return 0;
}
void
AlsaAudioBackend::midi_device_thread ()
{
snd_seq_t* seq;
if (snd_seq_open (&seq, "hw", SND_SEQ_OPEN_INPUT, 0) < 0) {
return;
}
if (snd_seq_set_client_name (seq, "Ardour")) {
snd_seq_close (seq);
return;
}
if (snd_seq_nonblock (seq, 1) < 0) {
snd_seq_close (seq);
return;
}
int npfds = snd_seq_poll_descriptors_count (seq, POLLIN);
if (npfds < 1) {
snd_seq_close (seq);
return;
}
int port = snd_seq_create_simple_port (seq, "port", SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT, SND_SEQ_PORT_TYPE_APPLICATION);
snd_seq_connect_from (seq, port, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE);
struct pollfd* pfds = (struct pollfd*) malloc (npfds * sizeof(struct pollfd));
snd_seq_poll_descriptors (seq, pfds, npfds, POLLIN);
snd_seq_drop_input (seq);
bool do_poll = true;
while (_run) {
if (do_poll) {
int perr = poll (pfds, npfds, 200 /* ms */);
if (perr == 0) {
continue;
}
if (perr < 0) {
break;
}
}
snd_seq_event_t *event;
ssize_t err = snd_seq_event_input (seq, &event);
#if EAGAIN == EWOULDBLOCK
if ((err == -EAGAIN) || (err == -ENOSPC))
#else
if ((err == -EAGAIN) || (err == -EWOULDBLOCK) || (err == -ENOSPC))
#endif
{
do_poll = true;
continue;
}
if (err < 0) {
break;
}
assert (event->source.client == SND_SEQ_CLIENT_SYSTEM);
const snd_seq_addr_t addr = event->data.addr;
switch (event->type) {
case SND_SEQ_EVENT_PORT_START:
case SND_SEQ_EVENT_PORT_EXIT:
case SND_SEQ_EVENT_PORT_CHANGE:
auto_update_midi_devices ();
engine.request_device_list_update();
default:
break;
}
do_poll = (0 == err);
}
free (pfds);
snd_seq_delete_simple_port (seq, port);
snd_seq_close (seq);
}
bool
AlsaAudioBackend::listen_for_midi_device_changes ()
{
if (pthread_create (&_midi_device_thread_id, NULL, _midi_device_thread, this)) {
return false;
}
return true;
}
void
AlsaAudioBackend::stop_listen_for_midi_device_changes ()
{
if (!_midi_device_thread_active) {
return;
}
pthread_join (_midi_device_thread_id, NULL);
_midi_device_thread_active = false;
}
/* set playback-latency for _system_inputs
* and capture-latency for _system_outputs
*/

View File

@ -374,8 +374,8 @@ class AlsaAudioBackend : public AudioBackend {
bool enabled;
uint32_t systemic_input_latency;
uint32_t systemic_output_latency;
AlsaMidiDeviceInfo()
: enabled (true)
AlsaMidiDeviceInfo (bool en = true)
: enabled (en)
, systemic_input_latency (0)
, systemic_output_latency (0)
{}
@ -384,6 +384,15 @@ class AlsaAudioBackend : public AudioBackend {
mutable std::map<std::string, struct AlsaMidiDeviceInfo *> _midi_devices;
struct AlsaMidiDeviceInfo * midi_device_info(std::string const) const;
/* midi device changes */
void auto_update_midi_devices();
bool listen_for_midi_device_changes ();
void stop_listen_for_midi_device_changes ();
void midi_device_thread ();
static void* _midi_device_thread (void *arg);
pthread_t _midi_device_thread_id;
bool _midi_device_thread_active;
/* processing */
float _dsp_load;
ARDOUR::DSPLoadCalculator _dsp_load_calc;