Browse Source

BLOCKS SDK: Added support for the Seaboard BLOCK and new config item API

tags/2021-05-28
jules 8 years ago
parent
commit
2bc7618424
11 changed files with 972 additions and 17 deletions
  1. +5
    -0
      modules/juce_blocks_basics/blocks/juce_Block.cpp
  2. +119
    -0
      modules/juce_blocks_basics/blocks/juce_Block.h
  3. +346
    -0
      modules/juce_blocks_basics/blocks/juce_BlockConfigManager.h
  4. +11
    -1
      modules/juce_blocks_basics/blocks/juce_ControlButton.h
  5. +1
    -0
      modules/juce_blocks_basics/juce_blocks_basics.cpp
  6. +13
    -1
      modules/juce_blocks_basics/littlefoot/juce_LittleFootRemoteHeap.h
  7. +47
    -0
      modules/juce_blocks_basics/protocol/juce_BlockModels.h
  8. +140
    -3
      modules/juce_blocks_basics/protocol/juce_BlocksProtocolDefinitions.h
  9. +33
    -0
      modules/juce_blocks_basics/protocol/juce_HostPacketBuilder.h
  10. +50
    -0
      modules/juce_blocks_basics/protocol/juce_HostPacketDecoder.h
  11. +207
    -12
      modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp

+ 5
- 0
modules/juce_blocks_basics/blocks/juce_Block.cpp View File

@@ -52,6 +52,11 @@ Block::Block (const juce::String& serial)
{
}
Block::Block (const juce::String& serial, const juce::String& version)
: serialNumber (serial), versionNumber (version), uid (getBlockUIDFromSerialNumber (serial))
{
}
Block::~Block() {}
void Block::addDataInputPortListener (DataInputPortListener* listener) { dataInputPortListeners.add (listener); }


+ 119
- 0
modules/juce_blocks_basics/blocks/juce_Block.h View File

@@ -42,6 +42,7 @@ public:
liveBlock,
loopBlock,
developerControlBlock,
touchBlock,
seaboardBlock // on-screen seaboard view
};
@@ -58,6 +59,9 @@ public:
/** The Block's serial number. */
const juce::String serialNumber;
/** The Block's version number */
juce::String versionNumber;
using UID = uint64;
/** This Block's UID.
@@ -256,6 +260,117 @@ public:
/** Sets the current program as the block's default state. */
virtual void saveProgramAsDefault() = 0;
//==============================================================================
/** Metadata for a given config item */
struct ConfigMetaData
{
static constexpr int32 numOptionNames = 8;
ConfigMetaData() {}
// Constructor to work around VS2015 bugs...
ConfigMetaData (uint32 itemIndex,
int32 itemValue,
juce::Range<int32> rangeToUse,
bool active,
const char* itemName,
uint32 itemType,
const char* options[ConfigMetaData::numOptionNames],
const char* groupName)
: item (itemIndex),
value (itemValue),
range (rangeToUse),
isActive (active),
name (itemName),
type (itemType),
group (groupName)
{
for (int i = 0; i < numOptionNames; ++i)
optionNames[i] = options[i];
}
ConfigMetaData (const ConfigMetaData& other)
{
*this = other;
}
const ConfigMetaData& operator= (const ConfigMetaData& other)
{
if (this != &other)
{
item = other.item;
value = other.value;
range = other.range;
isActive = other.isActive;
name = other.name;
type = other.type;
group = other.group;
for (int i = 0; i < numOptionNames; ++i)
optionNames[i] = other.optionNames[i];
}
return *this;
}
bool operator== (const ConfigMetaData& other) const
{
for (int32 optionIndex = 0; optionIndex < numOptionNames; ++optionIndex)
if (optionNames[optionIndex] != other.optionNames[optionIndex])
return false;
return item == other.item
&& value == other.value
&& range == other.range
&& isActive == other.isActive
&& name == other.name
&& group == other.group;
}
bool operator != (const ConfigMetaData& other) const
{
return ! (*this == other);
}
uint32 item = 0;
int32 value = 0;
juce::Range<int32> range;
bool isActive = false;
juce::String name;
uint32 type = 0;
juce::String optionNames[numOptionNames] = {};
juce::String group;
};
/** Returns the maximum number of config items available */
virtual uint32 getMaxConfigIndex() = 0;
/** Determine if this is a valid config item index */
virtual bool isValidUserConfigIndex (uint32 item) = 0;
/** Get local config item value */
virtual int32 getLocalConfigValue (uint32 item) = 0;
/** Set local config item value */
virtual void setLocalConfigValue (uint32 item, int32 value) = 0;
/** Set local config item range */
virtual void setLocalConfigRange (uint32 item, int32 min, int32 max) = 0;
/** Set if config item is active or not */
virtual void setLocalConfigItemActive (uint32 item, bool isActive) = 0;
/** Determine if config item is active or not */
virtual bool isLocalConfigItemActive (uint32 item) = 0;
/** Get config item metadata */
virtual ConfigMetaData getLocalConfigMetaData (uint32 item) = 0;
/** Request sync of factory config with block */
virtual void requestFactoryConfigSync() = 0;
/** Reset all items active status */
virtual void resetConfigListActiveStatus() = 0;
//==============================================================================
/** Allows the user to provide a function that will receive log messages from the block. */
virtual void setLogger (std::function<void(const String&)> loggingCallback) = 0;
@@ -264,6 +379,9 @@ public:
virtual bool sendFirmwareUpdatePacket (const uint8* data, uint8 size,
std::function<void (uint8)> packetAckCallback) = 0;
/** Provides a callback that will be called when a config changes. */
virtual void setConfigChangedCallback (std::function<void(Block&, const ConfigMetaData&, uint32)>) = 0;
//==============================================================================
/** Interface for objects listening to input data port. */
struct DataInputPortListener
@@ -292,6 +410,7 @@ public:
protected:
//==============================================================================
Block (const juce::String& serialNumberToUse);
Block (const juce::String& serial, const juce::String& version);
juce::ListenerList<DataInputPortListener> dataInputPortListeners;
juce::ListenerList<ProgramEventListener> programEventListeners;


+ 346
- 0
modules/juce_blocks_basics/blocks/juce_BlockConfigManager.h View File

@@ -0,0 +1,346 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#pragma once
// This file provides interfaces for managing the internal configuration of Blocks
// and synchronises with the connected Block
using namespace BlocksProtocol;
struct BlockConfigManager
{
void setDeviceIndex (TopologyIndex newDeviceIndex) { deviceIndex = newDeviceIndex; }
void setDeviceComms (PhysicalTopologySource::DeviceConnection* newConn) { deviceConnection = newConn; }
enum ConfigType
{
integer,
floating,
boolean,
colour,
options
};
static constexpr uint32 numConfigItems = 59;
struct ConfigDescription
{
ConfigItemId item;
int32 value;
int32 min;
int32 max;
bool isActive;
const char* name;
ConfigType type;
const char* optionNames[configMaxOptions];
const char* group;
static_assert (configMaxOptions == Block::ConfigMetaData::numOptionNames, "Config options size and config metadata size should be the same");
Block::ConfigMetaData toConfigMetaData() const
{
return Block::ConfigMetaData ((uint32) item, value, { min, max }, isActive, name, (uint32) type, (const char**) optionNames, group);
}
};
ConfigDescription configList[numConfigItems] =
{
{ midiStartChannel, 1, 0, 15, false, "MIDI Start Channel", ConfigType::integer, {}, "MIDI Settings" },
{ midiEndChannel, 15, 0, 15, false, "MIDI End Channel", ConfigType::integer, {}, "MIDI Settings" },
{ midiUseMPE, 1, 0, 1, false, "Use MPE", ConfigType::boolean, {}, "MIDI Settings" },
{ pitchBendRange, 48, 1, 96, false, "Pitch Bend Range", ConfigType::integer, {}, "MIDI Settings" },
{ octave, 0, -4, 6, false, "Octave", ConfigType::integer, {}, "Pitch" },
{ transpose, 0, -11, 11, false, "Transpose", ConfigType::integer, {}, "Pitch" },
{ slideCC, 74, 0, 127, false, "Slide CC", ConfigType::integer, {}, "5D Touch" },
{ slideMode, 0, 0, 2, false, "Slide Mode", ConfigType::options, { "Absolute",
"Relative Unipolar",
"Relative Bipolar" }, "5D Touch" },
{ velocitySensitivity, 100, 0, 127, false, "Strike Sensitivity", ConfigType::integer, {}, "5D Touch" },
{ glideSensitivity, 100, 0, 127, false, "Glide Sensitivity", ConfigType::integer, {}, "5D Touch" },
{ slideSensitivity, 100, 0, 127, false, "Slide Sensitivity", ConfigType::integer, {}, "5D Touch" },
{ pressureSensitivity, 100, 0, 127, false, "Pressure Sensitivity", ConfigType::integer, {}, "5D Touch" },
{ liftSensitivity, 100, 0, 127, false, "Lift Sensitivity", ConfigType::integer, {}, "5D Touch" },
{ fixedVelocity, 0, 0, 1, false, "Fixed Velocity", ConfigType::boolean, {}, "5D Touch" },
{ fixedVelocityValue, 127, 1, 127, false, "Fixed Velocity Value", ConfigType::integer, {}, "5D Touch" },
{ pianoMode, 0, 0, 1, false, "Piano Mode", ConfigType::boolean, {}, "5D Touch" },
{ glideLock, 0, 0, 127, false, "Glide Lock", ConfigType::integer, {}, "Play mode" },
{ mode, 4, 1, 5, false, "Mode", ConfigType::integer, {}, "Play mode" },
{ volume, 100, 0, 127, false, "Volume", ConfigType::integer, {}, "Play mode" },
{ scale, 0, 0, 18, false, "Scale", ConfigType::integer, {}, "Play mode" }, // NOTE: Should be options
{ hideMode, 0, 0, 1, false, "Hide Mode", ConfigType::boolean, {}, "Play mode" },
{ chord, 0, 0, 127, false, "Chord", ConfigType::integer, {}, "Play mode" }, // NOTE: Should be options
{ arpPattern, 0, 0, 127, false, "Arp Pattern", ConfigType::integer, {}, "Play mode" },
{ tempo, 120, 1, 300, false, "Tempo", ConfigType::integer, {}, "Rhythm" },
{ xTrackingMode, 1, 0, 4, false, "Glide Tracking Mode", ConfigType::options, { "Multi-Channel",
"Last Played",
"Highest",
"Lowest",
"Disabled" }, "5D Touch" },
{ yTrackingMode, 1, 0, 4, false, "Slide Tracking Mode", ConfigType::options, { "Multi-Channel",
"Last Played",
"Highest",
"Lowest",
"Disabled" }, "5D Touch" },
{ zTrackingMode, 1, 0, 4, false, "Pressure Tracking Mode", ConfigType::options, { "Multi-Channel",
"Last Played",
"Highest",
"Lowest",
"Disabled",
"Hardest" }, "5D Touch" },
// These can be defined for unique usage for a given Littlefoot script
{ user0, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user1, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user2, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user3, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user4, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user5, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user6, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user7, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user8, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user9, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user10, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user11, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user12, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user13, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user14, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user15, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user16, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user17, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user18, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user19, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user20, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user21, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user22, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user23, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user24, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user25, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user26, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user27, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user28, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user29, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user30, 0, 0, 127, false, {}, ConfigType::integer, {}, {} },
{ user31, 0, 0, 127, false, {}, ConfigType::integer, {}, {} }
};
//==============================================================================
int32 getItemValue (ConfigItemId item)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
return configList[itemIndex].value;
return 0;
}
void setItemValue (ConfigItemId item, int32 value)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
configList[itemIndex].value = value;
setBlockConfig (item, value);
}
int32 getItemMin (ConfigItemId item)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
return configList[itemIndex].min;
return 0;
}
void setItemMin (ConfigItemId item, int32 min)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
configList[itemIndex].min = min;
}
int32 getItemMax (ConfigItemId item)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
return configList[itemIndex].max;
return 0;
}
void setItemMax (ConfigItemId item, int32 max)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
configList[itemIndex].max = max;
// Send updateConfig message to Block
}
bool getItemActive (ConfigItemId item)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
return configList[itemIndex].isActive;
return false;
}
void setItemActive (ConfigItemId item, bool isActive)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
configList[itemIndex].isActive = isActive;
// Send setConfigState message to Block
}
juce::String getOptionName (ConfigItemId item, uint8 optionIndex)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex) && optionIndex < configMaxOptions)
return configList[itemIndex].optionNames[optionIndex];
return {};
}
Block::ConfigMetaData getMetaData (ConfigItemId item)
{
uint32 itemIndex;
if (getIndexForItem (item, itemIndex))
return configList[itemIndex].toConfigMetaData();
return {};
}
void resetConfigListActiveStatus()
{
for (uint32 i = 0; i < numConfigItems; ++i)
configList[i].isActive = false;
}
//==============================================================================
// Set Block Configuration
void setBlockConfig (ConfigItemId item, int32 value)
{
HostPacketBuilder<32> packet;
packet.writePacketSysexHeaderBytes (deviceIndex);
packet.addConfigSetMessage (item, value);
packet.writePacketSysexFooter();
if (deviceConnection != nullptr)
deviceConnection->sendMessageToDevice (packet.getData(), (size_t) packet.size());
}
void requestBlockConfig (ConfigItemId item)
{
HostPacketBuilder<32> packet;
packet.writePacketSysexHeaderBytes (deviceIndex);
packet.addRequestMessage (item);
packet.writePacketSysexFooter();
if (deviceConnection != nullptr)
deviceConnection->sendMessageToDevice(packet.getData(), (size_t) packet.size());
}
void requestFactoryConfigSync()
{
HostPacketBuilder<32> packet;
packet.writePacketSysexHeaderBytes(deviceIndex);
packet.addRequestFactorySyncMessage();
packet.writePacketSysexFooter();
if (deviceConnection != nullptr)
deviceConnection->sendMessageToDevice(packet.getData(), (size_t) packet.size());
}
void requestUserConfigSync()
{
HostPacketBuilder<32> packet;
packet.writePacketSysexHeaderBytes(deviceIndex);
packet.addRequestUserSyncMessage();
packet.writePacketSysexFooter();
if (deviceConnection != nullptr)
deviceConnection->sendMessageToDevice(packet.getData(), (size_t) packet.size());
}
void handleConfigUpdateMessage (int32 item, int32 value, int32 min, int32 max)
{
uint32 index;
if (getIndexForItem ((ConfigItemId) item, index))
{
configList[index].value = value;
configList[index].min = min;
configList[index].max = max;
configList[index].isActive = true;
}
}
void handleConfigSetMessage(int32 item, int32 value)
{
uint32 index;
if (getIndexForItem ((ConfigItemId) item, index))
configList[index].value = value;
}
private:
bool getIndexForItem (ConfigItemId item, uint32& index)
{
for (uint32 i = 0; i < numConfigItems; ++i)
{
if (configList[i].item == item)
{
index = i;
return true;
}
}
return false;
}
TopologyIndex deviceIndex;
PhysicalTopologySource::DeviceConnection* deviceConnection;
};

