|
1 | 1 | from __future__ import annotations |
| 2 | +from typing import Literal |
2 | 3 | from pathlib import Path |
3 | 4 |
|
4 | 5 | import numpy as np |
5 | 6 |
|
6 | 7 | from spikeinterface.core.core_tools import define_function_from_class |
| 8 | +from spikeinterface.core.segmentutils import ConcatenateSegmentRecording, AppendSegmentRecording |
7 | 9 | from .neobaseextractor import NeoBaseRecordingExtractor |
8 | 10 |
|
9 | 11 |
|
@@ -104,3 +106,106 @@ def _add_channel_groups(self): |
104 | 106 |
|
105 | 107 |
|
106 | 108 | read_intan = define_function_from_class(source_class=IntanRecordingExtractor, name="read_intan") |
| 109 | + |
| 110 | + |
| 111 | +class IntanSplitFilesRecordingExtractor(ConcatenateSegmentRecording, AppendSegmentRecording): |
| 112 | + """ |
| 113 | + Class for reading Intan traditional format split files from a folder and |
| 114 | + concatenating/appending them in temporal order. |
| 115 | +
|
| 116 | + Intan traditional format creates multiple files with time-based naming when recording |
| 117 | + for extended periods. This class automatically sorts the files by filename and concatenates |
| 118 | + them to create a continuous recording (monosegment) or appends them to a multisegment recording. |
| 119 | +
|
| 120 | + Parameters |
| 121 | + ---------- |
| 122 | + folder_path : str or Path |
| 123 | + Path to the folder containing split Intan files (.rhd or .rhs extensions) |
| 124 | + mode : "concatenate" | "append": default: "concatenate" |
| 125 | + The determines whether to concatenate intan files to make a monosegment or to append them |
| 126 | + to make a multisegment recording |
| 127 | + stream_id : str, default: None |
| 128 | + If there are several streams, specify the stream id you want to load. |
| 129 | + stream_name : str, default: None |
| 130 | + If there are several streams, specify the stream name you want to load. |
| 131 | + all_annotations : bool, default: False |
| 132 | + Load exhaustively all annotations from neo. |
| 133 | + use_names_as_ids : bool, default: False |
| 134 | + Determines the format of the channel IDs used by the extractor. If set to True, the channel IDs will be the |
| 135 | + names from NeoRawIO. If set to False, the channel IDs will be the ids provided by NeoRawIO. |
| 136 | + ignore_integrity_checks : bool, default: False |
| 137 | + If True, data that violates integrity assumptions will be loaded. At the moment the only integrity |
| 138 | + check we perform is that timestamps are continuous. Setting this to True will ignore this check and set |
| 139 | + the attribute `discontinuous_timestamps` to True in the underlying neo object. |
| 140 | +
|
| 141 | + Examples |
| 142 | + -------- |
| 143 | + >>> from spikeinterface.extractors import IntanSplitFilesRecordingExtractor |
| 144 | + >>> recording = IntanSplitFilesRecordingExtractor("/path/to/intan/folder") |
| 145 | + """ |
| 146 | + |
| 147 | + def __init__( |
| 148 | + self, |
| 149 | + folder_path, |
| 150 | + mode: Literal["append", "concatenate"] = "concatenate", |
| 151 | + stream_id=None, |
| 152 | + stream_name=None, |
| 153 | + all_annotations=False, |
| 154 | + use_names_as_ids=False, |
| 155 | + ignore_integrity_checks: bool = False, |
| 156 | + ): |
| 157 | + |
| 158 | + if mode not in ("append", "concatenate"): |
| 159 | + mode_error = ( |
| 160 | + "Possible options for the `mode` argument are 'concatenate' or 'append', you have entered " f"{mode}." |
| 161 | + ) |
| 162 | + raise ValueError(mode_error) |
| 163 | + |
| 164 | + folder_path = Path(folder_path) |
| 165 | + |
| 166 | + if not folder_path.exists() or not folder_path.is_dir(): |
| 167 | + raise ValueError(f"Folder path {folder_path} does not exist or is not a directory") |
| 168 | + |
| 169 | + # Find all Intan files |
| 170 | + file_path_list = [p for p in folder_path.iterdir() if p.suffix.lower() in [".rhd", ".rhs"]] |
| 171 | + |
| 172 | + if not file_path_list: |
| 173 | + raise ValueError(f"No Intan files (.rhd or .rhs) found in {folder_path}") |
| 174 | + |
| 175 | + # Sort files by filename (natural sort) |
| 176 | + file_path_list.sort(key=lambda x: x.name) |
| 177 | + |
| 178 | + # Read each file and create recording list |
| 179 | + recording_list = [] |
| 180 | + for file_path in file_path_list: |
| 181 | + recording = read_intan( |
| 182 | + file_path, |
| 183 | + stream_id=stream_id, |
| 184 | + stream_name=stream_name, |
| 185 | + all_annotations=all_annotations, |
| 186 | + use_names_as_ids=use_names_as_ids, |
| 187 | + ignore_integrity_checks=ignore_integrity_checks, |
| 188 | + ) |
| 189 | + recording_list.append(recording) |
| 190 | + |
| 191 | + # Initialize the parent class with the recording list |
| 192 | + if mode == "concatenate": |
| 193 | + ConcatenateSegmentRecording.__init__(self, recording_list) |
| 194 | + elif mode == "append": |
| 195 | + AppendSegmentRecording.__init__(self, recording_list) |
| 196 | + |
| 197 | + # Update kwargs to include our specific parameters |
| 198 | + self._kwargs = dict( |
| 199 | + folder_path=str(Path(folder_path).resolve()), |
| 200 | + mode=mode, |
| 201 | + stream_id=stream_id, |
| 202 | + stream_name=stream_name, |
| 203 | + all_annotations=all_annotations, |
| 204 | + use_names_as_ids=use_names_as_ids, |
| 205 | + ignore_integrity_checks=ignore_integrity_checks, |
| 206 | + ) |
| 207 | + |
| 208 | + |
| 209 | +read_split_intan_files = define_function_from_class( |
| 210 | + source_class=IntanSplitFilesRecordingExtractor, name="read_split_intan_files" |
| 211 | +) |
0 commit comments