Handle PCM devices

The software processes for playing back and capturing audio data are similar. This section describes the common steps.

Open the PCM device

The first thing you need to do in order to playback or capture sound is open a connection to a PCM playback or capture device.

The API calls for opening a PCM device are:

Use this call when you want to open a specific hardware device, and you know its name.
Use this call when you want to open a specific hardware device, and you know its card and device number.
Use this call to open the user's preferred device.

Using this function makes your application more flexible, because you don't need to know the card and device numbers; the function can pass back to you the card and device that it opened.

All these API calls set a PCM connection handle that you'll use as an argument to all other PCM API calls. This handle is very analogous to a file stream handle. It's a pointer to a snd_pcm_t structure, which is an opaque data type.

These functions, like others in the QSA API, work for both capture and playback channels. They take as an argument a channel direction, which is one of:


This code fragment uses these functions to open a playback device:

if (card == -1)
    if ((rtn = snd_pcm_open_preferred (&pcm_handle,
                  &card, &dev,
                  SND_PCM_OPEN_PLAYBACK)) < 0)
        return err ("device open");
    if ((rtn = snd_pcm_open (&pcm_handle, card, dev,
                  SND_PCM_OPEN_PLAYBACK)) < 0)
        return err ("device open");

If the user specifies a card and a device number on the command line, this code opens a connection to that specific PCM playback device. If the user doesn't specify a card, the code creates a connection to the preferred PCM playback device, and snd_pcm_open_preferred() stores the card and device numbers in the given variables.

Configure the PCM device

The next step in playing back or capturing the sound stream is to inform the device of the format of the data that you're about to send it or want to receive from it.

You can do this by filling in a snd_pcm_channel_params_t structure, and then calling snd_pcm_channel_params() or snd_pcm_plugin_params(). The difference between the functions is that the second one uses the plugin converters (see PCM plugin converters in the Audio Architecture chapter) if required.

If the device can't support the data parameters you're setting, or if all the subchannels of the device are currently in use, both of these functions fail.

The API calls for determining the current capabilities of a PCM device are:

Use the plugin converters. If the hardware has a free subchannel, the capabilities returned are extensive because the plugin converters make any necessary conversion.
Access the hardware directly. This function returns only what the hardware capabilities are.

Both of these functions take as an argument a pointer to a snd_pcm_channel_info_t structure. You must set the channel member of this structure to the desired direction (SND_PCM_CHANNEL_CAPTURE or SND_PCM_CHANNEL_PLAYBACK) before calling the functions. The functions fill in the other members of the structure.

It's the act of configuring the channel that allocates a subchannel to the client. Stated another way, hundreds of clients can open a handle to a PCM device with only one subchannel, but only one can configure it. After a client allocates a subchannel, it isn't returned to the free pool until the handle is closed. One result of this mechanism is that, from moment to moment, the capabilities of a PCM device change as other applications allocate and free subchannels. Additionally the act of configuring / allocating a subchannel changes its state from SND_PCM_STATUS_NOTREADY to SND_PCM_STATUS_READY.

If the API call succeeds, all parameters specified are accepted and are guaranteed to be in effect, except for the frag_size parameter, which is only a suggestion to the hardware. The hardware may adjust the fragment size, based on hardware requirements. For example, if the hardware can't deal with fragments crossing 64-kilobyte boundaries, and the suggested frag_size is 60 kilobytes, the driver will probably adjust it to 64 kilobytes.

Another aspect of configuration is determining how big to make the hardware buffer. This determines how much latency that the application has when sending data to the driver or reading data from it. The hardware buffer size is determined by multiplying the frag_size by the max_frags parameter, so for the application to know the buffer size, it must determine the actual frag_size that the driver is using.

You can do this by calling snd_pcm_channel_setup() or snd_pcm_plugin_setup(), depending on whether or not your application is using the plugin converters. Both of these functions take as an argument a pointer to a snd_pcm_channel_setup_t structure that they fill with information about how the channel is configured, including the true frag_size.

Control voice conversion

The libasound library supports devices with up to 8 voices.

Configuration of the libasound library is based on the maximum number of voices supported in hardware. If the numbers of source and destination voices are different, then snd_pcm_plugin_params() instantiates a voice converter.

The default voice conversion behavior is as follows:

From To Conversion
Mono Stereo Replicate channel 1 (left) to channel 2 (right)
Stereo Mono Remove channel 2 (right)
Mono 4-channel Replicate channel 1 to all other channels
Stereo 4-channel Replicate channel 1 (front left) to channel 3 (rear left), and channel 2 (front right) to channel 4 (rear right)

Previous versions of libasound converted stereo to mono by averaging the left and right channels to generate the mono stream. Now by default, the right channel is simply dropped.

You can use the voice conversion API to configure the conversion behavior and place any source channel in any destination channel slot:

Get the current voice conversion structure for a channel
Set the current voice conversion structure for a channel

The actual conversion is controlled by the snd_pcm_voice_conversion_t structure, which is defined as follows:

typedef struct snd_pcm_voice_conversion
   uint32_t     app_voices;
   uint32_t     hw_voices;
   uint32_t     matrix[32];
} snd_pcm_voice_conversion_t

The matrix member forms a 32-by-32-bit array that specifies how to convert the voices. The array is ranked with rows representing application voices, voice 0 first; the columns represent hardware voices, with the low voice being LSB-aligned and increasing right to left.

For example, consider a mono application stream directed to a 4-voice hardware device. A bit array of:

matrix[0] = 0x1;  //  00000001

causes the sound to be output on only the first hardware channel. A bit array of:

matrix[0] = 0x9;   // 00001001

causes the sound to appear on the first and last hardware channel.

Another example would be a stereo application stream to a 6 channel (5.1) output device. A bit array of:

matrix[0] = 0x1;  //  00000001
matrix[1] = 0x2;  //  00000010

causes the sound to appear on only the front two channels, while:

matrix[0] = 0x5;  //  00000101
matrix[1] = 0x2;  //  00000010

causes the stream signal to appear on the first four channels (likely the front and rear pairs, but not on the center or LFE channels). The bitmap used to describe the hardware (i.e. the columns) depends on the hardware, and you need to be mindful of the actual hardware you'll be running on to properly map the channels. For example:

  • If the hardware orders the channels such that the center channel is the third channel, then bit 2 represents the center.
  • If the hardware orders the channels such that the Rear Left is the third channel, then bit 2 represents the Rear Left.

If the number of source voices matches the number of destination voices, the converter isn't invoked, so you won't be able to reroute the channels. If you're playing a stereo file on stereo hardware, you can't use the voice matrix to swap the channels because the voice converter isn't used in this case.

If you call snd_pcm_plugin_get_voice_conversion() or snd_pcm_plugin_set_voice_conversion() before the voice conversion plugin has been instantiated, the functions fail and return -ENOENT.

Prepare the PCM subchannel

The next step in playing back or capturing the sound stream is to prepare the allocated subchannel to run.

Call one of the following functions to prepare the allocated subchannel:

This step and the SND_PCM_STATUS_PREPARED state may seem unnecessary, but they're required to correctly handle underrun conditions when playing back, and overrun conditions when capturing. For more information, see If the PCM subchannel stops during playback and If the PCM subchannel stops during capture, later in this chapter.

Close the PCM subchannel

When you've finished playing back or capturing audio data, you can close the subchannel by calling snd_pcm_close().

The call to snd_pcm_close() releases the subchannel and closes the handle.

Last modified: 2014-05-14

Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus