monome / norns

norns is many sound instruments.
http://monome.org
GNU General Public License v3.0
633 stars 147 forks source link

Virtual MIDI not usable as clock/pmap source #1744

Closed dstroud closed 9 months ago

dstroud commented 11 months ago

What the issue is: MIDI system real-time (clock) messages received by the "virtual" RawMidi port are ignored when clock source is set to MIDI. Likewise, control change messages are ignored when mapping MIDI to parameters.

How to reproduce it: I'm using CyberMIDI (MIDI over OSC) to send clock/cc over IP. These message types are retrievable in LUA via midi.to_msg().

Why it's worth addressing:

catfact commented 11 months ago

Probably a simple oversight and shouldn't be too tricky to fix if anyone wants to have a go

dstroud commented 11 months ago

Would be rad if so! I'm way out of my depth here, but one thing I noticed working on the related mod is that the virtual port gets added later in the boot process than physical ports (after the "system_post_startup" mod hook). Probably tangential but I figured it's worth bringing up if anyone is interested in a more general effort to close gaps between virtual and physical ports.

catfact commented 9 months ago

i looked at this but only very briefly:

if (2) is true, then i can see how it would not work to drive MIDI clock. MIDI clock messages from connected devices are handled at a lower level in matron - a device reads a clock byte, immediately informs the clock module (which is another part of matron independent of lua,) then posts an event to the lua interpreter.

https://github.com/monome/norns/blob/main/matron/src/device/device_midi.c#L288

if i understand corretly, it should work fine to read clock messages from the virtual device, but it would have to be driven by an external process.

the next step i had in mind was a proof-of-concept writing a small program to send periodic clock messages to the virtual device through ALSA rawmidi API. like starting from this example:

https://ccrma.stanford.edu/~craig/articles/linuxmidi/alsa-1.0/alsarawmidiout.c

... i have not quite got that going yet but thought i might as well check back first to ensure we are on the same page.

dstroud commented 9 months ago

Thanks Ezra- you are correct about how CyberMIDI works. MIDI is encoded as OSC to send between devices. When it's decoded the midi.vports[n].event callback is called directly.

I was able to successfully send/receive MIDI clock with a shieldXL which uses the virtual port for its TRS MIDI and that works fine which confirms your explanation.

So it sounds like this is just a limitation of my mod's implementation and not an issue with the virtual port in general. I'm OK with closing the issue since everything is working as expected.

catfact commented 9 months ago

When it's decoded the midi.vports[n].event callback is called directly.

ah ok, that explains why it would not work for parameter mapping either. (which i forgot to address.)

the parameter mapping code also observes MIDI messages at a different stage in the pipeline than vports does. however unlike MIDI clock input, this is still happening entirely in the lua layer: https://github.com/monome/norns/blob/main/lua/core/midi.lua#L456-L472

.. so basically, i think all you would need to do to make parameter mapping work is to call norns.menu_midi_event(data, d.port) in addition to midi.vports[d.port].event(data), as the main handler does.


however, making the mod able to drive MIDI clock events from lua would require changes to core: namely a dedicated FFI function to do that. we haven't seen a need for such a function before, but i at least would not be opposed to adding it.

dstroud commented 9 months ago

Aha! Thanks for the tip re: parameter mapping! Between this info and the upcoming Link improvements for clocking, feels like the need for any changes is greatly ameliorated on my end.

TBH, it seems like a better solution than my mod is something like RtMidi which I assume would not have this issue as it uses ALSA. I'll leave that up to someone who knows what they are doing 😬

catfact commented 9 months ago

Yeah if you asked me to implement something like cybemidi, my first thought would be to do it as a daemon process connected to virtual port. Rtmidi or portmidi would be options for that, but for Linux only I don't think it's any less convenient to use alsa apis directly.

But thinking about it, I think it would also work if cybemidi wrote to virtual device output, and then the daemon process did nothing but loopback on the virtual port, and then norns user would just select virtual device for input and everything works normally.