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

相关推荐

5月15日 华勤  测试工程师(实习生)一面(视频面试)老哥们,可以看下最后一个自我不足,有没有好的解决方法,可以在评论区留言。谢谢大家这个面试是,最近面试聊得最久的面试,也学到了很多东西,也是很感谢陪我聊天面试官。面试提问:一、请用英语自我介绍一下(我要求的,笑哭了,因为他上午问了我英语口语水平)二、用英语介绍一下香港这个城市三、之前了解过我们公司吗?四、你是哪里人,为什么不投西安和成都的分公司五、说一下你未来的规划(这里就进入面试官的陷阱了)六、看了你的未来规划,你是更偏向研发的为什么会投测试岗位呢?(我就和他说我只想找一份暑期时期,来让我的简历上一个台阶)七、面试官给我讲了一下公司的实习生计划(其实是在劝退我的)反问:一、实习生计划的淘汰率是多少(50%)二、公司里面嵌入式开发人员要求软硬都会吗?(分开的)三、为什么不愿意只招暑期实习生而是招求转正的(暑期实习没有任何意义,不能带来大的产出)四、正式招聘招双非的学生吗?(招,按照一定比例,一本、211、985,二本除非技术非常好)五、面试的时候忘记问为什么要求英语口语,后面在BOSS上追问了(后续会对接美国客户)自我不足:一、不是外国人,不要说自己英语好。哈哈,开个玩笑,其实我的英语自我介绍还不错。自我介绍结束之后,他上来就是一句please you introduce hong kong this city.我楞一下,然后开始介绍,发现脑子一片混乱,没有词语啊,然后我说please give me some seconds,I need to think about it。然后想了半天,还是没有词汇蹦出来,然后就瞎说了一些有很多美食,风景,外国人很多。下来之后,我发现其实不能这么回答,如果他只是想测试我的英语口语水平,我直接换个话题。(sir,if you just want to test how my Spoken English level is,maybe we can talk about my hometown,because I am not familiar with hongkong)这样既可以体现你的英语能力,又达到了面试官的目的。这点非常重要,当面试官问到你不熟悉的领域,你一定要把话题往你会的话题上扯,但是要有一定的相关度,这样才不会让你陷入自证陷阱。(老哥们遇在英语口语测试时,到的奇葩问题可以留在评论区。)二、一定要端正求职态度,为什么说这一点,因为几乎每家公司,都会问了解我们公司,为什么会投递我们公司,这其实是HR在看你的态度,你是真的想找一份工作,老老实实的干下去,还只是说,只是随便投递的,刚好有面试,就来了。一定不要有这种想法,所以在面试之前最后去网上了解一下关于该公司的业务,然后在面试的时候猛吹一波,让面试官相信你是真的想做这份工作。你自己的心里,也一定要认可我真的想成为这个公司的一员,这样才不会露馅。三、不用把项目介绍的很详细,只是笼统说一下完成了什么功能,为面试官留下提问空间(今天自我介绍的时候,这点做的很不错,但是面试官没问)四、当面试官问你的未来规划,你要往你是真的认可这份工作,想要扎根做下去,不要把自己的真实想法透露出来(我的错误回答:我出来工作,看一下行业发展水平,然后有其他打算)。(其实我还没有这个问题的完美的回答方案,有想法的老哥可以把应对方法在评论区留言。)
投递华勤技术等公司8个岗位 面试时最害怕被问到的问题 新手牛友村
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务