|
|
|
Title: Unofficial ALSA API documentation part 1
|
|
|
|
Author: Alessandro Mauri
|
|
|
|
|
|
|
|
# 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](https://www.alsa-project.org/alsa-doc/alsa-lib/index.html)
|
|
|
|
- The [PCM interface](https://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html)
|
|
|
|
- [PCM plugins](https://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html)
|
|
|
|
* [PCM external plugins SDK](https://www.alsa-project.org/alsa-doc/alsa-lib/pcm_external_plugins.html)
|
|
|
|
- [Device names](https://alsa-project.org/wiki/DeviceNames)
|
|
|
|
- [Resampler](https://alsa-project.org/wiki/ALSAresampler)
|
|
|
|
- [Frames periods](https://alsa-project.org/wiki/FramesPeriods)
|
|
|
|
- [Topology](https://alsa-project.org/wiki/ALSA_topology)
|
|
|
|
|
|
|
|
Unofficial links:
|
|
|
|
|
|
|
|
* [Userspace documentation](https://alsa.opensrc.org/)
|
|
|
|
* [A close look at ALSA](https://www.volkerschatz.com/noise/alsa.html)
|
|
|
|
* ST's (the microchip company) [general look at ALSA](https://wiki.st.com/stm32mpu/wiki/ALSA_overview)
|
|
|
|
|
|
|
|
## Functions
|
|
|
|
All the functions I encountered and some documentation / explanation
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
### Interfaces
|
|
|
|
[//]: # (TODO explain what interfaces are)
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
### 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:
|
|
|
|
|
|
|
|
```c
|
|
|
|
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
|
|
|
|
|
|
|
|
```c
|
|
|
|
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 to `snd_device_name_get_hint()` to extract usable values.
|
|
|
|
When no longer needed, hints should be passed to `snd_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
|
|
|
|
|
|
|
|
```c
|
|
|
|
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 referencing
|
|
|
|
`hints[i]` from `snd_device_name_hint()`, where `i` 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
|
|
|
|
|
|
|
|
```c
|
|
|
|
int snd_device_name_free_hint (void **hints);
|
|
|
|
```
|
|
|
|
|
|
|
|
ARGS:
|
|
|
|
|
|
|
|
* `(void **)hints`: hints array gotten from `snd_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
|
|
|
|
|
|
|
|
```c
|
|
|
|
#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 (SND_PCM_NONBLOCK)**: 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 (SND_PCM_ASYNC)**:
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
```c
|
|
|
|
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`
|
|
|
|
|
|
|
|
```c
|
|
|
|
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
|
|
|
|
- SND_PCM_NONBLOCK: non blocking mode
|
|
|
|
- SND_PCM_ASYNC: async notification mode
|
|
|
|
|
|
|
|
RESULT:
|
|
|
|
|
|
|
|
* `(snd_pcm_t)pcmp`: PCM handle, link to the PCM's defining struct. After use
|
|
|
|
the handle should be passed to `snd_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
|
|
|
|
|
|
|
|
```c
|
|
|
|
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
|
|
|
|
|
|
|
|
```c
|
|
|
|
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
|