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.

1852 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 saveProgramAsDefault()
  925. {
  926. sendCommandMessage (BlocksProtocol::saveProgramAsDefault);
  927. }
  928. void handleSharedDataACK (uint32 packetCounter) noexcept
  929. {
  930. pingFromDevice();
  931. remoteHeap.handleACKFromDevice (*this, packetCounter);
  932. }
  933. void pingFromDevice()
  934. {
  935. lastMessageReceiveTime = juce::Time::getCurrentTime();
  936. }
  937. void addDataInputPortListener (DataInputPortListener* listener) override
  938. {
  939. Block::addDataInputPortListener (listener);
  940. if (auto midiInput = getMidiInput())
  941. midiInput->start();
  942. }
  943. void sendMessage (const void* message, size_t messageSize) override
  944. {
  945. if (auto midiOutput = getMidiOutput())
  946. midiOutput->sendMessageNow ({ message, (int) messageSize });
  947. }
  948. void handleTimerTick()
  949. {
  950. if (++resetMessagesSent < 3)
  951. {
  952. if (resetMessagesSent == 1)
  953. sendCommandMessage (BlocksProtocol::endAPIMode);
  954. sendCommandMessage (BlocksProtocol::beginAPIMode);
  955. return;
  956. }
  957. if (ledGrid != nullptr)
  958. if (auto renderer = ledGrid->getRenderer())
  959. renderer->renderLEDGrid (*ledGrid);
  960. remoteHeap.sendChanges (*this);
  961. if (lastMessageSendTime < juce::Time::getCurrentTime() - juce::RelativeTime::milliseconds (pingIntervalMs))
  962. sendCommandMessage (BlocksProtocol::ping);
  963. }
  964. //==============================================================================
  965. std::unique_ptr<TouchSurface> touchSurface;
  966. juce::OwnedArray<ControlButton> controlButtons;
  967. std::unique_ptr<LEDGridImplementation> ledGrid;
  968. std::unique_ptr<LEDRowImplementation> ledRow;
  969. juce::OwnedArray<StatusLight> statusLights;
  970. BlocksProtocol::BlockDataSheet modelData;
  971. MIDIDeviceConnection* listenerToMidiConnection = nullptr;
  972. static constexpr int pingIntervalMs = 400;
  973. static constexpr uint32 maxBlockSize = BlocksProtocol::padBlockProgramAndHeapSize;
  974. static constexpr uint32 maxPacketCounter = BlocksProtocol::PacketCounter::maxValue;
  975. static constexpr uint32 maxPacketSize = 200;
  976. using PacketBuilder = BlocksProtocol::HostPacketBuilder<maxPacketSize>;
  977. using RemoteHeapType = littlefoot::LittleFootRemoteHeap<BlockImplementation>;
  978. RemoteHeapType remoteHeap;
  979. uint32 programSize = 0;
  980. Detector& detector;
  981. juce::Time lastMessageSendTime, lastMessageReceiveTime;
  982. private:
  983. uint32 resetMessagesSent = 0;
  984. bool isStillConnected = true;
  985. bool isMaster = false;
  986. const juce::MidiInput* getMidiInput() const
  987. {
  988. if (auto c = dynamic_cast<MIDIDeviceConnection*> (detector.getDeviceConnectionFor (*this)))
  989. return c->midiInput.get();
  990. jassertfalse;
  991. return nullptr;
  992. }
  993. juce::MidiInput* getMidiInput()
  994. {
  995. return const_cast<juce::MidiInput*> (static_cast<const BlockImplementation&>(*this).getMidiInput());
  996. }
  997. const juce::MidiOutput* getMidiOutput() const
  998. {
  999. if (auto c = dynamic_cast<MIDIDeviceConnection*> (detector.getDeviceConnectionFor (*this)))
  1000. return c->midiOutput.get();
  1001. jassertfalse;
  1002. return nullptr;
  1003. }
  1004. juce::MidiOutput* getMidiOutput()
  1005. {
  1006. return const_cast<juce::MidiOutput*> (static_cast<const BlockImplementation&>(*this).getMidiOutput());
  1007. }
  1008. void handleIncomingMidiMessage (const juce::MidiMessage& message) override
  1009. {
  1010. dataInputPortListeners.call (&Block::DataInputPortListener::handleIncomingDataPortMessage,
  1011. *this, message.getRawData(), (size_t) message.getRawDataSize());
  1012. }
  1013. void connectionBeingDeleted (const MIDIDeviceConnection& c) override
  1014. {
  1015. jassert (listenerToMidiConnection == &c);
  1016. juce::ignoreUnused (c);
  1017. listenerToMidiConnection->removeListener (this);
  1018. listenerToMidiConnection = nullptr;
  1019. }
  1020. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockImplementation)
  1021. };
  1022. //==============================================================================
  1023. struct LEDRowImplementation : public LEDRow
  1024. {
  1025. LEDRowImplementation (BlockImplementation& b) : LEDRow (b), blockImpl (b)
  1026. {
  1027. loadProgramOntoBlock();
  1028. }
  1029. /* Data format:
  1030. 0: 10 x 5-6-5 bits for button LED RGBs
  1031. 20: 15 x 5-6-5 bits for LED row colours
  1032. 50: 1 x 5-6-5 bits for LED row overlay colour
  1033. */
  1034. static constexpr uint32 totalDataSize = 256;
  1035. //==============================================================================
  1036. void setButtonColour (uint32 index, LEDColour colour)
  1037. {
  1038. if (index < 10)
  1039. write565Colour (16 * index, colour);
  1040. }
  1041. int getNumLEDs() const override
  1042. {
  1043. return blockImpl.modelData.numLEDRowLEDs;
  1044. }
  1045. void setLEDColour (int index, LEDColour colour) override
  1046. {
  1047. if ((uint32) index < 15u)
  1048. write565Colour (20 * 8 + 16 * (uint32) index, colour);
  1049. }
  1050. void setOverlayColour (LEDColour colour) override
  1051. {
  1052. write565Colour (50 * 8, colour);
  1053. }
  1054. void resetOverlayColour() override
  1055. {
  1056. write565Colour (50 * 8, {});
  1057. }
  1058. private:
  1059. void loadProgramOntoBlock()
  1060. {
  1061. littlefoot::Compiler compiler;
  1062. compiler.addNativeFunctions (PhysicalTopologySource::getStandardLittleFootFunctions());
  1063. auto err = compiler.compile (getLittleFootProgram(), totalDataSize);
  1064. if (err.failed())
  1065. {
  1066. DBG (err.getErrorMessage());
  1067. jassertfalse;
  1068. return;
  1069. }
  1070. blockImpl.setProgram (compiler.compiledObjectCode.begin(), (size_t) compiler.compiledObjectCode.size());
  1071. }
  1072. void write565Colour (uint32 bitIndex, LEDColour colour)
  1073. {
  1074. blockImpl.setDataBits (bitIndex, 5, colour.getRed() >> 3);
  1075. blockImpl.setDataBits (bitIndex + 5, 6, colour.getGreen() >> 2);
  1076. blockImpl.setDataBits (bitIndex + 11, 5, colour.getBlue() >> 3);
  1077. }
  1078. static const char* getLittleFootProgram() noexcept
  1079. {
  1080. return R"littlefoot(
  1081. int getColour (int bitIndex)
  1082. {
  1083. return makeARGB (255,
  1084. getHeapBits (bitIndex, 5) << 3,
  1085. getHeapBits (bitIndex + 5, 6) << 2,
  1086. getHeapBits (bitIndex + 11, 5) << 3);
  1087. }
  1088. int getButtonColour (int index)
  1089. {
  1090. return getColour (16 * index);
  1091. }
  1092. int getLEDColour (int index)
  1093. {
  1094. if (getHeapInt (50))
  1095. return getColour (50 * 8);
  1096. return getColour (20 * 8 + 16 * index);
  1097. }
  1098. void repaint()
  1099. {
  1100. for (int x = 0; x < 15; ++x)
  1101. setLED (x, 0, getLEDColour (x));
  1102. for (int i = 0; i < 10; ++i)
  1103. setLED (i, 1, getButtonColour (i));
  1104. }
  1105. void handleMessage (int p1, int p2) {}
  1106. )littlefoot";
  1107. }
  1108. BlockImplementation& blockImpl;
  1109. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDRowImplementation)
  1110. };
  1111. //==============================================================================
  1112. struct TouchSurfaceImplementation : public TouchSurface,
  1113. private juce::Timer
  1114. {
  1115. TouchSurfaceImplementation (BlockImplementation& b) : TouchSurface (b), blockImpl (b)
  1116. {
  1117. if (auto det = Detector::getFrom (block))
  1118. det->activeTouchSurfaces.add (this);
  1119. startTimer (500);
  1120. }
  1121. ~TouchSurfaceImplementation()
  1122. {
  1123. if (auto det = Detector::getFrom (block))
  1124. det->activeTouchSurfaces.removeFirstMatchingValue (this);
  1125. }
  1126. int getNumberOfKeywaves() const noexcept override
  1127. {
  1128. return blockImpl.modelData.numKeywaves;
  1129. }
  1130. void broadcastTouchChange (const TouchSurface::Touch& touchEvent)
  1131. {
  1132. auto& status = touches.getValue (touchEvent);
  1133. // Fake a touch end if we receive a duplicate touch-start with no preceding touch-end (ie: comms error)
  1134. if (touchEvent.isTouchStart && status.isActive)
  1135. killTouch (touchEvent, status, juce::Time::getMillisecondCounter());
  1136. // Fake a touch start if we receive an unexpected event with no matching start event. (ie: comms error)
  1137. if (! touchEvent.isTouchStart && ! status.isActive)
  1138. {
  1139. TouchSurface::Touch t (touchEvent);
  1140. t.isTouchStart = true;
  1141. t.isTouchEnd = false;
  1142. if (t.zVelocity <= 0) t.zVelocity = status.lastStrikePressure;
  1143. if (t.zVelocity <= 0) t.zVelocity = t.z;
  1144. if (t.zVelocity <= 0) t.zVelocity = 0.9f;
  1145. listeners.call (&TouchSurface::Listener::touchChanged, *this, t);
  1146. }
  1147. // Normal handling:
  1148. status.lastEventTime = juce::Time::getMillisecondCounter();
  1149. status.isActive = ! touchEvent.isTouchEnd;
  1150. if (touchEvent.isTouchStart)
  1151. status.lastStrikePressure = touchEvent.zVelocity;
  1152. listeners.call (&TouchSurface::Listener::touchChanged, *this, touchEvent);
  1153. }
  1154. void timerCallback() override
  1155. {
  1156. // Find touches that seem to have become stuck, and fake a touch-end for them..
  1157. static const uint32 touchTimeOutMs = 40;
  1158. for (auto& t : touches)
  1159. {
  1160. auto& status = t.value;
  1161. auto now = juce::Time::getMillisecondCounter();
  1162. if (status.isActive && now > status.lastEventTime + touchTimeOutMs)
  1163. killTouch (t.touch, status, now);
  1164. }
  1165. }
  1166. struct TouchStatus
  1167. {
  1168. uint32 lastEventTime = 0;
  1169. float lastStrikePressure = 0;
  1170. bool isActive = false;
  1171. };
  1172. void killTouch (const TouchSurface::Touch& touch, TouchStatus& status, uint32 timeStamp) noexcept
  1173. {
  1174. jassert (status.isActive);
  1175. TouchSurface::Touch killTouch (touch);
  1176. killTouch.z = 0;
  1177. killTouch.xVelocity = 0;
  1178. killTouch.yVelocity = 0;
  1179. killTouch.zVelocity = -1.0f;
  1180. killTouch.eventTimestamp = timeStamp;
  1181. killTouch.isTouchStart = false;
  1182. killTouch.isTouchEnd = true;
  1183. listeners.call (&TouchSurface::Listener::touchChanged, *this, killTouch);
  1184. status.isActive = false;
  1185. }
  1186. void cancelAllActiveTouches() noexcept override
  1187. {
  1188. const auto now = juce::Time::getMillisecondCounter();
  1189. for (auto& t : touches)
  1190. if (t.value.isActive)
  1191. killTouch (t.touch, t.value, now);
  1192. touches.clear();
  1193. }
  1194. BlockImplementation& blockImpl;
  1195. TouchList<TouchStatus> touches;
  1196. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TouchSurfaceImplementation)
  1197. };
  1198. //==============================================================================
  1199. struct ControlButtonImplementation : public ControlButton
  1200. {
  1201. ControlButtonImplementation (BlockImplementation& b, int index, BlocksProtocol::BlockDataSheet::ButtonInfo info)
  1202. : ControlButton (b), blockImpl (b), buttonInfo (info), buttonIndex (index)
  1203. {
  1204. if (auto det = Detector::getFrom (block))
  1205. det->activeControlButtons.add (this);
  1206. }
  1207. ~ControlButtonImplementation()
  1208. {
  1209. if (auto det = Detector::getFrom (block))
  1210. det->activeControlButtons.removeFirstMatchingValue (this);
  1211. }
  1212. ButtonFunction getType() const override { return buttonInfo.type; }
  1213. juce::String getName() const override { return BlocksProtocol::getButtonNameForFunction (buttonInfo.type); }
  1214. float getPositionX() const override { return buttonInfo.x; }
  1215. float getPositionY() const override { return buttonInfo.y; }
  1216. bool hasLight() const override { return blockImpl.isControlBlock(); }
  1217. bool setLightColour (LEDColour colour) override
  1218. {
  1219. if (hasLight())
  1220. {
  1221. if (auto row = blockImpl.ledRow.get())
  1222. {
  1223. row->setButtonColour ((uint32) buttonIndex, colour);
  1224. return true;
  1225. }
  1226. }
  1227. return false;
  1228. }
  1229. void broadcastButtonChange (Block::Timestamp timestamp, ControlButton::ButtonFunction button, bool isDown)
  1230. {
  1231. if (button == buttonInfo.type)
  1232. {
  1233. if (wasDown == isDown)
  1234. sendButtonChangeToListeners (timestamp, ! isDown);
  1235. sendButtonChangeToListeners (timestamp, isDown);
  1236. wasDown = isDown;
  1237. }
  1238. }
  1239. void sendButtonChangeToListeners (Block::Timestamp timestamp, bool isDown)
  1240. {
  1241. if (isDown)
  1242. listeners.call (&ControlButton::Listener::buttonPressed, *this, timestamp);
  1243. else
  1244. listeners.call (&ControlButton::Listener::buttonReleased, *this, timestamp);
  1245. }
  1246. BlockImplementation& blockImpl;
  1247. BlocksProtocol::BlockDataSheet::ButtonInfo buttonInfo;
  1248. int buttonIndex;
  1249. bool wasDown = false;
  1250. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControlButtonImplementation)
  1251. };
  1252. //==============================================================================
  1253. struct StatusLightImplementation : public StatusLight
  1254. {
  1255. StatusLightImplementation (Block& b, BlocksProtocol::BlockDataSheet::StatusLEDInfo i) : StatusLight (b), info (i)
  1256. {
  1257. }
  1258. juce::String getName() const override { return info.name; }
  1259. bool setColour (LEDColour newColour) override
  1260. {
  1261. // XXX TODO!
  1262. juce::ignoreUnused (newColour);
  1263. return false;
  1264. }
  1265. BlocksProtocol::BlockDataSheet::StatusLEDInfo info;
  1266. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StatusLightImplementation)
  1267. };
  1268. //==============================================================================
  1269. struct LEDGridImplementation : public LEDGrid
  1270. {
  1271. LEDGridImplementation (BlockImplementation& b) : LEDGrid (b), blockImpl (b)
  1272. {
  1273. }
  1274. int getNumColumns() const override { return blockImpl.modelData.lightGridWidth; }
  1275. int getNumRows() const override { return blockImpl.modelData.lightGridHeight; }
  1276. juce::Result setProgram (Program* newProgram) override
  1277. {
  1278. if (program.get() != newProgram)
  1279. {
  1280. program.reset (newProgram);
  1281. if (program != nullptr)
  1282. {
  1283. littlefoot::Compiler compiler;
  1284. compiler.addNativeFunctions (PhysicalTopologySource::getStandardLittleFootFunctions());
  1285. auto err = compiler.compile (newProgram->getLittleFootProgram(), newProgram->getHeapSize());
  1286. if (err.failed())
  1287. return err;
  1288. DBG ("Compiled littlefoot program, size = " << (int) compiler.compiledObjectCode.size() << " bytes");
  1289. blockImpl.setProgram (compiler.compiledObjectCode.begin(), (size_t) compiler.compiledObjectCode.size());
  1290. }
  1291. else
  1292. {
  1293. blockImpl.clearProgramAndData();
  1294. }
  1295. }
  1296. else
  1297. {
  1298. jassertfalse;
  1299. }
  1300. return juce::Result::ok();
  1301. }
  1302. Program* getProgram() const override { return program.get(); }
  1303. void sendProgramEvent (const ProgramEventMessage& m) override { blockImpl.sendProgramEvent (m); }
  1304. void saveProgramAsDefault() override { blockImpl.saveProgramAsDefault(); }
  1305. void setDataByte (size_t offset, uint8 value) override { blockImpl.setDataByte (offset, value); }
  1306. void setDataBytes (size_t offset, const void* data, size_t num) override { blockImpl.setDataBytes (offset, data, num); }
  1307. void setDataBits (uint32 startBit, uint32 numBits, uint32 value) override { blockImpl.setDataBits (startBit, numBits, value); }
  1308. uint8 getDataByte (size_t offset) override { return blockImpl.getDataByte (offset); }
  1309. BlockImplementation& blockImpl;
  1310. std::unique_ptr<Program> program;
  1311. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDGridImplementation)
  1312. };
  1313. //==============================================================================
  1314. #if DUMP_TOPOLOGY
  1315. static juce::String idToSerialNum (const BlockTopology& topology, Block::UID uid)
  1316. {
  1317. for (auto* b : topology.blocks)
  1318. if (b->uid == uid)
  1319. return b->serialNumber;
  1320. return "???";
  1321. }
  1322. static juce::String portEdgeToString (Block::ConnectionPort port)
  1323. {
  1324. switch (port.edge)
  1325. {
  1326. case Block::ConnectionPort::DeviceEdge::north: return "north";
  1327. case Block::ConnectionPort::DeviceEdge::south: return "south";
  1328. case Block::ConnectionPort::DeviceEdge::east: return "east";
  1329. case Block::ConnectionPort::DeviceEdge::west: return "west";
  1330. }
  1331. return {};
  1332. }
  1333. static juce::String portToString (Block::ConnectionPort port)
  1334. {
  1335. return portEdgeToString (port) + "_" + juce::String (port.index);
  1336. }
  1337. static void dumpTopology (const BlockTopology& topology)
  1338. {
  1339. MemoryOutputStream m;
  1340. m << "=============================================================================" << newLine
  1341. << "Topology: " << topology.blocks.size() << " device(s)" << newLine
  1342. << newLine;
  1343. int index = 0;
  1344. for (auto block : topology.blocks)
  1345. {
  1346. m << "Device " << index++ << (block->isMasterBlock() ? ": (MASTER)" : ":") << newLine;
  1347. m << " Description: " << block->getDeviceDescription() << newLine
  1348. << " Serial: " << block->serialNumber << newLine;
  1349. if (auto bi = BlockImplementation::getFrom (*block))
  1350. m << " Short address: " << (int) bi->getDeviceIndex() << newLine;
  1351. m << " Battery level: " + juce::String (juce::roundToInt (100.0f * block->getBatteryLevel())) + "%" << newLine
  1352. << " Battery charging: " + juce::String (block->isBatteryCharging() ? "y" : "n") << newLine
  1353. << " Width: " << block->getWidth() << newLine
  1354. << " Height: " << block->getHeight() << newLine
  1355. << " Millimeters per unit: " << block->getMillimetersPerUnit() << newLine
  1356. << newLine;
  1357. }
  1358. for (auto& connection : topology.connections)
  1359. {
  1360. m << idToSerialNum (topology, connection.device1)
  1361. << ":" << portToString (connection.connectionPortOnDevice1)
  1362. << " <-> "
  1363. << idToSerialNum (topology, connection.device2)
  1364. << ":" << portToString (connection.connectionPortOnDevice2) << newLine;
  1365. }
  1366. m << "=============================================================================" << newLine;
  1367. Logger::outputDebugString (m.toString());
  1368. }
  1369. #endif
  1370. };
  1371. //==============================================================================
  1372. struct PhysicalTopologySource::DetectorHolder : private juce::Timer
  1373. {
  1374. DetectorHolder (PhysicalTopologySource& pts)
  1375. : topologySource (pts),
  1376. detector (Internal::Detector::getDefaultDetector())
  1377. {
  1378. startTimerHz (30);
  1379. }
  1380. DetectorHolder (PhysicalTopologySource& pts, DeviceDetector& dd)
  1381. : topologySource (pts),
  1382. detector (new Internal::Detector (dd))
  1383. {
  1384. startTimerHz (30);
  1385. }
  1386. void timerCallback() override
  1387. {
  1388. if (! topologySource.hasOwnServiceTimer())
  1389. handleTimerTick();
  1390. }
  1391. void handleTimerTick()
  1392. {
  1393. for (auto& b : detector->currentTopology.blocks)
  1394. if (auto bi = Internal::BlockImplementation::getFrom (*b))
  1395. bi->handleTimerTick();
  1396. }
  1397. PhysicalTopologySource& topologySource;
  1398. Internal::Detector::Ptr detector;
  1399. };
  1400. //==============================================================================
  1401. PhysicalTopologySource::PhysicalTopologySource()
  1402. : detector (new DetectorHolder (*this))
  1403. {
  1404. detector->detector->activeTopologySources.add (this);
  1405. }
  1406. PhysicalTopologySource::PhysicalTopologySource (DeviceDetector& detectorToUse)
  1407. : detector (new DetectorHolder (*this, detectorToUse))
  1408. {
  1409. detector->detector->activeTopologySources.add (this);
  1410. }
  1411. PhysicalTopologySource::~PhysicalTopologySource()
  1412. {
  1413. detector->detector->detach (this);
  1414. detector = nullptr;
  1415. }
  1416. BlockTopology PhysicalTopologySource::getCurrentTopology() const
  1417. {
  1418. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
  1419. return detector->detector->currentTopology;
  1420. }
  1421. void PhysicalTopologySource::cancelAllActiveTouches() noexcept
  1422. {
  1423. detector->detector->cancelAllActiveTouches();
  1424. }
  1425. bool PhysicalTopologySource::hasOwnServiceTimer() const { return false; }
  1426. void PhysicalTopologySource::handleTimerTick() { detector->handleTimerTick(); }
  1427. PhysicalTopologySource::DeviceConnection::DeviceConnection() {}
  1428. PhysicalTopologySource::DeviceConnection::~DeviceConnection() {}
  1429. PhysicalTopologySource::DeviceDetector::DeviceDetector() {}
  1430. PhysicalTopologySource::DeviceDetector::~DeviceDetector() {}
  1431. const char* const* PhysicalTopologySource::getStandardLittleFootFunctions() noexcept
  1432. {
  1433. return BlocksProtocol::ledProgramLittleFootFunctions;
  1434. }
  1435. static bool blocksMatch (const Block::Array& list1, const Block::Array& list2) noexcept
  1436. {
  1437. if (list1.size() != list2.size())
  1438. return false;
  1439. for (auto&& b : list1)
  1440. if (! list2.contains (b))
  1441. return false;
  1442. return true;
  1443. }
  1444. bool BlockTopology::operator== (const BlockTopology& other) const noexcept
  1445. {
  1446. return connections == other.connections && blocksMatch (blocks, other.blocks);
  1447. }
  1448. bool BlockTopology::operator!= (const BlockTopology& other) const noexcept
  1449. {
  1450. return ! operator== (other);
  1451. }
  1452. bool BlockDeviceConnection::operator== (const BlockDeviceConnection& other) const noexcept
  1453. {
  1454. return (device1 == other.device1 && device2 == other.device2
  1455. && connectionPortOnDevice1 == other.connectionPortOnDevice1
  1456. && connectionPortOnDevice2 == other.connectionPortOnDevice2)
  1457. || (device1 == other.device2 && device2 == other.device1
  1458. && connectionPortOnDevice1 == other.connectionPortOnDevice2
  1459. && connectionPortOnDevice2 == other.connectionPortOnDevice1);
  1460. }
  1461. bool BlockDeviceConnection::operator!= (const BlockDeviceConnection& other) const noexcept
  1462. {
  1463. return ! operator== (other);
  1464. }