in Firmware/ExpressivePixelsMIDI/MIDI.hpp [676:952]
bool MidiInterface<EPXUart, Settings>::parse()
{
if (mSerial.available() == 0)
// No data available.
return false;
// Parsing algorithm:
// Get a byte from the serial buffer.
// If there is no pending message to be recomposed, start a new one.
// - Find type and channel (if pertinent)
// - Look for other bytes in buffer, call parser recursively,
// until the message is assembled or the buffer is empty.
// Else, add the extracted byte to the pending message, and check validity.
// When the message is done, store it.
const byte extracted = mSerial.read();
// Ignore Undefined
if (extracted == 0xf9 || extracted == 0xfd)
{
if (Settings::Use1ByteParsing)
{
return false;
}
else
{
return parse();
}
}
if (mPendingMessageIndex == 0)
{
// Start a new pending message
mPendingMessage[0] = extracted;
// Check for running status first
if (isChannelMessage(getTypeFromStatusByte(mRunningStatus_RX)))
{
// Only these types allow Running Status
// If the status byte is not received, prepend it
// to the pending message
if (extracted < 0x80)
{
mPendingMessage[0] = mRunningStatus_RX;
mPendingMessage[1] = extracted;
mPendingMessageIndex = 1;
}
// Else: well, we received another status byte,
// so the running status does not apply here.
// It will be updated upon completion of this message.
}
switch (getTypeFromStatusByte(mPendingMessage[0]))
{
// 1 byte messages
case Start:
case Continue:
case Stop:
case Clock:
case ActiveSensing:
case SystemReset:
case TuneRequest:
// Handle the message type directly here.
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
mMessage.channel = 0;
mMessage.data1 = 0;
mMessage.data2 = 0;
mMessage.valid = true;
// Do not reset all input attributes, Running Status must remain unchanged.
// We still need to reset these
mPendingMessageIndex = 0;
mPendingMessageExpectedLength = 0;
return true;
break;
// 2 bytes messages
case ProgramChange:
case AfterTouchChannel:
case TimeCodeQuarterFrame:
case SongSelect:
mPendingMessageExpectedLength = 2;
break;
// 3 bytes messages
case NoteOn:
case NoteOff:
case ControlChange:
case PitchBend:
case AfterTouchPoly:
case SongPosition:
mPendingMessageExpectedLength = 3;
break;
case SystemExclusive:
// The message can be any length
// between 3 and MidiMessage::sSysExMaxSize bytes
mPendingMessageExpectedLength = MidiMessage::sSysExMaxSize;
mRunningStatus_RX = InvalidType;
mMessage.sysexArray[0] = SystemExclusive;
break;
case InvalidType:
default:
// This is obviously wrong. Let's get the hell out'a here.
resetInput();
return false;
break;
}
if (mPendingMessageIndex >= (mPendingMessageExpectedLength - 1))
{
// Reception complete
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
mMessage.channel = getChannelFromStatusByte(mPendingMessage[0]);
mMessage.data1 = mPendingMessage[1];
mMessage.data2 = 0; // Completed new message has 1 data byte
mPendingMessageIndex = 0;
mPendingMessageExpectedLength = 0;
mMessage.valid = true;
return true;
}
else
{
// Waiting for more data
mPendingMessageIndex++;
}
if (Settings::Use1ByteParsing)
{
// Message is not complete.
return false;
}
else
{
// Call the parser recursively
// to parse the rest of the message.
return parse();
}
}
else
{
// First, test if this is a status byte
if (extracted >= 0x80)
{
// Reception of status bytes in the middle of an uncompleted message
// are allowed only for interleaved Real Time message or EOX
switch (extracted)
{
case Clock:
case Start:
case Continue:
case Stop:
case ActiveSensing:
case SystemReset:
// Here we will have to extract the one-byte message,
// pass it to the structure for being read outside
// the MIDI class, and recompose the message it was
// interleaved into. Oh, and without killing the running status..
// This is done by leaving the pending message as is,
// it will be completed on next calls.
mMessage.type = (MidiType)extracted;
mMessage.data1 = 0;
mMessage.data2 = 0;
mMessage.channel = 0;
mMessage.valid = true;
return true;
// End of Exclusive
case 0xf7:
if (mMessage.sysexArray[0] == SystemExclusive)
{
// Store the last byte (EOX)
mMessage.sysexArray[mPendingMessageIndex++] = 0xf7;
mMessage.type = SystemExclusive;
// Get length
mMessage.data1 = mPendingMessageIndex & 0xff; // LSB
mMessage.data2 = byte(mPendingMessageIndex >> 8); // MSB
mMessage.channel = 0;
mMessage.valid = true;
resetInput();
return true;
}
else
{
// Well well well.. error.
resetInput();
return false;
}
default:
break; // LCOV_EXCL_LINE - Coverage blind spot
}
}
// Add extracted data byte to pending message
if (mPendingMessage[0] == SystemExclusive)
mMessage.sysexArray[mPendingMessageIndex] = extracted;
else
mPendingMessage[mPendingMessageIndex] = extracted;
// Now we are going to check if we have reached the end of the message
if (mPendingMessageIndex >= (mPendingMessageExpectedLength - 1))
{
// "FML" case: fall down here with an overflown SysEx..
// This means we received the last possible data byte that can fit
// the buffer. If this happens, try increasing MidiMessage::sSysExMaxSize.
if (mPendingMessage[0] == SystemExclusive)
{
resetInput();
return false;
}
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
if (isChannelMessage(mMessage.type))
mMessage.channel = getChannelFromStatusByte(mPendingMessage[0]);
else
mMessage.channel = 0;
mMessage.data1 = mPendingMessage[1];
// Save data2 only if applicable
mMessage.data2 = mPendingMessageExpectedLength == 3 ? mPendingMessage[2] : 0;
// Reset local variables
mPendingMessageIndex = 0;
mPendingMessageExpectedLength = 0;
mMessage.valid = true;
// Activate running status (if enabled for the received type)
switch (mMessage.type)
{
case NoteOff:
case NoteOn:
case AfterTouchPoly:
case ControlChange:
case ProgramChange:
case AfterTouchChannel:
case PitchBend:
// Running status enabled: store it from received message
mRunningStatus_RX = mPendingMessage[0];
break;
default:
// No running status
mRunningStatus_RX = InvalidType;
break;
}
return true;
}
else
{
// Then update the index of the pending message.
mPendingMessageIndex++;
if (Settings::Use1ByteParsing)
{
// Message is not complete.
return false;
}
else
{
// Call the parser recursively to parse the rest of the message.
return parse();
}
}
}
}