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.