From e96b608758cd1bce779b8812f5b271fdc20d20d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20L=C3=B3pez-Cabanillas?= Date: Tue, 11 Jan 2022 11:01:27 +0100 Subject: [PATCH 01/11] auto connect all ports in alsa_seq MIDI driver Context: ticket #414 The alsa_seq driver creates multiple ports (N/16) for N="synth.midi-channels", each mapped to synth channels: port*16+channel. This is already implemented. When the "midi.autoconnect" is specified, instead of subscribing only the last created port, all ports are sequencially subscribed to each available external MIDI inputs. When the last port has been subscribed, the next subscription starts again from port 0. --- src/drivers/fluid_alsa.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/drivers/fluid_alsa.c b/src/drivers/fluid_alsa.c index 6f930edf2..d28c25483 100644 --- a/src/drivers/fluid_alsa.c +++ b/src/drivers/fluid_alsa.c @@ -35,6 +35,7 @@ #define ALSA_PCM_NEW_HW_PARAMS_API #include #include +#include #include "fluid_lash.h" @@ -919,6 +920,13 @@ static void fluid_alsa_seq_autoconnect_port_info(fluid_alsa_seq_driver_t *dev, s } FLUID_LOG(FLUID_INFO, "Connection of %s succeeded", pname); + + /* Calculate the next port to be auto-connected. When all ports have been connected + * in sequence, start again from port 0 for the next auto-connection. */ + dev->autoconn_dest.port++; + if (dev->autoconn_dest.port >= dev->port_count) { + dev->autoconn_dest.port = 0; + } } // Autoconnect a single client port (by id) to autoconnect_dest if it has right type/capabilities @@ -939,16 +947,19 @@ static void fluid_alsa_seq_autoconnect_port(fluid_alsa_seq_driver_t *dev, int cl fluid_alsa_seq_autoconnect_port_info(dev, pinfo); } -// Connect available ALSA MIDI inputs to the provided port_info -static void fluid_alsa_seq_autoconnect(fluid_alsa_seq_driver_t *dev, const snd_seq_port_info_t *dest_pinfo) +// Connect available ALSA MIDI inputs +static void fluid_alsa_seq_autoconnect(fluid_alsa_seq_driver_t *dev) { int err; snd_seq_t *seq = dev->seq_handle; snd_seq_client_info_t *cinfo; snd_seq_port_info_t *pinfo; - // subscribe to future new clients/ports showing up - if((err = snd_seq_connect_from(seq, snd_seq_port_info_get_port(dest_pinfo), + /* Subscribe to system:announce for future new clients/ports showing up. + * This subscription is made to the first port, and does not rotate + * dev->autoconn_dest, because this subscription never provides + * MIDI channel messages, only system (ALSA) messages. */ + if((err = snd_seq_connect_from(seq, dev->autoconn_dest.port, SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE)) < 0) { FLUID_LOG(FLUID_ERR, "snd_seq_connect_from() failed: %s", snd_strerror(err)); @@ -957,8 +968,6 @@ static void fluid_alsa_seq_autoconnect(fluid_alsa_seq_driver_t *dev, const snd_s snd_seq_client_info_alloca(&cinfo); snd_seq_port_info_alloca(&pinfo); - dev->autoconn_dest = *snd_seq_port_info_get_addr(dest_pinfo); - snd_seq_client_info_set_client(cinfo, -1); while(snd_seq_query_next_client(seq, cinfo) >= 0) @@ -1100,7 +1109,7 @@ new_fluid_alsa_seq_driver(fluid_settings_t *settings, FLUID_MEMSET(port_info, 0, snd_seq_port_info_sizeof()); fluid_settings_getint(settings, "synth.midi-channels", &midi_channels); - dev->port_count = midi_channels / 16; + dev->port_count = ceil(midi_channels / 16); snd_seq_port_info_set_capability(port_info, SND_SEQ_PORT_CAP_WRITE | @@ -1140,7 +1149,10 @@ new_fluid_alsa_seq_driver(fluid_settings_t *settings, if(dev->autoconn_inputs) { - fluid_alsa_seq_autoconnect(dev, port_info); + /* The next port to be auto-connected, starting by port 0 */ + dev->autoconn_dest = *snd_seq_port_info_get_addr(port_info); + dev->autoconn_dest.port = 0; + fluid_alsa_seq_autoconnect(dev); } /* tell the lash server our client id */ From d031d4bab75c5f09f5dee1d97f6440847defd91d Mon Sep 17 00:00:00 2001 From: Pedro Lopez-Cabanillas Date: Wed, 12 Jan 2022 18:20:40 +0100 Subject: [PATCH 02/11] auto connect for winmidi driver; synth.midi-channels setting limit respected in channel mappings --- src/drivers/fluid_winmidi.c | 44 +++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index ed8bd84ac..55e3f92f2 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -37,11 +37,13 @@ * and forward these messages on distinct MIDI channels set. * 1.1)For example, if the user chooses 2 devices at index 0 and 1, the user * must specify this by putting the name "0;1" in midi.winmidi.device setting. - * We get a fictif device composed of real devices (0,1). This fictif device + * We get a fictive device composed of real devices (0,1). This fictive device * behaves like a device with 32 MIDI channels whose messages are forwarded to * driver output as this: * - MIDI messages from real device 0 are output to MIDI channels set 0 to 15. * - MIDI messages from real device 1 are output to MIDI channels set 15 to 31. + * The above example assumes the setting synth.midi-channels value 32, but the + * default value for this setting is 16, in which case there will be no mapping. * * 1.2)Now another example with the name "1;0". The driver will forward * MIDI messages as this: @@ -57,6 +59,13 @@ * or use the multi device naming "0" (specifying only device index 0). * Both naming choice allows the driver to handle the same single device. * + * 3)If the device name is "default" and the setting "midi.autoconnect" is enabled, + * then all the available devices are opened, applying the appropriate channel + * mappings to each device (the first device is mapped to the 16 first channels, + * the second one to the next 16 channels, and so on with the limit of the + * synth.midi-channels setting. After arriving to the channels limit, the mapping + * restars with the channels 1-16. + * If the device name is specified, then midi.autoconnect setting is ignored. */ #include "fluidsynth_priv.h" @@ -458,6 +467,22 @@ fluid_winmidi_parse_device_name(fluid_winmidi_driver_t *dev, char *dev_name) return dev_count; } +static void fluid_winmidi_autoconnect_build_name(char *name) +{ + int i, j, n = 0; + int num = midiInGetNumDevs(); + if (num < 11) { + memset(name, 0, MAXPNAMELEN); + for(i=0; idev_infos[i]; dev_infos->dev = dev; /* driver structure */ dev_infos->midi_num = i; /* device order number */ - dev_infos->channel_map = i * 16; /* map from input to output */ + dev_infos->channel_map = ch_map; /* map from input to output */ + /* calculate the next channel mapping, up to synth.midi-channels */ + ch_map += 16; + if (ch_map >= midi_channels) { + ch_map = 0; + } FLUID_LOG(FLUID_DBG, "opening device at index %d", dev_infos->dev_idx); res = midiInOpen(&dev_infos->hmidiin, dev_infos->dev_idx, (DWORD_PTR) fluid_winmidi_callback, From 906ecb6ef61701534fb84e424605d13a72130486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20L=C3=B3pez-Cabanillas?= Date: Thu, 13 Jan 2022 08:19:59 +0100 Subject: [PATCH 03/11] Enumeration of ALSA Raw MIDI devices This is a preliminary revision of the ALSA Raw MIDI driver, implementing the enumeration of Raw MIDI devices. The list is retrieved once, before the actual driver is created, and it is stored as options in the setting "midi.alsa.device". --- src/drivers/fluid_alsa.c | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/drivers/fluid_alsa.c b/src/drivers/fluid_alsa.c index d28c25483..fb80294be 100644 --- a/src/drivers/fluid_alsa.c +++ b/src/drivers/fluid_alsa.c @@ -630,7 +630,62 @@ static fluid_thread_return_t fluid_alsa_audio_run_s16(void *d) void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings) { + int card = -1; + int err = 0; + fluid_settings_register_str(settings, "midi.alsa.device", "default", 0); + fluid_settings_add_option(settings, "midi.alsa.device", "default"); + + /* Enumeration of ALSA Raw MIDI devices */ + err = snd_card_next(&card); + while ((err == 0) && (card >= 0)) { + int device = -1; + snd_ctl_t *ctl; + char card_name[32]; + + sprintf(card_name, "hw:%d", card); + err = snd_ctl_open(&ctl, card_name, 0); + if (err == 0) { + for (;;) { + int num_subs; + int subdevice; + char newoption[64]; + snd_rawmidi_info_t *info; + const char *name; + const char *sub_name; + + err = snd_ctl_rawmidi_next_device(ctl, &device); + if ((err < 0) || (device < 0)) + break; + snd_rawmidi_info_alloca(&info); + snd_rawmidi_info_set_device(info, device); + snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); + err = snd_ctl_rawmidi_info(ctl, info); + if (err == 0) + num_subs = snd_rawmidi_info_get_subdevices_count(info); + else + break; + for (subdevice = 0; subdevice < num_subs; ++subdevice) { + snd_rawmidi_info_set_subdevice(info, subdevice); + err = snd_ctl_rawmidi_info(ctl, info); + if (err == 0) { + name = snd_rawmidi_info_get_name(info); + sub_name = snd_rawmidi_info_get_subdevice_name(info); + if (subdevice == 0 && sub_name[0] == '\0') { + sprintf(newoption, "hw:%d,%d %s", card, device, name); + fluid_settings_add_option(settings, "midi.alsa.device", newoption); + break; + } else { + sprintf(newoption, "hw:%d,%d,%d %s", card, device, subdevice, sub_name); + fluid_settings_add_option(settings, "midi.alsa.device", newoption); + } + } + } + } + } + snd_ctl_close(ctl); + err = snd_card_next(&card); + } } /* From 734de08ce9fd638be70d85725ba3af993e4183fb Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 6 Sep 2022 14:19:04 +0200 Subject: [PATCH 04/11] Format fluid_alsa.c --- src/drivers/fluid_alsa.c | 47 ++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/src/drivers/fluid_alsa.c b/src/drivers/fluid_alsa.c index fb80294be..61b928f97 100644 --- a/src/drivers/fluid_alsa.c +++ b/src/drivers/fluid_alsa.c @@ -554,7 +554,7 @@ static fluid_thread_return_t fluid_alsa_audio_run_s16(void *d) { FLUID_MEMSET(left, 0, buffer_size * sizeof(*left)); FLUID_MEMSET(right, 0, buffer_size * sizeof(*right)); - + (*dev->callback)(dev->data, buffer_size, 0, NULL, 2, handle); /* convert floating point data to 16 bit (with dithering) */ @@ -638,15 +638,20 @@ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings) /* Enumeration of ALSA Raw MIDI devices */ err = snd_card_next(&card); - while ((err == 0) && (card >= 0)) { + + while((err == 0) && (card >= 0)) + { int device = -1; snd_ctl_t *ctl; char card_name[32]; sprintf(card_name, "hw:%d", card); err = snd_ctl_open(&ctl, card_name, 0); - if (err == 0) { - for (;;) { + + if(err == 0) + { + for(;;) + { int num_subs; int subdevice; char newoption[64]; @@ -655,27 +660,44 @@ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings) const char *sub_name; err = snd_ctl_rawmidi_next_device(ctl, &device); - if ((err < 0) || (device < 0)) + + if((err < 0) || (device < 0)) + { break; + } + snd_rawmidi_info_alloca(&info); snd_rawmidi_info_set_device(info, device); snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); err = snd_ctl_rawmidi_info(ctl, info); - if (err == 0) + + if(err == 0) + { num_subs = snd_rawmidi_info_get_subdevices_count(info); + } else + { break; - for (subdevice = 0; subdevice < num_subs; ++subdevice) { + } + + for(subdevice = 0; subdevice < num_subs; ++subdevice) + { snd_rawmidi_info_set_subdevice(info, subdevice); err = snd_ctl_rawmidi_info(ctl, info); - if (err == 0) { + + if(err == 0) + { name = snd_rawmidi_info_get_name(info); sub_name = snd_rawmidi_info_get_subdevice_name(info); - if (subdevice == 0 && sub_name[0] == '\0') { + + if(subdevice == 0 && sub_name[0] == '\0') + { sprintf(newoption, "hw:%d,%d %s", card, device, name); fluid_settings_add_option(settings, "midi.alsa.device", newoption); break; - } else { + } + else + { sprintf(newoption, "hw:%d,%d,%d %s", card, device, subdevice, sub_name); fluid_settings_add_option(settings, "midi.alsa.device", newoption); } @@ -683,6 +705,7 @@ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings) } } } + snd_ctl_close(ctl); err = snd_card_next(&card); } @@ -979,7 +1002,9 @@ static void fluid_alsa_seq_autoconnect_port_info(fluid_alsa_seq_driver_t *dev, s /* Calculate the next port to be auto-connected. When all ports have been connected * in sequence, start again from port 0 for the next auto-connection. */ dev->autoconn_dest.port++; - if (dev->autoconn_dest.port >= dev->port_count) { + + if(dev->autoconn_dest.port >= dev->port_count) + { dev->autoconn_dest.port = 0; } } From cb89e7f0bef7c381e7b19b124111acd57c23e81a Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 6 Sep 2022 14:31:05 +0200 Subject: [PATCH 05/11] Do no call alloca in loop; use snprintf --- src/drivers/fluid_alsa.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/drivers/fluid_alsa.c b/src/drivers/fluid_alsa.c index 61b928f97..186829ba6 100644 --- a/src/drivers/fluid_alsa.c +++ b/src/drivers/fluid_alsa.c @@ -632,10 +632,13 @@ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings) { int card = -1; int err = 0; + snd_rawmidi_info_t *info; fluid_settings_register_str(settings, "midi.alsa.device", "default", 0); fluid_settings_add_option(settings, "midi.alsa.device", "default"); + snd_rawmidi_info_alloca(&info); + /* Enumeration of ALSA Raw MIDI devices */ err = snd_card_next(&card); @@ -645,7 +648,7 @@ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings) snd_ctl_t *ctl; char card_name[32]; - sprintf(card_name, "hw:%d", card); + FLUID_SNPRINTF(card_name, sizeof(card_name), "hw:%d", card); err = snd_ctl_open(&ctl, card_name, 0); if(err == 0) @@ -655,7 +658,6 @@ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings) int num_subs; int subdevice; char newoption[64]; - snd_rawmidi_info_t *info; const char *name; const char *sub_name; @@ -666,7 +668,6 @@ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings) break; } - snd_rawmidi_info_alloca(&info); snd_rawmidi_info_set_device(info, device); snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT); err = snd_ctl_rawmidi_info(ctl, info); @@ -692,13 +693,13 @@ void fluid_alsa_rawmidi_driver_settings(fluid_settings_t *settings) if(subdevice == 0 && sub_name[0] == '\0') { - sprintf(newoption, "hw:%d,%d %s", card, device, name); + FLUID_SNPRINTF(newoption, sizeof(newoption), "hw:%d,%d %s", card, device, name); fluid_settings_add_option(settings, "midi.alsa.device", newoption); break; } else { - sprintf(newoption, "hw:%d,%d,%d %s", card, device, subdevice, sub_name); + FLUID_SNPRINTF(newoption, sizeof(newoption), "hw:%d,%d,%d %s", card, device, subdevice, sub_name); fluid_settings_add_option(settings, "midi.alsa.device", newoption); } } From 7a81d39b9ed3eadcbb12b5ef1ad0b14225a0ded0 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 6 Sep 2022 14:32:31 +0200 Subject: [PATCH 06/11] Format fluid_winmidi.c --- src/drivers/fluid_winmidi.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index 55e3f92f2..ef7049acc 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -471,15 +471,20 @@ static void fluid_winmidi_autoconnect_build_name(char *name) { int i, j, n = 0; int num = midiInGetNumDevs(); - if (num < 11) { + + if(num < 11) + { memset(name, 0, MAXPNAMELEN); - for(i=0; ichannel_map = ch_map; /* map from input to output */ /* calculate the next channel mapping, up to synth.midi-channels */ ch_map += 16; - if (ch_map >= midi_channels) { + + if(ch_map >= midi_channels) + { ch_map = 0; } + FLUID_LOG(FLUID_DBG, "opening device at index %d", dev_infos->dev_idx); res = midiInOpen(&dev_infos->hmidiin, dev_infos->dev_idx, (DWORD_PTR) fluid_winmidi_callback, From b1d546fff161cbf62db11b76225c288af74a8e72 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 6 Sep 2022 14:47:03 +0200 Subject: [PATCH 07/11] Use snprintf --- src/drivers/fluid_winmidi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index ef7049acc..8d74bb196 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -474,12 +474,12 @@ static void fluid_winmidi_autoconnect_build_name(char *name) if(num < 11) { - memset(name, 0, MAXPNAMELEN); + FLUID_MEMSET(name, 0, MAXPNAMELEN); for(i = 0; i < num; ++i) { char x[4]; - j = snprintf(x, 4, "%d;", i); + j = FLUID_SNPRINTF(x, sizeof(x), "%d;", i); strncat(name, x, j); n += j; } From f4897077ba6dd96ffb5dd9ec3d1a6d80786afbd5 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 6 Sep 2022 14:49:40 +0200 Subject: [PATCH 08/11] Add debug log for winmidi autoconnect dev name --- src/drivers/fluid_winmidi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index 8d74bb196..7fbc8b19f 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -525,6 +525,7 @@ new_fluid_winmidi_driver(fluid_settings_t *settings, if((strcmp(dev_name, "default") == 0) && (autoconnect_inputs != 0)) { fluid_winmidi_autoconnect_build_name(dev_name); + FLUID_LOG(FLUID_DBG, "winmidi: autoconnect device name is now '%s'", dev_name); } /* parse device name, get the maximum number of devices to handle */ From ba8787c0fb29079852873e86de2174dd59785e78 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 6 Sep 2022 14:53:54 +0200 Subject: [PATCH 09/11] Use float division in fluid_alsa.c --- src/drivers/fluid_alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/fluid_alsa.c b/src/drivers/fluid_alsa.c index 186829ba6..ef1a1e203 100644 --- a/src/drivers/fluid_alsa.c +++ b/src/drivers/fluid_alsa.c @@ -1190,7 +1190,7 @@ new_fluid_alsa_seq_driver(fluid_settings_t *settings, FLUID_MEMSET(port_info, 0, snd_seq_port_info_sizeof()); fluid_settings_getint(settings, "synth.midi-channels", &midi_channels); - dev->port_count = ceil(midi_channels / 16); + dev->port_count = ceil(midi_channels / 16.0f); snd_seq_port_info_set_capability(port_info, SND_SEQ_PORT_CAP_WRITE | From c3cbf30a5ac9a25755c9784e35b5dd9928461629 Mon Sep 17 00:00:00 2001 From: derselbst Date: Tue, 6 Sep 2022 15:00:03 +0200 Subject: [PATCH 10/11] Update documentation of midi.alsa.device --- doc/fluidsettings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/fluidsettings.xml b/doc/fluidsettings.xml index 7a4811951..6797c27d3 100644 --- a/doc/fluidsettings.xml +++ b/doc/fluidsettings.xml @@ -736,7 +736,7 @@ Developers: alsa.device str default - ALSA MIDI hardware device to use for RAW ALSA MIDI driver (not to be confused with the MIDI port). + ALSA MIDI hardware device to use for RAW ALSA MIDI driver (not to be confused with the MIDI port). Since fluidsynth 2.3.0 this setting will be populated with available devices when fluidsynth starts up. alsa_seq.device From 55277760aca6bbbbb9c97c5f0dea12f25db7cf4c Mon Sep 17 00:00:00 2001 From: derselbst Date: Thu, 8 Sep 2022 10:56:24 +0200 Subject: [PATCH 11/11] Make check for MAXPNAMELEN more explicit --- src/drivers/fluid_winmidi.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/drivers/fluid_winmidi.c b/src/drivers/fluid_winmidi.c index 76487f4ba..b42edddc1 100644 --- a/src/drivers/fluid_winmidi.c +++ b/src/drivers/fluid_winmidi.c @@ -476,23 +476,27 @@ fluid_winmidi_parse_device_name(fluid_winmidi_driver_t *dev, char *dev_name) static void fluid_winmidi_autoconnect_build_name(char *name) { + char new_name[MAXPNAMELEN] = { 0 }; int i, j, n = 0; int num = midiInGetNumDevs(); - if(num < 11) + for (i = 0; i < num; ++i) { - FLUID_MEMSET(name, 0, MAXPNAMELEN); - - for(i = 0; i < num; ++i) + char x[4]; + j = FLUID_SNPRINTF(x, sizeof(x), "%d;", i); + n += j; + if (n >= sizeof(new_name)) { - char x[4]; - j = FLUID_SNPRINTF(x, sizeof(x), "%d;", i); - strncat(name, x, j); - n += j; + FLUID_LOG(FLUID_DBG, "winmidi: autoconnect dev name exceeds MAXPNAMELEN (%d), num (%d), n (%d)", MAXPNAMELEN, num, n); + return; } - - name[n - 1] = 0; + strncat(new_name, x, j); } + + name[n - 1] = 0; + + FLUID_MEMSET(name, 0, MAXPNAMELEN); + FLUID_STRCPY(name, new_name); } /*