First work to multiple API AvStreams. Python thread for UlDaq does compile, ready for testing

This commit is contained in:
Anne de Jong 2020-09-15 20:34:27 +02:00
parent 49ee42bb01
commit c9b84e4c96
7 changed files with 377 additions and 397 deletions

View File

@ -11,4 +11,5 @@ target_link_libraries(lasp_rtaudio pthread rtaudio)
target_link_libraries(lasp_uldaq uldaq)
if(win32)
target_link_libraries(lasp_rtaudio python37)
target_link_libraries(lasp_uldaq python37)
endif(win32)

View File

@ -109,6 +109,12 @@ class DAQConfiguration:
outputDelayBlocks: int = 0
nFramesPerBlock: int = 512
def getInputChannels(self):
return self.input_channel_configs
def getOutputChannels(self):
return self.output_channel_configs
def firstEnabledInputChannelNumber(self):
"""
Returns the channel number of the first enabled channel. Returns -1 if

View File

@ -164,7 +164,7 @@ def get_sampwidth_from_format_string(format_string):
ctypedef struct _Stream:
ctypedef struct PyStreamData:
PyObject* pyCallback
RtAudioFormat sampleformat
@ -209,7 +209,7 @@ cdef int audioCallback(void* outputbuffer,
"""
cdef:
int rval = 0
_Stream* stream
PyStreamData* stream
void* outputbuffercpy = NULL
void* inputbuffercpy = NULL
unsigned j, i
@ -217,7 +217,7 @@ cdef int audioCallback(void* outputbuffer,
unsigned noutputchannels_forwarded, ninputchannels_forwarded
bint ch_en
stream = <_Stream*>(userData)
stream = <PyStreamData*>(userData)
bytesperchan = stream.nBytesPerChan
ninputchannels_forwarded = stream.ninputchannels_forwarded
noutputchannels_forwarded = stream.noutputchannels_forwarded
@ -317,7 +317,7 @@ cdef int audioCallback(void* outputbuffer,
cdef void audioCallbackPythonThreadFunction(void* voidstream) nogil:
cdef:
_Stream* stream
PyStreamData* stream
cnp.NPY_TYPES npy_format
void* inputbuffer = NULL
void* outputbuffer = NULL
@ -326,9 +326,7 @@ cdef void audioCallbackPythonThreadFunction(void* voidstream) nogil:
unsigned nBytesPerChan
unsigned nFramesPerBlock
void* _TESTDATA
stream = <_Stream*> voidstream
stream = <PyStreamData*> voidstream
ninputchannels_forwarded = stream.ninputchannels_forwarded
noutputchannels_forwarded = stream.noutputchannels_forwarded
nBytesPerChan = stream.nBytesPerChan
@ -441,22 +439,22 @@ cdef void errorCallback(RtAudioError.Type _type,const string& errortxt) nogil:
cdef class RtAudio:
cdef:
cppRtAudio* _rtaudio
_Stream* _stream
PyStreamData* sd
int api
def __cinit__(self, unsigned int iapi):
cdef:
cppRtAudio.Api api = <cppRtAudio.Api> iapi
self._rtaudio = new cppRtAudio(api)
self._stream = NULL
self.sd = NULL
self._rtaudio.showWarnings(True)
self.api = api
def __dealloc__(self):
if self._stream is not NULL:
if self.sd is not NULL:
# fprintf(stderr, 'Force closing stream...')
self._rtaudio.closeStream()
self.cleanupStream(self._stream)
self.cleanupStream(self.sd)
del self._rtaudio
@staticmethod
@ -467,7 +465,7 @@ cdef class RtAudio:
apidict = {}
for api in apis:
apidict[<int> api] = {
'displayname': cppRtAudio.getApiDisplayName(api).decode('utf-8'),
'displayname': 'RtAudio - ' + cppRtAudio.getApiDisplayName(api).decode('utf-8'),
'name': cppRtAudio.getApiName(api).decode('utf-8')
}
return apidict
@ -478,22 +476,33 @@ cdef class RtAudio:
cpdef unsigned int getDefaultInputDevice(self):
return self._rtaudio.getDefaultInputDevice()
def getDeviceInfo(self, unsigned int device):
def getDeviceInfo(self):
"""
Return device information of the current device
"""
sampleformats = []
cdef:
cppRtAudio.DeviceInfo devinfo
devinfo = self._rtaudio.getDeviceInfo(device)
nf = devinfo.nativeFormats
for format_ in [ RTAUDIO_SINT8, RTAUDIO_SINT16, RTAUDIO_SINT24,
RTAUDIO_SINT32, RTAUDIO_FLOAT32, RTAUDIO_FLOAT64]:
if nf & format_:
sampleformats.append(_formats_rtkey[format_][0])
return DeviceInfo(
unsigned devcount, i
devinfo_py = []
devcount = self._rtaudio.getDeviceCount()
for i in range(devcount):
devinfo = self._rtaudio.getDeviceInfo(i)
nf = devinfo.nativeFormats
for format_ in [ RTAUDIO_SINT8, RTAUDIO_SINT16, RTAUDIO_SINT24,
RTAUDIO_SINT32, RTAUDIO_FLOAT32, RTAUDIO_FLOAT64]:
if nf & format_:
sampleformats.append(_formats_rtkey[format_][0])
devinfo_py.append(DeviceInfo(
api = self.api,
index = device,
index = i,
probed = devinfo.probed,
name = devinfo.name.decode('utf-8'),
outputchannels = devinfo.outputChannels,
@ -501,10 +510,12 @@ cdef class RtAudio:
duplexchannels = devinfo.duplexChannels,
samplerates = devinfo.sampleRates,
sampleformats = sampleformats,
prefsamplerate = devinfo.preferredSampleRate)
prefsamplerate = devinfo.preferredSampleRate))
return devinfo_py
@cython.nonecheck(True)
def openStream(self,
def start(self,
avstream
):
"""
@ -516,7 +527,7 @@ cdef class RtAudio:
Returns: None
"""
if self._stream is not NULL:
if self.sd is not NULL:
raise RuntimeError('Stream is already opened.')
daqconfig = avstream.daqconfig
@ -573,27 +584,27 @@ cdef class RtAudio:
print(f'samplewidth: {sw}')
# All set, allocate the stream!
self._stream = <_Stream*> malloc(sizeof(_Stream))
if self._stream == NULL:
self.sd = <PyStreamData*> malloc(sizeof(PyStreamData))
if self.sd == NULL:
raise MemoryError('Could not allocate stream: memory error.')
self._stream.pyCallback = <PyObject*> avstream._audioCallback
self.sd.pyCallback = <PyObject*> avstream._audioCallback
# Increase reference count to the callback
Py_INCREF(<object> avstream._audioCallback)
self._stream.sampleformat = _formats_strkey[sampleformat][0]
self._stream.stopThread.store(False)
self._stream.inputQueue = NULL
self._stream.outputQueue = NULL
self._stream.outputDelayQueue = NULL
self.sd.sampleformat = _formats_strkey[sampleformat][0]
self.sd.stopThread.store(False)
self.sd.inputQueue = NULL
self.sd.outputQueue = NULL
self.sd.outputDelayQueue = NULL
self._stream.thread = NULL
self.sd.thread = NULL
self._stream.outputDelayBlocks = outputDelayBlocks
self._stream.ninputchannels_forwarded = 0
self._stream.noutputchannels_forwarded = 0
self._stream.inputChannelsEnabled = NULL
self._stream.outputChannelsEnabled = NULL
self.sd.outputDelayBlocks = outputDelayBlocks
self.sd.ninputchannels_forwarded = 0
self.sd.noutputchannels_forwarded = 0
self.sd.inputChannelsEnabled = NULL
self.sd.outputChannelsEnabled = NULL
# Create channel maps for input channels, set RtAudio input stream
# parameters
@ -611,7 +622,7 @@ cdef class RtAudio:
input_ch = daqconfig.input_channel_configs
inputChannelsEnabled = <bool*> malloc(sizeof(bool)*ninputchannels_rtaudio)
self._stream.inputChannelsEnabled = inputChannelsEnabled
self.sd.inputChannelsEnabled = inputChannelsEnabled
for i in range(firstinputchannel, lastinputchannel+1):
ch_en = input_ch[i].channel_enabled
@ -619,13 +630,13 @@ cdef class RtAudio:
ninputchannels_forwarded += 1
inputChannelsEnabled[i] = ch_en
rtInputParams_ptr = &self._stream.inputParams
rtInputParams_ptr = &self.sd.inputParams
rtInputParams_ptr.deviceId = device.index
rtInputParams_ptr.nChannels = ninputchannels_rtaudio
rtInputParams_ptr.firstChannel = firstinputchannel
self._stream.inputQueue = new SafeQueue[void*]()
self._stream.ninputchannels_forwarded = ninputchannels_forwarded
self.sd.inputQueue = new SafeQueue[void*]()
self.sd.ninputchannels_forwarded = ninputchannels_forwarded
# Create channel maps for output channels
@ -643,24 +654,24 @@ cdef class RtAudio:
output_ch = daqconfig.output_channel_configs
outputChannelsEnabled = <bool*> malloc(sizeof(bool)*noutputchannels_rtaudio)
self._stream.outputChannelsEnabled = outputChannelsEnabled
self.sd.outputChannelsEnabled = outputChannelsEnabled
for i in range(firstoutputchannel, lastoutputchannel+1):
ch_en = output_ch[i].channel_enabled
if ch_en:
noutputchannels_forwarded += 1
outputChannelsEnabled[i] = ch_en
rtOutputParams_ptr = &self._stream.outputParams
rtOutputParams_ptr = &self.sd.outputParams
rtOutputParams_ptr.deviceId = device.index
rtOutputParams_ptr.nChannels = noutputchannels_rtaudio
rtOutputParams_ptr.firstChannel = firstoutputchannel
self._stream.outputQueue = new SafeQueue[void*]()
self._stream.noutputchannels_forwarded = noutputchannels_forwarded
self.sd.outputQueue = new SafeQueue[void*]()
self.sd.noutputchannels_forwarded = noutputchannels_forwarded
if monitorOutput and duplex_mode:
self._stream.outputDelayQueue = new SafeQueue[void*]()
self._stream.ninputchannels_forwarded += noutputchannels_forwarded
self.sd.outputDelayQueue = new SafeQueue[void*]()
self.sd.ninputchannels_forwarded += noutputchannels_forwarded
streamoptions.flags = RTAUDIO_HOG_DEVICE
@ -675,30 +686,43 @@ cdef class RtAudio:
samplerate,
&nFramesPerBlock,
audioCallback,
<void*> self._stream,
<void*> self.sd,
&streamoptions, # Stream options
errorCallback # Error callback
)
self._stream.nBytesPerChan = nFramesPerBlock*sw
self._stream.nFramesPerBlock = nFramesPerBlock
self.sd.nBytesPerChan = nFramesPerBlock*sw
self.sd.nFramesPerBlock = nFramesPerBlock
self._rtaudio.startStream()
except Exception as e:
print('Exception occured in stream opening: ', e)
self.cleanupStream(self._stream)
self._stream = NULL
self.cleanupStream(self.sd)
self.sd = NULL
raise e
with nogil:
self._stream.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
<void*> self._stream)
self.sd.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
<void*> self.sd)
# Allow it to start
CPPsleep_ms(500)
pass
return nFramesPerBlock
return nFramesPerBlock, samplerate
cdef cleanupStream(self, _Stream* stream):
def stop(self):
if self.sd is NULL:
raise RuntimeError('Stream is not running')
try:
self._rtaudio.stopStream()
self._rtaudio.closeStream()
except Exception as e:
print(e)
pass
self.cleanupStream(self.sd)
cdef cleanupStream(self, PyStreamData* stream):
# printf('Entrance function cleanupStream...\n')
cdef:
void* ptr
@ -743,42 +767,3 @@ cdef class RtAudio:
stream.pyCallback = NULL
# fprintf(stderr, "End cleanup callback...\n")
free(stream)
def startStream(self):
self._rtaudio.startStream()
def stopStream(self):
if self._stream is NULL:
raise RuntimeError('Stream is not opened')
try:
self._rtaudio.stopStream()
except:
pass
def closeStream(self):
# print('closeStream')
if self._stream is NULL:
raise RuntimeError('Stream is not opened')
# Closing stream
self._rtaudio.closeStream()
self.cleanupStream(self._stream)
self._stream = NULL
def abortStream(self):
if self._stream is NULL:
raise RuntimeError('Stream is not opened')
self._rtaudio.abortStream()
def isStreamOpen(self):
return self._rtaudio.isStreamOpen()
def isStreamRunning(self):
return self._rtaudio.isStreamRunning()
def getStreamTime(self):
return self._rtaudio.getStreamTime()
def setStreamTime(self, double time):
return self._rtaudio.setStreamTime(time)

