Popped old stash
This commit is contained in:
parent
a39d8300a1
commit
9715f3e844
|
@ -10,8 +10,8 @@ include_directories(
|
||||||
.
|
.
|
||||||
c
|
c
|
||||||
)
|
)
|
||||||
add_subdirectory(c)
|
add_subdirectory(c)
|
||||||
add_subdirectory(device)
|
add_subdirectory(device)
|
||||||
|
|
||||||
set_source_files_properties(wrappers.c PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CYTHON_EXTRA_C_FLAGS}")
|
set_source_files_properties(wrappers.c PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CYTHON_EXTRA_C_FLAGS}")
|
||||||
cython_add_module(wrappers wrappers.pyx)
|
cython_add_module(wrappers wrappers.pyx)
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
set_source_files_properties(lasp_daqdevice.c PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CYTHON_EXTRA_C_FLAGS}")
|
|
||||||
# set_source_files_properties(lasp_portaudio.c PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CYTHON_EXTRA_C_FLAGS}")
|
|
||||||
include_directories(/usr/include/rtaudio)
|
include_directories(/usr/include/rtaudio)
|
||||||
set_source_files_properties(lasp_rtaudio.pyx PROPERTIES CYTHON_IS_CXX TRUE)
|
set_source_files_properties(lasp_rtaudio.pyx PROPERTIES CYTHON_IS_CXX TRUE)
|
||||||
set_source_files_properties(lasp_rtaudio.cxx PROPERTIES COMPILE_FLAGS
|
set_source_files_properties(lasp_rtaudio.cxx PROPERTIES COMPILE_FLAGS
|
||||||
"${CMAKE_CXX_FLAGS} ${CYTHON_EXTRA_CXX_FLAGS}")
|
"${CMAKE_CXX_FLAGS} ${CYTHON_EXTRA_CXX_FLAGS}")
|
||||||
cython_add_module(lasp_daqdevice lasp_daqdevice.pyx)
|
|
||||||
# cython_add_module(lasp_portaudio lasp_portaudio.pyx) */
|
|
||||||
cython_add_module(lasp_rtaudio lasp_rtaudio.pyx)
|
cython_add_module(lasp_rtaudio lasp_rtaudio.pyx)
|
||||||
|
|
||||||
# target_link_libraries(lasp_portaudio portaudio) */
|
# target_link_libraries(lasp_portaudio portaudio) */
|
||||||
target_link_libraries(lasp_rtaudio rtaudio)
|
target_link_libraries(lasp_rtaudio rtaudio)
|
||||||
target_link_libraries(lasp_daqdevice asound)
|
|
||||||
|
|
|
@ -1,504 +0,0 @@
|
||||||
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) nogil
|
|
||||||
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}
|
|
||||||
Card name: {self.cardname}
|
|
||||||
Available sample formats: {self.available_formats}
|
|
||||||
Max input channels: {self.max_input_channels}
|
|
||||||
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
with nogil:
|
|
||||||
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
|
|
|
@ -336,7 +336,7 @@ class Measurement:
|
||||||
f.attrs['sensitivity'] = sens
|
f.attrs['sensitivity'] = sens
|
||||||
self._sens = sens
|
self._sens = sens
|
||||||
|
|
||||||
def exportAsWave(self, fn=None, force=False, sampwidth=None):
|
def exportAsWave(self, fn=None, force=False, newsampwidth=2, normalize=True):
|
||||||
"""
|
"""
|
||||||
Export measurement file as wave. In case the measurement data is stored
|
Export measurement file as wave. In case the measurement data is stored
|
||||||
as floats, the values are scaled between 0 and 1
|
as floats, the values are scaled between 0 and 1
|
||||||
|
@ -348,9 +348,12 @@ class Measurement:
|
||||||
force: If True, overwrites any existing files with the given name
|
force: If True, overwrites any existing files with the given name
|
||||||
, otherwise a RuntimeError is raised.
|
, otherwise a RuntimeError is raised.
|
||||||
|
|
||||||
sampwidth: sample width in bytes with which to export the data.
|
newsampwidth: sample width in bytes with which to export the data.
|
||||||
This should only be given in case the measurement data is stored as
|
This should only be given in case the measurement data is stored as
|
||||||
floating point values, otherwise an
|
floating point values, otherwise an error is thrown
|
||||||
|
|
||||||
|
normalize: If set: normalize the level to something sensible.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if fn is None:
|
if fn is None:
|
||||||
|
@ -362,39 +365,40 @@ class Measurement:
|
||||||
|
|
||||||
if os.path.exists(fn) and not force:
|
if os.path.exists(fn) and not force:
|
||||||
raise RuntimeError(f'File already exists: {fn}')
|
raise RuntimeError(f'File already exists: {fn}')
|
||||||
with self.file() as f:
|
|
||||||
audio = f['audio'][:]
|
|
||||||
|
|
||||||
if isinstance(audio.dtype, float):
|
with self.file() as f:
|
||||||
if sampwidth is None:
|
|
||||||
raise ValueError('sampwidth parameter should be given '
|
audio = f['audio']
|
||||||
'for float data in measurement file')
|
oldsampwidth = getSampWidth(audio.dtype)
|
||||||
elif sampwidth == 2:
|
|
||||||
itype = np.int16
|
max_ = 1.
|
||||||
elif sampwidth == 4:
|
if normalize:
|
||||||
itype = np.int32
|
# Find maximum value
|
||||||
|
for block in self.iterBlocks(f):
|
||||||
|
blockmax = np.max(np.abs(block))
|
||||||
|
max_ = blockmax if blockmax > max_ else max_
|
||||||
|
# Scale with maximum value only if we have a nonzero maximum value.
|
||||||
|
if max_ == 0.:
|
||||||
|
max_ = 1.
|
||||||
|
|
||||||
|
if newsampwidth == 2:
|
||||||
|
newtype = np.int16
|
||||||
|
elif newsampwidth == 4:
|
||||||
|
newtype = np.int32
|
||||||
else:
|
else:
|
||||||
raise ValueError('Invalid sample width, should be 2 or 4')
|
raise ValueError('Invalid sample width, should be 2 or 4')
|
||||||
|
|
||||||
# Find maximum
|
scalefac = 2**(8*(newsampwidth-oldsampwidth))
|
||||||
max = 0.
|
if normalize or isinstance(audio.dtype, float):
|
||||||
for block in self.iterBlocks():
|
scalefac *= .01*max
|
||||||
blockmax = np.max(np.abs(block))
|
|
||||||
if blockmax > max:
|
|
||||||
max = blockmax
|
|
||||||
# Scale with maximum value only if we have a nonzero maximum value.
|
|
||||||
if max == 0.:
|
|
||||||
max = 1.
|
|
||||||
scalefac = 2**(8 * sampwidth) / max
|
|
||||||
|
|
||||||
with wave.open(fn, 'w') as wf:
|
with wave.open(fn, 'w') as wf:
|
||||||
wf.setparams((self.nchannels, self.sampwidth, self.samplerate, 0,
|
wf.setparams((self.nchannels, self.sampwidth,
|
||||||
'NONE', 'NONE'))
|
self.samplerate, 0, 'NONE', 'NONE'))
|
||||||
for block in self.iterBlocks():
|
for block in self.iterBlocks(f):
|
||||||
if isinstance(block.dtype, float):
|
|
||||||
# Convert block to integral data type
|
# Convert block to integral data type
|
||||||
block = (block * scalefac).astype(itype)
|
block = (block*scalefac).astype(newtype)
|
||||||
wf.writeframes(np.asfortranarray(block).tobytes())
|
wf.writeframes(np.asfortranarray(block).tobytes())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fromtxt(fn,
|
def fromtxt(fn,
|
||||||
|
@ -431,6 +435,8 @@ class Measurement:
|
||||||
timestamp = os.path.getmtime(fn)
|
timestamp = os.path.getmtime(fn)
|
||||||
if mfn is None:
|
if mfn is None:
|
||||||
mfn = os.path.splitext(fn)[0] + '.h5'
|
mfn = os.path.splitext(fn)[0] + '.h5'
|
||||||
|
else:
|
||||||
|
mfn = os.path.splitext(mfn)[0] + '.h5'
|
||||||
|
|
||||||
dat = np.loadtxt(fn, skiprows=skiprows, delimiter=delimiter)
|
dat = np.loadtxt(fn, skiprows=skiprows, delimiter=delimiter)
|
||||||
if firstcoltime:
|
if firstcoltime:
|
||||||
|
|
|
@ -5,7 +5,7 @@ Author: J.A. de Jong - ASCEE
|
||||||
|
|
||||||
Description:
|
Description:
|
||||||
"""
|
"""
|
||||||
__all__ = ['close', 'Figure', 'Bode', 'PS', 'PSD']
|
__all__ = ['Figure', 'Bode', 'PS', 'PSD']
|
||||||
|
|
||||||
from .config import getReportQuality
|
from .config import getReportQuality
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
|
Loading…
Reference in New Issue
Block a user