fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).

This commit is contained in:
Alex Birch
2018-06-17 13:34:53 +01:00
parent a2be47c887
commit dff4d13a1d
1563 changed files with 601601 additions and 3466 deletions

View File

@ -0,0 +1,96 @@
A blocks packet consists of a stream of packed bits.
The first 7 bits of a message is the index of the device within the topology to which the
message should be delivered, or from which it originated. The 0x40 bit of this will be set
to indicate that it's a device->host message, or clear for host->device
The next 32 bits are a timestamp, in milliseconds since the source device was booted.
If sending from the host to the device, or for types of packet
where the timestamp is irrelevant, this can be 0.
This is followed by a sequence of 1 or more messages. Each message starts with
a 7 bit message type, followed by message-type-specific data.
---------------------------------------------------------------------------------------
Device topology update (device -> host)
7 bits - message type (0x01)
7 bits - number of device-info blocks to follow
8 bits - number of connection-info blocks to follow
This is followed by a series of device-info blocks of the form:
10 bits - device model identifier (see code for list of types)
32 bits - device GUID
10 bits - device firmware version
6 bits - battery status
Next follows by a series of connection-info blocks of ths form:
7 bits - device 1 index (i.e. index in the list of devices sent above)
5 bits - device 1 port type
7 bits - device 2 index
5 bits - device 2 port type
Ports are indicated by being either North, South, East or West on a device, plus
an index to indicate their position along that edge.
---------------------------------------------------------------------------------------
Control button down/up (device -> host)
7 bits - message type (down = 0x20, up = 0x21)
7 bits - device index
12 bits - control button ID (see code for values)
---------------------------------------------------------------------------------------
Touch start/move/end (device -> host)
7 bits - message type (start = 0x10, move = 0x11, end = 0x12)
7 bits - device index
5 bits - touch index
12 bits - touch X position
12 bits - touch Y position
8 bits - touch Z position
---------------------------------------------------------------------------------------
Touch start/move/end with velocity (device -> host)
7 bits - message type (start = 0x13, move = 0x14, end = 0x15)
7 bits - device index
5 bits - touch index
12 bits - touch X position
12 bits - touch Y position
8 bits - touch Z position
8 bits - X velocity
8 bits - Y velocity
8 bits - Z velocity
---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
Device command message (host -> device)
7 bits - message type (0x01)
8 bits - command type
Command types:
resetDevice = 0x00,
requestTopologyMessage = 0x01,
setHighResTouchDetectionMode = 0x02,
setLowResTouchDetectionMode = 0x03,
---------------------------------------------------------------------------------------
Modify shared state data block
7 bits - message type (0x02)
..then repeatedly:
3 bits - type of data change command
...extra command-specific bits..
3 bits - type of data change command
..etc..
3 bits - end of sequence command

View File

