mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-03 03:30:06 +00:00
BLOCKS: Request version number from old firmware
This commit is contained in:
parent
77993724df
commit
4f7137cc08
12 changed files with 721 additions and 120 deletions
230
modules/juce_blocks_basics/blocks/juce_BlocksVersion.cpp
Normal file
230
modules/juce_blocks_basics/blocks/juce_BlocksVersion.cpp
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
BlocksVersion::BlocksVersion (const String& versionString)
|
||||
{
|
||||
evaluate (versionString);
|
||||
}
|
||||
|
||||
String BlocksVersion::toString (bool extended) const
|
||||
{
|
||||
String output = String (major) + "." + String (minor) + "." + String (patch);
|
||||
|
||||
if (extended)
|
||||
{
|
||||
if (releaseType.isNotEmpty())
|
||||
output += "-" + releaseType + "-" + String (releaseCount);
|
||||
|
||||
if (commit.isNotEmpty())
|
||||
output += "-" + commit;
|
||||
|
||||
if (forced)
|
||||
output += "-f";
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static std::regex getRegEx()
|
||||
{
|
||||
static const std::string majorMinorPatchRegex { "([0-9]+)\\.([0-9]+)\\.([0-9]+)" };
|
||||
static const std::string releaseAndCommitDetailsRegex { "(?:-(alpha|beta|rc))?(?:-([0-9]+))?(?:-g([A-Za-z0-9]+))?" };
|
||||
static const std::string forcedUpdateRegex { "(-f)?" };
|
||||
|
||||
static const std::regex regEx ("(?:.+)?" + majorMinorPatchRegex + releaseAndCommitDetailsRegex + forcedUpdateRegex + "(?:.+)?");
|
||||
|
||||
return regEx;
|
||||
}
|
||||
|
||||
bool BlocksVersion::isValidVersion (const String& versionString)
|
||||
{
|
||||
return std::regex_match (versionString.toRawUTF8(), getRegEx());
|
||||
}
|
||||
|
||||
bool BlocksVersion::evaluate (const String& versionString)
|
||||
{
|
||||
std::cmatch groups;
|
||||
const bool result = std::regex_match (versionString.toRawUTF8(), groups, getRegEx());
|
||||
|
||||
jassert (result);
|
||||
|
||||
auto toInt = [](const std::sub_match<const char*> match)
|
||||
{
|
||||
return std::atoi (match.str().c_str());
|
||||
};
|
||||
|
||||
enum tags { FULL, MAJOR, MINOR, PATCH, RELEASE, COUNT, COMMIT, FORCED};
|
||||
|
||||
major = toInt (groups[MAJOR]);
|
||||
minor = toInt (groups[MINOR]);
|
||||
patch = toInt (groups[PATCH]);
|
||||
releaseType = String (groups[RELEASE]);
|
||||
releaseCount = toInt (groups[COUNT]);
|
||||
commit = String (groups[COMMIT]);
|
||||
forced = groups[FORCED].matched;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BlocksVersion::isEqualTo (const BlocksVersion& other) const
|
||||
{
|
||||
return major == other.major &&
|
||||
minor == other.minor &&
|
||||
patch == other.patch &&
|
||||
releaseType == other.releaseType &&
|
||||
releaseCount == other.releaseCount;
|
||||
}
|
||||
|
||||
bool BlocksVersion::isGreaterThan (const BlocksVersion& other) const
|
||||
{
|
||||
if (major != other.major) return (major > other.major);
|
||||
if (minor != other.minor) return (minor > other.minor);
|
||||
if (patch != other.patch) return (patch > other.patch);
|
||||
|
||||
return releaseTypeGreaterThan (other);
|
||||
}
|
||||
|
||||
bool BlocksVersion::releaseTypeGreaterThan (const BlocksVersion& other) const
|
||||
{
|
||||
auto getReleaseTypePriority = [](const juce::BlocksVersion& version)
|
||||
{
|
||||
String releaseTypes[4] = { "alpha", "beta", "rc", {} };
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
if (version.releaseType == releaseTypes[i])
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
if (releaseType != other.releaseType)
|
||||
return getReleaseTypePriority (*this) > getReleaseTypePriority (other);
|
||||
|
||||
return releaseCount > other.releaseCount;
|
||||
}
|
||||
|
||||
bool BlocksVersion::operator== (const BlocksVersion& other) const
|
||||
{
|
||||
return isEqualTo (other);
|
||||
}
|
||||
|
||||
bool BlocksVersion::operator!=(const BlocksVersion& other) const
|
||||
{
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
bool BlocksVersion::operator> (const BlocksVersion& other) const
|
||||
{
|
||||
return isGreaterThan (other);
|
||||
}
|
||||
|
||||
bool BlocksVersion::operator< (const BlocksVersion& other) const
|
||||
{
|
||||
return ! (*this > other) && (*this != other);
|
||||
}
|
||||
|
||||
bool BlocksVersion::operator<= (const BlocksVersion& other) const
|
||||
{
|
||||
return (*this < other) || (*this == other);
|
||||
}
|
||||
|
||||
bool BlocksVersion::operator>= (const BlocksVersion& other) const
|
||||
{
|
||||
return (*this > other) || (*this == other);
|
||||
}
|
||||
|
||||
#if JUCE_UNIT_TESTS
|
||||
class BlocksVersionUnitTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
BlocksVersionUnitTests() : UnitTest ("BlocksVersionUnitTests", "Blocks") {}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("Compare patch number");
|
||||
expect (BlocksVersion ("4.6.7") < BlocksVersion ("4.6.11"));
|
||||
expect (BlocksVersion ("4.6.6") > BlocksVersion ("4.6.2"));
|
||||
expect (BlocksVersion ("4.6.5") <= BlocksVersion ("4.6.8"));
|
||||
expect (BlocksVersion ("4.6.4") >= BlocksVersion ("4.6.3"));
|
||||
|
||||
beginTest ("Compare minor number");
|
||||
expect (BlocksVersion ("4.5.9") < BlocksVersion ("4.6.7"));
|
||||
expect (BlocksVersion ("4.15.2") > BlocksVersion ("4.6.6"));
|
||||
expect (BlocksVersion ("4.4.8") <= BlocksVersion ("4.6.5"));
|
||||
expect (BlocksVersion ("4.7.4") >= BlocksVersion ("4.6.3"));
|
||||
|
||||
beginTest ("Compare major number");
|
||||
expect (BlocksVersion ("4.6.9") < BlocksVersion ("8.5.7"));
|
||||
expect (BlocksVersion ("15.6.2") > BlocksVersion ("4.9.6"));
|
||||
expect (BlocksVersion ("4.6.8") <= BlocksVersion ("7.4.5"));
|
||||
expect (BlocksVersion ("5.6.4") >= BlocksVersion ("4.7.3"));
|
||||
|
||||
beginTest ("Compare build number");
|
||||
expect (BlocksVersion ("0.3.2-alpha-3-gjduh") < BlocksVersion ("0.3.2-alpha-12-gjduh"));
|
||||
expect (BlocksVersion ("0.3.2-alpha-4-gjduh") > BlocksVersion ("0.3.2-alpha-1-gjduh"));
|
||||
expect (BlocksVersion ("0.3.2-beta-5-gjduh") <= BlocksVersion ("0.3.2-beta-6-gjduh"));
|
||||
expect (BlocksVersion ("0.3.2-beta-6-gjduh") >= BlocksVersion ("0.3.2-beta-3-gjduh"));
|
||||
|
||||
beginTest ("Compare build type");
|
||||
expect (BlocksVersion ("0.3.2-alpha-3-gjduhenf") < BlocksVersion ("0.3.2-beta-1-gjduhenf"));
|
||||
expect (BlocksVersion ("0.3.2-beta-3-gjduhenf") < BlocksVersion ("0.3.2"));
|
||||
expect (BlocksVersion ("0.3.2") > BlocksVersion ("0.3.2-alpha-3-gjduhenf"));
|
||||
|
||||
beginTest ("Compare equal numbers");
|
||||
expect (BlocksVersion ("4.6.7") == BlocksVersion ("4.6.7"));
|
||||
expect (BlocksVersion ("4.6.7-alpha-3-gsdfsf") == BlocksVersion ("4.6.7-alpha-3-gsdfsf"));
|
||||
|
||||
beginTest ("Identify forced version");
|
||||
expect (BlocksVersion("0.2.2-2-g25eaec8a-f").forced);
|
||||
expect (BlocksVersion("0.2.2-2-f").forced);
|
||||
expect (! BlocksVersion("0.2.2-2-g25eaec8-d7").forced);
|
||||
|
||||
beginTest ("Valid Strings");
|
||||
expect (BlocksVersion::isValidVersion ("Rainbow 0.4.5-beta-1-g4c36e"));
|
||||
expect (! BlocksVersion::isValidVersion ("0.4-beta-1-g4c36e"));
|
||||
expect (! BlocksVersion::isValidVersion ("a.0.4-beta-1-g4c36e"));
|
||||
expect (BlocksVersion::isValidVersion ("BLOCKS control 0.2.2-2-g25eaec8a-f.syx"));
|
||||
expect (BlocksVersion("BLOCKS control 0.2.2-2-g25eaec8a-f.syx") == BlocksVersion("0.2.2-2-g25eaec8a-f"));
|
||||
|
||||
beginTest ("Default constructors");
|
||||
{
|
||||
BlocksVersion v1 ("4.5.9");
|
||||
BlocksVersion v2 (v1);
|
||||
BlocksVersion v3;
|
||||
v3 = v1;
|
||||
|
||||
expect (v2 == v1);
|
||||
expect (v3 == v1);
|
||||
|
||||
BlocksVersion emptyVersion;
|
||||
expect (emptyVersion == BlocksVersion ("0.0.0"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static BlocksVersionUnitTests BlocksVersionUnitTests;
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
82
modules/juce_blocks_basics/blocks/juce_BlocksVersion.h
Normal file
82
modules/juce_blocks_basics/blocks/juce_BlocksVersion.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct BlocksVersion
|
||||
{
|
||||
public:
|
||||
/** The main value in a version number x.0.0 */
|
||||
int major = 0;
|
||||
|
||||
/** The secondary value in a version number 1.x.0 */
|
||||
int minor = 0;
|
||||
|
||||
/** The tertiary value in a version number 1.0.x */
|
||||
int patch = 0;
|
||||
|
||||
/** The release tag for this version, such as "beta", "alpha", "rc", etc */
|
||||
juce::String releaseType;
|
||||
|
||||
/** A numberical value assosiated with the release tag, such as "beta 4" */
|
||||
int releaseCount = 0;
|
||||
|
||||
/** The assosiated git commit that generated this firmware version */
|
||||
juce::String commit;
|
||||
|
||||
/** Identify "forced" firmware builds **/
|
||||
bool forced = false;
|
||||
|
||||
juce::String toString (bool extended = false) const;
|
||||
|
||||
/** Constructs a version number from an formatted juce::String */
|
||||
BlocksVersion (const juce::String&);
|
||||
|
||||
/** Constructs a version number from another BlocksVersion */
|
||||
BlocksVersion (const BlocksVersion& other) = default;
|
||||
|
||||
/** Creates an empty version number **/
|
||||
BlocksVersion() = default;
|
||||
|
||||
/** Returns true if string format is valid */
|
||||
static bool isValidVersion (const juce::String& versionString);
|
||||
|
||||
bool operator == (const BlocksVersion&) const;
|
||||
bool operator != (const BlocksVersion&) const;
|
||||
bool operator < (const BlocksVersion&) const;
|
||||
bool operator > (const BlocksVersion&) const;
|
||||
bool operator <= (const BlocksVersion&) const;
|
||||
bool operator >= (const BlocksVersion&) const;
|
||||
|
||||
private:
|
||||
/** @internal */
|
||||
bool evaluate (const juce::String& versionString);
|
||||
bool releaseTypeGreaterThan (const BlocksVersion& otherReleaseType) const;
|
||||
|
||||
bool isGreaterThan (const BlocksVersion& other) const;
|
||||
bool isEqualTo (const BlocksVersion& other) const;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -28,6 +28,8 @@
|
|||
#endif
|
||||
#else
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace juce
|
||||
{
|
||||
#include "littlefoot/juce_LittleFootRemoteHeap.h"
|
||||
|
|
@ -41,6 +43,7 @@ namespace juce
|
|||
|
||||
#include "blocks/juce_BlockConfigManager.h"
|
||||
#include "blocks/juce_Block.cpp"
|
||||
#include "blocks/juce_BlocksVersion.cpp"
|
||||
#include "topology/juce_PhysicalTopologySource.cpp"
|
||||
#include "topology/juce_RuleBasedTopologySource.cpp"
|
||||
#include "visualisers/juce_DrumPadLEDProgram.cpp"
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ namespace juce
|
|||
#include "blocks/juce_ControlButton.h"
|
||||
#include "blocks/juce_TouchList.h"
|
||||
#include "blocks/juce_StatusLight.h"
|
||||
#include "blocks/juce_BlocksVersion.h"
|
||||
#include "topology/juce_Topology.h"
|
||||
#include "topology/juce_TopologySource.h"
|
||||
#include "topology/juce_PhysicalTopologySource.h"
|
||||
|
|
|
|||
|
|
@ -154,40 +154,43 @@ struct BlockSerialNumber
|
|||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Structure for the version number
|
||||
/** Structure for generic block data
|
||||
|
||||
@tags{Blocks}
|
||||
*/
|
||||
struct VersionNumber
|
||||
*/
|
||||
template <size_t MaxSize>
|
||||
struct BlockStringData
|
||||
{
|
||||
uint8 version[21] = {};
|
||||
uint8 data[MaxSize] = {};
|
||||
uint8 length = 0;
|
||||
|
||||
juce::String asString() const
|
||||
static const size_t maxLength { MaxSize };
|
||||
|
||||
bool isNotEmpty() const
|
||||
{
|
||||
return juce::String (reinterpret_cast<const char*> (version),
|
||||
std::min (sizeof (version), static_cast<size_t> (length)));
|
||||
return length > 0;
|
||||
}
|
||||
|
||||
bool operator== (const BlockStringData& other) const
|
||||
{
|
||||
if (length != other.length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < length; ++i)
|
||||
if (data[i] != other.data[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!= (const BlockStringData& other) const
|
||||
{
|
||||
return ! ( *this == other );
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Structure for the block name
|
||||
|
||||
@tags{Blocks}
|
||||
*/
|
||||
struct BlockName
|
||||
{
|
||||
uint8 name[33] = {};
|
||||
uint8 length = 0;
|
||||
|
||||
bool isValid() const { return length > 0; }
|
||||
|
||||
juce::String asString() const
|
||||
{
|
||||
return juce::String (reinterpret_cast<const char*> (name),
|
||||
std::min (sizeof (name), static_cast<size_t> (length)));
|
||||
}
|
||||
};
|
||||
using VersionNumber = BlockStringData<21>;
|
||||
using BlockName = BlockStringData<33>;
|
||||
|
||||
//==============================================================================
|
||||
/** Structure for the device status
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ struct HostPacketDecoder
|
|||
version.version.length = (uint8) reader.readBits (7);
|
||||
|
||||
for (uint32 i = 0; i < version.version.length; ++i)
|
||||
version.version.version[i] = (uint8) reader.readBits (7);
|
||||
version.version.data[i] = (uint8) reader.readBits (7);
|
||||
|
||||
handler.handleVersion (version);
|
||||
return true;
|
||||
|
|
@ -208,7 +208,7 @@ struct HostPacketDecoder
|
|||
name.name.length = (uint8) reader.readBits (7);
|
||||
|
||||
for (uint32 i = 0; i < name.name.length; ++i)
|
||||
name.name.name[i] = (uint8) reader.readBits (7);
|
||||
name.name.data[i] = (uint8) reader.readBits (7);
|
||||
|
||||
handler.handleName (name);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ public:
|
|||
BlocksProtocol::BlockName blockName,
|
||||
bool isMasterBlock)
|
||||
: Block (juce::String ((const char*) serial.serial, sizeof (serial.serial)),
|
||||
juce::String ((const char*) version.version, version.length),
|
||||
juce::String ((const char*) blockName.name, blockName.length)),
|
||||
juce::String ((const char*) version.data, version.length),
|
||||
juce::String ((const char*) blockName.data, blockName.length)),
|
||||
modelData (serial),
|
||||
remoteHeap (modelData.programAndHeapSize),
|
||||
detector (&detectorToUse),
|
||||
|
|
@ -81,8 +81,8 @@ public:
|
|||
|
||||
void markReconnected (const DeviceInfo& deviceInfo)
|
||||
{
|
||||
versionNumber = deviceInfo.version.asString();
|
||||
name = deviceInfo.name.asString();
|
||||
versionNumber = asString (deviceInfo.version);
|
||||
name = asString (deviceInfo.name);
|
||||
isMaster = deviceInfo.isMaster;
|
||||
|
||||
setProgram (nullptr);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,55 @@ namespace
|
|||
{
|
||||
return static_cast<Block::Timestamp> (timestamp);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
static int removeUnusedBlocksFromMap (std::map<Block::UID, V>& mapToClean, const juce::Array<DeviceInfo>& currentDevices)
|
||||
{
|
||||
int numRemoved = 0;
|
||||
|
||||
for (auto iter = mapToClean.begin(); iter != mapToClean.end();)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
for (auto& info : currentDevices)
|
||||
{
|
||||
if (info.uid == iter->first)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapToClean.erase (iter++);
|
||||
++numRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
return numRemoved;
|
||||
}
|
||||
|
||||
/* Returns true new element added to map or value of existing key changed */
|
||||
template <typename V>
|
||||
static bool insertOrAssign (std::map<Block::UID, V>& map, const Block::UID& key, const V& value)
|
||||
{
|
||||
const auto result = map.emplace (std::make_pair (key, value));
|
||||
|
||||
if (! result.second)
|
||||
{
|
||||
if (result.first->second == value)
|
||||
return false;
|
||||
|
||||
result.first->second = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Detector>
|
||||
|
|
@ -44,14 +93,16 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
|
|||
this->handleIncomingMessage (data, dataSize);
|
||||
};
|
||||
|
||||
if (auto midiDeviceConnection = static_cast<MIDIDeviceConnection*> (deviceConnection.get()))
|
||||
depreciatedVersionReader = std::make_unique<DepreciatedVersionReader> (*midiDeviceConnection);
|
||||
|
||||
startTimer (200);
|
||||
sendTopologyRequest();
|
||||
}
|
||||
|
||||
bool isStillConnected (const juce::StringArray& detectedDevices) const noexcept
|
||||
{
|
||||
return detectedDevices.contains (deviceName)
|
||||
&& ! failedToGetTopology();
|
||||
return detectedDevices.contains (deviceName) && ! failedToGetTopology();
|
||||
}
|
||||
|
||||
int getIndexFromDeviceID (Block::UID uid) const noexcept
|
||||
|
|
@ -63,15 +114,6 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
|
|||
return -1;
|
||||
}
|
||||
|
||||
const DeviceInfo* getDeviceInfoFromUID (Block::UID uid) const noexcept
|
||||
{
|
||||
for (auto& d : currentDeviceInfo)
|
||||
if (d.uid == uid)
|
||||
return &d;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const BlocksProtocol::DeviceStatus* getLastStatus (Block::UID deviceID) const noexcept
|
||||
{
|
||||
for (auto&& status : currentTopologyDevices)
|
||||
|
|
@ -114,37 +156,53 @@ struct ConnectedDeviceGroup : private juce::AsyncUpdater,
|
|||
|
||||
void endTopology()
|
||||
{
|
||||
currentDeviceInfo = getArrayOfDeviceInfo (incomingTopologyDevices);
|
||||
currentDeviceConnections = getArrayOfConnections (incomingTopologyConnections);
|
||||
currentTopologyDevices = incomingTopologyDevices;
|
||||
lastTopologyReceiveTime = juce::Time::getCurrentTime();
|
||||
|
||||
const int numRemoved = blockPings.removeIf ([this] (auto& ping)
|
||||
{
|
||||
for (auto& info : currentDeviceInfo)
|
||||
if (info.uid == ping.blockUID)
|
||||
return false;
|
||||
if (incomingTopologyDevices.isEmpty())
|
||||
{
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_CONNECTIVITY ("API Disconnected by topology update " << ping.blockUID);
|
||||
return true;
|
||||
});
|
||||
currentTopologyDevices.swapWith (incomingTopologyDevices);
|
||||
currentTopologyConnections.swapWith (incomingTopologyConnections);
|
||||
|
||||
if (numRemoved > 0)
|
||||
detector.handleTopologyChange();
|
||||
incomingTopologyDevices.clearQuick();
|
||||
incomingTopologyConnections.clearQuick();
|
||||
|
||||
refreshCurrentDeviceInfo();
|
||||
refreshCurrentDeviceConnections();
|
||||
|
||||
removeUnusedBlocksFromMap (versionNumbers, currentDeviceInfo);
|
||||
removeUnusedBlocksFromMap (blockNames, currentDeviceInfo);
|
||||
|
||||
removePingForDisconnectedBlocks();
|
||||
|
||||
detector.handleTopologyChange();
|
||||
}
|
||||
|
||||
void handleVersion (BlocksProtocol::DeviceVersion version)
|
||||
{
|
||||
for (auto& d : currentDeviceInfo)
|
||||
if (d.index == version.index && version.version.length > 1)
|
||||
d.version = version.version;
|
||||
const auto uid = getDeviceIDFromIndex (version.index);
|
||||
|
||||
if (uid == Block::UID() || version.version.length <= 1)
|
||||
return;
|
||||
|
||||
setVersion (uid, version.version);
|
||||
}
|
||||
|
||||
void handleName (BlocksProtocol::DeviceName name)
|
||||
{
|
||||
for (auto& d : currentDeviceInfo)
|
||||
if (d.index == name.index && name.name.length > 1)
|
||||
d.name = name.name;
|
||||
const auto uid = getDeviceIDFromIndex (name.index);
|
||||
|
||||
if (uid == Block::UID() || name.name.length <= 1)
|
||||
return;
|
||||
|
||||
if (insertOrAssign (blockNames, uid, name.name))
|
||||
{
|
||||
refreshCurrentDeviceInfo();
|
||||
detector.handleTopologyChange();
|
||||
}
|
||||
}
|
||||
|
||||
void handleControlButtonUpDown (BlocksProtocol::TopologyIndex deviceIndex, uint32 timestamp,
|
||||
|
|
@ -299,14 +357,19 @@ private:
|
|||
std::unique_ptr<PhysicalTopologySource::DeviceConnection> deviceConnection;
|
||||
|
||||
juce::Array<BlocksProtocol::DeviceStatus> incomingTopologyDevices, currentTopologyDevices;
|
||||
juce::Array<BlocksProtocol::DeviceConnection> incomingTopologyConnections;
|
||||
juce::Array<BlocksProtocol::DeviceConnection> incomingTopologyConnections, currentTopologyConnections;
|
||||
|
||||
juce::CriticalSection incomingPacketLock;
|
||||
juce::Array<juce::MemoryBlock> incomingPackets;
|
||||
|
||||
std::map<Block::UID, BlocksProtocol::VersionNumber> versionNumbers;
|
||||
std::map<Block::UID, BlocksProtocol::BlockName> blockNames;
|
||||
|
||||
std::unique_ptr<DepreciatedVersionReader> depreciatedVersionReader;
|
||||
|
||||
struct TouchStart { float x, y; };
|
||||
TouchList<TouchStart> touchStartPositions;
|
||||
|
||||
TouchList<TouchStart> touchStartPositions;
|
||||
|
||||
Block::UID masterBlock = 0;
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -338,11 +401,12 @@ private:
|
|||
|
||||
checkApiTimeouts (now);
|
||||
startApiModeOnConnectedBlocks();
|
||||
requestMasterBlockVersionIfNeeded();
|
||||
}
|
||||
|
||||
bool failedToGetTopology() const noexcept
|
||||
{
|
||||
return numTopologyRequestsSent > 4 && lastTopologyReceiveTime == juce::Time();
|
||||
return numTopologyRequestsSent >= 4 && lastTopologyReceiveTime == juce::Time();
|
||||
}
|
||||
|
||||
bool sendCommandMessage (BlocksProtocol::TopologyIndex deviceIndex, uint32 commandID) const
|
||||
|
|
@ -354,11 +418,36 @@ private:
|
|||
return sendMessageToDevice (p);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void requestMasterBlockVersionIfNeeded()
|
||||
{
|
||||
if (depreciatedVersionReader == nullptr)
|
||||
return;
|
||||
|
||||
const auto masterVersion = depreciatedVersionReader->getVersionNumber();
|
||||
|
||||
if (masterVersion.isNotEmpty())
|
||||
setVersion (masterBlock, masterVersion);
|
||||
}
|
||||
|
||||
void setVersion (const Block::UID uid, const BlocksProtocol::VersionNumber versionNumber)
|
||||
{
|
||||
if (uid == masterBlock)
|
||||
depreciatedVersionReader.reset();
|
||||
|
||||
if (insertOrAssign (versionNumbers, uid, versionNumber))
|
||||
{
|
||||
refreshCurrentDeviceInfo();
|
||||
detector.handleTopologyChange();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct BlockPingTime
|
||||
{
|
||||
Block::UID blockUID;
|
||||
juce::Time lastPing;
|
||||
juce::Time connected;
|
||||
};
|
||||
|
||||
juce::Array<BlockPingTime> blockPings;
|
||||
|
|
@ -375,7 +464,7 @@ private:
|
|||
else
|
||||
{
|
||||
LOG_CONNECTIVITY ("API Connected " << uid);
|
||||
blockPings.add ({ uid, now });
|
||||
blockPings.add ({ uid, now, now });
|
||||
detector.handleTopologyChange();
|
||||
}
|
||||
}
|
||||
|
|
@ -408,6 +497,8 @@ private:
|
|||
LOG_CONNECTIVITY ("API Disconnected " << uid << ", re-probing topology");
|
||||
currentDeviceInfo.clearQuick();
|
||||
blockPings.clearQuick();
|
||||
blockNames.clear();
|
||||
versionNumbers.clear();
|
||||
detector.handleTopologyChange();
|
||||
scheduleNewTopologyRequest();
|
||||
}
|
||||
|
|
@ -432,6 +523,22 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
/* Returns true is ping was removed */
|
||||
void removePingForDisconnectedBlocks()
|
||||
{
|
||||
const auto removed = [this] (auto& ping)
|
||||
{
|
||||
for (auto& info : currentDeviceInfo)
|
||||
if (info.uid == ping.blockUID)
|
||||
return false;
|
||||
|
||||
LOG_CONNECTIVITY ("API Disconnected by topology update " << ping.blockUID);
|
||||
return true;
|
||||
};
|
||||
|
||||
blockPings.removeIf (removed);
|
||||
}
|
||||
|
||||
void startApiModeOnConnectedBlocks()
|
||||
{
|
||||
for (auto& info : currentDeviceInfo)
|
||||
|
|
@ -445,6 +552,21 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void checkVersionNumberTimeouts()
|
||||
{
|
||||
for (const auto& device : currentDeviceInfo)
|
||||
{
|
||||
const auto version = versionNumbers.find (device.uid);
|
||||
|
||||
if (version == versionNumbers.end())
|
||||
{
|
||||
auto* ping = getPing (device.uid);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Block::UID getDeviceIDFromIndex (BlocksProtocol::TopologyIndex index) const noexcept
|
||||
{
|
||||
|
|
@ -463,38 +585,7 @@ private:
|
|||
if (uid == Block::UID())
|
||||
scheduleNewTopologyRequest();
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
juce::Array<BlockDeviceConnection> getArrayOfConnections (const juce::Array<BlocksProtocol::DeviceConnection>& connections)
|
||||
{
|
||||
juce::Array<BlockDeviceConnection> result;
|
||||
|
||||
for (auto&& c : connections)
|
||||
{
|
||||
BlockDeviceConnection dc;
|
||||
dc.device1 = getDeviceIDFromIndex (c.device1);
|
||||
dc.device2 = getDeviceIDFromIndex (c.device2);
|
||||
|
||||
if (dc.device1 <= 0 || dc.device2 <= 0)
|
||||
continue;
|
||||
|
||||
dc.connectionPortOnDevice1 = convertConnectionPort (dc.device1, c.port1);
|
||||
dc.connectionPortOnDevice2 = convertConnectionPort (dc.device2, c.port2);
|
||||
|
||||
result.add (dc);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Block::ConnectionPort convertConnectionPort (Block::UID uid, BlocksProtocol::ConnectorPort p) noexcept
|
||||
{
|
||||
if (auto* info = getDeviceInfoFromUID (uid))
|
||||
return BlocksProtocol::BlockDataSheet (info->serial).convertPortIndexToConnectorPort (p);
|
||||
|
||||
jassertfalse;
|
||||
return { Block::ConnectionPort::DeviceEdge::north, 0 };
|
||||
return uid;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -534,30 +625,79 @@ private:
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
juce::Array<DeviceInfo> getArrayOfDeviceInfo (const juce::Array<BlocksProtocol::DeviceStatus>& devices)
|
||||
BlocksProtocol::VersionNumber getVersionNumber (Block::UID uid)
|
||||
{
|
||||
juce::Array<DeviceInfo> result;
|
||||
const auto version = versionNumbers.find (uid);
|
||||
return version == versionNumbers.end() ? BlocksProtocol::VersionNumber() : version->second;
|
||||
}
|
||||
|
||||
for (auto& device : devices)
|
||||
BlocksProtocol::BlockName getName (Block::UID uid)
|
||||
{
|
||||
const auto name = blockNames.find (uid);
|
||||
return name == blockNames.end() ? BlocksProtocol::BlockName() : name->second;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const DeviceInfo* getDeviceInfoFromUID (Block::UID uid) const noexcept
|
||||
{
|
||||
for (auto& d : currentDeviceInfo)
|
||||
if (d.uid == uid)
|
||||
return &d;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Block::ConnectionPort convertConnectionPort (Block::UID uid, BlocksProtocol::ConnectorPort p) noexcept
|
||||
{
|
||||
if (auto* info = getDeviceInfoFromUID (uid))
|
||||
return BlocksProtocol::BlockDataSheet (info->serial).convertPortIndexToConnectorPort (p);
|
||||
|
||||
jassertfalse;
|
||||
return { Block::ConnectionPort::DeviceEdge::north, 0 };
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void refreshCurrentDeviceInfo()
|
||||
{
|
||||
currentDeviceInfo.clearQuick();
|
||||
|
||||
for (auto& device : currentTopologyDevices)
|
||||
{
|
||||
BlocksProtocol::VersionNumber version;
|
||||
BlocksProtocol::BlockName name;
|
||||
|
||||
const auto uid = getBlockUIDFromSerialNumber (device.serialNumber);
|
||||
|
||||
// For backwards compatibility we assume the first device we see in a group is the master and won't change
|
||||
if (masterBlock == 0)
|
||||
const auto uid = getBlockUIDFromSerialNumber (device.serialNumber);
|
||||
const auto version = getVersionNumber (uid);
|
||||
const auto name = getName (uid);
|
||||
|
||||
// For backwards compatibility we assume the first device we see in a group is the master and won't change
|
||||
if (masterBlock == 0)
|
||||
masterBlock = uid;
|
||||
|
||||
result.add ({ uid,
|
||||
device.index,
|
||||
device.serialNumber,
|
||||
version,
|
||||
name,
|
||||
masterBlock == uid });
|
||||
currentDeviceInfo.add ({ uid,
|
||||
device.index,
|
||||
device.serialNumber,
|
||||
version,
|
||||
name,
|
||||
masterBlock == uid });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
void refreshCurrentDeviceConnections()
|
||||
{
|
||||
currentDeviceConnections.clearQuick();
|
||||
|
||||
for (auto&& c : currentTopologyConnections)
|
||||
{
|
||||
BlockDeviceConnection dc;
|
||||
dc.device1 = getDeviceIDFromIndex (c.device1);
|
||||
dc.device2 = getDeviceIDFromIndex (c.device2);
|
||||
|
||||
if (dc.device1 <= 0 || dc.device2 <= 0)
|
||||
continue;
|
||||
|
||||
dc.connectionPortOnDevice1 = convertConnectionPort (dc.device1, c.port1);
|
||||
dc.connectionPortOnDevice2 = convertConnectionPort (dc.device2, c.port2);
|
||||
|
||||
currentDeviceConnections.add (dc);
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectedDeviceGroup)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/**
|
||||
Firmware below 0.2.5 does not report its version over the Blocks API.
|
||||
This class can make requests and process responses to retreive the master Block version.
|
||||
*/
|
||||
class DepreciatedVersionReader : private MIDIDeviceConnection::Listener,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DepreciatedVersionReader (MIDIDeviceConnection& deviceConnectionToUse)
|
||||
: deviceConnection (deviceConnectionToUse)
|
||||
{
|
||||
deviceConnection.addListener (this);
|
||||
startTimer (10);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
~DepreciatedVersionReader()
|
||||
{
|
||||
deviceConnection.removeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
BlocksProtocol::VersionNumber getVersionNumber()
|
||||
{
|
||||
if (! allRequestsComplete())
|
||||
return {};
|
||||
|
||||
auto highestVersion = result[0];
|
||||
|
||||
for (size_t i = 1; i < numFirmwareApps; ++i)
|
||||
{
|
||||
const BlocksVersion highest { asString (highestVersion) };
|
||||
const BlocksVersion test { asString ( result[i]) };
|
||||
|
||||
if (highest < test)
|
||||
highestVersion = result[i];
|
||||
}
|
||||
|
||||
return highestVersion;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static constexpr size_t numFirmwareApps = 3;
|
||||
BlocksProtocol::VersionNumber result[numFirmwareApps];
|
||||
MIDIDeviceConnection& deviceConnection;
|
||||
size_t currentRequest = 0;
|
||||
|
||||
//==============================================================================
|
||||
bool allRequestsComplete() const { return currentRequest >= numFirmwareApps; }
|
||||
|
||||
//==============================================================================
|
||||
void makeNextRequest()
|
||||
{
|
||||
static constexpr size_t requestSize { 8 };
|
||||
static constexpr uint8 requests[][requestSize] = {{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x00, 0xf7 }, // Main App
|
||||
{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x01, 0xf7 }, // Stm32
|
||||
{ 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03, 0x03, 0xf7 }}; // Bootloader
|
||||
|
||||
deviceConnection.sendMessageToDevice (&requests[currentRequest][0], requestSize);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void processVersionMessage (const uint8* data, const size_t size)
|
||||
{
|
||||
if (currentRequest >= numFirmwareApps || size < 1 || size - 1 > VersionNumber::maxLength)
|
||||
return;
|
||||
|
||||
result[currentRequest].length = uint8 (size - 1);
|
||||
memcpy (result[currentRequest].data, data, result[currentRequest].length);
|
||||
|
||||
++currentRequest;
|
||||
|
||||
allRequestsComplete() ? stopTimer() : startTimer (10);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleIncomingMidiMessage (const MidiMessage& message) override
|
||||
{
|
||||
const uint8 roliVersionHeader[] = { 0xf0, 0x00, 0x21, 0x10, 0x47, 0x03 };
|
||||
const auto headerSize = sizeof (roliVersionHeader);
|
||||
const auto data = message.getRawData();
|
||||
const auto size = size_t (message.getRawDataSize());
|
||||
|
||||
if (memcmp (data, roliVersionHeader, headerSize) == 0)
|
||||
processVersionMessage (data + headerSize, size - headerSize);
|
||||
}
|
||||
|
||||
void connectionBeingDeleted (const MIDIDeviceConnection&) override {}
|
||||
|
||||
//==============================================================================
|
||||
void timerCallback() override
|
||||
{
|
||||
startTimer (200);
|
||||
makeNextRequest();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DepreciatedVersionReader)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -36,20 +36,20 @@ namespace
|
|||
|
||||
static bool versionNumberChanged (const DeviceInfo& device, juce::String version) noexcept
|
||||
{
|
||||
auto deviceVersion = device.version.asString();
|
||||
auto deviceVersion = asString (device.version);
|
||||
return deviceVersion != version && deviceVersion.isNotEmpty();
|
||||
}
|
||||
|
||||
static void setVersionNumberForBlock (const DeviceInfo& deviceInfo, Block& block) noexcept
|
||||
{
|
||||
jassert (deviceInfo.uid == block.uid);
|
||||
block.versionNumber = deviceInfo.version.asString();
|
||||
block.versionNumber = asString (deviceInfo.version);
|
||||
}
|
||||
|
||||
static void setNameForBlock (const DeviceInfo& deviceInfo, Block& block)
|
||||
{
|
||||
jassert (deviceInfo.uid == block.uid);
|
||||
block.name = deviceInfo.name.asString();
|
||||
block.name = asString (deviceInfo.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -465,7 +465,7 @@ private:
|
|||
if (versionNumberChanged (updatedInfo, blockToUpdate->versionNumber))
|
||||
setVersionNumberForBlock (updatedInfo, *blockToUpdate);
|
||||
|
||||
if (updatedInfo.name.isValid())
|
||||
if (updatedInfo.name.isNotEmpty())
|
||||
setNameForBlock (updatedInfo, *blockToUpdate);
|
||||
|
||||
if (updatedInfo.isMaster != blockToUpdate->isMasterBlock())
|
||||
|
|
@ -692,6 +692,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (Detector)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Detector)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ struct MIDIDeviceConnection : public PhysicalTopologySource::DeviceConnection,
|
|||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
|
||||
|
||||
jassert (dataSize > sizeof (BlocksProtocol::roliSysexHeader) + 2);
|
||||
jassert (memcmp (data, BlocksProtocol::roliSysexHeader, sizeof (BlocksProtocol::roliSysexHeader)) == 0);
|
||||
jassert (memcmp (data, BlocksProtocol::roliSysexHeader, sizeof (BlocksProtocol::roliSysexHeader) - 1) == 0);
|
||||
jassert (static_cast<const uint8*> (data)[dataSize - 1] == 0xf7);
|
||||
|
||||
if (midiOutput != nullptr)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#define LOG_BLOCKS_CONNECTIVITY 0
|
||||
#define LOG_BLOCKS_PINGS 0
|
||||
#define DUMP_BANDWIDTH_STATS 0
|
||||
#define DUMP_TOPOLOGY 0
|
||||
|
||||
#define TOPOLOGY_LOG(text) \
|
||||
JUCE_BLOCK_WITH_FORCED_SEMICOLON (juce::String buf ("Topology Src: "); \
|
||||
|
|
@ -46,9 +47,21 @@
|
|||
#include "internal/juce_BandwidthStatsLogger.cpp"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
/** Helper function to create juce::String from BlockStringData */
|
||||
template <size_t MaxSize>
|
||||
juce::String asString (juce::BlocksProtocol::BlockStringData<MaxSize> blockString)
|
||||
{
|
||||
return { reinterpret_cast<const char*> (blockString.data),
|
||||
juce::jmin (sizeof (blockString.data), static_cast<size_t> (blockString.length))};
|
||||
}
|
||||
}
|
||||
|
||||
#include "internal/juce_MidiDeviceConnection.cpp"
|
||||
#include "internal/juce_MIDIDeviceDetector.cpp"
|
||||
#include "internal/juce_DeviceInfo.cpp"
|
||||
#include "internal/juce_DepreciatedVersionReader.cpp"
|
||||
#include "internal/juce_ConnectedDeviceGroup.cpp"
|
||||
#include "internal/juce_BlockImplementation.cpp"
|
||||
#include "internal/juce_Detector.cpp"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue