转:https://blog.csdn.net/wh_19910525/article/details/12749293

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

一、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通路。

二、配置声音通路

这里先绕开代码,先用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的路径选择。要打开红色通路,则进行如下操作:

  1. alsa_amixer cset numid=3,iface=MIXER,name='Left Input PGA Switch' 1
  2. alsa_amixer cset numid=73,iface=MIXER,name='Left Input PGA LINPUT1 Switch' 1
  3. alsa_amixer cset numid=69,iface=MIXER,name='Left Input Mixer Input PGA Switch' 1
  4. alsa_amixer cset numid=58,iface=MIXER,name='Left Output Mixer Left Input Mixer Switch' 1
  5. alsa_amixer cset numid=43,iface=MIXER,name='LINEOUT1 Switch' 1

numid3 : 使能Left Input PGA;

numid73: 令Left Input PGA选择LINPUT1输入源;

numid69: 令Left Input Mixer选择Left Input PGA输入源;

numid58: 令Left Output Mixer选择Left Input Mixer输入源;

numid43: 使能LINEOUT1。

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

三、AUDIO PATHS代码实现

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

1、实现Left Output Mixer的输入源选择:

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

2、添加名为Left Output Mixer的widget:

  1. static const struct snd_soc_dapm_widget wm8900_dapm_widgets[] = {
  2. /* Externally visible pins */
  3. ......省略......
  4. /* Input */
  5. ......省略......
  6. SND_SOC_DAPM_MIXER("Left Input Mixer", WM8900_REG_POWER2, 5, 0,
  7. wm8900_linmix_controls,
  8. ARRAY_SIZE(wm8900_linmix_controls)),
  9. ......省略......
  10. /* Output */
  11. ......省略......
  12. SND_SOC_DAPM_MIXER("Left Output Mixer", WM8900_REG_POWER3, 3, 0,
  13. wm8900_loutmix_controls,
  14. ARRAY_SIZE(wm8900_loutmix_controls)),
  15. ......省略......
  16. };

【MIXER:多个输入源混合成一个输出,用SND_SOC_DAPM_MIXER定义这个widget,类型为snd_soc_dapm_mixer;
  MUX:多路选择器,多路输入,但只能选择一路作为输出,用SND_SOC_DAPM_MUX定义这个widget,类型为snd_soc_dapm_mux;
  PGA:单路输入,单路输出,带gain调整的部件,用SND_SOC_DAPM_PGA定义这个widget,类型为snd_soc_dapm_pga。】

3、搭建音频路径:

  1. static const struct snd_soc_dapm_route audio_map[] = {
  2. /* Inputs */
  3. ......省略......
  4. /* Outputs */
  5. ......省略......
  6. {"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"},
  7. ......省略......
  8. };

前面的"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更清晰一点:

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

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

4、将kcontrols、widgets和route串联起来:

  1. static int wm8900_add_widgets(struct snd_soc_codec *codec)
  2. {
  3. snd_soc_dapm_new_controls(codec, wm8900_dapm_widgets,
  4. ARRAY_SIZE(wm8900_dapm_widgets));
  5. snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
  6. snd_soc_dapm_new_widgets(codec);
  7. return 0;
  8. }

四、dapm kcontrol

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

snd_soc_dapm_route的定义:

  1. /*
  2. * DAPM audio route definition.
  3. *
  4. * Defines an audio route originating at source via control and finishing
  5. * at sink.
  6. */
  7. struct snd_soc_dapm_route {
  8. const char *sink;
  9. const char *control;
  10. const char *source;
  11. };

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为例,简单介绍调用过程:

  1. snd_soc_dapm_add_routes
  2. -->snd_soc_dapm_add_route [添加一个snd_soc_dapm_mixer类型的route]
  3. -->分配snd_soc_dapm_path内存,初始化这个path,令path->sink=sink,path->source=source
  4. dapm_connect_mixer
  5. -->检查操作行为control是否存在:从sources中找到name匹配的control
  6. path->name = control.name;接着将这个path的sink、source和list插入到各自的链表中
  7. (注:path三大要素中的两个都已经得到即sink和source,差kcontrol。)
  8. dapm_set_path_status
  9. -->判定指定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是否使能,看起来不太合理。后面会解决这个疑问。这里我理解可能还有些偏差或有遗漏的地方。】

3、snd_soc_dapm_new_widgets()

  1. snd_soc_dapm_new_widgets
  2. -->遍历dapm_widgets链表,找到为switch、mixer类型的widget
  3. w->power_check = dapm_generic_check_power;
  4. dapm_new_mixer
  5. -->/* add dapm control with long name.
  6. * for dapm_mixer this is the concatenation of the
  7. * mixer and kcontrol name.
  8. * for dapm_mixer_named_ctl this is simply the
  9. * kcontrol name.
  10. */
  11. snd_soc_dapm_mixer dapm kcontrol:path->long_name = sink->name + kcontrol->name
  12. snd_soc_dapm_mixer_named_ctl dapm kcontrol:path->long_name = kcontrol->name
  13. snd_soc_cnew为path分配一个kcontrol,置这个kcontrol的name为path->long_name
  14. snd_ctl_add添加这个dapm kcontrol
  15. dapm_power_widgets
  16. -->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。

4、snd_soc_dapm_path[补充]

  1. /* dapm audio path between two widgets */
  2. struct snd_soc_dapm_path {
  3. char *name;
  4. char *long_name;
  5. /* source (input) and sink (output) widgets */
  6. struct snd_soc_dapm_widget *source;
  7. struct snd_soc_dapm_widget *sink;
  8. struct snd_kcontrol *kcontrol;
  9. /* status */
  10. u32 connect:1;  /* source and sink widgets are connected */
  11. u32 walked:1;   /* path has been walked */
  12. struct list_head list_source;
  13. struct list_head list_sink;
  14. struct list_head list;
  15. };

