Control Driver for a device connected with RTP-Midi/QmidiNet/Network MIDI 2.0

Hi Zynthianers!

This is a devel related question: Is there support for creating a controller driver for a device connected using RTP-Midi (or QmidiNet)? If nobody still dared to draw near this… do you have any suggestion of where I can start investigating? :slight_smile:

We have only implemented controller drivers for USB MIDI. We want to expand this to support multiple drivers per interface and to support non-USB interfaces but haven’t started that yet.

Thanks @riban for the quick response!

I’m happy to hear that this feature is, at least, in the waiting line. If you feel that I can help you on that, just count on me! :wink:

And I had you down as a 5 pin purist!

I only use two of them!

1 Like

I’ve managed to get a basic driver working with RTP-MIDI :slight_smile:

I just created one with the following: dev_ids = ["RTP MIDI"]. This port gets filtered in zynautoconnect, so normally, the call in zynthian_ctrldev_manager to get_midi_in_devid() would return None, and thus, no driver is loaded. I’ve modified it a little (added 2 lines of code, just for testing), letting it pass the filter, and the driver gets loaded and it receives MIDI events… :blush:

Of course, I know this is not a solution for many reasons. The first one is that I don’t know which device is connected to the other endpoint, so this driver would get loaded for any RTP-MIDI client… And I also don’t know other implications that this would present… BUT, just for testing (and having fun), it lets me keep going… :upside_down_face:

3 Likes

Some UPDATE on this:

As a good control driver, I would like to send feedback to my RTP-MIDI device (I like LEDs soo much! :upside_down_face:). That’s RTP-MIDI out from Zynthian to the device, and that does not seem to work. The other way round (device → Zynthian) is perfectly fine. I’ve searched the forums, and there is a bug issued for the problem I’m experiencing: RTP-MIDI not working properly · Issue #831 · zynthian/zynthian-issue-tracking · GitHub

I’m not even sure how I can make that connection manually… I’ll keep investigating it :slight_smile:


The thing is that the connections appear to be correctly made. The following command shows that the service jackrtpmidid is correctly connected to Zynthian:

# jack_lsp -c | grep --color -A 1 -B 1 rtpmidi
ZynMidiRouter:dev3_in
   jackrtpmidid:rtpmidi_out

ZynMidiRouter:dev3_out
   jackrtpmidid:rtpmidi_in
   ZynMidiRouter:ch3_out

jackrtpmidid:rtpmidi_in
   ZynMidiRouter:dev3_out

jackrtpmidid:rtpmidi_out
   ZynMidiRouter:dev3_in

More testing: I’ve run jack_midi_dump, and then I’ve connected it to the ZynMidiRouter:dev3_out, which is an output connected to the jackrtpmidid input port (if I’m not wrong, the service will receive MIDI events on that port, and send them to the network):

# jack_connect ZynMidiRouter:dev3_out midi-monitor:input

And then, when I change the state of a sequence with Zynpad, I see the expected events:

# jack_midi_dump 
 255: 96 70 05 note on  (channel  6): pitch 112, velocity   5
 255: 96 70 05 note on  (channel  6): pitch 112, velocity   5
 255: 96 70 05 note on  (channel  6): pitch 112, velocity   5

So, the jackrtpmidid service must be receiving the events, but somehow the other endpoint is not… It must be a dumb thing that I’m doing wrong, for sure… :sweat_smile:


More tests: I’ve disabled IPv6 in Zynthian, and in my machine, because the RTP-MIDI bridge that I’m using (rtpmidid) said that there were two advertisements of my Zynthian device (for both IPs, v4 and v6), and it ignored the last (which was the IPv4). BUT, to no avail, it made no difference.

To discard that the problem is in my PC, I’ve installed a RTP-MIDI connector in my Android phone, and connected it to a virtual MIDI controller. Then, on my PC I can receive the events from my phone without problems. I suspect that maybe the problem is inside jackrtpmidid


I’ve downloaded and compiled the code for jackrtpmidid on my PC. I want to try to reproduce the same connections on my PC, and see if what I want to achive works.

Yes, that matches my findings when I tried plugins for outputting midi signals from Zynthian. Unfortunately, I don’t have enough knowledge to find the cause. Send Midi CC+PrgCH? - #74 by ToFF

