Compare commits

..

No commits in common. "7ce45e9c820ab128a54756606b41bddb71b049b9" and "7430e2c600bc9f63a7723f0c54977c91c945d0fc" have entirely different histories.

5 changed files with 305 additions and 306 deletions

View File

@ -4,8 +4,7 @@ if(LASP_HAS_PORTAUDIO)
if(WIN32) if(WIN32)
else() else()
set(PA_USE_ALSA TRUE CACHE BOOL "Build PortAudio with ALSA backend") set(PA_USE_ALSA TRUE CACHE BOOL "Build PortAudio with ALSA backend")
set(PA_USE_JACK FALSE CACHE BOOL "Build PortAudio with Jack backend") set(PA_USE_JACK TRUE CACHE BOOL "Build PortAudio with Jack backend")
set(PA_USE_PULSEAUDIO FALSE CACHE BOOL "Build PortAudio with PulseAudio backend")
# set(PA_ALSA_DYNAMIC FALSE CACHE BOOL "Build static library of ALSA") # set(PA_ALSA_DYNAMIC FALSE CACHE BOOL "Build static library of ALSA")
set(PA_BUILD_SHARED_LIBS FALSE CACHE BOOL "Build static library") set(PA_BUILD_SHARED_LIBS FALSE CACHE BOOL "Build static library")
endif() endif()

View File

