3#include "debugtrace.hpp"
7#if LASP_HAS_RTAUDIO == 1
16using rte = std::runtime_error;
18using lck = std::scoped_lock<std::mutex>;
27 virtual std::unique_ptr<DeviceInfo>
clone()
const override {
28 return std::make_unique<DeviceInfo>(*
this);
35 vector<RtAudio::Api> apis;
36 RtAudio::getCompiledApi(apis);
38 for (
auto api : apis) {
40 us count = rtaudio.getDeviceCount();
41 for (
us devno = 0; devno < count; devno++) {
43 RtAudio::DeviceInfo devinfo = rtaudio.getDeviceInfo(devno);
44 if (!devinfo.probed) {
51 case RtAudio::LINUX_ALSA:
52 d.api = rtaudioAlsaApi;
54 case RtAudio::LINUX_PULSE:
55 d.api = rtaudioPulseaudioApi;
57 case RtAudio::WINDOWS_WASAPI:
58 d.api = rtaudioWasapiApi;
60 case RtAudio::WINDOWS_DS:
63 case RtAudio::WINDOWS_ASIO:
64 d.api = rtaudioAsioApi;
67 cerr <<
"Not implemented RtAudio API, skipping." << endl;
72 d.device_name = devinfo.name;
73 d._api_devindex = devno;
77 bool rate_48k_found =
false;
79 for (
us j = 0; j < devinfo.sampleRates.size(); j++) {
81 us rate_int = devinfo.sampleRates[j];
83 d.availableSampleRates.push_back((
double)rate_int);
85 if (!rate_48k_found) {
87 if (devinfo.preferredSampleRate == rate_int) {
88 d.prefSampleRateIndex = j;
91 if (rate_int == 48000) {
92 d.prefSampleRateIndex = j;
93 rate_48k_found =
true;
98 d.noutchannels = devinfo.outputChannels;
99 d.ninchannels = devinfo.inputChannels;
101 d.availableInputRanges = {1.0};
103 RtAudioFormat formats = devinfo.nativeFormats;
104 if (formats & RTAUDIO_SINT8) {
105 d.availableDataTypes.push_back(
108 if (formats & RTAUDIO_SINT16) {
109 d.availableDataTypes.push_back(
116 if (formats & RTAUDIO_SINT32) {
117 d.availableDataTypes.push_back(
120 if (formats & RTAUDIO_FLOAT64) {
121 d.availableDataTypes.push_back(
124 if (
d.availableDataTypes.size() == 0) {
125 std::cerr <<
"RtAudio: No data types found in device!" << endl;
128 d.prefDataTypeIndex =
d.availableDataTypes.size() - 1;
130 d.availableFramesPerBlock = {512, 1024, 2048, 4096, 8192};
131 d.prefFramesPerBlockIndex = 2;
133 devinfolist.push_back(std::make_unique<RtAudioDeviceInfo>(d));
138static int mycallback(
void *outputBuffer,
void *inputBuffer,
139 unsigned int nFrames,
double streamTime,
140 RtAudioStreamStatus status,
void *userData);
142static void myerrorcallback(RtAudioError::Type,
const string &errorText);
144class RtAudioDaq :
public Daq {
147 const us nFramesPerBlock;
149 RtAudioDaq(
const RtAudioDaq &) =
delete;
150 RtAudioDaq &
operator=(
const RtAudioDaq &) =
delete;
155 std::atomic<StreamStatus> _streamStatus{};
159 :
Daq(devinfo_gen, config), rtaudio(static_cast<RtAudio::Api>(
160 devinfo_gen.api.api_specific_subcode)),
161 nFramesPerBlock(
Daq::framesPerBlock()) {
168 throw rte(
"RtAudio backend cannot run in duplex mode.");
170 assert(!monitorOutput);
171 const RtAudioDeviceInfo &devinfo =
172 static_cast<const RtAudioDeviceInfo &
>(devinfo_gen);
174 std::unique_ptr<RtAudio::StreamParameters> inParams, outParams;
178 inParams = std::make_unique<RtAudio::StreamParameters>();
183 inParams->firstChannel = 0;
185 inParams->deviceId = devinfo._api_devindex;
189 outParams = std::make_unique<RtAudio::StreamParameters>();
194 outParams->firstChannel = 0;
196 outParams->deviceId = devinfo._api_devindex;
199 RtAudio::StreamOptions streamoptions;
200 streamoptions.flags = RTAUDIO_HOG_DEVICE | RTAUDIO_NONINTERLEAVED;
202 streamoptions.numberOfBuffers = 2;
203 streamoptions.streamName =
"LASP RtAudio DAQ stream";
204 streamoptions.priority = 0;
206 RtAudioFormat format;
210 case Dtype::dtype_fl32:
211 DEBUGTRACE_PRINT(
"Datatype float32");
212 format = RTAUDIO_FLOAT32;
214 case Dtype::dtype_fl64:
215 DEBUGTRACE_PRINT(
"Datatype float64");
216 format = RTAUDIO_FLOAT64;
218 case Dtype::dtype_int8:
219 DEBUGTRACE_PRINT(
"Datatype int8");
220 format = RTAUDIO_SINT8;
222 case Dtype::dtype_int16:
223 DEBUGTRACE_PRINT(
"Datatype int16");
224 format = RTAUDIO_SINT16;
226 case Dtype::dtype_int32:
227 DEBUGTRACE_PRINT(
"Datatype int32");
228 format = RTAUDIO_SINT32;
231 throw rte(
"Invalid data type specified for DAQ stream.");
237 unsigned int nFramesPerBlock_copy = nFramesPerBlock;
240 rtaudio.openStream(outParams.get(), inParams.get(), format,
241 static_cast<us>(samplerate()), &nFramesPerBlock_copy,
242 mycallback, (
void *)
this, &streamoptions,
245 if (nFramesPerBlock_copy != nFramesPerBlock) {
246 throw rte(
string(
"Got different number of frames per block back from RtAudio "
247 "backend: ") + std::to_string(nFramesPerBlock_copy) +
". I do not know what to do.");
256 assert(!monitorOutput);
259 throw rte(
"Stream already running");
263 if (inCallback && outCallback) {
264 throw rte(
"Either input or output stream possible for RtAudio. "
265 "Stream duplex mode not provided.");
272 "Input callback given, but stream does not provide input data");
275 _incallback = inCallback;
280 "Output callback given, but stream does not provide output data");
282 _outcallback = outCallback;
286 rtaudio.startStream();
290 status.isRunning =
true;
291 _streamStatus = status;
294 StreamStatus
getStreamStatus() const override final {
return _streamStatus; }
296 void stop() override final {
299 rtaudio.stopStream();
301 StreamStatus s = _streamStatus;
307 int streamCallback(
void *outputBuffer,
void *inputBuffer,
308 unsigned int nFrames, RtAudioStreamStatus status) {
315 auto stopWithError = [&](se e) {
316 DEBUGTRACE_PRINT(
"stopWithError");
317 StreamStatus stat = _streamStatus;
319 stat.isRunning =
false;
320 _streamStatus = stat;
325 case RTAUDIO_INPUT_OVERFLOW:
326 stopWithError(se::inputXRun);
329 case RTAUDIO_OUTPUT_UNDERFLOW:
330 stopWithError(se::outputXRun);
342 const us sw = dtype_descr.sw;
343 if (nFrames != nFramesPerBlock) {
344 cerr <<
"RtAudio backend error: nFrames does not match block size!"
346 stopWithError(se::logicError);
352 std::vector<byte_t *> ptrs;
353 ptrs.reserve(neninchannels);
357 assert(ch_min < ninchannels);
358 assert(ch_max < ninchannels);
361 for (
us ch = ch_min; ch <= ch_max; ch++) {
362 if (inchannel_config.at(ch).enabled) {
364 static_cast<byte_t *
>(inputBuffer) + sw * ch * nFramesPerBlock;
369 d.copyInFromRaw(ptrs);
375 assert(_outcallback);
376 std::vector<byte_t *> ptrs;
377 ptrs.reserve(nenoutchannels);
383 assert(ch_min < noutchannels);
384 assert(ch_max < noutchannels);
386 for (
us ch = ch_min; ch <= ch_max; ch++) {
387 if (outchannel_config.at(ch).enabled) {
388 ptrs.push_back(
static_cast<byte_t *
>(outputBuffer) +
389 sw * ch * nFramesPerBlock);
397 for (
auto ptr : ptrs) {
409 ~RtAudioDaq() =
default;
414 return std::make_unique<RtAudioDaq>(devinfo, config);
417void myerrorcallback(RtAudioError::Type,
const string &errorText) {
418 cerr <<
"RtAudio backend stream error: " << errorText << endl;
421 void *outputBuffer,
void *inputBuffer,
unsigned int nFrames,
422 __attribute__((unused))
double streamTime,
423 RtAudioStreamStatus status,
void *userData) {
425 return static_cast<RtAudioDaq *
>(userData)->streamCallback(
426 outputBuffer, inputBuffer, nFrames, status);
Configuration of a DAQ device.
int getHighestEnabledOutChannel() const
Get the highest channel number from the list of enabled output channels.
int getHighestEnabledInChannel() const
Get the enabled highest channel number from the list of enabled input channels.
int getLowestEnabledInChannel() const
Get the lowest channel number from the list of enabled input channels.
int getLowestEnabledOutChannel() const
Get the lowest channel number from the list of enabled output channels.
Data coming from / going to DAQ. Non-interleaved format, which means data in buffer is ordered by cha...
Base cass for all DAQ (Data Acquisition) interfaces. A DAQ can be a custom device,...
const DataTypeDescriptor & dtypeDescr() const
More elaborate description of the datatypes.
us neninchannels(bool include_monitorchannels=true) const
Returns the number of enabled input channels.
bool duplexMode() const
Whether the device runs in duplex mode (both input and output), or false if only input / only output.
virtual void start(InDaqCallback inCallback, OutDaqCallback outCallback)=0
Start the Daq.
DataTypeDescriptor::DataType dataType() const
Returns datatype (enum) corresponding to the datatype of the samples.
virtual void stop()=0
Stop the Daq device. Throws an exception if the device is not running at the time this method is call...
virtual StreamStatus getStreamStatus() const =0
Get stream status corresponding to current DAQ.
us nenoutchannels() const
Returns the number of enabled output channels.
DataType
Basic data types coming from a DAQ that we can deal with. The naming will be self-explainging.
Structure containing device info parameters.
unsigned ninchannels
The number of input channels available for the device.
unsigned noutchannels
The number of output channels available for the device.
virtual std::unique_ptr< DeviceInfo > clone() const
Clone a device info.
DeviceInfo & operator=(const DeviceInfo &)=delete
std::vector< std::unique_ptr< DeviceInfo > > DeviceInfoList
std::function< void(DaqData &)> OutDaqCallback
std::function< void(const DaqData &)> InDaqCallback
void fillRtAudioDeviceInfo(DeviceInfoList &devinfolist)
Append RtAudio backend devices to the list.
std::unique_ptr< Daq > createRtAudioDevice(const DeviceInfo &devinfo, const DaqConfiguration &config)
Method called from Daq::createDaq.
std::scoped_lock< std::mutex > lck
size_t us
We often use boolean values.