Skip to content

Commit a600740

Browse files
committed
merge with upstream
2 parents 611cca0 + 19706f4 commit a600740

6 files changed

Lines changed: 254 additions & 14 deletions

File tree

Source/Plugins/BinaryWriter/BinaryRecording.cpp

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -346,9 +346,17 @@ void BinaryRecording::openFiles(File rootFolder, int experimentNumber, int recor
346346
jsonFile->setProperty("channels", jsonSpikeChannels.getReference(i));
347347
}
348348

349-
Array<NpyType> msgType;
350-
msgType.add(NpyType("sync_text", BaseType::CHAR, 256));
351-
m_syncTextFile = new NpyFile(basepath + "sync_text.npy", msgType);
349+
File syncFile = File(basepath + "sync_messages.txt");
350+
Result res = syncFile.create();
351+
if (res.failed())
352+
{
353+
std::cerr << "Error creating sync text file:" << res.getErrorMessage() << std::endl;
354+
}
355+
else
356+
{
357+
m_syncTextFile = syncFile.createOutputStream();
358+
}
359+
352360
m_recordingNum = recordingNumber;
353361

354362
DynamicObject::Ptr jsonSettingsFile = new DynamicObject();
@@ -504,8 +512,8 @@ void BinaryRecording::writeData(int writeChannel, int realChannel, const float*
504512
double multFactor = 1 / (float(0x7fff) * getDataChannel(realChannel)->getBitVolts());
505513
FloatVectorOperations::copyWithMultiply(m_scaledBuffer.getData(), buffer, multFactor, size);
506514
AudioDataConverters::convertFloatToInt16LE(m_scaledBuffer.getData(), m_intBuffer.getData(), size);
507-
508-
m_DataFiles[m_fileIndexes[writeChannel]]->writeChannel(getTimestamp(writeChannel)-m_startTS[writeChannel],m_channelIndexes[writeChannel],m_intBuffer.getData(),size);
515+
int fileIndex = m_fileIndexes[writeChannel];
516+
m_DataFiles[fileIndex]->writeChannel(getTimestamp(writeChannel) - m_startTS[writeChannel], m_channelIndexes[writeChannel], m_intBuffer.getData(), size);
509517

510518
if (m_channelIndexes[writeChannel] == 0)
511519
{
@@ -515,7 +523,8 @@ void BinaryRecording::writeData(int writeChannel, int realChannel, const float*
515523
{
516524
m_tsBuffer[i] = (baseTS + i);
517525
}
518-
m_dataTimestampFiles[m_fileIndexes[writeChannel]]->writeData(m_tsBuffer, size*sizeof(int64));
526+
m_dataTimestampFiles[fileIndex]->writeData(m_tsBuffer, size*sizeof(int64));
527+
m_dataTimestampFiles[fileIndex]->increaseRecordCount(size);
519528
}
520529
}
521530

@@ -576,12 +585,14 @@ void BinaryRecording::writeEvent(int eventIndex, const MidiMessage& event)
576585
chanFile->writeData(&chan, sizeof(uint16));
577586
}
578587
writeEventMetaData(ev.get(), rec->metaDataFile);
588+
increaseEventCounts(rec);
579589
}
580590

581591
void BinaryRecording::writeTimestampSyncText(uint16 sourceID, uint16 sourceIdx, int64 timestamp, float, String text)
582592
{
583-
text.paddedRight(' ', 256);
584-
m_syncTextFile->writeData(text.toUTF8(), 256);
593+
if (!m_syncTextFile)
594+
return;
595+
m_syncTextFile->writeText(text + "\n", false, false);
585596
}
586597

587598

@@ -593,7 +604,7 @@ void BinaryRecording::writeSpike(int electrodeIndex, const SpikeEvent* spike)
593604
uint16 spikeChannel = m_spikeChannelIndexes[electrodeIndex];
594605

595606
int totalSamples = channel->getTotalSamples() * channel->getNumChannels();
596-
607+
597608

598609
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.
599610
{
@@ -629,8 +640,16 @@ void BinaryRecording::writeSpike(int electrodeIndex, const SpikeEvent* spike)
629640

630641
uint16 sortedID = spike->getSortedID();
631642
sortedFile->writeData(&sortedID, sizeof(uint16));
643+
increaseEventCounts(rec);
644+
}
632645

633-
writeEventMetaData(spike, rec->metaDataFile);
646+
void BinaryRecording::increaseEventCounts(EventRecording* rec)
647+
{
648+
rec->mainFile->increaseRecordCount();
649+
rec->timestampFile->increaseRecordCount();
650+
if (rec->extraFile) rec->extraFile->increaseRecordCount();
651+
if (rec->channelFile) rec->channelFile->increaseRecordCount();
652+
if (rec->metaDataFile) rec->metaDataFile->increaseRecordCount();
634653
}
635654

636655
RecordEngineManager* BinaryRecording::getEngineManager()

