Arduino Due MIDIUSB: Lost messages issue

Hi there,

I have built a musical instrument that works a lot like the one sketched in this very good tutorial (thanks, btw):

https://docs.arduino.cc/tutorials/generic/midi-device/

That generally works, but I stumble across this very issue:

I can verify the observations and tests in the last post there - some outbound messages are simply lost (I am positively sure they are sent via MidiUSB.sendMIDI(), so they must get lost on the way to the wire).

Looking through the code in MIDIUSB/src/MIDIUSB.cpp at master · arduino-libraries/MIDIUSB · GitHub , I find that it calls USB_Send() but unfortunately does not propagate the result to the caller (why?), so on the application level, I can not react to USB side errors.

As outlined in this issue: Hardcoded timeout in USB_Send · Issue #478 · arduino/ArduinoCore-avr · GitHub, there is a hardcoded timeout of 250ms in USB_Send(). My suspicion is that the timeout kicks in and the messages are dropped. Unfortunately, the proposed configurability of the timeout has never made it into the libs.

I have tried short circuiting the MidiUSB.sendMIDI() call and go directly to the USB driver to find the return value but can not access the Tx end point.

So I guess my question at hand is: Can you point me to a tutorial to rebuild the MidiUSB lib so I can make further investigations down the chain?

Thanks so much!

More info: There is a related open issue here:

Missing USB packets (Midi) - Hardware / Arduino Due - Arduino Forum

However, that person is writing a MIDI host, whereas I code for a MIDI device (meaning in other words that I do exclusive MIDI output whereas they exclusively read and process inbound MIDI messages). Interesting to see that the problem affects both sides...

The answer must be in

#include "api/PluggableUSB.h"

i would say.

Thanks, but I had already tried that (will agree that that should give me access), but all I get is "no such file or directory."

I found it on my W10 laptop without to much issue searching for PluggableUSB.h"

i found it at

C:\Users\username\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.7\cores\arduino\api

But looking at the file (and the.cpp) i did not get much wiser unfortunately.

fwiw: I found a solution.

The thing is that the USB MIDI Lib only exposes the sendMIDI function to callers which only allows a single MIDI message to be transferred to the host. That translates to a call to USB_send() with the fixed length 4 and forced the control flow to send everything "inline," ie during the matrix evaluation individually for each key pressed or released.

Since there are no other bytes added to the data stream, I thought it should also be possible to send several messages en bulk, ie stuff them all in a larger message.

Once I discovered that I have the full source code available, I simply added a member function sendMIDI bulk (a thin public wrapper around the protected _write member which allows for larger message transfers) and changed the control flow such that during a single loop (meaning matrix evaluation), the logic now collects all MIDI messages in one array and sends it bulk to the host in a single bulk send command.

So far this looks very stable - no lost messages yet. I used to have a github account, if I can bring that back to life, I will create a PR to have that included in the main code base.

1 Like

Ah ok, so somehow the 32-bit nature of the MCU specifies that transfers to the USB output are done in 32-bit ?

Yeah that is always nice. A fully uncompiled core.

That makes sense.

I am still not sure why the messages were getting lost to begin with. Does the output somehow add '0' bytes or stores stuff in the FIFO ? anyway i would have to fully look into the source code myself and see if it clarifies things for me, but i don't own a SAMD processor at all, and probably have better things to do. Still great you seem to have found the solution.

Probably easy enough, usually with these things, if you try and create a new one with the same e-mail it will tell you you already have one, which will forward to the retrieve password etc. Don't know if github works that way, but otherwise you can just create a new account anyway.

I do not think so. I believe I read somewhere that MIDI messages over UART are always 3 bytes in size but 4 bytes over USB - why I do not know, possibly indeed some alignment related performance enhancement, but the 4th byte is also used.

Actually, I am almost a 100% sure that the messages are not lost on the sender side but the receiver side, and the problem is definitely related to messages in rapid succession. My current theory is that most USB MIDI receivers are coded such that they monitor the USB channel for incoming packets, then listen for a short timeout period for possible follow up messages and go on to processing the data (which in my theory is some kind of state where incoming messages or individual bytes may be dropped or overwritten) before going back to the listening state. The "lost" packages would then be manifestations of race conditions in which the next packet falls right after the timeout window of the receiver. Something like that; the most reliable way to find out would be a USB sniffer. But just like you I do not have the time to analyze the full chain of events, I basically just want to work with the resulting system. That seems to be the case now.

Yes, but if I recall correctly, github is not satisfied with a password but asked me to provide a certificate for 2FA. I don't have any idea where that is, so restoring both a password and a certificate seems like a major act. If worse comes to worst, I'll just publish my modified code here and ask someone with a valid account to check it in on my behalf.

Well not all midi messages are 3-byte, there are some which are 1-byte in length as well.

They just require a password from me when i sign in and have a 'forgot password' link next to it.
I would just try with your e-mail and hit that button and see what happens first.

With the usb-host running in 32 or 64 bit architecture it makes sense, but that would most likely leave the 4th byte (or the other 3 bytes) empty.

So that would mean lack of allocated buffer space in some way. Sure it is buffered on the incoming, but what you are saying doesn't quite make sense to me. Issues would be showing up more often and in different circumstances. I am betting on the transmitter still.

Same here though.

No, sorry for being unclear. My theory is this:

  • the receiver waits for an incoming messages.
  • If a message is completly received, a timeouting wait for a follow up message is launched. If within that window x more messages come in, they are collated.
  • Once the grace timeout expires, all messages sampled are either processed inline or scheduled for further asynchronous processing. During this time, however, the reception of further messages may be disrupted or disabled (either because all of this happens in an interrupt handler, amutual exclusion issue or whatever).

The reason why I believe I can outrule the transmitter is that the major factor that makes a difference in the frequency of the error is the receiver setup - running exactly the same sketch against differently configured USB hosts yields drastically different results; in some configurations (eg a Raspberry Pi 5 with my device being on one USB interface the other one equipped with a cheapo USB-Audio adapter) the losses are very frequent, in others (eg the same Raspberry Pi with audio running over HDMI) the losses are very infrequent.

I can also observe that the losses are not random but related to rather fast successions on on-off sequences. Since in the Github issue I linked to earlier a delay after each individual send appeared to make some difference, it is obvious that rather narrow back-to-back transmissions also influence the problem.

One thing I WAS able to observe though (but I will not pursue this further myself) is that USB_send() does produce sporadic errors with the single message strategy but not with the bulk strategy. I monitored the error (which manifests itself in a 0 return from USB_send()) via the Onboard LED because for some reason I can not use serial monitor output on the Due's programming port when the other USB is in use for MIDI (thus it would be quite a nuisance to pinpoint those errors further). Oh well.

The "single message only" exposed USB_sendMIDI() function disregards any errors returned by USB_send(), so the caller would not even have the chance to react on it. My bulk function propagates the lower level error, though. I could probably respond to those errors on application level by trying to retransmit, but as long as the buld solution works, I am happy.

Ah there is 2 USB ports, i guess the 16u2 port is used for programming and the other is the SAMD native USB which you can use for MIDI ?
But there is 3 more UARTs though isn't there.

Me too but remaining curious.

correct, but it is much harder to find UART based MIDI controllers than USB MIDI. I am using fluidsynth which can handle USB MIDI devices right out of the box.

I was thinking more for the debug messages.

oh I see, good point, thanks! :hugs:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.