SynthLab SDK
Enqueueing MIDI Events

Your plugin framework will deliver a set of MIDI events with each incoming audio buffer during its audio buffer processing cycle. Typically these events are timestamped (from the host) with a buffer sample time value that aligns the MIDI events with samples in the audio buffer. Regardless of coding, your plugin framework will need to enqueue the MIDI messages for a particular block of audio data into the SynthProcessInfo struct prior to calling the SynthEngine's render() function.

The SynthProcessInfo struct stores a vector of midiEvents. This is one of two MIDI event structures, and is more generalized and more closely corresponds to raw MIDI data. The other MIDI Note Events structure is only for note-on and note-off messages and is used inside of the SynthEngine and SynthVoice objects. The code for this structure is below.

Things to note:

  • the MIDI bytes are encoded into simple uint32_t datatypes
  • there is an additional non-MIDI data value midiSampleOffset that should arrive from your framework; you will need to use this value to know which MIDI messages to send into the Engine for a given audio render block however this value is NOT used during the audio block render process because of the way MIDI messages are decoded and handled at the top of the block processing function
  • use the specialized constructor to create the MIDI event in one line of code
// --- general purpose MIDI event
struct midiEvent
{
midiEvent() {}
midiEvent(uint32_t _midiMessage,
uint32_t _midiChannel,
uint32_t _midiData1,
uint32_t _midiData2,
uint32_t _midiSampleOffset = 0)
: midiMessage(_midiMessage)
, midiChannel(_midiChannel)
, midiData1(_midiData1)
, midiData2(_midiData2)
, midiSampleOffset(_midiSampleOffset) {}
midiEvent& operator=(const midiEvent& data)
{
// removed for brevity
}
uint32_t midiMessage = 0;] // < BYTE message as UINT
uint32_t midiChannel = 0; // < BYTE channel as UINT
uint32_t midiData1 = 0; // < BYTE data 1 as UINT
uint32_t midiData2 = 0; // < BYTE data 2 as UINT
uint32_t midiSampleOffset = 0; // < sample offset of midi event within audio buffer
};
//

For each MIDI message your framework delivers during a particular audio block, you enqueue the MIDI data with a helper function. So, your framework code will need to have a loop that pushes these events into the queue. In this example, the MIDI data is hardcoded, but it would normally come from your plugin framework's received MIDI messages.

// --- your plugin framework prepares this synthBlockProcInfo struct
SynthLab::SynthProcessInfo synthBlockProcInfo;
// --- always clear out the events from the last audio block process
synthBlockProcInfo.clearMidiEvents();
// --- parse MIDI events that occur during each audio block and create the event structure
SynthLab::midiEvent synthEvent(NOTE_ON, /* NOTE_ON is declared in synthconstants.h */
0, /* Channel 0 (MIDI Channel 1) */
60, /* midi note number, Middle C */
127, /* velocity */
0); /* buffer time, not used */
// --- and push structure into SynthProcessInfo, which adds them to its vector
synthBlockProcInfo.pushMidiEvent(synthEvent);
//

When the SynthEngine's render function returns, these MIDI events will still be intact, and unaltered. You may need to use them for some other MIDI processing so they are avaiable. Be sure to clear them out before enqueueing the next batch for the next audio block.


synthlab_4.png