Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extend implementation of "midi.autoconnect" #414

Open
3 of 4 tasks
derselbst opened this issue Jul 7, 2018 · 20 comments
Open
3 of 4 tasks

extend implementation of "midi.autoconnect" #414

derselbst opened this issue Jul 7, 2018 · 20 comments

Comments

@derselbst
Copy link
Member

derselbst commented Jul 7, 2018

Implement "midi.autoconnect" for alsa_raw, jack and potentially other midi drivers.

  • CoreMidi
  • jack
  • winmidi
  • alsa raw
@jjceresa
Copy link
Collaborator

I don't quite understand this "autoconnect" need. Please does anyone could explain anymore what is expected ?

@derselbst
Copy link
Member Author

derselbst commented Sep 25, 2018 via email

@jjceresa
Copy link
Collaborator

jjceresa commented Sep 25, 2018

let me know if this clarifies it.

Thanks, now it is clear. As far i know i dont think that "autoconnect" can be implemented for Windows (i mean without a dependency taking account of multiple MIDI input devices).

@derselbst derselbst modified the milestones: 2.0-post, 2.1 Jul 8, 2019
@carlo-bramini
Copy link
Contributor

Excuse me, I was wondering if you could clarify few things that are not clear to me.

  1. The user starts FluidSynth. After that, the user adds or removes a plug and play MIDI device, typically connected with USB. When this happens, FluidSynth updates its list of MIDI devices. Should this update happen only when autoconnect feature is activated?

  2. In the current code, only one MIDI device is open each time, at least with winmidi. Now, when autoconnect feature is activated, does this mean that FluidSynth must open ALL avaliable devices when it starts?

  3. In my opinion, the list of available audio output devices should be updated, in the same manner of MIDI devices, but not automatically opened like in the autoconnected feature. For example, the user can plug or unplug USB speakers and the list of available devices is updated according that action. What do you think?

@derselbst
Copy link
Member Author

When this happens, FluidSynth updates its list of MIDI devices. Should this update happen only when autoconnect feature is activated?

The autoconnect feature only says that suitable MIDI devices should be automatically connected to. Whether this makes it necessary to maintain a list of devices or get a callback are details of the implementation. However, I currently don't see a need to keep that list up to date when autoconnect is disabled.

In the current code, only one MIDI device is open each time, at least with winmidi. Now, when autoconnect feature is activated, does this mean that FluidSynth must open ALL avaliable devices when it starts?

I'm not familiar with winmidi. The autoconnect feature originated from alsa, where fluidsynth connects to ports. Whenever a new port appears, fluidsynth gets a callback and connects to it. I hoped that smth. similar to this is available for winmidi as well. If not, the autoconnect feature could either be dropped for winmidi or, as compromise like you said, connect to every readable port that is available when fluidsynth starts up.

For example, the user can plug or unplug USB speakers and the list of available devices is updated according that action.

I think this treatment highly depends on the audio driver used. At least on windows, my experience is that the OS switches automatically to any plugged in speakers.

@derselbst derselbst removed this from the 2.1 milestone Oct 16, 2019
@pedrolcl
Copy link
Contributor

pedrolcl commented Jan 6, 2022

I wanted to use the "midi.autoconnect" setting in Windows today, and was disappointed. Then, while looking at #677 it seemed to be easy to implement it in winmidi, but there is a hardcoded policy about mapping MIDI channels that didn't match well my own use case (but is probably useful for others). Then, I've realized that another setting: "synth.midi-channels" was not taken into account when implementing autoconnect (for instance in ALSA, Jack, CoreMIDI drivers) neither in #677. So let's look to the possible use cases first.

  1. Probably the main motivation for using "midi.autoconnect" is an user who has a MIDI controller, and wants fluidsynth (or qsynth) just to make noise with the controller without thinking about connecting things. He uses just one single MIDI channel, or maybe a few, but anyway 16 MIDI channels are more than enough for him.

  2. There is another user with several MIDI controllers connected to the computer. He wants to trigger sounds in a single fluidsynth/qsynth instance, but using more than 16 channels. He sets "synth.midi-channels" to N=the desired number of channels, and fluidsynth creates N/16 MIDI IN ports. Then he probably connects each MIDI port to the right controller by hand.

Both settings are not incompatible. You may imagine combining them in the use case 2, to avoid the manual connection of the multiple MIDI IN ports to the external controllers. But this is not implemented, for instance in alsa_seq only the last created MIDI IN port gets connected to everything.

A windows user with #677 has something similar to the use case 2, but "synth.midi-channels" is ignored, even if he wants a maximum of 16 MIDI channels.

What do you think? Am I understood?

@derselbst
Copy link
Member Author

Sry, it's not quite clear to me what you're asking for or trying to achieve. So, all I can comment on are the two use-cases you described. UC1 is the one why midi.autoconnect was invented. But I don't see how UC2 can be compatible with autoconnect. As you said, the ports must be connect to the right controller by hand, and I'm skeptical that more magic to midi.autoconnect helps to circumvent that.

Not sure, why synth.midi-channels is ignored by the winmidi driver. To me it seems like the driver is simply optimistic that the synth will not ignore the 17-32 channels:

event.type = msg_type(msg_param);
event.channel = msg_chan(msg_param) + dev_infos->channel_map;
FLUID_LOG(FLUID_DBG, "\ndevice at index %d sending MIDI message on channel %d, forwarded on channel: %d",
dev_infos->dev_idx, msg_chan(msg_param), event.channel);
if(event.type != PITCH_BEND)
{
event.param1 = msg_p1(msg_param);
event.param2 = msg_p2(msg_param);
}
else /* Pitch bend is a 14 bit value */
{
event.param1 = (msg_p2(msg_param) << 7) | msg_p1(msg_param);
event.param2 = 0;
}
(*dev->driver.handler)(dev->driver.data, &event);

@pedrolcl
Copy link
Contributor

pedrolcl commented Jan 9, 2022

Sry, it's not quite clear to me what you're asking for or trying to achieve.

You are right. I've not made a proposal yet. But I have something in mind...

  1. all MIDI drivers should implement support for both settings "synth.midi-channels" and "midi.autoconnect" in the best possible way.
  2. allow both settings to be used together, or separately. When "synth.midi-channels" is not specified, then the default value of 16 MIDI channels should be respected (the winmidi driver doesn't do that, the coremidi driver does not try to implement any provision). When the "midi.autoconnect" setting is not specified, then the user should be responsible and manually map how the extra MIDI channels need to be assigned to the inputs (the mechanism depends on each driver). The third combination, when both settings are used together, then the channels should be fairly and automatically distributed among the connected inputs. The winmidi driver does already an automatic distribution: the first connected input is mapped to the channels 1-16, the second connected input to the channels 17-32, and so on.

/* try opening the devices */
for(i = 0; i < dev->dev_count; i++)
{
device_infos_t *dev_infos = &dev->dev_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 */

My proposal for the winmidi driver is to limit the maximum number of channels to the value provided in the setting "synth.midi-channels", and then if "midi.autoconnect" is requested, instead of parsing the value of "midi.winmidi.device", open all the suitable devices that are already enumerated here:

void fluid_winmidi_midi_driver_settings(fluid_settings_t *settings)
{
MMRESULT res;
MIDIINCAPS in_caps;
UINT i, num;
/* register midi.winmidi.device */
fluid_settings_register_str(settings, "midi.winmidi.device", "default", 0);
num = midiInGetNumDevs();
if(num > 0)
{
fluid_settings_add_option(settings, "midi.winmidi.device", "default");
/* add real devices names in options list */
for(i = 0; i < num; i++)
{
res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS));
if(res == MMSYSERR_NOERROR)
{
/* add new device name (prefixed by its index) */
char *new_dev_name = fluid_winmidi_get_device_name(i, in_caps.szPname);
if(!new_dev_name)
{
break;
}
fluid_settings_add_option(settings, "midi.winmidi.device",
new_dev_name);
FLUID_FREE(new_dev_name);
}
}
}
}

The ALSA Sequencer driver creates already several ports when "synth.midi-channels" is specified. My proposal is to assign the channel mappings to each input port distributed incrementally (the first port to the channels 1-16, the second port to the channels 17-32, and so on). And If "midi.autoconnect" is also provided, subscribe sequentially each readable device to the available input ports (using a counter with return to zero). Something similar may be implemented in the CoreMIDI driver.

@derselbst
Copy link
Member Author

then if "midi.autoconnect" is requested, instead of parsing the value of "midi.winmidi.device",

Why not parsing winmidi.device? If the user takes the effort to set it, it should be respected. You could still connect to all readable devices if winmidi.device is the default value.

The rest sounds good to me.

@pedrolcl
Copy link
Contributor

pedrolcl commented Jan 9, 2022

then if "midi.autoconnect" is requested, instead of parsing the value of "midi.winmidi.device",

Why not parsing winmidi.device? If the user takes the effort to set it, it should be respected. You could still connect to all readable devices if winmidi.device is the default value.

The rest sounds good to me.

Well, then "midi.autoconnect" is ignored when "midi.winmidi.device" value is not "default". The winmidi driver still needs to be updated to limit the number of MIDI channels to the same value used by the synthesizer.

@derselbst
Copy link
Member Author

Ok

pedrolcl added a commit to pedrolcl/fluidsynth that referenced this issue Jan 11, 2022
Context: ticket FluidSynth#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.
@jjceresa
Copy link
Collaborator

Not sure, why synth.midi-channels is ignored by the winmidi driver. To me it seems like the driver is simply optimistic that the synth will not ignore the 17-32 channels:

Yes and no. Please some clarifications about #677:

1)The synth.midi-channels winmidi independence of #677 was an important design choice. This makes the MIDI driver straightforward and predictable for musician as it keeps MIDI channels intact without doing magic (and incomprehensible) MIDI channels mapping transformation. Accidental MIDI channels conflicts is impossible inside #677 winmidi driver.

2)with #677, having more than one MIDI controller and one synth instance with only 16 MIDI channels is still a valid use case.
This is because any fluid MIDI driver (not only winmidi) is an object that could be connected to other thing that the fluid synth.
A typical musical system application is having an application object between the MIDI driver and the synth(s). This application object being fully under the musician control. The musician easily control the MIDI channels mapping of any MIDI controllers in only one central place. The MIDI controllers note/off can be redirected to fluid synth and other hardware synth.

@jjceresa
Copy link
Collaborator

Well, then "midi.autoconnect" is ignored when "midi.winmidi.device" value is not "default". The winmidi driver still needs to be updated to limit the number of MIDI channels to the same value used by the synthesizer.

If i understand, that will break the initial #677 design. As said "midi.autoconnect" seems incompatibe with UC2. I think the correct way to add autoconnect in #677 is:

if(midi-connect is set to "on" and "midi.winmidi.device" is set to "default")
{
Attempting to hear something from any MIDI input device (not already in use).
}
else
{
let #677 doing its job
}

@pedrolcl
Copy link
Contributor

1)The synth.midi-channels winmidi independence of #677 was an important design choice.

The synthesizer instance (the object that transforms MIDI events into audio) has only 16 MIDI channels by default. When you send MIDI events to the synth with a channel value above the maximum, the event has no effect on the synth, it produces no change on the sound.

That is why the synth.midi-channels setting is important: because you can set another limit (with an absolute maximum of 256).

When I've tried the following test in windows, it was quite clear for me that someting is wrong in the winmidi driver:

This test needs the following Windows software:

First, disconnect any other MIDI device from the system, and configure LoopMIDI with more than 1 port, for instance 4 ports like this:

imagen

Second, in Qsynth configure the winmidi driver to connect to all loopMIDI devices:

imagen

Third, in VMPK connect the "Windows MM" MIDI output to the first LoopMIDI device:

imagen

With your mouse, or your keyboard, you can trigger sounds in Qsynth using any MIDI channel. OK for now.

Now change the connection to the second loopMIDI device:

imagen

