| @@ -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); } | |||
| @@ -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; | |||
| @@ -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; | |||
| }; | |||
| @@ -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. */ | |||
| @@ -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" | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -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 | |||
| }; | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -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; | |||