Merge branch 'master' into develop

This commit is contained in:
Casper Jansen 2021-10-18 09:45:40 +02:00
commit 388cde38be
7 changed files with 75 additions and 25 deletions

View File

@ -28,7 +28,7 @@ typedef struct Fft_s {
d* real_storage; d* real_storage;
} Fft_s; } Fft_s;
#else #else
#error "Cannot compile lasp_ffc.c, no FFT backend specified" #error "Cannot compile lasp_ffc.c, no FFT backend specified. Should either be FFTPack, or FFTW"
#endif #endif
void load_fft_wisdom(const char* wisdom) { void load_fft_wisdom(const char* wisdom) {
@ -130,7 +130,7 @@ void Fft_ifft_single(const Fft* fft,const vc* freqdata,vd* result) {
getvdval(&fft->fft_work,0)); getvdval(&fft->fft_work,0));
#elif defined LASP_FFT_BACKEND_FFTW #elif LASP_FFT_BACKEND == FFTW
c* freqdata_ptr = (c*) getvcval(freqdata,0); c* freqdata_ptr = (c*) getvcval(freqdata,0);
c_copy(fft->complex_storage, freqdata_ptr,nfft/2+1); c_copy(fft->complex_storage, freqdata_ptr,nfft/2+1);
@ -219,7 +219,7 @@ void Fft_fft_single(const Fft* fft,const vd* timedata,vc* result) {
result_ptr[nfft+1] = 0; result_ptr[nfft+1] = 0;
} }
check_overflow_vx(fft->fft_work); check_overflow_vx(fft->fft_work);
#elif defined LASP_FFT_BACKEND_FFTW #elif LASP_FFT_BACKEND == FFTW
d* timedata_ptr = getvdval(timedata,0); d* timedata_ptr = getvdval(timedata,0);
c* result_ptr = getvcval(result,0); c* result_ptr = getvcval(result,0);

View File

@ -96,10 +96,10 @@ void print_job_queue(JobQueue* jq) {
JobQueue* JobQueue_alloc(const us max_jobs) { JobQueue* JobQueue_alloc(const us max_jobs) {
TRACE(15,"JobQueue_alloc"); TRACE(15,"JobQueue_alloc");
if(max_jobs > LASP_MAX_NUM_CHANNELS) { /* if(max_jobs > LASP_MAX_NUM_CHANNELS) { */
WARN("Max jobs restricted to LASP_MAX_NUM_CHANNELS"); /* WARN("Max jobs restricted to LASP_MAX_NUM_CHANNELS"); */
return NULL; /* return NULL; */
} /* } */
JobQueue* jq = a_malloc(sizeof(JobQueue)); JobQueue* jq = a_malloc(sizeof(JobQueue));

View File

@ -153,7 +153,7 @@ Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config)
if (monitorOutput && !(nenoutchannels() > 0)) { if (monitorOutput && !(nenoutchannels() > 0)) {
throw runtime_error( throw runtime_error(
"Output monitoring only possible when output is enabled"); "Output monitoring only possible when at least one output channel is enabled. Please make sure to enable at least one output channel");
} }
} }

View File

