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