Source/Plugins/BinaryWriter/BinaryRecording.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ namespace BinaryRecordingEngine
8484
NpyFile* createEventMetadataFile(const MetaDataEventObject* channel, String fileName, DynamicObject* jsonObject);
8585
void createChannelMetaData(const MetaDataInfoObject* channel, DynamicObject* jsonObject);
8686
void writeEventMetaData(const MetaDataEvent* event, NpyFile* file);
87+
void increaseEventCounts(EventRecording* rec);
8788
static String jsonTypeValue(BaseType type);
8889

8990
SpikeMode m_spikeMode;
@@ -102,7 +103,7 @@ namespace BinaryRecordingEngine
102103
OwnedArray<EventRecording> m_eventFiles;
103104
OwnedArray<EventRecording> m_spikeFiles;
104105
OwnedArray<NpyFile> m_dataTimestampFiles;
105-
ScopedPointer<NpyFile> m_syncTextFile;
106+
ScopedPointer<FileOutputStream> m_syncTextFile;
106107

107108
Array<unsigned int> m_spikeFileIndexes;
108109
Array<uint16> m_spikeChannelIndexes;
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
------------------------------------------------------------------
3+
4+
This file is part of the Open Ephys GUI
5+
Copyright (C) 2017 Open Ephys
6+
7+
------------------------------------------------------------------
8+
9+
This program is free software: you can redistribute it and/or modify
10+
it under the terms of the GNU General Public License as published by
11+
the Free Software Foundation, either version 3 of the License, or
12+
(at your option) any later version.
13+
14+
This program is distributed in the hope that it will be useful,
15+
but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
GNU General Public License for more details.
18+
19+
You should have received a copy of the GNU General Public License
20+
along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
22+
*/
23+
24+
#include "NpyFile.h"
25+
26+
using namespace BinaryRecordingEngine;
27+
28+
NpyFile::NpyFile(String path, const Array<NpyType>& typeList)
29+
{
30+
File file(path);
31+
Result res = file.create();
32+
if (res.failed())
33+
{
34+
std::cerr << "Error creating file " << path << ":" << res.getErrorMessage() << std::endl;
35+
return;
36+
}
37+
m_file = file.createOutputStream();
38+
if (!m_file)
39+
return;
40+
41+
m_okOpen = true;
42+
43+
String header = "{'descr': [";
44+
45+
int nTypes = typeList.size();
46+
String name;
47+
48+
for (int i = 0; i < nTypes; i++)
49+
{
50+
NpyType& type = typeList.getReference(i);
51+
if (i > 0) header += ", ";
52+
header += "('" + type.getName() + "', '" + type.getTypeString() + "', (" + String(type.getTypeLength()) + ",))";
53+
}
54+
header += "], 'fortran_order': False, 'shape': (1,), }";
55+
56+
int headLength = header.length();
57+
int padding = (int((headLength + 30 ) / 16) + 1) * 16;
58+
header = header.paddedRight(' ', padding);
59+
header += '\n';
60+
61+
uint8 magicNum = 0x093;
62+
m_file->write(&magicNum, sizeof(uint8));
63+
String magic = "NUMPY";
64+
uint16 len = header.length();
65+
m_file->write(magic.toUTF8(), magic.getNumBytesAsUTF8());
66+
uint16 ver = 0x0001;
67+
m_file->write(&ver, sizeof(uint16));
68+
m_file->write(&len, sizeof(uint16));
69+
m_file->write(header.toUTF8(), len);
70+
m_countPos = headLength + 4; //10-6
71+
72+
}
73+
74+
NpyFile::~NpyFile()
75+
{
76+
if (m_file->setPosition(m_countPos))
77+
{
78+
String newShape = String(m_recordCount) + ",), }";
79+
m_file->write(newShape.toUTF8(), newShape.getNumBytesAsUTF8());
80+
}
81+
else
82+
{
83+
std::cerr << "Error. Unable to seek to update header on file " << m_file->getFile().getFullPathName() << std::endl;
84+
}
85+
}
86+
87+
void NpyFile::writeData(const void* data, size_t size)
88+
{
89+
m_file->write(data, size);
90+
}
91+
92+
void NpyFile::increaseRecordCount(int count)
93+
{
94+
m_recordCount += count;
95+
}
96+
97+
98+
NpyType::NpyType(String n, BaseType t, size_t l)
99+
: name(n), type(t), length(l)
100+
{
101+
}
102+
103+
String NpyType::getTypeString() const
104+
{
105+
switch (type)
106+
{
107+
case BaseType::CHAR:
108+
return "S" + String(length + 1); //account for the null separator
109+
case BaseType::INT8:
110+
return "<i1";
111+
case BaseType::UINT8:
112+
return "<u1";
113+
case BaseType::INT16:
114+
return "<i2";
115+
case BaseType::UINT16:
116+
return "<u2";
117+
case BaseType::INT32:
118+
return "<i4";
119+
case BaseType::UINT32:
120+
return "<u4";
121+
case BaseType::INT64:
122+
return "<i8";
123+
case BaseType::UINT64:
124+
return "<u8";
125+
case BaseType::FLOAT:
126+
return "<f4";
127+
case BaseType::DOUBLE:
128+
return "<f8";
129+
default:
130+
return "<b1";
131+
}
132+
}
133+
134+
int NpyType::getTypeLength() const
135+
{
136+
if (type == BaseType::CHAR)
137+
return 1;
138+
else
139+
return length;
140+
}
141+
142+
String NpyType::getName() const
143+
{
144+
return name;
145+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
------------------------------------------------------------------
3+
4+
This file is part of the Open Ephys GUI
5+
Copyright (C) 2017 Open Ephys
6+
7+
------------------------------------------------------------------
8+
9+
This program is free software: you can redistribute it and/or modify
10+
it under the terms of the GNU General Public License as published by
11+
the Free Software Foundation, either version 3 of the License, or
12+
(at your option) any later version.
13+
14+
This program is distributed in the hope that it will be useful,
15+
but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
GNU General Public License for more details.
18+
19+
You should have received a copy of the GNU General Public License
20+
along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
22+
*/
23+
24+
#ifndef NPYFILE_H
25+
#define NPYFILE_H
26+
27+
#include <RecordingLib.h>
28+
29+
namespace BinaryRecordingEngine
30+
{
31+
32+
class NpyType
33+
{
34+
public:
35+
NpyType(String, BaseType, size_t);
36+
String getName() const;
37+
String getTypeString() const;
38+
int getTypeLength() const;
39+
private:
40+
String name;
41+
BaseType type;
42+
size_t length;
43+
};
44+
45+
class NpyFile
46+
{
47+
public:
48+
NpyFile(String path, const Array<NpyType>& typeList);
49+
~NpyFile();
50+
void writeData(const void* data, size_t size);
51+
void increaseRecordCount(int count = 1);
52+
private:
53+
ScopedPointer<FileOutputStream> m_file;
54+
bool m_okOpen{ false };
55+
int64 m_recordCount{ 0 };
56+
size_t m_countPos;
57+
};
58+
59+
};
60+
#endif

Source/Processors/Channel/MetaData.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,13 @@ MetaDataDescriptor& MetaDataDescriptor::operator=(const MetaDataDescriptor& othe
7171

7272
MetaDataDescriptor::MetaDataTypes MetaDataDescriptor::getType() const { return m_type; }
7373
unsigned int MetaDataDescriptor::getLength() const { return m_length; }
74-
size_t MetaDataDescriptor::getDataSize() const { return m_length*getTypeSize(m_type); }
74+
size_t MetaDataDescriptor::getDataSize() const
75+
{
76+
if (m_type == CHAR)
77+
return m_length*getTypeSize(m_type) + 1; //account for the null-rerminator
78+
else
79+
return m_length*getTypeSize(m_type);
80+
}
7581
String MetaDataDescriptor::getName() const { return m_name; }
7682
String MetaDataDescriptor::getDescription() const { return m_description; }
7783
String MetaDataDescriptor::getIdentifier() const { return m_identifier; }
@@ -120,14 +126,14 @@ size_t MetaDataDescriptor::getTypeSize(MetaDataDescriptor::MetaDataTypes type)
120126

121127
//This would be so much easier if VS2012 supported delegating constructors...
122128
MetaDataValue::MetaDataValue(MetaDataDescriptor::MetaDataTypes t, unsigned int length, const void* d)
123-
: m_type(t), m_length(length), m_size(length*MetaDataDescriptor::getTypeSize(t))
129+
: m_type(t), m_length(length), m_size(getSize(t, length))
124130
{
125131
allocSpace();
126132
setValue(d);
127133
}
128134

129135
MetaDataValue::MetaDataValue(MetaDataDescriptor::MetaDataTypes t, unsigned int length)
130-
: m_type(t), m_length(length), m_size(length*MetaDataDescriptor::getTypeSize(t))
136+
: m_type(t), m_length(length), m_size(getSize(t, length))
131137
{
132138
allocSpace();
133139
}
@@ -175,6 +181,14 @@ void MetaDataValue::allocSpace()
175181
m_data.calloc(m_size);
176182
}
177183

184+
size_t MetaDataValue::getSize(MetaDataDescriptor::MetaDataTypes type, unsigned int length)
185+
{
186+
if (type == MetaDataDescriptor::CHAR)
187+
return length*MetaDataDescriptor::getTypeSize(type) + 1; //account for the null-rerminator
188+
else
189+
return length*MetaDataDescriptor::getTypeSize(type);
190+
}
191+
178192
MetaDataValue::MetaDataValue(const MetaDataValue& v)
179193
: ReferenceCountedObject(),
180194
m_type(v.m_type), m_length(v.m_length), m_size(v.m_size)

Source/Processors/Channel/MetaData.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ class PLUGIN_API MetaDataValue
162162
MetaDataValue() = delete;
163163
void setValue(const void* data);
164164
void allocSpace();
165+
static size_t getSize(MetaDataDescriptor::MetaDataTypes type, unsigned int length);
165166
HeapBlock<char> m_data;
166167
MetaDataDescriptor::MetaDataTypes m_type;
167168
unsigned int m_length;

0 commit comments

Comments
 (0)