diff --git a/src/lasp/device/lasp_rtaudiodaq.cpp b/src/lasp/device/lasp_rtaudiodaq.cpp index b10d54c..467fa63 100644 --- a/src/lasp/device/lasp_rtaudiodaq.cpp +++ b/src/lasp/device/lasp_rtaudiodaq.cpp @@ -337,9 +337,9 @@ public: const auto &dtype_descr = dtypeDescr(); const auto dtype = dataType(); - us neninchannels = this->neninchannels(); - us nenoutchannels = this->nenoutchannels(); - us sw = dtype_descr.sw; + const us neninchannels = this->neninchannels(); + const us nenoutchannels = this->nenoutchannels(); + const us sw = dtype_descr.sw; if (nFrames != nFramesPerBlock) { cerr << "RtAudio backend error: nFrames does not match block size!" << endl; diff --git a/src/lasp/device/portaudio/lasp_portaudiodaq.cpp b/src/lasp/device/portaudio/lasp_portaudiodaq.cpp index f85e971..4715828 100644 --- a/src/lasp/device/portaudio/lasp_portaudiodaq.cpp +++ b/src/lasp/device/portaudio/lasp_portaudiodaq.cpp @@ -1,4 +1,4 @@ -#define DEBUGTRACE_ENABLED +/* #define DEBUGTRACE_ENABLED */ #include "debugtrace.hpp" #include "lasp_config.h" @@ -123,6 +123,10 @@ static int rawPaCallback(const void *inputBuffer, void *outputBuffer, class PortAudioDaq : public Daq { bool _shouldPaTerminate = false; PaStream *_stream = nullptr; + std::atomic _streamError = + StreamStatus::StreamError::noError; + InDaqCallback _incallback; + OutDaqCallback _outcallback; public: PortAudioDaq(const OurPaDeviceInfo &devinfo_gen, @@ -211,11 +215,12 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen, using Dtype = DataTypeDescriptor::DataType; const Dtype dtype = dataType(); - PaSampleFormat format; + // Sample format is bit flag + PaSampleFormat format = paNonInterleaved; switch (dtype) { case Dtype::dtype_fl32: DEBUGTRACE_PRINT("Datatype float32"); - format = paFloat32; + format |= paFloat32; break; case Dtype::dtype_fl64: DEBUGTRACE_PRINT("Datatype float64"); @@ -223,15 +228,15 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen, break; case Dtype::dtype_int8: DEBUGTRACE_PRINT("Datatype int8"); - format = paInt8; + format |= paInt8; break; case Dtype::dtype_int16: DEBUGTRACE_PRINT("Datatype int16"); - format = paInt16; + format |= paInt16; break; case Dtype::dtype_int32: DEBUGTRACE_PRINT("Datatype int32"); - format = paInt32; + format |= paInt32; break; default: throw rte("Invalid data type specified for DAQ stream."); @@ -250,7 +255,7 @@ PortAudioDaq::PortAudioDaq(const OurPaDeviceInfo &devinfo_gen, .hostApiSpecificStreamInfo = nullptr})); } if (nenoutchannels() > 0) { - instreamParams = std::make_unique( + outstreamParams = std::make_unique( PaStreamParameters({.device = devindex, .channelCount = (int)nenoutchannels(), .sampleFormat = format, @@ -279,6 +284,30 @@ void PortAudioDaq::start(InDaqCallback inCallback, OutDaqCallback outCallback) { if (Pa_IsStreamActive(_stream)) { 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); throwIfError(err); } @@ -293,6 +322,9 @@ void PortAudioDaq::stop() { } 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)) { status.isRunning = true; @@ -329,6 +361,84 @@ int PortAudioDaq::memberPaCallback(const void *inputBuffer, void *outputBuffer, PaStreamCallbackFlags statusFlags) { 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 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(const_cast(inputBuffer))[ch]; + ptrs.push_back(ch_ptr); + } + } + DaqData d{framesPerBuffer, neninchannels, dtype}; + d.copyInFromRaw(ptrs); + + _incallback(d); + } + + if (outputBuffer) { + assert(_outcallback); + std::vector 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(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; }