I’m narrowing it, but it’s been a long day, I need to step back and breathe a little :sweat_smile: I’ll keep this thread updated with any more news!

1 Like

I’ve made some more tests, trying to find the MWE (Minimal Working Example). I’ve run jack_midiseq as a MIDI events source, and jack_midisine as a MIDI events sink (a basic synth). Then, I’ve run jackrtpmidid (which creates two Jack endpoints, and should forward events to/from RTP), and rtpmidid with a fixed connection, called kirito in the pictures, to 127.0.0.1:5004 (the jackrtpmidid RTP endpoint on localhost). With this arrangement, there is a full-duplex RTP link between both services, and a MIDI event arriving at one endpoint should come out from the other.

The first test was with the following chain, and it worked perfectly (this is what is usually done in Zynthian):

  • sequencer → rtpmidid — (RTP) → jackrtpmidid → midisine → audio

Then, I tested the other way round, and that is what didn’t work (nor in my standalone MWE in my PC, nor in Zynthian):

  • sequencer → jackrtpmidid — (RTP) → rtpmidid → midisine → audio

Conclusions? The service jackrtpmidid does not emit to RTP the MIDI events that receives from Jack. Why? That’s another story. I’ll try to find out… :wink:

3 Likes

I believe we have a dungeon set aside for just such experimentation…

1 Like

I’ve found the problem. In jackrtpmidid.cpp, when a Jack event arrives, it is stored in a buffer, named JACK2RTP. And the problem is that nothing is done with that buffer. I’ve reviewed the commits on the repo, and some time ago, that buffer was handled by the class CRTP_MIDI (a pointer to the buffer was passed in the constructor) but now, that’s not the case. The code is there, because the class now holds it’s own buffer (instead of a pointer), and is being processed, but to no avail, as it is always empty.

I don’t know the reason of that removal. I suspect that, when there would be more that one instance of the object CRTP_MIDI, each one would keep its own buffer, and the main process would write on them according to the source of the data. But that is not implemented, at least as far as I can see.

I’ve modified the code (passing the pointer to the constructor of CRTP_MIDI) and it works (even though not very well, there are missed events and warnings on rtpmidid, something related to the timing of the events). So, @BEB , what do you think? :upside_down_face:

I’ve made a patch to fix the issue! It uses only the first session, but it could be extended to others. This is the patch:

