diff --git a/src/ilabs_streamsync/example_script.py b/src/ilabs_streamsync/example_script.py index 12bf205..e006f72 100644 --- a/src/ilabs_streamsync/example_script.py +++ b/src/ilabs_streamsync/example_script.py @@ -13,10 +13,9 @@ my_events = [] for cam in cams: - extract_audio_from_video(cam, output_dir) + extract_audio_from_video(cam, output_dir, overwrite=False) #This could potentially return filenames to avoid the hardcoding seen below. ss = StreamSync(raw, channel) - # TODO: Perhaps the extraction above could return the newly created paths so that this doesn't need to be hard coded. ss.add_stream("/Users/user/VideoSync_NonSubject/output/sinclair_alexis_audiosync_240110_CAM3_16bit.wav", channel=1) ss.plot_sync_pulses(tmin=0.5,tmax=50) diff --git a/src/ilabs_streamsync/streamdata.py b/src/ilabs_streamsync/streamdata.py new file mode 100644 index 0000000..9a30fcf --- /dev/null +++ b/src/ilabs_streamsync/streamdata.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +class StreamData: + """ + Store information about stream of data. + """ + def __init__(self, filename, sample_rate, pulses, data): + """ + Initialize object with associated properties. + + filename: str + Path to the file with stream data + sample_rate: int + Sampling rate of the data + pulses: np.array + Numpy array representing the pulses. + data: np.array + NumPy array representing all streams of data. + """ + self.filename = filename + self.sample_rate = sample_rate + self.pulses = pulses + self.data = data \ No newline at end of file diff --git a/src/ilabs_streamsync/streamsync.py b/src/ilabs_streamsync/streamsync.py index 7d423ab..f967202 100644 --- a/src/ilabs_streamsync/streamsync.py +++ b/src/ilabs_streamsync/streamsync.py @@ -10,6 +10,7 @@ import mne import numpy as np from scipy.io.wavfile import read as wavread +from streamdata import StreamData FFMPEG_TIMEOUT_SEC = 50 @@ -59,7 +60,7 @@ def __init__(self, reference_object, pulse_channel): self.sfreq = self.raw.info["sfreq"] # Hz - self.streams = [] # of (filename, srate, Pulses, Data) + self.streams = [] # list of StreamData objects def add_stream(self, stream, channel=None, events=None): """Add a new ``Raw`` or video stream, optionally with events. @@ -72,8 +73,7 @@ def add_stream(self, stream, channel=None, events=None): Events associated with the stream. TODO: should they be integer sample numbers? Timestamps? Do we support both? """ - srate, pulses, data = self._extract_data_from_stream(stream, channel=channel) - self.streams.append((stream, srate, pulses, data)) + self.streams.append(self._extract_data_from_stream(stream, channel=channel)) def _extract_data_from_stream(self, stream, channel): """Extract pulses and raw data from stream provided. TODO: Implement adding a annotation stream.""" @@ -86,7 +86,7 @@ def _extract_data_from_stream(self, stream, channel): def _extract_data_from_wav(self, stream, channel): """Return tuple of (pulse channel, audio channel) from stereo file.""" srate, wav_signal = wavread(stream) - return (srate, wav_signal[:,channel], wav_signal[:,1-channel]) + return StreamData(filename = stream, sample_rate=srate, pulses=wav_signal[:,channel], data=wav_signal[:,1-channel]) def remove_stream(self, stream): pass @@ -96,7 +96,7 @@ def do_syncing(self): # TODO (waves hands) do the hard part. # TODO spit out a report of correlation/association between all pairs of streams - def plot_sync_pulses(self, tmin=0, tmax=float('inf')): + def plot_sync_pulses(self, tmin=0, tmax=None): """Plot each stream in the class. tmin: int @@ -113,15 +113,15 @@ def plot_sync_pulses(self, tmin=0, tmax=float('inf')): axset[0].set_title("Reference MEG") # Plot all other streams for i, stream in enumerate(self.streams): - npts = len(stream[2]) - tt = np.arange(npts) / stream[1] + npts = len(stream.pulses) + tt = np.arange(npts) / stream.sample_rate idx = np.where((tt>=tmin) & (tt