以上的过程分析非常简略,其实一切都是围绕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]。

五、如何触发path connect

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

回到<三、AUDIO PATHS代码实现>复习一下"Left Input Mixer Switch"的kcontrol写法:SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0)。

1、SOC_DAPM_SINGLE宏定义:

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

SOC_SINGLE宏定义:

  1. #define SOC_SINGLE(xname, reg, shift, max, invert) /
  2. {   .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, /
  3. .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,/
  4. .put = snd_soc_put_volsw, /
  5. .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

2、snd_soc_dapm_put_volsw

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

这是底层方法差异,往上应该没必要说了,与snd_kcontrol探究一致。

linux音频 DAPM之二:audio paths与dapm kcontrol的更多相关文章

  1. Linux音频编程(二)声卡介绍

    一.声卡 1.声卡是audio interface,它含有hardware buffer,而这个hardware buffer是在声卡里面,不是内存.声卡的缓存是环状的,则ALSA中是将数据分成连续的 ...

  2. linux音频alsa-uda134x驱动文档阅读之一转自http://blog.csdn.net/wantianpei/article/details/7817293

    前言 目前,linux系统常用的音频驱动有两种形式:alsa oss alsa:现在是linux下音频驱动的主要形式,与简单的oss兼容.oss:过去的形式而我们板子上的uda1341用的就是alsa ...

  3. Linux音频编程指南

    Linux音频编程指南 虽然目前Linux的优势主要体现在网络服务方面,但事实上同样也有着非常丰富的媒体功能,本文就是以多媒体应用中最基本的声音为对象,介绍如何在Linux平台下开发实际的音频应用程序 ...

  4. Linux音频编程指南(转)

    转自: http://www.ibm.com/developerworks/cn/linux/l-audio/ Linux音频编程指南 虽然目前Linux的优势主要体现在网络服务方面,但事实上同样也有 ...

  5. Linux学习之CentOS(二十二)--单用户模式下修改Root用户的密码

    在上一篇随笔里面详细讲解了Linux系统的启动过程 (Linux学习之CentOS(二十一)--Linux系统启动详解),我们知道Linux系统的启动级别一共有6种级别,通过 /etc/inittab ...

  6. Linux音频编程

    1. 背景 在<Jasper语音助理介绍>中, 介绍了Linux音频系统, 本文主要介绍了Linux下音频编程相关内容. 音频编程主要包括播放(Playback)和录制(Record), ...

  7. Linux音频驱动-ALSA概述

    概述 ALSA(Advanced Linux Sound Architecture)是linux上主流的音频结构,在没有出现ALSA架构之前,一直使用的是OSS(Open Sound System)音 ...

  8. Linux shell脚本编程(二)

    Linux shell脚本编程(二) 练习:求100以内所有偶数之和; 使用至少三种方法实现; 示例1: #!/bin/bash # declare -i sum=0 #声明一个变量求和,初始值为0 ...

  9. 【jquery】一款不错的音频播放器——Amazing Audio Player

    前段时间分享了一款视频播放器,点击这里.今天介绍一款不错的音频播放器——Amazing Audio Player. 介绍: Amazing Audio Player 是一个使用很方便的 Windows ...

随机推荐

  1. 浅谈web前端安全

    单纯地在你的客户端弹出信息只是类似于迫使你在自己的房间脱衣服--没人看得到,自然也不算啥恶意行为.那么如果我把你的信息通过脚本发送到我的服务器保存起来呢?先放心,我不打算这么做,也没那笔闲钱去购置一个 ...

  2. mySQL 开启事件存储过程

    怎样在Navicat中设置,是数据库按照记录中的日期更新状态字段 其实这个很常用,比如你网站里的某条记录的日期——比如说数据库中某条活动记录的审核日期字段已经过期,亦即当前时间已经超过审核日期,那么定 ...

  3. linux无密登录

    ssh-keygen -t rsa ssh-copy-id -i ~/.ssh/id_rsa.pub bigdata@cor2

  4. rem的使用方法

    首先写入一下代码 <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" cont ...

  5. 关于在Java中链接SQLServer数据库中失败的原因分析

    首先声明:笔者是Java的初学者,并且一值是走在自学的道路上,长久以来只有“度娘”相伴.(加入了各种Java学习群,基本没有热心帮人解决问题的.可以理解-_-!!!)大神级的人物就不必看拙文了,没有什 ...

  6. hdu 3622(二分+2-sat判断可行性)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3622 思路:二分是容易想到的,由于题目中有明显的矛盾关系,因此可以用2-sat来验证其可行性.关键是如 ...

  7. day16 递归函数:一般的递归方法

    一.递归,在一个函数里面 调用 自己: pycharm的最大递归次数是997 查看与修改方法: # # print(sys.getrecursionlimit()) # sys.setrecursio ...

  8. php中使用curl来post一段json数据

    场景:在调用第三方接口时经常需要使用到curl进行数据交互,在初次使用时遇到一些小问题,记录下来随时查阅. 封装curl相关方法便于使用,方法如下: /** * @param $url * @para ...

  9. CMU-15445 LAB3:事务隔离,two-phase locking,锁管理器

    概述 本lab将实现一个锁管理器,事务通过锁管理器获取锁,事务管理器根据情况决定是否授予锁,或是阻塞等待其它事务释放该锁. 背景 事务属性 众所周知,事务具有如下属性: 原子性:事务要么执行完成,要么 ...

  10. boost::lockfree::spsc_queue

    #include <boost/thread/thread.hpp> #include <boost/lockfree/spsc_queue.hpp> #include < ...