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.

433 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  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. #define JUCE_DUMP_LITTLEFOOT_HEAP_STATUS 0
  18. namespace littlefoot
  19. {
  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. @tags{Blocks}
  27. */
  28. template <typename ImplementationClass>
  29. struct LittleFootRemoteHeap
  30. {
  31. LittleFootRemoteHeap (uint32 blockSizeToUse) noexcept : blockSize (blockSizeToUse)
  32. {
  33. resetDeviceStateToUnknown();
  34. }
  35. void clear() noexcept
  36. {
  37. zeromem (targetData, sizeof (targetData));
  38. invalidateData();
  39. }
  40. void resetDeviceStateToUnknown()
  41. {
  42. invalidateData();
  43. messagesSent.clear();
  44. resetDataRangeToUnknown (0, ImplementationClass::maxBlockSize);
  45. }
  46. void resetDataRangeToUnknown (size_t offset, size_t size) noexcept
  47. {
  48. auto* latestState = getLatestExpectedDataState();
  49. for (size_t i = 0; i < size; ++i)
  50. latestState[offset + i] = unknownByte;
  51. }
  52. void setByte (size_t offset, uint8 value) noexcept
  53. {
  54. if (offset < blockSize)
  55. {
  56. if (targetData[offset] != value)
  57. {
  58. targetData[offset] = value;
  59. needsSyncing = true;
  60. if (programStateKnown && offset < programSize)
  61. programStateKnown = false;
  62. }
  63. }
  64. else
  65. {
  66. jassertfalse;
  67. }
  68. }
  69. void setBytes (size_t offset, const uint8* data, size_t num) noexcept
  70. {
  71. for (size_t i = 0; i < num; ++i)
  72. setByte (offset + i, data[i]);
  73. }
  74. void setBits (uint32 startBit, uint32 numBits, uint32 value) noexcept
  75. {
  76. if (startBit + numBits > 8 * blockSize)
  77. {
  78. jassertfalse;
  79. return;
  80. }
  81. if (readLittleEndianBitsInBuffer (targetData, startBit, numBits) != value)
  82. {
  83. writeLittleEndianBitsInBuffer (targetData, startBit, numBits, value);
  84. invalidateData();
  85. }
  86. }
  87. uint8 getByte (size_t offset) noexcept
  88. {
  89. if (offset < blockSize)
  90. return targetData [offset];
  91. jassertfalse;
  92. return 0;
  93. }
  94. void invalidateData() noexcept
  95. {
  96. needsSyncing = true;
  97. programStateKnown = false;
  98. }
  99. bool isFullySynced() const noexcept
  100. {
  101. return ! needsSyncing;
  102. }
  103. static bool isAllZero (const uint8* data, size_t size) noexcept
  104. {
  105. for (size_t i = 0; i < size; ++i)
  106. if (data[i] != 0)
  107. return false;
  108. return true;
  109. }
  110. void sendChanges (ImplementationClass& bi, bool forceSend)
  111. {
  112. if ((needsSyncing && messagesSent.isEmpty()) || forceSend)
  113. {
  114. for (int maxChanges = 30; --maxChanges >= 0;)
  115. {
  116. if (isAllZero (targetData, blockSize))
  117. break;
  118. uint16 data[ImplementationClass::maxBlockSize];
  119. auto* latestState = getLatestExpectedDataState();
  120. for (uint32 i = 0; i < blockSize; ++i)
  121. data[i] = latestState[i];
  122. uint32 packetIndex = messagesSent.isEmpty() ? lastPacketIndexReceived
  123. : messagesSent.getLast()->packetIndex;
  124. packetIndex = (packetIndex + 1) & ImplementationClass::maxPacketCounter;
  125. if (! Diff (data, targetData, blockSize).createChangeMessage (bi, data, messagesSent, packetIndex))
  126. break;
  127. dumpStatus();
  128. }
  129. }
  130. for (auto* m : messagesSent)
  131. {
  132. if (m->dispatchTime >= Time::getCurrentTime() - RelativeTime::milliseconds (250))
  133. break;
  134. m->dispatchTime = Time::getCurrentTime();
  135. bi.sendMessageToDevice (m->packet);
  136. //DBG ("Sending packet " << (int) m->packetIndex << " - " << m->packet.size() << " bytes, device " << bi.getDeviceIndex());
  137. if (getTotalSizeOfMessagesSent() > 200)
  138. break;
  139. }
  140. }
  141. void handleACKFromDevice (ImplementationClass& bi, uint32 packetIndex) noexcept
  142. {
  143. //DBG ("ACK " << (int) packetIndex << " device " << (int) bi.getDeviceIndex());
  144. if (lastPacketIndexReceived != packetIndex)
  145. {
  146. lastPacketIndexReceived = packetIndex;
  147. for (int i = messagesSent.size(); --i >= 0;)
  148. {
  149. auto& m = *messagesSent.getUnchecked(i);
  150. if (m.packetIndex == packetIndex)
  151. {
  152. for (uint32 j = 0; j < blockSize; ++j)
  153. deviceState[j] = m.resultDataState[j];
  154. programStateKnown = false;
  155. messagesSent.removeRange (0, i + 1);
  156. dumpStatus();
  157. sendChanges (bi, false);
  158. if (messagesSent.isEmpty())
  159. needsSyncing = false;
  160. return;
  161. }
  162. }
  163. resetDeviceStateToUnknown();
  164. }
  165. }
  166. bool isProgramLoaded() noexcept
  167. {
  168. if (! programStateKnown)
  169. {
  170. programStateKnown = true;
  171. uint8 deviceMemory[ImplementationClass::maxBlockSize];
  172. for (size_t i = 0; i < blockSize; ++i)
  173. deviceMemory[i] = (uint8) deviceState[i];
  174. littlefoot::Program prog (deviceMemory, (uint32) blockSize);
  175. programLoaded = prog.checksumMatches();
  176. programSize = prog.getProgramSize();
  177. }
  178. return programLoaded;
  179. }
  180. const size_t blockSize;
  181. static constexpr uint16 unknownByte = 0x100;
  182. private:
  183. uint16 deviceState[ImplementationClass::maxBlockSize] = { 0 };
  184. uint8 targetData[ImplementationClass::maxBlockSize] = { 0 };
  185. uint32 programSize = 0;
  186. bool needsSyncing = true, programStateKnown = true, programLoaded = false;
  187. uint16* getLatestExpectedDataState() noexcept
  188. {
  189. return messagesSent.isEmpty() ? deviceState
  190. : messagesSent.getLast()->resultDataState;
  191. }
  192. struct ChangeMessage
  193. {
  194. typename ImplementationClass::PacketBuilder packet;
  195. Time dispatchTime;
  196. uint32 packetIndex;
  197. uint16 resultDataState[ImplementationClass::maxBlockSize];
  198. };
  199. OwnedArray<ChangeMessage> messagesSent;
  200. uint32 lastPacketIndexReceived = 0;
  201. int getTotalSizeOfMessagesSent() const noexcept
  202. {
  203. int total = 0;
  204. for (auto* m : messagesSent)
  205. if (m->dispatchTime != Time())
  206. total += m->packet.size();
  207. return total;
  208. }
  209. void dumpStatus()
  210. {
  211. #if JUCE_DUMP_LITTLEFOOT_HEAP_STATUS
  212. int differences = 0;
  213. constexpr int diffLen = 50;
  214. char areas[diffLen + 1] = { 0 };
  215. for (int i = 0; i < diffLen; ++i)
  216. areas[i] = '.';
  217. for (int i = 0; i < (int) blockSize; ++i)
  218. {
  219. if (targetData[i] != deviceState[i])
  220. {
  221. ++differences;
  222. areas[i * diffLen / (int) blockSize] = 'X';
  223. }
  224. }
  225. double proportionOK = ((int) blockSize - differences) / (double) blockSize;
  226. ignoreUnused (proportionOK);
  227. DBG ("Heap: " << areas << " " << String (roundToInt (100 * proportionOK)) << "% "
  228. << (isProgramLoaded() ? "Ready" : "Loading"));
  229. #endif
  230. }
  231. struct Diff
  232. {
  233. Diff (uint16* current, const uint8* target, size_t blockSizeToUse)
  234. : newData (target), blockSize (blockSizeToUse)
  235. {
  236. ranges.ensureStorageAllocated ((int) blockSize);
  237. for (int i = 0; i < (int) blockSize; ++i)
  238. ranges.add ({ i, 1, newData[i] == current[i], false });
  239. coalesceUniformRegions();
  240. coalesceSequences();
  241. trim();
  242. }
  243. bool createChangeMessage (const ImplementationClass& bi,
  244. const uint16* currentState,
  245. OwnedArray<ChangeMessage>& messagesCreated,
  246. uint32 nextPacketIndex)
  247. {
  248. if (ranges.isEmpty())
  249. return false;
  250. auto deviceIndex = bi.getDeviceIndex();
  251. if (deviceIndex < 0)
  252. return false;
  253. auto& message = *messagesCreated.add (new ChangeMessage());
  254. message.packetIndex = nextPacketIndex;
  255. for (uint32 i = 0; i < blockSize; ++i)
  256. message.resultDataState[i] = currentState[i];
  257. auto& p = message.packet;
  258. p.writePacketSysexHeaderBytes ((uint8) deviceIndex);
  259. p.beginDataChanges (nextPacketIndex);
  260. uint8 lastValue = 0;
  261. bool packetOverflow = false;
  262. for (auto& r : ranges)
  263. {
  264. if (r.isSkipped)
  265. {
  266. packetOverflow = ! p.skipBytes (r.length);
  267. }
  268. else if (r.isMixed)
  269. {
  270. jassert (r.length > 1);
  271. packetOverflow = ! p.setMultipleBytes (newData + r.index, r.length);
  272. if (! packetOverflow)
  273. lastValue = newData[r.index + r.length - 1];
  274. }
  275. else
  276. {
  277. auto value = newData[r.index];
  278. packetOverflow = ! p.setMultipleBytes (value, lastValue, r.length);
  279. if (! packetOverflow)
  280. lastValue = value;
  281. }
  282. if (packetOverflow)
  283. break;
  284. if (! r.isSkipped)
  285. for (int i = r.index; i < r.index + r.length; ++i)
  286. message.resultDataState[i] = newData[i];
  287. }
  288. p.endDataChanges (! packetOverflow);
  289. p.writePacketSysexFooter();
  290. return packetOverflow;
  291. }
  292. private:
  293. struct ByteSequence
  294. {
  295. int index, length;
  296. bool isSkipped, isMixed;
  297. };
  298. const uint8* const newData;
  299. const size_t blockSize;
  300. Array<ByteSequence> ranges;
  301. void coalesceUniformRegions()
  302. {
  303. for (int i = ranges.size(); --i > 0;)
  304. {
  305. auto& r1 = ranges.getReference (i - 1);
  306. auto r2 = ranges.getReference (i);
  307. if (r1.isSkipped == r2.isSkipped
  308. && (r1.isSkipped || newData[r1.index] == newData[r2.index]))
  309. {
  310. r1.length += r2.length;
  311. ranges.remove (i);
  312. i = jmin (ranges.size() - 1, i + 1);
  313. }
  314. }
  315. }
  316. void coalesceSequences()
  317. {
  318. for (int i = ranges.size(); --i > 0;)
  319. {
  320. auto& r1 = ranges.getReference (i - 1);
  321. auto r2 = ranges.getReference (i);
  322. if (! (r1.isSkipped || r2.isSkipped)
  323. && (r1.isMixed || r1.length == 1)
  324. && (r2.isMixed || r2.length == 1))
  325. {
  326. if (r1.length + r2.length < 32)
  327. {
  328. r1.length += r2.length;
  329. r1.isMixed = true;
  330. ranges.remove (i);
  331. i = jmin (ranges.size() - 1, i + 1);
  332. }
  333. }
  334. }
  335. }
  336. void trim()
  337. {
  338. while (ranges.size() > 0 && ranges.getLast().isSkipped)
  339. ranges.removeLast();
  340. }
  341. };
  342. };
  343. }