+ 11
- 1
modules/juce_blocks_basics/blocks/juce_ControlButton.h View File

@@ -70,7 +70,17 @@ public:
button4,
button5,
button6,
button7
button7,
// touch block buttons
velocitySensitivity,
glideSensitivity,
slideSensitivity,
pressSensitivity,
liftSensitivity,
fixedVelocity,
glideLock,
pianoMode
};
/** Returns the button's type. */


+ 1
- 0
modules/juce_blocks_basics/juce_blocks_basics.cpp View File

@@ -34,6 +34,7 @@ namespace juce
#include "protocol/juce_BlockModels.h"
}
#include "blocks/juce_BlockConfigManager.h"
#include "blocks/juce_Block.cpp"
#include "topology/juce_PhysicalTopologySource.cpp"
#include "topology/juce_RuleBasedTopologySource.cpp"


+ 13
- 1
modules/juce_blocks_basics/littlefoot/juce_LittleFootRemoteHeap.h View File

@@ -122,12 +122,24 @@ struct LittleFootRemoteHeap
return ! needsSyncing;
}
static bool isAllZero (const uint8* data, size_t size) noexcept
{
for (size_t i = 0; i < size; ++i)
if (data[i] != 0)
return false;
return true;
}
void sendChanges (ImplementationClass& bi, bool forceSend)
{
if ((needsSyncing && messagesSent.isEmpty()) || forceSend)
{
for (int maxChanges = 30; --maxChanges >= 0;)
{
if (isAllZero (targetData, blockSize))
break;
uint16 data[ImplementationClass::maxBlockSize];
auto* latestState = getLatestExpectedDataState();
@@ -216,7 +228,7 @@ struct LittleFootRemoteHeap
static constexpr uint16 unknownByte = 0x100;
private:
uint16 deviceState[ImplementationClass::maxBlockSize];
uint16 deviceState[ImplementationClass::maxBlockSize] = { 0 };
uint8 targetData[ImplementationClass::maxBlockSize] = { 0 };
uint32 programSize = 0;
bool needsSyncing = true, programStateKnown = true, programLoaded = false;


+ 47
- 0
modules/juce_blocks_basics/protocol/juce_BlockModels.h View File

@@ -32,6 +32,8 @@ struct BlockDataSheet
if (serialNumber.isLiveBlock()) initialiseForControlBlockLive();
if (serialNumber.isLoopBlock()) initialiseForControlBlockLoop();
if (serialNumber.isDevCtrlBlock()) initialiseForControlBlockDeveloper();
if (serialNumber.isTouchBlock()) initialiseForControlBlockTouch();
if (serialNumber.isSeaboardBlock()) initialiseForSeaboardBlock();
}
Block::ConnectionPort convertPortIndexToConnectorPort (BlocksProtocol::ConnectorPort port) const noexcept
@@ -139,6 +141,21 @@ private:
ControlButton::ButtonFunction::up);
}
void initialiseForControlBlockTouch()
{
initialiseControlBlock ("Touch BLOCK", Block::Type::touchBlock,
ControlButton::ButtonFunction::velocitySensitivity,
ControlButton::ButtonFunction::glideSensitivity,
ControlButton::ButtonFunction::slideSensitivity,
ControlButton::ButtonFunction::pressSensitivity,
ControlButton::ButtonFunction::liftSensitivity,
ControlButton::ButtonFunction::fixedVelocity,
ControlButton::ButtonFunction::glideLock,
ControlButton::ButtonFunction::pianoMode,
ControlButton::ButtonFunction::down,
ControlButton::ButtonFunction::up);
}
void initialiseControlBlock (const char* name, Block::Type type,
ControlButton::ButtonFunction b1, ControlButton::ButtonFunction b2,
ControlButton::ButtonFunction b3, ControlButton::ButtonFunction b4,
@@ -179,6 +196,27 @@ private:
numLEDRowLEDs = 15;
}
void initialiseForSeaboardBlock()
{
apiType = Block::Type::seaboardBlock;
description = "Seaboard BLOCK (6x3)";
widthUnits = 6;
heightUnits = 3;
lightGridWidth = 0;
lightGridHeight = 0;
numKeywaves = 24;
addPorts (2, 1, 0, 1);
hasTouchSurface = true;
programAndHeapSize = BlocksProtocol::padBlockProgramAndHeapSize;
addModeButton();
}
//==============================================================================
void addStatusLED (const char* name, float x, float y)
{
@@ -258,6 +296,15 @@ static const char* getButtonNameForFunction (ControlButton::ButtonFunction fn) n
case BF::button5: return "5";
case BF::button6: return "6";
case BF::button7: return "7";
case BF::velocitySensitivity: return "Velocity Sensitivity";
case BF::glideSensitivity: return "Glide Sensitivity";
case BF::slideSensitivity: return "Slide Sensitivity";
case BF::pressSensitivity: return "Press Sensitivity";
case BF::liftSensitivity: return "Lift Sensitivity";
case BF::fixedVelocity: return "Fixed Velocity";
case BF::glideLock: return "Glide Lock";
case BF::pianoMode: return "Piano Mode";
}
jassertfalse;


