diff --git a/lasp/lasp_avstream.py b/lasp/lasp_avstream.py index ad2f7c9..d806b39 100644 --- a/lasp/lasp_avstream.py +++ b/lasp/lasp_avstream.py @@ -55,6 +55,7 @@ class AvStream: self.daqconfig = daqconfig self._device = device self.avtype = avtype + self.duplex_mode = daqconfig.duplex_mode # Determine highest input channel number channelconfigs = daqconfig.en_input_channels @@ -125,7 +126,9 @@ class AvStream: """ Returns the current number of installed callbacks """ - return len(self._callbacks) + return len(self._callbacks[AvType.audio_input]) + \ + len(self._callbacks[AvType.audio_output]) + \ + len(self._callbacks[AvType.video]) def addCallback(self, cb: callable, cbtype: AvType): """ @@ -196,9 +199,15 @@ class AvStream: output_signal = None with self._callbacklock: for cb in self._callbacks[AvType.audio_input]: - cb(indata, self._aframectr()) + try: + cb(indata, self._aframectr()) + except Exception as e: + print(e) for cb in self._callbacks[AvType.audio_output]: - output_data = cb(indata, self._aframectr()) + try: + output_signal = cb(indata, self._aframectr()) + except Exception as e: + print(e) return output_signal, 0 if self._running else 1 def stop(self): diff --git a/lasp/lasp_record.py b/lasp/lasp_record.py index b6fa562..de85589 100644 --- a/lasp/lasp_record.py +++ b/lasp/lasp_record.py @@ -8,12 +8,19 @@ from .lasp_atomic import Atomic from threading import Condition from .lasp_avstream import AvType, AvStream import h5py +import dataclasses +import os import time +@dataclasses.dataclass +class RecordStatus: + curT: float + done: bool class Recording: - def __init__(self, fn, stream, rectime=None): + def __init__(self, fn, stream, rectime=None, wait = True, + progressCallback=None): """ Args: @@ -39,67 +46,100 @@ class Recording: self._aframeno = Atomic(0) self._vframeno = 0 - def start(self): - stream = self._stream + self._progressCallback = progressCallback + self._wait = wait - with h5py.File(self._fn, 'w') as f: - self._ad = f.create_dataset('audio', - (1, stream.blocksize, stream.nchannels), - dtype=stream.numpy_dtype, - maxshape=(None, stream.blocksize, - stream.nchannels), + self._f = h5py.File(self._fn, 'w') + self._deleteFile = False + + def setDelete(self, val: bool): + self._deleteFile = val + + def __enter__(self): + """ + + with self._recording(wait=False): + event_loop_here() + + or: + + with Recording(wait=True): + pass + """ + + stream = self._stream + f = self._f + + self._ad = f.create_dataset('audio', + (1, stream.blocksize, stream.nchannels), + dtype=stream.numpy_dtype, + maxshape=(None, stream.blocksize, + stream.nchannels), + compression='gzip' + ) + if stream.hasVideo(): + video_x, video_y = stream.video_x, stream.video_y + self._vd = f.create_dataset('video', + (1, video_y, video_x, 3), + dtype='uint8', + maxshape=( + None, video_y, video_x, 3), compression='gzip' ) - if stream.hasVideo(): - video_x, video_y = stream.video_x, stream.video_y - self._vd = f.create_dataset('video', - (1, video_y, video_x, 3), - dtype='uint8', - maxshape=( - None, video_y, video_x, 3), - compression='gzip' - ) - f.attrs['samplerate'] = stream.samplerate - f.attrs['nchannels'] = stream.nchannels - f.attrs['blocksize'] = stream.blocksize - f.attrs['sensitivity'] = stream.sensitivity - f.attrs['time'] = time.time() - self._running <<= True - # Videothread is going to start + f.attrs['samplerate'] = stream.samplerate + f.attrs['nchannels'] = stream.nchannels + f.attrs['blocksize'] = stream.blocksize + f.attrs['sensitivity'] = stream.sensitivity + f.attrs['time'] = time.time() + self._running <<= True - if not stream.isRunning(): - stream.start() + if not stream.isRunning(): + stream.start() + print('Starting record....') + stream.addCallback(self._aCallback, AvType.audio_input) + if stream.hasVideo(): stream.addCallback(self._aCallback, AvType.audio_input) - if stream.hasVideo(): - stream.addCallback(self._aCallback, AvType.audio_input) + if self._wait: with self._running_cond: + print('Stop recording with CTRL-C') try: - print('Starting record....') while self._running: self._running_cond.wait() except KeyboardInterrupt: print("Keyboard interrupt on record") self._running <<= False - stream.removeCallback(self._aCallback, AvType.audio_input) - if stream.hasVideo(): - stream.removeCallback(self._vCallback, AvType.video_input) - f['video_frame_positions'] = self._video_frame_positions - - print('\nEnding record') - - def stop(self): + def __exit__(self, type, value, traceback): self._running <<= False - with self._running_cond: - self._running_cond.notify() + stream = self._stream + stream.removeCallback(self._aCallback, AvType.audio_input) + if stream.hasVideo(): + stream.removeCallback(self._vCallback, AvType.video_input) + f['video_frame_positions'] = self._video_frame_positions + + self._f.close() + print('\nEnding record') + if self._deleteFile: + try: + os.remove(self._fn) + except Exception as e: + print(f'Error deleting file: {self._fn}') + + def _aCallback(self, frames, aframe): curT = self._aframeno()*self.blocksize/self.samplerate + recstatus = RecordStatus( + curT = curT, + done = False) + if self._progressCallback is not None: + self._progressCallback(recstatus) + curT_rounded_to_seconds = int(curT) if curT_rounded_to_seconds > self._curT_rounded_to_seconds: self._curT_rounded_to_seconds = curT_rounded_to_seconds @@ -112,7 +152,10 @@ class Recording: self._running <<= False with self._running_cond: self._running_cond.notify() - return + if self._progressCallback is not None: + recstatus.done = True + self._progressCallback(recstatus) + return self._ad.resize(self._aframeno()+1, axis=0) self._ad[self._aframeno(), :, :] = frames @@ -129,4 +172,6 @@ class Recording: if __name__ == '__main__': stream = AvStream() rec = Recording('test', stream, 5) + with rec(wait=True): + sleep rec.start() diff --git a/scripts/lasp_record b/scripts/lasp_record index ba09164..743bb0e 100755 --- a/scripts/lasp_record +++ b/scripts/lasp_record @@ -47,7 +47,8 @@ stream = AvStream(input_device, config) rec = Recording(args.filename, stream, args.duration) -rec.start() +with rec: + pass print('Stopping stream...') stream.stop()