229 lines
8.0 KiB
C++
229 lines
8.0 KiB
C++
#include "SerialMIDI_Parser.hpp"
|
|
|
|
BEGIN_CS_NAMESPACE
|
|
|
|
MIDIReadEvent SerialMIDI_Parser::handleRealTime(uint8_t midiByte) {
|
|
rtmsg.message = midiByte;
|
|
return MIDIReadEvent::REALTIME_MESSAGE;
|
|
}
|
|
|
|
MIDIReadEvent SerialMIDI_Parser::handleNonRealTimeStatus(uint8_t midiByte) {
|
|
#if !IGNORE_SYSEX
|
|
// If a SysEx message was being received, and now we receive another
|
|
// status byte, the status byte should terminate the SysEx message
|
|
// first, and then we can handle the new status byte later.
|
|
bool untermSysEx = currentHeader == uint8_t(MIDIMessageType::SysExStart);
|
|
if (untermSysEx) {
|
|
// Handle this new status byte later (unless it's just a SysEx End
|
|
// byte, in which case we can just terminate it now).
|
|
if (midiByte != uint8_t(MIDIMessageType::SysExEnd))
|
|
storeByte(midiByte);
|
|
// Terminate the SysEx message.
|
|
// Check if the SysEx buffer has enough space to store the end byte.
|
|
if (!hasSysExSpace()) {
|
|
// If not store the new status byte to handle it later, and
|
|
// return the chunk we have saved up to now.
|
|
storeByte(midiByte);
|
|
return MIDIReadEvent::SYSEX_CHUNK;
|
|
}
|
|
// Enough space is available in buffer, store the end byte and
|
|
// terminate the message.
|
|
addSysExByte(uint8_t(MIDIMessageType::SysExEnd));
|
|
endSysEx();
|
|
currentHeader = 0;
|
|
runningHeader = 0;
|
|
return MIDIReadEvent::SYSEX_MESSAGE;
|
|
} else
|
|
#endif
|
|
{
|
|
// Tune Request is a special System Common message of 1 byte.
|
|
if (midiByte == uint8_t(MIDIMessageType::TuneRequest)) {
|
|
midimsg.header = midiByte;
|
|
midimsg.data1 = 0;
|
|
midimsg.data2 = 0;
|
|
if (sysCommonCancelsRunningStatus)
|
|
runningHeader = 0;
|
|
currentHeader = 0;
|
|
return MIDIReadEvent::SYSCOMMON_MESSAGE;
|
|
}
|
|
#if !IGNORE_SYSEX
|
|
// If the new status byte is a SysExStart, reset the SysEx buffer
|
|
// and store the start byte.
|
|
else if (midiByte == uint8_t(MIDIMessageType::SysExStart)) {
|
|
startSysEx();
|
|
addSysExByte(uint8_t(MIDIMessageType::SysExStart));
|
|
runningHeader = 0;
|
|
currentHeader = midiByte;
|
|
return MIDIReadEvent::NO_MESSAGE;
|
|
}
|
|
// This should already have been handled by the if (untermSysEx) above.
|
|
else if (midiByte == uint8_t(MIDIMessageType::SysExEnd)) {
|
|
DEBUGREF(F("Unexpected SysEx End"));
|
|
return MIDIReadEvent::NO_MESSAGE;
|
|
}
|
|
#endif
|
|
// Otherwise, start a System Common or Channel message.
|
|
else {
|
|
// Save the newly received status byte.
|
|
currentHeader = midiByte;
|
|
// A new message starts, so we haven't received the second byte
|
|
// yet.
|
|
thirdByte = false;
|
|
return MIDIReadEvent::NO_MESSAGE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Relevant sources about MIDI running status:
|
|
* - MIDI 1.0 Detailed Specification: A-2 (pdf p.65):
|
|
* “cleared when a System Exclusive or Common status message is received”
|
|
* - BLE-MIDI: p.4 (pdf p.7):
|
|
* “System Common and System Real-Time messages do not cancel Running Status”
|
|
*/
|
|
|
|
MIDIReadEvent SerialMIDI_Parser::handleStatus(uint8_t midiByte) {
|
|
// If it's a Real-Time message
|
|
if (midiByte >= uint8_t(MIDIMessageType::TimingClock)) {
|
|
return handleRealTime(midiByte);
|
|
}
|
|
// Normal header (channel message, system exclusive, system common):
|
|
else {
|
|
return handleNonRealTimeStatus(midiByte);
|
|
}
|
|
}
|
|
|
|
MIDIReadEvent SerialMIDI_Parser::handleData(uint8_t midiByte) {
|
|
if (currentHeader == 0) {
|
|
// If we didn't receive a header, we can't do anything with this data
|
|
if (runningHeader == 0) {
|
|
DEBUGREF(F("Data byte ignored"));
|
|
return MIDIReadEvent::NO_MESSAGE;
|
|
}
|
|
// If we have an active running status, use that as the current header
|
|
currentHeader = runningHeader;
|
|
}
|
|
|
|
midimsg.header = currentHeader;
|
|
|
|
// If this is the third byte of three (second data byte)
|
|
if (thirdByte) {
|
|
// If it's a channel message
|
|
if (midimsg.hasValidChannelMessageHeader()) {
|
|
midimsg.data2 = midiByte;
|
|
// Next byte is either a header or the first data byte of the next
|
|
// message, so clear the thirdByte flag
|
|
thirdByte = false;
|
|
runningHeader = midimsg.header;
|
|
currentHeader = 0;
|
|
return MIDIReadEvent::CHANNEL_MESSAGE;
|
|
}
|
|
// If it's a system common message
|
|
else if (midimsg.hasValidSystemCommonHeader()) {
|
|
midimsg.data2 = midiByte;
|
|
thirdByte = false;
|
|
if (sysCommonCancelsRunningStatus)
|
|
runningHeader = 0;
|
|
currentHeader = 0;
|
|
return MIDIReadEvent::SYSCOMMON_MESSAGE;
|
|
}
|
|
}
|
|
|
|
// If this is not the third byte of three, it's either the second byte
|
|
// (first data byte) of a channel or system common message,
|
|
// or a SysEx data byte
|
|
|
|
// If it's a channel message
|
|
else if (midimsg.hasValidChannelMessageHeader()) {
|
|
// If it's a channel message with two data bytes
|
|
if (ChannelMessage(midimsg).hasTwoDataBytes()) {
|
|
midimsg.data1 = midiByte;
|
|
// We've received the second byte, expect the third byte next
|
|
thirdByte = true;
|
|
return MIDIReadEvent::NO_MESSAGE;
|
|
}
|
|
// If it's a channel message with one data byte
|
|
else {
|
|
midimsg.data1 = midiByte;
|
|
midimsg.data2 = 0;
|
|
runningHeader = midimsg.header;
|
|
currentHeader = 0;
|
|
// The message is finished
|
|
return MIDIReadEvent::CHANNEL_MESSAGE;
|
|
}
|
|
}
|
|
|
|
// If it's a system common message
|
|
else if (midimsg.hasValidSystemCommonHeader()) {
|
|
// If it's a system common message with two data bytes
|
|
if (SysCommonMessage(midimsg).getNumberOfDataBytes() == 2) {
|
|
midimsg.data1 = midiByte;
|
|
// We've received the second byte, expect the third byte next
|
|
thirdByte = true;
|
|
return MIDIReadEvent::NO_MESSAGE;
|
|
}
|
|
// If it's a system common message with one data byte
|
|
else if (SysCommonMessage(midimsg).getNumberOfDataBytes() == 1) {
|
|
midimsg.data1 = midiByte;
|
|
midimsg.data2 = 0;
|
|
if (sysCommonCancelsRunningStatus)
|
|
runningHeader = 0;
|
|
currentHeader = 0;
|
|
// The message is finished
|
|
return MIDIReadEvent::SYSCOMMON_MESSAGE;
|
|
}
|
|
}
|
|
|
|
// Otherwise, it's not a channel message
|
|
|
|
#if !IGNORE_SYSEX
|
|
// If we're receiving a SysEx message, it's a SysEx data byte
|
|
else if (currentHeader == uint8_t(MIDIMessageType::SysExStart)) {
|
|
// Check if the SysEx buffer has enough space to store the data
|
|
if (!hasSysExSpace()) {
|
|
storeByte(midiByte); // Remember to add it next time
|
|
return MIDIReadEvent::SYSEX_CHUNK;
|
|
}
|
|
|
|
addSysExByte(midiByte);
|
|
return MIDIReadEvent::NO_MESSAGE;
|
|
}
|
|
#endif // IGNORE_SYSEX
|
|
|
|
DEBUGREF(F("Data byte after invalid header")); // LCOV_EXCL_LINE
|
|
runningHeader = 0; // LCOV_EXCL_LINE
|
|
currentHeader = 0; // LCOV_EXCL_LINE
|
|
return MIDIReadEvent::NO_MESSAGE; // LCOV_EXCL_LINE
|
|
}
|
|
|
|
MIDIReadEvent SerialMIDI_Parser::feed(uint8_t midiByte) {
|
|
// DEBUGREF(hex << NAMEDVALUE(midiByte) << dec);
|
|
|
|
// If it's a status byte (first byte of a message)
|
|
if (isStatus(midiByte)) {
|
|
return handleStatus(midiByte);
|
|
}
|
|
// If it's a data byte
|
|
else {
|
|
return handleData(midiByte);
|
|
}
|
|
}
|
|
|
|
MIDIReadEvent SerialMIDI_Parser::resume() {
|
|
if (!hasStoredByte())
|
|
return MIDIReadEvent::NO_MESSAGE;
|
|
|
|
uint8_t midiByte = popStoredByte();
|
|
|
|
#if !IGNORE_SYSEX
|
|
// If a SysEx message was in progress
|
|
if (currentHeader == uint8_t(MIDIMessageType::SysExStart)) {
|
|
// Reset the buffer for the next chunk
|
|
startSysEx();
|
|
}
|
|
#endif
|
|
|
|
return feed(midiByte);
|
|
}
|
|
|
|
END_CS_NAMESPACE |