Skip to content

Commit ca28420

Browse files
committed
Merge pull request #26 from aacuevas/KwikChanges
Kwik changes and improvements
2 parents c10ec93 + 380f3f3 commit ca28420

15 files changed

Lines changed: 175 additions & 73 deletions

File tree

Builds/VisualStudio2013/open-ephys.sln

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
1-
Microsoft Visual Studio Solution File, Format Version 11.00
1+
Microsoft Visual Studio Solution File, Format Version 12.00
22
# Visual Studio 2013
3-
Project("{5A05F353-1D63-394C-DFB0-981BB2309002}") = "open-ephys", "open-ephys.vcxproj", "{9C924D66-7DEC-1AEF-B375-DB8666BFB909}"
3+
VisualStudioVersion = 12.0.40629.0
4+
MinimumVisualStudioVersion = 10.0.40219.1
5+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "open-ephys", "open-ephys.vcxproj", "{9C924D66-7DEC-1AEF-B375-DB8666BFB909}"
46
EndProject
57
Global
68
GlobalSection(SolutionConfigurationPlatforms) = preSolution
79
Debug|Win32 = Debug|Win32
8-
Release|Win32 = Release|Win32
10+
Debug|x64 = Debug|x64
11+
Debug64|Win32 = Debug64|Win32
912
Debug64|x64 = Debug64|x64
13+
Release|Win32 = Release|Win32
14+
Release|x64 = Release|x64
15+
Release64|Win32 = Release64|Win32
1016
Release64|x64 = Release64|x64
1117
EndGlobalSection
1218
GlobalSection(ProjectConfigurationPlatforms) = postSolution
1319
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Debug|Win32.ActiveCfg = Debug|Win32
1420
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Debug|Win32.Build.0 = Debug|Win32
15-
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Release|Win32.ActiveCfg = Release|Win32
16-
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Release|Win32.Build.0 = Release|Win32
21+
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Debug|x64.ActiveCfg = Debug|Win32
22+
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Debug64|Win32.ActiveCfg = Debug64|x64
1723
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Debug64|x64.ActiveCfg = Debug64|x64
1824
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Debug64|x64.Build.0 = Debug64|x64
25+
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Release|Win32.ActiveCfg = Release|Win32
26+
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Release|Win32.Build.0 = Release|Win32
27+
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Release|x64.ActiveCfg = Release|Win32
28+
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Release64|Win32.ActiveCfg = Release64|x64
1929
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Release64|x64.ActiveCfg = Release64|x64
2030
{9C924D66-7DEC-1AEF-B375-DB8666BFB909}.Release64|x64.Build.0 = Release64|x64
2131
EndGlobalSection

Source/Plugins/KWIKFormat/RecordEngine/HDF5FileFormat.cpp

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
#define SPIKE_CHUNK_YSIZE 40
4141
#endif
4242

43+
#ifndef TIMESTAMP_CHUNK_SIZE
44+
#define TIMESTAMP_CHUNK_SIZE 16
45+
#endif
46+
4347
#define MAX_TRANSFORM_SIZE 512
4448

4549
#define MAX_STR_SIZE 256
@@ -97,7 +101,7 @@ int HDF5FileBase::open(bool newfile, int nChans)
97101
FileAccPropList props = FileAccPropList::DEFAULT;
98102
if (nChans > 0)
99103
{
100-
props.setCache(0, 809, 8 * 2 * CHUNK_XSIZE * nChans, 1);
104+
props.setCache(0, 1667, 2 * 8 * 2 * CHUNK_XSIZE * nChans, 1);
101105
//std::cout << "opening HDF5 " << getFileName() << " with nchans: " << nChans << std::endl;
102106
}
103107

@@ -498,7 +502,7 @@ HDF5RecordingData::HDF5RecordingData(DataSet* data)
498502
this->size[2] = 1;
499503