View File

@ -1,3 +1,4 @@
cimport cython
from cpython.ref cimport PyObject,Py_INCREF, Py_DECREF
from .lasp_daqconfig import (DeviceInfo, InputMode, Range as pyRange,
DAQChannel)
@ -30,14 +31,12 @@ cdef struct DaqThreadData:
double* inbuffer
double* outbuffer
cdef void showErr(UlError err) nogil:
cdef:
char errmsg[UL_ERR_MSG_LEN]
ulGetErrMsg(err, errmsg)
fprintf(stderr, 'UlError: %s\n', errmsg)
cdef void inThreadFunction(void* threaddata_void) nogil:
"""
Stream input thread function
@ -269,7 +268,6 @@ cdef void outThreadFunction(void* threaddata_void) nogil:
fprintf(stderr, "Error stopping AOut output thread\n")
showErr(err)
cdef class UlDT9837A:
cdef:
DaqDeviceHandle handle
@ -280,7 +278,7 @@ cdef class UlDT9837A:
object input_range
object enabled_inputs
bint output_enabled
bint out_enabled
bint monitor_gen
DaqThreadData *td
@ -294,7 +292,7 @@ cdef class UlDT9837A:
self.input_range = 4*[False]
self.enabled_inputs = 4*[False]
self.output_enabled = False
self.out_enabled = False
self.monitor_gen = False
self.td = NULL
@ -340,7 +338,7 @@ cdef class UlDT9837A:
# Sanity checks
if inQueue and (not any(self.enabled_inputs) and (not self.monitor_gen)):
raise ValueError('Input queue given, but no input channels enabled and monitor output disabled')
if outQueue and not self.output_enabled:
if outQueue and not self.out_enabled:
raise ValueError('Output queue given, but output channel is not enabled')
# End sanity checks
@ -419,7 +417,7 @@ cdef class UlDT9837A:
fprintf(stderr, 'SFSG12\n')
# CONFIGURE OUTPUTS
if self.output_enabled:
if self.out_enabled:
fprintf(stderr, 'SFSG13\n')
td.noutChanDescriptors = 1
# WATCH THE TWO HERE!!!
@ -431,7 +429,7 @@ cdef class UlDT9837A:
# Create DAQ Threads
with nogil:
fprintf(stderr, 'SFSG14\n')
if self.output_enabled:
if self.out_enabled:
td.outThread = new CPPThread[void*, void (*)(void*)](
outThreadFunction, <void*> td)
@ -492,7 +490,7 @@ cdef class UlDT9837A:
del inqueue
del outqueue
def stop(self):
def stopScan(self):
self.cleanupThreadData(self.td)
self.td = NULL
@ -538,7 +536,7 @@ cdef class UlDT9837A:
if monitor_gen and not channelconfig.channel_enabled:
raise RuntimeError('Output channel should be enabled to enable channel monitoring')
self.output_enabled = channelconfig.channel_enabled
self.out_enabled = channelconfig.channel_enabled
self.monitor_gen = monitor_gen
def __dealloc__(self):
@ -581,11 +579,122 @@ cdef class UlDT9837A:
free(td)
fprintf(stderr, 'end cleanupThreadData()\n')
ctypedef struct PyStreamData:
PyObject* pyCallback
# Flag used to pass the stopThread.
atomic[bool] stopThread
# Number of frames per block
unsigned nFramesPerBlock
# Number of bytes per channel
unsigned int nBytesPerChan
unsigned ninchannels
unsigned noutchannels
# If these queue pointers are NULL, it means the stream does not have an
# input, or output.
SafeQueue[void*] *inQueue
SafeQueue[void*] *outQueue
CPPThread[void*, void (*)(void*)] *thread
cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
cdef:
PyStreamData* sd
cnp.NPY_TYPES npy_format
void* inbuffer = NULL
void* outbuffer = NULL
unsigned noutchannels
unsigned ninchannels
unsigned nBytesPerChan
unsigned nFramesPerBlock
unsigned sw = sizeof(double)
sd = <PyStreamData*> voidsd
ninchannels = sd.ninchannels
noutchannels = sd.noutchannels
nBytesPerChan = sd.nBytesPerChan
nFramesPerBlock = sd.nFramesPerBlock
with gil:
npy_format = cnp.NPY_FLOAT64
callback = <object> sd.pyCallback
while True:
if sd.stopThread.load() == True:
printf('Stopping thread...\n')
return
if sd.inQueue:
# fprintf(stderr, "Waiting on in queue\n")
inbuffer = sd.inQueue.dequeue()
if not inbuffer:
printf('Stopping thread...\n')
return
if sd.outQueue:
# fprintf(stderr, 'Allocating output buffer...\n')
outbuffer = malloc(nBytesPerChan*noutchannels)
# memset(outbuffer, 0, nBytesPerChan*noutchannels)
with gil:
# Obtain stream information
npy_input = None
npy_output = None
if sd.inQueue:
try:
npy_input = <object> data_to_ndarray(
inbuffer,
nFramesPerBlock,
ninchannels,
npy_format,
True, # Do transfer ownership
True) # F-contiguous is True: data is Fortran-cont.
except Exception as e:
print('exception in cython callback for audio input: ', str(e))
return
try:
rval = callback(npy_input,
npy_output,
nFramesPerBlock,
)
except Exception as e:
print('Exception in Cython callback: ', str(e))
return
if sd.outQueue:
sd.outQueue.enqueue(outbuffer)
if not sd.inQueue:
while sd.outQueue.size() > 10 and not sd.stopThread.load():
# printf('Sleeping...\n')
# No input queue to wait on, so we relax a bit here.
CPPsleep_ms(1);
# Outputbuffer is free'ed by the audiothread, so should not be touched
# here.
outbuffer = NULL
# Inputbuffer memory is owned by Numpy, so should not be free'ed
inbuffer = NULL
cdef class UlDaq:
cdef:
DaqDeviceHandle handle
PyStreamData *sd
UlDT9837A daq_device
def __cinit__(self):
@ -593,33 +702,13 @@ cdef class UlDaq:
Acquires a daq handle, and opens the device
"""
self.handle = 0
self.daq_device = None
self.sd = NULL
# # Open the device to probe the number of input and output ch.
# handle = ulCreateDaqDevice(descriptor)
# if not handle:
# raise RuntimeError('Unable to create device handle on device')
cpdef int getDeviceCount(self):
cdef:
DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]
DaqDeviceInterface interfaceType = ANY_IFC
UlError err
unsigned int numdevs = MAX_DEF_COUNT
err = ulGetDaqDeviceInventory(interfaceType,
devdescriptors,
&numdevs)
if(err != ERR_NO_ERROR):
raise RuntimeError(f'Device inventarization failed: {err}')
devices = []
return numdevs
def getDeviceInfo(self, unsigned int deviceno):
def getDeviceInfo(self):
"""
Returns device information objects (DeviceInfo) for all available
devices
"""
cdef:
DaqDeviceDescriptor devdescriptors[MAX_DEF_COUNT]
@ -630,6 +719,7 @@ cdef class UlDaq:
UlError err
unsigned int numdevs = MAX_DEF_COUNT
unsigned deviceno
err = ulGetDaqDeviceInventory(interfaceType,
devdescriptors,
@ -637,287 +727,190 @@ cdef class UlDaq:
if(err != ERR_NO_ERROR):
raise RuntimeError(f'Device inventarization failed: {err}')
if deviceno >= numdevs:
raise ValueError(f'Device number {deviceno} too high {err}')
py_devinfo = []
for deviceno in range(numdevs):
descriptor = devdescriptors[deviceno]
descriptor = devdescriptors[deviceno]
if descriptor.productName == b'DT9837A':
# Create proper interface name
if descriptor.devInterface == DaqDeviceInterface.USB_IFC:
name = 'USB - '
elif descriptor.devInterface == DaqDeviceInterface.BLUETOOTH_IFC:
name = 'Bluetooth - '
elif descriptor.devInterface == DaqDeviceInterface.ETHERNET_IFC:
name = 'Ethernet - '
name += descriptor.productName.decode('utf-8') + ', id ' + \
descriptor.uniqueId.decode('utf-8')
d = DeviceInfo(
api = -1,
index = deviceno,
probed = True,
name = name,
outputchannels = 1,
inputchannels = 4,
duplexchannels = 0,
samplerates = [10000, 16000, 20000, 32000, 48000, 50000] ,
sampleformats = ['64-bit floats'],
prefsamplerate = 48000,
hasInputIEPE = True)
py_devinfo.append(d)
return py_devinfo
if descriptor.productName == b'DT9837A':
# Create proper interface name
if descriptor.devInterface == DaqDeviceInterface.USB_IFC:
name = 'USB - '
elif descriptor.devInterface == DaqDeviceInterface.BLUETOOTH_IFC:
name = 'Bluetooth - '
elif descriptor.devInterface == DaqDeviceInterface.ETHERNET_IFC:
name = 'Ethernet - '
@cython.nonecheck(True)
def start(self, avstream):
"""
Opens a stream with specified parameters
name += descriptor.productName.decode('utf-8') + ', id ' + \
descriptor.uniqueId.decode('utf-8')
Args:
avstream: AvStream instance
return DeviceInfo(
api = -1,
index = deviceno,
probed = True,
name = name,
outputchannels = 1,
inputchannels = 4,
duplexchannels = 0,
samplerates = [100, 500, 1000, 2000, 4000, 8000, 16000, 20000, 32000, 48000, 50000] ,
sampleformats = ['64-bit floats'],
prefsamplerate = 48000,
hasInputIEPE = True)
Returns: None
"""
if self.sd is not NULL:
raise RuntimeError('Stream is already opened.')
daqconfig = avstream.daqconfig
avtype = avstream.avtype
device = avstream.device
cdef:
bint duplex_mode = daqconfig.duplex_mode
bint monitorOutput = daqconfig.monitor_gen
unsigned int nFramesPerBlock = daqconfig.nFramesPerBlock
unsigned int samplerate
unsigned int ninchannels = 0, noutchannels = 0
int i
bint in_stream=False, output_stream=False
if daqconfig.nFramesPerBlock > 8192 or daqconfig.nFramesPerBlock < 512:
raise ValueError('Invalid number of nFramesPerBlock')
if daqconfig.outputDelayBlocks != 0:
raise ValueError('OutputDelayBlocks not supported by API')
# Determine sample rate and sample format, determine whether we are an
# in or an output stream, or both
if avtype == AvType.audio_input or duplex_mode:
# Here, we override the sample format in case of duplex mode.
sampleformat = daqconfig.en_input_sample_format
samplerate = int(daqconfig.en_input_rate)
in_stream = True
if duplex_mode:
out_stream = True
else:
raise RuntimeError(f"No config found for device \"{descriptor.productName.decode('utf-8')}\".")
sampleformat = daqconfig.en_output_sample_format
samplerate = int(daqconfig.en_output_rate)
out_stream = True
# @cython.nonecheck(True)
# def openStream(self,
# avstream
# ):
# """
# Opens a stream with specified parameters
if 'DT9837A' in device.name:
self.daq_device = UlDT9837A(device.deviceno)
else:
raise RuntimeError(f'Device {device.name} not found or not configured')
# Args:
# avstream: AvStream instance
# All set, allocate the stream!
self.sd = <PyStreamData*> malloc(sizeof(PyStreamData))
if self.sd == NULL:
raise MemoryError('Could not allocate stream: memory error.')
# Returns: None
# """
self.sd.pyCallback = <PyObject*> avstream._audioCallback
# Increase reference count to the callback
Py_INCREF(<object> avstream._audioCallback)
# if self._stream is not NULL:
# raise RuntimeError('Stream is already opened.')
self.sd.stopThread.store(False)
self.sd.inQueue = NULL
self.sd.outQueue = NULL
# daqconfig = avstream.daqconfig
# avtype = avstream.avtype
# device = avstream.device
self.sd.thread = NULL
# cdef:
# bint duplex_mode = daqconfig.duplex_mode
# bint monitorOutput = daqconfig.monitor_gen
# size_t sw # Sample width in bytes
# unsigned int nFramesPerBlock = unsigned int(daqconfig.nFramesPerBlock)
# int firstinputchannel, firstoutputchannel
# int lastinputchannel, lastoutputchannel
# unsigned int ninputchannels_forwarded=0
# unsigned int ninputchannels_uldaq=0
# unsigned int noutputchannels_uldaq=0
# unsigned int noutputchannels_forwarded=0
# unsigned int samplerate
# int i
# bint input_stream=False, output_stream=False
# bool* inputChannelsEnabled
# bool* outputChannelsEnabled
self.sd.ninchannels = 0
self.sd.noutchannels = 0
self.sd.nBytesPerChan = daqconfig.nFramesPerBlock*sizeof(double)
# if daqconfig.nFramesPerBlock > 8192 or daqconfig.nFramesPerBlock < 512:
# raise ValueError('Invalid number of nFramesPerBlock')
# Create channel maps for in channels, set in stream
# parameters
if in_stream:
for i, ch in enumerate(daqconfig.getInputChannels()):
if ch.channel_enabled:
ninchannels += 1
self.daq_device.setInputChannelConfig(i, ch)
# if daqconfig.outputDelayBlocks < 0 or daqconfig.outputDelayBlocks > 10:
# raise ValueError('Invalid number of outputDelayBlocks')
self.sd.inQueue = new SafeQueue[void*]()
# try:
self.sd.ninchannels = ninchannels
# # Determine sample rate and sample format, determine whether we are an
# # input or an output stream, or both
# if avtype == AvType.audio_input or duplex_mode:
# # Here, we override the sample format in case of duplex mode.
# sampleformat = daqconfig.en_input_sample_format
# samplerate = int(daqconfig.en_input_rate)
# input_stream = True
# if duplex_mode:
# output_stream = True
# else:
# sampleformat = daqconfig.en_output_sample_format
# samplerate = int(daqconfig.en_output_rate)
# output_stream = True
# Create channel maps for output channels
if out_stream:
for i, ch in enumerate(daqconfig.getOutputChannels()):
if ch.channel_enabled:
noutchannels += 1
self.daq_device.setOutputChannelConfig(i, ch)
# sw = 64
self.sd.outQueue = new SafeQueue[void*]()
self.sd.noutchannels = noutchannels
# # All set, allocate the stream!
# self._stream = <_Stream*> malloc(sizeof(_Stream))
# if self._stream == NULL:
# raise MemoryError('Could not allocate stream: memory error.')
if monitorOutput and duplex_mode:
self.sd.ninchannels += noutchannels
# self._stream.pyCallback = <PyObject*> avstream._audioCallback
# # Increase reference count to the callback
# Py_INCREF(<object> avstream._audioCallback)
with nogil:
self.sd.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
<void*> self.sd)
# self._stream.sw = sw
# self._stream.stopThread.store(False)
# self._stream.inputQueue = NULL
# self._stream.outputQueue = NULL
# self._stream.outputDelayQueue = NULL
# Allow it to start
CPPsleep_ms(500)
# self._stream.thread = NULL
self.daq_device.startScan(
daqconfig.nFramesPerBlock,
samplerate,
self.sd.inQueue,
self.sd.outQueue)
# self._stream.outputDelayBlocks = outputDelayBlocks
# self._stream.ninputchannels_forwarded = 0
# self._stream.noutputchannels_forwarded = 0
# self._stream.inputChannelsEnabled = NULL
# self._stream.outputChannelsEnabled = NULL
return nFramesPerBlock, self.daq_device.td.samplerate
# # Create channel maps for input channels, set input stream
# # parameters
# if input_stream:
# firstinputchannel = daqconfig.firstEnabledInputChannelNumber()
# lastinputchannel = daqconfig.lastEnabledInputChannelNumber()
# ninputchannels_uldaq = lastinputchannel-firstinputchannel+1
def stop(self):
if self.sd is NULL:
raise RuntimeError('Stream is not opened')
# if lastinputchannel < 0 or ninputchannels_uldaq < 1:
# raise ValueError('Not enough input channels selected')
# input_ch = daqconfig.input_channel_configs
self.uldaq.stopScan()
self.cleanupStream(self.sd)
self.sd = NULL
# inputChannelsEnabled = <bool*> malloc(sizeof(bool)*ninputchannels_uldaq)
# self._stream.inputChannelsEnabled = inputChannelsEnabled
cdef cleanupStream(self, PyStreamData* sd):
# for i in range(firstinputchannel, lastinputchannel+1):
# ch_en = input_ch[i].channel_enabled
# if ch_en:
# ninputchannels_forwarded += 1
# inputChannelsEnabled[i] = ch_en
with nogil:
if sd.thread:
sd.stopThread.store(True)
if sd.inQueue:
# If waiting in the input queue, hereby we let it run.
sd.inQueue.enqueue(NULL)
# printf('Joining thread...\n')
# HERE WE SHOULD RELEASE THE GIL, as exiting the thread function
# will require the GIL, which is locked by this thread!
sd.thread.join()
# printf('Thread joined!\n')
del sd.thread
sd.thread = NULL
# self._stream.inputQueue = new SafeQueue[void*]()
# self._stream.ninputchannels_forwarded = ninputchannels_forwarded
if sd.outQueue:
while not sd.outQueue.empty():
free(sd.outQueue.dequeue())
del sd.outQueue
if sd.inQueue:
while not sd.inQueue.empty():
free(sd.inQueue.dequeue())
del sd.inQueue
fprintf(stderr, "End cleanup stream queues...\n")
if sd.pyCallback:
Py_DECREF(<object> sd.pyCallback)
sd.pyCallback = NULL
# # Create channel maps for output channels
# if output_stream:
# firstoutputchannel = daqconfig.firstEnabledOutputChannelNumber()
# lastoutputchannel = daqconfig.lastEnabledOutputChannelNumber()
# noutputchannels_uldaq = lastoutputchannel-firstoutputchannel+1
# if lastoutputchannel < 0 or noutputchannels_uldaq < 1:
# raise ValueError('Not enough output channels selected')
# output_ch = daqconfig.output_channel_configs
# outputChannelsEnabled = <bool*> malloc(sizeof(bool)*noutputchannels_uldaq)
# self._stream.outputChannelsEnabled = outputChannelsEnabled
# for i in range(firstoutputchannel, lastoutputchannel+1):
# ch_en = output_ch[i].channel_enabled
# if ch_en:
# noutputchannels_forwarded += 1
# outputChannelsEnabled[i] = ch_en
# rtOutputParams_ptr = &self._stream.outputParams
# rtOutputParams_ptr.deviceId = device.index
# rtOutputParams_ptr.nChannels = noutputchannels_uldaq
# rtOutputParams_ptr.firstChannel = firstoutputchannel
# self._stream.outputQueue = new SafeQueue[void*]()
# self._stream.noutputchannels_forwarded = noutputchannels_forwarded
# if monitorOutput and duplex_mode:
# self._stream.ninputchannels_forwarded += noutputchannels_forwarded
# # self._uldaq.openStream(rtOutputParams_ptr,
# # rtInputParams_ptr,
# # _formats_strkey[sampleformat][0],
# # samplerate,
# # &nFramesPerBlock,
# # audioCallback,
# # <void*> self._stream,
# # &streamoptions, # Stream options
# # errorCallback # Error callback
# # )
# # self._stream.nBytesPerChan = nFramesPerBlock*sw
# # self._stream.nFramesPerBlock = nFramesPerBlock
# except Exception as e:
# print('Exception occured in stream opening: ', e)
# self.cleanupStream(self._stream)
# self._stream = NULL
# raise e
# with nogil:
# self._stream.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
# <void*> self._stream)
# # Allow it to start
# CPPsleep(500)
# pass
# return nFramesPerBlock
# cdef cleanupStream(self, _Stream* stream):
# # printf('Entrance function cleanupStream...\n')
# cdef:
# void* ptr
# if stream == NULL:
# return
# with nogil:
# if stream.thread:
# stream.stopThread.store(True)
# if stream.inputQueue:
# # If waiting in the input queue, hereby we let it run.
# stream.inputQueue.enqueue(NULL)
# # printf('Joining thread...\n')
# # HERE WE SHOULD RELEASE THE GIL, as exiting the thread function
# # will require the GIL, which is locked by this thread!
# stream.thread.join()
# # printf('Thread joined!\n')
# del stream.thread
# stream.thread = NULL
# if stream.inputChannelsEnabled:
# free(stream.inputChannelsEnabled)
# if stream.outputChannelsEnabled:
# free(stream.outputChannelsEnabled)
# if stream.outputQueue:
# while not stream.outputQueue.empty():
# free(stream.outputQueue.dequeue())
# del stream.outputQueue
# if stream.inputQueue:
# while not stream.inputQueue.empty():
# free(stream.inputQueue.dequeue())
# del stream.inputQueue
# if stream.outputDelayQueue:
# while not stream.outputDelayQueue.empty():
# free(stream.outputDelayQueue.dequeue())
# del stream.outputDelayQueue
# fprintf(stderr, "End cleanup stream queues...\n")
# if stream.pyCallback:
# Py_DECREF(<object> stream.pyCallback)
# stream.pyCallback = NULL
# # fprintf(stderr, "End cleanup callback...\n")
# free(stream)
# def startStream(self):
# self._uldaq.startStream()
# def stopStream(self):
# if self._stream is NULL:
# raise RuntimeError('Stream is not opened')
# try:
# self._uldaq.stopStream()
# except:
# pass
# def closeStream(self):
# # print('closeStream')
# if self._stream is NULL:
# raise RuntimeError('Stream is not opened')
# # Closing stream
# self._uldaq.closeStream()
# self.cleanupStream(self._stream)
# self._stream = NULL
# def abortStream(self):
# if self._stream is NULL:
# raise RuntimeError('Stream is not opened')
# self._uldaq.abortStream()
# def isStreamOpen(self):
# return self._uldaq.isStreamOpen()
# def isStreamRunning(self):
# return self._uldaq.isStreamRunning()
# def getStreamTime(self):
# return self._uldaq.getStreamTime()
# def setStreamTime(self, double time):
# return self._uldaq.setStreamTime(time)
free(sd)

