Alsa connections on linux not visible in GUI tools?

Hi everyone,

I am a bit puzzled that midi connections on linux that I make from
within SC3 do work but are not visible in GUI programs that can show
alsa midi connections such as qjackctl or jack_lsp Is this normal and
known behavior?

Thanks!
Peter

MIDIOut or MIDIIn?

How are you setting up the MIDI object(s)? Short code snippet would be good.

It may be normal behavior, but it depends on precisely what you’re doing… without providing that detail, you can’t get an exact answer based on given info.

hjh

MIDIOut or MIDIIn?
Sorry, MIDIOut it is.

How are you setting up the MIDI object(s)? Short code snippet would be good.
I am using the following:

(
MIDIClient.disposeClient;
MIDIClient.init(1,1); // initialize with one in and one out port
~mo = MIDIOut.newByName(“FLUID Synth (472617)”, “Synth input port (472617:0)”);
~mo.connect; // alsa, connection not visible in patchbay
)

Thanks!
P

This is normal behavior. newByName does not make any ALSA MIDI connections.

SC MIDIOut connections will be visible in qjackctl if they were made by the connect method (or if another app made the connection – if I create a MIDI input module in VCV Rack and choose SuperCollider as the source, then this connection will be visible too).

newByName populates a uid in the MIDIOut object. This directly targets the device and ignores all visible connections.

So the short answer is: Delete your ~mo.connect line and simply go ahead with your project. Nothing to worry about here.

Detail:

To send MIDI, SC must call an ALSA MIDI function. This function takes a parameter for a specific device UID to receive the message. If this UID is 0, then the message goes out through ALSA MIDI connections (visible in patchbays). If it’s nonzero, then the message goes only to the specified device and all ALSA MIDI connections are ignored.

I tested this by using a Pd patch to print incoming MIDI, and also enabling MIDIFunc.trace(true) in SC. Then:

// .connect to SC
m = MIDIOut(0).connect(2);

m.control(0, 11, 64);  // seen in SC

// direct-send to Pd using UID
m.uid = MIDIClient.destinations[1].uid;
m.control(0, 11, 64);  // seen in Pd and not in SC

Note at this point that the SC-to-SC MIDI connection is visible in qjackctl, but the message went to Pd instead. If you’re using UIDs and .connect at the same time, then qjackctl’s display is misleading – it does not in any way reflect the actual target of the messages.

m.uid = 0;
m.control(0, 11, 64);  // seen in SC

This means a couple of things for your code snippet:

  • If newByName is successful, then the UID is nonzero, and anything you do with .connect will be irrelevant after that point.
  • Also, .connect without an argument creates an ALSA MIDI connection to MIDIClient.destinations[0] :astonished: – which strikes me as wrong behavior! It should probably throw an error in that case. In my system (Linux), the first MIDI destination is Midi Through – I’m pretty sure you didn’t want to connect to that. (But, you got away with it because connections are ignored.)

hjh

Dear James,

thank you for your thorough answer! I understand that there is

SC’s .connect method, which will create a visible connection.
and
SC’s .newByName method, which as you write “populates an uid in the
MIDIOut object.”

Of course I wonder how Alsa can know about which method was used in SC.
I further understand that in Alsa there is a mechanism that allows to
send midi to a destination without creating a visible connection, which
is the case for UIDs of nonzero.

Now I wonder, why does .newByName create a nonzero UID while .connect
does so and if this is intentional. Otherwise I’d argue that both
methods should use UID’s of nonzero so that connections become visible
to users.

Thanks again!
Peter

MIDIOut methods:

	*connectByUID {arg outport, uid;
		_ConnectMIDIOut
		^this.primitiveFailed;
	}
	*disconnectByUID {arg outport, uid;
		_DisconnectMIDIOut
		^this.primitiveFailed;
	}

These primitives call ALSA functions to make or break the connections.

ALSA, for what it’s worth, doesn’t care which method was used. If we issue a function call to make a connection, then it will make the connection.

I’m not sure of the history of this method.

I suggest, though, that if you would prefer to specify MIDI target devices by name and get an ALSA connection, it might be better to write your own function for it. This is something you can do today, without waiting for developers.

~midiOutConnectByName = { |outPortIndex = 0, deviceName, portName|
	var endPoint, index;
	endPoint = MIDIClient.destinations.detect { |ep,epi|
		index = epi;
		ep.device == deviceName and: { ep.name == portName }
	};
	if(endPoint.isNil) {
		Error("Failed to find MIDIOut port " + deviceName + portName).throw;
	};
	if(thisProcess.platform.name != \linux) {
		MIDIOut.new(index, endPoint.uid)
	} {
		if(index < MIDIClient.myoutports) {
			// 'index' here is for "SuperCollider:out0", ":out1" etc.
			// Practically speaking, this establishes associations:
			// out0 --> destinations[0]
			// out1 --> destinations[1] and so on.
			// It looks weird but, in fact, it does ensure a 1-to-1 connection.
			// Explained further in MIDIOut help.
			MIDIOut.new(index).connect(index)
		} {
			// If you didn't initialize enough MIDI output ports,
			// it will connect the new device to 0.
			// Connections with a UID are always 1-to-1.
			MIDIOut.new(0).connect(index)
		}
	}
};

m = ~midiOutConnectByName.value(0, "device", "port");

Alternately, you could create a private extension file to add a *newConnectByName method of your own (following the pattern given in the function – replace MIDIOut.new by ^this.new).

hjh

Dear James,

I’m not sure of the history of this method.

I suggest, though, that if you would prefer to specify MIDI target devices by name and get an ALSA connection, it might be better to write your own function for it. This is something you can do today, without waiting for developers.

Again thanks for your pristine explanation! Me personally I can handle
the current confusing situation, your suggested function looks very
elegant too! I wonder if I can nevertheless open a git issue to have
consistent behavior between these two methods and have future users not
stumble over this again?

best, P

I’ve already put in a pull request to update the documentation.

In general, I’d be cautious about changes that break compatibility, unless they are really obvious bugs. I don’t think this is one of those.

There’s a feature proposal out there for a MIDIOut factory class, to unify the object constructors for cross platform compatibility. If that’s ever implemented, it would be a great place to add the method you want.

This is also more in keeping with object-oriented design philosophy, where translating programming interfaces is often preferred over changing them (globally, for everybody) when a few users get confused. A factory would preserve backward compatibility while providing a new feature.

hjh