The events generated by VMPK now are ignored by Qsynth, because they are mapped to the MIDI channels 17-31, and the synth does not have those channels. To fix this situation you can change the settings in Qsynth like this, to set "synth.midi-channels" to 64 (for instance):

imagen

Now, all loopMIDI devices can be connected in VMPK, and they always trigger sounds. Looking to the "channels" window in Qsynth, you can see the 64 channels (program setting and activity).

What is my proposal?

The winmidi driver first reads the "synth.midi-channels" setting, and uses it as a limit for the channels mapping value. For instance, when "synth.midi-channels" has the default value (16), there will be no mapping. All connected devices will send the received events to the synth using the same original MIDI channel.

If the "synth.midi-channels" setting value is 32, then the first device won't have channel mapping, and the second will have a 17-32 mapping. The next device will have again a 1-16 channel mapping, the next will be 17-32 again, and so on.

@pedrolcl
Copy link
Contributor

2)with #677, having more than one MIDI controller and one synth instance with only 16 MIDI channels is still a valid use case.
This is because any fluid MIDI driver (not only winmidi) is an object that could be connected to other thing that the fluid synth.
A typical musical system application is having an application object between the MIDI driver and the synth(s). This application object being fully under the musician control. The musician easily control the MIDI channels mapping of any MIDI controllers in only one central place. The MIDI controllers note/off can be redirected to fluid synth and other hardware synth.

The usual use case of FluidSynth for a normal musician is to execute the command line client, "fluidsynth.exe" or a GUI client like Qsynth, and use these programs to produce sound from MIDI events. Right now, using any of these programs in windows, while connecting several input devices without changing the value of synth.midi-channels has the effect that some connected MIDI devices don't produce any sound at all. That is not acceptable, in my opinion.

@jjceresa
Copy link
Collaborator

If the "synth.midi-channels" setting value is 32, then the first device won't have channel mapping, and the second will have a 17-32 mapping. The next device will have again a 1-16 channel mapping, the next will be 17-32 again, and so on.

I understand what you explained so far Pedro, but having a magic MIDI channel mapping (modulo "synth.midi-channels") incorporated inside the MIDI driver could produce MIDI channels conflicts.

In your example, when a musician plays a noteon on the device 1, this note could will be stopped by an other musician playing a note off on device 3. This is not acceptable as well, and the only way to avoid this issue is to augment "synth.midi-channels" which is workaround.

My proposal can produce no sound from some device if the user don't augment "synth.midi-channels" or he doesn't set the MIDI channels mapping according to its needs. Note that the fluidsynth console application offers a router that allows the user to do MIDI channels mapping

Your proposal can produce incomprehensible MIDI channels conflicts that can only be fixed by rising "synth.midi-channels".

@pedrolcl
Copy link
Contributor

I understand what you explained so far Pedro, but having a magic MIDI channel mapping (modulo "synth.midi-channels") incorporated inside the MIDI driver could produce MIDI channels conflicts.

Your proposal is to avoid implementing midi.autoconnect at all in winmidi. What I am proposing is...

Quoting myself:

all MIDI drivers should implement support for both settings "synth.midi-channels" and "midi.autoconnect" in the best possible way.

Probably the main motivation for using "midi.autoconnect" is an user who has a MIDI controller, and wants fluidsynth (or qsynth) just to make noise with the controller without thinking about connecting things. He uses just one single MIDI channel, or maybe a few, but anyway 16 MIDI channels are more than enough for him.

And for this kind of user, that simply wants to have sound without worrying about connections, you can't seriously propose to muddle with the MIDI router!

@jjceresa
Copy link
Collaborator

Please, note that my previous message is only relevant for auto-connect set to off.

@pedrolcl
Copy link
Contributor

This issue is about extend implementation of "midi.autoconnect"

@jjceresa
Copy link
Collaborator

Your proposal is to avoid implementing midi.autoconnect at all in winmidi.

No at all Pedro. Adding midi.autoconnect is a nice idea, please reread my previous message:

if(midi-connect is set to "on" and "midi.winmidi.device" is set to "default")
{
Attempting to hear something from any MIDI input device (not already in use).
}
else
{
let #677 doing its job
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants