18、音频Ducking机制:Ducking触发条件、音量衰减策略、Ducking与焦点联动、应用场景分析
各位同学,今天我们来聊聊音频系统里一个非常实用、但又容易被忽视的机制——Ducking。说白了,就是“音频闪避”或“音频避让”。
你想想看,当你正在用导航,突然来了个电话,导航声音自动变小,电话挂断后又恢复。这就是典型的Ducking场景。我在项目中遇到过不少因为Ducking没处理好,导致用户体验翻车的案例。今天我们就把它彻底讲透。
18.1 Ducking是什么?
Ducking,直译是“闪避”。在音频领域,它指的是:当高优先级音频流(如通话、语音提示)开始播放时,系统自动降低低优先级音频流(如音乐、游戏音效)的音量。
嗯,这里要注意:Ducking不是简单的静音,而是平滑的音量衰减和恢复。如果直接静音,用户会感觉“断崖式”的体验,很突兀。
核心要点:Ducking的本质是“音量仲裁”,不是“音频抢占”。
18.2 Ducking触发条件
Ducking不会凭空触发。它需要满足几个条件。我个人习惯把触发条件分为两类:显式触发和隐式触发。
18.2.1 显式触发:应用主动请求
应用通过AudioManager的requestAudioFocus()方法,带上AUDIOFOCUS_FLAG_DUCK标志。这是最标准的做法。
// 示例:导航应用请求Ducking
AudioAttributes attrs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)
.setAudioAttributes(attrs)
.setOnAudioFocusChangeListener(focusChangeListener)
.build();
int result = audioManager.requestAudioFocus(focusRequest);
当result == AUDIOFOCUS_REQUEST_GRANTED时,系统就会通知当前持有焦点的低优先级应用:请Duck。
18.2.2 隐式触发:系统策略自动触发
有些场景下,系统会自动触发Ducking,不需要应用显式请求。比如:
- 通话来电:电话应用获得高优先级,系统自动让音乐Duck。
- 闹钟响起:闹钟优先级高于媒体,系统自动触发Duck。
- 系统通知:某些关键通知(如低电量警告)也会触发Duck。
我曾经在调试一个车载项目时,发现导航语音和音乐同时播放时,音乐没有Duck。查了半天,原来是导航应用没有正确设置AudioAttributes的Usage。系统不知道它是导航,自然就不会触发Duck。嗯,这个坑我踩过。
18.3 音量衰减策略
Ducking触发后,音量怎么降?降多少?恢复多快?这些都有讲究。
18.3.1 衰减幅度
Android系统默认的Duck衰减幅度是-20dB到-30dB。但不同厂商、不同场景会有差异。
| 场景 | 典型衰减幅度 | 说明 |
|---|---|---|
| 通话Duck音乐 | -25dB | 通话优先级最高,音乐几乎听不见 |
| 导航Duck音乐 | -15dB | 导航语音需要清晰,但音乐仍可感知 |
| 通知Duck音乐 | -10dB | 短暂通知,轻微衰减即可 |
我个人建议,如果你的应用需要自定义衰减幅度,可以通过AudioManager.setDuckingVolume()(需要系统权限)或者自己实现音量渐变逻辑。
18.3.2 衰减与恢复的平滑度
衰减和恢复不能“一刀切”。系统使用线性或指数渐变来实现平滑过渡。
- 衰减时间:通常100ms~300ms。太快会突兀,太慢会延迟。
- 恢复时间:通常500ms~1000ms。恢复可以比衰减慢一点,让用户感觉自然。
你想想看,如果导航语音说“前方300米右转”,音乐瞬间静音,然后又瞬间恢复,是不是很生硬?好的Ducking应该是:音乐缓缓降低,导航说完后,音乐再缓缓升起。
避坑指南:我曾经在某个项目中,把恢复时间设成了50ms。结果用户反馈“音乐像打嗝一样”。后来改成800ms,体验就好多了。恢复时间不要太短,给用户一个“适应期”。
18.4 Ducking与焦点联动
Ducking不是孤立存在的。它和AudioFocus(音频焦点)紧密绑定。说白了,Ducking是焦点变化的一种响应行为。
18.4.1 焦点变化与Ducking的对应关系
当应用请求焦点时,系统会判断当前焦点持有者应该做什么。焦点变化类型决定了Ducking是否触发:
- AUDIOFOCUS_GAIN:获得焦点,正常播放。如果之前被Duck,现在恢复。
- AUDIOFOCUS_LOSS:失去焦点,应该停止播放。不涉及Ducking。
- AUDIOFOCUS_LOSS_TRANSIENT:暂时失去焦点,可以暂停。也不涉及Ducking。
- AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:暂时失去焦点,但可以继续播放,只是需要降低音量。这就是Ducking的触发点。
所以,Ducking的本质是:焦点请求方说“我只需要短暂使用,你可以继续播放,但请小声点”。
18.4.2 焦点联动流程
我画了一张图,帮你理清整个流程:
流程很清晰:高优先级应用请求焦点 → AudioFlinger判断 → 通知低优先级应用Duck → 低优先级应用执行衰减 → 高优先级播放 → 结束后低优先级恢复。
18.5 应用场景分析
Ducking的应用场景非常广泛。我挑几个典型的说说。
18.5.1 导航+音乐
这是最常见的场景。用户一边开车一边听歌,导航语音播报时音乐自动降低。这里有个细节:导航语音播报期间,音乐是持续Duck还是只在语音播报时Duck?
我个人建议:只在语音播报时Duck。如果全程Duck,用户会感觉音乐一直很小声,体验不好。导航应用应该在语音播报前请求焦点,播报完后立即释放焦点。
18.5.2 通话+媒体
通话场景下,Ducking的优先级更高。通话时,媒体音量应该降到几乎听不见(-25dB以上)。而且通话结束后,媒体恢复的速度要慢一些,给用户一个“通话结束”的心理缓冲。
18.5.3 语音助手+媒体
“嘿,Siri”或“小爱同学”唤醒时,媒体应该Duck。这里有个坑:语音助手唤醒后,如果用户没有说话,媒体应该恢复吗?
我曾经遇到一个案例:用户唤醒语音助手后,停顿了几秒没说话,媒体恢复了。结果用户刚开口说话,媒体又Duck。这样反复切换,体验很差。解决方案是:语音助手在唤醒后,保持焦点一段时间(比如3秒),即使没有语音输入,也不释放焦点。
注意:Ducking不是万能的。如果低优先级应用没有正确监听焦点变化,或者没有实现音量衰减逻辑,Ducking就不会生效。所以,作为开发者,一定要在应用中正确处理onAudioFocusChange()回调。
18.6 总结
Ducking机制是Android音频系统中一个精巧的设计。它通过焦点联动、音量衰减和恢复,实现了多音频流的和谐共存。
记住几个关键点:
- 触发条件:显式请求或系统策略自动触发
- 衰减策略:幅度、时间、平滑度都要考虑
- 焦点联动:Ducking是焦点变化的响应行为
- 应用场景:导航、通话、语音助手等
好了,这一章就到这里。希望你在自己的项目中,能把Ducking用得更顺手。
公众号:蓝海资料掘金营,微信deep3321