The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

359 lines
20KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. // This file provides interfaces for managing the internal configuration of Blocks
  20. // and synchronises with the connected Block
  21. using namespace BlocksProtocol;
  22. using ConfigType = Block::ConfigMetaData::ConfigType;
  23. /** Manages the configuration of blocks
  24. @tags{Blocks}
  25. */
  26. struct BlockConfigManager
  27. {
  28. /** Structure describing a configuration */
  29. struct ConfigDescription
  30. {
  31. ConfigItemId item;
  32. int32 value;
  33. int32 min;
  34. int32 max;
  35. bool isActive;
  36. const char* name;
  37. ConfigType type;
  38. const char* optionNames[configMaxOptions];
  39. const char* group;
  40. static_assert (configMaxOptions == Block::ConfigMetaData::numOptionNames, "Config options size and config metadata size should be the same");
  41. Block::ConfigMetaData toConfigMetaData() const
  42. {
  43. return Block::ConfigMetaData ((uint32) item, value, { min, max }, isActive, name, type, (const char**) optionNames, group);
  44. }
  45. };
  46. BlockConfigManager (Array<ConfigDescription> defaultConfig)
  47. {
  48. for (auto c : defaultConfig)
  49. {
  50. uint32 itemIndex;
  51. if (getIndexForItem (c.item, itemIndex))
  52. configList[itemIndex] = c;
  53. }
  54. }
  55. void setDeviceIndex (TopologyIndex newDeviceIndex) { deviceIndex = newDeviceIndex; }
  56. void setDeviceComms (PhysicalTopologySource::DeviceConnection* newConn) { deviceConnection = newConn; }
  57. static constexpr uint32 numConfigItems = 69;
  58. static constexpr const char* midiSettingsGroup = "MIDI Settings";
  59. static constexpr const char* pitchGroup = "Pitch";
  60. static constexpr const char* playGroup = "Play mode";
  61. static constexpr const char* sensitivityGroup = "Sensitivity";
  62. static constexpr const char* rhythmGroup = "Rhythm";
  63. static constexpr const char* coloursGroup = "Colors";
  64. ConfigDescription configList[numConfigItems] =
  65. {
  66. { midiStartChannel, 2, 1, 16, false, "MIDI Start Channel", ConfigType::integer, {}, midiSettingsGroup },
  67. { midiEndChannel, 16, 1, 16, false, "MIDI End Channel", ConfigType::integer, {}, midiSettingsGroup },
  68. { midiUseMPE, 1, 0, 2, false, "MIDI Mode", ConfigType::options, { "Multi Channel",
  69. "MPE",
  70. "Single Channel" }, midiSettingsGroup },
  71. { pitchBendRange, 48, 1, 96, false, "Pitch Bend Range", ConfigType::integer, {}, midiSettingsGroup },
  72. { midiChannelRange, 15, 1, 15, false, "No. MIDI Channels", ConfigType::integer, {}, midiSettingsGroup },
  73. { MPEZone, 0, 0, 1, false, "MPE Zone", ConfigType::options, { "Lower Zone",
  74. "Upper Zone"}, midiSettingsGroup },
  75. { octave, 0, -4, 6, false, "Octave", ConfigType::integer, {}, pitchGroup },
  76. { transpose, 0, -11, 11, false, "Transpose", ConfigType::integer, {}, pitchGroup },
  77. { slideCC, 74, 0, 127, false, "Slide CC", ConfigType::integer, {}, playGroup },
  78. { slideMode, 0, 0, 2, false, "Slide Mode", ConfigType::options, { "Absolute",
  79. "Relative Unipolar",
  80. "Relative Bipolar" }, playGroup },
  81. { velocitySensitivity, 100, 0, 127, false, "Strike Sensitivity", ConfigType::integer, {}, sensitivityGroup },
  82. { glideSensitivity, 100, 0, 127, false, "Glide Sensitivity", ConfigType::integer, {}, sensitivityGroup },
  83. { slideSensitivity, 100, 0, 127, false, "Slide Sensitivity", ConfigType::integer, {}, sensitivityGroup },
  84. { pressureSensitivity, 100, 0, 127, false, "Pressure Sensitivity", ConfigType::integer, {}, sensitivityGroup },
  85. { liftSensitivity, 100, 0, 127, false, "Lift Sensitivity", ConfigType::integer, {}, sensitivityGroup },
  86. { fixedVelocity, 0, 0, 1, false, "Fixed Velocity", ConfigType::boolean, {}, sensitivityGroup },
  87. { fixedVelocityValue, 127, 1, 127, false, "Fixed Velocity Value", ConfigType::integer, {}, sensitivityGroup },
  88. { pianoMode, 0, 0, 1, false, "Piano Mode", ConfigType::boolean, {}, playGroup },
  89. { glideLock, 0, 0, 127, false, "Glide Rate", ConfigType::integer, {}, playGroup },
  90. { glideLockEnable, 0, 0, 1, false, "Glide Lock Enable", ConfigType::boolean, {}, playGroup },
  91. { mode, 4, 1, 5, false, "Mode", ConfigType::integer, {}, playGroup },
  92. { volume, 100, 0, 127, false, "Volume", ConfigType::integer, {}, playGroup },
  93. { scale, 0, 0, 18, false, "Scale", ConfigType::integer, {}, playGroup }, // NOTE: Should be options
  94. { hideMode, 0, 0, 1, false, "Hide Mode", ConfigType::boolean, {}, playGroup },
  95. { chord, 0, 0, 127, false, "Chord", ConfigType::integer, {}, playGroup }, // NOTE: Should be options
  96. { arpPattern, 0, 0, 127, false, "Arp Pattern", ConfigType::integer, {}, playGroup },
  97. { tempo, 120, 1, 300, false, "Tempo", ConfigType::integer, {}, rhythmGroup },
  98. { key, 0, 0, 11, false, "Key", ConfigType::options, { "C", "C#", "D", "D#",
  99. "E", "F", "F#", "G",
  100. "G#", "A", "A#", "B"}, playGroup },
  101. { autoTransposeToKey, 0, 0, 1, false, "Auto Transpose To Key",ConfigType::boolean, {}, pitchGroup },
  102. { xTrackingMode, 1, 1, 4, false, "Glide Tracking Mode", ConfigType::options, { "Multi-Channel",
  103. "Last Played",
  104. "Highest",
  105. "Lowest",
  106. "Disabled" }, playGroup },
  107. { yTrackingMode, 1, 1, 4, false, "Slide Tracking Mode", ConfigType::options, { "Multi-Channel",
  108. "Last Played",
  109. "Highest",
  110. "Lowest",
  111. "Disabled" }, playGroup },
  112. { zTrackingMode, 1, 0, 4, false, "Pressure Tracking Mode", ConfigType::options, { "Poly Aftertouch",
  113. "Last Played",
  114. "Highest",
  115. "Lowest",
  116. "Disabled",
  117. "Hardest" }, playGroup },
  118. { gammaCorrection, 0, 0, 1, false, "Gamma Correction", ConfigType::boolean, {}, coloursGroup },
  119. { globalKeyColour, INT32_MIN, INT32_MIN, INT32_MAX, false, "Global Key Color", ConfigType::colour, {}, coloursGroup },
  120. { rootKeyColour, INT32_MIN, INT32_MIN, INT32_MAX, false, "Root Key Color" , ConfigType::colour, {}, coloursGroup },
  121. { brightness, 100, 0, 100, false, "Brightness", ConfigType::integer, {}, coloursGroup },
  122. // These can be defined for unique usage for a given Littlefoot script
  123. { user0, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  124. { user1, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  125. { user2, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  126. { user3, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  127. { user4, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  128. { user5, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  129. { user6, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  130. { user7, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  131. { user8, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  132. { user9, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  133. { user10, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  134. { user11, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  135. { user12, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  136. { user13, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  137. { user14, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  138. { user15, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  139. { user16, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  140. { user17, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  141. { user18, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  142. { user19, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  143. { user20, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  144. { user21, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  145. { user22, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  146. { user23, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  147. { user24, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  148. { user25, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  149. { user26, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  150. { user27, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  151. { user28, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  152. { user29, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  153. { user30, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
  154. { user31, 0, 0, 127, false, {}, ConfigType::integer, {}, {} }
  155. };
  156. //==============================================================================
  157. int32 getItemValue (ConfigItemId item)
  158. {
  159. uint32 itemIndex;
  160. if (getIndexForItem (item, itemIndex))
  161. return configList[itemIndex].value;
  162. return 0;
  163. }
  164. void setItemValue (ConfigItemId item, int32 value)
  165. {
  166. uint32 itemIndex;
  167. if (getIndexForItem (item, itemIndex))
  168. configList[itemIndex].value = value;
  169. setBlockConfig (item, value);
  170. }
  171. int32 getItemMin (ConfigItemId item)
  172. {
  173. uint32 itemIndex;
  174. if (getIndexForItem (item, itemIndex))
  175. return configList[itemIndex].min;
  176. return 0;
  177. }
  178. void setItemMin (ConfigItemId item, int32 min)
  179. {
  180. uint32 itemIndex;
  181. if (getIndexForItem (item, itemIndex))
  182. configList[itemIndex].min = min;
  183. }
  184. int32 getItemMax (ConfigItemId item)
  185. {
  186. uint32 itemIndex;
  187. if (getIndexForItem (item, itemIndex))
  188. return configList[itemIndex].max;
  189. return 0;
  190. }
  191. void setItemMax (ConfigItemId item, int32 max)
  192. {
  193. uint32 itemIndex;
  194. if (getIndexForItem (item, itemIndex))
  195. configList[itemIndex].max = max;
  196. // Send updateConfig message to Block
  197. }
  198. bool getItemActive (ConfigItemId item)
  199. {
  200. uint32 itemIndex;
  201. if (getIndexForItem (item, itemIndex))
  202. return configList[itemIndex].isActive;
  203. return false;
  204. }
  205. void setItemActive (ConfigItemId item, bool isActive)
  206. {
  207. uint32 itemIndex;
  208. if (getIndexForItem (item, itemIndex))
  209. configList[itemIndex].isActive = isActive;
  210. // Send setConfigState message to Block
  211. }
  212. String getOptionName (ConfigItemId item, uint8 optionIndex)
  213. {
  214. uint32 itemIndex;
  215. if (getIndexForItem (item, itemIndex) && optionIndex < configMaxOptions)
  216. return configList[itemIndex].optionNames[optionIndex];
  217. return {};
  218. }
  219. Block::ConfigMetaData getMetaData (ConfigItemId item)
  220. {
  221. uint32 itemIndex;
  222. if (getIndexForItem (item, itemIndex))
  223. return configList[itemIndex].toConfigMetaData();
  224. return { static_cast<juce::uint32> (item) };
  225. }
  226. void resetConfigListActiveStatus()
  227. {
  228. for (auto& i : configList)
  229. i.isActive = false;
  230. }
  231. //==============================================================================
  232. // Set Block Configuration
  233. void setBlockConfig (ConfigItemId item, int32 value)
  234. {
  235. buildAndSendPacket ([item, value] (HostPacketBuilder<32>& p) { p.addConfigSetMessage (item, value); });
  236. }
  237. void requestBlockConfig (ConfigItemId item)
  238. {
  239. buildAndSendPacket ([item] (HostPacketBuilder<32>& p) { p.addRequestMessage (item); });
  240. }
  241. void requestFactoryConfigSync()
  242. {
  243. buildAndSendPacket ([] (HostPacketBuilder<32>& p) { p.addRequestFactorySyncMessage(); });
  244. }
  245. void requestUserConfigSync()
  246. {
  247. buildAndSendPacket ([] (HostPacketBuilder<32>& p) { p.addRequestUserSyncMessage(); });
  248. }
  249. void handleConfigUpdateMessage (int32 item, int32 value, int32 min, int32 max)
  250. {
  251. uint32 index;
  252. if (getIndexForItem ((ConfigItemId) item, index))
  253. {
  254. configList[index].value = value;
  255. configList[index].min = min;
  256. configList[index].max = max;
  257. configList[index].isActive = true;
  258. }
  259. }
  260. void handleConfigSetMessage(int32 item, int32 value)
  261. {
  262. uint32 index;
  263. if (getIndexForItem ((ConfigItemId) item, index))
  264. configList[index].value = value;
  265. }
  266. private:
  267. bool getIndexForItem (ConfigItemId item, uint32& index)
  268. {
  269. for (uint32 i = 0; i < numConfigItems; ++i)
  270. {
  271. if (configList[i].item == item)
  272. {
  273. index = i;
  274. return true;
  275. }
  276. }
  277. return false;
  278. }
  279. template<typename PacketBuildFn>
  280. void buildAndSendPacket (PacketBuildFn buildFn)
  281. {
  282. if (deviceConnection == nullptr)
  283. return;
  284. HostPacketBuilder<32> packet;
  285. packet.writePacketSysexHeaderBytes (deviceIndex);
  286. buildFn (packet);
  287. packet.writePacketSysexFooter();
  288. deviceConnection->sendMessageToDevice (packet.getData(), (size_t) packet.size());
  289. }
  290. TopologyIndex deviceIndex {};
  291. PhysicalTopologySource::DeviceConnection* deviceConnection {};
  292. };
  293. } // namespace juce