Merge branch 'master' into develop
This commit is contained in:
commit
388cde38be
@ -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);
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -231,7 +231,12 @@ class AvStreamProcess(mp.Process):
|
|||||||
self.siggen_activated = Atomic(False)
|
self.siggen_activated = Atomic(False)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
try:
|
||||||
msg, data = self.pipe.recv()
|
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()')
|
||||||
|
try:
|
||||||
for q in self.indata_qlist:
|
for q in self.indata_qlist:
|
||||||
# Fan out the input data to all queues in the queue list
|
# Fan out the input data to all queues in the queue list
|
||||||
q.put((msg, data))
|
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)
|
||||||
|
try:
|
||||||
for q in self.msg_qlist:
|
for q in self.msg_qlist:
|
||||||
# Fan out the input data to all queues in the queue list
|
# Fan out the input data to all queues in the queue list
|
||||||
q.put((msg, data))
|
q.put((msg, data))
|
||||||
|
except ValueError:
|
||||||
|
logging.error("Error with data queue, terminating process")
|
||||||
|
self.stopAllStreams()
|
||||||
|
self.terminate()
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -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
|
||||||
|
if self.muted:
|
||||||
|
# Mute it
|
||||||
|
signal *= 0
|
||||||
|
try:
|
||||||
self.dataq.put(signal.astype(dtype))
|
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):
|
||||||
|
try:
|
||||||
msg, data = self.pipe.recv()
|
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))
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user