Compare commits

..

No commits in common. "e9f500d460e2c07e307b5c63167bd9b5a0e2f658" and "08010e56dd57a703c5ba79a08ad5570128dce744" have entirely different histories.

13 changed files with 308 additions and 373 deletions

View File

@ -13,8 +13,8 @@ if(WIN32)
set(DEFAULT_PORTAUDIO ON)
set(DEFAULT_ULDAQ OFF)
else()
set(DEFAULT_RTAUDIO OFF)
set(DEFAULT_PORTAUDIO ON)
set(DEFAULT_RTAUDIO ON)
set(DEFAULT_PORTAUDIO OFF)
set(DEFAULT_ULDAQ ON)
endif()

View File

@ -3,10 +3,9 @@ if(LASP_HAS_PORTAUDIO)
message("Building with Portaudio backend")
if(WIN32)
else()
# Unix
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_PULSEAUDIO FALSE CACHE BOOL "Build PortAudio with PulseAudio backend")
set(PA_USE_JACK TRUE CACHE BOOL "Build PortAudio with Jack backend")
# set(PA_ALSA_DYNAMIC FALSE CACHE BOOL "Build static library of ALSA")
set(PA_BUILD_SHARED_LIBS FALSE CACHE BOOL "Build static library")
endif()
add_subdirectory(third_party/portaudio)

View File

@ -38,7 +38,6 @@ pybind11_add_module(lasp_cpp MODULE lasp_cpp.cpp
target_link_libraries(lasp_cpp PRIVATE lasp_device_lib lasp_dsp_lib
${OpenMP_CXX_LIBRARIES} ${LASP_FFT_LIBS} ${TARGET_OS_LINKLIBS})
if(DEFINED PY_BUILD_CMAKE_MODULE_NAME)
# Install the Python module
install(TARGETS lasp_cpp
EXCLUDE_FROM_ALL
@ -52,4 +51,3 @@ if(DEFINED PY_BUILD_CMAKE_MODULE_NAME)
DESTINATION ${PY_BUILD_CMAKE_MODULE_NAME}
OPTIONAL)
endif()
endif()

View File

@ -56,29 +56,13 @@ Daq::Daq(const DeviceInfo &devinfo, const DaqConfiguration &config)
if (!config.match(devinfo)) {
throw rte("DaqConfiguration does not match device info");
}
{
const int hich = getHighestEnabledInChannel();
if (hich + 1 > devinfo.ninchannels)
{
if (neninchannels(false) > devinfo.ninchannels) {
throw rte(
string("Highest of enabled input channel: ") +
to_string(hich) +
string(" is higher than device capability, which is: ") +
to_string(ninchannels) + ".");
"Number of enabled input channels is higher than device capability");
}
}
{
const int hoch = getHighestEnabledOutChannel();
if (hoch + 1 > devinfo.noutchannels)
{
if (nenoutchannels() > devinfo.noutchannels) {
throw rte(
string("Highest of enabled output channel: ") +
to_string(hoch) +
string(" is higher than device capability, which is: ") +
to_string(noutchannels) + ".");
}
"Number of enabled output channels is higher than device capability");
}
}

View File

