diff --git a/Builds/VisualStudio2013/Plugins/BinaryWriter/BinaryWriter.vcxproj b/Builds/VisualStudio2013/Plugins/BinaryWriter/BinaryWriter.vcxproj
index aaa3f13e9f26588f0f187b739da1769f1ac90178..e2f9f274aebabcff702d5ba3507f66fb15b77987 100644
--- a/Builds/VisualStudio2013/Plugins/BinaryWriter/BinaryWriter.vcxproj
+++ b/Builds/VisualStudio2013/Plugins/BinaryWriter/BinaryWriter.vcxproj
@@ -117,10 +117,12 @@
   <ItemGroup>
     <ClInclude Include="..\..\..\..\Source\Plugins\BinaryWriter\BinaryRecording.h" />
     <ClInclude Include="..\..\..\..\Source\Plugins\BinaryWriter\FileMemoryBlock.h" />
+    <ClInclude Include="..\..\..\..\Source\Plugins\BinaryWriter\NpyFile.h" />
     <ClInclude Include="..\..\..\..\Source\Plugins\BinaryWriter\SequentialBlockFile.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\..\Source\Plugins\BinaryWriter\BinaryRecording.cpp" />
+    <ClCompile Include="..\..\..\..\Source\Plugins\BinaryWriter\NpyFile.cpp" />
     <ClCompile Include="..\..\..\..\Source\Plugins\BinaryWriter\OpenEphysLib.cpp" />
     <ClCompile Include="..\..\..\..\Source\Plugins\BinaryWriter\SequentialBlockFile.cpp" />
   </ItemGroup>
diff --git a/Builds/VisualStudio2013/Plugins/BinaryWriter/BinaryWriter.vcxproj.filters b/Builds/VisualStudio2013/Plugins/BinaryWriter/BinaryWriter.vcxproj.filters
index cdb841320585eb08d8283cdbc2af0d06cdf4f9db..bc74dc84ad1170ee3966b405cfdc2d03cd8d1438 100644
--- a/Builds/VisualStudio2013/Plugins/BinaryWriter/BinaryWriter.vcxproj.filters
+++ b/Builds/VisualStudio2013/Plugins/BinaryWriter/BinaryWriter.vcxproj.filters
@@ -24,6 +24,9 @@
     <ClInclude Include="..\..\..\..\Source\Plugins\BinaryWriter\BinaryRecording.h">
       <Filter>Source Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\..\Source\Plugins\BinaryWriter\NpyFile.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\..\Source\Plugins\BinaryWriter\SequentialBlockFile.cpp">
@@ -35,5 +38,8 @@
     <ClCompile Include="..\..\..\..\Source\Plugins\BinaryWriter\OpenEphysLib.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\..\Source\Plugins\BinaryWriter\NpyFile.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/Source/Plugins/BinaryWriter/BinaryRecording.cpp b/Source/Plugins/BinaryWriter/BinaryRecording.cpp
index 9542f84937c0176e1d19b3e099fdfa19f8cb0c6d..b13ee7123bd6925ccf8da8a50c6e527ac44d8de2 100644
--- a/Source/Plugins/BinaryWriter/BinaryRecording.cpp
+++ b/Source/Plugins/BinaryWriter/BinaryRecording.cpp
@@ -31,6 +31,7 @@ BinaryRecording::BinaryRecording()
 {
 	m_scaledBuffer.malloc(MAX_BUFFER_SIZE);
 	m_intBuffer.malloc(MAX_BUFFER_SIZE);
+	m_tsBuffer.malloc(MAX_BUFFER_SIZE);
 }
 
 BinaryRecording::~BinaryRecording()