@ -48,10 +48,6 @@ public:
logicError, logicError,
}; };
// Below the only members of this class, which are public.
bool isRunning = false;
StreamError errorType{StreamError::noError};
/** /**
* @brief Map between error types and messages * @brief Map between error types and messages
*/ */
@ -65,7 +61,7 @@ public:
{StreamError::logicError, "Logic error (probably a bug)"}, {StreamError::logicError, "Logic error (probably a bug)"},
}; };
bool isRunning = false;
/** /**
* @brief Check if stream has error * @brief Check if stream has error
* *
@ -73,6 +69,8 @@ public:
*/ */
bool error() const { return errorType != StreamError::noError; }; bool error() const { return errorType != StreamError::noError; };
StreamError errorType{StreamError::noError};
std::string errorMsg() const { return errorMessages.at(errorType); } std::string errorMsg() const { return errorMessages.at(errorType); }
/** /**

View File

@ -1,18 +1,15 @@
/* #define DEBUGTRACE_ENABLED */ /* #define DEBUGTRACE_ENABLED */
#include "lasp_streammgr.h" #include "lasp_streammgr.h"
#include <assert.h>
#include <algorithm>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include "debugtrace.hpp" #include "debugtrace.hpp"
#include "lasp_biquadbank.h" #include "lasp_biquadbank.h"
#include "lasp_indatahandler.h" #include "lasp_indatahandler.h"
#include "lasp_thread.h" #include "lasp_thread.h"
#include <algorithm>
#include <assert.h>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
using std::cerr; using std::cerr;
using std::endl; using std::endl;
@ -112,6 +109,7 @@ void StreamMgr::rescanDAQDevices_impl(std::function<void()> callback) {
} }
} }
void StreamMgr::inCallback(const DaqData &data) { void 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);
@ -120,6 +118,7 @@ void StreamMgr::inCallback(const DaqData &data) {
if (std::count_if(_inputFilters.cbegin(), _inputFilters.cend(), if (std::count_if(_inputFilters.cbegin(), _inputFilters.cend(),
[](const auto &a) { return bool(a); }) > 0) { [](const auto &a) { return bool(a); }) > 0) {
/// Found a filter in vector of input filters. So we have to apply the /// Found a filter in vector of input filters. So we have to apply the
/// filters to each channel. /// filters to each channel.
@ -152,6 +151,7 @@ void StreamMgr::inCallback(const DaqData &data) {
} }
void StreamMgr::setSiggen(std::shared_ptr<Siggen> siggen) { void StreamMgr::setSiggen(std::shared_ptr<Siggen> siggen) {
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
checkRightThread(); checkRightThread();
@ -179,8 +179,7 @@ void StreamMgr::setSiggen(std::shared_ptr<Siggen> siggen) {
* *
* @return * @return
*/ */
template <typename T> template <typename T> bool fillData(DaqData &data, const vd &signal) {
bool fillData(DaqData &data, const vd &signal) {
/* DEBUGTRACE_ENTER; */ /* DEBUGTRACE_ENTER; */
assert(data.nframes == signal.size()); assert(data.nframes == signal.size());
@ -213,6 +212,7 @@ bool fillData(DaqData &data, const vd &signal) {
return true; return true;
} }
void StreamMgr::outCallback(DaqData &data) { void StreamMgr::outCallback(DaqData &data) {
/* DEBUGTRACE_ENTER; */ /* DEBUGTRACE_ENTER; */
std::scoped_lock<std::mutex> lck(_siggen_mtx); std::scoped_lock<std::mutex> lck(_siggen_mtx);
@ -220,21 +220,21 @@ void StreamMgr::outCallback(DaqData &data) {
if (_siggen) { if (_siggen) {
vd signal = _siggen->genSignal(data.nframes); vd signal = _siggen->genSignal(data.nframes);
switch (data.dtype) { switch (data.dtype) {
case (DataTypeDescriptor::DataType::dtype_fl32): case (DataTypeDescriptor::DataType::dtype_fl32):
fillData<float>(data, signal); fillData<float>(data, signal);
break; break;
case (DataTypeDescriptor::DataType::dtype_fl64): case (DataTypeDescriptor::DataType::dtype_fl64):
fillData<double>(data, signal); fillData<double>(data, signal);
break; break;
case (DataTypeDescriptor::DataType::dtype_int8): case (DataTypeDescriptor::DataType::dtype_int8):
fillData<int8_t>(data, signal); fillData<int8_t>(data, signal);
break; break;
case (DataTypeDescriptor::DataType::dtype_int16): case (DataTypeDescriptor::DataType::dtype_int16):
fillData<int16_t>(data, signal); fillData<int16_t>(data, signal);
break; break;
case (DataTypeDescriptor::DataType::dtype_int32): case (DataTypeDescriptor::DataType::dtype_int32):
fillData<int32_t>(data, signal); fillData<int32_t>(data, signal);
break; break;
} }
} else { } else {
// Set all values to 0. // Set all values to 0.
@ -257,6 +257,8 @@ StreamMgr::~StreamMgr() {
// virtual methods. This was really a bug. // virtual methods. This was really a bug.
_inputStream.reset(); _inputStream.reset();
_outputStream.reset(); _outputStream.reset();
} }
void StreamMgr::stopAllStreams() { void StreamMgr::stopAllStreams() {
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
@ -271,11 +273,11 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
bool isInput = std::count_if(config.inchannel_config.cbegin(), bool isInput = std::count_if(config.inchannel_config.cbegin(),
config.inchannel_config.cend(), config.inchannel_config.cend(),
[](auto &i) { return i.enabled; }) > 0; [](auto &i) { return i.enabled; });
bool isOutput = std::count_if(config.outchannel_config.cbegin(), bool isOutput = std::count_if(config.outchannel_config.cbegin(),
config.outchannel_config.cend(), config.outchannel_config.cend(),
[](auto &i) { return i.enabled; }) > 0; [](auto &i) { return i.enabled; });
// Find the first device that matches with the configuration // Find the first device that matches with the configuration
std::scoped_lock lck(_devices_mtx); std::scoped_lock lck(_devices_mtx);
@ -300,40 +302,34 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
bool isDuplex = isInput && isOutput; bool isDuplex = isInput && isOutput;
if (!isInput && !isOutput) { if (!isInput && !isOutput) {
throw rte( throw rte("Neither input, nor output channels enabled for "
"Attempted stream start failed, stream does not have any enabled " "stream. Cannot start.");
"channels. Please first enable channels in the channel configuration.");
} }
if (isInput && _inputStream) { if (isInput && _inputStream) {
throw rte( throw rte("Error: an input stream is already running. Please "
"Error: an input stream is already running. Please " "first stop existing stream");
"first stop existing stream");
} else if (isOutput && _outputStream) { } else if (isOutput && _outputStream) {
throw rte( throw rte("Error: output stream is already running. Please "
"Error: output stream is already running. Please " "first stop existing stream");
"first stop existing stream");
} else if (_inputStream) { } else if (_inputStream) {
if (_inputStream->duplexMode() && isOutput) { if (_inputStream->duplexMode() && isOutput) {
throw rte( throw rte("Error: output stream is already running (in duplex mode). "
"Error: output stream is already running (in duplex mode). " "Please "
"Please " "first stop existing stream");
"first stop existing stream");
} }
} }
if (_outputStream && isInput && _outputStream->duplexModeForced && if (_outputStream && isInput && _outputStream->duplexModeForced &&
config.match(*_outputStream)) { config.match(*_outputStream)) {
throw rte( throw rte("This device is already opened for output. If input is also "
"This device is already opened for output. If input is also " "required, please enable duplex mode for this device");
"required, please enable duplex mode for this device");
} }
if (_inputStream && isOutput && _inputStream->duplexModeForced && if (_inputStream && isOutput && _inputStream->duplexModeForced &&
config.match(*_inputStream)) { config.match(*_inputStream)) {
throw rte( throw rte("This device is already opened for input. If output is also "
"This device is already opened for input. If output is also " "required, please enable duplex mode for this device");
"required, please enable duplex mode for this device");
} }
InDaqCallback inCallback; InDaqCallback inCallback;
@ -356,13 +352,13 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
d fs = daq->samplerate(); d fs = daq->samplerate();
/// Create input filters /// Create input filters
_inputFilters.clear(); _inputFilters.clear();
/// No input filter for monitor channel, which comes as the first input /// No input filter for monitor channel, which comes as the first input channel
/// channel In the list /// In the list
if (config.monitorOutput && devinfo->hasInternalOutputMonitor) { if (config.monitorOutput && devinfo->hasInternalOutputMonitor) {
_inputFilters.push_back(nullptr); _inputFilters.push_back(nullptr);
} }
for (auto &ch : daq->inchannel_config) { for (auto &ch : daq->inchannel_config) {
if (ch.enabled) { if (ch.enabled) {
if (ch.digitalHighPassCutOn < 0) { if (ch.digitalHighPassCutOn < 0) {
@ -375,7 +371,7 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
SeriesBiquad::firstOrderHighPass(fs, ch.digitalHighPassCutOn))); SeriesBiquad::firstOrderHighPass(fs, ch.digitalHighPassCutOn)));
} }
} }
} // End of input filter creation } // End of input filter creation
} }
if (isOutput) { if (isOutput) {
@ -402,6 +398,7 @@ void StreamMgr::startStream(const DaqConfiguration &config) {
} }
} }
void StreamMgr::stopStream(const StreamType t) { void StreamMgr::stopStream(const StreamType t) {
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
checkRightThread(); checkRightThread();
@ -426,7 +423,7 @@ void StreamMgr::stopStream(const StreamType t) {
throw rte("Output stream is not running"); throw rte("Output stream is not running");
} }
_outputStream.reset(); _outputStream.reset();
} // end else } // end else
} }
} }
@ -439,9 +436,8 @@ void StreamMgr::addInDataHandler(InDataHandler *handler) {
if (std::find(_inDataHandlers.cbegin(), _inDataHandlers.cend(), handler) != if (std::find(_inDataHandlers.cbegin(), _inDataHandlers.cend(), handler) !=
_inDataHandlers.cend()) { _inDataHandlers.cend()) {
throw std::runtime_error( throw std::runtime_error("Error: handler already added. Probably start() "
"Error: handler already added. Probably start() " "is called more than once on a handler object");
"is called more than once on a handler object");
} }
_inDataHandlers.push_back(handler); _inDataHandlers.push_back(handler);
DEBUGTRACE_PRINT(_inDataHandlers.size()); DEBUGTRACE_PRINT(_inDataHandlers.size());
@ -471,6 +467,7 @@ Daq::StreamStatus StreamMgr::getStreamStatus(const StreamType type) const {
} }
const Daq *StreamMgr::getDaq(StreamType type) const { const Daq *StreamMgr::getDaq(StreamType type) const {
checkRightThread(); checkRightThread();
if (type == StreamType::input) { if (type == StreamType::input) {

View File

@ -3,22 +3,23 @@
#include "lasp_config.h" #include "lasp_config.h"
#if LASP_HAS_PORTAUDIO == 1 #if LASP_HAS_PORTAUDIO == 1
#include "lasp_portaudiodaq.h"
#include "portaudio.h"
#include <gsl-lite/gsl-lite.hpp> #include <gsl-lite/gsl-lite.hpp>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include "lasp_portaudiodaq.h"
#include "portaudio.h"
using rte = std::runtime_error; using rte = std::runtime_error;
using std::cerr; using std::cerr;
using std::endl; using std::endl;
using std::string; using std::string;
using std::to_string; using std::to_string;
inline void throwIfError(PaError e) { inline void throwIfError(PaError e)
{
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
if (e != paNoError) { if (e != paNoError)
{
throw rte(string("PortAudio backend error: ") + Pa_GetErrorText(e)); throw rte(string("PortAudio backend error: ") + Pa_GetErrorText(e));
} }
} }
@ -26,14 +27,16 @@ inline void throwIfError(PaError e) {
/** /**
* @brief Device info, plus PortAudio stuff * @brief Device info, plus PortAudio stuff
*/ */
class OurPaDeviceInfo : public DeviceInfo { class OurPaDeviceInfo : public DeviceInfo
public: {
public:
/** /**
* @brief Store instance to PaDeviceInfo. * @brief Store instance to PaDeviceInfo.
*/ */
PaDeviceInfo _paDevInfo; PaDeviceInfo _paDevInfo;
virtual std::unique_ptr<DeviceInfo> clone() const override final { virtual std::unique_ptr<DeviceInfo> clone() const override final
{
return std::make_unique<OurPaDeviceInfo>(*this); return std::make_unique<OurPaDeviceInfo>(*this);
} }
OurPaDeviceInfo &operator=(const OurPaDeviceInfo &) = delete; OurPaDeviceInfo &operator=(const OurPaDeviceInfo &) = delete;
@ -41,40 +44,44 @@ class OurPaDeviceInfo : public DeviceInfo {
OurPaDeviceInfo(const PaDeviceInfo &o) : DeviceInfo(), _paDevInfo(o) {} OurPaDeviceInfo(const PaDeviceInfo &o) : DeviceInfo(), _paDevInfo(o) {}
}; };
void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) { void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist)
{
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
bool shouldPaTerminate = false; try
try { {
PaError err = Pa_Initialize(); PaError err = Pa_Initialize();
/// PortAudio says that Pa_Terminate() should not be called whenever there /// PortAudio says that Pa_Terminate() should not be called whenever there
/// is an error in Pa_Initialize(). This is opposite to what most examples /// is an error in Pa_Initialize(). This is opposite to what most examples
/// of PortAudio show. /// of PortAudio show.
throwIfError(err); throwIfError(err);
shouldPaTerminate = true;
auto fin = gsl::finally([&err] { auto fin = gsl::finally([&err]
{
DEBUGTRACE_PRINT("Terminating PortAudio instance"); DEBUGTRACE_PRINT("Terminating PortAudio instance");
err = Pa_Terminate(); err = Pa_Terminate();
if (err != paNoError) { if (err != paNoError) {
cerr << "Error terminating PortAudio. Do not know what to do." << endl; cerr << "Error terminating PortAudio. Do not know what to do." << endl;
} } });
});
const PaHostApiIndex apicount = Pa_GetHostApiCount(); const PaHostApiIndex apicount = Pa_GetHostApiCount();
if (apicount < 0) { if (apicount < 0)
{
return; return;
} }
/* const PaDeviceInfo *deviceInfo; */ /* const PaDeviceInfo *deviceInfo; */
const int numDevices = Pa_GetDeviceCount(); const int numDevices = Pa_GetDeviceCount();
if (numDevices < 0) { if (numDevices < 0)
{
throw rte("PortAudio could not find any devices"); throw rte("PortAudio could not find any devices");
} }
for (us i = 0; i < (us)numDevices; i++) { for (us i = 0; i < (us)numDevices; i++)
{
/* DEBUGTRACE_PRINT(i); */ /* DEBUGTRACE_PRINT(i); */
bool hasDuplexMode = false;
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i); const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i);
if (!deviceInfo) { if (!deviceInfo)
{
throw rte("No device info struct returned"); throw rte("No device info struct returned");
} }
OurPaDeviceInfo d(*deviceInfo); OurPaDeviceInfo d(*deviceInfo);
@ -83,36 +90,36 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
d.device_name = deviceInfo->name; d.device_name = deviceInfo->name;
const PaHostApiInfo *hostapiinfo = Pa_GetHostApiInfo(deviceInfo->hostApi); const PaHostApiInfo *hostapiinfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
if (hostapiinfo == nullptr) { if (hostapiinfo == nullptr)
{
throw std::runtime_error("Hostapi nullptr!"); throw std::runtime_error("Hostapi nullptr!");
} }
switch (hostapiinfo->type) { switch (hostapiinfo->type)
case paALSA: {
// Duplex mode for alsa case paALSA:
hasDuplexMode = true; d.api = portaudioALSAApi;
d.api = portaudioALSAApi; break;
break; case paASIO:
case paASIO: d.api = portaudioASIOApi;
d.api = portaudioASIOApi; break;
break; case paDirectSound:
case paDirectSound: d.api = portaudioDirectSoundApi;
d.api = portaudioDirectSoundApi; break;
break; case paMME:
case paMME: d.api = portaudioWMMEApi;
d.api = portaudioWMMEApi; break;
break; case paWDMKS:
case paWDMKS: d.api = portaudioWDMKS;
d.api = portaudioWDMKS; break;
break; case paWASAPI:
case paWASAPI: d.api = portaudioWASAPIApi;
d.api = portaudioWASAPIApi; break;
break; case paPulseAudio:
case paPulseAudio: d.api = portaudioPulseApi;
d.api = portaudioPulseApi; break;
break; default:
default: throw rte("Unimplemented portaudio API!");
throw rte("Unimplemented portaudio API!"); break;
break;
} }
d.availableDataTypes = {DataTypeDescriptor::DataType::dtype_int16, d.availableDataTypes = {DataTypeDescriptor::DataType::dtype_int16,
@ -121,7 +128,7 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
d.prefDataTypeIndex = 2; d.prefDataTypeIndex = 2;
d.availableSampleRates = {8000.0, 9600.0, 11025.0, 12000.0, 16000.0, d.availableSampleRates = {8000.0, 9600.0, 11025.0, 12000.0, 16000.0,
22050.0, 24000.0, 32000.0, 44100.0, 48000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0,
88200.0, 96000.0, 192000.0}; 88200.0, 96000.0, 192000.0};
d.prefSampleRateIndex = 9; d.prefSampleRateIndex = 9;
@ -141,13 +148,8 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
} }
} }
catch (rte &e) { catch (rte &e)
if (shouldPaTerminate) { {
PaError err = Pa_Terminate();
if (err != paNoError) {
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
}
}
cerr << "PortAudio backend error: " << e.what() << std::endl; cerr << "PortAudio backend error: " << e.what() << std::endl;
return; return;
} }
@ -172,14 +174,16 @@ static int rawPaCallback(const void *inputBuffer, void *outputBuffer,
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData); PaStreamCallbackFlags statusFlags, void *userData);
class PortAudioDaq : public Daq { class PortAudioDaq : public Daq
{
bool _shouldPaTerminate = false;
PaStream *_stream = nullptr; PaStream *_stream = nullptr;
std::atomic<StreamStatus::StreamError> _streamError = std::atomic<StreamStatus::StreamError> _streamError =
StreamStatus::StreamError::noError; StreamStatus::StreamError::noError;
InDaqCallback _incallback; InDaqCallback _incallback;
OutDaqCallback _outcallback; OutDaqCallback _outcallback;
public: public:
PortAudioDaq(const OurPaDeviceInfo &devinfo_gen, PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
const DaqConfiguration &config); const DaqConfiguration &config);
@ -208,11 +212,13 @@ class PortAudioDaq : public Daq {
}; };
std::unique_ptr<Daq> createPortAudioDevice(const DeviceInfo &devinfo, std::unique_ptr<Daq> createPortAudioDevice(const DeviceInfo &devinfo,
const DaqConfiguration &config) { const DaqConfiguration &config)
{
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
const OurPaDeviceInfo *_info = const OurPaDeviceInfo *_info =
dynamic_cast<const OurPaDeviceInfo *>(&devinfo); dynamic_cast<const OurPaDeviceInfo *>(&devinfo);
if (_info == nullptr) { if (_info == nullptr)
{
throw rte("BUG: Could not cast DeviceInfo to OurPaDeviceInfo"); throw rte("BUG: Could not cast DeviceInfo to OurPaDeviceInfo");
} }
return std::make_unique<PortAudioDaq>(*_info, config); return std::make_unique<PortAudioDaq>(*_info, config);
@ -221,143 +227,144 @@ std::unique_ptr<Daq> createPortAudioDevice(const DeviceInfo &devinfo,
static int rawPaCallback(const void *inputBuffer, void *outputBuffer, static int rawPaCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData) { PaStreamCallbackFlags statusFlags, void *userData)
{
return static_cast<PortAudioDaq *>(userData)->memberPaCallback( return static_cast<PortAudioDaq *>(userData)->memberPaCallback(
inputBuffer, outputBuffer, framesPerBuffer, timeInfo, statusFlags); inputBuffer, outputBuffer, framesPerBuffer, timeInfo, statusFlags);
} }
PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen, PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
const DaqConfiguration &config) const DaqConfiguration &config)
: Daq(devinfo_gen, config) { : Daq(devinfo_gen, config)
{
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
bool shouldPaTerminate = false; PaError err = Pa_Initialize();
try { /// PortAudio says that Pa_Terminate() should not be called whenever there
PaError err = Pa_Initialize(); /// is an error in Pa_Initialize(). This is opposite to what most examples
/// PortAudio says that Pa_Terminate() should not be called whenever there /// of PortAudio show.
/// is an error in Pa_Initialize(). This is opposite to what most examples throwIfError(err);
/// of PortAudio show.
throwIfError(err);
// OK, Pa_Initialize successfully finished, it means we have to clean up // OK, Pa_Initialize successfully finished, it means we have to clean up with
// with Pa_Terminate in the destructor. // Pa_Terminate in the destructor.
shouldPaTerminate = true; _shouldPaTerminate = true;
// Going to find the device in the list. If its there, we have to retrieve // Going to find the device in the list. If its there, we have to retrieve
// the index, as this is required in the PaStreamParameters struct // the index, as this is required in the PaStreamParameters struct
int devindex = -1; int devindex = -1;
for (int i = 0; i < Pa_GetDeviceCount(); i++) { for (int i = 0; i < Pa_GetDeviceCount(); i++)
// DEBUGTRACE_PRINT(i); {
bool ok = true; // DEBUGTRACE_PRINT(i);
const PaDeviceInfo *info = Pa_GetDeviceInfo(i); bool ok = true;
if (!info) { const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
throw rte("No device structure returned from PortAudio"); if (!info)
} {
ok &= string(info->name) == devinfo_gen.device_name; throw rte("No device structure returned from PortAudio");
ok &= info->hostApi == devinfo_gen._paDevInfo.hostApi;
ok &= info->maxInputChannels == devinfo_gen._paDevInfo.maxInputChannels;
ok &= info->maxOutputChannels == devinfo_gen._paDevInfo.maxOutputChannels;
ok &= info->defaultSampleRate == devinfo_gen._paDevInfo.defaultSampleRate;
if (ok) {
devindex = i;
}
}
if (devindex < 0) {
throw rte(string("Device not found: ") + string(devinfo_gen.device_name));
} }
ok &= string(info->name) == devinfo_gen.device_name;
ok &= info->hostApi == devinfo_gen._paDevInfo.hostApi;
ok &= info->maxInputChannels == devinfo_gen._paDevInfo.maxInputChannels;
ok &= info->maxOutputChannels == devinfo_gen._paDevInfo.maxOutputChannels;
ok &= info->defaultSampleRate == devinfo_gen._paDevInfo.defaultSampleRate;
using Dtype = DataTypeDescriptor::DataType; if (ok)
const Dtype dtype = dataType(); {
// Sample format is bit flag devindex = i;
PaSampleFormat format = paNonInterleaved;
switch (dtype) {
case Dtype::dtype_fl32:
DEBUGTRACE_PRINT("Datatype float32");
format |= paFloat32;
break;
case Dtype::dtype_fl64:
DEBUGTRACE_PRINT("Datatype float64");
throw rte("Invalid data type specified for DAQ stream.");
break;
case Dtype::dtype_int8:
DEBUGTRACE_PRINT("Datatype int8");
format |= paInt8;
break;
case Dtype::dtype_int16:
DEBUGTRACE_PRINT("Datatype int16");
format |= paInt16;
break;
case Dtype::dtype_int32:
DEBUGTRACE_PRINT("Datatype int32");
format |= paInt32;
break;
default:
throw rte("Invalid data type specified for DAQ stream.");
break;
} }
std::unique_ptr<PaStreamParameters> instreamParams;
std::unique_ptr<PaStreamParameters> outstreamParams;
if (neninchannels() > 0) {
instreamParams = std::make_unique<PaStreamParameters>(PaStreamParameters(
{.device = devindex,
.channelCount = (int)getHighestEnabledInChannel() + 1,
.sampleFormat = format,
.suggestedLatency = framesPerBlock() / samplerate(),
.hostApiSpecificStreamInfo = nullptr}));
}
if (nenoutchannels() > 0) {
outstreamParams = std::make_unique<PaStreamParameters>(PaStreamParameters(
{.device = devindex,
.channelCount = (int)getHighestEnabledOutChannel() + 1,
.sampleFormat = format,
.suggestedLatency = framesPerBlock() / samplerate(),
.hostApiSpecificStreamInfo = nullptr}));
}
// Next step: check whether we are OK
err = Pa_IsFormatSupported(instreamParams.get(), outstreamParams.get(),
samplerate());
throwIfError(err);
err = Pa_OpenStream(&_stream, // stream
instreamParams.get(), // inputParameters
outstreamParams.get(), // outputParameters
samplerate(), // yeah,
framesPerBlock(), // framesPerBuffer
paNoFlag, // streamFlags
rawPaCallback, this);
throwIfError(err);
assert(_stream);
} catch (rte &e) {
if (shouldPaTerminate) {
PaError err = Pa_Terminate();
if (err != paNoError) {
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
}
}
throw;
} }
if (devindex < 0)
{
throw rte(string("Device not found: ") + string(devinfo_gen.device_name));
}
using Dtype = DataTypeDescriptor::DataType;
const Dtype dtype = dataType();
// Sample format is bit flag
PaSampleFormat format = paNonInterleaved;
switch (dtype)
{
case Dtype::dtype_fl32:
DEBUGTRACE_PRINT("Datatype float32");
format |= paFloat32;
break;
case Dtype::dtype_fl64:
DEBUGTRACE_PRINT("Datatype float64");
throw rte("Invalid data type specified for DAQ stream.");
break;
case Dtype::dtype_int8:
DEBUGTRACE_PRINT("Datatype int8");
format |= paInt8;
break;
case Dtype::dtype_int16:
DEBUGTRACE_PRINT("Datatype int16");
format |= paInt16;
break;
case Dtype::dtype_int32:
DEBUGTRACE_PRINT("Datatype int32");
format |= paInt32;
break;
default:
throw rte("Invalid data type specified for DAQ stream.");
break;
}
std::unique_ptr<PaStreamParameters> instreamParams;
std::unique_ptr<PaStreamParameters> outstreamParams;
if (neninchannels() > 0)
{
instreamParams = std::make_unique<PaStreamParameters>(
PaStreamParameters({.device = devindex,
.channelCount = (int)getHighestEnabledInChannel() + 1,
.sampleFormat = format,
.suggestedLatency = framesPerBlock() / samplerate(),
.hostApiSpecificStreamInfo = nullptr}));
}
if (nenoutchannels() > 0)
{
outstreamParams = std::make_unique<PaStreamParameters>(
PaStreamParameters({.device = devindex,
.channelCount = (int)getHighestEnabledOutChannel() + 1,
.sampleFormat = format,
.suggestedLatency = framesPerBlock() / samplerate(),
.hostApiSpecificStreamInfo = nullptr}));
}
// Next step: check whether we are OK
err = Pa_IsFormatSupported(instreamParams.get(), outstreamParams.get(),
samplerate());
throwIfError(err);
err = Pa_OpenStream(&_stream, // stream
instreamParams.get(), // inputParameters
outstreamParams.get(), // outputParameters
samplerate(), // yeah,
framesPerBlock(), // framesPerBuffer
paNoFlag, // streamFlags
rawPaCallback, this);
throwIfError(err);
} }
void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) { void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback)
{
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
assert(_stream); assert(_stream);
if (Pa_IsStreamActive(_stream))
if (Pa_IsStreamActive(_stream)) { {
throw rte("Stream is already running"); throw rte("Stream is already running");
} }
// Logical XOR // Logical XOR
if (inCallback && outCallback) { if (inCallback && outCallback)
throw rte( {
"Either input or output stream possible for PortAudio. " throw rte("Either input or output stream possible for RtAudio. "
"Stream duplex mode not provided."); "Stream duplex mode not provided.");
} }
if (neninchannels() > 0) { if (neninchannels() > 0)
if (!inCallback) { {
if (!inCallback)
{
throw rte( throw rte(
"Input callback given, but stream does not provide input data"); "Input callback given, but stream does not provide input data");
@ -365,8 +372,10 @@ void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) {
_incallback = inCallback; _incallback = inCallback;
} }
if (nenoutchannels() > 0) { if (nenoutchannels() > 0)
if (!outCallback) { {
if (!outCallback)
{
throw rte( throw rte(
"Output callback given, but stream does not provide output data"); "Output callback given, but stream does not provide output data");
} }
@ -376,94 +385,86 @@ void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) {
PaError err = Pa_StartStream(_stream); PaError err = Pa_StartStream(_stream);
throwIfError(err); throwIfError(err);
} }
void PortAudioDaq::stop() { void PortAudioDaq::stop()
{
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
assert(_stream); assert(_stream);
if (Pa_IsStreamStopped(_stream) > 1) { if (Pa_IsStreamStopped(_stream))
{
throw rte("Stream is already stopped"); throw rte("Stream is already stopped");
} }
PaError err = Pa_StopStream(_stream); PaError err = Pa_StopStream(_stream);
throwIfError(err); throwIfError(err);
} }
Daq::StreamStatus PortAudioDaq::getStreamStatus() const { Daq::StreamStatus PortAudioDaq::getStreamStatus() const
DEBUGTRACE_ENTER; {
// Stores an error type and whether the
Daq::StreamStatus status; Daq::StreamStatus status;
using StreamError = Daq::StreamStatus::StreamError; // Copy over atomic flag.
Daq::StreamStatus::StreamError errortype = _streamError.load(); status.errorType = _streamError;
// Check if stream is still running.
PaError err = Pa_IsStreamStopped(_stream); if (_stream)
if (err > 1) { {
// Stream is stopped due to an error in the callback. The exact error type if (Pa_IsStreamActive(_stream))
// is filled in in the if-statement above {
return status; status.isRunning = true;
} else if (err == 0) {
// Still running
status.isRunning = true;
} else if (err < 0) {
// Stream encountered an error.
switch (err) {
case paInternalError:
errortype = StreamError::driverError;
break;
case paDeviceUnavailable:
errortype = StreamError::driverError;
break;
case paInputOverflowed:
errortype = StreamError::inputXRun;
break;
case paOutputUnderflowed:
errortype = StreamError::outputXRun;
break;
default:
errortype = StreamError::driverError;
cerr << "Portaudio backend error:" << Pa_GetErrorText(err) << endl;
break;
} }
} }
status.errorType = errortype;
return status; return status;
} }
PortAudioDaq::~PortAudioDaq() { PortAudioDaq::~PortAudioDaq()
{
PaError err; PaError err;
assert(_stream); if (_stream)
if (Pa_IsStreamActive(_stream)) { {
// Stop the stream first if (Pa_IsStreamActive(_stream))
stop(); {
stop();
}
err = Pa_CloseStream(_stream);
_stream = nullptr;
if (err != paNoError)
{
cerr << "Error closing PortAudio stream. Do not know what to do." << endl;
}
assert(_shouldPaTerminate);
} }
err = Pa_CloseStream(_stream); if (_shouldPaTerminate)
_stream = nullptr; {
if (err != paNoError) { err = Pa_Terminate();
cerr << "Error closing PortAudio stream. Do not know what to do." << endl; if (err != paNoError)
} {
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
err = Pa_Terminate(); }
if (err != paNoError) {
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
} }
} }
int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer, int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags) { PaStreamCallbackFlags statusFlags)
// DEBUGTRACE_ENTER; {
DEBUGTRACE_ENTER;
typedef Daq::StreamStatus::StreamError se; typedef Daq::StreamStatus::StreamError se;
if (statusFlags & paPrimingOutput) { if (statusFlags & paPrimingOutput)
{
// Initial output buffers generated. So nothing with input yet // Initial output buffers generated. So nothing with input yet
return paContinue; return paContinue;
} }
if ((statusFlags & paInputUnderflow) || (statusFlags & paInputOverflow)) { if ((statusFlags & paInputUnderflow) || (statusFlags & paInputOverflow))
{
_streamError = se::inputXRun; _streamError = se::inputXRun;
return paAbort; return paAbort;
} }
if ((statusFlags & paOutputUnderflow) || (statusFlags & paOutputOverflow)) { if ((statusFlags & paOutputUnderflow) || (statusFlags & paOutputOverflow))
{
_streamError = se::outputXRun; _streamError = se::outputXRun;
return paAbort; return paAbort;
} }
if (framesPerBuffer != framesPerBlock()) { if (framesPerBuffer != framesPerBlock())
{
cerr << "Logic error: expected a block size of: " << framesPerBlock() cerr << "Logic error: expected a block size of: " << framesPerBlock()
<< endl; << endl;
_streamError = se::logicError; _streamError = se::logicError;
@ -475,7 +476,8 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
const auto &dtype_descr = dtypeDescr(); const auto &dtype_descr = dtypeDescr();
const auto dtype = dataType(); const auto dtype = dataType();
const us sw = dtype_descr.sw; const us sw = dtype_descr.sw;
if (inputBuffer) { if (inputBuffer)
{
assert(_incallback); assert(_incallback);
std::vector<byte_t *> ptrs; std::vector<byte_t *> ptrs;
ptrs.reserve(neninchannels); ptrs.reserve(neninchannels);
@ -487,8 +489,10 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
/// Only pass on the pointers of the channels we want. inputBuffer is /// Only pass on the pointers of the channels we want. inputBuffer is
/// noninterleaved, as specified in PortAudioDaq constructor. /// noninterleaved, as specified in PortAudioDaq constructor.
for (us ch = ch_min; ch <= ch_max; ch++) { for (us ch = ch_min; ch <= ch_max; ch++)
if (inchannel_config.at(ch).enabled) { {
if (inchannel_config.at(ch).enabled)
{
byte_t *ch_ptr = byte_t *ch_ptr =
reinterpret_cast<byte_t **>(const_cast<void *>(inputBuffer))[ch]; reinterpret_cast<byte_t **>(const_cast<void *>(inputBuffer))[ch];
ptrs.push_back(ch_ptr); ptrs.push_back(ch_ptr);
@ -500,7 +504,8 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
_incallback(d); _incallback(d);
} }
if (outputBuffer) { if (outputBuffer)
{
assert(_outcallback); assert(_outcallback);
std::vector<byte_t *> ptrs; std::vector<byte_t *> ptrs;
ptrs.reserve(nenoutchannels); ptrs.reserve(nenoutchannels);
@ -512,8 +517,10 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
assert(ch_min < noutchannels); assert(ch_min < noutchannels);
assert(ch_max < noutchannels); assert(ch_max < noutchannels);
/// Only pass on the pointers of the channels we want /// Only pass on the pointers of the channels we want
for (us ch = ch_min; ch <= ch_max; ch++) { for (us ch = ch_min; ch <= ch_max; ch++)
if (outchannel_config.at(ch).enabled) { {
if (outchannel_config.at(ch).enabled)
{
byte_t *ch_ptr = reinterpret_cast<byte_t **>(outputBuffer)[ch]; byte_t *ch_ptr = reinterpret_cast<byte_t **>(outputBuffer)[ch];
ptrs.push_back(ch_ptr); ptrs.push_back(ch_ptr);
} }
@ -523,7 +530,8 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
_outcallback(d); _outcallback(d);
// Copy over the buffer // Copy over the buffer
us j = 0; us j = 0;
for (auto ptr : ptrs) { for (auto ptr : ptrs)
{
d.copyToRaw(j, ptr); d.copyToRaw(j, ptr);
j++; j++;
} }

View File

@ -57,9 +57,6 @@ class SLM:
level_ref_value: Reference value for computing the levels in dB level_ref_value: Reference value for computing the levels in dB
offset_t: Offset to be added to output time data [s] offset_t: Offset to be added to output time data [s]
""" """
if tw == TimeWeighting.ufast:
raise RuntimeError('The current implementation of impulse time weigthing is incorrect, as it does not have the proper decay time constant.')
self.fbdesigner = fbdesigner self.fbdesigner = fbdesigner
if xmin is None and fbdesigner is not None: if xmin is None and fbdesigner is not None: