From e9dd581d8c4dceb7964dc2dae3408f19efd0d1b8 Mon Sep 17 00:00:00 2001
From: Aaron Cuevas Lopez <aacuelo@teleco.upv.es>
Date: Fri, 13 Jan 2017 04:12:26 +0100
Subject: [PATCH] Update Binary Recording plugin

---
 .../Plugins/BinaryWriter/BinaryRecording.cpp  | 144 +++++++++++-------
 Source/Plugins/BinaryWriter/BinaryRecording.h |  15 +-
 2 files changed, 99 insertions(+), 60 deletions(-)

diff --git a/Source/Plugins/BinaryWriter/BinaryRecording.cpp b/Source/Plugins/BinaryWriter/BinaryRecording.cpp
index d3abb257b..eeba19943 100644
--- a/Source/Plugins/BinaryWriter/BinaryRecording.cpp
+++ b/Source/Plugins/BinaryWriter/BinaryRecording.cpp
@@ -69,7 +69,7 @@ void BinaryRecording::openFiles(File rootFolder, int experimentNumber, int recor
 	openMessageFile(basepath, recordingNumber);
 	for (int i = 0; i < spikeFileArray.size(); i++)
 	{
-		openSpikeFile(basepath, getSpikeElectrode(i), recordingNumber);
+		openSpikeFile(basepath, i, recordingNumber);
 	}
 	m_recordingNum = recordingNumber;
 }
@@ -126,7 +126,7 @@ void BinaryRecording::writeData(int writeChannel, int realChannel, const float*
 		m_scaledBuffer.malloc(size);
 		m_intBuffer.malloc(size);
 	}
-	double multFactor = 1 / (float(0x7fff) * getChannel(realChannel)->bitVolts);
+	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);
 
@@ -135,7 +135,7 @@ void BinaryRecording::writeData(int writeChannel, int realChannel, const float*
 
 //Code below is copied from OriginalRecording, so it's not as clean as newer one
 
-void BinaryRecording::addSpikeElectrode(int index, const SpikeRecordInfo* elec)
+void BinaryRecording::addSpikeElectrode(int index, const SpikeChannel* elec)
 {
 	spikeFileArray.add(nullptr);
 }
@@ -185,11 +185,11 @@ void BinaryRecording::openEventFile(String basepath, int recordingNumber)
 
 }
 
-void BinaryRecording::openSpikeFile(String basePath, SpikeRecordInfo* elec, int recordingNumber)
+void BinaryRecording::openSpikeFile(String basePath, int spikeIndex, int recordingNumber)
 {
-
+	const SpikeChannel* elec = getSpikeChannel(spikeIndex);
 	FILE* spFile;
-	String fullPath = basePath + "_" + elec->name.removeCharacters(" ") + "_" + String(recordingNumber) + ".spikes";
+	String fullPath = basePath + "_" + elec->getName().removeCharacters(" ") + "_" + String(recordingNumber) + ".spikes";
 	
 	std::cout << "OPENING FILE: " << fullPath << std::endl;
 
@@ -207,7 +207,7 @@ void BinaryRecording::openSpikeFile(String basePath, SpikeRecordInfo* elec, int
 		fwrite(header.toUTF8(), 1, header.getNumBytesAsUTF8(), spFile);
 	}
 	diskWriteLock.exit();
-	spikeFileArray.set(elec->recordIndex, spFile);
+	spikeFileArray.set(spikeIndex, spFile);
 
 }
 
@@ -263,7 +263,7 @@ String BinaryRecording::generateEventHeader()
 
 	header += "header.sampleRate = ";
 	// all channels need to have the same sample rate under the current scheme
-	header += String(getChannel(0)->sampleRate);
+	header += String(getEventChannel(0)->getSampleRate());
 	header += ";\n";
 	header += "header.blockLength = ";
 	header += BLOCK_LENGTH;
@@ -283,7 +283,7 @@ String BinaryRecording::generateEventHeader()
 
 }
 
