#ifndef LASP_CPPDAQ_H #define LASP_CPPDAQ_H #ifdef HAS_RTAUDIO_API #include #endif #ifdef HAS_ULDAQ_API #include #endif #include "lasp_cppqueue.h" #include "string" #include "vector" #include #include #include using std::cerr; using std::cout; using std::endl; using std::getline; using std::runtime_error; using std::string; using std::vector; using std::to_string; typedef unsigned int us; typedef vector boolvec; typedef vector dvec; typedef vector usvec; typedef std::lock_guard mutexlock; class DataType { public: string 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) {} bool operator==(const DataType &o) { return (name == o.name && sw == o.sw && is_floating == o.is_floating); } }; const DataType dtype_invalid; const DataType dtype_fl32("32-bits floating point", 4, true); const DataType dtype_fl64("64-bits floating point", 8, true); const DataType dtype_int8("8-bits integers", 1, false); const DataType dtype_int16("16-bits integers", 2, false); const DataType dtype_int32("32-bits integers", 4, false); const std::vector dataTypes = { dtype_int8, dtype_int16, dtype_int32, dtype_fl32, dtype_fl64, }; class DaqApi { public: string apiname = "Invalid API"; int apicode = -1; 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); } operator string() const { return apiname + ", code: " + to_string(apicode); } static vector getAvailableApis(); }; #ifdef HAS_ULDAQ_API 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", 5, RtAudio::Api::WINDOWS_ASIO); #endif // Structure containing device info parameters class DeviceInfo { public: DaqApi api; string device_name = ""; int api_specific_devindex = -1; vector availableDataTypes; int prefDataTypeIndex = 0; vector availableSampleRates; int prefSampleRateIndex = -1; vector availableFramesPerBlock; unsigned prefFramesPerBlockIndex = 0; dvec availableInputRanges; int prefInputRangeIndex = 0; unsigned ninchannels = 0; unsigned noutchannels = 0; bool hasInputIEPE = false; bool hasInputACCouplingSwitch = false; bool hasInputTrigger = false; /* DeviceInfo(): */ /* datatype(dtype_invalid) { } */ double prefSampleRate() const { if (((us)prefSampleRateIndex < availableSampleRates.size()) && (prefSampleRateIndex >= 0)) { return availableSampleRates.at(prefSampleRateIndex); } else { throw std::runtime_error("No prefered sample rate available"); } } operator string() const { std::stringstream str; str << api.apiname + " " << api_specific_devindex << endl << " number of input channels: " << ninchannels << endl << " number of output channels: " << noutchannels << endl; return str.str(); } string serialize() const { // Simple serializer for this object, used because we found a bit late that // this object needs to be send over the wire. We do not want to make this // implementation in Python, as these objects are created here, in the C++ // code. The Python wrapper is just a readonly wrapper. std::stringstream str; str << api.apiname << "\t"; str << api.apicode << "\t"; str << api.api_specific_subcode << "\t"; str << device_name << "\t"; str << availableDataTypes.size() << "\t"; for(const DataType& dtype: availableDataTypes) { // WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!! str << dtype.name << "\t"; str << dtype.sw << "\t"; str << dtype.is_floating << "\t"; } str << prefDataTypeIndex << "\t"; str << availableSampleRates.size() << "\t"; for(const double& fs: availableSampleRates) { // WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!! str << fs << "\t"; } str << prefSampleRateIndex << "\t"; str << availableFramesPerBlock.size() << "\t"; for(const us& fb: availableFramesPerBlock) { // WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!! str << fb << "\t"; } str << prefFramesPerBlockIndex << "\t"; str << availableInputRanges.size() << "\t"; for(const double& ir: availableInputRanges) { // WARNING: THIS GOES COMPLETELY WRONG WHEN NAMES contain A TAB!!! str << ir << "\t"; } str << prefInputRangeIndex << "\t"; str << ninchannels << "\t"; str << noutchannels << "\t"; str << int(hasInputIEPE) << "\t"; str << int(hasInputACCouplingSwitch) << "\t"; str << int(hasInputTrigger) << "\t"; return str.str(); } static DeviceInfo deserialize(const string& dstr) { DeviceInfo devinfo; std::stringstream str(dstr); string tmp; us N; // Lambda functions for deserializing auto nexts = [&]() { getline(str, tmp, '\t'); return tmp; }; auto nexti = [&]() { getline(str, tmp, '\t'); return std::atoi(tmp.c_str()); }; auto nextf = [&]() { getline(str, tmp, '\t'); return std::atof(tmp.c_str()); }; // Api string apiname = nexts(); auto apicode = nexti(); auto api_specific_subcode = nexti(); DaqApi api(apiname, apicode, api_specific_subcode); devinfo.api = api; devinfo.device_name = nexts(); N = us(nexti()); for(us i=0;i inchannel_sensitivities; vector inchannel_names; vector inchannel_metadata; vector outchannel_sensitivities; vector outchannel_names; vector outchannel_metadata; us sampleRateIndex = 0; // Index in list of sample rates us dataTypeIndex = 0; // Required datatype for output, should be // present in the list us framesPerBlockIndex = 0; bool monitorOutput = false; boolvec inputIEPEEnabled; boolvec inputACCouplingMode; usvec inputRangeIndices; // Create a default configuration, with all channels disabled on both // input and output, and default channel names DaqConfiguration(const DeviceInfo &device); DaqConfiguration() {} 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 { mutable std::mutex mutex; public: static vector getDeviceInfo(); static Daq *createDaq(const DeviceInfo &, const DaqConfiguration &config); Daq(const DeviceInfo &devinfo, const DaqConfiguration &config); virtual void start(SafeQueue *inqueue, SafeQueue *outqueue) = 0; virtual void stop() = 0; virtual bool isRunning() const = 0; virtual ~Daq(){}; 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.at(framesPerBlockIndex); } bool duplexMode() const { return (neninchannels(false) > 0 && nenoutchannels() > 0); } }; #endif // LASP_CPPDAQ_H