@@ -127,6 +127,7 @@ def _source_name(self):
127127 def _parse_header (self ):
128128 self .signals_info_list = scan_files (self .dirname )
129129 _add_segment_order (self .signals_info_list )
130+ _add_segment_timing (self .signals_info_list )
130131
131132 # sort stream_name by higher sampling rate first
132133 srates = {info ["stream_name" ]: info ["sampling_rate" ] for info in self .signals_info_list }
@@ -269,9 +270,7 @@ def _parse_header(self):
269270 for seg_index in range (nb_segment ):
270271 info = self .signals_info_dict [seg_index , stream_name ]
271272
272- frame_start = float (info ["meta" ]["firstSample" ])
273- sampling_frequency = info ["sampling_rate" ]
274- t_start = frame_start / sampling_frequency
273+ t_start = info ["t_start" ]
275274
276275 self ._t_starts [stream_name ][seg_index ] = t_start
277276
@@ -282,8 +281,7 @@ def _parse_header(self):
282281 self ._t_starts [sync_stream_name ] = {}
283282 self ._t_starts [sync_stream_name ][seg_index ] = t_start
284283
285- t_stop = info ["sample_length" ] / info ["sampling_rate" ]
286- self ._t_stops [seg_index ] = max (self ._t_stops [seg_index ], t_stop )
284+ self ._t_stops [seg_index ] = max (self ._t_stops [seg_index ], info ["t_stop" ])
287285
288286 # fille into header dict
289287 self .header = {}
@@ -407,6 +405,40 @@ def scan_files(dirname):
407405 return info_list
408406
409407
408+ def _add_segment_timing (info_list ):
409+ """
410+ Add ``info["first_sample"]``, ``info["t_start"]``, and ``info["t_stop"]`` per signal.
411+
412+ Reads ``meta["firstSample"]`` (documented in every SpikeGLX phase) and converts
413+ to float. When absent, defaults to 0 with a UserWarning naming the file. Zero
414+ is the correct fallback for a file that starts at the beginning of its SpikeGLX
415+ run, which covers the expected causes of a missing tag: pre-2016 builds (the
416+ tag was introduced in 2016), recordings where the end-of-run write was
417+ interrupted, and ``.meta`` files modified after acquisition.
418+
419+ Then stores ``info["t_start"] = info["first_sample"] / info["sampling_rate"]``
420+ and ``info["t_stop"] = info["sample_length"] / info["sampling_rate"]`` in
421+ seconds, so downstream code can read both directly without recomputation.
422+ """
423+ for info in info_list :
424+ meta = info ["meta" ]
425+ if "firstSample" in meta :
426+ info ["first_sample" ] = float (meta ["firstSample" ])
427+ else :
428+ warn (
429+ f"'firstSample' missing from { info ['meta_file' ]} ; defaulting to 0. "
430+ f"Zero is correct for files that start at the beginning of their SpikeGLX run, "
431+ f"but wrong for any file that represents a non-initial trigger or that was "
432+ f"derived from a longer recording. Typical causes: pre-2016 SpikeGLX build, "
433+ f"interrupted end-of-run write, or post-acquisition modification of the .meta file." ,
434+ UserWarning ,
435+ stacklevel = 2 ,
436+ )
437+ info ["first_sample" ] = 0.0
438+ info ["t_start" ] = info ["first_sample" ] / info ["sampling_rate" ]
439+ info ["t_stop" ] = info ["sample_length" ] / info ["sampling_rate" ]
440+
441+
410442def _build_signals_info_dict (info_list ):
411443 """
412444 Re-index a flat list of info dicts into a dict keyed by (seg_index, stream_name).
0 commit comments