如何win7一键切换音频输出出设备切换

音频输出设备不能自动切换【windows10吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:277,932贴子:
音频输出设备不能自动切换收藏
系统win10 64位 1511主板技嘉b85m-d3v-a扬声器是显示器的(hdmi输出)
在线安装windows10只需三步即可完成!免激活,集成万能驱动,兼容各类电脑实现一键装机!
孤独跟夺取生命的死神一样慢慢向我走近,让我清晰的看到旋转扭曲的木杖上嵌着沾染鲜血的镰刀,仿佛下一秒就划破喉咙,撕裂身体,把我带进无尽的炼狱独守漆黑。
开始是前面板耳机不能用,后来在网上找到了解决方法,现在新问题又出现了网上说禁止掉前面板检测,果然管用,但是现在貌似直接就不知道有耳机插入,完全不知道自动切换啊
禁止掉前面板检测后确实插入耳机能用了,就是不能自动切换,没检测到一样,需要手动切换播放设备,取掉耳机也是一样,要手动切换扬声器
大神们还没起床吗?
声卡驱动没问题?
前几天刚买的。以前没用过iPhone。所以新申的ID。却无法在app store里下载应用
额,这个需要自己找一下驱动了,之前看到过这情况,缺少了驱动
老机箱?前面板是AC97的吧
这种情况大部分都是驱动问题…
苏宁易购小米windows10,正品行货+百城半日达,全国联保,服务一站式,全场为你而省!苏宁易购小米windows10,支持货到付款,让您省钱省心!
大部分还是会自己切换的,偶尔不会切换,就只点一下
登录百度帐号推荐应用ZEALER 科技生活第一站
让机械键盘完美适配你的Mac!已更新「一键切换音频输出」和 Keyboard Mastero 教程
日更新:Keyboard Maestro 的教程来了。帮大家实现一些简单的功能控制,做一点微小的事情。其实要实现功能键,很简单,Keyboard Maestro 内置了很多已经帮你做好的「Action」,直接拿来用就可以,楼主先上一个自己的功能按键图。最左边的组别是你所有定义的分组,中间是这个分组里包含的自定义动作。右边的「This hot key」选择你要更改的键盘按键,在下方的触发动作动作区域选择你需要的对应动作就可以了。创建一个新动作在中间下方选择加号,在右侧编辑区域选择自己需要的「Action」即可。你可以在「Action」的区域里搜索你所需要的动作,添加到右侧的编辑区即可。相比 Karabiner 用 xml 文件定义键盘而言,简单了很多。不过播放控制就只能限制于控制 iTunes 了,而且 Keyboard Maestro 对键盘的定义是全局的,意思是你修改了之后,所有的外接的、内置的键盘,只要你按了对应的按键,都会相应这个操作。日更新:Karabiner目前不支持macOS Sierra!!Karabiner目前不支持macOS Sierra!!Karabiner目前不支持macOS Sierra!!重要的事情说三遍。关于karabiner的适配情况可以关注Karabiner官网
目前的暂时替代办法是用Karabiner Element,但这个软件需要Xcode和Boost等等乱七八糟的支持,楼主好不容易鼓捣装上去了,但是不会用,实现的也仅仅只是一些简单的键位替代,遂放弃。目前楼主已转投Keyboard Maestro,他比Karabiner强大的多,不仅实现了macOS Sierra下的键盘快捷键设定,还大大提高了效率。最大的差别....Karabiner免费,Keyboard Maestro目前¥253.45,需要外币信用卡购买,但是中国的环境你懂得,不过依然大家如果条件足够请支持正版,这个软件值得这个价钱。我探索了之后,终于知道这个强大的键盘效率工具Keyboard Maestro之所以叫Maestro的原因。近期会带来Keyboard Maestro的教程,相关于Karabiner的教程依然会保留,并且会对Karabiner的xml进行简化工作。音频一键切换教程及代码应大家强烈要求,附上本人的音频输出切换代码。提前声明,直接照抄是不能使用的,建议先用文本编辑打开,修改后复制到Automator或者Keyboard Mastro里。另外是系统语言为中文的前提下,英文还请大家试着吃螃蟹。这是楼主在Keyboard Maestro下的快捷键设定截图和代码格式。---------插播上次更新的一段----------2.关于音频一键切换的问题,很多人和我一样遇到的问题是,在Automator可以完美运行,一到其他App就报错。为此我还特地去Apple Store请教了天(sha)才(bi)吧,他们甚至叫我重装系统......后来楼主自己解决了,是权限的问题。解决方法是系统偏好设置 - 安全性与隐私 - 隐私里,左边选辅助功能,然后在右边的app里全部打上钩。-----------------插播结束----------------- on run {input, parameters} tell application "System Preferences" activate set current pane to pane "com.apple.preference.sound" end tell tell application "System Events" tell application process "System Preferences" repeat until exists tab group 1 of window "声音" end repeat tell tab group 1 of window "声音" click radio button "输出" if (sele(和谐)cted of row 4 of table 1 of scroll area 1) then set sele(和谐)cted of row 3 of table 1 of scroll area 1 to true set devicesele(和谐)cted to "Generic USB Audio Device" else set sele(和谐)cted of row 4 of table 1 of scroll area 1 to true set devicesele(和谐)cted to "USB Audio DAC" end if end tell end tell end tell tell application "System Preferences" to quit return input end run(实在不懂为什么sel(河蟹)ect也是敏感词)大家要修改的地方我用不同颜色标记了出来。数字3和4对应设置里音频输出设备的序号,如下图。相信在社区里的都多多少少有英文阅读水平,都应该能大致看懂,楼主比较懒就不解释了......------------------------------------------日更新:因为很久都没登录ZEALER的账号,因此很久都没有回复,向提问的大家说声抱歉,也感谢 @Blues丶一直在评论区解答。1.这里要说一个错误,IKBC的键盘在mac下用Karabiner修改fn似乎是无解的,无论是用xml还是自带的Change Key。因为楼主一直没用fn键,因此忽略掉了这个错误,对大家说声抱歉。解决方案的话楼主应该不要可能继续研究了,毕竟是学生,有兴趣的同学可以试试用Keyboard Maestro看看能不能修改。2.关于音频一键切换的问题,很多人和我一样遇到的问题是,在Automator可以完美运行,一到其他App就报错。为此我还特地去Apple Store请教了天(sha)才(bi)吧,他们甚至叫我重装系统......后来楼主自己解决了,是权限的问题。解决方法是系统偏好设置 - 安全性与隐私 - 隐私里,左边选辅助功能,然后在右边的app里全部打上钩。3.关于发脚本的问题....这个我发了你也用不了,键盘的ID是每个人不一样的。有错误的话,还是再检查检查,看看O/o/0这三个字符有没有打错......大小写、中英文符号混淆之类的语法错误。------------------------------------------日更新:哎,无数个收藏的,就只有一个评论的,露珠有点受伤啊......昨晚又看了一遍OFFICE TOUR,老大改键盘用的应该是Keyboard Maestro,可以看做是Automator的增强版,通过结合AppleScript可以用来编写一些自动化的键盘操作,对于Keyboard Maestro我还在学习研究,但是用Karabiner实现的功能也已经足够丰富了。------------------------------------------ 以下为原文 ------------------------------------------首先一图晒桌面。市面上的机械键盘,大部分适配的都是Windows系统,所谓标榜有适配Mac的很多都是不完美,甚至只是给你换了个Command键帽。无论如何,不经过修改都不可能达到Apple自家的键盘那么方便。所以拿到机械键盘第一件事便是通过软件修改适配macOS。泡ZEALER的都应该看过OFFICE TOUR,视频里老大的机械键盘在macOS下用滑溜的一逼,想让你的机械键盘也和老大的一样甚至更牛逼吗,来,扔掉网上模糊和难懂的教程,我来带你飞。连接键盘时候的设置首先拿到机械键盘连上电脑,macOS会显示键盘设置助理,让你分别按左右Shift键的两个按键,照着按便是,这里不需要什么Tips。这里的选ANSI即可,无论你是87键或者104键,一般来说选这个都没问题。Command键的设置标准Windows键盘的最下方一行按键依次是Control – Windows – Alt - 空格 – Alt – Windows – Application – Control绝大部分的机械键盘的排列都是按照这个标准。标准Mac键盘的最下方一行按键依次是Control – Option(Alt) - Command - 空格 - Command – Option – Control如果直接把Windows键盘接到Mac上使用,那么键盘上的Windows键就成为了Command键,我们就会发现Command键和Option键的位置反过来了,如果你觉得不影响使用,可以不修改,但是楼主本着强迫症精神,也习惯了Mac键盘的布局,所以还是修改了位置。具体方法是,进入系统偏好设置 – 键盘,在右下角有个“修饰键”,点开之后选择你的键盘,在下方的Option键旁边选择Command,Command键选择Option,这样Windows键盘上的Command和Option的位置就和Mac键盘一样了。F1-F12按键更改Mac快捷键这里我们需要借助一个软件,Karabiner。这是一个更改键盘映射的免费软件,度娘一搜就有下载链接。安装之后,在状态栏会出现一个图标,点击Preferences。在Change Key的选详下,找到Change F1…F19 Key & Functional Key(懒得打了这里),点开,选择For PC keyboards,如果你选择第一个,则不需要按fn,选择第二个则需要按fn。在这里基本上F1 – F12的功能就已经和Mac的键盘基本没差了,亮度、声音、音乐控制、Mission Control和Launchpad的功能都可以实现。但是楼主继续本着强迫症精神,发现音乐控制是F7 F8 F9,而机械键盘的F1 - F12基本都是四个一组,你会发现,F8和F9的距离简直是令人发指,于是露珠愤然关掉了前面的勾,只留下了F3开启Mission Control和F4开启Launchpad的功能。当然你如果可以接受的话没问题,但是如果你和我一样是强迫症,那么继续往下看,我们一起走向进阶玩法。Application键改为Fn键不过在进阶玩法之前,请先看看键盘右下角Control键左边的那颗按键,如果写的是Fn,那么可以跳过这里,如果是Application键,则需要改为Fn键。如果你不知道Application键的话,请自行百度。修改的方法依然是用Karabiner,在Change Key里找到For PC Users,然后点开Change PC Application Key(Menu Key),然后勾上Application Key to Fn。Application键在Mac下是无法使用的,因此我们改为Fn。只不过Mac键盘的Fn键在键盘左下角,但是目前几乎没有任何一款机械键盘有这样的按键,因此这里是唯一的一点小瑕疵。还是老大那句话,几乎所有的产品都有可颠覆的空间。好了,到这里,你的机械键盘就基本和Mac键盘是一样的啦,如果不是强迫症,那么你就可以放心的使用了。但是世界上总有一些强迫症患者,例如我。先不说上面的F8与F9间隔一个宇宙的距离,再看键盘最右边,方向键上面的6颗按键,从来没用过,放着都是浪费,为什么不把他们改成Mac的快捷键?正好,Karabiner提供了一个自行映射的功能,通过编写xml文件来实现。进阶玩法个人设置成了右边三颗红色按键从左到右分别是上一首 – 暂停 – 下一首,蓝色则是静音 – 减小音量 – 增大音量,F12是一键倾倒废纸篓,PrtSc是一键音频切换,F1是一键截全屏,F2是区域截图。实现的方法很简单,跟着楼主慢慢来就可以,懂了之后你就可以完全自定义你的键盘。打开Karabiner,选择Misc & Uninstall选项卡,下面有个Open private.xml,点击这个之后会弹出一个文件夹,文件夹下是private.xml文件,用Mac的文本编辑打开。打开之后,会发现这个xml文件里有三句话。看不懂也不用管,我们把代码写在两个之间即可,下面放上我的代码,用它作为例子讲解如何自定义键盘映射。代码也是我从网上复制的,根据我自己的需求做了更改。代码分为三部分,第一部分是键盘声明,如下:vendorname里你可以任意起一个名字,想起什么都可以,我是用了键盘的名字。Vendorid填写的是你键盘的vendorid,那么怎么找到这个id呢?也很简单,点击状态栏上的Karabiner,选择Launch EventViewer。选择Devices,你的键盘vendorid就在这里了。这里的找productid和找vendorid的方法一样,productname我没改,因此不知道能不能改。至此,键盘声明完毕。第二部分,我也不知道叫什么,因为我也不是程序员(楼主是文科生)。name的话可以随便取一个名,就像是你整篇文章的标题。DeviceVendor就是你上面键盘声明的vendorname,DeviceProduct也和上面的productname一样,identifier不用管。第三部分,更改键盘映射。这里我分成了两小部分,首先是上面一部分,就是键盘按键的更改。我们拿一条例子来举例。什么autogen之类的你不用去管,我也不知道是什么,你只要按你的需求,改KeyCode::和ConsumerKeyCode::后面的按键代码就可以。那么,这些按键的代码是从哪里知道的呢?还是借助神器Karabiner的Launch EventViewer,就在Main的选项卡下,你随意按一个按键,是不是发现会出现了两行表格?最右边的misc一列下的KeyCode::就是我们要找的东西,这个就是你按下的这个按键原始的KeyCode。然后后面的ConsumerKeyCode::是你更改之后所要实现的按键,例如MUSIC_PREV就是音乐上一首。上面那条代码的意思就是,把Help键改为音乐控制的上一首按键。在这里我贴出一些常用的ConsumerKeyCode。MUSIC_PREV:上一首MUSIC_PLAY:播放/暂停MUSIC_NEXT:下一首VOLUME_MUTE:静音VOLUME_DOWN:减小音量VOLUME_UP:增大音量BRIGHTNESS_DOWN:亮度减小BRIGHTNESS_UP:亮度增大由于楼主是Mac外接显示器,因此我的xml里没有写亮度控制的代码上去。然后我们来看下面的部分。这是用一个按键代替组合按键。前面的Keycode::是你要更改的按键,后面的Keycode是组合键里最后按得键,ModifierFlag::是组合键前面按的键。例如上面这条代码就是把F12改成一键倾倒废纸篓。倾倒废纸篓的快捷键是Command Shift Del(和谐)ete,那么就将它的顺序倒过来写进代码里。后面的_L是左的意思,因为Command、Option、Control在空格两边都有,不写左右的话会报错,一定要写,只不过你写左或者右都没所谓。其他的一键代替组合键的方法,以此类推。代码编辑完毕后,关掉xml文件,回到Karabiner,先把下面Use prepared settings勾上,如果你的语法没有错误,Karabiner会退出一次。如果语法错误则会报错,重新打开xml文件进行修改。再次打开Karabiner,点Change Key,在第一行把勾打上,这个标题就是上面代码第二部分的name。关于一键音频输出切换,需要用到AppleScript脚本和Automator,看评论的人需要的多不多,如果人数较多我再来开个帖子详解。经过上面的修改,相信你的机械键盘已经完美适配Mac了,听着啪啪啪的感觉,是不是非常爽!如果还有什么问题不明白可以留言给露珠,露珠可以的话一定帮忙解决!
点击查看所有点赞用户
打开微信,点击顶部的“发现”,使用 “扫一扫”即可将网页分享到我的朋友圈。
需要才能回复Android Audio 音频输出设备切换 - CSDN博客
Android Audio 音频输出设备切换
扬声器输出和耳机输出是音频系统最基本的音频输出通路,实际应用中用户可根据自己需要随意切换,具体表现是通过插入耳机选择耳机输出,拔出耳机选择默认的扬声器输出。
图5-13 耳机插拔检测
& & & 如图5-13所示,扬声器与耳机输出切换的实现思路是内核底层驱动实现耳机插拔检测,然后通过uevent通信机制通知Java框架层,Java框架层通过Intent机制发送广播消息通知音频系统改变输出通道。下面介绍下具体实现方法[19]。
& & & 首先调用class_create函数创建一个switch类,然后调用device_create函数创建一个switch类从属设备h2w,再通过调用device_create_file函数创建h2w设备的名称和状态两个属性文件,于是在文件系统中便有了供用户空间访问的两个节点“/sys/class/switch/h2w/name”和“/sys/class/switch/h2w/state”。一个专用中断脚用于耳机插拔检测,当耳机插入或拔出时触发中断,更新h2w状态的变化并调用kobject_uevent_env函数发送一组字符串给Java框架层。Java框架层中启动UEventThread线程用于轮询并接收内核发上来的uevent数据。WiredAccessoryObserver继承了UEventObserver,在系统启动完成时便调用startObserving()启动监听h2w的状态变化,当内核发送上来的字符串匹配成功后执行onUEvent()进行处理,onUEvent()最终会利用Intent机制发送广播消息ACTION_HEADSET_PLUG。AudioService创建AudioServiceBroadcastReceiver实例并通过IntentFilter注册监听ACTION_HEADSET_PLUG,当接收到ACTION_HEADSET_PLUG后调用AudioSystem.setDeviceConnectionState()设置音频设备连接状态,AudioSystem.setDeviceConnectionState()最终将调用到Audio硬件抽象层的AudioPolicyManager,通过音频策略模块改变音频输出通道,从而实现扬声器与耳机输出切换。
本文已收录于以下专栏:
相关文章推荐
AudioPolicyService是Android音频系统的两大服务之一,另一个服务是AudioFlinger,这两大服务都在系统启动时有 MediaSever加载,加载的代码位于:framewor...
Android 处理音频焦点 AudioFocus
 ( 10:40:55)
