Control Surface main
MIDI Control Surface library for Arduino
BLEMIDIPacketBuilder.cpp
Go to the documentation of this file.
2
4
5template <bool ThreeBytes>
6bool BLEMIDIPacketBuilder::addImpl(uint8_t header, uint8_t data1, uint8_t data2,
7 uint16_t timestamp) {
8 initBuffer(timestamp);
9
10 uint8_t timestampLSB = getTimestampLSB(timestamp);
11
12 // If the header is the same as the previous message, use running status
13 if (header == runningHeader) {
14 // If the timestamp is the same, no need to send it again
15 if (timestampLSB == runningTimestamp) {
16 if (!hasSpaceFor(1 + ThreeBytes))
17 return false; // Buffer full
18 buffer.push_back(data1);
19 if (ThreeBytes)
20 buffer.push_back(data2);
21 }
22 // Timestamp is different, send again
23 else {
24 if (!hasSpaceFor(2 + ThreeBytes))
25 return false; // Buffer full
26 runningTimestamp = timestampLSB;
27 buffer.push_back(timestampLSB);
28 buffer.push_back(data1);
29 if (ThreeBytes)
30 buffer.push_back(data2);
31 }
32 }
33 // If the header is different, running status is not possible, send all
34 else {
35 if (!hasSpaceFor(3 + ThreeBytes))
36 return false; // Buffer full
37 runningHeader = header;
38 runningTimestamp = timestampLSB;
39 buffer.push_back(timestampLSB);
40 buffer.push_back(header);
41 buffer.push_back(data1);
42 if (ThreeBytes)
43 buffer.push_back(data2);
44 }
45 return true;
46}
47
49 buffer.resize(0);
50 runningHeader = 0;
51}
52
53void BLEMIDIPacketBuilder::setCapacity(uint16_t capacity) {
54 if (capacity < 5)
55 ERROR(F("capacity less than 5 bytes"), 0x2005); // LCOV_EXCL_LINE
56 buffer.shrink_to_fit();
57 buffer.reserve(capacity);
58}
59
60bool BLEMIDIPacketBuilder::add3B(uint8_t header, uint8_t data1, uint8_t data2,
61 uint16_t timestamp) {
62 constexpr bool ThreeBytes = true;
63 return addImpl<ThreeBytes>(header, data1, data2, timestamp);
64}
65
66bool BLEMIDIPacketBuilder::add2B(uint8_t header, uint8_t data1,
67 uint16_t timestamp) {
68 constexpr bool ThreeBytes = false;
69 return addImpl<ThreeBytes>(header, data1, 0, timestamp);
70}
71
72bool BLEMIDIPacketBuilder::addRealTime(uint8_t rt, uint16_t timestamp) {
73 initBuffer(timestamp);
74
75 if (!hasSpaceFor(2))
76 return false; // Buffer full
77
78 buffer.push_back(getTimestampLSB(timestamp));
79 buffer.push_back(rt);
80 runningTimestamp = 0; // Re-send the timestamp next time
81
82 return true;
83}
84
85bool BLEMIDIPacketBuilder::addSysCommon(uint8_t num_data, uint8_t header,
86 uint8_t data1, uint8_t data2,
87 uint16_t timestamp) {
88 initBuffer(timestamp);
89
90 uint8_t timestampLSB = getTimestampLSB(timestamp);
91
92 if (!hasSpaceFor(2 + num_data))
93 return false; // Buffer full
94 buffer.push_back(timestampLSB);
95 buffer.push_back(header);
96 if (num_data >= 1)
97 buffer.push_back(data1);
98 if (num_data >= 2)
99 buffer.push_back(data2);
100 runningTimestamp = 0; // Re-send the timestamp next time
101
102 return true;
103}
104
105bool BLEMIDIPacketBuilder::addSysEx(const uint8_t *&data, size_t &length,
106 uint16_t timestamp) {
107 initBuffer(timestamp);
108
109 // We can't do anything with an empty message
110 if (length == 0)
111 return true;
112
113 // If the first byte is a SysExStart byte we first have to write a
114 // timestamp + SysExStart.
115 if (*data == SysExStart) {
116 // We need space for at least the timestamp and a SysExStart
117 if (!hasSpaceFor(2))
118 return false; // Buffer full
119
120 // Normal running status is interrupted by SysEx
121 runningHeader = 0;
122
123 const uint8_t timestampLSB = getTimestampLSB(timestamp);
124
125 // Start of SysEx
126 buffer.push_back(timestampLSB);
127 buffer.push_back(SysExStart);
128 ++data; // First byte was added
129 --length;
130 }
131
132 // Copy the rest of the data, and terminate the message if necessary
133 continueSysEx(data, length, timestamp);
134 return true;
135}
136
137void BLEMIDIPacketBuilder::continueSysEx(const uint8_t *&data, size_t &length,
138 uint16_t timestamp) {
139 initBuffer(timestamp);
140
141 if (length == 0) {
142 // Message was finished, no continuation
143 data = nullptr;
144 return;
145 }
146
147 // Copy as much data as possible, but stop before the last byte, which
148 // could be a SysExEnd (and should be handled differently than data bytes)
149 while (length-- > 1 && buffer.size() < buffer.capacity())
150 buffer.push_back(*data++);
151
152 // If everything fit into the buffer
153 if (length == 0) {
154 // End of SysEx
155 if (*data == SysExEnd) {
156 if (hasSpaceFor(2)) {
157 buffer.push_back(getTimestampLSB(timestamp));
158 buffer.push_back(SysExEnd);
159 // Message was finished, no continuation
160 data = nullptr;
161 } else {
162 // Send the SysExEnd byte next time
163 ++length;
164 }
165 }
166 // End of chunk but not end of SysEx
167 else {
168 if (hasSpaceFor(1)) {
169 buffer.push_back(*data);
170 // Message was finished, no continuation
171 data = nullptr;
172 } else {
173 // Send the last byte next time
174 ++length;
175 }
176 }
177 }
178 // If the while loop stopped because the buffer was full
179 else {
180 // data is not set to nullptr to let the caller know where to start
181 // sending the next continuation packet,
182 ++length;
183 }
184}
185
186constexpr const uint8_t BLEMIDIPacketBuilder::SysExStart;
187constexpr const uint8_t BLEMIDIPacketBuilder::SysExEnd;
188
#define ERROR(msg, errc)
Definition: Error.hpp:22
#define END_CS_NAMESPACE
#define BEGIN_CS_NAMESPACE
std::vector< uint8_t > buffer
void setCapacity(uint16_t capacity)
Set the maximum capacity of the buffer.
void continueSysEx(const uint8_t *&data, size_t &length, uint16_t timestamp)
Add a SysEx continuation to the packet.
static constexpr const uint8_t SysExEnd
bool addSysEx(const uint8_t *&data, size_t &length, uint16_t timestamp)
Try adding (part of) a SysEx message to the packet.
bool hasSpaceFor(size_t bytes) const
Check if the buffer has space left.
static constexpr uint8_t getTimestampLSB(uint16_t timestamp)
Timestamp[1]: 0b1lll llll.
bool add2B(uint8_t header, uint8_t data1, uint16_t timestamp)
Try adding a 2-byte MIDI channel voice message to the packet.
bool addSysCommon(uint8_t num_data, uint8_t header, uint8_t data1, uint8_t data2, uint16_t timestamp)
Try adding a MIDI system common message to the packet.
bool addRealTime(uint8_t rt, uint16_t timestamp)
Try adding a MIDI real-time message to the packet.
static constexpr const uint8_t SysExStart
void reset()
Reset the builder to start a new packet.
bool addImpl(uint8_t header, uint8_t data1, uint8_t data2, uint16_t timestamp)
Try adding a 2-byte or 3-byte MIDI channel voice message to the packet.
bool add3B(uint8_t header, uint8_t data1, uint8_t data2, uint16_t timestamp)
Try adding a 3-byte MIDI channel voice message to the packet.
void initBuffer(uint16_t timestamp)
If this is the first byte/message in the packet, add the header containing the 6 most significant bit...