First work in the direction of getting a recording running again
This commit is contained in:
parent
9715f3e844
commit
3ea745245f
@ -115,7 +115,7 @@ set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py")
|
||||
set(DEPS "${CMAKE_CURRENT_SOURCE_DIR}/*.py"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lasp/*.py"
|
||||
"wrappers"
|
||||
"lasp_daqdevice")
|
||||
"lasp_rtaudio")
|
||||
|
||||
# )
|
||||
set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp")
|
||||
|
@ -1,6 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
__all__ = ['DAQDevice', 'DAQConfiguration', 'configs', 'query_devices',
|
||||
'roga_plugndaq', 'default_soundcard']
|
||||
from .lasp_daqdevice import DAQDevice, query_devices
|
||||
from .lasp_daqconfig import (DAQConfiguration, configs, roga_plugndaq,
|
||||
default_soundcard)
|
||||
__all__ = ['DAQConfiguration']
|
||||
from .lasp_daqconfig import DAQConfiguration, DAQInputChannel
|
||||
from .lasp_rtaudio import RtAudio
|
||||
|
@ -10,12 +10,30 @@ Data Acquistiion (DAQ) device descriptors, and the DAQ devices themselves
|
||||
"""
|
||||
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
|
||||
class DAQInputChannel:
|
||||
channel_enabled: bool
|
||||
channel_name: str
|
||||
sensitivity: float
|
||||
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class DAQConfiguration:
|
||||
"""
|
||||
@ -24,6 +42,8 @@ class DAQConfiguration:
|
||||
Args:
|
||||
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
|
||||
|
||||
==============================
|
||||
en_format: index of the format in the list of sample formats
|
||||
en_input_rate: index of enabled input sampling frequency [Hz]
|
||||
in the list of frequencies.
|
||||
@ -38,61 +58,43 @@ class DAQConfiguration:
|
||||
en_output_rate: index in the list of possible output sampling
|
||||
frequencies.
|
||||
en_output_channels: list of enabled output channels
|
||||
===============================
|
||||
|
||||
|
||||
"""
|
||||
input_device_name: bytes
|
||||
output_device_name: bytes
|
||||
en_bit_depth: int
|
||||
duplex_mode: bool
|
||||
|
||||
input_device_name: str
|
||||
output_device_name: str
|
||||
|
||||
en_input_sample_format: str
|
||||
en_output_sample_format: str
|
||||
en_input_rate: int
|
||||
en_output_rate: int
|
||||
|
||||
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
|
||||
def emptyFromDeviceAndSettings(device):
|
||||
return DAQConfiguration(
|
||||
name = 'UNKNOWN'
|
||||
input_device_name =
|
||||
|
||||
def loadConfigs():
|
||||
"""
|
||||
Returns a list of currently available configurations
|
||||
"""
|
||||
with lasp_shelve() as sh:
|
||||
return sh['daqconfigs'] if 'daqconfigs' in sh.keys() else {}
|
||||
|
||||
def findDAQDevice(config: DAQConfiguration) -> DeviceInfo:
|
||||
"""
|
||||
Search for a DaQ device for the given configuration.
|
||||
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
|
||||
|
||||
Args:
|
||||
config: configuration to search a device for
|
||||
"""
|
||||
devices = query_devices()
|
||||
@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
|
||||
|
@ -1,6 +1,7 @@
|
||||
import sys
|
||||
include "config.pxi"
|
||||
cimport cython
|
||||
from .lasp_daqconfig import DeviceInfo
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libc.string cimport memcpy
|
||||
@ -99,57 +100,27 @@ cdef extern from "RtAudio.h" nogil:
|
||||
unsigned int getStreamSampleRate()
|
||||
void showWarnings(bool value)
|
||||
|
||||
cdef class SampleFormat:
|
||||
cdef:
|
||||
RtAudioFormat _format
|
||||
string _name
|
||||
public:
|
||||
# The size in bytes of a single audio sample, for the current
|
||||
# format
|
||||
unsigned int sampleSize
|
||||
|
||||
def __cinit__(self, RtAudioFormat format_):
|
||||
self._format = format_
|
||||
if format_ == RTAUDIO_SINT8:
|
||||
self._name = b'8-bit integers'
|
||||
self.sampleSize = 1
|
||||
elif format_ == RTAUDIO_SINT16:
|
||||
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)
|
||||
|
||||
_formats_strkey = {
|
||||
'8-bit integers': (RTAUDIO_SINT8, 1),
|
||||
'16-bit integers': (RTAUDIO_SINT16,2),
|
||||
'24-bit integers': (RTAUDIO_SINT24,3),
|
||||
'32-bit integers': (RTAUDIO_SINT32,4),
|
||||
'32-bit floats': (RTAUDIO_FLOAT32, 4),
|
||||
'64-bit floats': (RTAUDIO_FLOAT64, 8),
|
||||
}
|
||||
_formats_rtkey = {
|
||||
RTAUDIO_SINT8: ('8-bit integers', 1),
|
||||
RTAUDIO_SINT16: ('16-bit integers',2),
|
||||
RTAUDIO_SINT24: ('24-bit integers',3),
|
||||
RTAUDIO_SINT32: ('32-bit integers',4),
|
||||
RTAUDIO_FLOAT32: ('32-bit floats', 4),
|
||||
RTAUDIO_FLOAT64: ('64-bit floats', 8),
|
||||
}
|
||||
cdef class _Stream:
|
||||
cdef:
|
||||
object pyCallback
|
||||
RtAudioFormat format
|
||||
unsigned int sampleSize
|
||||
RtAudioFormat sampleformat
|
||||
cppRtAudio.StreamParameters inputParams
|
||||
cppRtAudio.StreamParameters outputParams
|
||||
# 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 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.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:
|
||||
print(e)
|
||||
fromNPYToBuffer(npy_output, outputBuffer)
|
||||
@ -236,6 +207,8 @@ cdef void errorCallback(RtAudioError.Type _type,const string& errortxt) nogil:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
cdef class RtAudio:
|
||||
cdef:
|
||||
cppRtAudio _rtaudio
|
||||
@ -263,31 +236,26 @@ cdef class RtAudio:
|
||||
Return device information of the current 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 = []
|
||||
nf = devinfo.nativeFormats
|
||||
for format_ in [ RTAUDIO_SINT8, RTAUDIO_SINT16, RTAUDIO_SINT24,
|
||||
RTAUDIO_SINT32, RTAUDIO_FLOAT32, RTAUDIO_FLOAT64]:
|
||||
if nf & format_:
|
||||
sampleformats.append(SampleFormat(format_))
|
||||
|
||||
pydevinfo['sampleformats'] = sampleformats
|
||||
return pydevinfo
|
||||
sampleformats.append(_formats_rtkey[format_][0])
|
||||
return DeviceInfo(
|
||||
index = device,
|
||||
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,
|
||||
object inputParams,
|
||||
SampleFormat format,
|
||||
str sampleformat,
|
||||
unsigned int sampleRate,
|
||||
unsigned int bufferFrames,
|
||||
object pyCallback,
|
||||
@ -342,7 +310,7 @@ cdef class RtAudio:
|
||||
self._stream.bufferFrames = bufferFrames
|
||||
self._rtaudio.openStream(rtOutputParams_ptr,
|
||||
rtInputParams_ptr,
|
||||
format._format,
|
||||
_formats_strkey[sampleformat][0],
|
||||
sampleRate,
|
||||
&self._stream.bufferFrames,
|
||||
audioCallback,
|
||||
|
@ -7,7 +7,7 @@ import cv2 as cv
|
||||
from .lasp_atomic import Atomic
|
||||
from threading import Thread, Condition, Lock
|
||||
import time
|
||||
from .device import DAQDevice, roga_plugndaq
|
||||
from .device import DAQConfiguration
|
||||
import numpy as np
|
||||
__all__ = ['AvType', 'AvStream']
|
||||
|
||||
@ -21,9 +21,16 @@ class AvType:
|
||||
|
||||
|
||||
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.input_device = input_device
|
||||
self.output_device = output_device
|
||||
try:
|
||||
daq = DAQDevice(daqconfig)
|
||||
self.nchannels = len(daq.channels_en)
|
||||
|
@ -8,7 +8,7 @@ Common definitions used throughout the code.
|
||||
"""
|
||||
|
||||
__all__ = ['P_REF', 'FreqWeighting', 'TimeWeighting', 'getTime',
|
||||
'getFreq', 'lasp_shelve'
|
||||
'getFreq', 'lasp_shelve',
|
||||
'W_REF', 'U_REF', 'I_REF']
|
||||
|
||||
lasp_appdir = appdirs.user_data_dir('Lasp', 'ASCEE')
|
||||
@ -23,12 +23,21 @@ if not os.path.exists(lasp_appdir):
|
||||
|
||||
|
||||
class lasp_shelve:
|
||||
refcount = 0
|
||||
shelve = None
|
||||
|
||||
def __enter__(self):
|
||||
self.shelve = shelve.open(os.path.join(lasp_appdir, 'config.shelve'))
|
||||
return self.shelve
|
||||
if lasp_shelve.shelve is None:
|
||||
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):
|
||||
self.shelve.close()
|
||||
lasp_shelve.refcount -= 1
|
||||
if lasp_shelve.refcount == 0:
|
||||
lasp_shelve.shelve.close()
|
||||
lasp_shelve.shelve = None
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user