Setting up a MIDI thru on WIndows for multi-client access

I would like to simply route the incoming MIDI data bytes coming in from a connected device i.e. keyboard/controller/Lumatone etc. to a LoopMIDI (loopMIDI | Tobias Erichsen) port on Windows so that the data is available multi-client to any connecting program. On Mac the OS takes care of it, but on WIndows USB MIDI devices are treated exclusively unless they have a custom multi-client driver (which is rare). This is easy to do in pd or Max (connect a midiin object to a midiiout object and select the ports with midiinfo and umenu) but since I am doing much of my synthesis in Supoercollider I thought it would be more efficient to do everything in SC. However, everything I find in the help files indicates that the MIDI classes already parse the MIDI stream into message types. I’d like to keep all the data bytes running without any parsing in between. Any suggestions how this works best? Also I know this is possible with a 3rd party program like MIDI-OX, but the goal is to keep it all in SuperCollider. thanks everyone!

Hi, welcome to the forum!

Maybe not at the level you are asking for, but try this one. It will forward the messages from IAC Driver to Max 2

MIDIClient.init;
~midiIn = MIDIIn.findPort("IAC Driver", "Bus 1");
MIDIIn.connect(0, ~midiIn);
~midiOut = MIDIOut.newByName("to Max 2", "to Max 2");

(
[\noteOn, \noteOff, \control, \polytouch, \touch, \program, \bend, \sysex].do({|type| MIDIIn.addFuncTo(type, { |...args|
	currentEnvironment[\midiOut].performList(type, [args[1], args[2], args[3]])
})
})
)

Thank you kindly! This is similar to the code I have tried so far, only that the parsing step into messages has a little overhead which the Max version avoids by forwarding the byte stream directly. I guess the only way to do this is to write a new class? Going lower level? Would be terrific if the the MIDIn object had an option for “raw midi output”…

I think it doesn’t exactly work that way. It’s true that [midiin] → [midiout] in Max/Pd would avoid overhead in the user code, but I think it will not avoid CPU overhead.

The suggestion seems to be, here, that incoming MIDI messages will not be parsed in any way unless there is a note-on, note-off etc. responder registered. That’s definitely not the case in SC or Pd, where we can look at the source code and see what’s happening. Max is closed source, but it’s reasonable to suppose that the same considerations apply.

SC in Windows uses the PortMIDI library. It looks like PortMIDI dumps incoming MIDI messages into a queue; then, SC wakes up a function every millisecond, PMProcessMIDI(), to process anything that is in the queue – ./lang/LangPrimSource/SC_PortMIDI.cpp, line 225 ff. Inside this function, there is switch(status) with cases for note-off, note-on, etc. Pd’s sys_dispatchnextmidiin() function is similarly structured around switch(status).

That is, every MIDI message is always processed in terms of its type, and there is no way to bypass that. Pd/Max’s [midiin] fires in addition to this, not instead of this.

So, a “raw MIDI” option in MIDIIn would add overhead: each incoming MIDI message would be parsed and passed to a raw responder.

It might be useful to incur the CPU cost. But to be honest, I think there’s a very limited number of cases where raw MIDI in would be more convenient than the message-type responders. If this were logged as a feature request, I think it would need a stronger use case than “I don’t like the MIDI thru function that I have now.”

In programming in general, the language/environment provides basic resources. Then, you write abstractions on top of that to package the resources in a way that’s useful to you. Hyppasus suggested a code block that sets up MIDI thru. This could also be written into a function (though, leave MIDIClient.init outside the function!): ~makeMidiThru = { ...... }. Then you can just ~makeMidiThru.value and get it all in one step. This doesn’t necessarily indicate a flaw in SuperCollider – it’s just part of using any programming environment.

hjh