本文主要内容如标題所示,主要描述下針對com.android.musicFx這個應用打開音效設定時的音效的函數調用流程。先簡單說com.android.musicFx(後面簡寫成MusicFx)的相關知識,MusicFx第一次出現是在android2.3版本,預設入口在Music播放界面menu菜單,菜單裡有一個音效選項拉起MusicFx應用,進入應用後界面比較簡單,一個spinner 和幾個seekbar來選擇類型(這裡的類型指的是鄉村,爵士,搖滾等)和設定具體數值,我們分析的起點就從改變seekbar滑動的值開始。
在開始之前,還要提及幾個原則性概念,在2.3版本中同時還增加了對音頻混響的支援,代碼主要展現在android.media.audiofx這個包中,其中AudioEffect是android audio framework(android 音頻架構)提供的音頻效果控制的基類,我們隻能使用它的派生類。下面列出它的派生類: BassBoost重低音,Equalizer均衡器,Virtualizer虛拟器, PresetReverb預置混響,EnvirenmentReverb環境音混響,Visaulizer可視化。 當建立AudioEffect時,如果音頻效果應用到一個具體的AudioTrack和MediaPlayer的執行個體,應用程式必須指定該執行個體的音頻session ID,如果要應用Global音頻輸出混響的效果必須制定Session。 以下開始具體的代碼流程, 以MusicFx為例(第三方應用未必是下面的界面,但執行的思路應該是一樣的),音效設定界面是ActivityMusic.java,在這個界面有一些seekbar的監聽函數,任意取一段代碼如下,
if (mVirtualizerSupported) {
seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(final SeekBar seekBar, final int progress,
final boolean fromUser) {
// set parameter and state
ControlPanelEffect.setParameterInt(mContext, mCallingPackageName,
mAudioSession, ControlPanelEffect.Key.virt_strength, progress);
}
// If slider pos was 0 when starting re-enable effect
@Override
public void onStartTrackingTouch(final SeekBar seekBar) {
if (seekBar.getProgress() == 0) {
ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName,
mAudioSession, ControlPanelEffect.Key.virt_enabled, true);
}
}
// If slider pos = 0 when stopping disable effect
@Override
public void onStopTrackingTouch(final SeekBar seekBar) {
if (seekBar.getProgress() == 0) {
// disable
ControlPanelEffect.setParameterBoolean(mContext, mCallingPackageName,
mAudioSession, ControlPanelEffect.Key.virt_enabled, false);
}
}
});
}
從代碼上看先走onStartTrackingTouch的setParameterBoolean,實際是也是這樣的,去ControlPanelEffect這個類看下它做了什麼,
final Virtualizer virtualizerEffect = getVirtualizerEffect(audioSession);
if (virtualizerEffect != null) {
virtualizerEffect.setEnabled(prefs.getBoolean(
Key.virt_enabled.toString(), VIRTUALIZER_ENABLED_DEFAULT));
final int vIStrength = prefs.getInt(Key.virt_strength.toString(),
VIRTUALIZER_STRENGTH_DEFAULT);
setParameterInt(context, packageName,
audioSession, Key.virt_strength, vIStrength);
}
這段代碼說明或證明以下事實, 前面提到過如果是一個具體音效執行個體必須有audioSession這個參數, 參數來自于調用音效的具體應用程式,以音樂Music為例,跳轉音效設定的代碼如下,
Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL);
i.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mService.getAudioSessionId());
startActivityForResult(i, EFFECTS_PANEL);
在set之前先執行getVirtualizerEffect,其中會通過getParameter先擷取一下值,然後再調用setParameterInt。 上面的代碼是以Virtualizer為例,Virtualizer是一Audioeffect的子類,前面提到過我們也隻能使用Audioeffect的派生類,是以一定還可以找到BassBoost、Equalizer類似結構的代碼。 現在看下setParameterInt函數,
// Virtualizer
case virt_strength: {
final Virtualizer virtualizerEffect = getVirtualizerEffect(audioSession);
if (virtualizerEffect != null) {
virtualizerEffect.setStrength((short) value); // 關注這裡
value = virtualizerEffect.getRoundedStrength();
}
break;}
public void setStrength(short strength)
throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
checkStatus(setParameter(PARAM_STRENGTH, strength)); //終于看到setParameter了
}
getParameter和setParameter代碼調用流程類似,看完setParameter函數兩個應該都可以明白了,是以按思維習慣,看set過程時先忽略getParameter的細節。
public int setParameter(byte[] param, byte[] value)
throws IllegalStateException {
checkState("setParameter()");
return native_setParameter(param.length, param, value.length, value); // native 要用到JNI了
}
根據android JNI的名稱轉換原則, AudioEffect在android.media包下,是以下面的代碼在audio_media_AudioEffect.cpp中,在那裡會找下面對應關系,要注意下面代碼段中的注釋。
audio_media_AUdioEffect.cpp
{"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter},
去android_media_AudioEffect_native_setParameter裡看看有什麼,
static jint android_media_AudioEffect_native_setParameter(JNIEnv *env,
jobject thiz, int psize, jbyteArray pJavaParam, int vsize,
jbyteArray pJavaValue) {
//省略很多暫時無關代碼
AudioEffect* lpAudioEffect = (AudioEffect *) env->GetIntField(thiz,
fields.fidNativeAudioEffect);
lStatus = lpAudioEffect->setParameter(p); //看到這裡要去Audioeffect裡去看看了, 注意這回是 Audioeffect.cpp
if (lStatus == NO_ERROR) {
lStatus = p->status;
}
status_t AudioEffect::setParameter(effect_param_t *param)
{
//省略很多暫時無關代碼
return mIEffect->command(EFFECT_CMD_SET_PARAM, sizeof (effect_param_t) + psize, param, &size, ¶m->status);
}
看到這裡肯定很想知道mIEffect是什麼,就在Audioeffect.cpp這個檔案裡找,可以找到 mIEffect的由iEffect指派,而iEffect初始化代碼如下,
iEffect = audioFlinger->createEffect(getpid(), (effect_descriptor_t *)&mDescriptor,
mIEffectClient, priority, io, mSessionId, &mStatus, &mId, &enabled);
到這裡我們就來到AudioFlinger,對于建立音效的後面流程,還要涉及到EffectHandle,EffectModule一些類,更具體過程還有待分析,上面描述應該可以說清楚app層的值是怎麼傳到audioFlinger的了,是以本文的分析暫時算結束了,有不對的地方歡迎留言讨論。