-String BinaryRecording::generateSpikeHeader(SpikeRecordInfo* elec)
+String BinaryRecording::generateSpikeHeader(const SpikeChannel* elec)
 {
 	String header = "header.format = 'Open Ephys Data Format'; \n";
 	header += "header.version = " + String(VERSION_STRING) + "; \n";
@@ -300,15 +300,15 @@ String BinaryRecording::generateSpikeHeader(SpikeRecordInfo* elec)
 	header += "';\n";
 
 	header += "header.electrode = '";
-	header += elec->name;
+	header += elec->getName();
 	header += "';\n";
 
 	header += "header.num_channels = ";
-	header += elec->numChannels;
+	header += elec->getNumChannels();
 	header += ";\n";
 
 	header += "header.sampleRate = ";
-	header += String(elec->sampleRate);
+	header += String(elec->getSampleRate());
 	header += ";\n";
 
 	header = header.paddedRight(' ', HEADER_SIZE);
@@ -318,34 +318,41 @@ String BinaryRecording::generateSpikeHeader(SpikeRecordInfo* elec)
 	return header;
 }
 
-void BinaryRecording::writeEvent(int eventType, const MidiMessage& event, int64 timestamp)
+void BinaryRecording::writeEvent(int eventIndex, const MidiMessage& event)
+{
+	writeTTLEvent(eventIndex, event);
+	if (Event::getEventType(event) == EventChannel::TEXT)
+	{
+		TextEventPtr ev = TextEvent::deserializeFromMessage(event, getEventChannel(eventIndex));
+		if (ev == nullptr) return;
+		writeMessage(ev->getText(), ev->getSourceID(), ev->getChannel(), ev->getTimestamp());
+	}
+}
+
+void BinaryRecording::writeTimestampSyncText(uint16 sourceID, uint16 sourceIdx, uint64 timestamp, String text)
 {
-	if (isWritableEvent(eventType))
-		writeTTLEvent(event, timestamp);
-	if (eventType == GenericProcessor::MESSAGE)
-		writeMessage(event, timestamp);
+	writeMessage(text, sourceID, 255, timestamp);
 }
 
-void BinaryRecording::writeMessage(const MidiMessage& event, int64 timestamp)
+void BinaryRecording::writeMessage(String message, uint16 processorID, uint16 channel, uint64 timestamp)
 {
 	if (messageFile == nullptr)
 		return;
 
-	int msgLength = event.getRawDataSize() - 6;
-	const char* dataptr = (const char*)event.getRawData() + 6;
+	int msgLength = message.getNumBytesAsUTF8();
 
 	String timestampText(timestamp);
 
 	diskWriteLock.enter();
 	fwrite(timestampText.toUTF8(), 1, timestampText.length(), messageFile);
 	fwrite(" ", 1, 1, messageFile);
-	fwrite(dataptr, 1, msgLength, messageFile);
+	fwrite(message.toUTF8(), 1, msgLength, messageFile);
 	fwrite("\n", 1, 1, messageFile);
 	diskWriteLock.exit();
 
 }
 
-void BinaryRecording::writeTTLEvent(const MidiMessage& event, int64 timestamp)
+void BinaryRecording::writeTTLEvent(int eventIndex, const MidiMessage& event)
 {
 	// find file and write samples to disk
 	// std::cout << "Received event!" << std::endl;
@@ -353,56 +360,87 @@ void BinaryRecording::writeTTLEvent(const MidiMessage& event, int64 timestamp)
 	if (eventFile == nullptr)
 		return;
 
-	const uint8* dataptr = event.getRawData();
-
+	uint8 data[16];
 	//With the new external recording thread, this field has no sense.
 	int16 samplePos = 0;
 
-	diskWriteLock.enter();
+	EventPtr ev = Event::deserializeFromMessage(event, getEventChannel(eventIndex));
+	if (!ev) return;
+	*reinterpret_cast<uint64*>(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);
 
-	fwrite(&timestamp,					// ptr
-		8,   							// size of each element
-		1, 		  						// count
-		eventFile);   			// ptr to FILE object
 
-	fwrite(&samplePos,							// ptr
-		2,   							// size of each element
-		1, 		  						// count
-		eventFile);   			// ptr to FILE object
+	diskWriteLock.enter();
 
-	// write 1st four bytes of event (type, nodeId, eventId, eventChannel)
-	fwrite(dataptr, 1, 4, eventFile);
-	int16 recordingNumber = m_recordingNum;
-	// write recording number
-	fwrite(&recordingNumber,                     // ptr
-		2,                               // size of each element
-		1,                               // count
-		eventFile);             // ptr to FILE object
+	fwrite(&data,					// ptr
+		sizeof(uint8),   							// size of each element
+		16, 		  						// count
+		eventFile);   			// ptr to FILE object
 
 	diskWriteLock.exit();
 }
 
-void BinaryRecording::writeSpike(int electrodeIndex, const SpikeObject& spike, int64 timestamp)
-{
-	uint8_t spikeBuffer[MAX_SPIKE_BUFFER_LEN];
 
+void BinaryRecording::writeSpike(int electrodeIndex, const SpikeEvent* spike)
+{
 	if (spikeFileArray[electrodeIndex] == nullptr)
 		return;
 
-	packSpike(&spike, spikeBuffer, MAX_SPIKE_BUFFER_LEN);
-
-	int totalBytes = spike.nSamples * spike.nChannels * 2 + // account for samples
-		spike.nChannels * 4 +            // acount for gain
-		spike.nChannels * 2 +            // account for thresholds
-		SPIKE_METADATA_SIZE;             // 42, from SpikeObject.h
-
+	HeapBlock<char> spikeBuffer;
+	const SpikeChannel* channel = getSpikeChannel(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<uint64*>(spikeBuffer.getData() + 1) = spike->getTimestamp();
+	*reinterpret_cast<uint64*>(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;
+	for (int i = 0; i < numChannels; i++)
+	{
+		float scaleFactor = 1 / (float(0x7fff) * channel->getChannelBitVolts(i));
+		FloatVectorOperations::copyWithMultiply(m_scaledBuffer + ptrIdx, spike->getDataPointer(i), scaleFactor, chanSamples);
+		ptrIdx += chanSamples;
+	}
+	AudioDataConverters::convertFloatToInt16LE(m_scaledBuffer, (spikeBuffer.getData() + 42), totalSamples);
+	ptrIdx = totalSamples * 2 + 42;
+	for (int i = 0; i < numChannels; i++)
+	{
+		*reinterpret_cast<float*>(spikeBuffer.getData() + ptrIdx) = channel->getGain();
+		ptrIdx += sizeof(float);
+	}
+	for (int i = 0; i < numChannels; i++)
+	{
+		*reinterpret_cast<int16*>(spikeBuffer.getData() + ptrIdx) = spike->getThreshold(i);
+		ptrIdx += sizeof(int16);
+	}
 
 	diskWriteLock.enter();
 
 	fwrite(spikeBuffer, 1, totalBytes, spikeFileArray[electrodeIndex]);
 
-	int16 recordingNumber = m_recordingNum;
-	fwrite(&recordingNumber,                         // ptr
+	fwrite(&m_recordingNum,                         // ptr
 		2,                               // size of each element
 		1,                               // count
 		spikeFileArray[electrodeIndex]); // ptr to FILE object
diff --git a/Source/Plugins/BinaryWriter/BinaryRecording.h b/Source/Plugins/BinaryWriter/BinaryRecording.h
index 30a9f942f..579485272 100644
--- a/Source/Plugins/BinaryWriter/BinaryRecording.h
+++ b/Source/Plugins/BinaryWriter/BinaryRecording.h
@@ -47,23 +47,24 @@ namespace BinaryRecordingEngine
 		void openFiles(File rootFolder, int experimentNumber, int recordingNumber) override;
 		void closeFiles() override;
 		void writeData(int writeChannel, int realChannel, const float* buffer, int size) override;
-		void writeEvent(int eventType, const MidiMessage& event, int64 timestamp) override;
+		void writeEvent(int eventType, const MidiMessage& event) override;
 		void resetChannels() override;
-		void addSpikeElectrode(int index, const SpikeRecordInfo* elec) override;
-		void writeSpike(int electrodeIndex, const SpikeObject& spike, int64 timestamp) override;
+		void addSpikeElectrode(int index, const SpikeChannel* elec) override;
+		void writeSpike(int electrodeIndex, const SpikeEvent* spike) override;
+		void writeTimestampSyncText(uint16 sourceID, uint16 sourceIdx, uint64 timestamp, String text);
 
 		static RecordEngineManager* getEngineManager();
 
 	private:
 
-		void openSpikeFile(String basepath, SpikeRecordInfo* elec, int recordingNumber);
-		String generateSpikeHeader(SpikeRecordInfo* elec);
+		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(const MidiMessage& event, int64 timestamp);
-		void writeMessage(const MidiMessage& event, int64 timestamp);
+		void writeTTLEvent(int eventIndex, const MidiMessage& event);
+		void writeMessage(String message, uint16 processorID, uint16 channel, uint64 timestamp);
 
 		HeapBlock<float> m_scaledBuffer;
 		HeapBlock<int16> m_intBuffer;
-- 
GitLab