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, &timestamp, &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, &timestamp, &eventCode, 1);
-#endif
+			sourceBuffers[0]->addToBuffer(thisSample, &timestamps.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, &timestamp, 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, &timestamp, 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, &timestamp, 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, &current, 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;
 
 /**