@@ -45,13 +46,20 @@ String BinaryRecording::getEngineID() const
 
 void BinaryRecording::openFiles(File rootFolder, int experimentNumber, int recordingNumber)
 {
-	String basepath = rootFolder.getFullPathName() + rootFolder.separatorString + "experiment" + String(experimentNumber);
+	String basepath = rootFolder.getFullPathName() + rootFolder.separatorString + "experiment" + String(experimentNumber)
+		+ File::separatorString + "recording" + String(recordingNumber + 1) + File::separatorString;
+	String contPath = basepath + "continuous" + File::separatorString;
 	//Open channel files
 	int nProcessors = getNumRecordedProcessors();
 
 	m_channelIndexes.insertMultiple(0, 0, getNumRecordedChannels());
 	m_fileIndexes.insertMultiple(0, 0, getNumRecordedChannels());
 
+	Array<const DataChannel*> indexedDataChannels;
+	Array<unsigned int> indexedChannelCount;
+	Array<var> jsonContinuousfiles;
+	Array<var> jsonChannels;
+	StringArray continuousFileNames;
 	int lastId = 0;
 	for (int proc = 0; proc < nProcessors; proc++)
 	{
@@ -65,94 +73,421 @@ void BinaryRecording::openFiles(File rootFolder, int experimentNumber, int recor
 			const DataChannel* channelInfo = getDataChannel(realChan);
 			int sourceId = channelInfo->getSourceNodeID();
 			int sourceSubIdx = channelInfo->getSubProcessorIdx();
-			int nInfoArrays = m_dataChannels.size();
+			int nInfoArrays = indexedDataChannels.size();
 			bool found = false;
+			DynamicObject::Ptr jsonChan = new DynamicObject();
+			jsonChan->setProperty("name", channelInfo->getName());
+			jsonChan->setProperty("description", channelInfo->getDescription());
+			jsonChan->setProperty("identifier", channelInfo->getIdentifier());
+			jsonChan->setProperty("history", channelInfo->getHistoricString());
+			jsonChan->setProperty("bit_volts", channelInfo->getBitVolts());
+			jsonChan->setProperty("units", channelInfo->getDataUnits());
+			jsonChan->setProperty("source_processor_index", channelInfo->getSourceIndex());
+			jsonChan->setProperty("recorded_processor_index", channelInfo->getCurrentNodeChannelIdx());
+			createChannelMetaData(channelInfo, jsonChan);
 			for (int i = lastId; i < nInfoArrays; i++)
 			{
-				if (sourceId == m_dataChannels.getReference(i)[0]->getSourceNodeID() && sourceSubIdx == m_dataChannels.getReference(i)[0]->getSubProcessorIdx())
+				if (sourceId == indexedDataChannels[i]->getSourceNodeID() && sourceSubIdx == indexedDataChannels[i]->getSubProcessorIdx())
 				{
-					m_channelIndexes.set(recordedChan, m_dataChannels.getReference(i).size());
+					unsigned int count = indexedChannelCount[i];
+					m_channelIndexes.set(recordedChan, count);
 					m_fileIndexes.set(recordedChan, i);
-					m_dataChannels.getReference(i).add(getDataChannel(realChan));
+					indexedChannelCount.set(i, count + 1);
+					jsonChannels.getReference(i).append(var(jsonChan));
 					found = true;
 					break;
 				}
 			}
 			if (!found)
 			{
-				File datFile(basepath + "_" + String(pInfo.processorId) + "_" + String(sourceId) + "." + String(sourceSubIdx) + "_" + String(recordingNumber) + ".dat");
-				ScopedPointer<SequentialBlockFile> bFile = new SequentialBlockFile(pInfo.recordedChannels.size(), samplesPerBlock);
-				if (bFile->openFile(datFile))
-					m_DataFiles.add(bFile.release());
-				else
-					m_DataFiles.add(nullptr);
+				String datFileName(channelInfo->getCurrentNodeName() + "_(" + String(channelInfo->getCurrentNodeID()) + ")" + File::separatorString + channelInfo->getSourceName() + "_(" + String(sourceId) + "." + String(sourceSubIdx) + ")");
+				continuousFileNames.add(contPath + datFileName + ".dat");
+				
+				Array<NpyType> tstypes;
+				tstypes.add(NpyType("Timestamp", BaseType::INT64, 1));
+				
+				ScopedPointer<NpyFile> tFile = new NpyFile(contPath + datFileName + "_timestamps.npy", tstypes);
+				m_dataTimestampFiles.add(tFile.release());
 
-				ContinuousGroup newGroup;
-				newGroup.add(getDataChannel(realChan));
-				m_dataChannels.add(newGroup);
 				m_fileIndexes.set(recordedChan, nInfoArrays);
 				m_channelIndexes.set(recordedChan, 0);
-				
+				indexedChannelCount.add(1);
+				indexedDataChannels.add(channelInfo);
+
+				Array<var> jsonChanArray;
+				jsonChanArray.add(var(jsonChan));
+				jsonChannels.add(var(jsonChanArray));
+				DynamicObject::Ptr jsonFile = new DynamicObject();
+				jsonFile->setProperty("name", datFileName);
+				jsonFile->setProperty("sample_rate", channelInfo->getSampleRate());
+				jsonFile->setProperty("source_processor_name", channelInfo->getSourceName());
+				jsonFile->setProperty("source_processor_id", channelInfo->getSourceNodeID());
+				jsonFile->setProperty("source_processor_sub_idx", channelInfo->getSubProcessorIdx());
+				jsonFile->setProperty("recorded_processor", channelInfo->getCurrentNodeName());
+				jsonFile->setProperty("recorded_processor_id", channelInfo->getCurrentNodeID());
+				jsonContinuousfiles.add(var(jsonFile));
 			}
 		}
+		lastId = indexedDataChannels.size();
 	}
+	int nFiles = continuousFileNames.size();
+	for (int i = 0; i < nFiles; i++)
+	{
+		int numChannels = jsonChannels.getReference(i).size();
+		ScopedPointer<SequentialBlockFile> bFile = new SequentialBlockFile(numChannels, samplesPerBlock);
+		if (bFile->openFile(continuousFileNames[i]))
+			m_DataFiles.add(bFile.release());
+		else
+			m_DataFiles.add(nullptr);
+		DynamicObject::Ptr jsonFile = jsonContinuousfiles.getReference(i).getDynamicObject();
+		jsonFile->setProperty("num_channels", numChannels);
+		jsonFile->setProperty("channels", jsonChannels.getReference(i));
+	}
+
 	int nChans = getNumRecordedChannels();
-	//Origin Timestamp
+	//Timestamps
+	Array<uint32> procIDs;
 	for (int i = 0; i < nChans; i++)
 	{
 		m_startTS.add(getTimestamp(i));
 	}
 
-	//Other files, using OriginalRecording code
-	openEventFile(basepath, recordingNumber);
-	openMessageFile(basepath, recordingNumber);
-	for (int i = 0; i < spikeFileArray.size(); i++)
+	int nEvents = getNumRecordedEvents();
+	String eventPath(basepath + "events" + File::separatorString);
+	int binCount = 0, ttlCount = 0, textCount = 0;
+	Array<var> jsonEventFiles;
+
+	for (int ev = 0; ev < nEvents; ev++)
+	{
+		const EventChannel* chan = getEventChannel(ev);
+		String eventName;
+		Array<NpyType> types;
+		String typeName;
+
+		switch (chan->getChannelType())
+		{
+		case EventChannel::TEXT:
+			textCount++;
+			eventName += "TEXT" + String(textCount);
+			types.add(NpyType("message", BaseType::CHAR, chan->getLength()));
+			typeName = "text_message";
+			break;
+		case EventChannel::TTL:
+			ttlCount++;
+			eventName += "TTL" + String(ttlCount);
+			types.add(NpyType("TTL_Channel", BaseType::INT16, 1));
+			typeName = "ttl";
+			break;
+		default:
+			binCount++;
+			eventName += "BIN" + String(ttlCount);
+			types.add(NpyType("Data", chan->getEquivalentMetaDataType(), chan->getLength()));
+			typeName = jsonTypeValue(chan->getEquivalentMetaDataType());
+			break;
+		}
+		eventName += "_" + chan->getSourceName() + "(" + String(chan->getSourceNodeID()) + "." + String(chan->getSubProcessorIdx()) + ")";
+		String fName = eventPath + eventName;
+		ScopedPointer<EventRecording> rec = new EventRecording();
+		Array<NpyType> tsType;
+		tsType.add(NpyType("Timestamp", BaseType::INT64, 1));
+		//TTL channels behave a bit different
+		if (chan->getChannelType() == EventChannel::TTL)
+		{
+			if (m_TTLMode == TTLMode::JointWord)
+			{
+				types.add(NpyType("TTL_Word", BaseType::UINT8, chan->getDataSize()));
+			}
+			else if (m_TTLMode == TTLMode::SeparateWord)
+			{
+				Array<NpyType> wordType;
+				wordType.add(NpyType("TTL_Word", BaseType::UINT8, chan->getDataSize()));
+				rec->extraFile = new NpyFile(fName + "_TTLWord.npy", wordType);
+			}
+			//since the main TTL file already contins channel numbers, it would be redundant to store them on the timestamp file
+		}
+		else
+		{
+			if (m_eventMode == EventMode::SeparateChannel)
+			{
+				Array<NpyType> chanType;
+				chanType.add(NpyType("Channel", BaseType::UINT16, 1));
+				rec->channelFile = new NpyFile(fName + "_channel.npy", chanType);
+			}
+			else
+				tsType.add(NpyType("Channel", BaseType::UINT16, 1));
+		}
+		rec->mainFile = new NpyFile(fName + ".npy", types);
+		rec->timestampFile = new NpyFile(fName + "_timestamps.npy", tsType);
+		DynamicObject::Ptr jsonChannel = new DynamicObject();
+		jsonChannel->setProperty("name", chan->getName());
+		jsonChannel->setProperty("description", chan->getDescription());
+		jsonChannel->setProperty("identifier", chan->getIdentifier());
+		jsonChannel->setProperty("sample_rate", chan->getSampleRate());
+		jsonChannel->setProperty("type", typeName);
+		jsonChannel->setProperty("num_channels", (int)chan->getNumChannels());
+		jsonChannel->setProperty("source_processor", chan->getSourceName());
+		createChannelMetaData(chan, jsonChannel);
+
+		rec->metaDataFile = createEventMetadataFile(chan, fName + "_metadata.npy", jsonChannel);
+		m_eventFiles.add(rec.release());
+		jsonEventFiles.add(var(jsonChannel));
+	}
+
+	int nSpikes = getNumRecordedSpikes();
+	Array<const SpikeChannel*> indexedSpikes;
+	Array<uint16> indexedChannels;
+	m_spikeFileIndexes.insertMultiple(0, 0, nSpikes);
+	m_spikeChannelIndexes.insertMultiple(0, 0, nSpikes);
+	String spikePath(basepath + "spikes" + File::separatorString);
+	Array<var> jsonSpikeFiles;
+	Array<var> jsonSpikeChannels;
+	for (int sp = 0; sp < nSpikes; sp++)
+	{
+		const SpikeChannel* ch = getSpikeChannel(sp);
+		DynamicObject::Ptr jsonChannel = new DynamicObject();
+		unsigned int numSpikeChannels = ch->getNumChannels();
+		jsonChannel->setProperty("name", ch->getName());
+		jsonChannel->setProperty("description", ch->getDescription());
+		jsonChannel->setProperty("identifier", ch->getIdentifier());
+		Array<var> jsonChannelInfo;
+		for (int i = 0; i < numSpikeChannels; i++)
+		{
+			SourceChannelInfo sourceInfo = ch->getSourceChannelInfo()[i];
+			DynamicObject::Ptr jsonSpikeChInfo = new DynamicObject();
+			jsonSpikeChInfo->setProperty("source_processor_id", sourceInfo.processorID);
+			jsonSpikeChInfo->setProperty("source_processor_sub_idx", sourceInfo.subProcessorID);
+			jsonSpikeChInfo->setProperty("source_processor_channel", sourceInfo.channelIDX);
+			jsonChannelInfo.add(var(jsonSpikeChInfo));
+		}
+		jsonChannel->setProperty("source_channel_info", jsonChannelInfo);
+		createChannelMetaData(ch, jsonChannel);
+
+		int nIndexed = indexedSpikes.size();
+		bool found = false;
+		for (int i = 0; i < nIndexed; i++)
+		{
+			const SpikeChannel* ich = indexedSpikes[i];
+			//identical channels (same data and metadata) from the same processor go to the same file
+			if (ch->getSourceNodeID() == ich->getSourceNodeID() && ch->getSubProcessorIdx() == ich->getSubProcessorIdx() && *ch == *ich)
+			{
+				found = true;
+				m_spikeFileIndexes.set(sp, i);
+				unsigned int numChans = indexedChannels[i];
+				indexedChannels.set(i, numChans);
+				m_spikeChannelIndexes.set(sp, numChans + 1);
+				jsonSpikeChannels.getReference(i).append(var(jsonChannel));
+				break;
+			}
+		}
+		
+		if (!found)
+		{
+			int fileIndex = m_spikeFiles.size();
+			m_spikeFileIndexes.set(sp, fileIndex);
+			indexedSpikes.add(ch);
+			m_spikeChannelIndexes.set(sp, 0);
+			indexedChannels.add(1);
+			ScopedPointer<EventRecording> rec = new EventRecording();
+			Array<NpyType> spTypes;
+			for (int c = 0; c < ch->getNumChannels(); c++)
+			{
+				spTypes.add(NpyType("channel" + String(c + 1), BaseType::INT16, ch->getTotalSamples()));
+			}
+			String spikeName("spike_group_" + String(fileIndex + 1));
+			String fName(spikePath + spikeName);
+			rec->mainFile = new NpyFile(fName + ".npy", spTypes);
+			Array<NpyType> tsTypes;
+			tsTypes.add(NpyType("timestamp", BaseType::INT64, 1));
+			if (m_spikeMode == SpikeMode::AllInOne)
+			{
+				tsTypes.add(NpyType("electrode_index", BaseType::UINT16, 1));
+				tsTypes.add(NpyType("sorted_id", BaseType::UINT16, 1));
+			}
+			else
+			{
+				Array<NpyType> indexType;
+				indexType.add(NpyType("electrode_index", BaseType::UINT16, 1));
+				if (m_spikeMode == SpikeMode::AllSeparated)
+				{
+					Array<NpyType> sortedType;
+					sortedType.add(NpyType("sorted_id", BaseType::UINT16, 1));
+					rec->extraFile = new NpyFile(fName + "_sortedID.npy", sortedType);
+				}
+				else
+				{
+					indexType.add(NpyType("sorted_id", BaseType::UINT16, 1));
+				}
+				rec->channelFile = new NpyFile(fName + "indexes.npy", indexType);
+			}
+			rec->timestampFile = new NpyFile(fName + "_timestamps.npy", tsTypes);
+			Array<var> jsonChanArray;
+			jsonChanArray.add(var(jsonChannel));
+			jsonSpikeChannels.add(var(jsonChanArray));
+			DynamicObject::Ptr jsonFile = new DynamicObject();
+			
+			jsonFile->setProperty("name", spikeName);
+			jsonFile->setProperty("sample_rate", ch->getSampleRate());
+			jsonFile->setProperty("source_processor", ch->getSourceName());
+			jsonFile->setProperty("num_channels", (int)numSpikeChannels);
+			jsonFile->setProperty("pre_peak_samples", (int)ch->getPrePeakSamples());
+			jsonFile->setProperty("post_peak_samples", (int)ch->getPostPeakSamples());
+			
+			rec->metaDataFile = createEventMetadataFile(ch, fName + "_metadata.npy", jsonFile);
+			m_spikeFiles.add(rec.release());
+			jsonSpikeFiles.add(var(jsonFile));
+		}
+	}
+	int nSpikeFiles = jsonSpikeFiles.size();
+	for (int i = 0; i < nSpikeFiles; i++)
 	{
-		openSpikeFile(basepath, i, recordingNumber);
+		int size = jsonSpikeChannels.getReference(i).size();
+		DynamicObject::Ptr jsonFile = jsonSpikeFiles.getReference(i).getDynamicObject();
+		jsonFile->setProperty("num_channels", size);
+		jsonFile->setProperty("channels", jsonSpikeChannels.getReference(i));
 	}
+
+	Array<NpyType> msgType;
+	msgType.add(NpyType("sync_text", BaseType::CHAR, 256));
+	m_syncTextFile = new NpyFile(basepath + "sync_text.npy", msgType);
 	m_recordingNum = recordingNumber;
+
+	DynamicObject::Ptr jsonSettingsFile = new DynamicObject();
+	jsonSettingsFile->setProperty("GUI version", CoreServices::getGUIVersion());
+	jsonSettingsFile->setProperty("continuous", jsonContinuousfiles);
+	jsonSettingsFile->setProperty("events", jsonEventFiles);
+	jsonSettingsFile->setProperty("spikes", jsonSpikeFiles);
+	FileOutputStream settingsFileStream(File(basepath + "structure.oebin"));
+
+	jsonSettingsFile->writeAsJSON(settingsFileStream, 2, false);
 }
 
