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.

409 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2016 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace littlefoot
  18. {
  19. using namespace juce;
  20. //==============================================================================
  21. /**
  22. This class manages the synchronisation of a remote block of heap memory used
  23. by a littlefoot program running on a block.
  24. Data in the block can be changed by calling setByte, setBytes, setBits etc, and
  25. these changes will be flushed to the device when sendChanges is called.
  26. */
  27. template <typename ImplementationClass>
  28. struct LittleFootRemoteHeap
  29. {
  30. LittleFootRemoteHeap (uint32 blockSizeToUse) noexcept : blockSize (blockSizeToUse)
  31. {
  32. resetDeviceStateToUnknown();
  33. }
  34. void clear() noexcept
  35. {
  36. zeromem (targetData, sizeof (targetData));
  37. }
  38. void setByte (size_t offset, uint8 value) noexcept
  39. {
  40. if (offset < blockSize)
  41. {
  42. if (targetData [offset] != value)
  43. {
  44. targetData [offset] = value;
  45. invalidateData();
  46. }
  47. }
  48. else
  49. {
  50. jassertfalse;
  51. }
  52. }
  53. void setBytes (size_t offset, const uint8* data, size_t num) noexcept
  54. {
  55. for (size_t i = 0; i < num; ++i)
  56. setByte (offset + i, data[i]);
  57. }
  58. void setBits (uint32 startBit, uint32 numBits, uint32 value) noexcept
  59. {
  60. if (startBit + numBits > 8 * blockSize)
  61. {
  62. jassertfalse;
  63. return;
  64. }
  65. if (readLittleEndianBitsInBuffer (targetData, startBit, numBits) != value)
  66. {
  67. writeLittleEndianBitsInBuffer (targetData, startBit, numBits, value);
  68. invalidateData();
  69. }
  70. }
  71. uint8 getByte (size_t offset) noexcept
  72. {
  73. if (offset < blockSize)
  74. return targetData [offset];
  75. jassertfalse;
  76. return 0;
  77. }
  78. void invalidateData()
  79. {
  80. dataHasChanged = true;
  81. programStateKnown = false;
  82. }
  83. void sendChanges (ImplementationClass& bi)
  84. {
  85. if (dataHasChanged && messagesSent.isEmpty())
  86. {
  87. for (;;)
  88. {
  89. uint16 data[ImplementationClass::maxBlockSize];
  90. uint32 packetIndex;
  91. if (messagesSent.isEmpty())
  92. {
  93. for (uint32 i = 0; i < blockSize; ++i)
  94. data[i] = deviceState[i];
  95. packetIndex = lastPacketIndexReceived;
  96. }
  97. else
  98. {
  99. auto& lastPacket = messagesSent.getReference (messagesSent.size() - 1);
  100. for (uint32 i = 0; i < blockSize; ++i)
  101. data[i] = lastPacket.resultDataState[i];
  102. packetIndex = lastPacket.packetIndex;
  103. }
  104. packetIndex = (packetIndex + 1) & ImplementationClass::maxPacketCounter;
  105. if (! Diff (data, targetData, blockSize).createChangeMessage (bi, data, messagesSent, packetIndex))
  106. break;
  107. dumpStatus();
  108. }
  109. }
  110. for (auto& m : messagesSent)
  111. {
  112. if (m.dispatchTime >= Time::getCurrentTime() - RelativeTime::milliseconds (250))
  113. break;
  114. m.dispatchTime = Time::getCurrentTime();
  115. bi.sendMessageToDevice (m.packet);
  116. //DBG ("Sending packet " << (int) m.packetIndex << " - " << m.packet.size() << " bytes, device " << bi.getDeviceIndex());
  117. if (getTotalSizeOfMessagesSent() > 200)
  118. break;
  119. }
  120. }
  121. void handleACKFromDevice (ImplementationClass& bi, uint32 packetIndex) noexcept
  122. {
  123. //DBG ("ACK " << (int) packetIndex);
  124. if (lastPacketIndexReceived != packetIndex)
  125. {
  126. lastPacketIndexReceived = packetIndex;
  127. for (int i = messagesSent.size(); --i >= 0;)
  128. {
  129. auto& m = messagesSent.getReference(i);
  130. if (m.packetIndex == packetIndex)
  131. {
  132. for (uint32 j = 0; j < blockSize; ++j)
  133. deviceState[j] = m.resultDataState[j];
  134. messagesSent.removeRange (0, i + 1);
  135. dumpStatus();
  136. sendChanges (bi);
  137. if (messagesSent.isEmpty())
  138. dataHasChanged = false;
  139. return;
  140. }
  141. }
  142. resetDeviceStateToUnknown();
  143. }
  144. }
  145. bool isProgramLoaded() noexcept
  146. {
  147. if (! programStateKnown)
  148. {
  149. uint8 deviceMemory[ImplementationClass::maxBlockSize];
  150. bool anyUnknowns = false;
  151. for (size_t i = 0; i < blockSize; ++i)
  152. {
  153. anyUnknowns = (deviceState[i] > 255);
  154. deviceMemory[i] = (uint8) deviceState[i];
  155. }
  156. programLoaded = ! anyUnknowns && littlefoot::Program (deviceMemory, (uint32) blockSize).checksumMatches();
  157. programStateKnown = true;
  158. }
  159. return programLoaded;
  160. }
  161. const size_t blockSize;
  162. static constexpr uint16 unknownByte = 0x100;
  163. private:
  164. uint16 deviceState[ImplementationClass::maxBlockSize];
  165. uint8 targetData[ImplementationClass::maxBlockSize] = { 0 };
  166. bool dataHasChanged = true, programStateKnown = true, programLoaded = false;
  167. void resetDeviceStateToUnknown()
  168. {
  169. invalidateData();
  170. messagesSent.clear();
  171. for (uint32 i = 0; i < ImplementationClass::maxBlockSize; ++i)
  172. deviceState[i] = unknownByte;
  173. }
  174. struct ChangeMessage
  175. {
  176. typename ImplementationClass::PacketBuilder packet;
  177. Time dispatchTime;
  178. uint32 packetIndex;
  179. uint16 resultDataState[ImplementationClass::maxBlockSize];
  180. };
  181. Array<ChangeMessage> messagesSent;
  182. uint32 lastPacketIndexReceived = 0;
  183. int getTotalSizeOfMessagesSent() const noexcept
  184. {
  185. int total = 0;
  186. for (auto& m : messagesSent)
  187. if (m.dispatchTime != Time())
  188. total += m.packet.size();
  189. return total;
  190. }
  191. void dumpStatus()
  192. {
  193. #if DUMP_LITTLEFOOT_HEAP_STATUS
  194. int differences = 0;
  195. constexpr int diffLen = 50;
  196. char areas[diffLen + 1] = { 0 };
  197. for (int i = 0; i < diffLen; ++i)
  198. areas[i] = '.';
  199. for (int i = 0; i < (int) blockSize; ++i)
  200. {
  201. if (targetData[i] != deviceState[i])
  202. {
  203. ++differences;
  204. areas[i * diffLen / (int) blockSize] = 'X';
  205. }
  206. }
  207. double proportionOK = ((int) blockSize - differences) / (double) blockSize;
  208. juce::ignoreUnused (proportionOK);
  209. DBG ("Heap: " << areas << " " << String (roundToInt (100 * proportionOK)) << "% "
  210. << (isProgramLoaded() ? "Ready" : "Loading"));
  211. #endif
  212. }
  213. struct Diff
  214. {
  215. Diff (uint16* current, const uint8* target, size_t blockSizeToUse)
  216. : newData (target), blockSize (blockSizeToUse)
  217. {
  218. for (int i = 0; i < (int) blockSize; ++i)
  219. ranges.add ({ i, 1, newData[i] == current[i], false });
  220. coalesceUniformRegions();
  221. coalesceSequences();
  222. trim();
  223. }
  224. bool createChangeMessage (const ImplementationClass& bi,
  225. const uint16* currentState,
  226. Array<ChangeMessage>& messagesCreated,
  227. uint32 nextPacketIndex)
  228. {
  229. if (ranges.isEmpty())
  230. return false;
  231. auto deviceIndex = bi.getDeviceIndex();
  232. if (deviceIndex < 0)
  233. return false;
  234. messagesCreated.add ({});
  235. auto& message = messagesCreated.getReference (messagesCreated.size() - 1);
  236. message.packetIndex = nextPacketIndex;
  237. for (uint32 i = 0; i < blockSize; ++i)
  238. message.resultDataState[i] = currentState[i];
  239. auto& p = message.packet;
  240. p.writePacketSysexHeaderBytes ((uint8) deviceIndex);
  241. p.beginDataChanges (nextPacketIndex);
  242. uint8 lastValue = 0;
  243. bool packetOverflow = false;
  244. for (auto& r : ranges)
  245. {
  246. if (r.isSkipped)
  247. {
  248. packetOverflow = ! p.skipBytes (r.length);
  249. }
  250. else if (r.isMixed)
  251. {
  252. jassert (r.length > 1);
  253. packetOverflow = ! p.setMultipleBytes (newData + r.index, r.length);
  254. if (! packetOverflow)
  255. lastValue = newData[r.index + r.length - 1];
  256. }
  257. else
  258. {
  259. auto value = newData[r.index];
  260. packetOverflow = ! p.setMultipleBytes (value, lastValue, r.length);
  261. if (! packetOverflow)
  262. lastValue = value;
  263. }
  264. if (packetOverflow)
  265. break;
  266. if (! r.isSkipped)
  267. for (int i = r.index; i < r.index + r.length; ++i)
  268. message.resultDataState[i] = newData[i];
  269. }
  270. p.endDataChanges (! packetOverflow);
  271. p.writePacketSysexFooter();
  272. return packetOverflow;
  273. }
  274. private:
  275. struct ByteSequence
  276. {
  277. int index, length;
  278. bool isSkipped, isMixed;
  279. };
  280. const uint8* const newData;
  281. const size_t blockSize;
  282. Array<ByteSequence> ranges;
  283. void coalesceUniformRegions()
  284. {
  285. for (int i = 0; i < ranges.size() - 1; ++i)
  286. {
  287. auto& r1 = ranges.getReference (i);
  288. auto r2 = ranges.getReference (i + 1);
  289. if (r1.isSkipped == r2.isSkipped
  290. && (r1.isSkipped || newData[r1.index] == newData[r2.index]))
  291. {
  292. r1.length += r2.length;
  293. ranges.remove (i + 1);
  294. i = jmax (0, i - 2);
  295. }
  296. }
  297. }
  298. void coalesceSequences()
  299. {
  300. for (int i = 0; i < ranges.size() - 1; ++i)
  301. {
  302. auto& r1 = ranges.getReference (i);
  303. auto r2 = ranges.getReference (i + 1);
  304. if (! (r1.isSkipped || r2.isSkipped)
  305. && (r1.isMixed || r1.length == 1)
  306. && (r2.isMixed || r2.length == 1))
  307. {
  308. if (r1.length + r2.length < 32)
  309. {
  310. r1.length += r2.length;
  311. r1.isMixed = true;
  312. ranges.remove (i + 1);
  313. i = jmax (0, i - 2);
  314. }
  315. }
  316. }
  317. }
  318. void trim()
  319. {
  320. while (ranges.size() > 0 && ranges.getLast().isSkipped)
  321. ranges.removeLast();
  322. }
  323. };
  324. };
  325. }