From: Steinar H. Gunderson Date: Sun, 9 Oct 2016 13:01:57 +0000 (+0200) Subject: Listen on all MIDI ports (including hotplug). X-Git-Tag: 1.4.0~33 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=6379b0adf2cee84b46d5a0d8e2ee5e6a7d1615cf Listen on all MIDI ports (including hotplug). It's unlikely that there will be lots of real MIDI note data going on in the system, so just slurping up all of it seems reasonable (it's certainly the most user-friendly). --- diff --git a/midi_mapper.cpp b/midi_mapper.cpp index fca926f..dc5eab0 100644 --- a/midi_mapper.cpp +++ b/midi_mapper.cpp @@ -92,7 +92,6 @@ void MIDIMapper::start_thread() void MIDIMapper::thread_func() { - // TODO: Listen on any port, instead of hardcoding 24:0. snd_seq_t *seq; int err; @@ -106,9 +105,29 @@ void MIDIMapper::thread_func() SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION)); - snd_seq_addr_t addr; - RETURN_ON_ERROR("snd_seq_parse_address", snd_seq_parse_address(seq, &addr, "24:0")); - RETURN_ON_ERROR("snd_seq_connect_from", snd_seq_connect_from(seq, 0, addr.client, addr.port)); + // Listen to the announce port (0:1), which will tell us about new ports. + RETURN_ON_ERROR("snd_seq_connect_from", snd_seq_connect_from(seq, 0, /*client=*/0, /*port=*/1)); + + // Now go through all ports and subscribe to them. + snd_seq_client_info_t *cinfo; + snd_seq_client_info_alloca(&cinfo); + + snd_seq_client_info_set_client(cinfo, -1); + while (snd_seq_query_next_client(seq, cinfo) >= 0) { + int client = snd_seq_client_info_get_client(cinfo); + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca(&pinfo); + + snd_seq_port_info_set_client(pinfo, client); + snd_seq_port_info_set_port(pinfo, -1); + while (snd_seq_query_next_port(seq, pinfo) >= 0) { + constexpr int mask = SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; + if ((snd_seq_port_info_get_capability(pinfo) & mask) == mask) { + subscribe_to_port(seq, *snd_seq_port_info_get_addr(pinfo)); + } + } + } int num_alsa_fds = snd_seq_poll_descriptors_count(seq, POLLIN); unique_ptr fds(new pollfd[num_alsa_fds + 1]); @@ -233,14 +252,43 @@ void MIDIMapper::handle_event(snd_seq_t *seq, snd_seq_event_t *event) match_button(note, MIDIMappingBusProto::kClearPeakFieldNumber, MIDIMappingProto::kClearPeakBankFieldNumber, bind(&ControllerReceiver::clear_peak, receiver, _1)); } + case SND_SEQ_EVENT_PORT_START: + subscribe_to_port(seq, event->data.addr); + break; + case SND_SEQ_EVENT_PORT_EXIT: + printf("MIDI port %d:%d went away.\n", event->data.addr.client, event->data.addr.port); + break; case SND_SEQ_EVENT_NOTEOFF: - // Ignore. + case SND_SEQ_EVENT_CLIENT_START: + case SND_SEQ_EVENT_CLIENT_EXIT: + case SND_SEQ_EVENT_CLIENT_CHANGE: + case SND_SEQ_EVENT_PORT_CHANGE: + case SND_SEQ_EVENT_PORT_SUBSCRIBED: + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: break; default: printf("Ignoring MIDI event of unknown type %d.\n", event->type); } } +void MIDIMapper::subscribe_to_port(snd_seq_t *seq, const snd_seq_addr_t &addr) +{ + // Client 0 is basically the system; ignore it. + if (addr.client == 0) { + return; + } + + int err = snd_seq_connect_from(seq, 0, addr.client, addr.port); + if (err < 0) { + // Just print out a warning (i.e., don't die); it could + // very well just be e.g. another application. + printf("Couldn't subscribe to MIDI port %d:%d (%s).\n", + addr.client, addr.port, snd_strerror(err)); + } else { + printf("Subscribed to MIDI port %d:%d.\n", addr.client, addr.port); + } +} + void MIDIMapper::match_controller(int controller, int field_number, int bank_field_number, float value, function func) { if (bank_mismatch(bank_field_number)) { diff --git a/midi_mapper.h b/midi_mapper.h index 0c7bb60..2b59b9f 100644 --- a/midi_mapper.h +++ b/midi_mapper.h @@ -15,6 +15,7 @@ #include class MIDIMappingProto; +typedef struct snd_seq_addr snd_seq_addr_t; typedef struct snd_seq_event snd_seq_event_t; typedef struct _snd_seq snd_seq_t; @@ -50,6 +51,7 @@ public: private: void thread_func(); void handle_event(snd_seq_t *seq, snd_seq_event_t *event); + void subscribe_to_port(snd_seq_t *seq, const snd_seq_addr_t &addr); void match_controller(int controller, int field_number, int bank_field_number, float value, std::function func); void match_button(int note, int field_number, int bank_field_number, std::function func); bool bank_mismatch(int bank_field_number);