Control Surface new-input
MIDI Control Surface library for Arduino
MIDI_MessageTypes.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <AH/Arduino-Wrapper.h> // Print
4 #include <AH/STL/cstddef> // size_t
5 #include <AH/STL/vector>
7 #include <Settings/NamespaceSettings.hpp>
8 
9 #ifndef ARDUINO
10 #include <iostream>
11 #endif
12 
14 
15 #include <Def/Cable.hpp>
16 #include <Def/Channel.hpp>
17 #include <Def/MIDIAddress.hpp>
18 
20 
21 // -------------------------------------------------------------------------- //
22 
24 enum class MIDIMessageType : uint8_t {
25  NONE = 0x00,
26  /* Channel Voice Messages */
27  NOTE_OFF = 0x80, // 3B
28  NOTE_ON = 0x90, // 3B
29  KEY_PRESSURE = 0xA0, // 3B
30  CC = 0xB0, // 3B
31  CONTROL_CHANGE = CC, // 3B
32  PROGRAM_CHANGE = 0xC0, // 2B
33  CHANNEL_PRESSURE = 0xD0, // 2B
34  PITCH_BEND = 0xE0, // 3B
35 
36  SYSEX_START = 0xF0,
37 
38  /* System Common messages */
39  MTC_QUARTER_FRAME = 0xF1,
40  SONG_POSITION_POINTER = 0xF2,
41  SONG_SELECT = 0xF3,
42  UNDEFINED_SYSCOMMON_1 = 0xF4,
43  UNDEFINED_SYSCOMMON_2 = 0xF5,
44  TUNE_REQUEST = 0xF6,
45  SYSEX_END = 0xF7,
46 
47  /* System Real-Time messages */
48  TIMING_CLOCK = 0xF8,
49  UNDEFINED_REALTIME_1 = 0xF9,
50  START = 0xFA,
51  CONTINUE = 0xFB,
52  STOP = 0xFC,
53  UNDEFINED_REALTIME_2 = 0xFD,
54  ACTIVE_SENSING = 0xFE,
55  SYSTEM_RESET = 0xFF,
56 };
57 
61 enum class MIDICodeIndexNumber : uint8_t {
62  MISC_FUNCTION_CODES = 0x0,
63  CABLE_EVENTS = 0x1,
64  SYSTEM_COMMON_2B = 0x2,
65  SYSTEM_COMMON_3B = 0x3,
66  SYSEX_START_CONT = 0x4,
67  SYSTEM_COMMON_1B = 0x5,
68  SYSEX_END_1B = 0x5,
69  SYSEX_END_2B = 0x6,
70  SYSEX_END_3B = 0x7,
71 
72  NOTE_OFF = 0x8,
73  NOTE_ON = 0x9,
74  KEY_PRESSURE = 0xA,
75  CONTROL_CHANGE = 0xB,
76  PROGRAM_CHANGE = 0xC,
77  CHANNEL_PRESSURE = 0xD,
78  PITCH_BEND = 0xE,
79 
80  SINGLE_BYTE = 0xF,
81 };
82 
83 // -------------------------------------------------------------------------- //
84 
85 struct MIDIMessage {
87  MIDIMessage(uint8_t header, uint8_t data1, uint8_t data2,
90 
94  : header(uint8_t(header)), data1(data1), data2(data2), cable(cable) {}
95 
96  uint8_t header;
97  uint8_t data1;
98  uint8_t data2;
99 
101 
103  bool operator==(MIDIMessage other) const {
104  return this->header == other.header && this->data1 == other.data1 &&
105  this->data2 == other.data2 && this->cable == other.cable;
106  }
108  bool operator!=(MIDIMessage other) const { return !(*this == other); }
109 
113  return static_cast<MIDIMessageType>(header & 0xF0);
114  } else {
115  return static_cast<MIDIMessageType>(header);
116  }
117  }
122  header = static_cast<uint8_t>(type);
123  }
124 
126  uint8_t getData1() const { return data1; }
128  uint8_t getData2() const { return data2; }
130  void setData1(uint8_t data) { data1 = data; }
132  void setData2(uint8_t data) { data2 = data; }
133 
135  Cable getCable() const { return cable; }
137  void setCable(Cable cable) { this->cable = cable; }
138 
141  return header >= (uint8_t(MIDIMessageType::NOTE_OFF) | 0x00) &&
142  header <= (uint8_t(MIDIMessageType::PITCH_BEND) | 0x0F);
143  }
144 
151  return (header & 0xF8) == 0xF0 && header != 0xF0;
152  }
153 
156  uint16_t getData14bit() const {
157  return data1 | (uint16_t(data2) << uint16_t(7));
158  }
161  void setData14bit(uint16_t data) {
162  data1 = (data >> 0) & 0x7F;
163  data2 = (data >> 7) & 0x7F;
164  }
165 
168  void sanitize() {
169  header |= 0x80;
170  data1 &= 0x7F;
171  data2 &= 0x7F;
172  }
173 };
174 
177 
179  ChannelMessage(MIDIMessageType type, Channel channel, uint8_t data1,
180  uint8_t data2 = 0x00, Cable cable = CABLE_1)
181  : MIDIMessage(uint8_t(type) | channel.getRaw(), data1, data2, cable) {}
182 
183  explicit ChannelMessage(const MIDIMessage &msg) : MIDIMessage(msg) {}
184 
187  return static_cast<MIDIMessageType>(header & 0xF0);
188  }
191  header &= 0x0F;
192  header |= static_cast<uint8_t>(type) & 0xF0;
193  }
194 
196  Channel getChannel() const { return Channel(header & 0x0F); }
198  void setChannel(Channel channel) {
199  header &= 0xF0;
200  header |= channel.getRaw();
201  }
202 
210  MIDIAddress getAddress() const { return {data1, getChannelCable()}; }
211 
218  bool hasTwoDataBytes() const {
219  auto type = getMessageType();
220  return type <= MIDIMessageType::CONTROL_CHANGE ||
222  }
223 
224  constexpr static auto NOTE_OFF = MIDIMessageType::NOTE_OFF;
225  constexpr static auto NOTE_ON = MIDIMessageType::NOTE_ON;
227  constexpr static auto CC = MIDIMessageType::CC;
231  constexpr static auto PITCH_BEND = MIDIMessageType::PITCH_BEND;
232 };
233 
236 
238  SysCommonMessage(MIDIMessageType type, uint8_t data1 = 0x00,
239  uint8_t data2 = 0x00, Cable cable = CABLE_1)
240  : MIDIMessage(type, data1, data2, cable) {}
243  : SysCommonMessage(type, data1, 0x00, cable) {}
246  : SysCommonMessage(type, 0x00, 0x00, cable) {}
247 
248  explicit SysCommonMessage(const MIDIMessage &msg) : MIDIMessage(msg) {}
249 
252  return static_cast<MIDIMessageType>(header);
253  }
254 
256  uint8_t getNumberOfDataBytes() const {
258  return 2;
260  return 1;
261  else
262  return 0;
263  }
264 
265  constexpr static auto MTC_QUARTER_FRAME =
267  constexpr static auto SONG_POSITION_POINTER =
269  constexpr static auto SONG_SELECT = MIDIMessageType::SONG_SELECT;
270  constexpr static auto UNDEFINED_SYSCOMMON_1 =
272  constexpr static auto UNDEFINED_SYSCOMMON_2 =
275 };
276 
277 struct SysExMessage {
279  SysExMessage() : data(nullptr), length(0), cable(CABLE_1) {}
280 
282  SysExMessage(const uint8_t *data, uint16_t length, Cable cable = CABLE_1)
283  : data(data), length(length), cable(cable.getRaw()) {}
284 
286  SysExMessage(const std::vector<uint8_t> &vec, Cable cable = CABLE_1)
287  : SysExMessage(vec.data(), vec.size(), cable) {}
288 
290  template <uint16_t N>
291  SysExMessage(const uint8_t (&array)[N], Cable cable = CABLE_1)
292  : SysExMessage(array, N, cable) {}
293 
294  const uint8_t *data;
295  uint16_t length;
296 
298 
299  bool operator==(SysExMessage other) const {
300  return this->length == other.length && this->cable == other.cable &&
301  (this->length == 0 ||
302  memcmp(this->data, other.data, length) == 0);
303  }
304  bool operator!=(SysExMessage other) const { return !(*this == other); }
305 
307  Cable getCable() const { return cable; }
309  void setCable(Cable cable) { this->cable = cable; }
310 
311  bool isFirstChunk() const {
312  return length >= 1 && data[0] == uint8_t(MIDIMessageType::SYSEX_START);
313  }
314 
315  bool isLastChunk() const {
316  return length >= 1 &&
317  data[length - 1] == uint8_t(MIDIMessageType::SYSEX_END);
318  }
319 
320  bool isCompleteMessage() const { return isFirstChunk() && isLastChunk(); }
321 
322  constexpr static auto SYSEX_START = MIDIMessageType::SYSEX_START;
323  constexpr static auto SYSEX_END = MIDIMessageType::SYSEX_END;
324 };
325 
329  : message(message), cable(cable.getRaw()) {}
330 
333  : message(uint8_t(message)), cable(cable.getRaw()) {}
334 
335  uint8_t message;
337 
338  bool operator==(RealTimeMessage other) const {
339  return this->message == other.message && this->cable == other.cable;
340  }
341  bool operator!=(RealTimeMessage other) const { return !(*this == other); }
342 
345  message = static_cast<uint8_t>(type);
346  }
349  return static_cast<MIDIMessageType>(message);
350  }
351 
353  Cable getCable() const { return cable; }
355  void setCable(Cable cable) { this->cable = cable; }
356 
358  bool isValid() const { return message >= 0xF8; }
359 
361  constexpr static auto UNDEFINED_REALTIME_1 =
363  constexpr static auto START = MIDIMessageType::START;
364  constexpr static auto CONTINUE = MIDIMessageType::CONTINUE;
365  constexpr static auto STOP = MIDIMessageType::STOP;
366  constexpr static auto UNDEFINED_REALTIME_2 =
369  constexpr static auto RESET = MIDIMessageType::SYSTEM_RESET;
370 };
371 
372 #ifndef ARDUINO
373 inline std::ostream &operator<<(std::ostream &os, SysExMessage m) {
374  os << "SysExMessage [" << m.length << "] " << AH::HexDump(m.data, m.length)
375  << " (cable " << m.cable.getOneBased() << ")";
376  return os;
377 }
378 #endif
379 
380 inline Print &operator<<(Print &os, SysExMessage m) {
381  os << "SysExMessage [" << m.length << "] " << AH::HexDump(m.data, m.length)
382  << " (cable " << m.cable.getOneBased() << ")";
383  return os;
384 }
385 
387 inline Print &operator<<(Print &os, MIDIMessageType m) {
388  return os << enum_to_string(m);
389 }
390 
392 
std::remove_reference< decltype(*F(""))>::type * FlashString_t
constexpr Cable CABLE_1
Definition: Cable.hpp:118
MIDIMessageType
All possible MIDI status byte values (without channel).
MIDICodeIndexNumber
MIDI USB Code Index Numbers.
FlashString_t enum_to_string(MIDIMessageType)
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
#define AH_DIAGNOSTIC_POP()
Definition: Warnings.hpp:36
#define AH_DIAGNOSTIC_WERROR()
Definition: Warnings.hpp:35
A type-safe class for MIDI USB Cable numbers.
Definition: Cable.hpp:13
constexpr uint8_t getOneBased() const
Get the cable as an integer.
Definition: Cable.hpp:36
A type-safe class for MIDI channels.
Definition: Channel.hpp:13
constexpr uint8_t getRaw() const
Get the channel as an integer.
Definition: Channel.hpp:29
A type-safe utility class for saving a MIDI address consisting of a 7-bit address,...
A class for saving a MIDI channel and cable number.
Definition: MIDIAddress.hpp:24
Print & operator<<(Print &os, Quaternion e)
Printing.
Definition: Quaternion.cpp:28
constexpr static auto PROGRAM_CHANGE
constexpr static auto CONTROL_CHANGE
constexpr static auto CC
constexpr static auto NOTE_OFF
void setChannel(Channel channel)
Set the MIDI channel of the message.
MIDIChannelCable getChannelCable() const
Get the MIDI channel and cable number.
constexpr static auto KEY_PRESSURE
ChannelMessage(MIDIMessageType type, Channel channel, uint8_t data1, uint8_t data2=0x00, Cable cable=CABLE_1)
Constructor.
void setMessageType(MIDIMessageType type)
Set the MIDI message type.
MIDIAddress getAddress() const
Get the MIDI address of this message, using data1 as the address.
constexpr static auto CHANNEL_PRESSURE
MIDIMessageType getMessageType() const
Get the MIDI message type.
ChannelMessage(const MIDIMessage &msg)
constexpr static auto PITCH_BEND
Channel getChannel() const
Get the MIDI channel of the message.
constexpr static auto NOTE_ON
bool hasTwoDataBytes() const
Check whether this message has one or two data bytes.
void setCable(Cable cable)
Set the MIDI USB cable number of the message.
uint8_t data2
First MIDI data byte.
MIDIMessage(uint8_t header, uint8_t data1, uint8_t data2, Cable cable=CABLE_1)
Constructor.
bool hasValidChannelMessageHeader() const
Check whether the header is a valid header for a channel message.
uint8_t header
MIDI status byte (message type and channel).
void setData1(uint8_t data)
Set the first data byte.
bool operator==(MIDIMessage other) const
Check for equality.
void setData2(uint8_t data)
Set the second data byte.
Cable getCable() const
Get the MIDI USB cable number of the message.
void sanitize()
Make sure that the status byte has the most significant bit set and the data bytes have the most sign...
uint8_t getData1() const
Get the first data byte.
uint8_t data1
First MIDI data byte.
void setMessageType(MIDIMessageType type)
Set the MIDI message type.
MIDIMessageType getMessageType() const
Get the MIDI message type.
void setData14bit(uint16_t data)
If Data 1 and Data 2 represent a single 14-bit number, you can use this method to set that number.
Cable cable
USB MIDI cable number;.
uint16_t getData14bit() const
If Data 1 and Data 2 represent a single 14-bit number, you can use this method to retrieve that numbe...
uint8_t getData2() const
Get the second data byte.
bool hasValidSystemCommonHeader() const
Check whether the header is a valid header for a System Common message.
MIDIMessage(MIDIMessageType header, uint8_t data1, uint8_t data2, Cable cable=CABLE_1)
Constructor.
bool operator!=(MIDIMessage other) const
Check for inequality.
void setCable(Cable cable)
Set the MIDI USB cable number of the message.
RealTimeMessage(uint8_t message, Cable cable=CABLE_1)
Constructor.
RealTimeMessage(MIDIMessageType message, Cable cable=CABLE_1)
Constructor.
bool isValid() const
Check whether the header is a valid header for a Real-Time message.
bool operator!=(RealTimeMessage other) const
Cable getCable() const
Get the MIDI USB cable number of the message.
constexpr static auto RESET
constexpr static auto UNDEFINED_REALTIME_2
void setMessageType(MIDIMessageType type)
Set the MIDI message type.
constexpr static auto TIMING_CLOCK
MIDIMessageType getMessageType() const
Get the MIDI message type.
constexpr static auto UNDEFINED_REALTIME_1
constexpr static auto START
constexpr static auto ACTIVE_SENSING
constexpr static auto CONTINUE
bool operator==(RealTimeMessage other) const
constexpr static auto STOP
SysCommonMessage(MIDIMessageType type, uint8_t data1=0x00, uint8_t data2=0x00, Cable cable=CABLE_1)
Constructor.
uint8_t getNumberOfDataBytes() const
Get the number of data bytes of this type of System Common message.
constexpr static auto SONG_POSITION_POINTER
constexpr static auto TUNE_REQUEST
constexpr static auto UNDEFINED_SYSCOMMON_1
constexpr static auto MTC_QUARTER_FRAME
SysCommonMessage(MIDIMessageType type, Cable cable)
Constructor.
SysCommonMessage(const MIDIMessage &msg)
constexpr static auto SONG_SELECT
MIDIMessageType getMessageType() const
Get the MIDI message type.
constexpr static auto UNDEFINED_SYSCOMMON_2
SysCommonMessage(MIDIMessageType type, uint8_t data1, Cable cable)
Constructor.
void setCable(Cable cable)
Set the MIDI USB cable number of the message.
SysExMessage(const uint8_t(&array)[N], Cable cable=CABLE_1)
Constructor.
SysExMessage()
Constructor.
constexpr static auto SYSEX_START
SysExMessage(const uint8_t *data, uint16_t length, Cable cable=CABLE_1)
Constructor.
SysExMessage(const std::vector< uint8_t > &vec, Cable cable=CABLE_1)
Constructor.
const uint8_t * data
bool operator!=(SysExMessage other) const
Cable getCable() const
Get the MIDI USB cable number of the message.
bool operator==(SysExMessage other) const
bool isCompleteMessage() const
bool isLastChunk() const
bool isFirstChunk() const
constexpr static auto SYSEX_END