AudioEffect 基础:核心 API、会话与效果链
各位同学,今天我们来聊聊 AudioEffect 框架的基石。说实话,我刚接触 Android 音频时,也被这套机制绕得有点晕。但一旦你理解了「会话」和「效果链」这两个概念,整个框架就豁然开朗了。
我个人习惯把 AudioEffect 想象成一个「音频效果器插件系统」。你想想看,我们平时用手机听歌、录音、打电话,背后都有一堆效果器在工作——均衡器、混响、降噪……它们怎么挂上去的?怎么保证只影响某个音频流?这就是我们今天要讲的核心。
1. AudioEffect 类的核心 API
AudioEffect 是所有效果器的基类。它不直接干活,但定义了效果器的生命周期和通信方式。
先看几个最常用的 API:
// 创建效果器
AudioEffect effect = new AudioEffect(
effectType, // UUID,比如均衡器
effectUuid, // 具体实现 UUID
priority, // 优先级,影响效果链顺序
audioSessionId // 绑定到哪个音频会话
);
// 启用/禁用
effect.setEnabled(true);
// 释放资源
effect.release();
这里有个坑,我踩过好几次——创建效果器时,audioSessionId 不能传 0。传 0 表示全局效果,但很多设备上全局效果根本创建不出来。我曾经在某个项目里,因为传了 0,结果效果器创建成功但就是不生效,排查了半天……
核心 API 速查表
| 方法 | 说明 | 注意事项 |
|---|---|---|
| new AudioEffect(...) | 创建效果器实例 | sessionId 不能为 0(全局) |
| setEnabled(boolean) | 启用/禁用效果 | 创建后默认禁用 |
| release() | 释放效果器资源 | 必须调用,否则内存泄漏 |
| getDescriptor() | 获取效果器描述信息 | 包含名称、实现者等 |
| setParameter(...) | 设置效果参数 | 不同效果器参数格式不同 |
2. 效果会话(Session)的概念
会话是什么?说白了,就是一个「音频流身份证」。每个音频播放或录制流,都有一个唯一的 session ID。
为什么需要会话?我举个例子你就明白了:
你打开网易云音乐听歌,同时微信来了一条语音消息。这两个音频流是独立的。如果你给 session 100 挂了均衡器,那只会影响网易云音乐,不会影响微信语音。这就是会话隔离的作用。
获取会话 ID 的方式很简单:
// 播放时获取 session ID
AudioTrack track = new AudioTrack(...);
int sessionId = track.getAudioSessionId();
// 录制时获取 session ID
AudioRecord record = new AudioRecord(...);
int sessionId = record.getAudioSessionId();
嗯,这里要注意:同一个 AudioTrack 或 AudioRecord 的 session ID 在整个生命周期内保持不变。但如果你重新创建了一个 AudioTrack,它会分配一个新的 session ID。
小技巧:如果你想让多个 AudioTrack 共享同一个效果链,可以在创建时指定相同的 session ID:
AudioTrack track1 = new AudioTrack(..., AudioManager.AUDIO_SESSION_ID_GENERATE);
AudioTrack track2 = new AudioTrack(..., track1.getAudioSessionId());
这样 track1 和 track2 就会共享效果器。
3. 效果链(Effect Chain)的构建与销毁
效果链,就是挂载在同一个 session 上的多个效果器,按优先级排列形成的链条。音频数据会依次经过每个效果器处理。
构建效果链的步骤:
- 创建 AudioTrack/AudioRecord,获取 session ID
- 按优先级创建效果器,绑定到同一个 session
- 设置效果参数,启用效果器
- 开始播放/录制,音频流自动经过效果链
来看一个完整的例子:
// 1. 创建 AudioTrack
AudioTrack track = new AudioTrack(...);
int sessionId = track.getAudioSessionId();
// 2. 创建均衡器(优先级 0)
AudioEffect eq = new AudioEffect(
AudioEffect.EFFECT_TYPE_EQUALIZER,
UUID.randomUUID(),
0, // 优先级
sessionId
);
// 3. 创建混响器(优先级 1,排在均衡器后面)
AudioEffect reverb = new AudioEffect(
AudioEffect.EFFECT_TYPE_ENV_REVERB,
UUID.randomUUID(),
1, // 优先级更高,后处理
sessionId
);
// 4. 设置参数并启用
eq.setEnabled(true);
reverb.setEnabled(true);
// 5. 开始播放
track.play();
效果链的销毁顺序很重要。我建议:先停掉 AudioTrack,再释放效果器,最后释放 AudioTrack。顺序反了可能会 crash。
// 正确的销毁顺序
track.stop();
eq.release();
reverb.release();
track.release();
警告:效果器释放后,AudioTrack 还在播放的话,音频流会直接绕过效果器输出。但如果你先释放了 AudioTrack,再释放效果器,效果器内部可能还在引用 AudioTrack 的资源,导致野指针。我曾经在某个项目里因为顺序搞反,导致偶发性的 native crash,查了两天才定位到问题。
知识体系总览
下面这张图,是我自己总结的 AudioEffect 核心知识结构。你看一眼,应该就能把今天的内容串起来:
好了,今天的内容就到这里。记住三个关键词:API、Session、Chain。下次我们聊具体的效果器实现,比如均衡器怎么调参、混响怎么设置空间感。到时候我会分享一些我在车载音频项目里调音的经验,挺有意思的。
公众号:蓝海资料掘金营,微信 deep3321