Linux车机平台pulseaudio多alsasink配置

本文介绍了PulseAudio在嵌入式系统中的配置与调试过程,包括如何为不同类型的音频输出创建alsa-sink对象,以及如何通过配置实现硬件级别的音量和静音控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://www.freedesktop.org/wiki/Software/PulseAudio 官网上的介绍是这样的:
pulseaudio 是一个POSIX操作系统上的声音系统。是音频应用的代理。它允许你对音频数据,在从应用传递到硬件的过程中,做更多的操作。像把音频数据传递到另一台机器,更改采样率,声道,多路音频混音等。

车机平台,会包含多种声音的处理。多媒体,语音,导航等。
我要做的是,在平台中声卡驱动及alsalib接口已经就绪的状态下,提供的接口,用于不同音频类型的播放及音量控制,还有录音。
其实直接用alsa也不是不可以,但是感觉pulseaudio功能还是多一些。而且后续可以通过配置属性实现软件的 audio route 控制。

使用alsa的小工具,可以查看当前配置好的playback和record接口。下面是我开发板中的信息:

root@atlas7-arm:~# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: kasaudiocard [kas-audio-card], device 0: Music Playback (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: kasaudiocard [kas-audio-card], device 1: Navigation Playback (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: kasaudiocard [kas-audio-card], device 2: Alarm Playback (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
...

root@atlas7-arm:~# arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: kasaudiocard [kas-audio-card], device 8: Analog Capture (*) []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
...

#不同音频类型的输出方案
pulseaudio 通过alsalib API处理音频输出的是alsa-sink对象。每个alsa-sink对象会对应一个alsa hw的输出设备(hw:0,0 hw:0,1 dmixer这些)。
所以,想法就是为不同的alsa hw设备接口,分别创建不同的alsa-sink,这样在处理不同APP的音频数据时,采用对应的alsa-sink作为输出端。
这样,需要做的就是为pulseaudio生成不同的alsa-sink,然后在处理不同类型的音频数据时,做对应的选择即可。

#Pulseaudio 的配置文件及模块加载
pulseaudio是一个守护进程,参考了官网的一些资料,把pulseaudio运行在了system-wide模式。
In some situations however, such as embedded systems where no real notion of a user exists, it makes sense to use the system-wide mode.
默认的配置文件在 /etc/pulse/daemon.conf
pulseaudio 加载模块,可以通过配置pa文件的方式(普通模式下是default.pa, system-wide模式下是system.pa)来在pulseaudio daemon启动后,自动加载
通过pa文件自动加载的话,要确保/etc/pulse/daemon.conf中的相关配置正确(load-default-script-file 和 default-script-file )
也可以通过pacmd工具来发送命令动态加载。

#创建alsa-sink对象
有几种方式

  1. 加载module-alsa-sink模块,每加载一次module-alsa-sink,会创建一个对应的alsa-sink对象。
  2. 加载module-alsa-card 模块,module-alsa-card模块会根据profile set 来创建一个或者多个alsa-sink对象。
  3. 加载module-udev-detect 模块,这个模块会发现alsa-card设备,然后加载module-alsa-card,然后就和方式2一样

按照前面说的,其实我是希望创建3个alsa-sink,分别对应hw:0,0 (Music Playback),hw:0,1 (Navigation Playback),hw:0,2(Alarm Playback)这几个alsa设备。

先看一下直接加载module-alsa-sink这种方式,从参数说明看,可以通过device和device_id来指定具体要关联打开的alsa设备。
通过阅读代码和实际测试发现:
如果设置了device_id参数的话,device参数就不会生效,而是会先生成一个默认的profile set,然后根据其中每个profile配置里的设备字串模版,把device_id值带入,然后尝试打开设备。
而这个默认的profile set内容来自于 /usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf,其中配置了不同的mapping,比如这种

[General]
auto-profiles = yes
...
[Mapping analog-stereo]
device-strings = front:%f hw:%f
channel-map = left,right
paths-output = analog-output analog-output-lineout ...
paths-input = analog-input-front-mic analog-input-rear-mic ...
priority = 10

