In between state of connecting DAQ Devices
This commit is contained in:
parent
e95b02ae9a
commit
64821f8c6f
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,3 +21,5 @@ LASP.egg-info
|
|||||||
lasp_octave_fir.*
|
lasp_octave_fir.*
|
||||||
lasp/ui_*
|
lasp/ui_*
|
||||||
resources_rc.py
|
resources_rc.py
|
||||||
|
lasp/device/lasp_daqdevice.c
|
||||||
|
lasp/lasp_daqwidget.py
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
add_subdirectory(c)
|
|
||||||
configure_file(config.pxi.in config.pxi)
|
configure_file(config.pxi.in config.pxi)
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
find_package(PythonLibs REQUIRED )
|
find_package(PythonLibs REQUIRED )
|
||||||
@ -10,6 +10,8 @@ include_directories(
|
|||||||
.
|
.
|
||||||
c
|
c
|
||||||
)
|
)
|
||||||
|
add_subdirectory(c)
|
||||||
|
add_subdirectory(device)
|
||||||
|
|
||||||
# add the command to generate the source code
|
# add the command to generate the source code
|
||||||
# add_custom_command (
|
# add_custom_command (
|
||||||
@ -18,7 +20,7 @@ include_directories(
|
|||||||
# DEPENDS MakeTable
|
# DEPENDS MakeTable
|
||||||
# )
|
# )
|
||||||
|
|
||||||
set(ui_files ui_apsrt ui_mainwindow ui_figure ui_about ui_apswidget ui_revtime ui_slmwidget)
|
set(ui_files ui_apsrt ui_mainwindow ui_figure ui_about ui_apswidget ui_revtime ui_slmwidget ui_daq)
|
||||||
foreach(fn ${ui_files})
|
foreach(fn ${ui_files})
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT "${fn}.py"
|
OUTPUT "${fn}.py"
|
||||||
|
4
lasp/device/CMakeLists.txt
Normal file
4
lasp/device/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
set_source_files_properties(lasp_daqdevice.c PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CYTHON_EXTRA_C_FLAGS}")
|
||||||
|
cython_add_module(lasp_daqdevice lasp_daqdevice.pyx)
|
||||||
|
|
||||||
|
target_link_libraries(lasp_daqdevice asound)
|
6
lasp/device/__init__.py
Normal file
6
lasp/device/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/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)
|
138
lasp/device/lasp_daqconfig.py
Normal file
138
lasp/device/lasp_daqconfig.py
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""!
|
||||||
|
Author: J.A. de Jong - ASCEE
|
||||||
|
|
||||||
|
Description:
|
||||||
|
|
||||||
|
Data Acquistiion (DAQ) device descriptors, and the DAQ devices themselves
|
||||||
|
|
||||||
|
"""
|
||||||
|
__all__ = ['DAQConfiguration', 'roga_plugndaq', 'default_soundcard']
|
||||||
|
|
||||||
|
|
||||||
|
class DAQConfiguration:
|
||||||
|
def __init__(self, name,
|
||||||
|
cardname,
|
||||||
|
cardlongnamematch,
|
||||||
|
device_name,
|
||||||
|
en_format,
|
||||||
|
en_input_rate,
|
||||||
|
en_input_channels,
|
||||||
|
|
||||||
|
input_sensitivity,
|
||||||
|
input_gain_settings,
|
||||||
|
en_input_gain_setting,
|
||||||
|
|
||||||
|
en_output_rate,
|
||||||
|
en_output_channels):
|
||||||
|
"""
|
||||||
|
Initialize a device descriptor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: Name of the device to appear to the user
|
||||||
|
cardname: ALSA name identifier
|
||||||
|
cardlongnamematch: Long name according to ALSA
|
||||||
|
device_name: ASCII name with which to open the device when connected
|
||||||
|
en_format: index in the list of sample formats
|
||||||
|
en_input_rate: index of enabled input sampling frequency [Hz]
|
||||||
|
in the list of frequencies.
|
||||||
|
en_input_channels: list of channel indices which are used to
|
||||||
|
acquire data from.
|
||||||
|
input_sensitivity: List of sensitivity values, in units of [Pa^-1]
|
||||||
|
input_gain_setting: If a DAQ supports it, list of indices which
|
||||||
|
corresponds to a position in the possible input
|
||||||
|
gains for each channel. Should only be not equal
|
||||||
|
to None when the hardware supports changing the
|
||||||
|
input gain.
|
||||||
|
en_output_rate: index in the list of possible output sampling
|
||||||
|
frequencies.
|
||||||
|
en_output_channels: list of enabled output channels
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.name = name
|
||||||
|
self.cardlongnamematch = cardlongnamematch
|
||||||
|
self.cardname = cardname
|
||||||
|
self.device_name = device_name
|
||||||
|
self.en_format = en_format
|
||||||
|
|
||||||
|
self.en_input_rate = en_input_rate
|
||||||
|
self.en_input_channels = en_input_channels
|
||||||
|
|
||||||
|
self.input_sensitivity = input_sensitivity
|
||||||
|
self.input_gain_settings = input_gain_settings
|
||||||
|
|
||||||
|
self.en_output_rate = en_output_rate
|
||||||
|
self.en_output_channels = en_output_channels
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""
|
||||||
|
String representation of configuration
|
||||||
|
"""
|
||||||
|
rep = f"""Name: {self.name}
|
||||||
|
Enabled input channels: {self.en_input_channels}
|
||||||
|
Enabled input sampling frequency: {self.en_input_rate}
|
||||||
|
Input gain settings: {self.input_gain_settings}
|
||||||
|
Sensitivity: {self.input_sensitivity}
|
||||||
|
"""
|
||||||
|
return rep
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
roga_plugndaq = DAQConfiguration(name='Roga-instruments Plug.n.DAQ USB',
|
||||||
|
cardname='USB Audio CODEC',
|
||||||
|
cardlongnamematch='Burr-Brown from TI USB'
|
||||||
|
' Audio CODEC',
|
||||||
|
device_name='iec958:CARD=CODEC,DEV=0',
|
||||||
|
en_format=0,
|
||||||
|
en_input_rate=2,
|
||||||
|
en_input_channels=[0],
|
||||||
|
input_sensitivity=[1., 1.],
|
||||||
|
input_gain_settings=[-20, 0, 20],
|
||||||
|
en_input_gain_setting=[1, 1],
|
||||||
|
en_output_rate=1,
|
||||||
|
en_output_channels=[False, False]
|
||||||
|
)
|
||||||
|
|
||||||
|
default_soundcard = DAQConfiguration(name="Default device",
|
||||||
|
cardname=None,
|
||||||
|
cardlongnamematch=None,
|
||||||
|
device_name='default',
|
||||||
|
en_format=0,
|
||||||
|
en_input_rate=2,
|
||||||
|
en_input_channels=[0, 1],
|
||||||
|
input_sensitivity=[1., 1.],
|
||||||
|
input_gain_settings=[0],
|
||||||
|
en_input_gain_setting=[0, 0],
|
||||||
|
en_output_rate=1,
|
||||||
|
en_output_channels=[]
|
||||||
|
)
|
||||||
|
configs = (roga_plugndaq, default_soundcard)
|
498
lasp/device/lasp_daqdevice.pyx
Normal file
498
lasp/device/lasp_daqdevice.pyx
Normal file
@ -0,0 +1,498 @@
|
|||||||
|
include "config.pxi"
|
||||||
|
from libc.stdlib cimport malloc, free
|
||||||
|
from libc.stdio cimport printf, stderr, fprintf
|
||||||
|
import sys
|
||||||
|
import numpy as np
|
||||||
|
cimport numpy as cnp
|
||||||
|
|
||||||
|
__all__ = ['DAQDevice']
|
||||||
|
|
||||||
|
from libc.errno cimport EPIPE, EBADFD, ESTRPIPE
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "alsa/asoundlib.h":
|
||||||
|
int snd_card_get_longname(int index,char** name)
|
||||||
|
int snd_card_get_name(int index,char** name)
|
||||||
|
int snd_card_next(int* rcard)
|
||||||
|
|
||||||
|
ctypedef struct snd_pcm_t
|
||||||
|
ctypedef struct snd_pcm_info_t
|
||||||
|
ctypedef struct snd_pcm_hw_params_t
|
||||||
|
ctypedef enum snd_pcm_stream_t:
|
||||||
|
SND_PCM_STREAM_PLAYBACK
|
||||||
|
SND_PCM_STREAM_CAPTURE
|
||||||
|
ctypedef enum snd_pcm_format_t:
|
||||||
|
SND_PCM_FORMAT_S16_LE
|
||||||
|
SND_PCM_FORMAT_S16_BE
|
||||||
|
SND_PCM_FORMAT_U16_LE
|
||||||
|
SND_PCM_FORMAT_U16_BE
|
||||||
|
SND_PCM_FORMAT_S24_LE
|
||||||
|
SND_PCM_FORMAT_S24_BE
|
||||||
|
SND_PCM_FORMAT_U24_LE
|
||||||
|
SND_PCM_FORMAT_U24_BE
|
||||||
|
SND_PCM_FORMAT_S32_LE
|
||||||
|
SND_PCM_FORMAT_S32_BE
|
||||||
|
SND_PCM_FORMAT_U32_LE
|
||||||
|
SND_PCM_FORMAT_U32_BE
|
||||||
|
SND_PCM_FORMAT_S24_3LE
|
||||||
|
SND_PCM_FORMAT_S24_3BE
|
||||||
|
SND_PCM_FORMAT_U24_3LE
|
||||||
|
SND_PCM_FORMAT_U24_3BE
|
||||||
|
SND_PCM_FORMAT_S16
|
||||||
|
SND_PCM_FORMAT_U16
|
||||||
|
SND_PCM_FORMAT_S24
|
||||||
|
SND_PCM_FORMAT_U24
|
||||||
|
const char* snd_pcm_format_name (snd_pcm_format_t format)
|
||||||
|
ctypedef enum snd_pcm_access_t:
|
||||||
|
SND_PCM_ACCESS_RW_INTERLEAVED
|
||||||
|
ctypedef unsigned long snd_pcm_uframes_t
|
||||||
|
int snd_pcm_open(snd_pcm_t** pcm,char* name, snd_pcm_stream_t type, int mode)
|
||||||
|
int snd_pcm_close(snd_pcm_t*)
|
||||||
|
|
||||||
|
int snd_pcm_hw_params_set_access(snd_pcm_t*, snd_pcm_hw_params_t*, snd_pcm_access_t)
|
||||||
|
void snd_pcm_hw_params_alloca(snd_pcm_hw_params_t**)
|
||||||
|
int snd_pcm_hw_params_any(snd_pcm_t*, snd_pcm_hw_params_t* params)
|
||||||
|
int snd_pcm_hw_params_current(snd_pcm_t*, snd_pcm_hw_params_t* params)
|
||||||
|
int snd_pcm_hw_params_set_rate_resample(snd_pcm_t*, snd_pcm_hw_params_t*, int)
|
||||||
|
int snd_pcm_hw_params_set_rate(snd_pcm_t*, snd_pcm_hw_params_t*,unsigned int val,int dir)
|
||||||
|
int snd_pcm_hw_params_set_format(snd_pcm_t*, snd_pcm_hw_params_t*, snd_pcm_format_t)
|
||||||
|
int snd_pcm_hw_params_set_channels(snd_pcm_t*, snd_pcm_hw_params_t*, unsigned val)
|
||||||
|
int snd_pcm_hw_params_set_period_size_near(snd_pcm_t*,snd_pcm_hw_params_t*,
|
||||||
|
snd_pcm_uframes_t*,int* dir)
|
||||||
|
int snd_pcm_hw_params_get_period_size(snd_pcm_hw_params_t*,
|
||||||
|
snd_pcm_uframes_t*,int* dir)
|
||||||
|
int snd_pcm_info(snd_pcm_t*, snd_pcm_info_t*)
|
||||||
|
void snd_pcm_info_alloca(snd_pcm_info_t**)
|
||||||
|
int snd_pcm_info_get_card(snd_pcm_info_t*)
|
||||||
|
|
||||||
|
int snd_pcm_drain(snd_pcm_t*)
|
||||||
|
int snd_pcm_readi(snd_pcm_t*,void* buf,snd_pcm_uframes_t nframes)
|
||||||
|
int snd_pcm_hw_params(snd_pcm_t*,snd_pcm_hw_params_t*)
|
||||||
|
int snd_pcm_hw_params_test_rate(snd_pcm_t*, snd_pcm_hw_params_t*,
|
||||||
|
unsigned int val,int dir)
|
||||||
|
int snd_pcm_hw_params_test_format(snd_pcm_t*, snd_pcm_hw_params_t*,
|
||||||
|
snd_pcm_format_t)
|
||||||
|
int snd_pcm_hw_params_get_channels_max(snd_pcm_hw_params_t*,unsigned int*)
|
||||||
|
int snd_device_name_hint(int card, const char* iface, void*** hints)
|
||||||
|
char* snd_device_name_get_hint(void* hint, const char* id)
|
||||||
|
int snd_device_name_free_hint(void**)
|
||||||
|
char* snd_strerror(int rval)
|
||||||
|
|
||||||
|
# Check for these sample rates
|
||||||
|
check_rates = [8000, 44100, 48000, 96000, 19200]
|
||||||
|
|
||||||
|
# First value in tuple: number of significant bits
|
||||||
|
# Second value: number of bits used in memory
|
||||||
|
# Third value: S for signed, U for unsigned, L for little endian,
|
||||||
|
# and B for big endian.
|
||||||
|
check_formats = {SND_PCM_FORMAT_S16_LE: (16,16,'SL'),
|
||||||
|
SND_PCM_FORMAT_S16_BE: (16,16,'SB'),
|
||||||
|
SND_PCM_FORMAT_U16_LE: (16,16,'UL'),
|
||||||
|
SND_PCM_FORMAT_U16_BE: (16,16,'UB'),
|
||||||
|
SND_PCM_FORMAT_S24_LE: (24,32,'SL'),
|
||||||
|
SND_PCM_FORMAT_S24_BE: (24,32,'SB'),
|
||||||
|
SND_PCM_FORMAT_U24_LE: (24,32,'UL'),
|
||||||
|
SND_PCM_FORMAT_U24_BE: (24,32,'UB'),
|
||||||
|
SND_PCM_FORMAT_S32_LE: (32,32,'SL'),
|
||||||
|
SND_PCM_FORMAT_S32_BE: (32,32,'SB'),
|
||||||
|
SND_PCM_FORMAT_U32_LE: (32,32,'UL'),
|
||||||
|
SND_PCM_FORMAT_U32_BE: (32,32,'UB'),
|
||||||
|
SND_PCM_FORMAT_S24_3LE: (24,24,'SL'),
|
||||||
|
SND_PCM_FORMAT_S24_3BE: (24,24,'SB'),
|
||||||
|
SND_PCM_FORMAT_U24_3LE: (24,24,'UL'),
|
||||||
|
SND_PCM_FORMAT_U24_3BE: (24,24,'UB')}
|
||||||
|
|
||||||
|
devices_opened_card = [False, False, False, False, False, False]
|
||||||
|
|
||||||
|
cdef snd_pcm_t* open_device(char* name,
|
||||||
|
snd_pcm_stream_t streamtype):
|
||||||
|
"""
|
||||||
|
Helper function to properly open the first device of a card
|
||||||
|
"""
|
||||||
|
cdef snd_pcm_t* pcm
|
||||||
|
# if name in devices_opened:
|
||||||
|
# raise RuntimeError('Device %s is already opened.' %name)
|
||||||
|
cdef int rval = snd_pcm_open(&pcm, name, streamtype, 0)
|
||||||
|
if rval == 0:
|
||||||
|
return pcm
|
||||||
|
else:
|
||||||
|
return NULL
|
||||||
|
|
||||||
|
cdef int close_device(snd_pcm_t* dev):
|
||||||
|
rval = snd_pcm_close(dev)
|
||||||
|
if rval:
|
||||||
|
print('Error closing device')
|
||||||
|
return rval
|
||||||
|
|
||||||
|
class DeviceInfo:
|
||||||
|
"""
|
||||||
|
Will later be replaced by a dataclass. Storage container for a lot of
|
||||||
|
device parameters.
|
||||||
|
"""
|
||||||
|
def __repr__(self):
|
||||||
|
rep = f"""Device name: {self.device_name}
|
||||||
|
"""
|
||||||
|
return rep
|
||||||
|
|
||||||
|
def getDeviceInfo(char* device_name):
|
||||||
|
"""
|
||||||
|
Open the PCM device for both capture and playback, extract the number of
|
||||||
|
channels, the samplerates and encoding
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cardindex: Card number of device, numbered by ALSA
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
cdef:
|
||||||
|
snd_pcm_t* pcm
|
||||||
|
snd_pcm_hw_params_t* hwparams
|
||||||
|
snd_pcm_info_t* info
|
||||||
|
int rval, cardindex
|
||||||
|
unsigned max_input_channels=0, max_output_channels=0
|
||||||
|
char* c_cardname
|
||||||
|
char *c_cardlongname
|
||||||
|
|
||||||
|
deviceinfo = DeviceInfo()
|
||||||
|
deviceinfo.device_name = device_name.decode('ASCII')
|
||||||
|
pcm = open_device(device_name, SND_PCM_STREAM_CAPTURE)
|
||||||
|
if not pcm:
|
||||||
|
raise RuntimeError('Unable to open device')
|
||||||
|
|
||||||
|
snd_pcm_info_alloca(&info)
|
||||||
|
rval = snd_pcm_info(pcm, info)
|
||||||
|
if rval:
|
||||||
|
snd_pcm_close(pcm)
|
||||||
|
raise RuntimeError('Unable to obtain device info')
|
||||||
|
|
||||||
|
cardindex = snd_pcm_info_get_card(info)
|
||||||
|
cardname = ''
|
||||||
|
cardlongname = ''
|
||||||
|
if cardindex >= 0:
|
||||||
|
snd_card_get_name(cardindex, &c_cardname)
|
||||||
|
if c_cardname:
|
||||||
|
cardname = c_cardname.decode('ASCII')
|
||||||
|
printf('name: %s\n', c_cardname)
|
||||||
|
free(c_cardname)
|
||||||
|
|
||||||
|
rval = snd_card_get_longname(cardindex, &c_cardlongname)
|
||||||
|
if c_cardlongname:
|
||||||
|
printf('longname: %s\n', c_cardlongname)
|
||||||
|
cardlongname = c_cardlongname.decode('ASCII')
|
||||||
|
free(c_cardlongname)
|
||||||
|
deviceinfo.cardname = cardname
|
||||||
|
deviceinfo.cardlongname = cardlongname
|
||||||
|
|
||||||
|
# Check hardware parameters
|
||||||
|
snd_pcm_hw_params_alloca(&hwparams)
|
||||||
|
|
||||||
|
# Nothing said about the return value of this function in the documentation
|
||||||
|
snd_pcm_hw_params_any(pcm, hwparams)
|
||||||
|
|
||||||
|
# Check available sample formats
|
||||||
|
available_formats = []
|
||||||
|
for format in check_formats.keys():
|
||||||
|
rval = snd_pcm_hw_params_test_format(pcm, hwparams, format)
|
||||||
|
if rval == 0:
|
||||||
|
available_formats.append(check_formats[format])
|
||||||
|
deviceinfo.available_formats = available_formats
|
||||||
|
# # Restrict a configuration space to contain only real hardware rates.
|
||||||
|
# rval = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 0)
|
||||||
|
# if rval !=0:
|
||||||
|
# fprintf(stderr, 'Unable disable resampling rates')
|
||||||
|
|
||||||
|
# Check available input sample rates
|
||||||
|
available_input_rates = []
|
||||||
|
for rate in check_rates:
|
||||||
|
rval = snd_pcm_hw_params_test_rate(pcm, hwparams, rate, 0)
|
||||||
|
if rval == 0:
|
||||||
|
available_input_rates.append(rate)
|
||||||
|
deviceinfo.available_input_rates = available_input_rates
|
||||||
|
|
||||||
|
rval = snd_pcm_hw_params_get_channels_max(hwparams, &max_input_channels)
|
||||||
|
if rval != 0:
|
||||||
|
fprintf(stderr, "Could not obtain max input channels\n")
|
||||||
|
deviceinfo.max_input_channels = max_input_channels
|
||||||
|
|
||||||
|
# Close device
|
||||||
|
rval = snd_pcm_close(pcm)
|
||||||
|
if rval:
|
||||||
|
fprintf(stderr, 'Unable to close pcm device.\n')
|
||||||
|
|
||||||
|
deviceinfo.available_output_rates = []
|
||||||
|
deviceinfo.max_output_channels = 0
|
||||||
|
|
||||||
|
# ###############################################################
|
||||||
|
# Open device for output
|
||||||
|
pcm = open_device(device_name, SND_PCM_STREAM_CAPTURE)
|
||||||
|
if pcm == NULL:
|
||||||
|
# We are unable to open the device for playback, but we were able to
|
||||||
|
# open in for capture. So this is a valid device.
|
||||||
|
return deviceinfo
|
||||||
|
|
||||||
|
snd_pcm_hw_params_any(pcm, hwparams)
|
||||||
|
# Restrict a configuration space to contain only real hardware rates.
|
||||||
|
# rval = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 0)
|
||||||
|
# if rval != 0:
|
||||||
|
# fprintf(stderr, 'Unable disable resampling rates')
|
||||||
|
|
||||||
|
# Check available input sample rates
|
||||||
|
available_output_rates = []
|
||||||
|
for rate in check_rates:
|
||||||
|
rval = snd_pcm_hw_params_test_rate(pcm, hwparams, rate, 0)
|
||||||
|
if rval == 0:
|
||||||
|
available_output_rates.append(rate)
|
||||||
|
deviceinfo.available_output_rates = available_output_rates
|
||||||
|
rval = snd_pcm_hw_params_get_channels_max(hwparams, &max_output_channels)
|
||||||
|
if rval != 0:
|
||||||
|
fprintf(stderr, "Could not obtain max output channels")
|
||||||
|
deviceinfo.max_output_channels = max_output_channels
|
||||||
|
|
||||||
|
# Close device
|
||||||
|
rval = close_device(pcm)
|
||||||
|
if rval:
|
||||||
|
fprintf(stderr, 'Unable to close pcm device %s.', device_name)
|
||||||
|
|
||||||
|
return deviceinfo
|
||||||
|
|
||||||
|
|
||||||
|
def query_devices():
|
||||||
|
"""
|
||||||
|
Returns a list of available DAQ devices, where each device is represented
|
||||||
|
by a dictionary containing parameters of the device
|
||||||
|
"""
|
||||||
|
|
||||||
|
devices = []
|
||||||
|
|
||||||
|
cdef:
|
||||||
|
# Start cardindex at -1, such that the first one is picked by
|
||||||
|
# snd_card_next()
|
||||||
|
int cardindex = -1, rval=0, i=0
|
||||||
|
void** namehints_opaque
|
||||||
|
char** namehints
|
||||||
|
char* c_device_name
|
||||||
|
|
||||||
|
rval = snd_device_name_hint(-1, "pcm", &namehints_opaque)
|
||||||
|
if rval:
|
||||||
|
raise RuntimeError('Could not obtain name hints for card %i.'
|
||||||
|
%cardindex)
|
||||||
|
|
||||||
|
namehints = <char**> namehints_opaque
|
||||||
|
while namehints[i] != NULL:
|
||||||
|
# printf('namehint[i]: %s\n', namehints[i])
|
||||||
|
c_device_name = snd_device_name_get_hint(namehints[i], "NAME")
|
||||||
|
c_descr = snd_device_name_get_hint(namehints[i], "DESC")
|
||||||
|
if c_device_name:
|
||||||
|
device_name = c_device_name.decode('ASCII')
|
||||||
|
if c_descr:
|
||||||
|
device_desc = c_descr.decode('ASCII')
|
||||||
|
free(c_descr)
|
||||||
|
else:
|
||||||
|
device_desc = ''
|
||||||
|
try:
|
||||||
|
device = getDeviceInfo(c_device_name)
|
||||||
|
printf('device name: %s\n', c_device_name)
|
||||||
|
devices.append(device)
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
free(c_device_name)
|
||||||
|
|
||||||
|
i+=1
|
||||||
|
|
||||||
|
snd_device_name_free_hint(namehints_opaque)
|
||||||
|
|
||||||
|
return devices
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cdef class DAQDevice:
|
||||||
|
cdef:
|
||||||
|
snd_pcm_t* pcm
|
||||||
|
int device_index
|
||||||
|
object device, config
|
||||||
|
public snd_pcm_uframes_t blocksize
|
||||||
|
object callback
|
||||||
|
|
||||||
|
|
||||||
|
def __cinit(self):
|
||||||
|
self.pcm = NULL
|
||||||
|
|
||||||
|
def __init__(self, config, blocksize=2048):
|
||||||
|
"""
|
||||||
|
Initialize the DAQ device
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: DAQConfiguration instance
|
||||||
|
blocksize: Number of frames in a single acquisition block
|
||||||
|
callback: callback used to send data frames to
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.config = config
|
||||||
|
devices = query_devices()
|
||||||
|
|
||||||
|
self.device = None
|
||||||
|
for device in devices:
|
||||||
|
if self.config.match(device):
|
||||||
|
# To open the underlying PCM device
|
||||||
|
device_name = device.device_name.encode('ASCII')
|
||||||
|
self.device = device
|
||||||
|
|
||||||
|
if self.device is None:
|
||||||
|
raise RuntimeError(f'Device {self.config.name} is not available')
|
||||||
|
# if devices_opened[device_index]:
|
||||||
|
# raise RuntimeError(f'Device {self.config.name} is already opened')
|
||||||
|
# print('device_name opened:', device_name)
|
||||||
|
self.pcm = open_device(device_name, SND_PCM_STREAM_CAPTURE)
|
||||||
|
|
||||||
|
# Device is opened. We are going to configure
|
||||||
|
cdef:
|
||||||
|
snd_pcm_hw_params_t* params
|
||||||
|
int rval
|
||||||
|
snd_pcm_format_t format_code
|
||||||
|
snd_pcm_uframes_t period_size
|
||||||
|
snd_pcm_hw_params_alloca(¶ms);
|
||||||
|
|
||||||
|
# Fill it in with default values.
|
||||||
|
snd_pcm_hw_params_any(self.pcm, params);
|
||||||
|
|
||||||
|
# Set access interleaved
|
||||||
|
rval = snd_pcm_hw_params_set_access(self.pcm, params,
|
||||||
|
SND_PCM_ACCESS_RW_INTERLEAVED)
|
||||||
|
if rval != 0:
|
||||||
|
snd_pcm_close(self.pcm)
|
||||||
|
raise RuntimeError('Could not set access mode to interleaved')
|
||||||
|
|
||||||
|
# Set sampling frequency
|
||||||
|
cdef unsigned int rate
|
||||||
|
rate = device.available_input_rates[config.en_input_rate]
|
||||||
|
# printf('Set sample rate: %i\n', rate)
|
||||||
|
rval = snd_pcm_hw_params_set_rate(self.pcm,params, rate, 0)
|
||||||
|
if rval != 0:
|
||||||
|
snd_pcm_close(self.pcm)
|
||||||
|
raise RuntimeError('Could not set input sampling frequency')
|
||||||
|
|
||||||
|
# Set number of channels
|
||||||
|
channels_max = max(self.channels_en)+1
|
||||||
|
# print('channels_max:', channels_max)
|
||||||
|
if channels_max > self.device.max_input_channels:
|
||||||
|
snd_pcm_close(self.pcm)
|
||||||
|
raise ValueError('Highest required channel is larger than available'
|
||||||
|
' channels.')
|
||||||
|
rval = snd_pcm_hw_params_set_channels(self.pcm, params, channels_max)
|
||||||
|
if rval != 0:
|
||||||
|
snd_pcm_close(self.pcm)
|
||||||
|
raise RuntimeError('Could not set input channels, highest required'
|
||||||
|
' input channel: %i.' %channels_max)
|
||||||
|
|
||||||
|
# Find the format description
|
||||||
|
format_descr = self.device.available_formats[config.en_format]
|
||||||
|
# Obtain key from value of dictionary
|
||||||
|
|
||||||
|
format_code = list(check_formats.keys())[list(check_formats.values()).index(format_descr)]
|
||||||
|
# printf('Format code: %s\n', snd_pcm_format_name(format_code))
|
||||||
|
|
||||||
|
# print(format)
|
||||||
|
rval = snd_pcm_hw_params_set_format(self.pcm, params, format_code)
|
||||||
|
if rval != 0:
|
||||||
|
fprintf(stderr, "Could not set format: %s.", snd_strerror(rval))
|
||||||
|
|
||||||
|
# Set period size
|
||||||
|
cdef int dir = 0
|
||||||
|
bytedepth = format_descr[1]//8
|
||||||
|
# print('byte depth:', bytedepth)
|
||||||
|
period_size = blocksize
|
||||||
|
rval = snd_pcm_hw_params_set_period_size_near(self.pcm,
|
||||||
|
params,
|
||||||
|
&period_size, &dir)
|
||||||
|
|
||||||
|
if rval != 0:
|
||||||
|
snd_pcm_close(self.pcm)
|
||||||
|
raise RuntimeError("Could not set period size: %s."
|
||||||
|
%snd_strerror(rval))
|
||||||
|
|
||||||
|
# Write the parameters to the driver
|
||||||
|
rc = snd_pcm_hw_params(self.pcm, params);
|
||||||
|
if (rc < 0):
|
||||||
|
snd_pcm_close(self.pcm)
|
||||||
|
raise RuntimeError('Could not set hw parameters: %s' %snd_strerror(rc))
|
||||||
|
|
||||||
|
# Check the block size again, and store it
|
||||||
|
snd_pcm_hw_params_get_period_size(params, &self.blocksize,
|
||||||
|
&dir)
|
||||||
|
# print('Period size:', self.blocksize)
|
||||||
|
|
||||||
|
cdef object _getEmptyBuffer(self):
|
||||||
|
"""
|
||||||
|
Return right size empty buffer
|
||||||
|
"""
|
||||||
|
format_descr = self.device.available_formats[self.config.en_format]
|
||||||
|
LB = format_descr[2][1]
|
||||||
|
assert LB == 'L', 'Only little-endian data format supported'
|
||||||
|
if format_descr[1] == 16:
|
||||||
|
dtype = np.int16
|
||||||
|
elif format_descr[1] == 32:
|
||||||
|
dtype = np.int32
|
||||||
|
|
||||||
|
# interleaved data, order = C
|
||||||
|
return np.zeros((self.blocksize,
|
||||||
|
max(self.channels_en)+1),
|
||||||
|
dtype=dtype, order='C')
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
cdef int rval = 0
|
||||||
|
buf = self._getEmptyBuffer()
|
||||||
|
# buf2 = self._getEmptyBuffer()
|
||||||
|
cdef cnp.int16_t[:, ::1] bufv = buf
|
||||||
|
rval = snd_pcm_readi(self.pcm,<void*> &bufv[0, 0], self.blocksize)
|
||||||
|
# rval = 2048
|
||||||
|
if rval > 0:
|
||||||
|
# print('Samples obtained:' , rval)
|
||||||
|
return buf[:rval, self.channels_en]
|
||||||
|
|
||||||
|
# return buf
|
||||||
|
elif rval == -EPIPE:
|
||||||
|
raise RuntimeError('Error: buffer overrun: %s',
|
||||||
|
snd_strerror(rval))
|
||||||
|
elif rval == -EBADFD:
|
||||||
|
raise RuntimeError('Error: could not read from DAQ Device: %s',
|
||||||
|
snd_strerror(rval))
|
||||||
|
elif rval == -ESTRPIPE:
|
||||||
|
raise RuntimeError('Error: could not read from DAQ Device: %s',
|
||||||
|
snd_strerror(rval))
|
||||||
|
|
||||||
|
|
||||||
|
def __dealloc__(self):
|
||||||
|
# printf("dealloc\n")
|
||||||
|
cdef int rval
|
||||||
|
if self.pcm:
|
||||||
|
# print('Closing pcm')
|
||||||
|
# snd_pcm_drain(self.pcm)
|
||||||
|
rval = snd_pcm_close(self.pcm)
|
||||||
|
# devices_opened[self.device_index] = False
|
||||||
|
if rval != 0:
|
||||||
|
fprintf(stderr, 'Unable to properly close device: %s\n',
|
||||||
|
snd_strerror(rval))
|
||||||
|
self.pcm = NULL
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nchannels_all(self):
|
||||||
|
return self.device.max_input_channels
|
||||||
|
|
||||||
|
@property
|
||||||
|
def channels_en(self):
|
||||||
|
return self.config.en_input_channels
|
||||||
|
|
||||||
|
@property
|
||||||
|
def input_rate(self):
|
||||||
|
return self.device.available_input_rates[self.config.en_input_rate]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def channels(self):
|
||||||
|
return [self.config.en_input_channels]
|
||||||
|
|
||||||
|
cpdef bint isOpened(self):
|
||||||
|
if self.pcm:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
@ -6,48 +6,41 @@ Created on Sat Mar 10 08:28:03 2018
|
|||||||
@author: Read data from image stream and record sound at the same time
|
@author: Read data from image stream and record sound at the same time
|
||||||
"""
|
"""
|
||||||
import cv2 as cv
|
import cv2 as cv
|
||||||
import sounddevice as sd
|
|
||||||
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
|
||||||
__all__ = ['AvType','AvStream']
|
from .device import DAQDevice, roga_plugndaq
|
||||||
|
__all__ = ['AvType', 'AvStream']
|
||||||
|
|
||||||
|
video_x, video_y = 640, 480
|
||||||
|
dtype, sampwidth = 'int16', 2
|
||||||
|
|
||||||
# %%
|
|
||||||
blocksize = 2048
|
|
||||||
video_x,video_y = 640,480
|
|
||||||
dtype, sampwidth = 'int32',4
|
|
||||||
|
|
||||||
class AvType:
|
class AvType:
|
||||||
video=0
|
video = 0
|
||||||
audio=1
|
audio = 1
|
||||||
|
|
||||||
|
|
||||||
class AvStream:
|
class AvStream:
|
||||||
def __init__(self, audiodeviceno=None, video=None, nchannels = None, samplerate = None):
|
def __init__(self, daqconfig=roga_plugndaq, video=None):
|
||||||
|
|
||||||
audiodevice,audiodeviceno = self._findDevice(audiodeviceno)
|
self.daqconfig = daqconfig
|
||||||
if nchannels is None:
|
try:
|
||||||
self.nchannels = audiodevice['max_input_channels']
|
daq = DAQDevice(daqconfig)
|
||||||
if self.nchannels == 0:
|
self.nchannels = len(daq.channels_enabled)
|
||||||
raise RuntimeError('Device has no input channels')
|
self.samplerate = daq.input_rate
|
||||||
else:
|
self.blocksize = daq.blocksize
|
||||||
self.nchannels = nchannels
|
except Exception as e:
|
||||||
|
raise RuntimeError(f'Could not initialize DAQ device: {str(e)}')
|
||||||
self.audiodeviceno = audiodeviceno
|
|
||||||
if samplerate is None:
|
self.video_x, self.video_y = video_x, video_y
|
||||||
self.samplerate = audiodevice['default_samplerate']
|
|
||||||
else:
|
|
||||||
self.samplerate = samplerate
|
|
||||||
|
|
||||||
self.blocksize = blocksize
|
|
||||||
|
|
||||||
self.video_x, self.video_y = video_x,video_y
|
|
||||||
self.dtype, self.sampwidth = dtype, sampwidth
|
self.dtype, self.sampwidth = dtype, sampwidth
|
||||||
|
|
||||||
self._aframectr = Atomic(0)
|
self._aframectr = Atomic(0)
|
||||||
self._vframectr = Atomic(0)
|
self._vframectr = Atomic(0)
|
||||||
|
|
||||||
self._callbacklock = Lock()
|
self._callbacklock = Lock()
|
||||||
|
|
||||||
self._running = Atomic(False)
|
self._running = Atomic(False)
|
||||||
self._running_cond = Condition()
|
self._running_cond = Condition()
|
||||||
|
|
||||||
@ -59,51 +52,29 @@ class AvStream:
|
|||||||
|
|
||||||
def addCallback(self, cb):
|
def addCallback(self, cb):
|
||||||
"""
|
"""
|
||||||
|
Add as stream callback to the list of callbacks
|
||||||
"""
|
"""
|
||||||
with self._callbacklock:
|
with self._callbacklock:
|
||||||
if not cb in self._callbacks:
|
if cb not in self._callbacks:
|
||||||
self._callbacks.append(cb)
|
self._callbacks.append(cb)
|
||||||
|
|
||||||
def removeCallback(self, cb):
|
def removeCallback(self, cb):
|
||||||
with self._callbacklock:
|
with self._callbacklock:
|
||||||
if cb in self._callbacks:
|
if cb in self._callbacks:
|
||||||
self._callbacks.remove(cb)
|
self._callbacks.remove(cb)
|
||||||
|
|
||||||
def _findDevice(self, deviceno):
|
|
||||||
|
|
||||||
if deviceno is None:
|
|
||||||
deviceno = 0
|
|
||||||
devices = sd.query_devices()
|
|
||||||
found = []
|
|
||||||
for device in devices:
|
|
||||||
name = device['name']
|
|
||||||
if 'Umik' in name:
|
|
||||||
found.append((device,deviceno))
|
|
||||||
elif 'nanoSHARC' in name:
|
|
||||||
found.append((device,deviceno))
|
|
||||||
deviceno+=1
|
|
||||||
|
|
||||||
if len(found) == 0:
|
|
||||||
print('Please choose one of the following:')
|
|
||||||
print(sd.query_devices())
|
|
||||||
raise RuntimeError('Could not find a proper device')
|
|
||||||
|
|
||||||
return found[0]
|
|
||||||
else:
|
|
||||||
return (sd.query_devices(deviceno,kind='input'),deviceno)
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""
|
"""
|
||||||
|
Start the stream, which means the callbacks are called with stream
|
||||||
|
data (audio/video)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self._running:
|
if self._running:
|
||||||
raise RuntimeError('Stream already started')
|
raise RuntimeError('Stream already started')
|
||||||
|
|
||||||
assert self._audiothread == None
|
assert self._audiothread is None
|
||||||
assert self._videothread == None
|
assert self._videothread is None
|
||||||
|
|
||||||
self._running <<= True
|
self._running <<= True
|
||||||
self._audiothread = Thread(target=self._audioThread)
|
self._audiothread = Thread(target=self._audioThread)
|
||||||
if self._video is not None:
|
if self._video is not None:
|
||||||
@ -115,19 +86,16 @@ class AvStream:
|
|||||||
|
|
||||||
def _audioThread(self):
|
def _audioThread(self):
|
||||||
# Raw stream to allow for in24 packed data type
|
# Raw stream to allow for in24 packed data type
|
||||||
stream = sd.InputStream(
|
try:
|
||||||
device=self.audiodeviceno,
|
daq = DAQDevice(self.daqconfig)
|
||||||
dtype=self.dtype,
|
# Get a single block first and do not process it. This one often
|
||||||
blocksize=blocksize,
|
# contains quite some rubbish.
|
||||||
channels=self.nchannels,
|
data = daq.read()
|
||||||
samplerate=self.samplerate,
|
while self._running:
|
||||||
callback=self._audioCallback)
|
data = daq.read()
|
||||||
|
self._audioCallback(data)
|
||||||
with stream:
|
except RuntimeError as e:
|
||||||
with self._running_cond:
|
print(f'Runtime error occured during audio capture: {str(e)}')
|
||||||
while self._running:
|
|
||||||
self._running_cond.wait()
|
|
||||||
print('stopped audiothread')
|
|
||||||
|
|
||||||
def _videoThread(self):
|
def _videoThread(self):
|
||||||
cap = cv.VideoCapture(self._video)
|
cap = cv.VideoCapture(self._video)
|
||||||
@ -138,32 +106,32 @@ class AvStream:
|
|||||||
while self._running:
|
while self._running:
|
||||||
ret, frame = cap.read()
|
ret, frame = cap.read()
|
||||||
# print(frame.shape)
|
# print(frame.shape)
|
||||||
if ret==True:
|
if ret is True:
|
||||||
if vframectr == 0:
|
if vframectr == 0:
|
||||||
self._video_started <<= True
|
self._video_started <<= True
|
||||||
with self._callbacklock:
|
with self._callbacklock:
|
||||||
for cb in self._callbacks:
|
for cb in self._callbacks:
|
||||||
cb(AvType.video,frame,self._aframectr(),vframectr)
|
cb(AvType.video, frame, self._aframectr(), vframectr)
|
||||||
vframectr += 1
|
vframectr += 1
|
||||||
self._vframectr += 1
|
self._vframectr += 1
|
||||||
else:
|
else:
|
||||||
|
|
||||||
if loopctr == 10:
|
if loopctr == 10:
|
||||||
print('Error: no video capture!')
|
print('Error: no video capture!')
|
||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
loopctr +=1
|
loopctr += 1
|
||||||
|
|
||||||
cap.release()
|
cap.release()
|
||||||
print('stopped videothread')
|
print('stopped videothread')
|
||||||
|
|
||||||
def _audioCallback(self, indata, nframes, time, status):
|
def _audioCallback(self, indata):
|
||||||
"""This is called (from a separate thread) for each audio block."""
|
"""This is called (from a separate thread) for each audio block."""
|
||||||
if not self._video_started:
|
if not self._video_started:
|
||||||
return
|
return
|
||||||
|
|
||||||
with self._callbacklock:
|
with self._callbacklock:
|
||||||
for cb in self._callbacks:
|
for cb in self._callbacks:
|
||||||
cb(AvType.audio,indata,self._aframectr(),self._vframectr())
|
cb(AvType.audio, indata, self._aframectr(), self._vframectr())
|
||||||
self._aframectr += 1
|
self._aframectr += 1
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
@ -181,6 +149,6 @@ class AvStream:
|
|||||||
|
|
||||||
def isStarted(self):
|
def isStarted(self):
|
||||||
return self._running()
|
return self._running()
|
||||||
|
|
||||||
def hasVideo(self):
|
def hasVideo(self):
|
||||||
return True if self._video is not None else False
|
return True if self._video is not None else False
|
||||||
|
@ -197,6 +197,22 @@ class Measurement:
|
|||||||
"""
|
"""
|
||||||
return self._time
|
return self._time
|
||||||
|
|
||||||
|
def scaleBlock(self, block):
|
||||||
|
# When the data is stored as integers, we assume dB full-scale scaling.
|
||||||
|
# Hence, when we convert the data to floats, we divide by the maximum
|
||||||
|
# possible value.
|
||||||
|
if block.dtype == np.int32:
|
||||||
|
fac = 2**31
|
||||||
|
elif block.dtype == np.int16:
|
||||||
|
fac = 2**15
|
||||||
|
elif block.dtype == np.float64:
|
||||||
|
fac = 1.0
|
||||||
|
else:
|
||||||
|
raise RuntimeError(
|
||||||
|
f'Unimplemented data type from recording: {block.dtype}.')
|
||||||
|
sens = self._sens
|
||||||
|
return block.astype(LASP_NUMPY_FLOAT_TYPE)/fac/sens[np.newaxis, :]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def prms(self):
|
def prms(self):
|
||||||
"""
|
"""
|
||||||
@ -212,10 +228,12 @@ class Measurement:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
sens = self._sens
|
|
||||||
pms = 0.
|
pms = 0.
|
||||||
for block in self.iterBlocks():
|
|
||||||
pms += np.sum(block/sens[np.newaxis, :], axis=0)**2/self.N
|
with self.file() as f:
|
||||||
|
for block in self.iterBlocks(f):
|
||||||
|
block = self.scaleBlock(block)
|
||||||
|
pms += np.sum(block**2, axis=0)/self.N
|
||||||
self._prms = np.sqrt(pms)
|
self._prms = np.sqrt(pms)
|
||||||
return self._prms
|
return self._prms
|
||||||
|
|
||||||
@ -235,21 +253,7 @@ class Measurement:
|
|||||||
blocks = blocks.reshape(self.nblocks*self.blocksize,
|
blocks = blocks.reshape(self.nblocks*self.blocksize,
|
||||||
self.nchannels)
|
self.nchannels)
|
||||||
|
|
||||||
# When the data is stored as integers, we assume dB full-scale scaling.
|
blocks = self.scaleBlock(blocks)
|
||||||
# Hence, when we convert the data to floats, we divide by the maximum
|
|
||||||
# possible value.
|
|
||||||
if blocks.dtype == np.int32:
|
|
||||||
fac = 2**31
|
|
||||||
elif blocks.dtype == np.int16:
|
|
||||||
fac = 2**15
|
|
||||||
elif blocks.dtype == np.float64:
|
|
||||||
fac = 1.0
|
|
||||||
else:
|
|
||||||
raise RuntimeError(
|
|
||||||
f'Unimplemented data type from recording: {blocks.dtype}.')
|
|
||||||
sens = self._sens
|
|
||||||
blocks = blocks.astype(LASP_NUMPY_FLOAT_TYPE)/fac/sens[np.newaxis, :]
|
|
||||||
|
|
||||||
return blocks
|
return blocks
|
||||||
|
|
||||||
def iterBlocks(self, opened_file):
|
def iterBlocks(self, opened_file):
|
||||||
@ -284,7 +288,7 @@ class Measurement:
|
|||||||
|
|
||||||
valid = sens.ndim == 1
|
valid = sens.ndim == 1
|
||||||
valid &= sens.shape[0] == self.nchannels
|
valid &= sens.shape[0] == self.nchannels
|
||||||
valid &= isinstance(sens.dtype, float)
|
valid &= sens.dtype == float
|
||||||
if not valid:
|
if not valid:
|
||||||
raise ValueError('Invalid sensitivity value(s) given')
|
raise ValueError('Invalid sensitivity value(s) given')
|
||||||
with self.file('r+') as f:
|
with self.file('r+') as f:
|
||||||
|
@ -8,7 +8,7 @@ Read data from stream and record sound and video at the same time
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from .lasp_atomic import Atomic
|
from .lasp_atomic import Atomic
|
||||||
from threading import Condition
|
from threading import Condition
|
||||||
from .lasp_avstream import AvType
|
from .lasp_avstream import AvType, AvStream
|
||||||
import h5py
|
import h5py
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -122,6 +122,6 @@ class Recording:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
stream = AvStream()
|
||||||
rec = Recording('test', 5)
|
rec = Recording('test', stream, 5)
|
||||||
rec.start()
|
rec.start()
|
||||||
|
Loading…
Reference in New Issue
Block a user