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.

1877 lines
65KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2016 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license/
  7. Permission to use, copy, modify, and/or distribute this software for any
  8. purpose with or without fee is hereby granted, provided that the above
  9. copyright notice and this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
  11. TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  12. FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
  13. OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  14. USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  15. TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  16. OF THIS SOFTWARE.
  17. -----------------------------------------------------------------------------
  18. To release a closed-source product which uses other parts of JUCE not
  19. licensed under the ISC terms, commercial licenses are available: visit
  20. www.juce.com for more information.
  21. ==============================================================================
  22. */
  23. #define JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED \
  24. jassert (juce::MessageManager::getInstance()->currentThreadHasLockedMessageManager());
  25. #if DUMP_BANDWIDTH_STATS
  26. namespace
  27. {
  28. struct PortIOStats
  29. {
  30. PortIOStats (const char* nm) : name (nm) {}
  31. const char* const name;
  32. int byteCount = 0;
  33. int messageCount = 0;
  34. int bytesPerSec = 0;
  35. int largestMessageBytes = 0;
  36. int lastMessageBytes = 0;
  37. void update (double elapsedSec)
  38. {
  39. if (byteCount > 0)
  40. {
  41. bytesPerSec = (int) (byteCount / elapsedSec);
  42. byteCount = 0;
  43. juce::Logger::writeToLog (getString());
  44. }
  45. }
  46. juce::String getString() const
  47. {
  48. return juce::String (name) + ": "
  49. + "count=" + juce::String (messageCount).paddedRight (' ', 7)
  50. + "rate=" + (juce::String (bytesPerSec / 1024.0f, 1) + " Kb/sec").paddedRight (' ', 11)
  51. + "largest=" + (juce::String (largestMessageBytes) + " bytes").paddedRight (' ', 11)
  52. + "last=" + (juce::String (lastMessageBytes) + " bytes").paddedRight (' ', 11);
  53. }
  54. void registerMessage (int numBytes) noexcept
  55. {
  56. byteCount += numBytes;
  57. ++messageCount;
  58. lastMessageBytes = numBytes;
  59. largestMessageBytes = juce::jmax (largestMessageBytes, numBytes);
  60. }
  61. };
  62. static PortIOStats inputStats { "Input" }, outputStats { "Output" };
  63. static uint32 startTime = 0;
  64. static inline void resetOnSecondBoundary()
  65. {
  66. auto now = juce::Time::getMillisecondCounter();
  67. double elapsedSec = (now - startTime) / 1000.0;
  68. if (elapsedSec >= 1.0)
  69. {
  70. inputStats.update (elapsedSec);
  71. outputStats.update (elapsedSec);
  72. startTime = now;
  73. }
  74. }
  75. static inline void registerBytesOut (int numBytes)
  76. {
  77. outputStats.registerMessage (numBytes);
  78. resetOnSecondBoundary();
  79. }
  80. static inline void registerBytesIn (int numBytes)
  81. {
  82. inputStats.registerMessage (numBytes);
  83. resetOnSecondBoundary();
  84. }
  85. }
  86. juce::String getMidiIOStats()
  87. {
  88. return inputStats.getString() + " " + outputStats.getString();
  89. }
  90. #endif
  91. //==============================================================================
  92. struct PhysicalTopologySource::Internal
  93. {
  94. struct Detector;
  95. struct BlockImplementation;
  96. struct ControlButtonImplementation;
  97. struct RotaryDialImplementation;
  98. struct TouchSurfaceImplementation;
  99. struct LEDGridImplementation;
  100. struct LEDRowImplementation;
  101. //==============================================================================
  102. struct MIDIDeviceConnection : public DeviceConnection,
  103. public juce::MidiInputCallback
  104. {
  105. MIDIDeviceConnection() {}
  106. ~MIDIDeviceConnection()
  107. {
  108. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  109. listeners.call (&Listener::connectionBeingDeleted, *this);
  110. if (midiInput != nullptr)
  111. midiInput->stop();
  112. }
  113. struct Listener
  114. {
  115. virtual ~Listener() {}
  116. virtual void handleIncomingMidiMessage (const juce::MidiMessage& message) = 0;
  117. virtual void connectionBeingDeleted (const MIDIDeviceConnection&) = 0;
  118. };
  119. void addListener (Listener* l)
  120. {
  121. listeners.add (l);
  122. }
  123. void removeListener (Listener* l)
  124. {
  125. listeners.remove (l);
  126. }
  127. bool sendMessageToDevice (const void* data, size_t dataSize) override
  128. {
  129. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
  130. jassert (dataSize > sizeof (BlocksProtocol::roliSysexHeader) + 2);
  131. jassert (memcmp (data, BlocksProtocol::roliSysexHeader, sizeof (BlocksProtocol::roliSysexHeader)) == 0);
  132. jassert (static_cast<const uint8*> (data)[dataSize - 1] == 0xf7);
  133. if (midiOutput != nullptr)
  134. {
  135. midiOutput->sendMessageNow (juce::MidiMessage (data, (int) dataSize));
  136. return true;
  137. }
  138. return false;
  139. }
  140. void handleIncomingMidiMessage (juce::MidiInput*, const juce::MidiMessage& message) override
  141. {
  142. const auto data = message.getRawData();
  143. const int dataSize = message.getRawDataSize();
  144. const int bodySize = dataSize - (int) (sizeof (BlocksProtocol::roliSysexHeader) + 1);
  145. if (bodySize > 0 && memcmp (data, BlocksProtocol::roliSysexHeader, sizeof (BlocksProtocol::roliSysexHeader)) == 0)
  146. if (handleMessageFromDevice != nullptr)
  147. handleMessageFromDevice (data + sizeof (BlocksProtocol::roliSysexHeader), (size_t) bodySize);
  148. listeners.call (&Listener::handleIncomingMidiMessage, message);
  149. }
  150. std::unique_ptr<juce::MidiInput> midiInput;
  151. std::unique_ptr<juce::MidiOutput> midiOutput;
  152. private:
  153. juce::ListenerList<Listener> listeners;
  154. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MIDIDeviceConnection)
  155. };
  156. struct MIDIDeviceDetector : public DeviceDetector
  157. {
  158. MIDIDeviceDetector() {}
  159. juce::StringArray scanForDevices() override
  160. {
  161. juce::StringArray result;
  162. for (auto& pair : findDevices())
  163. result.add (pair.inputName + " & " + pair.outputName);
  164. return result;
  165. }
  166. DeviceConnection* openDevice (int index) override
  167. {
  168. auto pair = findDevices()[index];
  169. if (pair.inputIndex >= 0 && pair.outputIndex >= 0)
  170. {
  171. std::unique_ptr<MIDIDeviceConnection> dev (new MIDIDeviceConnection());
  172. dev->midiInput.reset (juce::MidiInput::openDevice (pair.inputIndex, dev.get()));
  173. dev->midiOutput.reset (juce::MidiOutput::openDevice (pair.outputIndex));
  174. if (dev->midiInput != nullptr)
  175. {
  176. dev->midiInput->start();
  177. return dev.release();
  178. }
  179. }
  180. return nullptr;
  181. }
  182. static bool isBlocksMidiDeviceName (const juce::String& name)
  183. {
  184. return name.indexOf (" BLOCK") > 0 || name.indexOf (" Block") > 0;
  185. }
  186. static String cleanBlocksDeviceName (juce::String name)
  187. {
  188. name = name.trim();
  189. if (name.endsWith (" IN)"))
  190. return name.dropLastCharacters (4);
  191. if (name.endsWith (" OUT)"))
  192. return name.dropLastCharacters (5);
  193. const int openBracketPosition = name.lastIndexOfChar ('[');
  194. if (openBracketPosition != -1 && name.endsWith ("]"))
  195. return name.dropLastCharacters (name.length() - openBracketPosition);
  196. return name;
  197. }
  198. struct MidiInputOutputPair
  199. {
  200. juce::String outputName, inputName;
  201. int outputIndex = -1, inputIndex = -1;
  202. };
  203. static juce::Array<MidiInputOutputPair> findDevices()
  204. {
  205. juce::Array<MidiInputOutputPair> result;
  206. auto midiInputs = juce::MidiInput::getDevices();
  207. auto midiOutputs = juce::MidiOutput::getDevices();
  208. for (int j = 0; j < midiInputs.size(); ++j)
  209. {
  210. if (isBlocksMidiDeviceName (midiInputs[j]))
  211. {
  212. MidiInputOutputPair pair;
  213. pair.inputName = midiInputs[j];
  214. pair.inputIndex = j;
  215. String cleanedInputName = cleanBlocksDeviceName (pair.inputName);
  216. for (int i = 0; i < midiOutputs.size(); ++i)
  217. {
  218. if (cleanBlocksDeviceName (midiOutputs[i]) == cleanedInputName)
  219. {
  220. pair.outputName = midiOutputs[i];
  221. pair.outputIndex = i;
  222. break;
  223. }
  224. }
  225. result.add (pair);
  226. }
  227. }
  228. return result;
  229. }
  230. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MIDIDeviceDetector)
  231. };
  232. //==============================================================================
  233. struct DeviceInfo
  234. {
  235. Block::UID uid;
  236. BlocksProtocol::TopologyIndex index;
  237. BlocksProtocol::BlockSerialNumber serial;
  238. bool isMaster;
  239. };
  240. static Block::Timestamp deviceTimestampToHost (uint32 timestamp) noexcept
  241. {
  242. return static_cast<Block::Timestamp> (timestamp);
  243. }
  244. static juce::Array<DeviceInfo> getArrayOfDeviceInfo (const juce::Array<BlocksProtocol::DeviceStatus>& devices)
  245. {
  246. juce::Array<DeviceInfo> result;
  247. bool isFirst = true;
  248. for (auto& device : devices)
  249. {
  250. result.add ({ getBlockUIDFromSerialNumber (device.serialNumber),
  251. device.index,
  252. device.serialNumber,
  253. isFirst });
  254. isFirst = false;
  255. }
  256. return result;
  257. }
  258. static bool containsBlockWithUID (const juce::Array<DeviceInfo>& devices, Block::UID uid) noexcept
  259. {
  260. for (auto&& d : devices)
  261. if (d.uid == uid)
  262. return true;
  263. return false;
  264. }
  265. static bool containsBlockWithUID (const Block::Array& blocks, Block::UID uid) noexcept
  266. {
  267. for (auto&& block : blocks)
  268. if (block->uid == uid)
  269. return true;
  270. return false;
  271. }
  272. //==============================================================================
  273. struct ConnectedDeviceGroup : private juce::AsyncUpdater,
  274. private juce::Timer
  275. {
  276. ConnectedDeviceGroup (Detector& d, const juce::String& name, DeviceConnection* connection)
  277. : detector (d), deviceName (name), deviceConnection (connection)
  278. {
  279. lastGlobalPingTime = juce::Time::getCurrentTime();
  280. deviceConnection->handleMessageFromDevice = [this] (const void* data, size_t dataSize)
  281. {
  282. this->handleIncomingMessage (data, dataSize);
  283. };
  284. startTimer (200);
  285. sendTopologyRequest();
  286. }
  287. bool isStillConnected (const juce::StringArray& detectedDevices) const noexcept
  288. {
  289. return detectedDevices.contains (deviceName)
  290. && ! failedToGetTopology()
  291. && lastGlobalPingTime > juce::Time::getCurrentTime() - juce::RelativeTime::seconds (pingTimeoutSeconds);
  292. }
  293. Block::UID getDeviceIDFromIndex (BlocksProtocol::TopologyIndex index) const noexcept
  294. {
  295. for (auto& d : currentDeviceInfo)
  296. if (d.index == index)
  297. return d.uid;
  298. return {};
  299. }
  300. int getIndexFromDeviceID (Block::UID uid) const noexcept
  301. {
  302. for (auto& d : currentDeviceInfo)
  303. if (d.uid == uid)
  304. return d.index;
  305. return -1;
  306. }
  307. DeviceInfo* getDeviceInfoFromUID (Block::UID uid) const noexcept
  308. {
  309. for (auto& d : currentDeviceInfo)
  310. if (d.uid == uid)
  311. return &d;
  312. return nullptr;
  313. }
  314. const BlocksProtocol::DeviceStatus* getLastStatus (Block::UID deviceID) const noexcept
  315. {
  316. for (auto&& status : currentTopologyDevices)
  317. if (getBlockUIDFromSerialNumber (status.serialNumber) == deviceID)
  318. return &status;
  319. return nullptr;
  320. }
  321. //==============================================================================
  322. juce::Time lastTopologyRequestTime, lastTopologyReceiveTime;
  323. int numTopologyRequestsSent = 0;
  324. void sendTopologyRequest()
  325. {
  326. ++numTopologyRequestsSent;
  327. lastTopologyRequestTime = juce::Time::getCurrentTime();
  328. sendCommandMessage (0, BlocksProtocol::requestTopologyMessage);
  329. }
  330. void scheduleNewTopologyRequest()
  331. {
  332. numTopologyRequestsSent = 0;
  333. lastTopologyReceiveTime = juce::Time();
  334. }
  335. bool failedToGetTopology() const noexcept
  336. {
  337. return numTopologyRequestsSent > 4 && lastTopologyReceiveTime == juce::Time();
  338. }
  339. bool hasAnyBlockStoppedPinging() const noexcept
  340. {
  341. auto now = juce::Time::getCurrentTime();
  342. for (auto& ping : blockPings)
  343. if (ping.lastPing < now - juce::RelativeTime::seconds (pingTimeoutSeconds))
  344. return true;
  345. return false;
  346. }
  347. void timerCallback() override
  348. {
  349. auto now = juce::Time::getCurrentTime();
  350. if ((now > lastTopologyReceiveTime + juce::RelativeTime::seconds (30.0) || hasAnyBlockStoppedPinging())
  351. && now > lastTopologyRequestTime + juce::RelativeTime::seconds (1.0)
  352. && numTopologyRequestsSent < 4)
  353. sendTopologyRequest();
  354. }
  355. //==============================================================================
  356. // The following methods will be called by the DeviceToHostPacketDecoder:
  357. void beginTopology (int numDevices, int numConnections)
  358. {
  359. incomingTopologyDevices.clearQuick();
  360. incomingTopologyDevices.ensureStorageAllocated (numDevices);
  361. incomingTopologyConnections.clearQuick();
  362. incomingTopologyConnections.ensureStorageAllocated (numConnections);
  363. }
  364. void handleTopologyDevice (BlocksProtocol::DeviceStatus status)
  365. {
  366. incomingTopologyDevices.add (status);
  367. }
  368. void handleTopologyConnection (BlocksProtocol::DeviceConnection connection)
  369. {
  370. incomingTopologyConnections.add (connection);
  371. }
  372. void endTopology()
  373. {
  374. currentDeviceInfo = getArrayOfDeviceInfo (incomingTopologyDevices);
  375. currentDeviceConnections = getArrayOfConnections (incomingTopologyConnections);
  376. currentTopologyDevices = incomingTopologyDevices;
  377. currentTopologyConnections = incomingTopologyConnections;
  378. detector.handleTopologyChange();
  379. lastTopologyReceiveTime = juce::Time::getCurrentTime();
  380. blockPings.clear();
  381. }
  382. void handleControlButtonUpDown (BlocksProtocol::TopologyIndex deviceIndex, uint32 timestamp,
  383. BlocksProtocol::ControlButtonID buttonID, bool isDown)
  384. {
  385. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  386. detector.handleButtonChange (deviceID, deviceTimestampToHost (timestamp), buttonID.get(), isDown);
  387. }
  388. void handleTouchChange (BlocksProtocol::TopologyIndex deviceIndex,
  389. uint32 timestamp,
  390. BlocksProtocol::TouchIndex touchIndex,
  391. BlocksProtocol::TouchPosition position,
  392. BlocksProtocol::TouchVelocity velocity,
  393. bool isStart, bool isEnd)
  394. {
  395. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  396. {
  397. TouchSurface::Touch touch;
  398. touch.index = (int) touchIndex.get();
  399. touch.x = position.x.toUnipolarFloat();
  400. touch.y = position.y.toUnipolarFloat();
  401. touch.z = position.z.toUnipolarFloat();
  402. touch.xVelocity = velocity.vx.toBipolarFloat();
  403. touch.yVelocity = velocity.vy.toBipolarFloat();
  404. touch.zVelocity = velocity.vz.toBipolarFloat();
  405. touch.eventTimestamp = deviceTimestampToHost (timestamp);
  406. touch.isTouchStart = isStart;
  407. touch.isTouchEnd = isEnd;
  408. touch.blockUID = deviceID;
  409. setTouchStartPosition (touch);
  410. detector.handleTouchChange (deviceID, touch);
  411. }
  412. }
  413. void setTouchStartPosition (TouchSurface::Touch& touch)
  414. {
  415. auto& startPos = touchStartPositions.getValue (touch);
  416. if (touch.isTouchStart)
  417. startPos = { touch.x, touch.y };
  418. touch.startX = startPos.x;
  419. touch.startY = startPos.y;
  420. }
  421. void handlePacketACK (BlocksProtocol::TopologyIndex deviceIndex, BlocksProtocol::PacketCounter counter)
  422. {
  423. if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
  424. detector.handleSharedDataACK (deviceID, counter);
  425. }
  426. //==============================================================================
  427. template <typename PacketBuilder>
  428. bool sendMessageToDevice (const PacketBuilder& builder) const
  429. {
  430. if (deviceConnection->sendMessageToDevice (builder.getData(), (size_t) builder.size()))
  431. {
  432. #if DUMP_BANDWIDTH_STATS
  433. registerBytesOut (builder.size());
  434. #endif
  435. return true;
  436. }
  437. return false;
  438. }
  439. bool sendCommandMessage (BlocksProtocol::TopologyIndex deviceIndex, uint32 commandID) const
  440. {
  441. BlocksProtocol::HostPacketBuilder<64> p;
  442. p.writePacketSysexHeaderBytes (deviceIndex);
  443. p.deviceControlMessage (commandID);
  444. p.writePacketSysexFooter();
  445. return sendMessageToDevice (p);
  446. }
  447. bool broadcastCommandMessage (uint32 commandID) const
  448. {
  449. return sendCommandMessage (BlocksProtocol::topologyIndexForBroadcast, commandID);
  450. }
  451. DeviceConnection* getDeviceConnection()
  452. {
  453. return deviceConnection.get();
  454. }
  455. Detector& detector;
  456. juce::String deviceName;
  457. juce::Array<DeviceInfo> currentDeviceInfo;
  458. juce::Array<BlockDeviceConnection> currentDeviceConnections;
  459. static constexpr double pingTimeoutSeconds = 6.0;
  460. private:
  461. //==============================================================================
  462. std::unique_ptr<DeviceConnection> deviceConnection;
  463. juce::Array<BlocksProtocol::DeviceStatus> incomingTopologyDevices, currentTopologyDevices;
  464. juce::Array<BlocksProtocol::DeviceConnection> incomingTopologyConnections, currentTopologyConnections;
  465. juce::CriticalSection incomingPacketLock;
  466. juce::Array<juce::MemoryBlock> incomingPackets;
  467. struct TouchStart
  468. {
  469. float x, y;
  470. };
  471. TouchList<TouchStart> touchStartPositions;
  472. juce::Time lastGlobalPingTime;
  473. struct BlockPingTime
  474. {
  475. Block::UID blockUID;
  476. juce::Time lastPing;
  477. };
  478. juce::Array<BlockPingTime> blockPings;
  479. Block::UID getDeviceIDFromMessageIndex (BlocksProtocol::TopologyIndex index) noexcept
  480. {
  481. auto uid = getDeviceIDFromIndex (index);
  482. if (uid == Block::UID())
  483. {
  484. scheduleNewTopologyRequest(); // force a re-request of the topology when we
  485. // get an event from a block that we don't know about
  486. }
  487. else
  488. {
  489. auto now = juce::Time::getCurrentTime();
  490. for (auto& ping : blockPings)
  491. {
  492. if (ping.blockUID == uid)
  493. {
  494. ping.lastPing = now;
  495. return uid;
  496. }
  497. }
  498. blockPings.add ({ uid, now });
  499. }
  500. return uid;
  501. }
  502. juce::Array<BlockDeviceConnection> getArrayOfConnections (const juce::Array<BlocksProtocol::DeviceConnection>& connections)
  503. {
  504. juce::Array<BlockDeviceConnection> result;
  505. for (auto&& c : connections)
  506. {
  507. BlockDeviceConnection dc;
  508. dc.device1 = getDeviceIDFromIndex (c.device1);
  509. dc.device2 = getDeviceIDFromIndex (c.device2);
  510. dc.connectionPortOnDevice1 = convertConnectionPort (dc.device1, c.port1);
  511. dc.connectionPortOnDevice2 = convertConnectionPort (dc.device2, c.port2);
  512. result.add (dc);
  513. }
  514. return result;
  515. }
  516. Block::ConnectionPort convertConnectionPort (Block::UID uid, BlocksProtocol::ConnectorPort p) noexcept
  517. {
  518. if (auto* info = getDeviceInfoFromUID (uid))
  519. return BlocksProtocol::BlockDataSheet (info->serial).convertPortIndexToConnectorPort (p);
  520. jassertfalse;
  521. return { Block::ConnectionPort::DeviceEdge::north, 0 };
  522. }
  523. //==============================================================================
  524. void handleIncomingMessage (const void* data, size_t dataSize)
  525. {
  526. juce::MemoryBlock mb (data, dataSize);
  527. {
  528. const juce::ScopedLock sl (incomingPacketLock);
  529. incomingPackets.add (std::move (mb));
  530. }
  531. triggerAsyncUpdate();
  532. #if DUMP_BANDWIDTH_STATS
  533. registerBytesIn ((int) dataSize);
  534. #endif
  535. }
  536. void handleAsyncUpdate() override
  537. {
  538. juce::Array<juce::MemoryBlock> packets;
  539. packets.ensureStorageAllocated (32);
  540. {
  541. const juce::ScopedLock sl (incomingPacketLock);
  542. incomingPackets.swapWith (packets);
  543. }
  544. for (auto& packet : packets)
  545. {
  546. lastGlobalPingTime = juce::Time::getCurrentTime();
  547. auto data = static_cast<const uint8*> (packet.getData());
  548. BlocksProtocol::HostPacketDecoder<ConnectedDeviceGroup>
  549. ::processNextPacket (*this, *data, data + 1, (int) packet.getSize() - 1);
  550. }
  551. }
  552. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectedDeviceGroup)
  553. };
  554. //==============================================================================
  555. /** This is the main singleton object that keeps track of connected blocks */
  556. struct Detector : public juce::ReferenceCountedObject,
  557. private juce::Timer
  558. {
  559. Detector() : defaultDetector (new MIDIDeviceDetector()), deviceDetector (*defaultDetector)
  560. {
  561. startTimer (10);
  562. }
  563. Detector (DeviceDetector& dd) : deviceDetector (dd)
  564. {
  565. startTimer (10);
  566. }
  567. ~Detector()
  568. {
  569. jassert (activeTopologySources.isEmpty());
  570. jassert (activeControlButtons.isEmpty());
  571. }
  572. using Ptr = juce::ReferenceCountedObjectPtr<Detector>;
  573. static Detector::Ptr getDefaultDetector()
  574. {
  575. auto& d = getDefaultDetectorPointer();
  576. if (d == nullptr)
  577. d = new Detector();
  578. return d;
  579. }
  580. static Detector::Ptr& getDefaultDetectorPointer()
  581. {
  582. static Detector::Ptr defaultDetector;
  583. return defaultDetector;
  584. }
  585. void detach (PhysicalTopologySource* pts)
  586. {
  587. activeTopologySources.removeAllInstancesOf (pts);
  588. if (activeTopologySources.isEmpty())
  589. {
  590. for (auto& b : currentTopology.blocks)
  591. if (auto bi = BlockImplementation::getFrom (*b))
  592. bi->sendCommandMessage (BlocksProtocol::endAPIMode);
  593. currentTopology = {};
  594. auto& d = getDefaultDetectorPointer();
  595. if (d != nullptr && d->getReferenceCount() == 2)
  596. getDefaultDetectorPointer() = nullptr;
  597. }
  598. }
  599. bool isConnected (Block::UID deviceID) const noexcept
  600. {
  601. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
  602. for (auto&& b : currentTopology.blocks)
  603. if (b->uid == deviceID)
  604. return true;
  605. return false;
  606. }
  607. const BlocksProtocol::DeviceStatus* getLastStatus (Block::UID deviceID) const noexcept
  608. {
  609. for (auto d : connectedDeviceGroups)
  610. if (auto status = d->getLastStatus (deviceID))
  611. return status;
  612. return nullptr;
  613. }
  614. void handleTopologyChange()
  615. {
  616. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  617. {
  618. juce::Array<DeviceInfo> newDeviceInfo;
  619. juce::Array<BlockDeviceConnection> newDeviceConnections;
  620. for (auto d : connectedDeviceGroups)
  621. {
  622. newDeviceInfo.addArray (d->currentDeviceInfo);
  623. newDeviceConnections.addArray (d->currentDeviceConnections);
  624. }
  625. for (int i = currentTopology.blocks.size(); --i >= 0;)
  626. {
  627. auto block = currentTopology.blocks.getUnchecked(i);
  628. if (! containsBlockWithUID (newDeviceInfo, block->uid))
  629. {
  630. if (auto bi = BlockImplementation::getFrom (*block))
  631. bi->invalidate();
  632. currentTopology.blocks.remove (i);
  633. }
  634. }
  635. for (auto& info : newDeviceInfo)
  636. if (info.serial.isValid())
  637. if (! containsBlockWithUID (currentTopology.blocks, getBlockUIDFromSerialNumber (info.serial)))
  638. currentTopology.blocks.add (new BlockImplementation (info.serial, *this, info.isMaster));
  639. currentTopology.connections.swapWith (newDeviceConnections);
  640. }
  641. for (auto d : activeTopologySources)
  642. d->listeners.call (&TopologySource::Listener::topologyChanged);
  643. #if DUMP_TOPOLOGY
  644. dumpTopology (currentTopology);
  645. #endif
  646. }
  647. void handleSharedDataACK (Block::UID deviceID, uint32 packetCounter) const
  648. {
  649. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  650. for (auto&& b : currentTopology.blocks)
  651. if (b->uid == deviceID)
  652. if (auto bi = BlockImplementation::getFrom (*b))
  653. bi->handleSharedDataACK (packetCounter);
  654. }
  655. void handleButtonChange (Block::UID deviceID, Block::Timestamp timestamp, uint32 buttonIndex, bool isDown) const
  656. {
  657. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  658. for (auto b : activeControlButtons)
  659. {
  660. if (b->block.uid == deviceID)
  661. {
  662. if (auto bi = BlockImplementation::getFrom (b->block))
  663. {
  664. bi->pingFromDevice();
  665. if (buttonIndex < (uint32) bi->modelData.buttons.size())
  666. b->broadcastButtonChange (timestamp, bi->modelData.buttons[(int) buttonIndex].type, isDown);
  667. }
  668. }
  669. }
  670. }
  671. void handleTouchChange (Block::UID deviceID, const TouchSurface::Touch& touchEvent)
  672. {
  673. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  674. for (auto t : activeTouchSurfaces)
  675. {
  676. if (t->block.uid == deviceID)
  677. {
  678. TouchSurface::Touch scaledEvent (touchEvent);
  679. scaledEvent.x *= t->block.getWidth();
  680. scaledEvent.y *= t->block.getHeight();
  681. scaledEvent.startX *= t->block.getWidth();
  682. scaledEvent.startY *= t->block.getHeight();
  683. t->broadcastTouchChange (scaledEvent);
  684. }
  685. }
  686. }
  687. void cancelAllActiveTouches() noexcept
  688. {
  689. for (auto surface : activeTouchSurfaces)
  690. surface->cancelAllActiveTouches();
  691. }
  692. //==============================================================================
  693. int getIndexFromDeviceID (Block::UID deviceID) const noexcept
  694. {
  695. for (auto c : connectedDeviceGroups)
  696. {
  697. const int index = c->getIndexFromDeviceID (deviceID);
  698. if (index >= 0)
  699. return index;
  700. }
  701. return -1;
  702. }
  703. template <typename PacketBuilder>
  704. bool sendMessageToDevice (Block::UID deviceID, const PacketBuilder& builder) const
  705. {
  706. for (auto c : connectedDeviceGroups)
  707. if (c->getIndexFromDeviceID (deviceID) >= 0)
  708. return c->sendMessageToDevice (builder);
  709. return false;
  710. }
  711. static Detector* getFrom (Block& b) noexcept
  712. {
  713. if (auto bi = BlockImplementation::getFrom (b))
  714. return &(bi->detector);
  715. jassertfalse;
  716. return nullptr;
  717. }
  718. DeviceConnection* getDeviceConnectionFor (const Block& b)
  719. {
  720. for (const auto& d : connectedDeviceGroups)
  721. {
  722. for (const auto& info : d->currentDeviceInfo)
  723. {
  724. if (info.uid == b.uid)
  725. return d->getDeviceConnection();
  726. }
  727. }
  728. return nullptr;
  729. }
  730. std::unique_ptr<MIDIDeviceDetector> defaultDetector;
  731. DeviceDetector& deviceDetector;
  732. juce::Array<PhysicalTopologySource*> activeTopologySources;
  733. juce::Array<ControlButtonImplementation*> activeControlButtons;
  734. juce::Array<TouchSurfaceImplementation*> activeTouchSurfaces;
  735. BlockTopology currentTopology;
  736. private:
  737. void timerCallback() override
  738. {
  739. startTimer (1500);
  740. auto detectedDevices = deviceDetector.scanForDevices();
  741. handleDevicesRemoved (detectedDevices);
  742. handleDevicesAdded (detectedDevices);
  743. }
  744. void handleDevicesRemoved (const juce::StringArray& detectedDevices)
  745. {
  746. bool anyDevicesRemoved = false;
  747. for (int i = connectedDeviceGroups.size(); --i >= 0;)
  748. {
  749. if (! connectedDeviceGroups.getUnchecked(i)->isStillConnected (detectedDevices))
  750. {
  751. connectedDeviceGroups.remove (i);
  752. anyDevicesRemoved = true;
  753. }
  754. }
  755. if (anyDevicesRemoved)
  756. handleTopologyChange();
  757. }
  758. void handleDevicesAdded (const juce::StringArray& detectedDevices)
  759. {
  760. bool anyDevicesAdded = false;
  761. for (const auto& devName : detectedDevices)
  762. {
  763. if (! hasDeviceFor (devName))
  764. {
  765. if (auto d = deviceDetector.openDevice (detectedDevices.indexOf (devName)))
  766. {
  767. connectedDeviceGroups.add (new ConnectedDeviceGroup (*this, devName, d));
  768. anyDevicesAdded = true;
  769. }
  770. }
  771. }
  772. if (anyDevicesAdded)
  773. handleTopologyChange();
  774. }
  775. bool hasDeviceFor (const juce::String& devName) const
  776. {
  777. for (auto d : connectedDeviceGroups)
  778. if (d->deviceName == devName)
  779. return true;
  780. return false;
  781. }
  782. juce::OwnedArray<ConnectedDeviceGroup> connectedDeviceGroups;
  783. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Detector)
  784. };
  785. //==============================================================================
  786. struct BlockImplementation : public Block,
  787. private MIDIDeviceConnection::Listener
  788. {
  789. BlockImplementation (const BlocksProtocol::BlockSerialNumber& serial, Detector& detectorToUse, bool master)
  790. : Block (juce::String ((const char*) serial.serial, sizeof (serial.serial))), modelData (serial),
  791. remoteHeap (modelData.programAndHeapSize), detector (detectorToUse), isMaster (master)
  792. {
  793. sendCommandMessage (BlocksProtocol::beginAPIMode);
  794. if (modelData.hasTouchSurface)
  795. touchSurface.reset (new TouchSurfaceImplementation (*this));
  796. int i = 0;
  797. for (auto b : modelData.buttons)
  798. controlButtons.add (new ControlButtonImplementation (*this, i++, b));
  799. if (modelData.lightGridWidth > 0 && modelData.lightGridHeight > 0)
  800. ledGrid.reset (new LEDGridImplementation (*this));
  801. for (auto s : modelData.statusLEDs)
  802. statusLights.add (new StatusLightImplementation (*this, s));
  803. if (modelData.numLEDRowLEDs > 0)
  804. ledRow.reset (new LEDRowImplementation (*this));
  805. listenerToMidiConnection = dynamic_cast<MIDIDeviceConnection*> (detector.getDeviceConnectionFor (*this));
  806. if (listenerToMidiConnection != nullptr)
  807. listenerToMidiConnection->addListener (this);
  808. }
  809. ~BlockImplementation()
  810. {
  811. if (listenerToMidiConnection != nullptr)
  812. listenerToMidiConnection->removeListener (this);
  813. }
  814. void invalidate()
  815. {
  816. isStillConnected = false;
  817. }
  818. Type getType() const override { return modelData.apiType; }
  819. juce::String getDeviceDescription() const override { return modelData.description; }
  820. int getWidth() const override { return modelData.widthUnits; }
  821. int getHeight() const override { return modelData.heightUnits; }
  822. float getMillimetersPerUnit() const override { return 47.0f; }
  823. bool isHardwareBlock() const override { return true; }
  824. juce::Array<Block::ConnectionPort> getPorts() const override { return modelData.ports; }
  825. bool isConnected() const override { return isStillConnected && detector.isConnected (uid); }
  826. bool isMasterBlock() const override { return isMaster; }
  827. TouchSurface* getTouchSurface() const override { return touchSurface.get(); }
  828. LEDGrid* getLEDGrid() const override { return ledGrid.get(); }
  829. LEDRow* getLEDRow() const override { return ledRow.get(); }
  830. juce::Array<ControlButton*> getButtons() const override
  831. {
  832. juce::Array<ControlButton*> result;
  833. result.addArray (controlButtons);
  834. return result;
  835. }
  836. juce::Array<StatusLight*> getStatusLights() const override
  837. {
  838. juce::Array<StatusLight*> result;
  839. result.addArray (statusLights);
  840. return result;
  841. }
  842. float getBatteryLevel() const override
  843. {
  844. if (auto status = detector.getLastStatus (uid))
  845. return status->batteryLevel.toUnipolarFloat();
  846. return 0.0f;
  847. }
  848. bool isBatteryCharging() const override
  849. {
  850. if (auto status = detector.getLastStatus (uid))
  851. return status->batteryCharging.get() != 0;
  852. return false;
  853. }
  854. bool supportsGraphics() const override
  855. {
  856. return false;
  857. }
  858. int getDeviceIndex() const noexcept
  859. {
  860. return isConnected() ? detector.getIndexFromDeviceID (uid) : -1;
  861. }
  862. template <typename PacketBuilder>
  863. bool sendMessageToDevice (const PacketBuilder& builder)
  864. {
  865. lastMessageSendTime = juce::Time::getCurrentTime();
  866. return detector.sendMessageToDevice (uid, builder);
  867. }
  868. bool sendCommandMessage (uint32 commandID)
  869. {
  870. int index = getDeviceIndex();
  871. if (index < 0)
  872. return false;
  873. BlocksProtocol::HostPacketBuilder<64> p;
  874. p.writePacketSysexHeaderBytes ((BlocksProtocol::TopologyIndex) index);
  875. p.deviceControlMessage (commandID);
  876. p.writePacketSysexFooter();
  877. return sendMessageToDevice (p);
  878. }
  879. static BlockImplementation* getFrom (Block& b) noexcept
  880. {
  881. if (auto bi = dynamic_cast<BlockImplementation*> (&b))
  882. return bi;
  883. jassertfalse;
  884. return nullptr;
  885. }
  886. bool isControlBlock() const
  887. {
  888. auto type = getType();
  889. return type == Block::Type::liveBlock
  890. || type == Block::Type::loopBlock
  891. || type == Block::Type::developerControlBlock;
  892. }
  893. //==============================================================================
  894. void clearProgramAndData()
  895. {
  896. programSize = 0;
  897. remoteHeap.clear();
  898. }
  899. void setProgram (const void* compiledCode, size_t codeSize)
  900. {
  901. clearProgramAndData();
  902. setDataBytes (0, compiledCode, codeSize);
  903. programSize = (uint32) codeSize;
  904. }
  905. void setDataByte (size_t offset, uint8 value)
  906. {
  907. remoteHeap.setByte (programSize + offset, value);
  908. }
  909. void setDataBytes (size_t offset, const void* newData, size_t num)
  910. {
  911. remoteHeap.setBytes (programSize + offset, static_cast<const uint8*> (newData), num);
  912. }
  913. void setDataBits (uint32 startBit, uint32 numBits, uint32 value)
  914. {
  915. remoteHeap.setBits (programSize * 8 + startBit, numBits, value);
  916. }
  917. uint8 getDataByte (size_t offset)
  918. {
  919. return remoteHeap.getByte (programSize + offset);
  920. }
  921. void sendProgramEvent (const LEDGrid::ProgramEventMessage& message)
  922. {
  923. static_assert (sizeof (LEDGrid::ProgramEventMessage::values) == 4 * BlocksProtocol::numProgramMessageInts,
  924. "Need to keep the internal and external messages structures the same");
  925. if (remoteHeap.isProgramLoaded())
  926. {
  927. auto index = getDeviceIndex();
  928. if (index >= 0)
  929. {
  930. BlocksProtocol::HostPacketBuilder<128> p;
  931. p.writePacketSysexHeaderBytes ((BlocksProtocol::TopologyIndex) index);
  932. if (p.addProgramEventMessage (message.values))
  933. {
  934. p.writePacketSysexFooter();
  935. sendMessageToDevice (p);
  936. }
  937. }
  938. else
  939. {
  940. jassertfalse;
  941. }
  942. }
  943. }
  944. void saveProgramAsDefault()
  945. {
  946. sendCommandMessage (BlocksProtocol::saveProgramAsDefault);
  947. }
  948. void handleSharedDataACK (uint32 packetCounter) noexcept
  949. {
  950. pingFromDevice();
  951. remoteHeap.handleACKFromDevice (*this, packetCounter);
  952. }
  953. void pingFromDevice()
  954. {
  955. lastMessageReceiveTime = juce::Time::getCurrentTime();
  956. }
  957. void addDataInputPortListener (DataInputPortListener* listener) override
  958. {
  959. Block::addDataInputPortListener (listener);
  960. if (auto midiInput = getMidiInput())
  961. midiInput->start();
  962. }
  963. void sendMessage (const void* message, size_t messageSize) override
  964. {
  965. if (auto midiOutput = getMidiOutput())
  966. midiOutput->sendMessageNow ({ message, (int) messageSize });
  967. }
  968. void handleTimerTick()
  969. {
  970. if (++resetMessagesSent < 3)
  971. {
  972. if (resetMessagesSent == 1)
  973. sendCommandMessage (BlocksProtocol::endAPIMode);
  974. sendCommandMessage (BlocksProtocol::beginAPIMode);
  975. return;
  976. }
  977. if (ledGrid != nullptr)
  978. if (auto renderer = ledGrid->getRenderer())
  979. renderer->renderLEDGrid (*ledGrid);
  980. remoteHeap.sendChanges (*this);
  981. if (lastMessageSendTime < juce::Time::getCurrentTime() - juce::RelativeTime::milliseconds (pingIntervalMs))
  982. sendCommandMessage (BlocksProtocol::ping);
  983. }
  984. //==============================================================================
  985. std::unique_ptr<TouchSurface> touchSurface;
  986. juce::OwnedArray<ControlButton> controlButtons;
  987. std::unique_ptr<LEDGridImplementation> ledGrid;
  988. std::unique_ptr<LEDRowImplementation> ledRow;
  989. juce::OwnedArray<StatusLight> statusLights;
  990. BlocksProtocol::BlockDataSheet modelData;
  991. MIDIDeviceConnection* listenerToMidiConnection = nullptr;
  992. static constexpr int pingIntervalMs = 400;
  993. static constexpr uint32 maxBlockSize = BlocksProtocol::padBlockProgramAndHeapSize;
  994. static constexpr uint32 maxPacketCounter = BlocksProtocol::PacketCounter::maxValue;
  995. static constexpr uint32 maxPacketSize = 200;
  996. using PacketBuilder = BlocksProtocol::HostPacketBuilder<maxPacketSize>;
  997. using RemoteHeapType = littlefoot::LittleFootRemoteHeap<BlockImplementation>;
  998. RemoteHeapType remoteHeap;
  999. uint32 programSize = 0;
  1000. Detector& detector;
  1001. juce::Time lastMessageSendTime, lastMessageReceiveTime;
  1002. private:
  1003. uint32 resetMessagesSent = 0;
  1004. bool isStillConnected = true;
  1005. bool isMaster = false;
  1006. const juce::MidiInput* getMidiInput() const
  1007. {
  1008. if (auto c = dynamic_cast<MIDIDeviceConnection*> (detector.getDeviceConnectionFor (*this)))
  1009. return c->midiInput.get();
  1010. jassertfalse;
  1011. return nullptr;
  1012. }
  1013. juce::MidiInput* getMidiInput()
  1014. {
  1015. return const_cast<juce::MidiInput*> (static_cast<const BlockImplementation&>(*this).getMidiInput());
  1016. }
  1017. const juce::MidiOutput* getMidiOutput() const
  1018. {
  1019. if (auto c = dynamic_cast<MIDIDeviceConnection*> (detector.getDeviceConnectionFor (*this)))
  1020. return c->midiOutput.get();
  1021. jassertfalse;
  1022. return nullptr;
  1023. }
  1024. juce::MidiOutput* getMidiOutput()
  1025. {
  1026. return const_cast<juce::MidiOutput*> (static_cast<const BlockImplementation&>(*this).getMidiOutput());
  1027. }
  1028. void handleIncomingMidiMessage (const juce::MidiMessage& message) override
  1029. {
  1030. dataInputPortListeners.call (&Block::DataInputPortListener::handleIncomingDataPortMessage,
  1031. *this, message.getRawData(), (size_t) message.getRawDataSize());
  1032. }
  1033. void connectionBeingDeleted (const MIDIDeviceConnection& c) override
  1034. {
  1035. jassert (listenerToMidiConnection == &c);
  1036. juce::ignoreUnused (c);
  1037. listenerToMidiConnection->removeListener (this);
  1038. listenerToMidiConnection = nullptr;
  1039. }
  1040. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockImplementation)
  1041. };
  1042. //==============================================================================
  1043. struct LEDRowImplementation : public LEDRow
  1044. {
  1045. LEDRowImplementation (BlockImplementation& b) : LEDRow (b), blockImpl (b)
  1046. {
  1047. loadProgramOntoBlock();
  1048. }
  1049. /* Data format:
  1050. 0: 10 x 5-6-5 bits for button LED RGBs
  1051. 20: 15 x 5-6-5 bits for LED row colours
  1052. 50: 1 x 5-6-5 bits for LED row overlay colour
  1053. */
  1054. static constexpr uint32 totalDataSize = 256;
  1055. //==============================================================================
  1056. void setButtonColour (uint32 index, LEDColour colour)
  1057. {
  1058. if (index < 10)
  1059. write565Colour (16 * index, colour);
  1060. }
  1061. int getNumLEDs() const override
  1062. {
  1063. return blockImpl.modelData.numLEDRowLEDs;
  1064. }
  1065. void setLEDColour (int index, LEDColour colour) override
  1066. {
  1067. if ((uint32) index < 15u)
  1068. write565Colour (20 * 8 + 16 * (uint32) index, colour);
  1069. }
  1070. void setOverlayColour (LEDColour colour) override
  1071. {
  1072. write565Colour (50 * 8, colour);
  1073. }
  1074. void resetOverlayColour() override
  1075. {
  1076. write565Colour (50 * 8, {});
  1077. }
  1078. private:
  1079. void loadProgramOntoBlock()
  1080. {
  1081. littlefoot::Compiler compiler;
  1082. compiler.addNativeFunctions (PhysicalTopologySource::getStandardLittleFootFunctions());
  1083. auto err = compiler.compile (getLittleFootProgram(), totalDataSize);
  1084. if (err.failed())
  1085. {
  1086. DBG (err.getErrorMessage());
  1087. jassertfalse;
  1088. return;
  1089. }
  1090. blockImpl.setProgram (compiler.compiledObjectCode.begin(), (size_t) compiler.compiledObjectCode.size());
  1091. }
  1092. void write565Colour (uint32 bitIndex, LEDColour colour)
  1093. {
  1094. blockImpl.setDataBits (bitIndex, 5, colour.getRed() >> 3);
  1095. blockImpl.setDataBits (bitIndex + 5, 6, colour.getGreen() >> 2);
  1096. blockImpl.setDataBits (bitIndex + 11, 5, colour.getBlue() >> 3);
  1097. }
  1098. static const char* getLittleFootProgram() noexcept
  1099. {
  1100. return R"littlefoot(
  1101. int getColour (int bitIndex)
  1102. {
  1103. return makeARGB (255,
  1104. getHeapBits (bitIndex, 5) << 3,
  1105. getHeapBits (bitIndex + 5, 6) << 2,
  1106. getHeapBits (bitIndex + 11, 5) << 3);
  1107. }
  1108. int getButtonColour (int index)
  1109. {
  1110. return getColour (16 * index);
  1111. }
  1112. int getLEDColour (int index)
  1113. {
  1114. if (getHeapInt (50))
  1115. return getColour (50 * 8);
  1116. return getColour (20 * 8 + 16 * index);
  1117. }
  1118. void repaint()
  1119. {
  1120. for (int x = 0; x < 15; ++x)
  1121. setLED (x, 0, getLEDColour (x));
  1122. for (int i = 0; i < 10; ++i)
  1123. setLED (i, 1, getButtonColour (i));
  1124. }
  1125. void handleMessage (int p1, int p2) {}
  1126. )littlefoot";
  1127. }
  1128. BlockImplementation& blockImpl;
  1129. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDRowImplementation)
  1130. };
  1131. //==============================================================================
  1132. struct TouchSurfaceImplementation : public TouchSurface,
  1133. private juce::Timer
  1134. {
  1135. TouchSurfaceImplementation (BlockImplementation& b) : TouchSurface (b), blockImpl (b)
  1136. {
  1137. if (auto det = Detector::getFrom (block))
  1138. det->activeTouchSurfaces.add (this);
  1139. startTimer (500);
  1140. }
  1141. ~TouchSurfaceImplementation()
  1142. {
  1143. if (auto det = Detector::getFrom (block))
  1144. det->activeTouchSurfaces.removeFirstMatchingValue (this);
  1145. }
  1146. int getNumberOfKeywaves() const noexcept override
  1147. {
  1148. return blockImpl.modelData.numKeywaves;
  1149. }
  1150. void broadcastTouchChange (const TouchSurface::Touch& touchEvent)
  1151. {
  1152. auto& status = touches.getValue (touchEvent);
  1153. // Fake a touch end if we receive a duplicate touch-start with no preceding touch-end (ie: comms error)
  1154. if (touchEvent.isTouchStart && status.isActive)
  1155. killTouch (touchEvent, status, juce::Time::getMillisecondCounter());
  1156. // Fake a touch start if we receive an unexpected event with no matching start event. (ie: comms error)
  1157. if (! touchEvent.isTouchStart && ! status.isActive)
  1158. {
  1159. TouchSurface::Touch t (touchEvent);
  1160. t.isTouchStart = true;
  1161. t.isTouchEnd = false;
  1162. if (t.zVelocity <= 0) t.zVelocity = status.lastStrikePressure;
  1163. if (t.zVelocity <= 0) t.zVelocity = t.z;
  1164. if (t.zVelocity <= 0) t.zVelocity = 0.9f;
  1165. listeners.call (&TouchSurface::Listener::touchChanged, *this, t);
  1166. }
  1167. // Normal handling:
  1168. status.lastEventTime = juce::Time::getMillisecondCounter();
  1169. status.isActive = ! touchEvent.isTouchEnd;
  1170. if (touchEvent.isTouchStart)
  1171. status.lastStrikePressure = touchEvent.zVelocity;
  1172. listeners.call (&TouchSurface::Listener::touchChanged, *this, touchEvent);
  1173. }
  1174. void timerCallback() override
  1175. {
  1176. // Find touches that seem to have become stuck, and fake a touch-end for them..
  1177. static const uint32 touchTimeOutMs = 40;
  1178. for (auto& t : touches)
  1179. {
  1180. auto& status = t.value;
  1181. auto now = juce::Time::getMillisecondCounter();
  1182. if (status.isActive && now > status.lastEventTime + touchTimeOutMs)
  1183. killTouch (t.touch, status, now);
  1184. }
  1185. }
  1186. struct TouchStatus
  1187. {
  1188. uint32 lastEventTime = 0;
  1189. float lastStrikePressure = 0;
  1190. bool isActive = false;
  1191. };
  1192. void killTouch (const TouchSurface::Touch& touch, TouchStatus& status, uint32 timeStamp) noexcept
  1193. {
  1194. jassert (status.isActive);
  1195. TouchSurface::Touch killTouch (touch);
  1196. killTouch.z = 0;
  1197. killTouch.xVelocity = 0;
  1198. killTouch.yVelocity = 0;
  1199. killTouch.zVelocity = -1.0f;
  1200. killTouch.eventTimestamp = timeStamp;
  1201. killTouch.isTouchStart = false;
  1202. killTouch.isTouchEnd = true;
  1203. listeners.call (&TouchSurface::Listener::touchChanged, *this, killTouch);
  1204. status.isActive = false;
  1205. }
  1206. void cancelAllActiveTouches() noexcept override
  1207. {
  1208. const auto now = juce::Time::getMillisecondCounter();
  1209. for (auto& t : touches)
  1210. if (t.value.isActive)
  1211. killTouch (t.touch, t.value, now);
  1212. touches.clear();
  1213. }
  1214. BlockImplementation& blockImpl;
  1215. TouchList<TouchStatus> touches;
  1216. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TouchSurfaceImplementation)
  1217. };
  1218. //==============================================================================
  1219. struct ControlButtonImplementation : public ControlButton
  1220. {
  1221. ControlButtonImplementation (BlockImplementation& b, int index, BlocksProtocol::BlockDataSheet::ButtonInfo info)
  1222. : ControlButton (b), blockImpl (b), buttonInfo (info), buttonIndex (index)
  1223. {
  1224. if (auto det = Detector::getFrom (block))
  1225. det->activeControlButtons.add (this);
  1226. }
  1227. ~ControlButtonImplementation()
  1228. {
  1229. if (auto det = Detector::getFrom (block))
  1230. det->activeControlButtons.removeFirstMatchingValue (this);
  1231. }
  1232. ButtonFunction getType() const override { return buttonInfo.type; }
  1233. juce::String getName() const override { return BlocksProtocol::getButtonNameForFunction (buttonInfo.type); }
  1234. float getPositionX() const override { return buttonInfo.x; }
  1235. float getPositionY() const override { return buttonInfo.y; }
  1236. bool hasLight() const override { return blockImpl.isControlBlock(); }
  1237. bool setLightColour (LEDColour colour) override
  1238. {
  1239. if (hasLight())
  1240. {
  1241. if (auto row = blockImpl.ledRow.get())
  1242. {
  1243. row->setButtonColour ((uint32) buttonIndex, colour);
  1244. return true;
  1245. }
  1246. }
  1247. return false;
  1248. }
  1249. void broadcastButtonChange (Block::Timestamp timestamp, ControlButton::ButtonFunction button, bool isDown)
  1250. {
  1251. if (button == buttonInfo.type)
  1252. {
  1253. if (wasDown == isDown)
  1254. sendButtonChangeToListeners (timestamp, ! isDown);
  1255. sendButtonChangeToListeners (timestamp, isDown);
  1256. wasDown = isDown;
  1257. }
  1258. }
  1259. void sendButtonChangeToListeners (Block::Timestamp timestamp, bool isDown)
  1260. {
  1261. if (isDown)
  1262. listeners.call (&ControlButton::Listener::buttonPressed, *this, timestamp);
  1263. else
  1264. listeners.call (&ControlButton::Listener::buttonReleased, *this, timestamp);
  1265. }
  1266. BlockImplementation& blockImpl;
  1267. BlocksProtocol::BlockDataSheet::ButtonInfo buttonInfo;
  1268. int buttonIndex;
  1269. bool wasDown = false;
  1270. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControlButtonImplementation)
  1271. };
  1272. //==============================================================================
  1273. struct StatusLightImplementation : public StatusLight
  1274. {
  1275. StatusLightImplementation (Block& b, BlocksProtocol::BlockDataSheet::StatusLEDInfo i) : StatusLight (b), info (i)
  1276. {
  1277. }
  1278. juce::String getName() const override { return info.name; }
  1279. bool setColour (LEDColour newColour) override
  1280. {
  1281. // XXX TODO!
  1282. juce::ignoreUnused (newColour);
  1283. return false;
  1284. }
  1285. BlocksProtocol::BlockDataSheet::StatusLEDInfo info;
  1286. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StatusLightImplementation)
  1287. };
  1288. //==============================================================================
  1289. struct LEDGridImplementation : public LEDGrid
  1290. {
  1291. LEDGridImplementation (BlockImplementation& b) : LEDGrid (b), blockImpl (b)
  1292. {
  1293. }
  1294. int getNumColumns() const override { return blockImpl.modelData.lightGridWidth; }
  1295. int getNumRows() const override { return blockImpl.modelData.lightGridHeight; }
  1296. juce::Result setProgram (Program* newProgram) override
  1297. {
  1298. if (program.get() != newProgram)
  1299. {
  1300. program.reset (newProgram);
  1301. if (program != nullptr)
  1302. {
  1303. littlefoot::Compiler compiler;
  1304. compiler.addNativeFunctions (PhysicalTopologySource::getStandardLittleFootFunctions());
  1305. auto err = compiler.compile (newProgram->getLittleFootProgram(), newProgram->getHeapSize());
  1306. if (err.failed())
  1307. return err;
  1308. DBG ("Compiled littlefoot program, size = " << (int) compiler.compiledObjectCode.size() << " bytes");
  1309. blockImpl.setProgram (compiler.compiledObjectCode.begin(), (size_t) compiler.compiledObjectCode.size());
  1310. }
  1311. else
  1312. {
  1313. blockImpl.clearProgramAndData();
  1314. }
  1315. }
  1316. else
  1317. {
  1318. jassertfalse;
  1319. }
  1320. return juce::Result::ok();
  1321. }
  1322. Program* getProgram() const override { return program.get(); }
  1323. void sendProgramEvent (const ProgramEventMessage& m) override { blockImpl.sendProgramEvent (m); }
  1324. void saveProgramAsDefault() override { blockImpl.saveProgramAsDefault(); }
  1325. void setDataByte (size_t offset, uint8 value) override { blockImpl.setDataByte (offset, value); }
  1326. void setDataBytes (size_t offset, const void* data, size_t num) override { blockImpl.setDataBytes (offset, data, num); }
  1327. void setDataBits (uint32 startBit, uint32 numBits, uint32 value) override { blockImpl.setDataBits (startBit, numBits, value); }
  1328. uint8 getDataByte (size_t offset) override { return blockImpl.getDataByte (offset); }
  1329. BlockImplementation& blockImpl;
  1330. std::unique_ptr<Program> program;
  1331. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDGridImplementation)
  1332. };
  1333. //==============================================================================
  1334. #if DUMP_TOPOLOGY
  1335. static juce::String idToSerialNum (const BlockTopology& topology, Block::UID uid)
  1336. {
  1337. for (auto* b : topology.blocks)
  1338. if (b->uid == uid)
  1339. return b->serialNumber;
  1340. return "???";
  1341. }
  1342. static juce::String portEdgeToString (Block::ConnectionPort port)
  1343. {
  1344. switch (port.edge)
  1345. {
  1346. case Block::ConnectionPort::DeviceEdge::north: return "north";
  1347. case Block::ConnectionPort::DeviceEdge::south: return "south";
  1348. case Block::ConnectionPort::DeviceEdge::east: return "east";
  1349. case Block::ConnectionPort::DeviceEdge::west: return "west";
  1350. }
  1351. return {};
  1352. }
  1353. static juce::String portToString (Block::ConnectionPort port)
  1354. {
  1355. return portEdgeToString (port) + "_" + juce::String (port.index);
  1356. }
  1357. static void dumpTopology (const BlockTopology& topology)
  1358. {
  1359. MemoryOutputStream m;
  1360. m << "=============================================================================" << newLine
  1361. << "Topology: " << topology.blocks.size() << " device(s)" << newLine
  1362. << newLine;
  1363. int index = 0;
  1364. for (auto block : topology.blocks)
  1365. {
  1366. m << "Device " << index++ << (block->isMasterBlock() ? ": (MASTER)" : ":") << newLine;
  1367. m << " Description: " << block->getDeviceDescription() << newLine
  1368. << " Serial: " << block->serialNumber << newLine;
  1369. if (auto bi = BlockImplementation::getFrom (*block))
  1370. m << " Short address: " << (int) bi->getDeviceIndex() << newLine;
  1371. m << " Battery level: " + juce::String (juce::roundToInt (100.0f * block->getBatteryLevel())) + "%" << newLine
  1372. << " Battery charging: " + juce::String (block->isBatteryCharging() ? "y" : "n") << newLine
  1373. << " Width: " << block->getWidth() << newLine
  1374. << " Height: " << block->getHeight() << newLine
  1375. << " Millimeters per unit: " << block->getMillimetersPerUnit() << newLine
  1376. << newLine;
  1377. }
  1378. for (auto& connection : topology.connections)
  1379. {
  1380. m << idToSerialNum (topology, connection.device1)
  1381. << ":" << portToString (connection.connectionPortOnDevice1)
  1382. << " <-> "
  1383. << idToSerialNum (topology, connection.device2)
  1384. << ":" << portToString (connection.connectionPortOnDevice2) << newLine;
  1385. }
  1386. m << "=============================================================================" << newLine;
  1387. Logger::outputDebugString (m.toString());
  1388. }
  1389. #endif
  1390. };
  1391. //==============================================================================
  1392. struct PhysicalTopologySource::DetectorHolder : private juce::Timer
  1393. {
  1394. DetectorHolder (PhysicalTopologySource& pts)
  1395. : topologySource (pts),
  1396. detector (Internal::Detector::getDefaultDetector())
  1397. {
  1398. startTimerHz (30);
  1399. }
  1400. DetectorHolder (PhysicalTopologySource& pts, DeviceDetector& dd)
  1401. : topologySource (pts),
  1402. detector (new Internal::Detector (dd))
  1403. {
  1404. startTimerHz (30);
  1405. }
  1406. void timerCallback() override
  1407. {
  1408. if (! topologySource.hasOwnServiceTimer())
  1409. handleTimerTick();
  1410. }
  1411. void handleTimerTick()
  1412. {
  1413. for (auto& b : detector->currentTopology.blocks)
  1414. if (auto bi = Internal::BlockImplementation::getFrom (*b))
  1415. bi->handleTimerTick();
  1416. }
  1417. PhysicalTopologySource& topologySource;
  1418. Internal::Detector::Ptr detector;
  1419. };
  1420. //==============================================================================
  1421. PhysicalTopologySource::PhysicalTopologySource()
  1422. : detector (new DetectorHolder (*this))
  1423. {
  1424. detector->detector->activeTopologySources.add (this);
  1425. }
  1426. PhysicalTopologySource::PhysicalTopologySource (DeviceDetector& detectorToUse)
  1427. : detector (new DetectorHolder (*this, detectorToUse))
  1428. {
  1429. detector->detector->activeTopologySources.add (this);
  1430. }
  1431. PhysicalTopologySource::~PhysicalTopologySource()
  1432. {
  1433. detector->detector->detach (this);
  1434. detector = nullptr;
  1435. }
  1436. BlockTopology PhysicalTopologySource::getCurrentTopology() const
  1437. {
  1438. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
  1439. return detector->detector->currentTopology;
  1440. }
  1441. void PhysicalTopologySource::cancelAllActiveTouches() noexcept
  1442. {
  1443. detector->detector->cancelAllActiveTouches();
  1444. }
  1445. bool PhysicalTopologySource::hasOwnServiceTimer() const { return false; }
  1446. void PhysicalTopologySource::handleTimerTick() { detector->handleTimerTick(); }
  1447. PhysicalTopologySource::DeviceConnection::DeviceConnection() {}
  1448. PhysicalTopologySource::DeviceConnection::~DeviceConnection() {}
  1449. PhysicalTopologySource::DeviceDetector::DeviceDetector() {}
  1450. PhysicalTopologySource::DeviceDetector::~DeviceDetector() {}
  1451. const char* const* PhysicalTopologySource::getStandardLittleFootFunctions() noexcept
  1452. {
  1453. return BlocksProtocol::ledProgramLittleFootFunctions;
  1454. }
  1455. static bool blocksMatch (const Block::Array& list1, const Block::Array& list2) noexcept
  1456. {
  1457. if (list1.size() != list2.size())
  1458. return false;
  1459. for (auto&& b : list1)
  1460. if (! list2.contains (b))
  1461. return false;
  1462. return true;
  1463. }
  1464. bool BlockTopology::operator== (const BlockTopology& other) const noexcept
  1465. {
  1466. return connections == other.connections && blocksMatch (blocks, other.blocks);
  1467. }
  1468. bool BlockTopology::operator!= (const BlockTopology& other) const noexcept
  1469. {
  1470. return ! operator== (other);
  1471. }
  1472. bool BlockDeviceConnection::operator== (const BlockDeviceConnection& other) const noexcept
  1473. {
  1474. return (device1 == other.device1 && device2 == other.device2
  1475. && connectionPortOnDevice1 == other.connectionPortOnDevice1
  1476. && connectionPortOnDevice2 == other.connectionPortOnDevice2)
  1477. || (device1 == other.device2 && device2 == other.device1
  1478. && connectionPortOnDevice1 == other.connectionPortOnDevice2
  1479. && connectionPortOnDevice2 == other.connectionPortOnDevice1);
  1480. }
  1481. bool BlockDeviceConnection::operator!= (const BlockDeviceConnection& other) const noexcept
  1482. {
  1483. return ! operator== (other);
  1484. }