【DAPDM 二】--- Audio paths 与 DAPM Kcontrol

2023-10-12 16:30
文章标签 audio kcontrol dapm paths dapdm

本文主要是介绍【DAPDM 二】--- Audio paths 与 DAPM Kcontrol,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【DAPDM 二】--- Audio paths 与 DAPM Kcontrol

    • 二、Audio paths 与 DAPM Kcontrol
      • 2.1 Audio Paths Overview
      • 2.2 配置声音通路
      • 2.3 Audio Paths 代码实现
        • 2.3.1 实现 Left Output Mixer 的输入源选择
        • 2.3.2 添加名为Left Output Mixer的widget
        • 2.3.3 搭建音频路径
        • 2.3.4 将kcontrols、widgets 和 route串联起来
      • 2.4 dapm kcontrol
      • 2.5 如何触发path connect


本文参考自 https://blog.csdn.net/azloong/article/details/6334922,欢迎前去学习原文

在用alsa_amixer controls时,除了我们之前提到的snd_soc_add_controls添加的kcontrols外,还有一些多出来的controls。
其实多出来的那些都是属于dapm kcontrol,主要用于切换音频路径。

二、Audio paths 与 DAPM Kcontrol

2.1 Audio Paths Overview

以标准内核2.6.32的wm8900 codec为例。

先看AUDIO PATHS OVERVIEW,
红色线路是
LINPUT1(Left Input) -> LEFT INPUT PGA -> LEFT INPUT MIXER -> LEFT OUTPUT MIXER -> LINEOUT1L
表示从LINPUT输入的信号通过这条路径送到 LINEOUT 输出,
再具现一点,就是录音信号直接放到SPK播出。
在这条路径上,有三个带 + 号的圆圈,那就是多路混合器 mixer,用于切换输入源 或 将多个输入源混合输出。

土黄色部分为LEFT INPUT PGA :可选择LINPUT1、LINPUT2和LINPUT3;
绿色部分是LEFT INPUT MIXER :可选择INPUTPGA、LINPUT2、LINPUT3和AUX/LCOM;
蓝色部分是LEFT OUTPUT MIXER:可选择INPUTMIXER、LINPUT3、AUX/LCOM和LEFT DAC等。

配置声音通路时,主要是对mixer做切换输入源操作。
如要实现Playback,则需要打通DAC -> OUTPUT MIXER -> LINEOUT通路。
在这里插入图片描述


2.2 配置声音通路

这时先绕开代码,先用alsa_amixer实际操作切换声音路径(以上图的红色路径为例),有个直观印象。

~ # alsa_amixer controls
numid=1,iface=MIXER,name=‘Mic Bias Level’

…省略…

numid=68,iface=MIXER,name=‘Left Input Mixer AUX Switch’
numid=69,iface=MIXER,name=‘Left Input Mixer Input PGA Switch’
numid=66,iface=MIXER,name=‘Left Input Mixer LINPUT2 Switch’
numid=67,iface=MIXER,name='Left Input Mixer LINPUT3 Switch’

numid=73,iface=MIXER,name=‘Left Input PGA LINPUT1 Switch’
numid=74,iface=MIXER,name=‘Left Input PGA LINPUT2 Switch’
numid=75,iface=MIXER,name=‘Left Input PGA LINPUT3 Switch’
numid=3,iface=MIXER,name='Left Input PGA Switch’

numid=2,iface=MIXER,name=‘Left Input PGA Volume’
numid=4,iface=MIXER,name='Left Input PGA ZC Switch’

numid=57,iface=MIXER,name=‘Left Output Mixer AUX Bypass Switch’
numid=60,iface=MIXER,name=‘Left Output Mixer DACL Switch’
numid=56,iface=MIXER,name=‘Left Output Mixer LINPUT3 Bypass Switch’
numid=58,iface=MIXER,name=‘Left Output Mixer Left Input Mixer Switch’
numid=59,iface=MIXER,name='Left Output Mixer Right Input Mixer Switch’

…省略…

以上打印出所有的codec kcontrols,
以之前对应的颜色区分了INPUT PGA、INPUT MIXER、OUTPUT MIXER三个mixer的路径选择。
要打开红色通路,则进行如下操作:

alsa_amixer cset numid=3,iface=MIXER,name='Left Input PGA Switch' 1---> numid3 : 使能Left Input PGA;alsa_amixer cset numid=73,iface=MIXER,name='Left Input PGA LINPUT1 Switch' 1---> numid73: 令Left Input PGA选择LINPUT1输入源;alsa_amixer cset numid=69,iface=MIXER,name='Left Input Mixer Input PGA Switch' 1---> numid69: 令Left Input Mixer选择Left Input PGA输入源;alsa_amixer cset numid=58,iface=MIXER,name='Left Output Mixer Left Input Mixer Switch' 1---> numid58: 令Left Output Mixer选择Left Input Mixer输入源;alsa_amixer cset numid=43,iface=MIXER,name='LINEOUT1 Switch' 1---> numid43: 使能LINEOUT1。

这里仅以最基本的alsa-util来配置回路,
在实际应用中,可自己实现alsa mixer或编写asound.conf虚拟出不同的devices,
前者较典型见Android2.2的ALSAControl.cpp,后者在之后的章节会提一下。
我偏向于asound.conf实现,灵活性较高,不同的codec改动asound.conf就行了。


2.3 Audio Paths 代码实现

知道声音通路如何配置后,回到驱动代码实现。
音频路径功能与普通的snd_kcontrol差不多,但写法稍微复杂一点,以’Left Output Mixer Left Input Mixer Switch’为例说明。

2.3.1 实现 Left Output Mixer 的输入源选择

通过 SOC_DAPM_SINGLE 配置宏,在调用时会 实现写寄存器。

static const struct snd_kcontrol_new wm8900_loutmix_controls[] = {SOC_DAPM_SINGLE("LINPUT3 Bypass Switch", WM8900_REG_LOUTMIXCTL1, 7, 1, 0),SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 7, 1, 0),SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0),SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 3, 1, 0),SOC_DAPM_SINGLE("DACL Switch", WM8900_REG_LOUTMIXCTL1, 8, 1, 0),
};

2.3.2 添加名为Left Output Mixer的widget
static const struct snd_soc_dapm_widget wm8900_dapm_widgets[] = {
/* Externally visible pins */
......省略......
/* Input */
......省略......
SND_SOC_DAPM_MIXER("Left Input Mixer", WM8900_REG_POWER2, 5, 0,wm8900_linmix_controls,ARRAY_SIZE(wm8900_linmix_controls)),
......省略......
/* Output */
......省略......
SND_SOC_DAPM_MIXER("Left Output Mixer", WM8900_REG_POWER3, 3, 0,wm8900_loutmix_controls,ARRAY_SIZE(wm8900_loutmix_controls)),
......省略......
};
  1. MIXER
    多个输入源混合成一个输出,用 SND_SOC_DAPM_MIXER 定义这个widget,
    类型为snd_soc_dapm_mixer;

  2. MUX
    多路选择器,多路输入,但只能选择一路作为输出,
    用SND_SOC_DAPM_MUX定义这个widget,类型为snd_soc_dapm_mux;

  3. PGA
    单路输入,单路输出,带gain调整的部件,
    用SND_SOC_DAPM_PGA定义这个widget,类型为snd_soc_dapm_pga。


2.3.3 搭建音频路径
static const struct snd_soc_dapm_route audio_map[] = {/* Inputs */......省略....../* Outputs */......省略......{"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"},......省略......
};

前面的 “Left Output Mixer” 表示操作目的对象 sink,对应 widgets 中的名为 Left Output Mixer 的 widget;
中间的"Left Input Mixer Switch"表示操作行为control,对应wm8900_loutmix_controls中的名为Left Input Mixer Switch的kcontrol;
后面的"Left Input Mixer"表示操作源对象source,对应widgets中的名为Left Input Mixer的widget。

合起来的意思:通过动作 “Left Input Mixer Switch” 将输入源 “Left Input Mixer” 送到目的混合器 “Left Output Mixer” 输出。

这里我的解析有点拗口,直接看dapm.txt更清晰一点:

e.g., from the WM8731 output mixer (wm8731.c)
The WM8731 output mixer has 3 inputs (sources)1. Line Bypass Input2. DAC (HiFi playback)3. Mic Sidetone Input
Each input in this example has a kcontrol associated with it (defined in example
above) and is connected to the output mixer via it's kcontrol name. We can now
connect the destination widget (wrt audio signal) with it's source widgets./* output mixer */{"Output Mixer", "Line Bypass Switch", "Line Input"},{"Output Mixer", "HiFi Playback Switch", "DAC"},{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
So we have :-Destination Widget  <=== Path Name <=== Source Widget
Or:-Sink, Path, Source
Or :-"Output Mixer" is connected to the "DAC" via the "HiFi Playback Switch".
When there is no path name connecting widgets (e.g. a direct connection) we
pass NULL for the path name.

注意:
dapm kcontrol名称 = 目的对象sink名称 + 操作行为control名称,
即’Left Output Mixer Left Input Mixer Switch’,操作源对象source名称被忽略。之后深入源码分析。


2.3.4 将kcontrols、widgets 和 route串联起来
static int wm8900_add_widgets(struct snd_soc_codec *codec)
{snd_soc_dapm_new_controls(codec, wm8900_dapm_widgets, ARRAY_SIZE(wm8900_dapm_widgets));snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));snd_soc_dapm_new_widgets(codec);return 0;
} 

2.4 dapm kcontrol

好了,如果仅仅是为了实现audio paths的话,了解以上应该是足够的了。
但人生的追求不应该那么简单,我们要从根源处剖析问题,以后再审视相似的问题时,站的高度也不同。

snd_soc_dapm_route的定义:

/** DAPM audio route definition.* Defines an audio route originating at source via control and finishing at sink.*/
struct snd_soc_dapm_route {const char *sink;const char *control;const char *source;
};

sink 为目的对象名称,control 为操作行为名称,source 为源对象名称。

为方便起见,先定义一些名词:
source 为输入源,在widgets中定义,如"Left Input Mixer";
sources 为输入源选择,如 wm8900_loutmix_controls
control 为操作行为,具体在sources数组中定义,如 “Left Input Mixer Switch”;
sink 为目的混合器,在widgets中定义,如" Left Output Mixer ";
route 为音频路径,连接source、control和sink,如 {“Left Output Mixer”, “Left Input Mixer Switch”, “Left Input Mixer”}


  1. snd_soc_dapm_new_controls()
    函数进行widget内存分配、链表初始化工作,比较简单。

  2. snd_soc_dapm_add_routes()
    该函数的注释值得一看:
    snd_soc_dapm_add_routes - Add routes between DAPM widgets.
    Connects 2 dapm widgets together via a named audio path.
    The sink is the widget receiving the audio signal, whilst the source is the sender of the audio signal.

以MIXER类型 snd_soc_dapm_mixer 为例,简单介绍调用过程:

snd_soc_dapm_add_routes-->	snd_soc_dapm_add_route [添加一个snd_soc_dapm_mixer类型的route]-->	分配snd_soc_dapm_path内存,初始化这个path,令path->sink=sink,path->source=source dapm_connect_mixer-->	检查操作行为control是否存在:从sources中找到name匹配的controlpath->name = control.name;接着将这个path的sink、source和list插入到各自的链表中(注:path三大要素中的两个都已经得到即sink和source,差kcontrol。)dapm_set_path_status-->	判定指定source是否被选择了,是则置p->connect = 1;否则置p->connect = 0

以上的核心部分是 path 的建立和链表插入操作,
如果在 sink->kcontrols 中找不到 name 相匹配的 kcontrol,则这个 route 无效,不创建 path。
这里 path->name 为找到的 sink->kcontrol.name。

其实 dapm_set_path_status 挺困惑的,path 的 connect 理应包含两个操作:
(1) 是指定 source 的选择
(2) 是sink本身的使能。
但这里 connect 状态仅仅是检查 source 是否选择而已,不检查指定sink是否使能,看起来不太合理。

后面会解决这个疑问。
这里我理解可能还有些偏差或有遗漏的地方。

  1. snd_soc_dapm_new_widgets()
snd_soc_dapm_new_widgets--->遍历dapm_widgets链表,找到为switch、mixer类型的widgetw->power_check = dapm_generic_check_power;dapm_new_mixer--->/* add dapm control with long name.* for dapm_mixer this is the concatenation of the* mixer and kcontrol name.* for dapm_mixer_named_ctl this is simply the* kcontrol name.*/snd_soc_dapm_mixer dapm kcontrol:			path->long_name = sink->name + kcontrol->namesnd_soc_dapm_mixer_named_ctl dapm kcontrol:	path->long_name = kcontrol->namesnd_soc_cnew为path分配一个kcontrol,置这个kcontrol的name为path->long_namesnd_ctl_add添加这个dapm kcontroldapm_power_widgets--->Scan each dapm widget for complete audio path.A complete path is a route that has valid endpoints i.e.:-

经过 snd_soc_dapm_new_widgets(),终于为 snd_soc_dapm_mixer 类型的 widget 建立用于 route 切换的 dapm kcontrol,
使得 alsa_amixer 可以通过控制这些 dapm kcontrol 来达到音频通路切换的目的。

注:SND_SOC_DAPM_MIXER 和 SND_SOC_DAPM_MIXER_NAMED_CTL 建立的 widget 仅体现在 dapm kcontrol 的名字上面,前者为 sink->name + kcontrol->name,后者简单的为 kcontrol->name。

  1. snd_soc_dapm_path [补充]
/* dapm audio path between two widgets */
struct snd_soc_dapm_path {char *name;char *long_name;/* source (input) and sink (output) widgets */struct snd_soc_dapm_widget *source;struct snd_soc_dapm_widget *sink;struct snd_kcontrol *kcontrol;/* status */u32 connect:1;	/* source and sink widgets are connected */u32 walked:1;	/* path has been walked */struct list_head list_source;struct list_head list_sink;struct list_head list;
};

以上的过程分析非常简略,其实一切都是围绕 path 展开的。
可以把重点放在 path 的分析上面,搞懂 path 数据,基本就能理解这个 dapm kcontrol 的一切了。

总的来说:

path->name = sink->kcontrol[i].name,上例是"Left Input Mixer Switch";

path->long_name = sink->name + sink->kcontrol[i].name,上例是"Left Output Mixer Left Input Mixer Switch";

path->source = source,上例是名为"Left Input Mixer"的widget;

path->sink = sink,上例是名为"Left Output Mixer"的widget;

path->connect:通道connect状态,根据sink->kcontrol[i]判断。

path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, path->long_name);可见path->kcontrol对应sink->kcontrol[i],但名为path->long_name。因此上层可以通过path->long_name找到对应的sink->kcontrol[i]。


2.5 如何触发path connect

dapm kcontrol 的触发很大程度上可以参考 snd_kcontrol 探究,但更为复杂。
当上层触发 dapm kcontrol 时,会做两个重要动作:
1是切换音频通路,这与普通的kcontrol做法基本一致;
2是使能dapm widget(power up/down),
这就是分歧之处。

回到本文《Chapter 2.3 Audio Paths 代码实现》
复习一下"Left Input Mixer Switch"的kcontrol写法:
SOC_DAPM_SINGLE(“Left Input Mixer Switch”, WM8900_REG_BYPASS1, 7, 1, 0)。

  1. SOC_DAPM_SINGLE宏定义
/* dapm kcontrol types */
#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) /
{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, /.info = snd_soc_info_volsw, /.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, /.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }

SOC_SINGLE宏定义:

#define SOC_SINGLE(xname, reg, shift, max, invert) /
{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, /.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,/.put = snd_soc_put_volsw, /.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }

从这里就可以看出,dapm kcontrol 跟普通的 kcontrol 不同之处,以 put 为例:
dapm kcontrol:.put = snd_soc_dapm_put_volsw
kcontrol:.put = snd_soc_put_volsw

  1. snd_soc_dapm_put_volsw
snd_soc_dapm_put_volsw-->dapm_mixer_update_power-->snd_kcontrol_chip-->找到dapm kcontrol所在的widget(也就是操作目的对象sink)-->snd_soc_test_bits-->Tests a register with a new value and checks if the new value is different from the old value. dapm_power_widgets-->power up/down对象widget,更底层可追溯到dapm_seq_run_coalesced检查是否有widget->event [这里不分析Event的情况,继续往下走]snd_soc_update_bits-->根据dapm kcontrol:SOC_DAPM_SINGLE定义的reg、shift和max设置音频通路,方法与普通的kcontrol一样

