diff --git a/Source/Processors/Channel/InfoObjects.cpp b/Source/Processors/Channel/InfoObjects.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0d3beae8e0ee976d2fd6c0c4ddc31bcb7104c35f --- /dev/null +++ b/Source/Processors/Channel/InfoObjects.cpp @@ -0,0 +1,397 @@ +/* +------------------------------------------------------------------ + +This file is part of the Open Ephys GUI +Copyright (C) 2014 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 "InfoObjects.h" +#include "../GenericProcessor/GenericProcessor.h" + +/** Common classes **/ +//Empty protected constructors +HistoryObject::HistoryObject() {} +NamedInfoObject::NamedInfoObject() {} + +//NodeInfoBase +NodeInfoBase::NodeInfoBase(uint16 id) : + m_nodeID(id) +{} + +unsigned int NodeInfoBase::getCurrentNodeID() const +{ + return m_nodeID; +} + +//History Object +String HistoryObject::getHistoricString() +{ + return m_historicString; +} + +void HistoryObject::addToHistoricString(String entry) +{ + if (m_historicString.isEmpty()) + m_historicString = entry; + else + m_historicString += (" -> " + entry); +} + +//SourceProcessorInfo +SourceProcessorInfo::SourceProcessorInfo(const GenericProcessor* source, uint16 subproc) + : m_sourceNodeID(source->nodeId), + m_sourceSubNodeIndex(subproc), + m_sourceType(source->getName()), + m_sourceName(source->getName()) //TODO: fix those two when we have the ability to rename processors +{ +} + +uint16 SourceProcessorInfo::getSourceNodeID() const +{ + return m_sourceNodeID; +} + +uint16 SourceProcessorInfo::getSubProcessorIdx() const +{ + return m_sourceSubNodeIndex; +} + +String SourceProcessorInfo::getSourceType() const +{ + return m_sourceType; +} + +String SourceProcessorInfo::getSourceName() const +{ + return m_sourceName; +} + +//NamedInfoObject +void NamedInfoObject::setName(String name) +{ + m_name = name; +} + +String NamedInfoObject::getName() const +{ + return m_name; +} + +void NamedInfoObject::setDescriptor(String descriptor) +{ + m_descriptor = descriptor; +} + +String NamedInfoObject::getDescriptor() const +{ + return m_descriptor; +} + +void NamedInfoObject::setDescription(String description) +{ + m_description = description; +} + +String NamedInfoObject::getDescription() const +{ + return m_description; +} + +//InfoObjectCommon +InfoObjectCommon::InfoObjectCommon(uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc) + : NodeInfoBase(source->getNodeId()), + SourceProcessorInfo(source, subproc), + m_sourceIndex(idx), + m_sourceTypeIndex(typeidx) +{ +} + +void InfoObjectCommon::setSampleRate(float sampleRate) +{ + m_sampleRate = sampleRate; +} + +float InfoObjectCommon::getSampleRate() const +{ + return m_sampleRate; +} + + +uint16 InfoObjectCommon::getSourceIndex() const +{ + return m_sourceIndex; +} + +uint16 InfoObjectCommon::getSourceTypeIndex() const +{ + return m_sourceTypeIndex; +} + +//DataChannel + +DataChannel::DataChannel(DataChannelTypes type, uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc) : + InfoObjectCommon(idx, typeidx, source, subproc), + m_type(type) +{ +} + +DataChannel::DataChannel(const DataChannel& ch) + : InfoObjectCommon(ch), //Call default copy constructors of base classes + MetaDataInfoObject(ch), + HistoryObject(ch), + m_type(ch.m_type), + m_bitVolts(ch.m_bitVolts), + m_isEnabled(true), + m_isMonitored(false), + m_isRecording(false) +{ +} + +DataChannel::~DataChannel() +{ +} + +void DataChannel::setBitVolts(float bitVolts) +{ + m_bitVolts = bitVolts; +} + +float DataChannel::getBitVolts() const +{ + return m_bitVolts; +} + +DataChannel::DataChannelTypes DataChannel::getChannelType() const +{ + return m_type; +} + +bool DataChannel::isEnabled() const +{ + return m_isEnabled; +} + +void DataChannel::setEnable(bool e) +{ + m_isEnabled = e; +} + +bool DataChannel::isMonitored() const +{ + return m_isMonitored; +} + +void DataChannel::setMonitored(bool e) +{ + m_isMonitored = e; +} + +void DataChannel::setRecordState(bool t) +{ + m_isRecording = t; +} + +bool DataChannel::getRecordState() const +{ + return m_isRecording; +} + +void DataChannel::reset() +{ + m_bitVolts = 1.0f; + m_isEnabled = true; + m_isMonitored = false; + m_isRecording = false; + setSampleRate(44100); +} + +//EventChannel +EventChannel::EventChannel(EventChannelTypes type, uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc) + : InfoObjectCommon(idx, typeidx, source, subproc), + m_type(type) +{ +} + +EventChannel::~EventChannel() +{ +} + +EventChannel::EventChannelTypes EventChannel::getChannelType() const +{ + return m_type; +} + +void EventChannel::setNumChannels(unsigned int numChannels) +{ + m_numChannels = numChannels; + //If event is TTL, set size to the needed number of bytes + if (m_type == TTL) + { + m_length = (numChannels + 7) / 8; + m_dataSize = m_length; + } +} + +unsigned int EventChannel::getNumChannels() const +{ + return m_numChannels; +} + +void EventChannel::setLength(unsigned int length) +{ + //Do nothing for TTLs, as the bytesize is fixed by the number of available channels + if (m_type == TTL) return; + + m_length = length; + m_dataSize = length * getTypeByteSize(m_type); + //for messages, add 1 byte to account for the null terminator + if (m_type == MESSAGE) m_dataSize += 1; +} + +unsigned int EventChannel::getLength() const +{ + return m_length; +} + +size_t EventChannel::getDataSize() const +{ + return m_dataSize; +} + +void EventChannel::setShouldBeRecorded(bool status) +{ + m_shouldBeRecorded = status; +} + +bool EventChannel::getShouldBeRecorded() const +{ + return m_shouldBeRecorded; +} + +size_t EventChannel::getTypeByteSize(EventChannel::EventChannelTypes type) +{ + switch (type) + { + case INT8_ARRAY: return sizeof(int8); + case UINT8_ARRAY: return sizeof(uint8); + case INT16_ARRAY: return sizeof(int16); + case UINT16_ARRAY: return sizeof(uint16); + case INT32_ARRAY: return sizeof(int32); + case UINT32_ARRAY: return sizeof(uint32); + case INT64_ARRAY: return sizeof(int64); + case UINT64_ARRAY: return sizeof(uint64); + default: return sizeof(char); + } +} + +//SpikeChannel + +SpikeChannel::SpikeChannel(ElectrodeTypes type, uint16 idx, uint16 typeidx, GenericProcessor* source, const Array<const DataChannel*>& sourceChannels, uint16 subproc) + : InfoObjectCommon(idx, typeidx, source, subproc), + m_type(type) +{ + int n = sourceChannels.size(); + jassert(n == getNumChannels(type)); + for (int i = 0; i < n; i++) + { + sourceChannelInfo info; + const DataChannel* chan = sourceChannels[i]; + info.processorID = chan->getSourceNodeID(); + info.subProcessorID = chan->getSubProcessorIdx(); + info.channelIDX = chan->getSourceIndex(); + m_sourceInfo.add(info); + } +} + +SpikeChannel::~SpikeChannel() +{} + +SpikeChannel::ElectrodeTypes SpikeChannel::getChannelType() const +{ + return m_type; +} + +Array<sourceChannelInfo> SpikeChannel::getSourceChannelInfo() const +{ + return m_sourceInfo; +} + +void SpikeChannel::setGain(float gain) +{ + m_gain = gain; +} + +float SpikeChannel::getGain() const +{ + return m_gain; +} + +void SpikeChannel::setNumSamples(unsigned int preSamples, unsigned int postSamples) +{ + m_numPreSamples = preSamples; + m_numPostSamples = postSamples; +} + +unsigned int SpikeChannel::getPrePeakSamples() const +{ + return m_numPreSamples; +} + +unsigned int SpikeChannel::getPostPeakSamples() const +{ + return m_numPostSamples; +} + +unsigned int SpikeChannel::getTotalSamples() const +{ + return m_numPostSamples + m_numPreSamples; +} + +unsigned int SpikeChannel::getNumChannels() const +{ + return getNumChannels(m_type); +} + +unsigned int SpikeChannel::getNumChannels(SpikeChannel::ElectrodeTypes type) +{ + switch (type) + { + case SINGLE: return 1; + case STEREOTRODE: return 2; + case TETRODE: return 4; + default: return 1; + } +} + +//ConfigurationObject +ConfigurationObject::ConfigurationObject(String descriptor, GenericProcessor* source, uint16 subproc) + : SourceProcessorInfo(source, subproc) +{ + setDescriptor(descriptor); +} + +void ConfigurationObject::setShouldBeRecorded(bool status) +{ + m_shouldBeRecorded = status; +} + +bool ConfigurationObject::getShouldBeRecorded() const +{ + return m_shouldBeRecorded; +} \ No newline at end of file diff --git a/Source/Processors/Channel/InfoObjects.h b/Source/Processors/Channel/InfoObjects.h new file mode 100644 index 0000000000000000000000000000000000000000..2cf7eb8f34bd3aabe7788a00691a22c7f10b24ee --- /dev/null +++ b/Source/Processors/Channel/InfoObjects.h @@ -0,0 +1,416 @@ +/* +------------------------------------------------------------------ + +This file is part of the Open Ephys GUI +Copyright (C) 2014 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/>. + +*/ + +#ifndef INFOOBJECTS_H_INCLUDED +#define INFOOBJECTS_H_INCLUDED + +#include "../../../JuceLibraryCode/JuceHeader.h" +#include "../PluginManager/OpenEphysPlugin.h" +#include "MetaData.h" + +class GenericProcessor; + +// ------- Ancilliary objects -------// + +/** +Structure with the basic info that identifies a channel +*/ +struct sourceChannelInfo +{ + uint16 processorID; + uint16 subProcessorID; + uint16 channelIDX; +}; + +class PLUGIN_API NodeInfoBase +{ + //This field should never be changed by anything except GenericProcessor base code + friend class GenericProcessor; +public: + /** Gets the ID of the processor which currently owns this copy of the info object */ + unsigned int getCurrentNodeID() const; +protected: + NodeInfoBase() = delete; + NodeInfoBase(uint16 id); + uint16 m_nodeID{ 0 }; +}; + +/** This class allows creating a string with an historic of all the data a node has gone through */ +class PLUGIN_API HistoryObject +{ +protected: + HistoryObject(); + +public: + /** Returns the historic string */ + String getHistoricString(); + /** Adds a new entry in the historic string*/ + void addToHistoricString(String entry); + +private: + String m_historicString; +}; + +class PLUGIN_API SourceProcessorInfo +{ +protected: + SourceProcessorInfo(const GenericProcessor* source, uint16 subproc = 0); + +public: + /** Gets the ID of the processor which created the channel object */ + uint16 getSourceNodeID() const; + + /** Gets the subprocessor index associated to this channel object*/ + uint16 getSubProcessorIdx() const; + + /** Gets the processor type of the node which created this object */ + String getSourceType() const; + + /** Gets the name of the processor which created this object */ + String getSourceName() const; + +private: + SourceProcessorInfo() = delete; + const uint16 m_sourceNodeID; + const uint16 m_sourceSubNodeIndex; + const String m_sourceType; + const String m_sourceName; + +}; + +class PLUGIN_API NamedInfoObject +{ +public: + /** Sets the object's name*/ + void setName(String name); + + /** Returns the name of a given object*/ + String getName() const; + + /** Sets the Channel Info Object description, to be stored in most file formats*/ + void setDescription(String description); + + /** Gets the Channel Info Object description */ + String getDescription() const; + + /** Sets a machine-readable data descriptor (eg.: data.continuous.headstage ) */ + void setDescriptor(String descriptor); + + String getDescriptor() const; +protected: + NamedInfoObject(); +private: + String m_name; + String m_descriptor; + String m_description; + +}; + +/** Common class for all info objects */ +class PLUGIN_API InfoObjectCommon : + public NodeInfoBase, public SourceProcessorInfo, NamedInfoObject +{ +protected: + InfoObjectCommon(uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc = 0); + +public: + + /** Sets the sample rate value for this channel. */ + void setSampleRate(float sampleRate); + + /** Returns the sample rate value for this channel. */ + float getSampleRate() const; + + + /** Gets the position of this channel in the source processor*/ + uint16 getSourceIndex() const; + + /** Gets the position in the source processor of this channel object, relative + to its subtype (HEADSTAGE, AUX or ADC for data channels, TTL, MESSAGE or BINARY for events, etc...) */ + uint16 getSourceTypeIndex() const; + +private: + /** Index of the object in the source processor */ + const uint16 m_sourceIndex; + /** Index of this particular subtype in the source processor */ + const uint16 m_sourceTypeIndex; + + float m_sampleRate{ 44100.0f }; +}; + +// ------- Main objects -------// + +class PLUGIN_API DataChannel + : public InfoObjectCommon, public MetaDataInfoObject, public HistoryObject +{ +public: + + enum DataChannelTypes + { + HEADSTAGE_CHANNEL = 0, + AUX_CHANNEL = 1, + ADC_CHANNEL = 2 + }; + //--------- CONSTRUCTOR / DESTRUCTOR --------// + + /** Default constructor for creating Channels from scratch. + @param type The type of data this channel represents (HEADSTAGE, ADC, AUX) + @param idx The index of this data channel in the source processor + @param typeidx The index of this particular type of data channel in the source processor + @param source A pointer to the source processor + @param subproc Optional. The source subprocessor index. + */ + DataChannel(DataChannelTypes type, uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc = 0); + + /** Copy constructor. */ + DataChannel(const DataChannel& ch); + + ~DataChannel(); + + //--------- DATA GET / SET METHODS --------// + + /** Sets the bitVolts value for this channel. */ + void setBitVolts(float bitVolts); + + /** Returns the bitVolts value for this channel. */ + float getBitVolts() const; + + DataChannelTypes getChannelType() const; + + //--------- STATUS METHODS ----------// + /** Toggled when a channel is disabled from further processing. */ + bool isEnabled() const; + + /** Toggled when a channel is disabled from further processing. */ + void setEnable(bool e); + + /** Informs whether the channel is being routed to the audio node */ + bool isMonitored() const; + + /** Sets if the channel needs to be routed to the audio node */ + void setMonitored(bool e); + + //Record methods will be phased out with the probe-based record system + /** Sets whether or not the channel will record. */ + void setRecordState(bool t); + + /** Informs whether or not the channel will record. */ + bool getRecordState() const; + + //---------- OTHER METHODS ------------// + /** Restores the default settings for a given channel. */ + void reset(); + +private: + const DataChannelTypes m_type; + float m_bitVolts{ 1.0f }; + bool m_isEnabled{ true }; + bool m_isMonitored{ false }; + bool m_isRecording{ false }; + + JUCE_LEAK_DETECTOR(DataChannel); +}; + +class PLUGIN_API EventChannel : + public InfoObjectCommon, public MetaDataInfoObject, public MetaDataEventObject +{ +public: + enum EventChannelTypes + { + //Numeration kept to maintain compatibility with old code + TTL = 3, + MESSAGE = 5, + //generic binary types. These will be treated by the majority of record engines as simple binary blobs, + //while having strict typing helps creating stabler plugins + INT8_ARRAY = 10, + UINT8_ARRAY, + INT16_ARRAY, + UINT16_ARRAY, + INT32_ARRAY, + UINT32_ARRAY, + INT64_ARRAY, + UINT64_ARRAY, + FLOAT_ARRAY, + DOUBLE_ARRAY + }; + + /** Default constructor + @param type The type of event this channel represents (TTL, MESSAGE, BYINARY_MSG) + @param idx The index of this event channel in the source processor + @param typeidx The index of this particular type of event channel in the source processor + @param source A pointer to the source processor + @param subproc Optional. The source subprocessor index. + */ + EventChannel(EventChannelTypes type, uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc = 0); + + ~EventChannel(); + + EventChannelTypes getChannelType() const; + + /** Sets the number of virtual channels this event can carry + -For TTL signals, it must be the number of bits in the TTL word. + -For other events, this might be used to differentiate between different origins within the same processor + */ + void setNumChannels(unsigned int numChannels); + + /** Gets the number of virtual channels + @see setNumChannels + */ + unsigned int getNumChannels() const; + + /** Sets the length of the event payload + -For TTL signals, this method will do nothing, as the size is fixed by the number of ttl channels + -For message events, the length of the string in characters + -For typed array events, the number of elements + */ + void setLength(unsigned int length); + + /** Gets the size of the event payload + -For TTL, it returns the number of bytes that form the full TTL word, same as getByteDataSize() + -For message events, the number of characters + -For typed array events, the number of elements. + */ + unsigned int getLength() const; + + /** Gets the size of the event payload in bytes*/ + size_t getDataSize() const; + + /** Sets if the event should be recorded or not. + Note that this option does not prevent the event from actually being recorded, it simply states + a processor's developer opinion on how the event should be treated, but can be overrided by the user. + + Meant for events that represent runtime changes in the state of a processor that can be useful to + other processors but hold no actual meaning for the experiment. + */ + void setShouldBeRecorded(bool status); + + /** Gets the event preference about being recorded */ + bool getShouldBeRecorded() const; + + /** Gets the size in bytes of an element depending of the type*/ + static size_t getTypeByteSize(EventChannelTypes type); + +private: + const EventChannelTypes m_type; + unsigned int m_numChannels{ 1 }; + size_t m_dataSize{ 1 }; + unsigned int m_length{ 1 }; + bool m_shouldBeRecorded{ true }; + + JUCE_LEAK_DETECTOR(EventChannel); +}; + +class PLUGIN_API SpikeChannel : + public InfoObjectCommon, public MetaDataInfoObject, public MetaDataEventObject +{ +public: + enum ElectrodeTypes + { + SINGLE, + STEREOTRODE, + TETRODE + }; + + /** Default constructor + @param type The type of electrode this channel represents (SINGLE, STEREOTRODE, TETRODE) + @param idx The index of this spike channel in the source processor + @param typeidx The index of this particular type of spike channel in the source processor + @param source A pointer to the source processor + @param souceChannels An array containing const pointers to the channels that originate the data for this spike electrode + @param subproc Optional. The source subprocessor index. + */ + SpikeChannel(ElectrodeTypes type, uint16 idx, uint16 typeidx, GenericProcessor* source, const Array<const DataChannel*>& sourceChannels, uint16 subproc = 0); + + ~SpikeChannel(); + + ElectrodeTypes getChannelType() const; + + /** Returns an array with info about the channels from which the spikes originate */ + Array<sourceChannelInfo> getSourceChannelInfo() const; + + /** Sets the electrode gain */ + void setGain(float gain); + + /** Gets the electrode gain */ + float getGain() const; + + /** Sets the number of samples, pre and post peak */ + void setNumSamples(unsigned int preSamples, unsigned int postSamples); + + /** Gets the number of pre peak samples */ + unsigned int getPrePeakSamples() const; + + /** Gets the number of post peak samples */ + unsigned int getPostPeakSamples() const; + + /** Gets the total number of samples */ + unsigned int getTotalSamples() const; + + /** Gets the number of channels associated with the electrode type */ + unsigned int getNumChannels() const; + + /** Gets the number of channels associated with a specific electrode type */ + static unsigned int getNumChannels(ElectrodeTypes type); + +private: + const ElectrodeTypes m_type; + Array<sourceChannelInfo> m_sourceInfo; + float m_gain{ 1.0f }; + unsigned int m_numPreSamples{ 8 }; + unsigned int m_numPostSamples{ 32 }; + + JUCE_LEAK_DETECTOR(DataChannel); +}; + +/** + Class for defining extra configuration objects to be shared with processors down the chain not associated with any particular channel or event. + It does not hold any data by itself, but can be filled with metadata fields to form any structure +*/ +class PLUGIN_API ConfigurationObject : + public SourceProcessorInfo, public NamedInfoObject, public MetaDataInfoObject +{ +public: + /**Default constructor + @param descriptor the descriptor field of NamedInfoObject, required for config objects + @param source The source processor + @param subproc Optional. The source subprocessor index + */ + ConfigurationObject(String descriptor, GenericProcessor* source, uint16 subproc = 0); + + /** Sets if the configuration should be recorded or not. + Similar to the events, this does not prevent the configuration data to be recorded, but rather states + a preference by the source developer. + */ + void setShouldBeRecorded(bool status); + + /** Gets the config preference about being recorded */ + bool getShouldBeRecorded() const; + +private: + bool m_shouldBeRecorded{ true }; + JUCE_LEAK_DETECTOR(ConfigurationObject); +}; + + + +#endif \ No newline at end of file