Hi, I just tracked down an intermittent USB MIDI data loss bug (it would fail after about 30 minutes, with a missed message, while doing pretty intensive USB MIDI activity). It turned out to be in the USB re-transmit logic, with a count of 2 being too low.
In looking at the code, if I’m understanding it correctly, there’s no big downside to increasing it a lot, e.g. 100, e.g.
midi_usb_config.transport_config.tx_retry_count = 100;
The only downside is that it might block for awhile until a transmit call succeeds or fails. But otherwise yes that’s why that setting exists, so that you can change/increase it if necessary.
For what it’s worth this setting is a quick fix for what you’ve already discovered, which used to happen basically anytime you did sequential Tx over USB MIDI with no delay in between. The “better fix” would be adding a Tx buffer/queue to the USB MIDI code so that a call to transmit while the peripheral is still busy would just enqueue the data to be sent as soon as it’s ready again.
certainly some kind of Queue would be much better, but in the meanwhile, especially since the Tx call doesn’t even return an error code, having a much higher default ‘standard’ value would be a good idea. I’d much rather the call blocked for a bit instead of silently discarding the data.
Yeah agreed.
For what it’s worth in all my own projects in which I’m using MIDI, I use a layer on top of the libDaisy MIDI stuff that implements a Tx byte queue, including the ability to serialize messages into bytes (including optionally utilizing Running Status when applicable) and flush/transmit them all at once in a separate call.
I also tend to use tinyUSB for USB MIDI which I believe already has its own internal queue for MIDI Tx. So both of those things together improve the situation a lot.
It’s been on my backlog for a long time to at least PR the Tx Queue stuff. It would probably be a breaking change, though, but perhaps not necessarily.
Anyway - hope bumping the retry count solves your problem. You can also increase the sleep time between retries but that would change actual libDaisy code.
Followup PSA: if you do make the retry count longer, you will ideally want some way to detect if the USB cable is plugged in and enumerated as a MIDI device! There doesn’t seem to currently be a very straight-forward way to do this, though!
To determine if a device is connected you could potentially modify the PCD callback definitions in libDaisy in usbd/usbd_conf.c. I think if you added a global device state variable that you set during the connect/reset/disconnect callbacks that would provide some reasonable clue as to whether device is connected to the host.
Or you could check the BSVLD bit of the GOTGCTL register of the appropriate PCD handle instance (indicated as FS or HS in libDaisy, depending on which port you’re using). More info and example of that here.
Either option should be relatively simple to implement.
Thanks for those suggestions and links, looks super useful!
I ended up going down a slightly different road at least for now.
In usb.cpp:
bool UsbIsConnected()
{
return (hUsbDeviceHS.dev_state == USBD_STATE_CONFIGURED);
}
This worked for detecting connections, but not disconnections.
As I had a GPIO pin hooked up to the USB ‘VBUS’, I was able to detect the disconnection that way, and then I had to call DeInit and ReInit to re-arm it.
I made ‘DeinitHS’ non-static so I could call it.
void UsbReInit()
{
DeinitHS();
midi_usb_config.transport_config.periph = MidiUsbTransport::Config::EXTERNAL;
midi_usb_config.transport_config.tx_retry_count = 100; // 2;
midi_usb.Init(midi_usb_config);
midi_usb.StartReceive();
}
(Note that I also had to differentiate between a Battery/Power Brick vs Computer)