Please note as of Wednesday, August 15th, 2018 this wiki has been set to read only. If you are a TI Employee and require Edit ability please contact x0211426 from the company directory.

Sitara Linux Audio Sample Application Overview

From Texas Instruments Wiki
Jump to: navigation, search


Sitara Linux Software Developer's Guide Sitara Linux SDK Audio: Sample ALSA Application

Introduction

Writing a linux audio audio application using the ALSA API involves the following steps:

  • Opening the audio device
  • Set the parameters of the device
  • Receive audio data from the device or deliver audio data to the device
  • Close the device

These steps are explained in detail in this section.

NOTE

User space ALSA libraries can be downloaded from this link ALSA Project Download. User needs to build and install them before he starts using the ALSA based applications.

A minimal playback application

This program opens an audio interface for playback, configures it for stereo, 16 bit, 44.1kHz, interleaved conventional read/write access. Then its delivers a chunk of random data to it, and exits. It represents about the simplest possible use of the ALSA Audio API, and isn't meant to be a real program.

Opening the audio device

To write a simple PCM application for ALSA, we first need a handle for the PCM device. Then we have to specify the direction of the PCM stream, which can be either playback or capture. We also have to provide some information about the configuration we would like to use, like buffer size, sample rate, pcm data format. So, first we declare:

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
 
#define BUFF_SIZE 4096
 
int main (int argc, char *argv[])
{
  int err;
  short buf[BUFF_SIZE];
  int rate = 44100; /* Sample rate */
  unsigned int exact_rate; /* Sample rate returned by */
  /* Handle for the PCM device */
  snd_pcm_t *playback_handle;
  /* Playback stream */
  snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
  /* This structure contains information about the hardware and can be used to specify the configuration to be used for */
  /* the PCM stream. */
  snd_pcm_hw_params_t *hw_params;

The most important ALSA interfaces to the PCM devices are the "plughw" and the "hw" interface. If you use the "plughw" interface, you need not care much about the sound hardware. If your sound card does not support the sample rate or sample format you specify, your data will be automatically converted. This also applies to the access type and the number of channels. With the "hw" interface, you have to check whether your hardware supports the configuration you would like to use. Otherwise, user can use the default interface for playback by:

/* Name of the PCM device, like plughw:0,0 */
/* The first number is the number of the soundcard, the second number is the number of the device. */
 
static char *device = "default"; /* playback device */

Now we can open the PCM device:

/* Open PCM. The last parameter of this function is the mode. */
if ((err = snd_pcm_open (&playback_handle, device, stream, 0)) < 0) {
    fprintf (stderr, "cannot open audio device (%s)\n", snd_strerror (err));
    exit (1);
}

Setting the parameters of the device

Now we initialize the variables and allocate the hwparams structure:

/* Allocate the snd_pcm_hw_params_t structure on the stack. */
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
    fprintf (stderr, "cannot allocate hardware parameters (%s)\n", snd_strerror (err));
    exit (1);
}


Before we can write PCM data to the soundcard, we have to specify access type, sample format, sample rate, number of channels, number of periods and period size. First, we initialize the hwparams structure with the full configuration space of the soundcard:

/* Init hwparams with full configuration space */
if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < 0) {
    fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err));
    exit (1);
}

Now configure the desired parameters. For this example, we assume that the soundcard can be configured for stereo playback of 16 Bit Little Endian data, sampled at 44100 Hz. Therefore, we restrict the configuration space to match this configuration only. The access type specifies the way in which multi-channel data is stored in the buffer. For INTERLEAVED access, each frame in the buffer contains the consecutive sample data for the channels. For 16 Bit stereo data, this means that the buffer contains alternating words of sample data for the left and right channel.

/* Set access type. */
if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
    fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err));
    exit (1);
}
 
/* Set sample format */
if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
    fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err));
    exit (1);
}
 
/* Set sample rate. If the exact rate is not supported by the hardware, use nearest possible rate. */
exact_rate = rate;
if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, &exact_rate, 0)) < 0) {
    fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err));
    exit (1);
}
 
if (rate != exact_rate) {
    fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n ==> Using %d Hz instead.\n", rate, exact_rate);
}
 
/* Set number of channels */
if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, 2)) < 0) {
    fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err));
    exit (1);
}

Now we apply the configuration to the PCM device pointed to by pcm_handle and prepare the PCM device.

