ProcessorGraph.cpp 13.96 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/FilterViewport.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(UI);
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);
}
}
// std::cout << "Clearing nodes..." << std::endl;
// for (int i = 0; i < getNumNodes(); i++)
// {
// Node* node = getNode(i);
// int id = node->nodeId;
// if (!(id == RECORD_NODE_ID || id == AUDIO_NODE_ID ||
// id == OUTPUT_NODE_ID || id == RESAMPLING_NODE_ID))
// {
// removeNode(id);
// }
// }
// std::cout << "Remaining nodes: " << std::endl;
// for (int i = 0; i < getNumNodes(); i++)
// {
// Node* node = getNode(i);
// std::cout << " " << node->getProcessor()->getName() << std::endl;
// }
// std::cout << std::endl;
}
void ProcessorGraph::updateConnections(Array<SignalChainTabButton*, CriticalSection> tabs)
{
clearConnections(); // clear processor graph
//createDefaultNodes(); // add audio and record nodes
std::cout << "Updating connections:" << std::endl;
for (int n = 0; n < tabs.size(); n++)
{
std::cout << "Signal chain " << n << std::endl;
//if (tabs[n]->hasNewConnections())
//{
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 source to the graph if it doesn't already exist
//Node* node = getNodeForId(source->getNodeId());
//if (node == 0)
// addNode(source, source->getNodeId());
// 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())
{
// add dest node to graph if it doesn't already exist
//node = getNodeForId(dest->getNodeId());
//if (node == 0)
// addNode(dest, dest->getNodeId());
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")) {
if (!doesProcessorWithSameNameExist(subProcessorType)) {
processor = new SourceNode(subProcessorType);
//} else {
}
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("Stream Viewer")) {
if (subProcessorType.equalsIgnoreCase("LFP Viewer")) {
std::cout << "Creating a display node." << std::endl;
processor = new LfpDisplayNode();
std::cout << "Graph data viewport: " << UI->getDataViewport() << std::endl;
processor->setDataViewport(UI->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::doesProcessorWithSameNameExist(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;
//config = ui->getConfiguration();
}
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(filterViewport->requestSignalChain());
std::cout << "Enabling processors..." << std::endl;
bool allClear;
if (getNumNodes() < 5)
{
UI->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->enable();
if (!allClear) {
std::cout << p->getName() << " said it's not OK." << std::endl;
sendActionMessage("Could not initialize acquisition.");
UI->disableCallbacks();
return false;
}
}
}
filterViewport->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();
allClear = p->disable();
if (!allClear) {
sendActionMessage("Could not stop acquisition.");
return false;
}
}
}
filterViewport->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();
}
GenericProcessor* ProcessorGraph::getSourceNode(int snID) {
std::cout << "Requested ID: " << snID << std::endl;
//if (snID != 0) {
Node* node = getNodeForId(snID);
if (node != 0) {
return (GenericProcessor*) node->getProcessor();
} else {
return 0;
}
}
void ProcessorGraph::saveState()
{
File file = File("./savedState.xml");
filterViewport->saveState(file);
}
void ProcessorGraph::loadState()
{
File file = File("./savedState.xml");
filterViewport->loadState(file);
}