-void BinaryRecording::closeFiles()
+NpyFile* BinaryRecording::createEventMetadataFile(const MetaDataEventObject* channel, String filename, DynamicObject* jsonFile)
 {
-	m_DataFiles.clear();
-	for (int i = 0; i < spikeFileArray.size(); i++)
+	int nMetaData = channel->getEventMetaDataCount();
+	if (nMetaData < 1) return nullptr;
+
+	Array<NpyType> types;
+	Array<var> jsonMetaData;
+	for (int i = 0; i < nMetaData; i++)
 	{
-		if (spikeFileArray[i] != nullptr)
-		{
-			diskWriteLock.enter();
-			fclose(spikeFileArray[i]);
-			spikeFileArray.set(i, nullptr);
-			diskWriteLock.exit();
-		}
+		const MetaDataDescriptor* md = channel->getEventMetaDataDescriptor(i);
+		types.add(NpyType(md->getName(), md->getType(), md->getLength()));
+		DynamicObject::Ptr jsonValues = new DynamicObject();
+		jsonValues->setProperty("name", md->getName());
+		jsonValues->setProperty("description", md->getDescription());
+		jsonValues->setProperty("identifier", md->getIdentifier());
+		jsonValues->setProperty("type", jsonTypeValue(md->getType()));
+		jsonValues->setProperty("length", (int)md->getLength());
+		jsonMetaData.add(var(jsonValues));
 	}
-	if (eventFile != nullptr)
+	if (jsonFile)
+		jsonFile->setProperty("event_metadata", jsonMetaData);
+	return new NpyFile(filename, types);
+}
+
+template <typename TO, typename FROM>
+void dataToVar(var& dataTo, const void* dataFrom, int length)
+{
+	const FROM* buffer = reinterpret_cast<const FROM*>(dataFrom);
+	for (int i = 0; i < length; i++)
 	{
-		diskWriteLock.enter();
-		fclose(eventFile);
-		eventFile = nullptr;
-		diskWriteLock.exit();
+		dataTo.append(static_cast<TO>(*(buffer + i)));
 	}
-	if (messageFile != nullptr)
+}
+
+void BinaryRecording::createChannelMetaData(const MetaDataInfoObject* channel, DynamicObject* jsonFile)
+{
+	int nMetaData = channel->getMetaDataCount();
+	if (nMetaData < 1) return;
+
+	Array<var> jsonMetaData;
+	for (int i = 0; i < nMetaData; i++)
 	{
-		diskWriteLock.enter();
-		fclose(messageFile);
-		messageFile = nullptr;
-		diskWriteLock.exit();
+		const MetaDataDescriptor* md = channel->getMetaDataDescriptor(i);
+		const MetaDataValue* mv = channel->getMetaDataValue(i);
+		DynamicObject::Ptr jsonValues = new DynamicObject();
+		MetaDataDescriptor::MetaDataTypes type = md->getType();
+		unsigned int length = md->getLength();
+		jsonValues->setProperty("name", md->getName());
+		jsonValues->setProperty("description", md->getDescription());
+		jsonValues->setProperty("identifier", md->getIdentifier());
+		jsonValues->setProperty("type", jsonTypeValue(type));
+		jsonValues->setProperty("length", (int)length);
+		var val;
+		if (type == MetaDataDescriptor::CHAR)
+		{
+			String tmp;
+			mv->getValue(tmp);
+			val = tmp;
+		}
+		else
+		{
+			const void* buf = mv->getRawValuePointer();
+			switch (type)
+			{
+			case MetaDataDescriptor::INT8:
+				dataToVar<int, int8>(val, buf, length);
+				break;
+			case MetaDataDescriptor::UINT8:
+				dataToVar<int, uint8>(val, buf, length);
+				break;
+			case MetaDataDescriptor::INT16:
+				dataToVar<int, int16>(val, buf, length);
+				break;
+			case MetaDataDescriptor::UINT16:
+				dataToVar<int, uint16>(val, buf, length);
+				break;
+			case MetaDataDescriptor::INT32:
+				dataToVar<int, int32>(val, buf, length);
+				break;
+				//A full uint32 doesn't fit in a regular int, so we increase size
+			case MetaDataDescriptor::UINT32:
+				dataToVar<int64, uint8>(val, buf, length);
+				break;
+			case MetaDataDescriptor::INT64:
+				dataToVar<int64, int64>(val, buf, length);
+				break;
+				//This might overrun and end negative if the uint64 is really big, but there is no way to store a full uint64 in a var
+			case MetaDataDescriptor::UINT64:
+				dataToVar<int64, uint64>(val, buf, length);
+				break;
+			case MetaDataDescriptor::FLOAT:
+				dataToVar<float, float>(val, buf, length);
+				break;
+			case MetaDataDescriptor::DOUBLE:
+				dataToVar<double, double>(val, buf, length);
+				break;
+			default:
+				val = "invalid";
+			}
+		}
+		jsonValues->setProperty("value", val);
+		jsonMetaData.add(var(jsonValues));
 	}
-	m_scaledBuffer.malloc(MAX_BUFFER_SIZE);
-	m_intBuffer.malloc(MAX_BUFFER_SIZE);
-	m_bufferSize = MAX_BUFFER_SIZE;
-	m_startTS.clear();
+	jsonFile->setProperty("channel_metadata", jsonMetaData);
+}
+
+void BinaryRecording::closeFiles()
+{
+	resetChannels();
 }
 
 void BinaryRecording::resetChannels()
 {
+	m_DataFiles.clear();
+	m_channelIndexes.clear();
+	m_fileIndexes.clear();
+	m_dataTimestampFiles.clear();
+	m_eventFiles.clear();
+	m_spikeChannelIndexes.clear();
+	m_spikeFileIndexes.clear();
+	m_spikeFiles.clear();
+	m_syncTextFile = nullptr;
+
 	m_scaledBuffer.malloc(MAX_BUFFER_SIZE);
 	m_intBuffer.malloc(MAX_BUFFER_SIZE);
+	m_tsBuffer.malloc(MAX_BUFFER_SIZE);
 	m_bufferSize = MAX_BUFFER_SIZE;
-	m_DataFiles.clear();
-	spikeFileArray.clear();
 	m_startTS.clear();
 }
 
@@ -164,336 +499,187 @@ void BinaryRecording::writeData(int writeChannel, int realChannel, const float*
 		m_bufferSize = size;
 		m_scaledBuffer.malloc(size);
 		m_intBuffer.malloc(size);
+		m_tsBuffer.malloc(size);
 	}
 	double multFactor = 1 / (float(0x7fff) * getDataChannel(realChannel)->getBitVolts());
 	FloatVectorOperations::copyWithMultiply(m_scaledBuffer.getData(), buffer, multFactor, size);
 	AudioDataConverters::convertFloatToInt16LE(m_scaledBuffer.getData(), m_intBuffer.getData(), size);
 
 	m_DataFiles[m_fileIndexes[writeChannel]]->writeChannel(getTimestamp(writeChannel)-m_startTS[writeChannel],m_channelIndexes[writeChannel],m_intBuffer.getData(),size);
+
+	if (m_channelIndexes[writeChannel] == 0)
+	{
+		int64 baseTS = getTimestamp(writeChannel);
+		//Let's hope that the compiler is smart enough to vectorize this. 
+		for (int i = 0; i < size; i++)
+		{
+			m_tsBuffer[i] = (baseTS + i);
+		}
+		m_dataTimestampFiles[m_fileIndexes[writeChannel]]->writeData(m_tsBuffer, size*sizeof(int64));
+	}
 }
 
-//Code below is copied from OriginalRecording, so it's not as clean as newer one
 
 void BinaryRecording::addSpikeElectrode(int index, const SpikeChannel* elec)
 {
-	spikeFileArray.add(nullptr);
 }
 
-void BinaryRecording::openEventFile(String basepath, int recordingNumber)
+void BinaryRecording::writeEventMetaData(const MetaDataEvent* event, NpyFile* file)
 {
-	FILE* chFile;
-	String fullPath = basepath + "_all_channels_" + String(recordingNumber) + ".events";
-
-
-	
-	std::cout << "OPENING FILE: " << fullPath << std::endl;
-
-	File f = File(fullPath);
-
-	bool fileExists = f.exists();
-
-	diskWriteLock.enter();
-
-	chFile = fopen(fullPath.toUTF8(), "ab");
-
-	if (!fileExists)
-	{
-		// create and write header
-		std::cout << "Writing header." << std::endl;
-		String header = generateEventHeader();
-		//std::cout << header << std::endl;
-		std::cout << "File ID: " << chFile << ", number of bytes: " << header.getNumBytesAsUTF8() << std::endl;
-
-
-		fwrite(header.toUTF8(), 1, header.getNumBytesAsUTF8(), chFile);
-
-		std::cout << "Wrote header." << std::endl;
-
-		// std::cout << "Block index: " << blockIndex << std::endl;
-
-	}
-	else
+	if (!file || !event) return;
+	int nMetaData = event->getMetadataValueCount();
+	for (int i = 0; i < nMetaData; i++)
 	{
-		std::cout << "File already exists, just opening." << std::endl;
-		fseek(chFile, 0, SEEK_END);
+		const MetaDataValue* val = event->getMetaDataValue(i);
+		file->writeData(val->getRawValuePointer(), val->getDataSize());
 	}
-
-	eventFile = chFile;
-	
-	diskWriteLock.exit();
-
 }
 
-void BinaryRecording::openSpikeFile(String basePath, int spikeIndex, int recordingNumber)
+void BinaryRecording::writeEvent(int eventIndex, const MidiMessage& event)
 {
-	const SpikeChannel* elec = getSpikeChannel(spikeIndex);
-	FILE* spFile;
-	String fullPath = basePath + "_" + elec->getName().removeCharacters(" ") + "_" + String(recordingNumber) + ".spikes";
-	
-	std::cout << "OPENING FILE: " << fullPath << std::endl;
-
-	File f = File(fullPath);
-
-	bool fileExists = f.exists();
-
-	diskWriteLock.enter();
-
-	spFile = fopen(fullPath.toUTF8(), "ab");
-
-	if (!fileExists)
+	EventPtr ev = Event::deserializeFromMessage(event, getEventChannel(eventIndex));
+	EventRecording* rec = m_eventFiles[eventIndex];
+	if (!rec) return;
+	const EventChannel* info = getEventChannel(eventIndex);
+	int64 ts = ev->getTimestamp();
+	rec->timestampFile->writeData(&ts, sizeof(int64));
+	if (ev->getEventType() == EventChannel::TTL)
 	{
-		String header = generateSpikeHeader(elec);
-		fwrite(header.toUTF8(), 1, header.getNumBytesAsUTF8(), spFile);
+		TTLEvent* ttl = static_cast<TTLEvent*>(ev.get());
+		int16 data = ttl->getChannel() * (ttl->getState() ? 1 : -1);
+		rec->mainFile->writeData(&data, sizeof(int16));
+		NpyFile* wordFile = nullptr;
+		if (m_TTLMode == TTLMode::JointWord)
+		{
+			wordFile = rec->mainFile;
+		}
+		else if (m_TTLMode == TTLMode::SeparateWord)
+		{
+			wordFile = rec->extraFile;
+		}
+		if (wordFile)
+			wordFile->writeData(ttl->getTTLWordPointer(), info->getDataSize());
 	}
-	diskWriteLock.exit();
-	spikeFileArray.set(spikeIndex, spFile);
-
-}
-
-void BinaryRecording::openMessageFile(String basepath, int recordNumber)
-{
-	FILE* mFile;
-	String fullPath = basepath + "_messages_" + String(recordNumber) + ".events";
-
-	fullPath += "messages";
-
-
-
-	std::cout << "OPENING FILE: " << fullPath << std::endl;
-
-	File f = File(fullPath);
-
-	//bool fileExists = f.exists();
-
-	diskWriteLock.enter();
-
-	mFile = fopen(fullPath.toUTF8(), "ab");
-
-	//If this file needs a header, it goes here
-
-	diskWriteLock.exit();
-	messageFile = mFile;
-
-}
-
-String BinaryRecording::generateEventHeader()
-{
-
-	String header = "header.format = 'Open Ephys Data Format'; \n";
-
-	header += "header.version = " + String(VERSION_STRING) + "; \n";
-	header += "header.header_bytes = ";
-	header += String(HEADER_SIZE);
-	header += ";\n";
-
-	header += "header.description = 'each record contains one 64-bit timestamp, one 16-bit sample position, one uint8 event type, one uint8 processor ID, one uint8 event ID, one uint8 event channel, and one uint16 recordingNumber'; \n";
-
-
-	header += "header.date_created = '";
-	header += generateDateString();
-	header += "';\n";
-
-	header += "header.channel = '";
-	header += "Events";
-	header += "';\n";
-
-    header += "header.channelType = 'Event';\n";
-
-
-	header += "header.sampleRate = ";
-	// all channels need to have the same sample rate under the current scheme
-	header += String(getEventChannel(0)->getSampleRate());
-	header += ";\n";
-	header += "header.blockLength = ";
-	header += BLOCK_LENGTH;
-	header += ";\n";
-	header += "header.bufferSize = ";
-	header += "1024";
-	header += ";\n";
-	header += "header.bitVolts = ";
-	header += "1";
-	header += ";\n";
-
-	header = header.paddedRight(' ', HEADER_SIZE);
-
-	//std::cout << header << std::endl;
-
-	return header;
-
-}
-
-String BinaryRecording::generateSpikeHeader(const SpikeChannel* elec)
-{
-	String header = "header.format = 'Open Ephys Data Format'; \n";
-	header += "header.version = " + String(VERSION_STRING) + "; \n";
-	header += "header.header_bytes = ";
-	header += String(HEADER_SIZE);
-	header += ";\n";
-
-	header += "header.description = 'Each record contains 1 uint8 eventType, 1 int64 timestamp, 1 int64 software timestamp, "
-		"1 uint16 sourceID, 1 uint16 numChannels (n), 1 uint16 numSamples (m), 1 uint16 sortedID, 1 uint16 electrodeID, "
-		"1 uint16 channel, 3 uint8 color codes, 2 float32 component projections, n*m uint16 samples, n float32 channelGains, n uint16 thresholds, and 1 uint16 recordingNumber'; \n";
-
-	header += "header.date_created = '";
-	header += generateDateString();
-	header += "';\n";
-
-	header += "header.electrode = '";
-	header += elec->getName();
-	header += "';\n";
-
-	header += "header.num_channels = ";
-	header += String(elec->getNumChannels());
-	header += ";\n";
-
-	header += "header.sampleRate = ";
-	header += String(elec->getSampleRate());
-	header += ";\n";
-
-	header = header.paddedRight(' ', HEADER_SIZE);
-
-	//std::cout << header << std::endl;
-
-	return header;
-}
-
-void BinaryRecording::writeEvent(int eventIndex, const MidiMessage& event)
-{
-	writeTTLEvent(eventIndex, event);
-	if (Event::getEventType(event) == EventChannel::TEXT)
+	else
 	{
-		TextEventPtr ev = TextEvent::deserializeFromMessage(event, getEventChannel(eventIndex));
-		if (ev == nullptr) return;
-		writeMessage(ev->getText(), ev->getSourceID(), ev->getChannel(), ev->getTimestamp());
+		rec->mainFile->writeData(ev->getRawDataPointer(), info->getDataSize());
+		NpyFile* chanFile = nullptr;
+		if (m_eventMode == EventMode::SeparateChannel)
+		{
+			chanFile = rec->channelFile;
+		}
+		else
+		{
+			chanFile = rec->timestampFile;
+		}
+		uint16 chan = ev->getChannel();
+		chanFile->writeData(&chan, sizeof(uint16));
 	}
+	writeEventMetaData(ev.get(), rec->metaDataFile);
 }
 
 void BinaryRecording::writeTimestampSyncText(uint16 sourceID, uint16 sourceIdx, int64 timestamp, float, String text)
 {
-	writeMessage(text, sourceID, 255, timestamp);
-}
-
-void BinaryRecording::writeMessage(String message, uint16 processorID, uint16 channel, int64 timestamp)
-{
-	if (messageFile == nullptr)
-		return;
-
-	int msgLength = message.getNumBytesAsUTF8();
-
-	String timestampText(timestamp);
-
-	diskWriteLock.enter();
-	fwrite(timestampText.toUTF8(), 1, timestampText.length(), messageFile);
-	fwrite(" ", 1, 1, messageFile);
-	fwrite(message.toUTF8(), 1, msgLength, messageFile);
-	fwrite("\n", 1, 1, messageFile);
-	diskWriteLock.exit();
-
+	text.paddedRight(' ', 256);
+	m_syncTextFile->writeData(text.toUTF8(), 256);
 }
 
