Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/spikeinterface/core/binaryrecordingextractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ class BinaryRecordingExtractor(BaseRecording):
The dtype of the binary file
time_axis : int, default: 0
The axis of the time dimension
t_starts : None or list of float, default: None
Times in seconds of the first sample for each segment
t_starts : None or list of float or None, default: None
Times in seconds of the first sample for each segment. An entry is None for a segment
without a start time.
channel_ids : list, default: None
A list of channel ids
file_offset : int, default: 0
Expand Down Expand Up @@ -57,7 +58,7 @@ def __init__(
sampling_frequency: float,
dtype: str | np.dtype,
num_channels: int | None = None,
t_starts: list[float] | None = None,
t_starts: list[float | None] | None = None,
channel_ids: list[str | int] | None = None,
time_axis: int = 0,
file_offset: int = 0,
Expand All @@ -83,7 +84,7 @@ def __init__(

if t_starts is not None:
assert len(t_starts) == len(file_path_list), "t_starts must be a list of the same size as file_paths"
t_starts = [float(t_start) for t_start in t_starts]
t_starts = [None if t_start is None else float(t_start) for t_start in t_starts]

if file_timestamps_paths is not None:
if isinstance(file_timestamps_paths, list):
Expand Down
23 changes: 13 additions & 10 deletions src/spikeinterface/core/numpyextractors.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ class NumpyRecording(BaseRecording):
The traces to instantiate a mono or multisegment Recording
sampling_frequency : float
The sampling frequency in Hz
t_starts : None or list of float
Times in seconds of the first sample for each segment
t_starts : None or list of float or None
Times in seconds of the first sample for each segment. An entry is None for a segment
without a start time.
channel_ids : list
An optional list of channel_ids. If None, linear channels are assumed
"""
Expand Down Expand Up @@ -55,7 +56,7 @@ def __init__(self, traces_list, sampling_frequency, t_starts=None, channel_ids=N

if t_starts is not None:
assert len(t_starts) == len(traces_list), "t_starts must be a list of same size than traces_list"
t_starts = [float(t_start) for t_start in t_starts]
t_starts = [None if t_start is None else float(t_start) for t_start in t_starts]

self._serializability["json"] = False
self._serializability["pickle"] = False
Expand Down Expand Up @@ -142,8 +143,9 @@ class SharedMemoryRecording(BaseRecording):
Note that the number of channels must be the same for all segments
sampling_frequency : float
The sampling frequency in Hz
t_starts : None or list of float
Times in seconds of the first sample for each segment
t_starts : None or list of float or None
Times in seconds of the first sample for each segment. An entry is None for a segment
without a start time.
channel_ids : list
An optional list of channel_ids. If None, linear channels are assumed
main_shm_owner : bool, default: True
Expand All @@ -153,11 +155,15 @@ class SharedMemoryRecording(BaseRecording):
def __init__(
self, shm_names, shape_list, dtype, sampling_frequency, channel_ids=None, t_starts=None, main_shm_owner=True
):
# Set these first: __del__ reads them, even when the constructor raises.
self.shms = []
# this is important so that the main owner can unlink the share mem buffer :
self.main_shm_owner = main_shm_owner

assert len(shape_list) == len(shm_names), "Each shm_name in `shm_names` must have a shape in `shape_list`"
assert all(shape_list[0][1] == shape[1] for shape in shape_list)

# create traces from sharedmem names
self.shms = []
traces_list = []
for shm_name, shape in zip(shm_names, shape_list):
shm = SharedMemory(shm_name, create=False)
Expand All @@ -176,15 +182,12 @@ def __init__(

if t_starts is not None:
assert len(t_starts) == len(traces_list), "t_starts must be a list of same size as traces_list"
t_starts = [float(t_start) for t_start in t_starts]
t_starts = [None if t_start is None else float(t_start) for t_start in t_starts]

self._serializability["memory"] = True
self._serializability["json"] = False
self._serializability["pickle"] = False

# this is important so that the main owner can unlink the share mem buffer :
self.main_shm_owner = main_shm_owner

for (
i,
traces,
Expand Down
33 changes: 33 additions & 0 deletions src/spikeinterface/core/tests/test_baserecording.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,39 @@ def test_save_load_binary_with_time_vector(create_cache_folder, mp_context):
assert not rec_saved_no_time_vector_par.has_time_vector(segment_index=0)


@pytest.mark.parametrize(
"save_kwargs",
[
dict(format="binary"),
dict(format="memory", sharedmem=False),
dict(format="memory", sharedmem=True),
dict(format="zarr"),
],
ids=["binary", "numpy", "sharedmem", "zarr"],
)
def test_save_with_t_start_on_some_segments(tmp_path, save_kwargs):
rec = generate_recording(num_channels=2, durations=[0.5, 0.2, 0.1], sampling_frequency=1000.0)
rec.shift_times(100.0, segment_index=1)
assert rec.get_segment_t_starts() == [None, 100.0, None]

if save_kwargs["format"] == "binary":
save_kwargs = dict(save_kwargs, folder=tmp_path / "saved")
elif save_kwargs["format"] == "zarr":
save_kwargs = dict(save_kwargs, folder=tmp_path / "saved.zarr")
saved = rec.save(**save_kwargs)

# load() builds the recording again from what was saved, e.g. from binary.json for the binary format
for rec_to_check in (saved, load(saved.to_dict())):
assert rec_to_check.get_segment_t_starts() == [None, 100.0, None]
for segment_index in range(rec.get_num_segments()):
assert type(rec_to_check.get_start_time(segment_index)) is float
assert rec_to_check.get_start_time(segment_index) == rec.get_start_time(segment_index)
assert np.array_equal(rec_to_check.get_times(segment_index), rec.get_times(segment_index))
assert np.array_equal(
rec_to_check.get_traces(segment_index=segment_index), rec.get_traces(segment_index=segment_index)
)


if __name__ == "__main__":
import tempfile

Expand Down
2 changes: 1 addition & 1 deletion src/spikeinterface/core/zarrextractors.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def __init__(
if t_starts is None:
t_start = None
else:
t_start = t_starts[segment_index]
t_start = float(t_starts[segment_index])
if np.isnan(t_start):
t_start = None
time_kwargs["t_start"] = t_start
Expand Down
Loading