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.

1846 lines
64KB

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