diff --git a/cpp_src/device/CMakeLists.txt b/cpp_src/device/CMakeLists.txt index 481fb24..13af29b 100644 --- a/cpp_src/device/CMakeLists.txt +++ b/cpp_src/device/CMakeLists.txt @@ -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) diff --git a/cpp_src/device/lasp_rtaudiodaq.cpp b/cpp_src/device/lasp_rtaudiodaq.cpp index c6acbb9..897bb47 100644 --- a/cpp_src/device/lasp_rtaudiodaq.cpp +++ b/cpp_src/device/lasp_rtaudiodaq.cpp @@ -17,37 +17,46 @@ using rte = std::runtime_error; using std::vector; using lck = std::scoped_lock; -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 clone() const override { + int ID; // Copy of RtAudio::DeviceInfo::ID + virtual std::unique_ptr clone() const override + { return std::make_unique(*this); } }; -void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) { +void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist) +{ DEBUGTRACE_ENTER; vector 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(); @@ -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(samplerate()), &nFramesPerBlock_copy, - mycallback, (void *)this, &streamoptions, - &myerrorcallback); + RtAudioErrorType err = rtaudio.openStream(outParams.get(), inParams.get(), format, + static_cast(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 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(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 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(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 createRtAudioDevice(const DeviceInfo &devinfo, - const DaqConfiguration &config) { + const DaqConfiguration &config) +{ return std::make_unique(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(userData)->streamCallback( outputBuffer, inputBuffer, nFrames, status); diff --git a/third_party/rtaudio b/third_party/rtaudio index 46b01b5..b4f0490 160000 --- a/third_party/rtaudio +++ b/third_party/rtaudio @@ -1 +1 @@ -Subproject commit 46b01b5b134f33d8ddc3dab76829d4b1350e0522 +Subproject commit b4f04903312e0e0efffbe77655172e0f060dc085