Controlling Zynthian remotely with MIDI

I did contact Micheal Mcintyre, the original author of rosegarden about this midi dump that rose garden does, but he is no longer involved with the project, but he referred me to Ted Felix as the current maintainer.

I am certainly aware of rosegarden desire to do this but couldn’t find any obvious way of preventing it. I certainly have an interest in Midi control of the the zynth, more from a remote control aspect, (see posts passim) and I know this is blocked on the lack of available time to rewrite the low level mechanisms in the gui control and midi router sections that might address these oversights, again past posts will indicate where these issues lie. It must always be remembered that this is a commercial product that has chosen to run fopen source, which personally I believe is why it attracts the attention it does, but along with that comes the complicated relationship between those two approaches.
It’s not difficult to put a list of feature requests into the github mechanism for this and I have several outstanding of precisely this kind. As an owner of a korg nanocontroller 2 I would like a Midi grammar based around cc messages, and as the person who originally proposed midi control I’ve used it in various situations. I don’t think it’s battle hardened and as with all software projects I suspect an API would be a more suitable approach, but these are easy exercises in hindsight.
I’ve never added up the hours I’ve devoted because my motivations are primarily selfish and I get involved to move the project in particular directions. We are getting very close to a stable release which will be a source of great joy for us because we can perhaps, tie down the basic functionality. It’s certainly not the device I first found and it’s developed. Sometimes positively sometimes less so, but that just my opinion.
So please don’t get resentful. There is no desire to exclude, but I can assure you that those that do work on this are busy, but bon areas that are of concern to themselves. (my last conversations were last night and they mostly cover zynaptiks and digital ins and outs).
Any analysis you produce is of benefit but regard it, please, as a tool you adapt not necessarily as a tool you steer, good code always drives out bad in the end and that is what has pushed most of the development I’ve seen. Your work is producing results, in my case it prompted me to reach out to someone I knew in the rg community and also to write up the basic coding startup mechanisms.
If you can post test cases and harnesses then that will probably be easier to comment on, as one of the biggest issues is ensuring one is comparing like with like.

Chris@wyleu

1 Like

Hi @Jan

Please do not think that anyone’s contribution here is not appreciated. This is a friendly and welcoming community that enjoys fun and sometimes robust discourse on various subjects, not always on topic but always trying to be good natured. If a post hasn’t been answered immediately it may be because those who are interested or feel qualified to answer are occupied by other commitments - or maybe they read it and thought, I need to ponder that - maybe forgetting to get back to it :blush:. The first thing I did when I joined this forum was to read everything. It took a few days but it was very good to see what had already been discussed, get a feel for the community and gain confidence in how to integrate with my fellow zynthianeers.

Bugs are best reported in the issue tracker where they are triaged, prioritised and processed appropriately. It sounds like you may have found an issue that needs to be reported there. An advantage to doing so is that the issue reporting uses a template that requests info of your current configuration, e.g. what MIDI input device are you using?

There are extra layers in your model including: physical MIDI interface (including buffers) and soundcard driver. These could influence the behaviour of your tests.

It looks like your test 2 and test 3 are the same. Is that a typo in your report?

I see you are using mido for your tests which is a Python layer for MIDI. Python is not a good platform for realtime messaging and indeed its jack interface is particularly poor for receiving messages. There may be an issue with this test tool that might hinder thorough investigation.

I haven’t followed your report completely (maybe due to the confusion around tests 2 & 3) but I think you are saying that when you send many CC messages your Zynthian is getting confused and corrupting messages. This is not an experience I have had with V3, V4 and custom Zynthians nor one that anyone else seems to have experienced so it will be interesting to understand what differs in your use case.

Does this mean you have a V4.4 official kit with a USB cable running from your laptop’s USB-C connector to the Zynthian’s power inlet? That is significant because the OTG interface is a fairly recent addition and not extensively tested due to the challenge of delivering sufficient power to Zynthian from the host PC.

1 Like

