Skip to content

Commit 550dc8d

Browse files
authored
Fix spikeglx events memmap (#1790)
* delay memory access till event access on spikeglx reader * fix tests * black
1 parent 01f5577 commit 550dc8d

File tree

1 file changed

+23
-8
lines changed

1 file changed

+23
-8
lines changed

neo/rawio/spikeglxrawio.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -246,12 +246,20 @@ def _parse_header(self):
246246
chan_name = local_chan
247247
chan_id = f"{stream_name}#{chan_name}"
248248
event_channels.append((chan_name, chan_id, "event"))
249-
# add events_memmap
250-
data = np.memmap(info["bin_file"], dtype="int16", mode="r", offset=0, order="C")
251-
data = data.reshape(-1, info["num_chan"])
252-
# The digital word is usually the last channel, after all the individual analog channels
253-
extracted_word = data[:, len(info["analog_channels"])]
254-
self._events_memmap = np.unpackbits(extracted_word.astype(np.uint8)[:, None], axis=1)
249+
# Create memmap for digital word but defer unpacking until needed
250+
# The digital word is stored as the last channel, after all the individual analog channels
251+
# For example: if there are 8 analog channels (indices 0-7), the digital word is at index 8
252+
num_samples = info["sample_length"]
253+
num_channels = info["num_chan"]
254+
data = np.memmap(
255+
info["bin_file"],
256+
dtype="int16",
257+
mode="r",
258+
shape=(num_samples, num_channels),
259+
order="C",
260+
)
261+
digital_word_channel_index = len(info["analog_channels"])
262+
self._events_memmap_digital_word = data[:, digital_word_channel_index]
255263
event_channels = np.array(event_channels, dtype=_event_channel_dtype)
256264

257265
# No spikes
@@ -342,7 +350,9 @@ def _get_event_timestamps(self, block_index, seg_index, event_channel_index, t_s
342350
info = self.signals_info_dict[0, "nidq"] # There are no events that are not in the nidq stream
343351
dig_ch = info["digital_channels"]
344352
if len(dig_ch) > 0:
345-
event_data = self._events_memmap
353+
# Unpack bits on-demand - this is when memory allocation happens
354+
event_data = np.unpackbits(self._events_memmap_digital_word.astype(np.uint8)[:, None], axis=1)
355+
346356
channel = dig_ch[event_channel_index]
347357
ch_idx = 7 - int(channel[2:]) # They are in the reverse order
348358
this_stream = event_data[:, ch_idx]
@@ -682,7 +692,12 @@ def extract_stream_info(meta_file, meta):
682692
info["sampling_rate"] = float(meta[k])
683693
info["num_chan"] = num_chan
684694

685-
info["sample_length"] = int(meta["fileSizeBytes"]) // 2 // num_chan
695+
# Calculate sample_length from actual file size instead of metadata to handle stub/modified files
696+
# The metadata fileSizeBytes may not match the actual .bin file (e.g., in test stub files)
697+
# Original calculation (only correct for unmodified files):
698+
# info["sample_length"] = int(meta["fileSizeBytes"]) // 2 // num_chan
699+
actual_file_size = Path(meta_file).with_suffix(".bin").stat().st_size
700+
info["sample_length"] = actual_file_size // 2 // num_chan # 2 bytes per int16 sample
686701
info["gate_num"] = gate_num
687702
info["trigger_num"] = trigger_num
688703
info["device"] = device

0 commit comments

Comments
 (0)