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