USB声卡解码器连接Android手机时问题的出现和分析[四] 最终分析和临时解决方案
农步祥 于 2017.10.13 08:48:57 | 源自: | 版权:原创 | 平均/总评分:09.42/311

在经历三篇分析文章从乐之邦Monitor 06 Plus等几块USB声卡由浅入深至几乎在被谷歌官方git的Android公开源代码库淹死的“奇妙冒险”后,我们可以得知,除了供电、个体设备兼容性和稳定性等硬性限制外,大多数Android 6.0以上的手机、平板以及机顶盒设备在通过OTG连接USB2.0音频规范的声卡 、解码器耳放时,不仅会出现绝大多数Android手机都存在的SRC现象,输出的采样率还会自动锁定在192kHz这一频率上。 虽然经历了长时间测试、分析并详细描述了这个现象的起因。但这里还是有必要复习一下Android系统在连接USB声卡时的大致工作机制流程和问题来源,以助于我们[作者、读者或厂商]最终完成最后一项微小的工作:如何解决这一问题。

Andorid系统连接USB声卡时SRC依旧存在

  • 这或许是许多音频爱好者或发烧友最为关心的问题了,只要经过简单的测试分析,就可以发现在绝大多数Android系统设备下,其实际的音频回放工作步骤是和手机内置音频CODEC的流程是基本一致的,除了海贝音乐等少量绕过系统音频抽象层HAL工作机制的应用外,一般的影音应用下音频回放同样是由于采样率锁定而出现SRC现象的,这点无论是基于乐之邦、XMOS等异步USB方案的千元级到万元级声卡解码器设备或者简单廉价如HTC U11本体的耳机线都无法避免。如果用户既不满意手机本体音质又偏偏喜欢网易云、苹果音乐、Google Play等在线音乐应用的。那么用户还是需要忍受一下SRC带来的音质劣化,当然如果是高清音频或者SACD发烧友,由于海贝音乐等应用的存在,可以很好地避免这些问题。

    采样率锁定在192的原因

  • 虽然“大胆假设”的预判方向正确,但“马虎求证”的过程差点意外翻车,幸运的是通过对Android系统底层运行信息的分析,大体上我们还是通过系统源代码和系统运行状态找到了问题出现的基本原因。首先是可以得知Android确实会趋向于将USB声卡的默认采样率设置为硬件支持的最高值。但由于驱动或HAL某些环节的失误,Android的音频抽象层HAL支持的最高采样率为192kHz,这也成为了XMOS等USB高速异步方案即使最高采样率超过了192kHz[支持384或更高],而默认采样率锁定在192kHz的原因。

    进一步的深度分析和寻找解决方案

    相信对于本站大多数读者来说,到了这里已经足够长见识了。和我们在现实生活中遇到的各类问题一样,发现是非常容易的,但能通过现象分析问题产生的原因就是非常少了,而能够掌握并解决问题的专家,就和有权限修改Android源代码的码农一样,永远只是那么一小撮。避免SRC仍是解决Android设备在音频应用中音质、耗电等问题一劳永逸的方法,当然这在某些机型[OPPO R11]或者某些应用场合下就是正解。但相信在购买U11耳机线的十几万用户大军中,有相当大的用户数量是以云音乐应用为主力的,有心情和财力找一台连插头都对不上的R11似乎也是不大可能。

    那么对于这些“一般”的Android手机用户,有没有办法去避免SRC呢?解决SRC的方法无非两种:1,采样率自动识别切换;2,用户指定设备采样率。前者iOS[ASIO]和Windows WASAPI已经实现,Android 4.X时期有一段时间也实现了采样率自动切换能力,但当时的带来的问题就是极大的音频延迟,因此从Android 5.X开始,谷歌转而用改进SRC算法的方式达成了音质和延迟的妥协。除非手机或应用厂商愿意向谷歌分享并说服其对Android音频子系统进行改造,否则终端用户很难通过自己的能力去实现。

    第二种方式就是类似Windows系统自带的音频API[通常是DirectSound]那样,通过驱动面板指定声卡采样率了。虽然我们没有能力靠自己修改源代码解决Android SRC问题,但如果能通过简单粗暴的方式解决让更多人受益也是极好的。因为,在分析完成后,我们通过进一步的测试,又发现了更多关于Android+USB声卡的有趣的现象。在体验Android X86以及Android模拟器时,我们也顺便在一台普通的台式电脑上硬盘安装Android X86系统,并连接了一块乐之邦的数字时代2,惊奇地发现其默认的回放采样率居然是44.1kHz。而通过播放测试和系统日志分析,系统HAL仅能识别44100Hz,并锁定采样率,至少证明了采样率是可以改变的。

    这一现象再次引起了我们的兴趣,还对Android X86以及手机平板上运行的Android系统的相关日志分析文件进行了比对,意外让我们发现:Android的默认采样率是可以和Windows那样自行修改的。和上文一样,以下涉及Android源代码的分析,对此无兴趣或无了解者可直接跳过。

    Android的Audio Policy“音频策略层”

    在分析涉及USB声卡的源码时,或许会有一些较为专业的读者会关注Android系统在连接USB声卡时为何会设置192kHz?毕竟底层的usb.c代码片段只是分析声卡的内核驱动所汇报支持的采样率,那么是谁最终决定系统使用哪个采样率的呢?在分析源代码经历了若干香蕉和蛋糕后,我们很快找到了答案。那就是HAL中被称为Audio_policy的“音频策略层”,这个音频策略层使用C++编写,所负责的部分即使不需要仔细阅读源码,也能从源代码代码目录结构中轻易分析出它要干什么:根据某个系统预设的文本文件来设定和管理声卡的驱动设置。这个设置文件名也能在现成的手机中找出来:/etc/audio_policy.conf

    通过阅读audio_policy.conf文件,我们大致可以得知这个设置文件管理着Android的内置以及外置音频设备的采样率、位率等常规设定。而在USB设备采样率生命部分,它是这么写的:

  • 可以看出,USB设备的采样率和位率,并不是具体的数字,而是一个叫dynamic的变量?到这里或许会有无证码农提出疑问了:dynamic不就是动态的意思吗?难道Android系统天生就支持采样率的动态切换?遗憾的是在分析Audio_policy的源代码后,dynamic这个变量确实存在,但只是被转换为普通的文本“Dynamic”,并没有找到任何负责采样率识别和转换的部分的运算,经过系统日志分析,更精彩的来了,这个Dynamic通过Audio_policy硬生生把他作为一个可选采样率传递给了底层硬件驱动。

  • 在上一篇源代码分析中可以得知,usb.c根本是不认识Dynamic这个所谓“动态”采样率的,会被直接过滤掉。当然这是不是384000采样率“消失”的元凶就根本无从得知了,或许HAL或AudioFlinger在传递采样率参数期间还做了别的事情。至少到Android 8.0[@Nexus6P]为止,这个动态采样率切换仍旧是无法实现的。

    临时解决方案

    读到这里,或许已经有人想到这个最“简单”的解决方法了:没错,就是修改/etc/audio_policy.conf。对于网易云用户来说,只要将usb_device下的sampling_rates从dynamic修改为44100后重新启动手机,就能将USB声卡的初始采样率从192000变更为44100了……虽然影音应用无法得兼,在高清视频等普遍使用48kHz音轨时会SRC至44.1kHz播放,但至少已经做到了可控,对于强迫症用户来说,还可以设置一下声卡回放的位率[XMOS、乐之邦方案默认是32bit,可以切换至24、16bit,语法可参考设置文本其它区域]。要说明的是,如果试图通过在配置文件列举采样率的方法实现动态采样率切换[如44100|48000|96000],是无效的。至于高清音频应用,这一修改并不会对海贝音乐等本来就绕过HAL的应用程序带来造成兼容性影响。

    不过,audio_policy.conf是系统文件,也就意味着用户需要破解手机获得root权限来修改了。这一方法简单粗暴有效,发现和解决过程似乎不如调教某K860那么有挑战性,但这也意味着设备会失去保修,而修改时由于手痒或手残很容易造成配置文件语法错误导致系统无声甚至无法正常启动的惨剧,另外这个文件并不是通用的,无法通过简单的复制粘贴来解决。因此我们不鼓励推荐用户自己动手,而是直接反馈给设备厂商来通过系统更新修正更为广谱有效。当然,也可以等待到某天谷歌真的实现动态采样率切换了,将SRC问题彻底扔进垃圾桶里。

     

    请评分
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    发生未知错误