-void BinaryRecording::writeTTLEvent(int eventIndex, const MidiMessage& event)
-{
-	// find file and write samples to disk
-	// std::cout << "Received event!" << std::endl;
-
-	if (eventFile == nullptr)
-		return;
-
-	uint8 data[16];
-	//With the new external recording thread, this field has no sense.
-	int16 samplePos = 0;
-
-	EventPtr ev = Event::deserializeFromMessage(event, getEventChannel(eventIndex));
-	if (!ev) return;
-	*reinterpret_cast<int64*>(data) = ev->getTimestamp();
-	*reinterpret_cast<int16*>(data + 8) = samplePos;
-	*(data + 10) = static_cast<uint8>(ev->getEventType());
-	*(data + 11) = static_cast<uint8>(ev->getSourceID());
-	*(data + 12) = (ev->getEventType() == EventChannel::TTL) ? (dynamic_cast<TTLEvent*>(ev.get())->getState() ? 1 : 0) : 0;
-	*(data + 13) = static_cast<uint8>(ev->getChannel());
-	*reinterpret_cast<uint16*>(data + 14) = static_cast<uint16>(m_recordingNum);
-
-
-	diskWriteLock.enter();
-
-	fwrite(&data,					// ptr
-		sizeof(uint8),   							// size of each element
-		16, 		  						// count
-		eventFile);   			// ptr to FILE object
-
-	diskWriteLock.exit();
-}
 
 
 void BinaryRecording::writeSpike(int electrodeIndex, const SpikeEvent* spike)
 {
-	if (spikeFileArray[electrodeIndex] == nullptr)
-		return;
-
-	HeapBlock<char> spikeBuffer;
 	const SpikeChannel* channel = getSpikeChannel(electrodeIndex);
+	EventRecording* rec = m_spikeFiles[m_spikeFileIndexes[electrodeIndex]];
+	uint16 spikeChannel = m_spikeChannelIndexes[electrodeIndex];
 
 	int totalSamples = channel->getTotalSamples() * channel->getNumChannels();
-	int numChannels = channel->getNumChannels();
-	int chanSamples = channel->getTotalSamples();
-
-	int totalBytes = totalSamples * 2 + // account for samples
-		numChannels * 4 +            // acount for gain
-		numChannels * 2 +            // account for thresholds
-		42;             // 42, from SpikeObject.h
-	spikeBuffer.malloc(totalBytes);
-	*(spikeBuffer.getData()) = static_cast<char>(channel->getChannelType());
-	*reinterpret_cast<int64*>(spikeBuffer.getData() + 1) = spike->getTimestamp();
-	*reinterpret_cast<int64*>(spikeBuffer.getData() + 9) = 0; //Legacy unused value
-	*reinterpret_cast<uint16*>(spikeBuffer.getData() + 17) = spike->getSourceID();
-	*reinterpret_cast<uint16*>(spikeBuffer.getData() + 19) = numChannels;
-	*reinterpret_cast<uint16*>(spikeBuffer.getData() + 21) = chanSamples;
-	*reinterpret_cast<uint16*>(spikeBuffer.getData() + 23) = spike->getSortedID();
-	*reinterpret_cast<uint16*>(spikeBuffer.getData() + 25) = electrodeIndex; //Legacy value
-	*reinterpret_cast<uint16*>(spikeBuffer.getData() + 27) = 0; //Legacy unused value
-	zeromem(spikeBuffer.getData() + 29, 3 * sizeof(uint8));
-	zeromem(spikeBuffer.getData() + 32, 2 * sizeof(float));
-	*reinterpret_cast<uint16*>(spikeBuffer.getData() + 40) = channel->getSampleRate();
-
-	int ptrIdx = 0;
-	uint16* dataIntPtr = reinterpret_cast<uint16*>(spikeBuffer.getData() + 42);
-	const float* spikeDataPtr = spike->getDataPointer();
-	for (int i = 0; i < numChannels; i++)
+	
+
+	if (totalSamples > m_bufferSize) //Shouldn't happen, and if it happens it'll be slow, but better this than crashing. Will be reset on file close and reset.
 	{
-		const float bitVolts = channel->getChannelBitVolts(i);
-		for (int j = 0; j < chanSamples; j++)
-		{
-			*(dataIntPtr + ptrIdx) = uint16(*(spikeDataPtr + ptrIdx) / bitVolts + 32768);
-			ptrIdx++;
-		}
+		std::cerr << "(spike) Write buffer overrun, resizing to" << totalSamples << std::endl;
+		m_bufferSize = totalSamples;
+		m_scaledBuffer.malloc(totalSamples);
+		m_intBuffer.malloc(totalSamples);
 	}
-	ptrIdx = totalSamples * 2 + 42;
-	for (int i = 0; i < numChannels; i++)
+	double multFactor = 1 / (float(0x7fff) * channel->getChannelBitVolts(0));
+	FloatVectorOperations::copyWithMultiply(m_scaledBuffer.getData(), spike->getDataPointer(), multFactor, totalSamples);
+	AudioDataConverters::convertFloatToInt16LE(m_scaledBuffer.getData(), m_intBuffer.getData(), totalSamples);
+	rec->mainFile->writeData(m_intBuffer.getData(), totalSamples*sizeof(int16));
+	int64 ts = spike->getTimestamp();
+	rec->timestampFile->writeData(&ts, sizeof(int64));
+	NpyFile* indexFile;
+	NpyFile* sortedFile;
+	if (m_spikeMode == SpikeMode::AllInOne)
 	{
-		//To get the same value as the original version
-		*reinterpret_cast<float*>(spikeBuffer.getData() + ptrIdx) = (int)(1.0f / channel->getChannelBitVolts(i)) * 1000;
-		ptrIdx += sizeof(float);
+		indexFile = rec->timestampFile;
+		sortedFile = rec->timestampFile;
 	}
-	for (int i = 0; i < numChannels; i++)
+	else if (m_spikeMode == SpikeMode::SeparateTimestamps)
 	{
-		*reinterpret_cast<int16*>(spikeBuffer.getData() + ptrIdx) = spike->getThreshold(i);
-		ptrIdx += sizeof(int16);
+		indexFile = rec->channelFile;
+		sortedFile = rec->channelFile;
 	}
+	else
+	{
+		indexFile = rec->channelFile;
+		sortedFile = rec->extraFile;
+	}
+	indexFile->writeData(&spikeChannel, sizeof(uint16));
 
-	diskWriteLock.enter();
-
-	fwrite(spikeBuffer, 1, totalBytes, spikeFileArray[electrodeIndex]);
-
-	fwrite(&m_recordingNum,                         // ptr
-		2,                               // size of each element
-		1,                               // count
-		spikeFileArray[electrodeIndex]); // ptr to FILE object
+	uint16 sortedID = spike->getSortedID();
+	sortedFile->writeData(&sortedID, sizeof(uint16));
 
-	diskWriteLock.exit();
+	writeEventMetaData(spike, rec->metaDataFile);
 }
 
 RecordEngineManager* BinaryRecording::getEngineManager()
 {
 	RecordEngineManager* man = new RecordEngineManager("RAWBINARY", "Binary", &(engineFactory<BinaryRecording>));
+	EngineParameter* param;
+	param = new EngineParameter(EngineParameter::MULTI, 0, "Spike TS/chan/sortedID File Mode|All in one|Separate timestamps|All Separated", 0);
+	man->addParameter(param);
+	param = new EngineParameter(EngineParameter::MULTI, 1, "TTL Event word file|In main file|Separated|Do not save ttl word", 0);
+	man->addParameter(param);
+	param = new EngineParameter(EngineParameter::MULTI, 2, "Other event channel file|With timestamp|Separate", 0);
+	man->addParameter(param);
 	return man;
 }
+
+void BinaryRecording::setParameter(EngineParameter& parameter)
+{
+	multiParameter(0, reinterpret_cast<int&>(m_spikeMode));
+	multiParameter(1, reinterpret_cast<int&>(m_TTLMode));
+	multiParameter(2, reinterpret_cast<int&>(m_eventMode));
+}
+
+String BinaryRecording::jsonTypeValue(BaseType type)
+{
+	switch (type)
+	{
+	case BaseType::CHAR:
+		return "string";
+	case BaseType::INT8:
+		return "int8";
+	case BaseType::UINT8:
+		return "uint8";
+	case BaseType::INT16:
+		return "int16";
+	case BaseType::UINT16:
+		return "uint16";
+	case BaseType::INT32:
+		return "int32";
+	case BaseType::UINT32:
+		return "uint32";
+	case BaseType::INT64:
+		return "int64";
+	case BaseType::UINT64:
+		return "uint64";
+	case BaseType::FLOAT:
+		return "float";
+	case BaseType::DOUBLE:
+		return "double";
+	default:
+		return String::empty;
+	}
+}
\ No newline at end of file
diff --git a/Source/Plugins/BinaryWriter/BinaryRecording.h b/Source/Plugins/BinaryWriter/BinaryRecording.h
index d710dd4dfa9bd8334c2a2e89b3d709694df93a66..e0493fac8926f7fff26d2a5e95ecf37fe586fcef 100644
--- a/Source/Plugins/BinaryWriter/BinaryRecording.h
+++ b/Source/Plugins/BinaryWriter/BinaryRecording.h
@@ -25,14 +25,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #include <RecordingLib.h>
 #include "SequentialBlockFile.h"
