-
Aaron Cuevas Lopez authoredAaron Cuevas Lopez authored
SourceNode.cpp 10.71 KiB
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2016 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 "SourceNode.h"
#include "../SourceNode/SourceNodeEditor.h"
#include <stdio.h>
#include "../../AccessClass.h"
#include "../PluginManager/OpenEphysPlugin.h"
SourceNode::SourceNode (const String& name_, DataThreadCreator dt)
: GenericProcessor (name_)
, sourceCheckInterval (2000)
, wasDisabled (true)
, dataThread (nullptr)
, ttlState (0)
{
setProcessorType (PROCESSOR_TYPE_SOURCE);
dataThread = dt (this);
if (dataThread != nullptr)
{
if (! dataThread->foundInputSource())
{
setEnabledState (false);
}
resizeBuffers();
}
else
{
setEnabledState (false);
// eventChannelState = 0;
}
// check for input source every few seconds
startTimer (sourceCheckInterval);
timestamp = 0;
}
SourceNode::~SourceNode()
{
if (dataThread->isThreadRunning())
{
std::cout << "Forcing thread to stop." << std::endl;
dataThread->stopThread (500);
}
}
//This is going to be quite slow, since is reallocating everything, but it's the
//safest way to handle a possible varying number of subprocessors
void SourceNode::resizeBuffers()
{
inputBuffers.clear();
eventCodeBuffers.clear();
eventStates.clear();
if (dataThread != nullptr)
{
int numSubProcs = dataThread->getNumSubProcessors();
for (int i = 0; i < numSubProcs; i++)
{
inputBuffers.add(dataThread->getBufferAddress(i));
eventCodeBuffers.add(new MemoryBlock(10000*sizeof(uint64)));
eventStates.add(0);
}
}
}
void SourceNode::requestChainUpdate()
{
CoreServices::updateSignalChain (getEditor());
}
void SourceNode::getEventChannelNames (StringArray& names)
{
if (dataThread != 0)
dataThread->getEventChannelNames(names);
}
void SourceNode::updateSettings()
{
if (dataThread)
{
dataThread->updateChannels();
resizeBuffers();
}
}
void SourceNode::actionListenerCallback (const String& msg)
{
//std::cout << msg << std::endl;
if (msg.equalsIgnoreCase ("HI"))
{
// std::cout << "HI." << std::endl;
// dataThread->setOutputHigh();
ttlState = 1;
}
else if (msg.equalsIgnoreCase ("LO"))
{
// std::cout << "LO." << std::endl;
// dataThread->setOutputLow();
ttlState = 0;
}
}
float SourceNode::getSampleRate(int sub) const
{
if (dataThread != nullptr)
return dataThread->getSampleRate(sub);
else
return 44100.0;
}
float SourceNode::getDefaultSampleRate() const
{
if (dataThread != nullptr)
return dataThread->getSampleRate(0);
else
return 44100.0;
}
int SourceNode::getDefaultNumDataOutputs(DataChannel::DataChannelTypes type, int sub) const
{
if (dataThread)
return dataThread->getNumDataOutputs(type, sub);
else return 0;
}
float SourceNode::getBitVolts (const DataChannel* chan) const
{
if (dataThread != 0)
return dataThread->getBitVolts (chan);
else
return 1.0f;
}
void SourceNode::setChannelInfo(int channel, String name, float bitVolts)
{
dataChannelArray[channel]->setName(name);
dataChannelArray[channel]->setBitVolts(bitVolts);
}
void SourceNode::createEventChannels()
{
ttlChannels.clear();
if (dataThread)
{
//Create base TTL event channels
int nSubs = dataThread->getNumSubProcessors();
for (int i = 0; i < nSubs; i++)
{
int nChans = dataThread->getNumTTLOutputs(i);
nChans = jmin(nChans, 64); //Just 64 TTL channels per source for now
if (nChans > 0)
{
EventChannel* chan = new EventChannel(EventChannel::TTL, nChans, 0, dataThread->getSampleRate(i), this, i);
chan->setName(getName() + " source TTL events input");
chan->setDescription("TTL Events coming from the hardware source processor \"" + getName() + "\"");
chan->setDescriptor("sourceevent.ttl");
eventChannelArray.add(chan);
ttlChannels.add(chan);
}
else
ttlChannels.add(nullptr);
}
//Add other events that the source might create
Array<EventChannel*> events;
dataThread->createExtraEvents(events);
eventChannelArray.addArray(events);
}
}
void SourceNode::setEnabledState (bool newState)
{
if (newState && ! dataThread->foundInputSource())
{
isEnabled = false;
}
else
{
isEnabled = newState;
}
}
void SourceNode::setParameter (int parameterIndex, float newValue)
{
editor->updateParameterButtons (parameterIndex);
//std::cout << "Got parameter change notification";
}
AudioProcessorEditor* SourceNode::createEditor()
{
if (dataThread != nullptr)
{
editor = dataThread->createEditor (this);
}
else
{
editor = nullptr;
}
if (editor == nullptr)
{
editor = new SourceNodeEditor (this, true);
}
return editor;
}
bool SourceNode::tryEnablingEditor()
{
if (! isSourcePresent())
{
//std::cout << "No input source found." << std::endl;
return false;
}
else if (isEnabled)
{
// If we're already enabled (e.g. if we're being called again
// due to timerCallback()), then there's no need to go through
// the editor again.
return true;
}
std::cout << "Input source found." << std::endl;
setEnabledState (true);
GenericEditor* ed = getEditor();
CoreServices::highlightEditor (ed);
return true;
}
void SourceNode::timerCallback()
{
if (! tryEnablingEditor() && isEnabled)
{
std::cout << "Input source lost." << std::endl;
setEnabledState (false);
GenericEditor* ed = getEditor();
CoreServices::highlightEditor (ed);
}
}
bool SourceNode::isReady()
{
return isSourcePresent() && dataThread->isReady();
}
bool SourceNode::isSourcePresent() const
{
return dataThread && dataThread->foundInputSource();
}
bool SourceNode::enable()
{
std::cout << "Source node received enable signal" << std::endl;
wasDisabled = false;
stopTimer();
if (dataThread != nullptr)
{
dataThread->startAcquisition();
return true;
}
else
{
return false;
}
}
bool SourceNode::disable()
{
std::cout << "Source node received disable signal" << std::endl;
if (dataThread != nullptr)
dataThread->stopAcquisition();
startTimer (2000); // timer to check for connected source
wasDisabled = true;
std::cout << "SourceNode returning true." << std::endl;
return true;
}
void SourceNode::acquisitionStopped()
{
if (! wasDisabled)
{
std::cout << "Source node sending signal to UI." << std::endl;
AccessClass::getUIComponent()->disableCallbacks();
setEnabledState (false);
GenericEditor* ed = (GenericEditor*) getEditor();
CoreServices::highlightEditor (ed);
}
}
int SourceNode::getNumSubProcessors() const
{
if (!dataThread) return 0;
return dataThread->getNumSubProcessors();
}
void SourceNode::process(AudioSampleBuffer& buffer)
{
int nSubs = dataThread->getNumSubProcessors();
int copiedChannels = 0;
for (int sub = 0; sub < nSubs; sub++)
{
int channelsToCopy = getNumOutputs(sub);
int nSamples = inputBuffers[sub]->readAllFromBuffer(buffer, ×tamp, static_cast<uint64*>(eventCodeBuffers[sub]->getData()), buffer.getNumSamples(), copiedChannels, channelsToCopy);
copiedChannels += channelsToCopy;
setTimestampAndSamples(timestamp, nSamples, sub);
if (ttlChannels[sub])
{
int numEventChannels = ttlChannels[sub]->getNumChannels();
// fill event buffer
uint64 last = eventStates[sub];
for (int i = 0; i < nSamples; ++i)
{
uint64 current = *(static_cast<uint64*>(eventCodeBuffers[sub]->getData()) + i);
//If there has been no change to the TTL word, avoid doing anything at all here
if (last != current)
{
//Create a TTL event for each bit that has changed
for (int c = 0; c < numEventChannels; ++c)
{
if (((current >> c) & 0x01) != ((last >> c) & 0x01))
{
TTLEventPtr event = TTLEvent::createTTLEvent(ttlChannels[sub], timestamp + i, ¤t, sizeof(uint64), c);
addEvent(ttlChannels[sub], event, i);
}
}
last = current;
}
}
eventStates.set(sub, last);
}
}
}
void SourceNode::saveCustomParametersToXml (XmlElement* parentElement)
{
XmlElement* channelXml = parentElement->createNewChildElement ("CHANNEL_INFO");
if (dataThread->usesCustomNames())
{
Array<ChannelCustomInfo> channelInfo;
dataThread->getChannelInfo (channelInfo);
for (int i = 0; i < channelInfo.size(); ++i)
{
XmlElement* chan = channelXml->createNewChildElement ("CHANNEL");
chan->setAttribute ("name", channelInfo[i].name);
chan->setAttribute ("number", i);
chan->setAttribute ("gain", channelInfo[i].gain);
}
}
}
void SourceNode::loadCustomParametersFromXml()
{
if (parametersAsXml != nullptr)
{
// use parametersAsXml to restore state
forEachXmlChildElement (*parametersAsXml, xmlNode)
{
if (xmlNode->hasTagName ("CHANNEL_INFO"))
{
forEachXmlChildElementWithTagName (*xmlNode, chan, "CHANNEL")
{
const int number = chan->getIntAttribute ("number");
const float gain = chan->getDoubleAttribute ("gain");
String name = chan->getStringAttribute ("name");
dataThread->modifyChannelGain (number, gain);
dataThread->modifyChannelName (number, name);
}
}
}
}
}