Input stream and output stream both running. Added lockfree boost dependency to git modules, removed carma build as it is not required and every time again downloads Armadillo. Added functions to enable / disable all channels at once. Fixed a bug with RtAudio input streams. Fixed a bug in StreamMgr leading to segfaults (how to: use std::move ;)).

This commit is contained in:
Anne de Jong 2022-09-27 17:20:45 +02:00
parent 5ce5fba50b
commit 288e7c8dc5
17 changed files with 244 additions and 182 deletions

8
.gitmodules vendored
View File

@ -14,7 +14,7 @@
path = third_party/tomlplusplus path = third_party/tomlplusplus
url = https://github.com/marzer/tomlplusplus url = https://github.com/marzer/tomlplusplus
[submodule "third_party/lockfree"] [submodule "third_party/lockfree"]
path = third_party/lockfree path = third_party/boost/lockfree
url = https://github.com/boostorg/lockfree url = https://github.com/boostorg/lockfree
[submodule "third_party/carma"] [submodule "third_party/carma"]
path = third_party/carma path = third_party/carma
@ -22,3 +22,9 @@
[submodule "third_party/thread-pool"] [submodule "third_party/thread-pool"]
path = third_party/thread-pool path = third_party/thread-pool
url = https://github.com/bshoshany/thread-pool url = https://github.com/bshoshany/thread-pool
[submodule "third_party/rtaudio"]
path = third_party/rtaudio
url = https://github.com/thestk/rtaudio
[submodule "third_party/boost/core"]
path = third_party/boost/core
url = https://github.com/boostorg/core

View File

@ -80,7 +80,6 @@ set(CMAKE_C_FLAGS_RELEASE "-O3 -flto -mfpmath=sse -march=x86-64 -mtune=native \
# ############################# End compilation flags # ############################# End compilation flags
include_directories(/usr/lib/python3.10/site-packages/numpy/core/include) include_directories(/usr/lib/python3.10/site-packages/numpy/core/include)
add_subdirectory(third_party/carma)
if(LASP_FFT_BACKEND STREQUAL "FFTW") if(LASP_FFT_BACKEND STREQUAL "FFTW")
find_library(fftw3 REQUIRED NAMES fftw fftw3) find_library(fftw3 REQUIRED NAMES fftw fftw3)

View File

@ -5,14 +5,15 @@ add_definitions(-DARMA_DONT_USE_WRAPPER)
configure_file(lasp_config.h.in lasp_config.h) configure_file(lasp_config.h.in lasp_config.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(SYSTEM ../../third_party/carma/include)
include_directories(SYSTEM
../../third_party/carma/extern/armadillo-code/include)
include_directories(SYSTEM include_directories(SYSTEM
../../third_party/carma/extern/pybind11/include) ../../third_party/carma/extern/pybind11/include)
include_directories(SYSTEM
../../third_party/carma/extern/armadillo-code/include)
include_directories(SYSTEM ../../third_party/carma/include)
include_directories(../../third_party/DebugTrace-cpp/include) include_directories(../../third_party/DebugTrace-cpp/include)
include_directories(../../third_party/lockfreeThreadsafe/include) include_directories(../../third_party/boost/core/include)
include_directories(../../third_party/boost/lockfree/include)
include_directories(../../third_party/gsl-lite/include) include_directories(../../third_party/gsl-lite/include)
include_directories(../../third_party/tomlplusplus/include) include_directories(../../third_party/tomlplusplus/include)
include_directories(../../third_party/thread-pool) include_directories(../../third_party/thread-pool)

View File

@ -2,8 +2,6 @@
#include "debugtrace.hpp" #include "debugtrace.hpp"
#include "lasp_daqconfig.h" #include "lasp_daqconfig.h"
DEBUGTRACE_VARIABLES;
#include "lasp_daq.h" #include "lasp_daq.h"
#if LASP_HAS_ULDAQ == 1 #if LASP_HAS_ULDAQ == 1
#include "lasp_uldaq.h" #include "lasp_uldaq.h"
@ -57,6 +55,7 @@ Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config)
} }
double Daq::samplerate() const { double Daq::samplerate() const {
DEBUGTRACE_ENTER;
return availableSampleRates.at(sampleRateIndex); return availableSampleRates.at(sampleRateIndex);
} }

View File

@ -1,4 +1,4 @@
#define DEBUGTRACE_ENABLED /* #define DEBUGTRACE_ENABLED */
#include "debugtrace.hpp" #include "debugtrace.hpp"
#include "lasp_daqconfig.h" #include "lasp_daqconfig.h"

View File

@ -315,6 +315,28 @@ public:
* enabled. * enabled.
*/ */
int getLowestOutChannel() const; int getLowestOutChannel() const;
/**
* @brief Set all input channels to enabled / disabled state.
*
* @param val true if enabled, false if disabled.
*/
void setAllInputEnabled(bool val) {
for(auto& ch: inchannel_config) {
ch.enabled = val;
}
}
/**
* @brief Set all output channels to enabled / disabled state.
*
* @param val true if enabled, false if disabled.
*/
void setAllOutputEnabled(bool val) {
for(auto& ch: outchannel_config) {
ch.enabled = val;
}
}
}; };
/** /**
* @} * @}

View File

@ -1,6 +1,7 @@
#include "lasp_daqdata.h" /* #define DEBUGTRACE_ENABLED */
#include "debugtrace.hpp" #include "debugtrace.hpp"
#include <cassert> #include <cassert>
#include "lasp_daqdata.h"
DEBUGTRACE_VARIABLES; DEBUGTRACE_VARIABLES;

View File

@ -17,6 +17,7 @@ using std::vector;
DEBUGTRACE_VARIABLES; DEBUGTRACE_VARIABLES;
void fillRtAudioDeviceInfo(vector<DeviceInfo> &devinfolist) { void fillRtAudioDeviceInfo(vector<DeviceInfo> &devinfolist) {
DEBUGTRACE_ENTER;
vector<RtAudio::Api> apis; vector<RtAudio::Api> apis;
RtAudio::getCompiledApi(apis); RtAudio::getCompiledApi(apis);
@ -34,25 +35,25 @@ void fillRtAudioDeviceInfo(vector<DeviceInfo> &devinfolist) {
// "Our device info struct" // "Our device info struct"
DeviceInfo d; DeviceInfo d;
switch (api) { switch (api) {
case RtAudio::LINUX_ALSA: case RtAudio::LINUX_ALSA:
d.api = rtaudioAlsaApi; d.api = rtaudioAlsaApi;
break; break;
case RtAudio::LINUX_PULSE: case RtAudio::LINUX_PULSE:
d.api = rtaudioPulseaudioApi; d.api = rtaudioPulseaudioApi;
break; break;
case RtAudio::WINDOWS_WASAPI: case RtAudio::WINDOWS_WASAPI:
d.api = rtaudioWasapiApi; d.api = rtaudioWasapiApi;
break; break;
case RtAudio::WINDOWS_DS: case RtAudio::WINDOWS_DS:
d.api = rtaudioDsApi; d.api = rtaudioDsApi;
break; break;
case RtAudio::WINDOWS_ASIO: case RtAudio::WINDOWS_ASIO:
d.api = rtaudioAsioApi; d.api = rtaudioAsioApi;
break; break;
default: default:
cerr << "Not implemented RtAudio API, skipping." << endl; cerr << "Not implemented RtAudio API, skipping." << endl;
continue; continue;
break; break;
} }
d.device_name = devinfo.name; d.device_name = devinfo.name;
@ -82,7 +83,7 @@ void fillRtAudioDeviceInfo(vector<DeviceInfo> &devinfolist) {
} }
/* if (formats & RTAUDIO_SINT32) { */ /* if (formats & RTAUDIO_SINT32) { */
/* d.availableDataTypes.push_back(DataTypeDescriptor::DataType::dtype_int24); /* d.availableDataTypes.push_back(DataTypeDescriptor::DataType::dtype_int24);
*/ */
/* } */ /* } */
if (formats & RTAUDIO_SINT32) { if (formats & RTAUDIO_SINT32) {
d.availableDataTypes.push_back( d.availableDataTypes.push_back(
@ -106,8 +107,9 @@ void fillRtAudioDeviceInfo(vector<DeviceInfo> &devinfolist) {
} }
} }
static int mycallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, static int mycallback(void *outputBuffer, void *inputBuffer,
double streamTime, RtAudioStreamStatus status, void *userData); unsigned int nFrames, double streamTime,
RtAudioStreamStatus status, void *userData);
static void myerrorcallback(RtAudioError::Type, const string &errorText); static void myerrorcallback(RtAudioError::Type, const string &errorText);
@ -124,91 +126,91 @@ class RtAudioDaq : public Daq {
std::atomic<StreamStatus> _streamStatus{}; std::atomic<StreamStatus> _streamStatus{};
public: public:
RtAudioDaq(const DeviceInfo &devinfo, const DaqConfiguration &config) RtAudioDaq(const DeviceInfo &devinfo, const DaqConfiguration &config)
: Daq(devinfo, config), : Daq(devinfo, config),
rtaudio(static_cast<RtAudio::Api>(devinfo.api.api_specific_subcode)), rtaudio(static_cast<RtAudio::Api>(devinfo.api.api_specific_subcode)),
nFramesPerBlock(Daq::framesPerBlock()) { nFramesPerBlock(Daq::framesPerBlock()) {
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
// We make sure not to run RtAudio in duplex mode. This seems to be buggy // We make sure not to run RtAudio in duplex mode. This seems to be buggy
// and untested. Better to use a hardware-type loopback into the system. // and untested. Better to use a hardware-type loopback into the system.
if (duplexMode()) { if (duplexMode()) {
throw runtime_error("RtAudio backend cannot run in duplex mode."); throw runtime_error("RtAudio backend cannot run in duplex mode.");
}
assert(!monitorOutput);
std::unique_ptr<RtAudio::StreamParameters> inParams, outParams;
if (neninchannels() > 0) {
inParams = std::make_unique<RtAudio::StreamParameters>();
// +1 to get the count.
inParams->nChannels = getHighestInChannel() + 1;
if (inParams->nChannels < 1) {
throw runtime_error("Invalid input number of channels");
} }
assert(!monitorOutput); inParams->firstChannel = 0;
inParams->deviceId = devinfo.api_specific_devindex;
std::unique_ptr<RtAudio::StreamParameters> inParams, outParams; } else {
if (neninchannels() > 0) { outParams = std::make_unique<RtAudio::StreamParameters>();
inParams = std::make_unique<RtAudio::StreamParameters>(); outParams->nChannels = getHighestOutChannel() + 1;
if (outParams->nChannels < 1) {
// +1 to get the count. throw runtime_error("Invalid output number of channels");
inParams->nChannels = getHighestInChannel() + 1;
if (inParams->nChannels < 1) {
throw runtime_error("Invalid input number of channels");
}
inParams->firstChannel = 0;
inParams->deviceId = devinfo.api_specific_devindex;
} else {
outParams = std::make_unique<RtAudio::StreamParameters>();
outParams->nChannels = getHighestOutChannel() + 1;
if (outParams->nChannels < 1) {
throw runtime_error("Invalid output number of channels");
}
outParams->firstChannel = 0;
outParams->deviceId = devinfo.api_specific_devindex;
} }
outParams->firstChannel = 0;
RtAudio::StreamOptions streamoptions; outParams->deviceId = devinfo.api_specific_devindex;
streamoptions.flags = RTAUDIO_HOG_DEVICE | RTAUDIO_NONINTERLEAVED;
streamoptions.numberOfBuffers = 2;
streamoptions.streamName = "RtAudio stream";
streamoptions.priority = 0;
RtAudioFormat format;
using Dtype = DataTypeDescriptor::DataType;
const Dtype dtype = dataType();
switch (dtype) {
case Dtype::dtype_fl32:
format = RTAUDIO_FLOAT32;
break;
case Dtype::dtype_fl64:
format = RTAUDIO_FLOAT64;
break;
case Dtype::dtype_int8:
format = RTAUDIO_SINT8;
break;
case Dtype::dtype_int16:
format = RTAUDIO_SINT16;
break;
case Dtype::dtype_int32:
format = RTAUDIO_SINT32;
break;
default:
throw runtime_error("Invalid data type specified for DAQ stream.");
break;
}
// Copy here, as it is used to return the *actual* number of frames per
// block.
unsigned int nFramesPerBlock_copy = nFramesPerBlock;
// Final step: open the stream.
rtaudio.openStream(outParams.get(), inParams.get(), format,
static_cast<us>(samplerate()), &nFramesPerBlock_copy,
mycallback, (void *)this, &streamoptions,
&myerrorcallback);
} }
RtAudio::StreamOptions streamoptions;
streamoptions.flags = RTAUDIO_HOG_DEVICE | RTAUDIO_NONINTERLEAVED;
streamoptions.numberOfBuffers = 2;
streamoptions.streamName = "RtAudio stream";
streamoptions.priority = 0;
RtAudioFormat format;
using Dtype = DataTypeDescriptor::DataType;
const Dtype dtype = dataType();
switch (dtype) {
case Dtype::dtype_fl32:
format = RTAUDIO_FLOAT32;
break;
case Dtype::dtype_fl64:
format = RTAUDIO_FLOAT64;
break;
case Dtype::dtype_int8:
format = RTAUDIO_SINT8;
break;
case Dtype::dtype_int16:
format = RTAUDIO_SINT16;
break;
case Dtype::dtype_int32:
format = RTAUDIO_SINT32;
break;
default:
throw runtime_error("Invalid data type specified for DAQ stream.");
break;
}
// Copy here, as it is used to return the *actual* number of frames per
// block.
unsigned int nFramesPerBlock_copy = nFramesPerBlock;
// Final step: open the stream.
rtaudio.openStream(outParams.get(), inParams.get(), format,
static_cast<us>(samplerate()), &nFramesPerBlock_copy,
mycallback, (void *)this, &streamoptions,
&myerrorcallback);
}
virtual void start(InDaqCallback inCallback, virtual void start(InDaqCallback inCallback,
OutDaqCallback outCallback) override { OutDaqCallback outCallback) override final {
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
@ -221,24 +223,25 @@ class RtAudioDaq : public Daq {
// Logical XOR // Logical XOR
if (inCallback && outCallback) { if (inCallback && outCallback) {
throw runtime_error("Either input or output stream possible for RtAudio. " throw runtime_error("Either input or output stream possible for RtAudio. "
"Stream duplex mode not provided."); "Stream duplex mode not provided.");
} }
if (inCallback) { if (inCallback) {
_incallback = inCallback; _incallback = inCallback;
if (neninchannels()==0) { if (neninchannels() == 0) {
throw runtime_error( throw runtime_error(
"Input callback given, but stream does not provide input data"); "Input callback given, but stream does not provide input data");
} }
} }
if (outCallback) { if (outCallback) {
_outcallback = outCallback; _outcallback = outCallback;
if (nenoutchannels()==0) { if (nenoutchannels() == 0) {
throw runtime_error( throw runtime_error(
"Output callback given, but stream does not provide output data"); "Output callback given, but stream does not provide output data");
} }
} }
// Start the stream. Throws on error.
rtaudio.startStream(); rtaudio.startStream();
// If we are here, we are running without errors. // If we are here, we are running without errors.
@ -247,9 +250,9 @@ class RtAudioDaq : public Daq {
_streamStatus = status; _streamStatus = status;
} }
StreamStatus getStreamStatus() const override { return _streamStatus; } StreamStatus getStreamStatus() const override final { return _streamStatus; }
void stop() override { void stop() override final {
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
if (getStreamStatus().runningOK()) { if (getStreamStatus().runningOK()) {
rtaudio.stopStream(); rtaudio.stopStream();
@ -261,11 +264,10 @@ class RtAudioDaq : public Daq {
} }
int streamCallback(void *outputBuffer, void *inputBuffer, int streamCallback(void *outputBuffer, void *inputBuffer,
unsigned int nFrames, double streamTime, unsigned int nFrames, double streamTime,
RtAudioStreamStatus status) {
RtAudioStreamStatus status) { DEBUGTRACE_ENTER;
/* DEBUGTRACE_ENTER; */
using se = StreamStatus::StreamError; using se = StreamStatus::StreamError;
@ -280,16 +282,16 @@ class RtAudioDaq : public Daq {
}; };
switch (status) { switch (status) {
case RTAUDIO_INPUT_OVERFLOW: case RTAUDIO_INPUT_OVERFLOW:
stopWithError(se::inputXRun); stopWithError(se::inputXRun);
return 1; return 1;
break; break;
case RTAUDIO_OUTPUT_UNDERFLOW: case RTAUDIO_OUTPUT_UNDERFLOW:
stopWithError(se::outputXRun); stopWithError(se::outputXRun);
return 1; return 1;
break; break;
default: default:
break; break;
} }
const auto &dtype_descr = DataTypeDescriptor(); const auto &dtype_descr = DataTypeDescriptor();
@ -299,25 +301,27 @@ class RtAudioDaq : public Daq {
us sw = dtype_descr.sw; us sw = dtype_descr.sw;
if (nFrames != nFramesPerBlock) { if (nFrames != nFramesPerBlock) {
cerr << "RtAudio backend error: nFrames does not match block size!" cerr << "RtAudio backend error: nFrames does not match block size!"
<< endl; << endl;
stopWithError(se::logicError); stopWithError(se::logicError);
return 1; return 1;
} }
if (inputBuffer) { if (inputBuffer) {
assert(_incallback);
std::vector<uint8_t *> ptrs; std::vector<uint8_t *> ptrs;
ptrs.reserve(neninchannels); ptrs.reserve(neninchannels);
/* DaqData(neninchannels_inc_mon, nFramesPerBlock, dtype); */
us i = 0;
for (int ch = getLowestInChannel(); ch <= getHighestInChannel(); ch++) { for (int ch = getLowestInChannel(); ch <= getHighestInChannel(); ch++) {
if (inchannel_config.at(ch).enabled) { if (inchannel_config.at(ch).enabled) {
ptrs.push_back(&static_cast<uint8_t *>( ptrs.push_back(static_cast<uint8_t *>(inputBuffer) +
inputBuffer)[sw * ninchannels * ch * nFramesPerBlock]); sw * i * nFramesPerBlock);
i++;
} }
} }
DaqData d{neninchannels, nFramesPerBlock, dtype}; DaqData d{neninchannels, nFramesPerBlock, dtype};
d.copyInFromRaw(ptrs); d.copyInFromRaw(ptrs);
assert(_incallback);
bool ret = _incallback(d); bool ret = _incallback(d);
if (!ret) { if (!ret) {
stopWithError(se::noError); stopWithError(se::noError);
@ -326,20 +330,24 @@ class RtAudioDaq : public Daq {
} }
if (outputBuffer) { if (outputBuffer) {
assert(_outcallback);
std::vector<uint8_t *> ptrs; std::vector<uint8_t *> ptrs;
ptrs.reserve(nenoutchannels); ptrs.reserve(nenoutchannels);
/* outCallback */ /* outCallback */
for (int ch = 0; ch <= getHighestOutChannel(); ch++) { us i = 0;
/* DEBUGTRACE_PRINT(outchannel_config.at(ch).enabled); */ for (int ch = getLowestOutChannel(); ch <= getHighestOutChannel(); ch++) {
if (outchannel_config.at(ch).enabled) { if (outchannel_config.at(ch).enabled) {
ptrs.push_back(&(static_cast<uint8_t *>(
outputBuffer)[sw * nenoutchannels * ch * nFramesPerBlock])); ptrs.push_back(static_cast<uint8_t *>(outputBuffer) +
sw * i * nFramesPerBlock);
i++;
} }
} }
DaqData d{nenoutchannels, nFramesPerBlock, dtype}; DaqData d{nenoutchannels, nFramesPerBlock, dtype};
assert(_outcallback);
bool ret = _outcallback(d); bool ret = _outcallback(d);
if (!ret) { if (!ret) {
stopWithError(se::noError); stopWithError(se::noError);
@ -362,7 +370,7 @@ class RtAudioDaq : public Daq {
}; };
std::unique_ptr<Daq> createRtAudioDevice(const DeviceInfo &devinfo, std::unique_ptr<Daq> createRtAudioDevice(const DeviceInfo &devinfo,
const DaqConfiguration &config) { const DaqConfiguration &config) {
return std::make_unique<RtAudioDaq>(devinfo, config); return std::make_unique<RtAudioDaq>(devinfo, config);
} }
@ -370,7 +378,7 @@ void myerrorcallback(RtAudioError::Type, const string &errorText) {
cerr << "RtAudio backend stream error: " << errorText << endl; cerr << "RtAudio backend stream error: " << errorText << endl;
} }
int mycallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, int mycallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames,
double streamTime, RtAudioStreamStatus status, void *userData) { double streamTime, RtAudioStreamStatus status, void *userData) {
return static_cast<RtAudioDaq *>(userData)->streamCallback( return static_cast<RtAudioDaq *>(userData)->streamCallback(
outputBuffer, inputBuffer, nFrames, streamTime, status); outputBuffer, inputBuffer, nFrames, streamTime, status);

View File

@ -57,7 +57,14 @@ void StreamMgr::checkRightThread() const {
void StreamMgr::rescanDAQDevices(bool background, void StreamMgr::rescanDAQDevices(bool background,
std::function<void()> callback) { std::function<void()> callback) {
DEBUGTRACE_ENTER;
checkRightThread(); checkRightThread();
if(!_devices_mtx.try_lock()) {
throw rte("A background DAQ device scan is probably already running");
}
_devices_mtx.unlock();
if (_inputStream || _outputStream) { if (_inputStream || _outputStream) {
throw rte("Rescanning DAQ devices only possible when no stream is running"); throw rte("Rescanning DAQ devices only possible when no stream is running");
} }
@ -70,6 +77,7 @@ void StreamMgr::rescanDAQDevices(bool background,
} }
} }
void StreamMgr::rescanDAQDevices_impl(std::function<void()> callback) { void StreamMgr::rescanDAQDevices_impl(std::function<void()> callback) {
DEBUGTRACE_ENTER;
std::scoped_lock lck(_devices_mtx); std::scoped_lock lck(_devices_mtx);
_devices = DeviceInfo::getDeviceInfo(); _devices = DeviceInfo::getDeviceInfo();
if (callback) { if (callback) {
@ -79,6 +87,7 @@ void StreamMgr::rescanDAQDevices_impl(std::function<void()> callback) {
bool StreamMgr::inCallback(const DaqData &data) { bool StreamMgr::inCallback(const DaqData &data) {
/* DEBUGTRACE_ENTER; */ /* DEBUGTRACE_ENTER; */
std::scoped_lock<std::mutex> lck(_inDataHandler_mtx); std::scoped_lock<std::mutex> lck(_inDataHandler_mtx);
for (auto &handler : _inDataHandlers) { for (auto &handler : _inDataHandlers) {
@ -122,8 +131,6 @@ template <typename T> bool fillData(DaqData &data, const vd &signal) {
/* DEBUGTRACE_ENTER; */ /* DEBUGTRACE_ENTER; */
assert(data.nframes == signal.size()); assert(data.nframes == signal.size());
/* cerr << "SFSG: data.nframes:" << data.nframes << endl; */
/* cerr << "SFSG: data.nchannels:" << data.nchannels << endl; */
T *res = reinterpret_cast<T *>(data.raw_ptr()); T *res = reinterpret_cast<T *>(data.raw_ptr());
if (std::is_floating_point<T>()) { if (std::is_floating_point<T>()) {
for (us ch = 0; ch < data.nchannels; ch++) { for (us ch = 0; ch < data.nchannels; ch++) {
@ -266,7 +273,7 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
if (isInput) { if (isInput) {
_inputStream = std::move(daq); _inputStream = std::move(daq);
if (daq->duplexMode()) { if (_inputStream->duplexMode()) {
assert(!_outputStream); assert(!_outputStream);
} }
} else { } else {

View File

@ -13,48 +13,48 @@ class StreamMgr;
class InDataHandler { class InDataHandler {
protected: protected:
StreamMgr &_mgr; StreamMgr &_mgr;
#if LASP_DEBUG == 1 #if LASP_DEBUG == 1
std::atomic<bool> stopCalled{false}; std::atomic<bool> stopCalled{false};
#endif #endif
public: public:
virtual ~InDataHandler(); virtual ~InDataHandler();
/** /**
* @brief When constructed, the handler is added to the stream manager, which * @brief When constructed, the handler is added to the stream manager, which
* will call the handlers's inCallback() until stop() is called. * will call the handlers's inCallback() until stop() is called.
* *
* @param mgr Stream manager. * @param mgr Stream manager.
*/ */
InDataHandler(StreamMgr &mgr); InDataHandler(StreamMgr &mgr);
/** /**
* @brief This function is called when input data from a DAQ is available. * @brief This function is called when input data from a DAQ is available.
* *
* @param daqdata Input data from DAQ * @param daqdata Input data from DAQ
* *
* @return true if no error. False to stop the stream from running. * @return true if no error. False to stop the stream from running.
*/ */
virtual bool inCallback(const DaqData &daqdata) = 0; virtual bool inCallback(const DaqData &daqdata) = 0;
/** /**
* @brief This function should be called from the constructor of the * @brief This function should be called from the constructor of the
* implementation of InDataHandler. It will start the stream's calling of * implementation of InDataHandler. It will start the stream's calling of
* inCallback(). * inCallback().
*/ */
void start(); void start();
/** /**
* @brief This function should be called from the destructor of derived * @brief This function should be called from the destructor of derived
* classes, to disable the calls to inCallback(), such that proper * classes, to disable the calls to inCallback(), such that proper
* destruction of the object is allowed and no other threads call methods * destruction of the object is allowed and no other threads call methods
* from the object. It removes the inCallback() from the callback list of the * from the object. It removes the inCallback() from the callback list of the
* StreamMgr(). **Failing to call this function results in deadlocks, errors * StreamMgr(). **Failing to call this function results in deadlocks, errors
* like "pure virtual function called", or other**. * like "pure virtual function called", or other**.
*/ */
void stop(); void stop();
}; };
/** /**
@ -101,7 +101,7 @@ class StreamMgr {
// Singleton, no public destructor // Singleton, no public destructor
~StreamMgr(); ~StreamMgr();
public: public:
enum class StreamType : us { enum class StreamType : us {
/** /**
* @brief Input stream * @brief Input stream
@ -136,15 +136,16 @@ public:
/** /**
* @brief Triggers a background scan of the DAQ devices, which updates the * @brief Triggers a background scan of the DAQ devices, which updates the
* internally stored list of devices. * internally stored list of devices. Throws a runtime error when a
* background thread is already scanning for devices.
* *
* @param background Perform searching for DAQ devices in the background. If * @param background Perform searching for DAQ devices in the background. If
* set to true, the function returns immediately. * set to true, the function returns immediately.
* @param callback Function to call when complete. * @param callback Function to call when complete.
*/ */
void void
rescanDAQDevices(bool background = false, rescanDAQDevices(bool background = false,
std::function<void()> callback = std::function<void()>()); std::function<void()> callback = std::function<void()>());
/** /**
* @brief Start a stream based on given configuration. * @brief Start a stream based on given configuration.
@ -163,6 +164,17 @@ public:
bool isStreamRunningOK(const StreamType type) const { bool isStreamRunningOK(const StreamType type) const {
return getStreamStatus(type).runningOK(); return getStreamStatus(type).runningOK();
} }
bool isStreamRunning(const StreamType type) const {
switch(type) {
case(StreamType::input):
return bool(_inputStream);
break;
case(StreamType::output):
return bool(_outputStream);
break;
}
return false;
}
/** /**
* @brief Get the streamstatus object corresponding to a given stream. * @brief Get the streamstatus object corresponding to a given stream.
@ -205,7 +217,7 @@ public:
*/ */
void setSiggen(std::shared_ptr<Siggen> s); void setSiggen(std::shared_ptr<Siggen> s);
private: private:
bool inCallback(const DaqData &data); bool inCallback(const DaqData &data);
bool outCallback(DaqData &data); bool outCallback(DaqData &data);

View File

@ -12,7 +12,7 @@ void init_daq(py::module &m) {
py::class_<Daq, DaqConfiguration, DeviceInfo> daq(m, "Daq"); py::class_<Daq, DaqConfiguration, DeviceInfo> daq(m, "Daq");
/// Daq::StreamStatus /// Daq::StreamStatus
py::class_<Daq::StreamStatus> ss(m, "StreamStatus"); py::class_<Daq::StreamStatus> ss(daq, "StreamStatus");
ss.def("error", &Daq::StreamStatus::error); ss.def("error", &Daq::StreamStatus::error);
ss.def("runningOK", &Daq::StreamStatus::runningOK); ss.def("runningOK", &Daq::StreamStatus::runningOK);
ss.def_readonly("isRunning", &Daq::StreamStatus::isRunning); ss.def_readonly("isRunning", &Daq::StreamStatus::isRunning);
@ -29,6 +29,8 @@ void init_daq(py::module &m) {
.value("apiSpecificError", .value("apiSpecificError",
Daq::StreamStatus::StreamError::apiSpecificError); Daq::StreamStatus::StreamError::apiSpecificError);
ss.def("errorMsg", &Daq::StreamStatus::errorMsg);
/// Daq /// Daq
daq.def("neninchannels", &Daq::neninchannels); daq.def("neninchannels", &Daq::neninchannels);
daq.def("nenoutchannels", &Daq::nenoutchannels); daq.def("nenoutchannels", &Daq::nenoutchannels);

View File

@ -91,4 +91,6 @@ void init_daqconfiguration(py::module &m) {
&DaqConfiguration::inchannel_config); &DaqConfiguration::inchannel_config);
daqconfig.def_readwrite("outchannel_config", daqconfig.def_readwrite("outchannel_config",
&DaqConfiguration::outchannel_config); &DaqConfiguration::outchannel_config);
daqconfig.def("setAllInputEnabled", &DaqConfiguration::setAllInputEnabled);
daqconfig.def("setAllOutputEnabled", &DaqConfiguration::setAllOutputEnabled);
} }

View File

@ -56,7 +56,7 @@ void init_dsp(py::module &m) {
}); });
/// BiquadBank /// BiquadBank
py::class_<BiquadBank> bqb(m, "BiquadBank"); py::class_<BiquadBank, std::shared_ptr<BiquadBank>> bqb(m, "BiquadBank");
bqb.def(py::init<const dmat&,const vd*>()); bqb.def(py::init<const dmat&,const vd*>());
bqb.def("setGains",&BiquadBank::setGains); bqb.def("setGains",&BiquadBank::setGains);
bqb.def("filter",&BiquadBank::filter); bqb.def("filter",&BiquadBank::filter);

View File

@ -29,4 +29,5 @@ void init_streammgr(py::module &m) {
smgr.def("getDeviceInfo", &StreamMgr::getDeviceInfo); smgr.def("getDeviceInfo", &StreamMgr::getDeviceInfo);
smgr.def("getStreamStatus", &StreamMgr::getStreamStatus); smgr.def("getStreamStatus", &StreamMgr::getStreamStatus);
smgr.def("isStreamRunningOK", &StreamMgr::isStreamRunningOK); smgr.def("isStreamRunningOK", &StreamMgr::isStreamRunningOK);
smgr.def("isStreamRunning", &StreamMgr::isStreamRunning);
} }

1
third_party/boost/core vendored Submodule

@ -0,0 +1 @@
Subproject commit b407b5d87df30f75ca501c1b6f2930c0913d4ca7

1
third_party/rtaudio vendored Submodule

@ -0,0 +1 @@
Subproject commit a3e9aa621e65a0744f125e9740d057a4d088e0e9