com.android.musicFx设置音效流程 -- 从app到AudioFlinger

      本文主要内容如标题所示,主要描述下针对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的了,所以本文的分析暂时算结束了,有不对的地方欢迎留言讨论。






  

你可能感兴趣的:(com.android.musicFx设置音效流程 -- 从app到AudioFlinger)