1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 The code included in this file is provided under the terms of the ISC license 11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission 12 To use, copy, modify, and/or distribute this software for any purpose with or 13 without fee is hereby granted provided that the above copyright notice and 14 this permission notice appear in all copies. 15 16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 18 DISCLAIMED. 19 20 ============================================================================== 21 */ 22 23 namespace juce 24 { 25 26 template <typename Detector> 27 struct BlockImplementation : public Block, 28 private MIDIDeviceConnection::Listener, 29 private Timer 30 { 31 public: 32 struct ControlButtonImplementation; 33 struct TouchSurfaceImplementation; 34 struct LEDGridImplementation; 35 struct LEDRowImplementation; 36 BlockImplementationjuce::BlockImplementation37 BlockImplementation (Detector& detectorToUse, const DeviceInfo& deviceInfo) 38 : Block (deviceInfo.serial.asString(), 39 deviceInfo.version.asString(), 40 deviceInfo.name.asString()), 41 modelData (deviceInfo.serial), 42 remoteHeap (modelData.programAndHeapSize), 43 detector (&detectorToUse), 44 config (modelData.defaultConfig) 45 { 46 markReconnected (deviceInfo); 47 48 if (modelData.hasTouchSurface) 49 touchSurface.reset (new TouchSurfaceImplementation (*this)); 50 51 int i = 0; 52 53 for (auto&& b : modelData.buttons) 54 controlButtons.add (new ControlButtonImplementation (*this, i++, b)); 55 56 if (modelData.lightGridWidth > 0 && modelData.lightGridHeight > 0) 57 ledGrid.reset (new LEDGridImplementation (*this)); 58 59 for (auto&& s : modelData.statusLEDs) 60 statusLights.add (new StatusLightImplementation (*this, s)); 61 62 updateMidiConnectionListener(); 63 } 64 ~BlockImplementationjuce::BlockImplementation65 ~BlockImplementation() override 66 { 67 markDisconnected(); 68 } 69 markDisconnectedjuce::BlockImplementation70 void markDisconnected() 71 { 72 if (auto surface = dynamic_cast<TouchSurfaceImplementation*> (touchSurface.get())) 73 surface->disableTouchSurface(); 74 75 disconnectMidiConnectionListener(); 76 connectionTime = Time(); 77 } 78 markReconnectedjuce::BlockImplementation79 void markReconnected (const DeviceInfo& deviceInfo) 80 { 81 if (wasPowerCycled()) 82 resetPowerCycleFlag(); 83 84 if (connectionTime == Time()) 85 connectionTime = Time::getCurrentTime(); 86 87 updateDeviceInfo (deviceInfo); 88 89 remoteHeap.reset(); 90 91 setProgram (nullptr); 92 93 if (auto surface = dynamic_cast<TouchSurfaceImplementation*> (touchSurface.get())) 94 surface->activateTouchSurface(); 95 96 updateMidiConnectionListener(); 97 } 98 updateDeviceInfojuce::BlockImplementation99 void updateDeviceInfo (const DeviceInfo& deviceInfo) 100 { 101 versionNumber = deviceInfo.version.asString(); 102 name = deviceInfo.name.asString(); 103 isMaster = deviceInfo.isMaster; 104 masterUID = deviceInfo.masterUid; 105 batteryCharging = deviceInfo.batteryCharging; 106 batteryLevel = deviceInfo.batteryLevel; 107 topologyIndex = deviceInfo.index; 108 } 109 setToMasterjuce::BlockImplementation110 void setToMaster (bool shouldBeMaster) 111 { 112 isMaster = shouldBeMaster; 113 } 114 updateMidiConnectionListenerjuce::BlockImplementation115 void updateMidiConnectionListener() 116 { 117 if (detector == nullptr) 118 return; 119 120 listenerToMidiConnection = dynamic_cast<MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this)); 121 122 if (listenerToMidiConnection != nullptr) 123 listenerToMidiConnection->addListener (this); 124 125 config.setDeviceComms (listenerToMidiConnection); 126 } 127 disconnectMidiConnectionListenerjuce::BlockImplementation128 void disconnectMidiConnectionListener() 129 { 130 if (listenerToMidiConnection != nullptr) 131 { 132 config.setDeviceComms (nullptr); 133 listenerToMidiConnection->removeListener (this); 134 listenerToMidiConnection = nullptr; 135 } 136 } 137 isConnectedjuce::BlockImplementation138 bool isConnected() const override 139 { 140 if (detector != nullptr) 141 return detector->isConnected (uid); 142 143 return false; 144 } 145 isConnectedViaBluetoothjuce::BlockImplementation146 bool isConnectedViaBluetooth() const override 147 { 148 if (detector != nullptr) 149 return detector->isConnectedViaBluetooth (*this); 150 151 return false; 152 } 153 getTypejuce::BlockImplementation154 Type getType() const override { return modelData.apiType; } getDeviceDescriptionjuce::BlockImplementation155 String getDeviceDescription() const override { return modelData.description; } getWidthjuce::BlockImplementation156 int getWidth() const override { return modelData.widthUnits; } getHeightjuce::BlockImplementation157 int getHeight() const override { return modelData.heightUnits; } getMillimetersPerUnitjuce::BlockImplementation158 float getMillimetersPerUnit() const override { return 47.0f; } isHardwareBlockjuce::BlockImplementation159 bool isHardwareBlock() const override { return true; } getPortsjuce::BlockImplementation160 juce::Array<Block::ConnectionPort> getPorts() const override { return modelData.ports; } getConnectionTimejuce::BlockImplementation161 Time getConnectionTime() const override { return connectionTime; } isMasterBlockjuce::BlockImplementation162 bool isMasterBlock() const override { return isMaster; } getConnectedMasterUIDjuce::BlockImplementation163 Block::UID getConnectedMasterUID() const override { return masterUID; } getRotationjuce::BlockImplementation164 int getRotation() const override { return rotation; } 165 getBlockAreaWithinLayoutjuce::BlockImplementation166 BlockArea getBlockAreaWithinLayout() const override 167 { 168 if (rotation % 2 == 0) 169 return { position.first, position.second, modelData.widthUnits, modelData.heightUnits }; 170 171 return { position.first, position.second, modelData.heightUnits, modelData.widthUnits }; 172 } 173 getTouchSurfacejuce::BlockImplementation174 TouchSurface* getTouchSurface() const override { return touchSurface.get(); } getLEDGridjuce::BlockImplementation175 LEDGrid* getLEDGrid() const override { return ledGrid.get(); } 176 getLEDRowjuce::BlockImplementation177 LEDRow* getLEDRow() override 178 { 179 if (ledRow == nullptr && modelData.numLEDRowLEDs > 0) 180 ledRow.reset (new LEDRowImplementation (*this)); 181 182 return ledRow.get(); 183 } 184 getButtonsjuce::BlockImplementation185 juce::Array<ControlButton*> getButtons() const override 186 { 187 juce::Array<ControlButton*> result; 188 result.addArray (controlButtons); 189 return result; 190 } 191 getStatusLightsjuce::BlockImplementation192 juce::Array<StatusLight*> getStatusLights() const override 193 { 194 juce::Array<StatusLight*> result; 195 result.addArray (statusLights); 196 return result; 197 } 198 getBatteryLeveljuce::BlockImplementation199 float getBatteryLevel() const override 200 { 201 return batteryLevel.toUnipolarFloat(); 202 } 203 isBatteryChargingjuce::BlockImplementation204 bool isBatteryCharging() const override 205 { 206 return batteryCharging.get() > 0; 207 } 208 supportsGraphicsjuce::BlockImplementation209 bool supportsGraphics() const override 210 { 211 return false; 212 } 213 getDeviceIndexjuce::BlockImplementation214 int getDeviceIndex() const noexcept 215 { 216 return isConnected() ? topologyIndex : -1; 217 } 218 219 template <typename PacketBuilder> sendMessageToDevicejuce::BlockImplementation220 bool sendMessageToDevice (const PacketBuilder& builder) 221 { 222 if (detector != nullptr) 223 { 224 lastMessageSendTime = Time::getCurrentTime(); 225 return detector->sendMessageToDevice (uid, builder); 226 } 227 228 return false; 229 } 230 sendCommandMessagejuce::BlockImplementation231 bool sendCommandMessage (uint32 commandID) 232 { 233 return buildAndSendPacket<64> ([commandID] (BlocksProtocol::HostPacketBuilder<64>& p) 234 { return p.deviceControlMessage (commandID); }); 235 } 236 handleProgramEventjuce::BlockImplementation237 void handleProgramEvent (const ProgramEventMessage& message) 238 { 239 programEventListeners.call ([&] (ProgramEventListener& l) { l.handleProgramEvent(*this, message); }); 240 } 241 handleCustomMessagejuce::BlockImplementation242 void handleCustomMessage (Block::Timestamp, const int32* data) 243 { 244 ProgramEventMessage m; 245 246 for (uint32 i = 0; i < BlocksProtocol::numProgramMessageInts; ++i) 247 m.values[i] = data[i]; 248 249 handleProgramEvent (m); 250 } 251 getFromjuce::BlockImplementation252 static BlockImplementation* getFrom (Block* b) noexcept 253 { 254 jassert (dynamic_cast<BlockImplementation*> (b) != nullptr); 255 return dynamic_cast<BlockImplementation*> (b); 256 } 257 getFromjuce::BlockImplementation258 static BlockImplementation* getFrom (Block& b) noexcept 259 { 260 return getFrom (&b); 261 } 262 263 //============================================================================== 264 std::function<void (const Block& block, const String&)> logger; 265 setLoggerjuce::BlockImplementation266 void setLogger (std::function<void (const Block& block, const String&)> newLogger) override 267 { 268 logger = std::move (newLogger); 269 } 270 handleLogMessagejuce::BlockImplementation271 void handleLogMessage (const String& message) const 272 { 273 if (logger != nullptr) 274 logger (*this, message); 275 } 276 277 //============================================================================== setProgramjuce::BlockImplementation278 Result setProgram (std::unique_ptr<Program> newProgram, 279 ProgramPersistency persistency = ProgramPersistency::setAsTemp) override 280 { 281 auto doProgramsMatch = [&] 282 { 283 if (program == nullptr || newProgram == nullptr) 284 return false; 285 286 return program->getLittleFootProgram() == newProgram->getLittleFootProgram() 287 && program->getSearchPaths() == newProgram->getSearchPaths(); 288 }(); 289 290 if (doProgramsMatch) 291 { 292 if (isProgramLoaded) 293 { 294 MessageManager::callAsync ([blockRef = Block::Ptr (this), this] 295 { 296 programLoadedListeners.call ([&] (ProgramLoadedListener& l) { l.handleProgramLoaded (*this); }); 297 }); 298 } 299 300 return Result::ok(); 301 } 302 303 program = std::move (newProgram); 304 return loadProgram (persistency); 305 } 306 loadProgramjuce::BlockImplementation307 Result loadProgram (ProgramPersistency persistency) 308 { 309 stopTimer(); 310 311 programSize = 0; 312 isProgramLoaded = shouldSaveProgramAsDefault = false; 313 314 if (program == nullptr) 315 { 316 remoteHeap.clearTargetData(); 317 return Result::ok(); 318 } 319 320 auto res = compileProgram(); 321 322 if (res.failed()) 323 return res; 324 325 programSize = (uint32) compiler.compiledObjectCode.size(); 326 327 remoteHeap.resetDataRangeToUnknown (0, remoteHeap.blockSize); 328 remoteHeap.clearTargetData(); 329 remoteHeap.sendChanges (*this, true); 330 331 remoteHeap.resetDataRangeToUnknown (0, programSize); 332 remoteHeap.setBytes (0, compiler.compiledObjectCode.begin(), programSize); 333 remoteHeap.sendChanges (*this, true); 334 335 this->resetConfigListActiveStatus(); 336 337 const auto legacyProgramChangeConfigIndex = getMaxConfigIndex(); 338 handleConfigItemChanged ({ legacyProgramChangeConfigIndex }, legacyProgramChangeConfigIndex); 339 340 shouldSaveProgramAsDefault = persistency == ProgramPersistency::setAsDefault; 341 startTimer (20); 342 343 return Result::ok(); 344 } 345 compileProgramjuce::BlockImplementation346 Result compileProgram() 347 { 348 compiler.addNativeFunctions (PhysicalTopologySource::getStandardLittleFootFunctions()); 349 350 const auto err = compiler.compile (program->getLittleFootProgram(), 512, program->getSearchPaths()); 351 352 if (err.failed()) 353 return err; 354 355 DBG ("Compiled littlefoot program, space needed: " 356 << (int) compiler.getCompiledProgram().getTotalSpaceNeeded() << " bytes"); 357 358 if (compiler.getCompiledProgram().getTotalSpaceNeeded() > getMemorySize()) 359 return Result::fail ("Program too large!"); 360 361 return Result::ok(); 362 } 363 getProgramjuce::BlockImplementation364 Program* getProgram() const override { return program.get(); } 365 sendProgramEventjuce::BlockImplementation366 void sendProgramEvent (const ProgramEventMessage& message) override 367 { 368 static_assert (sizeof (ProgramEventMessage::values) == 4 * BlocksProtocol::numProgramMessageInts, 369 "Need to keep the internal and external messages structures the same"); 370 371 if (remoteHeap.isProgramLoaded()) 372 { 373 buildAndSendPacket<128> ([&message] (BlocksProtocol::HostPacketBuilder<128>& p) 374 { return p.addProgramEventMessage (message.values); }); 375 } 376 } 377 timerCallbackjuce::BlockImplementation378 void timerCallback() override 379 { 380 if (remoteHeap.isFullySynced() && remoteHeap.isProgramLoaded()) 381 { 382 isProgramLoaded = true; 383 stopTimer(); 384 385 if (shouldSaveProgramAsDefault) 386 doSaveProgramAsDefault(); 387 388 programLoadedListeners.call([&] (ProgramLoadedListener& l) { l.handleProgramLoaded (*this); }); 389 } 390 else 391 { 392 startTimer (100); 393 } 394 } 395 saveProgramAsDefaultjuce::BlockImplementation396 void saveProgramAsDefault() override 397 { 398 shouldSaveProgramAsDefault = true; 399 400 if (! isTimerRunning() && isProgramLoaded) 401 doSaveProgramAsDefault(); 402 } 403 resetProgramToDefaultjuce::BlockImplementation404 void resetProgramToDefault() override 405 { 406 if (! shouldSaveProgramAsDefault) 407 setProgram (nullptr); 408 409 sendCommandMessage (BlocksProtocol::endAPIMode); 410 sendCommandMessage (BlocksProtocol::beginAPIMode); 411 } 412 getMemorySizejuce::BlockImplementation413 uint32 getMemorySize() override 414 { 415 return modelData.programAndHeapSize; 416 } 417 getHeapMemorySizejuce::BlockImplementation418 uint32 getHeapMemorySize() override 419 { 420 jassert (isPositiveAndNotGreaterThan (programSize, modelData.programAndHeapSize)); 421 return modelData.programAndHeapSize - programSize; 422 } 423 setDataBytejuce::BlockImplementation424 void setDataByte (size_t offset, uint8 value) override 425 { 426 remoteHeap.setByte (programSize + offset, value); 427 } 428 setDataBytesjuce::BlockImplementation429 void setDataBytes (size_t offset, const void* newData, size_t num) override 430 { 431 remoteHeap.setBytes (programSize + offset, static_cast<const uint8*> (newData), num); 432 } 433 setDataBitsjuce::BlockImplementation434 void setDataBits (uint32 startBit, uint32 numBits, uint32 value) override 435 { 436 remoteHeap.setBits (programSize * 8 + startBit, numBits, value); 437 } 438 getDataBytejuce::BlockImplementation439 uint8 getDataByte (size_t offset) override 440 { 441 return remoteHeap.getByte (programSize + offset); 442 } 443 handleSharedDataACKjuce::BlockImplementation444 void handleSharedDataACK (uint32 packetCounter) noexcept 445 { 446 pingFromDevice(); 447 remoteHeap.handleACKFromDevice (*this, packetCounter); 448 } 449 sendFirmwareUpdatePacketjuce::BlockImplementation450 bool sendFirmwareUpdatePacket (const uint8* data, uint8 size, std::function<void (uint8, uint32)> callback) override 451 { 452 firmwarePacketAckCallback = nullptr; 453 454 if (buildAndSendPacket<256> ([data, size] (BlocksProtocol::HostPacketBuilder<256>& p) 455 { return p.addFirmwareUpdatePacket (data, size); })) 456 { 457 firmwarePacketAckCallback = callback; 458 return true; 459 } 460 461 return false; 462 } 463 handleFirmwareUpdateACKjuce::BlockImplementation464 void handleFirmwareUpdateACK (uint8 resultCode, uint32 resultDetail) 465 { 466 if (firmwarePacketAckCallback != nullptr) 467 { 468 firmwarePacketAckCallback (resultCode, resultDetail); 469 firmwarePacketAckCallback = nullptr; 470 } 471 } 472 handleConfigUpdateMessagejuce::BlockImplementation473 void handleConfigUpdateMessage (int32 item, int32 value, int32 min, int32 max) 474 { 475 config.handleConfigUpdateMessage (item, value, min, max); 476 } 477 handleConfigSetMessagejuce::BlockImplementation478 void handleConfigSetMessage (int32 item, int32 value) 479 { 480 config.handleConfigSetMessage (item, value); 481 } 482 pingFromDevicejuce::BlockImplementation483 void pingFromDevice() 484 { 485 lastMessageReceiveTime = Time::getCurrentTime(); 486 } 487 getDeviceConnectionjuce::BlockImplementation488 MIDIDeviceConnection* getDeviceConnection() 489 { 490 return dynamic_cast<MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this)); 491 } 492 addDataInputPortListenerjuce::BlockImplementation493 void addDataInputPortListener (DataInputPortListener* listener) override 494 { 495 if (auto deviceConnection = getDeviceConnection()) 496 { 497 { 498 ScopedLock scopedLock (deviceConnection->criticalSecton); 499 Block::addDataInputPortListener (listener); 500 } 501 502 deviceConnection->midiInput->start(); 503 } 504 else 505 { 506 Block::addDataInputPortListener (listener); 507 } 508 } 509 removeDataInputPortListenerjuce::BlockImplementation510 void removeDataInputPortListener (DataInputPortListener* listener) override 511 { 512 if (auto deviceConnection = getDeviceConnection()) 513 { 514 { 515 ScopedLock scopedLock (deviceConnection->criticalSecton); 516 Block::removeDataInputPortListener (listener); 517 } 518 } 519 else 520 { 521 Block::removeDataInputPortListener (listener); 522 } 523 } 524 sendMessagejuce::BlockImplementation525 void sendMessage (const void* message, size_t messageSize) override 526 { 527 if (auto midiOutput = getMidiOutput()) 528 midiOutput->sendMessageNow ({ message, (int) messageSize }); 529 } 530 handleTimerTickjuce::BlockImplementation531 void handleTimerTick() 532 { 533 if (ledGrid != nullptr) 534 if (auto renderer = ledGrid->getRenderer()) 535 renderer->renderLEDGrid (*ledGrid); 536 537 remoteHeap.sendChanges (*this, false); 538 539 if (lastMessageSendTime < Time::getCurrentTime() - getPingInterval()) 540 sendCommandMessage (BlocksProtocol::ping); 541 } 542 getPingIntervaljuce::BlockImplementation543 RelativeTime getPingInterval() 544 { 545 return RelativeTime::milliseconds (isMaster ? masterPingIntervalMs : dnaPingIntervalMs); 546 } 547 548 //============================================================================== handleConfigItemChangedjuce::BlockImplementation549 void handleConfigItemChanged (const ConfigMetaData& data, uint32 index) 550 { 551 configItemListeners.call([&] (ConfigItemListener& l) { l.handleConfigItemChanged (*this, data, index); }); 552 } 553 handleConfigSyncEndedjuce::BlockImplementation554 void handleConfigSyncEnded() 555 { 556 configItemListeners.call([&] (ConfigItemListener& l) { l.handleConfigSyncEnded (*this); }); 557 } 558 getLocalConfigValuejuce::BlockImplementation559 int32 getLocalConfigValue (uint32 item) override 560 { 561 initialiseDeviceIndexAndConnection(); 562 return config.getItemValue ((BlocksProtocol::ConfigItemId) item); 563 } 564 setLocalConfigValuejuce::BlockImplementation565 void setLocalConfigValue (uint32 item, int32 value) override 566 { 567 initialiseDeviceIndexAndConnection(); 568 config.setItemValue ((BlocksProtocol::ConfigItemId) item, value); 569 } 570 setLocalConfigRangejuce::BlockImplementation571 void setLocalConfigRange (uint32 item, int32 min, int32 max) override 572 { 573 initialiseDeviceIndexAndConnection(); 574 config.setItemMin ((BlocksProtocol::ConfigItemId) item, min); 575 config.setItemMax ((BlocksProtocol::ConfigItemId) item, max); 576 } 577 setLocalConfigItemActivejuce::BlockImplementation578 void setLocalConfigItemActive (uint32 item, bool isActive) override 579 { 580 initialiseDeviceIndexAndConnection(); 581 config.setItemActive ((BlocksProtocol::ConfigItemId) item, isActive); 582 } 583 isLocalConfigItemActivejuce::BlockImplementation584 bool isLocalConfigItemActive (uint32 item) override 585 { 586 initialiseDeviceIndexAndConnection(); 587 return config.getItemActive ((BlocksProtocol::ConfigItemId) item); 588 } 589 getMaxConfigIndexjuce::BlockImplementation590 uint32 getMaxConfigIndex() override 591 { 592 return uint32 (BlocksProtocol::maxConfigIndex); 593 } 594 isValidUserConfigIndexjuce::BlockImplementation595 bool isValidUserConfigIndex (uint32 item) override 596 { 597 return item >= (uint32) BlocksProtocol::ConfigItemId::user0 598 && item < (uint32) (BlocksProtocol::ConfigItemId::user0 + numberOfUserConfigs); 599 } 600 getLocalConfigMetaDatajuce::BlockImplementation601 ConfigMetaData getLocalConfigMetaData (uint32 item) override 602 { 603 initialiseDeviceIndexAndConnection(); 604 return config.getMetaData ((BlocksProtocol::ConfigItemId) item); 605 } 606 requestFactoryConfigSyncjuce::BlockImplementation607 void requestFactoryConfigSync() override 608 { 609 initialiseDeviceIndexAndConnection(); 610 config.requestFactoryConfigSync(); 611 } 612 resetConfigListActiveStatusjuce::BlockImplementation613 void resetConfigListActiveStatus() override 614 { 615 config.resetConfigListActiveStatus(); 616 } 617 setNamejuce::BlockImplementation618 bool setName (const String& newName) override 619 { 620 return buildAndSendPacket<128> ([&newName] (BlocksProtocol::HostPacketBuilder<128>& p) 621 { return p.addSetBlockName (newName); }); 622 } 623 factoryResetjuce::BlockImplementation624 void factoryReset() override 625 { 626 buildAndSendPacket<32> ([] (BlocksProtocol::HostPacketBuilder<32>& p) 627 { return p.addFactoryReset(); }); 628 629 juce::Timer::callAfterDelay (5, [ref = WeakReference<BlockImplementation>(this)] 630 { 631 if (ref != nullptr) 632 ref->blockReset(); 633 }); 634 } 635 blockResetjuce::BlockImplementation636 void blockReset() override 637 { 638 bool messageSent = false; 639 640 if (isMasterBlock()) 641 { 642 sendMessage (BlocksProtocol::SpecialMessageFromHost::resetMaster, 643 sizeof (BlocksProtocol::SpecialMessageFromHost::resetMaster)); 644 messageSent = true; 645 } 646 else 647 { 648 messageSent = buildAndSendPacket<32> ([] (BlocksProtocol::HostPacketBuilder<32>& p) 649 { return p.addBlockReset(); }); 650 } 651 652 if (messageSent) 653 { 654 hasBeenPowerCycled = true; 655 656 if (detector != nullptr) 657 detector->notifyBlockIsRestarting (uid); 658 } 659 } 660 wasPowerCycledjuce::BlockImplementation661 bool wasPowerCycled() const { return hasBeenPowerCycled; } resetPowerCycleFlagjuce::BlockImplementation662 void resetPowerCycleFlag() { hasBeenPowerCycled = false; } 663 664 //============================================================================== 665 std::unique_ptr<TouchSurface> touchSurface; 666 OwnedArray<ControlButton> controlButtons; 667 std::unique_ptr<LEDGridImplementation> ledGrid; 668 std::unique_ptr<LEDRowImplementation> ledRow; 669 OwnedArray<StatusLight> statusLights; 670 671 BlocksProtocol::BlockDataSheet modelData; 672 673 MIDIDeviceConnection* listenerToMidiConnection = nullptr; 674 675 static constexpr int masterPingIntervalMs = 400; 676 static constexpr int dnaPingIntervalMs = 1666; 677 678 static constexpr uint32 maxBlockSize = BlocksProtocol::padBlockProgramAndHeapSize; 679 static constexpr uint32 maxPacketCounter = BlocksProtocol::PacketCounter::maxValue; 680 static constexpr uint32 maxPacketSize = 200; 681 682 using PacketBuilder = BlocksProtocol::HostPacketBuilder<maxPacketSize>; 683 684 using RemoteHeapType = littlefoot::LittleFootRemoteHeap<BlockImplementation>; 685 RemoteHeapType remoteHeap; 686 687 WeakReference<Detector> detector; 688 Time lastMessageSendTime, lastMessageReceiveTime; 689 690 BlockConfigManager config; 691 692 private: 693 littlefoot::Compiler compiler; 694 std::unique_ptr<Program> program; 695 uint32 programSize = 0; 696 697 std::function<void (uint8, uint32)> firmwarePacketAckCallback; 698 699 bool isMaster = false; 700 Block::UID masterUID = {}; 701 702 BlocksProtocol::BatteryLevel batteryLevel {}; 703 BlocksProtocol::BatteryCharging batteryCharging {}; 704 705 BlocksProtocol::TopologyIndex topologyIndex {}; 706 707 Time connectionTime {}; 708 709 std::pair<int, int> position; 710 int rotation = 0; 711 friend Detector; 712 713 bool isProgramLoaded = false; 714 bool shouldSaveProgramAsDefault = false; 715 bool hasBeenPowerCycled = false; 716 initialiseDeviceIndexAndConnectionjuce::BlockImplementation717 void initialiseDeviceIndexAndConnection() 718 { 719 config.setDeviceIndex ((TopologyIndex) getDeviceIndex()); 720 config.setDeviceComms (listenerToMidiConnection); 721 } 722 getMidiInputjuce::BlockImplementation723 const MidiInput* getMidiInput() const 724 { 725 if (detector != nullptr) 726 if (auto c = dynamic_cast<const MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this))) 727 return c->midiInput.get(); 728 729 jassertfalse; 730 return nullptr; 731 } 732 getMidiInputjuce::BlockImplementation733 MidiInput* getMidiInput() 734 { 735 return const_cast<MidiInput*> (static_cast<const BlockImplementation&>(*this).getMidiInput()); 736 } 737 getMidiOutputjuce::BlockImplementation738 const MidiOutput* getMidiOutput() const 739 { 740 if (detector != nullptr) 741 if (auto c = dynamic_cast<const MIDIDeviceConnection*> (detector->getDeviceConnectionFor (*this))) 742 return c->midiOutput.get(); 743 744 jassertfalse; 745 return nullptr; 746 } 747 getMidiOutputjuce::BlockImplementation748 MidiOutput* getMidiOutput() 749 { 750 return const_cast<MidiOutput*> (static_cast<const BlockImplementation&>(*this).getMidiOutput()); 751 } 752 handleIncomingMidiMessagejuce::BlockImplementation753 void handleIncomingMidiMessage (const MidiMessage& message) override 754 { 755 dataInputPortListeners.call ([&] (DataInputPortListener& l) { l.handleIncomingDataPortMessage (*this, message.getRawData(), 756 (size_t) message.getRawDataSize()); }); 757 } 758 connectionBeingDeletedjuce::BlockImplementation759 void connectionBeingDeleted (const MIDIDeviceConnection& c) override 760 { 761 jassert (listenerToMidiConnection == &c); 762 ignoreUnused (c); 763 disconnectMidiConnectionListener(); 764 } 765 doSaveProgramAsDefaultjuce::BlockImplementation766 void doSaveProgramAsDefault() 767 { 768 sendCommandMessage (BlocksProtocol::saveProgramAsDefault); 769 } 770 771 template <int packetBytes, typename PacketBuilderFn> buildAndSendPacketjuce::BlockImplementation772 bool buildAndSendPacket (PacketBuilderFn buildFn) 773 { 774 auto index = getDeviceIndex(); 775 776 if (index < 0) 777 { 778 jassertfalse; 779 return false; 780 } 781 782 BlocksProtocol::HostPacketBuilder<packetBytes> p; 783 p.writePacketSysexHeaderBytes ((BlocksProtocol::TopologyIndex) index); 784 785 if (! buildFn (p)) 786 return false; 787 788 p.writePacketSysexFooter(); 789 return sendMessageToDevice (p); 790 } 791 792 public: 793 //============================================================================== 794 struct TouchSurfaceImplementation : public TouchSurface, 795 private Timer 796 { TouchSurfaceImplementationjuce::BlockImplementation::TouchSurfaceImplementation797 TouchSurfaceImplementation (BlockImplementation& b) : TouchSurface (b), blockImpl (b) 798 { 799 activateTouchSurface(); 800 } 801 ~TouchSurfaceImplementationjuce::BlockImplementation::TouchSurfaceImplementation802 ~TouchSurfaceImplementation() override 803 { 804 disableTouchSurface(); 805 } 806 activateTouchSurfacejuce::BlockImplementation::TouchSurfaceImplementation807 void activateTouchSurface() 808 { 809 startTimer (500); 810 } 811 disableTouchSurfacejuce::BlockImplementation::TouchSurfaceImplementation812 void disableTouchSurface() 813 { 814 stopTimer(); 815 } 816 getNumberOfKeywavesjuce::BlockImplementation::TouchSurfaceImplementation817 int getNumberOfKeywaves() const noexcept override 818 { 819 return blockImpl.modelData.numKeywaves; 820 } 821 broadcastTouchChangejuce::BlockImplementation::TouchSurfaceImplementation822 void broadcastTouchChange (const TouchSurface::Touch& touchEvent) 823 { 824 auto& status = touches.getValue (touchEvent); 825 826 // Fake a touch end if we receive a duplicate touch-start with no preceding touch-end (ie: comms error) 827 if (touchEvent.isTouchStart && status.isActive) 828 killTouch (touchEvent, status, Time::getMillisecondCounter()); 829 830 // Fake a touch start if we receive an unexpected event with no matching start event. (ie: comms error) 831 if (! touchEvent.isTouchStart && ! status.isActive) 832 { 833 TouchSurface::Touch t (touchEvent); 834 t.isTouchStart = true; 835 t.isTouchEnd = false; 836 837 if (t.zVelocity <= 0) t.zVelocity = status.lastStrikePressure; 838 if (t.zVelocity <= 0) t.zVelocity = t.z; 839 if (t.zVelocity <= 0) t.zVelocity = 0.9f; 840 841 listeners.call ([&] (TouchSurface::Listener& l) { l.touchChanged (*this, t); }); 842 } 843 844 // Normal handling: 845 status.lastEventTime = Time::getMillisecondCounter(); 846 status.isActive = ! touchEvent.isTouchEnd; 847 848 if (touchEvent.isTouchStart) 849 status.lastStrikePressure = touchEvent.zVelocity; 850 851 listeners.call ([&] (TouchSurface::Listener& l) { l.touchChanged (*this, touchEvent); }); 852 } 853 timerCallbackjuce::BlockImplementation::TouchSurfaceImplementation854 void timerCallback() override 855 { 856 // Find touches that seem to have become stuck, and fake a touch-end for them.. 857 static const uint32 touchTimeOutMs = 500; 858 859 for (auto& t : touches) 860 { 861 auto& status = t.value; 862 auto now = Time::getMillisecondCounter(); 863 864 if (status.isActive && now > status.lastEventTime + touchTimeOutMs) 865 killTouch (t.touch, status, now); 866 } 867 } 868 869 struct TouchStatus 870 { 871 uint32 lastEventTime = 0; 872 float lastStrikePressure = 0; 873 bool isActive = false; 874 }; 875 killTouchjuce::BlockImplementation::TouchSurfaceImplementation876 void killTouch (const TouchSurface::Touch& touch, TouchStatus& status, uint32 timeStamp) noexcept 877 { 878 jassert (status.isActive); 879 880 TouchSurface::Touch killTouch (touch); 881 882 killTouch.z = 0; 883 killTouch.xVelocity = 0; 884 killTouch.yVelocity = 0; 885 killTouch.zVelocity = -1.0f; 886 killTouch.eventTimestamp = timeStamp; 887 killTouch.isTouchStart = false; 888 killTouch.isTouchEnd = true; 889 890 listeners.call ([&] (TouchSurface::Listener& l) { l.touchChanged (*this, killTouch); }); 891 892 status.isActive = false; 893 } 894 cancelAllActiveTouchesjuce::BlockImplementation::TouchSurfaceImplementation895 void cancelAllActiveTouches() noexcept override 896 { 897 const auto now = Time::getMillisecondCounter(); 898 899 for (auto& t : touches) 900 if (t.value.isActive) 901 killTouch (t.touch, t.value, now); 902 903 touches.clear(); 904 } 905 906 BlockImplementation& blockImpl; 907 TouchList<TouchStatus> touches; 908 909 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TouchSurfaceImplementation) 910 }; 911 912 //============================================================================== 913 struct ControlButtonImplementation : public ControlButton 914 { ControlButtonImplementationjuce::BlockImplementation::ControlButtonImplementation915 ControlButtonImplementation (BlockImplementation& b, int index, BlocksProtocol::BlockDataSheet::ButtonInfo info) 916 : ControlButton (b), blockImpl (b), buttonInfo (info), buttonIndex (index) 917 { 918 } 919 ~ControlButtonImplementationjuce::BlockImplementation::ControlButtonImplementation920 ~ControlButtonImplementation() override 921 { 922 } 923 getTypejuce::BlockImplementation::ControlButtonImplementation924 ButtonFunction getType() const override { return buttonInfo.type; } getNamejuce::BlockImplementation::ControlButtonImplementation925 String getName() const override { return BlocksProtocol::getButtonNameForFunction (buttonInfo.type); } getPositionXjuce::BlockImplementation::ControlButtonImplementation926 float getPositionX() const override { return buttonInfo.x; } getPositionYjuce::BlockImplementation::ControlButtonImplementation927 float getPositionY() const override { return buttonInfo.y; } 928 hasLightjuce::BlockImplementation::ControlButtonImplementation929 bool hasLight() const override { return blockImpl.isControlBlock(); } 930 setLightColourjuce::BlockImplementation::ControlButtonImplementation931 bool setLightColour (LEDColour colour) override 932 { 933 if (hasLight()) 934 { 935 if (auto row = blockImpl.ledRow.get()) 936 { 937 row->setButtonColour ((uint32) buttonIndex, colour); 938 return true; 939 } 940 } 941 942 return false; 943 } 944 broadcastButtonChangejuce::BlockImplementation::ControlButtonImplementation945 void broadcastButtonChange (Block::Timestamp timestamp, ControlButton::ButtonFunction button, bool isDown) 946 { 947 if (button == buttonInfo.type) 948 { 949 if (wasDown == isDown) 950 sendButtonChangeToListeners (timestamp, ! isDown); 951 952 sendButtonChangeToListeners (timestamp, isDown); 953 wasDown = isDown; 954 } 955 } 956 sendButtonChangeToListenersjuce::BlockImplementation::ControlButtonImplementation957 void sendButtonChangeToListeners (Block::Timestamp timestamp, bool isDown) 958 { 959 if (isDown) 960 listeners.call ([&] (ControlButton::Listener& l) { l.buttonPressed (*this, timestamp); }); 961 else 962 listeners.call ([&] (ControlButton::Listener& l) { l.buttonReleased (*this, timestamp); }); 963 } 964 965 BlockImplementation& blockImpl; 966 BlocksProtocol::BlockDataSheet::ButtonInfo buttonInfo; 967 int buttonIndex; 968 bool wasDown = false; 969 970 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControlButtonImplementation) 971 }; 972 973 974 //============================================================================== 975 struct StatusLightImplementation : public StatusLight 976 { StatusLightImplementationjuce::BlockImplementation::StatusLightImplementation977 StatusLightImplementation (Block& b, BlocksProtocol::BlockDataSheet::StatusLEDInfo i) : StatusLight (b), info (i) 978 { 979 } 980 getNamejuce::BlockImplementation::StatusLightImplementation981 String getName() const override { return info.name; } 982 setColourjuce::BlockImplementation::StatusLightImplementation983 bool setColour (LEDColour newColour) override 984 { 985 // XXX TODO! 986 ignoreUnused (newColour); 987 return false; 988 } 989 990 BlocksProtocol::BlockDataSheet::StatusLEDInfo info; 991 992 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StatusLightImplementation) 993 }; 994 995 //============================================================================== 996 struct LEDGridImplementation : public LEDGrid 997 { LEDGridImplementationjuce::BlockImplementation::LEDGridImplementation998 LEDGridImplementation (BlockImplementation& b) : LEDGrid (b), blockImpl (b) 999 { 1000 } 1001 getNumColumnsjuce::BlockImplementation::LEDGridImplementation1002 int getNumColumns() const override { return blockImpl.modelData.lightGridWidth; } getNumRowsjuce::BlockImplementation::LEDGridImplementation1003 int getNumRows() const override { return blockImpl.modelData.lightGridHeight; } 1004 1005 BlockImplementation& blockImpl; 1006 1007 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDGridImplementation) 1008 }; 1009 1010 //============================================================================== 1011 struct LEDRowImplementation : public LEDRow, 1012 private Timer 1013 { LEDRowImplementationjuce::BlockImplementation::LEDRowImplementation1014 LEDRowImplementation (BlockImplementation& b) : LEDRow (b) 1015 { 1016 startTimer (300); 1017 } 1018 setButtonColourjuce::BlockImplementation::LEDRowImplementation1019 void setButtonColour (uint32 index, LEDColour colour) 1020 { 1021 if (index < 10) 1022 { 1023 colours[index] = colour; 1024 flush(); 1025 } 1026 } 1027 getNumLEDsjuce::BlockImplementation::LEDRowImplementation1028 int getNumLEDs() const override 1029 { 1030 return static_cast<const BlockImplementation&> (block).modelData.numLEDRowLEDs; 1031 } 1032 setLEDColourjuce::BlockImplementation::LEDRowImplementation1033 void setLEDColour (int index, LEDColour colour) override 1034 { 1035 if ((uint32) index < 15u) 1036 { 1037 colours[10 + index] = colour; 1038 flush(); 1039 } 1040 } 1041 setOverlayColourjuce::BlockImplementation::LEDRowImplementation1042 void setOverlayColour (LEDColour colour) override 1043 { 1044 colours[25] = colour; 1045 flush(); 1046 } 1047 resetOverlayColourjuce::BlockImplementation::LEDRowImplementation1048 void resetOverlayColour() override 1049 { 1050 setOverlayColour ({}); 1051 } 1052 1053 private: 1054 LEDColour colours[26]; 1055 timerCallbackjuce::BlockImplementation::LEDRowImplementation1056 void timerCallback() override 1057 { 1058 stopTimer(); 1059 loadProgramOntoBlock(); 1060 flush(); 1061 } 1062 loadProgramOntoBlockjuce::BlockImplementation::LEDRowImplementation1063 void loadProgramOntoBlock() 1064 { 1065 if (block.getProgram() == nullptr) 1066 { 1067 auto err = block.setProgram (std::make_unique <DefaultLEDGridProgram> (block)); 1068 1069 if (err.failed()) 1070 { 1071 DBG (err.getErrorMessage()); 1072 jassertfalse; 1073 } 1074 } 1075 } 1076 flushjuce::BlockImplementation::LEDRowImplementation1077 void flush() 1078 { 1079 if (block.getProgram() != nullptr) 1080 for (uint32 i = 0; i < (uint32) numElementsInArray (colours); ++i) 1081 write565Colour (16 * i, colours[i]); 1082 } 1083 write565Colourjuce::BlockImplementation::LEDRowImplementation1084 void write565Colour (uint32 bitIndex, LEDColour colour) 1085 { 1086 block.setDataBits (bitIndex, 5, (uint32) (colour.getRed() >> 3)); 1087 block.setDataBits (bitIndex + 5, 6, (uint32) (colour.getGreen() >> 2)); 1088 block.setDataBits (bitIndex + 11, 5, (uint32) (colour.getBlue() >> 3)); 1089 } 1090 1091 struct DefaultLEDGridProgram : public Block::Program 1092 { DefaultLEDGridProgramjuce::BlockImplementation::LEDRowImplementation::DefaultLEDGridProgram1093 DefaultLEDGridProgram (Block& b) : Block::Program (b) {} 1094 getLittleFootProgramjuce::BlockImplementation::LEDRowImplementation::DefaultLEDGridProgram1095 String getLittleFootProgram() override 1096 { 1097 /* Data format: 1098 1099 0: 10 x 5-6-5 bits for button LED RGBs 1100 20: 15 x 5-6-5 bits for LED row colours 1101 50: 1 x 5-6-5 bits for LED row overlay colour 1102 */ 1103 return R"littlefoot( 1104 1105 #heapsize: 128 1106 1107 int getColour (int bitIndex) 1108 { 1109 return makeARGB (255, 1110 getHeapBits (bitIndex, 5) << 3, 1111 getHeapBits (bitIndex + 5, 6) << 2, 1112 getHeapBits (bitIndex + 11, 5) << 3); 1113 } 1114 1115 int getButtonColour (int index) 1116 { 1117 return getColour (16 * index); 1118 } 1119 1120 int getLEDColour (int index) 1121 { 1122 if (getHeapInt (50)) 1123 return getColour (50 * 8); 1124 1125 return getColour (20 * 8 + 16 * index); 1126 } 1127 1128 void repaint() 1129 { 1130 for (int x = 0; x < 15; ++x) 1131 fillPixel (getLEDColour (x), x, 0); 1132 1133 for (int i = 0; i < 10; ++i) 1134 fillPixel (getButtonColour (i), i, 1); 1135 } 1136 1137 void handleMessage (int p1, int p2) {} 1138 1139 )littlefoot"; 1140 } 1141 }; 1142 1143 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LEDRowImplementation) 1144 }; 1145 1146 private: 1147 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BlockImplementation) 1148 JUCE_DECLARE_WEAK_REFERENCEABLE (BlockImplementation) 1149 }; 1150 1151 } // namespace juce 1152