@ -48,10 +48,6 @@ public:
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
*/
@ -65,7 +61,7 @@ public:
{StreamError::logicError, "Logic error (probably a bug)"},
};
bool isRunning = false;
/**
* @brief Check if stream has error
*
@ -73,6 +69,8 @@ public:
*/
bool error() const { return errorType != StreamError::noError; };
StreamError errorType{StreamError::noError};
std::string errorMsg() const { return errorMessages.at(errorType); }
/**

View File

@ -56,12 +56,10 @@ DaqConfiguration::DaqConfiguration(const DeviceInfo &device) {
}
bool DaqConfiguration::match(const DeviceInfo &dev) const {
DEBUGTRACE_ENTER;
return (dev.device_name == device_name && dev.api == api);
}
int DaqConfiguration::getHighestEnabledInChannel() const {
DEBUGTRACE_ENTER;
for (int i = inchannel_config.size() - 1; i > -1; i--) {
if (inchannel_config.at(i).enabled)
return i;
@ -70,15 +68,13 @@ int DaqConfiguration::getHighestEnabledInChannel() const {
}
int DaqConfiguration::getHighestEnabledOutChannel() const {
DEBUGTRACE_ENTER;
for (int i = outchannel_config.size() - 1; i > -1; i--) {
for (us i = outchannel_config.size() - 1; i >= 0; i--) {
if (outchannel_config.at(i).enabled)
return i;
}
return -1;
}
int DaqConfiguration::getLowestEnabledInChannel() const {
DEBUGTRACE_ENTER;
for (us i = 0; i < inchannel_config.size(); i++) {
if (inchannel_config.at(i).enabled)
return i;

View File

@ -148,14 +148,7 @@ const DaqApi rtaudioAsioApi("RtAudio Windows ASIO", LASP_RTAUDIO_APICODE,
#endif
#if LASP_HAS_PORTAUDIO == 1
const us LASP_PORTAUDIO_APICODE = 2;
const DaqApi portaudioALSAApi("PortAudio Linux ALSA", LASP_PORTAUDIO_APICODE, 0);
const DaqApi portaudioPulseApi("PortAudio Linux PulseAudio", LASP_PORTAUDIO_APICODE, 1);
const DaqApi portaudioASIOApi("PortAudio Windows ASIO", LASP_PORTAUDIO_APICODE, 2);
const DaqApi portaudioDSApi("PortAudio Windows DirectSound", LASP_PORTAUDIO_APICODE, 3);
const DaqApi portaudioWMMEApi("PortAudio Windows WMME", LASP_PORTAUDIO_APICODE, 4);
const DaqApi portaudioWASAPIApi("PortAudio Windows WASAPI", LASP_PORTAUDIO_APICODE, 5);
const DaqApi portaudioWDMKS("PortAudio Windows WDMKS", LASP_PORTAUDIO_APICODE, 6);
const DaqApi portaudioDirectSoundApi("PortAudio Windows DirectSound", LASP_PORTAUDIO_APICODE, 7);
const DaqApi portaudioApi("PortAudio Linux ALSA", LASP_PORTAUDIO_APICODE, 0);
#endif
class DeviceInfo;

View File

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

View File

@ -3,22 +3,23 @@
#include "lasp_config.h"
#if LASP_HAS_PORTAUDIO == 1
#include "lasp_portaudiodaq.h"
#include "portaudio.h"
#include <gsl-lite/gsl-lite.hpp>
#include <mutex>
#include <string>
#include "lasp_portaudiodaq.h"
#include "portaudio.h"
using rte = std::runtime_error;
using std::cerr;
using std::endl;
using std::string;
using std::to_string;
inline void throwIfError(PaError e) {
inline void throwIfError(PaError e)
{
DEBUGTRACE_ENTER;
if (e != paNoError) {
if (e != paNoError)
{
throw rte(string("PortAudio backend error: ") + Pa_GetErrorText(e));
}
}
@ -26,95 +27,62 @@ inline void throwIfError(PaError e) {
/**
* @brief Device info, plus PortAudio stuff
*/
class OurPaDeviceInfo : public DeviceInfo {
class OurPaDeviceInfo : public DeviceInfo
{
public:
/**
* @brief Store instance to PaDeviceInfo.
*/
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);
}
OurPaDeviceInfo &operator=(const OurPaDeviceInfo &) = delete;
OurPaDeviceInfo(const OurPaDeviceInfo &) = default;
OurPaDeviceInfo(const PaDeviceInfo &o) : DeviceInfo(), _paDevInfo(o) {}
};
void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist)
{
DEBUGTRACE_ENTER;
bool shouldPaTerminate = false;
try {
try
{
PaError err = Pa_Initialize();
/// PortAudio says that Pa_Terminate() should not be called whenever there
/// is an error in Pa_Initialize(). This is opposite to what most examples
/// of PortAudio show.
throwIfError(err);
shouldPaTerminate = true;
auto fin = gsl::finally([&err] {
auto fin = gsl::finally([&err]
{
DEBUGTRACE_PRINT("Terminating PortAudio instance");
err = Pa_Terminate();
if (err != paNoError) {
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
}
});
} });
const PaHostApiIndex apicount = Pa_GetHostApiCount();
if (apicount < 0) {
return;
}
/* const PaDeviceInfo *deviceInfo; */
const int numDevices = Pa_GetDeviceCount();
if (numDevices < 0) {
if (numDevices < 0)
{
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); */
bool hasDuplexMode = false;
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i);
if (!deviceInfo) {
if (!deviceInfo)
{
throw rte("No device info struct returned");
}
OurPaDeviceInfo d(*deviceInfo);
OurPaDeviceInfo d;
d._paDevInfo = *deviceInfo;
// We store the name in d.device_name
d._paDevInfo.name = nullptr;
d.api = portaudioApi;
d.device_name = deviceInfo->name;
const PaHostApiInfo *hostapiinfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
if (hostapiinfo == nullptr) {
throw std::runtime_error("Hostapi nullptr!");
}
switch (hostapiinfo->type) {
case paALSA:
// Duplex mode for alsa
hasDuplexMode = true;
d.api = portaudioALSAApi;
break;
case paASIO:
d.api = portaudioASIOApi;
break;
case paDirectSound:
d.api = portaudioDirectSoundApi;
break;
case paMME:
d.api = portaudioWMMEApi;
break;
case paWDMKS:
d.api = portaudioWDMKS;
break;
case paWASAPI:
d.api = portaudioWASAPIApi;
break;
case paPulseAudio:
d.api = portaudioPulseApi;
break;
default:
throw rte("Unimplemented portaudio API!");
break;
}
d.availableDataTypes = {DataTypeDescriptor::DataType::dtype_int16,
DataTypeDescriptor::DataType::dtype_int32,
DataTypeDescriptor::DataType::dtype_fl32};
@ -137,20 +105,12 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
d.ninchannels = deviceInfo->maxInputChannels;
d.noutchannels = deviceInfo->maxOutputChannels;
// Duplex mode, only for ALSA devices
d.hasDuplexMode = hasDuplexMode;
devinfolist.push_back(std::make_unique<OurPaDeviceInfo>(d));
}
}
catch (rte &e) {
if (shouldPaTerminate) {
PaError err = Pa_Terminate();
if (err != paNoError) {
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
}
}
catch (rte &e)
{
cerr << "PortAudio backend error: " << e.what() << std::endl;
return;
}
@ -175,7 +135,9 @@ static int rawPaCallback(const void *inputBuffer, void *outputBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData);
class PortAudioDaq : public Daq {
class PortAudioDaq : public Daq
{
bool _shouldPaTerminate = false;
PaStream *_stream = nullptr;
std::atomic<StreamStatus::StreamError> _streamError =
StreamStatus::StreamError::noError;
@ -211,11 +173,13 @@ class PortAudioDaq : public Daq {
};
std::unique_ptr<Daq> createPortAudioDevice(const DeviceInfo &devinfo,
const DaqConfiguration &config) {
DEBUGTRACE_ENTER;
const DaqConfiguration &config)
{
const OurPaDeviceInfo *_info =
dynamic_cast<const OurPaDeviceInfo *>(&devinfo);
if (_info == nullptr) {
if (_info == nullptr)
{
throw rte("BUG: Could not cast DeviceInfo to OurPaDeviceInfo");
}
return std::make_unique<PortAudioDaq>(*_info, config);
@ -224,35 +188,38 @@ std::unique_ptr<Daq> createPortAudioDevice(const DeviceInfo &devinfo,
static int rawPaCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData) {
PaStreamCallbackFlags statusFlags, void *userData)
{
return static_cast<PortAudioDaq *>(userData)->memberPaCallback(
inputBuffer, outputBuffer, framesPerBuffer, timeInfo, statusFlags);
}
PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
const DaqConfiguration &config)
: Daq(devinfo_gen, config) {
: Daq(devinfo_gen, config)
{
DEBUGTRACE_ENTER;
bool shouldPaTerminate = false;
try {
PaError err = Pa_Initialize();
/// PortAudio says that Pa_Terminate() should not be called whenever there
/// is an error in Pa_Initialize(). This is opposite to what most examples
/// of PortAudio show.
throwIfError(err);
// OK, Pa_Initialize successfully finished, it means we have to clean up
// with Pa_Terminate in the destructor.
shouldPaTerminate = true;
// OK, Pa_Initialize successfully finished, it means we have to clean up with
// Pa_Terminate in the destructor.
_shouldPaTerminate = true;
// 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
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;
const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
if (!info) {
if (!info)
{
throw rte("No device structure returned from PortAudio");
}
ok &= string(info->name) == devinfo_gen.device_name;
@ -261,11 +228,13 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
ok &= info->maxOutputChannels == devinfo_gen._paDevInfo.maxOutputChannels;
ok &= info->defaultSampleRate == devinfo_gen._paDevInfo.defaultSampleRate;
if (ok) {
if (ok)
{
devindex = i;
}
}
if (devindex < 0) {
if (devindex < 0)
{
throw rte(string("Device not found: ") + string(devinfo_gen.device_name));
}
@ -273,7 +242,8 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
const Dtype dtype = dataType();
// Sample format is bit flag
PaSampleFormat format = paNonInterleaved;
switch (dtype) {
switch (dtype)
{
case Dtype::dtype_fl32:
DEBUGTRACE_PRINT("Datatype float32");
format |= paFloat32;
@ -302,17 +272,19 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
std::unique_ptr<PaStreamParameters> instreamParams;
std::unique_ptr<PaStreamParameters> outstreamParams;
if (neninchannels() > 0) {
instreamParams = std::make_unique<PaStreamParameters>(PaStreamParameters(
{.device = devindex,
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,
if (nenoutchannels() > 0)
{
outstreamParams = std::make_unique<PaStreamParameters>(
PaStreamParameters({.device = devindex,
.channelCount = (int)getHighestEnabledOutChannel() + 1,
.sampleFormat = format,
.suggestedLatency = framesPerBlock() / samplerate(),
@ -332,28 +304,28 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
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;
}
}
void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) {
void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback)
{
DEBUGTRACE_ENTER;
assert(_stream);
if (Pa_IsStreamActive(_stream)) {
if (Pa_IsStreamActive(_stream))
{
throw rte("Stream is already running");
}
if (neninchannels() > 0) {
if (!inCallback) {
// Logical XOR
if (inCallback && outCallback)
{
throw rte("Either input or output stream possible for RtAudio. "
"Stream duplex mode not provided.");
}
if (neninchannels() > 0)
{
if (!inCallback)
{
throw rte(
"Input callback given, but stream does not provide input data");
@ -361,8 +333,10 @@ void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) {
_incallback = inCallback;
}
if (nenoutchannels() > 0) {
if (!outCallback) {
if (nenoutchannels() > 0)
{
if (!outCallback)
{
throw rte(
"Output callback given, but stream does not provide output data");
}
@ -372,94 +346,86 @@ void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) {
PaError err = Pa_StartStream(_stream);
throwIfError(err);
}
void PortAudioDaq::stop() {
void PortAudioDaq::stop()
{
DEBUGTRACE_ENTER;
assert(_stream);
if (Pa_IsStreamStopped(_stream) > 1) {
if (Pa_IsStreamStopped(_stream))
{
throw rte("Stream is already stopped");
}
PaError err = Pa_StopStream(_stream);
throwIfError(err);
}
Daq::StreamStatus PortAudioDaq::getStreamStatus() const {
DEBUGTRACE_ENTER;
// Stores an error type and whether the
Daq::StreamStatus PortAudioDaq::getStreamStatus() const
{
Daq::StreamStatus status;
using StreamError = Daq::StreamStatus::StreamError;
Daq::StreamStatus::StreamError errortype = _streamError.load();
PaError err = Pa_IsStreamStopped(_stream);
if (err > 1) {
// Stream is stopped due to an error in the callback. The exact error type
// is filled in in the if-statement above
return status;
} else if (err == 0) {
// Still running
// Copy over atomic flag.
status.errorType = _streamError;
// Check if stream is still running.
if (_stream)
{
if (Pa_IsStreamActive(_stream))
{
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;
}
PortAudioDaq::~PortAudioDaq() {
PortAudioDaq::~PortAudioDaq()
{
PaError err;
assert(_stream);
if (Pa_IsStreamActive(_stream)) {
// Stop the stream first
if (_stream)
{
if (Pa_IsStreamActive(_stream))
{
stop();
}
err = Pa_CloseStream(_stream);
_stream = nullptr;
if (err != paNoError) {
if (err != paNoError)
{
cerr << "Error closing PortAudio stream. Do not know what to do." << endl;
}
assert(_shouldPaTerminate);
}
if (_shouldPaTerminate)
{
err = Pa_Terminate();
if (err != paNoError) {
if (err != paNoError)
{
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
}
}
}
int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags) {
// DEBUGTRACE_ENTER;
PaStreamCallbackFlags statusFlags)
{
DEBUGTRACE_ENTER;
typedef Daq::StreamStatus::StreamError se;
if (statusFlags & paPrimingOutput) {
if (statusFlags & paPrimingOutput)
{
// Initial output buffers generated. So nothing with input yet
return paContinue;
}
if ((statusFlags & paInputUnderflow) || (statusFlags & paInputOverflow)) {
if ((statusFlags & paInputUnderflow) || (statusFlags & paInputOverflow))
{
_streamError = se::inputXRun;
return paAbort;
}
if ((statusFlags & paOutputUnderflow) || (statusFlags & paOutputOverflow)) {
if ((statusFlags & paOutputUnderflow) || (statusFlags & paOutputOverflow))
{
_streamError = se::outputXRun;
return paAbort;
}
if (framesPerBuffer != framesPerBlock()) {
if (framesPerBuffer != framesPerBlock())
{
cerr << "Logic error: expected a block size of: " << framesPerBlock()
<< endl;
_streamError = se::logicError;
@ -471,7 +437,8 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
const auto &dtype_descr = dtypeDescr();
const auto dtype = dataType();
const us sw = dtype_descr.sw;
if (inputBuffer) {
if (inputBuffer)
{
assert(_incallback);
std::vector<byte_t *> ptrs;
ptrs.reserve(neninchannels);
@ -483,8 +450,10 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
/// Only pass on the pointers of the channels we want. inputBuffer is
/// noninterleaved, as specified in PortAudioDaq constructor.
for (us ch = ch_min; ch <= ch_max; ch++) {
if (inchannel_config.at(ch).enabled) {
for (us ch = ch_min; ch <= ch_max; ch++)
{
if (inchannel_config.at(ch).enabled)
{
byte_t *ch_ptr =
reinterpret_cast<byte_t **>(const_cast<void *>(inputBuffer))[ch];
ptrs.push_back(ch_ptr);
@ -496,7 +465,8 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
_incallback(d);
}
if (outputBuffer) {
if (outputBuffer)
{
assert(_outcallback);
std::vector<byte_t *> ptrs;
ptrs.reserve(nenoutchannels);
@ -508,8 +478,10 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
assert(ch_min < noutchannels);
assert(ch_max < noutchannels);
/// Only pass on the pointers of the channels we want
for (us ch = ch_min; ch <= ch_max; ch++) {
if (outchannel_config.at(ch).enabled) {
for (us ch = ch_min; ch <= ch_max; ch++)
{
if (outchannel_config.at(ch).enabled)
{
byte_t *ch_ptr = reinterpret_cast<byte_t **>(outputBuffer)[ch];
ptrs.push_back(ch_ptr);
}
@ -519,7 +491,8 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
_outcallback(d);
// Copy over the buffer
us j = 0;
for (auto ptr : ptrs) {
for (auto ptr : ptrs)
{
d.copyToRaw(j, ptr);
j++;
}

View File

@ -5,7 +5,7 @@ requires-python = ">=3.10"
description = "Library for Acoustic Signal Processing"
license = { "file" = "LICENSE" }
authors = [{ "name" = "J.A. de Jong", "email" = "j.a.dejong@ascee.nl" }]
version = "1.4.2"
version = "1.3.1"
keywords = ["DSP", "DAQ", "Signal processing"]
@ -53,7 +53,7 @@ include = [
build_type = "Release"
source_path = "."
options = { "LASP_HAS_PORTAUDIO" = "ON", "LASP_HAS_RTAUDIO" = "OFF" }
build_args = ["-j"]
build_args = ["-j12"]
install_components = ["python_modules"]
[tool.py-build-cmake.editable]

View File

@ -58,9 +58,6 @@ class SLM:
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
if xmin is None and fbdesigner is not None:
xmin = fbdesigner.xs[0]

@ -1 +1 @@
Subproject commit 614f2a9c68998b11010dc1734a77f84fcbd6fa2d
Subproject commit b6e810f2d33bcc234d67db5277d027949fec82f8

2
third_party/rtaudio vendored

@ -1 +1 @@
Subproject commit b4f04903312e0e0efffbe77655172e0f060dc085
Subproject commit 46b01b5b134f33d8ddc3dab76829d4b1350e0522