标签: 
分类: android应用技...
说 Audio Focus 前先说个很简单需求:来电时暂停正在播放的音乐,电话结束时恢复播放。
看到这个需求,第一反应肯定是:监听用户来电状态,作相应操作。这里不多做介绍,这样做有个不好的地...
扬声器输出和耳机输出是音频系统最基本的音频输出通路,实际应用中用户可根据自己需要随意切换,具体表现是通过插入耳机选择耳机输出,拔出耳机选择默认的扬声器输出。
图5-13 耳机...
Android支持多种设备的的输出。一台正常的机子,本身就自带话筒,扬声器,麦克风等多个声音输入输出设备,再加上五花八门的外置设备(通过耳机,蓝牙,wifi等方式连接),使声音的输出更具多样性。And...
相关文件:
/drivers/switch/switch_gpio.c
/drivers/switch/switch_class.c
节点创建流程:
1、在/sys/class/目录下创建“s...
扬声器输出和耳机输出是音频系统最基本的音频输出通路,实际应用中用户可根据自己需要随意切换,具体表现是通过插入耳机选择耳机输出,拔出耳机选择默认的扬声器输出。
        
      ...
本文主要介绍android上音频输出设备切换的代码流程
(此文部分内容参考自邓凡达老师的博客。感谢邓老师讲解)
上层程序要切换输出设备时,经过JNI调用,会调用AudioSystem::setFo...
Dealing with Audio Output Hardware [处理音频输出硬件设备]
用户在播放音乐的时候有多个选择,可以使用内置的扬声器,有线耳机或者是支持A2DP的蓝牙耳机。
【补充:...
输出设备信息包括输出输出设备支持的音频格式、设备ID、设备名称、以及扮演角色。
音频格式:通道数、采样率、有效位、音频类型等等。
设备ID:每个设备独一味二的标识,
扮演角色:用来表明音频设备的用途,...
他的最新文章
讲师:何宇健
讲师:董岩
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)简单聊一下Android音频通路的切换
时间: 11:17:21
&&&& 阅读:388
&&&& 评论:
&&&& 收藏:0
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&Android支持多种设备的的输出。一台正常的机子,本身就自带话筒,扬声器,麦克风等多个声音输入输出设备,再加上五花八门的外置设备(通过耳机,蓝牙,wifi等方式连接),使声音的输出更具多样性。Android支持如此多的设备连接,那么android内部是怎样对设备的输出输出进行控制的呢?这一次我们主要来看看音频通路的切换。
音频流、设备、音频策略
要想知道Andorid是怎样对设备的输出输出进行控制的,我们首先来了解一些音频相关的基本知识: stream_type、content_type、devices、routing_strategy。
stream_type:音频流的类型。在当前系统中,Android(6.0)一共定义了11种stream_type以供开发者使用。Android上层开发要想要发出声音,都必须先确定当前当前的音频类型。
content_type:具体输出类型。虽然当前一共有11种stream_type,但一旦进入到Attribute,Android就只将其整理成几种类型。这才是实际的类型。
device:音频输入输出设备。Android定义了多种设备输入输出设备(具体物理设备可能还是那几个,但是输出场景不尽相同)。
routing_strategy:音频路由策略。默认情况下,Android是根据路由策略去选择设备负责输出输入音频的。
stream_type在android中java层与c++层均有定义。并且对应的值是保持一致的。
device与stream_type一样,在java层和C++层均有定义,并且会根据使用的情况不同蕴含多个定义:
相对stream_type,routing_strategy只是一个定义在RountingStrategy.h的一个简单的枚举结构体:
usecase只是qcom内部定义的一个数据结构,位于hal层,用作处理处理内部声卡逻辑和输出方案。输出方案与声卡中的mixer_path_xxx.xml相联。而mixer_path等相关文件,才是具体的音频输出方案。
我们通过查看当前的声卡情况确定了当前具体的mixer_path文件——mixer_path_skue.xml。xml文件内部就是我们预定义的usecase的具体情况:
在mixer_path类文件中,一个标准的path就如上面的红框那样。有名字,有一定的参数。另外,一个patch之中,还可以嵌套另外一个patch。
由于usecase只是目前高通hal层独有的定义,所以本文不会花太多时间和精力去探讨usecase的相关设置和内容。目前来说,对这个有一定的认知就可。
AudioPolicy和AudioPolicyService
在了解完Audio一些基本的定义设定之后,我们来看一下Android的Audio整体架构。
Audio内部系统从上到下包含各方面的东西。对于声音输出的设备的选择与切换,我们主要需要关注2个地方。第一处,是外接设备如耳机,蓝牙设备等连接的通知。第二处就是Audio系统中核心的AudioFinger与AudioPolicyService的处理内容。
AudioFinger是Audio系统的工作引擎,管理者系统中输入输出音频流,并承担音频数据混音,以及读写Audio硬件等工作以实现数据的输入输出功能。AudioPolicyService是Audio系统策略控制中心,具体负责掌管系统中声音设备的选择和切换,音量控制等功能。
AudioFinger与AudioPolicyService的类图关系:
在AudioFlinger和AudioPolicyService的运作中其实包含着很多类,但同时,我们也可以发现,在Audio系统中, AudioFinger与AudioPolicyService是紧密相连的。总得来说,AudioFinger与AudioPolicyService是Audio系统的核心。所以下面我们很多内容的主角,都是他们2个。
基本的声音输出调用
发出声音是Android机器的一个最基本的功能。但是,Android是怎么发出声音的呢?就算不连接外设,Android最基本还有听筒和扬声器2个设备。那么,Android内部,是怎么控制他们2个发出声音的呢?下面我们来具体看 一下Android一般情况下发出声音时选择设备的过程。
我们要想分析Android中的声音输出,当然是先通过播放音频去一步一步了解Android是怎恶魔输出声音的。下面我们以一个最简单的AudioTrack播放音频为例,来看下Android的发生过程。
一个简单的AudioTrack播放的例子如下:
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 11025/2,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
audioLength, AudioTrack.MODE_STREAM);
audioTrack.play();
audioTrack.write(audioData, 0, sizeInBytes);
AudioTrack在接收参数创建的时候,就会将设置的steamtype保存在对应的AudioAttributes当中(AudioAttributes是一个描述关于音频流的信息的属性集合的类)。
我们知道,在android系统中,系统封装的对象是一层一层往下调用的。所以,在我们创建了一个java的AudioTrack对象的时候,其实在同时,在C++当中,我们已经做了很多操作了。下面我们来看一下,AudioTrack对象创建时,主要做了什么:
static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jobject jaa,
jint sampleRateInHertz, jint channelPositionMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
// create the native AudioTrack object
sp&AudioTrack& lpTrack = new AudioTrack();
// initialize the native AudioTrack object
status_t status = NO_ERROR;
switch (memoryMode) {
case MODE_STREAM:
status = lpTrack-&set(
AUDIO_STREAM_DEFAULT,
sampleRateInHertz,
nativeChannelMask,
frameCount,
AUDIO_OUTPUT_FLAG_NONE,
audioCallback, &(lpJniStorage-&mCallbackData),
sessionId,
AudioTrack::TRANSFER_SYNC,
if (status != NO_ERROR) {
ALOGE(&Error %d initializing AudioTrack&, status);
goto native_init_
// save our newly created C++ AudioTrack in the &nativeTrackInJavaObj& field
// of the Java object (in mNativeTrackInJavaObj)
setAudioTrack(env, thiz, lpTrack);
从上面的代码可以看出,在创建java层的AudioTrack对象时,对应的jni也创建出一个C++的AudioTrack对象,并且传入了部分参数和调用了其方法。
接下来我们来看看C++的AudioTrack对象的构造方法:
AudioTrack::AudioTrack()
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
mPausedPosition(0),
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
mPlaybackRateSet(false)
mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
mAttributes.usage = AUDIO_USAGE_UNKNOWN;
mAttributes.flags = 0x0;
strcpy(mAttributes.tags, &&);
我们可以看到,AudioTrack的无参构造方法只是进行了一些参数的初始化,那么,具体是AudioTrack初始化是进行在哪里呢?
我们再回到上面,发现jni层在创建完AudioTrack对象后,根据memoryMode的不同而进行了不同的AudioTrack-&set()操作,只是因为AudioTrack提供2种不同的输出方式(对内存的影响和要求不同)。我来看看看set中主要的操作:
status_t AudioTrack::set(…){
status_t status = createTrack_l();
if (status != NO_ERROR) {
if (mAudioTrackThread != 0) {
mAudioTrackThread-&requestExit();
// see comment in AudioTrack.h
mAudioTrackThread-&requestExitAndWait();
mAudioTrackThread.clear();
在AudioTrack的set()中,除了部分的参数判断和设置之外,我们可以看到,他调用了自身的createTrack_l()进行了进一步的设置。
status_t AudioTrack::createTrack_l()
const sp&IAudioFlinger&& audioFlinger = AudioSystem::get_audio_flinger();
if (audioFlinger == 0) {
ALOGE(&Could not get audioflinger&);
return NO_INIT;
audio_io_handle_
audio_stream_type_t streamType = mStreamT
audio_attributes_t *attr = (mStreamType == AUDIO_STREAM_DEFAULT) ? &mAttributes : NULL;
audio_offload_info_t tOffloadInfo = AUDIO_INFO_INITIALIZER;
if (mPlaybackRateSet == true && mOffloadInfo == NULL && mFormat == AUDIO_FORMAT_PCM_16_BIT) {
mOffloadInfo = &tOffloadI
status_t status = AudioSystem::getOutputForAttr(attr, &output,
(audio_session_t)mSessionId, &streamType, mClientUid,
mSampleRate, mFormat, mChannelMask,
mFlags, mSelectedDeviceId, mOffloadInfo);
IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
sp&IAudioTrack& track = audioFlinger-&createTrack(streamType,
mSampleRate,
mChannelMask,
&trackFlags,
mSharedBuffer,
&mSessionId,
mClientUid,
上面的代码可以看出,AudioTrack从这里开始,与AudioFlinger等进行大量的交互:获取句柄,获取输出,创建IAudioTrack指针对象等等。所以接下来,就是AudioFlinger的相关内容了。在这里,我们先简单总结下AudioTrack的创建过程:
根据AudioTrack的性质,Java层在创建完成AudioTrack对象直接调用play()和write()操作,那么其实从另一方面我们可以猜想,在Java层创建完成AudioTrack之后,系统已经设置好输出的设备等等操作,只等调用play()和write方法进行播放。所以为了验证我们的猜想,接下来针对AudioFlinger&AudioSystem的相关具体分析验证。
AudioFlinger&AudioPolicyService的控制过程
回到上面的内容,我们可以看到,AudioTrack在调用createTrack_l()的方法的时候,开始通过AudioSystem获取output。所以下面我们来看看AudioSystem的getOutputForAttr():
status_t AudioSystem::getOutputForAttr()
const sp&IAudioPolicyService&& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return NO_INIT;
return aps-&getOutputForAttr(attr, output, session, stream, uid,
samplingRate, format, channelMask,
flags, selectedDeviceId, offloadInfo);
从上面我们可以看到,AudioSystem只是作为一个过渡,然后通过获取AudioPolicyService的句柄去getOutputForAttr()。我们继续跟踪AudioPolicyService的情况,会发现其实他只是在AudioPolicyService中也只是作为一个过渡,真正进行getOutputForAttr()的,在AudioPolicyManager之中。
status_t AudioPolicyManager::getOutputForAttr()
*stream = streamTypefromAttributesInt(&attributes);
sp&DeviceDescriptor& deviceD
for (size_t i = 0; i & mAvailableOutputDevices.size(); i++) {
if (mAvailableOutputDevices[i]-&getId() == selectedDeviceId) {
deviceDesc = mAvailableOutputDevices[i];
mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);
//根据strategy获取device
routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
*output = getOutputForDevice(device, session, *stream,
samplingRate, format, channelMask,
flags, offloadInfo);
在AudioPolicyManager的getOutputForAttr()中,我们可以发现关键点在strategy的获取与device的获取当中。而在这当中,关键的参数恰恰是在先前从java层一步一步封装的过来的attributes。我们先来简单看一下attributes这个参数的数据结构:
从audio_attributes_t的结构我们可以看出,audio_attributes_t保存着需要输出音频的应用的相关配置信息。
然后,根据刚刚的代码,我们来了解一下strategy的获取:
uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) {
// flags to strategy mapping
if ((attr-&flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) {
return (uint32_t) STRATEGY_TRANSMITTED_THROUGH_SPEAKER;
if ((attr-&flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {
return (uint32_t) STRATEGY_ENFORCED_AUDIBLE;
// usage to strategy mapping
return static_cast&uint32_t&(mEngine-&getStrategyForUsage(attr-&usage));
虽然在这里,会先对flags参数进行比较,但是,在实际上flags大部分时候都是0。所以最后,都是根据“mEngine-&getStrategyForUsage(attr-&usage)”去选择StrategyForUsage。当然,再到下一步就到了就是switch和case的过程,这里就不继续展开了。
在获取到strategy之后,我们来看看Audio接着是怎么来确定device的。
先继续看AudioPolicyManager的getDeviceForStrategy():
audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
bool fromCache)
// Routing
// see if we have an explicit route
// scan the whole RouteMap, for each entry, convert the stream type to a strategy
// (getStrategy(stream)).
// if the strategy from the stream type in the RouteMap is the same as the argument above,
// and activity count is non-zero
// the device = the device from the descriptor in the RouteMap, and exit.
for (size_t routeIndex = 0; routeIndex & mOutputRoutes.size(); routeIndex++) {
sp&SessionRoute& route = mOutputRoutes.valueAt(routeIndex);
routing_strategy strat = getStrategy(route-&mStreamType);
bool strategyMatch = (strat == strategy) ||
((strategy == STRATEGY_ACCESSIBILITY) &&
((mEngine-&getStrategyForUsage(
AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY) == strat) ||
(strat == STRATEGY_MEDIA)));
if (strategyMatch && route-&isActive()) {
return route-&mDeviceDescriptor-&type();
if (fromCache) {
ALOGVV(&getDeviceForStrategy() from cache strategy %d, device %x&,
strategy, mDeviceForStrategy[strategy]);
return mDeviceForStrategy[strategy];
return mEngine-&getDeviceForStrategy(strategy);
调用AudioPolicyManager的getDeviceForStrategy()的时候,一般会先查下一下当前的RouteMap,看看有没有匹配的情况的。但由于我们新申请一个output的时候,传入的参数是false,所以这个时候,是会直接通过mEngine去直接获取device。
而在mEngine中,getDeviceForStrategy()又是一堆的选择判断,然后返回设备:
audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const
const DeviceVector &availableOutputDevices = mApmObserver-&getAvailableOutputDevices();
const DeviceVector &availableInputDevices = mApmObserver-&getAvailableInputDevices();
const SwAudioOutputCollection &outputs = mApmObserver-&getOutputs();
uint32_t device = AUDIO_DEVICE_NONE;
uint32_t availableOutputDevicesType = availableOutputDevices.types();
switch (strategy) {
case STRATEGY_MEDIA: {
uint32_t device2 = AUDIO_DEVICE_NONE;
if (isInCall() && (device == AUDIO_DEVICE_NONE)) {
// when in call, get the device for Phone strategy
device = getDeviceForStrategy(STRATEGY_PHONE);
if (strategy != STRATEGY_SONIFICATION) {
// no sonification on remote submix (e.g. WFD)
if (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8(&0&)) != 0) {
device2 = availableOutputDevices.types() & AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
if (isInCall() && (strategy == STRATEGY_MEDIA)) {
device = getDeviceForStrategy(STRATEGY_PHONE);
if ((device2 == AUDIO_DEVICE_NONE) &&
(mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) &&
(outputs.getA2dpOutput() != 0)) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
if (device2 == AUDIO_DEVICE_NONE) {
device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
ALOGVV(&getDeviceForStrategy() strategy %d, device %x&, strategy, device);
我们就其中一个strategty(STRATEGY_MEDIA)来具体看看Audio系统的选择输出设备:
1)& 首先我们会获取当前存在的设备集合availableOutputDevices
2)& 然后根据传入的strategty类型进行匹配选择
3)& 在选择之前会先检测是否处于特殊情况下(如通话中)
4)& 最后按照优先级匹配设备。
然后就这样,选择设备的流程就此结束。简单来说,选择设备的流程,主要是几个参数一步一步去确定然后最后确定合适的设备。具体选择设备的简单流程如图:
针对音频通路的切换,我们就简单聊到这里。谢谢。
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&原文:http://blog.csdn.net/u/article/details/
教程昨日排行
&&国之画&&&& &&&&&&
&& &&&&&&&&&&&&&&
鲁ICP备号-4
打开技术之扣,分享程序人生!

我要回帖

更多关于 怎么切换音频输出设备 的文章

 

随机推荐