Back to TILs

How to use RTAudio to sound card IO

Date: 2023-12-15Last modified: 2024-11-02

Table of contents

Introduction

This post is related to a very basic use of the RTAudio library to produced waves on the sound card. My pourpose is to understand how to use a sound card for digital communication.

#include <chrono>
#include <iostream>
#include <rtaudio/RtAudio.h>
#include <thread>

Output - Generator

// Two-channel sawtooth wave generator.
// The callback function will automatically be invoked when the underlying audio system needs data for output.
int saw_generator_callback(
    void               *outputBuffer,
    void               *inputBuffer,
    unsigned int        nBufferFrames,
    double              streamTime,
    RtAudioStreamStatus status,
    void               *userData )
{
  unsigned int i, j;
  double      *buffer     = (double *)outputBuffer;
  double      *lastValues = (double *)userData;

  if( status ) {
    std::cout << "Stream underflow detected!" << std::endl;
  }

  // Write interleaved audio data.
  for( i = 0; i < nBufferFrames; i++ ) {
    for( j = 0; j < 2; j++ ) {
      *buffer++ = lastValues[j];

      lastValues[j] += 0.005 * ( j + 1 + ( j * 0.1 ) );
      if( lastValues[j] >= 1.0 )
        lastValues[j] -= 2.0;
    }
  }

  return 0;
}

Input - read from the microphone

A return value of 1 will cause the stream to finish draining its internal buffers and then halt (equivalent to calling the RtAudio::stopStream() function).

A return value of 2 will cause the stream to stop immediately (equivalent to calling the RtAudio::abortStream() function).



## Main function

```cpp
int main()
{
  RtAudio *audio_dac_adc = NULL;

  // Default RtAudio constructor
  try {
    audio_dac_adc = new RtAudio();
  }
  catch( RtAudioError &error ) {
    error.printMessage();
    exit( EXIT_FAILURE );
  }

  // Determine the number of devices available
  unsigned int number_of_devices = audio_dac_adc->getDeviceCount();

  if( number_of_devices == 0 ) {
    delete audio_dac_adc;
    return 0;
  }

  // Scan through devices for various capabilities
  RtAudio::DeviceInfo info;
  for( unsigned int device_index = 1; device_index <= number_of_devices; device_index++ ) {

    try {
      info = audio_dac_adc->getDeviceInfo( device_index );
    }
    catch( RtAudioError &error ) {
      error.printMessage();
      break;
    }

    std::cout << "device = " << device_index << "  " << info.name << "\n";
    std::cout << ": maximum output channels = " << info.outputChannels << "\n";
    std::cout << ": maximum input channels = " << info.inputChannels << "\n";
    std::cout << ": maximum duplex channels = " << info.duplexChannels << "\n";

    if( info.nativeFormats | RTAUDIO_SINT8 ) {
      std::cout << ": native support for 8-bits signed integer\n";
    }

    if( info.nativeFormats | RTAUDIO_SINT16 ) {
      std::cout << ": native support for 16-bits signed integer\n";
    }

    if( info.nativeFormats | RTAUDIO_SINT24 ) {
      std::cout << ": native support for 24-bits signed integer\n";
    }

    if( info.nativeFormats | RTAUDIO_SINT32 ) {
      std::cout << ": native support for 32-bits signed integer\n";
    }

    if( info.nativeFormats | RTAUDIO_FLOAT32 ) {
      std::cout << ": native support for 32-bits Normalized between plus/minus 1.0\n";
    }

    if( info.nativeFormats | RTAUDIO_FLOAT64 ) {
      std::cout << ": native support for 64-bits Normalized between plus/minus 1.0\n";
    }

    bool found_192 = false;
    for( const auto sample_rate : info.sampleRates ) {
      std::cout << ": supported sample rate: " << sample_rate << "\n";
      if( sample_rate == 192000 ) {
        found_192 = true;
      }
    }

    if( found_192 ) {

      // file:///usr/share/doc/librtaudio-dev/api_ref/recording.html
      RtAudio::StreamParameters *outputParameters = NULL;
      RtAudio::StreamParameters  inputParameters
          = { .deviceId = device_index, .nChannels = info.inputChannels, .firstChannel = 0 };
      RtAudioFormat   format       = RTAUDIO_SINT16; // 16-bit signed integer.
      unsigned int    sampleRate   = 192000;
      unsigned int    bufferFrames = 256; // 256 sample frames
      RtAudioCallback callback     = &record_callback;
      void           *user_data    = NULL;
      // double                  user_data[2]  = { 0, 0 };
      RtAudio::StreamOptions *options       = NULL;
      RtAudioErrorCallback    errorCallback = NULL;

      try {
        audio_dac_adc->openStream(
            outputParameters,
            &inputParameters,
            format,
            sampleRate,
            &bufferFrames,
            callback,
            user_data,
            options,
            errorCallback );

        audio_dac_adc->startStream();
      }
      catch( RtAudioError &e ) {
        e.printMessage();
        exit( 0 );
      }

      for( auto t = 0; t < 5; ++t ) {
        std::cout << "\nWaiting for 1s - recording...\n";
        std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
      }

      try {
        // Stop the stream
        audio_dac_adc->stopStream();
      }
      catch( RtAudioError &e ) {
        e.printMessage();
      }

      if( audio_dac_adc->isStreamOpen() ) {
        audio_dac_adc->closeStream();
      }

      break;
    }
  }

  // Playback

  {
    RtAudio::StreamParameters  output_parameters;
    RtAudio::StreamParameters *input_parameters = NULL;
    output_parameters.deviceId                  = audio_dac_adc->getDefaultOutputDevice();
    output_parameters.nChannels                 = 2;
    output_parameters.firstChannel              = 0;
    unsigned int sampleRate                     = 44100;
    unsigned int bufferFrames                   = 256; // 256 sample frames
    double       data[2]                        = { 0, 0 };

    try {
      audio_dac_adc->openStream(
          &output_parameters,
          input_parameters,
          RTAUDIO_FLOAT64,
          sampleRate,
          &bufferFrames,
          &saw_generator_callback,
          (void *)&data );
      audio_dac_adc->startStream();
    }
    catch( RtAudioError &e ) {
      e.printMessage();
      exit( 0 );
    }

    for( auto t = 0; t < 3; ++t ) {
      std::cout << "\nWaiting for 1s - playing...\n";
      std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
    }

    try {
      // Stop the stream
      audio_dac_adc->stopStream();
    }
    catch( RtAudioError &e ) {
      e.printMessage();
    }

    if( audio_dac_adc->isStreamOpen() ) {
      audio_dac_adc->closeStream();
    }
  }

  // Clean up
  delete audio_dac_adc;

  return 0;
}

Possible output

device = 1  Built-in Audio Analog Stereo (echo cancelled with Built-in Audio Analog Stereo)
: maximum output channels = 1
: maximum input channels = 1
: maximum duplex channels = 0
: native support for 8-bits signed integer
: native support for 16-bits signed integer
: native support for 24-bits signed integer
: native support for 32-bits signed integer
: native support for 32-bits Normalized between plus/minus 1.0
: native support for 64-bits Normalized between plus/minus 1.0
: supported sample rate: 8000
: supported sample rate: 16000
: supported sample rate: 22050
: supported sample rate: 32000
: supported sample rate: 44100
: supported sample rate: 48000
: supported sample rate: 96000
: supported sample rate: 192000

Waiting for 1s - recording...

CB: counter=0 dt=0s t=0s
CB: counter=100 dt=0.00133333s t=0.133333s
CB: counter=200 dt=0.00133333s t=0.266667s
CB: counter=300 dt=0.00133333s t=0.4s
CB: counter=400 dt=0.00133333s t=0.533333s
CB: counter=500 dt=0.00133333s t=0.666667s
CB: counter=600 dt=0.00133333s t=0.8s
CB: counter=700 dt=0.00133333s t=0.933333s
Waiting for 1s - recording...

CB: counter=800 dt=0.00133333s t=1.06667s
CB: counter=900 dt=0.00133333s t=1.2s
CB: counter=1000 dt=0.00133333s t=1.33333s
CB: counter=1100 dt=0.00133333s t=1.46667s
CB: counter=1200 dt=0.00133333s t=1.6s
CB: counter=1300 dt=0.00133333s t=1.73333s
CB: counter=1400 dt=0.00133333s t=1.86667s
Waiting for 1s - recording...

CB: counter=1500 dt=0.00133333s t=2s
CB: counter=1600 dt=0.00133333s t=2.13333s
CB: counter=1700 dt=0.00133333s t=2.26667s
CB: counter=1800 dt=0.00133333s t=2.4s
CB: counter=1900 dt=0.00133333s t=2.53333s
CB: counter=2000 dt=0.00133333s t=2.66667s
CB: counter=2100 dt=0.00133333s t=2.8s
CB: counter=2200 dt=0.00133333s t=2.93333s
Waiting for 1s - recording...

CB: counter=2300 dt=0.00133333s t=3.06667s
CB: counter=2400 dt=0.00133333s t=3.2s
CB: counter=2500 dt=0.00133333s t=3.33333s
CB: counter=2600 dt=0.00133333s t=3.46667s
CB: counter=2700 dt=0.00133333s t=3.6s
CB: counter=2800 dt=0.00133333s t=3.73333s
CB: counter=2900 dt=0.00133333s t=3.86667s
Waiting for 1s - recording...

CB: counter=3000 dt=0.00133333s t=4s
CB: counter=3100 dt=0.00133333s t=4.13333s
CB: counter=3200 dt=0.00133333s t=4.26667s
CB: counter=3300 dt=0.00133333s t=4.4s
CB: counter=3400 dt=0.00133333s t=4.53333s
CB: counter=3500 dt=0.00133333s t=4.66667s
CB: counter=3600 dt=0.00133333s t=4.8s
CB: counter=3700 dt=0.00133333s t=4.93333s
Waiting for 1s - playing...

Waiting for 1s - playing...

Waiting for 1s - playing...

References