Updated API for recording using context manager. Easy for Recording, both in GUI and in CLI

This commit is contained in:
Anne de Jong 2019-12-23 12:25:37 +01:00
parent b88ec2904c
commit 705f77858d
3 changed files with 100 additions and 45 deletions

View File

@ -55,6 +55,7 @@ class AvStream:
self.daqconfig = daqconfig self.daqconfig = daqconfig
self._device = device self._device = device
self.avtype = avtype self.avtype = avtype
self.duplex_mode = daqconfig.duplex_mode
# Determine highest input channel number # Determine highest input channel number
channelconfigs = daqconfig.en_input_channels channelconfigs = daqconfig.en_input_channels
@ -125,7 +126,9 @@ class AvStream:
""" """
Returns the current number of installed callbacks 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): def addCallback(self, cb: callable, cbtype: AvType):
""" """
@ -196,9 +199,15 @@ class AvStream:
output_signal = None output_signal = None
with self._callbacklock: with self._callbacklock:
for cb in self._callbacks[AvType.audio_input]: 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]: 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 return output_signal, 0 if self._running else 1
def stop(self): def stop(self):

View File

@ -8,12 +8,19 @@ from .lasp_atomic import Atomic
from threading import Condition from threading import Condition
from .lasp_avstream import AvType, AvStream from .lasp_avstream import AvType, AvStream
import h5py import h5py
import dataclasses
import os
import time import time
@dataclasses.dataclass
class RecordStatus:
curT: float
done: bool
class Recording: class Recording:
def __init__(self, fn, stream, rectime=None): def __init__(self, fn, stream, rectime=None, wait = True,
progressCallback=None):
""" """
Args: Args:
@ -39,67 +46,100 @@ class Recording:
self._aframeno = Atomic(0) self._aframeno = Atomic(0)
self._vframeno = 0 self._vframeno = 0
def start(self): self._progressCallback = progressCallback
stream = self._stream self._wait = wait
with h5py.File(self._fn, 'w') as f: self._f = h5py.File(self._fn, 'w')
self._ad = f.create_dataset('audio', self._deleteFile = False
(1, stream.blocksize, stream.nchannels),
dtype=stream.numpy_dtype, def setDelete(self, val: bool):
maxshape=(None, stream.blocksize, self._deleteFile = val
stream.nchannels),
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' 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['samplerate'] = stream.samplerate
f.attrs['nchannels'] = stream.nchannels f.attrs['nchannels'] = stream.nchannels
f.attrs['blocksize'] = stream.blocksize f.attrs['blocksize'] = stream.blocksize
f.attrs['sensitivity'] = stream.sensitivity f.attrs['sensitivity'] = stream.sensitivity
f.attrs['time'] = time.time() f.attrs['time'] = time.time()
self._running <<= True self._running <<= True
# Videothread is going to start
if not stream.isRunning(): if not stream.isRunning():
stream.start() stream.start()
print('Starting record....')
stream.addCallback(self._aCallback, AvType.audio_input)
if stream.hasVideo():
stream.addCallback(self._aCallback, AvType.audio_input) stream.addCallback(self._aCallback, AvType.audio_input)
if stream.hasVideo():
stream.addCallback(self._aCallback, AvType.audio_input)
if self._wait:
with self._running_cond: with self._running_cond:
print('Stop recording with CTRL-C')
try: try:
print('Starting record....')
while self._running: while self._running:
self._running_cond.wait() self._running_cond.wait()
except KeyboardInterrupt: except KeyboardInterrupt:
print("Keyboard interrupt on record") print("Keyboard interrupt on record")
self._running <<= False self._running <<= False
stream.removeCallback(self._aCallback, AvType.audio_input)
if stream.hasVideo(): def __exit__(self, type, value, traceback):
stream.removeCallback(self._vCallback, AvType.video_input)
f['video_frame_positions'] = self._video_frame_positions
print('\nEnding record')
def stop(self):
self._running <<= False self._running <<= False
with self._running_cond: stream = self._stream
self._running_cond.notify() 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): def _aCallback(self, frames, aframe):
curT = self._aframeno()*self.blocksize/self.samplerate 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) curT_rounded_to_seconds = int(curT)
if curT_rounded_to_seconds > self._curT_rounded_to_seconds: if curT_rounded_to_seconds > self._curT_rounded_to_seconds:
self._curT_rounded_to_seconds = curT_rounded_to_seconds self._curT_rounded_to_seconds = curT_rounded_to_seconds
@ -112,7 +152,10 @@ class Recording:
self._running <<= False self._running <<= False
with self._running_cond: with self._running_cond:
self._running_cond.notify() 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.resize(self._aframeno()+1, axis=0)
self._ad[self._aframeno(), :, :] = frames self._ad[self._aframeno(), :, :] = frames
@ -129,4 +172,6 @@ class Recording:
if __name__ == '__main__': if __name__ == '__main__':
stream = AvStream() stream = AvStream()
rec = Recording('test', stream, 5) rec = Recording('test', stream, 5)
with rec(wait=True):
sleep
rec.start() rec.start()

View File

@ -47,7 +47,8 @@ stream = AvStream(input_device,
config) config)
rec = Recording(args.filename, stream, args.duration) rec = Recording(args.filename, stream, args.duration)
rec.start() with rec:
pass
print('Stopping stream...') print('Stopping stream...')
stream.stop() stream.stop()