Skip to content

Commit ab3a004

Browse files
committed
Tidied up Parse in MIDI.hpp
Tidied up Parse a little to reduce duplicate code. Also now deals with Undefined Midi Status bytes correctly. Undefined System Common F4 & F5 received will now reset Parse, and Undefined Realtime FD & F9 will be ignored (although F9 is currently used for Tick which is not really Midi spec). TuneRequest is also now treated as System Common (not Realtime), and will also reset Parse.
1 parent 92a8931 commit ab3a004

File tree

1 file changed

+85
-119
lines changed

1 file changed

+85
-119
lines changed

src/MIDI.hpp

Lines changed: 85 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -828,198 +828,164 @@ inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel
828828

829829
// Private method: MIDI parser
830830
template<class Transport, class Settings, class Platform>
831-
bool MidiInterface<Transport, Settings, Platform>::parse()
831+
bool MidiInterface<Transport, Settings, Platform>::parse()
832832
{
833833

834834
if (mTransport.available() == 0)
835835
return false;
836836

837837
mLastError &= ~(1UL << ErrorParse); // Clear ErrorParse bit
838-
/*Possible Errors:
839-
> SysEx Stop byte received with no pending SysEx Start.
840-
> Unsupported Status byte.
841-
> Data received without a valid Status byte or Running Status.
842-
> Warning SysEx split warning.
843-
.... could potentially add an error for when SysEx is aborted due to receiving a new non-realtime status byte.
844-
*/
845838

846839
const byte extracted = mTransport.read();
847840

848841
if (extracted >= 0x80) {
849-
// Lets try get a valid Status byte. Non-realtime status overrides any current Status
850-
const MidiType pendingType = getTypeFromStatusByte(extracted);
842+
// Get Status including Undefined ones so we can properly deal with them
843+
const MidiType pendingType = extracted < 0xF0 ? MidiType(extracted & 0xF0) : MidiType(extracted);
851844
switch (pendingType) {
852-
// Realtime
845+
// One byte realtime will complete now
853846
case Start:
854847
case Continue:
855848
case Stop:
856849
case Clock:
857-
case Tick:
850+
case Tick: // <---- Is tick relevant anymore?? .... Not part of the MIDI spec
858851
case ActiveSensing:
859852
case SystemReset:
860-
case TuneRequest:
861-
// Handle message now
853+
// Might as well quickly deal with these now.
862854
mMessage.type = pendingType;
863855
mMessage.channel = 0;
864856
mMessage.data1 = 0;
865857
mMessage.data2 = 0;
866858
mMessage.length = 1;
867859
mMessage.valid = true;
868860
return true;
869-
break;
870-
// 2 byte messages
871-
case ProgramChange:
872-
case AfterTouchChannel:
873-
case TimeCodeQuarterFrame:
874-
case SongSelect:
875-
mPendingMessage[0] = extracted;
876-
mPendingMessageExpectedLength = 2;
877-
break;
878-
// 3 byte messages
879861
case NoteOn:
880862
case NoteOff:
881863
case ControlChange:
882864
case PitchBend:
883865
case AfterTouchPoly:
884866
case SongPosition:
885-
mPendingMessage[0] = extracted;
886867
mPendingMessageExpectedLength = 3;
887868
break;
888-
// SysEx
869+
case ProgramChange:
870+
case AfterTouchChannel:
871+
case TimeCodeQuarterFrame:
872+
case SongSelect:
873+
mPendingMessageExpectedLength = 2;
874+
mPendingMessage[2] = 0; // Zero unused data
875+
break;
876+
// One byte common will be completed this run (This will also reset running status as this is not realtime)
877+
case TuneRequest:
878+
mPendingMessageExpectedLength = 1;
879+
mPendingMessage[1] = 0; // Zero unused data
880+
mPendingMessage[2] = 0; // Zero unused data
881+
break;
889882
case SystemExclusiveStart:
890-
mPendingMessage[0] = SystemExclusive;
891883
mPendingMessageExpectedLength = MidiMessage::sSysExMaxSize;
892884
mMessage.sysexArray[0] = SystemExclusiveStart;
893-
mLastError &= ~(1UL << WarningSplitSysEx); // Reset Warning Split SysEx bit
894885
break;
895886
case SystemExclusiveEnd:
896-
if (mPendingMessage[0] == SystemExclusive) {
897-
mMessage.sysexArray[mPendingMessageIndex++] = SystemExclusiveEnd; // Post Inc pending index here for correct length data
898-
mMessage.type = SystemExclusive;
899-
mMessage.data1 = mPendingMessageIndex & 0xff; // LSB
900-
mMessage.data2 = byte(mPendingMessageIndex >> 8); // MSB
901-
mMessage.channel = 0;
902-
mMessage.length = mPendingMessageIndex;
903-
if (mMessage.sysexArray[0] == SystemExclusiveEnd) {
904-
// This is the last chunk of a split SysEx message, and is NOT a valid SysEx message (it starts with 0xF7)
905-
mMessage.valid = false; // SysEx message is split so this is not technically valid
906-
launchCallback(); // Lets notify callback to deal with this
907-
resetInput(); // Restart message
908-
return false;
909-
} else {
910-
// We are in a valid SysEx message that hasn't overrun (starts with 0xF0) so lets complete it
911-
mMessage.valid = true;
912-
resetInput(); // Restart message
913-
return true;
914-
}
915-
} else { // Looks like a SysEx End without a Sysex Start
916-
mLastError |= 1UL << ErrorParse; // Error: SysEx Stop byte received with no pending SysEx Start.
917-
if (mErrorCallback)
918-
mErrorCallback(mLastError);
919-
resetInput(); // Restart message
920-
return false;
887+
if (mPendingMessage[0] == SystemExclusiveStart) { // If were currently doing SysEx
888+
mPendingMessageExpectedLength = ++mPendingMessageIndex; // Update expected lenght as we have an EOX and within Buffer
889+
break;
921890
}
922-
break;
923-
// Unsupported
924891
default:
925-
mPendingMessage[0] = InvalidType;
926-
mLastError |= 1UL << ErrorParse; // Error: Unsupported Status byte.
892+
mLastError |= 1UL << ErrorParse; // Error: Undefined Status or stray EOX
927893
if (mErrorCallback)
928894
mErrorCallback(mLastError);
929-
resetInput(); // Restart message
895+
if ((pendingType != Undefined_F9) && (pendingType != Undefined_FD)) { // Dont reset for 0xFD & 0xF9 (Undefined Realtime)
896+
resetInput(); // Input reset for stray EOX and Undefined common F4/F5
897+
}
930898
return false;
931-
break;
932899
}
933-
mPendingMessageIndex = 1; // If we are here, we have a valid Status! Lets try get some Data for it....
934-
mRunningStatus_RX = InvalidType; // Lets also reset Running Status until we have a complete message
935-
return (Settings::Use1ByteParsing) ? false : parse();
900+
mRunningStatus_RX = InvalidType; // Reset Running Status until valid channel message complete
901+
mPendingMessage[0] = extracted; // Status seems good so lets store in pending
902+
if (extracted != SystemExclusiveEnd) mPendingMessageIndex = 1; // Set PendingMessageIndex to 1 (needs to be unchanged for EOX)
936903

937904
} else {
938-
// Lets get some data... First off.. check for Status Byte, or use Running Status
905+
// Check Status
939906
if (mPendingMessageIndex == 0) {
940907
if (mRunningStatus_RX) {
941-
// Yay! We have Running Status
942908
mPendingMessage[0] = mRunningStatus_RX;
943909
mPendingMessageIndex = 1;
944910
} else {
945-
// ooops.... No Status Byte... No Running Status... lets ignore this data
946-
mLastError |= 1UL << ErrorParse; // Error: Data received without a valid Status byte or Running Status.
911+
mLastError |= 1UL << ErrorParse; // Error: No Status
947912
if (mErrorCallback)
948913
mErrorCallback(mLastError);
949914
return false;
950915
}
951916
}
952-
953-
// Status or Running Status is good so add extracted data byte to pending message
917+
// Add Data
954918
if (mPendingMessage[0] == SystemExclusive)
955919
mMessage.sysexArray[mPendingMessageIndex] = extracted;
956920
else
957921
mPendingMessage[mPendingMessageIndex] = extracted;
958-
959-
// Now we are going to check if we have reached the end of the message
960-
if (mPendingMessageIndex >= (mPendingMessageExpectedLength - 1)) {
961-
// SysEx larger than the allocated buffer size,
922+
mPendingMessageIndex++;
923+
}
924+
// Check for a complete message
925+
if (mPendingMessageIndex >= mPendingMessageExpectedLength) {
926+
// Process SysEx
927+
if (mPendingMessage[0] == SystemExclusiveStart || mPendingMessage[0] == SystemExclusiveEnd) {
928+
929+
mMessage.type = SystemExclusive;
930+
mMessage.data1 = mPendingMessageIndex & 0xff; // LSB
931+
mMessage.data2 = byte(mPendingMessageIndex >> 8); // MSB
932+
mMessage.channel = 0;
933+
mMessage.length = mPendingMessageIndex;
934+
935+
// SysEx can be larger than the allocated buffer size (must handle with callbacks only)
962936
// Split SysEx like so:
963-
// first: 0xF0 .... 0xF0
964-
// middle: 0xF7 .... 0xF0
965-
// last: 0xF7 .... 0xF7
966-
// ***** If the buffer has overrun, this SysEx message can now no longer be considered a valid Midi message and must be dealt with via callbacks only! ****
967-
if (mPendingMessage[0] == SystemExclusive) {
968-
// Warn at start of SysEx split
937+
// first: 0xF0 .... 0xF0
938+
// midlle: 0xF7 .... 0xF0
939+
// last: 0xF7 .... 0xF7
940+
941+
// SysEx buffer not full... EOX
942+
if (mPendingMessage[0] == SystemExclusiveEnd) {
943+
mMessage.sysexArray[mPendingMessageIndex - 1] = SystemExclusiveEnd;
944+
mPendingMessageIndex = 0;
945+
if (mMessage.sysexArray[0] == SystemExclusiveEnd) { // This is the last chunk of split SysEx
946+
mMessage.valid = false;
947+
launchCallback();
948+
} else { // Not Split SysEx
949+
mMessage.valid = true;
950+
}
951+
} else { // SysEx buffer full
969952
if (mMessage.sysexArray[0] == SystemExclusiveStart) {
970-
mLastError |= 1UL << WarningSplitSysEx; // We have this error already defined so may as well use it
971953
if (mErrorCallback)
972-
mErrorCallback(mLastError);
954+
mErrorCallback(1UL << WarningSplitSysEx); // Notify but no need to store this warning?
973955
}
974-
auto lastByte = mMessage.sysexArray[Settings::SysExMaxSize - 1];
975-
mMessage.sysexArray[Settings::SysExMaxSize - 1] = SystemExclusiveStart;
976-
mMessage.type = SystemExclusive;
977-
978-
// Get length
979-
mMessage.data1 = Settings::SysExMaxSize & 0xff; // LSB
980-
mMessage.data2 = byte(Settings::SysExMaxSize >> 8); // MSB
981-
mMessage.channel = 0;
982-
mMessage.length = Settings::SysExMaxSize;
983-
mMessage.valid = false; // SysEx message is split so this is not technically valid
984-
985-
// Notify callback to deal with the SysEx data chunk
956+
byte lastByte = mMessage.sysexArray[Settings::SysExMaxSize - 1]; // <--- change from Settings::SysExMaxSize to mPendingMessageIndex?
957+
mMessage.sysexArray[Settings::SysExMaxSize - 1] = SystemExclusiveStart; // <--- change from Settings::SysExMaxSize to mPendingMessageIndex?
958+
mMessage.valid = false;
986959
launchCallback();
987960

988961
// Prep next SysEx data chunk to start with 0xF7
989962
mMessage.sysexArray[0] = SystemExclusiveEnd;
990963
mMessage.sysexArray[1] = lastByte;
991964
mPendingMessageIndex = 2;
992-
// SysEx buffer has overrun so parse() will no longer return true, and will need to be dealt with via callbacks only
993-
return false;
994965
}
995966

996-
// Pending message is complete so lets save it
997-
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
998-
999-
if (isChannelMessage(mMessage.type)) {
1000-
mMessage.channel = getChannelFromStatusByte(mPendingMessage[0]);
1001-
// Message will be completed soon so lets update RunningStatus now as this is obviously a valid Channel Message.
1002-
mRunningStatus_RX = mPendingMessage[0];
1003-
} else
1004-
mMessage.channel = 0;
1005-
1006-
mMessage.data1 = mPendingMessage[1];
1007-
// Save data2 only if applicable
1008-
mMessage.data2 = mPendingMessageExpectedLength == 3 ? mPendingMessage[2] : 0;
1009-
mMessage.length = mPendingMessageExpectedLength;
1010-
mMessage.valid = true;
1011-
1012-
// Reset index for next message
1013-
mPendingMessageIndex = 0;
1014-
//mPendingMessageExpectedLength = 0; // <-- No need to reset this as its valid still for Running Status, or will be updated on next Status byte received.
967+
return mMessage.valid;
968+
}
1015969

1016-
return true;
1017-
} else {
1018-
// We need more data...
1019-
mPendingMessageIndex++;
970+
// Process message
971+
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
972+
if (isChannelMessage(mMessage.type)) {
973+
mMessage.channel = (mPendingMessage[0] & 0x0F) + 1;
974+
mRunningStatus_RX = mPendingMessage[0];
975+
} else
976+
mMessage.channel = 0;
977+
mMessage.data1 = mPendingMessage[1];
978+
mMessage.data2 = mPendingMessage[2];
979+
mMessage.length = mPendingMessageExpectedLength;
980+
mMessage.valid = true;
981+
982+
// Reset index for next message
983+
mPendingMessageIndex = 0;
1020984

1021-
return (Settings::Use1ByteParsing) ? false : parse();
1022-
}
985+
return true;
986+
} else {
987+
// We need more input...
988+
return (Settings::Use1ByteParsing) ? false : parse();
1023989
}
1024990
}
1025991

0 commit comments

Comments
 (0)