-
jsiegle authored
Three important changes: - The FilterList is now the ProcessorList - The FilterViewport is now the EditorViewport - Any classes that need to access important UI objects have become subclasses of the "AccessClass". Such objects automatically obtain pointers from the UIComponent and register the MessageCenter as an ActionListener. This will make it much easier to allocate pointers to objects.
jsiegle authoredThree important changes: - The FilterList is now the ProcessorList - The FilterViewport is now the EditorViewport - Any classes that need to access important UI objects have become subclasses of the "AccessClass". Such objects automatically obtain pointers from the UIComponent and register the MessageCenter as an ActionListener. This will make it much easier to allocate pointers to objects.
ProcessorGraph.cpp 12.94 KiB
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2012 Open Ephys
------------------------------------------------------------------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "ProcessorGraph.h"
#include "AudioNode.h"
#include "LfpDisplayNode.h"
#include "EventNode.h"
#include "FileReader.h"
#include "FilterNode.h"
#include "GenericProcessor.h"
#include "RecordNode.h"
#include "ResamplingNode.h"
#include "SignalGenerator.h"
#include "SourceNode.h"
#include "SpikeDetector.h"
#include "WiFiOutput.h"
#include "Utilities/Splitter.h"
#include "../UI/UIComponent.h"
#include "../UI/Configuration.h"
#include "../UI/EditorViewport.h"
ProcessorGraph::ProcessorGraph() :
currentNodeId(100),
RECORD_NODE_ID(199),
AUDIO_NODE_ID(200),
OUTPUT_NODE_ID(201),
RESAMPLING_NODE_ID(202),
totalAudioConnections(0),
totalRecordConnections(0)
{
// ProcessorGraph will always have 0 inputs (all content is generated within graph)
// but it will have N outputs, where N is the number of channels for the audio monitor
setPlayConfigDetails(0, // number of inputs
2, // number of outputs
44100.0, // sampleRate
128); // blockSize
createDefaultNodes();
}
ProcessorGraph::~ProcessorGraph() { }
void ProcessorGraph::createDefaultNodes()
{
// add output node -- sends output to the audio card
AudioProcessorGraph::AudioGraphIOProcessor* on =
new AudioProcessorGraph::AudioGraphIOProcessor(AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode);
// add record node -- sends output to disk
RecordNode* recn = new RecordNode();
recn->setNodeId(RECORD_NODE_ID);
//recn->setConfiguration(config);
// add audio node -- takes all inputs and selects those to be used for audio monitoring
AudioNode* an = new AudioNode();
recn->setNodeId(AUDIO_NODE_ID);
//an->setConfiguration(config);
// add resampling node -- resamples continuous signals to 44.1kHz
ResamplingNode* rn = new ResamplingNode(true);
rn->setNodeId(RESAMPLING_NODE_ID);
addNode(on,OUTPUT_NODE_ID);
addNode(recn,RECORD_NODE_ID);
addNode(an, AUDIO_NODE_ID);
addNode(rn, RESAMPLING_NODE_ID);
// connect audio network
for (int n = 0; n < 2; n++) {
addConnection(AUDIO_NODE_ID, n,
RESAMPLING_NODE_ID, n);
addConnection(RESAMPLING_NODE_ID, n,
OUTPUT_NODE_ID, n);
}
std::cout << "Default nodes created." << std::endl;
}
void* ProcessorGraph::createNewProcessor(String& description)//,
// GenericProcessor* source,
// GenericProcessor* dest)
{
GenericProcessor* processor = createProcessorFromDescription(description);
int id = currentNodeId++;
if (processor != 0) {
processor->setNodeId(id); // identifier within processor graph
std::cout << " Adding node to graph with ID number " << id << std::endl;
//processor->setFilterViewport(filterViewport);
//processor->setConfiguration(config);
//processor->addActionListener(messageCenter);
processor->setUIComponent(getUIComponent());
addNode(processor,id); // have to add it so it can be deleted by the graph
return processor->createEditor();
} else {
sendActionMessage("Not a valid processor type.");
return 0;
}
}
void ProcessorGraph::clearConnections()
{
for (int i = 0; i < getNumConnections(); i++)
{
const Connection* connection = getConnection(i);
if (connection->destNodeId == RESAMPLING_NODE_ID ||
connection->destNodeId == OUTPUT_NODE_ID)
{
; // leave it
} else {
removeConnection(i);
}
}
}
void ProcessorGraph::updateConnections(Array<SignalChainTabButton*, CriticalSection> tabs)
{
clearConnections(); // clear processor graph
std::cout << "Updating connections:" << std::endl;
for (int n = 0; n < tabs.size(); n++)
{
std::cout << "Signal chain " << n << std::endl;
GenericEditor* sourceEditor = (GenericEditor*) tabs[n]->getEditor();
GenericProcessor* source = (GenericProcessor*) sourceEditor->getProcessor();
while (source != 0)// && destEditor->isEnabled())
{
std::cout << "Source node: " << source->getName() << ", ";
GenericProcessor* dest = (GenericProcessor*) source->getDestNode();
if (dest != 0)
{
std::cout << "Dest node: " << dest->getName() << std::endl;
} else {
std::cout << "no dest node." << std::endl;
}
if (source->enabledState())
{
// add the connections to audio and record nodes if necessary
if (!(source->isSink() || source->isSource() ||
source->isSplitter() || source->isMerger()))
{
std::cout << " Connecting to audio and record nodes." << std::endl;
for (int chan = 0; chan < source->getNumOutputs(); chan++) {
addConnection(source->getNodeId(), // sourceNodeID
chan, // sourceNodeChannelIndex
AUDIO_NODE_ID, // destNodeID
getNextFreeAudioChannel()); // destNodeChannelIndex
addConnection(source->getNodeId(), // sourceNodeID
chan, // sourceNodeChannelIndex
RECORD_NODE_ID, // destNodeID
getNextFreeRecordChannel()); // destNodeChannelIndex
}
}
if (dest != 0) {
if (dest->enabledState())
std::cout << "OK." << std::endl;
else
std::cout << "Not OK." << std::endl;
if (dest->enabledState())
{
std::cout << " Connecting " << source->getName() << " channel ";
for (int chan = 0; chan < source->getNumOutputs(); chan++)
{
// eventually need to account for splitter and mergers
std::cout << chan << " ";
addConnection(source->getNodeId(), // sourceNodeID
chan, // sourceNodeChannelIndex
dest->getNodeId(), // destNodeID
chan); // destNodeChannelIndex
}
std::cout << " to " << dest->getName() << std::endl;
std::cout << " Connecting " << source->getName() <<
" event channel to " <<
dest->getName() << std::endl;
// connect event channel
addConnection(source->getNodeId(), // sourceNodeID
midiChannelIndex, // sourceNodeChannelIndex
dest->getNodeId(), // destNodeID
midiChannelIndex); // destNodeChannelIndex
}
}
}
source = dest; // switch source and dest
} // end while source != 0
} // end "tabs" for loop
} // end method
int ProcessorGraph::getNextFreeAudioChannel()
{
return totalAudioConnections++;
}
int ProcessorGraph::getNextFreeRecordChannel()
{
return totalRecordConnections++;
}
GenericProcessor* ProcessorGraph::createProcessorFromDescription(String& description)
{
int splitPoint = description.indexOf("/");
String processorType = description.substring(0,splitPoint);
String subProcessorType = description.substring(splitPoint+1);
std::cout << processorType << "::" << subProcessorType << std::endl;
GenericProcessor* processor = 0;
if (processorType.equalsIgnoreCase("Sources")) {
if (subProcessorType.equalsIgnoreCase("Intan Demo Board")) {
// only one Intan Demo Board at a time, please
if (!processorWithSameNameExists(subProcessorType)) {
processor = new SourceNode(subProcessorType);
}
std::cout << "Creating a new data source." << std::endl;
} else if (subProcessorType.equalsIgnoreCase("Signal Generator"))
{
processor = new SignalGenerator();
std::cout << "Creating a new signal generator." << std::endl;
} else if (subProcessorType.equalsIgnoreCase("Event Generator"))
{
processor = new EventNode();
std::cout << "Creating a new event node." << std::endl;
}
sendActionMessage("New source node created.");
} else if (processorType.equalsIgnoreCase("Filters")) {
if (subProcessorType.equalsIgnoreCase("Bandpass Filter")) {
std::cout << "Creating a new filter." << std::endl;
processor = new FilterNode();
} else if (subProcessorType.equalsIgnoreCase("Resampler")) {
std::cout << "Creating a new resampler." << std::endl;
processor = new ResamplingNode(false);
} else if (subProcessorType.equalsIgnoreCase("Spike Detector")) {
std::cout << "Creating a new spike detector." << std::endl;
processor = new SpikeDetector();
}
sendActionMessage("New filter node created.");
} else if (processorType.equalsIgnoreCase("Utilities")) {
if (subProcessorType.equalsIgnoreCase("Splitter")) {
std::cout << "Creating a new splitter." << std::endl;
processor = new Splitter();
sendActionMessage("New splitter created.");
}
} else if (processorType.equalsIgnoreCase("Sinks")) {
if (subProcessorType.equalsIgnoreCase("LFP Viewer")) {
std::cout << "Creating an LfpDisplayNode." << std::endl;
processor = new LfpDisplayNode();
// std::cout << "Graph data viewport: " << UI->getDataViewport() << std::endl;
// processor->setDataViewport(getDataViewport());
//processor->setUIComponent(UI);
} else if (subProcessorType.equalsIgnoreCase("WiFi Output")) {
std::cout << "Creating a WiFi node." << std::endl;
processor = new WiFiOutput();
}
sendActionMessage("New sink created.");
}
return processor;
}
bool ProcessorGraph::processorWithSameNameExists(const String& name)
{
for (int i = 0; i < getNumNodes(); i++)
{
Node* node = getNode(i);
if (name.equalsIgnoreCase(node->getProcessor()->getName()))
return true;
}
return false;
}
void ProcessorGraph::removeProcessor(GenericProcessor* processor) {
std::cout << "Removing processor with ID " << processor->getNodeId() << std::endl;
removeNode(processor->getNodeId());
}
// void ProcessorGraph::setUIComponent(UIComponent* ui)
// {
// UI = ui;
// }
// void ProcessorGraph::setFilterViewport(FilterViewport* fv)
// {
// filterViewport = fv;
// }
// void ProcessorGraph::setMessageCenter(MessageCenter* mc)
// {
// messageCenter = mc;
// }
// void ProcessorGraph::setConfiguration(Configuration* c)
// {
// config = c;
// }
bool ProcessorGraph::enableProcessors() {
updateConnections(getEditorViewport()->requestSignalChain());
std::cout << "Enabling processors..." << std::endl;
bool allClear;
if (getNumNodes() < 5)
{
getUIComponent()->disableCallbacks();
return false;
}
for (int i = 0; i < getNumNodes(); i++)
{
Node* node = getNode(i);
if (node->nodeId != OUTPUT_NODE_ID)
{
GenericProcessor* p = (GenericProcessor*) node->getProcessor();
allClear = p->isReady();
if (!allClear) {
std::cout << p->getName() << " said it's not OK." << std::endl;
sendActionMessage("Could not initialize acquisition.");
getUIComponent()->disableCallbacks();
return false;
}
}
}
for (int i = 0; i < getNumNodes(); i++)
{
Node* node = getNode(i);
if (node->nodeId != OUTPUT_NODE_ID)
{
GenericProcessor* p = (GenericProcessor*) node->getProcessor();
p->enable();
}
}
getEditorViewport()->signalChainCanBeEdited(false);
sendActionMessage("Acquisition started.");
return true;
}
bool ProcessorGraph::disableProcessors() {
std::cout << "Disabling processors..." << std::endl;
bool allClear;
for (int i = 0; i < getNumNodes(); i++)
{
Node* node = getNode(i);
if (node->nodeId != OUTPUT_NODE_ID)
{
GenericProcessor* p = (GenericProcessor*) node->getProcessor();
std::cout << "Disabling " << p->getName() << std::endl;
allClear = p->disable();
if (!allClear) {
sendActionMessage("Could not stop acquisition.");
return false;
}
}
}
getEditorViewport()->signalChainCanBeEdited(true);
sendActionMessage("Acquisition ended.");
return true;
}
AudioNode* ProcessorGraph::getAudioNode() {
Node* node = getNodeForId(AUDIO_NODE_ID);
return (AudioNode*) node->getProcessor();
}
RecordNode* ProcessorGraph::getRecordNode() {
Node* node = getNodeForId(RECORD_NODE_ID);
return (RecordNode*) node->getProcessor();
}
void ProcessorGraph::saveState()
{
File file = File("./savedState.xml");
getEditorViewport()->saveState(file);
}
void ProcessorGraph::loadState()
{
File file = File("./savedState.xml");
getEditorViewport()->loadState(file);
}