Removed stupid handleMessages from streammanager.

This commit is contained in:
Anne de Jong 2022-05-17 13:52:34 +02:00
parent 200ee69e2a
commit 16390352dc
4 changed files with 32 additions and 113 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "STL-Threadsafe"]
path = STL-Threadsafe
url = https://github.com/miachm/STL-Threadsafe

View File

@ -136,7 +136,6 @@ set(CMAKE_C_FLAGS_RELEASE "-O2 -mfpmath=sse -march=x86-64 -mtune=native \
# ############################# End compilation flags # ############################# End compilation flags
# Python searching. # Python searching.
set(Python_ADDITIONAL_VERSIONS "3.8") set(Python_ADDITIONAL_VERSIONS "3.8")
set(python_version_windll "38") set(python_version_windll "38")
@ -151,10 +150,8 @@ if(LASP_FFTPACK_BACKEND)
) )
endif() endif()
include_directories( include_directories(lasp/c)
lasp/c include_directories(STL-Threadsafe/include)
)
add_subdirectory(lasp) add_subdirectory(lasp)
add_subdirectory(test) add_subdirectory(test)

1
STL-Threadsafe Submodule

@ -0,0 +1 @@
Subproject commit 08b2d9e7f487121088a817071d1d42b2736996e9

View File

