Compare commits

...

6 Commits

Author SHA1 Message Date
Anne de Jong 08010e56dd From now on build default LASP with Portaudio backend. Also on Linux. Code cleanup of Portaudio glue code
Building, testing and releasing LASP if it has a tag / Build-Test-Ubuntu (push) Successful in 3m3s Details
Building, testing and releasing LASP if it has a tag / Release-Ubuntu (push) Has been skipped Details
2024-01-20 11:52:16 +01:00
Anne de Jong 292a9d5938 Merge branch 'RtAudioV6' into develop 2024-01-19 12:37:16 +01:00
Anne de Jong 373dcfb60f Bugfixes (that could potentially segfault) in PortAudio backend.
Building, testing and releasing LASP if it has a tag / Build-Test-Ubuntu (push) Successful in 2m54s Details
Building, testing and releasing LASP if it has a tag / Release-Ubuntu (push) Successful in 2s Details
2024-01-19 12:35:56 +01:00
Anne de Jong e8408ab53d Updated to RtAudio V6 2024-01-19 12:32:45 +01:00
Anne de Jong 0d152f6c14 Maded API changes to match RtAudio V6 2024-01-19 12:32:03 +01:00
Anne de Jong 46bef007ca Added muZ series impedance reference as measurementtype
Building, testing and releasing LASP if it has a tag / Build-Test-Ubuntu (push) Has been cancelled Details
Building, testing and releasing LASP if it has a tag / Release-Ubuntu (push) Has been cancelled Details
2024-01-15 16:41:29 +01:00
6 changed files with 261 additions and 135 deletions

View File

@ -34,7 +34,7 @@ if(LASP_HAS_RTAUDIO)
target_link_libraries(lasp_device_lib rtaudio)
endif()
if(LASP_HAS_PORTAUDIO)
target_link_libraries(lasp_device_lib portaudio)
target_link_libraries(lasp_device_lib PortAudio)
if(WIN32)
else()
target_link_libraries(lasp_device_lib asound)

View File

