ALSA(Advanced Linux Sound Architecture)是Linux操作系统上用于提供音频和MIDI功能的软件架构。它为Linux系统提供了强大的音频支持,包括音频录制、播放和处理,它设计用于提供高性能、低延迟、高质量的音频处理,并为开发者提供了一组API和工具。
主要框架
ALSA的涉及本身比较复杂,如果不是特别底层的驱动开发,一般我们只需关注alsa在应用层给我们提供的接口即可。
驱动层(Kernel Level): ALSA的核心部分包含在Linux内核中,提供了音频设备的驱动程序。这些驱动程序用于与硬件通信,实现音频数据的输入和输出。ALSA的内核组件主要包括snd内核模块。音频设备底层驱动程序使用 DMA 来搬运数据,每当 DMA 搬运完一个period 的数据就会触发一次中断,
用户空间库和工具: ALSA提供了一系列用户空间的库和工具,用于与内核层进行交互。其中最重要的是libasound(也就是下文提到的alsa-lib库),它包含用于音频处理的API。同时,还有一些命令行工具,如alsamixer、aplay和arecord等,用于配置和测试音频设备。
中间件层(Middleware Level): ALSA可以与其他音频中间件集成,如PulseAudio和JACK。这些中间件提供了更高级别的音频处理和路由功能。用户可以选择在ALSA之上使用这些中间件,以满足特定的应用场景需求。
PCM接口: ALSA的主要数据传输接口是PCM(Pulse Code Modulation)接口。PCM是一种数字音频表示方法,它将模拟音频信号转换为数字形式。ALSA通过PCM接口支持多种音频格式和配置,包括采样率、声道数、位深度等。
回调机制: ALSA采用回调机制,允许应用程序注册回调函数以处理音频数据。这种机制使得应用程序能够以异步方式与音频设备进行交互,提高了性能和灵活性。
alsa-lib (libasound)
alsa-lib 是一套 Linux 应用层的 C 语言函数库,只需调用这一套 API 即可完成对底层声卡设备的操控,譬如播放与录音。在ALSA project the Clib reference 这些模块接口。
PCM Interface模块
提供了 PCM 设备相关的操作接口,譬如打开/关闭 PCM 设备、配置 PCM 设备硬件或软件参数、控制 PCM 设备(启动、暂停、恢复、写入/读取数据),该模块下还包含了一些子模块。
这里解释下,PCM(脉冲编码调制)设备指的是能够接受和发送PCM音频数据的硬件设备。PCM是一种将模拟音频信号转换为数字形式的编码方式,它以脉冲的形式表示音频信号的振幅值。
Error Interface
该模块提供了关于错误处理相关的接口,譬如函数调用发生错误时,可调用该模块下提供的函数打印错
误描述信息。
Mixer Interface
提供了关于混音器相关的一系列操作接口,譬如音量、声道控制、增益等等。
在 Linux 内核设备驱动层、基于 ALSA 音频驱动框架注册的 sound 设备会在/dev/snd 目录下生成相应的设备节点文件。/dev/snd 目录下有文件。这些文件对应播放、录音、声卡控制的设备节点。
在 Linux 系统的/proc/asound 目录下,有很多的文件,这些文件记录了系统中声卡相关的信息。
通过"cat /proc/asound/cards"命令、查看 cards 文件的内容,可列出系统中可用的、注册的声卡
通过cat /proc/asound/devices 列出系统中所有声卡注册的设备,包括 control、pcm、timer、seq 等等。
通过cat /proc/asound/pcm 列出系统中的所有 PCM 设备,包括 playback 和 capture:
alsa-lib 移植注意事项
移植 alsa-lib 库之外,通常还需要移植 alsa-utils,alsa-utils 库,这些包含了一些用于测试、配置声卡的工具。
alsa-utils 提供了一些用于测试、配置声卡的工具,譬如 aplay、arecord、alsactl、alsaloop、alsamixer、amixer 等。
aplay:是一个用于测试音频播放功能程序,可以直接使用 aplay 播放 wav 格式的音频文件,不支持MP3格式的。
alsamixer 是一个用于配置声卡的混音器,它是一个字符图形化的配置工具,直接在开发板串口终端运行 alsamixer 命令,就可以打开图形化配置界面。
alsactl,将声卡配置保存在文件中,防止重启丢失配置项,使用这个命令 alsactl -f /var/lib/alsa/asound.state store
amixer 工具也是一个声卡配置工具,与 alsamixer 功能相同,区别在于 amixer 不是图形化配置工具,直接使用命令行配置
譬如将耳机音量左右声道都设置为 100,可执行如下命令进行设置:
amixer sset Headphone 100,100
譬如打开或关闭 Headphone Playback ZC:
amixer sset "Headphone Playback ZC" off #关闭 ZC
amixer sset "Headphone Playback ZC" on #打开 ZC
arecord 工具是一个用于录音测试的应用程序,
譬如使用 arecord 录制一段 10 秒钟的音频 arecord -f cd -d 10 test.wav
alsa-lib实现录音和播放的简单demo
c
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#define SAMPLE_RATE 44100
#define NUM_CHANNELS 2
#define SAMPLE_SIZE 16
#define BUFFER_SIZE 1024
void capture_and_playback() {
int err;
snd_pcm_t *capture_handle, *playback_handle;
snd_pcm_hw_params_t *hw_params;
char buffer[BUFFER_SIZE];
// 打开PCM录音设备
if ((err = snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0) {
fprintf(stderr, "Cannot open capture audio device (%s)\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
// 打开播放设备
if ((err = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf(stderr, "Cannot open playback audio device (%s)\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
// 配置录音和播放参数
//实例化hwparams对象
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
fprintf(stderr, "Cannot allocate hardware parameter structure (%s)\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
//获取PCM设备当前硬件配置,对hwparams进行初始化
if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0 || (err = snd_pcm_hw_params_any(playback_handle, hw_params)) < 0) {
fprintf(stderr, "Cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
//设置访问类型
if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0 ||
(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(EXIT_FAILURE);
}
//设置数据格式
if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0 ||
(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(EXIT_FAILURE);
}
//设置采样率
if ((err = snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &SAMPLE_RATE, 0)) < 0 ||
(err = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &SAMPLE_RATE, 0)) < 0) {
fprintf(stderr, "Cannot set sample rate (%s)\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
//设置声道数量
if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, NUM_CHANNELS)) < 0 ||
(err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, NUM_CHANNELS)) < 0) {
fprintf(stderr, "Cannot set channel count (%s)\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
//使配置生效
if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0 || (err = snd_pcm_hw_params(playback_handle, hw_params)) < 0) {
fprintf(stderr, "Cannot set parameters (%s)\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
//释放内存
snd_pcm_hw_params_free(hw_params);
// 开始录音和播放
while (1) {
// 录音
if ((err = snd_pcm_readi(capture_handle, buffer, BUFFER_SIZE)) != BUFFER_SIZE) {
fprintf(stderr, "Read from capture device failed (%s)\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
// 处理音频数据(在这里可以对音频数据进行处理)
// 播放
if ((err = snd_pcm_writei(playback_handle, buffer, BUFFER_SIZE)) != BUFFER_SIZE) {
fprintf(stderr, "Write to playback device failed (%s)\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
}
// 关闭设备
snd_pcm_close(capture_handle);
snd_pcm_close(playback_handle);
}
int main() {
capture_and_playback();
return 0;
}
参考:部分内容参考了《正点原子I.MX6U嵌入式 Linux C 应用编程指南》相关内容。旨在学习交流,如有侵权,请及时联系我删除