diff --git a/Source/AccessClass.cpp b/Source/AccessClass.cpp index 6b3770368b0c772388e3226bd64b22bfc2838f4f..d6f7af6b3bdcbe4e631bcc6749a43299d9c8d2a9 100644 --- a/Source/AccessClass.cpp +++ b/Source/AccessClass.cpp @@ -22,6 +22,8 @@ */ #include "AccessClass.h" +#include "Processors\GenericProcessor\GenericProcessor.h" +#include "Processors\MessageCenter\MessageCenterEditor.h" #include "UI/UIComponent.h" @@ -129,4 +131,13 @@ ActionBroadcaster* getBroadcaster() return bc.get(); } +MidiBuffer* getProcessorMidiBuffer(GenericProcessor* proc) +{ + return MidiBufferAccessor::getMidiBuffer(proc); +} + +MidiBuffer* MidiBufferAccessor::getMidiBuffer(GenericProcessor* proc) +{ + return proc->m_currentMidiBuffer; +} } diff --git a/Source/AccessClass.h b/Source/AccessClass.h index 8ccd63cc8ba86bed669cdb90d041f654ec742d0d..4008fec65c5d61233961fd16f3207414bc7e2b8f 100644 --- a/Source/AccessClass.h +++ b/Source/AccessClass.h @@ -36,6 +36,7 @@ class ControlPanel; class AudioComponent; class GraphViewer; class PluginManager; +class GenericProcessor; @@ -83,6 +84,14 @@ PluginManager* getPluginManager(); ActionBroadcaster* getBroadcaster(); +MidiBuffer* getProcessorMidiBuffer(GenericProcessor* proc); + +class MidiBufferAccessor +{ + friend MidiBuffer* getProcessorMidiBuffer(GenericProcessor* proc); +private: + static MidiBuffer* getMidiBuffer(GenericProcessor* proc); +}; }; diff --git a/Source/Processors/AudioNode/AudioNode.cpp b/Source/Processors/AudioNode/AudioNode.cpp index aaf2460194076bc1409580092b9de8523b1f515f..651327af588276fabcde9891b2d36363eb37dc3a 100755 --- a/Source/Processors/AudioNode/AudioNode.cpp +++ b/Source/Processors/AudioNode/AudioNode.cpp @@ -72,7 +72,7 @@ void AudioNode::updateBufferSize() } -void AudioNode::setChannel(Channel* ch) +void AudioNode::setChannel(const DataChannel* ch) { int channelNum = channelPointers.indexOf(ch); @@ -82,7 +82,7 @@ void AudioNode::setChannel(Channel* ch) setCurrentChannel(channelNum); } -void AudioNode::setChannelStatus(Channel* chan, bool status) +void AudioNode::setChannelStatus(const DataChannel* chan, bool status) { setChannel(chan); // add 2 to account for 2 output channels @@ -113,7 +113,7 @@ void AudioNode::addInputChannel(GenericProcessor* sourceNode, int chan) setPlayConfigDetails(channelIndex+1,0,44100.0,128); - channelPointers.add(sourceNode->channels[chan]); + channelPointers.add(sourceNode->getDataChannel(chan)); } @@ -177,10 +177,10 @@ void AudioNode::recreateBuffers() for (int i = 0; i < channelPointers.size(); i++) { // processor sample rate divided by sound card sample rate - numSamplesExpected.add((int)(channelPointers[i]->sampleRate/destBufferSampleRate*float(estimatedSamples)) + 1); + numSamplesExpected.add((int)(channelPointers[i]->getSampleRate()/destBufferSampleRate*float(estimatedSamples)) + 1); samplesInBackupBuffer.add(0); samplesInOverflowBuffer.add(0); - sourceBufferSampleRate.add(channelPointers[i]->sampleRate); + sourceBufferSampleRate.add(channelPointers[i]->getSampleRate()); filters.add(new Dsp::SmoothedFilterDesign<Dsp::RBJ::Design::LowPass, 1> (1024)); @@ -220,8 +220,7 @@ void AudioNode::updateFilter(int i) } -void AudioNode::process(AudioSampleBuffer& buffer, - MidiBuffer& events) +void AudioNode::process(AudioSampleBuffer& buffer) { float gain; int valuesNeeded = buffer.getNumSamples(); // samples needed to fill out the buffer @@ -314,13 +313,13 @@ void AudioNode::process(AudioSampleBuffer& buffer, samplesInBackupBuffer.set(i,leftoverSamples); } - gain = volume/(float(0x7fff) * channelPointers[i]->bitVolts); + gain = volume/(float(0x7fff) * channelPointers[i]->getBitVolts()); // Data are floats in units of microvolts, so dividing by bitVolts and 0x7fff (max value for 16b signed) // rescales to between -1 and +1. Audio output starts So, maximum gain applied to maximum data would be 10. int remainingSamples = numSamplesExpected[i] - samplesToCopyFromOverflowBuffer; - int samplesAvailable = numSamples.at(channelPointers[i]->sourceNodeId); + int samplesAvailable = numSamples.at(getProcessorFullId(channelPointers[i]->getSourceNodeID(), channelPointers[i]->getSubProcessorIdx())); int samplesToCopyFromIncomingBuffer = ((remainingSamples <= samplesAvailable) ? remainingSamples : diff --git a/Source/Processors/AudioNode/AudioNode.h b/Source/Processors/AudioNode/AudioNode.h index 2f65e6a3009980ad6af4a67fe3de12936799fe70..0fcc2106ef9ec42e73f7f56e6d6c25cccd1418ac 100755 --- a/Source/Processors/AudioNode/AudioNode.h +++ b/Source/Processors/AudioNode/AudioNode.h @@ -32,7 +32,6 @@ #include "AudioEditor.h" #include "../Dsp/Dsp.h" -#include "../Channel/Channel.h" class AudioEditor; @@ -89,26 +88,26 @@ public: /** Handle incoming data and decide which channels to monitor */ - void process(AudioSampleBuffer& buffer, MidiBuffer& midiMessages); + void process(AudioSampleBuffer& buffer) override; /** Used to change audio monitoring parameters (such as channels to monitor and volume) while acquisition is active. */ - void setParameter(int parameterIndex, float newValue); + void setParameter(int parameterIndex, float newValue) override; /** Creates the AudioEditor (located in the ControlPanel). */ - AudioProcessorEditor* createEditor(); + AudioProcessorEditor* createEditor() override; /** Sets the current channel (in advance of a parameter change). */ - void setChannel(Channel* ch); + void setChannel(const DataChannel* ch); /** Used to turn audio monitoring on and off for individual channels. */ - void setChannelStatus(Channel* ch, bool status); + void setChannelStatus(const DataChannel* ch, bool status); /** Resets the connections prior to a new round of data acquisition. */ - void resetConnections(); + void resetConnections() override; /** Resets the connections prior to a new round of data acquisition. */ - void enableCurrentChannel(bool); + void enableCurrentChannel(bool) override; /** Establishes a connection between a channel of a GenericProcessor and the AudioNode. */ void addInputChannel(GenericProcessor* source, int chan); @@ -118,11 +117,11 @@ public: void updateBufferSize(); - void prepareToPlay(double sampleRate_, int estimatedSamplesPerBlock); + void prepareToPlay(double sampleRate_, int estimatedSamplesPerBlock) override; void updateFilter(int i); - bool enable(); + bool enable() override; private: void recreateBuffers(); @@ -133,7 +132,7 @@ private: float noiseGateLevel; // in microvolts /** An array of pointers to the channels that feed into the AudioNode. */ - Array<Channel*> channelPointers; + Array<const DataChannel*> channelPointers; OwnedArray<AudioSampleBuffer> bufferA; OwnedArray<AudioSampleBuffer> bufferB; diff --git a/Source/Processors/Channel/InfoObjects.cpp b/Source/Processors/Channel/InfoObjects.cpp index 740f4338349ab4cd5e06085f5e601a73ec7b6a21..f53bc1d4e9ab7671d4da95e8c73469adcfc2a571 100644 --- a/Source/Processors/Channel/InfoObjects.cpp +++ b/Source/Processors/Channel/InfoObjects.cpp @@ -55,7 +55,7 @@ void HistoryObject::addToHistoricString(String entry) //SourceProcessorInfo SourceProcessorInfo::SourceProcessorInfo(const GenericProcessor* source, uint16 subproc) - : m_sourceNodeID(source->nodeId), + : m_sourceNodeID(source->getNodeId()), m_sourceSubNodeIndex(subproc), m_sourceType(source->getName()), m_sourceName(source->getName()) //TODO: fix those two when we have the ability to rename processors @@ -149,6 +149,7 @@ DataChannel::DataChannel(DataChannelTypes type, GenericProcessor* source, uint16 InfoObjectCommon(source->dataChannelCount++, source->dataChannelTypeCount[type]++, source, subproc), m_type(type) { + setName(getDefaultName()); } DataChannel::DataChannel(const DataChannel& ch) @@ -167,6 +168,11 @@ DataChannel::~DataChannel() { } +InfoObjectCommon::InfoObjectType DataChannel::getInfoObjectType() const +{ + return InfoObjectCommon::DATA_CHANNEL; +} + void DataChannel::setBitVolts(float bitVolts) { m_bitVolts = bitVolts; @@ -222,30 +228,38 @@ void DataChannel::reset() } //EventChannel -EventChannel::EventChannel(EventChannelTypes type, GenericProcessor* source, uint16 subproc) +EventChannel::EventChannel(EventChannelTypes type, unsigned int nChannels, unsigned int dataLength, GenericProcessor* source, uint16 subproc) : InfoObjectCommon(source->eventChannelCount++, source->eventChannelTypeCount[type]++, source, subproc), m_type(type) { + m_numChannels = nChannels; + if (m_type == TTL) + { + m_length = (m_numChannels + 7) / 8; + m_dataSize = m_length; + } + else + { + m_length = dataLength; + m_dataSize = dataLength * getTypeByteSize(m_type); + //for messages, add 1 byte to account for the null terminator + if (m_type == TEXT) m_dataSize += 1; + } + setName(getDefaultName()); } EventChannel::~EventChannel() { } -EventChannel::EventChannelTypes EventChannel::getChannelType() const +InfoObjectCommon::InfoObjectType EventChannel::getInfoObjectType() const { - return m_type; + return InfoObjectCommon::EVENT_CHANNEL; } -void EventChannel::setNumChannels(unsigned int numChannels) +EventChannel::EventChannelTypes EventChannel::getChannelType() const { - 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; - } + return m_type; } unsigned int EventChannel::getNumChannels() const @@ -253,17 +267,6 @@ 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 == TEXT) m_dataSize += 1; -} - unsigned int EventChannel::getLength() const { return m_length; @@ -317,11 +320,17 @@ SpikeChannel::SpikeChannel(ElectrodeTypes type, GenericProcessor* source, const info.channelIDX = chan->getSourceIndex(); m_sourceInfo.add(info); } + setName(getDefaultName()); } SpikeChannel::~SpikeChannel() {} +InfoObjectCommon::InfoObjectType SpikeChannel::getInfoObjectType() const +{ + return InfoObjectCommon::SPIKE_CHANNEL; +} + SpikeChannel::ElectrodeTypes SpikeChannel::getChannelType() const { return m_type; @@ -394,6 +403,7 @@ ConfigurationObject::ConfigurationObject(String descriptor, GenericProcessor* so : SourceProcessorInfo(source, subproc) { setDescriptor(descriptor); + setName(getDefaultName()); } void ConfigurationObject::setShouldBeRecorded(bool status) diff --git a/Source/Processors/Channel/InfoObjects.h b/Source/Processors/Channel/InfoObjects.h index 608c7a2c967969883ed2e31069101454591d18b6..a5149261ae518149764aea7993093970060dd900 100644 --- a/Source/Processors/Channel/InfoObjects.h +++ b/Source/Processors/Channel/InfoObjects.h @@ -119,6 +119,7 @@ public: String getDescriptor() const; protected: NamedInfoObject(); + virtual String getDefaultName() const = 0; private: String m_name; String m_descriptor; @@ -128,12 +129,18 @@ private: /** Common class for all info objects */ class PLUGIN_API InfoObjectCommon : - public NodeInfoBase, public SourceProcessorInfo, NamedInfoObject + public NodeInfoBase, public SourceProcessorInfo, public NamedInfoObject { protected: InfoObjectCommon(uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc = 0); public: + enum InfoObjectType + { + DATA_CHANNEL, + EVENT_CHANNEL, + SPIKE_CHANNEL + }; /** Sets the sample rate value for this channel. */ void setSampleRate(float sampleRate); @@ -149,6 +156,8 @@ public: to its subtype (HEADSTAGE, AUX or ADC for data channels, TTL, MESSAGE or BINARY for events, etc...) */ uint16 getSourceTypeIndex() const; + virtual InfoObjectType getInfoObjectType() const = 0; + private: /** Index of the object in the source processor */ const uint16 m_sourceIndex; @@ -220,6 +229,8 @@ public: /** Restores the default settings for a given channel. */ void reset(); + InfoObjectType getInfoObjectType() const override; + String getDefaultName() const override; private: const DataChannelTypes m_type; float m_bitVolts{ 1.0f }; @@ -259,35 +270,33 @@ public: /** Default constructor @param type The type of event this channel represents (TTL, TEXT, BYINARY_MSG) + @param numChannels The number of virtual channels + @param dataLength The length of the event payload @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. + + The virtual channels mean: + -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 + + The event length mean: + -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 + */ - EventChannel(EventChannelTypes type, GenericProcessor* source, uint16 subproc = 0); + EventChannel(EventChannelTypes type, unsigned int numChannels, unsigned int dataLength, 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 @@ -313,6 +322,8 @@ public: /** Gets the size in bytes of an element depending of the type*/ static size_t getTypeByteSize(EventChannelTypes type); + InfoObjectType getInfoObjectType() const override; + String getDefaultName() const override; private: const EventChannelTypes m_type; unsigned int m_numChannels{ 1 }; @@ -381,6 +392,8 @@ public: /** Gets the number of channels associated with a specific electrode type */ static unsigned int getNumChannels(ElectrodeTypes type); + InfoObjectType getInfoObjectType() const override; + String getDefaultName() const override; private: const ElectrodeTypes m_type; Array<sourceChannelInfo> m_sourceInfo; @@ -415,6 +428,7 @@ public: /** Gets the config preference about being recorded */ bool getShouldBeRecorded() const; + String getDefaultName() const override; private: bool m_shouldBeRecorded{ true }; JUCE_LEAK_DETECTOR(ConfigurationObject); diff --git a/Source/Processors/DataThreads/DataThread.cpp b/Source/Processors/DataThreads/DataThread.cpp index acd15c8760ff7b69839369731cc5873ad605dec4..3a284e7453af9014298cef3ac482d9d1c75663f8 100755 --- a/Source/Processors/DataThreads/DataThread.cpp +++ b/Source/Processors/DataThreads/DataThread.cpp @@ -26,15 +26,16 @@ DataThread::DataThread (SourceNode* s) : Thread ("Data Thread") - , dataBuffer (0) { sn = s; setPriority (10); - // set default to zero, so that sources that - // do not generate their own timestamps can simply increment - // this value - timestamp = 0; + int nSub = getNumSubProcessors(); + for (int i = 0; i < nSub; i++) + { + ttlEventWords.add(0); + timestamps.add(0); + } } @@ -61,11 +62,10 @@ void DataThread::run() } -DataBuffer* DataThread::getBufferAddress() const +DataBuffer* DataThread::getBufferAddress(int subProcessor) const { - std::cout << "Setting buffer address to " << dataBuffer << std::endl; - return dataBuffer; + return sourceBuffers[subProcessor]; } @@ -78,15 +78,22 @@ void DataThread::getChannelInfo (Array<ChannelCustomInfo>& infoArray) const void DataThread::updateChannels() { + ttlEventWords.clear(); + timestamps.clear(); + int nSub = getNumSubProcessors(); + for (int i = 0; i < nSub; i++) + { + ttlEventWords.add(0); + timestamps.add(0); + } if (usesCustomNames()) { - channelInfo.resize (sn->channels.size()); + channelInfo.resize (sn->getTotalDataChannels()); setDefaultChannelNames(); for (int i = 0; i < channelInfo.size(); ++i) { - sn->channels[i]->setName (channelInfo[i].name); - sn->channels[i]->bitVolts = channelInfo[i].gain; + sn->setChannelInfo(i, channelInfo[i].name, channelInfo[i].gain); } } } @@ -95,9 +102,11 @@ void DataThread::updateChannels() void DataThread::setOutputHigh() {} void DataThread::setOutputLow() {} -int DataThread::getNumAuxOutputs() const { return 0; } -int DataThread::getNumAdcOutputs() const { return 0; } -int DataThread::getNumEventChannels() const { return 0; } +int DataThread::getNumDataOutputs(DataChannel::DataChannelTypes type, int subproc) const { return 0; } + +unsigned int DataThread::getNumSubProcessors() const { return 0; } + +int DataThread::getNumTTLOutputs(int subproc) const { return 0; } void DataThread::getEventChannelNames (StringArray& names) const { } @@ -119,11 +128,6 @@ bool DataThread::usesCustomNames() const return false; } -void* DataThread::getDevice() -{ - return 0; -} - void DataThread::setDefaultChannelNames() { } @@ -133,7 +137,5 @@ GenericEditor* DataThread::createEditor (SourceNode*) return nullptr; } -bool DataThread::isDualSampleRate() -{ - return false; -} +void DataThread::createExtraEvents(Array<EventChannel>&) +{} \ No newline at end of file diff --git a/Source/Processors/DataThreads/DataThread.h b/Source/Processors/DataThreads/DataThread.h index ea1e547039b68bf500bf976b8e358b6e56c73339..a71f3ac8d972e259a85cfab67d1c411e34d54806 100755 --- a/Source/Processors/DataThreads/DataThread.h +++ b/Source/Processors/DataThreads/DataThread.h @@ -94,25 +94,24 @@ public: virtual bool stopAcquisition() = 0; /** Returns the number of continuous headstage channels the data source can provide.*/ - virtual int getNumHeadstageOutputs() const = 0; + virtual int getNumDataOutputs(DataChannel::DataChannelTypes type, int subProcessorIdx) const = 0; - /** Returns the number of continuous aux channels the data source can provide.*/ - virtual int getNumAuxOutputs() const; - - /** Returns the number of continuous ADC channels the data source can provide.*/ - virtual int getNumAdcOutputs() const; + /** Returns the number of TTL channels that each subprocessor generates*/ + virtual int getNumTTLOutputs(int subProcessorIdx) const = 0; /** Returns the sample rate of the data source.*/ - virtual float getSampleRate() const = 0; + virtual float getSampleRate(int subProcessorIdx) const = 0; /** Returns the number of virtual subprocessors this source can generate */ virtual unsigned int getNumSubProcessors() const; - /** Returns the volts per bit of the data source.*/ - virtual float getBitVolts (Channel* chan) const = 0; + virtual void getDefaultEventInfo(Array<GenericProcessor::DefaultEventInfo>& info) const; - /** Returns the number of event channels of the data source.*/ - virtual int getNumEventChannels() const; + /** Called to create extra event channels, apart from the default TTL ones*/ + virtual void createExtraEvents(Array<EventChannel>& events); + + /** Returns the volts per bit of the data source.*/ + virtual float getBitVolts (const DataChannel* chan) const = 0; /** Notifies if the device is ready for acquisition */ virtual bool isReady(); @@ -134,20 +133,22 @@ public: /** Returns a pointer to the data input device, in case other processors need to communicate with it.*/ - virtual void* getDevice(); + // virtual void* getDevice(); void getChannelInfo (Array<ChannelCustomInfo>& infoArray) const; /** Create the DataThread custom editor, if any*/ virtual GenericEditor* createEditor (SourceNode* sn); + void createTTLChannels(); + protected: virtual void setDefaultChannelNames(); SourceNode* sn; - uint64 eventCode; - int64 timestamp; + Array<uint64> ttlEventWords; + Array<int64> timestamps; Array<ChannelCustomInfo> channelInfo; OwnedArray<DataBuffer> sourceBuffers; diff --git a/Source/Processors/DataThreads/RhythmNode/RHD2000Editor.cpp b/Source/Processors/DataThreads/RhythmNode/RHD2000Editor.cpp index 5aeb37d65a9c3dd4bd0c4a7b9a04f251c485df3e..85712faae36d3c8e7cbd2fcec2ded7ea96b73188 100644 --- a/Source/Processors/DataThreads/RhythmNode/RHD2000Editor.cpp +++ b/Source/Processors/DataThreads/RhythmNode/RHD2000Editor.cpp @@ -127,7 +127,7 @@ void FPGAchannelList::update() staticLabels.clear(); RHD2000Thread* thread = (RHD2000Thread*)proc->getThread(); - ChannelType type; + DataChannel::DataChannelTypes type; // find out which streams are active. bool hsActive[MAX_NUM_HEADSTAGES+1]; diff --git a/Source/Processors/DataThreads/RhythmNode/RHD2000Editor.h b/Source/Processors/DataThreads/RhythmNode/RHD2000Editor.h index 74a4d9354a08a486cf09e7476a016b3b58510673..f88f6611e7f771fd6d18a4d29b85cc0897669739 100644 --- a/Source/Processors/DataThreads/RhythmNode/RHD2000Editor.h +++ b/Source/Processors/DataThreads/RhythmNode/RHD2000Editor.h @@ -76,7 +76,7 @@ public: private: Array<float> gains; - Array<ChannelType> types; + Array<DataChannel::DataChannelTypes> types; bool chainUpdate; @@ -96,7 +96,7 @@ private: class FPGAchannelComponent : public Component, Button::Listener, public ComboBox::Listener, public Label::Listener { public: - FPGAchannelComponent(FPGAchannelList* cl, int ch, int gainIndex_, String name_, Array<float> gains_, ChannelType type_); + FPGAchannelComponent(FPGAchannelList* cl, int ch, int gainIndex_, String name_, Array<float> gains_, DataChannel::DataChannelTypes type_); ~FPGAchannelComponent(); Colour getDefaultColor(int ID); void setImpedanceValues(float mag, float phase); @@ -117,7 +117,7 @@ public: void resized(); - const ChannelType type; + const DataChannel::DataChannelTypes type; private: Array<float> gains; FPGAchannelList* channelList; diff --git a/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.cpp b/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.cpp index 1376f93a976be2e62cd301e98ca55c55a79fd27e..e622b05276b6b55e27168dbfcce8b88b2f696635 100644 --- a/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.cpp +++ b/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.cpp @@ -102,7 +102,7 @@ RHD2000Thread::RHD2000Thread(SourceNode* sn) : DataThread(sn), headstagesArray.add(new RHDHeadstage(static_cast<Rhd2000EvalBoard::BoardDataSource>(i))); evalBoard = new Rhd2000EvalBoard; - dataBuffer = new DataBuffer(2, 10000); // start with 2 channels and automatically resize + sourceBuffers.add(new DataBuffer(2, 10000)); // start with 2 channels and automatically resize // Open Opal Kelly XEM6010 board. // Returns 1 if successful, -1 if FrontPanel cannot be loaded, and -2 if XEM6010 can't be found. @@ -196,6 +196,11 @@ bool RHD2000Thread::usesCustomNames() const return true; } +unsigned int RHD2000Thread::getNumSubProcessors() const +{ + return 1; +} + void RHD2000Thread::setDACthreshold(int dacOutput, float threshold) { dacThresholds[dacOutput]= threshold; @@ -207,7 +212,7 @@ void RHD2000Thread::setDACthreshold(int dacOutput, float threshold) void RHD2000Thread::setDACchannel(int dacOutput, int channel) { - if (channel < getNumHeadstageOutputs()) + if (channel < getNumDataOutputs(DataChannel::HEADSTAGE_CHANNEL, 0)) { int channelCount = 0; for (int i = 0; i < enabledStreams.size(); i++) @@ -824,7 +829,7 @@ void RHD2000Thread::setDefaultChannelNames() in.name = "CH" + String(channelNumber); else in.name = "CH_" + stream_prefix[i] + "_" + String(1 + k); - in.gain = getBitVolts(sn->channels[k]); + in.gain = getBitVolts(sn->getDataChannel(k)); channelInfo.set(channelNumber-1, in); } @@ -848,7 +853,7 @@ void RHD2000Thread::setDefaultChannelNames() in.name = "AUX" + String(aux_counter); else in.name = "AUX_" + stream_prefix[i] + "_" + String(1 + k); - in.gain = getBitVolts(sn->channels[chn]); + in.gain = getBitVolts(sn->getDataChannel(chn)); channelInfo.set(chn, in); } @@ -878,65 +883,69 @@ void RHD2000Thread::setDefaultChannelNames() int RHD2000Thread::getNumChannels() const { - return getNumHeadstageOutputs() + getNumAdcOutputs() + getNumAuxOutputs(); + return getNumDataOutputs(DataChannel::HEADSTAGE_CHANNEL, 0) + getNumDataOutputs(DataChannel::AUX_CHANNEL, 0) + getNumDataOutputs(DataChannel::ADC_CHANNEL, 0); } -int RHD2000Thread::getNumHeadstageOutputs() const +int RHD2000Thread::getNumDataOutputs(DataChannel::DataChannelTypes type, int subproc) const { - int newNumChannels = 0; - for (int i = 0; i < MAX_NUM_HEADSTAGES; ++i) - { - if (headstagesArray[i]->isPlugged()) - { - newNumChannels += headstagesArray[i]->getNumActiveChannels(); - } - } - - return newNumChannels; -} + if (subproc > 0) return 0; + if (type == DataChannel::HEADSTAGE_CHANNEL) + { + int newNumChannels = 0; + for (int i = 0; i < MAX_NUM_HEADSTAGES; ++i) + { + if (headstagesArray[i]->isPlugged()) + { + newNumChannels += headstagesArray[i]->getNumActiveChannels(); + } + } -int RHD2000Thread::getNumAuxOutputs() const -{ - int numAuxOutputs = 0; + return newNumChannels; + } + if (type == DataChannel::AUX_CHANNEL) + { + int numAuxOutputs = 0; - for (int i = 0; i < MAX_NUM_HEADSTAGES; ++i) - { - if (headstagesArray[i]->isPlugged() > 0) - { - numAuxOutputs += 3; - } - } + for (int i = 0; i < MAX_NUM_HEADSTAGES; ++i) + { + if (headstagesArray[i]->isPlugged() > 0) + { + numAuxOutputs += 3; + } + } - return numAuxOutputs; + return numAuxOutputs; + } + if (type == DataChannel::ADC_CHANNEL) + { + if (acquireAdcChannels) + { + return 8; + } + else + { + return 0; + } + } } -int RHD2000Thread::getNumAdcOutputs() const -{ - if (acquireAdcChannels) - { - return 8; - } - else - { - return 0; - } -} -int RHD2000Thread::getNumEventChannels() const +int RHD2000Thread::getNumTTLOutputs(int subproc) const { - return 16; // 8 inputs, 8 outputs + if (subproc > 0) return 0; + return 8; } -float RHD2000Thread::getSampleRate() const +float RHD2000Thread::getSampleRate(int subproc) const { return evalBoard->getSampleRate(); } -float RHD2000Thread::getBitVolts (Channel* ch) const +float RHD2000Thread::getBitVolts (const DataChannel* ch) const { - if (ch->type == ADC_CHANNEL) - return getAdcBitVolts (ch->index); - else if (ch->type == AUX_CHANNEL) + if (ch->getChannelType() == DataChannel::ADC_CHANNEL) + return getAdcBitVolts (ch->getSourceTypeIndex()); + else if (ch->getChannelType() == DataChannel::AUX_CHANNEL) return 0.0000374; else return 0.195f; @@ -1075,7 +1084,7 @@ bool RHD2000Thread::enableHeadstage(int hsNum, bool enabled, int nStr, int strCh std::cout << numChannelsPerDataStream[i] << " "; }*/ - dataBuffer->resize(getNumChannels(), 10000); + sourceBuffers[0]->resize(getNumChannels(), 10000); return true; } @@ -1142,7 +1151,7 @@ void RHD2000Thread::enableAdcs(bool t) { acquireAdcChannels = t; - dataBuffer->resize (getNumChannels(), 10000); + sourceBuffers[0]->resize (getNumChannels(), 10000); } @@ -1452,7 +1461,7 @@ bool RHD2000Thread::stopAcquisition() } - dataBuffer->clear(); + sourceBuffers[0]->clear(); if (deviceFound) { @@ -1502,7 +1511,7 @@ bool RHD2000Thread::updateBuffer() } index += 8; - timestamp = Rhd2000DataBlock::convertUsbTimeStamp(bufferPtr,index); + timestamps.set(0,Rhd2000DataBlock::convertUsbTimeStamp(bufferPtr,index)); index += 4; auxIndex = index; //skip the aux channels @@ -1566,102 +1575,9 @@ bool RHD2000Thread::updateBuffer() { index += 16; } - eventCode = *(uint16*)(bufferPtr + index); + ttlEventWords.set(0, *(uint16*)(bufferPtr + index)); index += 4; - dataBuffer->addToBuffer(thisSample, ×tamp, &eventCode, 1); -#if 0 - // do the neural data channels first - for (int dataStream = 0; dataStream < enabledStreams.size(); dataStream++) - { - if ((chipId[dataStream] == CHIP_ID_RHD2132) && (numChannelsPerDataStream[dataStream] == 16)) //RHD2132 16ch. headstage - chOffset = RHD2132_16CH_OFFSET; - else - chOffset = 0; - for (int chan = 0; chan < numChannelsPerDataStream[dataStream]; chan++) - { - - // std::cout << "reading sample stream " << streamNumber << " chan " << chan << " sample "<< samp << std::endl; - - channel++; - - int value = dataBlock->amplifierData[dataStream][chan+chOffset][samp]; - - thisSample[channel] = float(value-32768)*0.195f; - } - - - } - - - // then do the Intan AUX channels - for (int dataStream = 0; dataStream < enabledStreams.size(); dataStream++) - { - if (chipId[dataStream] != CHIP_ID_RHD2164_B) //Channel B of 2164 shouldn't be copied - { - if (samp % 4 == 1) // every 4th sample should have auxiliary input data - { - - // std::cout << "reading sample stream " << streamNumber << " aux ADCs " << std::endl; - - channel++; - thisSample[channel] = 0.0000374 * - float(dataBlock->auxiliaryData[dataStream][1][samp + 0] - 32768); - // constant offset keeps the values visible in the LFP Viewer - - auxBuffer[channel] = thisSample[channel]; - - channel++; - thisSample[channel] = 0.0000374 * - float(dataBlock->auxiliaryData[dataStream][1][samp + 1] - 32768); - // constant offset keeps the values visible in the LFP Viewer - - auxBuffer[channel] = thisSample[channel]; - - - channel++; - thisSample[channel] = 0.0000374 * - float(dataBlock->auxiliaryData[dataStream][1][samp + 2] - 32768); - // constant offset keeps the values visible in the LFP Viewer - - auxBuffer[channel] = thisSample[channel]; - - } - else // repeat last values from buffer - { - - //std::cout << "reading sample stream " << streamNumber << " aux ADCs " << std::endl; - - channel++; - thisSample[channel] = auxBuffer[channel]; - channel++; - thisSample[channel] = auxBuffer[channel]; - channel++; - thisSample[channel] = auxBuffer[channel]; - } - } - - } - - // finally, loop through acquisition board ADC channels if necessary - if (acquireAdcChannels) - { - for (int adcChan = 0; adcChan < 8; ++adcChan) - { - - channel++; - // ADC waveform units = volts - thisSample[channel] = - //0.000050354 * float(dataBlock->boardAdcData[adcChan][samp]); - 0.00015258789 * float(dataBlock->boardAdcData[adcChan][samp]) - 5 - 0.4096; // account for +/-5V input range and DC offset - } - } - // std::cout << channel << std::endl; - - timestamp = dataBlock->timeStamp[samp]; - //timestamp = timestamp; - eventCode = dataBlock->ttlIn[samp]; - dataBuffer->addToBuffer(thisSample, ×tamp, &eventCode, 1); -#endif + sourceBuffers[0]->addToBuffer(thisSample, ×tamps.getReference(0), &ttlEventWords.getReference(0), 1); } } @@ -1713,9 +1629,9 @@ int RHD2000Thread::getChannelFromHeadstage (int hs, int ch) const return -1; if (hs == MAX_NUM_HEADSTAGES) //let's consider this the ADC channels { - if (getNumAdcOutputs() > 0) + if (getNumDataOutputs(DataChannel::ADC_CHANNEL, 0) > 0) { - return getNumHeadstageOutputs() + getNumAuxOutputs() + ch; + return getNumDataOutputs(DataChannel::HEADSTAGE_CHANNEL, 0) + getNumDataOutputs(DataChannel::AUX_CHANNEL, 0) + ch; } else return -1; diff --git a/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.h b/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.h index 55b1a54f6b3ea5f406330bada441894f636f54ce..1534a9cb9c3c76ca7f3aa216316f3086104932c4 100644 --- a/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.h +++ b/Source/Processors/DataThreads/RhythmNode/RHD2000Thread.h @@ -75,15 +75,16 @@ public: // for communication with SourceNode processors: bool foundInputSource() override; - int getNumHeadstageOutputs() const override; - int getNumAuxOutputs() const override; - int getNumAdcOutputs() const override; - int getNumEventChannels() const override; + int getNumDataOutputs(DataChannel::DataChannelTypes type, int subProcessor) const override; + unsigned int getNumSubProcessors() const override; + + int getNumTTLOutputs(int subprocessor) const override; + bool usesCustomNames() const override; - float getSampleRate() const override; - float getBitVolts (Channel* chan) const override; + float getSampleRate(int subprocessor) const override; + float getBitVolts (const DataChannel* chan) const override; float getAdcBitVolts (int channelNum) const; diff --git a/Source/Processors/Editors/ChannelSelector.cpp b/Source/Processors/Editors/ChannelSelector.cpp index 3f4c4bdbec890d84d6ab75101d73ded3c8ad7380..5d27e9441fc3888ce8814fff40e9d0237627b902 100755 --- a/Source/Processors/Editors/ChannelSelector.cpp +++ b/Source/Processors/Editors/ChannelSelector.cpp @@ -195,7 +195,7 @@ void ChannelSelector::setNumChannels(int numChans) //Reassign numbers according to the actual channels (useful for channel mapper) for (int n = 0; n < numButtons; ++n) { - int num = ( (GenericEditor*)getParentComponent())->getChannel (n)->nodeIndex; + int num = ( (GenericEditor*)getParentComponent())->getChannelDisplayNumber (n); static_cast<ChannelSelectorButton*> (parameterButtonsManager.getButtonAt (n))->setChannel (n + 1, num + 1); if (isNotSink) @@ -650,11 +650,11 @@ void ChannelSelector::buttonClicked(Button* button) // get audio node, and inform it of the change GenericEditor* editor = (GenericEditor*)getParentComponent(); - Channel* ch = editor->getChannel(b->getChannel() - 1); + const DataChannel* ch = editor->getChannel(b->getChannel() - 1); //int channelNum = editor->getStartChannel() + b->getChannel() - 1; bool status = b->getToggleState(); - std::cout << "Requesting audio monitor for channel " << ch->nodeIndex + 1 << std::endl; + // std::cout << "Requesting audio monitor for channel " << ch->nodeIndex + 1 << std::endl; if (acquisitionIsActive) // use setParameter to change parameter safely { @@ -671,7 +671,7 @@ void ChannelSelector::buttonClicked(Button* button) // get record node, and inform it of the change GenericEditor* editor = (GenericEditor*)getParentComponent(); - Channel* ch = editor->getChannel(b->getChannel() - 1); + const DataChannel* ch = editor->getChannel(b->getChannel() - 1); //int channelNum = editor->getStartChannel() + b->getChannel() - 1; bool status = b->getToggleState(); @@ -684,7 +684,11 @@ void ChannelSelector::buttonClicked(Button* button) else // change parameter directly { //std::cout << "Setting record status for channel " << b->getChannel() << std::endl; - ch->setRecordState(status); + + //This is another of those ugly things that will go away once the + //probe recording system is implemented, but is needed to maintain compatibility + //between the older recording system and the newer channel objects. + const_cast<DataChannel*>(ch)->setRecordState(status); } AccessClass::getGraphViewer()->repaint(); diff --git a/Source/Processors/Editors/ChannelSelector.h b/Source/Processors/Editors/ChannelSelector.h index 9ae5901e6a738fa9829d2093cc2e7ae63d66f18d..c240c5507f560109966137e5a5c4a92895175eed 100644 --- a/Source/Processors/Editors/ChannelSelector.h +++ b/Source/Processors/Editors/ChannelSelector.h @@ -27,8 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "../../../JuceLibraryCode/JuceHeader.h" #include "../Editors/GenericEditor.h" #include "../../UI/Utils/TiledButtonGroupManager.h" - -#include "../Channel/Channel.h" +#include "../Channel/InfoObjects.h" #include <stdio.h> diff --git a/Source/Processors/Editors/GenericEditor.cpp b/Source/Processors/Editors/GenericEditor.cpp index d519d44c0bc0cca890ec115bd1b03d9e5e9856df..8efefd006032e3caecb9b83a2271a77e78ca7a8f 100755 --- a/Source/Processors/Editors/GenericEditor.cpp +++ b/Source/Processors/Editors/GenericEditor.cpp @@ -30,6 +30,7 @@ #include "../../UI/ProcessorList.h" #include "../../AccessClass.h" #include "../../UI/EditorViewport.h" +#include "../../UI/GraphViewer.h" #include <math.h> @@ -126,12 +127,15 @@ void GenericEditor::setDisplayName(const String& string) repaint(); } - String GenericEditor::getDisplayName() { return displayName; } +int GenericEditor::getChannelDisplayNumber(int chan) +{ + return chan; +} void GenericEditor::addParameterEditors(bool useDefaultParameterEditors=true) { @@ -514,7 +518,7 @@ void GenericEditor::update() for (int i = 0; i < numChannels; i++) { // std::cout << p->channels[i]->getRecordState() << std::endl; - channelSelector->setRecordStatus(i, p->channels[i]->getRecordState()); + channelSelector->setRecordStatus(i, p->getDataChannel(i)->getRecordState()); } } @@ -536,15 +540,20 @@ void GenericEditor::update() } -Channel* GenericEditor::getChannel(int chan) +const DataChannel* GenericEditor::getChannel(int chan) const { - return getProcessor()->channels[chan]; + return getProcessor()->getDataChannel(chan); } -Channel* GenericEditor::getEventChannel(int chan) +const EventChannel* GenericEditor::getEventChannel(int chan) const +{ + return getProcessor()->getEventChannel(chan); +} + +const SpikeChannel* GenericEditor::getSpikeChannel(int chan) const { - return getProcessor()->eventChannels[chan]; + return getProcessor()->getSpikeChannel(chan); } Array<int> GenericEditor::getActiveChannels() diff --git a/Source/Processors/Editors/GenericEditor.h b/Source/Processors/Editors/GenericEditor.h index c023b3c7161cd7a8a844d886481783efc06aa82c..beb032bc7eab64a9b1bba963f52ebc476289abf3 100755 --- a/Source/Processors/Editors/GenericEditor.h +++ b/Source/Processors/Editors/GenericEditor.h @@ -28,6 +28,7 @@ #include "../GenericProcessor/GenericProcessor.h" #include "../../CoreServices.h" #include "../PluginManager/OpenEphysPlugin.h" +#include "../Channel/InfoObjects.h" #include <stdio.h> @@ -147,6 +148,9 @@ public: /** Get name on title bar. */ String getDisplayName(); + /** Returns a custom channel number for the Channel Selector buttons. Useful for channel mappers */ + virtual int getChannelDisplayNumber(int chan); + /** Determines how wide the editor will be drawn. */ int desiredWidth; @@ -245,10 +249,12 @@ public: Array<ParameterEditor*> parameterEditors; /** Returns the Channel object for a given continuous channel number. */ - DataChannel* getChannel (int chan); + const DataChannel* getChannel (int chan) const; /** Returns the Channel object for a given event channel number. */ - EventChannel* getEventChannel (int chan); + const EventChannel* getEventChannel (int chan) const; + + const SpikeChannel* getSpikeChannel(int chan) const; /** Stores the font used to display the editor's name. */ Font titleFont; diff --git a/Source/Processors/Events/Events.cpp b/Source/Processors/Events/Events.cpp index 5c49d3191811214809ed7c3cf4177e2942238001..033efa25e5c21e6634ce449a9e0c6f4e29f4b26c 100644 --- a/Source/Processors/Events/Events.cpp +++ b/Source/Processors/Events/Events.cpp @@ -215,7 +215,7 @@ void TTLEvent::serialize(void* dstBuffer, size_t dstSize) const serializeMetaData(buffer + eventSize); } -TTLEvent* TTLEvent::createTTLEvent(const EventChannel* channelInfo, uint64 timestamp, const void* eventData, int dataSize, uint16 channel) +TTLEventPtr TTLEvent::createTTLEvent(const EventChannel* channelInfo, uint64 timestamp, const void* eventData, int dataSize, uint16 channel) { if (!createChecks(channelInfo, EventChannel::TTL, channel)) @@ -233,7 +233,7 @@ TTLEvent* TTLEvent::createTTLEvent(const EventChannel* channelInfo, uint64 times return new TTLEvent(channelInfo, timestamp, channel, eventData); } -TTLEvent* TTLEvent::createTTLEvent(const EventChannel* channelInfo, uint64 timestamp, const void* eventData, int dataSize, const MetaDataValueArray& metaData, uint16 channel) +TTLEventPtr TTLEvent::createTTLEvent(const EventChannel* channelInfo, uint64 timestamp, const void* eventData, int dataSize, const MetaDataValueArray& metaData, uint16 channel) { if (!createChecks(channelInfo, EventChannel::TTL, channel, metaData)) @@ -254,7 +254,7 @@ TTLEvent* TTLEvent::createTTLEvent(const EventChannel* channelInfo, uint64 times return event; } -TTLEvent* TTLEvent::deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo) +TTLEventPtr TTLEvent::deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo) { size_t totalSize = msg.getRawDataSize(); size_t dataSize = channelInfo->getDataSize(); @@ -266,7 +266,8 @@ TTLEvent* TTLEvent::deserializeFromMessage(const MidiMessage& msg, const EventCh return nullptr; } const uint8* buffer = msg.getRawData(); - if ((buffer + 0) != PROCESSOR_EVENT) + //TODO: remove the mask when the probe system is implemented + if (*(buffer + 0)&0x7F != PROCESSOR_EVENT) { jassertfalse; return nullptr; @@ -278,7 +279,7 @@ TTLEvent* TTLEvent::deserializeFromMessage(const MidiMessage& msg, const EventCh return nullptr; } - if ((buffer + 1) != EventChannel::TTL) { + if (*(buffer + 1) != EventChannel::TTL) { jassertfalse; return nullptr; } @@ -327,7 +328,7 @@ void TextEvent::serialize(void* dstBuffer, size_t dstSize) const serializeMetaData(buffer + eventSize); } -TextEvent* TextEvent::createTextEvent(const EventChannel* channelInfo, uint64 timestamp, const String& text, uint16 channel) +TextEventPtr TextEvent::createTextEvent(const EventChannel* channelInfo, uint64 timestamp, const String& text, uint16 channel) { if (!createChecks(channelInfo, EventChannel::TEXT, channel)) { @@ -344,7 +345,7 @@ TextEvent* TextEvent::createTextEvent(const EventChannel* channelInfo, uint64 ti return new TextEvent(channelInfo, timestamp, channel, text); } -TextEvent* TextEvent::createTextEvent(const EventChannel* channelInfo, uint64 timestamp, const String& text, const MetaDataValueArray& metaData, uint16 channel) +TextEventPtr TextEvent::createTextEvent(const EventChannel* channelInfo, uint64 timestamp, const String& text, const MetaDataValueArray& metaData, uint16 channel) { if (!createChecks(channelInfo, EventChannel::TEXT, channel, metaData)) { @@ -364,7 +365,7 @@ TextEvent* TextEvent::createTextEvent(const EventChannel* channelInfo, uint64 ti return event; } -TextEvent* TextEvent::deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo) +TextEventPtr TextEvent::deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo) { size_t totalSize = msg.getRawDataSize(); size_t dataSize = channelInfo->getDataSize(); @@ -376,7 +377,8 @@ TextEvent* TextEvent::deserializeFromMessage(const MidiMessage& msg, const Event return nullptr; } const uint8* buffer = msg.getRawData(); - if ((buffer + 0) != PROCESSOR_EVENT) + //TODO: remove the mask when the probe system is implemented + if (*(buffer + 0)&0x7F != PROCESSOR_EVENT) { jassertfalse; return nullptr; @@ -388,7 +390,7 @@ TextEvent* TextEvent::deserializeFromMessage(const MidiMessage& msg, const Event return nullptr; } - if ((buffer + 1) != EventChannel::TEXT) { + if (*(buffer + 1) != EventChannel::TEXT) { jassertfalse; return nullptr; } @@ -461,7 +463,7 @@ void BinaryEvent::serialize(void* dstBuffer, size_t dstSize) const } template<typename T> -BinaryEvent* BinaryEvent::createBinaryEvent(const EventChannel* channelInfo, uint64 timestamp, const T* data, int dataSize, uint16 channel) +BinaryEventPtr BinaryEvent::createBinaryEvent(const EventChannel* channelInfo, uint64 timestamp, const T* data, int dataSize, uint16 channel) { EventChannel::EventChannelTypes type = getType<T>(); if (type == EventChannel::INVALID) @@ -486,7 +488,7 @@ BinaryEvent* BinaryEvent::createBinaryEvent(const EventChannel* channelInfo, uin } template<typename T> -BinaryEvent* BinaryEvent::createBinaryEvent(const EventChannel* channelInfo, uint64 timestamp, const T* data, int dataSize, const MetaDataValueArray& metaData, uint16 channel) +BinaryEventPtr BinaryEvent::createBinaryEvent(const EventChannel* channelInfo, uint64 timestamp, const T* data, int dataSize, const MetaDataValueArray& metaData, uint16 channel) { EventChannel::EventChannelTypes type = getType<T>(); if (type == EventChannel::INVALID) @@ -512,7 +514,7 @@ BinaryEvent* BinaryEvent::createBinaryEvent(const EventChannel* channelInfo, uin return event; } -BinaryEvent* BinaryEvent::deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo) +BinaryEventPtr BinaryEvent::deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo) { size_t totalSize = msg.getRawDataSize(); size_t dataSize = channelInfo->getDataSize(); @@ -524,7 +526,8 @@ BinaryEvent* BinaryEvent::deserializeFromMessage(const MidiMessage& msg, const E return nullptr; } const uint8* buffer = msg.getRawData(); - if ((buffer + 0) != PROCESSOR_EVENT) + //TODO: remove the mask when the probe system is implemented + if (*(buffer + 0)&0x7F != PROCESSOR_EVENT) { jassertfalse; return nullptr; @@ -647,7 +650,7 @@ SpikeEvent* SpikeEvent::createBasicSpike(const SpikeChannel* channelInfo, uint64 } -SpikeEvent* SpikeEvent::createSpikeEvent(const SpikeChannel* channelInfo, uint64 timestamp, Array<float> thresholds, SpikeBuffer& dataSource) +SpikeEventPtr SpikeEvent::createSpikeEvent(const SpikeChannel* channelInfo, uint64 timestamp, Array<float> thresholds, SpikeBuffer& dataSource) { if (!channelInfo) { @@ -666,7 +669,7 @@ SpikeEvent* SpikeEvent::createSpikeEvent(const SpikeChannel* channelInfo, uint64 } -SpikeEvent* SpikeEvent::createSpikeEvent(const SpikeChannel* channelInfo, uint64 timestamp, Array<float> thresholds, SpikeBuffer& dataSource, const MetaDataValueArray& metaData) +SpikeEventPtr SpikeEvent::createSpikeEvent(const SpikeChannel* channelInfo, uint64 timestamp, Array<float> thresholds, SpikeBuffer& dataSource, const MetaDataValueArray& metaData) { if (!channelInfo) { @@ -690,7 +693,7 @@ SpikeEvent* SpikeEvent::createSpikeEvent(const SpikeChannel* channelInfo, uint64 return event; } -SpikeEvent* SpikeEvent::deserializeFromMessage(const MidiMessage& msg, const SpikeChannel* channelInfo) +SpikeEventPtr SpikeEvent::deserializeFromMessage(const MidiMessage& msg, const SpikeChannel* channelInfo) { int nChans = channelInfo->getNumChannels(); size_t totalSize = msg.getRawDataSize(); @@ -704,7 +707,8 @@ SpikeEvent* SpikeEvent::deserializeFromMessage(const MidiMessage& msg, const Spi return nullptr; } const uint8* buffer = msg.getRawData(); - if ((buffer + 0) != SPIKE_EVENT) + //TODO: remove the mask when the probe system is implemented + if (*(buffer + 0)&0x7F != SPIKE_EVENT) { jassertfalse; return nullptr; @@ -760,24 +764,24 @@ float* SpikeEvent::SpikeBuffer::operator[](const int index) } //Template definitions -template BinaryEvent* BinaryEvent::createBinaryEvent<int8>(const EventChannel*, uint64, const int8* data, int, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<uint8>(const EventChannel*, uint64, const uint8* data, int, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<int16>(const EventChannel*, uint64, const int16* data, int, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<uint16>(const EventChannel*, uint64, const uint16* data, int, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<int32>(const EventChannel*, uint64, const int32* data, int, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<uint32>(const EventChannel*, uint64, const uint32* data, int, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<int64>(const EventChannel*, uint64, const int64* data, int, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<uint64>(const EventChannel*, uint64, const uint64* data, int, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<float>(const EventChannel*, uint64, const float* data, int, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<double>(const EventChannel*, uint64, const double* data, int, uint16); - -template BinaryEvent* BinaryEvent::createBinaryEvent<int8>(const EventChannel*, uint64, const int8* data, int, const MetaDataValueArray&, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<uint8>(const EventChannel*, uint64, const uint8* data, int, const MetaDataValueArray&, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<int16>(const EventChannel*, uint64, const int16* data, int, const MetaDataValueArray&, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<uint16>(const EventChannel*, uint64, const uint16* data, int, const MetaDataValueArray&, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<int32>(const EventChannel*, uint64, const int32* data, int, const MetaDataValueArray&, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<uint32>(const EventChannel*, uint64, const uint32* data, int, const MetaDataValueArray&, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<int64>(const EventChannel*, uint64, const int64* data, int, const MetaDataValueArray&, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<uint64>(const EventChannel*, uint64, const uint64* data, int, const MetaDataValueArray&, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<float>(const EventChannel*, uint64, const float* data, int, const MetaDataValueArray&, uint16); -template BinaryEvent* BinaryEvent::createBinaryEvent<double>(const EventChannel*, uint64, const double* data, int, const MetaDataValueArray&, uint16); \ No newline at end of file +template BinaryEventPtr BinaryEvent::createBinaryEvent<int8>(const EventChannel*, uint64, const int8* data, int, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<uint8>(const EventChannel*, uint64, const uint8* data, int, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<int16>(const EventChannel*, uint64, const int16* data, int, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<uint16>(const EventChannel*, uint64, const uint16* data, int, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<int32>(const EventChannel*, uint64, const int32* data, int, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<uint32>(const EventChannel*, uint64, const uint32* data, int, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<int64>(const EventChannel*, uint64, const int64* data, int, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<uint64>(const EventChannel*, uint64, const uint64* data, int, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<float>(const EventChannel*, uint64, const float* data, int, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<double>(const EventChannel*, uint64, const double* data, int, uint16); + +template BinaryEventPtr BinaryEvent::createBinaryEvent<int8>(const EventChannel*, uint64, const int8* data, int, const MetaDataValueArray&, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<uint8>(const EventChannel*, uint64, const uint8* data, int, const MetaDataValueArray&, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<int16>(const EventChannel*, uint64, const int16* data, int, const MetaDataValueArray&, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<uint16>(const EventChannel*, uint64, const uint16* data, int, const MetaDataValueArray&, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<int32>(const EventChannel*, uint64, const int32* data, int, const MetaDataValueArray&, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<uint32>(const EventChannel*, uint64, const uint32* data, int, const MetaDataValueArray&, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<int64>(const EventChannel*, uint64, const int64* data, int, const MetaDataValueArray&, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<uint64>(const EventChannel*, uint64, const uint64* data, int, const MetaDataValueArray&, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<float>(const EventChannel*, uint64, const float* data, int, const MetaDataValueArray&, uint16); +template BinaryEventPtr BinaryEvent::createBinaryEvent<double>(const EventChannel*, uint64, const double* data, int, const MetaDataValueArray&, uint16); \ No newline at end of file diff --git a/Source/Processors/Events/Events.h b/Source/Processors/Events/Events.h index b97da0837c9e1c7a2aee37963a42524311637184..ed1922980455a2c5a5081121c97ba4f1341bd0cf 100644 --- a/Source/Processors/Events/Events.h +++ b/Source/Processors/Events/Events.h @@ -41,6 +41,8 @@ Source Event index - 2 bytes Timestamp - 8 bytes Event Virtual Channel - 2 bytes data - variable + +The first bit of EventType will be set to 1 when the event is recorded to avoid re-recording events. This will go away when the probe system is implemented. */ /** @@ -54,18 +56,23 @@ Timestamp - 8 bytes Thresholds - 4bytes*nChannels Data - 4bytes*nChannels*nSamples */ +class TTLEvent; +class TextEvent; +class BinaryEvent; +class SpikeEvent; enum EventType { - SYSYEM_EVENT, + SYSTEM_EVENT, PROCESSOR_EVENT, SPIKE_EVENT }; enum SystemEventType { - TIMESTAMP = 0, //timestamp and buffer size are now the same event - PARAMETER_CHANGE = 2 + TIMESTAMP_AND_SAMPLES = 0, //timestamp and buffer size are now the same event + PARAMETER_CHANGE = 2, + TIMESTAMP_SYNC_TEXT = 3 //Special text message, not associated with any event channel, for sourcenodes to send timestamp text messages }; class PLUGIN_API EventBase @@ -118,6 +125,7 @@ protected: }; +typedef ScopedPointer<TTLEvent> TTLEventPtr; class PLUGIN_API TTLEvent : public Event { @@ -129,9 +137,9 @@ public: const void* getTTLWordPointer() const; - static TTLEvent* createTTLEvent(const EventChannel* channelInfo, uint64 timestamp, const void* eventData, int dataSize, uint16 channel); - static TTLEvent* createTTLEvent(const EventChannel* channelInfo, uint64 timestamp, const void* eventData, int dataSize, const MetaDataValueArray& metaData, uint16 channel); - static TTLEvent* deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo); + static TTLEventPtr createTTLEvent(const EventChannel* channelInfo, uint64 timestamp, const void* eventData, int dataSize, uint16 channel); + static TTLEventPtr createTTLEvent(const EventChannel* channelInfo, uint64 timestamp, const void* eventData, int dataSize, const MetaDataValueArray& metaData, uint16 channel); + static TTLEventPtr deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo); private: TTLEvent() = delete; TTLEvent(const EventChannel* channelInfo, uint64 timestamp, uint16 channel, const void* eventData); @@ -140,6 +148,7 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TTLEvent); }; +typedef ScopedPointer<TextEvent> TextEventPtr; class PLUGIN_API TextEvent : public Event { @@ -147,9 +156,9 @@ public: void serialize(void* dstBuffer, size_t dstSize) const override; String getText() const; - static TextEvent* createTextEvent(const EventChannel* channelInfo, uint64 timestamp, const String& text, uint16 channel = 0); - static TextEvent* createTextEvent(const EventChannel* channelInfo, uint64 timestamp, const String& text, const MetaDataValueArray& metaData, uint16 channel = 0); - static TextEvent* deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo); + static TextEventPtr createTextEvent(const EventChannel* channelInfo, uint64 timestamp, const String& text, uint16 channel = 0); + static TextEventPtr createTextEvent(const EventChannel* channelInfo, uint64 timestamp, const String& text, const MetaDataValueArray& metaData, uint16 channel = 0); + static TextEventPtr deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo); private: TextEvent() = delete; TextEvent(const EventChannel* channelInfo, uint64 timestamp, uint16 channel, const String& text); @@ -158,6 +167,7 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TextEvent); }; +typedef ScopedPointer<BinaryEvent> BinaryEventPtr; class PLUGIN_API BinaryEvent : public Event { @@ -168,12 +178,12 @@ public: EventChannel::EventChannelTypes getBinaryType() const; template<typename T> - static BinaryEvent* createBinaryEvent(const EventChannel* channelInfo, uint64 timestamp, const T* data, int dataSize, uint16 channel = 0); + static BinaryEventPtr createBinaryEvent(const EventChannel* channelInfo, uint64 timestamp, const T* data, int dataSize, uint16 channel = 0); template<typename T> - static BinaryEvent* createBinaryEvent(const EventChannel* channelInfo, uint64 timestamp, const T* data, int dataSize, const MetaDataValueArray& metaData, uint16 channel = 0); + static BinaryEventPtr createBinaryEvent(const EventChannel* channelInfo, uint64 timestamp, const T* data, int dataSize, const MetaDataValueArray& metaData, uint16 channel = 0); - static BinaryEvent* deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo); + static BinaryEventPtr deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo); private: BinaryEvent() = delete; @@ -187,6 +197,7 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(BinaryEvent); }; +typedef ScopedPointer<SpikeEvent> SpikeEventPtr; class PLUGIN_API SpikeEvent : public EventBase { @@ -223,10 +234,10 @@ public: float getThreshold(int chan) const; - static SpikeEvent* createSpikeEvent(const SpikeChannel* channelInfo, uint64 timestamp, Array<float> thresholds, SpikeBuffer& dataSource); - static SpikeEvent* createSpikeEvent(const SpikeChannel* channelInfo, uint64 timestamp, Array<float> thresholds, SpikeBuffer& dataSource, const MetaDataValueArray& metaData); + static SpikeEventPtr createSpikeEvent(const SpikeChannel* channelInfo, uint64 timestamp, Array<float> thresholds, SpikeBuffer& dataSource); + static SpikeEventPtr createSpikeEvent(const SpikeChannel* channelInfo, uint64 timestamp, Array<float> thresholds, SpikeBuffer& dataSource, const MetaDataValueArray& metaData); - static SpikeEvent* deserializeFromMessage(const MidiMessage& msg, const SpikeChannel* channelInfo); + static SpikeEventPtr deserializeFromMessage(const MidiMessage& msg, const SpikeChannel* channelInfo); private: SpikeEvent() = delete; SpikeEvent(const SpikeChannel* channelInfo, uint64 timestamp, Array<float> thresholds, HeapBlock<float>& data); diff --git a/Source/Processors/FileReader/FileReader.cpp b/Source/Processors/FileReader/FileReader.cpp index 2e69693e68d8e9ece802df8dcf3625c858363f5b..99e65bcb9ec79a01be5a644fe3f52878e8f0d6c2 100644 --- a/Source/Processors/FileReader/FileReader.cpp +++ b/Source/Processors/FileReader/FileReader.cpp @@ -96,8 +96,10 @@ float FileReader::getDefaultSampleRate() const } -int FileReader::getNumHeadstageOutputs() const +int FileReader::getDefaultNumDataOutputs(DataChannel::DataChannelTypes type, int subproc) const { + if (subproc != 0) return 0; + if (type != DataChannel::HEADSTAGE_CHANNEL) return 0; if (input) return currentNumChannels; else @@ -105,16 +107,10 @@ int FileReader::getNumHeadstageOutputs() const } -int FileReader::getNumEventChannels() const -{ - return 8; -} - - -float FileReader::getBitVolts (Channel* chan) const +float FileReader::getBitVolts (const DataChannel* chan) const { if (input) - return chan->bitVolts; + return chan->getBitVolts(); else return 0.05f; } @@ -232,9 +228,9 @@ void FileReader::updateSettings() } -void FileReader::process (AudioSampleBuffer& buffer, MidiBuffer& events) +void FileReader::process (AudioSampleBuffer& buffer) { - setTimestamp (events, timestamp); + const int samplesNeeded = int (float (buffer.getNumSamples()) * (getDefaultSampleRate() / 44100.0f)); // FIXME: needs to account for the fact that the ratio might not be an exact @@ -270,36 +266,8 @@ void FileReader::process (AudioSampleBuffer& buffer, MidiBuffer& events) } timestamp += samplesNeeded; - setNumSamples (events, samplesNeeded); - - // code for testing events: - // // // =========================================================================== - - // if (counter == 100) - // { - // //std::cout << "Adding on event for node id: " << nodeId << std::endl; - // addEvent (events, // MidiBuffer - // TTL, // eventType - // 0, // sampleNum - // 1, // eventID - // 0); // eventChannel - // ++counter; - // } - // else if (counter > 102) - // { - // //std::cout << "Adding off event!" << std::endl; - // addEvent (events, // MidiBuffer - // TTL, // eventType - // 0, // sampleNum - // 0, // eventID - // 0); // eventChannel - // counter = 0; - // } - // else - // { - // ++counter; - // } - // // // =========================================================================== + setTimestampAndSamples(timestamp, samplesNeeded); + } diff --git a/Source/Processors/FileReader/FileReader.h b/Source/Processors/FileReader/FileReader.h index a6976278b19cffe89ce905f7b83dd1ff72397a0c..bc1ca1e03bb1a5d8e9d10354a94b9b9973397669 100644 --- a/Source/Processors/FileReader/FileReader.h +++ b/Source/Processors/FileReader/FileReader.h @@ -45,7 +45,7 @@ public: FileReader(); ~FileReader(); - void process (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override; + void process (AudioSampleBuffer& buffer) override; void setParameter (int parameterIndex, float newValue) override; AudioProcessorEditor* createEditor() override; @@ -54,11 +54,10 @@ public: bool isGeneratesTimestamps() const override { return true; } bool isReady() override; - int getNumHeadstageOutputs() const override; - int getNumEventChannels() const override; + int getDefaultNumDataOutputs(DataChannel::DataChannelTypes type, int) const override; float getDefaultSampleRate() const override; - float getBitVolts (Channel* chan) const override; + float getBitVolts (const DataChannel* chan) const override; void updateSettings() override; void setEnabledState (bool t) override; diff --git a/Source/Processors/GenericProcessor/GenericProcessor.cpp b/Source/Processors/GenericProcessor/GenericProcessor.cpp index 02648d0c5e6127c117a3f6334058ca6aaa75a12a..51b26d81c828cae5d006915c11a4fabe30782c0d 100755 --- a/Source/Processors/GenericProcessor/GenericProcessor.cpp +++ b/Source/Processors/GenericProcessor/GenericProcessor.cpp @@ -20,7 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ - #include "GenericProcessor.h" #include "../../UI/UIComponent.h" #include "../../AccessClass.h" @@ -44,7 +43,6 @@ GenericProcessor::GenericProcessor (const String& name) , sendSampleCount (true) , m_name (name) , m_isParamsWereLoaded (false) - , m_isTimestampSet (false) , m_processorType (PROCESSOR_TYPE_UTILITY) { settings.numInputs = settings.numOutputs = 0; @@ -293,7 +291,7 @@ void GenericProcessor::clearSettings() { // std::cout << channels[i]->getRecordState() << std::endl; m_recordStatus.set (i, dataChannelArray[i]->getRecordState()); - m_monitorStatus.set (i, dataChannelArray[i]->isMonitored); + m_monitorStatus.set (i, dataChannelArray[i]->isMonitored()); } dataChannelArray.clear(); @@ -321,13 +319,12 @@ void GenericProcessor::update() { DataChannel* sourceChan = sourceNode->dataChannelArray[i]; DataChannel* ch = new DataChannel (*sourceChan); - ch-> m_nodeID = getNodeId(); if (i < m_recordStatus.size()) { ch->setRecordState (m_recordStatus[i]); - ch->isMonitored = m_monitorStatus[i]; + ch->setMonitored( m_monitorStatus[i]); } ch->addToHistoricString(getName()); @@ -338,14 +335,12 @@ void GenericProcessor::update() { EventChannel* sourceChan = sourceNode->eventChannelArray[i]; EventChannel* ch = new EventChannel (*sourceChan); - ch->m_nodeID = getNodeId(); eventChannelArray.add (ch); } for (int i = 0; i < sourceNode->spikeChannelArray.size(); ++i) { SpikeChannel* sourceChan = sourceNode->spikeChannelArray[i]; SpikeChannel* ch = new SpikeChannel(*sourceChan); - ch->m_nodeID = getNodeId(); spikeChannelArray.add(ch); } for (int i = 0; i < sourceNode->configurationObjectArray.size(); ++i) @@ -361,6 +356,14 @@ void GenericProcessor::update() std::cout << getName() << " setting num outputs to " << settings.numOutputs << std::endl; createDataChannels(); //Only sources can create data channels + for (int i = 0; i < dataChannelArray.size(); i++) + { + if (i < m_recordStatus.size()) + dataChannelArray[i]->setRecordState(m_recordStatus[i]); + else + if (isSource()) + dataChannelArray[i]->setRecordState(true); + } } //Any processor, not only sources, can add new event and spike channels. It's best to do it in their dedicated methods @@ -384,18 +387,21 @@ void GenericProcessor::update() for (int i = 0; i < dataChannelArray.size(); i++) { DataChannel* channel = dataChannelArray[i]; + channel->m_nodeID = nodeId; uint32 sourceID = getProcessorFullId(channel->getSourceNodeID(), channel->getSubProcessorIdx()); dataChannelMap[sourceID][channel->getSourceIndex()] = i; } for (int i = 0; i < eventChannelArray.size(); i++) { EventChannel* channel = eventChannelArray[i]; + channel->m_nodeID = nodeId; uint32 sourceID = getProcessorFullId(channel->getSourceNodeID(), channel->getSubProcessorIdx()); eventChannelMap[sourceID][channel->getSourceIndex()] = i; } for (int i = 0; i < spikeChannelArray.size(); i++) { SpikeChannel* channel = spikeChannelArray[i]; + channel->m_nodeID = nodeId; uint32 sourceID = getProcessorFullId(channel->getSourceNodeID(), channel->getSubProcessorIdx()); spikeChannelMap[sourceID][channel->getSourceIndex()] = i; } @@ -413,6 +419,59 @@ void GenericProcessor::update() editor->update(); // allow the editor to update its settings } +void GenericProcessor::createDataChannels() +{ + createDataChannelsByType(DataChannel::HEADSTAGE_CHANNEL); + createDataChannelsByType(DataChannel::AUX_CHANNEL); + createDataChannelsByType(DataChannel::ADC_CHANNEL); +} + +void GenericProcessor::createDataChannelsByType(DataChannel::DataChannelTypes type) +{ + int nSub = getNumSubProcessors(); + for (int sub = 0; sub < nSub; sub++) + { + int nChans = getDefaultNumDataOutputs(type, sub); + for (int i = 0; i < nChans; i++) + { + DataChannel* chan = new DataChannel(type, this, sub); + chan->setSampleRate(getSampleRate(sub)); + chan->setBitVolts(getBitVolts(sub)); + chan->addToHistoricString(getName()); + chan->m_nodeID = nodeId; + dataChannelArray.add(chan); + } + } +} + +void GenericProcessor::createEventChannels() +{ + int nSub = getNumSubProcessors(); + for (int sub = 0; sub < nSub; sub++) + { + Array<DefaultEventInfo> events; + getDefaultEventInfo(events, sub); + int nChans = events.size(); + for (int i = 0; i < nChans; i++) + { + if (events[i].type != EventChannel::INVALID && events[i].nChannels > 0 && events[i].length > 0) + { + EventChannel* chan = new EventChannel(events[i].type, events[i].nChannels, events[i].length, this, sub); + chan->setSampleRate(getSampleRate(sub)); + chan->m_nodeID = nodeId; + eventChannelArray.add(chan); + } + } + } +} + +void GenericProcessor::getDefaultEventInfo(Array<DefaultEventInfo>& events, int subproc) const +{ + events.clear(); +} + +void GenericProcessor::createSpikeChannels() {}; +void GenericProcessor::createConfigurationObjects() {}; void GenericProcessor::setAllChannelsToRecord() { @@ -473,15 +532,17 @@ void GenericProcessor::disableEditor() /** Used to get the number of samples in a given buffer, for a given channel. */ -int GenericProcessor::getNumSamples (int channelNum) const +uint32 GenericProcessor::getNumSamples (int channelNum) const { int sourceNodeId = 0; + int subProcessorId = 0; int nSamples = 0; if (channelNum >= 0 - && channelNum < channels.size()) + && channelNum < dataChannelArray.size()) { - sourceNodeId = channels[channelNum]->sourceNodeId; + sourceNodeId = dataChannelArray[channelNum]->getSourceNodeID(); + subProcessorId = dataChannelArray[channelNum]->getSubProcessorIdx(); } else { @@ -489,10 +550,10 @@ int GenericProcessor::getNumSamples (int channelNum) const } // std::cout << "Requesting samples for channel " << channelNum << " with source node " << sourceNodeId << std::endl; - + uint32 sourceID = getProcessorFullId(sourceNodeId, subProcessorId); try { - nSamples = numSamples.at (sourceNodeId); + nSamples = numSamples.at (sourceID); } catch (std::exception& e) { @@ -505,53 +566,28 @@ int GenericProcessor::getNumSamples (int channelNum) const } -/** Used to get the number of samples in a given buffer, for a given source node. */ -void GenericProcessor::setNumSamples (MidiBuffer& events, int sampleIndex) -{ - // This amounts to adding a "buffer size" flag at a particular sample number, - // and a new flag is added each time "setNumSamples" is called. - // Thus, if the number of samples changes somewhere in the processing pipeline, - // the old sample number will remain. This is a problem if the number of - // samples gets smaller. - // If we allow the sample rate to change (e.g., with a resampling node), - // this code will have to be updated. The easiest approach will be for each - // processor to ignore any buffer size events that don't come from its - // immediate source. - // - - 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 - 4, // total bytes - 0); // sample index -} - - /** Used to get the timestamp for a given buffer, for a given source node. */ -int64 GenericProcessor::getTimestamp (int channelNum) const +uint64 GenericProcessor::getTimestamp (int channelNum) const { int sourceNodeId = 0; + int subProcessorIdx = 0; int64 ts = 0; if (channelNum >= 0 - && channelNum < channels.size()) + && channelNum < dataChannelArray.size()) { - sourceNodeId = channels[channelNum]->sourceNodeId; + sourceNodeId = dataChannelArray[channelNum]->getSourceNodeID(); + subProcessorIdx = dataChannelArray[channelNum]->getSubProcessorIdx(); } else { return 0; } + uint32 sourceID = getProcessorFullId(sourceNodeId, subProcessorIdx); try { - ts = timestamps.at (sourceNodeId); + ts = timestamps.at (sourceID); } catch (std::exception& e) { @@ -563,115 +599,123 @@ int64 GenericProcessor::getTimestamp (int channelNum) const /** Used to set the timestamp for a given buffer, for a given channel. */ -void GenericProcessor::setTimestamp (MidiBuffer& events, int64 timestamp) +void GenericProcessor::setTimestampAndSamples(uint64 timestamp, uint32 nSamples, int subProcessorIdx) { + /** Event packet structure + * SYSTEM_EVENT - 1 byte + * TIMESTAMP_AND_SAMPLES - 1 byte + * Source processorID - 2 bytes + * Source Subprocessor index - 2 bytes + * Zero-fill (to maintain aligment with other events) - 2 bytes + * Timestamp - 8 bytes + * Buffer sample nimber - 4 bytes + */ + + MidiBuffer& eventBuffer = *m_currentMidiBuffer; //std::cout << "Setting timestamp to " << timestamp << std:;endl; - m_isTimestampSet = true; - uint8 data[8]; - memcpy (data, ×tamp, 8); + uint8 data[20]; + data[0] = SYSTEM_EVENT; + data[1] = TIMESTAMP_AND_SAMPLES; + *reinterpret_cast<uint16*>(data + 2) = nodeId; + *reinterpret_cast<uint16*>(data + 4) = subProcessorIdx; + data[6] = 0; + data[7] = 0; + *reinterpret_cast<uint64*>(data + 8) = timestamp; + *reinterpret_cast<uint32*>(data + 16) = nSamples; - // generate timestamp - addEvent (events, // MidiBuffer - TIMESTAMP, // eventType - 0, // sampleNum - nodeId, // eventID - 0, // eventChannel - 8, // numBytes - data, // data - true); // isTimestampEvent + eventBuffer.addEvent(data, 20, 0); + + uint32 sourceID = getProcessorFullId(nodeId, subProcessorIdx); //since the processor generating the timestamp won't get the event, add it to the map - timestamps[nodeId] = timestamp; + timestamps[sourceID] = timestamp; + numSamples[sourceID] = nSamples; - if (m_isNeedsToSendTimestampMessage) + if (m_needsToSendTimestampMessages[subProcessorIdx]) { + /** Event packet structure + * SYSTEM_EVENT - 1 byte + * TIMESTAMP_SYNC_TEXT - 1 byte + * Source processorID - 2 bytes + * Source Subprocessor index - 2 bytes + * Zero-fill (to maintain aligment with other events) - 2 bytes + * Timestamp - 8 bytes + * string - variable + */ String eventString = "Processor: " - + String (getNodeId()) + + getName() + + " Id: " + + String (nodeId) + + " subProcessor: " + + String (subProcessorIdx); + " start time: " + String (timestamp) + "@" + String (getSampleRate()) + "Hz"; - CharPointer_UTF8 data = eventString.toUTF8(); + size_t textSize = eventString.getNumBytesAsUTF8(); + size_t dataSize = 17 + textSize; + HeapBlock<char> data(dataSize, true); + data[0] = SYSTEM_EVENT; + data[1] = TIMESTAMP_SYNC_TEXT; + *reinterpret_cast<uint16*>(data.getData() + 2) = nodeId; + *reinterpret_cast<uint16*>(data.getData() + 4) = subProcessorIdx; + *reinterpret_cast<uint64*>(data.getData() + 8) = timestamp; + memcpy(data.getData() + 16, eventString.toUTF8(), textSize); - addEvent (events, - MESSAGE, - 0, - 0, - 0, - data.sizeInBytes(), //It doesn't hurt to send the end-string null and can help avoid issues - (uint8*)data.getAddress(), - true); + eventBuffer.addEvent(data, dataSize, 0); - m_isNeedsToSendTimestampMessage = false; + m_needsToSendTimestampMessages.set(subProcessorIdx, false); } } -int GenericProcessor::processEventBuffer (MidiBuffer& events) +int GenericProcessor::processEventBuffer() { - // - // This loops through all events in the buffer, and uses the BUFFER_SIZE - // events to determine the number of samples in the current buffer. If - // there are multiple such events, the one with the highest number of - // samples will be used. - // This approach is not ideal, as it will become a problem if we allow - // the sample rate to change at different points in the signal chain. - // - int numRead = 0; - - if (events.getNumEvents() > 0) - { - MidiBuffer::Iterator i (events); - - const uint8* dataptr; - int dataSize; - - int samplePosition = -1; - - while (i.getNextEvent (dataptr, dataSize, samplePosition)) - { - if (*dataptr == BUFFER_SIZE) - { - int16 nr; - memcpy (&nr, dataptr + 2, 2); - - numRead = nr; - - uint8 sourceNodeId; - memcpy (&sourceNodeId, dataptr + 1, 1); - - numSamples[sourceNodeId] = numRead; - } - else if (*dataptr == TIMESTAMP) - { - int64 ts; - memcpy (&ts, dataptr + 6, 8); + // + // This loops through all events in the buffer, and uses the BUFFER_SIZE + // events to determine the number of samples in the current buffer. If + // there are multiple such events, the one with the highest number of + // samples will be used. + // This approach is not ideal, as it will become a problem if we allow + // the sample rate to change at different points in the signal chain. + // + int numRead = 0; + + MidiBuffer& eventBuffer = *m_currentMidiBuffer; + + if (eventBuffer.getNumEvents() > 0) + { + MidiBuffer::Iterator i(eventBuffer); - uint8 sourceNodeId; - memcpy (&sourceNodeId, dataptr + 1, 1); + const uint8* dataptr; + int dataSize; - timestamps[sourceNodeId] = ts; - } - else - { + int samplePosition = -1; - if (isWritableEvent (*dataptr) // a TTL event - && getNodeId() < 900 // not handled by a specialized processor (e.g. AudioNode)) - && *(dataptr + 4) > 0) // that's flagged for saving - { - // changing the const cast is dangerous, but probably necessary: - uint8* ptr = const_cast<uint8*> (dataptr); - *(ptr + 4) = 0; // set fifth byte of raw data to 0, so the event - // won't be saved twice - } - } - } - } + while (i.getNextEvent(dataptr, dataSize, samplePosition)) + { + //TODO: remove the mask when the probe system is implemented + if (*(dataptr + 0) & 0x7F == SYSTEM_EVENT && *(dataptr + 1) == TIMESTAMP_AND_SAMPLES) + { + uint16 sourceNodeID = *reinterpret_cast<const uint16*>(dataptr + 2); + uint16 sourceSubProcessorIdx = *reinterpret_cast<const uint16*>(dataptr + 4); + uint32 sourceID = getProcessorFullId(sourceNodeID, sourceSubProcessorIdx); + + uint64 timestamp = *reinterpret_cast<const uint64*>(dataptr + 8); + uint32 nSamples = *reinterpret_cast<const uint32*>(dataptr + 16); + numSamples[sourceID] = nSamples; + timestamps[sourceID] = timestamp; + } + //set the "recorded" bit on the first byte. This will go away when the probe system is implemented. + //doing a const cast is always a bad idea, but there's no better way to do this until whe change the event record system + *const_cast<uint8*>(dataptr + 0) = *(dataptr + 0) | 0x80; + } + } - return numRead; + return numRead; } @@ -712,16 +756,16 @@ int GenericProcessor::checkForEvents(bool checkForSpikes) return -1; } -void GenericProcessor::addEvent(int channelIndex, const Event& event, int sampleNum) +void GenericProcessor::addEvent(int channelIndex, const Event* event, int sampleNum) { addEvent(eventChannelArray[channelIndex], event, sampleNum); } -void GenericProcessor::addEvent(const EventChannel* channel, const Event& event, int sampleNum) +void GenericProcessor::addEvent(const EventChannel* channel, const Event* event, int sampleNum) { size_t size = channel->getDataSize() + channel->getTotalEventMetaDataSize() + EVENT_BASE_SIZE; HeapBlock<char> buffer(size); - event.serialize(buffer, size); + event->serialize(buffer, size); m_currentMidiBuffer->addEvent(buffer, size, sampleNum); } @@ -745,11 +789,67 @@ void GenericProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& even processEventBuffer (); // extract buffer sizes and timestamps, // set flag on all TTL events to zero - m_isTimestampSet = false; + process (buffer); +} + +const DataChannel* GenericProcessor::getDataChannel(int index) const +{ + return dataChannelArray[index]; +} + +const EventChannel* GenericProcessor::getEventChannel(int index) const +{ + return eventChannelArray[index]; +} + +const SpikeChannel* GenericProcessor::getSpikeChannel(int index) const +{ + return spikeChannelArray[index]; +} - process (buffer, eventBuffer); +const ConfigurationObject* GenericProcessor::getConfigurationObject(int index) const +{ + return configurationObjectArray[index]; } +int GenericProcessor::getDataChannelIndex(int channelIdx, int processorID, int subProcessorIdx) const +{ + uint32 sourceID = getProcessorFullId(processorID, subProcessorIdx); + try + { + return dataChannelMap.at(sourceID).at(channelIdx); + } + catch (...) + { + return -1; + } +} + +int GenericProcessor::getEventChannelIndex(int channelIdx, int processorID, int subProcessorIdx) const +{ + uint32 sourceID = getProcessorFullId(processorID, subProcessorIdx); + try + { + return eventChannelMap.at(sourceID).at(channelIdx); + } + catch (...) + { + return -1; + } +} + +int GenericProcessor::getSpikeChannelIndex(int channelIdx, int processorID, int subProcessorIdx) const +{ + uint32 sourceID = getProcessorFullId(processorID, subProcessorIdx); + try + { + return spikeChannelMap.at(sourceID).at(channelIdx); + } + catch (...) + { + return -1; + } +} /////// ---- LOADING AND SAVING ---- ////////// @@ -762,18 +862,24 @@ void GenericProcessor::saveToXml (XmlElement* parentElement) // loop through the channels - for (int i = 0; i < channels.size(); ++i) + for (int i = 0; i < dataChannelArray.size(); ++i) { if (! isSplitter() && ! isMerger()) - saveChannelParametersToXml (parentElement, i); + saveChannelParametersToXml (parentElement, i, InfoObjectCommon::DATA_CHANNEL); } - for (int i = 0; i < eventChannels.size(); ++i) + for (int i = 0; i < eventChannelArray.size(); ++i) { if (! isSplitter() && ! isMerger()) - saveChannelParametersToXml (parentElement, i, true); + saveChannelParametersToXml (parentElement, i, InfoObjectCommon::EVENT_CHANNEL); } + for (int i = 0; i < spikeChannelArray.size(); ++i) + { + if (!isSplitter() && !isMerger()) + saveChannelParametersToXml(parentElement, i, InfoObjectCommon::SPIKE_CHANNEL); + } + // Save editor parameters: XmlElement* editorChildNode = parentElement->createNewChildElement ("EDITOR"); getEditor()->saveEditorParameters (editorChildNode); @@ -784,11 +890,12 @@ void GenericProcessor::saveCustomParametersToXml (XmlElement* parentElement) { } -void GenericProcessor::saveChannelParametersToXml (XmlElement* parentElement, int channelNumber, bool isEventChannel) +void GenericProcessor::saveChannelParametersToXml (XmlElement* parentElement, int channelNumber, InfoObjectCommon::InfoObjectType type) { - if (! isEventChannel) + XmlElement* channelInfo; + if ( type == InfoObjectCommon::DATA_CHANNEL) { - XmlElement* channelInfo = parentElement->createNewChildElement ("CHANNEL"); + channelInfo = parentElement->createNewChildElement ("CHANNEL"); channelInfo->setAttribute ("name", String (channelNumber)); channelInfo->setAttribute ("number", channelNumber); @@ -800,17 +907,21 @@ void GenericProcessor::saveChannelParametersToXml (XmlElement* parentElement, in selectionState->setAttribute ("param", p); selectionState->setAttribute ("record", r); selectionState->setAttribute ("audio", a); - - saveCustomChannelParametersToXml (channelInfo, channelNumber); } - else + else if (type == InfoObjectCommon::EVENT_CHANNEL) { - XmlElement* channelInfo = parentElement->createNewChildElement ("EVENTCHANNEL"); + channelInfo = parentElement->createNewChildElement ("EVENTCHANNEL"); channelInfo->setAttribute ("name", String (channelNumber)); channelInfo->setAttribute ("number", channelNumber); - saveCustomChannelParametersToXml (channelInfo, channelNumber, true); } + else if (type == InfoObjectCommon::SPIKE_CHANNEL) + { + channelInfo = parentElement->createNewChildElement("SPIKECHANNEL"); + channelInfo->setAttribute("name", String(channelNumber)); + channelInfo->setAttribute("number", channelNumber); + } + saveCustomChannelParametersToXml(channelInfo, channelNumber, type); // deprecated parameter configuration: //std::cout <<"Creating Parameters" << std::endl; @@ -833,7 +944,7 @@ void GenericProcessor::saveChannelParametersToXml (XmlElement* parentElement, in // } } -void GenericProcessor::saveCustomChannelParametersToXml (XmlElement* channelInfo, int channelNum, bool isEventChannel) +void GenericProcessor::saveCustomChannelParametersToXml (XmlElement* channelInfo, int channelNum, InfoObjectCommon::InfoObjectType type) { } @@ -864,12 +975,16 @@ void GenericProcessor::loadFromXml() { if (xmlNode->hasTagName ("CHANNEL")) { - loadChannelParametersFromXml (xmlNode); + loadChannelParametersFromXml (xmlNode, InfoObjectCommon::DATA_CHANNEL); } else if (xmlNode->hasTagName ("EVENTCHANNEL")) { - loadChannelParametersFromXml (xmlNode, true); + loadChannelParametersFromXml (xmlNode, InfoObjectCommon::EVENT_CHANNEL); } + else if (xmlNode->hasTagName("SPIKECHANNEL")) + { + loadChannelParametersFromXml(xmlNode, InfoObjectCommon::SPIKE_CHANNEL); + } } } } @@ -878,11 +993,11 @@ void GenericProcessor::loadFromXml() } -void GenericProcessor::loadChannelParametersFromXml (XmlElement* channelInfo, bool isEventChannel) +void GenericProcessor::loadChannelParametersFromXml (XmlElement* channelInfo, InfoObjectCommon::InfoObjectType type) { int channelNum = channelInfo->getIntAttribute ("number"); - if (! isEventChannel) + if (type == InfoObjectCommon::DATA_CHANNEL) { forEachXmlChildElement (*channelInfo, subNode) { @@ -896,12 +1011,12 @@ void GenericProcessor::loadChannelParametersFromXml (XmlElement* channelInfo, bo } } - loadCustomChannelParametersFromXml (channelInfo, isEventChannel); + loadCustomChannelParametersFromXml (channelInfo, type); } void GenericProcessor::loadCustomParametersFromXml() { } -void GenericProcessor::loadCustomChannelParametersFromXml (XmlElement* channelInfo, bool isEventChannel) { } +void GenericProcessor::loadCustomChannelParametersFromXml (XmlElement* channelInfo, InfoObjectCommon::InfoObjectType type) { } void GenericProcessor::reset() {} @@ -976,8 +1091,14 @@ int GenericProcessor::getCurrentProgram() { return 0; } int GenericProcessor::getNumInputs() const { return settings.numInputs; } int GenericProcessor::getNumOutputs() const { return settings.numOutputs; } -int GenericProcessor::getNumDataOutputs(DataChannel::DataChannelTypes) const { return 0; } -int GenericProcessor::getNumEventOutputs(EventChannel::EventChannelTypes) const { return 0; } +int GenericProcessor::getNumOutputs(int subProcessorIdx) const //Defaults to only one subprocessor +{ + if (subProcessorIdx == 0) + return getNumOutputs(); + return 0; +} +int GenericProcessor::getDefaultNumDataOutputs(DataChannel::DataChannelTypes, int) const { return 0; } + int GenericProcessor::getNodeId() const { return nodeId; } int GenericProcessor::getTotalNumberOfChannels() const { return dataChannelArray.size() + eventChannelArray.size() + spikeChannelArray.size(); } @@ -987,11 +1108,14 @@ float GenericProcessor::getParameter (int parameterIndex) { return 1.0; } float GenericProcessor::getDefaultSampleRate() const { return 44100.0; } float GenericProcessor::getSampleRate(int) const { return getDefaultSampleRate(); } float GenericProcessor::getDefaultBitVolts() const { return 1.0; } -float GenericProcessor::getBitVolts (DataChannel* chan) const { return 1.0; } +float GenericProcessor::getBitVolts(int) const { return getDefaultBitVolts(); } +float GenericProcessor::getBitVolts (const DataChannel* chan) const { return 1.0; } GenericProcessor* GenericProcessor::getSourceNode() const { return sourceNode; } GenericProcessor* GenericProcessor::getDestNode() const { return destNode; } +int GenericProcessor::getNumSubProcessors() const { return 1; } + GenericEditor* GenericProcessor::getEditor() const { return editor; } AudioSampleBuffer* GenericProcessor::getContinuousBuffer() const { return 0; } @@ -1030,3 +1154,16 @@ bool GenericProcessor::disable() return true; } +GenericProcessor::DefaultEventInfo::DefaultEventInfo(EventChannel::EventChannelTypes t, unsigned int c, unsigned int l) + :type(t), + nChannels(c), + length(l) +{ +} + +GenericProcessor::DefaultEventInfo::DefaultEventInfo() + :type(EventChannel::INVALID), + nChannels(0), + length(0) +{ +} \ No newline at end of file diff --git a/Source/Processors/GenericProcessor/GenericProcessor.h b/Source/Processors/GenericProcessor/GenericProcessor.h index 315d263d79f59f4fda028bb06e75056c46849b31..98b71241d70c33944ae85c820c9e3c8e64ad363f 100755 --- a/Source/Processors/GenericProcessor/GenericProcessor.h +++ b/Source/Processors/GenericProcessor/GenericProcessor.h @@ -24,13 +24,6 @@ #ifndef __GENERICPROCESSOR_H_1F469DAF__ #define __GENERICPROCESSOR_H_1F469DAF__ -enum ChannelType -{ - HEADSTAGE_CHANNEL = 0 - , AUX_CHANNEL = 1 - , ADC_CHANNEL = 2 -}; - #include <JuceHeader.h> #include "../Editors/GenericEditor.h" #include "../Parameter/Parameter.h" @@ -54,6 +47,7 @@ class Parameter; using namespace Plugin; + class ChannelCreationIndexes { public: @@ -71,6 +65,13 @@ private: std::unordered_map<SpikeChannel::ElectrodeTypes, int> spikeChannelTypeCount; }; + +namespace AccessClass +{ + class MidiBufferAccessor; +}; + + /** Abstract base class for creating processors. @@ -89,6 +90,7 @@ class PLUGIN_API GenericProcessor : public AudioProcessor , public PluginClass , public ChannelCreationIndexes { + friend AccessClass::MidiBufferAccessor; public: /** Constructor (sets the processor's name). */ GenericProcessor (const String& name_); @@ -230,7 +232,7 @@ public: automatically calls process() in order to add the 'nSamples' variable to indicate the number of samples in the current buffer. */ - virtual void process (AudioSampleBuffer& continuousBuffer, MidiBuffer& eventBuffer) = 0; + virtual void process (AudioSampleBuffer& continuousBuffer) = 0; /** Pointer to a processor's immediate source node.*/ GenericProcessor* sourceNode; @@ -253,17 +255,15 @@ public: /** Returns the number of outputs from a specific processor.*/ virtual int getNumOutputs(int subProcessorIdx) const; - /** Returns the default number of datachannels outputs for a specific type*/ - virtual int getNumDataOutputs(DataChannel::DataChannelTypes type) const; - - /** Returns the default number of event channel outputs for a specific type*/ - virtual int getNumEventOutputs(EventChannel::EventChannelTypes type) const; - /** 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() const; + /** Returns the default bitVolts value for a given subprocessor + @see getDefaultBitVolts */ + virtual float getBitVolts(int subprocessorIdx = 0) const; + /** Returns the bit volts for a given channel **/ - virtual float getBitVolts (DataChannel* chan) const; + virtual float getBitVolts (const DataChannel* chan) const; /** Returns the next available channel (and increments the channel if the input is set to 'true'. */ virtual int getNextChannel (bool t); @@ -381,20 +381,6 @@ public: int nextAvailableChannel; - /** Can be called by processors that need to respond to incoming events. - Set respondToSpikes to true if the processor should also search for spikes*/ - virtual int checkForEvents (bool respondToSpikes = false); - - /** Makes it easier for processors to respond to incoming events, such as TTLs. - - Called by checkForEvents(). */ - virtual void handleEvent (EventChannel* eventInfo, MidiMessage& event, int samplePosition = 0); - - /** Makes it easier for processors to respond to incoming spikes. - - Called by checkForEvents(). */ - virtual void handleSpike(SpikeChannel* spikeInfo, MidiMessage& event, int samplePosition = 0); - /** Variable used to orchestrate saving the ProcessorGraph. */ int saveOrder; @@ -430,27 +416,9 @@ public: /** Default method for updating settings, called by every processor.*/ virtual void update(); - /** Method to create the data channels pertaining to this processor, called automatically by update() */ - virtual void createDataChannels(); - - /** Method to create the event channels pertaining to this processor, called automatically by update() */ - virtual void createEventChannels(); - - /** Method to create the spike channels pertaining to this processor, called automatically by update() */ - virtual void createSpikeChannels(); - - /** Method to create the configuration objects pertaining to this processor, called automatically by update() */ - virtual void createConfigurationObjects(); - - /** Custom method for updating settings, called automatically by update() after creating the info objects.*/ - virtual void updateSettings(); - /** Toggles record ON for all channels */ void setAllChannelsToRecord(); - /** Each processor has a unique integer ID that can be used to identify it.*/ - int nodeId; - /** An array of parameters that the user can modify.*/ OwnedArray<Parameter> parameters; @@ -469,10 +437,10 @@ public: virtual void saveCustomParametersToXml (XmlElement* parentElement); /** Saving generic settings for each channel (called by all processors). */ - void saveChannelParametersToXml (XmlElement* parentElement, int channelNumber, bool isEventChannel = false); + void saveChannelParametersToXml(XmlElement* parentElement, int channelNumber, InfoObjectCommon::InfoObjectType channelType); /** Saving custom settings for each channel. */ - virtual void saveCustomChannelParametersToXml (XmlElement* channelElement, int channelNumber, bool isEventChannel = false); + virtual void saveCustomChannelParametersToXml(XmlElement* channelElement, int channelNumber, InfoObjectCommon::InfoObjectType channelType); /** Load generic settings from XML (called by all processors). */ void loadFromXml(); @@ -481,10 +449,10 @@ public: virtual void loadCustomParametersFromXml(); /** Load generic parameters for each channel (called by all processors). */ - void loadChannelParametersFromXml (XmlElement* channelElement, bool isEventChannel = false); + void loadChannelParametersFromXml (XmlElement* channelElement, InfoObjectCommon::InfoObjectType channelType); /** Load custom parameters for each channel. */ - virtual void loadCustomChannelParametersFromXml (XmlElement* channelElement, bool isEventChannel = false); + virtual void loadCustomChannelParametersFromXml(XmlElement* channelElement, InfoObjectCommon::InfoObjectType channelType); /** Holds loaded parameters */ XmlElement* parametersAsXml; @@ -493,15 +461,15 @@ public: bool sendSampleCount; /** Used to get the number of samples in a given buffer, for a given channel. */ - int getNumSamples (int channelNumber, int subProcessorIdx = 0) const; + uint32 getNumSamples (int channelNumber) const; /** Used to get the timestamp for a given buffer, for a given channel. */ - int64 getTimestamp (int channelNumber) const; + uint64 getTimestamp (int channelNumber) const; /** Used to set the timestamp for a given buffer, for a given source node. */ - void setTimestampAndSamples (int64 timestamp, int nSamples, int subProcessorIdx = 0); + void setTimestampAndSamples (uint64 timestamp, uint32 nSamples, int subProcessorIdx = 0); - int getNumSubProcessors() const; + virtual int getNumSubProcessors() const; int getDataChannelIndex(int channelIdx, int processorID, int subProcessorIdx = 0) const; @@ -517,14 +485,54 @@ public: const ConfigurationObject* getConfigurationObject(int index) const; + int getTotalDataChannels() const; + + int getTotalEventChannels() const; + + int getTotalSpikeChannels() const; + + int getTotalConfigurationObjects() const; PluginProcessorType getProcessorType() const; - std::map<uint8, int> numSamples; - std::map<uint8, int64> timestamps; + static uint32 getProcessorFullId(uint16 processorId, uint16 subprocessorIdx); + struct DefaultEventInfo + { + DefaultEventInfo(); + DefaultEventInfo(EventChannel::EventChannelTypes type, unsigned int nChans, unsigned int length); + EventChannel::EventChannelTypes type{ EventChannel::INVALID }; + unsigned int nChannels{ 0 }; + unsigned int length{ 0 }; + }; protected: + + /** Can be called by processors that need to respond to incoming events. + Set respondToSpikes to true if the processor should also search for spikes*/ + virtual int checkForEvents(bool respondToSpikes = false); + + /** Makes it easier for processors to respond to incoming events, such as TTLs. + + Called by checkForEvents(). */ + virtual void handleEvent(EventChannel* eventInfo, MidiMessage& event, int samplePosition = 0); + + /** Makes it easier for processors to respond to incoming spikes. + + Called by checkForEvents(). */ + virtual void handleSpike(SpikeChannel* spikeInfo, MidiMessage& event, int samplePosition = 0); + + /** Returns the default number of datachannels outputs for a specific type and a specific subprocessor + Called by createDataChannels(). It is not needed to implement if createDataChannels() is overriden */ + virtual int getDefaultNumDataOutputs(DataChannel::DataChannelTypes type, int subProcessorIdx = 0) const; + + /** Returns info about the default events a specific subprocessor generates. + Called by createEventChannels(). It is not needed to implement if createEventChannels() is overriden */ + virtual void getDefaultEventInfo(Array<DefaultEventInfo>& events, int subProcessorIdx = 0) const; + + std::map<uint32, uint32> numSamples; + std::map<uint32, int64> timestamps; + /** Sets whether processor will have behaviour like Source, Sink, Splitter, Utility or Merge */ void setProcessorType (PluginProcessorType processorType); @@ -533,15 +541,38 @@ protected: OwnedArray<SpikeChannel> spikeChannelArray; OwnedArray<ConfigurationObject> configurationObjectArray; - void addEvent(int channelIndex, const Event& event, int sampleNum); - void addEvent(const EventChannel* channel, const Event& event, int sampleNum); + void addEvent(int channelIndex, const Event* event, int sampleNum); + void addEvent(const EventChannel* channel, const Event* event, int sampleNum); void addSpike(int channelIndex, const SpikeEvent& event, int sampleNum); void addSpike(const SpikeChannel* channel, const SpikeEvent& event, int sampleNum); + /** Method to create the data channels pertaining to this processor, called automatically by update()*/ + virtual void createDataChannels(); + + /** Method to create the event channels pertaining to this processor, called automatically by update() + Channels created by the default non-overloaded version of the method will be of 1 byte long and + only 1 channel. Those values can be changed in the updatesettings method or overloading this method + to add the channels with the proper characteristics */ + virtual void createEventChannels(); + + /** Method to create the spike channels pertaining to this processor, called automatically by update() */ + virtual void createSpikeChannels(); + + /** Method to create the configuration objects pertaining to this processor, called automatically by update() */ + virtual void createConfigurationObjects(); + + /** Custom method for updating settings, called automatically by update() after creating the info objects.*/ + virtual void updateSettings(); + private: + void createDataChannelsByType(DataChannel::DataChannelTypes type); + + /** Each processor has a unique integer ID that can be used to identify it.*/ + int nodeId; + /** Automatically extracts the number of samples in the buffer, then calls the process(), where custom actions take place.*/ virtual void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); @@ -565,17 +596,14 @@ private: bool m_isParamsWereLoaded; Array<bool> m_needsToSendTimestampMessages; - bool m_isTimestampSet; - MidiBuffer* m_currentMidiBuffer; - typedef std::map<uint16, unsigned int> ChannelIndexes; + typedef std::map<uint16, int> ChannelIndexes; typedef std::unordered_map<uint32, ChannelIndexes> ChannelIndexMap; ChannelIndexMap dataChannelMap; ChannelIndexMap eventChannelMap; ChannelIndexMap spikeChannelMap; - static uint32 getProcessorFullId(uint16 processorId, uint16 subprocessorIdx); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericProcessor); diff --git a/Source/Processors/Merger/Merger.cpp b/Source/Processors/Merger/Merger.cpp index 4fffa10b81171aacb64575b8ec64a8c6dbca53ee..9732a34ef7e2bbe00a9fadeb9642d144572ddbcc 100755 --- a/Source/Processors/Merger/Merger.cpp +++ b/Source/Processors/Merger/Merger.cpp @@ -26,7 +26,7 @@ #include "../../UI/EditorViewport.h" #include "../../AccessClass.h" -#include "../Channel/Channel.h" + Merger::Merger() : GenericProcessor("Merger"), @@ -121,7 +121,7 @@ bool Merger::sendEventsForSource(GenericProcessor* sourceNode) return false; } -bool Merger::stillHasSource() +bool Merger::stillHasSource() const { if (sourceNodeA == 0 || sourceNodeB == 0) { @@ -159,27 +159,38 @@ void Merger::addSettingsFromSourceNode(GenericProcessor* sn) { settings.numInputs += sn->getNumOutputs(); - for (int i = 0; i < sn->channels.size(); i++) + for (int i = 0; i < sn->getTotalDataChannels(); i++) { - Channel* sourceChan = sn->channels[i]; - Channel* ch = new Channel(*sourceChan); - channels.add(ch); + const DataChannel* sourceChan = sn->getDataChannel(i); + DataChannel* ch = new DataChannel(*sourceChan); + dataChannelArray.add(ch); } } if (sendEventsForSource(sn)) { - for (int i = 0; i < sn->eventChannels.size(); i++) + for (int i = 0; i < sn->getTotalEventChannels(); i++) { - Channel* sourceChan = sn->eventChannels[i]; - Channel* ch = new Channel(*sourceChan); - eventChannels.add(ch); + const EventChannel* sourceChan = sn->getEventChannel(i); + EventChannel* ch = new EventChannel(*sourceChan); + eventChannelArray.add(ch); } + for (int i = 0; i < sn->getTotalSpikeChannels(); i++) + { + const SpikeChannel* sourceChan = sn->getSpikeChannel(i); + SpikeChannel* ch = new SpikeChannel(*sourceChan); + spikeChannelArray.add(ch); + } } + for (int i = 0; i < sn->getTotalConfigurationObjects(); i++) + { + const ConfigurationObject* sourceChan = sn->getConfigurationObject(i); + ConfigurationObject* ch = new ConfigurationObject(*sourceChan); + configurationObjectArray.add(ch); + } settings.originalSource = sn->settings.originalSource; - settings.sampleRate = sn->settings.sampleRate; settings.numOutputs = settings.numInputs; @@ -207,17 +218,17 @@ void Merger::updateSettings() if (sourceNodeA == 0 && sourceNodeB == 0) { - settings.sampleRate = getDefaultSampleRate(); - settings.numOutputs = getNumHeadstageOutputs(); - for (int i = 0; i < getNumOutputs(); i++) + settings.numOutputs = getNumOutputs(); + + /* for (int i = 0; i < getNumOutputs(); i++) { Channel* ch = new Channel(this, i, HEADSTAGE_CHANNEL); ch->sampleRate = getDefaultSampleRate(); ch->bitVolts = getDefaultBitVolts(); channels.add(ch); - } + }*/ //generateDefaultChannelNames(settings.outputChannelNames); } diff --git a/Source/Processors/Merger/Merger.h b/Source/Processors/Merger/Merger.h index d25016cdd1a0885786d86a802fe9e4e21bcdab7a..de1562bfe77fa45490e75b09158edc9fc1d1f07d 100755 --- a/Source/Processors/Merger/Merger.h +++ b/Source/Processors/Merger/Merger.h @@ -52,24 +52,24 @@ public: AudioProcessorEditor* createEditor(); /** Nothing happens here, because Mergers are not part of the ProcessorGraph. */ - void process(AudioSampleBuffer& buffer, MidiBuffer& midiMessages) {} + void process(AudioSampleBuffer& buffer) override {} - bool isMerger() + bool isMerger() const override { return true; } - void switchIO(int); - void switchIO(); - void setMergerSourceNode(GenericProcessor* sn); + void switchIO(int) override; + void switchIO() override; + void setMergerSourceNode(GenericProcessor* sn) override; - void updateSettings(); + void updateSettings() override; void addSettingsFromSourceNode(GenericProcessor* sn); - bool stillHasSource(); + bool stillHasSource() const override; - void saveCustomParametersToXml(XmlElement* parentElement); - void loadCustomParametersFromXml(); + void saveCustomParametersToXml(XmlElement* parentElement) override; + void loadCustomParametersFromXml() override; bool sendContinuousForSource(GenericProcessor* sn); bool sendEventsForSource(GenericProcessor* sn); diff --git a/Source/Processors/MessageCenter/MessageCenter.cpp b/Source/Processors/MessageCenter/MessageCenter.cpp index ba17df9bb0fbf39d45a264c72de166666146715f..78581492e929dfb3df903267de15a4d34e024efe 100644 --- a/Source/Processors/MessageCenter/MessageCenter.cpp +++ b/Source/Processors/MessageCenter/MessageCenter.cpp @@ -25,7 +25,7 @@ #include "MessageCenterEditor.h" #include "../ProcessorGraph/ProcessorGraph.h" #include "../../AccessClass.h" - +#define MAX_MSG_LENGTH 512 //--------------------------------------------------------------------- MessageCenter::MessageCenter() : @@ -37,10 +37,6 @@ MessageCenter::MessageCenter() : 0, // number of outputs 44100.0, // sampleRate 128); // blockSize - - Channel* ch = new Channel(this, 0, EVENT_CHANNEL); - eventChannels.add(ch); - } MessageCenter::~MessageCenter() @@ -48,6 +44,12 @@ MessageCenter::~MessageCenter() } +void MessageCenter::getDefaultEventInfo(Array<DefaultEventInfo>& events, int sub) const +{ + if (sub > 0) return; + events.add(DefaultEventInfo(EventChannel::TEXT, 1, MAX_MSG_LENGTH)); +} + AudioProcessorEditor* MessageCenter::createEditor() { @@ -120,22 +122,26 @@ int64 MessageCenter::getTimestamp(bool softwareTime) return (softTimestamp); } -void MessageCenter::process(AudioSampleBuffer& buffer, MidiBuffer& eventBuffer) +void MessageCenter::process(AudioSampleBuffer& buffer) { softTimestamp = Time::getHighResolutionTicks() - lastTime; - setTimestamp(eventBuffer,getTimestamp()); + setTimestampAndSamples(getTimestamp(), 0); if (needsToSendTimestampMessage) { + MidiBuffer& eventBuffer = *AccessClass::getProcessorMidiBuffer(this); String eventString = "Software time: " + String(getTimestamp(true)) + "@" + String(Time::getHighResolutionTicksPerSecond()) + "Hz"; - CharPointer_UTF8 data = eventString.toUTF8(); - - addEvent(eventBuffer, - MESSAGE, - 0, - 0, - 0, - data.sizeInBytes(), //It doesn't hurt to send the end-string null and can help avoid issues - (uint8*)data.getAddress()); + + size_t textSize = eventString.getNumBytesAsUTF8(); + size_t dataSize = 17 + textSize; + HeapBlock<char> data(dataSize, true); + data[0] = SYSTEM_EVENT; + data[1] = TIMESTAMP_SYNC_TEXT; + *reinterpret_cast<uint16*>(data.getData() + 2) = getNodeId(); + *reinterpret_cast<uint16*>(data.getData() + 4) = 0; + *reinterpret_cast<uint64*>(data.getData() + 8) = getTimestamp(true); + memcpy(data.getData() + 16, eventString.toUTF8(), textSize); + + eventBuffer.addEvent(data, dataSize, 0); needsToSendTimestampMessage = false; } @@ -146,15 +152,10 @@ void MessageCenter::process(AudioSampleBuffer& buffer, MidiBuffer& eventBuffer) String eventString = messageCenterEditor->getLabelString(); - CharPointer_UTF8 data = eventString.toUTF8(); + eventString = eventString.dropLastCharacters(eventString.length() - MAX_MSG_LENGTH); - addEvent(eventBuffer, - MESSAGE, - 0, - 0, - 0, - data.sizeInBytes(), //It doesn't hurt to send the end-string null and can help avoid issues - (uint8*) data.getAddress()); + TextEventPtr event = TextEvent::createTextEvent(getEventChannel(0), getTimestamp(), eventString); + addEvent(getEventChannel(0), event, 0); newEventAvailable = false; } diff --git a/Source/Processors/MessageCenter/MessageCenter.h b/Source/Processors/MessageCenter/MessageCenter.h index c2f2896aa0609231172a4ee287e54706ba5fc0c1..d3266cf134695cbc26ec90c0d63869166769078e 100644 --- a/Source/Processors/MessageCenter/MessageCenter.h +++ b/Source/Processors/MessageCenter/MessageCenter.h @@ -50,26 +50,26 @@ public: ~MessageCenter(); /** Handle incoming data and decide which files and events to write to disk. */ - void process(AudioSampleBuffer& buffer, MidiBuffer& eventBuffer); + void process(AudioSampleBuffer& buffer) override; /** Called when new events arrive. */ - void setParameter(int parameterIndex, float newValue); + void setParameter(int parameterIndex, float newValue) override; /** Creates the MessageCenterEditor (located in the UI component). */ - AudioProcessorEditor* createEditor(); + AudioProcessorEditor* createEditor() override; /** A pointer to the Message Center editor. */ ScopedPointer<MessageCenterEditor> messageCenterEditor; - bool enable(); - bool disable(); + bool enable() override; + bool disable() override; - void startRecording() + void startRecording() override { isRecording = true; needsToSendTimestampMessage = true; } - void stopRecording() + void stopRecording() override { isRecording = false; needsToSendTimestampMessage = false; @@ -82,6 +82,8 @@ public: void removeSourceProcessor(GenericProcessor* p); int64 getTimestamp(bool softwareTime = false); +protected: + void getDefaultEventInfo(Array<DefaultEventInfo>& events, int) const override; private: bool newEventAvailable; diff --git a/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.cpp b/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.cpp index 8b995d60b6d0dfa8cffabc66c90b5ba86ce9bab6..bea8ec0944c6fd5c127dd26a26e7c92d999699fb 100644 --- a/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.cpp +++ b/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.cpp @@ -54,7 +54,7 @@ AudioProcessorEditor* PlaceholderProcessor::createEditor() } -void PlaceholderProcessor::process (AudioSampleBuffer& continuousBuffer, MidiBuffer& eventBuffer) +void PlaceholderProcessor::process (AudioSampleBuffer& continuousBuffer) { } diff --git a/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.h b/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.h index 70e365b2ffeb32783a722f3ddc5a6459aec4d85e..b48e96d77efe81efe99a8e154848ecd568991c0e 100644 --- a/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.h +++ b/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.h @@ -42,7 +42,7 @@ public: bool isSink() const override; bool isReady() override; - void process (AudioSampleBuffer& continuousBuffer, MidiBuffer& eventBuffer) override; + void process (AudioSampleBuffer& continuousBuffer) override; private: diff --git a/Source/Processors/ProcessorGraph/ProcessorGraph.cpp b/Source/Processors/ProcessorGraph/ProcessorGraph.cpp index f51da0ad39402ae6f5ced2e937c77023d898fb0f..a1682a65e4b4c62c63e38b24a652566482c971bc 100644 --- a/Source/Processors/ProcessorGraph/ProcessorGraph.cpp +++ b/Source/Processors/ProcessorGraph/ProcessorGraph.cpp @@ -435,7 +435,9 @@ void ProcessorGraph::connectProcessorToAudioAndRecordNodes(GenericProcessor* sou // THIS IS A HACK TO MAKE SURE AUDIO NODE KNOWS WHAT THE SAMPLE RATE SHOULD BE // IT CAN CAUSE PROBLEMS IF THE SAMPLE RATE VARIES ACROSS PROCESSORS - getAudioNode()->settings.sampleRate = source->getSampleRate(); + + //TODO: See if this causes problems with the newer architectures + //getAudioNode()->settings.sampleRate = source->getSampleRate(); addConnection(source->getNodeId(), // sourceNodeID chan, // sourceNodeChannelIndex @@ -550,7 +552,7 @@ void ProcessorGraph::removeProcessor(GenericProcessor* processor) //GenericProcessor* p = static_cast<GenericProcessor*>(getNode(i)->getProcessor()); if (p && p->isSource() && p->isGeneratesTimestamps()) { - newId = p->nodeId; + newId = p->getNodeId(); } } } diff --git a/Source/Processors/RecordNode/RecordNode.h b/Source/Processors/RecordNode/RecordNode.h index ef81e73f5034342ae512a9eb771a385f36437d79..1d315668f9409ae64d8a03c511ab0f2b27fd3d54 100755 --- a/Source/Processors/RecordNode/RecordNode.h +++ b/Source/Processors/RecordNode/RecordNode.h @@ -30,7 +30,6 @@ #include "../GenericProcessor/GenericProcessor.h" -#include "../Channel/Channel.h" #include "EventQueue.h" #define WRITE_BLOCK_LENGTH 1024 @@ -108,13 +107,13 @@ public: /** Selects a channel relative to a particular processor with ID = id */ - void setChannel(Channel* ch); + void setChannel(const DataChannel* ch); /** Turns recording on and off for a particular channel. Channel numbers are absolute (based on RecordNode channel mapping). */ - void setChannelStatus(Channel* ch, bool status); + void setChannelStatus(const DataChannel* ch, bool status); /** Used to clear all connections prior to the start of acquisition. */ diff --git a/Source/Processors/SourceNode/SourceNode.cpp b/Source/Processors/SourceNode/SourceNode.cpp index 25911415df9900b148ee92b6dd98ef87d0355431..d807976b3307556fa3e5a4b93ca86c636c8954ae 100755 --- a/Source/Processors/SourceNode/SourceNode.cpp +++ b/Source/Processors/SourceNode/SourceNode.cpp @@ -23,7 +23,6 @@ #include "SourceNode.h" #include "../SourceNode/SourceNodeEditor.h" -#include "../Channel/Channel.h" #include <stdio.h> #include "../../AccessClass.h" #include "../PluginManager/OpenEphysPlugin.h" @@ -34,7 +33,6 @@ SourceNode::SourceNode (const String& name_, DataThreadCreator dt) , sourceCheckInterval (2000) , wasDisabled (true) , dataThread (nullptr) - , inputBuffer (0) , ttlState (0) { setProcessorType (PROCESSOR_TYPE_SOURCE); @@ -48,27 +46,18 @@ SourceNode::SourceNode (const String& name_, DataThreadCreator dt) setEnabledState (false); } - numEventChannels = dataThread->getNumEventChannels(); - //eventChannelState = new int[numEventChannels]; - eventChannelState.malloc (numEventChannels); - for (int i = 0; i < numEventChannels; ++i) - { - eventChannelState[i] = 0; - } + resizeBuffers(); } else { setEnabledState (false); // eventChannelState = 0; - numEventChannels = 0; } // check for input source every few seconds startTimer (sourceCheckInterval); timestamp = 0; - //eventCodeBuffer = new uint64[10000]; //10000 samples per buffer max? - eventCodeBuffer.malloc (10000); } @@ -81,6 +70,25 @@ SourceNode::~SourceNode() } } +//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() { @@ -97,13 +105,12 @@ void SourceNode::getEventChannelNames (StringArray& names) void SourceNode::updateSettings() { - if (inputBuffer == 0 && dataThread != 0) - { - inputBuffer = dataThread->getBufferAddress(); - std::cout << "Input buffer address is " << inputBuffer << std::endl; - } + if (dataThread) + { + resizeBuffers(); - dataThread->updateChannels(); + dataThread->updateChannels(); + } } @@ -126,10 +133,10 @@ void SourceNode::actionListenerCallback (const String& msg) } -float SourceNode::getSampleRate() const +float SourceNode::getSampleRate(int sub) const { if (dataThread != nullptr) - return dataThread->getSampleRate(); + return dataThread->getSampleRate(sub); else return 44100.0; } @@ -138,57 +145,60 @@ float SourceNode::getSampleRate() const float SourceNode::getDefaultSampleRate() const { if (dataThread != nullptr) - return dataThread->getSampleRate(); + return dataThread->getSampleRate(0); else return 44100.0; } - -int SourceNode::getNumHeadstageOutputs() const +int SourceNode::getDefaultNumDataOutputs(DataChannel::DataChannelTypes type, int sub) const { - if (dataThread != nullptr) - return dataThread->getNumHeadstageOutputs(); - else - return 2; + if (dataThread) + return dataThread->getNumDataOutputs(type, sub); + else return 0; } - -int SourceNode::getNumAuxOutputs() const +float SourceNode::getBitVolts (DataChannel* chan) const { - if (dataThread != nullptr) - return dataThread->getNumAuxOutputs(); + if (dataThread != 0) + return dataThread->getBitVolts (chan); else - return 0; + return 1.0f; } - -int SourceNode::getNumAdcOutputs() const +void SourceNode::setChannelInfo(int channel, String name, float bitVolts) { - if (dataThread != nullptr) - return dataThread->getNumAdcOutputs(); - else - return 0; + dataChannelArray[channel]->setName(name); + dataChannelArray[channel]->setBitVolts(bitVolts); } - -int SourceNode::getNumEventChannels() const +void SourceNode::createEventChannels() { - if (dataThread != nullptr) - return dataThread->getNumEventChannels(); - else - return 0; + 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, this, i); + chan->setSampleRate(dataThread->getSampleRate(i)); + 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); + } } - -float SourceNode::getBitVolts (Channel* chan) const -{ - if (dataThread != 0) - return dataThread->getBitVolts (chan); - else - return 1.0f; -} - - void SourceNode::setEnabledState (bool newState) { if (newState && ! dataThread->foundInputSource()) @@ -329,53 +339,41 @@ void SourceNode::acquisitionStopped() } -void SourceNode::process (AudioSampleBuffer& buffer, MidiBuffer& events) +void SourceNode::process(AudioSampleBuffer& buffer) { - // clear the input buffers - events.clear(); - buffer.clear(); - - int nSamples = inputBuffer->readAllFromBuffer (buffer, ×tamp, eventCodeBuffer, buffer.getNumSamples()); - - setNumSamples (events, nSamples); - setTimestamp (events, timestamp); - - // fill event buffer - for (int i = 0; i < nSamples; ++i) - { - for (int c = 0; c < numEventChannels; ++c) - { - const int state = eventCodeBuffer[i] & (1 << c); - - if (eventChannelState[c] != state) - { - if (state == 0) - { - // signal channel state is OFF - addEvent (events, // MidiBuffer - TTL, // eventType - i, // sampleNum - 0, // eventID - c, // eventChannel - 8, - (uint8*)(&eventCodeBuffer[i])); - } - else - { - // signal channel state is ON - addEvent(events, // MidiBuffer - TTL, // eventType - i, // sampleNum - 1, // eventID - c, // eventChannel - 8, - (uint8*)(&eventCodeBuffer[i])); - } - - eventChannelState[c] = state; - } - } - } + int nSubs = dataThread->getNumSubProcessors(); + for (int sub = 0; sub < nSubs; sub++) + { + int nSamples = inputBuffers[sub]->readAllFromBuffer(buffer, ×tamp, static_cast<uint64*>(eventCodeBuffers[sub]->getData()), buffer.getNumSamples()); + + 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); + } + } } diff --git a/Source/Processors/SourceNode/SourceNode.h b/Source/Processors/SourceNode/SourceNode.h index 0dc674af39918a794f382364166f84b05214d3b1..68e75cac9522eb2a8148ceb02f8a40f9163bf8b8 100755 --- a/Source/Processors/SourceNode/SourceNode.h +++ b/Source/Processors/SourceNode/SourceNode.h @@ -50,7 +50,7 @@ public: void setEnabledState (bool newState) override; - void process (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override; + void process (AudioSampleBuffer& buffer) override; void setParameter (int parameterIndex, float newValue) override; @@ -59,16 +59,12 @@ public: void saveCustomParametersToXml (XmlElement* parentElement) override; void loadCustomParametersFromXml() override; - float getSampleRate() const override; - float getDefaultSampleRate() const override; - - float getBitVolts (Channel* chan) const override; + int getNumSubProcessors() const override; - int getNumHeadstageOutputs() const override; - int getNumAuxOutputs() const override; - int getNumAdcOutputs() const override; + float getSampleRate(int subProcessorIdx = 0) const override; + float getDefaultSampleRate() const override; - int getNumEventChannels() const override; + float getBitVolts (const DataChannel* chan) const override; void requestChainUpdate(); @@ -91,27 +87,33 @@ public: bool tryEnablingEditor(); + void setChannelInfo(int channel, String name, float bitVolts); +protected: + int getDefaultNumDataOutputs(DataChannel::DataChannelTypes type, int subProcessorIdx = 0) const override; + + void createEventChannels() override; private: void timerCallback() override; void updateSettings() override; - int numEventChannels; int sourceCheckInterval; bool wasDisabled; ScopedPointer<DataThread> dataThread; - DataBuffer* inputBuffer; + Array<DataBuffer*> inputBuffers; uint64 timestamp; //uint64* eventCodeBuffer; //int* eventChannelState; - HeapBlock<uint64> eventCodeBuffer; - HeapBlock<int> eventChannelState; + OwnedArray<MemoryBlock> eventCodeBuffers; + Array<uint64> eventStates; + Array<EventChannel*> ttlChannels; int ttlState; + void resizeBuffers(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SourceNode); diff --git a/Source/UI/UIComponent.cpp b/Source/UI/UIComponent.cpp index cdf0549a7bc2962c91261e4989ac7883f5ef994a..867e492de3e60d28bb896c63d084b6029b266ac4 100755 --- a/Source/UI/UIComponent.cpp +++ b/Source/UI/UIComponent.cpp @@ -25,6 +25,17 @@ #include "../Processors/PluginManager/PluginManager.h" #include <stdio.h> +#include "InfoLabel.h" +#include "ControlPanel.h" +#include "ProcessorList.h" +#include "EditorViewport.h" +#include "DataViewport.h" +#include "../Processors/MessageCenter/MessageCenterEditor.h" +#include "GraphViewer.h" +#include "../Processors/ProcessorGraph/ProcessorGraph.h" +#include "../Audio/AudioComponent.h" +#include "../MainWindow.h" + UIComponent::UIComponent(MainWindow* mainWindow_, ProcessorGraph* pgraph, AudioComponent* audio_) : mainWindow(mainWindow_), processorGraph(pgraph), audio(audio_) diff --git a/Source/UI/UIComponent.h b/Source/UI/UIComponent.h index 40dbbeb7edd6149b9598611dde3b6bee65d46034..61b32d4c29cccda71552ac9ed1f1c518af5b36f6 100755 --- a/Source/UI/UIComponent.h +++ b/Source/UI/UIComponent.h @@ -25,23 +25,19 @@ #define __UICOMPONENT_H_D97C73CF__ #include "../../JuceLibraryCode/JuceHeader.h" -#include "InfoLabel.h" -#include "ControlPanel.h" -#include "ProcessorList.h" -#include "EditorViewport.h" -#include "DataViewport.h" -#include "../Processors/MessageCenter/MessageCenterEditor.h" -#include "GraphViewer.h" -#include "../Processors/ProcessorGraph/ProcessorGraph.h" -#include "../Audio/AudioComponent.h" -#include "../MainWindow.h" + class MainWindow; class ProcessorList; - +class ControlPanel; class EditorViewportButton; class PluginManager; +class ProcessorGraph; +class AudioComponent; +class GraphViewer; +class MessageCenterEditor; +class InfoLabel; /**