linux的声音管理

前言

在使用arch与i3时,
发现不能在插入耳机后自动调用耳机输出声音,
而是需要使用 pavucontrol 工具来手动调整输出的硬件,
非常不方便,于是看一看linux的声音到底是怎么管理的.
音频系统本身没有像OSI模型一样每层之间有明确的界限,而是各自为政,互相有影响

名词解释

OSS

Open Sound System
开放声音系统.

  1. 直接和内核通信,驱动硬件.
  2. 提供单纯可移植的API给程序.

一开始的声卡支持硬件混音功能,后来由于制造商看法的改变,混音功能交给了软件而OSS没有跟进,
因此OSS本身不具有混音功能.但可以调用声卡的硬件混音功能.

ALSA

Advanced Linux Sound Architecture
高级linux声音体系
由于OSS闭源的原因,打算代替OSS.
过程中加入了OSS自身不支持的软件混音(尽管评价并不好)
并且顺便带了audio server的功能.
由于软件推广的原因,推出了OSS的模拟器
(结果模拟器比本尊还好,但抛弃了调用声卡硬件混音的功能,导致在模拟OSS的API时没有优势可言).
作用如下:

  1. 提供声卡驱动
  2. 提供用户操作的函数库
  3. 负责混音
    1. 两个程序同时发声时,另声卡发出混和了两种声音的一个声音
    2. 原则上如果声卡支持硬件混音则声卡自己来(然alsa并不支持)
    3. 最多支持8路音频硬件
    4. ALSA的混音饱受诟病,因此混音的部分常常放在pulseaudio中
      1. alsa API ->alsa混音->alsa底层
      2. alsa API ->pulseaudio混音->alsa底层
  4. 硬件资源调用
  5. 多声道支持

ALSA工具

用于和alsa进行交互

  • amixer 命令行中简单的工具
  • alsamixer 命令行中使用字符模拟GUI的工具
  • alsactl alsamixer的字符版工具,兼具保存配置的功能

配置文件放在 /var/lib/alsa/asound.state

封装器

oss,alsa等的后端太多,就需要有一个封装器包装一下.

  • OpenAL
  • SDL
  • libao2

可能引起额外的延迟

PulseAudio

桌面环境中常用的音频服务(守护进程),
处于alsa与应用程序之间,
充当代理的角色(有时alsa将pulseaudio视为一个虚拟的设备).

  • 用户–pulseaudio–虚拟设备–alsa–虚拟设备–pulseaudio–用户

同时有更强的软件能力,可以将远程的音频在本地播放.
但正常来说应该在网络层完成该功能而不是在应用里
pulseaudio的前端(配置该服务行为的软件)有:

  • paprefs(用于配置插口用途,比如将麦克风口改为耳机口)
  • pavucontrol(用于配置目前使用的播放设备)
  • kmix
  • plasma-pa等

配置文件放在 /etc/pulse/
也可以有用户的配置文件 ~/.config/pulse/

声卡

早期是将数字信号转换为模拟信号,
一般独显中也自带一个.

PCM

Pulse Code Modulation
脉冲编码调制
模拟信号经过采样,量化,编码转换后的数据流

回放

问题

自己的台式机在使用HDMI连接的带音响显示器时,无法在耳机插入后自动切换为耳机播放,
在windows下甚至也不行.
原因在于:
HDMI使用的是显卡自带的声卡且直接使用数字信号,
耳机如果插在主机上则使用的是主板自带的声卡,使用的是模拟信号.
系统可以在同一个声卡连接的不同端口(比如耳机和主机音响)之间自动切换,
却不能在两个声卡之间自动切换.

目前已知的信息

module-switch-on-connect

pacmd的module中有一个module-switch-on-connect
描述是当有新的sink连接时使用新的sink,
但自己电脑上无论是否插入耳机,都有两个sink,一个内置声卡,一个HDMI声卡

pulseaudio配置的结构

目前pulseaudio的结构并不很清楚,都是猜的
card – (sink)

profile |

port (sink可以使用哪些端口则受到profile的影响)
如果没有声音,原因可能是

  1. profile由于未知的原因不可用
  2. profile不可用导致该声卡不能关联合适的port,端口是最接近输出声音的地方,即使选择正确的输出设备,没有正确的端口也听不到声音.

pavucontrol中有三个tab比较关键,
分别是

  • playback
  • output-device
  • settings

settings

在此处为声卡(card)选择配置文件(profile)
等效的命令为

1
pacmd set-card-profile <card-idx> <profile-nm>
  • 有哪些声卡以及profile需要使用 pacmd list-cards 查看
  • BIOS设置音频为新版的HD(旧版为AC97),profile的状态大多是不可用的,
    • 目前除了更换为旧版以外还没有其他办法将profile设置为可用
    • profile不可用的原因也不清楚
      • 一说是因为该profile使用的port全部不可用
      • 但又来了问题, port不可用的原因是什么

output-device

在此处为输出设备(sink)选择端口(port).
等效的命令为

1
pacmd set-sink-port <sink-idx> <port-name>
  • 可以使用的sink和port使用 pacmd list-sinks 查看

playback

此处为应用(input)选择播放设备(sink)
等效的命令为

1
pacmd move-sink-input <input-idx> <sink-idx>
  • 以上众多命令大致都可以使用index或name来代替
  • 查看有那些input可以使用 pacmd list-sink-inputs

解决思想

在profile和port都设置好的情况下

  1. 设置默认的sink为外置声卡
  2. 检测耳机插入的事件
  3. 为事件加入钩子
    • 如果有input则切换所有input的sink为内置声卡
    • 如果没input则设置默认sink为内置声卡
  4. 检测耳机拔出事件
    • 有input则切换input的sink
    • 没有则设置默认sink

网上有,但用于检测hdmi状态的代码在自己的机器上不合适
https://gist.github.com/3v1n0/ad3ad73e0bdd466d7f4a95b3aa376285

参考

https://lp007819.wordpress.com/2010/10/08/linux音频系统的那点事/