@ -0,0 +1,289 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace BlocksProtocol
{
/**
All sysex messages to or from a BLOCKS device begin with these header bytes.
The next byte that follows indicates the device index within the topology, where
the 0x40 bit is set for device->host messages, and clear for host->device messages.
The lower 6 bits contain the topology index of the destination or source device.
*/
static const uint8 roliSysexHeader[] = { 0xf0, 0x00, 0x21, 0x10, 0x77 };
static uint8 calculatePacketChecksum (const uint8* data, uint32 size) noexcept
{
uint8 checksum = (uint8) size;
for (uint32 i = 0; i < size; ++i)
checksum += checksum * 2 + data[i];
return checksum & 0x7f;
}
//==============================================================================
/**
Helper class to define an integer with a specific bit size.
@tags{Blocks}
*/
template <int numBits>
struct IntegerWithBitSize
{
IntegerWithBitSize() noexcept = default;
IntegerWithBitSize (const IntegerWithBitSize&) noexcept = default;
IntegerWithBitSize& operator= (const IntegerWithBitSize&) noexcept = default;
IntegerWithBitSize (uint32 v) noexcept : value (v)
{
static_assert (numBits <= 32, "numBits must be <= 32");
jassert (v >= 0 && v <= maxValue);
}
enum
{
bits = numBits,
maxValue = static_cast<uint32> ((1ULL << numBits) - 1ULL)
};
operator uint32() const noexcept { return value; }
uint32 get() const noexcept { return value; }
uint8 getScaledToByte() const noexcept
{
return (uint8) (numBits < 8 ? (uint32) (value << (8 - numBits))
: (uint32) (value >> (numBits - 8)));
}
float toUnipolarFloat() const noexcept { return value / (float) maxValue; }
float toBipolarFloat() const noexcept { return static_cast<int32> (value << (32 - numBits)) / (float) 0x80000000u; }
static IntegerWithBitSize fromUnipolarFloat (float value) noexcept
{
static_assert (numBits <= 31, "numBits must be <= 31");
return IntegerWithBitSize ((uint32) jlimit (0, (int) maxValue, (int) (value * maxValue)));
}
static IntegerWithBitSize fromBipolarFloat (float value) noexcept
{
static_assert (numBits <= 31, "numBits must be <= 31");
return IntegerWithBitSize (maxValue & (uint32) jlimit ((int) -(maxValue / 2), (int) (maxValue / 2), (int) (value * (maxValue / 2))));
}
uint32 value = 0;
};
//==============================================================================
/**
This helper class allocates a block of 7-bit bytes and can push sequences of bits into it.
@see Packed7BitArrayReader
@tags{Blocks}
*/
template <int allocatedBytes>
struct Packed7BitArrayBuilder
{
const void* getData() const noexcept { return data; }
int size() const noexcept { return bytesWritten + (bitsInCurrentByte > 0 ? 1 : 0); }
bool hasCapacity (int bitsNeeded) const noexcept
{
return ((bytesWritten + 2) * 7 + bitsInCurrentByte + bitsNeeded) <= allocatedBytes * 7;
}
void writeHeaderSysexBytes (uint8 deviceIndex) noexcept
{
jassert (bytesWritten + bitsInCurrentByte == 0);
for (int i = 0; i < (int) sizeof (roliSysexHeader); ++i)
data[bytesWritten++] = roliSysexHeader[i];
jassert (deviceIndex < 128);
data[bytesWritten++] = deviceIndex & 0x7f;
}
void writePacketSysexFooter() noexcept
{
if (bitsInCurrentByte != 0)
{
bitsInCurrentByte = 0;
++bytesWritten;
}
jassert (hasCapacity (0));
uint32 headerBytes = (uint32) sizeof (roliSysexHeader) + 1;
data[bytesWritten] = calculatePacketChecksum (data + headerBytes, (uint32) bytesWritten - headerBytes);
++bytesWritten;
data[bytesWritten++] = 0xf7;
}
template <int numBits>
Packed7BitArrayBuilder& operator<< (IntegerWithBitSize<numBits> value) noexcept
{
writeBits (value.value, numBits);
return *this;
}
void writeBits (uint32 value, int numBits) noexcept
{
jassert (numBits <= 32);
jassert (hasCapacity (numBits));
jassert (numBits == 32 || (value >> numBits) == 0);
while (numBits > 0)
{
if (bitsInCurrentByte == 0)
{
if (numBits < 7)
{
data[bytesWritten] = (uint8) value;
bitsInCurrentByte = numBits;
return;
}
if (numBits == 7)
{
data[bytesWritten++] = (uint8) value;
return;
}
data[bytesWritten++] = (uint8) (value & 0x7f);
value >>= 7;
numBits -= 7;
}
else
{
const int bitsToDo = jmin (7 - bitsInCurrentByte, numBits);
data[bytesWritten] |= ((value & ((1 << bitsToDo) - 1)) << bitsInCurrentByte);
value >>= bitsToDo;
numBits -= bitsToDo;
bitsInCurrentByte += bitsToDo;
if (bitsInCurrentByte == 7)
{
bitsInCurrentByte = 0;
++bytesWritten;
}
}
}
}
/** Describes the current building state */
struct State
{
int bytesWritten, bitsInCurrentByte;
};
State getState() const noexcept
{
return { bytesWritten, bitsInCurrentByte };
}
void restore (State state) noexcept
{
bytesWritten = state.bytesWritten;
bitsInCurrentByte = state.bitsInCurrentByte;
}
private:
uint8 data[allocatedBytes];
int bytesWritten = 0, bitsInCurrentByte = 0;
};
//==============================================================================
/**
This helper class reads from a block of 7-bit bytes as sequences of bits.
@see Packed7BitArrayBuilder
@tags{Blocks}
*/
struct Packed7BitArrayReader
{
Packed7BitArrayReader (const void* sourceData, int numBytes) noexcept
: data (static_cast<const uint8*> (sourceData)), totalBits (numBytes * 7)
{
}
int getRemainingBits() const noexcept
{
return totalBits - bitsReadInCurrentByte;
}
template <typename Target>
Target read() noexcept
{
return Target (readBits (Target::bits));
}
uint32 readBits (int numBits) noexcept
{
jassert (numBits <= 32);
jassert (getRemainingBits() >= numBits);
uint32 value = 0;
int bitsSoFar = 0;
while (numBits > 0)
{
const uint32 valueInCurrentByte = (*data >> bitsReadInCurrentByte);
const int bitsAvailable = 7 - bitsReadInCurrentByte;
if (bitsAvailable > numBits)
{
value |= ((valueInCurrentByte & ((1 << numBits) - 1)) << bitsSoFar);
bitsReadInCurrentByte += numBits;
break;
}
value |= (valueInCurrentByte << bitsSoFar);
numBits -= bitsAvailable;
bitsSoFar += bitsAvailable;
bitsReadInCurrentByte = 0;
++data;
totalBits -= 7;
}
return value;
}
static bool checksumIsOK (const uint8* data, uint32 size) noexcept
{
return size > 1 && calculatePacketChecksum (data, size - 1) == data[size - 1];
}
private:
const uint8* data;
int totalBits, bitsReadInCurrentByte = 0;
};
} // namespace BlocksProtocol
} // namespace juce

View File