500504
this->xChunkSize = chunk[0];
501-
this->xPos = dims[0];
505+
this->xPos = 0;
502506
this->dSet = dataSet;
503507
this->rowXPos.clear();
504508
this->rowXPos.insertMultiple(0,0,this->size[1]);
@@ -663,6 +667,9 @@ void KWDFile::startNewRecording(int recordingNumber, int nChannels, HDF5Recordin
663667
this->multiSample = info->multiSample;
664668
uint8 mSample = info->multiSample ? 1 : 0;
665669

670+
ScopedPointer<HDF5RecordingData> bitVoltsSet;
671+
ScopedPointer<HDF5RecordingData> sampleRateSet;
672+
666673
String recordPath = String("/recordings/")+String(recordingNumber);
667674
CHECK_ERROR(createGroup(recordPath));
668675
CHECK_ERROR(setAttributeStr(info->name,recordPath,String("name")));
@@ -671,12 +678,29 @@ void KWDFile::startNewRecording(int recordingNumber, int nChannels, HDF5Recordin
671678
CHECK_ERROR(setAttribute(F32,&(info->sample_rate),recordPath,String("sample_rate")));
672679
CHECK_ERROR(setAttribute(U32,&(info->bit_depth),recordPath,String("bit_depth")));
673680
CHECK_ERROR(createGroup(recordPath+"/application_data"));
674-
CHECK_ERROR(setAttributeArray(F32,info->bitVolts.getRawDataPointer(),info->bitVolts.size(),recordPath+"/application_data",String("channel_bit_volts")));
681+
// CHECK_ERROR(setAttributeArray(F32,info->bitVolts.getRawDataPointer(),info->bitVolts.size(),recordPath+"/application_data",String("channel_bit_volts")));
682+
bitVoltsSet = createDataSet(F32, info->bitVolts.size(), 0, recordPath + "/application_data/channel_bit_volts");
683+
if (bitVoltsSet.get())
684+
bitVoltsSet->writeDataBlock(info->bitVolts.size(), F32, info->bitVolts.getRawDataPointer());
685+
else
686+
std::cerr << "Error creating bitvolts data set" << std::endl;
687+
675688
CHECK_ERROR(setAttribute(U8,&mSample,recordPath+"/application_data",String("is_multiSampleRate_data")));
676-
CHECK_ERROR(setAttributeArray(F32,info->channelSampleRates.getRawDataPointer(),info->channelSampleRates.size(),recordPath+"/application_data",String("channel_sample_rates")));
689+
//CHECK_ERROR(setAttributeArray(F32,info->channelSampleRates.getRawDataPointer(),info->channelSampleRates.size(),recordPath+"/application_data",String("channel_sample_rates")));
690+
sampleRateSet = createDataSet(F32, info->channelSampleRates.size(), 0, recordPath + "/application_data/channel_sample_rates");
691+
if (sampleRateSet.get())
692+
sampleRateSet->writeDataBlock(info->channelSampleRates.size(), F32, info->channelSampleRates.getRawDataPointer());
693+
else
694+
std::cerr << "Error creating sample rates data set" << std::endl;
695+
677696
recdata = createDataSet(I16,0,nChannels,CHUNK_XSIZE,recordPath+"/data");
678697
if (!recdata.get())
679698
std::cerr << "Error creating data set" << std::endl;
699+
700+
tsData = createDataSet(I64, 0, nChannels, TIMESTAMP_CHUNK_SIZE, recordPath + "/application_data/timestamps");
701+
if (!tsData.get())
702+
std::cerr << "Error creating timestamps data set" << std::endl;
703+
680704
curChan = nChannels;
681705
}
682706

@@ -689,6 +713,7 @@ void KWDFile::stopRecording()
689713
CHECK_ERROR(setAttributeArray(U32,samples.getRawDataPointer(),samples.size(),path,"valid_samples"));
690714
//ScopedPointer does the deletion and destructors the closings
691715
recdata = nullptr;
716+
tsData = nullptr;
692717
}
693718

694719
int KWDFile::createFileStructure()
@@ -723,6 +748,14 @@ void KWDFile::writeRowData(int16* data, int nSamples, int channel)
723748
}
724749
}
725750

751+
void KWDFile::writeTimestamps(int64* ts, int nTs, int channel)
752+
{
753+
if (channel >= 0 && channel < nChannels)
754+
{
755+
CHECK_ERROR(tsData->writeDataRow(channel, nTs, I64, ts));
756+
}
757+
}
758+
726759
//KWE File
727760

