From 9aaae25c69166acfe05ecdd361410f0ac7bb0689 Mon Sep 17 00:00:00 2001 From: jsiegle <jsiegle@mit.edu> Date: Sat, 18 Jan 2014 16:31:04 -0500 Subject: [PATCH] Merge GraphViewer code from spikesorting branch --- Builds/MacOSX/Info.plist | 4 +- Builds/VisualStudio2012/resources.rc | 6 +- JuceLibraryCode/JuceHeader.h | 4 +- Source/Processors/Channel.cpp | 3 +- Source/Processors/Channel.h | 2 +- Source/Processors/Editors/GenericEditor.cpp | 115 +++++++++++++--- Source/Processors/Editors/GenericEditor.h | 32 ++++- Source/Processors/Editors/MergerEditor.cpp | 27 +++- Source/Processors/Editors/MergerEditor.h | 2 + Source/Processors/Editors/SplitterEditor.cpp | 9 +- Source/Processors/GenericProcessor.cpp | 100 +++++++++++--- Source/Processors/GenericProcessor.h | 23 +++- Source/Processors/Utilities/Merger.cpp | 63 ++++++++- Source/Processors/Utilities/Merger.h | 3 + Source/Processors/Utilities/Splitter.cpp | 10 +- Source/UI/ControlPanel.cpp | 15 ++- Source/UI/ControlPanel.h | 28 ++-- Source/UI/GraphViewer.cpp | 132 ++++++++++++++----- Source/UI/GraphViewer.h | 12 +- Source/UI/UIComponent.cpp | 19 ++- Source/UI/UIComponent.h | 9 +- open-ephys.jucer | 2 +- 22 files changed, 502 insertions(+), 118 deletions(-) diff --git a/Builds/MacOSX/Info.plist b/Builds/MacOSX/Info.plist index 259599932..e8a1d2714 100644 --- a/Builds/MacOSX/Info.plist +++ b/Builds/MacOSX/Info.plist @@ -16,9 +16,9 @@ <key>CFBundleSignature</key> <string>????</string> <key>CFBundleShortVersionString</key> - <string>0.2.0</string> + <string>0.2.1</string> <key>CFBundleVersion</key> - <string>0.2.0</string> + <string>0.2.1</string> <key>NSHumanReadableCopyright</key> <string>Open Ephys</string> <key>NSHighResolutionCapable</key> diff --git a/Builds/VisualStudio2012/resources.rc b/Builds/VisualStudio2012/resources.rc index bae929af0..94a64eb84 100644 --- a/Builds/VisualStudio2012/resources.rc +++ b/Builds/VisualStudio2012/resources.rc @@ -7,7 +7,7 @@ #include <windows.h> VS_VERSION_INFO VERSIONINFO -FILEVERSION 0,2,0,0 +FILEVERSION 0,2,1,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -15,9 +15,9 @@ BEGIN BEGIN VALUE "CompanyName", "Open Ephys\0" VALUE "FileDescription", "open-ephys\0" - VALUE "FileVersion", "0.2.0\0" + VALUE "FileVersion", "0.2.1\0" VALUE "ProductName", "open-ephys\0" - VALUE "ProductVersion", "0.2.0\0" + VALUE "ProductVersion", "0.2.1\0" END END diff --git a/JuceLibraryCode/JuceHeader.h b/JuceLibraryCode/JuceHeader.h index 127ae06a1..c740f3b37 100644 --- a/JuceLibraryCode/JuceHeader.h +++ b/JuceLibraryCode/JuceHeader.h @@ -39,8 +39,8 @@ namespace ProjectInfo { const char* const projectName = "open-ephys"; - const char* const versionString = "0.2.0"; - const int versionNumber = 0x200; + const char* const versionString = "0.2.1"; + const int versionNumber = 0x201; } #endif // __APPHEADERFILE_YNSYIRR__ diff --git a/Source/Processors/Channel.cpp b/Source/Processors/Channel.cpp index 3232d6413..d94ff8451 100644 --- a/Source/Processors/Channel.cpp +++ b/Source/Processors/Channel.cpp @@ -24,7 +24,7 @@ #include "Channel.h" -Channel::Channel(GenericProcessor* p, int n) : num(n), eventType(0), processor(p), sampleRate(44100.0), bitVolts(1.0f), isEventChannel(false), isMonitored(false), isEnabled(true), isRecording(false) +Channel::Channel(GenericProcessor* p, int n) : num(n), eventType(0), processor(p), sampleRate(44100.0), bitVolts(1.0f), isADCchannel(false),isEventChannel(false), isMonitored(false), isEnabled(true), isRecording(false) { nodeId = p->getNodeId(); @@ -38,6 +38,7 @@ Channel::Channel(const Channel& ch) isEventChannel = ch.isEventChannel; isEnabled = ch.isEnabled; isMonitored = false; + isADCchannel = ch.isADCchannel; sampleRate = ch.sampleRate; bitVolts = ch.bitVolts; name = ch.name; diff --git a/Source/Processors/Channel.h b/Source/Processors/Channel.h index 99925a800..8ec61b722 100644 --- a/Source/Processors/Channel.h +++ b/Source/Processors/Channel.h @@ -94,7 +94,7 @@ public: // boolean values: bool isEventChannel; - + bool isADCchannel; bool isMonitored; bool isEnabled; diff --git a/Source/Processors/Editors/GenericEditor.cpp b/Source/Processors/Editors/GenericEditor.cpp index ec60676fe..a06d7330e 100755 --- a/Source/Processors/Editors/GenericEditor.cpp +++ b/Source/Processors/Editors/GenericEditor.cpp @@ -33,11 +33,14 @@ #include <math.h> +#ifndef M_PI +#define M_PI 3.14159265359 +#endif GenericEditor::GenericEditor(GenericProcessor* owner, bool useDefaultParameterEditors=true) : AudioProcessorEditor(owner), desiredWidth(150), isFading(false), accumulator(0.0), acquisitionIsActive(false), - drawerButton(0), channelSelector(0), - isSelected(false), isEnabled(true), tNum(-1) + drawerButton(0), channelSelector(0),drawerWidth(170), + isSelected(false), isEnabled(true), isCollapsed(false), tNum(-1), drawerOpen(false) { constructorInitialize(owner, useDefaultParameterEditors); } @@ -109,6 +112,12 @@ void GenericEditor::constructorInitialize(GenericProcessor* owner, bool useDefau } +void GenericEditor::updateName() +{ + nodeId = getProcessor()->getNodeId(); + repaint(); +} + void GenericEditor::addParameterEditors(bool useDefaultParameterEditors=true) { if (useDefaultParameterEditors) @@ -170,11 +179,14 @@ void GenericEditor::refreshColors() void GenericEditor::resized() { - if (drawerButton != 0) - drawerButton->setBounds(getWidth()-14, 40, 10, getHeight()-60); + if (!isCollapsed) + { + if (drawerButton != 0) + drawerButton->setBounds(getWidth()-14, 40, 10, getHeight()-60); - if (channelSelector != 0) - channelSelector->setBounds(desiredWidth - drawerWidth, 30, channelSelector->getDesiredWidth(), getHeight()-45); + if (channelSelector != 0) + channelSelector->setBounds(desiredWidth - drawerWidth, 30, channelSelector->getDesiredWidth(), getHeight()-45); + } } @@ -256,7 +268,7 @@ void GenericEditor::setEnabledState(bool t) void GenericEditor::startAcquisition() { - std::cout << "GenericEditor received message to start acquisition." << std::endl; + //std::cout << "GenericEditor received message to start acquisition." << std::endl; if (channelSelector != 0) channelSelector->startAcquisition(); @@ -306,11 +318,17 @@ void GenericEditor::paint(Graphics& g) g.setColour(Colours::lightgrey); // draw colored background - g.fillRect(1,1,getWidth()-(2+offset),getHeight()-2); - - // draw gray workspace - g.setGradientFill(backgroundGradient); - g.fillRect(1,22,getWidth()-2, getHeight()-29); + if (!isCollapsed) + { + g.fillRect(1,1,getWidth()-(2+offset),getHeight()-2); + // draw gray workspace + g.setGradientFill(backgroundGradient); + g.fillRect(1,22,getWidth()-2, getHeight()-29); + } + else + { + g.fillAll(); + } g.setFont(titleFont); g.setFont(14); @@ -325,8 +343,18 @@ void GenericEditor::paint(Graphics& g) } // draw title - g.drawText(name, 6, 5, 500, 15, Justification::left, false); + if (!isCollapsed) + { + if (!getProcessor()->isMerger() && !getProcessor()->isSplitter()) + g.drawText(name+" ("+String(nodeId)+")", 6, 5, 500, 15, Justification::left, false); + else + g.drawText(name, 6, 5, 500, 15, Justification::left, false); + } else { + g.addTransform(AffineTransform::rotation(-M_PI/2.0)); + g.drawText(name, -getHeight()+6, 5, 500, 15, Justification::left, false); + g.addTransform(AffineTransform::rotation(M_PI/2.0)); + } if (isSelected) { @@ -389,6 +417,7 @@ bool GenericEditor::checkDrawerButton(Button* button) drawerWidth = channelSelector->getDesiredWidth() + 20; desiredWidth += drawerWidth; + drawerOpen = true; } else @@ -397,6 +426,7 @@ bool GenericEditor::checkDrawerButton(Button* button) channelSelector->setVisible(false); desiredWidth -= drawerWidth; + drawerOpen = false; } getEditorViewport()->makeEditorVisible(this); @@ -532,17 +562,67 @@ void GenericEditor::setChannelSelectionState(int chan, bool p, bool r, bool a) } } +bool GenericEditor::getCollapsedState() +{ + return isCollapsed; +} + +void GenericEditor::switchCollapsedState() +{ + + if (!getProcessor()->isMerger() && !getProcessor()->isSplitter()) + { + + if (isCollapsed) + { + // open it up + desiredWidth = originalWidth; + isCollapsed = false; + + } else { + originalWidth = desiredWidth; + desiredWidth = 25; + isCollapsed = true; + } + + for (int i = 0; i < getNumChildComponents(); i++) + { + Component* c = getChildComponent(i); + c->setVisible(!isCollapsed); + } + + if (channelSelector != nullptr) + { + if (!drawerOpen) + channelSelector->setVisible(false); + } + + collapsedStateChanged(); + + getEditorViewport()->refreshEditors(); + } +} + void GenericEditor::saveEditorParameters(XmlElement* xml) { - //xml->setAttribute("Attribute", "WHAT"); + xml->setAttribute("isCollapsed", isCollapsed); + + saveCustomParameters(xml); } void GenericEditor::loadEditorParameters(XmlElement* xml) { - //xml->setAttribute("Attribute", "WHAT"); + bool isCollapsed = xml->getBoolAttribute("isCollapsed", false); + + if (isCollapsed) + { + switchCollapsedState(); + } + + loadCustomParameters(xml); } @@ -796,6 +876,11 @@ void UtilityButton::resized() } +String UtilityButton::getLabel() +{ + return label; +} + void UtilityButton::setLabel(String label_) { label = label_; diff --git a/Source/Processors/Editors/GenericEditor.h b/Source/Processors/Editors/GenericEditor.h index 105436eb8..18a459a4b 100755 --- a/Source/Processors/Editors/GenericEditor.h +++ b/Source/Processors/Editors/GenericEditor.h @@ -120,6 +120,9 @@ public: return name; } + /** Updates name if processor ID changes. */ + void updateName(); + /** Determines how wide the editor will be drawn. */ int desiredWidth; @@ -247,14 +250,29 @@ public: void setChannelSelectionState(int chan, bool p, bool r, bool a); /** Writes editor state to xml */ - virtual void saveEditorParameters(XmlElement* xml); + void saveEditorParameters(XmlElement* xml); + + /** Writes editor state to xml */ + void loadEditorParameters(XmlElement* xml); /** Writes editor state to xml */ - virtual void loadEditorParameters(XmlElement* xml); + virtual void saveCustomParameters(XmlElement* xml) { } + + /** Writes editor state to xml */ + virtual void loadCustomParameters(XmlElement* xml) { } /** Syncs parametereditor colors with parameter values */ void updateParameterButtons(int parameterIndex = -1); - + + /** Checks to see whether or not an editor is collapsed */ + bool getCollapsedState(); + + /** Collapses an editor if it's open, and opens it if it's collpased*/ + void switchCollapsedState(); + + /** Notifies the editor that the collapsed state changed, for non-standard function. */ + virtual void collapsedStateChanged() {} + /** Returns the editor of this processor's source */ GenericEditor* getSourceEditor(); @@ -262,7 +280,7 @@ public: GenericEditor* getDestEditor(); /** Returns the editors a splitter or merger is connected to */ - virtual Array<GenericEditor*> getConnectedEditors() { } + virtual Array<GenericEditor*> getConnectedEditors(){ Array<GenericEditor*> a; return a;} protected: @@ -272,6 +290,9 @@ protected: /** Determines the width of the ChannelSelector drawer when opened. */ int drawerWidth; + /** Saves the open/closed state of the ChannelSelector drawer. */ + bool drawerOpen; + /** Can be overridden to customize the layout of ParameterEditors. */ //Ideally this would be virtual, but since it's run in the construct and because virtual functions don't get overriden in the constructor, it's not. @@ -299,11 +320,13 @@ private: bool isSelected; bool isEnabled; + bool isCollapsed; /**Used to determine if an editor is a splitter or Merger to avoid calling on CHannelSelector*/ bool isSplitOrMerge; int tNum; + int originalWidth; /**initializing function Used to share constructor functions*/ void constructorInitialize(GenericProcessor* owner, bool useDefaultParameterEditors); @@ -382,6 +405,7 @@ public: } void setLabel(String label); + String getLabel(); private: void paintButton(Graphics& g, bool isMouseOver, bool isButtonDown); diff --git a/Source/Processors/Editors/MergerEditor.cpp b/Source/Processors/Editors/MergerEditor.cpp index bcfcd905d..c56fbcbe5 100755 --- a/Source/Processors/Editors/MergerEditor.cpp +++ b/Source/Processors/Editors/MergerEditor.cpp @@ -57,7 +57,7 @@ MergerEditor::MergerEditor(GenericProcessor* parentNode, bool useDefaultParamete : GenericEditor(parentNode, useDefaultParameterEditors) { - desiredWidth = 90; + desiredWidth = 85; pipelineSelectorA = new ImageButton("Pipeline A"); @@ -114,6 +114,8 @@ void MergerEditor::buttonEvent(Button* button) processor->switchIO(1); } + + getEditorViewport()->makeEditorVisible(this, false); } void MergerEditor::mouseDown(const MouseEvent& e) @@ -158,6 +160,8 @@ void MergerEditor::mouseDown(const MouseEvent& e) processor->setMergerSourceNode(availableProcessors[result-2]); availableProcessors[result-2]->setDestNode(getProcessor()); + getGraphViewer()->updateNodeLocations(); + getEditorViewport()->makeEditorVisible(this, false, true); } } @@ -186,6 +190,27 @@ void MergerEditor::switchSource(int source) } } +Array<GenericEditor*> MergerEditor::getConnectedEditors() +{ + + Array<GenericEditor*> editors; + + Merger* processor = (Merger*) getProcessor(); + + for (int pathNum = 0; pathNum < 2; pathNum++) + { + processor->switchIO(); + + if (processor->getSourceNode() != nullptr) + editors.add(processor->getSourceNode()->getEditor()); + else + editors.add(nullptr); + } + + return editors; + +} + int MergerEditor::getPathForEditor(GenericEditor* editor) { Merger* processor = (Merger*) getProcessor(); diff --git a/Source/Processors/Editors/MergerEditor.h b/Source/Processors/Editors/MergerEditor.h index edcf05efe..654889677 100755 --- a/Source/Processors/Editors/MergerEditor.h +++ b/Source/Processors/Editors/MergerEditor.h @@ -54,6 +54,8 @@ public: int getPathForEditor(GenericEditor* editor); + Array<GenericEditor*> getConnectedEditors(); + private: ImageButton* pipelineSelectorA; diff --git a/Source/Processors/Editors/SplitterEditor.cpp b/Source/Processors/Editors/SplitterEditor.cpp index 1e360b7b8..1d750f4f5 100755 --- a/Source/Processors/Editors/SplitterEditor.cpp +++ b/Source/Processors/Editors/SplitterEditor.cpp @@ -23,6 +23,7 @@ #include "SplitterEditor.h" #include "../Utilities/Splitter.h" +#include "../../UI/EditorViewport.h" // PipelineSelectorButton::PipelineSelectorButton() // : DrawableButton ("Selector", DrawableButton::ImageFitted) @@ -55,7 +56,7 @@ SplitterEditor::SplitterEditor(GenericProcessor* parentNode, bool useDefaultPara : GenericEditor(parentNode, useDefaultParameterEditors) { - desiredWidth = 90; + desiredWidth = 85; pipelineSelectorA = new ImageButton("Pipeline A"); @@ -103,6 +104,8 @@ void SplitterEditor::buttonEvent(Button* button) Splitter* processor = (Splitter*) getProcessor(); processor->switchIO(0); + getEditorViewport()->makeEditorVisible(this, false); + } else if (button == pipelineSelectorB) { @@ -111,6 +114,8 @@ void SplitterEditor::buttonEvent(Button* button) Splitter* processor = (Splitter*) getProcessor(); processor->switchIO(1); + getEditorViewport()->makeEditorVisible(this, false); + } } @@ -132,6 +137,8 @@ void SplitterEditor::switchDest(int dest) processor->switchIO(1); } + + getEditorViewport()->makeEditorVisible(this, false); } void SplitterEditor::switchIO(int dest) diff --git a/Source/Processors/GenericProcessor.cpp b/Source/Processors/GenericProcessor.cpp index c4689864b..ef13adae8 100755 --- a/Source/Processors/GenericProcessor.cpp +++ b/Source/Processors/GenericProcessor.cpp @@ -27,8 +27,9 @@ GenericProcessor::GenericProcessor(const String& name_) : AccessClass(), sourceNode(0), destNode(0), isEnabled(true), wasConnected(false), nextAvailableChannel(0), saveOrder(-1), loadOrder(-1), currentChannel(-1), - parametersAsXml(nullptr), name(name_), paramsWereLoaded(false) + parametersAsXml(nullptr), name(name_), paramsWereLoaded(false), editor(0), sendSampleCount(true) { + settings.numInputs = settings.numOutputs = settings.sampleRate = 0; } GenericProcessor::~GenericProcessor() @@ -41,6 +42,17 @@ AudioProcessorEditor* GenericProcessor::createEditor() return editor; } +void GenericProcessor::setNodeId(int id) +{ + nodeId = id; + + if (editor != 0) + { + editor->updateName(); + } + +} + Parameter& GenericProcessor::getParameterByName(String name_) { // doesn't work @@ -277,6 +289,14 @@ void GenericProcessor::update() { std::cout << getName() << " updating settings." << std::endl; + // SO patched here to keep original channel names + + int oldNumChannels = channels.size(); + StringArray oldNames; + for (int k = 0; k < oldNumChannels; k++) + { + oldNames.add(channels[k]->getName()); + } clearSettings(); @@ -314,15 +334,33 @@ void GenericProcessor::update() } else { - settings.numOutputs = getDefaultNumOutputs(); settings.sampleRate = getDefaultSampleRate(); - - for (int i = 0; i < getNumOutputs(); i++) + + int numChan = getNumOutputs(); + int numADC_Chan = getDefaultADCoutputs(); + for (int i = 0; i < numChan; i++) { - Channel* ch = new Channel(this, i); + Channel* ch = new Channel(this, i ); + + // if (i < oldNumChannels) + // ch->setName(oldNames[i]); + //else if (i >= numChan-numADC_Chan) + // ch->setName("ADC"+String(1+i-(numChan-numADC_Chan))); + + if (i >= numChan-numADC_Chan) + ch->setName("ADC"+String(1+i-(numChan-numADC_Chan))); + + if (i >= numChan-numADC_Chan) + ch->isADCchannel = true; + + ch->sampleRate = getDefaultSampleRate(); - ch->bitVolts = getDefaultBitVolts(); + + // if (ch->isADCchannel) + ch->bitVolts = getDefaultBitVolts(); + // else + // ch->bitVolts = getDefaultAdcBitVolts(); // should implement this if (i < recordStatus.size()) { @@ -383,7 +421,9 @@ int GenericProcessor::getNumSamples(MidiBuffer& events) // the sample rate to change at different points in the signal chain. // - int numRead = 0; + int numRead = 0; + + //int numRead = 0; if (events.getNumEvents() > 0) { @@ -396,17 +436,22 @@ int GenericProcessor::getNumSamples(MidiBuffer& events) const uint8* dataptr; int dataSize; - int samplePosition = -5; + int samplePosition = -1; while (i.getNextEvent(dataptr, dataSize, samplePosition)) { - if (*dataptr == BUFFER_SIZE) - { + if (*dataptr == BUFFER_SIZE) + { - numRead = samplePosition; + int16 nr; + memcpy(&nr, dataptr+2, 2); - } else if (*dataptr == TTL && // a TTL event + numRead = nr; + + } else + + if (*dataptr == TTL && // a TTL event getNodeId() < 900 && // not handled by a specialized processor (e.g. AudioNode)) *(dataptr+1) > 0) // that's flagged for saving { @@ -435,14 +480,19 @@ void GenericProcessor::setNumSamples(MidiBuffer& events, int sampleIndex) // immediate source. // - uint8 data[2]; + uint8 data[4]; + + int16 si = (int16) sampleIndex; data[0] = BUFFER_SIZE; // most-significant byte data[1] = nodeId; // least-significant byte + memcpy(data+2, &si, 2); events.addEvent(data, // spike data - 2, // total bytes - sampleIndex); // sample index + 4, // total bytes + 0); // sample index + + // std::cout << "Processor " << getNodeId() << " adding a new sample count of " << sampleIndex << std::endl; } @@ -517,7 +567,8 @@ void GenericProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& event process(buffer, eventBuffer, nSamples); - setNumSamples(eventBuffer, nSamples); // adds it back, + if (sendSampleCount) + setNumSamples(eventBuffer, nSamples); // adds it back, // even if it's unchanged } @@ -631,6 +682,8 @@ void GenericProcessor::saveCustomChannelParametersToXml(XmlElement* channelInfo, void GenericProcessor::loadFromXml() { + update(); // make sure settings are updated + std::cout << "Loading parameters for " << name << std::endl; if (!paramsWereLoaded) @@ -641,6 +694,17 @@ void GenericProcessor::loadFromXml() // use parametersAsXml to restore state loadCustomParametersFromXml(); + // load editor parameters + forEachXmlChildElement(*parametersAsXml, xmlNode) + { + + if (xmlNode->hasTagName("EDITOR")) + { + getEditor()->loadEditorParameters(xmlNode); + } + + } + forEachXmlChildElement(*parametersAsXml, xmlNode) { if (xmlNode->hasTagName("CHANNEL")) @@ -654,10 +718,6 @@ void GenericProcessor::loadFromXml() loadChannelParametersFromXml(xmlNode, true); } - else if (xmlNode->hasTagName("EDITOR")) - { - getEditor()->loadEditorParameters(xmlNode); - } } } diff --git a/Source/Processors/GenericProcessor.h b/Source/Processors/GenericProcessor.h index edec0be3d..7db4f07c3 100755 --- a/Source/Processors/GenericProcessor.h +++ b/Source/Processors/GenericProcessor.h @@ -277,6 +277,11 @@ public: return 2; } + virtual int getDefaultADCoutputs() + { + return 0; + } + /** Returns the default number of volts per bit, in case a processor is a source, of the processor gain otherwise. (assumes data comes from a 16bit source)*/ virtual float getDefaultBitVolts() { @@ -302,10 +307,7 @@ public: } /** Sets the unique integer ID for a processor. */ - void setNodeId(int id) - { - nodeId = id; - } + void setNodeId(int id); /** Returns a pointer to the processor immediately preceding a given processor in the signal chain. */ GenericProcessor* getSourceNode() @@ -473,12 +475,14 @@ public: SPIKE = 4, EEG = 5, CONTINUOUS = 6, - SERIAL = 7 + NETWORK = 7, + EYE_POSITION = 8, + SERIAL = 9 }; enum eventChannelTypes { - GENERIC_EVENT = 999, + GENERIC_EVENT = 100, SINGLE_ELECTRODE = 1, STEREOTRODE = 2, TETRODE = 4 @@ -577,9 +581,15 @@ public: /** Load custom parameters for each channel. */ virtual void loadCustomChannelParametersFromXml(XmlElement* channelElement, bool isEventChannel=false); + /** handle messages from other processors */ + virtual String interProcessorCommunication(String command) { return String("OK"); }; + /** Holds loaded parameters */ XmlElement* parametersAsXml; + /** When set to false, this disables the sending of sample counts through the event buffer. */ + bool sendSampleCount; + private: /** Automatically extracts the number of samples in the buffer, then @@ -605,6 +615,7 @@ private: bool paramsWereLoaded; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GenericProcessor); }; diff --git a/Source/Processors/Utilities/Merger.cpp b/Source/Processors/Utilities/Merger.cpp index b51b8fb2d..2bf958ece 100755 --- a/Source/Processors/Utilities/Merger.cpp +++ b/Source/Processors/Utilities/Merger.cpp @@ -32,7 +32,7 @@ Merger::Merger() : GenericProcessor("Merger"), sourceNodeA(0), sourceNodeB(0), activePath(0)//, tabA(-1), tabB(-1) { - + sendSampleCount = false; } Merger::~Merger() @@ -64,6 +64,11 @@ void Merger::setMergerSourceNode(GenericProcessor* sn) sourceNodeB = sn; std::cout << "Setting source node B." << std::endl; } + + if (sn != nullptr) + { + sn->setDestNode(this); + } } void Merger::switchIO(int sourceNum) @@ -84,7 +89,7 @@ void Merger::switchIO(int sourceNum) //std::cout << "Source node: " << getSourceNode() << std::endl; } - getEditorViewport()->makeEditorVisible((GenericEditor*) getEditor(), false); + // getEditorViewport()->makeEditorVisible((GenericEditor*) getEditor(), false); } @@ -191,6 +196,60 @@ void Merger::updateSettings() } +void Merger::saveCustomParametersToXml(XmlElement* parentElement) +{ + XmlElement* mainNode = parentElement->createNewChildElement("MERGER"); + if (sourceNodeA!= nullptr) + mainNode->setAttribute("NodeA", sourceNodeA->getNodeId()); + else + mainNode->setAttribute("NodeA", -1); + + if (sourceNodeB != nullptr) + mainNode->setAttribute("NodeB", sourceNodeB->getNodeId()); + else + mainNode->setAttribute("NodeB", -1); +} + + +void Merger::loadCustomParametersFromXml() +{ + if (1) + { + if (parametersAsXml != nullptr) + { + forEachXmlChildElement(*parametersAsXml, mainNode) + { + if (mainNode->hasTagName("MERGER")) + { + int NodeAid = mainNode->getIntAttribute("NodeA"); + int NodeBid = mainNode->getIntAttribute("NodeB"); + + ProcessorGraph *gr = getProcessorGraph(); + Array<GenericProcessor*> p = gr->getListOfProcessors(); + + for (int k = 0; k < p.size(); k++) + { + if (p[k]->getNodeId() == NodeAid) + { + std::cout << "Setting Merger source A to " << NodeAid << std::endl; + switchIO(0); + setMergerSourceNode(p[k]); + } + if (p[k]->getNodeId() == NodeBid) + { + std::cout << "Setting Merger source B to " << NodeBid << std::endl; + switchIO(1); + setMergerSourceNode(p[k]); + } + } + + updateSettings(); + } + } + } +} +} + // void Merger::setNumOutputs(int /*outputs*/) // { // numOutputs = 0; diff --git a/Source/Processors/Utilities/Merger.h b/Source/Processors/Utilities/Merger.h index 069b2da50..9f0ccd674 100755 --- a/Source/Processors/Utilities/Merger.h +++ b/Source/Processors/Utilities/Merger.h @@ -67,6 +67,9 @@ public: void addSettingsFromSourceNode(GenericProcessor* sn); bool stillHasSource(); + + void saveCustomParametersToXml(XmlElement* parentElement); + void loadCustomParametersFromXml(); private: diff --git a/Source/Processors/Utilities/Splitter.cpp b/Source/Processors/Utilities/Splitter.cpp index d1854748d..1105d3251 100755 --- a/Source/Processors/Utilities/Splitter.cpp +++ b/Source/Processors/Utilities/Splitter.cpp @@ -30,7 +30,7 @@ Splitter::Splitter() : GenericProcessor("Splitter"), destNodeA(0), destNodeB(0), activePath(0) { - + sendSampleCount = false; } Splitter::~Splitter() @@ -83,22 +83,22 @@ void Splitter::setSplitterDestNode(GenericProcessor* dn) void Splitter::switchIO(int destNum) { - std::cout << "Switching to dest number " << destNum << std::endl; + //std::cout << "Switching to dest number " << destNum << std::endl; activePath = destNum; if (destNum == 0) { destNode = destNodeA; - std::cout << "Dest node: " << getDestNode() << std::endl; + // std::cout << "Dest node: " << getDestNode() << std::endl; } else { destNode = destNodeB; - std::cout << "Dest node: " << getDestNode() << std::endl; + // std::cout << "Dest node: " << getDestNode() << std::endl; } - getEditorViewport()->makeEditorVisible(getEditor(), false); + // getEditorViewport()->makeEditorVisible(getEditor(), false); } diff --git a/Source/UI/ControlPanel.cpp b/Source/UI/ControlPanel.cpp index d1bc5eac4..e600b51a4 100755 --- a/Source/UI/ControlPanel.cpp +++ b/Source/UI/ControlPanel.cpp @@ -759,8 +759,7 @@ void ControlPanel::disableCallbacks() void ControlPanel::timerCallback() { //std::cout << "Message Received." << std::endl; - - refreshMeters(); + refreshMeters(); } @@ -869,4 +868,16 @@ void ControlPanel::loadStateFromXml(XmlElement* xml) getProcessorGraph()->getAudioNode()->updateBufferSize(); +} + + +StringArray ControlPanel::getRecentlyUsedFilenames() +{ + return filenameComponent->getRecentlyUsedFilenames(); +} + + +void ControlPanel::setRecentlyUsedFilenames(const StringArray& filenames) +{ + filenameComponent->setRecentlyUsedFilenames(filenames); } \ No newline at end of file diff --git a/Source/UI/ControlPanel.h b/Source/UI/ControlPanel.h index 2b106f3f7..86f24bf22 100755 --- a/Source/UI/ControlPanel.h +++ b/Source/UI/ControlPanel.h @@ -32,8 +32,7 @@ #include "CustomLookAndFeel.h" #include "../AccessClass.h" #include "../Processors/Editors/GenericEditor.h" // for UtilityButton -#include "../Processors/Visualization/OpenGLCanvas.h" - +#include <queue> /** @@ -303,7 +302,6 @@ public: /** Used to manually turn recording on and off.*/ void setRecordState(bool isRecording); - /** Returns a boolean that indicates whether or not the FilenameComponet is visible. */ bool isOpen() @@ -328,10 +326,25 @@ public: /** Load settings. */ void loadStateFromXml(XmlElement*); + + void handleIncomdingMessages(); + + /** Informs the Control Panel that recording has begun.*/ + void startRecording(); + + /** Informs the Control Panel that recording has stopped.*/ + void stopRecording(); + + /** Returns a list of recently used directories for saving data. */ + StringArray getRecentlyUsedFilenames(); + /** Sets the list of recently used directories for saving data. */ + void setRecentlyUsedFilenames(const StringArray& filenames); + + ScopedPointer<RecordButton> recordButton; private: ScopedPointer<PlayButton> playButton; - ScopedPointer<RecordButton> recordButton; + ScopedPointer<Clock> masterClock; ScopedPointer<CPUMeter> cpuMeter; ScopedPointer<DiskSpaceMeter> diskMeter; @@ -352,12 +365,6 @@ private: void resized(); void buttonClicked(Button* button); - - /** Informs the Control Panel that recording has begun.*/ - void startRecording(); - - /** Informs the Control Panel that recording has stopped.*/ - void stopRecording(); bool initialize; @@ -372,6 +379,7 @@ private: bool keyPressed(const KeyPress& key); + Font font; bool open; diff --git a/Source/UI/GraphViewer.cpp b/Source/UI/GraphViewer.cpp index ca7e52b11..fc3da28cd 100644 --- a/Source/UI/GraphViewer.cpp +++ b/Source/UI/GraphViewer.cpp @@ -27,7 +27,7 @@ GraphViewer::GraphViewer() { labelFont = Font("Paragraph", 50, Font::plain); - + rootNum = 0; } GraphViewer::~GraphViewer() @@ -41,7 +41,7 @@ void GraphViewer::addNode(GenericEditor* editor) GraphNode* gn = new GraphNode(editor, this); addAndMakeVisible(gn); availableNodes.add(gn); - + gn->setBounds(20, 20, 150, 50); updateNodeLocations(); } @@ -69,40 +69,98 @@ void GraphViewer::updateNodeLocations() // set the initial locations for (int i = 0; i < availableNodes.size(); i++) { - availableNodes[i]->updateBoundaries(); + } - - // perform checks - //checkLayout(); // not helpful...yet - + + rootNum = 0; + + // do initial layout + for (int i = 0; i < availableNodes.size(); i++) + { + checkLayout(availableNodes[i]); + } + + // check for overlap + for (int i = 0; i < availableNodes.size(); i++) + { + for (int j = 0; j < availableNodes.size(); j++) + { + if (j != i) + { + if (availableNodes[j]->getLevel() == availableNodes[i]->getLevel() && + availableNodes[j]->getHorzShift() == availableNodes[i]->getHorzShift()) + { + availableNodes[j]->setHorzShift(availableNodes[j]->getHorzShift()+1); + } + } + } + } + repaint(); } -void GraphViewer::checkLayout() +void GraphViewer::checkLayout(GraphNode* gn) { - - for (int i = 0; i < availableNodes.size(); i++) + + if (gn != nullptr) { - int sourceIndex = indexOfEditor(availableNodes[i]->getSource()); - - if (sourceIndex > -1) + + GraphNode* sourceNode; + + if (gn->isMerger()) { - if (availableNodes[i]->getHorzShift() < availableNodes[sourceIndex]->getHorzShift()) + Array<GenericEditor*> editors = gn->getConnectedEditors(); + + int level1 = 0; + int level2 = 0; + + if (editors[0] != nullptr) + { + level1 = getNodeForEditor(editors[0])->getLevel(); + } + + if (editors[1] != nullptr) { - availableNodes[i]->setHorzShift(availableNodes[sourceIndex]->getHorzShift()); + level2 = getNodeForEditor(editors[1])->getLevel(); } + + // std::cout << "LEVEL1 = " << level1 << " LEVEL2 = " << level2 << std::endl; + + sourceNode = level1 > level2 ? getNodeForEditor(editors[0]) : + getNodeForEditor(editors[1]); // choose the higher source + + } else { + sourceNode = getNodeForEditor(gn->getSource()); } - - int destIndex = indexOfEditor(availableNodes[i]->getDest()); - - if (destIndex > -1) + + if (sourceNode == nullptr) { - if (availableNodes[i]->getLevel() > availableNodes[destIndex]->getLevel()) + gn->setLevel(0); + gn->setHorzShift(rootNum); + rootNum++; + } else if (sourceNode->isSplitter()) + { + Array<GenericEditor*> editors = sourceNode->getConnectedEditors(); + + if (gn->hasEditor(editors[1])) { - availableNodes[destIndex]->setLevel(availableNodes[i]->getLevel()+1); + gn->setLevel(sourceNode->getLevel()+1); // increase level + gn->setHorzShift(sourceNode->getHorzShift()+1); // increase horz shift + } else { + gn->setLevel(sourceNode->getLevel()+1); // increase level + gn->setHorzShift(sourceNode->getHorzShift()); // same horz shift } + + } else { + + gn->setLevel(sourceNode->getLevel()+1); // increase level + gn->setHorzShift(sourceNode->getHorzShift()); // same horz shift } + + checkLayout(getNodeForEditor(gn->getDest())); + } + } int GraphViewer::indexOfEditor(GenericEditor* editor) @@ -120,6 +178,16 @@ int GraphViewer::indexOfEditor(GenericEditor* editor) return index; } +GraphNode* GraphViewer::getNodeForEditor(GenericEditor* editor) +{ + int index = indexOfEditor(editor); + + if (index > -1) + return availableNodes[index]; + else + return nullptr; +} + int GraphViewer::nodesAtLevel(int level) { @@ -240,33 +308,35 @@ GraphNode::~GraphNode() int GraphNode::getLevel() { - int level = -1; + // int level = -1; - GenericEditor* ed = editor; + // GenericEditor* ed = editor; - while (ed != nullptr) - { - level += 1; - ed = ed->getSourceEditor(); - } + // while (ed != nullptr) + // { + // level += 1; + // ed = ed->getSourceEditor(); + // } - return level; + return vertShift; } void GraphNode::setLevel(int level) { - setBounds(getX(), 20+getLevel()*40, getWidth(), getHeight()); + setBounds(getX(), 20+level*40, getWidth(), getHeight()); + vertShift = level; } int GraphNode::getHorzShift() { - return gv->getHorizontalShift(this); + return horzShift; //gv->getHorizontalShift(this); } void GraphNode::setHorzShift(int shift) { setBounds(20+shift*140, getY(), getWidth(), getHeight()); + horzShift = shift; } void GraphNode::mouseEnter(const MouseEvent& m) diff --git a/Source/UI/GraphViewer.h b/Source/UI/GraphViewer.h index 1454631b6..65017ee90 100644 --- a/Source/UI/GraphViewer.h +++ b/Source/UI/GraphViewer.h @@ -71,12 +71,16 @@ public: void setLevel(int); int getHorzShift(); void setHorzShift(int); + + int horzShift; + int vertShift; private: GenericEditor* editor; Font labelFont; + bool mouseOver; @@ -97,19 +101,23 @@ public: void addNode(GenericEditor* editor); void removeNode(GenericEditor* editor); void removeAllNodes(); + void updateNodeLocations();; int nodesAtLevel(int lvl); int getHorizontalShift(GraphNode*); + GraphNode* getNodeForEditor(GenericEditor* editor); private: void connectNodes(int, int, Graphics&); - void checkLayout(); + void checkLayout(GraphNode*); + - void updateNodeLocations(); int indexOfEditor(GenericEditor* editor); Font labelFont; + + int rootNum; OwnedArray<GraphNode> availableNodes; diff --git a/Source/UI/UIComponent.cpp b/Source/UI/UIComponent.cpp index abe50fb1e..5811e0649 100755 --- a/Source/UI/UIComponent.cpp +++ b/Source/UI/UIComponent.cpp @@ -32,12 +32,11 @@ UIComponent::UIComponent(MainWindow* mainWindow_, ProcessorGraph* pgraph, AudioC processorGraph->setUIComponent(this); infoLabel = new InfoLabel(); + graphViewer = new GraphViewer(); dataViewport = new DataViewport(); addChildComponent(dataViewport); dataViewport->addTabToDataViewport("Info", infoLabel,0); - - graphViewer = new GraphViewer(); dataViewport->addTabToDataViewport("Graph", graphViewer,0); std::cout << "Created data viewport." << std::endl; @@ -124,7 +123,7 @@ void UIComponent::resized() top = 40; if (processorList->isOpen()) - left = 207; + left = 202; else left = 6; @@ -170,9 +169,9 @@ void UIComponent::resized() { if (processorList->isOpen()) if (editorViewportButton->isOpen()) - processorList->setBounds(5,5,200,h-200); + processorList->setBounds(5,5,195,h-200); else - processorList->setBounds(5,5,200,h-50); + processorList->setBounds(5,5,195,h-50); else processorList->setBounds(5,5,195,34); } @@ -504,6 +503,16 @@ void UIComponent::loadStateFromXml(XmlElement* xml) } } +StringArray UIComponent::getRecentlyUsedFilenames() +{ + return controlPanel->getRecentlyUsedFilenames(); +} + +void UIComponent::setRecentlyUsedFilenames(const StringArray& filenames) +{ + controlPanel->setRecentlyUsedFilenames(filenames); +} + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% EditorViewportButton::EditorViewportButton(UIComponent* ui) : UI(ui) diff --git a/Source/UI/UIComponent.h b/Source/UI/UIComponent.h index d0b8cc1a6..a8e5e5637 100755 --- a/Source/UI/UIComponent.h +++ b/Source/UI/UIComponent.h @@ -35,9 +35,6 @@ #include "../Processors/ProcessorGraph.h" #include "../Audio/AudioComponent.h" #include "../MainWindow.h" -#include "../Processors/Visualization/OpenGLCanvas.h" - -//#include "../OpenGL.h" class MainWindow; class ProcessorList; @@ -163,6 +160,10 @@ public: /** Load settings. */ void loadStateFromXml(XmlElement*); + StringArray getRecentlyUsedFilenames(); + + void setRecentlyUsedFilenames(const StringArray& filenames); + private: ScopedPointer<DataViewport> dataViewport; @@ -173,7 +174,7 @@ private: ScopedPointer<MessageCenter> messageCenter; ScopedPointer<InfoLabel> infoLabel; ScopedPointer<GraphViewer> graphViewer; - + /** Pointer to the GUI's MainWindow, which owns the UIComponent. */ MainWindow* mainWindow; diff --git a/open-ephys.jucer b/open-ephys.jucer index 9ab19fe82..ba5c1efba 100644 --- a/open-ephys.jucer +++ b/open-ephys.jucer @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> -<JUCERPROJECT id="ynSYIrr" name="open-ephys" projectType="guiapp" version="0.2.0" +<JUCERPROJECT id="ynSYIrr" name="open-ephys" projectType="guiapp" version="0.2.1" juceLinkage="amalg_multi" buildVST="1" buildRTAS="0" buildAU="1" pluginName="Juce Project" pluginDesc="Juce Project" pluginManufacturer="yourcompany" pluginManufacturerCode="Manu" pluginCode="Plug" pluginChannelConfigs="{1, 1}, {2, 2}" -- GitLab