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.

1152 lines
37KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. template <typename Detector>
  20. struct BlockImplementation : public Block,
  21. private MIDIDeviceConnection::Listener,
  22. private Timer
  23. {
  24. public:
  25. struct ControlButtonImplementation;
  26. struct TouchSurfaceImplementation;
  27. struct LEDGridImplementation;
  28. struct LEDRowImplementation;
  29. BlockImplementation (Detector& detectorToUse, const DeviceInfo& deviceInfo)
  30. : Block (deviceInfo.serial.asString(),
  31. deviceInfo.version.asString(),
  32. deviceInfo.name.asString()),
  33. modelData (deviceInfo.serial),
  34. remoteHeap (modelData.programAndHeapSize),
  35. detector (&detectorToUse),
  36. config (modelData.defaultConfig)
  37. {
  38. markReconnected (deviceInfo);
  39. if (modelData.hasTouchSurface)
  40. touchSurface.reset (new TouchSurfaceImplementation (*this));
  41. int i = 0;
  42. for (auto&& b : modelData.buttons)
  43. controlButtons.add (new ControlButtonImplementation (*this, i++, b));
  44. if (modelData.lightGridWidth > 0 && modelData.lightGridHeight > 0)
  45. ledGrid.reset (new LEDGridImplementation (*this));
  46. for (auto&& s : modelData.statusLEDs)
  47. statusLights.add (new StatusLightImplementation (*this, s));
  48. updateMidiConnectionListener();
  49. }
  50. ~BlockImplementation() override
  51. {
  52. markDisconnected();
  53. }
  54. void markDisconnected()
  55. {
  56. if (auto surface = dynamic_cast<TouchSurfaceImplementation*> (touchSurface.get()))
  57. surface->disableTouchSurface();
  58. disconnectMidiConnectionListener();
  59. connectionTime = Time();
  60. }
  61. void markReconnected (const DeviceInfo& deviceInfo)
  62. {
  63. if (wasPowerCycled())
  64. resetPowerCycleFlag();
  65. if (connectionTime == Time())
  66. connectionTime = Time::getCurrentTime();
  67. updateDeviceInfo (deviceInfo);
  68. remoteHeap.reset();
  69. setProgram (nullptr);
  70. if (auto surface = dynamic_cast<TouchSurfaceImplementation*> (touchSurface.get()))
  71. surface->activateTouchSurface();
  72. updateMidiConnectionListener();
  73. }
  74. void updateDeviceInfo (const DeviceInfo& deviceInfo)
  75. {
  76. versionNumber = deviceInfo.version.asString();
  77. name = deviceInfo.name.asString();
  78. isMaster = deviceInfo.isMaster;
  79. masterUID = deviceInfo.masterUid;
  80. batteryCharging = deviceInfo.batteryCharging;
  81. batteryLevel = deviceInfo.batteryLevel;
  82. topologyIndex = deviceInfo.index;
  83. }
  84. void setToMaster (bool shouldBeMaster)
  85. {
  86. isMaster = shouldBeMaster;
  87. }
  88. void updateMidiConnectionListener()
  89. {
  90. if (detector == nullptr)
  91. return;
  92. listenerToMidiConnection = dynamic_cast<MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this));
  93. if (listenerToMidiConnection != nullptr)
  94. listenerToMidiConnection->addListener (this);
  95. config.setDeviceComms (listenerToMidiConnection);
  96. }
  97. void disconnectMidiConnectionListener()
  98. {
  99. if (listenerToMidiConnection != nullptr)
  100. {
  101. config.setDeviceComms (nullptr);
  102. listenerToMidiConnection->removeListener (this);
  103. listenerToMidiConnection = nullptr;
  104. }
  105. }
  106. bool isConnected() const override
  107. {
  108. if (detector != nullptr)
  109. return detector->isConnected (uid);
  110. return false;
  111. }
  112. bool isConnectedViaBluetooth() const override
  113. {
  114. if (detector != nullptr)
  115. return detector->isConnectedViaBluetooth (*this);
  116. return false;
  117. }
  118. Type getType() const override { return modelData.apiType; }
  119. String getDeviceDescription() const override { return modelData.description; }
  120. int getWidth() const override { return modelData.widthUnits; }
  121. int getHeight() const override { return modelData.heightUnits; }
  122. float getMillimetersPerUnit() const override { return 47.0f; }
  123. bool isHardwareBlock() const override { return true; }
  124. juce::Array<Block::ConnectionPort> getPorts() const override { return modelData.ports; }
  125. Time getConnectionTime() const override { return connectionTime; }
  126. bool isMasterBlock() const override { return isMaster; }
  127. Block::UID getConnectedMasterUID() const override { return masterUID; }
  128. int getRotation() const override { return rotation; }
  129. BlockArea getBlockAreaWithinLayout() const override
  130. {
  131. if (rotation % 2 == 0)
  132. return { position.first, position.second, modelData.widthUnits, modelData.heightUnits };
  133. return { position.first, position.second, modelData.heightUnits, modelData.widthUnits };
  134. }
  135. TouchSurface* getTouchSurface() const override { return touchSurface.get(); }
  136. LEDGrid* getLEDGrid() const override { return ledGrid.get(); }
  137. LEDRow* getLEDRow() override
  138. {
  139. if (ledRow == nullptr && modelData.numLEDRowLEDs > 0)
  140. ledRow.reset (new LEDRowImplementation (*this));
  141. return ledRow.get();
  142. }
  143. juce::Array<ControlButton*> getButtons() const override
  144. {
  145. juce::Array<ControlButton*> result;
  146. result.addArray (controlButtons);
  147. return result;
  148. }
  149. juce::Array<StatusLight*> getStatusLights() const override
  150. {
  151. juce::Array<StatusLight*> result;
  152. result.addArray (statusLights);
  153. return result;
  154. }
  155. float getBatteryLevel() const override
  156. {
  157. return batteryLevel.toUnipolarFloat();
  158. }
  159. bool isBatteryCharging() const override
  160. {
  161. return batteryCharging.get() > 0;
  162. }
  163. bool supportsGraphics() const override
  164. {
  165. return false;
  166. }
  167. int getDeviceIndex() const noexcept
  168. {
  169. return isConnected() ? topologyIndex : -1;
  170. }
  171. template <typename PacketBuilder>
  172. bool sendMessageToDevice (const PacketBuilder& builder)
  173. {
  174. if (detector != nullptr)
  175. {
  176. lastMessageSendTime = Time::getCurrentTime();
  177. return detector->sendMessageToDevice (uid, builder);
  178. }
  179. return false;
  180. }
  181. bool sendCommandMessage (uint32 commandID)
  182. {
  183. return buildAndSendPacket<64> ([commandID] (BlocksProtocol::HostPacketBuilder<64>& p)
  184. { return p.deviceControlMessage (commandID); });
  185. }
  186. void handleProgramEvent (const ProgramEventMessage& message)
  187. {
  188. programEventListeners.call ([&] (ProgramEventListener& l) { l.handleProgramEvent(*this, message); });
  189. }
  190. void handleCustomMessage (Block::Timestamp, const int32* data)
  191. {
  192. ProgramEventMessage m;
  193. for (uint32 i = 0; i < BlocksProtocol::numProgramMessageInts; ++i)
  194. m.values[i] = data[i];
  195. handleProgramEvent (m);
  196. }
  197. static BlockImplementation* getFrom (Block* b) noexcept
  198. {
  199. jassert (dynamic_cast<BlockImplementation*> (b) != nullptr);
  200. return dynamic_cast<BlockImplementation*> (b);
  201. }
  202. static BlockImplementation* getFrom (Block& b) noexcept
  203. {
  204. return getFrom (&b);
  205. }
  206. //==============================================================================
  207. std::function<void (const Block& block, const String&)> logger;
  208. void setLogger (std::function<void (const Block& block, const String&)> newLogger) override
  209. {
  210. logger = std::move (newLogger);
  211. }
  212. void handleLogMessage (const String& message) const
  213. {
  214. if (logger != nullptr)
  215. logger (*this, message);
  216. }
  217. //==============================================================================
  218. Result setProgram (std::unique_ptr<Program> newProgram,
  219. ProgramPersistency persistency = ProgramPersistency::setAsTemp) override
  220. {
  221. auto doProgramsMatch = [&]
  222. {
  223. if (program == nullptr || newProgram == nullptr)
  224. return false;
  225. return program->getLittleFootProgram() == newProgram->getLittleFootProgram()
  226. && program->getSearchPaths() == newProgram->getSearchPaths();
  227. }();
  228. if (doProgramsMatch)
  229. {
  230. if (isProgramLoaded)
  231. {
  232. MessageManager::callAsync ([blockRef = Block::Ptr (this), this]
  233. {
  234. programLoadedListeners.call ([&] (ProgramLoadedListener& l) { l.handleProgramLoaded (*this); });
  235. });
  236. }
  237. return Result::ok();
  238. }
  239. program = std::move (newProgram);
  240. return loadProgram (persistency);
  241. }
  242. Result loadProgram (ProgramPersistency persistency)
  243. {
  244. stopTimer();
  245. programSize = 0;
  246. isProgramLoaded = shouldSaveProgramAsDefault = false;
  247. if (program == nullptr)
  248. {
  249. remoteHeap.clearTargetData();
  250. return Result::ok();
  251. }
  252. auto res = compileProgram();
  253. if (res.failed())
  254. return res;
  255. programSize = (uint32) compiler.compiledObjectCode.size();
  256. remoteHeap.resetDataRangeToUnknown (0, remoteHeap.blockSize);
  257. remoteHeap.clearTargetData();
  258. remoteHeap.sendChanges (*this, true);
  259. remoteHeap.resetDataRangeToUnknown (0, programSize);
  260. remoteHeap.setBytes (0, compiler.compiledObjectCode.begin(), programSize);
  261. remoteHeap.sendChanges (*this, true);
  262. this->resetConfigListActiveStatus();
  263. const auto legacyProgramChangeConfigIndex = getMaxConfigIndex();
  264. handleConfigItemChanged ({ legacyProgramChangeConfigIndex }, legacyProgramChangeConfigIndex);
  265. shouldSaveProgramAsDefault = persistency == ProgramPersistency::setAsDefault;
  266. startTimer (20);
  267. return Result::ok();
  268. }
  269. Result compileProgram()
  270. {
  271. compiler.addNativeFunctions (PhysicalTopologySource::getStandardLittleFootFunctions());
  272. const auto err = compiler.compile (program->getLittleFootProgram(), 512, program->getSearchPaths());
  273. if (err.failed())
  274. return err;
  275. DBG ("Compiled littlefoot program, space needed: "
  276. << (int) compiler.getCompiledProgram().getTotalSpaceNeeded() << " bytes");
  277. if (compiler.getCompiledProgram().getTotalSpaceNeeded() > getMemorySize())
  278. return Result::fail ("Program too large!");
  279. return Result::ok();
  280. }
  281. Program* getProgram() const override { return program.get(); }
  282. void sendProgramEvent (const ProgramEventMessage& message) override
  283. {
  284. static_assert (sizeof (ProgramEventMessage::values) == 4 * BlocksProtocol::numProgramMessageInts,
  285. "Need to keep the internal and external messages structures the same");
  286. if (remoteHeap.isProgramLoaded())
  287. {
  288. buildAndSendPacket<128> ([&message] (BlocksProtocol::HostPacketBuilder<128>& p)
  289. { return p.addProgramEventMessage (message.values); });
  290. }
  291. }
  292. void timerCallback() override
  293. {
  294. if (remoteHeap.isFullySynced() && remoteHeap.isProgramLoaded())
  295. {
  296. isProgramLoaded = true;
  297. stopTimer();
  298. if (shouldSaveProgramAsDefault)
  299. doSaveProgramAsDefault();
  300. programLoadedListeners.call([&] (ProgramLoadedListener& l) { l.handleProgramLoaded (*this); });
  301. }
  302. else
  303. {
  304. startTimer (100);
  305. }
  306. }
  307. void saveProgramAsDefault() override
  308. {
  309. shouldSaveProgramAsDefault = true;
  310. if (! isTimerRunning() && isProgramLoaded)
  311. doSaveProgramAsDefault();
  312. }
  313. void resetProgramToDefault() override
  314. {
  315. if (! shouldSaveProgramAsDefault)
  316. setProgram (nullptr);
  317. sendCommandMessage (BlocksProtocol::endAPIMode);
  318. sendCommandMessage (BlocksProtocol::beginAPIMode);
  319. }
  320. uint32 getMemorySize() override
  321. {
  322. return modelData.programAndHeapSize;
  323. }
  324. uint32 getHeapMemorySize() override
  325. {
  326. jassert (isPositiveAndNotGreaterThan (programSize, modelData.programAndHeapSize));
  327. return modelData.programAndHeapSize - programSize;
  328. }
  329. void setDataByte (size_t offset, uint8 value) override
  330. {
  331. remoteHeap.setByte (programSize + offset, value);
  332. }
  333. void setDataBytes (size_t offset, const void* newData, size_t num) override
  334. {
  335. remoteHeap.setBytes (programSize + offset, static_cast<const uint8*> (newData), num);
  336. }
  337. void setDataBits (uint32 startBit, uint32 numBits, uint32 value) override
  338. {
  339. remoteHeap.setBits (programSize * 8 + startBit, numBits, value);
  340. }
  341. uint8 getDataByte (size_t offset) override
  342. {
  343. return remoteHeap.getByte (programSize + offset);
  344. }
  345. void handleSharedDataACK (uint32 packetCounter) noexcept
  346. {
  347. pingFromDevice();
  348. remoteHeap.handleACKFromDevice (*this, packetCounter);
  349. }
  350. bool sendFirmwareUpdatePacket (const uint8* data, uint8 size, std::function<void (uint8, uint32)> callback) override
  351. {
  352. firmwarePacketAckCallback = nullptr;
  353. if (buildAndSendPacket<256> ([data, size] (BlocksProtocol::HostPacketBuilder<256>& p)
  354. { return p.addFirmwareUpdatePacket (data, size); }))
  355. {
  356. firmwarePacketAckCallback = callback;
  357. return true;
  358. }
  359. return false;
  360. }
  361. void handleFirmwareUpdateACK (uint8 resultCode, uint32 resultDetail)
  362. {
  363. if (firmwarePacketAckCallback != nullptr)
  364. {
  365. firmwarePacketAckCallback (resultCode, resultDetail);
  366. firmwarePacketAckCallback = nullptr;
  367. }
  368. }
  369. void handleConfigUpdateMessage (int32 item, int32 value, int32 min, int32 max)
  370. {
  371. config.handleConfigUpdateMessage (item, value, min, max);
  372. }
  373. void handleConfigSetMessage (int32 item, int32 value)
  374. {
  375. config.handleConfigSetMessage (item, value);
  376. }
  377. void pingFromDevice()
  378. {
  379. lastMessageReceiveTime = Time::getCurrentTime();
  380. }
  381. MIDIDeviceConnection* getDeviceConnection()
  382. {
  383. return dynamic_cast<MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this));
  384. }
  385. void addDataInputPortListener (DataInputPortListener* listener) override
  386. {
  387. if (auto deviceConnection = getDeviceConnection())
  388. {
  389. {
  390. ScopedLock scopedLock (deviceConnection->criticalSecton);
  391. Block::addDataInputPortListener (listener);
  392. }
  393. deviceConnection->midiInput->start();
  394. }
  395. else
  396. {
  397. Block::addDataInputPortListener (listener);
  398. }
  399. }
  400. void removeDataInputPortListener (DataInputPortListener* listener) override
  401. {
  402. if (auto deviceConnection = getDeviceConnection())
  403. {
  404. {
  405. ScopedLock scopedLock (deviceConnection->criticalSecton);
  406. Block::removeDataInputPortListener (listener);
  407. }
  408. }
  409. else
  410. {
  411. Block::removeDataInputPortListener (listener);
  412. }
  413. }
  414. void sendMessage (const void* message, size_t messageSize) override
  415. {
  416. if (auto midiOutput = getMidiOutput())
  417. midiOutput->sendMessageNow ({ message, (int) messageSize });
  418. }
  419. void handleTimerTick()
  420. {
  421. if (ledGrid != nullptr)
  422. if (auto renderer = ledGrid->getRenderer())
  423. renderer->renderLEDGrid (*ledGrid);
  424. remoteHeap.sendChanges (*this, false);
  425. if (lastMessageSendTime < Time::getCurrentTime() - getPingInterval())
  426. sendCommandMessage (BlocksProtocol::ping);
  427. }
  428. RelativeTime getPingInterval()
  429. {
  430. return RelativeTime::milliseconds (isMaster ? masterPingIntervalMs : dnaPingIntervalMs);
  431. }
  432. //==============================================================================
  433. void handleConfigItemChanged (const ConfigMetaData& data, uint32 index)
  434. {
  435. configItemListeners.call([&] (ConfigItemListener& l) { l.handleConfigItemChanged (*this, data, index); });
  436. }
  437. void handleConfigSyncEnded()
  438. {
  439. configItemListeners.call([&] (ConfigItemListener& l) { l.handleConfigSyncEnded (*this); });
  440. }
  441. int32 getLocalConfigValue (uint32 item) override
  442. {
  443. initialiseDeviceIndexAndConnection();
  444. return config.getItemValue ((BlocksProtocol::ConfigItemId) item);
  445. }
  446. void setLocalConfigValue (uint32 item, int32 value) override
  447. {
  448. initialiseDeviceIndexAndConnection();
  449. config.setItemValue ((BlocksProtocol::ConfigItemId) item, value);
  450. }
  451. void setLocalConfigRange (uint32 item, int32 min, int32 max) override
  452. {
  453. initialiseDeviceIndexAndConnection();
  454. config.setItemMin ((BlocksProtocol::ConfigItemId) item, min);
  455. config.setItemMax ((BlocksProtocol::ConfigItemId) item, max);
  456. }
  457. void setLocalConfigItemActive (uint32 item, bool isActive) override
  458. {
  459. initialiseDeviceIndexAndConnection();
  460. config.setItemActive ((BlocksProtocol::ConfigItemId) item, isActive);
  461. }
  462. bool isLocalConfigItemActive (uint32 item) override
  463. {
  464. initialiseDeviceIndexAndConnection();
  465. return config.getItemActive ((BlocksProtocol::ConfigItemId) item);
  466. }
  467. uint32 getMaxConfigIndex() override
  468. {
  469. return uint32 (BlocksProtocol::maxConfigIndex);
  470. }
  471. bool isValidUserConfigIndex (uint32 item) override
  472. {
  473. return item >= (uint32) BlocksProtocol::ConfigItemId::user0
  474. && item < (uint32) (BlocksProtocol::ConfigItemId::user0 + numberOfUserConfigs);
  475. }
  476. ConfigMetaData getLocalConfigMetaData (uint32 item) override
  477. {
  478. initialiseDeviceIndexAndConnection();
  479. return config.getMetaData ((BlocksProtocol::ConfigItemId) item);
  480. }
  481. void requestFactoryConfigSync() override
  482. {
  483. initialiseDeviceIndexAndConnection();
  484. config.requestFactoryConfigSync();
  485. }
  486. void resetConfigListActiveStatus() override
  487. {
  488. config.resetConfigListActiveStatus();
  489. }
  490. bool setName (const String& newName) override
  491. {
  492. return buildAndSendPacket<128> ([&newName] (BlocksProtocol::HostPacketBuilder<128>& p)
  493. { return p.addSetBlockName (newName); });
  494. }
  495. void factoryReset() override
  496. {
  497. buildAndSendPacket<32> ([] (BlocksProtocol::HostPacketBuilder<32>& p)
  498. { return p.addFactoryReset(); });
  499. juce::Timer::callAfterDelay (5, [ref = WeakReference<BlockImplementation>(this)]
  500. {
  501. if (ref != nullptr)
  502. ref->blockReset();
  503. });
  504. }
  505. void blockReset() override
  506. {
  507. bool messageSent = false;
  508. if (isMasterBlock())
  509. {
  510. sendMessage (BlocksProtocol::SpecialMessageFromHost::resetMaster,
  511. sizeof (BlocksProtocol::SpecialMessageFromHost::resetMaster));
  512. messageSent = true;
  513. }
  514. else
  515. {
  516. messageSent = buildAndSendPacket<32> ([] (BlocksProtocol::HostPacketBuilder<32>& p)
  517. { return p.addBlockReset(); });
  518. }
  519. if (messageSent)
  520. {
  521. hasBeenPowerCycled = true;
  522. if (detector != nullptr)
  523. detector->notifyBlockIsRestarting (uid);
  524. }
  525. }
  526. bool wasPowerCycled() const { return hasBeenPowerCycled; }
  527. void resetPowerCycleFlag() { hasBeenPowerCycled = false; }
  528. //==============================================================================
  529. std::unique_ptr<TouchSurface> touchSurface;
  530. OwnedArray<ControlButton> controlButtons;
  531. std::unique_ptr<LEDGridImplementation> ledGrid;
  532. std::unique_ptr<LEDRowImplementation> ledRow;
  533. OwnedArray<StatusLight> statusLights;
  534. BlocksProtocol::BlockDataSheet modelData;
  535. MIDIDeviceConnection* listenerToMidiConnection = nullptr;
  536. static constexpr int masterPingIntervalMs = 400;
  537. static constexpr int dnaPingIntervalMs = 1666;
  538. static constexpr uint32 maxBlockSize = BlocksProtocol::padBlockProgramAndHeapSize;
  539. static constexpr uint32 maxPacketCounter = BlocksProtocol::PacketCounter::maxValue;
  540. static constexpr uint32 maxPacketSize = 200;
  541. using PacketBuilder = BlocksProtocol::HostPacketBuilder<maxPacketSize>;
  542. using RemoteHeapType = littlefoot::LittleFootRemoteHeap<BlockImplementation>;
  543. RemoteHeapType remoteHeap;
  544. WeakReference<Detector> detector;
  545. Time lastMessageSendTime, lastMessageReceiveTime;
  546. BlockConfigManager config;
  547. private:
  548. littlefoot::Compiler compiler;
  549. std::unique_ptr<Program> program;
  550. uint32 programSize = 0;
  551. std::function<void (uint8, uint32)> firmwarePacketAckCallback;
  552. bool isMaster = false;
  553. Block::UID masterUID = {};
  554. BlocksProtocol::BatteryLevel batteryLevel {};
  555. BlocksProtocol::BatteryCharging batteryCharging {};
  556. BlocksProtocol::TopologyIndex topologyIndex {};
  557. Time connectionTime {};
  558. std::pair<int, int> position;
  559. int rotation = 0;
  560. friend Detector;
  561. bool isProgramLoaded = false;
  562. bool shouldSaveProgramAsDefault = false;
  563. bool hasBeenPowerCycled = false;
  564. void initialiseDeviceIndexAndConnection()
  565. {
  566. config.setDeviceIndex ((TopologyIndex) getDeviceIndex());
  567. config.setDeviceComms (listenerToMidiConnection);
  568. }
  569. const MidiInput* getMidiInput() const
  570. {
  571. if (detector != nullptr)
  572. if (auto c = dynamic_cast<const MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this)))
  573. return c->midiInput.get();
  574. jassertfalse;
  575. return nullptr;
  576. }
  577. MidiInput* getMidiInput()
  578. {
  579. return const_cast<MidiInput*> (static_cast<const BlockImplementation&>(*this).getMidiInput());
  580. }
  581. const MidiOutput* getMidiOutput() const
  582. {
  583. if (detector != nullptr)
  584. if (auto c = dynamic_cast<const MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this)))
  585. return c->midiOutput.get();
  586. jassertfalse;
  587. return nullptr;
  588. }
  589. MidiOutput* getMidiOutput()
  590. {
  591. return const_cast<MidiOutput*> (static_cast<const BlockImplementation&>(*this).getMidiOutput());
  592. }
  593. void handleIncomingMidiMessage (const MidiMessage& message) override
  594. {
  595. dataInputPortListeners.call ([&] (DataInputPortListener& l) { l.handleIncomingDataPortMessage (*this, message.getRawData(),
  596. (size_t) message.getRawDataSize()); });
  597. }
  598. void connectionBeingDeleted (const MIDIDeviceConnection& c) override
  599. {
  600. jassert (listenerToMidiConnection == &c);
  601. ignoreUnused (c);
  602. disconnectMidiConnectionListener();
  603. }
  604. void doSaveProgramAsDefault()
  605. {
  606. sendCommandMessage (BlocksProtocol::saveProgramAsDefault);
  607. }
  608. template <int packetBytes, typename PacketBuilderFn>
  609. bool buildAndSendPacket (PacketBuilderFn buildFn)
  610. {
  611. auto index = getDeviceIndex();
  612. if (index < 0)
  613. {
  614. jassertfalse;
  615. return false;
  616. }
  617. BlocksProtocol::HostPacketBuilder<packetBytes> p;
  618. p.writePacketSysexHeaderBytes ((BlocksProtocol::TopologyIndex) index);
  619. if (! buildFn (p))
  620. return false;
  621. p.writePacketSysexFooter();
  622. return sendMessageToDevice (p);
  623. }
  624. public:
  625. //==============================================================================
  626. struct TouchSurfaceImplementation : public TouchSurface,
  627. private Timer
  628. {
  629. TouchSurfaceImplementation (BlockImplementation& b) : TouchSurface (b), blockImpl (b)
  630. {
  631. activateTouchSurface();
  632. }
  633. ~TouchSurfaceImplementation() override
  634. {
  635. disableTouchSurface();
  636. }
  637. void activateTouchSurface()
  638. {
  639. startTimer (500);
  640. }
  641. void disableTouchSurface()
  642. {
  643. stopTimer();
  644. }
  645. int getNumberOfKeywaves() const noexcept override
  646. {
  647. return blockImpl.modelData.numKeywaves;
  648. }
  649. void broadcastTouchChange (const TouchSurface::Touch& touchEvent)
  650. {
  651. auto& status = touches.getValue (touchEvent);
  652. // Fake a touch end if we receive a duplicate touch-start with no preceding touch-end (ie: comms error)
  653. if (touchEvent.isTouchStart && status.isActive)
  654. killTouch (touchEvent, status, Time::getMillisecondCounter());
  655. // Fake a touch start if we receive an unexpected event with no matching start event. (ie: comms error)
  656. if (! touchEvent.isTouchStart && ! status.isActive)
  657. {
  658. TouchSurface::Touch t (touchEvent);
  659. t.isTouchStart = true;
  660. t.isTouchEnd = false;
  661. if (t.zVelocity <= 0) t.zVelocity = status.lastStrikePressure;
  662. if (t.zVelocity <= 0) t.zVelocity = t.z;
  663. if (t.zVelocity <= 0) t.zVelocity = 0.9f;
  664. listeners.call ([&] (TouchSurface::Listener& l) { l.touchChanged (*this, t); });
  665. }
  666. // Normal handling:
  667. status.lastEventTime = Time::getMillisecondCounter();
  668. status.isActive = ! touchEvent.isTouchEnd;
  669. if (touchEvent.isTouchStart)
  670. status.lastStrikePressure = touchEvent.zVelocity;
  671. listeners.call ([&] (TouchSurface::Listener& l) { l.touchChanged (*this, touchEvent); });
  672. }
  673. void timerCallback() override
  674. {
  675. // Find touches that seem to have become stuck, and fake a touch-end for them..
  676. static const uint32 touchTimeOutMs = 500;
  677. for (auto& t : touches)
  678. {
  679. auto& status = t.value;
  680. auto now = Time::getMillisecondCounter();
  681. if (status.isActive && now > status.lastEventTime + touchTimeOutMs)
  682. killTouch (t.touch, status, now);
  683. }
  684. }
  685. struct TouchStatus
  686. {
  687. uint32 lastEventTime = 0;
  688. float lastStrikePressure = 0;
  689. bool isActive = false;
  690. };
  691. void killTouch (const TouchSurface::Touch& touch, TouchStatus& status, uint32 timeStamp) noexcept
  692. {
  693. jassert (status.isActive);
  694. TouchSurface::Touch killTouch (touch);
  695. killTouch.z = 0;
  696. killTouch.xVelocity = 0;
  697. killTouch.yVelocity = 0;
  698. killTouch.zVelocity = -1.0f;
  699. killTouch.eventTimestamp = timeStamp;
  700. killTouch.isTouchStart = false;
  701. killTouch.isTouchEnd = true;
  702. listeners.call ([&] (TouchSurface::Listener& l) { l.touchChanged (*this, killTouch); });
  703. status.isActive = false;
  704. }
  705. void cancelAllActiveTouches() noexcept override
  706. {
  707. const auto now = Time::getMillisecondCounter();
  708. for (auto& t : touches)
  709. if (t.value.isActive)
  710. killTouch (t.touch, t.value, now);
  711. touches.clear();
  712. }
  713. BlockImplementation& blockImpl;
  714. TouchList<TouchStatus> touches;
  715. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TouchSurfaceImplementation)
  716. };
  717. //==============================================================================
  718. struct ControlButtonImplementation : public ControlButton
  719. {
  720. ControlButtonImplementation (BlockImplementation& b, int index, BlocksProtocol::BlockDataSheet::ButtonInfo info)
  721. : ControlButton (b), blockImpl (b), buttonInfo (info), buttonIndex (index)
  722. {
  723. }
  724. ~ControlButtonImplementation() override
  725. {
  726. }
  727. ButtonFunction getType() const override { return buttonInfo.type; }
  728. String getName() const override { return BlocksProtocol::getButtonNameForFunction (buttonInfo.type); }
  729. float getPositionX() const override { return buttonInfo.x; }
  730. float getPositionY() const override { return buttonInfo.y; }
  731. bool hasLight() const override { return blockImpl.isControlBlock(); }
  732. bool setLightColour (LEDColour colour) override
  733. {
  734. if (hasLight())
  735. {
  736. if (auto row = blockImpl.ledRow.get())
  737. {
  738. row->setButtonColour ((uint32) buttonIndex, colour);
  739. return true;
  740. }
  741. }
  742. return false;
  743. }
  744. void broadcastButtonChange (Block::Timestamp timestamp, ControlButton::ButtonFunction button, bool isDown)
  745. {
  746. if (button == buttonInfo.type)
  747. {
  748. if (wasDown == isDown)
  749. sendButtonChangeToListeners (timestamp, ! isDown);
  750. sendButtonChangeToListeners (timestamp, isDown);
  751. wasDown = isDown;
  752. }
  753. }
  754. void sendButtonChangeToListeners (Block::Timestamp timestamp, bool isDown)
  755. {
  756. if (isDown)
  757. listeners.call ([&] (ControlButton::Listener& l) { l.buttonPressed (*this, timestamp); });
  758. else
  759. listeners.call ([&] (ControlButton::Listener& l) { l.buttonReleased (*this, timestamp); });
  760. }
  761. BlockImplementation& blockImpl;
  762. BlocksProtocol::BlockDataSheet::ButtonInfo buttonInfo;
  763. int buttonIndex;
  764. bool wasDown = false;
  765. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControlButtonImplementation)
  766. };
  767. //==============================================================================
  768. struct StatusLightImplementation : public StatusLight
  769. {
  770. StatusLightImplementation (Block& b, BlocksProtocol::BlockDataSheet::StatusLEDInfo i) : StatusLight (b), info (i)
  771. {
  772. }
  773. String getName() const override { return info.name; }
  774. bool setColour (LEDColour newColour) override
  775. {
  776. // XXX TODO!
  777. ignoreUnused (newColour);
  778. return false;
  779. }
  780. BlocksProtocol::BlockDataSheet::StatusLEDInfo info;
  781. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StatusLightImplementation)
  782. };
  783. //==============================================================================
  784. struct LEDGridImplementation : public LEDGrid
  785. {
  786. LEDGridImplementation (BlockImplementation& b) : LEDGrid (b), blockImpl (b)
  787. {
  788. }
  789. int getNumColumns() const override { return blockImpl.modelData.lightGridWidth; }
  790. int getNumRows() const override { return blockImpl.modelData.lightGridHeight; }
  791. BlockImplementation& blockImpl;
  792. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDGridImplementation)
  793. };
  794. //==============================================================================
  795. struct LEDRowImplementation : public LEDRow,
  796. private Timer
  797. {
  798. LEDRowImplementation (BlockImplementation& b) : LEDRow (b)
  799. {
  800. startTimer (300);
  801. }
  802. void setButtonColour (uint32 index, LEDColour colour)
  803. {
  804. if (index < 10)
  805. {
  806. colours[index] = colour;
  807. flush();
  808. }
  809. }
  810. int getNumLEDs() const override
  811. {
  812. return static_cast<const BlockImplementation&> (block).modelData.numLEDRowLEDs;
  813. }
  814. void setLEDColour (int index, LEDColour colour) override
  815. {
  816. if ((uint32) index < 15u)
  817. {
  818. colours[10 + index] = colour;
  819. flush();
  820. }
  821. }
  822. void setOverlayColour (LEDColour colour) override
  823. {
  824. colours[25] = colour;
  825. flush();
  826. }
  827. void resetOverlayColour() override
  828. {
  829. setOverlayColour ({});
  830. }
  831. private:
  832. LEDColour colours[26];
  833. void timerCallback() override
  834. {
  835. stopTimer();
  836. loadProgramOntoBlock();
  837. flush();
  838. }
  839. void loadProgramOntoBlock()
  840. {
  841. if (block.getProgram() == nullptr)
  842. {
  843. auto err = block.setProgram (std::make_unique <DefaultLEDGridProgram> (block));
  844. if (err.failed())
  845. {
  846. DBG (err.getErrorMessage());
  847. jassertfalse;
  848. }
  849. }
  850. }
  851. void flush()
  852. {
  853. if (block.getProgram() != nullptr)
  854. for (uint32 i = 0; i < (uint32) numElementsInArray (colours); ++i)
  855. write565Colour (16 * i, colours[i]);
  856. }
  857. void write565Colour (uint32 bitIndex, LEDColour colour)
  858. {
  859. block.setDataBits (bitIndex, 5, (uint32) (colour.getRed() >> 3));
  860. block.setDataBits (bitIndex + 5, 6, (uint32) (colour.getGreen() >> 2));
  861. block.setDataBits (bitIndex + 11, 5, (uint32) (colour.getBlue() >> 3));
  862. }
  863. struct DefaultLEDGridProgram : public Block::Program
  864. {
  865. DefaultLEDGridProgram (Block& b) : Block::Program (b) {}
  866. String getLittleFootProgram() override
  867. {
  868. /* Data format:
  869. 0: 10 x 5-6-5 bits for button LED RGBs
  870. 20: 15 x 5-6-5 bits for LED row colours
  871. 50: 1 x 5-6-5 bits for LED row overlay colour
  872. */
  873. return R"littlefoot(
  874. #heapsize: 128
  875. int getColour (int bitIndex)
  876. {
  877. return makeARGB (255,
  878. getHeapBits (bitIndex, 5) << 3,
  879. getHeapBits (bitIndex + 5, 6) << 2,
  880. getHeapBits (bitIndex + 11, 5) << 3);
  881. }
  882. int getButtonColour (int index)
  883. {
  884. return getColour (16 * index);
  885. }
  886. int getLEDColour (int index)
  887. {
  888. if (getHeapInt (50))
  889. return getColour (50 * 8);
  890. return getColour (20 * 8 + 16 * index);
  891. }
  892. void repaint()
  893. {
  894. for (int x = 0; x < 15; ++x)
  895. fillPixel (getLEDColour (x), x, 0);
  896. for (int i = 0; i < 10; ++i)
  897. fillPixel (getButtonColour (i), i, 1);
  898. }
  899. void handleMessage (int p1, int p2) {}
  900. )littlefoot";
  901. }
  902. };
  903. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDRowImplementation)
  904. };
  905. private:
  906. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockImplementation)
  907. JUCE_DECLARE_WEAK_REFERENCEABLE (BlockImplementation)
  908. };
  909. } // namespace juce