Linux车机平台pulseaudio多alsasink配置

2023-11-11

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的啊。后面调整一下这个值,可能会有改善的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux车机平台pulseaudio多alsasink配置 的相关文章

  • vmsplice() 和 TCP

    在原来的vmsplice 执行 有人建议 http lwn net Articles 181169 如果您的用户态缓冲区是管道中可容纳的最大页面数的 2 倍 则缓冲区后半部分成功的 vmsplice 将保证内核使用缓冲区的前半部分完成 但事
  • ioctl 命令的用户权限检查

    我正在实现 char 驱动程序 Linux 并且我的驱动程序中有某些 IOCTL 命令仅需要由 ADMIN 执行 我的问题是如何在 ioctl 命令实现下检查用户权限并限制非特权用户访问 IOCTL 您可以使用bool capable in
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • Google BQ:运行参数化查询,其中参数变量是 BQ 表目标

    我正在尝试从 Linux 命令行为 BQ 表目标运行 SQL 此 SQL 脚本将用于多个日期 客户端和 BQ 表目标 因此这需要在我的 BQ API 命令行调用中使用参数 标志 parameter 现在 我已经点击此链接来了解参数化查询 h
  • python获取上传/下载速度

    我想在我的计算机上监控上传和下载速度 一个名为 conky 的程序已经在 conky conf 中执行了以下操作 Connection quality alignr wireless link qual perc wlan0 downspe
  • 通过 Visual Studio 2017 使用远程调试时 Linux 控制台输出在哪里?

    我的Visual Studio 2017 VS2017 成功连接Linux系统 代码如下 include
  • 如何阻止ubuntu在使用apt安装或更新软件包时弹出“Daemons using outdatedlibraries”? [关闭]

    Closed 这个问题是与编程或软件开发无关 help closed questions 目前不接受答案 我最近新安装了 Ubuntu 22 04 LTS 我发现每次使用 apt 安装或更新软件包时 它都会询问我有关Which servic
  • 如何根据标签将单个 XML 文件拆分为多个

    我有一个带有标签的 XML 文件 我想像这样分割文件
  • 使用循环在 C 中管道传输两个或多个 shell 命令

    我正在尝试执行ls wc l通过 C 语言程序 而不是使用命令行 这是我当前的工作代码 int main int pfds 2 pipe pfds pid t pid fork if pid 0 The child process clos
  • 并行运行 shell 脚本

    我有一个 shell 脚本 打乱大型文本文件 600 万行和 6 列 根据第一列对文件进行排序 输出 1000 个文件 所以伪代码看起来像这样 file1 sh bin bash for i in seq 1 1000 do Generat
  • .net-core:ILDASM / ILASM 的等效项

    net core 是否有相当于 ILDASM ILASM 的功能 具体来说 我正在寻找在 Linux 上运行的东西 因此为什么是 net core ildasm 和 ilasm 工具都是使用此存储库中的 CoreCLR 构建的 https
  • 内核的panic()函数是否完全冻结所有其他进程?

    我想确认内核的panic 功能和其他类似kernel halt and machine halt 一旦触发 保证机器完全冻结 那么 所有的内核和用户进程都被冻结了吗 是panic 可以被调度程序中断吗 中断处理程序仍然可以执行吗 用例 如果
  • Intel 上的 gcc 中的 _mm_pause 用法

    我参考过这个网页 https software intel com en us articles benefitting power and performance sleep loops https software intel com
  • 使用 python 脚本更改 shell 中的工作目录

    我想实现一个用户态命令 它将采用其参数之一 路径 并将目录更改为该目录 程序完成后 我希望 shell 位于该目录中 所以我想实施cd命令 但需要外部程序 可以在 python 脚本中完成还是我必须编写 bash 包装器 Example t
  • 如何在 *nix 中登录时运行脚本?

    我知道我曾经知道如何做到这一点 但是 如何在 unix 中登录时运行脚本 bash 可以 From 维基百科 Bash http en wikipedia org wiki Bash 28Unix shell 29 当 Bash 启动时 它
  • Linux 为一组进程保留一个处理器(动态)

    有没有办法将处理器排除在正常调度之外 也就是说 使用sched setaffinity我可以指示线程应该在哪个处理器上运行 但我正在寻找相反的情况 也就是说 我想从正常调度中排除给定的处理器 以便只有已明确调度的进程才能在那里运行 我还知道
  • 使用 gdb 调试 Linux 内核模块

    我想知道 API 在内核模块 中返回什么 从几种形式可以知道 这并不是那么简单 我们需要加载符号表来调试内核模块 所以我所做的就是 1 尝试找到内核模块的 text bss和 data段地址 2 在 gdb 中使用 add symbol f
  • 我什么时候应该编写 Linux 内核模块?

    有些人出于某种原因想要将 Linux 中的代码从用户空间移动到内核空间 很多时候 原因似乎是代码应该具有特别高的优先级 或者只是 内核空间更快 这对我来说似乎很奇怪 我什么时候应该考虑编写内核模块 有一套标准吗 我怎样才能激励将代码保存在
  • cdc_acm:无法设置 dtr/rts - 无法与 USB cdc 设备通信

    我试图使用 pic24fj128gb206 枚举 usb cdc 设备 设备似乎已正确枚举 但是当我将设备连接到 Linux PC 时 我从内核收到以下警告消息 cdc acm 1 8 1 6 7 1 0 failed to set dtr
  • 如何在 Mac OSX Mavericks 中正确运行字符串工具?

    如何在 Mac OSX Mavericks 中正确运行字符串工具 我尝试按照我在网上找到的示例来运行它 strings a UserParser class 但我收到此错误 错误 Applications Xcode app Content

随机推荐

  • .Net WebAPI JWT身份验证

    一 开发环境 VS2017 enterprise win10 Pro 64 net 4 6 2 二 开发过程 1 使用VS2017 创建 netframework项目 选择WebApi 2 从Nuget包中搜索并安装JWT 3 在Model
  • 动态路由-BGP的基础配置

    一 给每个路由器配置ip地址 AR6 1 1 添加ip地址 interface GigabitEthernet0 0 0 ip address 1 1 1 1 255 255 255 0 2 配置BGP对等体 bgp 100 配置bgp的A
  • 使用PyQt(Python+Qt)+moviepy开发的视频截取、音视频分离、MP4转GIF动图工具免费下载分享

    专栏 Python基础教程目录 专栏 使用PyQt开发图形界面Python应用 专栏 PyQt入门学习 老猿Python博文目录 在因博文素材需要将软件操作制作成动画时 发现网上相关绿色使用工具都需要注册 否则动态上就会打上各种LOGO 无
  • C++ 类成员指针

    1 成员指针简介 成员指针是C 引入的一种新机制 它的申明方式和使用方式都与一般的指针有所不同 成员指针分为成员函数指针和数据成员指针 2 成员函数指针 在事件驱动和多线程应用中被广泛用于调用回调函数 在多线程应用中 每个线程都通过指向成员
  • IIS错误页面隐藏版本信息 - Web.Config customErrors配置

    目录 背景 分析暴露原因 解决办法 扩展学习customErrors使用方法 customErrors元素配置结构 元素属性 Mod 属性选项 示例 参考文章 背景 项目现按照国网的要求 测试后发现系统错误页面存在服务器版本号泄露 不允许部
  • Java Servlet的主要功能和作用是什么?

    Servlet 通过创建一个框架来扩展服务器的能力 以提供在 Web 上进行请求和响应服务 当客户机发送请求至服务器时 服务器可以将请求信息发送给 Servlet 并让 Servlet 建立起服务器返回给客户机的响应 当启动 Web 服务器
  • Kali Linux 2020.1修改Root用户密码

    背景信息 多年以来 Kali从BackTrack继承了默认的root用户策略 作为对Kali工具和策略的评估的一部分 因此Kali 决定对此进行更改 并将Kali移至 传统默认非根用户 模型 那如果我们因某些情况想开启Root用户这应该如何
  • facebook stetho Android调试工具

    什么是Stetho 官网简介 Stetho is a debug bridge for Android applications enabling the powerful Chrome Developer Tools and much m
  • 如何利用matlab神经网络进行水量预测

    数据收集 百度搜索EPS数据库登陆 我们选择进入城市数据库 我们的目的是要进行某年水量预测 我们的目的是知道了某一年的土地 人口等信息 就可以预测出该年的用水量 因此我们需要搜集一些信息用来训练 可以多选几年如2003 2017 然后点击下
  • 13 集成测试之自顶向下集成测试方法

    自顶向下集成测试方法 前言 深度优先集成方法 宽度优先集成方法 总结 前言 自顶向下集成方法可以采取深度优先或者宽度优先策略 深度优先集成方法 深度优先从最左边分支自上而下开始测试并向上结合 测试完一个分支后再测试下一个分支 如图测试顺序为
  • 渗压计工作原理及选型

    渗压计适合埋设在水工建筑物和基岩内 或安装在测压管 钻孔 堤坝 管道或压力容器中 以测量孔隙水压力或液位 主要部件均采用特殊钢材制造 适合在各种恶劣环境中使用 一般型号后缀为标准型 可以为低量程型和为通气 差压 型 另可根据客户要求提供高压
  • Hypertable 简介 一个 C++ 的Bigtable开源实现

    1 Introduction 随着互联网技术的发展 尤其是云计算平台的出现 分布式应用程序需要处理大量的数据 PB级 在一个或多个云计算平台中 成千上万的计算主机 如何保证数据的有效存储和组织 为应用提供高效和可靠的访问接口 并且保持良好的
  • scss中的样式复用:继承;占位符;混合宏

    文章目录 一 使用 extend实现样式复用 继承 二 使用占位符实现样式复用 占位 三 使用混合宏实现样式复用 混合宏 四 参数运算符 待更新 类名复用 未验证 待更新 react中使用sass 了解css in js解决方案 在reac
  • 【Pandas 数据查找函数 详解】

    本文介绍了Pandas数据查找常用函数 掌握了这些函数的应用 让你在数据处理时 手到擒来 游刃有余 目录 一 查找数据位置 s str find 和s str index 函数 二 数据的查找判断 1 判断开头或结尾是否是指定字符串s st
  • 保姆级Obsidian学习教程【超完整,小白必备】

    前言 本篇文章学习视频来源 沙牛obsidian优质课程 学 习 软 件 obsidian 课 程 对 应 资 源 云盘资源 说 明 Obsidian是基于Markdown文件的本地知识管理软件 并且开发者承诺Obsidian对于个人使用者
  • java integer long 转换_long(Long)与int(Integer)之间的转换

    1 将long型转化为int型 这里的long型是基础类型 long a 10 int b int a 2 将Long型转换为int 型的 这里的Long型是包装类型 Long a 10 int b a intValue 3 将Long型转
  • 编译GDB --enable-targets=all --enable-64-bit-bfd

    这次尝试才用一种新的BLOG发帖 大家都可以把要发到BLOG的文章投递到MAILLIST 然后大家REVIEW 等REVIEW的差不离了 再发到BLOG上 欢迎大家帮忙review 编译GDB teawater hellogcc 1 取得源
  • 2.1python中的赋值运算符和比较运算符

    在程序中 使用赋值运算符可以帮助我们更加高效的完成一些需要多行程序完成的工作 使用比较运算符可以让程序员通过比较更加明白所编程序是否与自己的所思考的相同 1 首先 我们先看一下赋值运算符的具体内容 如下 赋值运算符符号 含义 a b b a
  • 微信H5(公众号)跳转微信小程序实现及其传参

    1 使用微信开放标签 wx open launch weapp 跳转
  • Linux车机平台pulseaudio多alsasink配置

    https www freedesktop org wiki Software PulseAudio 官网上的介绍是这样的 pulseaudio 是一个POSIX操作系统上的声音系统 是音频应用的代理 它允许你对音频数据 在从应用传递到硬件