嵌入式音视频必备知识-基于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++##项目##如果可以选,你最想从事什么工作#
全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务