尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

高通8155座舱音频链路全解析:从Android HAL到DSP的音频数据流与ASoC实战

高通8155座舱音频链路全解析:从Android HAL到DSP的音频数据流与ASoC实战 1. 8155音频链路到底在解决什么问题先把场景摆出来。高通8155这颗座舱芯片现在路上跑的中高端车型里装车量非常大它一颗SoC里同时跑着QNX虚拟机、Android虚拟机、还有负责音频的ADSP子系统。你坐在车里说一句打开空调语音助手能听懂你放一首歌四个车门喇叭加中置能出声你接个电话麦克风阵列要拾音、要回声消除。这些事背后是一条从应用层一路扎到DSP固件的完整音频数据流。很多人第一次接触这块会以为音频嘛不就是ALSA那一套。真上手才发现8155上的音频链路跟手机、跟普通Linux开发板完全不是一回事。它横跨了Android HAL层、QNX侧的音频服务、共享内存的跨虚拟机传输、ADSP上的固件处理中间还夹着ASoC框架、PCM设备节点、DMA缓冲区、时钟同步这些环节。任何一环配置错了表现可能是没声音、有杂音、延迟半秒、只有前排响排查起来非常折磨人。这篇内容我打算把这条链路从头到尾捋一遍。适合谁看做座舱音频驱动开发的、做HAL适配的、做语音交互集成的以及那些被音频通路不通卡住好几天的工程师。我会讲清楚数据从哪来、经过哪些层、每层做了什么转换、DSP里又发生了什么以及实际调试时怎么定位问题。核心关键词就几个高通8155、HAL、DSP、音频数据流、ASoC围绕它们展开。需要先说明一点8155的具体寄存器手册和ADSP固件细节属于厂商NDA范围我不会涉及任何未公开的私有实现。下面讲的是基于公开的ASoC框架、Android音频架构、以及DSP通用处理模型的合理还原结合我在类似平台上调试音频通路的经验补充。你拿这套思路去对照自己手上的代码基本能对上号。2. 从应用层到HAL音频数据的第一段旅程2.1 Android音频框架在8155上的落点Android这边一个App要放音走的是标准路径App调用AudioTrackAudioTrack把PCM数据交给AudioFlingerAudioFlinger通过audio HAL把数据送出去。在8155这种座舱平台上Android通常跑在虚拟机里audio HAL就是虚拟机跟外界打交道的边界。这里有个容易混淆的点audio HAL不是驱动它是用户态的抽象层。它向上提供统一的接口比如out_write、in_read向下对接真正的内核驱动或者跨虚拟机通道。8155上常见的做法是HAL通过某种IPC机制把PCM数据送到QNX侧因为音频的硬件管理、时钟、DSP固件加载往往由QNX这个宿主来管。为什么这么设计因为座舱里音频资源是全局共享的——导航播报、媒体、电话、提示音要混音还要处理优先级抢占比如倒车雷达报警要压过音乐。这些逻辑放在QNX侧统一调度比放在Android里各自为政要可靠得多。Android虚拟机重启了音频基础服务还在这是车规场景的硬需求。2.2 HAL里那些必须搞清楚的配置项打开audio HAL的配置文件通常是audio_policy_configuration.xml和对应的HAL实现你会看到一堆跟音频通路相关的定义。我挑几个实际调试中最容易出问题的说。PCM设备与后端绑定。每个mixPort和devicePort的对应关系决定了数据往哪走。比如primary output对应到哪个DSP的PCM端口deep buffer又走哪条路。配错了表现就是媒体有声音但导航没声音这种诡异现象。采样率与位深。8155的ADSP支持多种采样率但混音器通常工作在固定采样率常见48kHz。如果App送进来44.1kHz的数据中间要做重采样。重采样放在哪一层做直接影响CPU占用和音质。我的经验是尽量让HAL或DSP硬件做别在Java层用软件重采样延迟和功耗都吃不消。通道数映射。座舱常见的是多通道输出比如前左、前右、后左、后右、中置、低音。HAL里要定义清楚每个逻辑通道对应物理的哪个DAC通道。这里有个坑不同车型的扬声器布局不一样同一套HAL代码换个车型可能左右声道就反了必须靠配置区分。!-- 简化示意实际配置更复杂 -- mixPort nameprimary output rolesource profile name formatAUDIO_FORMAT_PCM_16_BIT samplingRates48000 channelMasksAUDIO_CHANNEL_OUT_STEREO/ /mixPort devicePort tagNameSpeaker typeAUDIO_DEVICE_OUT_SPEAKER rolesink profile name formatAUDIO_FORMAT_PCM_16_BIT samplingRates48000 channelMasksAUDIO_CHANNEL_OUT_STEREO/ /devicePort2.3 跨虚拟机传输数据怎么从Android到QNX这是8155音频链路里最黑盒的一段。Android虚拟机里的HAL拿到PCM数据后要通过虚拟化框架提供的共享内存通道送到QNX侧。常见的技术手段是基于共享内存加中断通知的机制数据本身是零拷贝或者一次拷贝。实际调试时这一段出问题的典型症状是HAL层日志显示write成功但QNX侧收不到数据。这时候要查几个地方共享内存区域是否映射成功、通知机制通常是某种虚拟中断或信号是否触发、两边的缓冲区读写指针是否同步。我踩过一次坑是缓冲区大小两边配置不一致Android侧按4KB写QNX侧按8KB读结果数据错位出来的声音是断断续续的噪音。这种问题看日志看不出来得两边对着算缓冲区大小。提示跨虚拟机音频调试强烈建议在两端都加上带时间戳的读写指针日志出问题时把两份日志按时间对齐能快速定位是没发出去还是没收到还是收到了但解析错。3. QNX侧与ASoC音频通路的骨架3.1 ASoC框架在车机上的角色ASoCALSA System on Chip是Linux内核里为嵌入式音频设计的一套框架它把音频系统拆成三部分PlatformSoC侧的DMA和CPU DAI、Codec编解码器驱动、Machine板级胶水层。8155的QNX侧音频驱动虽然不一定完全照搬Linux的ASoC但设计思想是一致的理解ASoC对看懂整条链路帮助极大。Platform层负责管理DMA引擎PCM数据通过DMA搬运不占用CPU。Codec层负责配置实际的音频硬件接口比如I2S、TDM设置增益、静音、采样率等。Machine层则把前两者粘起来定义一条具体的音频通路哪个CPU DAI连哪个Codec DAI用哪个DMA通道时钟怎么配。在8155上ADSP往往承担了Codec和部分Platform的职责。音频数据通过共享内存送到ADSPADSP内部的固件做混音、音效、编解码最后通过I2S/TDM接口送到外部的音频Codec芯片比如功放或DAC。3.2 DAI链路与时钟声音稳定的根基DAIDigital Audio Interface是芯片间传输数字音频的接口。8155到外部Codec之间常见的是I2S或TDM。TDM能在一根数据线上传多路音频座舱多通道场景用得多。时钟是这里的关键。I2S/TDM需要三根时钟位时钟BCLK、帧时钟LRCK也叫WS、主时钟MCLK。BCLK频率 采样率 × 位深 × 通道数。比如48kHz、16bit、8通道TDMBCLK就是48000×16×8 6.144MHz。MCLK通常是采样率的256倍或512倍给Codec内部做参考。时钟配错的典型症状声音变调采样率不对、有周期性的咔哒声时钟不同步、完全没声音MCLK没输出。我遇到过一次MCLK的分频系数配错导致Codec锁不住时钟声音每隔几秒断一下。用示波器量MCLK频率跟Codec手册要求的对不上改分频系数就好了。时钟信号作用常见频率48kHz/16bit/8ch TDM配错的表现MCLKCodec主参考时钟12.288MHz256×Codec不工作、无声BCLK位时钟6.144MHz变调、杂音LRCK帧同步48kHz声道错乱、断音3.3 PCM设备节点与DMA缓冲区在QNX或Linux侧每个音频通路通常对应一个PCM设备节点比如/dev/snd/pcmC0D0pcard0 device0 playback。应用或上层服务通过open、ioctl、write操作它。DMA缓冲区的大小直接决定延迟和抗抖动能力。缓冲区太小容易underrun数据没及时供上声音断太大延迟高语音交互会感觉反应慢。座舱里通常分场景配置媒体播放可以用大缓冲区比如几十毫秒语音交互要用小缓冲区几毫秒到十几毫秒。计算缓冲区有个经验公式缓冲区字节数 采样率 × 通道数 × 位深/8 × 目标延迟秒数。比如48kHz、2通道、16bit、目标20ms延迟48000×2×2×0.02 3840字节。实际会取2的幂次对齐比如4096字节。注意period周期和buffer缓冲区是两个概念。一个buffer通常包含多个period中断按period触发。period太小会导致中断过于频繁CPU占用飙升period太大则单次传输延迟高。一般period设为buffer的1/4到1/8比较均衡。4. DSP内部音频数据的深加工4.1 ADSP固件加载与音频处理流水线8155的ADSP是一颗独立的DSP核心跑自己的实时操作系统和固件。音频数据送到ADSP后固件里通常有一条处理流水线解码如果是压缩格式→ 混音 → 音效处理EQ、动态范围控制→ 声道映射 → 输出到硬件接口。固件加载是启动阶段的事。系统上电后bootloader把ADSP固件从存储里读出来加载到ADSP的内存然后启动ADSP核心。这一步出问题整个音频子系统都起不来。调试时如果发现所有音频通路都没声音先确认ADSP固件有没有加载成功看启动日志里ADSP的握手信息。固件加载失败的常见原因固件文件路径不对、版本跟硬件不匹配、加载时序有问题ADSP还没准备好就发数据。我见过一次是固件版本和SoC的silicon revision不匹配加载时校验失败日志里有一行不起眼的错误找了半天。4.2 混音与音效处理的实现逻辑座舱里同时可能有导航、媒体、电话、提示音多路音频它们要在ADSP里混音。混音本质是采样点相加但要处理溢出——两个16bit的样本相加可能超过16bit范围所以内部通常用32bit累加最后再做饱和处理或限幅。音效处理里EQ均衡器是最常见的。它通过一组滤波器通常是IIR biquad调整不同频段的增益。每个biquad有几个系数这些系数由上层根据用户设置算好下发给DSP。DSP对每个采样点做滤波运算。这里有个性能考量DSP的MIPS是有限的。如果同时开太多音效多段EQ、环绕声、动态压缩可能吃满DSP算力导致音频卡顿。实际项目里要评估音效链的算力预算别一股脑全开。4.3 语音前处理AEC、降噪、波束成形语音交互场景下麦克风采集的信号要经过前处理才能用。核心是三个AEC回声消除、NS降噪、BF波束成形。AEC的作用是消除扬声器放出来的声音被麦克风又收回去的部分。原理是DSP知道当前正在播放什么参考信号用它去估计麦克风里对应的回声成分然后减掉。AEC做不好语音识别会把车机自己放的音乐当成用户说话或者打电话时对方听到自己的回声。降噪是抑制稳态背景噪声发动机声、风噪。波束成形是用多个麦克风的相位差把拾音聚焦到说话人方向抑制其他方向的噪声。这些算法都在ADSP里跑对实时性要求极高。AEC的参考信号和麦克风信号的同步误差要控制在采样级几十微秒差一点效果就大打折扣。调试语音前处理时参考信号和麦克风信号的延迟对齐是最关键也最费时间的环节。5. 调试实战音频通路不通怎么查5.1 分层定位法从现象反推故障层音频问题最忌讳一上来就瞎改代码。我的习惯是分层定位先确定问题出在哪一层再深入那一层查。第一步确认是全链路不通还是某条通路不通。如果所有音频都没声音问题大概率在ADSP固件、时钟、或者硬件。如果只有某一路比如只有导航没声音问题在HAL配置或混音路由。第二步用工具逐层验证。Android侧可以用dumpsys audio看音频状态用tinymix如果有看混音器控件。QNX侧看PCM设备节点是否正常DMA是否在跑。ADSP侧看固件日志。第三步抓数据。在关键节点抓PCM数据看数据是否正常。比如在HAL出口抓在QNX入口抓对比两边数据是否一致。数据一致但没声音问题在DSP之后数据不一致问题在传输环节。5.2 几个我踩过的真实坑坑一采样率不匹配导致的快放。现象是声音听起来比正常快、音调高。原因是上层送44.1kHzDSP按48kHz播放。查HAL配置发现采样率写错了。这种问题耳朵一听就能判断方向但定位到具体配置项要翻好几层。坑二共享内存缓冲区指针不同步。现象是周期性爆音。原因是Android侧和QNX侧的读写指针更新不是原子的偶尔读到中间状态。解决办法是用双缓冲加内存屏障或者用硬件提供的同步机制。坑三DSP算力不足导致的卡顿。现象是音频偶尔卡一下不规律。查DSP负载发现接近100%。原因是音效链开太多。砍掉几个不常用的音效或者优化滤波器实现问题消失。坑四时钟主从配反。现象是Codec完全不出声。8155和外部Codec之间谁做时钟主设备要配清楚。配反了两边都在等对方给时钟结果谁都不动。现象可能原因排查方向完全无声ADSP固件未加载、MCLK无输出查启动日志、量时钟声音变调采样率不匹配查HAL和DSP采样率配置周期性爆音缓冲区指针不同步查跨虚拟机同步机制不规律卡顿DSP算力不足查DSP负载、精简音效单路无声混音路由配置错查HAL mixPort配置5.3 日志与工具的组合用法单靠一种工具很难定位跨层问题。我的组合拳是Android侧dumpsys QNX侧PCM状态 DSP固件日志 示波器量时钟。具体流程先在Android侧确认AudioTrack有没有正常write再看HAL有没有收到数据。然后跳到QNX侧看PCM设备有没有被打开、DMA有没有在跑。如果QNX侧正常问题在DSP或硬件这时候量时钟、看固件日志。如果QNX侧就没收到数据问题在跨虚拟机传输。这套流程走下来基本能在半小时内把问题范围缩小到某一层。剩下的就是那一层内部的细节排查了。提示调试音频一定要有参考基准。准备一个已知正常的音频文件比如标准正弦波用它测试比用音乐文件更容易发现问题——正弦波出问题一听就知道是失真还是断续。6. 性能与延迟座舱音频的隐形指标6.1 端到端延迟的构成座舱音频的延迟从用户操作到听到声音中间累积了很多环节App缓冲、AudioFlinger缓冲、HAL缓冲、跨虚拟机传输、DSP处理、DMA、Codec。每一环都有缓冲加起来可能几十到几百毫秒。语音交互对延迟最敏感。用户说完话系统要尽快响应。如果音频播放延迟太高会出现用户已经说完系统还在放上一句提示音的尴尬。一般要求端到端延迟控制在100ms以内语音场景最好50ms以内。降低延迟的手段减小各级缓冲区、提高DSP处理优先级、优化跨虚拟机传输路径。但缓冲区不能无限小太小会underrun。要在延迟和稳定性之间找平衡。6.2 DSP负载与音效预算ADSP的算力是固定的音效越多负载越高。实际项目里要给音效链做算力预算。比如AEC占多少MIPS、EQ占多少、混音占多少加起来不能超过DSP总MIPS的70%留30%余量应对峰值。如果预算超了要么砍音效要么优化算法。优化方向包括降低滤波器阶数、用定点运算代替浮点、减少不必要的重采样。这些优化需要跟算法团队一起做不是驱动工程师能单独搞定的。6.3 多场景并发下的资源竞争座舱里音频场景是并发的导航在播报、媒体在放歌、电话进来了。这时候要处理优先级和混音。电话优先级最高进来时媒体要 duck降低音量或暂停。导航播报时媒体也要 duck。这些策略在QNX侧的音频策略服务里配置。配置不当会出现电话来了音乐还在大声放或者导航播报被音乐盖住。调试这类问题要理清每个场景的优先级和 duck 策略用实际场景组合测试。7. 一些实际项目里的经验补充先说一个关于版本管理的教训。8155的音频链路涉及Android HAL、QNX驱动、ADSP固件三部分它们之间有版本兼容性要求。我遇到过HAL升级了但ADSP固件没跟着升结果接口对不上音频直接不通。所以这三个组件的版本要作为一个整体管理升级时一起升别单独动某一个。再说配置与代码分离。不同车型的扬声器布局、音效参数、混音策略都不一样。如果这些硬编码在代码里每换个车型就要改代码重新编译维护成本极高。正确做法是把这些做成配置文件代码只负责读取和应用。这样换个车型只改配置不动代码。关于测试覆盖音频问题很多是场景相关的单测很难覆盖。我的做法是建一个场景测试矩阵不同音源媒体、导航、电话、提示音× 不同组合单独、两两、全部× 不同操作播放、暂停、切换、插拔。这个矩阵跑一遍能发现大部分路由和优先级问题。最后说调试环境。音频调试最好有一套可控的环境能单独控制每一路音源、能抓每一层的PCM数据、能实时看DSP负载。这套环境搭起来费劲但搭好之后调试效率提升巨大。我在项目里花了两周搭这套环境后面省下的时间远超这个投入。关于DSP固件加载补充一个细节加载时机很关键。如果ADSP还没准备好就发音频数据数据会丢。正确的做法是等ADSP发来ready信号后再开始传输。这个握手信号在启动日志里能看到调试启动问题时重点关注。还有一点时钟的抖动对音质影响很大。即使频率对了如果抖动大也会有可闻的失真。高质量的音频Codec对MCLK抖动有要求PCB布局和时钟源选择要注意。这是硬件和驱动配合的事出问题时两边都要查。整体捋下来8155的音频链路是一条跨越多个软件层和硬件模块的长链路。理解它的关键在于建立分层的心智模型知道数据从哪来、经过哪几层、每层做什么、层与层之间怎么交接。有了这个模型遇到问题就能快速定位到某一层而不是盲目地到处改。实际调试中日志、工具、参考基准三样东西配合使用大部分问题都能在合理时间内解决。
返回列表