关于AudioEffect使用过程中的构造以及处理流程遇到的几个问题
最近遇到几个与AudioEffect相关的问题,在此记录下作为一个记忆总结
android系统中如果想使用自己开发或者第三方的音效算法,有一种比较简单的办法就是放到hal层的out_write接口中,这样做优点是简单,方便快速集成。还有标准的做法就是做成android标准的音效接口,上层应用就可以像使用android自带的音效一样来调用自己所添加的自定义音效。
如何实现自定义的音效库以及AudioEffect的构造流程分析,已经有不少写的很好的文章,可以参考以下博客
AudioEffect构造流程跟踪 & 音效库实现(native侧)
https://blog.csdn.net/wkw1125/article/details/65632960
Android Effect 解析
https://blog.csdn.net/kuang_tian_you/article/details/83510713
这里主要记录我遇到的几个问题,我看很少有文章讲到,在此做个记录
之前讲到音效集成自己放在hal层或是在送往alsa输出之前做处理这种方法简单同时也能保证系统输出的音频数据都能经过音效算法处理。做成android音效标准接口就有需要注意的地方了,先来看下android audioeffect 处理的地方,在audioflinger的playbackthread中PlaybackThread::threadLoop()
这就意味中必须是meplayer或是audiotrack播放的音频流才能获得音效作用,但是对于一些android TV来说,很多方案商的TV In的音频数据比如HDMI IN、AV IN并不通过audiotrack去播放,例如google专门为android TV设计的TIF架构,走的是audiopatch机制,audio in到 audio out的处理基本上在hal层就干完了,不经过audiotrack 那么如何使用auudioeffect音效呢,这样就能想到如果音效不在audioflinger那一层处理,而是也在hal层去处理,这个功能就完成了。可以看到audioeffect在构造的过程中音效enable时会更新一个状态机
在playbackthread里音效process实时更新状态
EffectModule::updateState中调用start_l(),里面有个接口
可以看到addEffectToHal_l会根据是否有EFFECT_FLAG_TYPE_PRE_PROC和EFFECT_FLAG_TYPE_POST_PROC的flag来判断是否将音效add到hal层
hal层对应实现addEffect这个接口 同时在写到alsa之前调用process即有了音效处理的作用
这就意味着在构造音效库的时候就要指定这些flag,举例如下:
const effect_descriptor_t AvlDescriptor={
{0x4a959f5c,0xe33a,0x4df2,0x8c3f,{0x30,0x66,0xf9,0x27,0x5e,0xdf}},
{0x08246a2a,0xb2d3,0x4621,0xb804,{0x42,0xc9,0xb4,0x78,0xeb,0x9d}},
EFFECT_CONTROL_API_VERSION,
EFFECT_FLAG_TYPE_POST_PROC | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_NO_PROCESS | EFFECT_FLAG_OFFLOAD_SUPPORTED,
AVL_CUP_LOAD_ARM9E,
AVL_MEM_USAGE,
"Avl",
"xxxx",
};
EFFECT_FLAG_NO_PROCESS这个flag也很重要,这个flag声明之后 audioflinger那一层将会直接bypass处理不会经过音效作用,具体代码在EffectModule::process()里
同时我们也注意到这个状态机的更新在playbackthread的process loop里,所以创建音效时需要保证playbackthread在running的状态,因为playbackthread在android启动后没有音频播放会进入stabdby状态,所以为了保证音效创建成功,创建的同时可以播放一段空的audiotrack以保证playbackthread是active的。
在这里我们所有的音效都是全局的,即创建音效时指定的sessionId为AUDIO_SESSION_OUTPUT_MIX 这样系统创建一次音效,音效链将会一直绑定在audioflinger上,整个系统将一直有音效作用,而不必像一般内度应用那样创建的时候获取mediaplayer或是audiotrack的sessionId
参数audioSession,相同audioSession ID的AudioTrack和MediaPlayer共享Audio Effect,这是android的接口注释
这里需要注意的是一旦有应用使用非全局的sessionId,并且enable之后会导致我们设置的全局音效被禁用,来看看这部分代码:
进去checkSuspendOnEffectEnabled会调用到
void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
bool enabled,
audio_session_t sessionId)
{
Mutex::Autolock _l(mLock);
checkSuspendOnEffectEnabled_l(effect, enabled, sessionId);
}
void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
bool enabled,
audio_session_t sessionId)
{
if (mType != RECORD) {
// suspend all effects in AUDIO_SESSION_OUTPUT_MIX when enabling any effect on
// another session. This gives the priority to well behaved effect control panels
// and applications not using global effects.
// Enabling post processing in AUDIO_SESSION_OUTPUT_STAGE session does not affect
// global effects
if ((sessionId != AUDIO_SESSION_OUTPUT_MIX) && (sessionId != AUDIO_SESSION_OUTPUT_STAGE)) {
setEffectSuspended_l(NULL, enabled, AUDIO_SESSION_OUTPUT_MIX);
}
}
sp<EffectChain> chain = getEffectChain_l(sessionId);
if (chain != 0) {
chain->checkSuspendOnEffectEnabled(effect, enabled);
}
}
可以看到当sessionId不是全局的时候
void AudioFlinger::ThreadBase::setEffectSuspended_l(
const effect_uuid_t *type, bool suspend, audio_session_t sessionId)
{
sp<EffectChain> chain = getEffectChain_l(sessionId);
if (chain != 0) {
if (type != NULL) {
chain->setEffectSuspended_l(type, suspend);
} else {
chain->setEffectSuspendedAll_l(suspend);
}
}
updateSuspendedSessions_l(type, suspend, sessionId);
}
setEffectSuspended_l会disable到其他的音效,所以对于系统集成音效来说,所有音效的使用尽量都使用全局的sessionId
原文链接:https://blog.csdn.net/songche123/article/details/100118269