diff --git a/modules/juce_blocks_basics/blocks/juce_Block.cpp b/modules/juce_blocks_basics/blocks/juce_Block.cpp index 5a5cb14daa..1fefb13946 100644 --- a/modules/juce_blocks_basics/blocks/juce_Block.cpp +++ b/modules/juce_blocks_basics/blocks/juce_Block.cpp @@ -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); } diff --git a/modules/juce_blocks_basics/blocks/juce_Block.h b/modules/juce_blocks_basics/blocks/juce_Block.h index c505bc9951..ef05c21080 100644 --- a/modules/juce_blocks_basics/blocks/juce_Block.h +++ b/modules/juce_blocks_basics/blocks/juce_Block.h @@ -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 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 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 loggingCallback) = 0; @@ -264,6 +379,9 @@ public: virtual bool sendFirmwareUpdatePacket (const uint8* data, uint8 size, std::function packetAckCallback) = 0; + /** Provides a callback that will be called when a config changes. */ + virtual void setConfigChangedCallback (std::function) = 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 dataInputPortListeners; juce::ListenerList programEventListeners; diff --git a/modules/juce_blocks_basics/blocks/juce_BlockConfigManager.h b/modules/juce_blocks_basics/blocks/juce_BlockConfigManager.h new file mode 100644 index 0000000000..c47464f4c6 --- /dev/null +++ b/modules/juce_blocks_basics/blocks/juce_BlockConfigManager.h @@ -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; +}; diff --git a/modules/juce_blocks_basics/blocks/juce_ControlButton.h b/modules/juce_blocks_basics/blocks/juce_ControlButton.h index 95f3fe26af..2117e2cb60 100644 --- a/modules/juce_blocks_basics/blocks/juce_ControlButton.h +++ b/modules/juce_blocks_basics/blocks/juce_ControlButton.h @@ -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. */ diff --git a/modules/juce_blocks_basics/juce_blocks_basics.cpp b/modules/juce_blocks_basics/juce_blocks_basics.cpp index 477482d9a4..140a88136a 100644 --- a/modules/juce_blocks_basics/juce_blocks_basics.cpp +++ b/modules/juce_blocks_basics/juce_blocks_basics.cpp @@ -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" diff --git a/modules/juce_blocks_basics/littlefoot/juce_LittleFootRemoteHeap.h b/modules/juce_blocks_basics/littlefoot/juce_LittleFootRemoteHeap.h index 98dbc6fb85..143b05099c 100644 --- a/modules/juce_blocks_basics/littlefoot/juce_LittleFootRemoteHeap.h +++ b/modules/juce_blocks_basics/littlefoot/juce_LittleFootRemoteHeap.h @@ -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; diff --git a/modules/juce_blocks_basics/protocol/juce_BlockModels.h b/modules/juce_blocks_basics/protocol/juce_BlockModels.h index 72ef08d53d..4eb46236cf 100644 --- a/modules/juce_blocks_basics/protocol/juce_BlockModels.h +++ b/modules/juce_blocks_basics/protocol/juce_BlockModels.h @@ -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; diff --git a/modules/juce_blocks_basics/protocol/juce_BlocksProtocolDefinitions.h b/modules/juce_blocks_basics/protocol/juce_BlocksProtocolDefinitions.h index 9fa3079476..ac291216b7 100644 --- a/modules/juce_blocks_basics/protocol/juce_BlocksProtocolDefinitions.h +++ b/modules/juce_blocks_basics/protocol/juce_BlocksProtocolDefinitions.h @@ -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 }; diff --git a/modules/juce_blocks_basics/protocol/juce_HostPacketBuilder.h b/modules/juce_blocks_basics/protocol/juce_HostPacketBuilder.h index afeaf63e77..f537aaac7d 100644 --- a/modules/juce_blocks_basics/protocol/juce_HostPacketBuilder.h +++ b/modules/juce_blocks_basics/protocol/juce_HostPacketBuilder.h @@ -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 data; diff --git a/modules/juce_blocks_basics/protocol/juce_HostPacketDecoder.h b/modules/juce_blocks_basics/protocol/juce_HostPacketDecoder.h index e985289c00..b26e03534f 100644 --- a/modules/juce_blocks_basics/protocol/juce_HostPacketDecoder.h +++ b/modules/juce_blocks_basics/protocol/juce_HostPacketDecoder.h @@ -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().get(); + + if (type == updateConfig) + { + auto item = (int32) reader.read>().get(); + auto value = (int32) reader.read>().get(); + auto min = (int32) reader.read>().get(); + auto max = (int32) reader.read>().get(); + + handler.handleConfigUpdateMessage (deviceIndex, item, value, min, max); + return true; + } + + if (type == setConfig) + { + auto item = (int32) reader.read>().get(); + auto value = (int32) reader.read>().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; diff --git a/modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp b/modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp index 834ba22316..f604823e93 100644 --- a/modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp +++ b/modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp @@ -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& 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& 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 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 (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 configChanged) override + { + configChangedCallback = configChanged; + } + //============================================================================== std::unique_ptr touchSurface; juce::OwnedArray controlButtons; @@ -1448,11 +1640,14 @@ struct PhysicalTopologySource::Internal Detector& detector; juce::Time lastMessageSendTime, lastMessageReceiveTime; + BlockConfigManager config; + std::function configChangedCallback; + private: std::unique_ptr program; uint32 programSize = 0; - std::function firmwarePacketAckCallback; + std::function firmwarePacketAckCallback; uint32 resetMessagesSent = 0; bool isStillConnected = true;