@ -0,0 +1,324 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace BlocksProtocol
{
#ifndef DOXYGEN
// This file isn't part of the public API, it's where we encode the knowledge base
// of all the different types of block we know about..
struct BlockDataSheet
{
BlockDataSheet (const BlocksProtocol::BlockSerialNumber& serial) : serialNumber (serial)
{
if (serialNumber.isPadBlock()) initialiseForPadBlock2x2();
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
{
return ports[(int) port.get()];
}
const BlocksProtocol::BlockSerialNumber serialNumber;
Block::Type apiType = Block::Type::unknown;
const char* description = nullptr;
int widthUnits = 0, heightUnits = 0;
int lightGridWidth = 0, lightGridHeight = 0, lightGridStartIndex = 0;
bool hasTouchSurface = false;
int numKeywaves = 0;
int numLEDRowLEDs = 0;
uint32 programAndHeapSize = 0;
struct ButtonInfo
{
ControlButton::ButtonFunction type;
float x, y;
};
struct StatusLEDInfo
{
juce::String name;
float x, y;
};
juce::Array<ButtonInfo> buttons;
juce::Array<StatusLEDInfo> statusLEDs;
juce::Array<Block::ConnectionPort> ports;
juce::Array<const char*> dials;
private:
//==============================================================================
void initialiseForPadBlock2x2()
{
apiType = Block::Type::lightPadBlock;
description = "Pad BLOCK (2x2)";
widthUnits = 2;
heightUnits = 2;
lightGridWidth = 15;
lightGridHeight = 15;
addPorts (2, 2, 2, 2);
hasTouchSurface = true;
programAndHeapSize = BlocksProtocol::padBlockProgramAndHeapSize;
addModeButton();
}
void initialiseForControlBlockLoop()
{
initialiseControlBlock ("Loop BLOCK", Block::Type::loopBlock,
ControlButton::ButtonFunction::mode,
ControlButton::ButtonFunction::volume,
ControlButton::ButtonFunction::click,
ControlButton::ButtonFunction::snap,
ControlButton::ButtonFunction::back,
ControlButton::ButtonFunction::playOrPause,
ControlButton::ButtonFunction::record,
ControlButton::ButtonFunction::learn,
ControlButton::ButtonFunction::down,
ControlButton::ButtonFunction::up);
}
void initialiseForControlBlockLive()
{
initialiseControlBlock ("Live BLOCK", Block::Type::liveBlock,
ControlButton::ButtonFunction::mode,
ControlButton::ButtonFunction::volume,
ControlButton::ButtonFunction::scale,
ControlButton::ButtonFunction::chord,
ControlButton::ButtonFunction::arp,
ControlButton::ButtonFunction::sustain,
ControlButton::ButtonFunction::octave,
ControlButton::ButtonFunction::love,
ControlButton::ButtonFunction::down,
ControlButton::ButtonFunction::up);
}
void initialiseForControlBlockDeveloper()
{
initialiseControlBlock ("Dev Ctrl BLOCK", Block::Type::developerControlBlock,
ControlButton::ButtonFunction::button0,
ControlButton::ButtonFunction::button1,
ControlButton::ButtonFunction::button2,
ControlButton::ButtonFunction::button3,
ControlButton::ButtonFunction::button4,
ControlButton::ButtonFunction::button5,
ControlButton::ButtonFunction::button6,
ControlButton::ButtonFunction::button7,
ControlButton::ButtonFunction::down,
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,
ControlButton::ButtonFunction b5, ControlButton::ButtonFunction b6,
ControlButton::ButtonFunction b7, ControlButton::ButtonFunction b8,
ControlButton::ButtonFunction b9, ControlButton::ButtonFunction b10)
{
apiType = type;
description = name;
widthUnits = 2;
heightUnits = 1;
programAndHeapSize = BlocksProtocol::controlBlockProgramAndHeapSize;
addPorts (2, 1, 2, 1);
float x1 = 0.2f;
float x2 = 0.6f;
float x3 = 1.0f;
float x4 = 1.4f;
float x5 = 1.8f;
float y1 = 0.405f;
float y2 = 0.798f;
addButtons (b1, x1, y1,
b2, x2, y1,
b3, x3, y1,
b4, x4, y1,
b5, x5, y1,
b6, x1, y2,
b7, x2, y2,
b8, x3, y2,
b9, x4, y2,
b10, x5, y2);
numLEDRowLEDs = 15;
}
void initialiseForSeaboardBlock()
{
apiType = Block::Type::seaboardBlock;
description = "Seaboard BLOCK (6x3)";
widthUnits = 6;
heightUnits = 3;
lightGridWidth = 0;
lightGridHeight = 0;
numKeywaves = 24;
addPortsSW (Block::ConnectionPort::DeviceEdge::west, 1);
addPortsNE (Block::ConnectionPort::DeviceEdge::north, 2);
addPortsNE (Block::ConnectionPort::DeviceEdge::east, 1);
hasTouchSurface = true;
programAndHeapSize = BlocksProtocol::padBlockProgramAndHeapSize;
addModeButton();
}
//==============================================================================
void addStatusLED (const char* name, float x, float y)
{
statusLEDs.add ({ name, x, y });
}
template <typename... Args>
void addButtons (ControlButton::ButtonFunction fn, float x, float y, Args... others)
{
addButtons (fn, x, y);
addButtons (others...);
}
void addButtons (ControlButton::ButtonFunction fn, float x, float y)
{
buttons.add ({ fn, x, y });
}
void addModeButton()
{
addButtons (ControlButton::ButtonFunction::mode, -1.0f, -1.0f);
}
void addPorts (int numNorth, int numEast, int numSouth, int numWest)
{
addPortsNE (Block::ConnectionPort::DeviceEdge::north, numNorth);
addPortsNE (Block::ConnectionPort::DeviceEdge::east, numEast);
addPortsSW (Block::ConnectionPort::DeviceEdge::south, numSouth);
addPortsSW (Block::ConnectionPort::DeviceEdge::west, numWest);
}
void addPortsNE (Block::ConnectionPort::DeviceEdge edge, int num)
{
for (int i = 0; i < num; ++i)
ports.add ({ edge, i});
}
void addPortsSW (Block::ConnectionPort::DeviceEdge edge, int num)
{
for (int i = 0; i < num; ++i)
ports.add ({ edge, num - i - 1});
}
};
//==============================================================================
static const char* getButtonNameForFunction (ControlButton::ButtonFunction fn) noexcept
{
using BF = ControlButton::ButtonFunction;
switch (fn)
{
case BF::mode: return "Mode";
case BF::volume: return "Volume";
case BF::up: return "Up";
case BF::down: return "Down";
case BF::scale: return "Scale";
case BF::chord: return "Chord";
case BF::arp: return "Arp";
case BF::sustain: return "Sustain";
case BF::octave: return "Octave";
case BF::love: return "Love";
case BF::click: return "Click";
case BF::snap: return "Snap";
case BF::back: return "Back";
case BF::playOrPause: return "Play/Pause";
case BF::record: return "Record";
case BF::learn: return "Learn";
case BF::button0: return "0";
case BF::button1: return "1";
case BF::button2: return "2";
case BF::button3: return "3";
case BF::button4: return "4";
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;
return nullptr;
}
#endif
} // namespace BlocksProtocol
} // namespace juce

View File

@ -0,0 +1,545 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace BlocksProtocol
{
/** This value is incremented when the format of the API changes in a way which
breaks compatibility.
*/
static constexpr uint32 currentProtocolVersion = 1;
using ProtocolVersion = IntegerWithBitSize<8>;
//==============================================================================
/** A timestamp for a packet, in milliseconds since device boot-up */
using PacketTimestamp = IntegerWithBitSize<32>;
/** This relative timestamp is for use inside a packet, and it represents a
number of milliseconds that should be added to the packet's timestamp.
*/
using PacketTimestampOffset = IntegerWithBitSize<5>;
//==============================================================================
/** Messages that a device may send to the host. */
enum class MessageFromDevice
{
deviceTopology = 0x01,
packetACK = 0x02,
firmwareUpdateACK = 0x03,
deviceTopologyExtend = 0x04,
deviceTopologyEnd = 0x05,
deviceVersionList = 0x06,
deviceNameList = 0x07,
touchStart = 0x10,
touchMove = 0x11,
touchEnd = 0x12,
touchStartWithVelocity = 0x13,
touchMoveWithVelocity = 0x14,
touchEndWithVelocity = 0x15,
configMessage = 0x18,
controlButtonDown = 0x20,
controlButtonUp = 0x21,
programEventMessage = 0x28,
logMessage = 0x30
};
/** Messages that the host may send to a device. */
enum class MessageFromHost
{
deviceCommandMessage = 0x01,
sharedDataChange = 0x02,
programEventMessage = 0x03,
firmwareUpdatePacket = 0x04,
configMessage = 0x10,
factoryReset = 0x11,
blockReset = 0x12,
setName = 0x20
};
/** This is the first item in a BLOCKS message, identifying the message type. */
using MessageType = IntegerWithBitSize<7>;
//==============================================================================
/** This is a type of index identifier used to refer to a block within a group.
It refers to the index of a device in the list of devices that was most recently
sent via a topology change message
(It's not a global UID for a block unit).
NB: to send a message to all devices, pass the getDeviceIndexForBroadcast() value.
*/
using TopologyIndex = uint8;
static constexpr int topologyIndexBits = 7;
/** Use this value as the index if you want a message to be sent to all devices in
the group.
*/
static constexpr TopologyIndex topologyIndexForBroadcast = 63;
using DeviceCount = IntegerWithBitSize<7>;
using ConnectionCount = IntegerWithBitSize<8>;
//==============================================================================
/** Battery charge level. */
using BatteryLevel = IntegerWithBitSize<5>;
/** Battery charger connection flag. */
using BatteryCharging = IntegerWithBitSize<1>;
//==============================================================================
/** ConnectorPort is an index, starting at 0 for the leftmost port on the
top edge, and going clockwise.
*/
using ConnectorPort = IntegerWithBitSize<5>;
//==============================================================================
/** Structure describing a block's serial number
@tags{Blocks}
*/
struct BlockSerialNumber
{
uint8 serial[16];
bool isValid() const noexcept
{
for (auto c : serial)
if (c == 0)
return false;
return isAnyControlBlock() || isPadBlock() || isSeaboardBlock();
}
bool isPadBlock() const noexcept { return hasPrefix ("LPB") || hasPrefix ("LPM"); }
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() || isTouchBlock(); }
bool hasPrefix (const char* prefix) const noexcept { return memcmp (serial, prefix, 3) == 0; }
};
/** Structure for the version number
@tags{Blocks}
*/
struct VersionNumber
{
uint8 version[21] = {};
uint8 length = 0;
};
/** Structure for the block name
@tags{Blocks}
*/
struct BlockName
{
uint8 name[33] = {};
uint8 length = 0;
};
/** Structure for the device status
@tags{Blocks}
*/
struct DeviceStatus
{
BlockSerialNumber serialNumber;
TopologyIndex index;
BatteryLevel batteryLevel;
BatteryCharging batteryCharging;
};
/** Structure for the device connection
@tags{Blocks}
*/
struct DeviceConnection
{
TopologyIndex device1, device2;
ConnectorPort port1, port2;
};
/** Structure for the device version
@tags{Blocks}
*/
struct DeviceVersion
{
TopologyIndex index;
VersionNumber version;
};
/** Structure used for the device name
@tags{Blocks}
*/
struct DeviceName
{
TopologyIndex index;
BlockName name;
};
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,
glideLockEnable = 19,
// Live
mode = 20,
volume = 21,
scale = 22,
hideMode = 23,
chord = 24,
arpPattern = 25,
tempo = 26,
// Tracking
xTrackingMode = 30,
yTrackingMode = 31,
zTrackingMode = 32,
// Graphics
gammaCorrection = 33,
// 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.
@tags{Blocks}
*/
struct TouchPosition
{
using Xcoord = IntegerWithBitSize<12>;
using Ycoord = IntegerWithBitSize<12>;
using Zcoord = IntegerWithBitSize<8>;
Xcoord x;
Ycoord y;
Zcoord z;
enum { bits = Xcoord::bits + Ycoord::bits + Zcoord::bits };
};
/** The velocities for each dimension of a touch.
@tags{Blocks}
*/
struct TouchVelocity
{
using VXcoord = IntegerWithBitSize<8>;
using VYcoord = IntegerWithBitSize<8>;
using VZcoord = IntegerWithBitSize<8>;
VXcoord vx;
VYcoord vy;
VZcoord vz;
enum { bits = VXcoord::bits + VYcoord::bits + VZcoord::bits };
};
/** The index of a touch, i.e. finger number. */
using TouchIndex = IntegerWithBitSize<5>;
using PacketCounter = IntegerWithBitSize<10>;
//==============================================================================
enum DeviceCommands
{
beginAPIMode = 0x00,
requestTopologyMessage = 0x01,
endAPIMode = 0x02,
ping = 0x03,
debugMode = 0x04,
saveProgramAsDefault = 0x05
};
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,
clusterConfigSync = 0x08
};
using ConfigCommand = IntegerWithBitSize<4>;
using ConfigItemIndex = IntegerWithBitSize<8>;
using ConfigItemValue = IntegerWithBitSize<32>;
//==============================================================================
/** An ID for a control-block button type */
using ControlButtonID = IntegerWithBitSize<12>;
//==============================================================================
using RotaryDialIndex = IntegerWithBitSize<7>;
using RotaryDialAngle = IntegerWithBitSize<14>;
using RotaryDialDelta = IntegerWithBitSize<14>;
//==============================================================================
enum DataChangeCommands
{
endOfPacket = 0,
endOfChanges = 1,
skipBytesFew = 2,
skipBytesMany = 3,
setSequenceOfBytes = 4,
setFewBytesWithValue = 5,
setFewBytesWithLastValue = 6,
setManyBytesWithValue = 7
};
using PacketIndex = IntegerWithBitSize<16>;
using DataChangeCommand = IntegerWithBitSize<3>;
using ByteCountFew = IntegerWithBitSize<4>;
using ByteCountMany = IntegerWithBitSize<8>;
using ByteValue = IntegerWithBitSize<8>;
using ByteSequenceContinues = IntegerWithBitSize<1>;
using FirmwareUpdateACKCode = IntegerWithBitSize<7>;
using FirmwareUpdateACKDetail = IntegerWithBitSize<32>;
using FirmwareUpdatePacketSize = IntegerWithBitSize<7>;
static constexpr uint32 numProgramMessageInts = 3;
static constexpr uint32 apiModeHostPingTimeoutMs = 5000;
static constexpr uint32 padBlockProgramAndHeapSize = 7200;
static constexpr uint32 padBlockStackSize = 800;
static constexpr uint32 controlBlockProgramAndHeapSize = 3000;
static constexpr uint32 controlBlockStackSize = 800;
//==============================================================================
/** Contains the number of bits required to encode various items in the packets */
enum BitSizes
{
topologyMessageHeader = MessageType::bits + ProtocolVersion::bits + DeviceCount::bits + ConnectionCount::bits,
topologyDeviceInfo = sizeof (BlockSerialNumber) * 7 + BatteryLevel::bits + BatteryCharging::bits,
topologyConnectionInfo = topologyIndexBits + ConnectorPort::bits + topologyIndexBits + ConnectorPort::bits,
typeDeviceAndTime = MessageType::bits + PacketTimestampOffset::bits,
touchMessage = typeDeviceAndTime + TouchIndex::bits + TouchPosition::bits,
touchMessageWithVelocity = touchMessage + TouchVelocity::bits,
programEventMessage = MessageType::bits + 32 * numProgramMessageInts,
packetACK = MessageType::bits + PacketCounter::bits,
firmwareUpdateACK = MessageType::bits + FirmwareUpdateACKCode::bits + FirmwareUpdateACKDetail::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,
};
//==============================================================================
// These are the littlefoot functions provided for use in BLOCKS programs
static constexpr const char* ledProgramLittleFootFunctions[] =
{
"min/iii",
"min/fff",
"max/iii",
"max/fff",
"clamp/iiii",
"clamp/ffff",
"abs/ii",
"abs/ff",
"map/ffffff",
"map/ffff",
"mod/iii",
"getRandomFloat/f",
"getRandomInt/ii",
"log/vi",
"logHex/vi",
"getMillisecondCounter/i",
"getFirmwareVersion/i",
"getTimeInCurrentFunctionCall/i",
"getBatteryLevel/f",
"isBatteryCharging/b",
"isMasterBlock/b",
"isConnectedToHost/b",
"setStatusOverlayActive/vb",
"getNumBlocksInTopology/i",
"getBlockIDForIndex/ii",
"getBlockIDOnPort/ii",
"getPortToMaster/i",
"getBlockTypeForID/ii",
"sendMessageToBlock/viiii",
"sendMessageToHost/viii",
"getHorizontalDistFromMaster/i",
"getVerticalDistFromMaster/i",
"getAngleFromMaster/i",
"setAutoRotate/vb",
"getClusterIndex/i",
"getClusterWidth/i",
"getClusterHeight/i",
"getClusterXpos/i",
"getClusterYpos/i",
"getNumBlocksInCurrentCluster/i",
"getBlockIdForBlockInCluster/ii",
"isMasterInCurrentCluster/b",
"setClusteringActive/vb",
"makeARGB/iiiii",
"blendARGB/iii",
"fillPixel/viii",
"blendPixel/viii",
"fillRect/viiiii",
"blendRect/viiiii",
"blendGradientRect/viiiiiiii",
"blendCircle/vifffb",
"addPressurePoint/vifff",
"drawPressureMap/v",
"fadePressureMap/v",
"drawNumber/viiii",
"clearDisplay/v",
"clearDisplay/vi",
"displayBatteryLevel/v",
"sendMIDI/vi",
"sendMIDI/vii",
"sendMIDI/viii",
"sendNoteOn/viii",
"sendNoteOff/viii",
"sendAftertouch/viii",
"sendCC/viii",
"sendPitchBend/vii",
"sendPitchBend/viii",
"sendChannelPressure/vii",
"addPitchCorrectionPad/viiffff",
"setPitchCorrectionEnabled/vb",
"getPitchCorrectionPitchBend/iii",
"setChannelRange/vbii",
"assignChannel/ii",
"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",
"setButtonMode/vii",
"setButtonType/viii",
"setButtonMinMaxDefault/viiii",
"setButtonColours/viii",
"setButtonTriState/vii",
nullptr
};
} // namespace BlocksProtocol
} // namespace juce