@ -87,7 +87,9 @@ public:
if (handle == 0) { if (handle == 0) {
throw runtime_error("Unable to create a handle to the specified DAQ " throw runtime_error("Unable to create a handle to the specified DAQ "
"device. Is the device currently in use?"); "device. Is the device currently in use? Please make sure to set "
"the DAQ configuration in duplex mode if simultaneous input and "
"output is required.");
} }
err = ulConnectDaqDevice(handle); err = ulConnectDaqDevice(handle);
@ -579,14 +581,14 @@ void fillUlDaqDeviceInfo(vector<DeviceInfo> &devinfolist) {
devinfo.availableSampleRates.push_back(50400); devinfo.availableSampleRates.push_back(50400);
devinfo.availableSampleRates.push_back(51000); devinfo.availableSampleRates.push_back(51000);
devinfo.prefSampleRateIndex = 5; devinfo.prefSampleRateIndex = 11;
devinfo.availableFramesPerBlock.push_back(512); devinfo.availableFramesPerBlock.push_back(512);
devinfo.availableFramesPerBlock.push_back(1024); devinfo.availableFramesPerBlock.push_back(1024);
devinfo.availableFramesPerBlock.push_back(2048); devinfo.availableFramesPerBlock.push_back(2048);
devinfo.availableFramesPerBlock.push_back(4096); devinfo.availableFramesPerBlock.push_back(4096);
devinfo.availableFramesPerBlock.push_back(8192); devinfo.availableFramesPerBlock.push_back(8192);
devinfo.prefFramesPerBlockIndex = 1; devinfo.prefFramesPerBlockIndex = 2;
devinfo.availableInputRanges = {1.0, 10.0}; devinfo.availableInputRanges = {1.0, 10.0};
devinfo.prefInputRangeIndex = 0; devinfo.prefInputRangeIndex = 0;

View File

@ -231,7 +231,12 @@ class AvStreamProcess(mp.Process):
self.siggen_activated = Atomic(False) self.siggen_activated = Atomic(False)
while True: while True:
msg, data = self.pipe.recv() try:
msg, data = self.pipe.recv()
except OSError:
logging.error("Error with pipe, terminating process")
self.stopAllStreams()
self.terminate()
logging.debug(f"Streamprocess obtained message {msg}") logging.debug(f"Streamprocess obtained message {msg}")
if msg == StreamMsg.activateSiggen: if msg == StreamMsg.activateSiggen:
@ -419,18 +424,28 @@ class AvStreamProcess(mp.Process):
# explanation. # explanation.
def sendInQueues(self, msg, *data): def sendInQueues(self, msg, *data):
# logging.debug('sendInQueues()') # logging.debug('sendInQueues()')
for q in self.indata_qlist: try:
# Fan out the input data to all queues in the queue list for q in self.indata_qlist:
q.put((msg, data)) # Fan out the input data to all queues in the queue list
q.put((msg, data))
except ValueError:
logging.error("Error with data queue, terminating process")
self.stopAllStreams()
self.terminate()
def sendAllQueues(self, msg, *data): def sendAllQueues(self, msg, *data):
""" """
Destined for all queues, including capture data queues Destined for all queues, including capture data queues
""" """
self.sendInQueues(msg, *data) self.sendInQueues(msg, *data)
for q in self.msg_qlist: try:
# Fan out the input data to all queues in the queue list for q in self.msg_qlist:
q.put((msg, data)) # Fan out the input data to all queues in the queue list
q.put((msg, data))
except ValueError:
logging.error("Error with data queue, terminating process")
self.stopAllStreams()
self.terminate()
@dataclass @dataclass

View File

@ -68,6 +68,7 @@ class SiggenMessage(Enum):
newSiggenData = auto() # Forward new equalizer settings newSiggenData = auto() # Forward new equalizer settings
ready = auto() # Send out once, once the signal generator is ready with ready = auto() # Send out once, once the signal generator is ready with
# pre-generating data. # pre-generating data.
mute = auto() # Mute / unmute siggen
# These messages are send back to the main thread over the pipe # These messages are send back to the main thread over the pipe
error = auto() error = auto()
@ -87,6 +88,9 @@ class SiggenData:
# The data type to output # The data type to output
dtype: np.dtype dtype: np.dtype
# Muted?
muted: bool
# Level of output signal [dBFS]el # Level of output signal [dBFS]el
level_dB: float level_dB: float
@ -146,6 +150,9 @@ class SiggenProcess(mp.Process):
signaltype = siggendata.signaltype signaltype = siggendata.signaltype
signaltypedata = siggendata.signaltypedata signaltypedata = siggendata.signaltypedata
# Muted state
self.muted = siggendata.muted
if signaltype == SignalType.Periodic: if signaltype == SignalType.Periodic:
freq, = signaltypedata freq, = signaltypedata
siggen = pyxSiggen.sineWave(fs, freq, level_dB) siggen = pyxSiggen.sineWave(fs, freq, level_dB)
@ -181,7 +188,16 @@ class SiggenProcess(mp.Process):
if np.issubdtype(dtype, np.integer): if np.issubdtype(dtype, np.integer):
bitdepth_fixed = dtype.itemsize * 8 bitdepth_fixed = dtype.itemsize * 8
signal *= 2 ** (bitdepth_fixed - 1) - 1 signal *= 2 ** (bitdepth_fixed - 1) - 1
self.dataq.put(signal.astype(dtype)) if self.muted:
# Mute it
signal *= 0
try:
self.dataq.put(signal.astype(dtype))
except ValueError:
# As of Python 3.8, a value error on a Queue means that the oter
# end of the process died.
logging.error("Error with data queue, terminating process")
self.terminate()
def newEqualizer(self, eqdata): def newEqualizer(self, eqdata):
""" """
@ -207,6 +223,13 @@ class SiggenProcess(mp.Process):
eq.setLevels(eq_levels) eq.setLevels(eq_levels)
return eq return eq
def sendPipe(self, msgtype, msg):
try:
self.pipe.send((msgtype, msg))
except OSError:
logging.error("Error with pipe, terminating process")
self.terminate()
def run(self): def run(self):
# The main function of the actual process # The main function of the actual process
# First things first # First things first
@ -215,26 +238,33 @@ class SiggenProcess(mp.Process):
try: try:
self.siggen = self.newSiggen(self.siggendata) self.siggen = self.newSiggen(self.siggendata)
except Exception as e: except Exception as e:
self.pipe.send((SiggenMessage.error, str(e))) self.sendPipe(SiggenMessage.error, str(e))
try: try:
self.eq = self.newEqualizer(self.siggendata.eqdata) self.eq = self.newEqualizer(self.siggendata.eqdata)
except Exception as e: except Exception as e:
self.pipe.send((SiggenMessage.error, str(e))) self.sendPipe(SiggenMessage.error, str(e))
# Pre-generate blocks of signal data # Pre-generate blocks of signal data
while self.dataq.qsize() < self.nblocks_buffer: while self.dataq.qsize() < self.nblocks_buffer:
self.generate() self.generate()
self.pipe.send((SiggenMessage.ready, None)) self.sendPipe(SiggenMessage.ready, None)
while True: while True:
# Wait here for a while, to check for messages to consume # Wait here for a while, to check for messages to consume
if self.pipe.poll(timeout=QUEUE_BUFFER_TIME / 4): if self.pipe.poll(timeout=QUEUE_BUFFER_TIME / 4):
msg, data = self.pipe.recv() try:
msg, data = self.pipe.recv()
except OSError:
logging.error("Error with pipe, terminating process")
self.terminate()
if msg == SiggenMessage.endProcess: if msg == SiggenMessage.endProcess:
logging.debug("Signal generator caught 'endProcess' message. Exiting.") logging.debug("Signal generator caught 'endProcess' message. Exiting.")
return 0 return 0
elif msg == SiggenMessage.mute:
self.muted = data
elif msg == SiggenMessage.adjustVolume: elif msg == SiggenMessage.adjustVolume:
level_dB = data level_dB = data
logging.debug(f"Signal generator caught 'adjustVolume' message. New volume = {level_dB:.1f} dB FS") logging.debug(f"Signal generator caught 'adjustVolume' message. New volume = {level_dB:.1f} dB FS")
@ -254,7 +284,7 @@ class SiggenProcess(mp.Process):
try: try:
self.generate() self.generate()
except SiggenWorkerDone: except SiggenWorkerDone:
self.pipe.send(SiggenMessage.done) self.sendPipe(SiggenMessage.done, None)
return 0 return 0
return 1 return 1
@ -302,6 +332,9 @@ class Siggen:
""" """
self.pipe.send((SiggenMessage.adjustVolume, new_level)) self.pipe.send((SiggenMessage.adjustVolume, new_level))
def mute(self, mute):
self.pipe.send((SiggenMessage.mute, mute))
def setEqData(self, eqdata): def setEqData(self, eqdata):
self.pipe.send((SiggenMessage.newEqSettings, eqdata)) self.pipe.send((SiggenMessage.newEqSettings, eqdata))

View File

@ -449,7 +449,7 @@ cdef class SosFilterBank:
""" """
if sos.shape[0] != self.nsections: if sos.shape[0] != self.nsections:
raise RuntimeError('Invalid number of sections in filter data, should be {self.nsections.}') raise RuntimeError(f'Invalid number of sections in filter data, should be {self.nsections}.')
elif sos.shape[1] != 6: elif sos.shape[1] != 6:
raise RuntimeError('Illegal number of filter coefficients in section. Should be 6.') raise RuntimeError('Illegal number of filter coefficients in section. Should be 6.')
cdef dmat coefs = dmat_foreign_data(sos.size,1, cdef dmat coefs = dmat_foreign_data(sos.size,1,