Browse Source

BLOCKS: Use unique identifier to find MIDI ports and fix some connection issues

tags/2021-05-28
ed 6 years ago
parent
commit
fcbdf0629f
7 changed files with 131 additions and 54 deletions
  1. +11
    -2
      modules/juce_blocks_basics/blocks/juce_Block.h
  2. +29
    -9
      modules/juce_blocks_basics/topology/internal/juce_BlockImplementation.cpp
  3. +11
    -4
      modules/juce_blocks_basics/topology/internal/juce_ConnectedDeviceGroup.cpp
  4. +1
    -1
      modules/juce_blocks_basics/topology/internal/juce_DepreciatedVersionReader.cpp
  5. +14
    -4
      modules/juce_blocks_basics/topology/internal/juce_Detector.cpp
  6. +62
    -21
      modules/juce_blocks_basics/topology/internal/juce_MIDIDeviceDetector.cpp
  7. +3
    -13
      modules/juce_blocks_basics/topology/internal/juce_MidiDeviceConnection.cpp

+ 11
- 2
modules/juce_blocks_basics/blocks/juce_Block.h View File

@@ -110,14 +110,23 @@ public:
/** Returns the time this block object was connected to the topology. /** Returns the time this block object was connected to the topology.
Only valid when isConnected == true. Only valid when isConnected == true.
@see isConnected
@see Block::isConnected
*/ */
virtual Time getConnectionTime() const = 0; virtual Time getConnectionTime() const = 0;
/** Returns true if this block or the master block this block is connected to,
is connected via bluetooth.
Only valid when isConnected == true.
@see Block::isConnected, Block::isMasterBlock
*/
virtual bool isConnectedViaBluetooth() const = 0;
/** Returns true if this block is directly connected to the application, /** Returns true if this block is directly connected to the application,
as opposed to only being connected to a different block via a connection port. as opposed to only being connected to a different block via a connection port.
@see ConnectionPort
@see Block::ConnectionPort
*/ */
virtual bool isMasterBlock() const = 0; virtual bool isMasterBlock() const = 0;


+ 29
- 9
modules/juce_blocks_basics/topology/internal/juce_BlockImplementation.cpp View File

@@ -63,11 +63,7 @@ public:
~BlockImplementation() ~BlockImplementation()
{ {
if (listenerToMidiConnection != nullptr)
{
config.setDeviceComms (nullptr);
listenerToMidiConnection->removeListener (this);
}
markDisconnected();
} }
void markDisconnected() void markDisconnected()
@@ -75,6 +71,7 @@ public:
if (auto surface = dynamic_cast<TouchSurfaceImplementation*> (touchSurface.get())) if (auto surface = dynamic_cast<TouchSurfaceImplementation*> (touchSurface.get()))
surface->disableTouchSurface(); surface->disableTouchSurface();
disconnectMidiConnectionListener();
connectionTime = Time(); connectionTime = Time();
} }
@@ -121,6 +118,32 @@ public:
config.setDeviceComms (listenerToMidiConnection); config.setDeviceComms (listenerToMidiConnection);
} }
void disconnectMidiConnectionListener()
{
if (listenerToMidiConnection != nullptr)
{
config.setDeviceComms (nullptr);
listenerToMidiConnection->removeListener (this);
listenerToMidiConnection = nullptr;
}
}
bool isConnected() const override
{
if (detector != nullptr)
return detector->isConnected (uid);
return false;
}
bool isConnectedViaBluetooth() const override
{
if (detector != nullptr)
return detector->isConnectedViaBluetooth (*this);
return false;
}
Type getType() const override { return modelData.apiType; } Type getType() const override { return modelData.apiType; }
String getDeviceDescription() const override { return modelData.description; } String getDeviceDescription() const override { return modelData.description; }
int getWidth() const override { return modelData.widthUnits; } int getWidth() const override { return modelData.widthUnits; }
@@ -128,7 +151,6 @@ public:
float getMillimetersPerUnit() const override { return 47.0f; } float getMillimetersPerUnit() const override { return 47.0f; }
bool isHardwareBlock() const override { return true; } bool isHardwareBlock() const override { return true; }
juce::Array<Block::ConnectionPort> getPorts() const override { return modelData.ports; } juce::Array<Block::ConnectionPort> getPorts() const override { return modelData.ports; }
bool isConnected() const override { return detector && detector->isConnected (uid); }
Time getConnectionTime() const override { return connectionTime; } Time getConnectionTime() const override { return connectionTime; }
bool isMasterBlock() const override { return isMaster; } bool isMasterBlock() const override { return isMaster; }
Block::UID getConnectedMasterUID() const override { return masterUID; } Block::UID getConnectedMasterUID() const override { return masterUID; }
@@ -696,9 +718,7 @@ private:
{ {
jassert (listenerToMidiConnection == &c); jassert (listenerToMidiConnection == &c);
ignoreUnused (c); ignoreUnused (c);
listenerToMidiConnection->removeListener (this);
listenerToMidiConnection = nullptr;
config.setDeviceComms (nullptr);
disconnectMidiConnectionListener();
} }
void doSaveProgramAsDefault() void doSaveProgramAsDefault()


