Portaudio backend seems to be working. No extensive checks performed yet.

This commit is contained in:
Anne de Jong 2023-06-15 09:48:45 +02:00
parent 77b1848bb4
commit 64a268e277
2 changed files with 120 additions and 10 deletions

View File

@ -337,9 +337,9 @@ public:
const auto &dtype_descr = dtypeDescr(); const auto &dtype_descr = dtypeDescr();
const auto dtype = dataType(); const auto dtype = dataType();
us neninchannels = this->neninchannels(); const us neninchannels = this->neninchannels();
us nenoutchannels = this->nenoutchannels(); const us nenoutchannels = this->nenoutchannels();
us sw = dtype_descr.sw; const 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;

View File

@ -1,4 +1,4 @@
#define DEBUGTRACE_ENABLED /* #define DEBUGTRACE_ENABLED */
#include "debugtrace.hpp" #include "debugtrace.hpp"
#include "lasp_config.h" #include "lasp_config.h"
@ -123,6 +123,10 @@ static int rawPaCallback(const void *inputBuffer, void *outputBuffer,
class PortAudioDaq : public Daq { class PortAudioDaq : public Daq {
bool _shouldPaTerminate = false; bool _shouldPaTerminate = false;
PaStream *_stream = nullptr; PaStream *_stream = nullptr;
std::atomic<StreamStatus::StreamError> _streamError =
StreamStatus::StreamError::noError;
InDaqCallback _incallback;
OutDaqCallback _outcallback;
public: public:
PortAudioDaq(const OurPaDeviceInfo &devinfo_gen, PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
@ -211,11 +215,12 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
using Dtype = DataTypeDescriptor::DataType; using Dtype = DataTypeDescriptor::DataType;
const Dtype dtype = dataType(); const Dtype dtype = dataType();
PaSampleFormat format; // Sample format is bit flag
PaSampleFormat format = paNonInterleaved;
switch (dtype) { switch (dtype) {
case Dtype::dtype_fl32: case Dtype::dtype_fl32:
DEBUGTRACE_PRINT("Datatype float32"); DEBUGTRACE_PRINT("Datatype float32");
format = paFloat32; format |= paFloat32;
break; break;
case Dtype::dtype_fl64: case Dtype::dtype_fl64:
DEBUGTRACE_PRINT("Datatype float64"); DEBUGTRACE_PRINT("Datatype float64");
@ -223,15 +228,15 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
break; break;
case Dtype::dtype_int8: case Dtype::dtype_int8:
DEBUGTRACE_PRINT("Datatype int8"); DEBUGTRACE_PRINT("Datatype int8");
format = paInt8; format |= paInt8;
break; break;
case Dtype::dtype_int16: case Dtype::dtype_int16:
DEBUGTRACE_PRINT("Datatype int16"); DEBUGTRACE_PRINT("Datatype int16");
format = paInt16; format |= paInt16;
break; break;
case Dtype::dtype_int32: case Dtype::dtype_int32:
DEBUGTRACE_PRINT("Datatype int32"); DEBUGTRACE_PRINT("Datatype int32");
format = paInt32; format |= paInt32;
break; break;
default: default:
throw rte("Invalid data type specified for DAQ stream."); throw rte("Invalid data type specified for DAQ stream.");
@ -250,7 +255,7 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen,
.hostApiSpecificStreamInfo = nullptr})); .hostApiSpecificStreamInfo = nullptr}));
} }
if (nenoutchannels() > 0) { if (nenoutchannels() > 0) {
instreamParams = std::make_unique<PaStreamParameters>( outstreamParams = std::make_unique<PaStreamParameters>(
PaStreamParameters({.device = devindex, PaStreamParameters({.device = devindex,
.channelCount = (int)nenoutchannels(), .channelCount = (int)nenoutchannels(),
.sampleFormat = format, .sampleFormat = format,
@ -279,6 +284,30 @@ void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) {
if (Pa_IsStreamActive(_stream)) { if (Pa_IsStreamActive(_stream)) {
throw rte("Stream is already running"); throw rte("Stream is already running");
} }
// 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");
}
_incallback = inCallback;
}
if (nenoutchannels() > 0) {
if (!outCallback) {
throw rte(
"Output callback given, but stream does not provide output data");
}
_outcallback = outCallback;
}
PaError err = Pa_StartStream(_stream); PaError err = Pa_StartStream(_stream);
throwIfError(err); throwIfError(err);
} }
@ -293,6 +322,9 @@ void PortAudioDaq::stop() {
} }
Daq::StreamStatus PortAudioDaq::getStreamStatus() const { Daq::StreamStatus PortAudioDaq::getStreamStatus() const {
Daq::StreamStatus status; Daq::StreamStatus status;
// Copy over atomic flag.
status.errorType = _streamError;
// Check if stream is still running.
if (_stream) { if (_stream) {
if (Pa_IsStreamActive(_stream)) { if (Pa_IsStreamActive(_stream)) {
status.isRunning = true; status.isRunning = true;
@ -329,6 +361,84 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer,
PaStreamCallbackFlags statusFlags) { PaStreamCallbackFlags statusFlags) {
DEBUGTRACE_ENTER; DEBUGTRACE_ENTER;
typedef Daq::StreamStatus::StreamError se;
if (statusFlags & paPrimingOutput) {
// Initial output buffers generated. So nothing with input yet
return paContinue;
}
if ((statusFlags & paInputUnderflow) || (statusFlags & paInputOverflow)) {
_streamError = se::inputXRun;
return paAbort;
}
if ((statusFlags & paOutputUnderflow) || (statusFlags & paOutputOverflow)) {
_streamError = se::outputXRun;
return paAbort;
}
if (framesPerBuffer != framesPerBlock()) {
cerr << "Logic error: expected a block size of: " << framesPerBlock()
<< endl;
_streamError = se::logicError;
return paAbort;
}
const us neninchannels = this->neninchannels();
const us nenoutchannels = this->nenoutchannels();
const auto &dtype_descr = dtypeDescr();
const auto dtype = dataType();
const us sw = dtype_descr.sw;
if (inputBuffer) {
assert(_incallback);
std::vector<byte_t *> ptrs;
ptrs.reserve(neninchannels);
const us ch_min = getLowestEnabledInChannel();
const us ch_max = getHighestEnabledInChannel();
assert(ch_min < ninchannels);
assert(ch_max < ninchannels);
/// 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) {
byte_t *ch_ptr =
reinterpret_cast<byte_t **>(const_cast<void *>(inputBuffer))[ch];
ptrs.push_back(ch_ptr);
}
}
DaqData d{framesPerBuffer, neninchannels, dtype};
d.copyInFromRaw(ptrs);
_incallback(d);
}
if (outputBuffer) {
assert(_outcallback);
std::vector<byte_t *> ptrs;
ptrs.reserve(nenoutchannels);
/* outCallback */
const us ch_min = getLowestEnabledOutChannel();
const us ch_max = getHighestEnabledOutChannel();
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) {
byte_t *ch_ptr = reinterpret_cast<byte_t **>(outputBuffer)[ch];
ptrs.push_back(ch_ptr);
}
}
DaqData d{framesPerBuffer, nenoutchannels, dtype};
_outcallback(d);
// Copy over the buffer
us j = 0;
for (auto ptr : ptrs) {
d.copyToRaw(j, ptr);
j++;
}
}
return paContinue; return paContinue;
} }