-
-//Defines for the old-stype original recording spikes and event files
-#define HEADER_SIZE 1024
-#define BLOCK_LENGTH 1024
-#define VERSION 0.4
-#define VSTR(s) #s
-#define VSTR2(s) VSTR(s)
-#define VERSION_STRING VSTR2(VERSION)
+#include "NpyFile.h"
 
 namespace BinaryRecordingEngine
 {
@@ -52,36 +45,71 @@ namespace BinaryRecordingEngine
 		void addSpikeElectrode(int index, const SpikeChannel* elec) override;
 		void writeSpike(int electrodeIndex, const SpikeEvent* spike) override;
 		void writeTimestampSyncText(uint16 sourceID, uint16 sourceIdx, int64 timestamp, float, String text) override;
+		void setParameter(EngineParameter& parameter) override;
 
 		static RecordEngineManager* getEngineManager();
 
 	private:
-		typedef Array<const DataChannel*> ContinuousGroup;
-		void openSpikeFile(String basepath, int spikeIndex, int recordingNumber);
-		String generateSpikeHeader(const SpikeChannel* elec);
-		String generateEventHeader();
 
-		void openMessageFile(String basepath, int recordingNumber);
-		void openEventFile(String basepath, int recordingNumber);
-		void writeTTLEvent(int eventIndex, const MidiMessage& event);
-		void writeMessage(String message, uint16 processorID, uint16 channel, int64 timestamp);
+		class EventRecording
+		{
+		public:
+			ScopedPointer<NpyFile> mainFile;
+			ScopedPointer<NpyFile> timestampFile;
+			ScopedPointer<NpyFile> metaDataFile;
+			ScopedPointer<NpyFile> channelFile;
+			ScopedPointer<NpyFile> extraFile;
+		};
+		
+		enum SpikeMode
+		{
+			AllInOne = 0,
+			SeparateTimestamps = 1,
+			AllSeparated = 2
+		};
+
+		enum TTLMode
+		{
+			JointWord = 0,
+			SeparateWord = 1,
+			NoWord = 2
+		};
+
+		enum EventMode
+		{
+			JointChannel = 0,
+			SeparateChannel = 1
+		};
+
+		NpyFile* createEventMetadataFile(const MetaDataEventObject* channel, String fileName, DynamicObject* jsonObject);
+		void createChannelMetaData(const MetaDataInfoObject* channel, DynamicObject* jsonObject);
+		void writeEventMetaData(const MetaDataEvent* event, NpyFile* file);
+		static String jsonTypeValue(BaseType type);
+
+		SpikeMode m_spikeMode;
+		TTLMode m_TTLMode;
+		EventMode m_eventMode;
+
 
 		HeapBlock<float> m_scaledBuffer;
 		HeapBlock<int16> m_intBuffer;
+		HeapBlock<int64> m_tsBuffer;
 		int m_bufferSize;
 
 		OwnedArray<SequentialBlockFile>  m_DataFiles;
 		Array<unsigned int> m_channelIndexes;
 		Array<unsigned int> m_fileIndexes;
-		Array<ContinuousGroup> m_dataChannels;
+		OwnedArray<EventRecording> m_eventFiles;
+		OwnedArray<EventRecording> m_spikeFiles;
+		OwnedArray<NpyFile> m_dataTimestampFiles;
+		ScopedPointer<NpyFile> m_syncTextFile;
+
+		Array<unsigned int> m_spikeFileIndexes;
+		Array<uint16> m_spikeChannelIndexes;
 
-		FILE* eventFile;
-		FILE* messageFile;
-		Array<FILE*> spikeFileArray;
 		int m_recordingNum;
 		Array<int64> m_startTS;
 
-		CriticalSection diskWriteLock;
 
 		//Compile-time constants
 		const int samplesPerBlock{ 4096 };
diff --git a/Source/Plugins/BinaryWriter/SequentialBlockFile.cpp b/Source/Plugins/BinaryWriter/SequentialBlockFile.cpp
index 5b5c256ce8cd97202ecbbfc0b6b3609bc96ff6f5..15e75d057457035e27299522844a68b12ca44daf 100644
--- a/Source/Plugins/BinaryWriter/SequentialBlockFile.cpp
+++ b/Source/Plugins/BinaryWriter/SequentialBlockFile.cpp
@@ -44,8 +44,15 @@ SequentialBlockFile::~SequentialBlockFile()
 		m_memBlocks.remove(0);
 }
 
-bool SequentialBlockFile::openFile(File file)
+bool SequentialBlockFile::openFile(String filename)
 {
+	File file(filename);
+	Result res = file.create();
+	if (res.failed())
+	{
+		std::cerr << "Error creating file " << filename << ":" << res.getErrorMessage() << std::endl;
+		return false;
+	}
 	m_file = file.createOutputStream(streamBufferSize);
 	if (!m_file)
 		return false;
diff --git a/Source/Plugins/BinaryWriter/SequentialBlockFile.h b/Source/Plugins/BinaryWriter/SequentialBlockFile.h
index 31ed0b5f0ce5994f616a31cb05c34bde136a7d17..b5fd350aad449cad3b1687abe8b1abf390f84f15 100644
--- a/Source/Plugins/BinaryWriter/SequentialBlockFile.h
+++ b/Source/Plugins/BinaryWriter/SequentialBlockFile.h
@@ -37,7 +37,7 @@ namespace BinaryRecordingEngine
 		SequentialBlockFile(int nChannels, int samplesPerBlock);
 		~SequentialBlockFile();
 
-		bool openFile(File file);
+		bool openFile(String filename);
 		bool writeChannel(uint64 startPos, int channel, int16* data, int nSamples);
 
 	private:
diff --git a/Source/Plugins/NWBFormat/NWBRecording.cpp b/Source/Plugins/NWBFormat/NWBRecording.cpp
index 081a4ff9b487fff244ebcaed34403e83a86dfc4d..e729934bbff4de16a8c9345cb4ee97d507d44630 100644
--- a/Source/Plugins/NWBFormat/NWBRecording.cpp
+++ b/Source/Plugins/NWBFormat/NWBRecording.cpp
@@ -98,6 +98,10 @@
 	 for (int i = 0; i < nEvents; i++)
 		 eventChannels.add(getEventChannel(i));
 
+	 int nSpikes = getNumRecordedSpikes();
+	 for (int i = 0; i < nSpikes; i++)
+		 spikeChannels.add(getSpikeChannel(i));
+
 	 //open the file
 	 recordFile->open(getNumRecordedChannels() + continuousChannels.size() + eventChannels.size() + spikeChannels.size()); //total channels + timestamp arrays, to create a big enough buffer
 
@@ -113,19 +117,14 @@
 	 recordFile->stopRecording();
 	 recordFile->close();
 	 recordFile = nullptr;
-	 resetChannels(false);
+	 resetChannels();
  }
 
- void NWBRecordEngine::resetChannels()
- {
-	 resetChannels(true);
- }
 
  
- void NWBRecordEngine::resetChannels(bool resetSpikes)
+ void NWBRecordEngine::resetChannels()
  {
-	 if (resetSpikes)
-		 spikeChannels.clear();
+	 spikeChannels.clear();
 	 eventChannels.clear();
 	 continuousChannels.clear();
 	 datasetIndexes.clear();
@@ -171,7 +170,6 @@ void NWBRecordEngine::writeTimestampSyncText(uint16 sourceID, uint16 sourceIdx,
 
 void NWBRecordEngine::addSpikeElectrode(int index,const  SpikeChannel* elec) 
 {
-	spikeChannels.add(elec);
 }
 
 void NWBRecordEngine::writeSpike(int electrodeIndex, const SpikeEvent* spike) 
diff --git a/Source/Plugins/NWBFormat/NWBRecording.h b/Source/Plugins/NWBFormat/NWBRecording.h
index e521a34c35dbb2a4627a318bce94c7bd6d33d534..e74219d6c4f8d675574f9d6e74d6f8d45cd7453a 100644
--- a/Source/Plugins/NWBFormat/NWBRecording.h
+++ b/Source/Plugins/NWBFormat/NWBRecording.h
@@ -48,7 +48,6 @@
 			static RecordEngineManager* getEngineManager();
 			
 		private:
-			void resetChannels(bool resetSpikes);
 			ScopedPointer<NWBFile> recordFile;
 			Array<int> datasetIndexes;
 			Array<int> writeChannelIndexes;
diff --git a/Source/Processors/Channel/InfoObjects.cpp b/Source/Processors/Channel/InfoObjects.cpp
index c49d4b8923f15e113ef2c3cd05f69599bdd73f2c..cb847a6b0468b46e0b8d14e62230fd050c0cadae 100644
--- a/Source/Processors/Channel/InfoObjects.cpp
+++ b/Source/Processors/Channel/InfoObjects.cpp
@@ -30,18 +30,23 @@ HistoryObject::HistoryObject() {}
 NamedInfoObject::NamedInfoObject() {}
 
 //NodeInfoBase
-NodeInfoBase::NodeInfoBase(uint16 id) :
-	m_nodeID(id)
+NodeInfoBase::NodeInfoBase(uint16 id, uint16 idx) :
+m_nodeID(id), m_nodeIdx(idx)
 {}
 
 NodeInfoBase::~NodeInfoBase()
 {}
 
-unsigned int NodeInfoBase::getCurrentNodeID() const
+uint16 NodeInfoBase::getCurrentNodeID() const
 {
 	return m_nodeID;
 }
 
+uint16 NodeInfoBase::getCurrentNodeChannelIdx() const
+{
+	return m_nodeIdx;
+}
+
 String NodeInfoBase::getCurrentNodeType() const
 {
 	return m_currentNodeType;
@@ -56,7 +61,7 @@ String NodeInfoBase::getCurrentNodeName() const
 HistoryObject::~HistoryObject()
 {}
 
-String HistoryObject::getHistoricString()
+String HistoryObject::getHistoricString() const
 {
 	return m_historicString;
 }
@@ -143,7 +148,7 @@ String NamedInfoObject::getDescription() const
 
 //InfoObjectCommon
 InfoObjectCommon::InfoObjectCommon(uint16 idx, uint16 typeidx, float sampleRate, const GenericProcessor* source, uint16 subproc)
-	:	NodeInfoBase(source->getNodeId()),
+	:	NodeInfoBase(source->getNodeId(), idx),
 		SourceProcessorInfo(source, subproc),
 		m_sourceIndex(idx),
 		m_sourceTypeIndex(typeidx),
@@ -170,6 +175,29 @@ uint16 InfoObjectCommon::getSourceTypeIndex() const
 	return m_sourceTypeIndex;
 }
 
+bool InfoObjectCommon::operator==(const InfoObjectCommon& other) const
+{
+	return isEqual(other);
+}
+
+bool InfoObjectCommon::isEqual(const InfoObjectCommon& other) const
+{
+	return isEqual(other, false);
+}
+
+bool InfoObjectCommon::isSimilar(const InfoObjectCommon& other) const
+{
+	return isEqual(other, true);
+}
+
+bool InfoObjectCommon::isEqual(const InfoObjectCommon& other, bool similar) const
+{
+	if (getInfoObjectType() != other.getInfoObjectType()) return false;
+	if (m_sampleRate != other.m_sampleRate) return false;
+	if (!similar && getIdentifier().trim() != other.getIdentifier().trim()) return false;
+	return checkEqual(other, similar);
+}
+
 //DataChannel
 
 DataChannel::DataChannel(DataChannelTypes type, float sampleRate, GenericProcessor* source, uint16 subproc) :
@@ -297,6 +325,16 @@ void DataChannel::setDefaultNameAndDescription()
 	setIdentifier("genericdata.continuous");
 }
 
+bool DataChannel::checkEqual(const InfoObjectCommon& other, bool similar) const
+{
+	const DataChannel& o = dynamic_cast<const DataChannel&>(other);
+	if (m_bitVolts != o.m_bitVolts) return false;
+	if (!similar && m_unitName != o.m_unitName) return false;
+	if (similar)
+		return hasSimilarMetadata(o);
+	else return hasSameMetadata(o);
+}
+
 //EventChannel
 EventChannel::EventChannel(EventChannelTypes type, unsigned int nChannels, unsigned int dataLength, float sampleRate, GenericProcessor* source, uint16 subproc)
 	: InfoObjectCommon(source->eventChannelCount++, source->eventChannelTypeCount[type]++, sampleRate, source, subproc),
@@ -418,6 +456,56 @@ void EventChannel::setDefaultNameAndDescription()
 	setIdentifier("genericevent");
 }
 
+
+BaseType EventChannel::getEquivalentMetaDataType(const EventChannel& ev)
+{
+	switch (ev.getChannelType())
+	{
+	case EventChannel::TEXT:
+		return MetaDataDescriptor::CHAR;
+	case EventChannel::INT8_ARRAY:
+		return MetaDataDescriptor::INT8;
+	case EventChannel::UINT8_ARRAY:
+		return MetaDataDescriptor::UINT8;
+	case EventChannel::INT16_ARRAY:
+		return MetaDataDescriptor::INT16;
+	case EventChannel::UINT16_ARRAY:
+		return MetaDataDescriptor::UINT16;
+	case EventChannel::INT32_ARRAY:
+		return MetaDataDescriptor::INT32;
+	case EventChannel::UINT32_ARRAY:
+		return MetaDataDescriptor::UINT32;
+	case EventChannel::INT64_ARRAY:
+		return MetaDataDescriptor::INT64;
+	case EventChannel::UINT64_ARRAY:
+		return MetaDataDescriptor::UINT64;
+	case EventChannel::FLOAT_ARRAY:
+		return MetaDataDescriptor::FLOAT;
+	case EventChannel::DOUBLE_ARRAY:
+		return MetaDataDescriptor::DOUBLE;
+	default:
+		return MetaDataDescriptor::UINT8;
+	}
+}
+
+BaseType EventChannel::getEquivalentMetaDataType() const
+{
+	return getEquivalentMetaDataType(*this);
+}
+
+bool EventChannel::checkEqual(const InfoObjectCommon& other, bool similar) const
+{
+	const EventChannel& o = dynamic_cast<const EventChannel&>(other);
+	if (m_type != o.m_type) return false;
+	if (m_numChannels != o.m_numChannels) return false;
+	if (m_length != o.m_length) return false;
+	if (similar && !hasSimilarMetadata(o)) return false;
+	if (!similar && !hasSameMetadata(o)) return false;
+	if (similar && !hasSimilarEventMetadata(o)) return false;
+	if (!similar && !hasSameEventMetadata(o)) return false;
+	return true;
+}
+
 //SpikeChannel
 
 SpikeChannel::SpikeChannel(ElectrodeTypes type, GenericProcessor* source, const Array<const DataChannel*>& sourceChannels, uint16 subproc)
@@ -428,7 +516,7 @@ SpikeChannel::SpikeChannel(ElectrodeTypes type, GenericProcessor* source, const
 	jassert(n == getNumChannels(type));
 	for (int i = 0; i < n; i++)
 	{
-		sourceChannelInfo info;
+		SourceChannelInfo info;
 		const DataChannel* chan = sourceChannels[i];
 		info.processorID = chan->getSourceNodeID();
 		info.subProcessorID = chan->getSubProcessorIdx();
@@ -452,7 +540,7 @@ SpikeChannel::ElectrodeTypes SpikeChannel::getChannelType() const
 	return m_type;
 }
 
-Array<sourceChannelInfo> SpikeChannel::getSourceChannelInfo() const
+Array<SourceChannelInfo> SpikeChannel::getSourceChannelInfo() const
 {
 	return m_sourceInfo;
 }
@@ -549,6 +637,27 @@ void SpikeChannel::setDefaultNameAndDescription()
 	setIdentifier("spikesource");
 }
 
+bool SpikeChannel::checkEqual(const InfoObjectCommon& other, bool similar) const
+{
+	const SpikeChannel& o = dynamic_cast<const SpikeChannel&>(other);
+	if (m_type != o.m_type) return false;
+	if (m_numPostSamples != o.m_numPostSamples) return false;
+	if (m_numPreSamples != o.m_numPreSamples) return false;
+
+	int nChans = m_channelBitVolts.size();
+	if (nChans != o.m_channelBitVolts.size()) return false;
+	for (int i = 0; i < nChans; i++)
+	{
+		if (m_channelBitVolts[i] != o.m_channelBitVolts[i]) return false;
+	}
+
+	if (similar && !hasSimilarMetadata(o)) return false;
+	if (!similar && !hasSameMetadata(o)) return false;
+	if (similar && !hasSimilarEventMetadata(o)) return false;
+	if (!similar && !hasSameEventMetadata(o)) return false;
+	return true;
+}
+
 //ConfigurationObject
 ConfigurationObject::ConfigurationObject(String identifier, GenericProcessor* source, uint16 subproc)
 	: SourceProcessorInfo(source, subproc)
diff --git a/Source/Processors/Channel/InfoObjects.h b/Source/Processors/Channel/InfoObjects.h
index a3e1679b52941ab288494aa98d7b47c58ca6248b..cdda5769ef0e0546a494e5c4617b5ae97ab67666 100644
--- a/Source/Processors/Channel/InfoObjects.h
+++ b/Source/Processors/Channel/InfoObjects.h
@@ -35,7 +35,7 @@ class GenericProcessor;
 /**
 Structure with the basic info that identifies a channel
 */
-struct sourceChannelInfo
+struct SourceChannelInfo
 {
 	uint16 processorID;
 	uint16 subProcessorID;
@@ -49,15 +49,19 @@ class PLUGIN_API NodeInfoBase
 public:
     virtual ~NodeInfoBase();
 	/** Gets the ID of the processor which currently owns this copy of the info object */
-	unsigned int getCurrentNodeID() const;
+	uint16 getCurrentNodeID() const;
+	/** Gets the index of this channel in the processor which currently owns this copy of the info object */
+	uint16 getCurrentNodeChannelIdx() const;
 	/** Gets the type of the processor which currently owns this copy of the info object */
 	String getCurrentNodeType() const;
 	/** Gets the name of the processor which currently owns this copy of the info object */
 	String getCurrentNodeName() const;
 protected:
 	NodeInfoBase() = delete;
-	NodeInfoBase(uint16 id);
+	NodeInfoBase(uint16 id, uint16 idx);
+private:
 	uint16 m_nodeID{ 0 };
+	uint16 m_nodeIdx{ 0 };
 	String m_currentNodeType;
 	String m_currentNodeName;
 };
@@ -71,7 +75,7 @@ protected:
 public:
     virtual ~HistoryObject();
 	/** Returns the historic string */
-	String getHistoricString();
+	String getHistoricString() const;
 	/** Adds a new entry in the historic string*/
 	void addToHistoricString(String entry);
 
@@ -171,7 +175,13 @@ public:
 
 	virtual InfoObjectType getInfoObjectType() const = 0;
 
+	bool isEqual(const InfoObjectCommon& other) const;
+	bool isSimilar(const InfoObjectCommon& other) const;
+	bool operator==(const InfoObjectCommon& other) const;
+
 private:
+	bool isEqual(const InfoObjectCommon& other, bool similar) const;
+	virtual bool checkEqual(const InfoObjectCommon& other, bool similar) const = 0;
 	/** Index of the object in the source processor */
 	const uint16 m_sourceIndex;
 	/** Index of this particular subtype in the source processor */
@@ -251,6 +261,7 @@ public:
 	InfoObjectType getInfoObjectType() const override;
 	void setDefaultNameAndDescription() override;
 private:
+	bool checkEqual(const InfoObjectCommon& other, bool similar) const override;
 	const DataChannelTypes m_type;
 	float m_bitVolts{ 1.0f };
 	bool m_isEnabled{ true };
@@ -341,9 +352,16 @@ public:
 	/** Gets the size in bytes of an element depending of the type*/
 	static size_t getTypeByteSize(EventChannelTypes type);
 
+	/** Handy method to get an equivalente metadata value type for the main event data*/
+	static BaseType getEquivalentMetaDataType(const EventChannel& ev);
+
+	/** Handy method to get an equivalente metadata value type for the main event data*/
+	BaseType getEquivalentMetaDataType() const;
+
 	InfoObjectType getInfoObjectType() const override;
 	void setDefaultNameAndDescription() override;
 private:
+	bool checkEqual(const InfoObjectCommon& other, bool similar) const override;
 	const EventChannelTypes m_type;
 	unsigned int m_numChannels{ 1 };
 	size_t m_dataSize{ 1 };
@@ -378,7 +396,7 @@ public:
 	ElectrodeTypes getChannelType() const;
 
 	/** Returns an array with info about the channels from which the spikes originate */
-	Array<sourceChannelInfo> getSourceChannelInfo() const;
+	Array<SourceChannelInfo> getSourceChannelInfo() const;
 
 	/** Sets the number of samples, pre and post peak */
 	void setNumSamples(unsigned int preSamples, unsigned int postSamples);
@@ -413,8 +431,9 @@ public:
 	InfoObjectType getInfoObjectType() const override;
 	void setDefaultNameAndDescription() override;
 private:
+	bool checkEqual(const InfoObjectCommon& other, bool similar) const override;
 	const ElectrodeTypes m_type;
-	Array<sourceChannelInfo> m_sourceInfo;
+	Array<SourceChannelInfo> m_sourceInfo;
 	unsigned int m_numPreSamples{ 8 };
 	unsigned int m_numPostSamples{ 32 };
 	Array<float> m_channelBitVolts;
diff --git a/Source/Processors/Channel/MetaData.cpp b/Source/Processors/Channel/MetaData.cpp
index 5dca331398785c7bcdfb1643bddcd620919b5858..8c6711b16d9c5775ed510c9e130fa29d3b436a46 100644
--- a/Source/Processors/Channel/MetaData.cpp
+++ b/Source/Processors/Channel/MetaData.cpp
@@ -76,7 +76,7 @@ String MetaDataDescriptor::getName() const { return m_name; }
 String MetaDataDescriptor::getDescription() const { return m_description; }
 String MetaDataDescriptor::getIdentifier() const { return m_identifier; }
 
-bool MetaDataDescriptor::isEqual(const MetaDataDescriptor& other) const
+bool MetaDataDescriptor::isSimilar(const MetaDataDescriptor& other) const
 {
 	if ((m_type == other.m_type) && (m_length == other.m_length))
 		return true;
@@ -84,6 +84,14 @@ bool MetaDataDescriptor::isEqual(const MetaDataDescriptor& other) const
 		return false;
 }
 