View File

@ -107,13 +107,9 @@ class AvStream:
# Possible, but long not tested: store video
self._videothread = None
self._rtaudio = RtAudio(daqconfig.api)
self.blocksize = self._rtaudio.openStream(self)
def close(self):
self._rtaudio.closeStream()
self._rtaudio = None
self._audiobackend = RtAudio(daqconfig.api)
self.blocksize, self.samplerate = self._audiobackend.openStream(self)
def nCallbacks(self):
"""Returns the current number of installed callbacks."""
@ -152,7 +148,7 @@ class AvStream:
self._videothread.start()
else:
self._video_started <<= True
self._rtaudio.startStream()
self._audiobackend.start()
def _videoThread(self):
cap = cv.VideoCapture(self._video)
@ -221,7 +217,8 @@ class AvStream:
self._aframectr <<= 0
self._vframectr <<= 0
self._video_started <<= False
self._rtaudio.stopStream()
self._audiobackend.stop()
self._audiobackend = None
def isRunning(self):
return self._running()

View File

@ -27,8 +27,7 @@ config = DAQConfiguration.loadConfigs()[args.input_daq]
print(config)
rtaudio = RtAudio()
count = rtaudio.getDeviceCount()
devices = [rtaudio.getDeviceInfo(i) for i in range(count)]
devices = rtaudio.getDeviceInfo()
input_devices = {}
for device in devices:

View File

@ -18,8 +18,7 @@ from lasp.device import DAQConfiguration, RtAudio
config = DAQConfiguration.loadConfigs()[args.device]
rtaudio = RtAudio()
count = rtaudio.getDeviceCount()
devices = [rtaudio.getDeviceInfo(i) for i in range(count)]
devices = rtaudio.getDeviceInfo()
output_devices = {}
for device in devices: