How To Handle SysEx in Plug’n Script

With Plug’n Script 3.5 you can now handle MIDI SysEx (System Exclusive) events. Let’s see in details how this works in a MIDI script.

MIDI System Exclusive Syntax

In MIDI, System Exclusive (SysEx) allows you to send/receive binary data of any size within a MIDI stream. A SysEx stream can be easily recognized, as it starts with an event using 0xF0 as the first byte, and ends with 0xF7. All bytes in between are strictly 7-bits long (first bit is always 0), which makes it easy to differentiate with other types of events.

In Plug’n Script, SysEx events are implemented as a series of 4-bytes-long events containing the data. The last event may actually be shorter, with one of the bytes containing the  0xF7 marker. This lets you manage large amounts of data without having to allocate large buffers in the audio thread (which is not recommended).

Receiving SysEx MIDI

Plug’n Script built-in MIDI utils (for both scripting and native) include a couple of functions that let you detect and decode SysEx streams. To use the MIDI library, just include the header at the beginning of your script:

// Angelscript syntax
#include "library/Midi.hxx"

// C/C++ syntax
#include "library/Midi.hxx"

You can check if a received event is a SysEx Start or SysEx End event using the getType function:

switch(MidiEventUtils::getType(event))
{
    case kSysexStart:
      // starting SysEx stream - read data from byte1
    break;
    case kSysexStart:
    // Ending SysEx stream (may contain data bytes before the 0x7F end marker)
    break;
}

You can also check if an event is SysEx or not using the isSysexData function. The example process function below filters out all SysEx events:

void processBlock(BlockData& data)
{
    for(uint i=0;i<data.inputMidiEvents.length;i++)
    {
        // filtering out SysEx data
        if(!MidiEventUtils::isSysexData(data.inputMidiEvents[i]))
        {
            // forward event (unchanged) if not SysEx
            data.outputMidiEvents.push(data.inputMidiEvents[i]);
        }
    }
}

Because the length of a SysEx stream is arbitrary and does not have to be a multiple of 4, the Midi library also provides utility functions to access any of the four bytes of SysEx events programmatically instead of using byte0, byte1 etc. like you would for other types of events:

for(int i=0;i<4;i++)
{
    // access byte #i, and stop reading if SyxEx end marker
    uint8 byte=MidiEventUtils::getEventByte(evt);
    if(byte==0xF7)
       break; // SysEx stream is finished
    else
      //read data
}

Sending SysEx MIDI

To send a SysEx stream with Plug’n Script, you simply send a series of 4 bytes events, the first one starting with 0xF0 in the first byte, and the last message containing the 0xF7 marker anywhere. Again, the library provides utility functions to help you with this task: MidiEventUtils::setType now accepts kSysexStart and kSysexEnd values.

Note that in the case of kSysexEnd, it will set the first byte of the event, creating an empty last SysEx message, which may not work in most cases (the 0x7F end marker will usually have to be manually placed in the middle of the event unless the data size is a multiple of 4.

Just like for reading, the framework provides a helper function to set the 4 bytes programmatically:

for(int i=0;i<4;i++) 
{ 
    // get value
    //...

    // set byte #i, and stop reading if SyxEx end marker 
    MidiEventUtils::setEventByte(evt,value);
}

And the finishSysex function will add the end marker after the last written byte in the event (if there is enough room for it):

// ending sysex stream
if(!MidiEventUtils::finishSysex(tempEvent,lastByte))
{
     // not enough room: push event to output and create a new empty event with SysEx end
     data.outputMidiEvents.push(tempEvent);
     MidiEventUtils::setType(tempEvent,);
}

// push last event with SysEx end
data.outputMidiEvents.push(tempEvent,kSysexEnd);

SysEx & Timing

While you can send an entire SysEx stream at a single timestamp, you may sometimes want to spread the data over multiple frames (blocks) in case you want to transfer a large amount of data. This can be easily done by waiting for the next frame before sending the rest of the stream.

Please be aware that a SysEx stream should not be interrupted by other events, it is supposed to be all streamed without any interruption. Any note or CC event sent within a Sysex data stream will end the Sysex stream that will be considered as invalid by the host application or the receiver.

Examples

You can check the built-in MIDI log and MIDI Timecode scripts that use the new SysEx features of Plug’Script to receive and send these MIDI messages.

Enjoy!

>discuss this topic in the forum

Leave a Reply

Your email address will not be published. Required fields are marked *