+bool MetaDataDescriptor::isEqual(const MetaDataDescriptor& other) const
+{
+	if ((m_type == other.m_type) && (m_length == other.m_length) && m_identifier.trim() == other.m_identifier.trim())
+		return true;
+	else
+		return false;
+}
+
 bool MetaDataDescriptor::operator==(const MetaDataDescriptor& other) const
 {
 	return isEqual(other);
@@ -209,7 +217,7 @@ void MetaDataValue::setValue(const String& data)
 void MetaDataValue::getValue(String& data) const
 {
 	jassert(m_type == MetaDataDescriptor::CHAR);
-	data.createStringFromData(m_data.getData(), m_length);
+	data = String::createStringFromData(m_data.getData(), m_length);
 }
 
 template <typename T>
@@ -335,6 +343,36 @@ int MetaDataInfoObject::findMetaData(MetaDataDescriptor::MetaDataTypes type, uns
 	return -1;
 }
 
+bool MetaDataInfoObject::hasSameMetadata(const MetaDataInfoObject& other) const
+{
+	return checkMetaDataCoincidence(other, false);
+}
+
+bool MetaDataInfoObject::hasSimilarMetadata(const MetaDataInfoObject& other) const
+{
+	return checkMetaDataCoincidence(other, true);
+}
+
+bool MetaDataInfoObject::checkMetaDataCoincidence(const MetaDataInfoObject& other, bool similar) const
+{
+	int nMetaData = m_metaDataDescriptorArray.size();
+	if (nMetaData != other.m_metaDataDescriptorArray.size()) return false;
+	for (int i = 0; i < nMetaData; i++)
+	{
+		MetaDataDescriptorPtr md = m_metaDataDescriptorArray[i];
+		MetaDataDescriptorPtr mdo = other.m_metaDataDescriptorArray[i];
+		if (similar)
+		{
+			if (!md->isSimilar(*mdo)) return false;
+		}
+		else
+		{
+			if (!md->isEqual(*mdo)) return false;
+		}
+	}
+	return true;
+}
+
 //MetaDataEventObject
 
 MetaDataEventObject::MetaDataEventObject() {}
@@ -398,6 +436,36 @@ int MetaDataEventObject::findEventMetaData(MetaDataDescriptor::MetaDataTypes typ
 	return -1;
 }
 
+bool MetaDataEventObject::hasSameEventMetadata(const MetaDataEventObject& other) const
+{
+	return checkMetaDataCoincidence(other, false);
+}
+
+bool MetaDataEventObject::hasSimilarEventMetadata(const MetaDataEventObject& other) const
+{
+	return checkMetaDataCoincidence(other, true);
+}
+
+bool MetaDataEventObject::checkMetaDataCoincidence(const MetaDataEventObject& other, bool similar) const
+{
+	int nMetaData = m_eventMetaDataDescriptorArray.size();
+	if (nMetaData != other.m_eventMetaDataDescriptorArray.size()) return false;
+	for (int i = 0; i < nMetaData; i++)
+	{
+		MetaDataDescriptorPtr md = m_eventMetaDataDescriptorArray[i];
+		MetaDataDescriptorPtr mdo = other.m_eventMetaDataDescriptorArray[i];
+		if (similar)
+		{
+			if (!md->isSimilar(*mdo)) return false;
+		}
+		else
+		{
+			if (!md->isEqual(*mdo)) return false;
+		}
+	}
+	return true;
+}
+
 size_t MetaDataEventObject::getMaxEventMetaDataSize() const
 {
 	return m_maxSize;
diff --git a/Source/Processors/Channel/MetaData.h b/Source/Processors/Channel/MetaData.h
index 7ecbb0b164667dd86e86ef57eebf310a3cea54f8..88f316715f1a624a4b106f4d20dd78ed8c868486 100644
--- a/Source/Processors/Channel/MetaData.h
+++ b/Source/Processors/Channel/MetaData.h
@@ -85,8 +85,12 @@ public:
 	/** Gets the machine-readable identifier for this field */
 	String getIdentifier() const;
 
+	/** Returns true if both descriptors have the same type, length and identifier */
 	bool isEqual(const MetaDataDescriptor& other) const;
+	/** Returns true if both descriptors have the same type, length and identifier */
 	bool operator==(const MetaDataDescriptor& other) const;
+	/** Returns true if both descriptors have the same type and length, regardless of the identifier */
+	bool isSimilar(const MetaDataDescriptor& other) const;
 
 	static size_t getTypeSize(MetaDataTypes type);
 private:
@@ -184,9 +188,13 @@ public:
 	const MetaDataValue* getMetaDataValue(int index) const;
 	int findMetaData(MetaDataDescriptor::MetaDataTypes type, unsigned int length, String identifier = String::empty) const;
 	const int getMetaDataCount() const;
+	bool hasSameMetadata(const MetaDataInfoObject& other) const;
+	bool hasSimilarMetadata(const MetaDataInfoObject& other) const;
 protected:
 	MetaDataDescriptorArray m_metaDataDescriptorArray;
 	MetaDataValueArray m_metaDataValueArray;
+private:
+	bool checkMetaDataCoincidence(const MetaDataInfoObject& other, bool similar) const;
 };
 
 class PLUGIN_API MetaDataEventLock
@@ -215,11 +223,15 @@ public:
 	int getEventMetaDataCount() const;
 	//gets the largest metadata size, which can be useful to reserve buffers in advance
 	size_t getMaxEventMetaDataSize() const;
+	bool hasSameEventMetadata(const MetaDataEventObject& other) const;
+	bool hasSimilarEventMetadata(const MetaDataEventObject& other) const;
 protected:
 	MetaDataDescriptorArray m_eventMetaDataDescriptorArray;
 	MetaDataEventObject();
 	size_t m_totalSize{ 0 };
 	size_t m_maxSize{ 0 };
+private:
+	bool checkMetaDataCoincidence(const MetaDataEventObject& other, bool similar) const;
 };
 
 //And the base from which event objects can hold their metadata before serializing
@@ -239,4 +251,6 @@ protected:
 //Helper function to compare identifier strings
 bool compareIdentifierStrings(const String& identifier, const String& compareWith);
 
+typedef MetaDataDescriptor::MetaDataTypes BaseType;
+
 #endif
\ No newline at end of file
diff --git a/Source/Processors/Events/Events.cpp b/Source/Processors/Events/Events.cpp
index b2b11a2115621e22a8f0547b628d71ce5f141c76..dbf122acb86a0d8e8cbf9ee93218156d3c81aff4 100644
--- a/Source/Processors/Events/Events.cpp
+++ b/Source/Processors/Events/Events.cpp
@@ -218,6 +218,14 @@ String SystemEvent::getSyncText(const MidiMessage& msg)
 }
 
 //Event
+Event::Event(const Event& other)
+	: Event(other)
+{
+	size_t size = other.m_channelInfo->getDataSize();
+	m_data.malloc(size);
+	memcpy(m_data.getData(), other.m_data.getData(), size);
+}
+
 EventChannel::EventChannelTypes Event::getEventType() const
 {
 	return m_eventType;
@@ -300,6 +308,11 @@ bool Event::createChecks(const EventChannel* channelInfo, EventChannel::EventCha
 	return true;
 }
 
+const void* Event::getRawDataPointer() const
+{
+	return m_data.getData();
+}
+
 //TTLEvent
 
 TTLEvent::TTLEvent(const EventChannel* channelInfo, int64 timestamp, uint16 channel, const void* eventData)
@@ -452,21 +465,22 @@ TTLEventPtr TTLEvent::deserializeFromMessage(const MidiMessage& msg, const Event
 
 //TextEvent
 TextEvent::TextEvent(const EventChannel* channelInfo, int64 timestamp, uint16 channel, const String& text)
-	: Event(channelInfo, timestamp, channel),
-	m_text(text)
+	: Event(channelInfo, timestamp, channel)
 {
+	m_data.calloc(channelInfo->getDataSize());
+	text.copyToUTF8(m_data.getData(), channelInfo->getDataSize());
 }
 
 TextEvent::~TextEvent() {}
 
 TextEvent::TextEvent(const TextEvent& other)
-	: Event(other),
-	m_text(other.m_text)
-{}
+	: Event(other)
+{
+}
 
 String TextEvent::getText() const
 {
-	return m_text;
+	return String(m_data.getData(), m_channelInfo->getLength());
 }
 
 void TextEvent::serialize(void* dstBuffer, size_t dstSize) const
@@ -477,10 +491,10 @@ void TextEvent::serialize(void* dstBuffer, size_t dstSize) const
 
 	size_t dataSize = m_channelInfo->getDataSize();
 	size_t eventSize = dataSize + EVENT_BASE_SIZE;
-	size_t stringSize = m_text.getNumBytesAsUTF8();
-	memcpy((buffer + EVENT_BASE_SIZE), m_text.toUTF8(), stringSize);
-	if ((dataSize - stringSize) > 0)
-		zeromem((buffer + EVENT_BASE_SIZE + stringSize), dataSize - stringSize);
+	//size_t stringSize = m_text.getNumBytesAsUTF8();
+	memcpy((buffer + EVENT_BASE_SIZE), m_data, dataSize);
+//	if ((dataSize - stringSize) > 0)
+//		zeromem((buffer + EVENT_BASE_SIZE + stringSize), dataSize - stringSize);
 	serializeMetaData(buffer + eventSize);
 }
 
@@ -492,7 +506,7 @@ TextEventPtr TextEvent::createTextEvent(const EventChannel* channelInfo, int64 t
 		return nullptr;
 	}
 
-	if (text.length() > channelInfo->getLength())
+	if (text.getNumBytesAsUTF8() > channelInfo->getDataSize())
 	{
 		jassertfalse;
 		return nullptr;
@@ -509,7 +523,7 @@ TextEventPtr TextEvent::createTextEvent(const EventChannel* channelInfo, int64 t
 		return nullptr;
 	}
 
-	if (text.length() > channelInfo->getLength())
+	if (text.getNumBytesAsUTF8() > channelInfo->getDataSize())
 	{
 		jassertfalse;
 		return nullptr;
@@ -600,9 +614,6 @@ BinaryEvent::BinaryEvent(const BinaryEvent& other)
 	:Event(other),
 	m_type(other.m_type)
 {
-	size_t size = other.m_channelInfo->getDataSize();
-	m_data.malloc(size);
-	memcpy(m_data.getData(), other.m_data.getData(), size);
 }
 
 BinaryEvent::~BinaryEvent() {}
diff --git a/Source/Processors/Events/Events.h b/Source/Processors/Events/Events.h
index 2a5076e2b84ac63265ac038851039783381ee6f5..45a5fadbe2b4442b98be06ee820f120473099a8e 100644
--- a/Source/Processors/Events/Events.h
+++ b/Source/Processors/Events/Events.h
@@ -134,12 +134,18 @@ class PLUGIN_API Event
 public:
 	virtual ~Event();
 	virtual void serialize(void* dstBuffer, size_t dstSize) const override = 0;
+	Event(const Event& other);
+	Event& operator=(const Event&) = delete;
+
 	EventChannel::EventChannelTypes getEventType() const;
 	const EventChannel* getChannelInfo() const;
 
 	/** Gets the channel that triggered the event */
 	uint16 getChannel() const;
 
+	/* Gets the raw data payload */
+	const void* getRawDataPointer() const;
+
 	static EventChannel::EventChannelTypes getEventType(const MidiMessage& msg);
 	static EventPtr deserializeFromMessage(const MidiMessage& msg, const EventChannel* channelInfo);
 
@@ -154,6 +160,8 @@ protected:
 	const EventChannel* m_channelInfo;
 	const EventChannel::EventChannelTypes m_eventType;
 
+	HeapBlock<char> m_data;
+
 };
 
 typedef ScopedPointer<TTLEvent> TTLEventPtr;
@@ -179,7 +187,6 @@ private:
 	TTLEvent() = delete;
 	TTLEvent(const EventChannel* channelInfo, int64 timestamp, uint16 channel, const void* eventData);
 
-	HeapBlock<char> m_data;
 	JUCE_LEAK_DETECTOR(TTLEvent);
 };
 
@@ -202,7 +209,6 @@ private:
 	TextEvent() = delete;
 	TextEvent(const EventChannel* channelInfo, int64 timestamp, uint16 channel, const String& text);
 
-	const String m_text;
 	JUCE_LEAK_DETECTOR(TextEvent);
 };
 
@@ -235,7 +241,6 @@ private:
 	template<typename T>
 	static EventChannel::EventChannelTypes getType();
 
-	HeapBlock<char> m_data;
 	const EventChannel::EventChannelTypes m_type;
 	JUCE_LEAK_DETECTOR(BinaryEvent);
 };
