Line data Source code
1 : #include "BLEMIDIPacketBuilder.hpp"
2 :
3 : BEGIN_CS_NAMESPACE
4 :
5 : template <bool ThreeBytes>
6 56 : bool BLEMIDIPacketBuilder::addImpl(uint8_t header, uint8_t data1, uint8_t data2,
7 : uint16_t timestamp) {
8 56 : initBuffer(timestamp);
9 :
10 56 : uint8_t timestampLSB = getTimestampLSB(timestamp);
11 :
12 : // If the header is the same as the previous message, use running status
13 56 : if (header == runningHeader) {
14 : // If the timestamp is the same, no need to send it again
15 17 : if (timestampLSB == runningTimestamp) {
16 9 : if (!hasSpaceFor(1 + ThreeBytes))
17 1 : return false; // Buffer full
18 8 : buffer.push_back(data1);
19 : if (ThreeBytes)
20 8 : buffer.push_back(data2);
21 : }
22 : // Timestamp is different, send again
23 : else {
24 8 : if (!hasSpaceFor(2 + ThreeBytes))
25 1 : return false; // Buffer full
26 7 : runningTimestamp = timestampLSB;
27 7 : buffer.push_back(timestampLSB);
28 7 : buffer.push_back(data1);
29 : if (ThreeBytes)
30 7 : buffer.push_back(data2);
31 : }
32 : }
33 : // If the header is different, running status is not possible, send all
34 : else {
35 39 : if (!hasSpaceFor(3 + ThreeBytes))
36 3 : return false; // Buffer full
37 36 : runningHeader = header;
38 36 : runningTimestamp = timestampLSB;
39 36 : buffer.push_back(timestampLSB);
40 36 : buffer.push_back(header);
41 36 : buffer.push_back(data1);
42 : if (ThreeBytes)
43 32 : buffer.push_back(data2);
44 : }
45 51 : return true;
46 : }
47 :
48 34 : void BLEMIDIPacketBuilder::reset() {
49 34 : buffer.resize(0);
50 34 : runningHeader = 0;
51 34 : }
52 :
53 56 : void BLEMIDIPacketBuilder::setCapacity(uint16_t capacity) {
54 56 : if (capacity < 5)
55 : ERROR(F("capacity less than 5 bytes"), 0x2005); // LCOV_EXCL_LINE
56 56 : buffer.shrink_to_fit();
57 56 : buffer.reserve(capacity);
58 56 : }
59 :
60 51 : bool BLEMIDIPacketBuilder::add3B(uint8_t header, uint8_t data1, uint8_t data2,
61 : uint16_t timestamp) {
62 51 : constexpr bool ThreeBytes = true;
63 51 : return addImpl<ThreeBytes>(header, data1, data2, timestamp);
64 : }
65 :
66 5 : bool BLEMIDIPacketBuilder::add2B(uint8_t header, uint8_t data1,
67 : uint16_t timestamp) {
68 5 : constexpr bool ThreeBytes = false;
69 5 : return addImpl<ThreeBytes>(header, data1, 0, timestamp);
70 : }
71 :
72 10 : bool BLEMIDIPacketBuilder::addRealTime(uint8_t rt, uint16_t timestamp) {
73 10 : initBuffer(timestamp);
74 :
75 10 : if (!hasSpaceFor(2))
76 3 : return false; // Buffer full
77 :
78 7 : buffer.push_back(getTimestampLSB(timestamp));
79 7 : buffer.push_back(rt);
80 7 : runningTimestamp = 0; // Re-send the timestamp next time
81 :
82 7 : return true;
83 : }
84 :
85 10 : bool BLEMIDIPacketBuilder::addSysCommon(uint8_t num_data, uint8_t header,
86 : uint8_t data1, uint8_t data2,
87 : uint16_t timestamp) {
88 10 : initBuffer(timestamp);
89 :
90 10 : uint8_t timestampLSB = getTimestampLSB(timestamp);
91 :
92 10 : if (!hasSpaceFor(2 + num_data))
93 3 : return false; // Buffer full
94 7 : buffer.push_back(timestampLSB);
95 7 : buffer.push_back(header);
96 7 : if (num_data >= 1)
97 5 : buffer.push_back(data1);
98 7 : if (num_data >= 2)
99 3 : buffer.push_back(data2);
100 7 : runningTimestamp = 0; // Re-send the timestamp next time
101 :
102 7 : return true;
103 : }
104 :
105 20 : bool BLEMIDIPacketBuilder::addSysEx(const uint8_t *&data, size_t &length,
106 : uint16_t timestamp) {
107 20 : initBuffer(timestamp);
108 :
109 : // We can't do anything with an empty message
110 20 : if (length == 0)
111 1 : return true;
112 :
113 : // If the first byte is a SysExStart byte we first have to write a
114 : // timestamp + SysExStart.
115 19 : if (*data == SysExStart) {
116 : // We need space for at least the timestamp and a SysExStart
117 14 : if (!hasSpaceFor(2))
118 3 : return false; // Buffer full
119 :
120 : // Normal running status is interrupted by SysEx
121 11 : runningHeader = 0;
122 :
123 11 : const uint8_t timestampLSB = getTimestampLSB(timestamp);
124 :
125 : // Start of SysEx
126 11 : buffer.push_back(timestampLSB);
127 11 : buffer.push_back(SysExStart);
128 11 : ++data; // First byte was added
129 11 : --length;
130 : }
131 :
132 : // Copy the rest of the data, and terminate the message if necessary
133 16 : continueSysEx(data, length, timestamp);
134 16 : return true;
135 : }
136 :
137 32 : void BLEMIDIPacketBuilder::continueSysEx(const uint8_t *&data, size_t &length,
138 : uint16_t timestamp) {
139 32 : initBuffer(timestamp);
140 :
141 32 : if (length == 0) {
142 : // Message was finished, no continuation
143 1 : data = nullptr;
144 1 : 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 102 : while (length-- > 1 && buffer.size() < buffer.capacity())
150 71 : buffer.push_back(*data++);
151 :
152 : // If everything fit into the buffer
153 31 : if (length == 0) {
154 : // End of SysEx
155 21 : if (*data == SysExEnd) {
156 17 : if (hasSpaceFor(2)) {
157 12 : buffer.push_back(getTimestampLSB(timestamp));
158 12 : buffer.push_back(SysExEnd);
159 : // Message was finished, no continuation
160 12 : data = nullptr;
161 : } else {
162 : // Send the SysExEnd byte next time
163 5 : ++length;
164 : }
165 : }
166 : // End of chunk but not end of SysEx
167 : else {
168 4 : if (hasSpaceFor(1)) {
169 3 : buffer.push_back(*data);
170 : // Message was finished, no continuation
171 3 : data = nullptr;
172 : } else {
173 : // Send the last byte next time
174 1 : ++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 10 : ++length;
183 : }
184 : }
185 :
186 : constexpr const uint8_t BLEMIDIPacketBuilder::SysExStart;
187 : constexpr const uint8_t BLEMIDIPacketBuilder::SysExEnd;
188 :
189 : END_CS_NAMESPACE
|