@ -16,16 +16,15 @@ from typing import List
import numpy as np import numpy as np
from .device import Daq, DaqChannel, DaqConfiguration, DeviceInfo from .device import Daq, DaqChannel, DaqConfiguration, DeviceInfo
from .filter import highpass
from .lasp_atomic import Atomic from .lasp_atomic import Atomic
from .lasp_common import AvType from .lasp_common import AvType
from .lasp_multiprocessingpatch import apply_patch from .lasp_multiprocessingpatch import apply_patch
from .filter import highpass
from .wrappers import SosFilterBank from .wrappers import SosFilterBank
apply_patch() apply_patch()
__all__ = ["StreamManager", "ignoreSigInt", "StreamStatus"]
__all__ = ['StreamManager', 'ignoreSigInt', 'StreamStatus']
def ignoreSigInt(): def ignoreSigInt():
@ -66,7 +65,6 @@ class StreamMsg(Enum):
getStreamMetaData = auto() getStreamMetaData = auto()
endProcess = auto() endProcess = auto()
scanDaqDevices = auto() scanDaqDevices = auto()
""" """
Second part, status messages that are send back on all listeners Second part, status messages that are send back on all listeners
""" """
@ -107,7 +105,7 @@ class AudioStream:
processCallback: callback function that will be called from a different processCallback: callback function that will be called from a different
thread, with arguments (AudioStream, in thread, with arguments (AudioStream, in
""" """
logging.debug('AudioStream()') logging.debug("AudioStream()")
# self.running = Atomic(False) # self.running = Atomic(False)
# self.aframectr = Atomic(0) # self.aframectr = Atomic(0)
@ -134,24 +132,22 @@ class AudioStream:
en_out_ch = daqconfig.getEnabledOutChannels() en_out_ch = daqconfig.getEnabledOutChannels()
if en_in_ch == 0 and en_out_ch == 0: if en_in_ch == 0 and en_out_ch == 0:
raise RuntimeError('No enabled input / output channels') raise RuntimeError("No enabled input / output channels")
elif en_out_ch == 0 and avtype in (AvType.audio_duplex, elif en_out_ch == 0 and avtype in (AvType.audio_duplex, AvType.audio_output):
AvType.audio_output): raise RuntimeError("No enabled output channels")
raise RuntimeError('No enabled output channels') elif en_in_ch == 0 and avtype in (AvType.audio_input, AvType.audio_duplex):
elif en_in_ch == 0 and avtype in (AvType.audio_input, raise RuntimeError("No enabled input channels")
AvType.audio_duplex):
raise RuntimeError('No enabled input channels')
logging.debug('Ready to start device...') logging.debug("Ready to start device...")
samplerate = self.daq.start(self.streamCallback) samplerate = self.daq.start(self.streamCallback)
# Create required Highpass filters for incoming data # Create required Highpass filters for incoming data
self.hpfs = [None]*len(en_in_ch) self.hpfs = [None] * len(en_in_ch)
for i, ch in enumerate(en_in_ch): for i, ch in enumerate(en_in_ch):
# Simple filter with a single bank and one section # Simple filter with a single bank and one section
if ch.highpass > 0: if ch.highpass > 0:
fb = SosFilterBank(1, 1) fb = SosFilterBank(1, 1)
hpf = highpass(samplerate, ch.highpass, Q=np.sqrt(2)) hpf = highpass(samplerate, ch.highpass, Q=np.sqrt(2) / 2)
fb.setFilter(0, hpf[None, :]) fb.setFilter(0, hpf[None, :])
self.hpfs[i] = fb self.hpfs[i] = fb
@ -164,7 +160,6 @@ class AudioStream:
) )
self.running = True self.running = True
def streamCallback(self, indata, outdata, nframes): def streamCallback(self, indata, outdata, nframes):
""" """
This is called (from a separate thread) for each block This is called (from a separate thread) for each block
@ -191,12 +186,11 @@ class AudioStream:
# be an empty filter # be an empty filter
if self.hpfs[i] is not None: if self.hpfs[i] is not None:
indata_float = indata[:, [i]].astype(np.float) indata_float = indata[:, [i]].astype(np.float)
filtered_ch_float = self.hpfs[i].filter_( filtered_ch_float = self.hpfs[i].filter_(indata_float)
indata_float
)
indata_filtered[:, i] = filtered_ch_float.astype( indata_filtered[:, i] = filtered_ch_float.astype(
self.streammetadata.dtype)[:, 0] self.streammetadata.dtype
)[:, 0]
else: else:
# One-to-one copy # One-to-one copy
indata_filtered[:, i] = indata[:, i] indata_filtered[:, i] = indata[:, i]
@ -261,7 +255,7 @@ class AvStreamProcess(mp.Process):
""" """
The actual function running in a different process. The actual function running in a different process.
""" """
# First things first, ignore interrupt signals # First things first, ignore interrupt signals for THIS process
# https://stackoverflow.com/questions/21104997/keyboard-interrupt-with-pythons-multiprocessing # https://stackoverflow.com/questions/21104997/keyboard-interrupt-with-pythons-multiprocessing
signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN)
@ -296,8 +290,7 @@ class AvStreamProcess(mp.Process):
StreamMsg.streamMetaData, avtype, stream.streammetadata StreamMsg.streamMetaData, avtype, stream.streammetadata
) )
else: else:
self.sendAllQueues( self.sendAllQueues(StreamMsg.streamMetaData, avtype, None)
StreamMsg.streamMetaData, avtype, None)
elif msg == StreamMsg.startStream: elif msg == StreamMsg.startStream:
avtype, daqconfig = data avtype, daqconfig = data
@ -319,12 +312,9 @@ class AvStreamProcess(mp.Process):
while not self.outq.empty(): while not self.outq.empty():
self.outq.get() self.outq.get()
try: try:
stream = AudioStream(avtype, self.devices, stream = AudioStream(avtype, self.devices, daqconfig, self.streamCallback)
daqconfig, self.streamCallback)
self.streams[avtype] = stream self.streams[avtype] = stream
self.sendAllQueues( self.sendAllQueues(StreamMsg.streamStarted, avtype, stream.streammetadata)
StreamMsg.streamStarted, avtype, stream.streammetadata
)
except Exception as e: except Exception as e:
self.sendAllQueues( self.sendAllQueues(
@ -332,7 +322,6 @@ class AvStreamProcess(mp.Process):
) )
return return
def stopStream(self, avtype: AvType): def stopStream(self, avtype: AvType):
""" """
Stop an existing stream, and sets the attribute in the list of streams Stop an existing stream, and sets the attribute in the list of streams
@ -345,8 +334,7 @@ class AvStreamProcess(mp.Process):
if stream is not None: if stream is not None:
try: try:
stream.stop() stream.stop()
self.sendAllQueues( self.sendAllQueues(StreamMsg.streamStopped, stream.avtype)
StreamMsg.streamStopped, stream.avtype)
except Exception as e: except Exception as e:
self.sendAllQueues( self.sendAllQueues(
StreamMsg.streamError, StreamMsg.streamError,
@ -487,9 +475,7 @@ class StreamManager:
""" """
def __init__(self): def __init__(self):
"""Open a stream for audio in/output and video input. For audio output, """Open a stream for audio in/output and video input. For audio output,"""
"""
# Initialize streamstatus # Initialize streamstatus
self.streamstatus = {t: StreamStatus() for t in list(AvType)} self.streamstatus = {t: StreamStatus() for t in list(AvType)}
@ -525,67 +511,12 @@ class StreamManager:
self.our_msgqueue = self.addMsgQueueListener() self.our_msgqueue = self.addMsgQueueListener()
# Create the stream process # Create the stream process
self.streamProcess = AvStreamProcess(child_pipe, self.streamProcess = AvStreamProcess(
self.msg_qlist, child_pipe, self.msg_qlist, self.indata_qlist, self.outq
self.indata_qlist, self.outq) )
self.streamProcess.start() self.streamProcess.start()
def handleMessages(self): def scanDaqDevices(self):
"""
Handle messages that are still on the pipe.
"""
# logging.debug('StreamManager::handleMessages()')
msgs = []
while not self.our_msgqueue.empty():
msg, data = self.our_msgqueue.get()
logging.debug(f'StreamManager obtained message {msg}')
if msg == StreamMsg.streamStarted:
avtype, streammetadata = data
# logging.debug(f'{avtype}, {streammetadata}')
self.streamstatus[avtype].lastStatus = msg
self.streamstatus[avtype].errorTxt = None
self.streamstatus[avtype].streammetadata = streammetadata
elif msg == StreamMsg.streamStopped:
(avtype,) = data
self.streamstatus[avtype].lastStatus = msg
self.streamstatus[avtype].errorTxt = None
self.streamstatus[avtype].streammetadata = None
elif msg == StreamMsg.streamError:
avtype, errorTxt = data
if avtype is not None:
self.streamstatus[avtype].lastStatus = msg
self.streamstatus[avtype].errorTxt = errorTxt
logging.debug(f'Message: {errorTxt}')
elif msg == StreamMsg.streamTemporaryError:
avtype, errorTxt = data
if avtype is not None:
logging.debug(f'Message: {errorTxt}')
elif msg == StreamMsg.streamFatalError:
avtype, errorTxt = data
logging.critical(f"Streamprocess fatal error: {errorTxt}")
self.cleanup()
elif msg == StreamMsg.streamMetaData:
avtype, metadata = data
self.streamstatus[avtype].streammetadata = metadata
elif msg == StreamMsg.deviceList:
devices, = data
# logging.debug(devices)
self.devices = devices
msgs.append((msg, data))
return msgs
def getDeviceList(self):
self.handleMessages()
return self.devices
def rescanDaqDevices(self):
""" """
Output the message to the stream process to rescan the list of devices Output the message to the stream process to rescan the list of devices
""" """
@ -595,7 +526,6 @@ class StreamManager:
""" """
Sends a request for the stream status over the pipe, for given AvType Sends a request for the stream status over the pipe, for given AvType
""" """
self.handleMessages()
self.sendPipe(StreamMsg.getStreamMetaData, avtype) self.sendPipe(StreamMsg.getStreamMetaData, avtype)
def getOutputQueue(self): def getOutputQueue(self):
@ -604,11 +534,8 @@ 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 addMsgQueueListener(self): def addMsgQueueListener(self):
""" """
Add a listener queue to the list of message queues, and return the Add a listener queue to the list of message queues, and return the
@ -652,7 +579,7 @@ class StreamManager:
del self.indata_qlist[idx] del self.indata_qlist[idx]
del self.indata_qlist_local[idx] del self.indata_qlist_local[idx]
def startStream(self, avtype: AvType, daqconfig: DaqConfiguration, wait=False): def startStream(self, avtype: AvType, daqconfig: DaqConfiguration):
""" """
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)
@ -663,19 +590,10 @@ class StreamManager:
""" """
logging.debug("Starting stream...") logging.debug("Starting stream...")
self.handleMessages()
self.sendPipe(StreamMsg.startStream, avtype, daqconfig) 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):
logging.debug(f'StreamManager::stopStream({avtype})') logging.debug(f"StreamManager::stopStream({avtype})")
self.handleMessages()
self.sendPipe(StreamMsg.stopStream, avtype) self.sendPipe(StreamMsg.stopStream, avtype)
def stopAllStreams(self): def stopAllStreams(self):