diff --git a/Source/Processors/GenericProcessor/GenericProcessor.cpp b/Source/Processors/GenericProcessor/GenericProcessor.cpp
index b0f11fd1ea3f1ac4f200b4f4cdad0a055f86f7ec..7597465ddbcb21140cc89120722b3ccd4356ed18 100755
--- a/Source/Processors/GenericProcessor/GenericProcessor.cpp
+++ b/Source/Processors/GenericProcessor/GenericProcessor.cpp
@@ -412,6 +412,7 @@ void GenericProcessor::updateChannelIndexes(bool updateNodeID)
 		if (updateNodeID)
 		{
 			channel->m_nodeID = nodeId;
+			channel->m_nodeIdx = i;
 			channel->m_currentNodeName = getName();
 			channel->m_currentNodeType = getName(); //Fix when the ability to name individual processors is implemented
 		}
@@ -424,6 +425,7 @@ void GenericProcessor::updateChannelIndexes(bool updateNodeID)
 		if (updateNodeID)
 		{
 			channel->m_nodeID = nodeId;
+			channel->m_nodeIdx = i;
 			channel->m_currentNodeName = getName();
 			channel->m_currentNodeType = getName(); //Fix when the ability to name individual processors is implemented
 		}
@@ -436,6 +438,7 @@ void GenericProcessor::updateChannelIndexes(bool updateNodeID)
 		if (updateNodeID)
 		{
 			channel->m_nodeID = nodeId;
+			channel->m_nodeIdx = i;
 			channel->m_currentNodeName = getName();
 			channel->m_currentNodeType = getName(); //Fix when the ability to name individual processors is implemented
 		}
diff --git a/Source/Processors/RecordNode/EngineConfigWindow.cpp b/Source/Processors/RecordNode/EngineConfigWindow.cpp
index 6fb06d7c12ae92d68ca98f87c3f2c4e7285efa9d..ffd7e00540c6f6a6801b643f406c41f75154ba71 100644
--- a/Source/Processors/RecordNode/EngineConfigWindow.cpp
+++ b/Source/Processors/RecordNode/EngineConfigWindow.cpp
@@ -33,11 +33,26 @@ EngineParameterComponent::EngineParameterComponent(EngineParameter& param)
         but->setBounds(120,0,100,20);
         addAndMakeVisible(but);
         control = but;
+		name = param.name;
     }
+	else if (param.type == EngineParameter::MULTI)
+	{
+		ComboBox* box = new ComboBox();
+		box->setBounds(120, 0, 100, 20);
+		StringArray options = StringArray::fromTokens(param.name, "|", "\"");
+		name = options[0];
+		options.remove(0);
+		box->addItemList(options, 1);
+		box->setSelectedId(param.multiParam.value + 1, dontSendNotification);
+		box->setEditableText(false);
+		addAndMakeVisible(box);
+		control = box;
+	}
     else
     {
         Label* lab = new Label();
         lab->setFont(Font("Small Text",10,Font::plain));
+		name = param.name;
         switch (param.type)
         {
             case EngineParameter::BOOL:
@@ -63,7 +78,7 @@ EngineParameterComponent::EngineParameterComponent(EngineParameter& param)
         addAndMakeVisible(lab);
         control = lab;
     }
-    this->setTooltip(param.name);
+    this->setTooltip(name);
 }
 
 EngineParameterComponent::~EngineParameterComponent()
@@ -74,7 +89,7 @@ void EngineParameterComponent::paint(Graphics& g)
 {
     g.setColour(Colours::black);
     g.setFont(13);
-    g.drawText(parameter.name+":",0,0,100,30,Justification::left,true);
+    g.drawText(name+":",0,0,100,30,Justification::left,true);
 }
 
 void EngineParameterComponent::labelTextChanged(Label* l)
@@ -104,25 +119,28 @@ void EngineParameterComponent::saveValue()
     switch (parameter.type)
     {
         case EngineParameter::BOOL:
-            parameter.boolParam.value = ((ToggleButton*)control.get())->getToggleState();
+            parameter.boolParam.value = static_cast<ToggleButton*>(control.get())->getToggleState();
             break;
         case EngineParameter::INT:
-            parameter.intParam.value = ((Label*)control.get())->getText().getIntValue();
+            parameter.intParam.value = static_cast<Label*>(control.get())->getText().getIntValue();
             if (parameter.intParam.value < parameter.intParam.min)
                 parameter.intParam.value = parameter.intParam.min;
             if (parameter.intParam.value > parameter.intParam.max)
                 parameter.intParam.value = parameter.intParam.max;
             break;
         case EngineParameter::FLOAT:
-            parameter.floatParam.value = ((Label*)control.get())->getText().getFloatValue();
+            parameter.floatParam.value = static_cast<Label*>(control.get())->getText().getFloatValue();
             if (parameter.floatParam.value < parameter.floatParam.min)
                 parameter.floatParam.value = parameter.floatParam.min;
             if (parameter.floatParam.value > parameter.floatParam.max)
                 parameter.floatParam.value = parameter.floatParam.max;
             break;
         case EngineParameter::STR:
-            parameter.strParam.value = ((Label*)control.get())->getText();
+            parameter.strParam.value = static_cast<Label*>(control.get())->getText();
             break;
+		case EngineParameter::MULTI:
+			parameter.multiParam.value = static_cast<ComboBox*>(control.get())->getSelectedId() - 1;
+			break;
     }
 }
 
@@ -135,7 +153,7 @@ EngineConfigComponent::EngineConfigComponent(RecordEngineManager* man, int heigh
     for (int i = 0; i < man->getNumParameters(); i++)
     {
         EngineParameterComponent* par = new EngineParameterComponent(man->getParameter(i));
-        if (man->getParameter(i).type == EngineParameter::STR)
+        if (man->getParameter(i).type == EngineParameter::STR || man->getParameter(i).type == EngineParameter::MULTI)
             hasString=true;
         par->setBounds(10,10+40*i,300,30);
         addAndMakeVisible(par);
diff --git a/Source/Processors/RecordNode/EngineConfigWindow.h b/Source/Processors/RecordNode/EngineConfigWindow.h
index d57975b7bf8fed8dad5eb7a7f8fff0ddfbe0551f..0e3e1925bb1e1714bea8cdf213d903d6d5749b5f 100644
--- a/Source/Processors/RecordNode/EngineConfigWindow.h
+++ b/Source/Processors/RecordNode/EngineConfigWindow.h
@@ -43,6 +43,7 @@ private:
     ScopedPointer<Component> control;
     EngineParameter::EngineParameterType type;
     EngineParameter& parameter;
+	String name;
     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EngineParameterComponent);
 };
 
diff --git a/Source/Processors/RecordNode/OriginalRecording.h b/Source/Processors/RecordNode/OriginalRecording.h
index 3730992ec2d91d1e0aef513f8eedc5b6fb7849aa..849e479f1dac1fe796fc2064effb8be7c68b607e 100644
--- a/Source/Processors/RecordNode/OriginalRecording.h
+++ b/Source/Processors/RecordNode/OriginalRecording.h
@@ -44,7 +44,7 @@ public:
     OriginalRecording();
     ~OriginalRecording();
 
-    void setParameter(EngineParameter& parameter);
+    void setParameter(EngineParameter& parameter) override;
     String getEngineID() const override;
     void openFiles(File rootFolder, int experimentNumber, int recordingNumber) override;
 	void closeFiles() override;
diff --git a/Source/Processors/RecordNode/RecordEngine.cpp b/Source/Processors/RecordNode/RecordEngine.cpp
index 6aaef305fd4b5b3db8118d256a01200f92536645..c627fd54b49c5c48ed9af8ae17ea8c52cc93a638 100644
--- a/Source/Processors/RecordNode/RecordEngine.cpp
+++ b/Source/Processors/RecordNode/RecordEngine.cpp
@@ -108,6 +108,11 @@ int RecordEngine::getNumRecordedEvents() const
 	return AccessClass::getProcessorGraph()->getRecordNode()->getTotalEventChannels();
 }
 
+int RecordEngine::getNumRecordedSpikes() const
+{
+	return AccessClass::getProcessorGraph()->getRecordNode()->getTotalSpikeChannels();
+}
+
 void RecordEngine::registerSpikeSource (const GenericProcessor* processor) {}
 
 int RecordEngine::getNumRecordedProcessors() const
@@ -186,6 +191,10 @@ EngineParameter::EngineParameter (EngineParameter::EngineParameterType paramType
     {
         strParam.value = defaultValue;
     }
+	else if (paramType == MULTI)
+	{
+		multiParam.value = defaultValue;
+	}
 }
 
 
@@ -209,6 +218,9 @@ void EngineParameter::restoreDefault()
             strParam.value = def;
             break;
 
+		case MULTI:
+			multiParam.value = def;
+
         default:
             break;
     }
@@ -329,6 +341,10 @@ void RecordEngineManager::saveParametersToXml (XmlElement* xml)
                 param->setAttribute ("value", parameters[i]->strParam.value);
                 break;
 
+			case EngineParameter::MULTI:
+				param->setAttribute("type", "multi");
+				param->setAttribute("value", parameters[i]->multiParam.value);
+
             default:
                 break;
         }
@@ -363,6 +379,11 @@ void RecordEngineManager::loadParametersFromXml (XmlElement* xml)
                 {
                     parameters[i]->strParam.value = xmlNode->getStringAttribute ("value");
                 }
+				else if ((xmlNode->getStringAttribute("type") == "multi")
+					&& (parameters[i]->type == EngineParameter::MULTI))
+				{
+					parameters[i]->multiParam.value = xmlNode->getIntAttribute("value");
+				}
             }
         }
     }
diff --git a/Source/Processors/RecordNode/RecordEngine.h b/Source/Processors/RecordNode/RecordEngine.h
index f6864ec06021f644be3c7c78aaa81274926f5f7b..d6234c07dc506af898501cd0002e0f1e2e4f4785 100644
--- a/Source/Processors/RecordNode/RecordEngine.h
+++ b/Source/Processors/RecordNode/RecordEngine.h
@@ -38,6 +38,8 @@
         v = parameter.floatParam.value
 #define strParameter(i,v) if ((parameter.id == i) && (parameter.type == EngineParameter::STR)) \
         v = parameter.strParam.value
+#define multiParameter(i,v) if ((parameter.id == i) && (parameter.type == EngineParameter::MULTI)) \
+        v = parameter.multiParam.value
 
 struct RecordProcessorInfo
 {
@@ -165,7 +167,7 @@ protected:
     /** Generate a Matlab-compatible datestring */
     String generateDateString() const;
 
-    /** Gets the current block's first timestamp for a given channel */
+    /** Gets the current block's first timestamp for a given recorded channel */
     int64 getTimestamp (int channel) const;
 
     /** Gets the actual channel number from a recorded channel index */
@@ -178,8 +180,9 @@ protected:
 	 (right now all channels are recorded) */
 	int getNumRecordedEvents() const;
 
-	/** TODO: to fill when the probe system is implemented*/
-	//int getNumRecordedSpikes() const;
+	/** Gets the number of recorded spike channels
+	(right now all channels are recorded) */
+	int getNumRecordedSpikes() const;
 
     /** Gets the number of processors being recorded
     */
@@ -219,7 +222,7 @@ typedef RecordEngine* (*EngineCreator)();
 struct PLUGIN_API EngineParameter
 {
 public:
-    enum EngineParameterType { STR, INT, FLOAT, BOOL };
+    enum EngineParameterType { STR, INT, FLOAT, BOOL, MULTI };
 
     EngineParameter (EngineParameterType paramType,
                      int paramId,
@@ -250,6 +253,11 @@ public:
         {
             bool value;
         } boolParam;
+		
+		struct 
+		{
+			int value;
+		} multiParam;
     };
 
     //Strings can't be inside an union. This means wasting a bit of memory, but adds more safety than using char*