Popped old stash
This commit is contained in:
parent
a39d8300a1
commit
9715f3e844
@ -10,8 +10,8 @@ include_directories(
|
||||
.
|
||||
c
|
||||
)
|
||||
add_subdirectory(c)
|
||||
add_subdirectory(device)
|
||||
add_subdirectory(c)
|
||||
add_subdirectory(device)
|
||||
|
||||
set_source_files_properties(wrappers.c PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CYTHON_EXTRA_C_FLAGS}")
|
||||
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)
|
||||
set_source_files_properties(lasp_rtaudio.pyx PROPERTIES CYTHON_IS_CXX TRUE)
|
||||
set_source_files_properties(lasp_rtaudio.cxx PROPERTIES COMPILE_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)
|
||||
|
||||
# target_link_libraries(lasp_portaudio portaudio) */
|
||||
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
|
||||
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
|
||||
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
|
||||
, 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
|
||||
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:
|
||||
@ -362,39 +365,40 @@ class Measurement:
|
||||
|
||||
if os.path.exists(fn) and not force:
|
||||
raise RuntimeError(f'File already exists: {fn}')
|
||||
with self.file() as f:
|
||||
audio = f['audio'][:]
|
||||
|
||||
if isinstance(audio.dtype, float):
|
||||
if sampwidth is None:
|
||||
raise ValueError('sampwidth parameter should be given '
|
||||
'for float data in measurement file')
|
||||
elif sampwidth == 2:
|
||||
itype = np.int16
|
||||
elif sampwidth == 4:
|
||||
itype = np.int32
|
||||
with self.file() as f:
|
||||
|
||||
audio = f['audio']
|
||||
oldsampwidth = getSampWidth(audio.dtype)
|
||||
|
||||
max_ = 1.
|
||||
if normalize:
|
||||
# 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:
|
||||
raise ValueError('Invalid sample width, should be 2 or 4')
|
||||
|
||||
# Find maximum
|
||||
max = 0.
|
||||
for block in self.iterBlocks():
|
||||
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
|
||||
scalefac = 2**(8*(newsampwidth-oldsampwidth))
|
||||
if normalize or isinstance(audio.dtype, float):
|
||||
scalefac *= .01*max
|
||||
|
||||
with wave.open(fn, 'w') as wf:
|
||||
wf.setparams((self.nchannels, self.sampwidth, self.samplerate, 0,
|
||||
'NONE', 'NONE'))
|
||||
for block in self.iterBlocks():
|
||||
if isinstance(block.dtype, float):
|
||||
with wave.open(fn, 'w') as wf:
|
||||
wf.setparams((self.nchannels, self.sampwidth,
|
||||
self.samplerate, 0, 'NONE', 'NONE'))
|
||||
for block in self.iterBlocks(f):
|
||||
# Convert block to integral data type
|
||||
block = (block * scalefac).astype(itype)
|
||||
wf.writeframes(np.asfortranarray(block).tobytes())
|
||||
block = (block*scalefac).astype(newtype)
|
||||
wf.writeframes(np.asfortranarray(block).tobytes())
|
||||
|
||||
@staticmethod
|
||||
def fromtxt(fn,
|
||||
@ -431,6 +435,8 @@ class Measurement:
|
||||
timestamp = os.path.getmtime(fn)
|
||||
if mfn is None:
|
||||
mfn = os.path.splitext(fn)[0] + '.h5'
|
||||
else:
|
||||
mfn = os.path.splitext(mfn)[0] + '.h5'
|
||||
|
||||
dat = np.loadtxt(fn, skiprows=skiprows, delimiter=delimiter)
|
||||
if firstcoltime:
|
||||
|
@ -5,7 +5,7 @@ Author: J.A. de Jong - ASCEE
|
||||
|
||||
Description:
|
||||
"""
|
||||
__all__ = ['close', 'Figure', 'Bode', 'PS', 'PSD']
|
||||
__all__ = ['Figure', 'Bode', 'PS', 'PSD']
|
||||
|
||||
from .config import getReportQuality
|
||||
import matplotlib.pyplot as plt
|
||||
|
Loading…
Reference in New Issue
Block a user