First of all, many thanks for your responses. Please keep in maind that I love linux based systems and also not scared of a little bugfixing. But indeed I did not expect reverse engineering the Zynthian lowlevel layers to fix this bug which makes Zynthian pretty useless as a synthesizer connected to a DAW.

No bother on that one. I fully agree with the Rosegarden design to send note off messages (123) to every channel when pressing the stop button. This is perfectly fine behavior, in accordance with the MIDI standard and best practices. My strong opinion is that Zynthian should be able to handle a bunch of messages and not creating it’s own version of messages that are completely different from the messages that have been received on a hardware level.

I have just added another test with the Zynthian services stopped (test 4). And what happened? The test fails. So the problem seems to origin in the combi of Mido with Jack; the Mido script using the Jack API fails.

Thank you. Hopefully my ‘selfish’ intentions are enough in line with building a stable release. Zynthian has the potential to revolutionize the Synthesizer market, when the product is of sufficient quality. So thanks for responding and not letting me alone in the dark.

I will file a bug, in case this is picked up in a timely matter, otherwise I rather fix it myself by kicking out MIDO as a whole and solely using RTMIDI as the foundation for Zynthian. I am pretty displeased with the current state of affairs to be honest. I expected to connect my DAW and start composing music. Instead I am reverse engineering and writing code. Before I file the bug I will do some more testing with other power sources and other USB ports (see below).

Agreed. However Test 1 on level 1 (RTMIDI) results in properly received data so I expect that ‘level 0’ is fine as well. Of course it might also be caused by using my USB input, instead of a DIN with the controller board.

I have clarified test 2 and 3 in the original post. Thanks for the feedback.

Layer 3 is a python wrapper around C++, Layer 4 and 5 are written in Python. So what is the problem also using Python to test the … Python code of Zynthian…

Of course that I wonder as well. I am getting confused and a bit uncertain about the situation. Have I done something wrong in the setup? How can it be that my Mido layer results in wrong output? I use MIDI over the USB-C connector. I read in your post at the end that this is not extensively tested. I have ordered a separate cable to deliver MIDI input via USB-A and separate power via USB-C. Btw I just followed USB-C per advice somewhere on the wiki.

Anyway again many many thanks for offering feedback. This is a great help and and engages me to find the root cause and solve this annoying bug.

Oh btw who has written the libs for Zynthian-webconf layer for Zynthian. I tried to find it on Github but only saw Jofomodo as the one and only developer…

He is, pretty much. Others do stuff but we’re here because of his generosity of time and we stayed because of his enthusiasm and kindness.

3 Likes

@jan I am confused about your reference to mido. As far as I can tell the Mido Python module is not used by Zynthian. Real time processing (MIDI, audio, physical interface) is done within C or C++ libraries which may have Python wrappers for control and monitoring. The core module for processing MIDI is zynmidirouter whose source code is within the zyncoder repository. This C module acts as a JACK client, processing MIDI and making it available for routing. The actual routing is done by JACK at the request of Python modules. Python talks to zynmidirouter to configure filters, etc. but the real time stuff is done by the C code.

Respect!! Amazing guy. We need to cherish these diamonds in the world. Nevertheless what will happen with Zynthian in case Jofemodo ‘ends under the bus’??? God forbid but I hope there is a backup plan for having one person coded pretty much everything…

I will do my best to answer in detail. Zynthian-webconf, that shows the MIDI log consists of a templates folder that contains midi_log.html page. That file contains a html portion and a underlying javascript portion. That javascript portion creates a zynthianSocket with an object MidiLogMessageHandler. That class is defined in lib/midi_log_handler derived from ZynthianWebSocketMessageHandler (you are probably referring to that class). Ok so far so good.

The MidiLogMessageHandler file inports the Python mido package. Line 119 and 120 opens with MIDO the midi_port. The method is provided with the callback method/function. In this way the MIDO object knows who to call when a messages arrives (on_midi_in). That method is defined in lin 125-127 and such connects to the GUI layer.

The MIDO package is part of Python3, which is currently 3.7 in Zynthian and 3.9.2 in my Debian based laptop. Updating in Zynthian is not possible since apt update seems to be disabled. Not yet tried to update, little concerned about breaking Zynthian image.

Hope this helps clarifying.

In such a case, what is highly probable, zynthian project & code will continue its way, as everything is open source. This is s how it works. Do you want to code? Code! Do you want to play? Play! Do you want to talk? Let’s talk! :wink:

Regarding your issues…

MIDO is not used at all for anything related with RT performance so it can’t be the cause of any of your problems. It’s only used by webconf’s MIDI log. Please, don’t insist on it. Mido does nothing to do with your problems. You could totally delete Mido from zynthian and the UI still would work without a glitch.

I read above you are connecting zynthian to your PC, using the USB OTG for MIDI. Are you powering your device with a decent “Y” cable and a good power supply? If not, i’m pretty sure your PC’s USB port has not enough power for running the zynthian and this is, very probably, the source of all your problems.

BTW, I was doing some tests with Rosegarden + QMidinet and it works quite well for me. For instance, I could play the Branderburg Concert example with the General MIDI snapshot included by default. The instruments were correctly selected adn zynthian had not problems with CCs nor other MIDI stuff. There was only a few hanging notes that i would like to investigate …

Best Regards,

2 Likes

Would be nice to see a sketched design showing the flow between layers. For instance webconf connects via mido to Jack: MidiLogMessageHandler.mido_port = mido.open_input(self.midi_port_name, callback=self.on_midi_in)
Still try to get my head around how Jack is fed with midi data. Do I understand correctly that Zynthian has it’s own driver? I do not see any module in lsmod. I’m still a bit lost. Sorry :slight_smile:

I power Zynthian with a separate cable via Y-connection. Yesterday I have ordered a separate cable to deliver MIDI via a USB-A port and not OTG anymore. I sincerely hope that the separate MIDI flow solves it. Nevertheless:
root@zynthian:~# /opt/vc/bin/vcgencmd get_throttled
throttled=0x0

I would not be surprised that you have discovered the same bug. It starts with some hanging notes, then the system gets more and more corrupted with misinterpreted messages and no other option than reboot. Often I need to reboot after 30 minutes of composing. Moreover, it only happens with instant bursts of messages, never with a gradual flow of MIDI data, such as with piano roll note on/off.

Thanks, would indeed be nice to have a talk. I am really amazed about what you achieved. The more I learn the more I am wondered. In fact I feel humble next to you having this developed mostly by yourself. Would be nice to contribute. I have dropped you a LinkedIn invite.

1 Like

I have found the issue wrt to the wrong messages in webconf by using the following script (indent lost by copy pasting):
import os
import logging
import mido
import time

def calback(msg):
if not (“clock”) in str(msg):
print(“callback”,msg)

mido.set_backend(‘mido.backends.rtmidi/UNIX_JACK’)
msg = mido.open_input(‘ZynMidiRouter:midi_out’,callback=calback)

with mido.open_input(‘ZynMidiRouter:midi_out’) as inport:
for msg in inport:
if not (“clock”) in str(msg):
print(“looping”,msg)

It shows that the webconf shows not the output that is provided to Zynthian. the ‘looping’ messages show the correct messages, but the ‘callback’ messages do not. It only happens when a burst of messages are send from the DAW to RPI. Just for the sake of completeness the output:
root@zynthian:/zynthian# python3 /zynthian/testMido.py
callback control_change channel=0 control=64 value=0 time=0
looping control_change channel=0 control=64 value=0 time=0
callback control_change channel=1 control=64 value=0 time=0
looping control_change channel=1 control=64 value=0 time=0
callback control_change channel=2 control=64 value=0 time=0
looping control_change channel=2 control=64 value=0 time=0
callback control_change channel=3 control=64 value=0 time=0
looping control_change channel=3 control=64 value=0 time=0
callback control_change channel=4 control=64 value=0 time=0
looping control_change channel=4 control=64 value=0 time=0
callback control_change channel=5 control=64 value=0 time=0
looping control_change channel=5 control=64 value=0 time=0
callback control_change channel=6 control=64 value=0 time=0
looping control_change channel=6 control=64 value=0 time=0
callback control_change channel=7 control=64 value=0 time=0
looping control_change channel=7 control=64 value=0 time=0
callback control_change channel=8 control=64 value=0 time=0
looping control_change channel=8 control=64 value=0 time=0
callback control_change channel=9 control=64 value=0 time=0
looping control_change channel=9 control=64 value=0 time=0
callback control_change channel=10 control=64 value=0 time=0
looping control_change channel=10 control=64 value=0 time=0
callback control_change channel=11 control=64 value=0 time=0
looping control_change channel=11 control=64 value=0 time=0
callback control_change channel=12 control=64 value=0 time=0
looping control_change channel=12 control=64 value=0 time=0
callback control_change channel=13 control=64 value=0 time=0
looping control_change channel=13 control=64 value=0 time=0
callback control_change channel=14 control=64 value=0 time=0
looping control_change channel=14 control=64 value=0 time=0
callback control_change channel=15 control=64 value=0 time=0
looping control_change channel=15 control=64 value=0 time=0
callback control_change channel=0 control=123 value=0 time=0
looping control_change channel=0 control=123 value=0 time=0
callback control_change channel=1 control=123 value=0 time=0
looping control_change channel=1 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
callback control_change channel=2 control=123 value=0 time=0
looping control_change channel=2 control=123 value=0 time=0
looping control_change channel=3 control=123 value=0 time=0
looping control_change channel=4 control=123 value=0 time=0
looping control_change channel=5 control=123 value=0 time=0
looping control_change channel=6 control=123 value=0 time=0
looping control_change channel=7 control=123 value=0 time=0
looping control_change channel=8 control=123 value=0 time=0
looping control_change channel=9 control=123 value=0 time=0
looping control_change channel=10 control=123 value=0 time=0
looping control_change channel=11 control=123 value=0 time=0
looping control_change channel=12 control=123 value=0 time=0
looping control_change channel=13 control=123 value=0 time=0
looping control_change channel=14 control=123 value=0 time=0
looping control_change channel=15 control=123 value=0 time=0

I expect it has something to do with threading in the MIDO package. Did some searching in the MIDO package but decided not to spend to much time on it. As Jose already mentioned MIDI logging in webconf is not core in message processing and only used for showing the logs. I am gonna use the script as mentioned above for further bug hunting in the other packages to find the root cause. Should I file a bug for this one? Maybe it is already solved in Python 3.9.7…

Update: with larger bursts also the loop gave errors. I now got rid of MIDO and using RTMIDI for logging and finally finally reliability in the console. Ok next step…

1 Like

This is my last update in this topic. I am not using webconf anymore for debugging. My conclusion is that mido is not reliable in Python 3.7 for logging. I created a small rtmidi script that connects on jack level:

#!/usr/bin/env python
from rtmidi import (API_LINUX_ALSA, API_MACOSX_CORE, API_RTMIDI_DUMMY,
API_UNIX_JACK, API_WINDOWS_MM, MidiIn, MidiOut,
get_compiled_api)
#from future import print_function
import rtmidi.midiutil# import open_midiinput

import logging
import sys
import time

midi = rtmidi.MidiIn(3)

port =0
port_name = midi.open_port(port)

print(“Entering main loop. Press Control-C to exit.”)
try:
timer = time.time()
while True:
message = midi.get_message()
if message:
##print(message[0][0] & 0x0F)
if len(message[0]) > 2:
print(“command: “,”{:02X}”.format(message[0][0] & 0xF0),“channel:”,"{:02d}".format(1+(message[0][0] & 0x0F)),", control: “,”{:03d}".format(message[0][1]),", value: “,”{:03d}".format(message[0][2]))
else:
print(“command: “,”{:02X}”.format(message[0][0] & 0xF0),“channel:”,"{:02d}".format(1+(message[0][0] & 0x0F)),", control: “,”{:03d}".format(message[0][1]))
except KeyboardInterrupt:
print(’’)
finally:
print(“Exit.”)
midi.close_port()
del midi

2 Likes