From a39d8300a1aaaacb568e81e4977ccd95d5260691 Mon Sep 17 00:00:00 2001 From: "J.A. de Jong - ASCEE" Date: Sun, 8 Dec 2019 14:19:10 +0100 Subject: [PATCH] Somewhere inbetween. Everything broken --- .gitignore | 2 +- CMakeLists.txt | 7 +- lasp/CMakeLists.txt | 1 + lasp/config.pxi.in | 67 +----- lasp/device/CMakeLists.txt | 9 + lasp/device/lasp_daqconfig.py | 79 ++----- lasp/device/lasp_rtaudio.pyx | 389 ++++++++++++++++++++++++++++++++++ lasp/lasp_common.py | 29 ++- lasp/wrappers.pyx | 62 ++++++ test/test_rtaudio.py | 35 +++ 10 files changed, 544 insertions(+), 136 deletions(-) create mode 100644 lasp/device/lasp_rtaudio.pyx create mode 100644 test/test_rtaudio.py diff --git a/.gitignore b/.gitignore index 6cba860..2295d6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.a *.pyc +lasp_rtaudio.cxx dist CMakeFiles CMakeCache.txt @@ -23,6 +24,5 @@ LASP.egg-info lasp_octave_fir.* lasp/ui_* resources_rc.py -lasp/device/lasp_daqdevice.c .ropeproject .spyproject diff --git a/CMakeLists.txt b/CMakeLists.txt index aa71987..6e971c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required (VERSION 3.0) -project(beamforming) +project(LASP) # Whether we want to use blas yes or no set(LASP_USE_BLAS TRUE) @@ -39,6 +39,7 @@ set(Python_ADDITIONAL_VERSIONS "3") if(LASP_DEBUG) set(TRACERNAME LASPTracer) + set(LASP_DEBUG_CYTHON=True) set(CMAKE_BUILD_TYPE Debug) message("Building debug code") set(CMAKE_BUILD_TYPE Debug) @@ -46,8 +47,6 @@ if(LASP_DEBUG) add_definitions(-DTRACERNAME=${TRACERNAME}) add_definitions(-DDEBUG) add_definitions(-DTRACER=1) - - set(CYTHON_VARIABLES "#cython: boundscheck=True") # This will produce html files set(CYTHON_ANNOTATE ON) # Add the __FILENAME__ macro @@ -55,9 +54,9 @@ if(LASP_DEBUG) else() message("Building LASP for release") set(CMAKE_BUILD_TYPE Release) + set(LASP_DEBUG_CYTHON=False) set(CYTHON_ANNOTATE OFF) set(CYTHON_NO_DOCSTRINGS ON) - set(CYTHON_VARIABLES "#cython: boundscheck=False,wraparound=False,embedsignature=False,emit_code_comments=False") # Strip unnecessary symbols # set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--gc-sections") # set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--gc-sections") diff --git a/lasp/CMakeLists.txt b/lasp/CMakeLists.txt index 09755dd..9aba87c 100644 --- a/lasp/CMakeLists.txt +++ b/lasp/CMakeLists.txt @@ -1,6 +1,7 @@ configure_file(config.pxi.in config.pxi) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +set(CYTHON_EXECUTABLE "cython3") include(UseCython) find_package(Numpy REQUIRED ) diff --git a/lasp/config.pxi.in b/lasp/config.pxi.in index e46bf54..a5023bb 100644 --- a/lasp/config.pxi.in +++ b/lasp/config.pxi.in @@ -1,68 +1,7 @@ import numpy as np -cimport numpy as np -from libcpp cimport bool +cimport numpy as cnp DEF LASP_FLOAT = "@LASP_FLOAT@" +DEF LASP_DEBUG_CYTHON = "@LASP_DEBUG_CYTHON@" -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 = 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) +from libcpp cimport bool diff --git a/lasp/device/CMakeLists.txt b/lasp/device/CMakeLists.txt index 23e87b9..91ad0bd 100644 --- a/lasp/device/CMakeLists.txt +++ b/lasp/device/CMakeLists.txt @@ -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_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) diff --git a/lasp/device/lasp_daqconfig.py b/lasp/device/lasp_daqconfig.py index b8efb4e..e249a4d 100644 --- a/lasp/device/lasp_daqconfig.py +++ b/lasp/device/lasp_daqconfig.py @@ -9,11 +9,12 @@ Data Acquistiion (DAQ) device descriptors, and the DAQ devices themselves """ 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 class DAQConfiguration: @@ -21,11 +22,9 @@ class DAQConfiguration: 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 + input_device_name: ASCII name with which to open the device when connected + outut_device_name: ASCII name with which to open the device when connected + en_format: index of the format in the list of sample formats en_input_rate: index of enabled input sampling frequency [Hz] in the list of frequencies. en_input_channels: list of channel indices which are used to @@ -42,19 +41,13 @@ class DAQConfiguration: """ - - name: str - cardname: str - cardlongnamematch: str - device_name: str - en_format: int + input_device_name: bytes + output_device_name: bytes + en_bit_depth: int en_input_rate: int en_input_channels: list - input_sensitivity: list - input_gain_settings: list en_input_gain_settings: list = field(default_factory=list) en_output_rate: int = -1 - en_output_channels: list = field(default_factory=list) def match(self, device): """ @@ -83,50 +76,12 @@ class DAQConfiguration: 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=[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) - + @staticmethod + def emptyFromDeviceAndSettings(device): + return DAQConfiguration( + name = 'UNKNOWN' + input_device_name = + def findDAQDevice(config: DAQConfiguration) -> DeviceInfo: """ diff --git a/lasp/device/lasp_rtaudio.pyx b/lasp/device/lasp_rtaudio.pyx new file mode 100644 index 0000000..30b9377 --- /dev/null +++ b/lasp/device/lasp_rtaudio.pyx @@ -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, + 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) + + diff --git a/lasp/lasp_common.py b/lasp/lasp_common.py index d6fdf3e..9687943 100644 --- a/lasp/lasp_common.py +++ b/lasp/lasp_common.py @@ -2,14 +2,36 @@ # -*- coding: utf-8 -*- import numpy as np from .wrappers import Window as wWindow +import appdirs, os, shelve """ Common definitions used throughout the code. """ -__all__ = ['P_REF', 'FreqWeighting', 'TimeWeighting', 'getTime', 'calfile', - 'getFreq', +__all__ = ['P_REF', 'FreqWeighting', 'TimeWeighting', 'getTime', + 'getFreq', 'lasp_shelve' '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 P_REF = 2e-5 @@ -19,9 +41,6 @@ I_REF = 1e-12 # 1 picoWatt/ m^2 # Reference velocity for sound velocity level U_REF = 5e-8 -# Todo: fix This -calfile = None - class Window: hann = (wWindow.hann, 'Hann') diff --git a/lasp/wrappers.pyx b/lasp/wrappers.pyx index 73243ca..20469df 100644 --- a/lasp/wrappers.pyx +++ b/lasp/wrappers.pyx @@ -2,6 +2,68 @@ This file contains the Cython wrapper functions to C implementations. """ 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'] diff --git a/test/test_rtaudio.py b/test/test_rtaudio.py new file mode 100644 index 0000000..582986c --- /dev/null +++ b/test/test_rtaudio.py @@ -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() + + +