diff --git a/Plugins/LfpDisplayNode/DisplayBuffer.cpp b/Plugins/LfpDisplayNode/DisplayBuffer.cpp index 969f0c015..b8f325af6 100644 --- a/Plugins/LfpDisplayNode/DisplayBuffer.cpp +++ b/Plugins/LfpDisplayNode/DisplayBuffer.cpp @@ -64,7 +64,8 @@ void DisplayBuffer::addChannel( ContinuousChannel::Type type, bool isRecorded, int group, - float ypos, + float ypos, + String description, String structure) { ChannelMetadata metadata = ChannelMetadata(); @@ -75,6 +76,7 @@ void DisplayBuffer::addChannel( metadata.structure = structure; metadata.type = type; metadata.isRecorded = isRecorded; + metadata.description = description; channelMetadata.add(metadata); channelMap[channelNum] = numChannels; diff --git a/Plugins/LfpDisplayNode/DisplayBuffer.h b/Plugins/LfpDisplayNode/DisplayBuffer.h index d84420974..868633a84 100644 --- a/Plugins/LfpDisplayNode/DisplayBuffer.h +++ b/Plugins/LfpDisplayNode/DisplayBuffer.h @@ -60,7 +60,8 @@ namespace LfpViewer { ContinuousChannel::Type channelType, bool isRecorded, int group = 0, - float ypos = 0, + float ypos = 0, + String description = "", String structure = "None"); /** Initializes the event channel at the start of each buffer */ @@ -87,10 +88,11 @@ namespace LfpViewer { String structure = "None"; ContinuousChannel::Type type; bool isRecorded = false; + String description = ""; }; Array channelMetadata; - + String name; int id; @@ -128,4 +130,4 @@ namespace LfpViewer { }; -#endif //__DISPLAYBUFFER_H__ \ No newline at end of file +#endif //__DISPLAYBUFFER_H__ diff --git a/Plugins/LfpDisplayNode/LfpDisplay.cpp b/Plugins/LfpDisplayNode/LfpDisplay.cpp index 94cf722cd..044a8fede 100644 --- a/Plugins/LfpDisplayNode/LfpDisplay.cpp +++ b/Plugins/LfpDisplayNode/LfpDisplay.cpp @@ -981,27 +981,38 @@ void LfpDisplay::rebuildDrawableChannelsList() removeAllChildren(); // start with clean slate Array channelsToDraw; // all visible channels will be added to this array - + Array filteredChannels; + if(canvasSplit -> displayBuffer) { + filteredChannels = canvasSplit -> getFilteredChannels(); + } // iterate over all channels and select drawable ones - for (int i = 0, drawableChannelNum = 0; i < channels.size(); i++) + for (int i = 0, drawableChannelNum = 0, filterChannelIndex = 0; i < channels.size(); i++) { - //std::cout << "Checking for hidden channels" << std::endl; - if (displaySkipAmt == 0 || (i % displaySkipAmt == 0)) // no skips, add all channels - { - channels[i]->setHidden(false); - channelInfo[i]->setHidden(false); - - channelInfo[i]->setDrawableChannelNumber(drawableChannelNum++); - channelInfo[i]->resized(); // to update the conditional drawing of enableButton and channel num - - channelsToDraw.add(LfpDisplay::LfpChannelTrack{ - channels[i], - channelInfo[i] - }); + int channelNumber = filteredChannels.size() ? canvasSplit->displayBuffer->channelMetadata[i].description.getIntValue(): -1; + //the filter list can have channels that aren't selected for acqusition; this skips those filtered channels + while(filterChannelIndex < filteredChannels.size() && channelNumber > filteredChannels[filterChannelIndex]){ + filterChannelIndex++; + } + if(filteredChannels.size() == 0 || (filterChannelIndex < filteredChannels.size() && channelNumber == filteredChannels[filterChannelIndex])) { + if (displaySkipAmt == 0 || ((filteredChannels.size() ? filterChannelIndex : i) % displaySkipAmt == 0)) // no skips, add all channels + { - addAndMakeVisible(channels[i]); - addAndMakeVisible(channelInfo[i]); + channels[i]->setHidden(false); + channelInfo[i]->setHidden(false); + + channelInfo[i]->setDrawableChannelNumber(drawableChannelNum++); + channelInfo[i]->resized(); // to update the conditional drawing of enableButton and channel num + + channelsToDraw.add(LfpDisplay::LfpChannelTrack{ + channels[i], + channelInfo[i] + }); + + addAndMakeVisible(channels[i]); + addAndMakeVisible(channelInfo[i]); + } + filterChannelIndex++; } else // skip some channels { diff --git a/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp b/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp index 5b216777e..eada7bd60 100644 --- a/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp +++ b/Plugins/LfpDisplayNode/LfpDisplayCanvas.cpp @@ -1661,8 +1661,13 @@ void LfpDisplaySplitter::visibleAreaChanged() void LfpDisplaySplitter::refresh() { updateScreenBuffer(); - - lfpDisplay->refresh(); // redraws only the new part of the screen buffer, unless fullredraw is set to true + if(shouldRebuildChannelList) { + shouldRebuildChannelList = false; + lfpDisplay->rebuildDrawableChannelsList(); // calls resized()/refresh() after rebuilding list + } + else { + lfpDisplay->refresh(); // redraws only the new part of the screen buffer, unless fullredraw is set to true + } } void LfpDisplaySplitter::comboBoxChanged(juce::ComboBox *comboBox) diff --git a/Plugins/LfpDisplayNode/LfpDisplayCanvas.h b/Plugins/LfpDisplayNode/LfpDisplayCanvas.h index 9b5d9aae4..884a81edc 100644 --- a/Plugins/LfpDisplayNode/LfpDisplayCanvas.h +++ b/Plugins/LfpDisplayNode/LfpDisplayCanvas.h @@ -340,6 +340,11 @@ class LfpDisplaySplitter : public Component, uint16 selectedStreamId; void refreshScreenBuffer(); + + bool shouldRebuildChannelList = false; + + void setFilteredChannels(Array channels){filteredChannels = channels;} + Array getFilteredChannels(){return filteredChannels;} private: @@ -381,6 +386,8 @@ class LfpDisplaySplitter : public Component, int displayBufferSize; int scrollBarThickness; + + Array filteredChannels = Array(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LfpDisplaySplitter); diff --git a/Plugins/LfpDisplayNode/LfpDisplayNode.cpp b/Plugins/LfpDisplayNode/LfpDisplayNode.cpp index d4d7cd1c7..03d37e403 100644 --- a/Plugins/LfpDisplayNode/LfpDisplayNode.cpp +++ b/Plugins/LfpDisplayNode/LfpDisplayNode.cpp @@ -91,13 +91,14 @@ void LfpDisplayNode::updateSettings() displayBufferMap[streamId]->sampleRate = channel->getSampleRate(); displayBufferMap[streamId]->name = name; } - +// displayBufferMap[streamId]->addChannel(channel->getName(), // name ch, // index channel->getChannelType(), // type channel->isRecorded, 0, // group - channel->position.y // ypos + channel->position.y, // ypos + channel-> getDescription() ); } @@ -179,6 +180,11 @@ bool LfpDisplayNode::stopAcquisition() LfpDisplayEditor* editor = (LfpDisplayEditor*) getEditor(); editor->disable(); + for(auto split : splitDisplays) { + Array emptyArray = Array(); + split -> setFilteredChannels(emptyArray); + } + for (auto buffer : displayBuffers) buffer->ttlState = 0; @@ -327,3 +333,62 @@ void LfpDisplayNode::acknowledgeTrigger(int id) { latestTrigger.set(id, -1); } + +bool LfpDisplayNode::getIntField(DynamicObject::Ptr payload, String name, int& value, int lowerBound, int upperBound) { + if(!payload->hasProperty(name) || !payload->getProperty(name).isInt()) + return false; + int tempVal = payload->getProperty(name); + if((upperBound != INT_MIN && tempVal > upperBound) || (lowerBound != INT_MAX && tempVal < lowerBound)) + return false; + value = tempVal; + return true; +} + + +void LfpDisplayNode::handleBroadcastMessage(String msg) { + var parsedMessage = JSON::parse(msg); + if(!parsedMessage.isObject()) + return; + DynamicObject::Ptr jsonMessage = parsedMessage.getDynamicObject(); + if(jsonMessage == nullptr) + return; + String pluginName= jsonMessage -> getProperty("plugin"); + if(pluginName != "LFPViewer") { + return; + } + String command = jsonMessage -> getProperty("command"); + DynamicObject::Ptr payload = jsonMessage -> getProperty("payload").getDynamicObject(); + if(command == "filter") { + if(payload.get() == nullptr){ + LOGD("Tried to filter in LFPViewer, but could not find a payload"); + return; + } + int split, start, rows, cols, colsPerRow, end; + if(!getIntField(payload, "split", split, 0, 2) || !getIntField(payload, "start", start, 0)) { + LOGD("Tried to filter in LFPViewer, but a valid split and start weren't provided"); + return; + } + Array channelNames; + //If an end is specificed add channels from start to end + //Else calculate the rectangular selection based on rows and columns + if(getIntField(payload, "end", end, 0)) { + for(int index = 0; index < (end - start); index++) { + channelNames.add(start + index); + } + } + else { + if(!getIntField(payload, "rows", rows, 0) || !getIntField(payload, "cols", cols, 0) || !getIntField(payload, "colsPerRow", colsPerRow, 0)) { + LOGD("Tried to filter by rectangular selection in LFPViewer, but valid row/column/columnsPerRow counts weren't provided"); + return; + } + for(int row = 0; row < rows; row++) { + for(int col = 0; col < cols; col++) { + channelNames.add(start + col + row*colsPerRow); + } + } + } + splitDisplays[split] -> setFilteredChannels(channelNames); + splitDisplays[split] -> shouldRebuildChannelList = true; + } +} + diff --git a/Plugins/LfpDisplayNode/LfpDisplayNode.h b/Plugins/LfpDisplayNode/LfpDisplayNode.h index f7c738cfc..f357cc06b 100644 --- a/Plugins/LfpDisplayNode/LfpDisplayNode.h +++ b/Plugins/LfpDisplayNode/LfpDisplayNode.h @@ -100,6 +100,12 @@ class LfpDisplayNode : public GenericProcessor /** Acknowledges receipt of a trigger for a given split display*/ void acknowledgeTrigger(int splitId); + /** Reads from int value from payload, returns if the value was found and is within bounds*/ + bool getIntField(DynamicObject::Ptr payload, String name, int& value, int lowerBound = INT_MAX, int upperBound = INT_MIN); + + /** Handles messages from other processors during acquisition*/ + void handleBroadcastMessage(String msg) override; + private: /** Initializes trigger channels within a process block*/