002_音频开发_基础篇_Linux音频架构简介
文章目录
- 002_音频开发_基础篇_Linux音频架构简介
- 创作背景
- `Linux` 音频架构
- `ALSA` 简介
- `ASoC` 驱动
- 硬件架构
- 软件架构
- `Machine`
- `Platform`
- `Codec`
- `ASoC` 驱动 `PCM`
- `ALSA`设备文件结构
- `ALSA` 使用
- 常用概念
- `alsa-lib`
- `ALSA` `Open` 流程
- `ALSA` `Write` 流程
- 2种写入方法
- 2种读取方法
- `Ring Buffer`
- `Ring buffer` 管理
创作背景
学历代表过去、能力代表现在、学习力代表将来。 一个良好的学习方法是通过输出来倒逼自己输入。写博客既是对过去零散知识点的总结和复盘,也是参加了 零声教育 写博客活动。
零声教育体验课:https://xxetb.xetslk.com/s/3fbO81
本文是开发过程中的知识点总结,供大家学习交流使用,如有任何错误或不足之处,请在评论区指出。
Linux
音频架构
-
OSS (Open Sound System)
:是Linux
系统上最早的音频接口系统之一。它提供了一个统一的接口来管理音频设备和音频数据的输入输出。但在发展过程中逐渐被ALSA
替代。 -
ALSA (Advanced Linux Sound Architecture)
:- 是
Linux
内核中的音频驱动架构,提供了对各种音频设备的支持,包括声卡、数字音频接口等。 ALSA
提供了一组API
和工具,用于在Linux
上进行音频编程和音频流处理。2002
年它被引进入Linux内核的开发版本(2.5.4
-2.5.5
)。- 从
2.6
版本开始ALSA
成为Linux
内核中默认的标准音频驱动程序集,OSS
则被标记为废弃。 - 为了向后兼容,
ALSA
提供内核模块来模拟OSS
,这样之前的许多在OSS
基础上开发的应用程序不需要任何改动就可以在ALSA
上运行。 libaoss
库也可以模拟OSS
,而它不需要内核模块。
- 是
-
ASoC (ALSA System on Chip)
:- 是
ALSA
的一个子系统,专门用于在嵌入式系统中管理和配置与硬件集成的音频设备。ASoC
提供了一种灵活的框架,使得开发者能够轻松地将ALSA
应用于各种嵌入式系统中的音频硬件。 ASoC
是ALSA
在SoC
方面的发展和演变,它在本质上仍然属于ALSA
,但是在ALSA
架构的基础上对CPU
相关的代码和CODEC
相关的代码进行了分离。其原因是,采用传统ALSA
架构的情况下,同一型号的CODEC
工作于不同的CPU
时,需要不同的驱动,这不符合代码重用的要求。
- 是
ALSA
简介
目前 linux
的主流音频体系架构,官网:http://www.alsa-project.org/
软件包名称 | 描述 |
---|---|
alsa-driver | 内核驱动程序,包括硬件相关的和一些公共代码。 |
alsa-lib | ALSA 核心库,提供 ALSA API 的实现和访问接口。需要包含头文件 asoundlib.h ,链接共享库 libasound.so |
alsa-utils | 包含 ALSA 工具,用于配置和管理 ALSA 音频设备。 |
alsa-tools | 包含用于测试和调试 ALSA 音频设备的工具。 |
alsa-plugins | 包含用于扩展 ALSA 功能的插件。用 jack 模拟 alsa 接口。用 oss 来模拟 alsa 接口。 |
alsa-firmware | 包含用于 ALSA 音频设备的固件文件。 |
alsa-ucm-conf | ALSA UCM (Use Case Manager) 配置文件,用于管理音频设备的用例。 |
alsa-topology-conf | ALSA 拓扑配置文件,用于配置音频设备的拓扑结构。 |
alsa-oss | ALSA OSS (Open Sound System) 兼容层,用于支持使用 OSS 接口的应用程序。 |
pyalsa | Python 绑定,用于在 Python 中访问 ALSA API 。 |
tinycompress | ALSA 压缩库,用于与压缩音频硬件进行交互。 |
ASoC
驱动
硬件架构
软件架构
Machine
- 是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机。
- 每个
Machine
上的硬件实现可能都不一样,CPU
不一样,Codec
不一样,音频的输入、输出设备也不一样。 Machine
为CPU
、Codec
、输入输出设备提供了一个载体。- 单独的
Platform
和Codec
驱动是不能工作的,它必须由Machine
驱动把它们结合在一起才能完成整个设备的音频处理工作。 - 例如:
Rockchip_es8323
就是一个Machine
:
Platform
- 一般是指某一个SoC平台,比如
RK3229
,S3c4430
,MT6795
等等。 - 与音频相关的通常包含该SoC中的时钟、
DMA
、I2S
、PCM
等等。 - 只要指定了
SoC
,那么我们可以认为它会有一个对应的Platform
,它只与SoC
相关,与Machine
无关,这样我们就可以把Platform
抽象出来。 - 同一款
SoC
不用做任何的改动,就可以用在不同的Machine
中。 - 实际上,把
Platform
认为是某个SoC
更好理解。
Codec
- 字面上的意思就是编解码器,
Codec
里面包含了I2S
接口、D/A
、A/D
、Mixer
、PA(功放)
,通常包含多种输入(Mic
、Line-in
、I2S
、PCM
)和多个输出(耳机、喇叭、听筒,Line-out
)。 Code
c和Platform
一样,是可重用的部件,同一个Codec
可以被不同的Machin
e使用。- 嵌入式
Codec
通常通过I2C对内部的寄存器进行控制。 Controls
就是对Codec
输入输出的寄存器进行操作。
ASoC
驱动 PCM
- 在实例化
snd_card
的时候进行创建,一个pcm
实例由一个playback stream
和一个capture stream
组成, - 这两个
stream
又分别有一个或多个substreams
组成。 - 大多数情况下是一个声卡,一个
pcm
实例,pcm
下面有一个playback stream
和capture stream
,playback
和capture
下面各自有一个substream
。
ALSA
设备文件结构
声卡初始化之后,会生成设备节点,以供上层调用,alsa
驱动的设备文件结构,可以在/dev/snd
下用ls
查看,如下所示:
controlC0 --> 用于声卡的控制,例如通道选择,混音,麦克风的控制等
pcmC0D0c --> 用于录音的pcm设备
pcmC0D0p --> 用于播放的pcm设备
timer --> 定时器
alsa-driver
中的命名规则:C0
代表的是声卡0
,D0
代表声卡中的设备0
,pcmC0D0c
最后一个c
代表capture
,pcmC0D0p
最后一个p
代表`playback
ALSA
使用
常用概念
- 样本长度(
sample
,format
): 样本是记录音频数据最基本的单位,计算机对每个通道采样量化时数字比特位数,常见的有16
、24
和32
位,也就是format
。 - 通道数(
channel
):1
表示单声道/Mono
,2
则是立体声/Stereo
。 - 帧(
frame
): 构成一个完整的声音单元,Frame = Sample(format) * channel
。 - 采样率(
rate
): 又称sample rate
,采样率,即每秒的采样次数,针对帧而言;常用的采样率如8KHz
的人声,44.1KHz
的mp3
音乐,96Khz
的蓝光音频。 - 周期(
period
): 每次硬件中断处理音频数据的帧数,对于音频设备的数据读写,以此为单位。 - Buffer size: 数据缓冲区大小,这里指
runtime
的buffer size
,而不是结构图snd_pcm_hardware
中定义的buffer_bytes_max
;一般来说buffer_size
=period_size
*period_count
,period_count
相当于处理完一个buffer
数据所需的硬件中断次数,一般就是DMA
的中断。 - 比特率(
Bits Per Second
): 比特率表示每秒的比特数,比特率
=采样率
×通道数
×样本长度
。 - 交错模式(
interleaved
): 是一种音频数据的记录方式,在交错模式下,数据以连续桢的形式存放,即首先记录完桢1
的左声道样本和右声道样本(假设为立体声格式),再开始桢2
的记录。 - 非交错模式: 首先记录的是一个周期内
所有
桢的左声道
样本,再记录右声道
样本,数据是以连续通道的方式存储。不过多数情况下,我们只需要使用交错模式就可以了。
alsa-lib
- 指用户空间的函数库,提供给应用程序使用,应用程序应包括头文件
asoundlib.h
。并使用共享库libasound.so
- 应用程序开发者应该使用
libasound
而不是内核中的ALSA
接口。因为libasound
提供最高级并且编程方便的编程接口。并且提供一个设备逻辑命名功能,这样开发者甚至不需要知道类似设备文件这样的低层接口。 - 相反,
OSS/Free
驱动是在内核系统调用级上编程,它要求开发者提供设备文件名并且利用ioctrl
来实现相应的功能。 ALSA
包含插件功能,使用插件可以扩展新的声卡驱动,包括完全用软件实现的虚拟声卡。ALSA
提供一系列基于命令行的工具集,比如:- 混音器(
mixer
), - 音频文件播放器(
aplay
), - 以及控制特定声卡特定属性的工具。
- 混音器(
AlSA
本身也提供混音的plugin
,dmix
.
ALSA
Open
流程
通过alsa lib的snd_pcm_open
函数打开音频设备,这样间接会调用到驱动提供的接口:
open(fn, "/dev/snd/pcmC0D0p")
:- 打开播放的
PCM
设备, 建立与驱动的联系。
- 打开播放的
ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS)
:- 设置音频相关的参数,包括传输的
format
,channels
,rate
等。
- 设置音频相关的参数,包括传输的
mmap(NULL, buffer_size)
:- 建立内存映射, 给应用直接访问设备内存的能力,当用户进程在分配的内存范围内读写时,实际就是访问是设备。
ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)
:- 进行播放前的准备,通知
widget
更新电源状态,包括设置codec
,platform
相关寄存器,准备好进行播放。
- 进行播放前的准备,通知
ALSA
Write
流程
应用通过 alsa-lib
里面的 API
函数 snd_pcm_writei
写入数据。
2种写入方法
- 通过
copy_form_user
把应用的音频数据拷贝到DMA
中,然后通过DMA
把数据发送到SoC
的I2S
控制器的FIFO
,再到codec
,最后由喇叭或者手机播放。
- 通过
mmap
建立内存映射,应用直接操作映射好的内存区域,相当于直接写到DMA
中,然后通过DMA
把数据发送到SoC
的I2S
控制器的FIFO
,再到codec
,最后由喇叭或者手机播放。
2种读取方法
与写入一样有两种方法:
- 通过
copy_to_user
。 - 通过
mmap
。
Ring Buffer
- 播放时,应用程序把音频数据源源不断地写入
dma buffer
中, 然后相应platform
的dma
操作则不停地从该buffer
中取出数据,经dai
送往codec
中。 - 录音时则正好相反,
codec
源源不断地把A/D
转换好的音频数据经过dai
送入dma buffer
中,而应用程序则不断地从该buffer
中读走音频数据。
Ring buffer
管理
alsa driver
使用 Ring Buffer
对 dma buffer
进行管理:
Buffer size
: 就是一个HW buffer(虚拟)
,相当于应用的period
*count
,实际的Buffer
大小。snd_pcm_runtime.hw_ptr_base
: 环形缓冲区每一圈的基地址,当读写指针越过一圈后,它按buffer size
进行移动;snd_pcm_runtime.status->hw_ptr
: 硬件逻辑位置,播放时相当于读指针,录音时相当于写指针;snd_pcm_runtime.control->appl_ptr
: 应用逻辑位置,播放时相当于写指针,录音时相当于读指针;snd_pcm_runtime.boundary
: 扩展后的逻辑缓冲区大小,通常是(2^n)*size, runtime->boundary一般都是较大的数。 不是真实的缓冲区的地址 偏移,经过转换才得到 物理缓冲的偏移。