+ 11
- 4
modules/juce_blocks_basics/topology/internal/juce_ConnectedDeviceGroup.cpp View File

@@ -41,8 +41,6 @@ struct ConnectedDeviceGroup : private AsyncUpdater,
{ {
if (auto midiDeviceConnection = static_cast<MIDIDeviceConnection*> (deviceConnection.get())) if (auto midiDeviceConnection = static_cast<MIDIDeviceConnection*> (deviceConnection.get()))
{ {
depreciatedVersionReader = std::make_unique<DepreciatedVersionReader> (*midiDeviceConnection);
ScopedLock lock (midiDeviceConnection->criticalSecton); ScopedLock lock (midiDeviceConnection->criticalSecton);
setMidiMessageCallback(); setMidiMessageCallback();
} }
@@ -300,7 +298,8 @@ private:
struct TouchStart { float x, y; }; struct TouchStart { float x, y; };
TouchList<TouchStart> touchStartPositions; TouchList<TouchStart> touchStartPositions;
Block::UID masterBlock = 0;
static constexpr Block::UID invalidUid = 0;
Block::UID masterBlock = invalidUid;
//============================================================================== //==============================================================================
void timerCallback() override void timerCallback() override
@@ -499,6 +498,9 @@ private:
removeDevice (uid); removeDevice (uid);
if (uid == masterBlock)
masterBlock = invalidUid;
scheduleNewTopologyRequest(); scheduleNewTopologyRequest();
} }
@@ -643,9 +645,14 @@ private:
if (! getDeviceInfoFromUID (uid)) if (! getDeviceInfoFromUID (uid))
{ {
// For backwards compatibility we assume the first device we see in a group is the master and won't change // For backwards compatibility we assume the first device we see in a group is the master and won't change
if (masterBlock == 0)
if (masterBlock == invalidUid)
{
masterBlock = uid; masterBlock = uid;
if (auto midiDeviceConnection = static_cast<MIDIDeviceConnection*> (deviceConnection.get()))
depreciatedVersionReader = std::make_unique<DepreciatedVersionReader> (*midiDeviceConnection);
}
currentDeviceInfo.add ({ uid, currentDeviceInfo.add ({ uid,
device.index, device.index,
device.serialNumber, device.serialNumber,


+ 1
- 1
modules/juce_blocks_basics/topology/internal/juce_DepreciatedVersionReader.cpp View File

@@ -24,7 +24,7 @@ namespace juce
{ {
/** /**
Firmware below 0.2.5 does not report its version over the Blocks API.
Firmware below 0.3.0 does not report its version over the Blocks API.
This class can make requests and process responses to retreive the master Block version. This class can make requests and process responses to retreive the master Block version.
*/ */
class DepreciatedVersionReader : private MIDIDeviceConnection::Listener, class DepreciatedVersionReader : private MIDIDeviceConnection::Listener,


+ 14
- 4
modules/juce_blocks_basics/topology/internal/juce_Detector.cpp View File

@@ -94,6 +94,16 @@ struct Detector : public ReferenceCountedObject,
return false; return false;
} }
bool isConnectedViaBluetooth (const Block& block) const noexcept
{
if (const auto connection = getDeviceConnectionFor (block))
if (const auto midiConnection = dynamic_cast<const MIDIDeviceConnection*> (connection))
if (midiConnection->midiInput != nullptr)
return midiConnection->midiInput->getName().containsIgnoreCase ("bluetooth");
return false;
}
void handleDeviceAdded (const DeviceInfo& info) void handleDeviceAdded (const DeviceInfo& info)
{ {
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
@@ -146,9 +156,9 @@ struct Detector : public ReferenceCountedObject,
if (blockIt != currentTopology.blocks.end()) if (blockIt != currentTopology.blocks.end())
{ {
const auto block = *blockIt;
const Block::Ptr block { *blockIt };
if (auto blockImpl = BlockImpl::getFrom (block))
if (auto blockImpl = BlockImpl::getFrom (block.get()))
blockImpl->markDisconnected(); blockImpl->markDisconnected();
currentTopology.blocks.removeObject (block); currentTopology.blocks.removeObject (block);
@@ -177,9 +187,9 @@ struct Detector : public ReferenceCountedObject,
if (blockIt != currentTopology.blocks.end()) if (blockIt != currentTopology.blocks.end())
{ {
const auto block = *blockIt;
const Block::Ptr block { *blockIt };
if (auto blockImpl = BlockImpl::getFrom (block))
if (auto blockImpl = BlockImpl::getFrom (block.get()))
blockImpl->markReconnected (info); blockImpl->markReconnected (info);
if (! containsBlockWithUID (blocksToAdd, info.uid)) if (! containsBlockWithUID (blocksToAdd, info.uid))


+ 62
- 21
modules/juce_blocks_basics/topology/internal/juce_MIDIDeviceDetector.cpp View File

@@ -32,25 +32,27 @@ struct MIDIDeviceDetector : public PhysicalTopologySource::DeviceDetector
StringArray result; StringArray result;
for (auto& pair : findDevices()) for (auto& pair : findDevices())
result.add (pair.inputName + " & " + pair.outputName);
result.add (pair.input.identifier + " & " + pair.output.identifier);
return result; return result;
} }
PhysicalTopologySource::DeviceConnection* openDevice (int index) override PhysicalTopologySource::DeviceConnection* openDevice (int index) override
{ {
auto pair = findDevices()[index];
const auto allDevices = findDevices();
if (pair.inputIndex >= 0 && pair.outputIndex >= 0)
if (allDevices.size() > index)
{ {
std::unique_ptr<MIDIDeviceConnection> dev (new MIDIDeviceConnection());
const auto pair = allDevices[index];
auto dev = std::make_unique<MIDIDeviceConnection>();
if (dev->lockAgainstOtherProcesses (pair.inputName, pair.outputName))
if (auto lock = createMidiPortLock (pair.input.name, pair.output.name))
{ {
lockedFromOutside = false; lockedFromOutside = false;
dev->midiInput.reset (MidiInput::openDevice (pair.inputIndex, dev.get()));
dev->midiOutput.reset (MidiOutput::openDevice (pair.outputIndex));
dev->setLockAgainstOtherProcesses (lock);
dev->midiInput.reset (MidiInput::openDevice (pair.input.identifier, dev.get()));
dev->midiOutput.reset (MidiOutput::openDevice (pair.output.identifier));
if (dev->midiInput != nullptr) if (dev->midiInput != nullptr)
{ {
@@ -96,33 +98,43 @@ struct MIDIDeviceDetector : public PhysicalTopologySource::DeviceDetector
struct MidiInputOutputPair struct MidiInputOutputPair
{ {
String outputName, inputName;
int outputIndex = -1, inputIndex = -1;
MidiDeviceInfo input, output;
}; };
static Array<MidiInputOutputPair> findDevices() static Array<MidiInputOutputPair> findDevices()
{ {
Array<MidiInputOutputPair> result; Array<MidiInputOutputPair> result;
auto midiInputs = MidiInput::getDevices();
auto midiOutputs = MidiOutput::getDevices();
auto midiInputs = MidiInput::getAvailableDevices();
auto midiOutputs = MidiOutput::getAvailableDevices();
for (int j = 0; j < midiInputs.size(); ++j)
for (const auto& input : midiInputs)
{ {
if (isBlocksMidiDeviceName (midiInputs[j]))
if (isBlocksMidiDeviceName (input.name))
{ {
MidiInputOutputPair pair; MidiInputOutputPair pair;
pair.inputName = midiInputs[j];
pair.inputIndex = j;
pair.input = input;
String cleanedInputName = cleanBlocksDeviceName (pair.inputName);
for (int i = 0; i < midiOutputs.size(); ++i)
String cleanedInputName = cleanBlocksDeviceName (input.name);
int inputOccurences = 0;
int outputOccurences = 0;
for (const auto& p : result)
if (cleanBlocksDeviceName (p.input.name) == cleanedInputName)
++inputOccurences;
for (const auto& output : midiOutputs)
{ {
if (cleanBlocksDeviceName (midiOutputs[i]) == cleanedInputName)
if (cleanBlocksDeviceName (output.name) == cleanedInputName)
{ {
pair.outputName = midiOutputs[i];
pair.outputIndex = i;
break;
if (outputOccurences == inputOccurences)
{
pair.output = output;
break;
}
++outputOccurences;
} }
} }
@@ -136,6 +148,35 @@ struct MIDIDeviceDetector : public PhysicalTopologySource::DeviceDetector
private: private:
bool lockedFromOutside = true; bool lockedFromOutside = true;
/** For backwards compatibility, the block interprocess lock has to use the midi input name.
The below is necceccary because blocks of the same type might duplicate a port name, so
must share an interporcess lock.
*/
std::shared_ptr<InterProcessLock> createMidiPortLock (const String& midiInName, const String& midiOutName)
{
const juce::String lockIdentifier = "blocks_sdk_"
+ File::createLegalFileName (midiInName)
+ "_" + File::createLegalFileName (midiOutName);
const auto existingLock = midiPortLocks.find (lockIdentifier);
if (existingLock != midiPortLocks.end())
if (existingLock->second.use_count() > 0)
return existingLock->second.lock();
auto interprocessLock = std::make_shared<InterProcessLock> (lockIdentifier);
if (interprocessLock->enter (500))
{
midiPortLocks[lockIdentifier] = interprocessLock;
return interprocessLock;
}
return nullptr;
}
std::map<juce::String, std::weak_ptr<InterProcessLock>> midiPortLocks;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MIDIDeviceDetector) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MIDIDeviceDetector)
}; };


+ 3
- 13
modules/juce_blocks_basics/topology/internal/juce_MidiDeviceConnection.cpp View File

@@ -36,21 +36,11 @@ struct MIDIDeviceConnection : public PhysicalTopologySource::DeviceConnection,
if (midiInput != nullptr) if (midiInput != nullptr)
midiInput->stop(); midiInput->stop();
if (interprocessLock != nullptr)
interprocessLock->exit();
} }
bool lockAgainstOtherProcesses (const String& midiInName, const String& midiOutName)
void setLockAgainstOtherProcesses (std::shared_ptr<InterProcessLock> newLock)
{ {
interprocessLock.reset (new InterProcessLock ("blocks_sdk_"
+ File::createLegalFileName (midiInName)
+ "_" + File::createLegalFileName (midiOutName)));
if (interprocessLock->enter (500))
return true;
interprocessLock = nullptr;
return false;
midiPortLock = newLock;
} }
struct Listener struct Listener
@@ -115,7 +105,7 @@ struct MIDIDeviceConnection : public PhysicalTopologySource::DeviceConnection,
private: private:
ListenerList<Listener> listeners; ListenerList<Listener> listeners;
std::unique_ptr<InterProcessLock> interprocessLock;
std::shared_ptr<InterProcessLock> midiPortLock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MIDIDeviceConnection) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MIDIDeviceConnection)
}; };


Loading…
Cancel
Save