RtAudio in a theoretically working state. Now its time for some testing.
This commit is contained in:
parent
622dd1f319
commit
6b8abb878a
@ -27,4 +27,4 @@ add_library(lasp_lib
|
||||
|
||||
|
||||
|
||||
target_link_libraries(lasp_lib ${LASP_FFT_LIBRARY} openblas)
|
||||
target_link_libraries(lasp_lib ${LASP_FFT_LIBRARY} openblas pthread)
|
||||
|
@ -1,21 +1,21 @@
|
||||
set(cpp_daq_files lasp_cppdaq.cpp)
|
||||
set(mycpp_daq_linklibs pthread)
|
||||
set(cpp_daq_linklibs pthread)
|
||||
|
||||
if(LASP_RTAUDIO)
|
||||
include_directories(/usr/include/rtaudio)
|
||||
list(APPEND cpp_daq_files lasp_cpprtaudio.cpp)
|
||||
list(PREPEND mycpp_daq_linklibs rtaudio)
|
||||
list(PREPEND cpp_daq_linklibs rtaudio)
|
||||
endif()
|
||||
if(LASP_ULDAQ)
|
||||
list(APPEND cpp_daq_files lasp_cppuldaq.cpp)
|
||||
list(PREPEND mycpp_daq_linklibs uldaq)
|
||||
list(PREPEND cpp_daq_linklibs uldaq)
|
||||
endif()
|
||||
if(win32)
|
||||
list(APPEND mycpp_daq_linklibs python)
|
||||
list(APPEND cpp_daq_linklibs python)
|
||||
endif(win32)
|
||||
|
||||
add_library(cpp_daq ${cpp_daq_files})
|
||||
target_link_libraries(cpp_daq ${mycpp_daq_linklibs})
|
||||
target_link_libraries(cpp_daq ${cpp_daq_linklibs})
|
||||
|
||||
foreach(cython_file lasp_daq lasp_deviceinfo lasp_daqconfig)
|
||||
|
||||
@ -27,7 +27,7 @@ foreach(cython_file lasp_daq lasp_deviceinfo lasp_daqconfig)
|
||||
|
||||
cython_add_module(${cython_file} ${cython_file}.pyx)
|
||||
|
||||
target_link_libraries(${cython_file} ${mycpp_daq_linklibs})
|
||||
target_link_libraries(${cython_file} cpp_daq ${cpp_daq_linklibs})
|
||||
|
||||
endforeach()
|
||||
|
||||
@ -36,4 +36,4 @@ endforeach()
|
||||
# also used in the testing directory. But better to already link cpp_daq with
|
||||
# linklibs.
|
||||
|
||||
# set(cpp_daq_linklibs "${mycpp_daq_linklibs}" CACHE INTERNAL "cpp_daq_linklibs")
|
||||
# set(cpp_daq_linklibs "${cpp_daq_linklibs}" CACHE INTERNAL "cpp_daq_linklibs")
|
||||
|
@ -49,55 +49,96 @@ cdef extern from "lasp_cppdaq.h" nogil:
|
||||
unsigned apicode
|
||||
unsigned api_specific_subcode
|
||||
|
||||
@staticmethod
|
||||
vector[DaqApi] getAvailableApis();
|
||||
|
||||
cdef cppclass DataType:
|
||||
string name
|
||||
unsigned sw
|
||||
bool is_floating
|
||||
DataType dtype_fl64
|
||||
bool operator==(const DataType&)
|
||||
|
||||
DataType dtype_fl64
|
||||
DataType dtype_fl32
|
||||
DataType dtype_int8
|
||||
DataType dtype_int16
|
||||
DataType dtype_int32
|
||||
DataType dtype_invalid
|
||||
|
||||
cdef cppclass cppDeviceInfo "DeviceInfo":
|
||||
DaqApi api
|
||||
string device_name
|
||||
unsigned devindex
|
||||
vector[DataType] availableDataTypes
|
||||
vector[double] availableSampleRates
|
||||
vector[us] availableFramesPerBlock
|
||||
|
||||
int api_specific_devindex
|
||||
|
||||
vector[DataType] availableDataTypes
|
||||
int prefDataTypeIndex
|
||||
|
||||
vector[double] availableSampleRates
|
||||
int prefSampleRateIndex
|
||||
|
||||
vector[us] availableFramesPerBlock
|
||||
unsigned prefFramesPerBlockIndex
|
||||
|
||||
dvec availableInputRanges
|
||||
int prefInputRangeIndex
|
||||
int prefFramesPerBlockIndex
|
||||
|
||||
unsigned ninchannels
|
||||
unsigned noutchannels
|
||||
|
||||
bool hasInputIEPE
|
||||
bool hasInputACCouplingSwitch
|
||||
bool hasInputTrigger
|
||||
vector[double] availableInputRanges
|
||||
|
||||
cdef cppclass DaqConfiguration:
|
||||
cdef cppclass cppDaqConfiguration "DaqConfiguration":
|
||||
DaqApi api
|
||||
string device_name
|
||||
|
||||
boolvec eninchannels
|
||||
boolvec enoutchannels
|
||||
vector[string] channel_names
|
||||
vector[double] channel_sensitivities
|
||||
unsigned sampleRateIndex
|
||||
DataType datatype
|
||||
bool monitorOutput
|
||||
unsigned nFramesPerBlock;
|
||||
|
||||
boolvec inputIEPEEnabled;
|
||||
boolvec inputACCouplingMode;
|
||||
usvec inputRangeIndices;
|
||||
vector[double] inchannel_sensitivities
|
||||
vector[string] inchannel_names
|
||||
|
||||
vector[string] outchannel_names
|
||||
|
||||
us sampleRateIndex
|
||||
|
||||
us dataTypeIndex
|
||||
|
||||
us framesPerBlockIndex
|
||||
|
||||
bool monitorOutput
|
||||
vector[us] input_qty_idx
|
||||
|
||||
boolvec inputIEPEEnabled
|
||||
boolvec inputACCouplingMode
|
||||
|
||||
usvec inputRangeIndices
|
||||
|
||||
DaqConfiguration()
|
||||
|
||||
int getHighestInChannel()
|
||||
int getHighestOutChannel()
|
||||
|
||||
int getLowestInChannel()
|
||||
int getLowestOutChannel()
|
||||
|
||||
cdef cppclass cppDaq "Daq":
|
||||
void start(SafeQueue[void*] *inQueue,
|
||||
SafeQueue[void*] *outQueue) except +
|
||||
void stop()
|
||||
void stop() except +
|
||||
double samplerate()
|
||||
us neninchannels()
|
||||
us neninchannels(bool include_monitorchannel=False)
|
||||
us nenoutchannels()
|
||||
DataType getDataType()
|
||||
DataType dataType()
|
||||
us framesPerBlock()
|
||||
bool isRunning()
|
||||
bool duplexMode()
|
||||
|
||||
@staticmethod
|
||||
cppDaq* createDaqDevice(cppDeviceInfo&, DaqConfiguration&)
|
||||
cppDaq* createDaq(cppDeviceInfo&, cppDaqConfiguration&) except +
|
||||
|
||||
@staticmethod
|
||||
vector[cppDeviceInfo] getDeviceInfo()
|
||||
vector[cppDeviceInfo] getDeviceInfo() except +
|
||||
|
@ -24,6 +24,24 @@ vector<DeviceInfo> Daq::getDeviceInfo() {
|
||||
return devs;
|
||||
}
|
||||
|
||||
vector<DaqApi> DaqApi::getAvailableApis() {
|
||||
|
||||
vector<DaqApi> apis;
|
||||
apis.resize(6);
|
||||
#ifdef HAS_ULDAQ_API
|
||||
apis[uldaqapi.apicode] = uldaqapi;
|
||||
#endif
|
||||
#ifdef HAS_RTAUDIO_API
|
||||
apis[rtaudioAlsaApi.apicode] = rtaudioAlsaApi;
|
||||
apis[rtaudioPulseaudioApi.apicode] = rtaudioPulseaudioApi;
|
||||
apis[rtaudioWasapiApi.apicode] = rtaudioWasapiApi;
|
||||
apis[rtaudioDsApi.apicode] = rtaudioDsApi;
|
||||
apis[rtaudioAsioApi.apicode] = rtaudioAsioApi;
|
||||
#endif
|
||||
return apis;
|
||||
}
|
||||
|
||||
|
||||
DaqConfiguration::DaqConfiguration(const DeviceInfo &device) {
|
||||
|
||||
api = device.api;
|
||||
@ -61,19 +79,37 @@ bool DaqConfiguration::match(const DeviceInfo& dev) const {
|
||||
return (dev.device_name == device_name && dev.api == api);
|
||||
}
|
||||
|
||||
Daq *Daq::createDevice(const DaqConfiguration &config,
|
||||
const vector<DeviceInfo> &devinfos) {
|
||||
|
||||
bool match;
|
||||
DeviceInfo devinfo;
|
||||
for (auto cur_devinfo : devinfos) {
|
||||
if ((match = config.match(cur_devinfo))) {
|
||||
devinfo = cur_devinfo;
|
||||
break;
|
||||
}
|
||||
int DaqConfiguration::getHighestInChannel() const {
|
||||
for(int i=eninchannels.size()-1; i>-1;i--) {
|
||||
if(eninchannels[i]) return i;
|
||||
}
|
||||
if (!match) {
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int DaqConfiguration::getHighestOutChannel() const {
|
||||
for(us i=enoutchannels.size()-1; i>=0;i--) {
|
||||
if(enoutchannels[i]) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int DaqConfiguration::getLowestInChannel() const {
|
||||
for(us i=0; i<eninchannels.size();i++) {
|
||||
if(eninchannels[i]) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int DaqConfiguration::getLowestOutChannel() const {
|
||||
for(us i=0; i<enoutchannels.size();i++) {
|
||||
if(enoutchannels[i]) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Daq *Daq::createDaq(const DeviceInfo& devinfo,
|
||||
const DaqConfiguration &config) {
|
||||
|
||||
if(!config.match(devinfo)) {
|
||||
throw runtime_error("DaqConfiguration does not match device info");
|
||||
}
|
||||
|
||||
// Some basic sanity checks
|
||||
@ -112,13 +148,14 @@ Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config)
|
||||
|
||||
|
||||
double Daq::samplerate() const {
|
||||
mutexlock lock(mutex);
|
||||
assert(sampleRateIndex < availableSampleRates.size());
|
||||
return availableSampleRates[sampleRateIndex];
|
||||
}
|
||||
|
||||
|
||||
DataType Daq::dataType() const {
|
||||
|
||||
mutexlock lock(mutex);
|
||||
assert((us)dataTypeIndex < availableDataTypes.size());
|
||||
return availableDataTypes[dataTypeIndex];
|
||||
}
|
||||
@ -128,15 +165,16 @@ double Daq::inputRangeForChannel(us ch) const {
|
||||
if (!(ch < ninchannels)) {
|
||||
throw runtime_error("Invalid channel number");
|
||||
}
|
||||
mutexlock lock(mutex);
|
||||
assert(inputRangeIndices.size() == eninchannels.size());
|
||||
return availableInputRanges[inputRangeIndices[ch]];
|
||||
}
|
||||
|
||||
|
||||
us Daq::neninchannels() const {
|
||||
us Daq::neninchannels(bool include_monitorchannel) const {
|
||||
mutexlock lock(mutex);
|
||||
us inch = std::count(eninchannels.begin(), eninchannels.end(), true);
|
||||
if (monitorOutput)
|
||||
if (monitorOutput && include_monitorchannel)
|
||||
inch++;
|
||||
return inch;
|
||||
}
|
||||
|
@ -12,8 +12,8 @@
|
||||
#include "lasp_cppqueue.h"
|
||||
#include "string"
|
||||
#include "vector"
|
||||
#include <mutex>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
|
||||
using std::cerr;
|
||||
@ -30,14 +30,17 @@ typedef vector<us> usvec;
|
||||
typedef std::lock_guard<std::mutex> mutexlock;
|
||||
|
||||
class DataType {
|
||||
public:
|
||||
string name;
|
||||
unsigned sw;
|
||||
bool is_floating;
|
||||
public:
|
||||
string name;
|
||||
unsigned sw;
|
||||
bool is_floating;
|
||||
|
||||
DataType(const char *name, unsigned sw, bool is_floating)
|
||||
DataType(const char *name, unsigned sw, bool is_floating)
|
||||
: name(name), sw(sw), is_floating(is_floating) {}
|
||||
DataType() : name("invalid data type"), sw(0), is_floating(false) {}
|
||||
DataType() : name("invalid data type"), sw(0), is_floating(false) {}
|
||||
bool operator==(const DataType &o) {
|
||||
return (name == o.name && sw == o.sw && is_floating == o.is_floating);
|
||||
}
|
||||
};
|
||||
|
||||
const DataType dtype_invalid;
|
||||
@ -48,27 +51,25 @@ const DataType dtype_int16("16-bits integers", 2, false);
|
||||
const DataType dtype_int32("32-bits integers", 4, false);
|
||||
|
||||
const std::vector<DataType> dataTypes = {
|
||||
dtype_int8,
|
||||
dtype_int16,
|
||||
dtype_int32,
|
||||
dtype_fl32,
|
||||
dtype_fl64,
|
||||
dtype_int8, dtype_int16, dtype_int32, dtype_fl32, dtype_fl64,
|
||||
};
|
||||
|
||||
class DaqApi {
|
||||
public:
|
||||
string apiname = "";
|
||||
unsigned apicode = 0;
|
||||
unsigned api_specific_subcode = 0;
|
||||
public:
|
||||
string apiname = "Invalid API";
|
||||
int apicode = -1;
|
||||
unsigned api_specific_subcode = 0;
|
||||
|
||||
DaqApi(string apiname, unsigned apicode, unsigned api_specific_subcode = 0)
|
||||
DaqApi(string apiname, unsigned apicode, unsigned api_specific_subcode = 0)
|
||||
: apiname(apiname), apicode(apicode),
|
||||
api_specific_subcode(api_specific_subcode) {}
|
||||
DaqApi() {}
|
||||
bool operator==(const DaqApi &other) const {
|
||||
return (apiname == other.apiname && apicode == other.apicode &&
|
||||
api_specific_subcode == other.api_specific_subcode);
|
||||
}
|
||||
api_specific_subcode(api_specific_subcode) {}
|
||||
DaqApi() {}
|
||||
bool operator==(const DaqApi &other) const {
|
||||
return (apiname == other.apiname && apicode == other.apicode &&
|
||||
api_specific_subcode == other.api_specific_subcode);
|
||||
}
|
||||
|
||||
static vector<DaqApi> getAvailableApis();
|
||||
};
|
||||
|
||||
#ifdef HAS_ULDAQ_API
|
||||
@ -76,127 +77,143 @@ const DaqApi uldaqapi("UlDaq", 0);
|
||||
#endif
|
||||
#ifdef HAS_RTAUDIO_API
|
||||
const DaqApi rtaudioAlsaApi("RtAudio Linux ALSA", 1, RtAudio::Api::LINUX_ALSA);
|
||||
const DaqApi rtaudioPulseaudioApi("RtAudio Linux Pulseaudio", 2, RtAudio::Api::LINUX_PULSE);
|
||||
const DaqApi rtaudioWasapiApi("RtAudio Windows Wasapi", 3, RtAudio::Api::WINDOWS_WASAPI);
|
||||
const DaqApi rtaudioDsApi("RtAudio Windows DirectSound", 4, RtAudio::Api::WINDOWS_DS);
|
||||
const DaqApi rtaudioAsioApi("RtAudio Windows ASIO", 4, RtAudio::Api::WINDOWS_ASIO);
|
||||
const DaqApi rtaudioPulseaudioApi("RtAudio Linux Pulseaudio", 2,
|
||||
RtAudio::Api::LINUX_PULSE);
|
||||
const DaqApi rtaudioWasapiApi("RtAudio Windows Wasapi", 3,
|
||||
RtAudio::Api::WINDOWS_WASAPI);
|
||||
const DaqApi rtaudioDsApi("RtAudio Windows DirectSound", 4,
|
||||
RtAudio::Api::WINDOWS_DS);
|
||||
const DaqApi rtaudioAsioApi("RtAudio Windows ASIO", 5,
|
||||
RtAudio::Api::WINDOWS_ASIO);
|
||||
#endif
|
||||
|
||||
|
||||
// Structure containing device info parameters
|
||||
class DeviceInfo {
|
||||
public:
|
||||
DaqApi api;
|
||||
string device_name = "";
|
||||
public:
|
||||
DaqApi api;
|
||||
string device_name = "";
|
||||
|
||||
int api_specific_devindex = -1;
|
||||
int api_specific_devindex = -1;
|
||||
|
||||
vector<DataType> availableDataTypes;
|
||||
int prefDataTypeIndex = 0;
|
||||
vector<DataType> availableDataTypes;
|
||||
int prefDataTypeIndex = 0;
|
||||
|
||||
vector<double> availableSampleRates;
|
||||
int prefSampleRateIndex = -1;
|
||||
vector<double> availableSampleRates;
|
||||
int prefSampleRateIndex = -1;
|
||||
|
||||
vector<us> availableFramesPerBlock;
|
||||
unsigned prefFramesPerBlockIndex = 0;
|
||||
vector<us> availableFramesPerBlock;
|
||||
unsigned prefFramesPerBlockIndex = 0;
|
||||
|
||||
dvec availableInputRanges;
|
||||
int prefInputRangeIndex = 0;
|
||||
dvec availableInputRanges;
|
||||
int prefInputRangeIndex = 0;
|
||||
|
||||
unsigned ninchannels = 0;
|
||||
unsigned noutchannels = 0;
|
||||
unsigned ninchannels = 0;
|
||||
unsigned noutchannels = 0;
|
||||
|
||||
bool hasInputIEPE = false;
|
||||
bool hasInputACCouplingSwitch = false;
|
||||
bool hasInputTrigger = false;
|
||||
bool hasInputIEPE = false;
|
||||
bool hasInputACCouplingSwitch = false;
|
||||
bool hasInputTrigger = false;
|
||||
|
||||
/* DeviceInfo(): */
|
||||
/* datatype(dtype_invalid) { } */
|
||||
/* DeviceInfo(): */
|
||||
/* datatype(dtype_invalid) { } */
|
||||
|
||||
double prefSampleRate() const {
|
||||
if (((us) prefSampleRateIndex < availableSampleRates.size()) &&
|
||||
(prefSampleRateIndex >= 0)) {
|
||||
return availableSampleRates[prefSampleRateIndex];
|
||||
} else {
|
||||
throw std::runtime_error("No prefered sample rate available");
|
||||
}
|
||||
double prefSampleRate() const {
|
||||
if (((us)prefSampleRateIndex < availableSampleRates.size()) &&
|
||||
(prefSampleRateIndex >= 0)) {
|
||||
return availableSampleRates[prefSampleRateIndex];
|
||||
} else {
|
||||
throw std::runtime_error("No prefered sample rate available");
|
||||
}
|
||||
}
|
||||
|
||||
operator string() const {
|
||||
std::stringstream str;
|
||||
str << api.apiname + " " << api_specific_devindex
|
||||
operator string() const {
|
||||
std::stringstream str;
|
||||
str << api.apiname + " " << api_specific_devindex
|
||||
<< " number of input channels: " << ninchannels
|
||||
<< " number of output channels: " << noutchannels;
|
||||
return str.str();
|
||||
}
|
||||
return str.str();
|
||||
}
|
||||
};
|
||||
|
||||
// Device configuration parameters
|
||||
class DaqConfiguration {
|
||||
public:
|
||||
DaqApi api;
|
||||
string device_name;
|
||||
public:
|
||||
DaqApi api;
|
||||
string device_name;
|
||||
|
||||
boolvec eninchannels; // Enabled input channels
|
||||
boolvec enoutchannels; // Enabled output channels
|
||||
boolvec eninchannels; // Enabled input channelsvice(const DeviceInfo& devinfo,
|
||||
boolvec enoutchannels; // Enabled output channels
|
||||
|
||||
vector<double> inchannel_sensitivities;
|
||||
vector<string> inchannel_names;
|
||||
vector<double> inchannel_sensitivities;
|
||||
vector<string> inchannel_names;
|
||||
vector<string> inchannel_qtys;
|
||||
|
||||
// This is not necessary at the moment
|
||||
/* vector<double> outchannel_sensitivities; */
|
||||
vector<string> outchannel_names;
|
||||
// This is not necessary at the moment
|
||||
/* vector<double> outchannel_sensitivities; */
|
||||
vector<string> outchannel_names;
|
||||
|
||||
us sampleRateIndex = 0; // Index in list of sample rates
|
||||
us sampleRateIndex = 0; // Index in list of sample rates
|
||||
|
||||
us dataTypeIndex = 0; // Required datatype for output, should be
|
||||
// present in the list
|
||||
us dataTypeIndex = 0; // Required datatype for output, should be
|
||||
// present in the list
|
||||
|
||||
us framesPerBlockIndex = 0;
|
||||
us framesPerBlockIndex = 0;
|
||||
|
||||
bool monitorOutput = false; // Whether the first output channel should be replicated
|
||||
// to the input as the first channel
|
||||
bool monitorOutput = false;
|
||||
|
||||
boolvec inputIEPEEnabled;
|
||||
boolvec inputACCouplingMode;
|
||||
|
||||
boolvec inputIEPEEnabled;
|
||||
boolvec inputACCouplingMode;
|
||||
usvec inputRangeIndices;
|
||||
|
||||
usvec inputRangeIndices;
|
||||
// Create a default configuration, with all channels disabled on both
|
||||
// input and output, and default channel names
|
||||
DaqConfiguration(const DeviceInfo &device);
|
||||
DaqConfiguration() {}
|
||||
|
||||
// Create a default configuration, with all channels disabled on both
|
||||
// input and output, and default channel names
|
||||
DaqConfiguration(const DeviceInfo &device);
|
||||
bool match(const DeviceInfo &devinfo) const;
|
||||
|
||||
bool match(const DeviceInfo &devinfo) const;
|
||||
int getHighestInChannel() const;
|
||||
int getHighestOutChannel() const;
|
||||
|
||||
int getLowestInChannel() const;
|
||||
int getLowestOutChannel() const;
|
||||
};
|
||||
|
||||
class Daq;
|
||||
class Daq : public DaqConfiguration,public DeviceInfo {
|
||||
class Daq : public DaqConfiguration, public DeviceInfo {
|
||||
|
||||
mutable std::mutex mutex;
|
||||
|
||||
public:
|
||||
public:
|
||||
static vector<DeviceInfo> getDeviceInfo();
|
||||
|
||||
static Daq *createDevice(const DaqConfiguration &config,
|
||||
const std::vector<DeviceInfo> &devinfos);
|
||||
static Daq *createDaq(const DeviceInfo &, const DaqConfiguration &config);
|
||||
|
||||
Daq(const DeviceInfo &devinfo, const DaqConfiguration &config);
|
||||
|
||||
virtual void start(SafeQueue<void *> *inqueue,
|
||||
SafeQueue<void *> *outqueue) = 0;
|
||||
SafeQueue<void *> *outqueue) = 0;
|
||||
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual bool isRunning() const = 0;
|
||||
|
||||
virtual ~Daq(){};
|
||||
us neninchannels() const;
|
||||
us neninchannels(bool include_monitorchannel = true) const;
|
||||
us nenoutchannels() const;
|
||||
|
||||
double samplerate() const;
|
||||
double inputRangeForChannel(us ch) const;
|
||||
DataType dataType() const;
|
||||
|
||||
us framesPerBlock() const {
|
||||
mutexlock lock(mutex);
|
||||
return availableFramesPerBlock[framesPerBlockIndex];
|
||||
}
|
||||
bool duplexMode() const {
|
||||
return (neninchannels(false) > 0 && nenoutchannels() > 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // LASP_CPPDAQ_H
|
||||
|
@ -2,6 +2,9 @@
|
||||
#include <RtAudio.h>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
using std::atomic;
|
||||
|
||||
void fillRtAudioDeviceInfo(vector<DeviceInfo> &devinfolist) {
|
||||
@ -79,19 +82,265 @@ void fillRtAudioDeviceInfo(vector<DeviceInfo> &devinfolist) {
|
||||
|
||||
}
|
||||
|
||||
int mycallback(void *outputBuffer, void *inputBuffer,
|
||||
unsigned int nFrames,
|
||||
double streamTime,
|
||||
RtAudioStreamStatus status,
|
||||
void *userData);
|
||||
|
||||
void myerrorcallback(RtAudioError::Type,const string& errorText);
|
||||
|
||||
class AudioDaq: public Daq {
|
||||
|
||||
atomic<bool> stopThread;
|
||||
SafeQueue<void*> *inqueue = NULL;
|
||||
SafeQueue<void*> *outqueue = NULL;
|
||||
SafeQueue<void*> *outDelayqueue = NULL;
|
||||
|
||||
std::thread* thread = NULL;
|
||||
RtAudio* rtaudio = NULL;
|
||||
RtAudio::StreamParameters* instreamparams = nullptr;
|
||||
RtAudio::StreamParameters* outstreamparams = nullptr;
|
||||
|
||||
SafeQueue<void*> *inqueue = NULL;
|
||||
SafeQueue<void*> *outqueue = NULL;
|
||||
us nFramesPerBlock;
|
||||
|
||||
void* inbuffer = NULL;
|
||||
void* outbuffer = NULL;
|
||||
public:
|
||||
AudioDaq(const DeviceInfo& devinfo,
|
||||
const DaqConfiguration& config):
|
||||
Daq(devinfo, config) {
|
||||
|
||||
public:
|
||||
friend void threadfcn(AudioDaq*);
|
||||
nFramesPerBlock = this->framesPerBlock();
|
||||
|
||||
|
||||
if(neninchannels(false) > 0) {
|
||||
instreamparams = new RtAudio::StreamParameters();
|
||||
instreamparams->nChannels = getHighestInChannel();
|
||||
instreamparams->firstChannel = 0;
|
||||
instreamparams->deviceId = devinfo.api_specific_devindex;
|
||||
}
|
||||
|
||||
if(nenoutchannels() > 0) {
|
||||
outstreamparams = new RtAudio::StreamParameters();
|
||||
outstreamparams->nChannels = getHighestOutChannel();
|
||||
outstreamparams->firstChannel = 0;
|
||||
outstreamparams->deviceId = devinfo.api_specific_devindex;
|
||||
}
|
||||
|
||||
RtAudio::StreamOptions streamoptions;
|
||||
streamoptions.flags = RTAUDIO_NONINTERLEAVED | RTAUDIO_HOG_DEVICE;
|
||||
|
||||
streamoptions.numberOfBuffers = 2;
|
||||
streamoptions.streamName = "RtAudio stream";
|
||||
streamoptions.priority = 0;
|
||||
|
||||
RtAudioFormat format;
|
||||
DataType dtype = dataType();
|
||||
if(dtype == dtype_fl32) {
|
||||
format = RTAUDIO_FLOAT32;
|
||||
} else if(dtype == dtype_fl64) {
|
||||
format = RTAUDIO_FLOAT64;
|
||||
} else if(dtype == dtype_int8) {
|
||||
format = RTAUDIO_SINT8;
|
||||
} else if(dtype == dtype_int16) {
|
||||
format = RTAUDIO_SINT16;
|
||||
} else if(dtype == dtype_int32) {
|
||||
format = RTAUDIO_SINT32;
|
||||
} else {
|
||||
throw runtime_error("Invalid data type");
|
||||
}
|
||||
|
||||
try {
|
||||
rtaudio = new RtAudio((RtAudio::Api) devinfo.api_specific_devindex);
|
||||
if(!rtaudio) {
|
||||
throw runtime_error("RtAudio allocation failed");
|
||||
}
|
||||
rtaudio->openStream(
|
||||
outstreamparams,
|
||||
instreamparams,
|
||||
format,
|
||||
(us) samplerate(),
|
||||
&nFramesPerBlock,
|
||||
&mycallback,
|
||||
(void*) this,
|
||||
&streamoptions,
|
||||
&myerrorcallback
|
||||
);
|
||||
} catch(...) {
|
||||
if(rtaudio) delete rtaudio;
|
||||
throw;
|
||||
}
|
||||
if(monitorOutput) {
|
||||
outDelayqueue = new SafeQueue<void*>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
friend int mycallback(void *outputBuffer, void *inputBuffer,
|
||||
unsigned int nFrames,
|
||||
double streamTime,
|
||||
RtAudioStreamStatus status,
|
||||
void *userData);
|
||||
|
||||
|
||||
void start(SafeQueue<void*> *inqueue, SafeQueue<void*> *outqueue) {
|
||||
if(isRunning()){
|
||||
throw runtime_error("Stream already running");
|
||||
}
|
||||
|
||||
if(neninchannels(false) > 0 && !inqueue) {
|
||||
throw runtime_error("inqueue argument not given");
|
||||
}
|
||||
if(nenoutchannels() > 0 && !outqueue) {
|
||||
throw runtime_error("outqueue argument not given");
|
||||
}
|
||||
assert(rtaudio);
|
||||
rtaudio->startStream();
|
||||
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if(!isRunning()) {
|
||||
cerr << "Stream is already stopped" << endl;
|
||||
}
|
||||
else {
|
||||
assert(rtaudio);
|
||||
rtaudio->stopStream();
|
||||
}
|
||||
|
||||
}
|
||||
bool isRunning() const {return (rtaudio && rtaudio->isStreamRunning());}
|
||||
|
||||
~AudioDaq() {
|
||||
assert(rtaudio);
|
||||
if(rtaudio->isStreamRunning()) {
|
||||
stop();
|
||||
}
|
||||
if(rtaudio->isStreamOpen()) {
|
||||
rtaudio->closeStream();
|
||||
}
|
||||
|
||||
if(rtaudio) delete rtaudio;
|
||||
if(outDelayqueue) delete outDelayqueue;
|
||||
if(instreamparams) delete instreamparams;
|
||||
if(outstreamparams) delete outstreamparams;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Daq* createRtAudioDevice(const DeviceInfo& devinfo,
|
||||
const DaqConfiguration& config) {
|
||||
|
||||
AudioDaq *daq = NULL;
|
||||
|
||||
try {
|
||||
daq = new AudioDaq(devinfo, config);
|
||||
|
||||
} catch (runtime_error &e) {
|
||||
if (daq)
|
||||
delete daq;
|
||||
throw;
|
||||
}
|
||||
return daq;
|
||||
}
|
||||
|
||||
|
||||
int mycallback(
|
||||
void *outputBuffervoid,
|
||||
void *inputBuffervoid,
|
||||
unsigned int nFrames,
|
||||
double streamTime,
|
||||
RtAudioStreamStatus status,
|
||||
void *userData) {
|
||||
|
||||
u_int8_t* inputBuffer = (u_int8_t*) inputBuffervoid;
|
||||
u_int8_t* outputBuffer = (u_int8_t*) outputBuffervoid;
|
||||
|
||||
AudioDaq* daq = (AudioDaq*) userData;
|
||||
DataType dtype = daq->dataType();
|
||||
/* us neninchannels = daq->neninchannels(); */
|
||||
us neninchannels_inc_mon = daq->neninchannels();
|
||||
us nenoutchannels = daq->nenoutchannels();
|
||||
|
||||
bool monitorOutput = daq->monitorOutput;
|
||||
us bytesperchan = dtype.sw*nFrames;
|
||||
us monitorOffset = ((us) monitorOutput)*bytesperchan;
|
||||
|
||||
SafeQueue<void*> *inqueue = daq->inqueue;
|
||||
SafeQueue<void*> *outqueue = daq->outqueue;
|
||||
SafeQueue<void*> *outDelayqueue = daq->outDelayqueue;
|
||||
|
||||
const boolvec& eninchannels = daq->eninchannels;
|
||||
const boolvec& enoutchannels = daq->enoutchannels;
|
||||
|
||||
if(inputBuffer || monitorOutput) {
|
||||
|
||||
u_int8_t *inbuffercpy = (u_int8_t*) malloc(bytesperchan*neninchannels_inc_mon);
|
||||
if(inputBuffer) {
|
||||
us j=0; // OUR buffer channel counter
|
||||
us i=0; // RtAudio channel counter
|
||||
for(us ch=daq->getLowestInChannel();ch<=daq->getHighestInChannel();ch++) {
|
||||
if(eninchannels[ch]) {
|
||||
memcpy(
|
||||
&(inbuffercpy[monitorOffset+j*bytesperchan]),
|
||||
&(inputBuffer[i*bytesperchan]),
|
||||
bytesperchan);
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if(monitorOutput) {
|
||||
assert(outDelayqueue);
|
||||
|
||||
if(!daq->outDelayqueue->empty()) {
|
||||
void* dat = daq->outDelayqueue->dequeue();
|
||||
memcpy((void*) inbuffercpy, dat, bytesperchan);
|
||||
free(dat);
|
||||
} else {
|
||||
cerr << "Warning: output delay queue appears empty!" << endl;
|
||||
memset(inbuffercpy, 0, bytesperchan);
|
||||
}
|
||||
}
|
||||
assert(inqueue);
|
||||
inqueue->enqueue(inbuffercpy);
|
||||
|
||||
}
|
||||
|
||||
if(outputBuffer) {
|
||||
assert(outqueue);
|
||||
if(!outqueue->empty()) {
|
||||
u_int8_t* outbuffercpy = (u_int8_t*) outqueue->dequeue();
|
||||
us j=0; // OUR buffer channel counter
|
||||
us i=0; // RtAudio channel counter
|
||||
for(us ch=0;ch<=daq->getHighestOutChannel();ch++) {
|
||||
if(enoutchannels[ch]) {
|
||||
memcpy(
|
||||
&(outputBuffer[i*bytesperchan]),
|
||||
&(outbuffercpy[j*bytesperchan]),
|
||||
bytesperchan);
|
||||
j++;
|
||||
}
|
||||
else {
|
||||
memset(
|
||||
&(outputBuffer[i*bytesperchan]),0,bytesperchan);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if(!monitorOutput) {
|
||||
free(outbuffercpy);
|
||||
} else {
|
||||
assert(outDelayqueue);
|
||||
outDelayqueue->enqueue((void*) outbuffercpy);
|
||||
}
|
||||
}
|
||||
else {
|
||||
cerr << "Stream output buffer underflow, zero-ing buffer... " << endl;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
void myerrorcallback(RtAudioError::Type,const string& errorText) {
|
||||
cerr << errorText << endl;
|
||||
}
|
||||
|
@ -1,2 +1,10 @@
|
||||
include "lasp_common_decls.pxd"
|
||||
|
||||
ctypedef struct PyStreamData
|
||||
|
||||
cdef class Daq:
|
||||
cdef:
|
||||
PyStreamData *sd
|
||||
cppDaq* daq_device
|
||||
|
||||
cdef cleanupStream(self, PyStreamData* sd)
|
||||
|
@ -1,11 +1,27 @@
|
||||
cimport cython
|
||||
from .lasp_deviceinfo cimport DeviceInfo
|
||||
from .lasp_daqconfig cimport DaqConfiguration
|
||||
|
||||
from cpython.ref cimport PyObject,Py_INCREF, Py_DECREF
|
||||
from .lasp_device_common import AvType
|
||||
|
||||
__all__ = ['Daq']
|
||||
|
||||
cdef cnp.NPY_TYPES getNumpyDataType(DataType& dt):
|
||||
if(dt == dtype_fl32):
|
||||
return cnp.NPY_FLOAT32
|
||||
elif(dt == dtype_fl64):
|
||||
return cnp.NPY_FLOAT64
|
||||
elif(dt == dtype_int8):
|
||||
return cnp.NPY_INT8
|
||||
elif(dt == dtype_int16):
|
||||
return cnp.NPY_INT16
|
||||
elif(dt == dtype_int32):
|
||||
return cnp.NPY_INT32
|
||||
else:
|
||||
raise ValueError('Unknown data type')
|
||||
|
||||
|
||||
|
||||
ctypedef struct PyStreamData:
|
||||
PyObject* pyCallback
|
||||
@ -24,6 +40,8 @@ ctypedef struct PyStreamData:
|
||||
|
||||
double samplerate
|
||||
|
||||
cnp.NPY_TYPES npy_format
|
||||
|
||||
# If either of these queue pointers are NULL, it means the stream does not have an
|
||||
# input, or output.
|
||||
SafeQueue[void*] *inQueue
|
||||
@ -33,7 +51,6 @@ ctypedef struct PyStreamData:
|
||||
cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
|
||||
cdef:
|
||||
PyStreamData* sd = <PyStreamData*> voidsd
|
||||
cnp.NPY_TYPES npy_format
|
||||
|
||||
double* inbuffer = NULL
|
||||
double* outbuffer = NULL
|
||||
@ -43,8 +60,6 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
|
||||
unsigned nBytesPerChan= sd.nBytesPerChan
|
||||
unsigned nFramesPerBlock= sd.nFramesPerBlock
|
||||
|
||||
unsigned sw = sizeof(double)
|
||||
|
||||
double sleeptime = (<double> sd.nFramesPerBlock)/(4*sd.samplerate);
|
||||
us sleeptime_us = <us> (sleeptime*1e6);
|
||||
|
||||
@ -64,11 +79,10 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
|
||||
outbuffer,
|
||||
nFramesPerBlock,
|
||||
noutchannels,
|
||||
npy_format,
|
||||
sd.npy_format,
|
||||
False, # Do not transfer ownership
|
||||
True) # F-contiguous
|
||||
try:
|
||||
|
||||
rval = callback(None,
|
||||
npy_output,
|
||||
nFramesPerBlock,
|
||||
@ -93,10 +107,9 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
|
||||
inbuffer,
|
||||
nFramesPerBlock,
|
||||
ninchannels,
|
||||
npy_format,
|
||||
sd.npy_format,
|
||||
True, # Do transfer ownership
|
||||
True) # F-contiguous is True: data is Fortran-cont.
|
||||
|
||||
rval = callback(npy_input,
|
||||
None,
|
||||
nFramesPerBlock,
|
||||
@ -118,9 +131,6 @@ cdef void audioCallbackPythonThreadFunction(void* voidsd) nogil:
|
||||
fprintf(stderr, 'Exiting python thread...\n')
|
||||
|
||||
cdef class Daq:
|
||||
cdef:
|
||||
PyStreamData *sd
|
||||
cppDaq* daq_device
|
||||
|
||||
def __cinit__(self):
|
||||
|
||||
@ -132,9 +142,9 @@ cdef class Daq:
|
||||
self.sd = NULL
|
||||
|
||||
def __dealloc__(self):
|
||||
fprintf(stderr, "UlDaq.__dealloc__\n")
|
||||
# fprintf(stderr, "UlDaq.__dealloc__\n")
|
||||
if self.sd is not NULL:
|
||||
fprintf(stderr, "UlDaq.__dealloc__, stopping stream.\n")
|
||||
fprintf(stderr, "UlDaq.__dealloc__: stopping stream.\n")
|
||||
self.stop()
|
||||
|
||||
def isRunning(self):
|
||||
@ -144,143 +154,101 @@ cdef class Daq:
|
||||
def getDeviceInfo():
|
||||
cdef:
|
||||
vector[cppDeviceInfo] devinfos = cppDaq.getDeviceInfo()
|
||||
pydevinfo = []
|
||||
pydevinfo = {}
|
||||
for i in range(devinfos.size()):
|
||||
d = DeviceInfo()
|
||||
d.devinfo = <cppDeviceInfo> devinfos[i]
|
||||
pydevinfo.append(d)
|
||||
if d.api not in pydevinfo.keys():
|
||||
pydevinfo[d.api] = [d]
|
||||
else:
|
||||
pydevinfo[d.api].append(d)
|
||||
|
||||
return pydevinfo
|
||||
|
||||
# @cython.nonecheck(True)
|
||||
# def start(self, avstream):
|
||||
# """
|
||||
# Opens a stream with specified parameters
|
||||
@cython.nonecheck(True)
|
||||
def start(self, avstream):
|
||||
"""
|
||||
Opens a stream with specified parameters
|
||||
|
||||
# Args:
|
||||
# avstream: AvStream instance
|
||||
Args:
|
||||
avstream: AvStream instance
|
||||
|
||||
# Returns: None
|
||||
# """
|
||||
Returns: None
|
||||
"""
|
||||
cdef:
|
||||
DaqConfiguration pydaqconfig
|
||||
DeviceInfo pydevinfo
|
||||
cppDaqConfiguration* daqconfig
|
||||
cppDeviceInfo* devinfo
|
||||
vector[cppDeviceInfo] devinfos
|
||||
|
||||
# if self.sd is not NULL:
|
||||
# assert self.daq_device is not NULL
|
||||
# raise RuntimeError('Stream is already opened.')
|
||||
if self.sd is not NULL:
|
||||
assert self.daq_device is not NULL
|
||||
raise RuntimeError('Stream is already opened.')
|
||||
|
||||
# daqconfig = avstream.daqconfig
|
||||
# avtype = avstream.avtype
|
||||
# device = avstream.device
|
||||
pydaqconfig = avstream.daqconfig
|
||||
avtype = avstream.avtype
|
||||
pydevinfo = avstream.deviceinfo
|
||||
|
||||
# cdef:
|
||||
# bint duplex_mode = daqconfig.duplex_mode
|
||||
# bint monitorOutput = daqconfig.monitor_gen
|
||||
daqconfig = &(pydaqconfig.config)
|
||||
devinfo = &(pydevinfo.devinfo)
|
||||
|
||||
# unsigned int nFramesPerBlock = daqconfig.nFramesPerBlock
|
||||
# unsigned int samplerate
|
||||
self.daq_device = cppDaq.createDaq(devinfo[0], daqconfig[0])
|
||||
cdef:
|
||||
cppDaq* daq = self.daq_device
|
||||
|
||||
# int i
|
||||
# bint in_stream=False
|
||||
# bint out_stream=False
|
||||
cdef:
|
||||
unsigned int nFramesPerBlock = daq.framesPerBlock()
|
||||
double samplerate = daq.samplerate()
|
||||
DataType dtype = daq.dataType()
|
||||
|
||||
# cppDeviceInfo devinfo
|
||||
# DaqConfiguration cppconfig
|
||||
|
||||
# cppDaq* daq_device
|
||||
|
||||
# if daqconfig.nFramesPerBlock > 8192 or daqconfig.nFramesPerBlock < 512:
|
||||
# raise ValueError('Invalid number of nFramesPerBlock')
|
||||
if nFramesPerBlock > 8192 or nFramesPerBlock < 512:
|
||||
del self.daq_device
|
||||
raise ValueError('Invalid number of nFramesPerBlock')
|
||||
|
||||
|
||||
|
||||
# # if daqconfig.outputDelayBlocks != 0:
|
||||
# # print('WARNING: OutputDelayBlocks not supported by API')
|
||||
|
||||
# # Determine sample rate and sample format, determine whether we are an
|
||||
# # in or an output stream, or both
|
||||
# # print(f'AvType: {avtype}')
|
||||
# # print(f'Dup: {duplex_mode}')
|
||||
|
||||
# if avtype == AvType.audio_input or duplex_mode:
|
||||
# # Here, we override the sample format in case of duplex mode.
|
||||
# sampleformat = daqconfig.en_input_sample_format
|
||||
# samplerate = int(daqconfig.en_input_rate)
|
||||
# in_stream = True
|
||||
# if duplex_mode:
|
||||
# fprintf(stderr, 'Duplex mode enabled\n')
|
||||
# out_stream = True
|
||||
# elif avtype == AvType.audio_output:
|
||||
# sampleformat = daqconfig.en_output_sample_format
|
||||
# samplerate = int(daqconfig.en_output_rate)
|
||||
# out_stream = True
|
||||
# else:
|
||||
# raise ValueError(f'Invalid stream type {avtype}')
|
||||
|
||||
# if out_stream and daqconfig.firstEnabledOutputChannelNumber() == -1:
|
||||
# raise RuntimeError('No output channels enabled')
|
||||
|
||||
# if in_stream and daqconfig.firstEnabledInputChannelNumber() == -1:
|
||||
# raise RuntimeError('No input channels enabled')
|
||||
# All set, allocate the stream!
|
||||
self.sd = <PyStreamData*> malloc(sizeof(PyStreamData))
|
||||
if self.sd == NULL:
|
||||
del daq
|
||||
raise MemoryError('Could not allocate stream: memory error.')
|
||||
|
||||
|
||||
# # All set, allocate the stream!
|
||||
# self.sd = <PyStreamData*> malloc(sizeof(PyStreamData))
|
||||
# if self.sd == NULL:
|
||||
# raise MemoryError('Could not allocate stream: memory error.')
|
||||
self.sd.stopThread.store(False)
|
||||
self.sd.inQueue = NULL
|
||||
self.sd.outQueue = NULL
|
||||
|
||||
self.sd.thread = NULL
|
||||
self.sd.samplerate = <double> samplerate
|
||||
|
||||
# self.sd.stopThread.store(False)
|
||||
# self.sd.inQueue = NULL
|
||||
# self.sd.outQueue = NULL
|
||||
self.sd.ninchannels = daq.neninchannels()
|
||||
self.sd.noutchannels = daq.nenoutchannels()
|
||||
self.sd.nBytesPerChan = nFramesPerBlock*dtype.sw
|
||||
self.sd.nFramesPerBlock = nFramesPerBlock
|
||||
self.sd.npy_format = getNumpyDataType(dtype)
|
||||
|
||||
# self.sd.thread = NULL
|
||||
# self.sd.samplerate = <double> samplerate
|
||||
if daq.neninchannels() > 0:
|
||||
self.sd.inQueue = new SafeQueue[void*]()
|
||||
if daq.nenoutchannels() > 0:
|
||||
self.sd.inQueue = new SafeQueue[void*]()
|
||||
|
||||
# self.sd.ninchannels = 0
|
||||
# self.sd.noutchannels = 0
|
||||
# self.sd.nBytesPerChan = daqconfig.nFramesPerBlock*sizeof(double)
|
||||
# self.sd.nFramesPerBlock = daqconfig.nFramesPerBlock
|
||||
self.sd.pyCallback = <PyObject*> avstream._audioCallback
|
||||
|
||||
# Increase reference count to the callback
|
||||
Py_INCREF(<object> avstream._audioCallback)
|
||||
|
||||
# # Create channel maps for in channels, set in stream
|
||||
# # parameters
|
||||
# inch_enabled = 4*[False]
|
||||
# if in_stream:
|
||||
# inch_enabled = [True if ch.channel_enabled else False for ch in
|
||||
# daqconfig.getInputChannels()]
|
||||
with nogil:
|
||||
self.sd.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
|
||||
<void*> self.sd)
|
||||
|
||||
# self.sd.inQueue = new SafeQueue[void*]()
|
||||
# Allow stream stome time to start
|
||||
CPPsleep_ms(500)
|
||||
|
||||
# # Create channel maps for output channels
|
||||
# outch_enabled = 1*[False]
|
||||
# if out_stream:
|
||||
# outch_enabled = [True if ch.channel_enabled else False for ch in
|
||||
# daqconfig.getOutputChannels()]
|
||||
self.daq_device.start(
|
||||
self.sd.inQueue,
|
||||
self.sd.outQueue)
|
||||
|
||||
# self.sd.outQueue = new SafeQueue[void*]()
|
||||
|
||||
# daq_device = createDaqDevice(devinfo, cppconfig)
|
||||
|
||||
# self.sd.pyCallback = <PyObject*> avstream._audioCallback
|
||||
# self.sd.ninchannels = daq_device.neninchannels()
|
||||
# self.sd.noutchannels = daq_device.nenoutchannels()
|
||||
|
||||
# self.daq_device = daq_device
|
||||
|
||||
# # Increase reference count to the callback
|
||||
# Py_INCREF(<object> avstream._audioCallback)
|
||||
|
||||
# with nogil:
|
||||
# self.sd.thread = new CPPThread[void*, void (*)(void*)](audioCallbackPythonThreadFunction,
|
||||
# <void*> self.sd)
|
||||
|
||||
# # Allow it to start
|
||||
# CPPsleep_ms(500)
|
||||
|
||||
# self.daq_device.start(
|
||||
# self.sd.inQueue,
|
||||
# self.sd.outQueue)
|
||||
|
||||
# return nFramesPerBlock, self.daq_device.samplerate()
|
||||
return nFramesPerBlock, self.daq_device.samplerate()
|
||||
|
||||
def stop(self):
|
||||
if self.sd is NULL:
|
||||
|
@ -0,0 +1,5 @@
|
||||
include "lasp_common_decls.pxd"
|
||||
|
||||
cdef class DaqConfiguration:
|
||||
cdef:
|
||||
cppDaqConfiguration config
|
@ -13,6 +13,7 @@ from typing import List
|
||||
from dataclasses_json import dataclass_json
|
||||
from ..lasp_common import lasp_shelve, SIQtys, Qty
|
||||
|
||||
import json
|
||||
|
||||
|
||||
class CouplingMode:
|
||||
@ -25,111 +26,126 @@ class Range:
|
||||
tenV = '+/- 10 V'
|
||||
undefined = 'Undefined'
|
||||
|
||||
|
||||
@dataclass_json
|
||||
@dataclass
|
||||
class DAQChannel:
|
||||
class DaqChannel:
|
||||
channel_enabled: bool
|
||||
channel_name: str = 'Unnamed channel'
|
||||
sensitivity: float = 1.0
|
||||
qty: Qty = SIQtys.default
|
||||
range_: str = 'Undefined'
|
||||
range_index: int = 0
|
||||
ACCoupling_enabled: bool = False
|
||||
IEPE_enabled: bool = False
|
||||
|
||||
|
||||
|
||||
cdef class DAQConfiguration:
|
||||
cdef class DaqConfiguration:
|
||||
"""
|
||||
Initialize a device descriptor
|
||||
"""
|
||||
# DaqConfiguration config
|
||||
|
||||
def getInputChannels(self):
|
||||
return self.input_channel_configs
|
||||
@staticmethod
|
||||
def from_json(self, jsonstring):
|
||||
config_dict = json.loads(jsonstring)
|
||||
return DaqConfiguration.from_dict(config_dict)
|
||||
|
||||
def getOutputChannels(self):
|
||||
return self.output_channel_configs
|
||||
@staticmethod
|
||||
def from_dict(pydict):
|
||||
cdef:
|
||||
cppDaqConfiguration config
|
||||
vector[DaqApi] apis = DaqApi.getAvailableApis()
|
||||
|
||||
config.api = apis[pydict['apicode']]
|
||||
config.device_name = pydict['device_name'].encode('utf-8')
|
||||
config.eninchannels = pydict['eninchannels']
|
||||
config.enoutchannels = pydict['enoutchannels']
|
||||
config.inchannel_names = pydict['inchannel_names']
|
||||
config.outchannel_names = pydict['outchannel_names']
|
||||
config.sampleRateIndex = pydict['sampleRateIndex']
|
||||
config.framesPerBlockIndex = pydict['framesPerBlockIndex']
|
||||
config.dataTypeIndex = pydict['dataTypeIndex']
|
||||
config.monitorOutput = pydict['monitorOutput']
|
||||
config.inputIEPEEnabled = pydict['inputIEPEEnabled']
|
||||
config.inputACCouplingMode = pydict['inputACCouplingMode']
|
||||
config.inputRangeIndices = pydict['inputRangeIndices']
|
||||
|
||||
def firstEnabledOutputChannelNumber(self):
|
||||
"""
|
||||
Returns the channel number of the first enabled output channel. Returns -1 if
|
||||
no channels are enabled.
|
||||
"""
|
||||
for i, ch in enumerate(self.output_channel_configs):
|
||||
if ch.channel_enabled:
|
||||
return i
|
||||
return -1
|
||||
pydaqcfg = DaqConfiguration()
|
||||
pydaqcfg.config = config
|
||||
|
||||
def lastEnabledInputChannelNumber(self):
|
||||
last = -1
|
||||
for i, ch in enumerate(self.input_channel_configs):
|
||||
if ch.channel_enabled:
|
||||
last = i
|
||||
return last
|
||||
return pydaqcfg
|
||||
|
||||
def lastEnabledOutputChannelNumber(self):
|
||||
last = -1
|
||||
for i, ch in enumerate(self.output_channel_configs):
|
||||
print(ch)
|
||||
if ch.channel_enabled:
|
||||
last = i
|
||||
return last
|
||||
def to_json(self):
|
||||
return json.dumps(dict(
|
||||
apicode = self.api.apicode,
|
||||
device_name = self.config.device_name.decode('utf-8'),
|
||||
|
||||
def getEnabledInputChannels(self):
|
||||
en_channels = []
|
||||
for chan in self.input_channel_configs:
|
||||
if chan.channel_enabled:
|
||||
en_channels.append(chan)
|
||||
return en_channels
|
||||
eninchannels = self.eninchannels(),
|
||||
enoutchannels = self.enoutchannels(),
|
||||
|
||||
def getEnabledInputChannelNames(self):
|
||||
return [ch.channel_name for ch in self.getEnabledInputChannels()]
|
||||
inchannel_names = [name.decode('utf-8') for name in
|
||||
self.config.inchannel_names],
|
||||
outchannel_names = [name.decode('utf-8') for name in
|
||||
self.config.outchannel_names],
|
||||
sampleRateIndex = self.config.sampleRateIndex,
|
||||
dataTypeIndex = self.config.dataTypeIndex,
|
||||
nFramesPerBlockIndex = self.config.framesPerBlockIndex,
|
||||
monitorOutput = self.config.monitorOutput,
|
||||
|
||||
def getEnabledInputChannelQtys(self):
|
||||
return [ch.qty for ch in self.getEnabledInputChannels()]
|
||||
inputIEPEEnabled = self.config.inputIEPEEnabled,
|
||||
inputACCouplingMode = self.config.inputACCouplingMode,
|
||||
inputRangeIndices = self.config.inputRangeIndices,
|
||||
))
|
||||
|
||||
def getEnabledInputChannelSensitivities(self):
|
||||
return [
|
||||
float(channel.sensitivity)
|
||||
for channel in self.getEnabledInputChannels()
|
||||
]
|
||||
def getInChannel(self, i:int):
|
||||
return DaqChannel(
|
||||
channel_enabled=self.config.eninchannels[i],
|
||||
channel_name=self.config.inchannel_names[i].decode('utf-8'),
|
||||
sensitivity=self.config.inchannel_sensitivities[i],
|
||||
range_index=self.config.inputRangeIndices[i],
|
||||
ACCoupling_enabled=self.config.inputACCouplingMode[i],
|
||||
IEPE_enabled=self.config.inputIEPEEnabled[i]
|
||||
)
|
||||
def getOutChannel(self, i:int):
|
||||
return DaqChannel(
|
||||
channel_enabled=self.config.enoutchannels[i],
|
||||
channel_name=self.config.outchannel_names[i].decode('utf-8'),
|
||||
)
|
||||
def setInChannel(self, i:int, daqchannel: DaqChannel):
|
||||
self.config.eninchannels[i] = daqchannel.channel_enabled
|
||||
self.config.inchannel_names[i] = daqchannel.channel_name
|
||||
self.config.inchannel_sensitivities[i] = daqchannel.sensitivity
|
||||
self.config.inputRangeIndices[i] = daqchannel.range_index
|
||||
self.config.inputACCouplingMode[i] = daqchannel.ACCoupling_enabled
|
||||
self.config.inputIEPEEnabled[i] = daqchannel.IEPE_enabled
|
||||
|
||||
def getEnabledOutputChannels(self):
|
||||
en_channels = []
|
||||
for chan in self.output_channel_configs:
|
||||
if chan.channel_enabled:
|
||||
en_channels.append(chan)
|
||||
return en_channels
|
||||
def setOutChannel(self, i:int, daqchannel: DaqChannel):
|
||||
self.config.enoutchannels[i] = daqchannel.channel_enabled
|
||||
self.config.outchannel_names[i] = daqchannel.channel_name
|
||||
|
||||
def getEnabledOutputChannelQtys(self):
|
||||
return [ch.qty for ch in self.getEnabledOutputChannels()]
|
||||
def eninchannels(self):
|
||||
return self.config.eninchannels
|
||||
|
||||
def getEnabledOutputChannelSensitivities(self):
|
||||
return [
|
||||
float(channel.sensitivity)
|
||||
for channel in self.getEnabledOutputChannels()
|
||||
]
|
||||
def enoutchannels(self):
|
||||
return self.config.enoutchannels
|
||||
|
||||
@staticmethod
|
||||
def loadConfigsJSON():
|
||||
with lasp_shelve() as sh:
|
||||
configs_json = sh.load('daqconfigs', {})
|
||||
return configs_json
|
||||
return configs_json
|
||||
|
||||
@staticmethod
|
||||
def loadConfigs():
|
||||
"""
|
||||
Returns a list of currently available configurations
|
||||
"""
|
||||
configs_json = DAQConfiguration.loadConfigsJSON()
|
||||
configs_json = DaqConfiguration.loadConfigsJSON()
|
||||
configs = {}
|
||||
for name, val in configs_json.items():
|
||||
configs[name] = DAQConfiguration.from_json(val)
|
||||
return configs
|
||||
configs[name] = DaqConfiguration.from_json(val)
|
||||
return configs
|
||||
|
||||
def saveConfig(self, name):
|
||||
configs_json = DAQConfiguration.loadConfigsJSON()
|
||||
configs_json = DaqConfiguration.loadConfigsJSON()
|
||||
|
||||
with lasp_shelve() as sh:
|
||||
configs_json[name] = self.to_json()
|
||||
@ -138,6 +154,6 @@ cdef class DAQConfiguration:
|
||||
@staticmethod
|
||||
def deleteConfig(name):
|
||||
with lasp_shelve() as sh:
|
||||
cur_configs = DAQConfiguration.loadConfigs()
|
||||
cur_configs = DaqConfiguration.loadConfigs()
|
||||
del cur_configs[name]
|
||||
sh.store('daqconfigs', cur_configs)
|
||||
|
@ -48,7 +48,7 @@ import os
|
||||
import time
|
||||
import wave
|
||||
from .lasp_common import SIQtys, Qty
|
||||
from .device import DAQChannel
|
||||
from .device import DaqChannel
|
||||
import logging
|
||||
|
||||
|
||||
@ -217,7 +217,7 @@ class Measurement:
|
||||
|
||||
@property
|
||||
def channelConfig(self):
|
||||
return [DAQChannel(channel_enabled=True,
|
||||
return [DaqChannel(channel_enabled=True,
|
||||
channel_name=chname,
|
||||
sensitivity=sens,
|
||||
qty=qty)
|
||||
|
Loading…
Reference in New Issue
Block a user