正在Audio 学习的路上,

送给自已的话:
你走过的路、见过的人、看过的书,学过的东西,最终都会回馈到你的身上。
做自已想做且能做的事,不要让生活留下遗憾和后悔。
不求结果,但求问心无愧!

Date:2019/09/22 - 23:12

这篇关于【DAPDM 二】--- Audio paths 与 DAPM Kcontrol的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/197054

相关文章

Apple quietly slips WebRTC audio, video into Safari's WebKit spec

转自:http://www.zdnet.com/article/apple-quietly-slips-webrtc-audio-video-into-safaris-webkit-spec/?from=timeline&isappinstalled=0 http://www.zdnet.com/article/apple-quietly-slips-webrtc-audio-video-

LLM系列 | 38:解读阿里开源语音多模态模型Qwen2-Audio

引言 模型概述 模型架构 训练方法 性能评估 实战演示 总结 引言 金山挂月窥禅径,沙鸟听经恋法门。 小伙伴们好,我是微信公众号《小窗幽记机器学习》的小编:卖铁观音的小男孩,今天这篇小作文主要是介绍阿里巴巴的语音多模态大模型Qwen2-Audio。近日,阿里巴巴Qwen团队发布了最新的大规模音频-语言模型Qwen2-Audio及其技术报告。该模型在音频理解和多模态交互

Usb Audio Device Descriptor(10) Hid Device

对于 Standard Interface Descriptor, 当 bInterfaceClass=0x03时,即为HID设备。Standard Interface Descriptor如下 struct usb_standard_interface_descriptor{U8 bLength; /*Size of this descriptor in bytes*/U8 bDescrip

Android rk3399 UAC(USB Audio)开发笔记

一、UAC有1.0和2.0,因Windows对2.0支持不好,我使用的是UAC1.0驱动 内核配置:CONFIG_USB_CONFIGFS_F_UAC1          ---这个宏配置无需物理codec,使用虚拟 alsa codec  驱动路径:"kernel\drivers\usb\gadget\function\f_uac1.c" 内核配置:CONFIG_USB_CONFIGFS_

大数据Java基础-JAVA IO 9】java IO流 (九) Path、Paths、Files的使用

1.NIO的使用说明: >Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO AP。 >NIO与原来的IO同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作。 >NIO将以更加高效的方式进行文件的读写操作。 >随着 JDK 7 的发布,Java对N

Win10 - 即插即用的external audio device detected问题

问题     有些牌子的笔记本,在win10下每次插入外设耳机,都会跳出带有 external audio device detected 字样的音频输出设备选择框需要选择 方案     1、在开始菜单选择 运行 ,输入 regedit 后回车打开注册表     2、在注册表中定位到 HKEY_CURRENT_USER\SOFTWARE\Realtek\Audio\RtkNGUI64

Audio Effect

Android:AudioEffect——音乐特效控制 https://blog.csdn.net/qq_42192693/article/details/105047003 AudioEffect构造流程跟踪 & 音效库实现(native侧) https://blog.csdn.net/wkw1125/article/details/65632960?utm_medium=distribu

Android audio debug

dumpsys media.audio_flinger dumpsys media.audio_policy dumpsys audio

LeetCode 63 Unique Paths II

题意: 给出一个带有障碍物的棋盘,每次行动向下或向右移动一格,求从左上角到右下角有几种方案。 思路: 简单dp题,假设dp[i][j]表示第i行第j列的方案数,那么状态转移方程就为 dp[i][j] = dp[i - 1][j] + dp[i][j - 1] 。 注意下边界条件就好了,而且对于障碍物,直接把dp清零即可。 可以发现这个dp只和当前行和上一行有关,进而做空间优化,用一

LeetCode 62 Unique Paths

题意: 一个n*m的棋盘,每次行动只能向下或者向右走1格,求从左上角走到右下角有几种不同的方案数。 思路: 因为行动只能向下向右,所以总步数是一定的,即n - m + 2步。那么问题就变成了这里面的哪几步是向下的,就是组合数了,即从n - m + 2个中选n - 1个的组合数。 题目里说的n和m值太夸张了,因为他的函数返回int……所以肯定很小。 代码: class S