+ 140
- 3
modules/juce_blocks_basics/protocol/juce_BlocksProtocolDefinitions.h View File

@@ -47,6 +47,7 @@ enum class MessageFromDevice
firmwareUpdateACK = 0x03,
deviceTopologyExtend = 0x04,
deviceTopologyEnd = 0x05,
deviceVersionList = 0x06,
touchStart = 0x10,
touchMove = 0x11,
@@ -56,6 +57,8 @@ enum class MessageFromDevice
touchMoveWithVelocity = 0x14,
touchEndWithVelocity = 0x15,
configMessage = 0x18,
controlButtonDown = 0x20,
controlButtonUp = 0x21,
@@ -70,7 +73,9 @@ enum class MessageFromHost
deviceCommandMessage = 0x01,
sharedDataChange = 0x02,
programEventMessage = 0x03,
firmwareUpdatePacket = 0x04
firmwareUpdatePacket = 0x04,
configMessage = 0x10
};
@@ -120,19 +125,27 @@ struct BlockSerialNumber
if (c == 0)
return false;
return isAnyControlBlock() || isPadBlock();
return isAnyControlBlock() || isPadBlock() || isSeaboardBlock();
}
bool isPadBlock() const noexcept { return hasPrefix ("LPB"); }
bool isLiveBlock() const noexcept { return hasPrefix ("LIC"); }
bool isLoopBlock() const noexcept { return hasPrefix ("LOC"); }
bool isDevCtrlBlock() const noexcept { return hasPrefix ("DCB"); }
bool isTouchBlock() const noexcept { return hasPrefix ("TCB"); }
bool isSeaboardBlock() const noexcept { return hasPrefix ("SBB"); }
bool isAnyControlBlock() const noexcept { return isLiveBlock() || isLoopBlock() || isDevCtrlBlock(); }
bool isAnyControlBlock() const noexcept { return isLiveBlock() || isLoopBlock() || isDevCtrlBlock() || isTouchBlock(); }
bool hasPrefix (const char* prefix) const noexcept { return memcmp (serial, prefix, 3) == 0; }
};
struct VersionNumber
{
uint8 version[21] = {};
uint8 length = 0;
};
struct DeviceStatus
{
BlockSerialNumber serialNumber;
@@ -147,9 +160,93 @@ struct DeviceConnection
ConnectorPort port1, port2;
};
struct DeviceVersion
{
TopologyIndex index;
VersionNumber version;
};
static constexpr uint8 maxBlocksInTopologyPacket = 6;
static constexpr uint8 maxConnectionsInTopologyPacket = 24;
//==============================================================================
/** Configuration Item Identifiers. */
enum ConfigItemId
{
// MIDI
midiStartChannel = 0,
midiEndChannel = 1,
midiUseMPE = 2,
pitchBendRange = 3,
octave = 4,
transpose = 5,
slideCC = 6,
slideMode = 7,
octaveTopology = 8,
// Touch
velocitySensitivity = 10,
glideSensitivity = 11,
slideSensitivity = 12,
pressureSensitivity = 13,
liftSensitivity = 14,
fixedVelocity = 15,
fixedVelocityValue = 16,
pianoMode = 17,
glideLock = 18,
// Live
mode = 20,
volume = 21,
scale = 22,
hideMode = 23,
chord = 24,
arpPattern = 25,
tempo = 26,
// Tracking
xTrackingMode = 30,
yTrackingMode = 31,
zTrackingMode = 32,
// User
user0 = 64,
user1 = 65,
user2 = 66,
user3 = 67,
user4 = 68,
user5 = 69,
user6 = 70,
user7 = 71,
user8 = 72,
user9 = 73,
user10 = 74,
user11 = 75,
user12 = 76,
user13 = 77,
user14 = 78,
user15 = 79,
user16 = 80,
user17 = 81,
user18 = 82,
user19 = 83,
user20 = 84,
user21 = 85,
user22 = 86,
user23 = 87,
user24 = 88,
user25 = 89,
user26 = 90,
user27 = 91,
user28 = 92,
user29 = 93,
user30 = 94,
user31 = 95
};
static constexpr uint8 numberOfUserConfigs = 32;
static constexpr uint8 maxConfigIndex = uint8 (ConfigItemId::user0) + numberOfUserConfigs;
static constexpr uint8 configUserConfigNameLength = 32;
static constexpr uint8 configMaxOptions = 8;
static constexpr uint8 configOptionNameLength = 16;
//==============================================================================
/** The coordinates of a touch. */
struct TouchPosition
@@ -197,6 +294,23 @@ enum DeviceCommands
using DeviceCommand = IntegerWithBitSize<9>;
//==============================================================================
enum ConfigCommands
{
setConfig = 0x00,
requestConfig = 0x01, // Request a config update
requestFactorySync = 0x02, // Requests all active factory config data
requestUserSync = 0x03, // Requests all active user config data
updateConfig = 0x04, // Set value, min and max
updateUserConfig = 0x05, // As above but contains user config metadata
setConfigState = 0x06, // Set config activation state and whether it is saved in flash
factorySyncEnd = 0x07
};
using ConfigCommand = IntegerWithBitSize<4>;
using ConfigItemIndex = IntegerWithBitSize<8>;
using ConfigItemValue = IntegerWithBitSize<32>;
//==============================================================================
/** An ID for a control-block button type */
using ControlButtonID = IntegerWithBitSize<12>;
@@ -259,6 +373,10 @@ enum BitSizes
firmwareUpdateACK = MessageType::bits + FirmwareUpdateACKCode::bits,
controlButtonMessage = typeDeviceAndTime + ControlButtonID::bits,
configSetMessage = MessageType::bits + ConfigCommand::bits + ConfigItemIndex::bits + ConfigItemValue::bits,
configRespMessage = MessageType::bits + ConfigCommand::bits + ConfigItemIndex::bits + (ConfigItemValue::bits * 3),
configSyncEndMessage = MessageType::bits + ConfigCommand::bits,
};
//==============================================================================
@@ -299,10 +417,14 @@ static constexpr const char* ledProgramLittleFootFunctions[] =
"getVerticalDistFromMaster/i",
"getAngleFromMaster/i",
"setAutoRotate/vb",
"getClusterIndex/i",
"getClusterWidth/i",
"getClusterHeight/i",
"getClusterXpos/i",
"getClusterYpos/i",
"getNumBlocksInCurrentCluster/i",
"getBlockIdForBlockInCluster/ii",
"isMasterInCurrentCluster/b",
"makeARGB/iiiii",
"blendARGB/iii",
"fillPixel/viii",
@@ -317,6 +439,7 @@ static constexpr const char* ledProgramLittleFootFunctions[] =
"drawNumber/viiii",
"clearDisplay/v",
"clearDisplay/vi",
"displayBatteryLevel/v",
"sendMIDI/vi",
"sendMIDI/vii",
"sendMIDI/viii",
@@ -331,5 +454,19 @@ static constexpr const char* ledProgramLittleFootFunctions[] =
"deassignChannel/vii",
"getControlChannel/i",
"useMPEDuplicateFilter/vb",
"getSensorValue/iii",
"handleTouchAsSeaboard/vi",
"setPowerSavingEnabled/vb",
"getLocalConfig/ii",
"setLocalConfig/vii",
"requestRemoteConfig/vii",
"setRemoteConfig/viii",
"setLocalConfigItemRange/viii",
"setLocalConfigActiveState/vibb",
"linkBlockIDtoController/vi",
"repaintControl/v",
"onControlPress/vi",
"onControlRelease/vi",
"initControl/viiiiiiiii",
nullptr
};

