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

相关推荐

一·语言方面:由于很多大数据框架都是基于JAVA开发的,个人认为JAVA语言是必不可少的,有余力的话可以去掌握以下PYTHON😊二·数据结构与算法:大二课上没好好学,现在只能再补一遍了,悲😣打算刷完Leetcode的Hot100就先放一边,等秋招再刷别的题😇三·MYSQL:一个关系型数据库管理系统,SQL BOY的开始,初步领略通过建表来管理数据的思想,学完可以去刷一下基础50题和进阶50题(虽然本菜鸡只刷了基础50😓)四·Linux:会安装虚拟机,掌握常见linux常见命令即可五·Hadoop:一个分布式计算框架。所谓分布式,就是将一台电脑无法处理的数据量分散给一个由多台电脑组成的计算机集群来处理,互相分工,彼此合作,形成一个有机整体。框架分为三部分:HDFS(数据存储),MapReduce(数据计算),Yarn(资源调度)。虽然MapReduce已经逐渐被Spark淘汰,但思想依然值得借鉴。高可用机制也应当学习六·Maven:会用即可,打包项目用的七·Hive:也是一种SQL,有了MYSQL的基础应该还算好理解八·Scala和Spark:Spark是用Scala写的,在学习之前得先浏览一遍基本语法。Spark是基于内存的批式计算引擎,已经逐渐取代MapReduce(基于磁盘的计算引擎),APACHE也基本上停止了对MapReduce的更新(经典白学😃)。将来很多离线数仓项目也都是基于SPARK的,可以说非常重要了九·Kafka,Hbase,Flume和ZooKeeper:都是一些小组件,将来面试可能会问到十·Flink:一个流式实时计算引擎,本人JAVA基础较弱,听的时候也是懵懵懂懂当然,仅仅这些肯定远远不够,本人将来还要学Doris,数仓建模理论,数仓项目,八股面经等等。上述理解如有错误还请友好指正(叠甲叠甲叠甲😱)。这些理论也只是匆匆过了一遍(大一太贪玩,大二2月份才开始学),发布笔记也只是记录自己的学习过程,提醒自己不要懈怠(还有当个免费网盘😏)。如果能帮助到你就更好了,虽然我也比较迷茫😖
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务