Line data Source code
1 : #pragma once
2 :
3 : #include <AH/Settings/Warnings.hpp>
4 : #include <Settings/NamespaceSettings.hpp>
5 : #include <stddef.h> // size_t
6 : #include <stdint.h> // uint8_t
7 :
8 : #ifndef ARDUINO
9 : #include <vector>
10 : #endif
11 :
12 : AH_DIAGNOSTIC_WERROR()
13 :
14 : #include <Def/Channel.hpp>
15 : #include <Def/Cable.hpp>
16 :
17 : BEGIN_CS_NAMESPACE
18 :
19 : // -------------------------------------------------------------------------- //
20 :
21 : enum class MIDIMessageType : uint8_t {
22 : NOTE_OFF = 0x80, // 3B
23 : NOTE_ON = 0x90, // 3B
24 : KEY_PRESSURE = 0xA0, // 3B
25 : CC = 0xB0, // 3B
26 : CONTROL_CHANGE = CC, // 3B
27 : PROGRAM_CHANGE = 0xC0, // 2B
28 : CHANNEL_PRESSURE = 0xD0, // 2B
29 : PITCH_BEND = 0xE0, // 3B
30 :
31 : SYSEX_START = 0xF0,
32 : SYSEX_END = 0xF7,
33 :
34 : TUNE_REQUEST = 0xF6,
35 :
36 : /* System Real-Time messages */
37 : TIMING_CLOCK = 0xF8,
38 : UNDEFINED_REALTIME_1 = 0xF9,
39 : START = 0xFA,
40 : CONTINUE = 0xFB,
41 : STOP = 0xFC,
42 : UNDEFINED_REALTIME_2 = 0xFD,
43 : ACTIVE_SENSING = 0xFE,
44 : RESET = 0xFF,
45 : };
46 :
47 : /// See table 4-1 in <https://usb.org/sites/default/files/midi10.pdf>.
48 : enum class MIDICodeIndexNumber : uint8_t {
49 : MISC_FUNCTION_CODES = 0x0,
50 : CABLE_EVENTS = 0x1,
51 : SYSTEM_COMMON_2B = 0x2,
52 : SYSTEM_COMMON_3B = 0x3,
53 : SYSEX_START_CONT = 0x4,
54 : SYSTEM_COMMON_1B = 0x5,
55 : SYSEX_END_1B = 0x5,
56 : SYSEX_END_2B = 0x6,
57 : SYSEX_END_3B = 0x7,
58 :
59 : NOTE_OFF = 0x8,
60 : NOTE_ON = 0x9,
61 : KEY_PRESSURE = 0xA,
62 : CONTROL_CHANGE = 0xB,
63 : PROGRAM_CHANGE = 0xC,
64 : CHANNEL_PRESSURE = 0xD,
65 : PITCH_BEND = 0xE,
66 :
67 : SINGLE_BYTE = 0xF,
68 : };
69 :
70 : // -------------------------------------------------------------------------- //
71 :
72 : struct ChannelMessage {
73 : /// Constructor.
74 221 : ChannelMessage(uint8_t header, uint8_t data1, uint8_t data2, uint8_t CN)
75 221 : : header(header), data1(data1), data2(data2), CN(CN) {}
76 :
77 : /// Constructor.
78 : ChannelMessage(MIDIMessageType type, Channel channel, uint8_t data1,
79 : uint8_t data2 = 0x00, Cable cable = CABLE_1)
80 : : ChannelMessage(uint8_t(type) | channel.getRaw(), data1, data2,
81 : cable.getRaw()) {}
82 :
83 : uint8_t header; ///< MIDI status byte (message type and channel).
84 : uint8_t data1; ///< First MIDI data byte
85 : uint8_t data2; ///< First MIDI data byte
86 :
87 : uint8_t CN; ///< USB MIDI cable number;
88 :
89 : /// Check for equality.
90 25 : bool operator==(ChannelMessage other) const {
91 50 : return this->header == other.header && this->data1 == other.data1 &&
92 25 : this->data2 == other.data2 && this->CN == other.CN;
93 : }
94 : /// Check for inequality.
95 : bool operator!=(ChannelMessage other) const { return !(*this == other); }
96 :
97 : /// Get the MIDI channel of the message.
98 2 : Channel getChannel() const { return Channel(header & 0x0F); }
99 : /// Set the MIDI channel of the message.
100 1 : void setChannel(Channel channel) {
101 1 : header &= 0xF0;
102 1 : header |= channel.getRaw();
103 1 : }
104 :
105 : /// Get the MIDI USB cable number of the message.
106 : Cable getCable() const { return Cable(CN); }
107 : /// Set the MIDI USB cable number of the message.
108 : void setCable(Cable cable) { CN = cable.getRaw(); }
109 :
110 : /// Get the MIDI message type.
111 773 : MIDIMessageType getMessageType() const {
112 773 : return static_cast<MIDIMessageType>(header & 0xF0);
113 : }
114 : /// Set the MIDI message type.
115 : void setMessageType(MIDIMessageType type) {
116 : header &= 0x0F;
117 : header |= static_cast<uint8_t>(type) & 0xF0;
118 : }
119 :
120 : /// Check whether this message has one or two data bytes.
121 : ///
122 : /// - 2 data bytes: Note On/Off, Aftertouch, Control Change or Pitch Bend
123 : /// - 1 data byte: Program Change or Channel Pressure
124 451 : bool hasTwoDataBytes() const {
125 451 : auto type = getMessageType();
126 451 : return type <= MIDIMessageType::CONTROL_CHANGE ||
127 332 : type == MIDIMessageType::PITCH_BEND;
128 451 : }
129 :
130 : /// Check whether the header is a valid header for a channel message.
131 322 : bool hasValidHeader() const {
132 322 : auto type = getMessageType();
133 644 : return type >= MIDIMessageType::NOTE_OFF &&
134 322 : type <= MIDIMessageType::PITCH_BEND;
135 322 : }
136 : };
137 :
138 : struct SysExMessage {
139 : /// Constructor.
140 3 : SysExMessage() : data(nullptr), length(0), CN(0) {}
141 :
142 : /// Constructor.
143 97 : SysExMessage(const uint8_t *data, size_t length, uint8_t CN)
144 97 : : data(data), length(length), CN(CN) {}
145 :
146 : /// Constructor.
147 24 : SysExMessage(const uint8_t *data, size_t length, Cable cable = CABLE_1)
148 24 : : data(data), length(length), CN(cable.getRaw()) {}
149 :
150 : #ifndef ARDUINO
151 : /// Constructor.
152 12 : SysExMessage(const std::vector<uint8_t> &vec, Cable cable = CABLE_1)
153 12 : : SysExMessage(vec.data(), vec.size(), cable) {}
154 : #endif
155 :
156 : const uint8_t *data;
157 : uint8_t length;
158 : uint8_t CN;
159 :
160 7 : bool operator==(SysExMessage other) const {
161 14 : return this->length == other.length &&
162 7 : this->data == other.data && // TODO: compare contents or pointer?
163 7 : this->CN == other.CN;
164 : }
165 : bool operator!=(SysExMessage other) const { return !(*this == other); }
166 :
167 : /// Get the MIDI USB cable number of the message.
168 : Cable getCable() const { return Cable(CN); }
169 : /// Set the MIDI USB cable number of the message.
170 : void setCable(Cable cable) { CN = cable.getRaw(); }
171 : };
172 :
173 : struct RealTimeMessage {
174 : /// Constructor.
175 105 : RealTimeMessage(uint8_t message, uint8_t cn) : message(message), CN(cn) {}
176 :
177 : /// Constructor.
178 8 : RealTimeMessage(MIDIMessageType message, uint8_t cn)
179 8 : : message(uint8_t(message)), CN(cn) {}
180 :
181 : /// Constructor.
182 : RealTimeMessage(uint8_t message, Cable cable = CABLE_1)
183 : : message(message), CN(cable.getRaw()) {}
184 :
185 : /// Constructor.
186 : RealTimeMessage(MIDIMessageType message, Cable cable = CABLE_1)
187 : : message(uint8_t(message)), CN(cable.getRaw()) {}
188 :
189 : uint8_t message;
190 : uint8_t CN;
191 :
192 27 : bool operator==(RealTimeMessage other) const {
193 27 : return this->message == other.message && this->CN == other.CN;
194 : }
195 : bool operator!=(RealTimeMessage other) const { return !(*this == other); }
196 :
197 : /// Get the MIDI USB cable number of the message.
198 : Cable getCable() const { return Cable(CN); }
199 : /// Set the MIDI USB cable number of the message.
200 : void setCable(Cable cable) { CN = cable.getRaw(); }
201 : };
202 :
203 : END_CS_NAMESPACE
204 :
205 : AH_DIAGNOSTIC_POP()
|