You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
alemauri.eu/src/alsa/part1.md

298 lines
9.1 KiB

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