Skip to content

Commit 9fc58a0

Browse files
committed
Simplify VECTORIZED implementation to use memmap
- Replace np.fromfile() with np.memmap for multiprocessing compatibility - Minimize code changes in _get_analogsignal_chunk() - Remove file handle caching logic (memmap handles this) - All tests still pass with identical results
1 parent 35d4a41 commit 9fc58a0

File tree

1 file changed

+9
-34
lines changed

1 file changed

+9
-34
lines changed

neo/rawio/brainvisionrawio.py

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -257,60 +257,35 @@ def _get_analogsignal_chunk(
257257
self, block_index, seg_index, i_start, i_stop, stream_index, channel_indexes
258258
):
259259
"""
260-
Override the base class method to handle VECTORIZED orientation.
261-
262-
For MULTIPLEXED data: ch1_s1, ch2_s1, ..., chN_s1, ch1_s2, ch2_s2, ...
263-
For VECTORIZED data: ch1_s1, ch1_s2, ..., ch1_sM, ch2_s1, ch2_s2, ..., ch2_sM, ...
260+
Override to handle VECTORIZED orientation.
261+
VECTORIZED: all samples for ch1, then all samples for ch2, etc.
264262
"""
265263
if self._data_orientation == "MULTIPLEXED":
266-
# Use the default implementation for MULTIPLEXED
267264
return super()._get_analogsignal_chunk(
268265
block_index, seg_index, i_start, i_stop, stream_index, channel_indexes
269266
)
270267

271-
# VECTORIZED implementation
268+
# VECTORIZED: use memmap to read each channel's data block
272269
buffer_id = self.header["signal_streams"][stream_index]["buffer_id"]
273270
buffer_desc = self.get_analogsignal_buffer_description(block_index, seg_index, buffer_id)
274271

275272
i_start = i_start or 0
276273
i_stop = i_stop or buffer_desc["shape"][0]
277274

278-
# Open file on demand
279-
if not hasattr(self, "_memmap_analogsignal_buffers"):
280-
self._memmap_analogsignal_buffers = {}
281-
if block_index not in self._memmap_analogsignal_buffers:
282-
self._memmap_analogsignal_buffers[block_index] = {}
283-
if seg_index not in self._memmap_analogsignal_buffers[block_index]:
284-
self._memmap_analogsignal_buffers[block_index][seg_index] = {}
285-
if buffer_id not in self._memmap_analogsignal_buffers[block_index][seg_index]:
286-
fid = open(buffer_desc["file_path"], mode="rb")
287-
self._memmap_analogsignal_buffers[block_index][seg_index][buffer_id] = fid
288-
else:
289-
fid = self._memmap_analogsignal_buffers[block_index][seg_index][buffer_id]
290-
291-
# Determine which channels to read
292275
if channel_indexes is None:
293276
channel_indexes = np.arange(self._nb_channel)
294-
else:
295-
channel_indexes = np.asarray(channel_indexes)
296277

297-
num_samples = i_stop - i_start
298278
dtype = np.dtype(buffer_desc["dtype"])
279+
num_samples = i_stop - i_start
280+
total_samples_per_channel = buffer_desc["shape"][0]
299281

300-
# For VECTORIZED, each channel's data is stored contiguously
301-
# We need to read from different parts of the file for each channel
302282
raw_sigs = np.empty((num_samples, len(channel_indexes)), dtype=dtype)
303283

304-
total_samples_per_channel = buffer_desc["shape"][0]
305-
306284
for i, chan_idx in enumerate(channel_indexes):
307-
# Calculate offset for this channel's data in the file
308-
channel_offset = buffer_desc["file_offset"] + chan_idx * total_samples_per_channel * dtype.itemsize
309-
sample_offset = channel_offset + i_start * dtype.itemsize
310-
311-
# Seek to the position and read the data
312-
fid.seek(sample_offset)
313-
raw_sigs[:, i] = np.fromfile(fid, dtype=dtype, count=num_samples)
285+
offset = buffer_desc["file_offset"] + chan_idx * total_samples_per_channel * dtype.itemsize
286+
channel_data = np.memmap(buffer_desc["file_path"], dtype=dtype, mode='r',
287+
offset=offset, shape=(total_samples_per_channel,))
288+
raw_sigs[:, i] = channel_data[i_start:i_stop]
314289

315290
return raw_sigs
316291

0 commit comments

Comments
 (0)