Linux音频系统研究

1. ALSA驱动层

ALSA(Advanced Linux Sound Architecture)是Liunx音频驱动层,负责处理音频设备在内核的驱动,ALSA比古老的OSS高级的一点在于它还提供了用户空间的ALSA-lib用来帮助用户空间的应用程序来访问音频设备。

以前的OSS驱动仅仅向用户空间暴露音频设备的dev设备文件,应用通过系统调用read(),write()和ioctl()来访问音频设备。

现在的ALSA-lib使用统一的API来访问声卡,屏蔽了底层设备的差异性,提高了应用了通用性。

目前的Linux系统中,ALSA在内核启动后会初始化/proc/asound文件夹,这个文件夹下面包含了内核识别了的音频设备。可以进入这个文件夹里面探索一番,也可以通过ALSA提供的命令工具来显示信息。

arecord -l #列出所有录音设备
aplay -l #列出所有播放设备
arecord -Dhw:0,6 -d 10 -f cd -c 2 -t wav test.wav #使用0号声卡的6号设备录音
aplay test.wav #播放音频文件

2. Udev层

udev属于用户空间层,但是udev干的是初始化/dev/snd/下面设备文件的工作,当然也包括音频设备文件。

udev通过接收内核事件uevent,根据/etc/udev/rules.d/或者/usr/lib/udev/rules.d/下面的规则文件,建立对应的dev设备文件。如果没有对应的规则,就按照内核设备名建立dev设备文件。当然,还可以执行其他的操作比如建立符号链接等。

rules文件的编写规则很简单,基本上就是一个匹配-执行的过程。如果要手写rules规则文件,则需要了解udevadmin的用法。详情可参见这篇文章LINUX下 Udev详解

udevadmin是一个udev命令工具,可以查询devfs文件系统/dev/下面的文件的设备文件路径。

设备文件路径不是实际意义上的文件路径,而是devfs中抽象的文件路径。

例如使用udevadmin查询设备路径

udevadm info -q path -n /dev/snd/pcmC0D6c
/devices/pci0000:00/0000:00:1f.3/skl_hda_dsp_generic/sound/card0/pcmC0D6c

输出的即为抽象的设备文件路径。

然后我们可以继续使用设备文件路径来获取该设备各个父节点的信息,这些信息都可以用来匹配rules。

udevadm info -ap /devices/pci0000:00/0000:00:1f.3/skl_hda_dsp_generic/sound/card0/pcmC0D6c
  looking at device '//devices/pci0000:00/0000:00:1f.3/skl_hda_dsp_generic/sound/card0/pcmC0D6c':
    KERNEL=="pcmC0D6c"
    SUBSYSTEM=="sound"
    DRIVER==""
    ···········
    ATTRS{device}=="0x34c8"
    ATTRS{vendor}=="0x8086"
    ATTRS{broken_parity_status}=="0"

  looking at parent device '//devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

省略好多,都是各个父节点的信息,通过这些信息,我们可以写rules文件来对特定的设备进行处理,甚至包括应用pulseaudio配置。

在udev匹配时应用pulseaudio配置,参见/usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf文件。

Default profile definitions for the ALSA backend of PulseAudio. This
is used as fallback for all cards that have no special mapping
assigned (and should be good enough for the vast majority of
cards). If you want to assign a different profile set than this one
to a device, either set the udev property PULSE_PROFILE_SET for the
card, or use the "profile_set" module argument when loading
module-alsa-card.

3. PulseAudio层

当驱动加载好,udev设置完毕以后,就是pulseaudio开始工作了,pulseaudio是一个用户空间音频服务进程,作用就是统一管理系统的音频流,并且进行混音等操作。

对于处于默认工作模式下的pulseaudio,会读取/etc/pulse/default.pa的设置进行初始化。

pulseaudio和udev结合的比较紧密,在/etc/pulse/default.pa文件中可以看到,pulseaudio也是通过module-udev-detect.so进行了大量音频设备的建立。

module-udev-detect.so等等一系列pulseaudio的模块文件,都在/usr/lib/pulse-12.2.13/modules/文件夹下面。

module-udev-detect.so文件的行为对我们来说是一个黑盒。module-udev-detect.so文件完成了一系列的pulseaudio音频设备的建立,并且不需要依赖pulseaudio配置文件。

如果module-udev-detect.so的一些操作不符合我们的预期,我们就需要手动写/usr/share/pulseaudio/alsa-mixer/profile-sets/配置文件,并且在/etc/pulse/default.pa手动加载配置文件,实现我们的目的。

/etc/pulse/default.pa文件中的命令和pulseaudio提供的命令行工具pacmdpactl非常相似。

    list-modules              List loaded modules
    list-cards                List cards
    list-sinks                List loaded sinks
    list-sources              List loaded sources
    ······
    load-module               Load a module (args: name, arguments)
    ······

比如通过load-module载入module-alsa-card这个模块,我们可以实现手动载入一个alsa声卡,并且通过/usr/share/pulseaudio/alsa-mixer/profile-sets/配置文件指定sink和source,达到自由组合音频流的目的。

比如我们用声卡0再次创建一个pulseaudio虚拟声卡,并指定配置文件,只需要在/etc/pulse/default.pa文件中加上这一行。

load-module module-alsa-card device_id=0 profile_set=default-juju.conf

而手动写pulseaudio配置文件,又是一个大学问。可以参看/usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf里面有详细的说明。看pulseaudio官网Writing pulseaudio profiles也有说明。

然后pulseaudio配置文件,它叫做profile,实际上就是各种alsa设备到pulseaudio设备的mapping。

除了profile还有paths文件,实际上就是mixer混音器,在pacmd list-cards显示下又叫做port端口。文件在/usr/share/pulseaudio/alsa-mixer/paths/下面,这个写法也有讲究,我就不研究了,大家感兴趣就研究一下。

修改完pulseaudio的各种配置以后,使用pulseaudio -k杀死pulseaudio守护进程,它会自动重启。

如果想看调试信息的话,可以手动指定pulseaudio守护进程在命令行前台运行并且输出调试信息。

pulseaudio -k;pulseaudio --log-level=4 --daemonize=no --log-time &

标签: linux驱动, ALSA, Udev, PulseAudio

赞赏排名 赞赏支持

已有 2 条评论

  1. Hansen Hansen

    哈哈哈最近在研究Linux 的多路音频输出,你的文章对我帮助很大。感谢

添加新评论