Skip to content

midi: allow bigger sysex messages to be sent, such as the OLED image - #174

Merged
jamiefaye merged 5 commits into
SynthstromAudible:communityfrom
bfredl:oled_sysex
Jul 10, 2023
Merged

midi: allow bigger sysex messages to be sent, such as the OLED image#174
jamiefaye merged 5 commits into
SynthstromAudible:communityfrom
bfredl:oled_sysex

Conversation

@bfredl

@bfredl bfredl commented Jul 8, 2023

Copy link
Copy Markdown
Collaborator

Some simplifications were made to the driver code by Rohan under the
assumption we were only going to queue up to 32 usb-midi messages
for a device per "tick" of the main routine.
These had to be reconsidered, like there is now again a callback from the usb driver
interrupt back to midi_engine to check for more data to send.

It doesn't seem to be possible to increase the amount of data sent in
each call to the driver more than 32 messages (or 128 bytes).
Instead, implement a ring buffer for midi data waiting to be sent over USB.
When we get an interrupt for usbSendComplete, we can check the ring
buffer again and restart the low-level send until the ring buffer is
empty.

Implement a sysex request for sending the contents of the OLED display
as a sysex reply message. This needs more work before being
generally useful as an external display (not to mention client code for multiple platforms)
, but serves as a test case for larger sysex messages. This also tangents #121 , an unified build
would eventually allow developers with a 7SEG display to test how their code
would look on a virtual OLED display.

@bfredl

bfredl commented Jul 8, 2023

Copy link
Copy Markdown
Collaborator Author

I have tested these for

  • serial midi
  • deluge acting as a USB device
  • deluge acting as USB host

I will try to find a way to test the last one before marking this as ready

@bfredl
bfredl force-pushed the oled_sysex branch 2 times, most recently from fe72bae to b39b9b2 Compare July 8, 2023 20:20
@jamiefaye

Copy link
Copy Markdown
Collaborator

Looking forward to this one going in.

bfredl added 3 commits July 9, 2023 20:02
Some simplifications were made to the driver code by Rohan under the
assumption we were only going to queue up to 32 usb-midi messages
for a device per "tick" of the main routine.
These had to be reconsidered, like there is now again a callback from the usb driver
interrupt back to midi_engine to check for more data to send.

It doesn't seem to be possible to increase the amount of data sent in
each call to the driver more than 32 messages (or 128 bytes).
Instead, implement a ring buffer for midi data waiting to be sent over USB.
When we get an interrupt for usbSendComplete, we can check the ring
buffer again and restart the low-level send until the ring buffer is
empty.

Implement a sysex request for sending the contents of the OLED display
as a sysex reply message. This needs more work before being
generally useful as an external display, but serves as a test case
for larger sysex messages.
…icies

These objects are intended to be used this way. If disconnected these
get disabled, but not pointers into invalid memory

above class MIDIDevice:
> These never get destructed. So we're safe having various Instruments etc holding pointers to them.
@bfredl

bfredl commented Jul 9, 2023

Copy link
Copy Markdown
Collaborator Author