diff --git a/jackrtpmidid.cpp b/jackrtpmidid.cpp
index dbee800..bfd8460 100644
--- a/jackrtpmidid.cpp
+++ b/jackrtpmidid.cpp
@@ -90,7 +90,29 @@ void* RTThreadFunc (CThread* Control)
     while (Control->ShouldStop==false)
     {
         if (RTPMIDIHandler1)
+        {
+            // NOTE: writePtr could be modified by Jack thread, but readPtr is not
+            int readPtr = JACK2RTP.ReadPtr;
+            int writePtr = JACK2RTP.WritePtr;
+
+            if (writePtr > readPtr)
+            {
+                if (RTPMIDIHandler1->SendRTPMIDIBlock(writePtr - readPtr,
+                    JACK2RTP.FIFO + readPtr))
+                    JACK2RTP.ReadPtr = writePtr;
+            }
+
+            else if (readPtr > writePtr)
+            {
+                if (RTPMIDIHandler1->SendRTPMIDIBlock(
+                    MIDI_CHAR_FIFO_SIZE - readPtr, JACK2RTP.FIFO + readPtr))
+                    if (RTPMIDIHandler1->SendRTPMIDIBlock(writePtr, JACK2RTP.FIFO))
+                        JACK2RTP.ReadPtr = writePtr;
+            }
+
             RTPMIDIHandler1->RunSession();
+        }
+
         if (RTPMIDIHandler2)
             RTPMIDIHandler2->RunSession();
         SystemSleepMillis(1);

As a side note, I’ve seen that both jackrtpmidid and rtpmidid ignores the delta time field of the RTP payload. This is a problem when many events arrive at the same time (which is not so rare). In my MWE, for instance, if you connect a jack-keyboard and swipe the keys, you will not hear what you expect. I think that may be fixed in a future release, if someone feels that it is truly a problem… :slight_smile:

2 Likes

I’ve applied the patch, and compiled inside Zynthian, replacing the current jackrtpmidid binary. And now, my remote device receives MIDI events from my driver correctly! :star_struck: If anyone is interested, I can fork the repo and upload a Debian package for Zynthian with the change. And, of course, if @BEB likes the patch, I can also upload a PR with it.

Meanwhile, I can keep going with my driver… :wink:

3 Likes

Good work @oscaracena. It would be good to add this to mainstream zynthian. We will wait until you have done some more testing then maybe you could provide the deb for us to include in our repo until such time as the fix is applied upstream.

2 Likes

Thanks @riban! I’ve already made the package, so anyone can join in testing it! :wink: It is available for download here:

I’ve compiled it for AMD64 and ARM64. Moreover, I’ve included the systemd service that is defined in Zynthian. As a note: this package installs the binary in /usr/bin, and the service file in /lib/systemd. Currently installed version uses /usr/local/bin and /etc/systemd, which takes precedence. In order to test this package, the installed version must be removed.

2 Likes

Hi @oscaracena

I am currently busy with Network UMP development stuff for NAMM 2025, but I will take a look to your changes and check to include them into the “official” jackrtpmidid if needed.

To answer your remark about the changes you have seen : the reason is simply that my RTP-MIDI stack is used in many different applications, so I modify sometimes the code accordingly, and there may have a side effect. In the case of the Zynthian, as I never use the Zynthian → MIDI OUT direction, I probably did not test / implement the output… :pensive:

Benoit

2 Likes

Not to worry, I’ll be busy again in a couple of days, so I perfectly understand you!

Yep, I thought that could be the case. Don’t worry either, I can keep going with the packaged version (which I feel works very decently). Thanks for checking it out, and of course, for developing it in first place!

I’ll take a look to your jacknetumpd too… :slight_smile:

Hi again!

Just for anyone interested, I’ve also made a Debian package for @BEB 's Jack NetUMP service (which I’ve also migrated to Android, but that is not finished yet). I think this is the service that will be used in the incoming support for MIDI 2.0 in Zynthian. The releases page is here:

4 Likes

Hello Zynthianers,

I’ve been working on this for a while, and I have some fresh news :grinning: I’ve seen that Network MIDI 2.0 is almost ready (if not fully working) on Zynthian, so I gave it a try. My goal was the same, create a device driver for a controller connected via Network MIDI 2.0.

The main problem is that the service (jacknetumpd) statically creates the Jack ports, and the device driver does not know if/when a remote device is connected/disconnected, or what device it is. So, I’ve made a few changes to fix it:

  • On jacknetumpd, and specifically in @BEB 's NetUMP library, I’ve added a pair of callbacks to be notified when a device is connected or disconnected. Then, I modified the service to add a Jack property on the output port, with the UMP Endpoint Name, when a client connects; and also remove it when disconnects.

  • On Zynthian, I’ve edited the zynthian_autoconnect.py to: 1) populate the aliases of the device/port using that Jack property; and 2) observe for property changes to update the aliases, and request a midi connect.

With this changes, when I connect my remote device using Network MIDI, it is detected and the proper driver gets loaded. :slightly_smiling_face: :clap:

Of course, the problem is that there are few Network MIDI 2.0 capable devices. For instance, on Android, MIDI 2.0 is not available until Android 13, but with no support for Network MIDI even on Android 16. For this specific case, I’ve written an Android APP that creates a virtual device (MIDI 1.0) and forwards the events to/from a Network MIDI 2.0 peer (I’ve used the great @BEB 's NetUMP library). This enables any Android device (from version 10 and up) to be used as MIDI controller (using whatever available APP suits you), seen as a Network MIDI 2.0 in Zynthian. That works well also with Web MIDI, so you can use any MIDI capable Web APP with Zynthian (btw, I’m working on a PWA as a controller, stay tuned! :wink: ).

If anyone is interested on the Android bridge, I can upload the APK somewhere (or even publish it in the Play Store). And, regarding the changes in zynthian_autoconnect.py, if @jofemodo or @riban feels that it could be useful, I can submit a PR, and also to @BEB 's jacknetumpd and NetUMP.

8 Likes