Control Surface  1.2.0
MIDI Control Surface library for Arduino
USBMIDI_Parser.cpp
Go to the documentation of this file.
1 #include "USBMIDI_Parser.hpp"
2 #include <Settings/SettingsWrapper.hpp>
3 
5 
6 // https://usb.org/sites/default/files/midi10.pdf
8  DEBUG("MIDIUSB packet:\t" << hex << packet[0] << ' ' << packet[1] << ' '
9  << packet[2] << ' ' << packet[3] << dec);
10  // MIDI USB cable number
11  uint8_t CN = packet[0] >> 4;
12  // MIDI USB code index number
13  MIDICodeIndexNumber CIN = static_cast<MIDICodeIndexNumber>(packet[0] & 0xF);
14 
15  // SysEx constants
16  constexpr uint8_t SysExEnd =
17  static_cast<uint8_t>(MIDIMessageType::SYSEX_END);
18  constexpr uint8_t SysExStart =
19  static_cast<uint8_t>(MIDIMessageType::SYSEX_START);
20 
21  // Main parser logic
22  // =========================================================================
23 
24  // ------------------- Channel message (2 or 3 bytes) ------------------- //
25 
26  if (CIN >= MIDICodeIndexNumber::NOTE_OFF &&
27  CIN <= MIDICodeIndexNumber::PITCH_BEND) {
28  // 2- or 3-byte MIDI channel message
29 
30  // uint8_t type = packet[1] & 0xF0;
31  // if (CIN != type) // invalid MIDI USB packet
32  // return NO_MESSAGE;
33  midimsg.header = packet[1];
34  midimsg.data1 = packet[2];
35  midimsg.data2 = packet[3];
36  midimsg.CN = CN;
37  return MIDIReadEvent::CHANNEL_MESSAGE;
38  }
39 
40  // ---------------- SysEx starts or continues (3 bytes) ----------------- //
41 
42 #if !IGNORE_SYSEX
43  else if (CIN == MIDICodeIndexNumber::SYSEX_START_CONT) {
44  // SysEx starts or continues (3 bytes)
45  if (packet[1] == SysExStart)
46  startSysEx(CN); // start a new message
47  // (overwrite previous unfinished message)
48  else if (!receivingSysEx(CN)) { // If we haven't received a SysExStart
49  DEBUGREF(F("Error: No SysExStart received"));
50  return MIDIReadEvent::NO_MESSAGE; // ignore the data
51  }
52  addSysExByte(CN, packet[1]) && // add three data bytes to buffer
53  addSysExByte(CN, packet[2]) && //
54  addSysExByte(CN, packet[3]);
55  return MIDIReadEvent::NO_MESSAGE; // SysEx is not finished yet
56  }
57 
58  // --------------- SysEx ends with following single byte ---------------- //
59 
60  else if (CIN == MIDICodeIndexNumber::SYSEX_END_1B) {
61  // SysEx ends with following single byte
62  // (or Single-byte System Common Message, not implemented)
63  if (packet[1] != SysExEnd) {
64  // System Common (not implemented)
65  return MIDIReadEvent::NO_MESSAGE;
66  } else if (!receivingSysEx(CN)) { // If we haven't received a SysExStart
67  DEBUGFN(F("Error: No SysExStart received"));
68  return MIDIReadEvent::NO_MESSAGE; // ignore the data
69  }
70  if (addSysExByte(CN, packet[1])) {
71  endSysEx(CN);
72  return MIDIReadEvent::SYSEX_MESSAGE;
73  } else {
74  return MIDIReadEvent::NO_MESSAGE; // Buffer full, ignore message
75  }
76  }
77 
78  // ---------------- SysEx ends with following two bytes ----------------- //
79 
80  else if (CIN == MIDICodeIndexNumber::SYSEX_END_2B) {
81  // SysEx ends with following two bytes
82  if (packet[1] == SysExStart)
83  startSysEx(CN); // start a new message
84  // (overwrite previous unfinished message)
85  else if (!receivingSysEx(CN)) { // If we haven't received a SysExStart
86  DEBUGFN(F("Error: No SysExStart received"));
87  return MIDIReadEvent::NO_MESSAGE; // ignore the data
88  }
89  if ( // add two data bytes to buffer
90  addSysExByte(CN, packet[1]) && addSysExByte(CN, SysExEnd)) {
91  endSysEx(CN);
92  return MIDIReadEvent::SYSEX_MESSAGE;
93  } else
94  return MIDIReadEvent::NO_MESSAGE; // Buffer full, ignore message
95  }
96 
97  // --------------- SysEx ends with following three bytes ---------------- //
98 
99  else if (CIN == MIDICodeIndexNumber::SYSEX_END_3B) {
100  // SysEx ends with following three bytes
101  if (packet[1] == SysExStart)
102  startSysEx(CN); // start a new message
103  // (overwrite previous unfinished message)
104  else if (!receivingSysEx(CN)) { // If we haven't received a SysExStart
105  DEBUGFN(F("Error: No SysExStart received"));
106  return MIDIReadEvent::NO_MESSAGE; // ignore the data
107  }
108  if ( // add three data bytes to buffer
109  addSysExByte(CN, packet[1]) && //
110  addSysExByte(CN, packet[2]) && //
111  addSysExByte(CN, SysExEnd)) {
112  endSysEx(CN);
113  return MIDIReadEvent::SYSEX_MESSAGE;
114  } else {
115  return MIDIReadEvent::NO_MESSAGE; // Buffer full, ignore message
116  }
117  }
118 #endif // IGNORE_SYSEX
119 
120  else if (CIN == MIDICodeIndexNumber::MISC_FUNCTION_CODES) {
121  // Miscellaneous function codes. Reserved for future extensions.
122  // (not implemented)
123  } else if (CIN == MIDICodeIndexNumber::CABLE_EVENTS) {
124  // Cable events. Reserved for future expansion.
125  // (not implemented)
126  } else if (CIN == MIDICodeIndexNumber::SYSTEM_COMMON_2B) {
127  // Two-byte System Common message
128  // (not implemented)
129  } else if (CIN == MIDICodeIndexNumber::SYSTEM_COMMON_3B) {
130  // Three-byte System Common message
131  // (not implemented)
132  }
133 
134  // ---------------------- Single byte (real-time) ----------------------- //
135 
136  else if (CIN == MIDICodeIndexNumber::SINGLE_BYTE) {
137  // Single Byte
138  rtmsg.message = packet[1];
139  rtmsg.CN = CN;
141  }
142 
143  return MIDIReadEvent::NO_MESSAGE;
144 }
145 
USBMIDI_Parser::startSysEx
void startSysEx(uint8_t CN)
Definition: USBMIDI_Parser.hpp:32
MIDICodeIndexNumber::MISC_FUNCTION_CODES
@ MISC_FUNCTION_CODES
USBMIDI_Parser::parse
MIDIReadEvent parse(uint8_t *packet)
Definition: USBMIDI_Parser.cpp:7
USBMIDI_Parser::endSysEx
void endSysEx(uint8_t CN)
Definition: USBMIDI_Parser.hpp:33
DEBUGREF
#define DEBUGREF(x)
Print an expression and its location (file and line number) to the debug output if debugging is enabl...
Definition: Debug.hpp:84
ChannelMessage::CN
uint8_t CN
USB MIDI cable number;.
Definition: MIDI_MessageTypes.hpp:87
AH::dec
Print & dec(Print &printer)
Definition: PrintStream.cpp:77
BEGIN_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
Definition: Settings/NamespaceSettings.hpp:9
USBMIDI_Parser::receivingSysEx
bool receivingSysEx(uint8_t CN) const
Definition: USBMIDI_Parser.hpp:40
ChannelMessage::header
uint8_t header
MIDI status byte (message type and channel).
Definition: MIDI_MessageTypes.hpp:83
ChannelMessage::data2
uint8_t data2
First MIDI data byte.
Definition: MIDI_MessageTypes.hpp:85
USBMIDI_Parser.hpp
MIDI_Parser::rtmsg
RealTimeMessage rtmsg
Definition: MIDI_Parser.hpp:41
END_CS_NAMESPACE
#define END_CS_NAMESPACE
Definition: Settings/NamespaceSettings.hpp:10
USBMIDI_Parser::addSysExByte
bool addSysExByte(uint8_t CN, uint8_t data)
Definition: USBMIDI_Parser.hpp:37
MIDICodeIndexNumber
MIDICodeIndexNumber
See table 4-1 in https://usb.org/sites/default/files/midi10.pdf.
Definition: MIDI_MessageTypes.hpp:48
MIDIReadEvent::NO_MESSAGE
@ NO_MESSAGE
No new incoming methods.
AH::hex
Print & hex(Print &printer)
Definition: PrintStream.cpp:62
MIDI_Parser::midimsg
ChannelMessage midimsg
Definition: MIDI_Parser.hpp:40
ChannelMessage::data1
uint8_t data1
First MIDI data byte.
Definition: MIDI_MessageTypes.hpp:84
MIDI_Notes::F
constexpr int8_t F
Definition: Notes.hpp:23
DEBUGFN
#define DEBUGFN(x)
Print an expression and its function (function name and line number) to the debug output if debugging...
Definition: Debug.hpp:93
RealTimeMessage::CN
uint8_t CN
Definition: MIDI_MessageTypes.hpp:190
MIDIReadEvent
MIDIReadEvent
Result of the MIDI interface read methods.
Definition: MIDI_Parser.hpp:15
DEBUG
#define DEBUG(x)
Print an expression to the debug output if debugging is enabled.
Definition: Debug.hpp:75
RealTimeMessage::message
uint8_t message
Definition: MIDI_MessageTypes.hpp:189