|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2020 - Raw Material Software Limited
-
- 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
- {
-
- //==============================================================================
- /** This is the main singleton object that keeps track of connected blocks */
- struct Detector : public ReferenceCountedObject,
- private Timer,
- private AsyncUpdater
- {
- using BlockImpl = BlockImplementation<Detector>;
-
- Detector() : defaultDetector (new MIDIDeviceDetector()), deviceDetector (*defaultDetector)
- {
- startTimer (10);
- }
-
- Detector (PhysicalTopologySource::DeviceDetector& dd) : deviceDetector (dd)
- {
- startTimer (10);
- }
-
- ~Detector() override
- {
- jassert (activeTopologySources.isEmpty());
- }
-
- using Ptr = ReferenceCountedObjectPtr<Detector>;
-
- static Detector::Ptr getDefaultDetector()
- {
- auto& d = getDefaultDetectorPointer();
-
- if (d == nullptr)
- d = new Detector();
-
- return d;
- }
-
- static Detector::Ptr& getDefaultDetectorPointer()
- {
- static Detector::Ptr defaultDetector;
- return defaultDetector;
- }
-
- void detach (PhysicalTopologySource* pts)
- {
- activeTopologySources.removeAllInstancesOf (pts);
-
- if (activeTopologySources.isEmpty())
- {
- for (auto& b : currentTopology.blocks)
- if (auto bi = BlockImpl::getFrom (b))
- bi->sendCommandMessage (BlocksProtocol::endAPIMode);
-
- currentTopology = {};
-
- auto& d = getDefaultDetectorPointer();
-
- if (d != nullptr && d->getReferenceCount() == 2)
- getDefaultDetectorPointer() = nullptr;
- }
- }
-
- bool isConnected (Block::UID deviceID) const noexcept
- {
- JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED // This method must only be called from the message thread!
-
- for (auto&& b : currentTopology.blocks)
- if (b->uid == deviceID)
- return true;
-
- 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)
- {
- JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
-
- const auto blockWasRemoved = containsBlockWithUID (blocksToRemove, info.uid);
- const auto knownBlock = std::find_if (previouslySeenBlocks.begin(), previouslySeenBlocks.end(),
- [uid = info.uid] (Block::Ptr block) { return uid == block->uid; });
-
- Block::Ptr block;
-
- if (knownBlock != previouslySeenBlocks.end())
- {
- block = *knownBlock;
-
- if (auto* blockImpl = BlockImpl::getFrom (*block))
- {
- blockImpl->markReconnected (info);
- previouslySeenBlocks.removeObject (block);
- }
- }
- else
- {
- block = new BlockImpl (*this, info);
- }
-
- currentTopology.blocks.addIfNotAlreadyThere (block);
-
- if (blockWasRemoved)
- {
- blocksToUpdate.addIfNotAlreadyThere (block);
- blocksToAdd.removeObject (block);
- }
- else
- {
- blocksToAdd.addIfNotAlreadyThere (block);
- blocksToUpdate.removeObject (block);
- }
-
- blocksToRemove.removeObject (block);
-
- triggerAsyncUpdate();
- }
-
- void handleDeviceRemoved (const DeviceInfo& info)
- {
- JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
-
- const auto blockIt = std::find_if (currentTopology.blocks.begin(), currentTopology.blocks.end(),
- [uid = info.uid] (Block::Ptr block) { return uid == block->uid; });
-
- if (blockIt != currentTopology.blocks.end())
- {
- const Block::Ptr block { *blockIt };
-
- if (auto blockImpl = BlockImpl::getFrom (block.get()))
- blockImpl->markDisconnected();
-
- currentTopology.blocks.removeObject (block);
- previouslySeenBlocks.addIfNotAlreadyThere (block);
-
- blocksToRemove.addIfNotAlreadyThere (block);
- blocksToUpdate.removeObject (block);
- blocksToAdd.removeObject (block);
- triggerAsyncUpdate();
- }
- }
-
- void handleConnectionsChanged()
- {
- JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
- triggerAsyncUpdate();
- }
-
- void handleDevicesUpdated (const Array<DeviceInfo>& infos)
- {
- bool shouldTriggerUpdate { false };
-
- for (auto& info : infos)
- {
- if (containsBlockWithUID (blocksToRemove, info.uid))
- continue;
-
- const auto blockIt = std::find_if (currentTopology.blocks.begin(), currentTopology.blocks.end(),
- [uid = info.uid] (Block::Ptr block) { return uid == block->uid; });
-
-
- if (blockIt != currentTopology.blocks.end())
- {
- const Block::Ptr block { *blockIt };
-
- if (auto blockImpl = BlockImpl::getFrom (block.get()))
- blockImpl->updateDeviceInfo (info);
-
- if (! containsBlockWithUID (blocksToAdd, info.uid))
- {
- blocksToUpdate.addIfNotAlreadyThere (block);
- shouldTriggerUpdate = true;
- }
- }
- }
-
- if (shouldTriggerUpdate)
- triggerAsyncUpdate();
- }
-
- void handleDeviceUpdated (const DeviceInfo& info)
- {
- handleDevicesUpdated ({ info });
- }
-
- void handleBatteryChargingChanged (Block::UID deviceID, const BlocksProtocol::BatteryCharging isCharging)
- {
- if (auto block = currentTopology.getBlockWithUID (deviceID))
- if (auto blockImpl = BlockImpl::getFrom (*block))
- blockImpl->batteryCharging = isCharging;
- }
-
- void handleBatteryLevelChanged (Block::UID deviceID, const BlocksProtocol::BatteryLevel batteryLevel)
- {
- if (auto block = currentTopology.getBlockWithUID (deviceID))
- if (auto blockImpl = BlockImpl::getFrom (*block))
- blockImpl->batteryLevel = batteryLevel;
- }
-
- void handleIndexChanged (Block::UID deviceID, const BlocksProtocol::TopologyIndex index)
- {
- if (auto block = currentTopology.getBlockWithUID (deviceID))
- if (auto blockImpl = BlockImpl::getFrom (*block))
- blockImpl->topologyIndex = index;
- }
-
- void notifyBlockIsRestarting (Block::UID deviceID)
- {
- for (auto& group : connectedDeviceGroups)
- group->handleBlockRestarting (deviceID);
- }
-
- Array<Block::UID> getDnaDependentDeviceUIDs (Block::UID uid)
- {
- JUCE_ASSERT_MESSAGE_THREAD
-
- Array<Block::UID> dependentDeviceUIDs;
-
- if (auto block = getBlockImplementationWithUID (uid))
- {
- if (auto master = getBlockImplementationWithUID (block->masterUID))
- {
- auto graph = BlockGraph (currentTopology, [uid] (Block::Ptr b) { return b->uid != uid; });
- const auto pathWithoutBlock = graph.getTraversalPathFromMaster (master);
-
- for (const auto b : currentTopology.blocks)
- {
- if (b->uid != uid && ! pathWithoutBlock.contains (b))
- {
- TOPOLOGY_LOG ( "Dependent device: " + b->name);
- dependentDeviceUIDs.add (b->uid);
- }
- }
- }
- }
-
- return dependentDeviceUIDs;
- }
-
- void handleSharedDataACK (Block::UID deviceID, uint32 packetCounter) const
- {
- JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
-
- if (auto* bi = getBlockImplementationWithUID (deviceID))
- bi->handleSharedDataACK (packetCounter);
- }
-
- void handleFirmwareUpdateACK (Block::UID deviceID, uint8 resultCode, uint32 resultDetail)
- {
- if (auto* bi = getBlockImplementationWithUID (deviceID))
- bi->handleFirmwareUpdateACK (resultCode, resultDetail);
- }
-
- void handleConfigUpdateMessage (Block::UID deviceID, int32 item, int32 value, int32 min, int32 max)
- {
- if (auto* bi = getBlockImplementationWithUID (deviceID))
- bi->handleConfigUpdateMessage (item, value, min, max);
- }
-
- void notifyBlockOfConfigChange (BlockImpl& bi, uint32 item)
- {
- if (item >= bi.getMaxConfigIndex())
- bi.handleConfigItemChanged ({ item }, item);
- else
- bi.handleConfigItemChanged (bi.getLocalConfigMetaData (item), item);
- }
-
- void handleConfigSetMessage (Block::UID deviceID, int32 item, int32 value)
- {
- if (auto* bi = getBlockImplementationWithUID (deviceID))
- {
- bi->handleConfigSetMessage (item, value);
- notifyBlockOfConfigChange (*bi, uint32 (item));
- }
- }
-
- void handleConfigFactorySyncEndMessage (Block::UID deviceID)
- {
- if (auto* bi = getBlockImplementationWithUID (deviceID))
- bi->handleConfigSyncEnded();
- }
-
- void handleConfigFactorySyncResetMessage (Block::UID deviceID)
- {
- if (auto* bi = getBlockImplementationWithUID (deviceID))
- bi->resetConfigListActiveStatus();
- }
-
- void handleLogMessage (Block::UID deviceID, const String& message) const
- {
- JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
-
- if (auto* bi = getBlockImplementationWithUID (deviceID))
- bi->handleLogMessage (message);
- }
-
- void handleButtonChange (Block::UID deviceID, Block::Timestamp timestamp, uint32 buttonIndex, bool isDown) const
- {
- JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
-
- if (auto* bi = getBlockImplementationWithUID (deviceID))
- {
- bi->pingFromDevice();
-
- if (isPositiveAndBelow (buttonIndex, bi->getButtons().size()))
- if (auto* cbi = dynamic_cast<BlockImpl::ControlButtonImplementation*> (bi->getButtons().getUnchecked (int (buttonIndex))))
- cbi->broadcastButtonChange (timestamp, bi->modelData.buttons[(int) buttonIndex].type, isDown);
- }
- }
-
- void handleTouchChange (Block::UID deviceID, const TouchSurface::Touch& touchEvent)
- {
- JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
-
- auto block = currentTopology.getBlockWithUID (deviceID);
- if (block != nullptr)
- {
- if (auto* surface = dynamic_cast<BlockImpl::TouchSurfaceImplementation*> (block->getTouchSurface()))
- {
- TouchSurface::Touch scaledEvent (touchEvent);
-
- scaledEvent.x *= (float) block->getWidth();
- scaledEvent.y *= (float) block->getHeight();
- scaledEvent.startX *= (float) block->getWidth();
- scaledEvent.startY *= (float) block->getHeight();
-
- surface->broadcastTouchChange (scaledEvent);
- }
- }
- }
-
- void cancelAllActiveTouches() noexcept
- {
- for (auto& block : currentTopology.blocks)
- if (auto* surface = block->getTouchSurface())
- surface->cancelAllActiveTouches();
- }
-
- void handleCustomMessage (Block::UID deviceID, Block::Timestamp timestamp, const int32* data)
- {
- if (auto* bi = getBlockImplementationWithUID (deviceID))
- bi->handleCustomMessage (timestamp, data);
- }
-
- //==============================================================================
- template <typename PacketBuilder>
- bool sendMessageToDevice (Block::UID deviceID, const PacketBuilder& builder) const
- {
- for (auto* c : connectedDeviceGroups)
- if (c->contains (deviceID))
- return c->sendMessageToDevice (builder);
-
- return false;
- }
-
- static Detector* getFrom (Block& b) noexcept
- {
- if (auto* bi = BlockImpl::getFrom (b))
- return (bi->detector);
-
- jassertfalse;
- return nullptr;
- }
-
- PhysicalTopologySource::DeviceConnection* getDeviceConnectionFor (const Block& b)
- {
- for (const auto& d : connectedDeviceGroups)
- {
- if (d->contains (b.uid))
- return d->getDeviceConnection();
- }
-
- return nullptr;
- }
-
- const PhysicalTopologySource::DeviceConnection* getDeviceConnectionFor (const Block& b) const
- {
- for (const auto& d : connectedDeviceGroups)
- {
- if (d->contains (b.uid))
- return d->getDeviceConnection();
- }
-
- return nullptr;
- }
-
- std::unique_ptr<MIDIDeviceDetector> defaultDetector;
- PhysicalTopologySource::DeviceDetector& deviceDetector;
-
- Array<PhysicalTopologySource*> activeTopologySources;
-
- BlockTopology currentTopology;
-
- private:
- Block::Array previouslySeenBlocks, blocksToAdd, blocksToRemove, blocksToUpdate;
-
- void timerCallback() override
- {
- startTimer (1500);
-
- auto detectedDevices = deviceDetector.scanForDevices();
-
- handleDevicesRemoved (detectedDevices);
- handleDevicesAdded (detectedDevices);
- }
-
- bool containsBlockWithUID (const Block::Array& blocks, Block::UID uid)
- {
- for (const auto block : blocks)
- if (block->uid == uid)
- return true;
-
- return false;
- }
-
- void handleDevicesRemoved (const StringArray& detectedDevices)
- {
- for (int i = connectedDeviceGroups.size(); --i >= 0;)
- if (! connectedDeviceGroups.getUnchecked(i)->isStillConnected (detectedDevices))
- connectedDeviceGroups.remove (i);
- }
-
- void handleDevicesAdded (const StringArray& detectedDevices)
- {
- for (const auto& devName : detectedDevices)
- {
- if (! hasDeviceFor (devName))
- {
- if (auto d = deviceDetector.openDevice (detectedDevices.indexOf (devName)))
- {
- connectedDeviceGroups.add (new ConnectedDeviceGroup<Detector> (*this, devName, d));
- }
- }
- }
- }
-
- bool hasDeviceFor (const String& devName) const
- {
- for (auto d : connectedDeviceGroups)
- if (d->deviceName == devName)
- return true;
-
- return false;
- }
-
- BlockImpl* getBlockImplementationWithUID (Block::UID deviceID) const noexcept
- {
- if (auto block = currentTopology.getBlockWithUID (deviceID))
- return BlockImpl::getFrom (*block);
-
- return nullptr;
- }
-
- OwnedArray<ConnectedDeviceGroup<Detector>> connectedDeviceGroups;
-
- //==============================================================================
- /** This is a friend of the BlocksImplementation that will scan and set the
- physical positions of the blocks.
-
- Returns an array of blocks that were updated.
- */
- struct BlocksLayoutTraverser
- {
- static Block::Array updateBlocks (const BlockTopology& topology)
- {
- Block::Array updated;
- Array<Block::UID> visited;
-
- for (auto& block : topology.blocks)
- {
- if (block->isMasterBlock() && ! visited.contains (block->uid))
- {
- if (auto* bi = BlockImpl::getFrom (block))
- {
- if (bi->rotation != 0 || bi->position.first != 0 || bi->position.second != 0)
- {
- bi->rotation = 0;
- bi->position = {};
- updated.add (block);
- }
- }
-
- layoutNeighbours (*block, topology, visited, updated);
- }
- }
-
- return updated;
- }
-
- private:
- // returns the distance from corner clockwise
- static int getUnitForIndex (Block::Ptr block, Block::ConnectionPort::DeviceEdge edge, int index)
- {
- if (block->getType() == Block::seaboardBlock)
- {
- if (edge == Block::ConnectionPort::DeviceEdge::north)
- {
- if (index == 0) return 1;
- if (index == 1) return 4;
- }
- else if (edge != Block::ConnectionPort::DeviceEdge::south)
- {
- return 1;
- }
- }
- else if (block->getType() == Block::lumiKeysBlock)
- {
- if (edge == Block::ConnectionPort::DeviceEdge::north)
- {
- switch (index)
- {
- case 0 : return 0;
- case 1 : return 2;
- case 2 : return 3;
- case 3 : return 5;
- default : jassertfalse;
- }
- }
- else if (edge == Block::ConnectionPort::DeviceEdge::south)
- {
- jassertfalse;
- }
- }
-
- if (edge == Block::ConnectionPort::DeviceEdge::south)
- return block->getWidth() - (index + 1);
-
- if (edge == Block::ConnectionPort::DeviceEdge::west)
- return block->getHeight() - (index + 1);
-
- return index;
- }
-
- // returns how often north needs to rotate by 90 degrees
- static int getRotationForEdge (Block::ConnectionPort::DeviceEdge edge)
- {
- switch (edge)
- {
- case Block::ConnectionPort::DeviceEdge::north: return 0;
- case Block::ConnectionPort::DeviceEdge::east: return 1;
- case Block::ConnectionPort::DeviceEdge::south: return 2;
- case Block::ConnectionPort::DeviceEdge::west: return 3;
- default: break;
- }
-
- jassertfalse;
- return 0;
- }
-
- static void layoutNeighbours (const Block::Ptr block,
- const BlockTopology& topology,
- Array<Block::UID>& visited,
- Block::Array& updated)
- {
- visited.add (block->uid);
-
- for (auto& connection : topology.connections)
- {
- if ((connection.device1 == block->uid && ! visited.contains (connection.device2))
- || (connection.device2 == block->uid && ! visited.contains (connection.device1)))
- {
- const auto theirUid = connection.device1 == block->uid ? connection.device2 : connection.device1;
- const auto neighbourPtr = topology.getBlockWithUID (theirUid);
-
- if (auto* neighbour = dynamic_cast<BlockImpl*> (neighbourPtr.get()))
- {
- const auto myBounds = block->getBlockAreaWithinLayout();
- const auto& myPort = connection.device1 == block->uid ? connection.connectionPortOnDevice1 : connection.connectionPortOnDevice2;
- const auto& theirPort = connection.device1 == block->uid ? connection.connectionPortOnDevice2 : connection.connectionPortOnDevice1;
- const auto myOffset = getUnitForIndex (block, myPort.edge, myPort.index);
- const auto theirOffset = getUnitForIndex (neighbourPtr, theirPort.edge, theirPort.index);
-
- {
- const auto neighbourRotation = (2 + block->getRotation()
- + getRotationForEdge (myPort.edge)
- - getRotationForEdge (theirPort.edge)) % 4;
-
- if (neighbour->rotation != neighbourRotation)
- {
- neighbour->rotation = neighbourRotation;
- updated.addIfNotAlreadyThere (neighbourPtr);
- }
- }
-
- std::pair<int, int> delta;
- const auto theirBounds = neighbour->getBlockAreaWithinLayout();
-
- switch ((block->getRotation() + getRotationForEdge (myPort.edge)) % 4)
- {
- case 0: // over me
- delta = { myOffset - (theirBounds.width - (theirOffset + 1)), -theirBounds.height };
- break;
- case 1: // right of me
- delta = { myBounds.width, myOffset - (theirBounds.height - (theirOffset + 1)) };
- break;
- case 2: // under me
- delta = { (myBounds.width - (myOffset + 1)) - theirOffset, myBounds.height };
- break;
- case 3: // left of me
- delta = { -theirBounds.width, (myBounds.height - (myOffset + 1)) - theirOffset };
- break;
- default:
- break;
- }
-
- {
- const auto neighbourX = myBounds.x + delta.first;
- const auto neighbourY = myBounds.y + delta.second;
-
- if (neighbour->position.first != neighbourX
- || neighbour->position.second != neighbourY)
- {
- neighbour->position.first = neighbourX;
- neighbour->position.second = neighbourY;
-
- updated.addIfNotAlreadyThere (neighbourPtr);
- }
- }
-
- layoutNeighbours (neighbourPtr, topology, visited, updated);
- }
- }
- }
- }
- };
-
- //==============================================================================
- #if DUMP_TOPOLOGY
- static String idToSerialNum (const BlockTopology& topology, Block::UID uid)
- {
- for (auto* b : topology.blocks)
- if (b->uid == uid)
- return b->serialNumber;
-
- return "???";
- }
-
- static String portEdgeToString (Block::ConnectionPort port)
- {
- switch (port.edge)
- {
- case Block::ConnectionPort::DeviceEdge::north: return "north";
- case Block::ConnectionPort::DeviceEdge::south: return "south";
- case Block::ConnectionPort::DeviceEdge::east: return "east";
- case Block::ConnectionPort::DeviceEdge::west: return "west";
- default: break;
- }
-
- return {};
- }
-
- static String portToString (Block::ConnectionPort port)
- {
- return portEdgeToString (port) + "_" + String (port.index);
- }
-
- static void dumpTopology (const BlockTopology& topology)
- {
- MemoryOutputStream m;
-
- m << "=============================================================================" << newLine
- << "Topology: " << topology.blocks.size() << " device(s)" << newLine
- << newLine;
-
- int index = 0;
-
- for (auto block : topology.blocks)
- {
- m << "Device " << index++ << (block->isMasterBlock() ? ": (MASTER)" : ":") << newLine;
-
- m << " Description: " << block->getDeviceDescription() << newLine
- << " Serial: " << block->serialNumber << newLine;
-
- if (auto bi = BlockImplementation<Detector>::getFrom (*block))
- m << " Short address: " << (int) bi->getDeviceIndex() << newLine;
-
- m << " Battery level: " + String (roundToInt (100.0f * block->getBatteryLevel())) + "%" << newLine
- << " Battery charging: " + String (block->isBatteryCharging() ? "y" : "n") << newLine
- << " Width: " << block->getWidth() << newLine
- << " Height: " << block->getHeight() << newLine
- << " Millimeters per unit: " << block->getMillimetersPerUnit() << newLine
- << newLine;
- }
-
- for (auto& connection : topology.connections)
- {
- m << idToSerialNum (topology, connection.device1)
- << ":" << portToString (connection.connectionPortOnDevice1)
- << " <-> "
- << idToSerialNum (topology, connection.device2)
- << ":" << portToString (connection.connectionPortOnDevice2) << newLine;
- }
-
- m << "=============================================================================" << newLine;
-
- Logger::outputDebugString (m.toString());
- }
- #endif
-
- //==============================================================================
- void updateBlockPositions()
- {
- const auto updated = BlocksLayoutTraverser::updateBlocks (currentTopology);
-
- for (const auto block : updated)
- {
- if (containsBlockWithUID (blocksToAdd, block->uid) || containsBlockWithUID (blocksToRemove, block->uid))
- continue;
-
- blocksToUpdate.addIfNotAlreadyThere (block);
- }
- }
-
- void updateBlockConnections()
- {
- currentTopology.connections.clearQuick();
-
- for (auto d : connectedDeviceGroups)
- currentTopology.connections.addArray (d->getCurrentDeviceConnections());
- }
-
- void handleAsyncUpdate() override
- {
- updateBlockConnections();
- updateBlockPositions();
-
- for (auto* d : activeTopologySources)
- {
- for (const auto block : blocksToAdd)
- d->listeners.call ([&block] (TopologySource::Listener& l) { l.blockAdded (block); });
-
- for (const auto block : blocksToRemove)
- d->listeners.call ([&block] (TopologySource::Listener& l) { l.blockRemoved (block); });
-
- for (const auto block : blocksToUpdate)
- d->listeners.call ([&block] (TopologySource::Listener& l) { l.blockUpdated (block); });
- }
-
- const auto topologyChanged = blocksToAdd.size() > 0 || blocksToRemove.size() > 0 || blocksToUpdate.size() > 0;
-
- if (topologyChanged)
- {
- #if DUMP_TOPOLOGY
- dumpTopology (currentTopology);
- #endif
-
- for (auto* d : activeTopologySources)
- d->listeners.call ([] (TopologySource::Listener& l) { l.topologyChanged(); });
- }
-
- blocksToUpdate.clear();
- blocksToAdd.clear();
- blocksToRemove.clear();
-
- static const int maxBlocksToSave = 100;
-
- if (previouslySeenBlocks.size() > maxBlocksToSave)
- previouslySeenBlocks.removeRange (0, 2 * (previouslySeenBlocks.size() - maxBlocksToSave));
- }
-
- //==============================================================================
- JUCE_DECLARE_WEAK_REFERENCEABLE (Detector)
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Detector)
- };
-
- } // namespace juce
|