auto-profiles 配置为yes的话,就会自动为每个mapping创建一个profile。
然后在创建alsa-sink时,就会循环的对每个profile中的device-strings带入device_id参数。比如device_id设置为1,front:1 hw:1 就会被尝试打开。
其实可以看出来,这个默认的配置并不能和咱们嵌入式开发板上的声卡配置相匹配。
在不自己重新配置的情况下,我怎么能打开hw:0,1 hw:0,2 这样的设备呢…

如果不使用device_id参数,直接配置device参数为hw:0,0或者 hw:0,1 这样,就会直接使用这个字串去打开alsa设备。
像这样 load-module module-alsa-sink device=hw:0,0
当然我们可以设置其他相关的音频参数 load-module module-alsa-sink device=hw:0,0 channels=2 rate=44100,也可以为这个alsa-sink指定名称 sink_name=Muisc-Playback
这样的话,只要在 system.pa 写入如下几行,就可以按需求配置出不同的alsa-sink对象了。

load-module module-alsa-sink device=hw:0,0 sink_name=Muisc-Playback
load-module module-alsa-sink device=hw:0,1 sink_name=Navigation-Playback
load-module module-alsa-sink device=hw:0,2 sink_name=Alarm-Playback

再来看一下加载module-alsa-card 模块这种方式,
从代码里可以发现,这种方式还是会先生成profile set,然后根据一个选中的 profile(active_profile),为其中每个output_mapping创建一个alsa-sink对象。
(input_mapping创建对应的alsa-source对象)
其实本人目前对于profile,mapping,path的概念也是一知半解。
从已知的情况来看,一个profile就是一组mapping的集合,一个mapping对应puseaudio中的一个sink或者source,而path则对应这sink或者source上的port。
path中定义了一些控制元素element,也就是和alsa mixer control element对应的。
从代码看来,alsa-sink创建后,会激活其中的一个port,会probe其中的element,pusleaudio只关注其中可以控制mute和volume的element,如果有这种类型的element,则会记录下来,在之后的volume和mute操作中,会通过这些element来使用硬件接口实现volume和mute。

如果按照默认的配置,因为 hw:0 这个设备字串可以成功打开,analog-stereo这个profile会被选中,module-alsa-card模块会用hw:0创建一个alsa-sink对象。然后probe analog-output path中指定的那些element,当然probe后也都是无效的。
前面也提到了,默认的profile和我们开发板的声卡配置是不能匹配的。而且我是希望创建多个alsa-sink的,所以参考原有的配置,并查找资料写了一个自己的配置
/usr/share/pulseaudio/alsa-mixer/profile-sets/my-default.conf(测试只写了2个mapping)

[General]
auto-profiles = no

[Profile output:Music-Playback+output:Navigation-Playback]
description = multiple-sink-profiles
output-mappings = Music-Playback Navigation-Playback
priority = 10

[Mapping Music-Playback]
device-strings = hw:%f,0
channel-map = left,right
paths-output = music-analog-output
priority = 9
direction = output

[Mapping Navigation-Playback]
device-strings = hw:%f,1
channel-map = left,right
paths-output = navi-analog-output
priority = 9
direction = output

/usr/share/pulseaudio/alsa-mixer/paths/music-analog-output.conf是这样写的,navi-analog-output.conf类似

[Element Music Stream Vol]
switch = ignore
volume = merge

[Element Music Stream Mute]
switch =mute
volume = ignore

其中 Music Stream Vol 和 Music Stream Mute 分别是 hw:0,0 这个卡的volume和mute的control element。
声卡0 的alsa mixer的控制元素,可以通过如下命令查看到

amixer -c 0 contents

这个 [Profile output:Music-Playback+output:Navigation-Playback] 是一个profile包含多个mapping的写法方式。
auto-profiles = no 是为了不给mapping在自动生成profile,不然的话,这个文件读出来会有3个profile,而且后两个自动生成的profile优先级会高于第一个。那样就不会选择第一个profile为active_profile了。(当然咱们也可以通过 module-alsa-card 的profile 指定active_profile)
通过指定自己的profile set方式来加载 module-alsa-card 模块:

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

会发现module-alsa-card 会根据profile的output-mappings为我们创建了对应的2个alsa-sink(加载多个的alsa-sink和alsa-source的方式都类似)

最后一种通过加载module-udev-detect 模块和第二种类似,只是在默认情况下,module-alsa-card 的参数没有设置profile set,那样就会使用默认的配置了。并且也没有看到有参数可以指定profile set,所以这个方式暂时不使用了。

#Puseaudio使用硬件接口控制volume和mute
pusleaudio最终还是需要通过alsa mixer 接口来控制volume和mute的,这样就需要pulseaudio知道对应的mixer control element。

如果使用直接加载 module-alsa-sink 的方式来创建alsa-sink的话,有一个control参数可以设置,代码中可以发现,通过这个control参数可以设置一个element,pulseaudio会去检测这个element的属性,看是否可以控制volume或者mute的。(代码在alsa-sink.c的find_mixer()函数中),但是如果需要volume和mute两个element,该如何设置呢,这个还不太清楚。

如果使用加载module-alsa-card 模块的方式在创建alsa-sink的话,就可以通过在path的配置中指定element。这样pulseaudio可以同时得到volume和mute的控制element。个人觉得如果需要使用硬件接口来控制volume和mute的话,还是需要这个方式吧。

#控制调试Pulseaudio
启动pulseaudio我用的这个命令,把能打印的log都打印出来。

pulseaudio --log-level=4 --daemonize=no --system --single-user --log-target=file:/skypine/pa.log --log-time &

pacmd可以通过protocal-native模块和pulseaudio交互。可以查询各种信息。用如下命令启动。启动后输入help就会有用法。

PULSE_RUNTIME_PATH=/var/run/pulse pacmd

#alsa-sink的选择
创建alsa-sink的时候,会有名称的。
使用paplay测试可以加 -d 参数,比如想用music那个alsa-sink就这样

paplay -d Muisc-Playback test.wav

如果是用API播放,pa_stream_connect_playback()第二次参数填alsa-sink的名称。

#遇到的一些问题
我的hw:0,1卡驱动里是配置固定48000采样率的。建立alsa-sink之后,如果送入44100采样率的数据发现播放没有声音了。
原因是,由于hw不支持,pulseaudio不能重新用44100参数打开设备,就会使用软件的resampler来重采样,然后默认配置的resample method是 speex-float-0。dump了一下resample的输出,发现全是0数据。后来换了一个resample方式,没问题了。板子上speex的库也是有的,resample数据的API返回值也正确,不知道为啥数据不对。暂时没继续看了。

使用自己配置的profile,创建出来的music alsa-sink,播放没有声音。
原因,从log看并没有什么不正常。仔细查了一下,发现alsa-sink在avtive port之后查询了一下 “Music Stream Mute” 这个element 的mute 状态。得到的结果是0(结果没错),然是接着通过alsa mixer API设置了mute状态为!0,alsa-sink被静音了。手动通过amixer 把这个element值设置回来就好了。但是不清楚代码逻辑为啥是这样的。
因为我这边不是一定需要用硬件控制mute啊,直接把 [Element Music Stream Mute] 中的 switch 设置为 ignore 也就不会有问题了。

alsa-sink播放的时候,经常会underrun,把log关掉或者输出到文件后会好一些,但是偶尔还是会出现。
查了一下log,发现alsa-sink线程在hw buffer数据充足,而且没有其他事件时,sleep了一个固定的时间,然后underrun了,线程又被alsa叫醒。
最简单的办法是加载 alsa-sink 时,tsched设置为0,这个值默认是1的。这样不sleep,就不会underrun,但性能上可能差一些吧。
其实看代码,sleep设置的时间是hw buffer 的数据时长 减去了一个 watermark 值(默认20ms),按道理应该可以在underrun之前结束sleep的啊。后面调整一下这个值,可能会有改善的。

