forked from open-ephys/plugin-GUI
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSequentialBlockFile.cpp
More file actions
161 lines (136 loc) · 4.84 KB
/
Copy pathSequentialBlockFile.cpp
File metadata and controls
161 lines (136 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
------------------------------------------------------------------
This file is part of the Open Ephys GUI
Copyright (C) 2013 Open Ephys
------------------------------------------------------------------
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SequentialBlockFile.h"
using namespace BinaryRecordingEngine;
SequentialBlockFile::SequentialBlockFile(int nChannels, int samplesPerBlock) :
m_file(nullptr),
m_nChannels(nChannels),
m_samplesPerBlock(samplesPerBlock),
m_blockSize(nChannels*samplesPerBlock),
m_lastBlockFill(0)
{
m_memBlocks.ensureStorageAllocated(blockArrayInitSize);
for (int i = 0; i < nChannels; i++)
m_currentBlock.add(-1);
}
SequentialBlockFile::~SequentialBlockFile()
{
//Ensure that all remaining blocks are flushed in order. Keep the last one
int n = m_memBlocks.size();
for (int i = 0; i < n - 1; i++)
{
m_memBlocks.remove(0);
}
//manually flush the last one to avoid trailing zeroes
m_memBlocks[0]->partialFlush(m_lastBlockFill * m_nChannels);
}
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;
m_memBlocks.add(new FileBlock(m_file, m_blockSize, 0));
return true;
}
bool SequentialBlockFile::writeChannel(uint64 startPos, int channel, int16* data, int nSamples)
{
if (!m_file)
return false;
int bIndex = m_memBlocks.size() - 1;
if ((bIndex < 0) || (m_memBlocks[bIndex]->getOffset() + m_samplesPerBlock) < (startPos + nSamples))
allocateBlocks(startPos, nSamples);
for (bIndex = m_memBlocks.size() - 1; bIndex >= 0; bIndex--)
{
if (m_memBlocks[bIndex]->getOffset() <= startPos)
break;
}
if (bIndex < 0)
{
std::cerr << "BINARY WRITER: Memory block unloaded ahead of time for chan " << channel << " start " << startPos << " ns " << nSamples << " first " << m_memBlocks[0]->getOffset() <<std::endl;
for (int i = 0; i < m_nChannels; i++)
std::cout << "channel " << i << " last block " << m_currentBlock[i] << std::endl;
return false;
}
int writtenSamples = 0;
int startIdx = startPos - m_memBlocks[bIndex]->getOffset();
int startMemPos = startIdx*m_nChannels;
int dataIdx = 0;
int lastBlockIdx = m_memBlocks.size() - 1;
while (writtenSamples < nSamples)
{
int16* blockPtr = m_memBlocks[bIndex]->getData();
int samplesToWrite = jmin((nSamples - writtenSamples), (m_samplesPerBlock - startIdx));
for (int i = 0; i < samplesToWrite; i++)
{
*(blockPtr + startMemPos + channel + i*m_nChannels) = *(data + dataIdx);
dataIdx++;
}
writtenSamples += samplesToWrite;
//Update the last block fill index
size_t samplePos = startIdx + samplesToWrite;
if (bIndex == lastBlockIdx && samplePos > m_lastBlockFill)
{
m_lastBlockFill = samplePos;
}
startIdx = 0;
startMemPos = 0;
bIndex++;
}
m_currentBlock.set(channel, bIndex - 1); //store the last block a channel was written in
return true;
}
void SequentialBlockFile::allocateBlocks(uint64 startIndex, int numSamples)
{
//First deallocate full blocks
//Search for the earliest unused block;
unsigned int minBlock = 0xFFFFFFFF; //large number;
for (int i = 0; i < m_nChannels; i++)
{
if (m_currentBlock[i] < minBlock)
minBlock = m_currentBlock[i];
}
//Update block indexes
for (int i = 0; i < m_nChannels; i++)
{
m_currentBlock.set(i, m_currentBlock[i] - minBlock);
}
m_memBlocks.removeRange(0, minBlock);
//for (int i = 0; i < minBlock; i++)
//{
//Not the most efficient way, as it has to move back all the elements, but it's a simple array of pointers, so it's quick enough
// m_memBlocks.remove(0);
//}
//Look for the last available position and calculate needed space
uint64 lastOffset = m_memBlocks.getLast()->getOffset();
uint64 maxAddr = lastOffset + m_samplesPerBlock - 1;
uint64 newSpaceNeeded = numSamples - (maxAddr - startIndex);
int newBlocks = (newSpaceNeeded + m_samplesPerBlock - 1) / m_samplesPerBlock; //Fast ceiling division
for (int i = 0; i < newBlocks; i++)
{
lastOffset += m_samplesPerBlock;
m_memBlocks.add(new FileBlock(m_file, m_blockSize, lastOffset));
}
if (newBlocks > 0)
m_lastBlockFill = 0; //we've added some new blocks, so the last one will be empty
}