Control Surface main
MIDI Control Surface library for Arduino
Loading...
Searching...
No Matches
USBMIDI_Sender.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <Settings/SettingsWrapper.hpp>
5
7
13 public:
15 template <class Send>
16 void sendChannelMessage(ChannelMessage, Send &&send);
17
19 template <class Send>
20 void sendSysCommonMessage(SysCommonMessage, Send &&send);
21
23 template <class Send>
24 void sendRealTimeMessage(RealTimeMessage, Send &&send);
25
29 template <class Send>
30 void sendSysEx(SysExMessage, Send &&send);
31
34 template <class Send>
35 void sendFullSysEx(SysExMessage, Send &&send);
36
37#if !NO_SYSEX_OUTPUT
38 private:
41 template <class Send>
42 void sendSysExStartCont1(const uint8_t *data, Cable cable, Send &send);
49 template <class Send>
50 void sendSysExStartCont(const uint8_t *&data, uint16_t &length, Cable cable,
51 Send &send);
54 template <class Send>
55 void sendSysExEnd(const uint8_t *data, uint16_t length, Cable cable,
56 Send &send);
57
58 private:
64 uint8_t storedSysExData[16][3];
66 uint8_t storedSysExLength[16] = {};
67#endif
68
69 private:
71};
72
73template <class Send>
75 send(msg.cable, CIN(msg.header >> 4), // CN|CIN
76 msg.header, // status
77 msg.data1, // data 1
78 msg.data2); // data 2
79}
80
81template <class Send>
83 auto cn = msg.cable;
84 switch (msg.getNumberOfDataBytes()) {
85 case 2: // 3B
86 send(cn, CIN::SystemCommon3B, msg.header, msg.data1, msg.data2);
87 break;
88 case 1: // 2B
89 send(cn, CIN::SystemCommon2B, msg.header, msg.data1, 0);
90 break;
91 case 0: // 1B
92 send(cn, CIN::SystemCommon1B, msg.header, 0, 0);
93 break;
94 default: break;
95 }
96}
97
98template <class Send>
100 send(msg.cable, CIN::SingleByte, msg.message, 0, 0);
101}
102
103// This is the readable documentation version for sending full SysEx messages,
104// the chunked version below is more complicated, but the principle is similar.
105template <class Send>
107 size_t length = msg.length;
108 const uint8_t *data = msg.data;
109 Cable cn = msg.cable;
110 while (length > 3) {
111 send(cn, CIN::SysExStartCont, data[0], data[1], data[2]);
112 data += 3;
113 length -= 3;
114 }
115 switch (length) {
116 case 3: send(cn, CIN::SysExEnd3B, data[0], data[1], data[2]); break;
117 case 2: send(cn, CIN::SysExEnd2B, data[0], data[1], 0); break;
118 case 1: send(cn, CIN::SysExEnd1B, data[0], 0, 0); break;
119 default: break;
120 }
121}
122
123#if !NO_SYSEX_OUTPUT
124
125template <class Send>
126void USBMIDI_Sender::sendSysExStartCont1(const uint8_t *data, Cable cable,
127 Send &send) {
128 send(cable, CIN::SysExStartCont, data[0], data[1], data[2]);
129}
130
131template <class Send>
132void USBMIDI_Sender::sendSysExStartCont(const uint8_t *&data, uint16_t &length,
133 Cable cable, Send &send) {
134 while (length > 3) {
135 sendSysExStartCont1(data, cable, send);
136 data += 3;
137 length -= 3;
138 }
139}
140
141template <class Send>
142void USBMIDI_Sender::sendSysExEnd(const uint8_t *data, uint16_t length,
143 Cable cn, Send &send) {
144 switch (length) {
145 case 3: send(cn, CIN::SysExEnd3B, data[0], data[1], data[2]); break;
146 case 2: send(cn, CIN::SysExEnd2B, data[0], data[1], 0); break;
147 case 1: send(cn, CIN::SysExEnd1B, data[0], 0, 0); break;
148 default: break; // LCOV_EXCL_LINE
149 }
150}
151
152template <class Send>
153void USBMIDI_Sender::sendSysEx(const SysExMessage msg, Send &&send) {
154 // Don't bother trying to send empty messages
155 if (msg.length == 0)
156 return;
157
158 Cable cable = msg.cable;
159 uint8_t c = cable.getRaw();
160 uint16_t length = msg.length;
161 const uint8_t *data = msg.data;
162
163 // Even if the previous chunk wasn't terminated correctly, if this is a new
164 // SysEx message, always forget the previous unsent chunk.
165 if (msg.isFirstChunk()) {
166 // TODO: send a SysExEnd for previous chunk?
167 storedSysExLength[c] = 0;
168 }
169
170 // Complete the previous unsent chunk to (at most) 3 bytes
171 while (length > 0 && storedSysExLength[c] < 3) {
172 storedSysExData[c][storedSysExLength[c]++] = *data++;
173 --length;
174 }
175
176 // If all bytes of the new chunk were used, there are <= 3 stored bytes
177 if (length == 0) {
178 // If this chunk is the last one, terminate the SysEx (termination can
179 // have 1, 2 or 3 bytes, so no need to store anything)
180 if (msg.isLastChunk()) {
182 storedSysExLength[c] = 0;
183 }
184 // If it's the end of the chunk but not the end of the SysEx, and if we
185 // have exactly 3 bytes left, we can send them immediately
186 else if (storedSysExLength[c] == 3) {
187 sendSysExStartCont1(storedSysExData[c], cable, send);
188 storedSysExLength[c] = 0;
189 }
190 // If we have less than 3 bytes, we cannot send them now, we have to
191 // store them and wait for the next chunk
192 }
193 // If there are new bytes left in the chunk, there are exactly 3 stored
194 // bytes
195 else {
196 // First send the 3 stored bytes
197 sendSysExStartCont1(storedSysExData[c], cable, send);
198 // Then send whatever new data is left in the new chunk (but leave at
199 // least 3 bytes)
200 sendSysExStartCont(data, length, cable, send);
201 // If this chunk is the last one, terminate the SysEx (termination can
202 // have 1, 2 or 3 bytes, so no need to store anything)
203 if (msg.isLastChunk()) {
204 sendSysExEnd(data, length, cable, send);
205 storedSysExLength[c] = 0;
206 }
207 // If it's the end of the chunk but not the end of the SysEx, and if we
208 // have exactly 3 bytes left, we can send them immediately
209 else if (length == 3) {
210 sendSysExStartCont1(data, cable, send);
211 storedSysExLength[c] = 0;
212 }
213 // If we have 1 or 2 bytes left, we cannot send them now, so store them
214 // until we get enough data to fill a 3-byte packet
215 else {
216 storedSysExLength[c] = length;
217 memcpy(storedSysExData[c], data, length);
218 }
219 }
220}
221
222#else
223
224template <class Send>
226
227#endif
228
MIDICodeIndexNumber
MIDI USB Code Index Numbers.
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
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 sendFullSysEx(SysExMessage, Send &&send)
Send a MIDI System Exclusive message using the given sender.
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