嵌入式音视频必备知识-基于ALSA实现录制和播放
视频讲解(代码领取见视频):ALSA录制和播放分享-嵌入式音视频必备-详解周期和缓冲区的知识
这个要配合代码来理解,本文不包括alsa录制和播放的封装。
1. ALSA 架构概述
ALSA 是 Linux 系统上的音频架构,它提供了以下主要功能:
1. 音频设备驱动
2. 音频设备管理
3. 音频数据采集和播放
4. 混音器控制
5. MIDI 支持
1.1 系统架构图
+------------------------+ | 应用程序层 | +------------------------+ | ALSA 库 (libasound) | +------------------------+ | ALSA 核心 | +------------------------+ | 设备驱动层 | +------------------------+ | 硬件设备 | +------------------------+
2. ALSA 主要组件
2.1 PCM 设备
用于数字音频数据的采集和播放
支持多种采样格式和参数
主要接口:
snd_pcm_open() // 打开PCM设备 snd_pcm_hw_params() // 设置硬件参数 snd_pcm_writei() // 写入数据(播放) snd_pcm_readi() // 读取数据(采集)
2.2 混音器
控制音频设备的音量、静音等
主要接口:
snd_mixer_open() // 打开混音器 snd_mixer_attach() // 附加到声卡 snd_mixer_load() // 加载混音器 snd_mixer_selem_set_playback_volume_all() // 设置音量
3. 音频参数说明
3.1 基本参数
- 采样率(Sample Rate):如 44.1kHz, 48kHz
- 通道数(Channels):单声道、立体声等
- 采样格式(Format):S16_LE, S24_LE 等
- 访问类型(Access):RW_INTERLEAVED, RW_NONINTERLEAVED
3.2 缓冲区参数
- 周期大小(Period Size)
- 缓冲区大小(Buffer Size)
- 周期数(Periods)
在第四章节扩展讲解
4 周期/缓冲区进阶
4.1 核心概念区分
注意:这里的帧和我们平时学的:1024个样本作为一帧编码单元 不是一个概念。
4.2 周期的作用
硬件中断触发:
- 每当硬件处理完一个周期的数据,会触发中断通知应用填充下一个周期。
延迟与吞吐量平衡:
- 周期越小,延迟越低(中断更频繁),但CPU占用更高。
- 周期越大,延迟越高,但减少中断开销(适合高吞吐场景)。
4.3 ALSA 数据流示意图
+-------------------+ +-------------------+ +-------------------+ | 应用程序缓冲区 | | ALSA 环形缓冲区 | | 硬件 FIFO | | (User Space) | ----> | (Kernel Space) | ----> | (声卡/DMA) | +-------------------+ +-------------------+ +-------------------+ ^ | 硬件中断 | (每完成一个 Period) +---------------+ | 周期触发 | +---------------+
4.3.1 关键组件说明
4.3.2 周期与缓冲区的具体示例
假设配置如下:
- 缓冲区大小 (Buffer Size) = 4 个周期 = 4096 帧
- 周期大小 (Period Size) = 1024 帧
- 音频格式:立体声(2 声道),16 位样本 = 4 字节/帧
数据流步骤:
1. 应用写入数据
- 每次调用 snd_pcm_writei() 写入 1024 帧 (即 1 个周期)。
- 写入 4 次后,填满整个缓冲区( 4096 帧 )。
2. 硬件处理数据
- 声卡每处理完 1024 帧 (1 个周期),触发中断通知内核。
- 内核从环形缓冲区释放已处理的周期,应用可继续填充新数据。
3. 中断触发时机
- 当硬件处理完 Period 1 后,触发中断,应用收到通知后可填充 Period 3 (环形缓冲区复用)。
- 保证始终有数据可处理,避免欠载(Playback)或超载(Capture)。
4.3.3 图示时序
时间轴:|----- Period 1 -----|----- Period 2 -----|----- Period 3 -----|----- Period 4 -----| 操作: [App 写入 Period 1] [HW 处理 Period 1] [App 写入 Period 2] [HW 处理 Period 2] ... 缓冲区状态: +---------------------------------------------------------------+ | Period 1 | Period 2 | Period 3 | Period 4 | (环形复用) | +---------------------------------------------------------------+
4.4 数据读写的最小单位
实际最小单位是帧(Frame),但推荐按周期对齐读写。
ALSA 的 snd_pcm_readi() / snd_pcm_writei() 理论上可以读写任意帧数,但:
- 低于周期大小:可能导致未完成周期,引发欠载(Playback)或超载(Capture)。
- 非整数倍周期:可能被驱动舍入,降低效率。
4.5 如何设置周期和缓冲区?
4.5.1 实际应用示例
低延迟配置
// 44.1kHz采样率 unsigned int rate = 44100; snd_pcm_uframes_t buffer_size = rate * 0.1; // 100ms缓冲 snd_pcm_uframes_t period_size = buffer_size / 2; // 2个周期 int periods = 2; // 设置参数 snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size); snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, 0); snd_pcm_hw_params_set_periods(handle, params, periods, 0);
高稳定性配置
// 44.1kHz采样率 unsigned int rate = 44100; snd_pcm_uframes_t buffer_size = rate * 0.5; // 500ms缓冲 snd_pcm_uframes_t period_size = buffer_size / 8; // 8个周期 int periods = 8; // 设置参数 snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size); snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, 0); snd_pcm_hw_params_set_periods(handle, params, periods, 0);
4.5.2 通过命令行工具查询
# 查看当前设备的周期和缓冲区配置 cat /proc/asound/card0/pcm0c/sub0/hw_params
输出示例:
access: RW_INTERLEAVED
format: S16_LE
subformat: STD
channels: 2
rate: 44100 (1572864000/35666)
period_size: 1102
buffer_size: 4410
4.5.3 周期大小的选择原则
4.5 常见问题
Q1: 能否读写小于周期大小的数据?
可以但不推荐:ALSA 允许,但可能因未填满周期导致硬件等待,增加延迟或错误风险。
Q2: 周期和缓冲区大小如何影响性能?
Q3: 如何查询当前周期大小?
snd_pcm_uframes_t period_size; snd_pcm_hw_params_get_period_size(params, &period_size, NULL); printf("Period Size: %lu frames\n", period_size);
Q4: 周期是否可以动态调整?
不可以:周期和缓冲区大小需在初始化时设置,运行时不可更改。
Q5: 为什么实际延迟比理论值高?
硬件处理、中断延迟、调度策略等均会增加实际延迟。可通过工具测量:
sudo apt install linux-tools-common sudo latencytop
5 调试工具
5.1 命令行工具
用来测试系统本身是否可以正常录音和播放:
# 列出音频设备 aplay -l arecord -l # 测试播放 aplay test.wav # 测试录音 arecord test.wav # 查看混音器设置 amixer
测试录制的pcm数据
aplay -f S16_LE -r 44100 -c 2 recording.pcm#嵌入式##校招##C++##项目##如果可以选,你最想从事什么工作#