/* Apply HW parameter settings to PCM device and prepare device. */
if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < 0) {
    fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err));
    exit (1);
}
 
snd_pcm_hw_params_free (hw_params);
 
if ((err = snd_pcm_prepare (playback_handle)) < 0) {
    fprintf (stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror (err));
    exit (1);
}

Writing data to the device

After the PCM device is configured, we can start writing PCM data to it. The first write access will start the PCM playback. For interleaved write access, we use the function:

/* Write some junk data to produce sound. */
if ((err = snd_pcm_writei (playback_handle, buf, BUFF_SIZE/2)) != BUFF_SIZE/2) {
    fprintf (stderr, "write to audio interface failed (%s)\n", snd_strerror (err));
    exit (1);
} else {
    fprintf (stdout, "snd_pcm_writei successful\n");
}

After the PCM playback is started, we have to make sure that our application sends enough data to the soundcard buffer. Otherwise, a buffer under-run will occur. After such an under-run has occurred, snd_pcm_prepare should be called.

Closing the device

After the data has been transferred, the device needs to be closed by calling:

snd_pcm_close (playback_handle);

A minimal record application

This program opens an audio interface for capture, configures it for stereo, 16 bit, 44.1kHz, interleaved conventional read/write access. Then its reads a chunk of random data from it, and exits. It isn't meant to be a real program.

Note that it is not possible to use one pcm handle for both playback and capture. So you have to configure two handles if you want to access the PCM device in both directions.

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
 
#define BUFF_SIZE 4096
 
int main (int argc, char *argv[])
{
  int err;
  short buf[BUFF_SIZE];
  int rate = 44100; /* Sample rate */
  int exact_rate; /* Sample rate returned by */
  snd_pcm_t *capture_handle;
 
  /* This structure contains information about the hardware and can be used to specify the configuration to be used for */
  /* the PCM stream. */
  snd_pcm_hw_params_t *hw_params;
 
  /* Name of the PCM device, like hw:0,0 */
  /* The first number is the number of the soundcard, the second number is the number of the device. */
  static char *device = "hw:0,0"; /* capture device */
 
  /* Open PCM. The last parameter of this function is the mode. */
  if ((err = snd_pcm_open (&capture_handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
    fprintf (stderr, "cannot open audio device (%s)\n", snd_strerror (err));
    exit (1);
  }
 
  memset(buf,0,BUFF_SIZE);
 
  /* Allocate the snd_pcm_hw_params_t structure on the stack. */
  if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
    fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err));
    exit (1);
  }
 
  /* Init hwparams with full configuration space */
  if ((err = snd_pcm_hw_params_any (capture_handle, hw_params)) < 0) {
    fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err));
    exit (1);
  }
 
  /* Set access type. */
  if ((err = snd_pcm_hw_params_set_access (capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
    fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err));
    exit (1);
  }
 
  /* Set sample format */
  if ((err = snd_pcm_hw_params_set_format (capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
    fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err));
    exit (1);
  }
 
  /* Set sample rate. If the exact rate is not supported by the hardware, use nearest possible rate. */
  exact_rate = rate;
  if ((err = snd_pcm_hw_params_set_rate_near (capture_handle, hw_params, &exact_rate, 0)) < 0) {
    fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err));
    exit (1);
  }
 
  if (rate != exact_rate) {
    fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n ==> Using %d Hz instead.\n", rate, exact_rate);
  }
 
  /* Set number of channels */
  if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, 2)) < 0) {
    fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err));
    exit (1);
  }
 
  /* Apply HW parameter settings to PCM device and prepare device. */
  if ((err = snd_pcm_hw_params (capture_handle, hw_params)) < 0) {
    fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err));
    exit (1);
  }
 
  snd_pcm_hw_params_free (hw_params);
 
  if ((err = snd_pcm_prepare (capture_handle)) < 0) {
    fprintf (stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror (err));
    exit (1);
  }
 
  /* Read data into the buffer. */
  if ((err = snd_pcm_readi (capture_handle, buf, 128)) != 128) {
    fprintf (stderr, "read from audio interface failed (%s)\n", snd_strerror (err));
    exit (1);
  } else {
    fprintf (stdout, "snd_pcm_readi successful\n");
  }
 
  snd_pcm_close (capture_handle);
 
  exit (0);
}

Archived

Sitara Linux SDK 08.00.00.00