1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    The code included in this file is provided under the terms of the ISC license
11    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12    To use, copy, modify, and/or distribute this software for any purpose with or
13    without fee is hereby granted provided that the above copyright notice and
14    this permission notice appear in all copies.
15 
16    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18    DISCLAIMED.
19 
20   ==============================================================================
21 */
22 
23 namespace juce
24 {
25 namespace BlocksProtocol
26 {
27 
28 /**
29     Helper class for constructing a packet for sending to a BLOCKS device
30 
31     @tags{Blocks}
32 */
33 template <int maxPacketBytes>
34 struct HostPacketBuilder
35 {
HostPacketBuilderHostPacketBuilder36     HostPacketBuilder() noexcept {}
37     HostPacketBuilder (const HostPacketBuilder&) = delete;
38     HostPacketBuilder (HostPacketBuilder&&) = default;
39 
getDataHostPacketBuilder40     const void* getData() const noexcept        { return data.getData(); }
sizeHostPacketBuilder41     int size() const noexcept                   { return data.size(); }
42 
43     //==============================================================================
writePacketSysexHeaderBytesHostPacketBuilder44     void writePacketSysexHeaderBytes (TopologyIndex deviceIndex) noexcept
45     {
46         static_assert (maxPacketBytes > 10, "Not enough bytes for a sensible message!");
47 
48         jassert ((deviceIndex & 64) == 0);
49 
50         data.writeHeaderSysexBytes (deviceIndex);
51     }
52 
writePacketSysexFooterHostPacketBuilder53     void writePacketSysexFooter() noexcept
54     {
55         data.writePacketSysexFooter();
56     }
57 
58     //==============================================================================
deviceControlMessageHostPacketBuilder59     bool deviceControlMessage (DeviceCommand command) noexcept
60     {
61         if (! data.hasCapacity ((int) MessageType::bits + (int) DeviceCommand::bits))
62             return false;
63 
64         writeMessageType (MessageFromHost::deviceCommandMessage);
65         data << command;
66         return true;
67     }
68 
69     //==============================================================================
beginDataChangesHostPacketBuilder70     bool beginDataChanges (PacketIndex packetIndex) noexcept
71     {
72         if (! data.hasCapacity ((int) MessageType::bits + (int) PacketIndex::bits + (int) DataChangeCommand::bits))
73             return false;
74 
75         writeMessageType (MessageFromHost::sharedDataChange);
76         data << packetIndex;
77         return true;
78     }
79 
endDataChangesHostPacketBuilder80     bool endDataChanges (bool isLastChange) noexcept
81     {
82         if (! data.hasCapacity (DataChangeCommand::bits))
83             return false;
84 
85         data << DataChangeCommand ((uint32) isLastChange ? endOfChanges : endOfPacket);
86         return true;
87     }
88 
skipBytesHostPacketBuilder89     bool skipBytes (int numToSkip) noexcept
90     {
91         if (numToSkip <= 0)
92             return true;
93 
94         auto state = data.getState();
95 
96         while (numToSkip > ByteCountMany::maxValue)
97         {
98             if (! skipBytes (ByteCountMany::maxValue))
99             {
100                 data.restore (state);
101                 return false;
102             }
103 
104             numToSkip -= ByteCountMany::maxValue;
105         }
106 
107         if (numToSkip > ByteCountFew::maxValue)
108         {
109             if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountMany::bits))
110             {
111                 data.restore (state);
112                 return false;
113             }
114 
115             data << DataChangeCommand ((uint32) skipBytesMany) << ByteCountMany ((uint32) numToSkip);
116             return true;
117         }
118 
119         if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountFew::bits))
120         {
121             data.restore (state);
122             return false;
123         }
124 
125         data << DataChangeCommand ((uint32) skipBytesFew) << ByteCountFew ((uint32) numToSkip);
126         return true;
127     }
128 
setMultipleBytesHostPacketBuilder129     bool setMultipleBytes (const uint8* values, int num) noexcept
130     {
131         if (num <= 0)
132             return true;
133 
134         if (! data.hasCapacity (DataChangeCommand::bits * 2 + num * (1 + ByteValue::bits)))
135             return false;
136 
137         data << DataChangeCommand ((uint32) setSequenceOfBytes);
138 
139         for (int i = 0; i < num; ++i)
140             data << ByteValue ((uint32) values[i])
141                  << ByteSequenceContinues (i < num - 1 ? 1 : 0);
142 
143         return true;
144     }
145 
setMultipleBytesHostPacketBuilder146     bool setMultipleBytes (uint8 value, uint8 lastValue, int num) noexcept
147     {
148         if (num <= 0)
149             return true;
150 
151         if (num == 1)
152             return setMultipleBytes (&value, 1); // (this is a more compact message)
153 
154         auto state = data.getState();
155 
156         if (num > ByteCountMany::maxValue)
157         {
158             if (! setMultipleBytes (value, lastValue, ByteCountMany::maxValue))
159             {
160                 data.restore (state);
161                 return false;
162             }
163 
164             return setMultipleBytes (value, lastValue, num - ByteCountMany::maxValue);
165         }
166 
167         if (num > ByteCountFew::maxValue)
168         {
169             if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountMany::bits + ByteValue::bits))
170             {
171                 data.restore (state);
172                 return false;
173             }
174 
175             data << DataChangeCommand ((uint32) setManyBytesWithValue)
176                  << ByteCountMany ((uint32) num)
177                  << ByteValue ((uint32) value);
178 
179             return true;
180         }
181 
182         if (value == lastValue)
183         {
184             if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountFew::bits))
185             {
186                 data.restore (state);
187                 return false;
188             }
189 
190             data << DataChangeCommand ((uint32) setFewBytesWithLastValue) << ByteCountFew ((uint32) num);
191             return true;
192         }
193 
194         if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountFew::bits + ByteValue::bits))
195         {
196             data.restore (state);
197             return false;
198         }
199 
200         data << DataChangeCommand ((uint32) setFewBytesWithValue) << ByteCountFew ((uint32) num)
201              << ByteValue ((uint32) value);
202 
203         return true;
204     }
205 
addProgramEventMessageHostPacketBuilder206     bool addProgramEventMessage (const int32* messageData)
207     {
208         if (! data.hasCapacity (BitSizes::programEventMessage))
209             return false;
210 
211         writeMessageType (MessageFromHost::programEventMessage);
212 
213         for (uint32 i = 0; i < numProgramMessageInts; ++i)
214             data << IntegerWithBitSize<32> ((uint32) messageData[i]);
215 
216         return true;
217     }
218 
addFirmwareUpdatePacketHostPacketBuilder219     bool addFirmwareUpdatePacket (const uint8* packetData, uint8 size)
220     {
221         if (! data.hasCapacity (MessageType::bits + FirmwareUpdatePacketSize::bits + 7 * size))
222             return false;
223 
224         writeMessageType (MessageFromHost::firmwareUpdatePacket);
225         data << FirmwareUpdatePacketSize (size);
226 
227         for (uint8 i = 0; i < size; ++i)
228             data << IntegerWithBitSize<7> ((uint32) packetData[i]);
229 
230         return true;
231     }
232 
233     //==============================================================================
addConfigSetMessageHostPacketBuilder234     bool addConfigSetMessage (int32 item, int32 value)
235     {
236         if (! data.hasCapacity (BitSizes::configSetMessage))
237             return false;
238 
239         writeMessageType(MessageFromHost::configMessage);
240         ConfigCommand type = ConfigCommands::setConfig;
241         data << type << IntegerWithBitSize<8> ((uint32) item) << IntegerWithBitSize<32>((uint32) value);
242         return true;
243     }
244 
addRequestMessageHostPacketBuilder245     bool addRequestMessage (int32 item)
246     {
247         if (! data.hasCapacity (BitSizes::configSetMessage))
248             return false;
249 
250         writeMessageType(MessageFromHost::configMessage);
251         ConfigCommand type = ConfigCommands::requestConfig;
252         data << type << IntegerWithBitSize<32> (0) << IntegerWithBitSize<8> ((uint32) item);
253         return true;
254     }
255 
addRequestFactorySyncMessageHostPacketBuilder256     bool addRequestFactorySyncMessage()
257     {
258         if (! data.hasCapacity ((int) MessageType::bits + (int) ConfigCommand::bits))
259             return false;
260 
261         writeMessageType (MessageFromHost::configMessage);
262         ConfigCommand type = ConfigCommands::requestFactorySync;
263         data << type;
264         return true;
265     }
266 
addRequestUserSyncMessageHostPacketBuilder267     bool addRequestUserSyncMessage()
268     {
269         if (! data.hasCapacity ((int) MessageType::bits + (int) ConfigCommand::bits))
270             return false;
271 
272         writeMessageType (MessageFromHost::configMessage);
273         ConfigCommand type = ConfigCommands::requestUserSync;
274         data << type;
275         return true;
276     }
277 
278     //==============================================================================
addFactoryResetHostPacketBuilder279     bool addFactoryReset()
280     {
281         if (! data.hasCapacity (MessageType::bits))
282             return false;
283 
284         writeMessageType (MessageFromHost::factoryReset);
285         return true;
286     }
287 
addBlockResetHostPacketBuilder288     bool addBlockReset()
289     {
290         if (! data.hasCapacity (MessageType::bits))
291             return false;
292 
293         writeMessageType (MessageFromHost::blockReset);
294         return true;
295     }
296 
addSetBlockNameHostPacketBuilder297     bool addSetBlockName (const String& name)
298     {
299         if (name.length() > 32 || ! data.hasCapacity (MessageType::bits + 7 + (7 * name.length())))
300             return false;
301 
302         writeMessageType (MessageFromHost::setName);
303 
304         data << IntegerWithBitSize<7> ((uint32) name.length());
305 
306         for (auto i = 0; i < name.length(); ++i)
307             data << IntegerWithBitSize<7> ((uint32) name.toRawUTF8()[i]);
308 
309         data << IntegerWithBitSize<7> (0);
310 
311         return true;
312     }
313 
314     //==============================================================================
315 private:
316     Packed7BitArrayBuilder<maxPacketBytes> data;
317 
writeMessageTypeHostPacketBuilder318     void writeMessageType (MessageFromHost type) noexcept
319     {
320         data << MessageType ((uint32) type);
321     }
322 };
323 
324 } // namespace BlocksProtocol
325 } // namespace juce
326