First work in the direction of getting a recording running again

This commit is contained in:
Anne de Jong 2019-12-17 14:09:45 +01:00
parent 9715f3e844
commit 3ea745245f
6 changed files with 112 additions and 128 deletions

View File

@ -115,7 +115,7 @@ set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py")
set(DEPS "${CMAKE_CURRENT_SOURCE_DIR}/*.py" set(DEPS "${CMAKE_CURRENT_SOURCE_DIR}/*.py"
"${CMAKE_CURRENT_SOURCE_DIR}/lasp/*.py" "${CMAKE_CURRENT_SOURCE_DIR}/lasp/*.py"
"wrappers" "wrappers"
"lasp_daqdevice") "lasp_rtaudio")
# ) # )
set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp") set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp")

View File

@ -1,6 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/python3
__all__ = ['DAQDevice', 'DAQConfiguration', 'configs', 'query_devices', __all__ = ['DAQConfiguration']
'roga_plugndaq', 'default_soundcard'] from .lasp_daqconfig import DAQConfiguration, DAQInputChannel
from .lasp_daqdevice import DAQDevice, query_devices from .lasp_rtaudio import RtAudio
from .lasp_daqconfig import (DAQConfiguration, configs, roga_plugndaq,
default_soundcard)

View File

@ -10,12 +10,30 @@ Data Acquistiion (DAQ) device descriptors, and the DAQ devices themselves
""" """
from dataclasses import dataclass, field from dataclasses import dataclass, field
@dataclass
class DeviceInfo:
index: int
probed: bool
name: str
outputchannels: int
inputchannels: int
duplexchannels: int
samplerates: list
sampleformats: list
prefsamplerate: int
from ..lasp_common import lasp_shelve
@dataclass @dataclass
class DAQInputChannel: class DAQInputChannel:
channel_enabled: bool channel_enabled: bool
channel_name: str channel_name: str
sensitivity: float sensitivity: float
@dataclass @dataclass
class DAQConfiguration: class DAQConfiguration:
""" """
@ -24,6 +42,8 @@ class DAQConfiguration:
Args: Args:
input_device_name: ASCII name with which to open the device when connected input_device_name: ASCII name with which to open the device when connected
outut_device_name: ASCII name with which to open the device when connected outut_device_name: ASCII name with which to open the device when connected
==============================
en_format: index of the format in the list of sample formats en_format: index of the format in the list of sample formats
en_input_rate: index of enabled input sampling frequency [Hz] en_input_rate: index of enabled input sampling frequency [Hz]
in the list of frequencies. in the list of frequencies.
@ -38,61 +58,43 @@ class DAQConfiguration:
en_output_rate: index in the list of possible output sampling en_output_rate: index in the list of possible output sampling
frequencies. frequencies.
en_output_channels: list of enabled output channels en_output_channels: list of enabled output channels
===============================
""" """
input_device_name: bytes duplex_mode: bool
output_device_name: bytes
en_bit_depth: int input_device_name: str
output_device_name: str
en_input_sample_format: str
en_output_sample_format: str
en_input_rate: int en_input_rate: int
en_output_rate: int
en_input_channels: list en_input_channels: list
en_input_gain_settings: list = field(default_factory=list)
en_output_rate: int = -1
def match(self, device):
"""
Returns True when a device specification dictionary matches to the
configuration.
Args:
device: dictionary specifying device settings
"""
match = True
if self.cardlongnamematch is not None:
match &= self.cardlongnamematch in device.cardlongname
if self.cardname is not None:
match &= self.cardname == device.cardname
if self.device_name is not None:
match &= self.device_name == device.device_name
match &= self.en_format < len(device.available_formats)
match &= self.en_input_rate < len(device.available_input_rates)
match &= max(
self.en_input_channels) < device.max_input_channels
if len(self.en_output_channels) > 0:
match &= max(
self.en_output_channels) < device.max_output_channels
match &= self.en_output_rate < len(
device.available_output_rates)
return match
@staticmethod @staticmethod
def emptyFromDeviceAndSettings(device): def loadConfigs():
return DAQConfiguration(
name = 'UNKNOWN'
input_device_name =
def findDAQDevice(config: DAQConfiguration) -> DeviceInfo:
""" """
Search for a DaQ device for the given configuration. Returns a list of currently available configurations
Args:
config: configuration to search a device for
""" """
devices = query_devices() with lasp_shelve() as sh:
return sh['daqconfigs'] if 'daqconfigs' in sh.keys() else {}
def saveConfig(self, name):
with lasp_shelve() as sh:
if 'daqconfigs' in sh.keys():
cur_configs = sh['daqconfigs']
else:
cur_configs = {}
cur_configs[name] = self
sh['daqconfigs'] = cur_configs
@staticmethod
def deleteConfig(name):
with lasp_shelve() as sh:
cur_configs = sh['daqconfigs']
del cur_configs[name]
sh['daqconfigs'] = cur_configs
for device in devices:
if config.match(device):
return device
return None

View File

@ -1,6 +1,7 @@
import sys import sys
include "config.pxi" include "config.pxi"
cimport cython cimport cython
from .lasp_daqconfig import DeviceInfo
from libcpp.string cimport string from libcpp.string cimport string
from libcpp.vector cimport vector from libcpp.vector cimport vector
from libc.string cimport memcpy from libc.string cimport memcpy
@ -99,57 +100,27 @@ cdef extern from "RtAudio.h" nogil:
unsigned int getStreamSampleRate() unsigned int getStreamSampleRate()
void showWarnings(bool value) void showWarnings(bool value)
cdef class SampleFormat: _formats_strkey = {
cdef: '8-bit integers': (RTAUDIO_SINT8, 1),
RtAudioFormat _format '16-bit integers': (RTAUDIO_SINT16,2),
string _name '24-bit integers': (RTAUDIO_SINT24,3),
public: '32-bit integers': (RTAUDIO_SINT32,4),
# The size in bytes of a single audio sample, for the current '32-bit floats': (RTAUDIO_FLOAT32, 4),
# format '64-bit floats': (RTAUDIO_FLOAT64, 8),
unsigned int sampleSize }
_formats_rtkey = {
def __cinit__(self, RtAudioFormat format_): RTAUDIO_SINT8: ('8-bit integers', 1),
self._format = format_ RTAUDIO_SINT16: ('16-bit integers',2),
if format_ == RTAUDIO_SINT8: RTAUDIO_SINT24: ('24-bit integers',3),
self._name = b'8-bit integers' RTAUDIO_SINT32: ('32-bit integers',4),
self.sampleSize = 1 RTAUDIO_FLOAT32: ('32-bit floats', 4),
elif format_ == RTAUDIO_SINT16: RTAUDIO_FLOAT64: ('64-bit floats', 8),
self._name = b'16-bit integers' }
self.sampleSize = 2
elif format_ == RTAUDIO_SINT24:
self._name = b'24-bit integers'
self.sampleSize = 3
elif format_ == RTAUDIO_SINT32:
self._name = b'32-bit integers'
self.sampleSize = 4
elif format_ == RTAUDIO_FLOAT32:
self._name = b'32-bit floats'
self.sampleSize = 4
elif format_ == RTAUDIO_FLOAT64:
self._name = b'64-bit floats'
self.sampleSize = 8
else:
raise ValueError('Invalid RtAudioFormat code')
def __repr__(self):
return self._name.decode('utf-8')
def __str__(self):
return self.__repr__()
# Pre-define format and expose to Python
Format_SINT8 = SampleFormat(RTAUDIO_SINT8)
Format_SINT16 = SampleFormat(RTAUDIO_SINT16)
Format_SINT24 = SampleFormat(RTAUDIO_SINT24)
Format_SINT32 = SampleFormat(RTAUDIO_SINT32)
Format_FLOAT32 = SampleFormat(RTAUDIO_FLOAT32)
Format_FLOAT64 = SampleFormat(RTAUDIO_FLOAT64)
cdef class _Stream: cdef class _Stream:
cdef: cdef:
object pyCallback object pyCallback
RtAudioFormat format unsigned int sampleSize
RtAudioFormat sampleformat
cppRtAudio.StreamParameters inputParams cppRtAudio.StreamParameters inputParams
cppRtAudio.StreamParameters outputParams cppRtAudio.StreamParameters outputParams
# These boolean values tell us whether the structs above here are # These boolean values tell us whether the structs above here are
@ -226,7 +197,7 @@ cdef int audioCallback(void* outputBuffer,
assert outputBuffer != NULL, "Bug: RtAudio does not give output buffer!" assert outputBuffer != NULL, "Bug: RtAudio does not give output buffer!"
assert npy_output.shape[0] == stream.outputParams.nChannels, "Bug: channel mismatch in output buffer!" assert npy_output.shape[0] == stream.outputParams.nChannels, "Bug: channel mismatch in output buffer!"
assert npy_output.shape[1] == nFrames, "Bug: frame mismatch in output buffer!" assert npy_output.shape[1] == nFrames, "Bug: frame mismatch in output buffer!"
assert npy_output.itemsize == stream._format.sampleSize, "Bug: invalid sample type in output buffer!" assert npy_output.itemsize == stream.sampleSize, "Bug: invalid sample type in output buffer!"
except AssertionError as e: except AssertionError as e:
print(e) print(e)
fromNPYToBuffer(npy_output, outputBuffer) fromNPYToBuffer(npy_output, outputBuffer)
@ -236,6 +207,8 @@ cdef void errorCallback(RtAudioError.Type _type,const string& errortxt) nogil:
pass pass
cdef class RtAudio: cdef class RtAudio:
cdef: cdef:
cppRtAudio _rtaudio cppRtAudio _rtaudio
@ -263,31 +236,26 @@ cdef class RtAudio:
Return device information of the current device Return device information of the current device
""" """
cdef cppRtAudio.DeviceInfo devinfo = self._rtaudio.getDeviceInfo(device) cdef cppRtAudio.DeviceInfo devinfo = self._rtaudio.getDeviceInfo(device)
pydevinfo = {}
pydevinfo['index'] = device
pydevinfo['probed'] = devinfo.probed
pydevinfo['name'] = devinfo.name.decode('utf-8')
pydevinfo['outputchannels'] = devinfo.outputChannels
pydevinfo['inputchannels'] = devinfo.inputChannels
pydevinfo['duplexchannels'] = devinfo.duplexChannels
pydevinfo['isdefaultoutput'] = devinfo.isDefaultOutput
pydevinfo['isdefaultinpput'] = devinfo.isDefaultInput
pydevinfo['samplerates'] = devinfo.sampleRates
pydevinfo['prefsamprate'] = devinfo.preferredSampleRate
# Manually add these
nf = devinfo.nativeFormats
sampleformats = [] sampleformats = []
nf = devinfo.nativeFormats
for format_ in [ RTAUDIO_SINT8, RTAUDIO_SINT16, RTAUDIO_SINT24, for format_ in [ RTAUDIO_SINT8, RTAUDIO_SINT16, RTAUDIO_SINT24,
RTAUDIO_SINT32, RTAUDIO_FLOAT32, RTAUDIO_FLOAT64]: RTAUDIO_SINT32, RTAUDIO_FLOAT32, RTAUDIO_FLOAT64]:
if nf & format_: if nf & format_:
sampleformats.append(SampleFormat(format_)) sampleformats.append(_formats_rtkey[format_][0])
return DeviceInfo(
pydevinfo['sampleformats'] = sampleformats index = device,
return pydevinfo probed = devinfo.probed,
name = devinfo.name.decode('utf-8'),
outputchannels = devinfo.outputChannels,
inputchannels = devinfo.inputChannels,
duplexchannels = devinfo.duplexChannels,
samplerates = devinfo.sampleRates,
sampleformats = sampleformats,
prefsamplerate = devinfo.preferredSampleRate)
def openStream(self,object outputParams, def openStream(self,object outputParams,
object inputParams, object inputParams,
SampleFormat format, str sampleformat,
unsigned int sampleRate, unsigned int sampleRate,
unsigned int bufferFrames, unsigned int bufferFrames,
object pyCallback, object pyCallback,
@ -342,7 +310,7 @@ cdef class RtAudio:
self._stream.bufferFrames = bufferFrames self._stream.bufferFrames = bufferFrames
self._rtaudio.openStream(rtOutputParams_ptr, self._rtaudio.openStream(rtOutputParams_ptr,
rtInputParams_ptr, rtInputParams_ptr,
format._format, _formats_strkey[sampleformat][0],
sampleRate, sampleRate,
&self._stream.bufferFrames, &self._stream.bufferFrames,
audioCallback, audioCallback,

View File

@ -7,7 +7,7 @@ import cv2 as cv
from .lasp_atomic import Atomic from .lasp_atomic import Atomic
from threading import Thread, Condition, Lock from threading import Thread, Condition, Lock
import time import time
from .device import DAQDevice, roga_plugndaq from .device import DAQConfiguration
import numpy as np import numpy as np
__all__ = ['AvType', 'AvStream'] __all__ = ['AvType', 'AvStream']
@ -21,9 +21,16 @@ class AvType:
class AvStream: class AvStream:
def __init__(self, daqconfig=roga_plugndaq, video=None): def __init__(self,
rtaudio_input,
rtaudio_output,
input_device,
output_device, daqconfig, video=None):
self.daqconfig = daqconfig self.daqconfig = daqconfig
self.input_device = input_device
self.output_device = output_device
try: try:
daq = DAQDevice(daqconfig) daq = DAQDevice(daqconfig)
self.nchannels = len(daq.channels_en) self.nchannels = len(daq.channels_en)

View File

@ -8,7 +8,7 @@ Common definitions used throughout the code.
""" """
__all__ = ['P_REF', 'FreqWeighting', 'TimeWeighting', 'getTime', __all__ = ['P_REF', 'FreqWeighting', 'TimeWeighting', 'getTime',
'getFreq', 'lasp_shelve' 'getFreq', 'lasp_shelve',
'W_REF', 'U_REF', 'I_REF'] 'W_REF', 'U_REF', 'I_REF']
lasp_appdir = appdirs.user_data_dir('Lasp', 'ASCEE') lasp_appdir = appdirs.user_data_dir('Lasp', 'ASCEE')
@ -23,12 +23,21 @@ if not os.path.exists(lasp_appdir):
class lasp_shelve: class lasp_shelve:
refcount = 0
shelve = None
def __enter__(self): def __enter__(self):
self.shelve = shelve.open(os.path.join(lasp_appdir, 'config.shelve')) if lasp_shelve.shelve is None:
return self.shelve assert lasp_shelve.refcount == 0
lasp_shelve.shelve = shelve.open(os.path.join(lasp_appdir, 'config.shelve'))
lasp_shelve.refcount += 1
return lasp_shelve.shelve
def __exit__(self, type, value, traceback): def __exit__(self, type, value, traceback):
self.shelve.close() lasp_shelve.refcount -= 1
if lasp_shelve.refcount == 0:
lasp_shelve.shelve.close()
lasp_shelve.shelve = None