Playing a sine wave works, recording seems to work, although we have not checked what is actually recorded. For that we now switch to ACME!
This commit is contained in:
parent
b031dfb280
commit
57fe4e6b7c
@ -5,13 +5,14 @@ Author: J.A. de Jong
|
|||||||
Description: Controlling an audio stream in a different process.
|
Description: Controlling an audio stream in a different process.
|
||||||
"""
|
"""
|
||||||
#import cv2 as cv
|
#import cv2 as cv
|
||||||
|
from .lasp_multiprocessingpatch import apply_patch
|
||||||
|
apply_patch()
|
||||||
|
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
import time, logging, signal
|
import time, logging, signal
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from enum import unique, Enum, auto
|
from enum import unique, Enum, auto
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from .lasp_multiprocessingpatch import apply_patch
|
|
||||||
apply_patch()
|
|
||||||
|
|
||||||
from .lasp_atomic import Atomic
|
from .lasp_atomic import Atomic
|
||||||
from .lasp_common import AvType
|
from .lasp_common import AvType
|
||||||
@ -127,6 +128,7 @@ class AudioStream:
|
|||||||
blocksize = self.daq.nFramesPerBlock,
|
blocksize = self.daq.nFramesPerBlock,
|
||||||
dtype = self.daq.getNumpyDataType()
|
dtype = self.daq.getNumpyDataType()
|
||||||
)
|
)
|
||||||
|
self.running <<= True
|
||||||
|
|
||||||
|
|
||||||
def streamCallback(self, indata, outdata, nframes):
|
def streamCallback(self, indata, outdata, nframes):
|
||||||
@ -134,7 +136,12 @@ class AudioStream:
|
|||||||
This is called (from a separate thread) for each block
|
This is called (from a separate thread) for each block
|
||||||
of audio data.
|
of audio data.
|
||||||
"""
|
"""
|
||||||
return self.processCallback(self, indata, outdata)
|
if not self.running():
|
||||||
|
return 1
|
||||||
|
rv = self.processCallback(self, indata, outdata)
|
||||||
|
if rv != 0:
|
||||||
|
self.running <<= False
|
||||||
|
return rv
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
@ -187,7 +194,7 @@ class AvStreamProcess(mp.Process):
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
msg, data = self.pipe.recv()
|
msg, data = self.pipe.recv()
|
||||||
logging.debug(f'Obtained message {msg}')
|
logging.debug(f'Streamprocess obtained message {msg}')
|
||||||
|
|
||||||
if msg == StreamMsg.activateSiggen:
|
if msg == StreamMsg.activateSiggen:
|
||||||
self.siggen_activated <<= True
|
self.siggen_activated <<= True
|
||||||
@ -207,12 +214,14 @@ class AvStreamProcess(mp.Process):
|
|||||||
return
|
return
|
||||||
|
|
||||||
elif msg == StreamMsg.getStreamMetaData:
|
elif msg == StreamMsg.getStreamMetaData:
|
||||||
avtype = data
|
avtype, = data
|
||||||
stream = self.streams[avtype]
|
stream = self.streams[avtype]
|
||||||
if stream is not None:
|
if stream is not None:
|
||||||
self.sendPipe(StreamMsg.streamMetaData, avtype, stream.streammetadata)
|
self.sendPipeAndAllQueues(StreamMsg.streamMetaData, avtype,
|
||||||
|
stream.streammetadata)
|
||||||
else:
|
else:
|
||||||
self.sendPipe(StreamMsg.streamMetaData, avtype, None)
|
self.sendPipeAndAllQueues(StreamMsg.streamMetaData, avtype,
|
||||||
|
None)
|
||||||
|
|
||||||
elif msg == StreamMsg.startStream:
|
elif msg == StreamMsg.startStream:
|
||||||
avtype, daqconfig = data
|
avtype, daqconfig = data
|
||||||
@ -254,7 +263,8 @@ class AvStreamProcess(mp.Process):
|
|||||||
stream.stop()
|
stream.stop()
|
||||||
self.sendPipeAndAllQueues(StreamMsg.streamStopped, stream.avtype)
|
self.sendPipeAndAllQueues(StreamMsg.streamStopped, stream.avtype)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.sendPipeAndAllQueues(StreamMsg.streamError, stream.avtype, "Error occured in stopping stream: {str(e)}")
|
self.sendPipeAndAllQueues(StreamMsg.streamError, stream.avtype,
|
||||||
|
"Error occured in stopping stream: {str(e)}")
|
||||||
self.streams[avtype] = None
|
self.streams[avtype] = None
|
||||||
|
|
||||||
|
|
||||||
@ -301,7 +311,7 @@ class AvStreamProcess(mp.Process):
|
|||||||
else:
|
else:
|
||||||
avtype = (avtype,)
|
avtype = (avtype,)
|
||||||
for t in avtype:
|
for t in avtype:
|
||||||
if self.streams[t] is not None:
|
if self.streams[t] is not None and self.streams[t].running():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -321,27 +331,34 @@ class AvStreamProcess(mp.Process):
|
|||||||
def streamCallback(self, audiostream, indata, outdata):
|
def streamCallback(self, audiostream, indata, outdata):
|
||||||
"""This is called (from a separate thread) for each audio block."""
|
"""This is called (from a separate thread) for each audio block."""
|
||||||
# logging.debug('streamCallback()')
|
# logging.debug('streamCallback()')
|
||||||
|
if outdata is not None:
|
||||||
if self.siggen_activated:
|
if self.siggen_activated():
|
||||||
# logging.debug('siggen_activated')
|
if not self.outq.empty():
|
||||||
if self.outq.empty():
|
newdata = self.outq.get()
|
||||||
|
if newdata.shape[0] != outdata.shape[0] or newdata.ndim != 1:
|
||||||
|
msgtxt = 'Invalid output data obtained from queue'
|
||||||
|
logging.fatal(msgtxt)
|
||||||
|
self.sendPipeAndAllQueues(StreamMsg.streamFatalError,
|
||||||
|
audiostream.avtype,
|
||||||
|
msgtxt
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
outdata[:, :] = newdata[:, None]
|
||||||
|
else:
|
||||||
outdata[:, :] = 0
|
outdata[:, :] = 0
|
||||||
msgtxt = 'Output signal buffer underflow'
|
msgtxt = 'Output signal buffer underflow'
|
||||||
|
logging.error(msgtxt)
|
||||||
self.sendPipeAndAllQueues(StreamMsg.streamError,
|
self.sendPipeAndAllQueues(StreamMsg.streamError,
|
||||||
audiostream.avtype,
|
audiostream.avtype,
|
||||||
msgtxt)
|
msgtxt)
|
||||||
|
|
||||||
|
# Siggen not activated
|
||||||
else:
|
else:
|
||||||
newdata = self.outq.get()
|
logging.debug('siggen not activated')
|
||||||
if newdata.shape[0] != outdata.shape[0] or newdata.ndim != 1:
|
outdata[:, :] = 0
|
||||||
self.sendPipeAndAllQueues(StreamMsg.streamFatalError,
|
|
||||||
audiostream.avtype,
|
|
||||||
'Invalid output data obtained from queue')
|
|
||||||
return 1
|
|
||||||
outdata[:, :] = newdata[:, None]
|
|
||||||
|
|
||||||
if indata is not None:
|
if indata is not None:
|
||||||
self.putAllInQueues(StreamMsg.streamData, audiostream.avtype,
|
self.putAllInQueues(StreamMsg.streamData, indata)
|
||||||
indata)
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -419,10 +436,13 @@ class StreamManager:
|
|||||||
Handle messages that are still on the pipe.
|
Handle messages that are still on the pipe.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# logging.debug('StreamManager::handleMessages()')
|
||||||
while self.pipe.poll():
|
while self.pipe.poll():
|
||||||
msg, data = self.pipe.recv()
|
msg, data = self.pipe.recv()
|
||||||
|
logging.debug(f'StreamManager obtained message {msg}')
|
||||||
if msg == StreamMsg.streamStarted:
|
if msg == StreamMsg.streamStarted:
|
||||||
avtype, streammetadata = data
|
avtype, streammetadata = data
|
||||||
|
# logging.debug(f'{avtype}, {streammetadata}')
|
||||||
self.streamstatus[avtype].lastStatus = msg
|
self.streamstatus[avtype].lastStatus = msg
|
||||||
self.streamstatus[avtype].errorTxt = None
|
self.streamstatus[avtype].errorTxt = None
|
||||||
self.streamstatus[avtype].streammetadata = streammetadata
|
self.streamstatus[avtype].streammetadata = streammetadata
|
||||||
@ -439,6 +459,11 @@ class StreamManager:
|
|||||||
self.streamstatus[avtype].lastStatus = msg
|
self.streamstatus[avtype].lastStatus = msg
|
||||||
self.streamstatus[avtype].errorTxt = None
|
self.streamstatus[avtype].errorTxt = None
|
||||||
|
|
||||||
|
elif msg == StreamMsg.streamFatalError:
|
||||||
|
avtype, errorTxt = data
|
||||||
|
logging.critical(f'Streamprocess fatal error: {errorTxt}')
|
||||||
|
self.cleanup()
|
||||||
|
|
||||||
elif msg == StreamMsg.streamMetaData:
|
elif msg == StreamMsg.streamMetaData:
|
||||||
avtype, metadata = data
|
avtype, metadata = data
|
||||||
self.streamstatus[avtype].streammetadata = metadata
|
self.streamstatus[avtype].streammetadata = metadata
|
||||||
@ -454,10 +479,10 @@ class StreamManager:
|
|||||||
|
|
||||||
def getStreamStatus(self, avtype: AvType):
|
def getStreamStatus(self, avtype: AvType):
|
||||||
"""
|
"""
|
||||||
Returns the current stream Status.
|
Sends a request for the stream status over the pipe, for given AvType
|
||||||
"""
|
"""
|
||||||
self.handleMessages()
|
self.handleMessages()
|
||||||
return self.streamstatus[avtype]
|
self.sendPipe(StreamMsg.getStreamMetaData, avtype)
|
||||||
|
|
||||||
def getOutputQueue(self):
|
def getOutputQueue(self):
|
||||||
"""
|
"""
|
||||||
@ -465,6 +490,7 @@ class StreamManager:
|
|||||||
|
|
||||||
Note, should (of course) only be used by one signal generator at the time!
|
Note, should (of course) only be used by one signal generator at the time!
|
||||||
"""
|
"""
|
||||||
|
self.handleMessages()
|
||||||
return self.outq
|
return self.outq
|
||||||
|
|
||||||
def activateSiggen(self):
|
def activateSiggen(self):
|
||||||
@ -504,15 +530,28 @@ class StreamManager:
|
|||||||
"""Returns the current number of installed listeners."""
|
"""Returns the current number of installed listeners."""
|
||||||
return len(self.in_qlist)
|
return len(self.in_qlist)
|
||||||
|
|
||||||
def startStream(self, avtype: AvType, daqconfig: DaqConfiguration):
|
def startStream(self, avtype: AvType, daqconfig: DaqConfiguration,
|
||||||
|
wait=False):
|
||||||
"""
|
"""
|
||||||
Start the stream, which means the callbacks are called with stream
|
Start the stream, which means the callbacks are called with stream
|
||||||
data (audio/video)
|
data (audio/video)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
wait: Wait until the stream starts talking before returning from
|
||||||
|
this function.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
logging.debug('Starting stream...')
|
logging.debug('Starting stream...')
|
||||||
self.sendPipe(StreamMsg.startStream, avtype, daqconfig)
|
|
||||||
self.handleMessages()
|
self.handleMessages()
|
||||||
|
self.sendPipe(StreamMsg.startStream, avtype, daqconfig)
|
||||||
|
if wait:
|
||||||
|
# Wait for a message to come into the pipe
|
||||||
|
while True:
|
||||||
|
if self.pipe.poll():
|
||||||
|
self.handleMessages()
|
||||||
|
if self.streamstatus[avtype].lastStatus != StreamMsg.streamStopped:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
def stopStream(self, avtype: AvType):
|
def stopStream(self, avtype: AvType):
|
||||||
self.handleMessages()
|
self.handleMessages()
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os, platform
|
||||||
import platform
|
|
||||||
import shelve
|
import shelve
|
||||||
import sys
|
import sys
|
||||||
import appdirs
|
import appdirs
|
||||||
|
@ -48,8 +48,6 @@ import os, time, wave, logging
|
|||||||
from .lasp_common import SIQtys, Qty, getFreq
|
from .lasp_common import SIQtys, Qty, getFreq
|
||||||
from .device import DaqChannel
|
from .device import DaqChannel
|
||||||
from .wrappers import AvPowerSpectra, Window, PowerSpectra
|
from .wrappers import AvPowerSpectra, Window, PowerSpectra
|
||||||
logger = logging.Logger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getSampWidth(dtype):
|
def getSampWidth(dtype):
|
||||||
@ -242,7 +240,7 @@ class Measurement:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
# If quantity data is not available, this is an 'old'
|
# If quantity data is not available, this is an 'old'
|
||||||
# measurement file.
|
# measurement file.
|
||||||
logger.debug('Physical quantity data not available in measurement file. Assuming {SIQtys.default}')
|
logging.debug('Physical quantity data not available in measurement file. Assuming {SIQtys.default}')
|
||||||
self._qtys = [SIQtys.default for i in range(self.nchannels)]
|
self._qtys = [SIQtys.default for i in range(self.nchannels)]
|
||||||
|
|
||||||
def setAttribute(self, atrname, value):
|
def setAttribute(self, atrname, value):
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3.8
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Read data from stream and record sound and video at the same time
|
Read data from stream and record sound and video at the same time
|
||||||
"""
|
"""
|
||||||
import dataclasses
|
import dataclasses, logging, os, time, h5py
|
||||||
import logging
|
from .lasp_avstream import StreamManager, StreamMetaData, StreamMsg
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import h5py
|
|
||||||
from .lasp_avstream import StreamMsg, StreamManager
|
|
||||||
from .lasp_common import AvType
|
from .lasp_common import AvType
|
||||||
|
|
||||||
|
|
||||||
@ -40,7 +36,8 @@ class Recording:
|
|||||||
if ext not in fn:
|
if ext not in fn:
|
||||||
fn += ext
|
fn += ext
|
||||||
|
|
||||||
self._stream = stream
|
self.smgr = streammgr
|
||||||
|
self.metadata = None
|
||||||
|
|
||||||
self.rectime = rectime
|
self.rectime = rectime
|
||||||
self._fn = fn
|
self._fn = fn
|
||||||
@ -61,66 +58,26 @@ class Recording:
|
|||||||
# when a recording is canceled.
|
# when a recording is canceled.
|
||||||
self._deleteFile = False
|
self._deleteFile = False
|
||||||
|
|
||||||
f = self._f
|
|
||||||
nchannels = len(stream.input_channel_names)
|
|
||||||
|
|
||||||
# Input queue
|
|
||||||
self.inq = stream.addListener()
|
|
||||||
|
|
||||||
# Start the stream, if it is not running
|
|
||||||
try:
|
try:
|
||||||
if not stream.isRunning():
|
# Input queue
|
||||||
metadata = stream.start()
|
self.inq = streammgr.addListener()
|
||||||
else:
|
|
||||||
metadata = stream.getStreamMetaData()
|
except RuntimeError:
|
||||||
except:
|
|
||||||
# Cleanup stuff, something is going wrong when starting the stream
|
# Cleanup stuff, something is going wrong when starting the stream
|
||||||
try:
|
try:
|
||||||
f.close()
|
self._f.close()
|
||||||
except:
|
except Exception as e:
|
||||||
pass
|
logging.error(
|
||||||
|
'Error preliminary closing measurement file {fn}: {str(e)}')
|
||||||
|
|
||||||
self.__deleteFile()
|
self.__deleteFile()
|
||||||
self.blocksize = metadata['blocksize']
|
raise
|
||||||
self.samplerate = metadata['samplerate']
|
|
||||||
self.dtype = metadata['dtype']
|
|
||||||
|
|
||||||
# The 'Audio' dataset as specified in lasp_measurement, where data is
|
# Try to obtain stream metadata
|
||||||
# send to. We use gzip as compression, this gives moderate a moderate
|
streammgr.getStreamStatus(AvType.audio_input)
|
||||||
# compression to the data.
|
streammgr.getStreamStatus(AvType.audio_duplex)
|
||||||
self._ad = f.create_dataset('audio',
|
|
||||||
(1, self.blocksize, nchannels),
|
|
||||||
dtype=self.dtype,
|
|
||||||
maxshape=(
|
|
||||||
None, # This means, we can add blocks
|
|
||||||
# indefinitely
|
|
||||||
self.blocksize,
|
|
||||||
nchannels),
|
|
||||||
compression='gzip'
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO: This piece of code is not up-to-date and should be changed at a
|
self._ad = None
|
||||||
# later instance once we really want to record video simultaneously
|
|
||||||
# with audio.
|
|
||||||
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'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Set the bunch of attributes
|
|
||||||
f.attrs['samplerate'] = self.samplerate
|
|
||||||
f.attrs['nchannels'] = nchannels
|
|
||||||
f.attrs['blocksize'] = self.blocksize
|
|
||||||
f.attrs['sensitivity'] = stream.input_sensitivity
|
|
||||||
f.attrs['channel_names'] = stream.input_channel_names
|
|
||||||
f.attrs['time'] = time.time()
|
|
||||||
|
|
||||||
# Measured quantities
|
|
||||||
f.attrs['qtys'] = [qty.to_json() for qty in stream.input_qtys]
|
|
||||||
|
|
||||||
logging.debug('Starting record....')
|
logging.debug('Starting record....')
|
||||||
# TODO: Fix this later when we want video
|
# TODO: Fix this later when we want video
|
||||||
@ -139,6 +96,92 @@ class Recording:
|
|||||||
finally:
|
finally:
|
||||||
self.finish()
|
self.finish()
|
||||||
|
|
||||||
|
def handleQueue(self):
|
||||||
|
"""
|
||||||
|
This method should be called to grab data from the input queue, which
|
||||||
|
is filled by the stream, and put it into a file. It should be called at
|
||||||
|
a regular interval to prevent overflowing of the queue. It is called
|
||||||
|
within the start() method of the recording, if block is set to True.
|
||||||
|
Otherwise, it should be called from its parent at regular intervals.
|
||||||
|
For example, in Qt this can be done using a QTimer.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
while self.inq.qsize() > 0:
|
||||||
|
msg, data = self.inq.get()
|
||||||
|
if msg == StreamMsg.streamData:
|
||||||
|
samples, = data
|
||||||
|
self.__addTimeData(samples)
|
||||||
|
elif msg == StreamMsg.streamStarted:
|
||||||
|
logging.debug(f'handleQueue obtained message {msg}')
|
||||||
|
avtype, metadata = data
|
||||||
|
if metadata is None:
|
||||||
|
raise RuntimeError('BUG: no stream metadata')
|
||||||
|
self.processStreamMetaData(metadata)
|
||||||
|
elif msg == StreamMsg.streamMetaData:
|
||||||
|
logging.debug(f'handleQueue obtained message {msg}')
|
||||||
|
avtype, metadata = data
|
||||||
|
if metadata is not None:
|
||||||
|
self.processStreamMetaData(metadata)
|
||||||
|
else:
|
||||||
|
logging.debug(f'handleQueue obtained message {msg}')
|
||||||
|
# An error occured, we do not remove the file, but we stop.
|
||||||
|
self.stop = True
|
||||||
|
logging.debug(f'Stream message: {msg}. Recording stopped unexpectedly')
|
||||||
|
raise RuntimeError('Recording stopped unexpectedly')
|
||||||
|
|
||||||
|
|
||||||
|
def processStreamMetaData(self, md: StreamMetaData):
|
||||||
|
"""
|
||||||
|
Stream metadata has been catched. This is used to set all metadata in
|
||||||
|
the measurement file
|
||||||
|
|
||||||
|
"""
|
||||||
|
logging.debug('Recording::processStreamMetaData()')
|
||||||
|
# The 'Audio' dataset as specified in lasp_measurement, where data is
|
||||||
|
# send to. We use gzip as compression, this gives moderate a moderate
|
||||||
|
# compression to the data.
|
||||||
|
f = self._f
|
||||||
|
blocksize = md.blocksize
|
||||||
|
nchannels = len(md.in_ch)
|
||||||
|
self._ad = f.create_dataset('audio',
|
||||||
|
(1, blocksize, nchannels),
|
||||||
|
dtype=md.dtype,
|
||||||
|
maxshape=(
|
||||||
|
None, # This means, we can add blocks
|
||||||
|
# indefinitely
|
||||||
|
blocksize,
|
||||||
|
nchannels),
|
||||||
|
compression='gzip'
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: This piece of code is not up-to-date and should be changed at a
|
||||||
|
# later instance once we really want to record video simultaneously
|
||||||
|
# with audio.
|
||||||
|
# if smgr.hasVideo():
|
||||||
|
# video_x, video_y = smgr.video_x, smgr.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'
|
||||||
|
# )
|
||||||
|
|
||||||
|
# Set the bunch of attributes
|
||||||
|
f.attrs['samplerate'] = md.fs
|
||||||
|
f.attrs['nchannels'] = nchannels
|
||||||
|
f.attrs['blocksize'] = blocksize
|
||||||
|
f.attrs['sensitivity'] = [ch.sensitivity for ch in md.in_ch]
|
||||||
|
f.attrs['channel_names'] = [ch.channel_name for ch in md.in_ch]
|
||||||
|
f.attrs['time'] = time.time()
|
||||||
|
self.blocksize = blocksize
|
||||||
|
self.fs = md.fs
|
||||||
|
|
||||||
|
# Measured physical quantity metadata
|
||||||
|
f.attrs['qtys'] = [ch.qty.to_json() for ch in md.in_ch]
|
||||||
|
self.metadata = md
|
||||||
|
|
||||||
def setDelete(self, val: bool):
|
def setDelete(self, val: bool):
|
||||||
"""
|
"""
|
||||||
Set the delete flag. If set, measurement file is deleted at the end of
|
Set the delete flag. If set, measurement file is deleted at the end of
|
||||||
@ -153,16 +196,18 @@ class Recording:
|
|||||||
remove the queue from the stream, etc.
|
remove the queue from the stream, etc.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
stream = self._stream
|
logging.debug('Recording::finish()')
|
||||||
|
smgr = self.smgr
|
||||||
|
|
||||||
# TODO: Fix when video
|
# TODO: Fix when video
|
||||||
# if stream.hasVideo():
|
# if smgr.hasVideo():
|
||||||
# stream.removeCallback(self._vCallback, AvType.video_input)
|
# smgr.removeCallback(self._vCallback, AvType.video_input)
|
||||||
# self._f['video_frame_positions'] = self._video_frame_positions
|
# self._f['video_frame_positions'] = self._video_frame_positions
|
||||||
|
|
||||||
try:
|
try:
|
||||||
stream.removeListener(self.inq)
|
smgr.removeListener(self.inq)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f'Could not remove queue from stream: {e}')
|
logging.error(f'Could not remove queue from smgr: {e}')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Close the recording file
|
# Close the recording file
|
||||||
@ -181,39 +226,29 @@ class Recording:
|
|||||||
try:
|
try:
|
||||||
os.remove(self._fn)
|
os.remove(self._fn)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.debug(f'Error deleting file: {self._fn}')
|
logging.error(f'Error deleting file: {self._fn}')
|
||||||
|
|
||||||
def handleQueue(self):
|
|
||||||
"""
|
|
||||||
This method should be called to grab data from the input queue, which
|
|
||||||
is filled by the stream, and put it into a file. It should be called at
|
|
||||||
a regular interval to prevent overflowing of the queue. It is called
|
|
||||||
within the start() method of the recording, if block is set to True.
|
|
||||||
Otherwise, it should be called from its parent at regular intervals.
|
|
||||||
For example, in Qt this can be done using a QTimer.
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
while self.inq.qsize() > 0:
|
|
||||||
msg, data = self.inq.get()
|
|
||||||
if msg == StreamMsg.streamData:
|
|
||||||
self.__addTimeData(data)
|
|
||||||
elif msg == StreamMsg.streamStarted:
|
|
||||||
pass
|
|
||||||
elif msg == StreamMsg.streamMetaData:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# An error occured, we do not remove the file, but we stop.
|
|
||||||
self.stop = True
|
|
||||||
|
|
||||||
def __addTimeData(self, indata):
|
def __addTimeData(self, indata):
|
||||||
"""
|
"""
|
||||||
Called by handleQueue() and adds new time data to the storage file.
|
Called by handleQueue() and adds new time data to the storage file.
|
||||||
"""
|
"""
|
||||||
|
# logging.debug('Recording::__addTimeData()')
|
||||||
|
|
||||||
|
if self.stop:
|
||||||
|
# Stop flag is raised. We stop recording here.
|
||||||
|
return
|
||||||
|
|
||||||
# The current time that is recorded and stored into the file, without
|
# The current time that is recorded and stored into the file, without
|
||||||
# the new data
|
# the new data
|
||||||
curT = self._ablockno*self.blocksize/self.samplerate
|
if not self.metadata:
|
||||||
|
# We obtained stream data, but metadata is not yet available.
|
||||||
|
# Therefore, we request it explicitly and then we return
|
||||||
|
logging.info('Requesting stream metadata')
|
||||||
|
self.smgr.getStreamStatus(AvType.audio_input)
|
||||||
|
self.smgr.getStreamStatus(AvType.audio_duplex)
|
||||||
|
return
|
||||||
|
|
||||||
|
curT = self._ablockno*self.blocksize/self.fs
|
||||||
recstatus = RecordStatus(
|
recstatus = RecordStatus(
|
||||||
curT=curT,
|
curT=curT,
|
||||||
done=False)
|
done=False)
|
||||||
|
@ -1,33 +1,36 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3.8
|
||||||
import sys, logging, os, argparse
|
import sys, logging, os, argparse
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
FORMAT = "[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s"
|
||||||
import multiprocessing
|
parser = argparse.ArgumentParser(
|
||||||
from lasp.lasp_multiprocessingpatch import apply_patch
|
description='Acquire data and store to a measurement file.'
|
||||||
|
)
|
||||||
from lasp.device import DaqConfigurations
|
parser.add_argument('filename', type=str,
|
||||||
from lasp.lasp_avstream import AvStream, AvType
|
|
||||||
from lasp.lasp_record import Recording
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
multiprocessing.set_start_method('forkserver', force=True)
|
|
||||||
apply_patch()
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description='Acquire data and store a measurement file'
|
|
||||||
)
|
|
||||||
parser.add_argument('filename', type=str,
|
|
||||||
help='File name to record to.'
|
help='File name to record to.'
|
||||||
' Extension is automatically added.')
|
' Extension is automatically added.')
|
||||||
parser.add_argument('--duration', '-d', type=float,
|
parser.add_argument('--duration', '-d', type=float,
|
||||||
help='The recording duration in [s]')
|
help='The recording duration in [s]')
|
||||||
|
|
||||||
device_help = 'DAQ Device to record from'
|
device_help = 'DAQ Device to record from'
|
||||||
parser.add_argument('--input-daq', '-i', help=device_help, type=str,
|
parser.add_argument('--input-daq', '-i', help=device_help, type=str,
|
||||||
default='Default')
|
default='Default')
|
||||||
|
parser.add_argument('--log', '-l',
|
||||||
args = parser.parse_args()
|
help='Specify log level [info, debug, warning, ...]',
|
||||||
|
type=str, default='info')
|
||||||
|
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
numeric_level = getattr(logging, args.log.upper(), None)
|
||||||
|
if not isinstance(numeric_level, int):
|
||||||
|
raise ValueError('Invalid log level: %s' % args.loglevel)
|
||||||
|
logging.basicConfig(format=FORMAT, level=numeric_level)
|
||||||
|
|
||||||
|
import multiprocessing
|
||||||
|
from lasp.device import DaqConfigurations
|
||||||
|
from lasp import AvType, StreamManager, Recording# configureLogging
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
try:
|
||||||
|
streammgr = StreamManager()
|
||||||
configs = DaqConfigurations.loadConfigs()
|
configs = DaqConfigurations.loadConfigs()
|
||||||
|
|
||||||
config_keys = [key for key in configs.keys()]
|
config_keys = [key for key in configs.keys()]
|
||||||
@ -38,6 +41,7 @@ if __name__ == '__main__':
|
|||||||
try:
|
try:
|
||||||
daqindex = int(choosen_index)
|
daqindex = int(choosen_index)
|
||||||
except:
|
except:
|
||||||
|
print('Invalid configuration number. Exiting.')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
choosen_key = config_keys[daqindex]
|
choosen_key = config_keys[daqindex]
|
||||||
@ -45,17 +49,19 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
print(f'Choosen configuration: {choosen_key}')
|
print(f'Choosen configuration: {choosen_key}')
|
||||||
|
|
||||||
try:
|
streammgr.startStream(AvType.audio_input, config, wait=True)
|
||||||
stream = AvStream(
|
rec = Recording(args.filename, streammgr, args.duration)
|
||||||
AvType.audio_input,
|
|
||||||
config)
|
streammgr.stopStream(AvType.audio_output)
|
||||||
# stream.start()
|
|
||||||
rec = Recording(args.filename, stream, args.duration)
|
|
||||||
# input('Stream started, press any key to start record')
|
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
stream.cleanup()
|
streammgr.cleanup()
|
||||||
del stream
|
del stream
|
||||||
except NameError:
|
except NameError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
multiprocessing.set_start_method('forkserver', force=True)
|
||||||
|
|
||||||
|
main(args)
|
||||||
|
@ -1,27 +1,35 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
import argparse
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import sys, logging, os, argparse
|
import sys, logging, os, argparse
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Play a sine wave'
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument('--freq', '-f', help='Sine frequency [Hz]', type=float,
|
||||||
|
default=1000.)
|
||||||
|
|
||||||
|
parser.add_argument('--log', '-l',
|
||||||
|
help='Specify log level [info, debug, warning, ...]',
|
||||||
|
type=str, default='info')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
numeric_level = getattr(logging, args.log.upper(), None)
|
||||||
|
if not isinstance(numeric_level, int):
|
||||||
|
raise ValueError('Invalid log level: %s' % args.loglevel)
|
||||||
|
|
||||||
|
FORMAT = "[%(levelname)s %(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s"
|
||||||
|
logging.basicConfig(format=FORMAT, level=numeric_level)
|
||||||
|
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
from lasp.lasp_multiprocessingpatch import apply_patch
|
from lasp import (StreamManager, AvType, Siggen, SignalType, SiggenData)
|
||||||
from lasp.lasp_avstream import StreamManager
|
|
||||||
from lasp.lasp_common import AvType
|
|
||||||
from lasp.lasp_siggen import Siggen, SignalType, SiggenData
|
|
||||||
from lasp.device import DaqConfigurations
|
from lasp.device import DaqConfigurations
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
multiprocessing.set_start_method('forkserver', force=True)
|
multiprocessing.set_start_method('forkserver', force=True)
|
||||||
parser = argparse.ArgumentParser(
|
logging.info(f'Playing frequency {args.freq} [Hz]')
|
||||||
description='Play a sine wave'
|
|
||||||
)
|
|
||||||
device_help = 'DAQ Device to play to'
|
|
||||||
parser.add_argument('--device', '-d', help=device_help, type=str,
|
|
||||||
default='Default')
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
configs = DaqConfigurations.loadConfigs()
|
configs = DaqConfigurations.loadConfigs()
|
||||||
|
|
||||||
@ -33,6 +41,7 @@ if __name__ == '__main__':
|
|||||||
try:
|
try:
|
||||||
daqindex = int(choosen_index)
|
daqindex = int(choosen_index)
|
||||||
except:
|
except:
|
||||||
|
print('Invalid configuration number. Exiting.')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
@ -47,12 +56,12 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
siggendata = SiggenData(
|
siggendata = SiggenData(
|
||||||
fs=48e3,
|
fs=48e3,
|
||||||
nframes_per_block=2048,
|
nframes_per_block=1024,
|
||||||
dtype=np.dtype(np.int16),
|
dtype=np.dtype(np.int16),
|
||||||
eqdata=None,
|
eqdata=None,
|
||||||
level_dB=-20,
|
level_dB=-20,
|
||||||
signaltype=SignalType.Periodic,
|
signaltype=SignalType.Periodic,
|
||||||
signaltypedata=(1000.,)
|
signaltypedata=(args.freq,)
|
||||||
)
|
)
|
||||||
siggen = Siggen(outq, siggendata)
|
siggen = Siggen(outq, siggendata)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user