@@ -44,6 +44,21 @@ String BinaryRecording::getEngineID() const
4444 return " RAWBINARY" ;
4545}
4646
47+ String BinaryRecording::getProcessorString (const InfoObjectCommon* channelInfo)
48+ {
49+ String fName = (channelInfo->getCurrentNodeName ().replaceCharacter (' ' , ' _' ) + " -" + String (channelInfo->getCurrentNodeID ()));
50+ if (channelInfo->getCurrentNodeID () == channelInfo->getSourceNodeID ()) // it is the channel source
51+ {
52+ fName += " ." + String (channelInfo->getSubProcessorIdx ());
53+ }
54+ else
55+ {
56+ fName += " _" + String (channelInfo->getSourceNodeID ()) + " ." + String (channelInfo->getSubProcessorIdx ());
57+ }
58+ fName += File::separatorString;
59+ return fName ;
60+ }
61+
4762void BinaryRecording::openFiles (File rootFolder, int experimentNumber, int recordingNumber)
4863{
4964 String basepath = rootFolder.getFullPathName () + rootFolder.separatorString + " experiment" + String (experimentNumber)
@@ -76,7 +91,7 @@ void BinaryRecording::openFiles(File rootFolder, int experimentNumber, int recor
7691 int nInfoArrays = indexedDataChannels.size ();
7792 bool found = false ;
7893 DynamicObject::Ptr jsonChan = new DynamicObject ();
79- jsonChan->setProperty (" name " , channelInfo->getName ());
94+ jsonChan->setProperty (" channel_name " , channelInfo->getName ());
8095 jsonChan->setProperty (" description" , channelInfo->getDescription ());
8196 jsonChan->setProperty (" identifier" , channelInfo->getIdentifier ());
8297 jsonChan->setProperty (" history" , channelInfo->getHistoricString ());
@@ -100,13 +115,13 @@ void BinaryRecording::openFiles(File rootFolder, int experimentNumber, int recor
100115 }
101116 if (!found)
102117 {
103- String datFileName (channelInfo-> getCurrentNodeName () + " _( " + String ( channelInfo-> getCurrentNodeID ()) + " ) " + File::separatorString + channelInfo-> getSourceName () + " _( " + String (sourceId) + " . " + String (sourceSubIdx) + " ) " );
104- continuousFileNames.add (contPath + datFileName + " .dat" );
118+ String datPath = getProcessorString ( channelInfo);
119+ continuousFileNames.add (contPath + datPath + " continuous .dat" );
105120
106121 Array<NpyType> tstypes;
107122 tstypes.add (NpyType (" Timestamp" , BaseType::INT64, 1 ));
108123
109- ScopedPointer<NpyFile> tFile = new NpyFile (contPath + datFileName + " _timestamps .npy" , tstypes);
124+ ScopedPointer<NpyFile> tFile = new NpyFile (contPath + datPath + " timestamps .npy" , tstypes);
110125 m_dataTimestampFiles.add (tFile.release ());
111126
112127 m_fileIndexes.set (recordedChan, nInfoArrays);
@@ -118,7 +133,7 @@ void BinaryRecording::openFiles(File rootFolder, int experimentNumber, int recor
118133 jsonChanArray.add (var (jsonChan));
119134 jsonChannels.add (var (jsonChanArray));
120135 DynamicObject::Ptr jsonFile = new DynamicObject ();
121- jsonFile->setProperty (" name " , datFileName);
136+ jsonFile->setProperty (" folder_name " , datPath. replace (File::separatorString, " / " )); // to make it more system agnostic, replace separator with only one slash
122137 jsonFile->setProperty (" sample_rate" , channelInfo->getSampleRate ());
123138 jsonFile->setProperty (" source_processor_name" , channelInfo->getSourceName ());
124139 jsonFile->setProperty (" source_processor_id" , channelInfo->getSourceNodeID ());
@@ -154,81 +169,56 @@ void BinaryRecording::openFiles(File rootFolder, int experimentNumber, int recor
154169
155170 int nEvents = getNumRecordedEvents ();
156171 String eventPath (basepath + " events" + File::separatorString);
157- int binCount = 0 , ttlCount = 0 , textCount = 0 ;
158172 Array<var> jsonEventFiles;
159173
160174 for (int ev = 0 ; ev < nEvents; ev++)
161175 {
162176 const EventChannel* chan = getEventChannel (ev);
163- String eventName;
164- Array< NpyType> types ;
165- String typeName ;
177+ String eventName = getProcessorString (chan) ;
178+ NpyType type ;
179+ String dataFileName ;
166180
167181 switch (chan->getChannelType ())
168182 {
169183 case EventChannel::TEXT:
170- textCount++;
171- eventName += " TEXT" + String (textCount);
172- types.add (NpyType (" message" , BaseType::CHAR, chan->getLength ()));
173- typeName = " text_message" ;
184+ eventName += " TEXT_group" ;
185+ type = NpyType (BaseType::CHAR, chan->getLength ());
186+ dataFileName = " text" ;
174187 break ;
175188 case EventChannel::TTL:
176- ttlCount++;
177- eventName += " TTL" + String (ttlCount);
178- types.add (NpyType (" TTL_Channel" , BaseType::INT16, 1 ));
179- typeName = " ttl" ;
189+ eventName += " TTL" ;
190+ type = NpyType (BaseType::INT16, 1 );
191+ dataFileName = " channel_states" ;
180192 break ;
181193 default :
182- binCount++;
183- eventName += " BIN" + String (ttlCount);
184- types.add (NpyType (" Data" , chan->getEquivalentMetaDataType (), chan->getLength ()));
185- typeName = jsonTypeValue (chan->getEquivalentMetaDataType ());
194+ eventName += " BINARY_group" ;
195+ type = NpyType (chan->getEquivalentMetaDataType (), chan->getLength ());
196+ dataFileName = " data_array" ;
186197 break ;
187198 }
188- eventName += " _" + chan->getSourceName () + " (" + String (chan->getSourceNodeID ()) + " ." + String (chan->getSubProcessorIdx ()) + " )" ;
189- String fName = eventPath + eventName;
199+ eventName += " _" + String (chan->getSourceIndex () + 1 ) + File::separatorString;
190200 ScopedPointer<EventRecording> rec = new EventRecording ();
191- Array<NpyType> tsType;
192- tsType.add (NpyType (" Timestamp" , BaseType::INT64, 1 ));
193- // TTL channels behave a bit different
194- if (chan->getChannelType () == EventChannel::TTL)
195- {
196- if (m_TTLMode == TTLMode::JointWord)
197- {
198- types.add (NpyType (" TTL_Word" , BaseType::UINT8, chan->getDataSize ()));
199- }
200- else if (m_TTLMode == TTLMode::SeparateWord)
201- {
202- Array<NpyType> wordType;
203- wordType.add (NpyType (" TTL_Word" , BaseType::UINT8, chan->getDataSize ()));
204- rec->extraFile = new NpyFile (fName + " _TTLWord.npy" , wordType);
205- }
206- // since the main TTL file already contins channel numbers, it would be redundant to store them on the timestamp file
207- }
208- else
201+
202+ rec->mainFile = new NpyFile (eventPath + eventName + dataFileName + " .npy" , type);
203+ rec->timestampFile = new NpyFile (eventPath + eventName + " timestamps.npy" , NpyType (BaseType::INT64, 1 ));
204+ rec->channelFile = new NpyFile (eventPath + eventName + " channels.npy" , NpyType (BaseType::UINT16, 1 ));
205+ if (chan->getChannelType () == EventChannel::TTL && m_saveTTLWords)
209206 {
210- if (m_eventMode == EventMode::SeparateChannel)
211- {
212- Array<NpyType> chanType;
213- chanType.add (NpyType (" Channel" , BaseType::UINT16, 1 ));
214- rec->channelFile = new NpyFile (fName + " _channel.npy" , chanType);
215- }
216- else
217- tsType.add (NpyType (" Channel" , BaseType::UINT16, 1 ));
207+ rec->extraFile = new NpyFile (eventPath + eventName + " full_words.npy" , NpyType (BaseType::UINT8, chan->getDataSize ()));
218208 }
219- rec->mainFile = new NpyFile (fName + " .npy" , types);
220- rec->timestampFile = new NpyFile (fName + " _timestamps.npy" , tsType);
209+
221210 DynamicObject::Ptr jsonChannel = new DynamicObject ();
222- jsonChannel->setProperty (" name" , chan->getName ());
211+ jsonChannel->setProperty (" folder_name" , eventName.replace (File::separatorString, " /" ));
212+ jsonChannel->setProperty (" channel_name" , chan->getName ());
223213 jsonChannel->setProperty (" description" , chan->getDescription ());
224214 jsonChannel->setProperty (" identifier" , chan->getIdentifier ());
225215 jsonChannel->setProperty (" sample_rate" , chan->getSampleRate ());
226- jsonChannel->setProperty (" type" , typeName );
216+ jsonChannel->setProperty (" type" , jsonTypeValue (type. getType ()) );
227217 jsonChannel->setProperty (" num_channels" , (int )chan->getNumChannels ());
228218 jsonChannel->setProperty (" source_processor" , chan->getSourceName ());
229219 createChannelMetaData (chan, jsonChannel);
230220
231- rec->metaDataFile = createEventMetadataFile (chan, fName + " _metadata .npy" , jsonChannel);
221+ rec->metaDataFile = createEventMetadataFile (chan, eventPath + eventName + " metadata .npy" , jsonChannel);
232222 m_eventFiles.add (rec.release ());
233223 jsonEventFiles.add (var (jsonChannel));
234224 }
@@ -241,12 +231,13 @@ void BinaryRecording::openFiles(File rootFolder, int experimentNumber, int recor
241231 String spikePath (basepath + " spikes" + File::separatorString);
242232 Array<var> jsonSpikeFiles;
243233 Array<var> jsonSpikeChannels;
234+ std::map<uint32, int > groupMap;
244235 for (int sp = 0 ; sp < nSpikes; sp++)
245236 {
246237 const SpikeChannel* ch = getSpikeChannel (sp);
247238 DynamicObject::Ptr jsonChannel = new DynamicObject ();
248239 unsigned int numSpikeChannels = ch->getNumChannels ();
249- jsonChannel->setProperty (" name " , ch->getName ());
240+ jsonChannel->setProperty (" channel_name " , ch->getName ());
250241 jsonChannel->setProperty (" description" , ch->getDescription ());
251242 jsonChannel->setProperty (" identifier" , ch->getIdentifier ());
252243 Array<var> jsonChannelInfo;
@@ -288,51 +279,31 @@ void BinaryRecording::openFiles(File rootFolder, int experimentNumber, int recor
288279 m_spikeChannelIndexes.set (sp, 0 );
289280 indexedChannels.add (1 );
290281 ScopedPointer<EventRecording> rec = new EventRecording ();
291- Array<NpyType> spTypes;
292- for (int c = 0 ; c < ch->getNumChannels (); c++)
293- {
294- spTypes.add (NpyType (" channel" + String (c + 1 ), BaseType::INT16, ch->getTotalSamples ()));
295- }
296- String spikeName (" spike_group_" + String (fileIndex + 1 ));
297- String fName (spikePath + spikeName);
298- rec->mainFile = new NpyFile (fName + " .npy" , spTypes);
282+
283+ uint32 procID = GenericProcessor::getProcessorFullId (ch->getSourceNodeID (), ch->getSubProcessorIdx ());
284+ int groupIndex = ++groupMap[procID];
285+
286+ String spikeName = getProcessorString (ch) + " spike_group_" + String (groupIndex) + File::separatorString;
287+
288+ rec->mainFile = new NpyFile (spikePath + spikeName + " spike_waveforms.npy" , NpyType (BaseType::INT16, ch->getTotalSamples ()), ch->getNumChannels ());
289+ rec->timestampFile = new NpyFile (spikePath + spikeName + " spike_times.npy" , NpyType (BaseType::INT64, 1 ));
290+ rec->channelFile = new NpyFile (spikePath + spikeName + " spike_electrode_indices.npy" , NpyType (BaseType::UINT16, 1 ));
291+ rec->extraFile = new NpyFile (spikePath + spikeName + " spike_clusters.npy" , NpyType (BaseType::UINT16, 1 ));
299292 Array<NpyType> tsTypes;
300- tsTypes.add (NpyType (" timestamp" , BaseType::INT64, 1 ));
301- if (m_spikeMode == SpikeMode::AllInOne)
302- {
303- tsTypes.add (NpyType (" electrode_index" , BaseType::UINT16, 1 ));
304- tsTypes.add (NpyType (" sorted_id" , BaseType::UINT16, 1 ));
305- }
306- else
307- {
308- Array<NpyType> indexType;
309- indexType.add (NpyType (" electrode_index" , BaseType::UINT16, 1 ));
310- if (m_spikeMode == SpikeMode::AllSeparated)
311- {
312- Array<NpyType> sortedType;
313- sortedType.add (NpyType (" sorted_id" , BaseType::UINT16, 1 ));
314- rec->extraFile = new NpyFile (fName + " _sortedID.npy" , sortedType);
315- }
316- else
317- {
318- indexType.add (NpyType (" sorted_id" , BaseType::UINT16, 1 ));
319- }
320- rec->channelFile = new NpyFile (fName + " indexes.npy" , indexType);
321- }
322- rec->timestampFile = new NpyFile (fName + " _timestamps.npy" , tsTypes);
293+
323294 Array<var> jsonChanArray;
324295 jsonChanArray.add (var (jsonChannel));
325296 jsonSpikeChannels.add (var (jsonChanArray));
326297 DynamicObject::Ptr jsonFile = new DynamicObject ();
327298
328- jsonFile->setProperty (" name " , spikeName);
299+ jsonFile->setProperty (" folder_name " , spikeName. replace (File::separatorString, " / " ) );
329300 jsonFile->setProperty (" sample_rate" , ch->getSampleRate ());
330301 jsonFile->setProperty (" source_processor" , ch->getSourceName ());
331302 jsonFile->setProperty (" num_channels" , (int )numSpikeChannels);
332303 jsonFile->setProperty (" pre_peak_samples" , (int )ch->getPrePeakSamples ());
333304 jsonFile->setProperty (" post_peak_samples" , (int )ch->getPostPeakSamples ());
334305
335- rec->metaDataFile = createEventMetadataFile (ch, fName + " _metadata .npy" , jsonFile);
306+ rec->metaDataFile = createEventMetadataFile (ch, spikePath + spikeName + " metadata .npy" , jsonFile);
336307 m_spikeFiles.add (rec.release ());
337308 jsonSpikeFiles.add (var (jsonFile));
338309 }
@@ -552,38 +523,23 @@ void BinaryRecording::writeEvent(int eventIndex, const MidiMessage& event)
552523 const EventChannel* info = getEventChannel (eventIndex);
553524 int64 ts = ev->getTimestamp ();
554525 rec->timestampFile ->writeData (&ts, sizeof (int64));
526+
527+ uint16 chan = ev->getChannel ();
528+ rec->channelFile ->writeData (&chan, sizeof (uint16));
529+
555530 if (ev->getEventType () == EventChannel::TTL)
556531 {
557532 TTLEvent* ttl = static_cast <TTLEvent*>(ev.get ());
558533 int16 data = ttl->getChannel () * (ttl->getState () ? 1 : -1 );
559534 rec->mainFile ->writeData (&data, sizeof (int16));
560- NpyFile* wordFile = nullptr ;
561- if (m_TTLMode == TTLMode::JointWord)
562- {
563- wordFile = rec->mainFile ;
564- }
565- else if (m_TTLMode == TTLMode::SeparateWord)
566- {
567- wordFile = rec->extraFile ;
568- }
569- if (wordFile)
570- wordFile->writeData (ttl->getTTLWordPointer (), info->getDataSize ());
535+ if (rec->extraFile )
536+ rec->extraFile ->writeData (ttl->getTTLWordPointer (), info->getDataSize ());
571537 }
572538 else
573539 {
574540 rec->mainFile ->writeData (ev->getRawDataPointer (), info->getDataSize ());
575- NpyFile* chanFile = nullptr ;
576- if (m_eventMode == EventMode::SeparateChannel)
577- {
578- chanFile = rec->channelFile ;
579- }
580- else
581- {
582- chanFile = rec->timestampFile ;
583- }
584- uint16 chan = ev->getChannel ();
585- chanFile->writeData (&chan, sizeof (uint16));
586541 }
542+
587543 writeEventMetaData (ev.get (), rec->metaDataFile );
588544 increaseEventCounts (rec);
589545}
@@ -617,29 +573,15 @@ void BinaryRecording::writeSpike(int electrodeIndex, const SpikeEvent* spike)
617573 FloatVectorOperations::copyWithMultiply (m_scaledBuffer.getData (), spike->getDataPointer (), multFactor, totalSamples);
618574 AudioDataConverters::convertFloatToInt16LE (m_scaledBuffer.getData (), m_intBuffer.getData (), totalSamples);
619575 rec->mainFile ->writeData (m_intBuffer.getData (), totalSamples*sizeof (int16));
576+
620577 int64 ts = spike->getTimestamp ();
621578 rec->timestampFile ->writeData (&ts, sizeof (int64));
622- NpyFile* indexFile;
623- NpyFile* sortedFile;
624- if (m_spikeMode == SpikeMode::AllInOne)
625- {
626- indexFile = rec->timestampFile ;
627- sortedFile = rec->timestampFile ;
628- }
629- else if (m_spikeMode == SpikeMode::SeparateTimestamps)
630- {
631- indexFile = rec->channelFile ;
632- sortedFile = rec->channelFile ;
633- }
634- else
635- {
636- indexFile = rec->channelFile ;
637- sortedFile = rec->extraFile ;
638- }
639- indexFile->writeData (&spikeChannel, sizeof (uint16));
579+
580+ rec->channelFile ->writeData (&spikeChannel, sizeof (uint16));
640581
641582 uint16 sortedID = spike->getSortedID ();
642- sortedFile->writeData (&sortedID, sizeof (uint16));
583+ rec->extraFile ->writeData (&sortedID, sizeof (uint16));
584+
643585 increaseEventCounts (rec);
644586}
645587
@@ -656,20 +598,15 @@ RecordEngineManager* BinaryRecording::getEngineManager()
656598{
657599 RecordEngineManager* man = new RecordEngineManager (" RAWBINARY" , " Binary" , &(engineFactory<BinaryRecording>));
658600 EngineParameter* param;
659- param = new EngineParameter (EngineParameter::MULTI, 0 , " Spike TS/chan/sortedID File Mode|All in one|Separate timestamps|All Separated" , 0 );
660- man->addParameter (param);
661- param = new EngineParameter (EngineParameter::MULTI, 1 , " TTL Event word file|In main file|Separated|Do not save ttl word" , 0 );
662- man->addParameter (param);
663- param = new EngineParameter (EngineParameter::MULTI, 2 , " Other event channel file|With timestamp|Separate" , 0 );
601+ param = new EngineParameter (EngineParameter::BOOL, 0 , " Record TTL full words" , true );
664602 man->addParameter (param);
603+
665604 return man;
666605}
667606
668607void BinaryRecording::setParameter (EngineParameter& parameter)
669608{
670- multiParameter (0 , reinterpret_cast <int &>(m_spikeMode));
671- multiParameter (1 , reinterpret_cast <int &>(m_TTLMode));
672- multiParameter (2 , reinterpret_cast <int &>(m_eventMode));
609+ boolParameter (0 , m_saveTTLWords);
673610}
674611
675612String BinaryRecording::jsonTypeValue (BaseType type)
0 commit comments