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 
26 // This file provides interfaces for managing the internal configuration of Blocks
27 // and synchronises with the connected Block
28 
29 using namespace BlocksProtocol;
30 
31 using ConfigType = Block::ConfigMetaData::ConfigType;
32 
33 /** Manages the configuration of blocks
34 
35     @tags{Blocks}
36 */
37 struct BlockConfigManager
38 {
39     /** Structure describing a configuration */
40     struct ConfigDescription
41     {
42         ConfigItemId item;
43         int32 value;
44         int32 min;
45         int32 max;
46         bool isActive;
47         const char* name;
48         ConfigType type;
49         const char* optionNames[configMaxOptions];
50         const char* group;
51 
52         static_assert (configMaxOptions == Block::ConfigMetaData::numOptionNames, "Config options size and config metadata size should be the same");
53 
toConfigMetaDataBlockConfigManager::ConfigDescription54         Block::ConfigMetaData toConfigMetaData() const
55         {
56             return Block::ConfigMetaData ((uint32) item, value, { min, max }, isActive, name, type, (const char**) optionNames, group);
57         }
58     };
59 
BlockConfigManagerBlockConfigManager60     BlockConfigManager (Array<ConfigDescription> defaultConfig)
61     {
62         for (auto c : defaultConfig)
63         {
64             uint32 itemIndex;
65 
66             if (getIndexForItem (c.item, itemIndex))
67                 configList[itemIndex] = c;
68         }
69     }
70 
setDeviceIndexBlockConfigManager71     void setDeviceIndex (TopologyIndex newDeviceIndex)                       { deviceIndex = newDeviceIndex; }
setDeviceCommsBlockConfigManager72     void setDeviceComms (PhysicalTopologySource::DeviceConnection* newConn)  { deviceConnection = newConn; }
73 
74     static constexpr uint32 numConfigItems = 69;
75 
76     static constexpr const char* midiSettingsGroup = "MIDI Settings";
77     static constexpr const char* pitchGroup = "Pitch";
78     static constexpr const char* playGroup = "Play mode";
79     static constexpr const char* sensitivityGroup = "Sensitivity";
80     static constexpr const char* rhythmGroup = "Rhythm";
81     static constexpr const char* coloursGroup = "Colors";
82 
83     ConfigDescription configList[numConfigItems] =
84     {
85         { midiStartChannel,     2,      1,      16,         false,  "MIDI Start Channel",   ConfigType::integer,    {},               midiSettingsGroup },
86         { midiEndChannel,       16,     1,      16,         false,  "MIDI End Channel",     ConfigType::integer,    {},               midiSettingsGroup },
87         { midiUseMPE,           1,      0,      2,          false,  "MIDI Mode",            ConfigType::options,    { "Multi Channel",
88                                                                                                                   "MPE",
89                                                                                                                   "Single Channel" }, midiSettingsGroup },
90         { pitchBendRange,       48,     1,      96,         false,  "Pitch Bend Range",     ConfigType::integer,    {},               midiSettingsGroup },
91         { midiChannelRange,     15,     1,      15,         false,  "No. MIDI Channels",    ConfigType::integer,    {},               midiSettingsGroup },
92         { MPEZone,              0,      0,      1,          false,  "MPE Zone",             ConfigType::options,    { "Lower Zone",
93                                                                                                                   "Upper Zone"},      midiSettingsGroup },
94         { octave,               0,      -4,     6,          false,  "Octave",               ConfigType::integer,    {},               pitchGroup },
95         { transpose,            0,      -11,    11,         false,  "Transpose",            ConfigType::integer,    {},               pitchGroup },
96         { slideCC,              74,     0,      127,        false,  "Slide CC",             ConfigType::integer,    {},               playGroup },
97         { slideMode,            0,      0,      2,          false,  "Slide Mode",           ConfigType::options,    { "Absolute",
98                                                                                                                   "Relative Unipolar",
99                                                                                                                   "Relative Bipolar" }, playGroup },
100         { velocitySensitivity,  100,    0,      127,        false,  "Strike Sensitivity",   ConfigType::integer,    {},               sensitivityGroup },
101         { glideSensitivity,     100,    0,      127,        false,  "Glide Sensitivity",    ConfigType::integer,    {},               sensitivityGroup },
102         { slideSensitivity,     100,    0,      127,        false,  "Slide Sensitivity",    ConfigType::integer,    {},               sensitivityGroup },
103         { pressureSensitivity,  100,    0,      127,        false,  "Pressure Sensitivity", ConfigType::integer,    {},               sensitivityGroup },
104         { liftSensitivity,      100,    0,      127,        false,  "Lift Sensitivity",     ConfigType::integer,    {},               sensitivityGroup },
105         { fixedVelocity,        0,      0,      1,          false,  "Fixed Velocity",       ConfigType::boolean,    {},               sensitivityGroup },
106         { fixedVelocityValue,   127,    1,      127,        false,  "Fixed Velocity Value", ConfigType::integer,    {},               sensitivityGroup },
107         { pianoMode,            0,      0,      1,          false,  "Piano Mode",           ConfigType::boolean,    {},               playGroup },
108         { glideLock,            0,      0,      127,        false,  "Glide Rate",           ConfigType::integer,    {},               playGroup },
109         { glideLockEnable,      0,      0,      1,          false,  "Glide Lock Enable",    ConfigType::boolean,    {},               playGroup },
110         { mode,                 4,      1,      5,          false,  "Mode",                 ConfigType::integer,    {},               playGroup },
111         { volume,               100,    0,      127,        false,  "Volume",               ConfigType::integer,    {},               playGroup },
112         { scale,                0,      0,      18,         false,  "Scale",                ConfigType::integer,    {},               playGroup }, // NOTE: Should be options
113         { hideMode,             0,      0,      1,          false,  "Hide Mode",            ConfigType::boolean,    {},               playGroup },
114         { chord,                0,      0,      127,        false,  "Chord",                ConfigType::integer,    {},               playGroup }, // NOTE: Should be options
115         { arpPattern,           0,      0,      127,        false,  "Arp Pattern",          ConfigType::integer,    {},               playGroup },
116         { tempo,                120,    1,      300,        false,  "Tempo",                ConfigType::integer,    {},               rhythmGroup },
117         { key,                  0,      0,      11,         false,  "Key",                  ConfigType::options,    { "C", "C#", "D", "D#",
118                                                                                                                   "E", "F", "F#", "G",
119                                                                                                                   "G#", "A", "A#", "B"}, playGroup },
120         { autoTransposeToKey,   0,      0,      1,          false,  "Auto Transpose To Key",ConfigType::boolean,    {},               pitchGroup },
121         { xTrackingMode,        1,      1,      4,          false,  "Glide Tracking Mode",  ConfigType::options,    { "Multi-Channel",
122                                                                                                                   "Last Played",
123                                                                                                                   "Highest",
124                                                                                                                   "Lowest",
125                                                                                                                   "Disabled" },   playGroup },
126         { yTrackingMode,        1,      1,      4,          false,  "Slide Tracking Mode",  ConfigType::options,    { "Multi-Channel",
127                                                                                                                   "Last Played",
128                                                                                                                   "Highest",
129                                                                                                                   "Lowest",
130                                                                                                                   "Disabled" },   playGroup },
131         { zTrackingMode,        1,      0,      4,          false,  "Pressure Tracking Mode", ConfigType::options, { "Poly Aftertouch",
132                                                                                                                   "Last Played",
133                                                                                                                   "Highest",
134                                                                                                                   "Lowest",
135                                                                                                                   "Disabled",
136                                                                                                                   "Hardest" },    playGroup },
137 
138         { gammaCorrection,      0,         0,         1,    false,  "Gamma Correction",     ConfigType::boolean,    {},             coloursGroup },
139         { globalKeyColour, INT32_MIN, INT32_MIN, INT32_MAX, false,  "Global Key Color",     ConfigType::colour,     {},             coloursGroup },
140         { rootKeyColour,   INT32_MIN, INT32_MIN, INT32_MAX, false,  "Root Key Color"  ,     ConfigType::colour,     {},             coloursGroup },
141         { brightness,           100,    0,    100,          false,  "Brightness",           ConfigType::integer,    {},             coloursGroup },
142 
143         // These can be defined for unique usage for a given Littlefoot script
144         { user0,                0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
145         { user1,                0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
146         { user2,                0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
147         { user3,                0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
148         { user4,                0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
149         { user5,                0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
150         { user6,                0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
151         { user7,                0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
152         { user8,                0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
153         { user9,                0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
154         { user10,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
155         { user11,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
156         { user12,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
157         { user13,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
158         { user14,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
159         { user15,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
160         { user16,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
161         { user17,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
162         { user18,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
163         { user19,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
164         { user20,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
165         { user21,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
166         { user22,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
167         { user23,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
168         { user24,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
169         { user25,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
170         { user26,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
171         { user27,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
172         { user28,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
173         { user29,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
174         { user30,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} },
175         { user31,               0,    0,      127,          false,  {},                     ConfigType::integer,    {},               {} }
176     };
177 
178     //==============================================================================
getItemValueBlockConfigManager179     int32 getItemValue (ConfigItemId item)
180     {
181         uint32 itemIndex;
182 
183         if (getIndexForItem (item, itemIndex))
184             return configList[itemIndex].value;
185 
186         return 0;
187     }
188 
setItemValueBlockConfigManager189     void setItemValue (ConfigItemId item, int32 value)
190     {
191         uint32 itemIndex;
192 
193         if (getIndexForItem (item, itemIndex))
194             configList[itemIndex].value = value;
195 
196         setBlockConfig (item, value);
197     }
198 
getItemMinBlockConfigManager199     int32 getItemMin (ConfigItemId item)
200     {
201         uint32 itemIndex;
202 
203         if (getIndexForItem (item, itemIndex))
204             return configList[itemIndex].min;
205 
206         return 0;
207     }
208 
setItemMinBlockConfigManager209     void setItemMin (ConfigItemId item, int32 min)
210     {
211         uint32 itemIndex;
212 
213         if (getIndexForItem (item, itemIndex))
214             configList[itemIndex].min = min;
215     }
216 
getItemMaxBlockConfigManager217     int32 getItemMax (ConfigItemId item)
218     {
219         uint32 itemIndex;
220 
221         if (getIndexForItem (item, itemIndex))
222             return configList[itemIndex].max;
223 
224         return 0;
225     }
226 
setItemMaxBlockConfigManager227     void setItemMax (ConfigItemId item, int32 max)
228     {
229         uint32 itemIndex;
230 
231         if (getIndexForItem (item, itemIndex))
232             configList[itemIndex].max = max;
233 
234         // Send updateConfig message to Block
235     }
236 
getItemActiveBlockConfigManager237     bool getItemActive (ConfigItemId item)
238     {
239         uint32 itemIndex;
240 
241         if (getIndexForItem (item, itemIndex))
242             return configList[itemIndex].isActive;
243 
244         return false;
245     }
246 
setItemActiveBlockConfigManager247     void setItemActive (ConfigItemId item, bool isActive)
248     {
249         uint32 itemIndex;
250 
251         if (getIndexForItem (item, itemIndex))
252             configList[itemIndex].isActive = isActive;
253 
254         // Send setConfigState message to Block
255     }
256 
getOptionNameBlockConfigManager257     String getOptionName (ConfigItemId item, uint8 optionIndex)
258     {
259         uint32 itemIndex;
260 
261         if (getIndexForItem (item, itemIndex) && optionIndex < configMaxOptions)
262             return configList[itemIndex].optionNames[optionIndex];
263 
264         return {};
265     }
266 
getMetaDataBlockConfigManager267     Block::ConfigMetaData getMetaData (ConfigItemId item)
268     {
269         uint32 itemIndex;
270 
271         if (getIndexForItem (item, itemIndex))
272             return configList[itemIndex].toConfigMetaData();
273 
274         return { static_cast<juce::uint32> (item) };
275     }
276 
resetConfigListActiveStatusBlockConfigManager277     void resetConfigListActiveStatus()
278     {
279         for (auto& i : configList)
280             i.isActive = false;
281     }
282 
283     //==============================================================================
284     // Set Block Configuration
setBlockConfigBlockConfigManager285     void setBlockConfig (ConfigItemId item, int32 value)
286     {
287         buildAndSendPacket ([item, value] (HostPacketBuilder<32>& p) { p.addConfigSetMessage (item, value); });
288     }
289 
requestBlockConfigBlockConfigManager290     void requestBlockConfig (ConfigItemId item)
291     {
292         buildAndSendPacket ([item] (HostPacketBuilder<32>& p) { p.addRequestMessage (item); });
293     }
294 
requestFactoryConfigSyncBlockConfigManager295     void requestFactoryConfigSync()
296     {
297         buildAndSendPacket ([] (HostPacketBuilder<32>& p) { p.addRequestFactorySyncMessage(); });
298     }
299 
requestUserConfigSyncBlockConfigManager300     void requestUserConfigSync()
301     {
302         buildAndSendPacket ([] (HostPacketBuilder<32>& p) { p.addRequestUserSyncMessage(); });
303     }
304 
handleConfigUpdateMessageBlockConfigManager305     void handleConfigUpdateMessage (int32 item, int32 value, int32 min, int32 max)
306     {
307         uint32 index;
308 
309         if (getIndexForItem ((ConfigItemId) item, index))
310         {
311             configList[index].value = value;
312             configList[index].min = min;
313             configList[index].max = max;
314             configList[index].isActive = true;
315         }
316     }
317 
handleConfigSetMessageBlockConfigManager318     void handleConfigSetMessage(int32 item, int32 value)
319     {
320         uint32 index;
321 
322         if (getIndexForItem ((ConfigItemId) item, index))
323             configList[index].value = value;
324     }
325 
326 private:
getIndexForItemBlockConfigManager327     bool getIndexForItem (ConfigItemId item, uint32& index)
328     {
329         for (uint32 i = 0; i < numConfigItems; ++i)
330         {
331             if (configList[i].item == item)
332             {
333                 index = i;
334                 return true;
335             }
336         }
337 
338         return false;
339     }
340 
341     template <typename PacketBuildFn>
buildAndSendPacketBlockConfigManager342     void buildAndSendPacket (PacketBuildFn buildFn)
343     {
344         if (deviceConnection == nullptr)
345             return;
346 
347         HostPacketBuilder<32> packet;
348         packet.writePacketSysexHeaderBytes (deviceIndex);
349         buildFn (packet);
350         packet.writePacketSysexFooter();
351         deviceConnection->sendMessageToDevice (packet.getData(), (size_t) packet.size());
352     }
353 
354     TopologyIndex deviceIndex {};
355     PhysicalTopologySource::DeviceConnection* deviceConnection {};
356 };
357 
358 } // namespace juce
359