<think>好的,用户想了解Linux环境下音频DSP的相关技术和实现方法,特别是开发教程和技术文档。首先,我需要整理Linux音频子系统的基础知识,比如ALSA架构的作用,因为它涉及到驱动和用户空间的交互。记得要提到ALSA和OSS的区别,引用[1]和[4]中提到过ALSA驱动和取消OSS API的部分,可能需要引用这些内容。 接下来,音频DSP开发的关键点包括实时处理、低延迟和算法优化。用户可能需要知道如何利用ALSA插件或用户空间的库来实现DSP功能,比如引用[3]中提到的CODEC芯片和DSP单元,这里可以解释如何在Linux中集成这些硬件。 然后,开发工具和框架部分,应该列举常用的工具,如alsa-lib、JACK、GStreamer,以及专业框架如PipeWire。这里需要检查引用[2]提到的alsa-lib编译配置的问题,可能需要提醒用户注意路径一致性,所以引用[2]可以作为配置注意事项的参考。 在实现方法中,分步骤说明如何从配置开发环境到算法开发。比如,配置ALSA和内核模块时,引用[4]中的配置步骤,如何使能WM8960驱动,这部分可以作为内核配置的例子。同时,用户空间的DSP处理可能需要引用[3]中关于CODEC芯片的功能需求,说明为什么需要特定的处理。 最后,相关资源和文档部分,需要引导用户查阅ALSA和内核文档,推荐开源项目如PulseAudio和SoX,这些在引用[1]的开源项目里提到过。此外,书籍推荐和联系社区也是必要的,引用[1]中的书籍与作者沟通部分可以支持这一点。 生成的问题要覆盖用户可能进一步关心的方向,比如实时处理优化、低延迟配置、算法集成、调试工具以及硬件选择,这些都能帮助用户深入学习和解决问题。确保所有引用都正确对应到用户提供的资料,避免错误引用。</think>### Linux 音频 DSP 开发技术解析 #### 一、Linux 音频子系统基础 Linux 音频子系统基于 **ALSA(Advanced Linux Sound Architecture)** 框架实现,其核心功能包括驱动管理、音频数据流处理和硬件编解码支持[^1]。 - **ALSA 驱动层**:负责与硬件交互,如 CODEC 芯片(如 WM8960)的配置,涉及时钟同步、数据格式转换等[^4]。 - **ALSA 插件系统**:通过插件(如 `dmix`、`ladspa`)实现混音、重采样等 DSP 功能。 - **低延迟优化**:需配置实时内核(PREEMPT_RT)和调整音频缓冲区大小,例如通过 `/etc/asound.conf` 设置 `period_size` 和 `buffer_size`[^2]。 #### 二、音频 DSP 开发关键技术 1. **实时音频处理** - **用户空间 DSP**:使用 ALSA 库(`libasound`)直接操作 PCM 数据流,结合 FFT 或 FIR 滤波算法实现均衡器、降噪等功能。 - **内核空间 DSP**:通过编写 ALSA 插件或自定义内核模块(如 `snd-pcm-oss`)实现硬件级处理[^4]。 2. **开发工具链** - **ALSA Utilities**:`aplay`、`arecord` 用于测试音频输入输出。 - **专业框架**: ```bash # 使用 GStreamer 实现流水线式 DSP gst-launch-1.0 alsasrc ! audioconvert ! audioresample ! equalizer-3bands ! alsasink ``` - **调试工具**:`strace` 跟踪系统调用,`latencytop` 分析延迟瓶颈。 3. **硬件集成示例** 以 I.MX6ULL 平台为例,需在内核中启用 `SND_SOC_IMX_WM8960` 驱动,并配置设备树绑定 CODEC 与 CPU 的接口(如 I2S、SAI)[^4]。 #### 三、实现步骤 1. **环境配置** - 编译 ALSA 库时,需确保开发板与主机的路径一致(如 `/usr/share/arm-alsa`),避免兼容性问题[^2]。 - 内核配置路径示例: ``` Device Drivers → Sound → ALSA → SoC Audio → Freescale CPUs → WM8960 support ``` 2. **DSP 算法开发** - **参考代码片段(C语言)**: ```c snd_pcm_readi(pcm_handle, buffer, frames); // 读取音频数据 apply_fir_filter(buffer, coefficients); // 应用 FIR 滤波器 snd_pcm_writei(pcm_handle, buffer, frames); // 写回处理后的数据 ``` 3. **性能优化** - 使用 `sched_setscheduler()` 设置线程为 `SCHED_FIFO` 优先级。 - 通过 `hw_params` 设置 `SND_PCM_ACCESS_RW_INTERLEAVED` 减少内存拷贝。 #### 四、相关资源 - **官方文档**:ALSA 项目官网提供完整的 API 手册和案例。 - **开源项目参考**: - PipeWire:支持低延迟音频处理的现代框架。 - SoX:跨平台音频处理工具,含种 DSP 算法实现。 - **书籍推荐**:《Linux Sound Programming》详细讲解 ALSA 内核与用户空间开发。 ---
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值