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.

2751 lines
95KB

  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. namespace juce
  18. {
  19. #define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED \
  20. jassert (juce::MessageManager::getInstance()->currentThreadHasLockedMessageManager());
  21. #if DUMP_BANDWIDTH_STATS
  22. namespace
  23. {
  24. struct PortIOStats
  25. {
  26. PortIOStats (const char* nm) : name (nm) {}
  27. const char* const name;
  28. int byteCount = 0;
  29. int messageCount = 0;
  30. int bytesPerSec = 0;
  31. int largestMessageBytes = 0;
  32. int lastMessageBytes = 0;
  33. void update (double elapsedSec)
  34. {
  35. if (byteCount > 0)
  36. {
  37. bytesPerSec = (int) (byteCount / elapsedSec);
  38. byteCount = 0;
  39. juce::Logger::writeToLog (getString());
  40. }
  41. }
  42. juce::String getString() const
  43. {
  44. return juce::String (name) + ": "
  45. + "count=" + juce::String (messageCount).paddedRight (' ', 7)
  46. + "rate=" + (juce::String (bytesPerSec / 1024.0f, 1) + " Kb/sec").paddedRight (' ', 11)
  47. + "largest=" + (juce::String (largestMessageBytes) + " bytes").paddedRight (' ', 11)
  48. + "last=" + (juce::String (lastMessageBytes) + " bytes").paddedRight (' ', 11);
  49. }
  50. void registerMessage (int numBytes) noexcept
  51. {
  52. byteCount += numBytes;
  53. ++messageCount;
  54. lastMessageBytes = numBytes;
  55. largestMessageBytes = juce::jmax (largestMessageBytes, numBytes);
  56. }
  57. };
  58. static PortIOStats inputStats { "Input" }, outputStats { "Output" };
  59. static uint32 startTime = 0;
  60. static inline void resetOnSecondBoundary()
  61. {
  62. auto now = juce::Time::getMillisecondCounter();
  63. double elapsedSec = (now - startTime) / 1000.0;
  64. if (elapsedSec >= 1.0)
  65. {
  66. inputStats.update (elapsedSec);
  67. outputStats.update (elapsedSec);
  68. startTime = now;
  69. }
  70. }
  71. static inline void registerBytesOut (int numBytes)
  72. {
  73. outputStats.registerMessage (numBytes);
  74. resetOnSecondBoundary();
  75. }
  76. static inline void registerBytesIn (int numBytes)
  77. {
  78. inputStats.registerMessage (numBytes);
  79. resetOnSecondBoundary();
  80. }
  81. }
  82. juce::String getMidiIOStats()
  83. {
  84. return inputStats.getString() + " " + outputStats.getString();
  85. }
  86. #endif
  87. //==============================================================================
  88. struct PhysicalTopologySource::Internal
  89. {
  90. struct Detector;
  91. struct BlockImplementation;
  92. struct ControlButtonImplementation;
  93. struct RotaryDialImplementation;
  94. struct TouchSurfaceImplementation;
  95. struct LEDGridImplementation;
  96. struct LEDRowImplementation;
  97. //==============================================================================
  98. struct MIDIDeviceConnection : public DeviceConnection,
  99. public juce::MidiInputCallback
  100. {
  101. MIDIDeviceConnection() {}
  102. ~MIDIDeviceConnection()
  103. {
  104. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  105. listeners.call ([this] (Listener& l) { l.connectionBeingDeleted (*this); });
  106. if (midiInput != nullptr)
  107. midiInput->stop();
  108. if (interprocessLock != nullptr)
  109. interprocessLock->exit();
  110. }
  111. bool lockAgainstOtherProcesses (const String& midiInName, const String& midiOutName)
  112. {
  113. interprocessLock.reset (new juce::InterProcessLock ("blocks_sdk_"
  114. + File::createLegalFileName (midiInName)
  115. + "_" + File::createLegalFileName (midiOutName)));
  116. if (interprocessLock->enter (500))
  117. return true;
  118. interprocessLock = nullptr;
  119. return false;
  120. }
  121. struct Listener
  122. {
  123. virtual ~Listener() {}
  124. virtual void handleIncomingMidiMessage (const juce::MidiMessage& message) = 0;
  125. virtual void connectionBeingDeleted (const MIDIDeviceConnection&) = 0;
  126. };
  127. void addListener (Listener* l)
  128. {
  129. listeners.add (l);
  130. }
  131. void removeListener (Listener* l)
  132. {
  133. listeners.remove (l);
  134. }
  135. bool sendMessageToDevice (const void* data, size_t dataSize) override
  136. {
  137. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
  138. jassert (dataSize > sizeof (BlocksProtocol::roliSysexHeader) + 2);
  139. jassert (memcmp (data, BlocksProtocol::roliSysexHeader, sizeof (BlocksProtocol::roliSysexHeader)) == 0);
  140. jassert (static_cast<const uint8*> (data)[dataSize - 1] == 0xf7);
  141. if (midiOutput != nullptr)
  142. {
  143. midiOutput->sendMessageNow (juce::MidiMessage (data, (int) dataSize));
  144. return true;
  145. }
  146. return false;
  147. }
  148. void handleIncomingMidiMessage (juce::MidiInput*, const juce::MidiMessage& message) override
  149. {
  150. const auto data = message.getRawData();
  151. const int dataSize = message.getRawDataSize();
  152. const int bodySize = dataSize - (int) (sizeof (BlocksProtocol::roliSysexHeader) + 1);
  153. if (bodySize > 0 && memcmp (data, BlocksProtocol::roliSysexHeader, sizeof (BlocksProtocol::roliSysexHeader)) == 0)
  154. if (handleMessageFromDevice != nullptr)
  155. handleMessageFromDevice (data + sizeof (BlocksProtocol::roliSysexHeader), (size_t) bodySize);
  156. listeners.call ([&] (Listener& l) { l.handleIncomingMidiMessage (message); });
  157. }
  158. std::unique_ptr<juce::MidiInput> midiInput;
  159. std::unique_ptr<juce::MidiOutput> midiOutput;
  160. private:
  161. juce::ListenerList<Listener> listeners;
  162. std::unique_ptr<juce::InterProcessLock> interprocessLock;
  163. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MIDIDeviceConnection)
  164. };
  165. struct MIDIDeviceDetector : public DeviceDetector
  166. {
  167. MIDIDeviceDetector() {}
  168. juce::StringArray scanForDevices() override
  169. {
  170. juce::StringArray result;
  171. for (auto& pair : findDevices())
  172. result.add (pair.inputName + " & " + pair.outputName);
  173. return result;
  174. }
  175. DeviceConnection* openDevice (int index) override
  176. {
  177. auto pair = findDevices()[index];
  178. if (pair.inputIndex >= 0 && pair.outputIndex >= 0)
  179. {
  180. std::unique_ptr<MIDIDeviceConnection> dev (new MIDIDeviceConnection());
  181. if (dev->lockAgainstOtherProcesses (pair.inputName, pair.outputName))
  182. {
  183. lockedFromOutside = false;
  184. dev->midiInput.reset (juce::MidiInput::openDevice (pair.inputIndex, dev.get()));
  185. dev->midiOutput.reset (juce::MidiOutput::openDevice (pair.outputIndex));
  186. if (dev->midiInput != nullptr)
  187. {
  188. dev->midiInput->start();
  189. return dev.release();
  190. }
  191. }
  192. else
  193. {
  194. lockedFromOutside = true;
  195. }
  196. }
  197. return nullptr;
  198. }
  199. bool isLockedFromOutside() const override
  200. {
  201. return lockedFromOutside;
  202. }
  203. static bool isBlocksMidiDeviceName (const juce::String& name)
  204. {
  205. return name.indexOf (" BLOCK") > 0 || name.indexOf (" Block") > 0;
  206. }
  207. static String cleanBlocksDeviceName (juce::String name)
  208. {
  209. name = name.trim();
  210. if (name.endsWith (" IN)"))
  211. return name.dropLastCharacters (4);
  212. if (name.endsWith (" OUT)"))
  213. return name.dropLastCharacters (5);
  214. const int openBracketPosition = name.lastIndexOfChar ('[');
  215. if (openBracketPosition != -1 && name.endsWith ("]"))
  216. return name.dropLastCharacters (name.length() - openBracketPosition);
  217. return name;
  218. }
  219. struct MidiInputOutputPair
  220. {
  221. juce::String outputName, inputName;
  222. int outputIndex = -1, inputIndex = -1;
  223. };
  224. static juce::Array<MidiInputOutputPair> findDevices()
  225. {
  226. juce::Array<MidiInputOutputPair> result;
  227. auto midiInputs = juce::MidiInput::getDevices();
  228. auto midiOutputs = juce::MidiOutput::getDevices();
  229. for (int j = 0; j < midiInputs.size(); ++j)
  230. {
  231. if (isBlocksMidiDeviceName (midiInputs[j]))
  232. {
  233. MidiInputOutputPair pair;
  234. pair.inputName = midiInputs[j];
  235. pair.inputIndex = j;
  236. String cleanedInputName = cleanBlocksDeviceName (pair.inputName);
  237. for (int i = 0; i < midiOutputs.size(); ++i)
  238. {
  239. if (cleanBlocksDeviceName (midiOutputs[i]) == cleanedInputName)
  240. {
  241. pair.outputName = midiOutputs[i];
  242. pair.outputIndex = i;
  243. break;
  244. }
  245. }
  246. result.add (pair);
  247. }
  248. }
  249. return result;
  250. }
  251. private:
  252. bool lockedFromOutside = true;
  253. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MIDIDeviceDetector)
  254. };
  255. //==============================================================================
  256. struct DeviceInfo
  257. {
  258. Block::UID uid;
  259. BlocksProtocol::TopologyIndex index;
  260. BlocksProtocol::BlockSerialNumber serial;
  261. BlocksProtocol::VersionNumber version;
  262. BlocksProtocol::BlockName name;
  263. bool isMaster;
  264. };
  265. static juce::String getVersionString (const BlocksProtocol::VersionNumber& v)
  266. {
  267. return juce::String (reinterpret_cast<const char*> (v.version),
  268. std::min (sizeof (v.version), static_cast<size_t> (v.length)));
  269. }
  270. static juce::String getNameString (const BlocksProtocol::BlockName& n)
  271. {
  272. return juce::String (reinterpret_cast<const char*> (n.name),
  273. std::min (sizeof (n.name), static_cast<size_t> (n.length)));
  274. }
  275. static Block::Timestamp deviceTimestampToHost (uint32 timestamp) noexcept
  276. {
  277. return static_cast<Block::Timestamp> (timestamp);
  278. }
  279. static juce::Array<DeviceInfo> getArrayOfDeviceInfo (const juce::Array<BlocksProtocol::DeviceStatus>& devices)
  280. {
  281. juce::Array<DeviceInfo> result;
  282. bool isFirst = true;
  283. for (auto& device : devices)
  284. {
  285. BlocksProtocol::VersionNumber version;
  286. BlocksProtocol::BlockName name;
  287. result.add ({ getBlockUIDFromSerialNumber (device.serialNumber),
  288. device.index,
  289. device.serialNumber,
  290. version,
  291. name,
  292. isFirst });
  293. isFirst = false;
  294. }
  295. return result;
  296. }
  297. static bool containsBlockWithUID (const juce::Array<DeviceInfo>& devices, Block::UID uid) noexcept
  298. {
  299. for (auto&& d : devices)
  300. if (d.uid == uid)
  301. return true;
  302. return false;
  303. }
  304. static bool containsBlockWithUID (const Block::Array& blocks, Block::UID uid) noexcept
  305. {
  306. for (auto&& block : blocks)
  307. if (block->uid == uid)
  308. return true;
  309. return false;
  310. }
  311. static bool versionNumberChanged (const DeviceInfo& device, juce::String version) noexcept
  312. {
  313. auto deviceVersion = getVersionString (device.version);
  314. return deviceVersion != version && deviceVersion.isNotEmpty();
  315. }
  316. static bool nameIsValid (const DeviceInfo& device)
  317. {
  318. return device.name.length > 0;
  319. }
  320. static void setVersionNumberForBlock (const DeviceInfo& deviceInfo, Block& block) noexcept
  321. {
  322. if (deviceInfo.uid != block.uid)
  323. {
  324. jassertfalse;
  325. return;
  326. }
  327. block.versionNumber = getVersionString (deviceInfo.version);
  328. }
  329. static void setNameForBlock (const juce::Array<DeviceInfo>& devices, Block& block) noexcept
  330. {
  331. for (auto&& d : devices)
  332. {
  333. if (d.uid == block.uid)
  334. {
  335. setNameForBlock (d, block);
  336. return;
  337. }
  338. }
  339. }
  340. static void setNameForBlock (const DeviceInfo& deviceInfo, Block& block)
  341. {
  342. if (deviceInfo.uid != block.uid)
  343. {
  344. jassertfalse;
  345. return;
  346. }
  347. block.name = getNameString (deviceInfo.name);
  348. }
  349. //==============================================================================
  350. struct ConnectedDeviceGroup : private juce::AsyncUpdater,
  351. private juce::Timer
  352. {
  353. ConnectedDeviceGroup (Detector& d, const juce::String& name, DeviceConnection* connection)
  354. : detector (d), deviceName (name), deviceConnection (connection)
  355. {
  356. lastGlobalPingTime = juce::Time::getCurrentTime();
  357. deviceConnection->handleMessageFromDevice = [this] (const void* data, size_t dataSize)
  358. {
  359. this->handleIncomingMessage (data, dataSize);
  360. };
  361. startTimer (200);
  362. sendTopologyRequest();
  363. }
  364. bool isStillConnected (const juce::StringArray& detectedDevices) const noexcept
  365. {
  366. return detectedDevices.contains (deviceName)
  367. && ! failedToGetTopology()
  368. && lastGlobalPingTime > juce::Time::getCurrentTime() - juce::RelativeTime::seconds (pingTimeoutSeconds);
  369. }
  370. Block::UID getDeviceIDFromIndex (BlocksProtocol::TopologyIndex index) const noexcept
  371. {
  372. for (auto& d : currentDeviceInfo)
  373. if (d.index == index)
  374. return d.uid;
  375. return {};
  376. }
  377. int getIndexFromDeviceID (Block::UID uid) const noexcept
  378. {
  379. for (auto& d : currentDeviceInfo)
  380. if (d.uid == uid)
  381. return d.index;
  382. return -1;
  383. }
  384. const DeviceInfo* getDeviceInfoFromUID (Block::UID uid) const noexcept
  385. {
  386. for (auto& d : currentDeviceInfo)
  387. if (d.uid == uid)
  388. return &d;
  389. return nullptr;
  390. }
  391. const BlocksProtocol::DeviceStatus* getLastStatus (Block::UID deviceID) const noexcept
  392. {
  393. for (auto&& status : currentTopologyDevices)
  394. if (getBlockUIDFromSerialNumber (status.serialNumber) == deviceID)
  395. return &status;
  396. return nullptr;
  397. }
  398. //==============================================================================
  399. juce::Time lastTopologyRequestTime, lastTopologyReceiveTime;
  400. int numTopologyRequestsSent = 0;
  401. void sendTopologyRequest()
  402. {
  403. ++numTopologyRequestsSent;
  404. lastTopologyRequestTime = juce::Time::getCurrentTime();
  405. sendCommandMessage (0, BlocksProtocol::requestTopologyMessage);
  406. }
  407. void scheduleNewTopologyRequest()
  408. {
  409. numTopologyRequestsSent = 0;
  410. lastTopologyReceiveTime = juce::Time();
  411. }
  412. bool failedToGetTopology() const noexcept
  413. {
  414. return numTopologyRequestsSent > 4 && lastTopologyReceiveTime == juce::Time();
  415. }
  416. bool hasAnyBlockStoppedPinging() const noexcept
  417. {
  418. auto now = juce::Time::getCurrentTime();
  419. for (auto& ping : blockPings)
  420. if (ping.lastPing < now - juce::RelativeTime::seconds (pingTimeoutSeconds))
  421. return true;
  422. return false;
  423. }
  424. void timerCallback() override
  425. {
  426. auto now = juce::Time::getCurrentTime();
  427. if ((now > lastTopologyReceiveTime + juce::RelativeTime::seconds (30.0) || hasAnyBlockStoppedPinging())
  428. && now > lastTopologyRequestTime + juce::RelativeTime::seconds (1.0)
  429. && numTopologyRequestsSent < 4)
  430. sendTopologyRequest();
  431. }
  432. //==============================================================================
  433. // The following methods will be called by the HostPacketDecoder:
  434. void beginTopology (int numDevices, int numConnections)
  435. {
  436. incomingTopologyDevices.clearQuick();
  437. incomingTopologyDevices.ensureStorageAllocated (numDevices);
  438. incomingTopologyConnections.clearQuick();
  439. incomingTopologyConnections.ensureStorageAllocated (numConnections);
  440. }
  441. void extendTopology (int numDevices, int numConnections)
  442. {
  443. incomingTopologyDevices.ensureStorageAllocated (incomingTopologyDevices.size() + numDevices);
  444. incomingTopologyConnections.ensureStorageAllocated (incomingTopologyConnections.size() + numConnections);
  445. }
  446. void handleTopologyDevice (BlocksProtocol::DeviceStatus status)
  447. {
  448. incomingTopologyDevices.add (status);
  449. }
  450. void handleTopologyConnection (BlocksProtocol::DeviceConnection connection)
  451. {
  452. incomingTopologyConnections.add (connection);
  453. }
  454. void endTopology()
  455. {
  456. currentDeviceInfo = getArrayOfDeviceInfo (incomingTopologyDevices);
  457. currentDeviceConnections = getArrayOfConnections (incomingTopologyConnections);
  458. currentTopologyDevices = incomingTopologyDevices;
  459. lastTopologyReceiveTime = juce::Time::getCurrentTime();
  460. blockPings.clear();
  461. }
  462. void handleVersion (BlocksProtocol::DeviceVersion version)
  463. {
  464. for (auto& d : currentDeviceInfo)
  465. if (d.index == version.index && version.version.length > 1)
  466. d.version = version.version;
  467. }
  468. void notifyDetectorTopologyChanged()
  469. {
  470. detector.handleTopologyChange();
  471. }
  472. void handleName (BlocksProtocol::DeviceName name)
  473. {
  474. for (auto& d : currentDeviceInfo)
  475. if (d.index == name.index && name.name.length > 1)
  476. d.name = name.name;
  477. }
  478. void handleControlButtonUpDown (BlocksProtocol::TopologyIndex deviceIndex, uint32 timestamp,
  479. BlocksProtocol::ControlButtonID buttonID, bool isDown)
  480. {
  481. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  482. detector.handleButtonChange (deviceID, deviceTimestampToHost (timestamp), buttonID.get(), isDown);
  483. }
  484. void handleCustomMessage (BlocksProtocol::TopologyIndex deviceIndex, uint32 timestamp, const int32* data)
  485. {
  486. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  487. detector.handleCustomMessage (deviceID, deviceTimestampToHost (timestamp), data);
  488. }
  489. void handleTouchChange (BlocksProtocol::TopologyIndex deviceIndex,
  490. uint32 timestamp,
  491. BlocksProtocol::TouchIndex touchIndex,
  492. BlocksProtocol::TouchPosition position,
  493. BlocksProtocol::TouchVelocity velocity,
  494. bool isStart, bool isEnd)
  495. {
  496. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  497. {
  498. TouchSurface::Touch touch;
  499. touch.index = (int) touchIndex.get();
  500. touch.x = position.x.toUnipolarFloat();
  501. touch.y = position.y.toUnipolarFloat();
  502. touch.z = position.z.toUnipolarFloat();
  503. touch.xVelocity = velocity.vx.toBipolarFloat();
  504. touch.yVelocity = velocity.vy.toBipolarFloat();
  505. touch.zVelocity = velocity.vz.toBipolarFloat();
  506. touch.eventTimestamp = deviceTimestampToHost (timestamp);
  507. touch.isTouchStart = isStart;
  508. touch.isTouchEnd = isEnd;
  509. touch.blockUID = deviceID;
  510. setTouchStartPosition (touch);
  511. detector.handleTouchChange (deviceID, touch);
  512. }
  513. }
  514. void setTouchStartPosition (TouchSurface::Touch& touch)
  515. {
  516. auto& startPos = touchStartPositions.getValue (touch);
  517. if (touch.isTouchStart)
  518. startPos = { touch.x, touch.y };
  519. touch.startX = startPos.x;
  520. touch.startY = startPos.y;
  521. }
  522. void handlePacketACK (BlocksProtocol::TopologyIndex deviceIndex, BlocksProtocol::PacketCounter counter)
  523. {
  524. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  525. detector.handleSharedDataACK (deviceID, counter);
  526. }
  527. void handleFirmwareUpdateACK (BlocksProtocol::TopologyIndex deviceIndex, BlocksProtocol::FirmwareUpdateACKCode resultCode, BlocksProtocol::FirmwareUpdateACKDetail resultDetail)
  528. {
  529. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  530. detector.handleFirmwareUpdateACK (deviceID, (uint8) resultCode.get(), (uint32) resultDetail.get());
  531. }
  532. void handleConfigUpdateMessage (BlocksProtocol::TopologyIndex deviceIndex, int32 item, int32 value, int32 min, int32 max)
  533. {
  534. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  535. detector.handleConfigUpdateMessage (deviceID, item, value, min, max);
  536. }
  537. void handleConfigSetMessage (BlocksProtocol::TopologyIndex deviceIndex, int32 item, int32 value)
  538. {
  539. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  540. detector.handleConfigSetMessage (deviceID, item, value);
  541. }
  542. void handleConfigFactorySyncEndMessage (BlocksProtocol::TopologyIndex deviceIndex)
  543. {
  544. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  545. detector.handleConfigFactorySyncEndMessage (deviceID);
  546. }
  547. void handleConfigFactorySyncResetMessage (BlocksProtocol::TopologyIndex deviceIndex)
  548. {
  549. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  550. detector.handleConfigFactorySyncResetMessage (deviceID);
  551. }
  552. void handleLogMessage (BlocksProtocol::TopologyIndex deviceIndex, const String& message)
  553. {
  554. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  555. detector.handleLogMessage (deviceID, message);
  556. }
  557. //==============================================================================
  558. template <typename PacketBuilder>
  559. bool sendMessageToDevice (const PacketBuilder& builder) const
  560. {
  561. if (deviceConnection->sendMessageToDevice (builder.getData(), (size_t) builder.size()))
  562. {
  563. #if DUMP_BANDWIDTH_STATS
  564. registerBytesOut (builder.size());
  565. #endif
  566. return true;
  567. }
  568. return false;
  569. }
  570. bool sendCommandMessage (BlocksProtocol::TopologyIndex deviceIndex, uint32 commandID) const
  571. {
  572. BlocksProtocol::HostPacketBuilder<64> p;
  573. p.writePacketSysexHeaderBytes (deviceIndex);
  574. p.deviceControlMessage (commandID);
  575. p.writePacketSysexFooter();
  576. return sendMessageToDevice (p);
  577. }
  578. bool broadcastCommandMessage (uint32 commandID) const
  579. {
  580. return sendCommandMessage (BlocksProtocol::topologyIndexForBroadcast, commandID);
  581. }
  582. DeviceConnection* getDeviceConnection()
  583. {
  584. return deviceConnection.get();
  585. }
  586. Detector& detector;
  587. juce::String deviceName;
  588. juce::Array<DeviceInfo> currentDeviceInfo;
  589. juce::Array<BlockDeviceConnection> currentDeviceConnections;
  590. static constexpr double pingTimeoutSeconds = 6.0;
  591. private:
  592. //==============================================================================
  593. std::unique_ptr<DeviceConnection> deviceConnection;
  594. juce::Array<BlocksProtocol::DeviceStatus> incomingTopologyDevices, currentTopologyDevices;
  595. juce::Array<BlocksProtocol::DeviceConnection> incomingTopologyConnections;
  596. juce::CriticalSection incomingPacketLock;
  597. juce::Array<juce::MemoryBlock> incomingPackets;
  598. struct TouchStart
  599. {
  600. float x, y;
  601. };
  602. TouchList<TouchStart> touchStartPositions;
  603. juce::Time lastGlobalPingTime;
  604. struct BlockPingTime
  605. {
  606. Block::UID blockUID;
  607. juce::Time lastPing;
  608. };
  609. juce::Array<BlockPingTime> blockPings;
  610. Block::UID getDeviceIDFromMessageIndex (BlocksProtocol::TopologyIndex index) noexcept
  611. {
  612. auto uid = getDeviceIDFromIndex (index);
  613. if (uid == Block::UID())
  614. {
  615. scheduleNewTopologyRequest(); // force a re-request of the topology when we
  616. // get an event from a block that we don't know about
  617. }
  618. else
  619. {
  620. auto now = juce::Time::getCurrentTime();
  621. for (auto& ping : blockPings)
  622. {
  623. if (ping.blockUID == uid)
  624. {
  625. ping.lastPing = now;
  626. return uid;
  627. }
  628. }
  629. blockPings.add ({ uid, now });
  630. }
  631. return uid;
  632. }
  633. juce::Array<BlockDeviceConnection> getArrayOfConnections (const juce::Array<BlocksProtocol::DeviceConnection>& connections)
  634. {
  635. juce::Array<BlockDeviceConnection> result;
  636. for (auto&& c : connections)
  637. {
  638. BlockDeviceConnection dc;
  639. dc.device1 = getDeviceIDFromIndex (c.device1);
  640. dc.device2 = getDeviceIDFromIndex (c.device2);
  641. if (dc.device1 <= 0 || dc.device2 <= 0)
  642. continue;
  643. dc.connectionPortOnDevice1 = convertConnectionPort (dc.device1, c.port1);
  644. dc.connectionPortOnDevice2 = convertConnectionPort (dc.device2, c.port2);
  645. result.add (dc);
  646. }
  647. return result;
  648. }
  649. Block::ConnectionPort convertConnectionPort (Block::UID uid, BlocksProtocol::ConnectorPort p) noexcept
  650. {
  651. if (auto* info = getDeviceInfoFromUID (uid))
  652. return BlocksProtocol::BlockDataSheet (info->serial).convertPortIndexToConnectorPort (p);
  653. jassertfalse;
  654. return { Block::ConnectionPort::DeviceEdge::north, 0 };
  655. }
  656. //==============================================================================
  657. void handleIncomingMessage (const void* data, size_t dataSize)
  658. {
  659. juce::MemoryBlock mb (data, dataSize);
  660. {
  661. const juce::ScopedLock sl (incomingPacketLock);
  662. incomingPackets.add (std::move (mb));
  663. }
  664. triggerAsyncUpdate();
  665. #if DUMP_BANDWIDTH_STATS
  666. registerBytesIn ((int) dataSize);
  667. #endif
  668. }
  669. void handleAsyncUpdate() override
  670. {
  671. juce::Array<juce::MemoryBlock> packets;
  672. packets.ensureStorageAllocated (32);
  673. {
  674. const juce::ScopedLock sl (incomingPacketLock);
  675. incomingPackets.swapWith (packets);
  676. }
  677. for (auto& packet : packets)
  678. {
  679. auto data = static_cast<const uint8*> (packet.getData());
  680. BlocksProtocol::HostPacketDecoder<ConnectedDeviceGroup>
  681. ::processNextPacket (*this, *data, data + 1, (int) packet.getSize() - 1);
  682. }
  683. lastGlobalPingTime = juce::Time::getCurrentTime();
  684. }
  685. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectedDeviceGroup)
  686. };
  687. //==============================================================================
  688. /** This is the main singleton object that keeps track of connected blocks */
  689. struct Detector : public juce::ReferenceCountedObject,
  690. private juce::Timer
  691. {
  692. Detector() : defaultDetector (new MIDIDeviceDetector()), deviceDetector (*defaultDetector)
  693. {
  694. topologyBroadcastThrottle.detector = this;
  695. startTimer (10);
  696. }
  697. Detector (DeviceDetector& dd) : deviceDetector (dd)
  698. {
  699. topologyBroadcastThrottle.detector = this;
  700. startTimer (10);
  701. }
  702. ~Detector()
  703. {
  704. jassert (activeTopologySources.isEmpty());
  705. }
  706. using Ptr = juce::ReferenceCountedObjectPtr<Detector>;
  707. static Detector::Ptr getDefaultDetector()
  708. {
  709. auto& d = getDefaultDetectorPointer();
  710. if (d == nullptr)
  711. d = new Detector();
  712. return d;
  713. }
  714. static Detector::Ptr& getDefaultDetectorPointer()
  715. {
  716. static Detector::Ptr defaultDetector;
  717. return defaultDetector;
  718. }
  719. void detach (PhysicalTopologySource* pts)
  720. {
  721. activeTopologySources.removeAllInstancesOf (pts);
  722. if (activeTopologySources.isEmpty())
  723. {
  724. for (auto& b : currentTopology.blocks)
  725. if (auto bi = BlockImplementation::getFrom (*b))
  726. bi->sendCommandMessage (BlocksProtocol::endAPIMode);
  727. currentTopology = {};
  728. topologyBroadcastThrottle.lastTopology = {};
  729. auto& d = getDefaultDetectorPointer();
  730. if (d != nullptr && d->getReferenceCount() == 2)
  731. getDefaultDetectorPointer() = nullptr;
  732. }
  733. }
  734. bool isConnected (Block::UID deviceID) const noexcept
  735. {
  736. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
  737. for (auto&& b : currentTopology.blocks)
  738. if (b->uid == deviceID)
  739. return true;
  740. return false;
  741. }
  742. const BlocksProtocol::DeviceStatus* getLastStatus (Block::UID deviceID) const noexcept
  743. {
  744. for (auto d : connectedDeviceGroups)
  745. if (auto status = d->getLastStatus (deviceID))
  746. return status;
  747. return nullptr;
  748. }
  749. void handleTopologyChange()
  750. {
  751. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  752. {
  753. juce::Array<DeviceInfo> newDeviceInfo;
  754. juce::Array<BlockDeviceConnection> newDeviceConnections;
  755. for (auto d : connectedDeviceGroups)
  756. {
  757. newDeviceInfo.addArray (d->currentDeviceInfo);
  758. newDeviceConnections.addArray (d->currentDeviceConnections);
  759. }
  760. for (int i = currentTopology.blocks.size(); --i >= 0;)
  761. {
  762. auto currentBlock = currentTopology.blocks.getUnchecked (i);
  763. auto newDeviceIter = std::find_if (newDeviceInfo.begin(), newDeviceInfo.end(),
  764. [&] (DeviceInfo& info) { return info.uid == currentBlock->uid; });
  765. if (newDeviceIter == newDeviceInfo.end())
  766. {
  767. if (auto bi = BlockImplementation::getFrom (*currentBlock))
  768. bi->invalidate();
  769. disconnectedBlocks.addIfNotAlreadyThere (currentTopology.blocks.removeAndReturn (i).get());
  770. }
  771. else
  772. {
  773. updateCurrentBlockInfo (currentBlock, *newDeviceIter);
  774. }
  775. }
  776. static const int maxBlocksToSave = 100;
  777. if (disconnectedBlocks.size() > maxBlocksToSave)
  778. disconnectedBlocks.removeRange (0, 2 * (disconnectedBlocks.size() - maxBlocksToSave));
  779. for (auto& info : newDeviceInfo)
  780. if (info.serial.isValid() && ! containsBlockWithUID (currentTopology.blocks, getBlockUIDFromSerialNumber (info.serial)))
  781. addBlock (info);
  782. currentTopology.connections.swapWith (newDeviceConnections);
  783. }
  784. topologyBroadcastThrottle.scheduleTopologyChangeCallback();
  785. }
  786. void handleSharedDataACK (Block::UID deviceID, uint32 packetCounter) const
  787. {
  788. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  789. if (auto* bi = getBlockImplementationWithUID (deviceID))
  790. bi->handleSharedDataACK (packetCounter);
  791. }
  792. void handleFirmwareUpdateACK (Block::UID deviceID, uint8 resultCode, uint32 resultDetail)
  793. {
  794. if (auto* bi = getBlockImplementationWithUID (deviceID))
  795. bi->handleFirmwareUpdateACK (resultCode, resultDetail);
  796. }
  797. void handleConfigUpdateMessage (Block::UID deviceID, int32 item, int32 value, int32 min, int32 max)
  798. {
  799. if (auto* bi = getBlockImplementationWithUID (deviceID))
  800. bi->handleConfigUpdateMessage (item, value, min, max);
  801. }
  802. void notifyBlockOfConfigChange (BlockImplementation& bi, uint32 item)
  803. {
  804. if (auto configChangedCallback = bi.configChangedCallback)
  805. {
  806. if (item >= bi.getMaxConfigIndex())
  807. configChangedCallback (bi, {}, item);
  808. else
  809. configChangedCallback (bi, bi.getLocalConfigMetaData (item), item);
  810. }
  811. }
  812. void handleConfigSetMessage (Block::UID deviceID, int32 item, int32 value)
  813. {
  814. if (auto* bi = getBlockImplementationWithUID (deviceID))
  815. {
  816. bi->handleConfigSetMessage (item, value);
  817. notifyBlockOfConfigChange (*bi, uint32 (item));
  818. }
  819. }
  820. void handleConfigFactorySyncEndMessage (Block::UID deviceID)
  821. {
  822. if (auto* bi = getBlockImplementationWithUID (deviceID))
  823. notifyBlockOfConfigChange (*bi, bi->getMaxConfigIndex());
  824. }
  825. void handleConfigFactorySyncResetMessage (Block::UID deviceID)
  826. {
  827. if (auto* bi = getBlockImplementationWithUID (deviceID))
  828. bi->resetConfigListActiveStatus();
  829. }
  830. void handleLogMessage (Block::UID deviceID, const String& message) const
  831. {
  832. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  833. if (auto* bi = getBlockImplementationWithUID (deviceID))
  834. bi->handleLogMessage (message);
  835. }
  836. void handleButtonChange (Block::UID deviceID, Block::Timestamp timestamp, uint32 buttonIndex, bool isDown) const
  837. {
  838. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  839. if (auto* bi = getBlockImplementationWithUID (deviceID))
  840. {
  841. bi->pingFromDevice();
  842. if (isPositiveAndBelow (buttonIndex, bi->getButtons().size()))
  843. if (auto* cbi = dynamic_cast<ControlButtonImplementation*> (bi->getButtons().getUnchecked (int (buttonIndex))))
  844. cbi->broadcastButtonChange (timestamp, bi->modelData.buttons[(int) buttonIndex].type, isDown);
  845. }
  846. }
  847. void handleTouchChange (Block::UID deviceID, const TouchSurface::Touch& touchEvent)
  848. {
  849. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  850. auto block = currentTopology.getBlockWithUID (deviceID);
  851. if (block != nullptr)
  852. {
  853. if (auto* surface = dynamic_cast<TouchSurfaceImplementation*> (block->getTouchSurface()))
  854. {
  855. TouchSurface::Touch scaledEvent (touchEvent);
  856. scaledEvent.x *= block->getWidth();
  857. scaledEvent.y *= block->getHeight();
  858. scaledEvent.startX *= block->getWidth();
  859. scaledEvent.startY *= block->getHeight();
  860. surface->broadcastTouchChange (scaledEvent);
  861. }
  862. }
  863. }
  864. void cancelAllActiveTouches() noexcept
  865. {
  866. for (auto& block : currentTopology.blocks)
  867. if (auto* surface = block->getTouchSurface())
  868. surface->cancelAllActiveTouches();
  869. }
  870. void handleCustomMessage (Block::UID deviceID, Block::Timestamp timestamp, const int32* data)
  871. {
  872. if (auto* bi = getBlockImplementationWithUID (deviceID))
  873. bi->handleCustomMessage (timestamp, data);
  874. }
  875. //==============================================================================
  876. int getIndexFromDeviceID (Block::UID deviceID) const noexcept
  877. {
  878. for (auto* c : connectedDeviceGroups)
  879. {
  880. auto index = c->getIndexFromDeviceID (deviceID);
  881. if (index >= 0)
  882. return index;
  883. }
  884. return -1;
  885. }
  886. template <typename PacketBuilder>
  887. bool sendMessageToDevice (Block::UID deviceID, const PacketBuilder& builder) const
  888. {
  889. for (auto* c : connectedDeviceGroups)
  890. if (c->getIndexFromDeviceID (deviceID) >= 0)
  891. return c->sendMessageToDevice (builder);
  892. return false;
  893. }
  894. static Detector* getFrom (Block& b) noexcept
  895. {
  896. if (auto* bi = BlockImplementation::getFrom (b))
  897. return (bi->detector);
  898. jassertfalse;
  899. return nullptr;
  900. }
  901. DeviceConnection* getDeviceConnectionFor (const Block& b)
  902. {
  903. for (const auto& d : connectedDeviceGroups)
  904. {
  905. for (const auto& info : d->currentDeviceInfo)
  906. {
  907. if (info.uid == b.uid)
  908. return d->getDeviceConnection();
  909. }
  910. }
  911. return nullptr;
  912. }
  913. const DeviceConnection* getDeviceConnectionFor (const Block& b) const
  914. {
  915. for (const auto& d : connectedDeviceGroups)
  916. {
  917. for (const auto& info : d->currentDeviceInfo)
  918. {
  919. if (info.uid == b.uid)
  920. return d->getDeviceConnection();
  921. }
  922. }
  923. return nullptr;
  924. }
  925. std::unique_ptr<MIDIDeviceDetector> defaultDetector;
  926. DeviceDetector& deviceDetector;
  927. juce::Array<PhysicalTopologySource*> activeTopologySources;
  928. BlockTopology currentTopology;
  929. juce::ReferenceCountedArray<Block, CriticalSection> disconnectedBlocks;
  930. private:
  931. void timerCallback() override
  932. {
  933. startTimer (1500);
  934. auto detectedDevices = deviceDetector.scanForDevices();
  935. handleDevicesRemoved (detectedDevices);
  936. handleDevicesAdded (detectedDevices);
  937. }
  938. void handleDevicesRemoved (const juce::StringArray& detectedDevices)
  939. {
  940. bool anyDevicesRemoved = false;
  941. for (int i = connectedDeviceGroups.size(); --i >= 0;)
  942. {
  943. if (! connectedDeviceGroups.getUnchecked(i)->isStillConnected (detectedDevices))
  944. {
  945. connectedDeviceGroups.remove (i);
  946. anyDevicesRemoved = true;
  947. }
  948. }
  949. if (anyDevicesRemoved)
  950. handleTopologyChange();
  951. }
  952. void handleDevicesAdded (const juce::StringArray& detectedDevices)
  953. {
  954. bool anyDevicesAdded = false;
  955. for (const auto& devName : detectedDevices)
  956. {
  957. if (! hasDeviceFor (devName))
  958. {
  959. if (auto d = deviceDetector.openDevice (detectedDevices.indexOf (devName)))
  960. {
  961. connectedDeviceGroups.add (new ConnectedDeviceGroup (*this, devName, d));
  962. anyDevicesAdded = true;
  963. }
  964. }
  965. }
  966. if (anyDevicesAdded)
  967. handleTopologyChange();
  968. }
  969. bool hasDeviceFor (const juce::String& devName) const
  970. {
  971. for (auto d : connectedDeviceGroups)
  972. if (d->deviceName == devName)
  973. return true;
  974. return false;
  975. }
  976. void addBlock (DeviceInfo info)
  977. {
  978. if (! reactivateBlockIfKnown (info))
  979. addNewBlock (info);
  980. }
  981. bool reactivateBlockIfKnown (DeviceInfo info)
  982. {
  983. auto uid = getBlockUIDFromSerialNumber (info.serial);
  984. for (int i = 0; i < disconnectedBlocks.size(); ++i)
  985. {
  986. if (disconnectedBlocks.getUnchecked (i)->uid == uid)
  987. {
  988. auto block = disconnectedBlocks.removeAndReturn (i);
  989. if (auto blockImpl = BlockImplementation::getFrom (*block))
  990. {
  991. blockImpl->revalidate (info.version, info.name, info.isMaster);
  992. currentTopology.blocks.add (block);
  993. return true;
  994. }
  995. }
  996. }
  997. return false;
  998. }
  999. void addNewBlock (DeviceInfo info)
  1000. {
  1001. currentTopology.blocks.add (new BlockImplementation (info.serial, *this, info.version,
  1002. info.name, info.isMaster));
  1003. }
  1004. void updateCurrentBlockInfo (Block::Ptr blockToUpdate, DeviceInfo& updatedInfo)
  1005. {
  1006. if (versionNumberChanged (updatedInfo, blockToUpdate->versionNumber))
  1007. setVersionNumberForBlock (updatedInfo, *blockToUpdate);
  1008. if (nameIsValid (updatedInfo))
  1009. setNameForBlock (updatedInfo, *blockToUpdate);
  1010. if (updatedInfo.isMaster != blockToUpdate->isMasterBlock())
  1011. BlockImplementation::getFrom (*blockToUpdate)->setToMaster (updatedInfo.isMaster);
  1012. }
  1013. BlockImplementation* getBlockImplementationWithUID (Block::UID deviceID) const noexcept
  1014. {
  1015. if (auto&& block = currentTopology.getBlockWithUID (deviceID))
  1016. return BlockImplementation::getFrom (*block);
  1017. return nullptr;
  1018. }
  1019. juce::OwnedArray<ConnectedDeviceGroup> connectedDeviceGroups;
  1020. //==============================================================================
  1021. /** Flurries of topology messages sometimes arrive due to loose connections.
  1022. Avoid informing listeners until they've stabilised.
  1023. */
  1024. struct TopologyBroadcastThrottle : private juce::Timer
  1025. {
  1026. TopologyBroadcastThrottle() = default;
  1027. void scheduleTopologyChangeCallback()
  1028. {
  1029. #ifdef JUCE_BLOCKS_TOPOLOGY_BROADCAST_THROTTLE_TIME
  1030. startTimer (JUCE_BLOCKS_TOPOLOGY_BROADCAST_THROTTLE_TIME);
  1031. #else
  1032. startTimer (750);
  1033. #endif
  1034. }
  1035. void timerCallback() override
  1036. {
  1037. if (detector->currentTopology != lastTopology)
  1038. {
  1039. lastTopology = detector->currentTopology;
  1040. BlocksTraverser traverser;
  1041. traverser.traverseBlockArray (detector->currentTopology);
  1042. for (auto* d : detector->activeTopologySources)
  1043. d->listeners.call ([] (TopologySource::Listener& l) { l.topologyChanged(); });
  1044. #if DUMP_TOPOLOGY
  1045. dumpTopology (lastTopology);
  1046. #endif
  1047. }
  1048. stopTimer();
  1049. }
  1050. Detector* detector = nullptr;
  1051. BlockTopology lastTopology;
  1052. };
  1053. TopologyBroadcastThrottle topologyBroadcastThrottle;
  1054. JUCE_DECLARE_WEAK_REFERENCEABLE (Detector)
  1055. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Detector)
  1056. };
  1057. //==============================================================================
  1058. /** This is a friend of the BlocksImplementation that will scan and set the
  1059. physical positions of the blocks */
  1060. struct BlocksTraverser
  1061. {
  1062. void traverseBlockArray (const BlockTopology& topology)
  1063. {
  1064. juce::Array<Block::UID> visited;
  1065. for (auto& block : topology.blocks)
  1066. {
  1067. if (block->isMasterBlock() && ! visited.contains (block->uid))
  1068. {
  1069. if (auto* bi = dynamic_cast<BlockImplementation*> (block))
  1070. {
  1071. bi->masterUID = {};
  1072. bi->position = {};
  1073. bi->rotation = 0;
  1074. }
  1075. layoutNeighbours (*block, topology, block->uid, visited);
  1076. }
  1077. }
  1078. }
  1079. // returns the distance from corner clockwise
  1080. int getUnitForIndex (Block::Ptr block, Block::ConnectionPort::DeviceEdge edge, int index)
  1081. {
  1082. if (block->getType() == Block::seaboardBlock)
  1083. {
  1084. if (edge == Block::ConnectionPort::DeviceEdge::north)
  1085. {
  1086. if (index == 0) return 1;
  1087. if (index == 1) return 4;
  1088. }
  1089. else if (edge == Block::ConnectionPort::DeviceEdge::east
  1090. || edge == Block::ConnectionPort::DeviceEdge::west)
  1091. {
  1092. return 1;
  1093. }
  1094. }
  1095. if (edge == Block::ConnectionPort::DeviceEdge::south)
  1096. return block->getWidth() - (index + 1);
  1097. if (edge == Block::ConnectionPort::DeviceEdge::west)
  1098. return block->getHeight() - (index + 1);
  1099. return index;
  1100. }
  1101. // returns how often north needs to rotate by 90 degrees
  1102. int getRotationForEdge (Block::ConnectionPort::DeviceEdge edge)
  1103. {
  1104. switch (edge)
  1105. {
  1106. case Block::ConnectionPort::DeviceEdge::north: return 0;
  1107. case Block::ConnectionPort::DeviceEdge::east: return 1;
  1108. case Block::ConnectionPort::DeviceEdge::south: return 2;
  1109. case Block::ConnectionPort::DeviceEdge::west: return 3;
  1110. }
  1111. jassertfalse;
  1112. return 0;
  1113. }
  1114. void layoutNeighbours (Block::Ptr block, const BlockTopology& topology,
  1115. Block::UID masterUid, juce::Array<Block::UID>& visited)
  1116. {
  1117. visited.add (block->uid);
  1118. for (auto& connection : topology.connections)
  1119. {
  1120. if ((connection.device1 == block->uid && ! visited.contains (connection.device2))
  1121. || (connection.device2 == block->uid && ! visited.contains (connection.device1)))
  1122. {
  1123. const auto theirUid = connection.device1 == block->uid ? connection.device2 : connection.device1;
  1124. const auto neighbourPtr = topology.getBlockWithUID (theirUid);
  1125. if (auto* neighbour = dynamic_cast<BlockImplementation*> (neighbourPtr.get()))
  1126. {
  1127. const auto myBounds = block->getBlockAreaWithinLayout();
  1128. const auto& myPort = connection.device1 == block->uid ? connection.connectionPortOnDevice1 : connection.connectionPortOnDevice2;
  1129. const auto& theirPort = connection.device1 == block->uid ? connection.connectionPortOnDevice2 : connection.connectionPortOnDevice1;
  1130. const auto myOffset = getUnitForIndex (block, myPort.edge, myPort.index);
  1131. const auto theirOffset = getUnitForIndex (neighbourPtr, theirPort.edge, theirPort.index);
  1132. neighbour->masterUID = masterUid;
  1133. neighbour->rotation = (2 + block->getRotation()
  1134. + getRotationForEdge (myPort.edge)
  1135. - getRotationForEdge (theirPort.edge)) % 4;
  1136. Point<int> delta;
  1137. const auto theirBounds = neighbour->getBlockAreaWithinLayout();
  1138. switch ((block->getRotation() + getRotationForEdge (myPort.edge)) % 4)
  1139. {
  1140. case 0: // over me
  1141. delta = { myOffset - (theirBounds.getWidth() - (theirOffset + 1)), -theirBounds.getHeight() };
  1142. break;
  1143. case 1: // right of me
  1144. delta = { myBounds.getWidth(), myOffset - (theirBounds.getHeight() - (theirOffset + 1)) };
  1145. break;
  1146. case 2: // under me
  1147. delta = { (myBounds.getWidth() - (myOffset + 1)) - theirOffset, myBounds.getHeight() };
  1148. break;
  1149. case 3: // left of me
  1150. delta = { -theirBounds.getWidth(), (myBounds.getHeight() - (myOffset + 1)) - theirOffset };
  1151. break;
  1152. }
  1153. neighbour->position = myBounds.getPosition() + delta;
  1154. }
  1155. layoutNeighbours (neighbourPtr, topology, masterUid, visited);
  1156. }
  1157. }
  1158. }
  1159. };
  1160. //==============================================================================
  1161. struct BlockImplementation : public Block,
  1162. private MIDIDeviceConnection::Listener,
  1163. private Timer
  1164. {
  1165. BlockImplementation (const BlocksProtocol::BlockSerialNumber& serial, Detector& detectorToUse, BlocksProtocol::VersionNumber version, BlocksProtocol::BlockName blockName, bool master)
  1166. : Block (juce::String ((const char*) serial.serial, sizeof (serial.serial)),
  1167. juce::String ((const char*) version.version, version.length),
  1168. juce::String ((const char*) blockName.name, blockName.length)),
  1169. modelData (serial),
  1170. remoteHeap (modelData.programAndHeapSize),
  1171. detector (&detectorToUse),
  1172. isMaster (master)
  1173. {
  1174. sendCommandMessage (BlocksProtocol::beginAPIMode);
  1175. if (modelData.hasTouchSurface)
  1176. touchSurface.reset (new TouchSurfaceImplementation (*this));
  1177. int i = 0;
  1178. for (auto&& b : modelData.buttons)
  1179. controlButtons.add (new ControlButtonImplementation (*this, i++, b));
  1180. if (modelData.lightGridWidth > 0 && modelData.lightGridHeight > 0)
  1181. ledGrid.reset (new LEDGridImplementation (*this));
  1182. for (auto&& s : modelData.statusLEDs)
  1183. statusLights.add (new StatusLightImplementation (*this, s));
  1184. if (modelData.numLEDRowLEDs > 0)
  1185. ledRow.reset (new LEDRowImplementation (*this));
  1186. updateMidiConnectionListener();
  1187. }
  1188. ~BlockImplementation()
  1189. {
  1190. if (listenerToMidiConnection != nullptr)
  1191. {
  1192. config.setDeviceComms (nullptr);
  1193. listenerToMidiConnection->removeListener (this);
  1194. }
  1195. }
  1196. void invalidate()
  1197. {
  1198. isStillConnected = false;
  1199. if (auto surface = dynamic_cast<TouchSurfaceImplementation*> (touchSurface.get()))
  1200. surface->disableTouchSurface();
  1201. }
  1202. void revalidate (BlocksProtocol::VersionNumber newVersion, BlocksProtocol::BlockName newName, bool master)
  1203. {
  1204. versionNumber = getVersionString (newVersion);
  1205. name = getNameString (newName);
  1206. isMaster = master;
  1207. isStillConnected = true;
  1208. if (auto surface = dynamic_cast<TouchSurfaceImplementation*> (touchSurface.get()))
  1209. surface->activateTouchSurface();
  1210. updateMidiConnectionListener();
  1211. }
  1212. void setToMaster (bool shouldBeMaster)
  1213. {
  1214. isMaster = shouldBeMaster;
  1215. }
  1216. void updateMidiConnectionListener()
  1217. {
  1218. if (detector == nullptr)
  1219. return;
  1220. listenerToMidiConnection = dynamic_cast<MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this));
  1221. if (listenerToMidiConnection != nullptr)
  1222. listenerToMidiConnection->addListener (this);
  1223. config.setDeviceComms (listenerToMidiConnection);
  1224. }
  1225. Type getType() const override { return modelData.apiType; }
  1226. juce::String getDeviceDescription() const override { return modelData.description; }
  1227. int getWidth() const override { return modelData.widthUnits; }
  1228. int getHeight() const override { return modelData.heightUnits; }
  1229. float getMillimetersPerUnit() const override { return 47.0f; }
  1230. bool isHardwareBlock() const override { return true; }
  1231. juce::Array<Block::ConnectionPort> getPorts() const override { return modelData.ports; }
  1232. bool isConnected() const override { return isStillConnected && detector && detector->isConnected (uid); }
  1233. bool isMasterBlock() const override { return isMaster; }
  1234. Block::UID getConnectedMasterUID() const override { return masterUID; }
  1235. int getRotation() const override { return rotation; }
  1236. Rectangle<int> getBlockAreaWithinLayout() const override
  1237. {
  1238. if (rotation % 2 == 0)
  1239. return { position.getX(), position.getY(), modelData.widthUnits, modelData.heightUnits };
  1240. return { position.getX(), position.getY(), modelData.heightUnits, modelData.widthUnits };
  1241. }
  1242. TouchSurface* getTouchSurface() const override { return touchSurface.get(); }
  1243. LEDGrid* getLEDGrid() const override { return ledGrid.get(); }
  1244. LEDRow* getLEDRow() const override { return ledRow.get(); }
  1245. juce::Array<ControlButton*> getButtons() const override
  1246. {
  1247. juce::Array<ControlButton*> result;
  1248. result.addArray (controlButtons);
  1249. return result;
  1250. }
  1251. juce::Array<StatusLight*> getStatusLights() const override
  1252. {
  1253. juce::Array<StatusLight*> result;
  1254. result.addArray (statusLights);
  1255. return result;
  1256. }
  1257. float getBatteryLevel() const override
  1258. {
  1259. if (detector == nullptr)
  1260. return 0.0f;
  1261. if (auto status = detector->getLastStatus (uid))
  1262. return status->batteryLevel.toUnipolarFloat();
  1263. return 0.0f;
  1264. }
  1265. bool isBatteryCharging() const override
  1266. {
  1267. if (detector == nullptr)
  1268. return false;
  1269. if (auto status = detector->getLastStatus (uid))
  1270. return status->batteryCharging.get() != 0;
  1271. return false;
  1272. }
  1273. bool supportsGraphics() const override
  1274. {
  1275. return false;
  1276. }
  1277. int getDeviceIndex() const noexcept
  1278. {
  1279. if (detector == nullptr)
  1280. return -1;
  1281. return isConnected() ? detector->getIndexFromDeviceID (uid) : -1;
  1282. }
  1283. template <typename PacketBuilder>
  1284. bool sendMessageToDevice (const PacketBuilder& builder)
  1285. {
  1286. if (detector != nullptr)
  1287. {
  1288. lastMessageSendTime = juce::Time::getCurrentTime();
  1289. return detector->sendMessageToDevice (uid, builder);
  1290. }
  1291. return false;
  1292. }
  1293. bool sendCommandMessage (uint32 commandID)
  1294. {
  1295. int index = getDeviceIndex();
  1296. if (index < 0)
  1297. return false;
  1298. BlocksProtocol::HostPacketBuilder<64> p;
  1299. p.writePacketSysexHeaderBytes ((BlocksProtocol::TopologyIndex) index);
  1300. p.deviceControlMessage (commandID);
  1301. p.writePacketSysexFooter();
  1302. return sendMessageToDevice (p);
  1303. }
  1304. void handleCustomMessage (Block::Timestamp, const int32* data)
  1305. {
  1306. ProgramEventMessage m;
  1307. for (uint32 i = 0; i < BlocksProtocol::numProgramMessageInts; ++i)
  1308. m.values[i] = data[i];
  1309. programEventListeners.call ([&] (ProgramEventListener& l) { l.handleProgramEvent (*this, m); });
  1310. }
  1311. static BlockImplementation* getFrom (Block& b) noexcept
  1312. {
  1313. if (auto bi = dynamic_cast<BlockImplementation*> (&b))
  1314. return bi;
  1315. jassertfalse;
  1316. return nullptr;
  1317. }
  1318. bool isControlBlock() const
  1319. {
  1320. auto type = getType();
  1321. return type == Block::Type::liveBlock
  1322. || type == Block::Type::loopBlock
  1323. || type == Block::Type::touchBlock
  1324. || type == Block::Type::developerControlBlock;
  1325. }
  1326. //==============================================================================
  1327. std::function<void(const String&)> logger;
  1328. void setLogger (std::function<void(const String&)> newLogger) override
  1329. {
  1330. logger = newLogger;
  1331. }
  1332. void handleLogMessage (const String& message) const
  1333. {
  1334. if (logger != nullptr)
  1335. logger (message);
  1336. }
  1337. //==============================================================================
  1338. juce::Result setProgram (Program* newProgram) override
  1339. {
  1340. if (newProgram == nullptr || program.get() != newProgram)
  1341. {
  1342. {
  1343. std::unique_ptr<Program> p (newProgram);
  1344. if (program != nullptr
  1345. && newProgram != nullptr
  1346. && program->getLittleFootProgram() == newProgram->getLittleFootProgram())
  1347. return juce::Result::ok();
  1348. stopTimer();
  1349. std::swap (program, p);
  1350. }
  1351. stopTimer();
  1352. programSize = 0;
  1353. isProgramLoaded = shouldSaveProgramAsDefault = false;
  1354. if (program != nullptr)
  1355. {
  1356. littlefoot::Compiler compiler;
  1357. compiler.addNativeFunctions (PhysicalTopologySource::getStandardLittleFootFunctions());
  1358. auto err = compiler.compile (program->getLittleFootProgram(), 512, program->getSearchPaths());
  1359. if (err.failed())
  1360. return err;
  1361. DBG ("Compiled littlefoot program, space needed: "
  1362. << (int) compiler.getCompiledProgram().getTotalSpaceNeeded() << " bytes");
  1363. if (compiler.getCompiledProgram().getTotalSpaceNeeded() > getMemorySize())
  1364. return Result::fail ("Program too large!");
  1365. auto size = (size_t) compiler.compiledObjectCode.size();
  1366. programSize = (uint32) size;
  1367. remoteHeap.resetDataRangeToUnknown (0, remoteHeap.blockSize);
  1368. remoteHeap.clear();
  1369. remoteHeap.sendChanges (*this, true);
  1370. remoteHeap.resetDataRangeToUnknown (0, (uint32) size);
  1371. remoteHeap.setBytes (0, compiler.compiledObjectCode.begin(), size);
  1372. remoteHeap.sendChanges (*this, true);
  1373. this->resetConfigListActiveStatus();
  1374. if (auto changeCallback = this->configChangedCallback)
  1375. changeCallback (*this, {}, this->getMaxConfigIndex());
  1376. startTimer (20);
  1377. }
  1378. else
  1379. {
  1380. remoteHeap.clear();
  1381. }
  1382. }
  1383. else
  1384. {
  1385. jassertfalse;
  1386. }
  1387. return juce::Result::ok();
  1388. }
  1389. Program* getProgram() const override { return program.get(); }
  1390. void sendProgramEvent (const ProgramEventMessage& message) override
  1391. {
  1392. static_assert (sizeof (ProgramEventMessage::values) == 4 * BlocksProtocol::numProgramMessageInts,
  1393. "Need to keep the internal and external messages structures the same");
  1394. if (remoteHeap.isProgramLoaded())
  1395. {
  1396. auto index = getDeviceIndex();
  1397. if (index >= 0)
  1398. {
  1399. BlocksProtocol::HostPacketBuilder<128> p;
  1400. p.writePacketSysexHeaderBytes ((BlocksProtocol::TopologyIndex) index);
  1401. if (p.addProgramEventMessage (message.values))
  1402. {
  1403. p.writePacketSysexFooter();
  1404. sendMessageToDevice (p);
  1405. }
  1406. }
  1407. else
  1408. {
  1409. jassertfalse;
  1410. }
  1411. }
  1412. }
  1413. void timerCallback() override
  1414. {
  1415. if (remoteHeap.isFullySynced() && remoteHeap.isProgramLoaded())
  1416. {
  1417. isProgramLoaded = true;
  1418. stopTimer();
  1419. if (shouldSaveProgramAsDefault)
  1420. doSaveProgramAsDefault();
  1421. if (programLoadedCallback != nullptr)
  1422. programLoadedCallback (*this);
  1423. }
  1424. else
  1425. {
  1426. startTimer (100);
  1427. }
  1428. }
  1429. void saveProgramAsDefault() override
  1430. {
  1431. shouldSaveProgramAsDefault = true;
  1432. if (! isTimerRunning() && isProgramLoaded)
  1433. doSaveProgramAsDefault();
  1434. }
  1435. uint32 getMemorySize() override
  1436. {
  1437. return modelData.programAndHeapSize;
  1438. }
  1439. uint32 getHeapMemorySize() override
  1440. {
  1441. jassert (isPositiveAndNotGreaterThan (programSize, modelData.programAndHeapSize));
  1442. return modelData.programAndHeapSize - programSize;
  1443. }
  1444. void setDataByte (size_t offset, uint8 value) override
  1445. {
  1446. remoteHeap.setByte (programSize + offset, value);
  1447. }
  1448. void setDataBytes (size_t offset, const void* newData, size_t num) override
  1449. {
  1450. remoteHeap.setBytes (programSize + offset, static_cast<const uint8*> (newData), num);
  1451. }
  1452. void setDataBits (uint32 startBit, uint32 numBits, uint32 value) override
  1453. {
  1454. remoteHeap.setBits (programSize * 8 + startBit, numBits, value);
  1455. }
  1456. uint8 getDataByte (size_t offset) override
  1457. {
  1458. return remoteHeap.getByte (programSize + offset);
  1459. }
  1460. void handleSharedDataACK (uint32 packetCounter) noexcept
  1461. {
  1462. pingFromDevice();
  1463. remoteHeap.handleACKFromDevice (*this, packetCounter);
  1464. }
  1465. bool sendFirmwareUpdatePacket (const uint8* data, uint8 size, std::function<void (uint8, uint32)> callback) override
  1466. {
  1467. firmwarePacketAckCallback = {};
  1468. auto index = getDeviceIndex();
  1469. if (index >= 0)
  1470. {
  1471. BlocksProtocol::HostPacketBuilder<256> p;
  1472. p.writePacketSysexHeaderBytes ((BlocksProtocol::TopologyIndex) index);
  1473. if (p.addFirmwareUpdatePacket (data, size))
  1474. {
  1475. p.writePacketSysexFooter();
  1476. if (sendMessageToDevice (p))
  1477. {
  1478. firmwarePacketAckCallback = callback;
  1479. return true;
  1480. }
  1481. }
  1482. }
  1483. else
  1484. {
  1485. jassertfalse;
  1486. }
  1487. return false;
  1488. }
  1489. void handleFirmwareUpdateACK (uint8 resultCode, uint32 resultDetail)
  1490. {
  1491. if (firmwarePacketAckCallback != nullptr)
  1492. {
  1493. firmwarePacketAckCallback (resultCode, resultDetail);
  1494. firmwarePacketAckCallback = {};
  1495. }
  1496. }
  1497. void handleConfigUpdateMessage (int32 item, int32 value, int32 min, int32 max)
  1498. {
  1499. config.handleConfigUpdateMessage (item, value, min, max);
  1500. }
  1501. void handleConfigSetMessage(int32 item, int32 value)
  1502. {
  1503. config.handleConfigSetMessage (item, value);
  1504. }
  1505. void pingFromDevice()
  1506. {
  1507. lastMessageReceiveTime = juce::Time::getCurrentTime();
  1508. }
  1509. void addDataInputPortListener (DataInputPortListener* listener) override
  1510. {
  1511. Block::addDataInputPortListener (listener);
  1512. if (auto midiInput = getMidiInput())
  1513. midiInput->start();
  1514. }
  1515. void sendMessage (const void* message, size_t messageSize) override
  1516. {
  1517. if (auto midiOutput = getMidiOutput())
  1518. midiOutput->sendMessageNow ({ message, (int) messageSize });
  1519. }
  1520. void handleTimerTick()
  1521. {
  1522. if (++resetMessagesSent < 3)
  1523. {
  1524. if (resetMessagesSent == 1)
  1525. sendCommandMessage (BlocksProtocol::endAPIMode);
  1526. sendCommandMessage (BlocksProtocol::beginAPIMode);
  1527. return;
  1528. }
  1529. if (ledGrid != nullptr)
  1530. if (auto renderer = ledGrid->getRenderer())
  1531. renderer->renderLEDGrid (*ledGrid);
  1532. remoteHeap.sendChanges (*this, false);
  1533. if (lastMessageSendTime < juce::Time::getCurrentTime() - juce::RelativeTime::milliseconds (pingIntervalMs))
  1534. sendCommandMessage (BlocksProtocol::ping);
  1535. }
  1536. //==============================================================================
  1537. int32 getLocalConfigValue (uint32 item) override
  1538. {
  1539. initialiseDeviceIndexAndConnection();
  1540. return config.getItemValue ((BlocksProtocol::ConfigItemId) item);
  1541. }
  1542. void setLocalConfigValue (uint32 item, int32 value) override
  1543. {
  1544. initialiseDeviceIndexAndConnection();
  1545. config.setItemValue ((BlocksProtocol::ConfigItemId) item, value);
  1546. }
  1547. void setLocalConfigRange (uint32 item, int32 min, int32 max) override
  1548. {
  1549. initialiseDeviceIndexAndConnection();
  1550. config.setItemMin ((BlocksProtocol::ConfigItemId) item, min);
  1551. config.setItemMax ((BlocksProtocol::ConfigItemId) item, max);
  1552. }
  1553. void setLocalConfigItemActive (uint32 item, bool isActive) override
  1554. {
  1555. initialiseDeviceIndexAndConnection();
  1556. config.setItemActive ((BlocksProtocol::ConfigItemId) item, isActive);
  1557. }
  1558. bool isLocalConfigItemActive (uint32 item) override
  1559. {
  1560. initialiseDeviceIndexAndConnection();
  1561. return config.getItemActive ((BlocksProtocol::ConfigItemId) item);
  1562. }
  1563. uint32 getMaxConfigIndex() override
  1564. {
  1565. return uint32 (BlocksProtocol::maxConfigIndex);
  1566. }
  1567. bool isValidUserConfigIndex (uint32 item) override
  1568. {
  1569. return item >= (uint32) BlocksProtocol::ConfigItemId::user0
  1570. && item < (uint32) (BlocksProtocol::ConfigItemId::user0 + numberOfUserConfigs);
  1571. }
  1572. ConfigMetaData getLocalConfigMetaData (uint32 item) override
  1573. {
  1574. initialiseDeviceIndexAndConnection();
  1575. return config.getMetaData ((BlocksProtocol::ConfigItemId) item);
  1576. }
  1577. void requestFactoryConfigSync() override
  1578. {
  1579. initialiseDeviceIndexAndConnection();
  1580. config.requestFactoryConfigSync();
  1581. }
  1582. void resetConfigListActiveStatus() override
  1583. {
  1584. config.resetConfigListActiveStatus();
  1585. }
  1586. void setConfigChangedCallback (std::function<void(Block&, const ConfigMetaData&, uint32)> configChanged) override
  1587. {
  1588. configChangedCallback = std::move (configChanged);
  1589. }
  1590. void setProgramLoadedCallback (std::function<void(Block&)> programLoaded) override
  1591. {
  1592. programLoadedCallback = std::move (programLoaded);
  1593. }
  1594. void factoryReset() override
  1595. {
  1596. auto index = getDeviceIndex();
  1597. if (index >= 0)
  1598. {
  1599. BlocksProtocol::HostPacketBuilder<32> p;
  1600. p.writePacketSysexHeaderBytes ((BlocksProtocol::TopologyIndex) index);
  1601. p.addFactoryReset();
  1602. p.writePacketSysexFooter();
  1603. sendMessageToDevice (p);
  1604. }
  1605. else
  1606. {
  1607. jassertfalse;
  1608. }
  1609. }
  1610. void blockReset() override
  1611. {
  1612. auto index = getDeviceIndex();
  1613. if (index >= 0)
  1614. {
  1615. BlocksProtocol::HostPacketBuilder<32> p;
  1616. p.writePacketSysexHeaderBytes ((BlocksProtocol::TopologyIndex) index);
  1617. p.addBlockReset();
  1618. p.writePacketSysexFooter();
  1619. sendMessageToDevice (p);
  1620. }
  1621. else
  1622. {
  1623. jassertfalse;
  1624. }
  1625. }
  1626. bool setName (const juce::String& newName) override
  1627. {
  1628. auto index = getDeviceIndex();
  1629. if (index >= 0)
  1630. {
  1631. BlocksProtocol::HostPacketBuilder<128> p;
  1632. p.writePacketSysexHeaderBytes ((BlocksProtocol::TopologyIndex) index);
  1633. if (p.addSetBlockName (newName))
  1634. {
  1635. p.writePacketSysexFooter();
  1636. if (sendMessageToDevice (p))
  1637. return true;
  1638. }
  1639. }
  1640. else
  1641. {
  1642. jassertfalse;
  1643. }
  1644. return false;
  1645. }
  1646. //==============================================================================
  1647. std::unique_ptr<TouchSurface> touchSurface;
  1648. juce::OwnedArray<ControlButton> controlButtons;
  1649. std::unique_ptr<LEDGridImplementation> ledGrid;
  1650. std::unique_ptr<LEDRowImplementation> ledRow;
  1651. juce::OwnedArray<StatusLight> statusLights;
  1652. BlocksProtocol::BlockDataSheet modelData;
  1653. MIDIDeviceConnection* listenerToMidiConnection = nullptr;
  1654. static constexpr int pingIntervalMs = 400;
  1655. static constexpr uint32 maxBlockSize = BlocksProtocol::padBlockProgramAndHeapSize;
  1656. static constexpr uint32 maxPacketCounter = BlocksProtocol::PacketCounter::maxValue;
  1657. static constexpr uint32 maxPacketSize = 200;
  1658. using PacketBuilder = BlocksProtocol::HostPacketBuilder<maxPacketSize>;
  1659. using RemoteHeapType = littlefoot::LittleFootRemoteHeap<BlockImplementation>;
  1660. RemoteHeapType remoteHeap;
  1661. WeakReference<Detector> detector;
  1662. juce::Time lastMessageSendTime, lastMessageReceiveTime;
  1663. BlockConfigManager config;
  1664. std::function<void(Block&, const ConfigMetaData&, uint32)> configChangedCallback;
  1665. std::function<void(Block&)> programLoadedCallback;
  1666. private:
  1667. std::unique_ptr<Program> program;
  1668. uint32 programSize = 0;
  1669. std::function<void(uint8, uint32)> firmwarePacketAckCallback;
  1670. uint32 resetMessagesSent = 0;
  1671. bool isStillConnected = true;
  1672. bool isMaster = false;
  1673. Block::UID masterUID = {};
  1674. Point<int> position;
  1675. int rotation = 0;
  1676. friend BlocksTraverser;
  1677. bool isProgramLoaded = false;
  1678. bool shouldSaveProgramAsDefault = false;
  1679. void initialiseDeviceIndexAndConnection()
  1680. {
  1681. config.setDeviceIndex ((TopologyIndex) getDeviceIndex());
  1682. config.setDeviceComms (listenerToMidiConnection);
  1683. }
  1684. const juce::MidiInput* getMidiInput() const
  1685. {
  1686. if (detector != nullptr)
  1687. if (auto c = dynamic_cast<const MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this)))
  1688. return c->midiInput.get();
  1689. jassertfalse;
  1690. return nullptr;
  1691. }
  1692. juce::MidiInput* getMidiInput()
  1693. {
  1694. return const_cast<juce::MidiInput*> (static_cast<const BlockImplementation&>(*this).getMidiInput());
  1695. }
  1696. const juce::MidiOutput* getMidiOutput() const
  1697. {
  1698. if (detector != nullptr)
  1699. if (auto c = dynamic_cast<const MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this)))
  1700. return c->midiOutput.get();
  1701. jassertfalse;
  1702. return nullptr;
  1703. }
  1704. juce::MidiOutput* getMidiOutput()
  1705. {
  1706. return const_cast<juce::MidiOutput*> (static_cast<const BlockImplementation&>(*this).getMidiOutput());
  1707. }
  1708. void handleIncomingMidiMessage (const juce::MidiMessage& message) override
  1709. {
  1710. dataInputPortListeners.call ([&] (DataInputPortListener& l) { l.handleIncomingDataPortMessage (*this, message.getRawData(),
  1711. (size_t) message.getRawDataSize()); });
  1712. }
  1713. void connectionBeingDeleted (const MIDIDeviceConnection& c) override
  1714. {
  1715. jassert (listenerToMidiConnection == &c);
  1716. juce::ignoreUnused (c);
  1717. listenerToMidiConnection->removeListener (this);
  1718. listenerToMidiConnection = nullptr;
  1719. config.setDeviceComms (nullptr);
  1720. }
  1721. void doSaveProgramAsDefault()
  1722. {
  1723. sendCommandMessage (BlocksProtocol::saveProgramAsDefault);
  1724. }
  1725. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockImplementation)
  1726. };
  1727. //==============================================================================
  1728. struct LEDRowImplementation : public LEDRow,
  1729. private Timer
  1730. {
  1731. LEDRowImplementation (BlockImplementation& b) : LEDRow (b)
  1732. {
  1733. startTimer (300);
  1734. }
  1735. void setButtonColour (uint32 index, LEDColour colour)
  1736. {
  1737. if (index < 10)
  1738. {
  1739. colours[index] = colour;
  1740. flush();
  1741. }
  1742. }
  1743. int getNumLEDs() const override
  1744. {
  1745. return static_cast<const BlockImplementation&> (block).modelData.numLEDRowLEDs;
  1746. }
  1747. void setLEDColour (int index, LEDColour colour) override
  1748. {
  1749. if ((uint32) index < 15u)
  1750. {
  1751. colours[10 + index] = colour;
  1752. flush();
  1753. }
  1754. }
  1755. void setOverlayColour (LEDColour colour) override
  1756. {
  1757. colours[25] = colour;
  1758. flush();
  1759. }
  1760. void resetOverlayColour() override
  1761. {
  1762. setOverlayColour ({});
  1763. }
  1764. private:
  1765. LEDColour colours[26];
  1766. void timerCallback() override
  1767. {
  1768. stopTimer();
  1769. loadProgramOntoBlock();
  1770. flush();
  1771. }
  1772. void loadProgramOntoBlock()
  1773. {
  1774. if (block.getProgram() == nullptr)
  1775. {
  1776. auto err = block.setProgram (new DefaultLEDGridProgram (block));
  1777. if (err.failed())
  1778. {
  1779. DBG (err.getErrorMessage());
  1780. jassertfalse;
  1781. }
  1782. }
  1783. }
  1784. void flush()
  1785. {
  1786. if (block.getProgram() != nullptr)
  1787. for (uint32 i = 0; i < (uint32) numElementsInArray (colours); ++i)
  1788. write565Colour (16 * i, colours[i]);
  1789. }
  1790. void write565Colour (uint32 bitIndex, LEDColour colour)
  1791. {
  1792. block.setDataBits (bitIndex, 5, colour.getRed() >> 3);
  1793. block.setDataBits (bitIndex + 5, 6, colour.getGreen() >> 2);
  1794. block.setDataBits (bitIndex + 11, 5, colour.getBlue() >> 3);
  1795. }
  1796. struct DefaultLEDGridProgram : public Block::Program
  1797. {
  1798. DefaultLEDGridProgram (Block& b) : Block::Program (b) {}
  1799. juce::String getLittleFootProgram() override
  1800. {
  1801. /* Data format:
  1802. 0: 10 x 5-6-5 bits for button LED RGBs
  1803. 20: 15 x 5-6-5 bits for LED row colours
  1804. 50: 1 x 5-6-5 bits for LED row overlay colour
  1805. */
  1806. return R"littlefoot(
  1807. #heapsize: 128
  1808. int getColour (int bitIndex)
  1809. {
  1810. return makeARGB (255,
  1811. getHeapBits (bitIndex, 5) << 3,
  1812. getHeapBits (bitIndex + 5, 6) << 2,
  1813. getHeapBits (bitIndex + 11, 5) << 3);
  1814. }
  1815. int getButtonColour (int index)
  1816. {
  1817. return getColour (16 * index);
  1818. }
  1819. int getLEDColour (int index)
  1820. {
  1821. if (getHeapInt (50))
  1822. return getColour (50 * 8);
  1823. return getColour (20 * 8 + 16 * index);
  1824. }
  1825. void repaint()
  1826. {
  1827. for (int x = 0; x < 15; ++x)
  1828. fillPixel (getLEDColour (x), x, 0);
  1829. for (int i = 0; i < 10; ++i)
  1830. fillPixel (getButtonColour (i), i, 1);
  1831. }
  1832. void handleMessage (int p1, int p2) {}
  1833. )littlefoot";
  1834. }
  1835. };
  1836. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDRowImplementation)
  1837. };
  1838. //==============================================================================
  1839. struct TouchSurfaceImplementation : public TouchSurface,
  1840. private juce::Timer
  1841. {
  1842. TouchSurfaceImplementation (BlockImplementation& b) : TouchSurface (b), blockImpl (b)
  1843. {
  1844. activateTouchSurface();
  1845. }
  1846. ~TouchSurfaceImplementation()
  1847. {
  1848. disableTouchSurface();
  1849. }
  1850. void activateTouchSurface()
  1851. {
  1852. startTimer (500);
  1853. }
  1854. void disableTouchSurface()
  1855. {
  1856. stopTimer();
  1857. }
  1858. int getNumberOfKeywaves() const noexcept override
  1859. {
  1860. return blockImpl.modelData.numKeywaves;
  1861. }
  1862. void broadcastTouchChange (const TouchSurface::Touch& touchEvent)
  1863. {
  1864. auto& status = touches.getValue (touchEvent);
  1865. // Fake a touch end if we receive a duplicate touch-start with no preceding touch-end (ie: comms error)
  1866. if (touchEvent.isTouchStart && status.isActive)
  1867. killTouch (touchEvent, status, juce::Time::getMillisecondCounter());
  1868. // Fake a touch start if we receive an unexpected event with no matching start event. (ie: comms error)
  1869. if (! touchEvent.isTouchStart && ! status.isActive)
  1870. {
  1871. TouchSurface::Touch t (touchEvent);
  1872. t.isTouchStart = true;
  1873. t.isTouchEnd = false;
  1874. if (t.zVelocity <= 0) t.zVelocity = status.lastStrikePressure;
  1875. if (t.zVelocity <= 0) t.zVelocity = t.z;
  1876. if (t.zVelocity <= 0) t.zVelocity = 0.9f;
  1877. listeners.call ([&] (TouchSurface::Listener& l) { l.touchChanged (*this, t); });
  1878. }
  1879. // Normal handling:
  1880. status.lastEventTime = juce::Time::getMillisecondCounter();
  1881. status.isActive = ! touchEvent.isTouchEnd;
  1882. if (touchEvent.isTouchStart)
  1883. status.lastStrikePressure = touchEvent.zVelocity;
  1884. listeners.call ([&] (TouchSurface::Listener& l) { l.touchChanged (*this, touchEvent); });
  1885. }
  1886. void timerCallback() override
  1887. {
  1888. // Find touches that seem to have become stuck, and fake a touch-end for them..
  1889. static const uint32 touchTimeOutMs = 500;
  1890. for (auto& t : touches)
  1891. {
  1892. auto& status = t.value;
  1893. auto now = juce::Time::getMillisecondCounter();
  1894. if (status.isActive && now > status.lastEventTime + touchTimeOutMs)
  1895. killTouch (t.touch, status, now);
  1896. }
  1897. }
  1898. struct TouchStatus
  1899. {
  1900. uint32 lastEventTime = 0;
  1901. float lastStrikePressure = 0;
  1902. bool isActive = false;
  1903. };
  1904. void killTouch (const TouchSurface::Touch& touch, TouchStatus& status, uint32 timeStamp) noexcept
  1905. {
  1906. jassert (status.isActive);
  1907. TouchSurface::Touch killTouch (touch);
  1908. killTouch.z = 0;
  1909. killTouch.xVelocity = 0;
  1910. killTouch.yVelocity = 0;
  1911. killTouch.zVelocity = -1.0f;
  1912. killTouch.eventTimestamp = timeStamp;
  1913. killTouch.isTouchStart = false;
  1914. killTouch.isTouchEnd = true;
  1915. listeners.call ([&] (TouchSurface::Listener& l) { l.touchChanged (*this, killTouch); });
  1916. status.isActive = false;
  1917. }
  1918. void cancelAllActiveTouches() noexcept override
  1919. {
  1920. const auto now = juce::Time::getMillisecondCounter();
  1921. for (auto& t : touches)
  1922. if (t.value.isActive)
  1923. killTouch (t.touch, t.value, now);
  1924. touches.clear();
  1925. }
  1926. BlockImplementation& blockImpl;
  1927. TouchList<TouchStatus> touches;
  1928. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TouchSurfaceImplementation)
  1929. };
  1930. //==============================================================================
  1931. struct ControlButtonImplementation : public ControlButton
  1932. {
  1933. ControlButtonImplementation (BlockImplementation& b, int index, BlocksProtocol::BlockDataSheet::ButtonInfo info)
  1934. : ControlButton (b), blockImpl (b), buttonInfo (info), buttonIndex (index)
  1935. {
  1936. }
  1937. ~ControlButtonImplementation()
  1938. {
  1939. }
  1940. ButtonFunction getType() const override { return buttonInfo.type; }
  1941. juce::String getName() const override { return BlocksProtocol::getButtonNameForFunction (buttonInfo.type); }
  1942. float getPositionX() const override { return buttonInfo.x; }
  1943. float getPositionY() const override { return buttonInfo.y; }
  1944. bool hasLight() const override { return blockImpl.isControlBlock(); }
  1945. bool setLightColour (LEDColour colour) override
  1946. {
  1947. if (hasLight())
  1948. {
  1949. if (auto row = blockImpl.ledRow.get())
  1950. {
  1951. row->setButtonColour ((uint32) buttonIndex, colour);
  1952. return true;
  1953. }
  1954. }
  1955. return false;
  1956. }
  1957. void broadcastButtonChange (Block::Timestamp timestamp, ControlButton::ButtonFunction button, bool isDown)
  1958. {
  1959. if (button == buttonInfo.type)
  1960. {
  1961. if (wasDown == isDown)
  1962. sendButtonChangeToListeners (timestamp, ! isDown);
  1963. sendButtonChangeToListeners (timestamp, isDown);
  1964. wasDown = isDown;
  1965. }
  1966. }
  1967. void sendButtonChangeToListeners (Block::Timestamp timestamp, bool isDown)
  1968. {
  1969. if (isDown)
  1970. listeners.call ([&] (ControlButton::Listener& l) { l.buttonPressed (*this, timestamp); });
  1971. else
  1972. listeners.call ([&] (ControlButton::Listener& l) { l.buttonReleased (*this, timestamp); });
  1973. }
  1974. BlockImplementation& blockImpl;
  1975. BlocksProtocol::BlockDataSheet::ButtonInfo buttonInfo;
  1976. int buttonIndex;
  1977. bool wasDown = false;
  1978. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControlButtonImplementation)
  1979. };
  1980. //==============================================================================
  1981. struct StatusLightImplementation : public StatusLight
  1982. {
  1983. StatusLightImplementation (Block& b, BlocksProtocol::BlockDataSheet::StatusLEDInfo i) : StatusLight (b), info (i)
  1984. {
  1985. }
  1986. juce::String getName() const override { return info.name; }
  1987. bool setColour (LEDColour newColour) override
  1988. {
  1989. // XXX TODO!
  1990. juce::ignoreUnused (newColour);
  1991. return false;
  1992. }
  1993. BlocksProtocol::BlockDataSheet::StatusLEDInfo info;
  1994. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StatusLightImplementation)
  1995. };
  1996. //==============================================================================
  1997. struct LEDGridImplementation : public LEDGrid
  1998. {
  1999. LEDGridImplementation (BlockImplementation& b) : LEDGrid (b), blockImpl (b)
  2000. {
  2001. }
  2002. int getNumColumns() const override { return blockImpl.modelData.lightGridWidth; }
  2003. int getNumRows() const override { return blockImpl.modelData.lightGridHeight; }
  2004. BlockImplementation& blockImpl;
  2005. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDGridImplementation)
  2006. };
  2007. //==============================================================================
  2008. #if DUMP_TOPOLOGY
  2009. static juce::String idToSerialNum (const BlockTopology& topology, Block::UID uid)
  2010. {
  2011. for (auto* b : topology.blocks)
  2012. if (b->uid == uid)
  2013. return b->serialNumber;
  2014. return "???";
  2015. }
  2016. static juce::String portEdgeToString (Block::ConnectionPort port)
  2017. {
  2018. switch (port.edge)
  2019. {
  2020. case Block::ConnectionPort::DeviceEdge::north: return "north";
  2021. case Block::ConnectionPort::DeviceEdge::south: return "south";
  2022. case Block::ConnectionPort::DeviceEdge::east: return "east";
  2023. case Block::ConnectionPort::DeviceEdge::west: return "west";
  2024. }
  2025. return {};
  2026. }
  2027. static juce::String portToString (Block::ConnectionPort port)
  2028. {
  2029. return portEdgeToString (port) + "_" + juce::String (port.index);
  2030. }
  2031. static void dumpTopology (const BlockTopology& topology)
  2032. {
  2033. MemoryOutputStream m;
  2034. m << "=============================================================================" << newLine
  2035. << "Topology: " << topology.blocks.size() << " device(s)" << newLine
  2036. << newLine;
  2037. int index = 0;
  2038. for (auto block : topology.blocks)
  2039. {
  2040. m << "Device " << index++ << (block->isMasterBlock() ? ": (MASTER)" : ":") << newLine;
  2041. m << " Description: " << block->getDeviceDescription() << newLine
  2042. << " Serial: " << block->serialNumber << newLine;
  2043. if (auto bi = BlockImplementation::getFrom (*block))
  2044. m << " Short address: " << (int) bi->getDeviceIndex() << newLine;
  2045. m << " Battery level: " + juce::String (juce::roundToInt (100.0f * block->getBatteryLevel())) + "%" << newLine
  2046. << " Battery charging: " + juce::String (block->isBatteryCharging() ? "y" : "n") << newLine
  2047. << " Width: " << block->getWidth() << newLine
  2048. << " Height: " << block->getHeight() << newLine
  2049. << " Millimeters per unit: " << block->getMillimetersPerUnit() << newLine
  2050. << newLine;
  2051. }
  2052. for (auto& connection : topology.connections)
  2053. {
  2054. m << idToSerialNum (topology, connection.device1)
  2055. << ":" << portToString (connection.connectionPortOnDevice1)
  2056. << " <-> "
  2057. << idToSerialNum (topology, connection.device2)
  2058. << ":" << portToString (connection.connectionPortOnDevice2) << newLine;
  2059. }
  2060. m << "=============================================================================" << newLine;
  2061. Logger::outputDebugString (m.toString());
  2062. }
  2063. #endif
  2064. };
  2065. //==============================================================================
  2066. struct PhysicalTopologySource::DetectorHolder : private juce::Timer
  2067. {
  2068. DetectorHolder (PhysicalTopologySource& pts)
  2069. : topologySource (pts),
  2070. detector (Internal::Detector::getDefaultDetector())
  2071. {
  2072. startTimerHz (30);
  2073. }
  2074. DetectorHolder (PhysicalTopologySource& pts, DeviceDetector& dd)
  2075. : topologySource (pts),
  2076. detector (new Internal::Detector (dd))
  2077. {
  2078. startTimerHz (30);
  2079. }
  2080. void timerCallback() override
  2081. {
  2082. if (! topologySource.hasOwnServiceTimer())
  2083. handleTimerTick();
  2084. }
  2085. void handleTimerTick()
  2086. {
  2087. for (auto& b : detector->currentTopology.blocks)
  2088. if (auto bi = Internal::BlockImplementation::getFrom (*b))
  2089. bi->handleTimerTick();
  2090. }
  2091. PhysicalTopologySource& topologySource;
  2092. Internal::Detector::Ptr detector;
  2093. };
  2094. //==============================================================================
  2095. PhysicalTopologySource::PhysicalTopologySource (bool startDetached)
  2096. {
  2097. if (! startDetached)
  2098. setActive (true);
  2099. }
  2100. PhysicalTopologySource::PhysicalTopologySource (DeviceDetector& detectorToUse, bool startDetached)
  2101. : customDetector (&detectorToUse)
  2102. {
  2103. if (! startDetached)
  2104. setActive (true);
  2105. }
  2106. PhysicalTopologySource::~PhysicalTopologySource()
  2107. {
  2108. setActive (false);
  2109. }
  2110. void PhysicalTopologySource::setActive (bool shouldBeActive)
  2111. {
  2112. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  2113. if (isActive() == shouldBeActive)
  2114. return;
  2115. if (shouldBeActive)
  2116. {
  2117. if (customDetector == nullptr)
  2118. detector = std::make_unique<DetectorHolder>(*this);
  2119. else
  2120. detector = std::make_unique<DetectorHolder>(*this, *customDetector);
  2121. detector->detector->activeTopologySources.add (this);
  2122. }
  2123. else
  2124. {
  2125. detector->detector->detach (this);
  2126. detector.reset();
  2127. }
  2128. listeners.call ([](TopologySource::Listener& l){ l.topologyChanged(); });
  2129. }
  2130. bool PhysicalTopologySource::isActive() const
  2131. {
  2132. return detector != nullptr;
  2133. }
  2134. bool PhysicalTopologySource::isLockedFromOutside() const
  2135. {
  2136. if (detector != nullptr && detector->detector != nullptr)
  2137. return detector->detector->deviceDetector.isLockedFromOutside();
  2138. return false;
  2139. }
  2140. BlockTopology PhysicalTopologySource::getCurrentTopology() const
  2141. {
  2142. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
  2143. if (detector != nullptr)
  2144. return detector->detector->currentTopology;
  2145. return {};
  2146. }
  2147. void PhysicalTopologySource::cancelAllActiveTouches() noexcept
  2148. {
  2149. if (detector != nullptr)
  2150. detector->detector->cancelAllActiveTouches();
  2151. }
  2152. bool PhysicalTopologySource::hasOwnServiceTimer() const { return false; }
  2153. void PhysicalTopologySource::handleTimerTick()
  2154. {
  2155. if (detector != nullptr)
  2156. detector->handleTimerTick();
  2157. }
  2158. PhysicalTopologySource::DeviceConnection::DeviceConnection() {}
  2159. PhysicalTopologySource::DeviceConnection::~DeviceConnection() {}
  2160. PhysicalTopologySource::DeviceDetector::DeviceDetector() {}
  2161. PhysicalTopologySource::DeviceDetector::~DeviceDetector() {}
  2162. const char* const* PhysicalTopologySource::getStandardLittleFootFunctions() noexcept
  2163. {
  2164. return BlocksProtocol::ledProgramLittleFootFunctions;
  2165. }
  2166. template <typename ListType>
  2167. static bool collectionsMatch (const ListType& list1, const ListType& list2) noexcept
  2168. {
  2169. if (list1.size() != list2.size())
  2170. return false;
  2171. for (auto&& b : list1)
  2172. if (! list2.contains (b))
  2173. return false;
  2174. return true;
  2175. }
  2176. bool BlockTopology::operator== (const BlockTopology& other) const noexcept
  2177. {
  2178. return collectionsMatch (connections, other.connections) && collectionsMatch (blocks, other.blocks);
  2179. }
  2180. bool BlockTopology::operator!= (const BlockTopology& other) const noexcept
  2181. {
  2182. return ! operator== (other);
  2183. }
  2184. bool BlockDeviceConnection::operator== (const BlockDeviceConnection& other) const noexcept
  2185. {
  2186. return (device1 == other.device1 && device2 == other.device2
  2187. && connectionPortOnDevice1 == other.connectionPortOnDevice1
  2188. && connectionPortOnDevice2 == other.connectionPortOnDevice2)
  2189. || (device1 == other.device2 && device2 == other.device1
  2190. && connectionPortOnDevice1 == other.connectionPortOnDevice2
  2191. && connectionPortOnDevice2 == other.connectionPortOnDevice1);
  2192. }
  2193. bool BlockDeviceConnection::operator!= (const BlockDeviceConnection& other) const noexcept
  2194. {
  2195. return ! operator== (other);
  2196. }
  2197. } // namespace juce