@ -17,37 +17,46 @@ using rte = std::runtime_error;
using std::vector;
using lck = std::scoped_lock<std::mutex>;
class RtAudioDeviceInfo : public DeviceInfo {
const unsigned RTAUDIO_MAX_CHANNELS = 8;
class RtAudioDeviceInfo : public DeviceInfo
{
public:
/**
* @brief Specific for the device (Sub-API). Important for the RtAudio
* backend, as RtAudio is able to handle different API's.
*/
int _api_devindex;
virtual std::unique_ptr<DeviceInfo> clone() const override {
int ID; // Copy of RtAudio::DeviceInfo::ID
virtual std::unique_ptr<DeviceInfo> clone() const override
{
return std::make_unique<DeviceInfo>(*this);
}
};
void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist)
{
DEBUGTRACE_ENTER;
vector<RtAudio::Api> apis;
RtAudio::getCompiledApi(apis);
for (auto api : apis) {
for (auto api : apis)
{
RtAudio rtaudio(api);
us count = rtaudio.getDeviceCount();
for (us devno = 0; devno < count; devno++) {
const us count = rtaudio.getDeviceCount();
const auto ids = rtaudio.getDeviceIds();
for (us i = 0; i < count; i++)
{
us id = ids.at(i);
RtAudio::DeviceInfo devinfo = rtaudio.getDeviceInfo(id);
RtAudio::DeviceInfo devinfo = rtaudio.getDeviceInfo(devno);
if (!devinfo.probed) {
// Device capabilities not successfully probed. Continue to next
continue;
}
// "Our device info struct"
RtAudioDeviceInfo d;
switch (api) {
switch (api)
{
case RtAudio::LINUX_ALSA:
d.api = rtaudioAlsaApi;
break;
@ -70,43 +79,49 @@ void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
}
d.device_name = devinfo.name;
d._api_devindex = devno;
d.ID = id;
/// When 48k is available we overwrite the default sample rate with the 48
/// kHz value, which is our preffered rate,
bool rate_48k_found = false;
for (us j = 0; j < devinfo.sampleRates.size(); j++) {
for (us j = 0; j < devinfo.sampleRates.size(); j++)
{
us rate_int = devinfo.sampleRates[j];
d.availableSampleRates.push_back((double)rate_int);
if (!rate_48k_found) {
if (!rate_48k_found)
{
if (devinfo.preferredSampleRate == rate_int) {
if (devinfo.preferredSampleRate == rate_int)
{
d.prefSampleRateIndex = j;
}
if (rate_int == 48000) {
if (rate_int == 48000)
{
d.prefSampleRateIndex = j;
rate_48k_found = true;
}
}
}
d.noutchannels = devinfo.outputChannels;
d.ninchannels = devinfo.inputChannels;
d.noutchannels = std::min(devinfo.outputChannels, RTAUDIO_MAX_CHANNELS);
d.ninchannels = std::min(devinfo.inputChannels, RTAUDIO_MAX_CHANNELS);
d.availableInputRanges = {1.0};
d.availableOutputRanges = {1.0};
RtAudioFormat formats = devinfo.nativeFormats;
if (formats & RTAUDIO_SINT8) {
if (formats & RTAUDIO_SINT8)
{
d.availableDataTypes.push_back(
DataTypeDescriptor::DataType::dtype_int8);
}
if (formats & RTAUDIO_SINT16) {
if (formats & RTAUDIO_SINT16)
{
d.availableDataTypes.push_back(
DataTypeDescriptor::DataType::dtype_int16);
}
@ -114,15 +129,18 @@ void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) {
/* d.availableDataTypes.push_back(DataTypeDescriptor::DataType::dtype_int24);
*/
/* } */
if (formats & RTAUDIO_SINT32) {
if (formats & RTAUDIO_SINT32)
{
d.availableDataTypes.push_back(
DataTypeDescriptor::DataType::dtype_fl32);
}
if (formats & RTAUDIO_FLOAT64) {
if (formats & RTAUDIO_FLOAT64)
{
d.availableDataTypes.push_back(
DataTypeDescriptor::DataType::dtype_fl64);
}
if (d.availableDataTypes.size() == 0) {
if (d.availableDataTypes.size() == 0)
{
std::cerr << "RtAudio: No data types found in device!" << endl;
}
@ -140,9 +158,8 @@ static int mycallback(void *outputBuffer, void *inputBuffer,
unsigned int nFrames, double streamTime,
RtAudioStreamStatus status, void *userData);
static void myerrorcallback(RtAudioError::Type, const string &errorText);
class RtAudioDaq : public Daq {
class RtAudioDaq : public Daq
{
RtAudio rtaudio;
const us nFramesPerBlock;
@ -182,10 +199,11 @@ public:
/// 0. For now, our fix is to shift out the channels we want, and let
/// RtAudio pass on all channels.
inParams->firstChannel = 0;
inParams->nChannels = devinfo_gen.ninchannels;
inParams->deviceId = devinfo._api_devindex;
} else {
inParams->nChannels = devinfo.ninchannels;
inParams->deviceId = devinfo.ID;
}
else
{
outParams = std::make_unique<RtAudio::StreamParameters>();
@ -193,8 +211,8 @@ public:
/// 0. For now, our fix is to shift out the channels we want, and let
/// RtAudio pass on all channels.
outParams->firstChannel = 0;
outParams->nChannels = devinfo_gen.noutchannels;
outParams->deviceId = devinfo._api_devindex;
outParams->nChannels = devinfo.noutchannels;
outParams->deviceId = devinfo.ID;
}
RtAudio::StreamOptions streamoptions;
@ -207,7 +225,8 @@ public:
RtAudioFormat format;
using Dtype = DataTypeDescriptor::DataType;
const Dtype dtype = dataType();
switch (dtype) {
switch (dtype)
{
case Dtype::dtype_fl32:
DEBUGTRACE_PRINT("Datatype float32");
format = RTAUDIO_FLOAT32;
@ -238,36 +257,46 @@ public:
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);
RtAudioErrorType err = rtaudio.openStream(outParams.get(), inParams.get(), format,
static_cast<us>(samplerate()), &nFramesPerBlock_copy,
mycallback, (void *)this, &streamoptions);
if (err != RTAUDIO_NO_ERROR)
{
throw std::runtime_error(string("Error opening stream: ") + rtaudio.getErrorText());
}
if (nFramesPerBlock_copy != nFramesPerBlock) {
if (nFramesPerBlock_copy != nFramesPerBlock)
{
throw rte(string("Got different number of frames per block back from RtAudio "
"backend: ") + std::to_string(nFramesPerBlock_copy) + ". I do not know what to do.");
"backend: ") +
std::to_string(nFramesPerBlock_copy) + ". I do not know what to do.");
}
}
virtual void start(InDaqCallback inCallback,
OutDaqCallback outCallback) override final {
OutDaqCallback outCallback) override final
{
DEBUGTRACE_ENTER;
assert(!monitorOutput);
if (getStreamStatus().runningOK()) {
if (getStreamStatus().runningOK())
{
throw rte("Stream already running");
}
// Logical XOR
if (inCallback && outCallback) {
if (inCallback && outCallback)
{
throw rte("Either input or output stream possible for RtAudio. "
"Stream duplex mode not provided.");
}
if (neninchannels() > 0) {
if (!inCallback) {
if (neninchannels() > 0)
{
if (!inCallback)
{
throw rte(
"Input callback given, but stream does not provide input data");
@ -275,8 +304,10 @@ public:
_incallback = inCallback;
}
if (nenoutchannels() > 0) {
if (!outCallback) {
if (nenoutchannels() > 0)
{
if (!outCallback)
{
throw rte(
"Output callback given, but stream does not provide output data");
}
@ -284,7 +315,11 @@ public:
}
// Start the stream. Throws on error.
rtaudio.startStream();
const auto err = rtaudio.startStream();
if (err != RTAUDIO_NO_ERROR)
{
throw std::runtime_error(string("Error starting stream: ") + rtaudio.getErrorText());
}
// If we are here, we are running without errors.
StreamStatus status;
@ -294,10 +329,15 @@ public:
StreamStatus getStreamStatus() const override final { return _streamStatus; }
void stop() override final {
void stop() override final
{
DEBUGTRACE_ENTER;
if (getStreamStatus().runningOK()) {
rtaudio.stopStream();
if (getStreamStatus().runningOK())
{
const auto err = rtaudio.stopStream();
if(err != RTAUDIO_NO_ERROR) {
std::cerr << "Error occured while stopping the stream: " << rtaudio.getErrorText() << endl;
}
}
StreamStatus s = _streamStatus;
s.isRunning = false;
@ -306,14 +346,16 @@ public:
}
int streamCallback(void *outputBuffer, void *inputBuffer,
unsigned int nFrames, RtAudioStreamStatus status) {
unsigned int nFrames, RtAudioStreamStatus status)
{
DEBUGTRACE_ENTER;
using se = StreamStatus::StreamError;
int rval = 0;
auto stopWithError = [&](se e) {
auto stopWithError = [&](se e)
{
DEBUGTRACE_PRINT("stopWithError");
StreamStatus stat = _streamStatus;
stat.errorType = e;
@ -322,7 +364,8 @@ public:
rval = 1;
};
switch (status) {
switch (status)
{
case RTAUDIO_INPUT_OVERFLOW:
stopWithError(se::inputXRun);
return 1;
@ -341,14 +384,16 @@ public:
const us neninchannels = this->neninchannels();
const us nenoutchannels = this->nenoutchannels();
const us sw = dtype_descr.sw;
if (nFrames != nFramesPerBlock) {
if (nFrames != nFramesPerBlock)
{
cerr << "RtAudio backend error: nFrames does not match block size!"
<< endl;
stopWithError(se::logicError);
return 1;
}
if (inputBuffer) {
if (inputBuffer)
{
assert(_incallback);
std::vector<byte_t *> ptrs;
ptrs.reserve(neninchannels);
@ -359,8 +404,10 @@ public:
assert(ch_max < ninchannels);
/// Only pass on the pointers of the channels we want
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 *ptr =
static_cast<byte_t *>(inputBuffer) + sw * ch * nFramesPerBlock;
ptrs.push_back(ptr);
@ -369,10 +416,11 @@ public:
DaqData d{nFramesPerBlock, neninchannels, dtype};
d.copyInFromRaw(ptrs);
_incallback(d);
_incallback(d);
}
if (outputBuffer) {
if (outputBuffer)
{
assert(_outcallback);
std::vector<byte_t *> ptrs;
ptrs.reserve(nenoutchannels);
@ -384,8 +432,10 @@ public:
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)
{
ptrs.push_back(static_cast<byte_t *>(outputBuffer) +
sw * ch * nFramesPerBlock);
}
@ -395,7 +445,8 @@ public:
_outcallback(d);
// Copy over the buffer
us j = 0;
for (auto ptr : ptrs) {
for (auto ptr : ptrs)
{
d.copyToRaw(j, ptr);
j++;
}
@ -411,17 +462,16 @@ public:
};
std::unique_ptr<Daq> createRtAudioDevice(const DeviceInfo &devinfo,
const DaqConfiguration &config) {
const DaqConfiguration &config)
{
return std::make_unique<RtAudioDaq>(devinfo, config);
}
void myerrorcallback(RtAudioError::Type, const string &errorText) {
cerr << "RtAudio backend stream error: " << errorText << endl;
}
int mycallback(
void *outputBuffer, void *inputBuffer, unsigned int nFrames,
__attribute__((unused)) double streamTime, // Not used parameter streamTime
RtAudioStreamStatus status, void *userData) {
RtAudioStreamStatus status, void *userData)
{
return static_cast<RtAudioDaq *>(userData)->streamCallback(
outputBuffer, inputBuffer, nFrames, status);

View File

@ -15,9 +15,11 @@ 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));
}
}
@ -25,21 +27,25 @@ 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);
}
};
void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist)
{
DEBUGTRACE_ENTER;
try {
try
{
PaError err = Pa_Initialize();
/// PortAudio says that Pa_Terminate() should not be called whenever there
@ -47,28 +53,33 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
/// of PortAudio show.
throwIfError(err);
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 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); */
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i);
if (!deviceInfo) {
if (!deviceInfo)
{
throw rte("No device info struct returned");
}
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;
@ -78,7 +89,7 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
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,
88200.0, 96000.0, 192000.0};
d.prefSampleRateIndex = 9;
@ -87,6 +98,9 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
d.prefFramesPerBlockIndex = 2;
d.availableInputRanges = {1.0};
// d.prefInputRangeIndex = 0; // Constructor-defined
d.availableOutputRanges = {1.0};
// d.prefOutputRangeIndex = 0; // Constructor-defined
d.ninchannels = deviceInfo->maxInputChannels;
d.noutchannels = deviceInfo->maxOutputChannels;
@ -95,7 +109,8 @@ void fillPortAudioDeviceInfo(DeviceInfoList &devinfolist) {
}
}
catch (rte &e) {
catch (rte &e)
{
cerr << "PortAudio backend error: " << e.what() << std::endl;
return;
}
@ -120,7 +135,8 @@ 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 =
@ -157,11 +173,13 @@ public:
};
std::unique_ptr<Daq> createPortAudioDevice(const DeviceInfo &devinfo,
const DaqConfiguration &config) {
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);
@ -170,14 +188,16 @@ 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;
PaError err = Pa_Initialize();
@ -193,23 +213,28 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
// 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._paDevInfo.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;
if (ok) {
if (ok)
{
devindex = i;
}
}
if (devindex < 0) {
if (devindex < 0)
{
throw rte(string("Device not found: ") + string(devinfo_gen.device_name));
}
@ -217,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;
@ -246,18 +272,20 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
std::unique_ptr<PaStreamParameters> instreamParams;
std::unique_ptr<PaStreamParameters> outstreamParams;
if (neninchannels() > 0) {
if (neninchannels() > 0)
{
instreamParams = std::make_unique<PaStreamParameters>(
PaStreamParameters({.device = devindex,
.channelCount = (int)neninchannels(),
.channelCount = (int)getHighestEnabledInChannel() + 1,
.sampleFormat = format,
.suggestedLatency = framesPerBlock() / samplerate(),
.hostApiSpecificStreamInfo = nullptr}));
}
if (nenoutchannels() > 0) {
if (nenoutchannels() > 0)
{
outstreamParams = std::make_unique<PaStreamParameters>(
PaStreamParameters({.device = devindex,
.channelCount = (int)nenoutchannels(),
.channelCount = (int)getHighestEnabledOutChannel() + 1,
.sampleFormat = format,
.suggestedLatency = framesPerBlock() / samplerate(),
.hostApiSpecificStreamInfo = nullptr}));
@ -278,21 +306,26 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
throwIfError(err);
}
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");
}
// Logical XOR
if (inCallback && outCallback) {
if (inCallback && outCallback)
{
throw rte("Either input or output stream possible for RtAudio. "
"Stream duplex mode not provided.");
}
if (neninchannels() > 0) {
if (!inCallback) {
if (neninchannels() > 0)
{
if (!inCallback)
{
throw rte(
"Input callback given, but stream does not provide input data");
@ -300,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");
}
@ -311,46 +346,57 @@ 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)) {
if (Pa_IsStreamStopped(_stream))
{
throw rte("Stream is already stopped");
}
PaError err = Pa_StopStream(_stream);
throwIfError(err);
}
Daq::StreamStatus PortAudioDaq::getStreamStatus() const {
Daq::StreamStatus PortAudioDaq::getStreamStatus() const
{
Daq::StreamStatus status;
// Copy over atomic flag.
status.errorType = _streamError;
// Check if stream is still running.
if (_stream) {
if (Pa_IsStreamActive(_stream)) {
if (_stream)
{
if (Pa_IsStreamActive(_stream))
{
status.isRunning = true;
}
}
return status;
}
PortAudioDaq::~PortAudioDaq() {
PortAudioDaq::~PortAudioDaq()
{
PaError err;
if (_stream) {
if (Pa_IsStreamActive(_stream)) {
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) {
if (_shouldPaTerminate)
{
err = Pa_Terminate();
if (err != paNoError) {
if (err != paNoError)
{
cerr << "Error terminating PortAudio. Do not know what to do." << endl;
}
}
@ -358,23 +404,28 @@ PortAudioDaq::~PortAudioDaq() {
int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags) {
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;
@ -386,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);
@ -398,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);
@ -411,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);
@ -423,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);
}
@ -434,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

@ -22,23 +22,37 @@ classifiers = [
]
urls = { "Documentation" = "https://lasp.ascee.nl" }
dependencies = ["scipy", "numpy", "matplotlib>=3.7.2", "appdirs",
"dataclasses_json", "h5py"]
dependencies = [
"scipy",
"numpy",
"matplotlib>=3.7.2",
"appdirs",
"dataclasses_json",
"h5py",
]
[build-system] # How pip and other frontends should build this project
requires = ["py-build-cmake~=0.1.8", "pybind11" ]
requires = ["py-build-cmake~=0.1.8", "pybind11"]
build-backend = "py_build_cmake.build"
[tool.py-build-cmake.module] # Where to find the Python module to package
directory = "python_src"
[tool.py-build-cmake.sdist] # What to include in source distributions
include = ["CMakeLists.txt", "cmake", "cpp_src", "python_src", "img", "scripts",
"third_party"]
include = [
"CMakeLists.txt",
"cmake",
"cpp_src",
"python_src",
"img",
"scripts",
"third_party",
]
[tool.py-build-cmake.cmake] # How to build the CMake project
build_type = "Release"
source_path = "."
options = { "LASP_HAS_PORTAUDIO" = "ON", "LASP_HAS_RTAUDIO" = "OFF" }
build_args = ["-j12"]
install_components = ["python_modules"]

View File

@ -91,6 +91,9 @@ class MeasurementType(Enum):
# Measurement serves as impedance tube calibration (long tube case)
muZCalLong = 1 << 3
# Series impedance reference
muZSeriesImpedanceRef = 1 << 4
def __str__(self):
match self:
case MeasurementType.NotSpecific: return '-'
@ -98,6 +101,7 @@ class MeasurementType(Enum):
case MeasurementType.CALGeneral: return 'General calibration'
case MeasurementType.muZCalShort: return 'ASCEE μZ short length calibration'
case MeasurementType.muZCalLong: return 'ASCEE μZ long length calibration'
case MeasurementType.muZSeriesImpedanceRef: return 'ASCEE μZ series impedance empty reference'
case _: raise ValueError("Not a MeasurementType")

@ -1 +1 @@
Subproject commit cb8d3dcbc6fa74c67f3e236be89b12d5630da141
Subproject commit daaf637f6f9fce670031221abfd7dfde92e5cce3