728761
KWEFile::KWEFile(String basename) : HDF5FileBase()
@@ -867,18 +900,17 @@ KWXFile::KWXFile(String basename) : HDF5FileBase()
867900
{
868901
initFile(basename);
869902
numElectrodes=0;
870-
transformVector = new int16[MAX_TRANSFORM_SIZE];
903+
transformVector.malloc(MAX_TRANSFORM_SIZE);
871904
}
872905

873906
KWXFile::KWXFile() : HDF5FileBase()
874907
{
875908
numElectrodes=0;
876-
transformVector = new int16[MAX_TRANSFORM_SIZE];
909+
transformVector.malloc(MAX_TRANSFORM_SIZE);
877910
}
878911

879912
KWXFile::~KWXFile()
880913
{
881-
delete transformVector;
882914
}
883915

884916
String KWXFile::getFileName()

Source/Plugins/KWIKFormat/RecordEngine/HDF5FileFormat.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ class KWDFile : public HDF5FileBase
128128
void writeBlockData(int16* data, int nSamples);
129129
void writeRowData(int16* data, int nSamples);
130130
void writeRowData(int16* data, int nSamples, int channel);
131+
void writeTimestamps(int64* ts, int nTs, int channel);
131132
String getFileName();
132133

133134
protected:
@@ -140,6 +141,7 @@ class KWDFile : public HDF5FileBase
140141
String filename;
141142
bool multiSample;
142143
ScopedPointer<HDF5RecordingData> recdata;
144+
ScopedPointer<HDF5RecordingData> tsData;
143145

144146
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(KWDFile);
145147
};
@@ -203,7 +205,8 @@ class KWXFile : public HDF5FileBase
203205
OwnedArray<HDF5RecordingData> timeStamps;
204206
Array<int> channelArray;
205207
int numElectrodes;
206-
int16* transformVector;
208+
HeapBlock<int16> transformVector;
209+
//int16* transformVector;
207210

208211
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(KWXFile);
209212
};

Source/Plugins/KWIKFormat/RecordEngine/HDF5Recording.cpp

Lines changed: 71 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,23 @@
2222
*/
2323

2424
#include "HDF5Recording.h"
25-
#define MAX_BUFFER_SIZE 10000
25+
#define MAX_BUFFER_SIZE 40960
26+
#define CHANNEL_TIMESTAMP_PREALLOC_SIZE 128
27+
#define CHANNEL_TIMESTAMP_MIN_WRITE 32
28+
#define TIMESTAMP_EACH_NSAMPLES 1024
2629

27-
HDF5Recording::HDF5Recording() : processorIndex(-1), hasAcquired(false)
30+
HDF5Recording::HDF5Recording() : processorIndex(-1), hasAcquired(false), bufferSize(MAX_BUFFER_SIZE)
2831
{
2932
//timestamp = 0;
30-
scaledBuffer = new float[MAX_BUFFER_SIZE];
31-
intBuffer = new int16[MAX_BUFFER_SIZE];
33+
scaledBuffer.malloc(MAX_BUFFER_SIZE);
34+
intBuffer.malloc(MAX_BUFFER_SIZE);
3235
}
3336

3437
HDF5Recording::~HDF5Recording()
35-
{
36-
delete scaledBuffer;
37-
delete intBuffer;
38+
{
3839
}
3940

40-
String HDF5Recording::getEngineID()
41+
String HDF5Recording::getEngineID() const
4142
{
4243
return "KWIK";
4344
}
@@ -65,6 +66,9 @@ void HDF5Recording::registerProcessor(const GenericProcessor* proc)
6566

6667
void HDF5Recording::resetChannels()
6768
{
69+
scaledBuffer.malloc(MAX_BUFFER_SIZE);
70+
intBuffer.malloc(MAX_BUFFER_SIZE);
71+
bufferSize = MAX_BUFFER_SIZE;
6872
processorIndex = -1;
6973
fileArray.clear();
7074
channelsPerProcessor.clear();
@@ -73,6 +77,8 @@ void HDF5Recording::resetChannels()
7377
processorMap.clear();
7478
infoArray.clear();
7579
recordedChanToKWDChan.clear();
80+
channelLeftOverSamples.clear();
81+
channelTimestampArray.clear();
7682
if (spikesFile)
7783
spikesFile->resetChannels();
7884
}
@@ -129,31 +135,11 @@ void HDF5Recording::openFiles(File rootFolder, int experimentNumber, int recordi
129135
int procPos = processorRecPos[index];
130136
recordedChanToKWDChan.add(procPos);
131137
processorRecPos.set(index, procPos+1);
138+
channelTimestampArray.add(new Array<int64>);
139+
channelTimestampArray.getLast()->ensureStorageAllocated(CHANNEL_TIMESTAMP_PREALLOC_SIZE);
140+
channelLeftOverSamples.add(0);
132141
}
133-
#if 0
134-
for (int i = 0; i < processorMap.size(); i++)
135-
{
136-
int index = processorMap[i];
137-
if (getChannel(i)->getRecordState())
138-
{
139-
if (!fileArray[index]->isOpen())
140-
{
141-
fileArray[index]->initFile(getChannel(i)->nodeId,basepath);
142-
if (hasAcquired)
143-
infoArray[index]->start_time = (*timestamps)[getChannel(i)->sourceNodeId]; //the timestamps of the first channel
144-
else
145-
infoArray[index]->start_time = 0;
146-
}
147-
channelsPerProcessor.set(index, channelsPerProcessor[index] + 1);
148-
bitVoltsArray[index]->add(getChannel(i)->bitVolts);
149-
sampleRatesArray[index]->add(getChannel(i)->sampleRate);
150-
if (getChannel(i)->sampleRate != infoArray[index]->sample_rate)
151-
{
152-
infoArray[index]->multiSample = true;
153-
}
154-
}
155-
}
156-
#endif
142+
157143
for (int i = 0; i < fileArray.size(); i++)
158144
{
159145
if ((!fileArray[i]->isOpen()) && (fileArray[i]->isReadyToOpen()))
@@ -189,25 +175,72 @@ void HDF5Recording::closeFiles()
189175
{
190176
if (fileArray[i]->isOpen())
191177
{
178+
std::cout << "Closed file " << i << std::endl;
192179
fileArray[i]->stopRecording();
193180
fileArray[i]->close();
194181
bitVoltsArray[i]->clear();
195182
sampleRatesArray[i]->clear();
196183
}
197184
channelsPerProcessor.set(i, 0);
198185
}
186+
recordedChanToKWDChan.clear();
187+
channelTimestampArray.clear();
188+
channelLeftOverSamples.clear();
189+
scaledBuffer.malloc(MAX_BUFFER_SIZE);
190+
intBuffer.malloc(MAX_BUFFER_SIZE);
191+
bufferSize = MAX_BUFFER_SIZE;
199192
}
200193

201194
void HDF5Recording::writeData(int writeChannel, int realChannel, const float* buffer, int size)
202195
{
203-
// int64 t1 = Time::getHighResolutionTicks();
196+
if (size > bufferSize) //Shouldn't happen, and if it happens it'll be slow, but better this than crashing. Will be reset on flie close and reset.
197+
{
198+
std::cerr << "Write buffer overrun, resizing to" << size << std::endl;
199+
bufferSize = size;
200+
scaledBuffer.malloc(size);
201+
intBuffer.malloc(size);
202+
}
204203
double multFactor = 1 / (float(0x7fff) * getChannel(realChannel)->bitVolts);
205204
int index = processorMap[getChannel(realChannel)->recordIndex];
206-
FloatVectorOperations::copyWithMultiply(scaledBuffer, buffer, multFactor, size);
207-
AudioDataConverters::convertFloatToInt16LE(scaledBuffer, intBuffer, size);
208-
fileArray[index]->writeRowData(intBuffer, size, recordedChanToKWDChan[writeChannel]);
209-
// int64 t2 = Time::getHighResolutionTicks();
210-
// std::cout << "record time: " << float(t2 - t1) / float(Time::getHighResolutionTicksPerSecond()) << std::endl;
205+
FloatVectorOperations::copyWithMultiply(scaledBuffer.getData(), buffer, multFactor, size);
206+
AudioDataConverters::convertFloatToInt16LE(scaledBuffer.getData(), intBuffer.getData(), size);
207+
fileArray[index]->writeRowData(intBuffer.getData(), size, recordedChanToKWDChan[writeChannel]);
208+
209+
int sampleOffset = channelLeftOverSamples[writeChannel];
210+
int blockStart = sampleOffset;
211+
int64 currentTS = getTimestamp(realChannel);
212+
213+
if (sampleOffset > 0)
214+
{
215+
currentTS += TIMESTAMP_EACH_NSAMPLES - sampleOffset;
216+
blockStart += TIMESTAMP_EACH_NSAMPLES - sampleOffset;
217+
}
218+
219+
for (int i = 0; i < size; i += TIMESTAMP_EACH_NSAMPLES)
220+
{
221+
if ((blockStart + i) < (sampleOffset + size))
222+
{
223+
channelTimestampArray[writeChannel]->add(currentTS);
224+
currentTS += TIMESTAMP_EACH_NSAMPLES;
225+
}
226+
}
227+
channelLeftOverSamples.set(writeChannel, (size + sampleOffset) % TIMESTAMP_EACH_NSAMPLES);
228+
}
229+
230+
void HDF5Recording::endChannelBlock(bool lastBlock)
231+
{
232+
int nCh = channelTimestampArray.size();
233+
for (int ch = 0; ch < nCh; ++ch)
234+
{
235+
int tsSize = channelTimestampArray[ch]->size();
236+
if ((tsSize > 0) && ((tsSize > CHANNEL_TIMESTAMP_MIN_WRITE) || lastBlock))
237+
{
238+
int realChan = getRealChannel(ch);
239+
int index = processorMap[getChannel(realChan)->recordIndex];
240+
fileArray[index]->writeTimestamps(channelTimestampArray[ch]->getRawDataPointer(), tsSize, recordedChanToKWDChan[ch]);
241+
channelTimestampArray[ch]->clearQuick();
242+
}
243+
}
211244
}
212245

213246
void HDF5Recording::writeEvent(int eventType, const MidiMessage& event, int64 timestamp)

Source/Plugins/KWIKFormat/RecordEngine/HDF5Recording.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class HDF5Recording : public RecordEngine
3232
public:
3333
HDF5Recording();
3434
~HDF5Recording();
35-
String getEngineID();
35+
String getEngineID() const override;
3636
void openFiles(File rootFolder, int experimentNumber, int recordingNumber) override;
3737
void closeFiles() override;
3838
void writeData(int writeChannel, int realChannel, const float* buffer, int size) override;
@@ -43,6 +43,7 @@ class HDF5Recording : public RecordEngine
4343
void registerProcessor(const GenericProcessor* processor) override;
4444
void resetChannels() override;
4545
void startAcquisition() override;
46+
void endChannelBlock(bool lastBlock) override;
4647

4748
static RecordEngineManager* getEngineManager();
4849
private:
@@ -54,12 +55,17 @@ class HDF5Recording : public RecordEngine
5455
Array<int> recordedChanToKWDChan;
5556
OwnedArray<Array<float>> bitVoltsArray;
5657
OwnedArray<Array<float>> sampleRatesArray;
58+
OwnedArray<Array<int64>> channelTimestampArray;
59+
Array<int> channelLeftOverSamples;
5760
OwnedArray<KWDFile> fileArray;
5861
OwnedArray<HDF5RecordingInfo> infoArray;
5962
ScopedPointer<KWEFile> eventFile;
6063
ScopedPointer<KWXFile> spikesFile;
61-
float* scaledBuffer;
62-
int16* intBuffer;
64+
HeapBlock<float> scaledBuffer;
65+
HeapBlock<int16> intBuffer;
66+
int bufferSize;
67+
//float* scaledBuffer;
68+
//int16* intBuffer;
6369

6470
bool hasAcquired;
6571

Source/Processors/DataThreads/RhythmNode/RHD2000Thread.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,10 @@ RHD2000Thread::~RHD2000Thread()
188188

189189
//deleteAndZero(dataBlock);
190190

191-
delete dacStream;
192-
delete dacChannels;
193-
delete dacThresholds;
194-
delete dacChannelsToUpdate;
191+
delete[] dacStream;
192+
delete[] dacChannels;
193+
delete[] dacThresholds;
194+
delete[] dacChannelsToUpdate;
195195

196196
}
197197

0 commit comments

Comments
 (0)