+ 33
- 0
modules/juce_blocks_basics/protocol/juce_HostPacketBuilder.h View File

@@ -224,6 +224,39 @@ struct HostPacketBuilder
return true;
}
//==============================================================================
bool addConfigSetMessage (int32 item, int32 value)
{
writeMessageType (MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::setConfig;
data << type << IntegerWithBitSize<8> ((uint32) item) << IntegerWithBitSize<32> ((uint32) value);
return true;
}
bool addRequestMessage (int32 item)
{
writeMessageType (MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::requestConfig;
data << type << IntegerWithBitSize<32> (0) << IntegerWithBitSize<8> ((uint32) item);
return true;
}
bool addRequestFactorySyncMessage()
{
writeMessageType (MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::requestFactorySync;
data << type;
return true;
}
bool addRequestUserSyncMessage()
{
writeMessageType (MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::requestUserSync;
data << type;
return true;
}
//==============================================================================
private:
Packed7BitArrayBuilder<maxPacketBytes> data;


+ 50
- 0
modules/juce_blocks_basics/protocol/juce_HostPacketDecoder.h View File

@@ -64,6 +64,7 @@ struct HostPacketDecoder
case MessageFromDevice::deviceTopology: return handleTopology (handler, reader, true);
case MessageFromDevice::deviceTopologyExtend: return handleTopology (handler, reader, false);
case MessageFromDevice::deviceTopologyEnd: return handleTopologyEnd (handler, reader);
case MessageFromDevice::deviceVersionList: return handleVersion (handler, reader);
case MessageFromDevice::touchStart: return handleTouch (handler, reader, deviceIndex, packetTimestamp, true, false);
case MessageFromDevice::touchMove: return handleTouch (handler, reader, deviceIndex, packetTimestamp, false, false);
case MessageFromDevice::touchEnd: return handleTouch (handler, reader, deviceIndex, packetTimestamp, false, true);
@@ -75,6 +76,7 @@ struct HostPacketDecoder
case MessageFromDevice::programEventMessage: return handleCustomMessage (handler, reader, deviceIndex, packetTimestamp);
case MessageFromDevice::packetACK: return handlePacketACK (handler, reader, deviceIndex);
case MessageFromDevice::firmwareUpdateACK: return handleFirmwareUpdateACK (handler, reader, deviceIndex);
case MessageFromDevice::configMessage: return handleConfigMessage (handler, reader, deviceIndex);
case MessageFromDevice::logMessage: return handleLogMessage (handler, reader, deviceIndex);
default:
@@ -112,6 +114,8 @@ struct HostPacketDecoder
if (newTopology)
handler.beginTopology ((int) numDevices, (int) numConnections);
else
handler.extendTopology ((int) numDevices, (int) numConnections);
for (uint32 i = 0; i < numDevices; ++i)
handleTopologyDevice (handler, reader);
@@ -166,6 +170,20 @@ struct HostPacketDecoder
handler.handleTopologyConnection (connection);
}
static bool handleVersion (Handler& handler, Packed7BitArrayReader& reader)
{
DeviceVersion version;
version.index = (TopologyIndex) reader.readBits (topologyIndexBits);
version.version.length = (uint8) reader.readBits (7);
for (uint32 i = 0; i < version.version.length; ++i)
version.version.version[i] = (uint8) reader.readBits (7);
handler.handleVersion (version);
return true;
}
static bool handleTouch (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex,
PacketTimestamp packetTimestamp, bool isStart, bool isEnd)
{
@@ -273,6 +291,38 @@ struct HostPacketDecoder
return true;
}
static bool handleConfigMessage (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex)
{
ConfigCommand type = reader.read<ConfigCommand>().get();
if (type == updateConfig)
{
auto item = (int32) reader.read<IntegerWithBitSize<8>>().get();
auto value = (int32) reader.read<IntegerWithBitSize<32>>().get();
auto min = (int32) reader.read<IntegerWithBitSize<32>>().get();
auto max = (int32) reader.read<IntegerWithBitSize<32>>().get();
handler.handleConfigUpdateMessage (deviceIndex, item, value, min, max);
return true;
}
if (type == setConfig)
{
auto item = (int32) reader.read<IntegerWithBitSize<8>>().get();
auto value = (int32) reader.read<IntegerWithBitSize<32>>().get();
handler.handleConfigSetMessage (deviceIndex, item, value);
return true;
}
if (type == factorySyncEnd)
{
handler.handleConfigFactorySyncEndMessage (deviceIndex);
}
return true;
}
static bool handleLogMessage (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex)
{
String message;


+ 207
- 12
modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp View File

@@ -309,6 +309,7 @@ struct PhysicalTopologySource::Internal
Block::UID uid;
BlocksProtocol::TopologyIndex index;
BlocksProtocol::BlockSerialNumber serial;
BlocksProtocol::VersionNumber version;
bool isMaster;
};
@@ -324,9 +325,12 @@ struct PhysicalTopologySource::Internal
for (auto& device : devices)
{
BlocksProtocol::VersionNumber version;
result.add ({ getBlockUIDFromSerialNumber (device.serialNumber),
device.index,
device.serialNumber,
version,
isFirst });
isFirst = false;
@@ -353,6 +357,23 @@ struct PhysicalTopologySource::Internal
return false;
}
static bool versionNumberAddedToBlock (const juce::Array<DeviceInfo>& devices, Block::UID uid, juce::String version) noexcept
{
if (version.length() == 0)
for (auto&& d : devices)
if (d.uid == uid && d.version.length)
return true;
return false;
}
static void setVersionNumberForBlock (const juce::Array<DeviceInfo>& devices, Block& block) noexcept
{
for (auto&& d : devices)
if (d.uid == block.uid)
block.versionNumber = juce::String ((const char*) d.version.version, d.version.length);
}
//==============================================================================
struct ConnectedDeviceGroup : private juce::AsyncUpdater,
private juce::Timer
@@ -468,6 +489,12 @@ struct PhysicalTopologySource::Internal
incomingTopologyConnections.ensureStorageAllocated (numConnections);
}
void extendTopology (int numDevices, int numConnections)
{
incomingTopologyDevices.ensureStorageAllocated (incomingTopologyDevices.size() + numDevices);
incomingTopologyConnections.ensureStorageAllocated (incomingTopologyConnections.size() + numConnections);
}
void handleTopologyDevice (BlocksProtocol::DeviceStatus status)
{
incomingTopologyDevices.add (status);
@@ -490,6 +517,19 @@ struct PhysicalTopologySource::Internal
blockPings.clear();
}
void handleVersion (BlocksProtocol::DeviceVersion version)
{
for (auto& d : currentDeviceInfo)
{
if (d.index == version.index)
{
d.version = version.version;
detector.handleTopologyChange();
return;
}
}
}
void handleControlButtonUpDown (BlocksProtocol::TopologyIndex deviceIndex, uint32 timestamp,
BlocksProtocol::ControlButtonID buttonID, bool isDown)
{
@@ -555,6 +595,24 @@ struct PhysicalTopologySource::Internal
detector.handleFirmwareUpdateACK (deviceID, (uint8) resultCode.get());
}
void handleConfigUpdateMessage (BlocksProtocol::TopologyIndex deviceIndex, int32 item, int32 value, int32 min, int32 max)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
detector.handleConfigUpdateMessage (deviceID, item, value, min, max);
}
void handleConfigSetMessage (BlocksProtocol::TopologyIndex deviceIndex, int32 item, int32 value)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
detector.handleConfigSetMessage (deviceID, item, value);
}
void handleConfigFactorySyncEndMessage (BlocksProtocol::TopologyIndex deviceIndex)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
detector.handleConfigFactorySyncEndMessage (deviceID);
}
void handleLogMessage (BlocksProtocol::TopologyIndex deviceIndex, const String& message)
{
if (auto deviceID = getDeviceIDFromMessageIndex (deviceIndex))
@@ -828,12 +886,16 @@ struct PhysicalTopologySource::Internal
currentTopology.blocks.remove (i);
}
else if (versionNumberAddedToBlock (newDeviceInfo, block->uid, block->versionNumber))
{
setVersionNumberForBlock (newDeviceInfo, *block);
}
}
for (auto& info : newDeviceInfo)
if (info.serial.isValid())
if (! containsBlockWithUID (currentTopology.blocks, getBlockUIDFromSerialNumber (info.serial)))
currentTopology.blocks.add (new BlockImplementation (info.serial, *this, info.isMaster));
currentTopology.blocks.add (new BlockImplementation (info.serial, *this, info.version, info.isMaster));
currentTopology.connections.swapWith (newDeviceConnections);
}
@@ -864,6 +926,48 @@ struct PhysicalTopologySource::Internal
bi->handleFirmwareUpdateACK (resultCode);
}
void handleConfigUpdateMessage (Block::UID deviceID, int32 item, int32 value, int32 min, int32 max)
{
for (auto&& b : currentTopology.blocks)
if (b->uid == deviceID)
if (auto bi = BlockImplementation::getFrom (*b))
bi->handleConfigUpdateMessage (item, value, min, max);
}
void notifyBlockOfConfigChange (BlockImplementation& bi, uint32 item)
{
if (auto configChangedCallback = bi.configChangedCallback)
{
if (item >= bi.getMaxConfigIndex())
configChangedCallback (bi, {}, item);
else
configChangedCallback (bi, bi.getLocalConfigMetaData (item), item);
}
}
void handleConfigSetMessage (Block::UID deviceID, int32 item, int32 value)
{
for (auto&& b : currentTopology.blocks)
{
if (b->uid == deviceID)
{
if (auto bi = BlockImplementation::getFrom (*b))
{
bi->handleConfigSetMessage (item, value);
notifyBlockOfConfigChange (*bi, uint32 (item));
}
}
}
}
void handleConfigFactorySyncEndMessage (Block::UID deviceID)
{
for (auto&& b : currentTopology.blocks)
if (b->uid == deviceID)
if (auto bi = BlockImplementation::getFrom (*b))
notifyBlockOfConfigChange (*bi, bi->getMaxConfigIndex());
}
void handleLogMessage (Block::UID deviceID, const String& message) const
{
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
@@ -930,9 +1034,9 @@ struct PhysicalTopologySource::Internal
//==============================================================================
int getIndexFromDeviceID (Block::UID deviceID) const noexcept
{
for (auto c : connectedDeviceGroups)
for (auto* c : connectedDeviceGroups)
{
const int index = c->getIndexFromDeviceID (deviceID);
auto index = c->getIndexFromDeviceID (deviceID);
if (index >= 0)
return index;
@@ -944,7 +1048,7 @@ struct PhysicalTopologySource::Internal
template <typename PacketBuilder>
bool sendMessageToDevice (Block::UID deviceID, const PacketBuilder& builder) const
{
for (auto c : connectedDeviceGroups)
for (auto* c : connectedDeviceGroups)
if (c->getIndexFromDeviceID (deviceID) >= 0)
return c->sendMessageToDevice (builder);
@@ -953,7 +1057,7 @@ struct PhysicalTopologySource::Internal
static Detector* getFrom (Block& b) noexcept
{
if (auto bi = BlockImplementation::getFrom (b))
if (auto* bi = BlockImplementation::getFrom (b))
return &(bi->detector);
jassertfalse;
@@ -1050,9 +1154,13 @@ struct PhysicalTopologySource::Internal
private MIDIDeviceConnection::Listener,
private Timer
{
BlockImplementation (const BlocksProtocol::BlockSerialNumber& serial, Detector& detectorToUse, bool master)
: Block (juce::String ((const char*) serial.serial, sizeof (serial.serial))), modelData (serial),
remoteHeap (modelData.programAndHeapSize), detector (detectorToUse), isMaster (master)
BlockImplementation (const BlocksProtocol::BlockSerialNumber& serial, Detector& detectorToUse, BlocksProtocol::VersionNumber version, bool master)
: Block (juce::String ((const char*) serial.serial, sizeof (serial.serial)),
juce::String ((const char*) version.version, version.length)),
modelData (serial),
remoteHeap (modelData.programAndHeapSize),
detector (detectorToUse),
isMaster (master)
{
sendCommandMessage (BlocksProtocol::beginAPIMode);
@@ -1060,21 +1168,24 @@ struct PhysicalTopologySource::Internal
touchSurface.reset (new TouchSurfaceImplementation (*this));
int i = 0;
for (auto b : modelData.buttons)
for (auto&& b : modelData.buttons)
controlButtons.add (new ControlButtonImplementation (*this, i++, b));
if (modelData.lightGridWidth > 0 && modelData.lightGridHeight > 0)
ledGrid.reset (new LEDGridImplementation (*this));
for (auto s : modelData.statusLEDs)
for (auto&& s : modelData.statusLEDs)
statusLights.add (new StatusLightImplementation (*this, s));
if (modelData.numLEDRowLEDs > 0)
ledRow.reset (new LEDRowImplementation (*this));
listenerToMidiConnection = dynamic_cast<MIDIDeviceConnection*> (detector.getDeviceConnectionFor (*this));
if (listenerToMidiConnection != nullptr)
listenerToMidiConnection->addListener (this);
config.setDeviceComms (listenerToMidiConnection);
}
~BlockImplementation()
@@ -1189,6 +1300,7 @@ struct PhysicalTopologySource::Internal
return type == Block::Type::liveBlock
|| type == Block::Type::loopBlock
|| type == Block::Type::touchBlock
|| type == Block::Type::developerControlBlock;
}
@@ -1242,7 +1354,7 @@ struct PhysicalTopologySource::Internal
if (compiler.getCompiledProgram().getTotalSpaceNeeded() > getMemorySize())
return Result::fail ("Program too large!");
size_t size = (size_t) compiler.compiledObjectCode.size();
auto size = (size_t) compiler.compiledObjectCode.size();
programSize = (uint32) size;
remoteHeap.resetDataRangeToUnknown (0, remoteHeap.blockSize);
@@ -1252,6 +1364,11 @@ struct PhysicalTopologySource::Internal
remoteHeap.resetDataRangeToUnknown (0, (uint32) size);
remoteHeap.setBytes (0, compiler.compiledObjectCode.begin(), size);
remoteHeap.sendChanges (*this, true);
this->resetConfigListActiveStatus();
if (auto changeCallback = this->configChangedCallback)
changeCallback (*this, {}, this->getMaxConfigIndex());
}
else
{
@@ -1383,6 +1500,16 @@ struct PhysicalTopologySource::Internal
}
}
void handleConfigUpdateMessage (int32 item, int32 value, int32 min, int32 max)
{
config.handleConfigUpdateMessage (item, value, min, max);
}
void handleConfigSetMessage(int32 item, int32 value)
{
config.handleConfigSetMessage (item, value);
}
void pingFromDevice()
{
lastMessageReceiveTime = juce::Time::getCurrentTime();
@@ -1423,6 +1550,71 @@ struct PhysicalTopologySource::Internal
sendCommandMessage (BlocksProtocol::ping);
}
//==============================================================================
int32 getLocalConfigValue (uint32 item) override
{
config.setDeviceIndex ((TopologyIndex) getDeviceIndex());
return config.getItemValue ((BlocksProtocol::ConfigItemId) item);
}
void setLocalConfigValue (uint32 item, int32 value) override
{
config.setDeviceIndex ((TopologyIndex) getDeviceIndex());
config.setItemValue ((BlocksProtocol::ConfigItemId) item, value);
}
void setLocalConfigRange (uint32 item, int32 min, int32 max) override
{
config.setDeviceIndex ((TopologyIndex) getDeviceIndex());
config.setItemMin ((BlocksProtocol::ConfigItemId) item, min);
config.setItemMax ((BlocksProtocol::ConfigItemId) item, max);
}
void setLocalConfigItemActive (uint32 item, bool isActive) override
{
config.setDeviceIndex ((TopologyIndex) getDeviceIndex());
config.setItemActive ((BlocksProtocol::ConfigItemId) item, isActive);
}
bool isLocalConfigItemActive (uint32 item) override
{
config.setDeviceIndex ((TopologyIndex) getDeviceIndex());
return config.getItemActive ((BlocksProtocol::ConfigItemId) item);
}
uint32 getMaxConfigIndex () override
{
return uint32 (BlocksProtocol::maxConfigIndex);
}
bool isValidUserConfigIndex (uint32 item) override
{
return item >= (uint32) BlocksProtocol::ConfigItemId::user0
&& item < (uint32) (BlocksProtocol::ConfigItemId::user0 + numberOfUserConfigs);
}
ConfigMetaData getLocalConfigMetaData (uint32 item) override
{
config.setDeviceIndex ((TopologyIndex) getDeviceIndex());
return config.getMetaData ((BlocksProtocol::ConfigItemId) item);
}
void requestFactoryConfigSync() override
{
config.setDeviceIndex ((TopologyIndex) getDeviceIndex());
config.requestFactoryConfigSync();
}
void resetConfigListActiveStatus() override
{
config.resetConfigListActiveStatus();
}
void setConfigChangedCallback (std::function<void(Block&, const ConfigMetaData&, uint32)> configChanged) override
{
configChangedCallback = configChanged;
}
//==============================================================================
std::unique_ptr<TouchSurface> touchSurface;
juce::OwnedArray<ControlButton> controlButtons;
@@ -1448,11 +1640,14 @@ struct PhysicalTopologySource::Internal
Detector& detector;
juce::Time lastMessageSendTime, lastMessageReceiveTime;
BlockConfigManager config;
std::function<void(Block&, const ConfigMetaData&, uint32)> configChangedCallback;
private:
std::unique_ptr<Program> program;
uint32 programSize = 0;
std::function<void (uint8)> firmwarePacketAckCallback;
std::function<void(uint8)> firmwarePacketAckCallback;
uint32 resetMessagesSent = 0;
bool isStillConnected = true;


Loading…
Cancel
Save