How to forward raw MIDI bytes to USB MIDI?

progmars

Member
I'm working on a pair of devices that send (Mini Pro + NRF24L01) and receive (Teensy + NRF24L01) raw MIDI bytes from the source device. Currently, I don't even care about what kind of MIDI message it is, I just want to forward it to USB MIDI. There will be no Sysex messages coming in from the source device. I'm not quite sure about running status though - the source might be sending messages with running status.

Teensy's documentation says:

A generic send function is also available, primarily meant for use to forward messages between Serial (5 pin DIN) MIDI or USB Host MIDI.
usbMIDI.send(type, data1, data2, channel)

To use this function, I would need to parse the MIDI message.

I have looked at the Interface_3x3 example and it uses SerialMIDI library to read and parse the incoming data:
Code:
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI1);

if (MIDI1.read()) {
    // get a MIDI IN1 (Serial) message
    byte type = MIDI1.getType();
    byte channel = MIDI1.getChannel();
    byte data1 = MIDI1.getData1();
    byte data2 = MIDI1.getData2();

    usbMIDI.send(type, data1, data2, channel, 0);

Is there any way to feed the raw bytes into serialMIDI? Should I use a serial port with TX+RX connected, send raw bytes to it, and read from it using serialMIDI? Seems somewhat overkill to me. Or should I use some other library that's capable of collecting raw bytes and parsing them?
 
I think I finally found something; sharing it in case it might be useful to others. It turns out I can reuse Control Surface MIDI library parser alone. It's a bit confusingly named SerialMIDI_Parser but actually, it is not linked to a serial port at all, it just accumulates bytes in a serial manner and tries to extract messages.

Code:
SerialMIDI_Parser parser;

// in the loop:

    // feed the payload bytes and try to parse the message out, if possible
    MIDIReadEvent event = MIDIReadEvent::NO_MESSAGE;

    // in my case, payload is known to always contain a single MIDI message only
    for (uint8_t pl = 0; pl < payload.length; pl ++)
    {
        event = parser.parse(payload.message[pl]);
    }
    payload.usedLength = 0; // to prevent consuming the same data in the next iteration until we receive new bytes in the payload

    // only a single message is expected in the payload, so we can process it outside the "for" loop
    if (event == MIDIReadEvent::CHANNEL_MESSAGE)
    {
        usbMidi.send(parser.getChannelMessage());
    } 
    else if (event == MIDIReadEvent::REALTIME_MESSAGE)
    {
        usbMidi.send(parser.getRealTimeMessage());
    } 
    
    // MIDI Controllers should discard incoming MIDI messages.
    // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
    // update reads the messages internally and discards them if we don't have any processing callbacks set
    usbMidi.update();

It looks naive but seems to work well enough. If there are better ways to do it, I will appreciate your suggestions. I somehow could not find a similar reusable byte-accumulating-parser in the default Teensy USB MIDI libraries.
 
Is there any way to feed the raw bytes into serialMIDI? Should I use a serial port with TX+RX connected, send raw bytes to it, and read from it using serialMIDI? Seems somewhat overkill to me.

It's not overkill at all. The Interface_3x3 example is the correct way.

The actual data format used by USB MIDI is not the same as traditional Serial MIDI. Messages are aligned to 32 bit boundaries with virtual cable/port info added and a redundant copy of the message type bits. Sysex messages are encoded in a special way too. So you can't just copy the raw bytes between Serial MIDI and USB MIDI.

Teensy USB MIDI isn't designed to give you access to the raw data bytes at the USB bulk packet level. It uses the same API (or as similar as I could make it) to the widely used MIDI library. But if it did give raw data access, you would need to use the special 32 bit fields format specific to USB MIDI. A different set of bytes are needed to represent the same MIDI messages.
 
Yes, I suspected that USB MIDI protocol has its specifics and it would not work with direct pass-through of "old style" serial MIDI bytes. I hoped there was a way to tell USB MIDI API "Here are raw bytes of serial MIDI without separated status & channel; please interpret them as necessary to extract a proper MIDI message and then send it to USB MIDI according to its protocol."

At least I got it working with the Control Surface library, although it's larg-ish and includes lots of stuff that I don't actually need. All I needed was an accumulating MIDI parser. Teensy's MIDI also has something similar, but it is not exposed as an API, because, as you said, to be compatible with other popular MIDI APIs.
 
To have direct access to the RAW data would be so good for debugging and understanding how the device actually communicates. Now there is too many layers of confusion, that makes very difficult to follow the device documentation.

Anyway, as it is, how can I access the data in MIDI1.getSysExArray(), i.e to print it to Serial 1 for debugging terminal.

the

Serial1.write(usbMIDI.getType());
Serial1.write(usbMIDI.getChannel());
Serial1.write(usbMIDI.getData1());
Serial1.write(usbMIDI.getData2());

Serial1.write(usbMIDI.getCable());

Work as expected, but how do I access the bytes in ExArray?

To answer my self, this seems to work

Code:
      while (usbMIDI.read()) {
    Serial1.write(usbMIDI.getType());
    Serial1.write(usbMIDI.getChannel());
    Serial1.write(usbMIDI.getData1());
    Serial1.write(usbMIDI.getData2());
    Serial1.write(0xFF);
    Serial1.write(0xFF);
    Serial1.write(0xFF);
    Serial1.write(usbMIDI.getSysExArray(),usbMIDI.getSysExArrayLength() );
    Serial1.write(0x00);
    Serial1.write(0x00);
    Serial1.write(0x00);
    Serial1.write(usbMIDI.getCable());
     }

And I get (the 0xFF, 0xFF, 0xFF, and 0x00, 0x00, 0x00, as separator)

Code:
0xF0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xF0, 0xF7, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x06
0x00, 0xFF, 0xFF, 0xFF, 0xF0, 0x7E, 0x7F, 0x06, 0x01, 0xF7, 0x00, 0x00, 0x00, 0x0F,

And now then need to figure out what that actually is.
 
Last edited:
Simple things done so difficult, so the effective data on what I receive seems to be

0xF0, 0xF7 // empty message?

0xF0, 0x7E, 0x7F, 0x06, 0x01, 0xF7,// seems to be this

View attachment 29811

And now should send the response as follows, + the 25 following bytes, but how to do that as I can not send just the bare data? (i.e all these bytes, but have to find what function is sending what of these bytes.)

View attachment 29812
 
Back
Top