View File

@ -0,0 +1,325 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace BlocksProtocol
{
/**
Helper class for constructing a packet for sending to a BLOCKS device
@tags{Blocks}
*/
template <int maxPacketBytes>
struct HostPacketBuilder
{
HostPacketBuilder() noexcept {}
HostPacketBuilder (const HostPacketBuilder&) = delete;
HostPacketBuilder (HostPacketBuilder&&) = default;
const void* getData() const noexcept { return data.getData(); }
int size() const noexcept { return data.size(); }
//==============================================================================
void writePacketSysexHeaderBytes (TopologyIndex deviceIndex) noexcept
{
static_assert (maxPacketBytes > 10, "Not enough bytes for a sensible message!");
jassert ((deviceIndex & 64) == 0);
data.writeHeaderSysexBytes (deviceIndex);
}
void writePacketSysexFooter() noexcept
{
data.writePacketSysexFooter();
}
//==============================================================================
bool deviceControlMessage (DeviceCommand command) noexcept
{
if (! data.hasCapacity (MessageType::bits + DeviceCommand::bits))
return false;
writeMessageType (MessageFromHost::deviceCommandMessage);
data << command;
return true;
}
//==============================================================================
bool beginDataChanges (PacketIndex packetIndex) noexcept
{
if (! data.hasCapacity (MessageType::bits + PacketIndex::bits + DataChangeCommand::bits))
return false;
writeMessageType (MessageFromHost::sharedDataChange);
data << packetIndex;
return true;
}
bool endDataChanges (bool isLastChange) noexcept
{
if (! data.hasCapacity (DataChangeCommand::bits))
return false;
data << DataChangeCommand ((uint32) isLastChange ? endOfChanges : endOfPacket);
return true;
}
bool skipBytes (int numToSkip) noexcept
{
if (numToSkip <= 0)
return true;
auto state = data.getState();
while (numToSkip > ByteCountMany::maxValue)
{
if (! skipBytes (ByteCountMany::maxValue))
{
data.restore (state);
return false;
}
numToSkip -= ByteCountMany::maxValue;
}
if (numToSkip > ByteCountFew::maxValue)
{
if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountMany::bits))
{
data.restore (state);
return false;
}
data << DataChangeCommand ((uint32) skipBytesMany) << ByteCountMany ((uint32) numToSkip);
return true;
}
if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountFew::bits))
{
data.restore (state);
return false;
}
data << DataChangeCommand ((uint32) skipBytesFew) << ByteCountFew ((uint32) numToSkip);
return true;
}
bool setMultipleBytes (const uint8* values, int num) noexcept
{
if (num <= 0)
return true;
if (! data.hasCapacity (DataChangeCommand::bits * 2 + num * (1 + ByteValue::bits)))
return false;
data << DataChangeCommand ((uint32) setSequenceOfBytes);
for (int i = 0; i < num; ++i)
data << ByteValue ((uint32) values[i])
<< ByteSequenceContinues (i < num - 1 ? 1 : 0);
return true;
}
bool setMultipleBytes (uint8 value, uint8 lastValue, int num) noexcept
{
if (num <= 0)
return true;
if (num == 1)
return setMultipleBytes (&value, 1); // (this is a more compact message)
auto state = data.getState();
if (num > ByteCountMany::maxValue)
{
if (! setMultipleBytes (value, lastValue, ByteCountMany::maxValue))
{
data.restore (state);
return false;
}
return setMultipleBytes (value, lastValue, num - ByteCountMany::maxValue);
}
if (num > ByteCountFew::maxValue)
{
if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountMany::bits + ByteValue::bits))
{
data.restore (state);
return false;
}
data << DataChangeCommand ((uint32) setManyBytesWithValue)
<< ByteCountMany ((uint32) num)
<< ByteValue ((uint32) value);
return true;
}
if (value == lastValue)
{
if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountFew::bits))
{
data.restore (state);
return false;
}
data << DataChangeCommand ((uint32) setFewBytesWithLastValue) << ByteCountFew ((uint32) num);
return true;
}
if (! data.hasCapacity (DataChangeCommand::bits * 2 + ByteCountFew::bits + ByteValue::bits))
{
data.restore (state);
return false;
}
data << DataChangeCommand ((uint32) setFewBytesWithValue) << ByteCountFew ((uint32) num)
<< ByteValue ((uint32) value);
return true;
}
bool addProgramEventMessage (const int32* messageData)
{
if (! data.hasCapacity (BitSizes::programEventMessage))
return false;
writeMessageType (MessageFromHost::programEventMessage);
for (uint32 i = 0; i < numProgramMessageInts; ++i)
data << IntegerWithBitSize<32> ((uint32) messageData[i]);
return true;
}
bool addFirmwareUpdatePacket (const uint8* packetData, uint8 size)
{
if (! data.hasCapacity (MessageType::bits + FirmwareUpdatePacketSize::bits + 7 * size))
return false;
writeMessageType (MessageFromHost::firmwareUpdatePacket);
data << FirmwareUpdatePacketSize (size);
for (uint8 i = 0; i < size; ++i)
data << IntegerWithBitSize<7> ((uint32) packetData[i]);
return true;
}
//==============================================================================
bool addConfigSetMessage (int32 item, int32 value)
{
if (! data.hasCapacity (BitSizes::configSetMessage))
return false;
writeMessageType(MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::setConfig;
data << type << IntegerWithBitSize<8> ((uint32) item) << IntegerWithBitSize<32>((uint32) value);
return true;
}
bool addRequestMessage (int32 item)
{
if (! data.hasCapacity (BitSizes::configSetMessage))
return false;
writeMessageType(MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::requestConfig;
data << type << IntegerWithBitSize<32> (0) << IntegerWithBitSize<8> ((uint32) item);
return true;
}
bool addRequestFactorySyncMessage()
{
if (! data.hasCapacity (MessageType::bits + ConfigCommand::bits))
return false;
writeMessageType (MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::requestFactorySync;
data << type;
return true;
}
bool addRequestUserSyncMessage()
{
if (! data.hasCapacity (MessageType::bits + ConfigCommand::bits))
return false;
writeMessageType (MessageFromHost::configMessage);
ConfigCommand type = ConfigCommands::requestUserSync;
data << type;
return true;
}
//==============================================================================
bool addFactoryReset()
{
if (! data.hasCapacity (MessageType::bits))
return false;
writeMessageType(MessageFromHost::factoryReset);
return true;
}
bool addBlockReset()
{
if (! data.hasCapacity (MessageType::bits))
return false;
writeMessageType(MessageFromHost::blockReset);
return true;
}
bool addSetBlockName (const juce::String& name)
{
if (name.length() > 32 || ! data.hasCapacity (MessageType::bits + 7 + (7 * name.length())))
return false;
writeMessageType (MessageFromHost::setName);
data << IntegerWithBitSize<7> ((uint32) name.length());
for (auto i = 0; i < name.length(); ++i)
data << IntegerWithBitSize<7> ((uint32) name.toRawUTF8()[i]);
data << IntegerWithBitSize<7> (0);
return true;
}
//==============================================================================
private:
Packed7BitArrayBuilder<maxPacketBytes> data;
void writeMessageType (MessageFromHost type) noexcept
{
data << MessageType ((uint32) type);
}
};
} // namespace BlocksProtocol
} // namespace juce

View File

@ -0,0 +1,363 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided 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.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace BlocksProtocol
{
/**
Parses data packets from a BLOCKS device, and translates them into callbacks
on a handler object
@tags{Blocks}
*/
template <typename Handler>
struct HostPacketDecoder
{
static void processNextPacket (Handler& handler, TopologyIndex deviceIndex, const void* data, int size)
{
if (Packed7BitArrayReader::checksumIsOK (static_cast<const uint8*> (data), (uint32) size))
{
Packed7BitArrayReader reader (data, size - 1);
if (reader.getRemainingBits() < (int) PacketTimestamp::bits)
{
jassertfalse; // not a valid message..
return;
}
auto packetTimestamp = reader.read<PacketTimestamp>();
deviceIndex &= 63; // top bit is used as a direction indicator
while (processNextMessage (handler, reader, deviceIndex, packetTimestamp))
{}
}
}
static bool processNextMessage (Handler& handler, Packed7BitArrayReader& reader,
TopologyIndex deviceIndex, PacketTimestamp packetTimestamp)
{
if (reader.getRemainingBits() < MessageType::bits)
return false;
auto messageType = reader.read<MessageType>().get();
if (messageType == 0)
return false;
switch ((MessageFromDevice) messageType)
{
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::deviceNameList: return handleName (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);
case MessageFromDevice::touchStartWithVelocity: return handleTouchWithVelocity (handler, reader, deviceIndex, packetTimestamp, true, false);
case MessageFromDevice::touchMoveWithVelocity: return handleTouchWithVelocity (handler, reader, deviceIndex, packetTimestamp, false, false);
case MessageFromDevice::touchEndWithVelocity: return handleTouchWithVelocity (handler, reader, deviceIndex, packetTimestamp, false, true);
case MessageFromDevice::controlButtonDown: return handleButtonDownOrUp (handler, reader, deviceIndex, packetTimestamp, true);
case MessageFromDevice::controlButtonUp: return handleButtonDownOrUp (handler, reader, deviceIndex, packetTimestamp, false);
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:
jassertfalse; // got an invalid message type, could be a corrupt packet, or a
// message type that the host doesn't expect to get
return false;
}
}
static bool handleTopology (Handler& handler, Packed7BitArrayReader& reader, bool newTopology)
{
if (reader.getRemainingBits() < DeviceCount::bits + ConnectionCount::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
auto deviceProtocolVersion = reader.read<ProtocolVersion>();
if (deviceProtocolVersion > currentProtocolVersion)
{
jassertfalse;
return false;
}
const uint32 numDevices = reader.read<DeviceCount>();
const uint32 numConnections = reader.read<ConnectionCount>();
if ((uint32) reader.getRemainingBits() < numDevices * BitSizes::topologyDeviceInfo
+ numConnections * BitSizes::topologyConnectionInfo)
{
jassertfalse; // not enough data available for this message type!
return false;
}
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);
for (uint32 i = 0; i < numConnections; ++i)
handleTopologyConnection (handler, reader);
// Packet must be last in topology, otherwise wait for topology end message
if (numDevices < maxBlocksInTopologyPacket && numConnections < maxConnectionsInTopologyPacket)
handler.endTopology();
return true;
}
static bool handleTopologyEnd (Handler& handler, Packed7BitArrayReader& reader)
{
auto deviceProtocolVersion = reader.read<ProtocolVersion>();
if (deviceProtocolVersion > currentProtocolVersion)
{
jassertfalse;
return false;
}
handler.endTopology();
return true;
}
static void handleTopologyDevice (Handler& handler, Packed7BitArrayReader& reader)
{
DeviceStatus status;
for (uint32 i = 0; i < sizeof (BlockSerialNumber); ++i)
status.serialNumber.serial[i] = (uint8) reader.readBits (7);
status.index = (TopologyIndex) reader.readBits (topologyIndexBits);
status.batteryLevel = reader.read<BatteryLevel>();
status.batteryCharging = reader.read<BatteryCharging>();
handler.handleTopologyDevice (status);
}
static void handleTopologyConnection (Handler& handler, Packed7BitArrayReader& reader)
{
DeviceConnection connection;
connection.device1 = (uint8) reader.readBits (topologyIndexBits);
connection.port1 = reader.read<ConnectorPort>();
connection.device2 = (uint8) reader.readBits (topologyIndexBits);
connection.port2 = reader.read<ConnectorPort>();
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 handleName (Handler& handler, Packed7BitArrayReader& reader)
{
DeviceName name;
name.index = (TopologyIndex) reader.readBits (topologyIndexBits);
name.name.length = (uint8) reader.readBits (7);
for (uint32 i = 0; i < name.name.length; ++i)
name.name.name[i] = (uint8) reader.readBits (7);
handler.handleName (name);
return true;
}
static bool handleTouch (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex,
PacketTimestamp packetTimestamp, bool isStart, bool isEnd)
{
if (reader.getRemainingBits() < BitSizes::touchMessage - MessageType::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
auto timeOffset = reader.read<PacketTimestampOffset>();
auto touchIndex = reader.read<TouchIndex>();
auto x = reader.read<TouchPosition::Xcoord>();
auto y = reader.read<TouchPosition::Ycoord>();
auto z = reader.read<TouchPosition::Zcoord>();
handleTouch (handler, deviceIndex, packetTimestamp.get() + timeOffset.get(),
touchIndex, { x, y, z }, { 0, 0, 0 }, isStart, isEnd);
return true;
}
static bool handleTouchWithVelocity (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex,
PacketTimestamp packetTimestamp, bool isStart, bool isEnd)
{
if (reader.getRemainingBits() < BitSizes::touchMessageWithVelocity - MessageType::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
auto timeOffset = reader.read<PacketTimestampOffset>();
auto touchIndex = reader.read<TouchIndex>();
auto x = reader.read<TouchPosition::Xcoord>();
auto y = reader.read<TouchPosition::Ycoord>();
auto z = reader.read<TouchPosition::Zcoord>();
auto vx = reader.read<TouchVelocity::VXcoord>();
auto vy = reader.read<TouchVelocity::VYcoord>();
auto vz = reader.read<TouchVelocity::VZcoord>();
handleTouch (handler, deviceIndex, packetTimestamp.get() + timeOffset.get(),
touchIndex, { x, y, z }, { vx, vy, vz }, isStart, isEnd);
return true;
}
static void handleTouch (Handler& handler, TopologyIndex deviceIndex, uint32 timestamp, TouchIndex touchIndex,
TouchPosition position, TouchVelocity velocity, bool isStart, bool isEnd)
{
handler.handleTouchChange (deviceIndex, timestamp, touchIndex, position, velocity, isStart, isEnd);
}
static bool handleButtonDownOrUp (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex,
PacketTimestamp packetTimestamp, bool isDown)
{
if (reader.getRemainingBits() < BitSizes::controlButtonMessage - MessageType::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
auto timeOffset = reader.read<PacketTimestampOffset>();
auto buttonID = reader.read<ControlButtonID>();
handler.handleControlButtonUpDown (deviceIndex, packetTimestamp.get() + timeOffset.get(), buttonID, isDown);
return true;
}
static bool handleCustomMessage (Handler& handler, Packed7BitArrayReader& reader,
TopologyIndex deviceIndex, PacketTimestamp packetTimestamp)
{
if (reader.getRemainingBits() < BitSizes::programEventMessage - MessageType::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
int32 data[numProgramMessageInts] = {};
for (uint32 i = 0; i < numProgramMessageInts; ++i)
data[i] = (int32) reader.read<IntegerWithBitSize<32>>().get();
handler.handleCustomMessage (deviceIndex, packetTimestamp.get(), data);
return true;
}
static bool handlePacketACK (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex)
{
if (reader.getRemainingBits() < BitSizes::packetACK - MessageType::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
handler.handlePacketACK (deviceIndex, reader.read<PacketCounter>());
return true;
}
static bool handleFirmwareUpdateACK (Handler& handler, Packed7BitArrayReader& reader, TopologyIndex deviceIndex)
{
if (reader.getRemainingBits() < FirmwareUpdateACKCode::bits)
{
jassertfalse; // not enough data available for this message type!
return false;
}
handler.handleFirmwareUpdateACK (deviceIndex, reader.read<FirmwareUpdateACKCode>(), reader.read<FirmwareUpdateACKDetail>());
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;
while (reader.getRemainingBits() >= 7)
{
uint32 c = reader.read<IntegerWithBitSize<7>>();
message << (char) c;
}
handler.handleLogMessage (deviceIndex, message);
return true;
}
};
} // namespace BlocksProtocol
} // namespace juce