Somewhere inbetween. Everything broken
This commit is contained in:
parent
59f82ae14c
commit
a39d8300a1
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
*.a
|
*.a
|
||||||
*.pyc
|
*.pyc
|
||||||
|
lasp_rtaudio.cxx
|
||||||
dist
|
dist
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
@ -23,6 +24,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
|
|
||||||
.ropeproject
|
.ropeproject
|
||||||
.spyproject
|
.spyproject
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required (VERSION 3.0)
|
cmake_minimum_required (VERSION 3.0)
|
||||||
project(beamforming)
|
project(LASP)
|
||||||
|
|
||||||
# Whether we want to use blas yes or no
|
# Whether we want to use blas yes or no
|
||||||
set(LASP_USE_BLAS TRUE)
|
set(LASP_USE_BLAS TRUE)
|
||||||
@ -39,6 +39,7 @@ set(Python_ADDITIONAL_VERSIONS "3")
|
|||||||
|
|
||||||
if(LASP_DEBUG)
|
if(LASP_DEBUG)
|
||||||
set(TRACERNAME LASPTracer)
|
set(TRACERNAME LASPTracer)
|
||||||
|
set(LASP_DEBUG_CYTHON=True)
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
message("Building debug code")
|
message("Building debug code")
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
@ -46,8 +47,6 @@ if(LASP_DEBUG)
|
|||||||
add_definitions(-DTRACERNAME=${TRACERNAME})
|
add_definitions(-DTRACERNAME=${TRACERNAME})
|
||||||
add_definitions(-DDEBUG)
|
add_definitions(-DDEBUG)
|
||||||
add_definitions(-DTRACER=1)
|
add_definitions(-DTRACER=1)
|
||||||
|
|
||||||
set(CYTHON_VARIABLES "#cython: boundscheck=True")
|
|
||||||
# This will produce html files
|
# This will produce html files
|
||||||
set(CYTHON_ANNOTATE ON)
|
set(CYTHON_ANNOTATE ON)
|
||||||
# Add the __FILENAME__ macro
|
# Add the __FILENAME__ macro
|
||||||
@ -55,9 +54,9 @@ if(LASP_DEBUG)
|
|||||||
else()
|
else()
|
||||||
message("Building LASP for release")
|
message("Building LASP for release")
|
||||||
set(CMAKE_BUILD_TYPE Release)
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
|
set(LASP_DEBUG_CYTHON=False)
|
||||||
set(CYTHON_ANNOTATE OFF)
|
set(CYTHON_ANNOTATE OFF)
|
||||||
set(CYTHON_NO_DOCSTRINGS ON)
|
set(CYTHON_NO_DOCSTRINGS ON)
|
||||||
set(CYTHON_VARIABLES "#cython: boundscheck=False,wraparound=False,embedsignature=False,emit_code_comments=False")
|
|
||||||
# Strip unnecessary symbols
|
# Strip unnecessary symbols
|
||||||
# set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--gc-sections")
|
# set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--gc-sections")
|
||||||
# set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--gc-sections")
|
# set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--gc-sections")
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
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")
|
||||||
|
set(CYTHON_EXECUTABLE "cython3")
|
||||||
include(UseCython)
|
include(UseCython)
|
||||||
find_package(Numpy REQUIRED )
|
find_package(Numpy REQUIRED )
|
||||||
|
|
||||||
|
@ -1,68 +1,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
cimport numpy as np
|
cimport numpy as cnp
|
||||||
from libcpp cimport bool
|
|
||||||
|
|
||||||
DEF LASP_FLOAT = "@LASP_FLOAT@"
|
DEF LASP_FLOAT = "@LASP_FLOAT@"
|
||||||
|
DEF LASP_DEBUG_CYTHON = "@LASP_DEBUG_CYTHON@"
|
||||||
|
|
||||||
IF LASP_FLOAT == "double":
|
from libcpp cimport bool
|
||||||
ctypedef double d
|
|
||||||
ctypedef double complex c
|
|
||||||
NUMPY_FLOAT_TYPE = np.float64
|
|
||||||
NUMPY_COMPLEX_TYPE = np.complex128
|
|
||||||
CYTHON_NUMPY_FLOAT_t = np.NPY_FLOAT64
|
|
||||||
CYTHON_NUMPY_COMPLEX_t = np.NPY_COMPLEX128
|
|
||||||
|
|
||||||
ELSE:
|
|
||||||
ctypedef float d
|
|
||||||
ctypedef float complex c
|
|
||||||
NUMPY_FLOAT_TYPE = np.float32
|
|
||||||
NUMPY_COMPLEX_TYPE = np.complex64
|
|
||||||
CYTHON_NUMPY_FLOAT_t = np.NPY_FLOAT32
|
|
||||||
CYTHON_NUMPY_COMPLEX_t = np.NPY_COMPLEX64
|
|
||||||
|
|
||||||
ctypedef size_t us
|
|
||||||
|
|
||||||
cdef extern from "lasp_tracer.h":
|
|
||||||
void setTracerLevel(int)
|
|
||||||
void TRACE(int,const char*)
|
|
||||||
void fsTRACE(int)
|
|
||||||
void feTRACE(int)
|
|
||||||
void clearScreen()
|
|
||||||
|
|
||||||
|
|
||||||
cdef extern from "lasp_mat.h":
|
|
||||||
ctypedef struct dmat:
|
|
||||||
us n_cols
|
|
||||||
us n_rows
|
|
||||||
d* _data
|
|
||||||
bint _foreign_data
|
|
||||||
ctypedef struct cmat:
|
|
||||||
pass
|
|
||||||
ctypedef cmat vc
|
|
||||||
ctypedef dmat vd
|
|
||||||
|
|
||||||
dmat dmat_foreign_data(us n_rows,
|
|
||||||
us n_cols,
|
|
||||||
d* data,
|
|
||||||
bint own_data)
|
|
||||||
cmat cmat_foreign_data(us n_rows,
|
|
||||||
us n_cols,
|
|
||||||
c* data,
|
|
||||||
bint own_data)
|
|
||||||
|
|
||||||
cmat cmat_alloc(us n_rows,us n_cols)
|
|
||||||
dmat dmat_alloc(us n_rows,us n_cols)
|
|
||||||
vd vd_foreign(const us size,d* data)
|
|
||||||
void vd_free(vd*)
|
|
||||||
|
|
||||||
void dmat_free(dmat*)
|
|
||||||
void cmat_free(cmat*)
|
|
||||||
void cmat_copy(cmat* to,cmat* from_)
|
|
||||||
|
|
||||||
|
|
||||||
cdef extern from "numpy/arrayobject.h":
|
|
||||||
void PyArray_ENABLEFLAGS(np.ndarray arr, int flags)
|
|
||||||
|
|
||||||
|
|
||||||
cdef extern from "lasp_python.h":
|
|
||||||
object dmat_to_ndarray(dmat*,bint transfer_ownership)
|
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
set_source_files_properties(lasp_daqdevice.c PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CYTHON_EXTRA_C_FLAGS}")
|
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_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)
|
target_link_libraries(lasp_daqdevice asound)
|
||||||
|
@ -9,11 +9,12 @@ Data Acquistiion (DAQ) device descriptors, and the DAQ devices themselves
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from .lasp_daqdevice import query_devices, DeviceInfo
|
|
||||||
__all__ = ['DAQConfiguration', 'roga_plugndaq', 'umik',
|
|
||||||
'default_soundcard', 'configs',
|
|
||||||
'findDAQDevice']
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DAQInputChannel:
|
||||||
|
channel_enabled: bool
|
||||||
|
channel_name: str
|
||||||
|
sensitivity: float
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DAQConfiguration:
|
class DAQConfiguration:
|
||||||
@ -21,11 +22,9 @@ class DAQConfiguration:
|
|||||||
Initialize a device descriptor
|
Initialize a device descriptor
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: Name of the device to appear to the user
|
input_device_name: ASCII name with which to open the device when connected
|
||||||
cardname: ALSA name identifier
|
outut_device_name: ASCII name with which to open the device when connected
|
||||||
cardlongnamematch: Long name according to ALSA
|
en_format: index of the format in the list of sample formats
|
||||||
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]
|
en_input_rate: index of enabled input sampling frequency [Hz]
|
||||||
in the list of frequencies.
|
in the list of frequencies.
|
||||||
en_input_channels: list of channel indices which are used to
|
en_input_channels: list of channel indices which are used to
|
||||||
@ -42,19 +41,13 @@ class DAQConfiguration:
|
|||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
input_device_name: bytes
|
||||||
name: str
|
output_device_name: bytes
|
||||||
cardname: str
|
en_bit_depth: int
|
||||||
cardlongnamematch: str
|
|
||||||
device_name: str
|
|
||||||
en_format: int
|
|
||||||
en_input_rate: int
|
en_input_rate: int
|
||||||
en_input_channels: list
|
en_input_channels: list
|
||||||
input_sensitivity: list
|
|
||||||
input_gain_settings: list
|
|
||||||
en_input_gain_settings: list = field(default_factory=list)
|
en_input_gain_settings: list = field(default_factory=list)
|
||||||
en_output_rate: int = -1
|
en_output_rate: int = -1
|
||||||
en_output_channels: list = field(default_factory=list)
|
|
||||||
|
|
||||||
def match(self, device):
|
def match(self, device):
|
||||||
"""
|
"""
|
||||||
@ -83,50 +76,12 @@ class DAQConfiguration:
|
|||||||
|
|
||||||
return match
|
return match
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
roga_plugndaq = DAQConfiguration(name='Roga-instruments Plug.n.DAQ USB',
|
def emptyFromDeviceAndSettings(device):
|
||||||
cardname='USB Audio CODEC',
|
return DAQConfiguration(
|
||||||
cardlongnamematch='Burr-Brown from TI USB'
|
name = 'UNKNOWN'
|
||||||
' Audio CODEC',
|
input_device_name =
|
||||||
device_name='iec958:CARD=CODEC,DEV=0',
|
|
||||||
en_format=0,
|
|
||||||
en_input_rate=2,
|
|
||||||
en_input_channels=[0],
|
|
||||||
input_sensitivity=[46.92e-3, 46.92e-3],
|
|
||||||
input_gain_settings=[-20, 0, 20],
|
|
||||||
en_input_gain_settings=[1, 1],
|
|
||||||
en_output_rate=1,
|
|
||||||
en_output_channels=[False, False]
|
|
||||||
)
|
|
||||||
umik = DAQConfiguration(name='UMIK-1',
|
|
||||||
cardname='Umik-1 Gain: 18dB',
|
|
||||||
cardlongnamematch='miniDSP Umik-1 Gain: 18dB',
|
|
||||||
device_name='iec958:CARD=U18dB,DEV=0',
|
|
||||||
en_format=0,
|
|
||||||
en_input_rate=0,
|
|
||||||
en_input_channels=[0],
|
|
||||||
input_sensitivity=[1., 1.],
|
|
||||||
input_gain_settings=[0., 0.],
|
|
||||||
en_input_gain_settings=[0, 0],
|
|
||||||
en_output_rate=0,
|
|
||||||
en_output_channels=[True, True]
|
|
||||||
)
|
|
||||||
|
|
||||||
default_soundcard = DAQConfiguration(name="Default device",
|
|
||||||
cardname=None,
|
|
||||||
cardlongnamematch=None,
|
|
||||||
device_name='default',
|
|
||||||
en_format=0,
|
|
||||||
en_input_rate=2,
|
|
||||||
en_input_channels=[0],
|
|
||||||
input_sensitivity=[1.0, 1.0],
|
|
||||||
input_gain_settings=[0],
|
|
||||||
en_input_gain_settings=[0, 0],
|
|
||||||
en_output_rate=1,
|
|
||||||
en_output_channels=[]
|
|
||||||
)
|
|
||||||
configs = (roga_plugndaq, default_soundcard)
|
|
||||||
|
|
||||||
|
|
||||||
def findDAQDevice(config: DAQConfiguration) -> DeviceInfo:
|
def findDAQDevice(config: DAQConfiguration) -> DeviceInfo:
|
||||||
"""
|
"""
|
||||||
|
389
lasp/device/lasp_rtaudio.pyx
Normal file
389
lasp/device/lasp_rtaudio.pyx
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
import sys
|
||||||
|
include "config.pxi"
|
||||||
|
cimport cython
|
||||||
|
from libcpp.string cimport string
|
||||||
|
from libcpp.vector cimport vector
|
||||||
|
from libc.string cimport memcpy
|
||||||
|
|
||||||
|
cdef extern from "RtAudio.h" nogil:
|
||||||
|
ctypedef unsigned long RtAudioStreamStatus
|
||||||
|
RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW
|
||||||
|
RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW
|
||||||
|
|
||||||
|
cdef cppclass RtAudioError:
|
||||||
|
ctypedef enum Type:
|
||||||
|
WARNING
|
||||||
|
DEBUG_WARNING
|
||||||
|
UNSPECIFIED
|
||||||
|
NO_DEVICES_FOUND
|
||||||
|
INVALID_DEVICE
|
||||||
|
MEMORY_ERROR
|
||||||
|
INVALID_PARAMETER
|
||||||
|
INVALID_USE
|
||||||
|
DRIVER_ERROR
|
||||||
|
SYSTEM_ERROR
|
||||||
|
THREAD_ERROR
|
||||||
|
|
||||||
|
ctypedef unsigned long RtAudioStreamFlags
|
||||||
|
RtAudioStreamFlags RT_AUDIO_NONINTERLEAVED
|
||||||
|
RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY
|
||||||
|
RtAudioStreamFlags RTAUDIO_HOG_DEVICE
|
||||||
|
RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT
|
||||||
|
RtAudioStreamFlags RTAUDIO_JACK_DONT_CONNECT
|
||||||
|
|
||||||
|
ctypedef unsigned long RtAudioFormat
|
||||||
|
RtAudioFormat RTAUDIO_SINT8
|
||||||
|
RtAudioFormat RTAUDIO_SINT16
|
||||||
|
RtAudioFormat RTAUDIO_SINT24
|
||||||
|
RtAudioFormat RTAUDIO_SINT32
|
||||||
|
RtAudioFormat RTAUDIO_FLOAT32
|
||||||
|
RtAudioFormat RTAUDIO_FLOAT64
|
||||||
|
|
||||||
|
ctypedef int (*RtAudioCallback)(void* outputBuffer,
|
||||||
|
void* inputBuffer,
|
||||||
|
unsigned int nFrames,
|
||||||
|
double streamTime,
|
||||||
|
RtAudioStreamStatus status,
|
||||||
|
void* userData)
|
||||||
|
|
||||||
|
ctypedef void (*RtAudioErrorCallback)(RtAudioError.Type _type,
|
||||||
|
const string& errortxt)
|
||||||
|
|
||||||
|
cdef cppclass cppRtAudio "RtAudio":
|
||||||
|
cppclass DeviceInfo:
|
||||||
|
bool probed
|
||||||
|
string name
|
||||||
|
unsigned int outputChannels
|
||||||
|
unsigned int inputChannels
|
||||||
|
unsigned int duplexChannels
|
||||||
|
bool isDefaultOutput
|
||||||
|
bool isDefaultInput
|
||||||
|
vector[unsigned int] sampleRates
|
||||||
|
unsigned int preferredSampleRate
|
||||||
|
RtAudioFormat nativeFormats
|
||||||
|
|
||||||
|
cppclass StreamOptions:
|
||||||
|
RtAudioStreamFlags flags
|
||||||
|
unsigned int numberOfBuffers
|
||||||
|
string streamName
|
||||||
|
int priority
|
||||||
|
cppclass StreamParameters:
|
||||||
|
unsigned int deviceId
|
||||||
|
unsigned int nChannels
|
||||||
|
unsigned int firstChannel
|
||||||
|
|
||||||
|
RtAudio() except +
|
||||||
|
# ~RtAudio() Destructors should not be listed
|
||||||
|
unsigned int getDeviceCount()
|
||||||
|
DeviceInfo getDeviceInfo(unsigned int device)
|
||||||
|
unsigned int getDefaultOutputDevice()
|
||||||
|
unsigned int getDefaultInputDevice()
|
||||||
|
void openStream(StreamParameters* outputParameters,
|
||||||
|
StreamParameters* intputParameters,
|
||||||
|
RtAudioFormat _format,
|
||||||
|
unsigned int sampleRate,
|
||||||
|
unsigned int* bufferFrames,
|
||||||
|
RtAudioCallback callback,
|
||||||
|
void* userData,
|
||||||
|
void* StreamOptions,
|
||||||
|
RtAudioErrorCallback) except +
|
||||||
|
void closeStream()
|
||||||
|
void startStream() except +
|
||||||
|
void stopStream() except +
|
||||||
|
void abortStream() except +
|
||||||
|
bool isStreamOpen()
|
||||||
|
bool isStreamRunning()
|
||||||
|
double getStreamTime()
|
||||||
|
void setStreamTime(double) except +
|
||||||
|
long getStreamLatency()
|
||||||
|
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)
|
||||||
|
|
||||||
|
cdef class _Stream:
|
||||||
|
cdef:
|
||||||
|
object pyCallback
|
||||||
|
RtAudioFormat format
|
||||||
|
cppRtAudio.StreamParameters inputParams
|
||||||
|
cppRtAudio.StreamParameters outputParams
|
||||||
|
# These boolean values tell us whether the structs above here are
|
||||||
|
# initialized and contain valid data
|
||||||
|
bool hasInput
|
||||||
|
bool hasOutput
|
||||||
|
unsigned int bufferFrames
|
||||||
|
|
||||||
|
|
||||||
|
# It took me quite a long time to fully understand Cython's idiosyncrasies
|
||||||
|
# concerning C(++) callbacks, the GIL and passing Python objects as pointers
|
||||||
|
# into C(++) functions. But finally, here it is!
|
||||||
|
cdef object fromBufferToNPYNoCopy(buffer_format_type,
|
||||||
|
void* buf,
|
||||||
|
size_t nchannels,
|
||||||
|
size_t nframes):
|
||||||
|
cdef cnp.npy_intp[2] dims = [nchannels, nframes];
|
||||||
|
|
||||||
|
array = cnp.PyArray_SimpleNewFromData(2, dims, buffer_format_type,
|
||||||
|
buf)
|
||||||
|
|
||||||
|
return array
|
||||||
|
|
||||||
|
cdef void fromNPYToBuffer(cnp.ndarray arr,
|
||||||
|
void* buf):
|
||||||
|
"""
|
||||||
|
Copy a Python numpy array over to a buffer
|
||||||
|
No checks, just memcpy! Careful!
|
||||||
|
"""
|
||||||
|
memcpy(buf, arr.data, arr.size*arr.itemsize)
|
||||||
|
|
||||||
|
|
||||||
|
cdef int audioCallback(void* outputBuffer,
|
||||||
|
void* inputBuffer,
|
||||||
|
unsigned int nFrames,
|
||||||
|
double streamTime,
|
||||||
|
RtAudioStreamStatus status,
|
||||||
|
void* userData) nogil:
|
||||||
|
"""
|
||||||
|
Calls the Python callback function and converts data
|
||||||
|
|
||||||
|
"""
|
||||||
|
cdef int rval = 0
|
||||||
|
|
||||||
|
with gil:
|
||||||
|
if status == RTAUDIO_INPUT_OVERFLOW:
|
||||||
|
print('Input overflow.')
|
||||||
|
return 0
|
||||||
|
if status == RTAUDIO_OUTPUT_UNDERFLOW:
|
||||||
|
print('Output underflow.')
|
||||||
|
return 0
|
||||||
|
stream = <_Stream>(userData)
|
||||||
|
|
||||||
|
# Obtain stream information
|
||||||
|
npy_input = None
|
||||||
|
if stream.hasInput:
|
||||||
|
assert inputBuffer != NULL
|
||||||
|
# cdef
|
||||||
|
try:
|
||||||
|
npy_output, rval = stream.pyCallback(npy_input,
|
||||||
|
nFrames,
|
||||||
|
streamTime)
|
||||||
|
except Exception as e:
|
||||||
|
print('Exception in Python callback: ', str(e))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if stream.hasOutput:
|
||||||
|
if npy_output is None:
|
||||||
|
print('No output buffer given!')
|
||||||
|
return 1
|
||||||
|
IF LASP_DEBUG_CYTHON:
|
||||||
|
try:
|
||||||
|
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!"
|
||||||
|
except AssertionError as e:
|
||||||
|
print(e)
|
||||||
|
fromNPYToBuffer(npy_output, outputBuffer)
|
||||||
|
return rval
|
||||||
|
|
||||||
|
cdef void errorCallback(RtAudioError.Type _type,const string& errortxt) nogil:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
cdef class RtAudio:
|
||||||
|
cdef:
|
||||||
|
cppRtAudio _rtaudio
|
||||||
|
_Stream _stream
|
||||||
|
|
||||||
|
def __cinit__(self):
|
||||||
|
self._stream = None
|
||||||
|
|
||||||
|
def __dealloc__(self):
|
||||||
|
if self._stream is not None:
|
||||||
|
print('Force closing stream')
|
||||||
|
self._rtaudio.closeStream()
|
||||||
|
|
||||||
|
cpdef unsigned int getDeviceCount(self):
|
||||||
|
return self._rtaudio.getDeviceCount()
|
||||||
|
|
||||||
|
cpdef unsigned int getDefaultOutputDevice(self):
|
||||||
|
return self._rtaudio.getDefaultOutputDevice()
|
||||||
|
|
||||||
|
cpdef unsigned int getDefaultInputDevice(self):
|
||||||
|
return self._rtaudio.getDefaultInputDevice()
|
||||||
|
|
||||||
|
def getDeviceInfo(self, unsigned int device):
|
||||||
|
"""
|
||||||
|
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 = []
|
||||||
|
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
|
||||||
|
|
||||||
|
def openStream(self,object outputParams,
|
||||||
|
object inputParams,
|
||||||
|
SampleFormat format,
|
||||||
|
unsigned int sampleRate,
|
||||||
|
unsigned int bufferFrames,
|
||||||
|
object pyCallback,
|
||||||
|
object options = None,
|
||||||
|
object pyErrorCallback = None):
|
||||||
|
"""
|
||||||
|
Opening a stream with specified parameters
|
||||||
|
|
||||||
|
Args:
|
||||||
|
outputParams: dictionary of stream outputParameters, set to None
|
||||||
|
if no outputPararms are specified
|
||||||
|
inputParams: dictionary of stream inputParameters, set to None
|
||||||
|
if no inputPararms are specified
|
||||||
|
sampleRate: desired sample rate.
|
||||||
|
bufferFrames: the amount of frames in a callback buffer
|
||||||
|
callback: callback to call. Note: this callback is called on a
|
||||||
|
different thread!
|
||||||
|
options: A dictionary of optional additional stream options
|
||||||
|
errorCallback: client-defined function that will be invoked when an
|
||||||
|
error has occured.
|
||||||
|
|
||||||
|
Returns: None
|
||||||
|
"""
|
||||||
|
if self._stream is not None:
|
||||||
|
raise RuntimeError('Stream is already opened.')
|
||||||
|
|
||||||
|
cdef cppRtAudio.StreamParameters *rtOutputParams_ptr = NULL
|
||||||
|
cdef cppRtAudio.StreamParameters *rtInputParams_ptr = NULL
|
||||||
|
|
||||||
|
cdef cppRtAudio.StreamOptions streamoptions
|
||||||
|
streamoptions.flags = RTAUDIO_HOG_DEVICE
|
||||||
|
streamoptions.numberOfBuffers = 4
|
||||||
|
|
||||||
|
self._stream = _Stream()
|
||||||
|
self._stream.pyCallback = pyCallback
|
||||||
|
|
||||||
|
if outputParams is not None:
|
||||||
|
rtOutputParams_ptr = &self._stream.outputParams
|
||||||
|
rtOutputParams_ptr.deviceId = outputParams['deviceid']
|
||||||
|
rtOutputParams_ptr.nChannels = outputParams['nchannels']
|
||||||
|
rtOutputParams_ptr.firstChannel = outputParams['firstchannel']
|
||||||
|
self._stream.hasOutput = True
|
||||||
|
|
||||||
|
if inputParams is not None:
|
||||||
|
rtOutputParams_ptr = &self._stream.inputParams
|
||||||
|
rtInputParams_ptr.deviceId = inputParams['deviceid']
|
||||||
|
rtInputParams_ptr.nChannels = inputParams['nchannels']
|
||||||
|
rtInputParams_ptr.firstChannel = inputParams['firstchannel']
|
||||||
|
self._stream.hasInput = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._stream.bufferFrames = bufferFrames
|
||||||
|
self._rtaudio.openStream(rtOutputParams_ptr,
|
||||||
|
rtInputParams_ptr,
|
||||||
|
format._format,
|
||||||
|
sampleRate,
|
||||||
|
&self._stream.bufferFrames,
|
||||||
|
audioCallback,
|
||||||
|
<void*> self._stream,
|
||||||
|
&streamoptions, # Stream options
|
||||||
|
errorCallback # Error callback
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print('Exception occured in stream opening: ', str(e))
|
||||||
|
self._stream = None
|
||||||
|
|
||||||
|
def startStream(self):
|
||||||
|
self._rtaudio.startStream()
|
||||||
|
|
||||||
|
def stopStream(self):
|
||||||
|
if not self._stream:
|
||||||
|
raise RuntimeError('Stream is not opened')
|
||||||
|
self._rtaudio.stopStream()
|
||||||
|
|
||||||
|
def closeStream(self):
|
||||||
|
if not self._stream:
|
||||||
|
raise RuntimeError('Stream is not opened')
|
||||||
|
# Closing stream
|
||||||
|
self._rtaudio.closeStream()
|
||||||
|
self._stream = None
|
||||||
|
|
||||||
|
def abortStream(self):
|
||||||
|
if not self._stream:
|
||||||
|
raise RuntimeError('Stream is not opened')
|
||||||
|
self._rtaudio.abortStream()
|
||||||
|
|
||||||
|
def isStreamOpen(self):
|
||||||
|
return self._rtaudio.isStreamOpen()
|
||||||
|
|
||||||
|
def isStreamRunning(self):
|
||||||
|
return self._rtaudio.isStreamRunning()
|
||||||
|
|
||||||
|
def getStreamTime(self):
|
||||||
|
return self._rtaudio.getStreamTime()
|
||||||
|
|
||||||
|
def setStreamTime(self, double time):
|
||||||
|
return self._rtaudio.setStreamTime(time)
|
||||||
|
|
||||||
|
|
@ -2,14 +2,36 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from .wrappers import Window as wWindow
|
from .wrappers import Window as wWindow
|
||||||
|
import appdirs, os, shelve
|
||||||
"""
|
"""
|
||||||
Common definitions used throughout the code.
|
Common definitions used throughout the code.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = ['P_REF', 'FreqWeighting', 'TimeWeighting', 'getTime', 'calfile',
|
__all__ = ['P_REF', 'FreqWeighting', 'TimeWeighting', 'getTime',
|
||||||
'getFreq',
|
'getFreq', 'lasp_shelve'
|
||||||
'W_REF', 'U_REF', 'I_REF']
|
'W_REF', 'U_REF', 'I_REF']
|
||||||
|
|
||||||
|
lasp_appdir = appdirs.user_data_dir('Lasp', 'ASCEE')
|
||||||
|
|
||||||
|
|
||||||
|
if not os.path.exists(lasp_appdir):
|
||||||
|
try:
|
||||||
|
os.mkdir(lasp_appdir)
|
||||||
|
except:
|
||||||
|
print('Fatal error: could not create application directory')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
class lasp_shelve:
|
||||||
|
def __enter__(self):
|
||||||
|
self.shelve = shelve.open(os.path.join(lasp_appdir, 'config.shelve'))
|
||||||
|
return self.shelve
|
||||||
|
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
self.shelve.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Reference sound pressure level
|
# Reference sound pressure level
|
||||||
P_REF = 2e-5
|
P_REF = 2e-5
|
||||||
|
|
||||||
@ -19,9 +41,6 @@ I_REF = 1e-12 # 1 picoWatt/ m^2
|
|||||||
# Reference velocity for sound velocity level
|
# Reference velocity for sound velocity level
|
||||||
U_REF = 5e-8
|
U_REF = 5e-8
|
||||||
|
|
||||||
# Todo: fix This
|
|
||||||
calfile = None
|
|
||||||
|
|
||||||
|
|
||||||
class Window:
|
class Window:
|
||||||
hann = (wWindow.hann, 'Hann')
|
hann = (wWindow.hann, 'Hann')
|
||||||
|
@ -2,6 +2,68 @@
|
|||||||
This file contains the Cython wrapper functions to C implementations.
|
This file contains the Cython wrapper functions to C implementations.
|
||||||
"""
|
"""
|
||||||
include "config.pxi"
|
include "config.pxi"
|
||||||
|
IF LASP_FLOAT == "double":
|
||||||
|
ctypedef double d
|
||||||
|
ctypedef double complex c
|
||||||
|
NUMPY_FLOAT_TYPE = np.float64
|
||||||
|
NUMPY_COMPLEX_TYPE = np.complex128
|
||||||
|
CYTHON_NUMPY_FLOAT_t = cnp.NPY_FLOAT64
|
||||||
|
CYTHON_NUMPY_COMPLEX_t = cnp.NPY_COMPLEX128
|
||||||
|
|
||||||
|
ELSE:
|
||||||
|
ctypedef float d
|
||||||
|
ctypedef float complex c
|
||||||
|
NUMPY_FLOAT_TYPE = np.float32
|
||||||
|
NUMPY_COMPLEX_TYPE = np.complex64
|
||||||
|
CYTHON_NUMPY_FLOAT_t = cnp.NPY_FLOAT32
|
||||||
|
CYTHON_NUMPY_COMPLEX_t = cnp.NPY_COMPLEX64
|
||||||
|
|
||||||
|
ctypedef size_t us
|
||||||
|
|
||||||
|
cdef extern from "lasp_tracer.h":
|
||||||
|
void setTracerLevel(int)
|
||||||
|
void TRACE(int,const char*)
|
||||||
|
void fsTRACE(int)
|
||||||
|
void feTRACE(int)
|
||||||
|
void clearScreen()
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "lasp_mat.h":
|
||||||
|
ctypedef struct dmat:
|
||||||
|
us n_cols
|
||||||
|
us n_rows
|
||||||
|
d* _data
|
||||||
|
bint _foreign_data
|
||||||
|
ctypedef struct cmat:
|
||||||
|
pass
|
||||||
|
ctypedef cmat vc
|
||||||
|
ctypedef dmat vd
|
||||||
|
|
||||||
|
dmat dmat_foreign_data(us n_rows,
|
||||||
|
us n_cols,
|
||||||
|
d* data,
|
||||||
|
bint own_data)
|
||||||
|
cmat cmat_foreign_data(us n_rows,
|
||||||
|
us n_cols,
|
||||||
|
c* data,
|
||||||
|
bint own_data)
|
||||||
|
|
||||||
|
cmat cmat_alloc(us n_rows,us n_cols)
|
||||||
|
dmat dmat_alloc(us n_rows,us n_cols)
|
||||||
|
vd vd_foreign(const us size,d* data)
|
||||||
|
void vd_free(vd*)
|
||||||
|
|
||||||
|
void dmat_free(dmat*)
|
||||||
|
void cmat_free(cmat*)
|
||||||
|
void cmat_copy(cmat* to,cmat* from_)
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "numpy/arrayobject.h":
|
||||||
|
void PyArray_ENABLEFLAGS(cnp.ndarray arr, int flags)
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "lasp_python.h":
|
||||||
|
object dmat_to_ndarray(dmat*,bint transfer_ownership)
|
||||||
|
|
||||||
__all__ = ['AvPowerSpectra']
|
__all__ = ['AvPowerSpectra']
|
||||||
|
|
||||||
|
35
test/test_rtaudio.py
Normal file
35
test/test_rtaudio.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
import numpy as np
|
||||||
|
from lasp_rtaudio import RtAudio, SampleFormat, Format_SINT32, Format_FLOAT64
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
nframes = 0
|
||||||
|
samplerate = 48000
|
||||||
|
omg = 2*np.pi*1000
|
||||||
|
|
||||||
|
def mycallback(input_, nframes, streamtime):
|
||||||
|
t = np.linspace(streamtime, streamtime + nframes/samplerate,
|
||||||
|
nframes)[np.newaxis,:]
|
||||||
|
outp = 0.1*np.sin(omg*t)
|
||||||
|
return outp, 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pa = RtAudio()
|
||||||
|
count = pa.getDeviceCount()
|
||||||
|
# dev = pa.getDeviceInfo(0)
|
||||||
|
for i in range(count):
|
||||||
|
dev = pa.getDeviceInfo(i)
|
||||||
|
print(dev)
|
||||||
|
|
||||||
|
outputparams = {'deviceid': 0, 'nchannels': 1, 'firstchannel': 0}
|
||||||
|
pa.openStream(outputparams, None , Format_FLOAT64,samplerate, 512, mycallback)
|
||||||
|
pa.startStream()
|
||||||
|
|
||||||
|
input()
|
||||||
|
|
||||||
|
pa.stopStream()
|
||||||
|
pa.closeStream()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user