Update: sending as a host to devices now works for most devices. a buffer limit of 64 bytes seems common, so lets use that for devices. some devices have even lower limits tho, like my MIDIMATE II serial MIDI to USB converter which only accepts sending one (1) midi packet at a time. (We could try to figure this out from the configuration data, I tried with the bMAXPacketSize field but it doesn't seem to be the right one).

@bfredl
bfredl marked this pull request as ready for review July 9, 2023 18:51

@m-m-adams m-m-adams left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added some comments where I think this could be improved a bit.

The only big issue is that the current behaviour of send all messages to all devices doesn't work well for sysex since it's much more data and sysex parsing bugs are very common (especially with older gear). In peripheral mode that's fixable by enabling the third upstream port (already in the config), in host mode it would need some extra code to check that the device being sent to is the one requesting data

Comment thread src/RZA1/usb/r_usb_basic/src/driver/r_usb_plibusbip.c
Comment thread src/deluge/io/midi/midi_device_manager.cpp Outdated
Comment thread src/deluge/io/midi/midi_device_manager.cpp Outdated
class MIDIDeviceUSBUpstream final : public MIDIDeviceUSB {
public:
MIDIDeviceUSBUpstream(uint8_t portNum = 0) { portNumber = portNum; }
MIDIDeviceUSBUpstream(uint8_t portNum = 0) : MIDIDeviceUSB(portNum) {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the portnum still necessary after giving it a default value in the mididevice USB class?

Comment thread src/deluge/io/midi/midi_device_manager.cpp Outdated
// (3 payload bytes per USB-MIDI message)
#define MIDI_SEND_BUFFER_LEN 32
// NOTE: increasing this even more doesn't work.
// Looks like a hardware limitation (maybe we more in FS mode)?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just been reading the the usb midi spec and we might be able to use the maximum buffer size specified by a downstream device instead of this guesswork.

Comment thread src/deluge/io/midi/midi_engine.cpp Outdated
Comment thread src/deluge/io/midi/midi_engine.cpp Outdated

for (int d = 0; d < MAX_NUM_USB_MIDI_DEVICES; d++) {
if (connectionFlags & (1 << d)) {
connectedDevice = &connectedUSBMIDIDevices[ip][d];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this works properly, won't it just send to the first connected device? Since each ConnectedUsbMidiDevice holds an array of 4 USBMidiDevices it should check that the current device is actually held by the connecteddevice it finds

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This follows the existing logic in sendMessage and it does seem to work. connectionFlags is private to the inner USBMIDIDevice and should only have a flag set for the specific connected device. It's a bit weird way of structuring things tho.

What I think we don't support at all is virtual port/cable differentiation for a specific connected device. That would be an important follow-up refactor.

@bfredl

bfredl commented Jul 10, 2023

Copy link
Copy Markdown
Collaborator Author

The only big issue is that the current behaviour of send all messages to all devices doesn't work well for sysex since it's much more data and sysex parsing bugs are very common (especially with older gear). In peripheral mode that's fixable by enabling the third upstream port (already in the config), in host mode it would need some extra code to check that the device being sent to is the one requesting data

This is already what this branch implements. Sysex replies are being sent to the specific device that sent a request. not sure what makes it seem like otherwise.

I agree the thrird port should be added at this point, but differentiation between virtual ports is already implemented (request the oled data on virtual midi out 2; get a reply on virtual midi in 2).

@bfredl

bfredl commented Jul 10, 2023

Copy link
Copy Markdown
Collaborator Author

Thanks for the review. I tried to make the naming of new stuff more consistent and added a bit more comments.

@m-m-adams

Copy link
Copy Markdown
Collaborator

This is already what this branch implements. Sysex replies are being sent to the specific device that sent a request. not sure what makes it seem like otherwise.

My assumption was based on the original firmware behaviour on flush to send the buffered midi to all devices and I don't see logic that changes that. It's differentiated by midiDeviceUSB but that's not respected by the flush functions. If I've misread the code to change that then no worries, I've got nothing that can be hosted and log midi messages to test with so that statement is purely based on reading the code

I agree the thrird port should be added at this point, but differentiation between virtual ports is already implemented (request the oled data on virtual midi out 2; get a reply on virtual midi in 2).

Yeah it's not a huge deal, it just needs to be declared with the other 2. It wouldn't have to be added to the menu structures or preferences files since it should just be used by a companion app or similar

@bfredl

bfredl commented Jul 10, 2023

Copy link
Copy Markdown
Collaborator Author

My assumption was based on the original firmware behaviour on flush to send the buffered midi to all devices .

what happened is that all devices gets flushed at the same time even though one device's buffer was full. I'd like to untangle that temporal mix-up at some point as well. But there was still a dedicated buffer for each physical device so it was always possible to send messages to a specific device (even though with note/cc data they often contained exactly the same contents).

@m-m-adams

Copy link
Copy Markdown
Collaborator

Makes sense, thanks

@bfredl

bfredl commented Jul 10, 2023

Copy link
Copy Markdown
Collaborator Author

Added the third port dedicated for sysex data (it never sends channel/MPE data).

I kept some refactors in the midi device menu even though I didn't add
the new port to the menu right now, as there is no relevant settings.
we could add it later by just adjusting `lowestDeviceNum`
@jamiefaye
jamiefaye added this pull request to the merge queue Jul 10, 2023
Merged via the queue into SynthstromAudible:community with commit 7dbb780 Jul 10, 2023

rle = false; // not yet implemented

uint8_t reply_hdr[5] = {0xf0, 0x7e, 0x02, 0x40, rle ? 0x02 : 0x01};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading this correctly, the message being sent is "Universal Non-realtime sample data packet".

Should this be changed to use 0x7d as the second byte, or is there a specific reason this message type was chosen?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no you are right, it is supposed to be 0x7d

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants