Control Surface master
MIDI Control Surface library for Arduino
USBMIDI_Sender.hpp
Go to the documentation of this file.
1#pragma once
2
4
6
8
14 public:
16 template <class Send>
17 void sendChannelMessage(ChannelMessage, Send &&send);
18
20 template <class Send>
21 void sendSysCommonMessage(SysCommonMessage, Send &&send);
22
24 template <class Send>
25 void sendRealTimeMessage(RealTimeMessage, Send &&send);
26
30 template <class Send>
31 void sendSysEx(SysExMessage, Send &&send);
32
33 private:
36 template <class Send>
37 void sendSysExStartCont1(const uint8_t *data, Cable cable, Send &send);
44 template <class Send>
45 void sendSysExStartCont(const uint8_t *&data, uint16_t &length, Cable cable,
46 Send &send);
49 template <class Send>
50 void sendSysExEnd(const uint8_t *data, uint16_t length, Cable cable,
51 Send &send);
52
53 private:
59 uint8_t storedSysExData[16][3];
61 uint8_t storedSysExLength[16] = {};
62
64};
65
66template <class Send>
68 send(msg.cable, CIN(msg.header >> 4), // CN|CIN
69 msg.header, // status
70 msg.data1, // data 1
71 msg.data2); // data 2
72}
73
74template <class Send>
76 auto cn = msg.cable;
77 switch (msg.getNumberOfDataBytes()) {
78 case 2: // 3B
79 send(cn, CIN::SYSTEM_COMMON_3B, msg.header, msg.data1, msg.data2);
80 break;
81 case 1: // 2B
82 send(cn, CIN::SYSTEM_COMMON_2B, msg.header, msg.data1, 0);
83 break;
84 case 0: // 1B
85 send(cn, CIN::SYSTEM_COMMON_1B, msg.header, 0, 0);
86 break;
87 default: break;
88 }
89}
90
91template <class Send>
93 send(msg.cable, CIN::SINGLE_BYTE, msg.message, 0, 0);
94}
95
96// This is the readable documentation version for sending full SysEx messages
97#if 0
98template <class Send>
99void USBMIDI_Sender::sendFullSysEx(SysExMessage msg, Send &send) {
100 size_t length = msg.length;
101 const uint8_t *data = msg.data;
102 Cable cn = msg.cable;
103 while (length > 3) {
104 send(cn, CIN::SYSEX_START_CONT, data[0], data[1], data[2]);
105 data += 3;
106 length -= 3;
107 }
108 switch (length) {
109 case 3: send(cn, CIN::SYSEX_END_3B, data[0], data[1], data[2]); break;
110 case 2: send(cn, CIN::SYSEX_END_2B, data[0], data[1], 0); break;
111 case 1: send(cn, CIN::SYSEX_END_1B, data[0], 0, 0); break;
112 default: break;
113 }
114}
115#endif // 0
116
117template <class Send>
118void USBMIDI_Sender::sendSysExStartCont1(const uint8_t *data, Cable cable,
119 Send &send) {
120 send(cable, CIN::SYSEX_START_CONT, data[0], data[1], data[2]);
121}
122
123template <class Send>
124void USBMIDI_Sender::sendSysExStartCont(const uint8_t *&data, uint16_t &length,
125 Cable cable, Send &send) {
126 while (length > 3) {
127 sendSysExStartCont1(data, cable, send);
128 data += 3;
129 length -= 3;
130 }
131}
132
133template <class Send>
134void USBMIDI_Sender::sendSysExEnd(const uint8_t *data, uint16_t length,
135 Cable cn, Send &send) {
136 switch (length) {
137 case 3: send(cn, CIN::SYSEX_END_3B, data[0], data[1], data[2]); break;
138 case 2: send(cn, CIN::SYSEX_END_2B, data[0], data[1], 0); break;
139 case 1: send(cn, CIN::SYSEX_END_1B, data[0], 0, 0); break;
140 default: break; // LCOV_EXCL_LINE
141 }
142}
143
144template <class Send>
145void USBMIDI_Sender::sendSysEx(const SysExMessage msg, Send &&send) {
146 // Don't bother trying to send empty messages
147 if (msg.length == 0)
148 return;
149
150 Cable cable = msg.cable;
151 uint8_t c = cable.getRaw();
152 uint16_t length = msg.length;
153 const uint8_t *data = msg.data;
154
155 // Even if the previous chunk wasn't terminated correctly, if this is a new
156 // SysEx message, always forget the previous unsent chunk.
157 if (msg.isFirstChunk()) {
158 // TODO: send a SysExEnd for previous chunk?
159 storedSysExLength[c] = 0;
160 }
161
162 // Complete the previous unsent chunk to (at most) 3 bytes
163 while (length > 0 && storedSysExLength[c] < 3) {
164 storedSysExData[c][storedSysExLength[c]++] = *data++;
165 --length;
166 }
167
168 // If all bytes of the new chunk were used, there are <= 3 stored bytes
169 if (length == 0) {
170 // If this chunk is the last one, terminate the SysEx (termination can
171 // have 1, 2 or 3 bytes, so no need to store anything)
172 if (msg.isLastChunk()) {
174 storedSysExLength[c] = 0;
175 }
176 // If it's the end of the chunk but not the end of the SysEx, and if we
177 // have exactly 3 bytes left, we can send them immediately
178 else if (storedSysExLength[c] == 3) {
179 sendSysExStartCont1(storedSysExData[c], cable, send);
180 storedSysExLength[c] = 0;
181 }
182 // If we have less than 3 bytes, we cannot send them now, we have to
183 // store them and wait for the next chunk
184 }
185 // If there are new bytes left in the chunk, there are exactly 3 stored
186 // bytes
187 else {
188 // First send the 3 stored bytes
189 sendSysExStartCont1(storedSysExData[c], cable, send);
190 // Then send whatever new data is left in the new chunk (but leave at
191 // least 3 bytes)
192 sendSysExStartCont(data, length, cable, send);
193 // If this chunk is the last one, terminate the SysEx (termination can
194 // have 1, 2 or 3 bytes, so no need to store anything)
195 if (msg.isLastChunk()) {
196 sendSysExEnd(data, length, cable, send);
197 storedSysExLength[c] = 0;
198 }
199 // If it's the end of the chunk but not the end of the SysEx, and if we
200 // have exactly 3 bytes left, we can send them immediately
201 else if (length == 3) {
202 sendSysExStartCont1(data, cable, send);
203 storedSysExLength[c] = 0;
204 }
205 // If we have 1 or 2 bytes left, we cannot send them now, so store them
206 // until we get enough data to fill a 3-byte packet
207 else {
208 storedSysExLength[c] = length;
209 memcpy(storedSysExData[c], data, length);
210 }
211 }
212}
213
215
MIDICodeIndexNumber
MIDI USB Code Index Numbers.
#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 getRaw() const
Get the cable as an integer.
Definition: Cable.hpp:29
A class for sending MIDI USB messages.
void sendChannelMessage(ChannelMessage, Send &&send)
Send a MIDI Channel message using the given sender.
void sendSysEx(SysExMessage, Send &&send)
Send a MIDI System Exclusive message using the given sender.
void sendSysExEnd(const uint8_t *data, uint16_t length, Cable cable, Send &send)
Send a SysExEnd USB packet.
void sendSysExStartCont1(const uint8_t *data, Cable cable, Send &send)
Send a single SysEx starts or continues USB packet.
void sendSysCommonMessage(SysCommonMessage, Send &&send)
Send a MIDI System Common message using the given sender.
void sendSysExStartCont(const uint8_t *&data, uint16_t &length, Cable cable, Send &send)
Send as many SysEx starts or continues USB packets, such that the remaining length is 3,...
uint8_t storedSysExLength[16]
Number of remaining SysEx bytes stored.
void sendRealTimeMessage(RealTimeMessage, Send &&send)
Send a MIDI Real-Time message using the given sender.
uint8_t storedSysExData[16][3]
Stores remainder of outgoing SysEx chunks.
uint8_t data2
First MIDI data byte.
uint8_t header
MIDI status byte (message type and channel).
uint8_t data1
First MIDI data byte.
Cable cable
USB MIDI cable number;.
uint8_t getNumberOfDataBytes() const
Get the number of data bytes of this type of System Common message.
const uint8_t * data
bool isLastChunk() const
bool isFirstChunk() const