summaryrefslogtreecommitdiff
path: root/libs/backends/alsa
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2018-12-23 21:48:07 +0100
committerRobin Gareus <robin@gareus.org>2018-12-23 21:58:56 +0100
commit79e247e00a6812c989405a560d6ff8debb413eb5 (patch)
tree1c7ab9e3706f8aa5756b9ec7e27f796dc20e58b3 /libs/backends/alsa
parent2d87af198868aa9f41338569947d95f6f0185e6e (diff)
Dynamic ALSA MIDI I/O device discovery and re/connect
Diffstat (limited to 'libs/backends/alsa')
-rw-r--r--libs/backends/alsa/alsa_audiobackend.cc141
-rw-r--r--libs/backends/alsa/alsa_audiobackend.h13
2 files changed, 152 insertions, 2 deletions
diff --git a/libs/backends/alsa/alsa_audiobackend.cc b/libs/backends/alsa/alsa_audiobackend.cc
index dc085aadbd..ecbeb5a058 100644
--- a/libs/backends/alsa/alsa_audiobackend.cc
+++ b/libs/backends/alsa/alsa_audiobackend.cc
@@ -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
*/
diff --git a/libs/backends/alsa/alsa_audiobackend.h b/libs/backends/alsa/alsa_audiobackend.h
index 37889c78cb..ef218b49a1 100644
--- a/libs/backends/alsa/alsa_audiobackend.h
+++ b/libs/backends/alsa/alsa_audiobackend.h
@@ -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;