Unofficial ALSA API documentation
ALSA has to be the worst documented API in the whole FOSS world, so since I had to go trough the pain of reverse-engineering other programs I thought to save you form the same pain. So I present to you the most half-hassed description of the ALSA API on the interweb.
Official links:
- ALSA documentation main page
Unofficial links:
- Userspace documentation
- A close look at ALSA
- ST’s (the microchip company) general look at ALSA
Functions
All the functions I encountered and some documentation / explanation
Interfaces
Streams
In ALSA a stream is the collection of audio data flowing from an application to a card (playback), or from a card to an application (capture). ALSA uses the ring buffer to store outgoing (playback) and incoming (capture, record) samples. There are two pointers being maintained to allow a precise communication between application and device; pointing to current processed sample by hardware and last processed sample by application. The modern audio chips allow to program the transfer time periods. It means that the stream of samples is divided to small chunks. Device acknowledges to application when the transfer of a chunk is complete.
The type of stream (or the direction in which sound is flowing) can be either
capture or playback and is determined by snd_pcm_stream_t
which is defined as:
typedef enum _snd_pcm_stream {
SND_PCM_STREAM_PLAYBACK = 0, // Playback stream
SND_PCM_STREAM_CAPTURE, // Capture stream
SND_PCM_STREAM_LAST = SND_PCM_STREAM_CAPTURE
} snd_pcm_stream_t;
Hints
Hints are names, descriptions and other information about sound cards, interfaces and others. To get access to those hints there is a family of functions.
snd_device_name_hint
Get hints from a specified card of specified interface
int snd_device_name_hint (int card, const char *iface, void ***hints);
ARGS:
(int)card
: specifies the card index number, -1 means all cards(char *)iface
: interface identification (like “pcm”, “rawmidi”, “timer”, “seq”)
RESULT:
(void ***)hints
: hints will receive a NULL-terminated array of device name hints, which can be passed tosnd_device_name_get_hint()
to extract usable values. When no longer needed, hints should be passed tosnd_device_name_free_hint()
to release resources.
RETURN: (int)
0 on success or negative error code
snd_device_name_get_hint
Extract selected hint form hints array
char* snd_device_name_get_hint (const void *hint, const char *id);
ARGS:
(const void *)hint
: a pointer to a hint which can be gotten by referencinghints[i]
fromsnd_device_name_hint()
, wherei
is the card index(const char *)id
: hint value to extract, valid IDs are- NAME: name of device
- DESC: description of device
- IOID: input / output identification (“Input” or “Output”), NULL means both
RESULT: RETURN: (char *)
gives a pointer to a string containing the requested
hint or NULL on error, the result should be freed when done.
snd_device_name_free_hint
Free hints array
int snd_device_name_free_hint (void **hints);
ARGS:
(void **)hints
: hints array gotten fromsnd_device_name_hint
RETURN: (int)
0 on success or negative code on error
Example
Get hints from all pcms of all cards and print them out
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
int main (int argc, char *argv[])
{
void **hints, **n;
char *name, *descr, *descr1, *io;
if (snd_device_name_hint(-1, "pcm", &hints) < 0)
return -1;
n = hints;
while (*n != NULL) {
name = snd_device_name_get_hint(*n, "NAME");
descr = snd_device_name_get_hint(*n, "DESC");
io = snd_device_name_get_hint(*n, "IOID");
printf("%s\n", name);
if ((descr1 = descr) != NULL) {
printf(" ");
while (*descr1) {
if (*descr1 == '\n')
printf("\n ");
else
putchar(*descr1);
descr1++;
}
putchar('\n');
}
if (name != NULL)
free(name);
if (descr != NULL)
free(descr);
if (io != NULL)
free(io);
n++;
}
snd_device_name_free_hint(hints);
return 0;
}
PCMs
PCM is an abbreviation for “Pulse Code Modulation”. PCM is how digital audio is typically represented in a computer. The audio signal is represented by samples of its instantaneous amplitude taken at regular intervals the representation of each sample can take several forms.
In ALSA we usually use the term PCM to refer to a PCM device, a PCM device is something like an abstract sound card. It can either be a hardware device or a PCM plugin layer device (like dmix, softvol, etc).
Open modes
Blocking mode (0): when opening a PCM in blocked (which is the default mode) the behavior is that if the resources are already in use with another application, then it blocks the caller, until resources are free.
Non blocking mode (SNDPCMNONBLOCK): the non-blocked mode doesn’t block the
caller in any way and instead returns -EBUSY
(that is EBUSY * -1
) error when
the resources are not available.
Asynchronous mode (SNDPCMASYNC):
The opening modes affect all the standard I/O operations (such as writing), in
the same way the options operate with open(2)
and write(2)
, so instead of
making the calling process wait when the resource is not available, operations
return -EAGAIN /* EAGAIN * -1 */
, which literally means resource temporarily
unavailable. The operation mode for successive (to opening) I/O calls can be
changed with the snd_pcm_nonblock()
function.
In ALSA PCM devices are controlled trough handles, which are basically pointers
to a data structure snd_pcm_t
that defines it
typedef struct _snd_pcm {
char *name;
snd_pcm_type_t type;
int stream;
int mode;
int poll_fd;
int setup;
unsigned int access; /* access mode */
unsigned int format; /* SND_PCM_FORMAT_* */
unsigned int subformat; /* subformat */
unsigned int rate; /* rate in Hz */
unsigned int channels; /* channels */
size_t fragment_size; /* fragment size */
unsigned int fragments; /* fragments */
unsigned int start_mode; /* start mode */
unsigned int ready_mode; /* ready detection mode */
unsigned int xrun_mode; /* xrun detection mode */
size_t avail_min; /* min avail frames for wakeup */
size_t xfer_min; /* xfer min size */
size_t xfer_align; /* xfer size need to be a multiple */
unsigned int time: 1; /* timestamp switch */
size_t boundary; /* pointers wrap point */
unsigned int info; /* Info for returned setup */
unsigned int msbits; /* used most significant bits */
unsigned int rate_master; /* Exact rate is rate_master / */
unsigned int rate_divisor; /* rate_divisor */
size_t fifo_size; /* chip FIFO size in frames */
size_t buffer_size;
size_t bits_per_sample;
size_t bits_per_frame;
size_t *appl_ptr;
volatile size_t *hw_ptr;
int mmap_rw;
snd_pcm_channel_info_t *mmap_channels;
snd_pcm_channel_area_t *running_areas;
snd_pcm_channel_area_t *stopped_areas;
void *stopped;
snd_pcm_ops_t *ops;
snd_pcm_fast_ops_t *fast_ops;
snd_pcm_t *op_arg;
snd_pcm_t *fast_op_arg;
void *private;
} snd_pcm_t;
That’s a lot of stuff but luckily we have functions to operate on this struct.
snd_pcm_open
Open a pcm returning it’s handle that is pointer to it’s defining data structure,
snd_pcm_t
int snd_pcm_open (snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode);
ARGS:
(const char)name
: PCM name identifier(snd_pcm_stream_t)stream
: wanted stream(int)mode
: mode of opening, valid modes are- 0: blocking mode
- SNDPCMNONBLOCK: non blocking mode
- SNDPCMASYNC: async notification mode
RESULT:
(snd_pcm_t)pcmp
: PCM handle, link to the PCM’s defining struct. After use the handle should be passed tosnd_pcm_close()
to close and free the resources.
RETURN: (int)
0 on success or negative code on error
snd_pcm_close
Close a PCM handle freeing the allocated resources
int snd_pcm_close (snd_pcm_t *pcm);
ARGS:
(snd_pcm_t *)pcm
: the PCM handle to close
RESULT: RETURN: (int)
0 on success or negative code on error
snd_pcm_nonblock
Change the non-blocking mode on opened PCMs
int snd_pcm_nonblock (snd_pcm_t *pcm, int nonblock);
ARGS:
(snd_pcm_t *)pcm
: the PCM handle to modify(int)nonblock
: nonblock mode- 0: block
- 1: nonblock
- 2: abort
RESULT: RETURN: (int)
0 on success or negative code on error