/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2016 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ /** Parses data packets from a BLOCKS device, and translates them into callbacks on a handler object */ template struct HostPacketDecoder { static void processNextPacket (Handler& handler, TopologyIndex deviceIndex, const void* data, int size) { if (Packed7BitArrayReader::checksumIsOK (static_cast (data), (uint32) size)) { Packed7BitArrayReader reader (data, size - 1); if (reader.getRemainingBits() < (int) PacketTimestamp::bits) { jassertfalse; // not a valid message.. return; } auto packetTimestamp = reader.read(); 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().get(); if (messageType == 0) return false; switch ((MessageFromDevice) messageType) { case MessageFromDevice::deviceTopology: return handleTopology (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::packetACK: return handlePacketACK (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) { if (reader.getRemainingBits() < DeviceCount::bits + ConnectionCount::bits) { jassertfalse; // not enough data available for this message type! return false; } auto deviceProtocolVersion = reader.read(); if (deviceProtocolVersion > currentProtocolVersion) { jassertfalse; return false; } const uint32 numDevices = reader.read(); const uint32 numConnections = reader.read(); if ((uint32) reader.getRemainingBits() < numDevices * BitSizes::topologyDeviceInfo + numConnections * BitSizes::topologyConnectionInfo) { jassertfalse; // not enough data available for this message type! return false; } handler.beginTopology ((int) numDevices, (int) numConnections); for (uint32 i = 0; i < numDevices; ++i) handleTopologyDevice (handler, reader); for (uint32 i = 0; i < numConnections; ++i) handleTopologyConnection (handler, reader); 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(); status.batteryCharging = reader.read(); handler.handleTopologyDevice (status); } static void handleTopologyConnection (Handler& handler, Packed7BitArrayReader& reader) { DeviceConnection connection; connection.device1 = (uint8) reader.readBits (topologyIndexBits); connection.port1 = reader.read(); connection.device2 = (uint8) reader.readBits (topologyIndexBits); connection.port2 = reader.read(); handler.handleTopologyConnection (connection); } 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(); auto touchIndex = reader.read(); auto x = reader.read(); auto y = reader.read(); auto z = reader.read(); 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(); auto touchIndex = reader.read(); auto x = reader.read(); auto y = reader.read(); auto z = reader.read(); auto vx = reader.read(); auto vy = reader.read(); auto vz = reader.read(); 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(); auto buttonID = reader.read(); handler.handleControlButtonUpDown (deviceIndex, packetTimestamp.get() + timeOffset.get(), buttonID, isDown); 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()); return true; } };