Android 4.4 音量调节流程分析(一)
最近在做Android Audio方面的工作,有需求是在调节Volume_Up_Key & Volume_Down_key时,Spearker or Headset每音阶的衰减变为3db左右。所以利用Source Insight分析Android源码中音量控制的流程,如有错误,欢迎指正,谢谢!
以下是调节音量的流程:
Step_1.首先在调节机台Volume_Up_Key & Volume_Down_Key操作时,系统会调用到AudioManager.java中handleKeyUp & handleKeyDown函数,以 handleKeyDown函数为例:
1 public void handleKeyDown(KeyEvent event, int stream) { 2 int keyCode = event.getKeyCode(); 3 switch (keyCode) { 4 case KeyEvent.KEYCODE_VOLUME_UP: /*KeyEvent 在KeyEvent.java中定义*/ 5 case KeyEvent.KEYCODE_VOLUME_DOWN: 6 7 int flags = FLAG_SHOW_UI | FLAG_VIBRATE; 8 9 if (mUseMasterVolume) { 10 adjustMasterVolume( 11 keyCode == KeyEvent.KEYCODE_VOLUME_UP 12 ? ADJUST_RAISE 13 : ADJUST_LOWER, 14 flags); 15 } else { 16 adjustSuggestedStreamVolume( 17 keyCode == KeyEvent.KEYCODE_VOLUME_UP 18 ? ADJUST_RAISE 19 : ADJUST_LOWER, 20 stream, 21 flags); 22 } 23 break; 24 ... ... 25 } 26 }
其中是否进入adjustMasterVolume 函数是通过mUseMasterVolume的值判断的,而mUseMasterVolume的值是在AudioManager的构造函数中定义,其值的大小如下:mUseMasterVolume = mContext.getResources().getBoolean(com.android.internal.R.bool.config_useMasterVolume),所以首先从系统的配置文件config.xml中查找config_useMasterVolume值的大小
<bool name="config_useMasterVolume">false</bool>
所以handleKeyDown中 switch语句中会选择进入adjustSuggestedStreamVolume函数。
1 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { 2 IAudioService service = getService(); 3 try { 4 if (mUseMasterVolume) { 5 service.adjustMasterVolume(direction, flags, mContext.getOpPackageName()); 6 } else { 7 service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, 8 mContext.getOpPackageName()); 9 } 10 ... ...
11 } 12 }
Step_2.在adjustSuggestedStreamVolume函数中首先会通过binder机制得到AudioService,并将音量控制过程转入到AudioService.java中。
1 public void adjustStreamVolume(int streamType, int direction, int flags, 2 String callingPackage) { 3 ... ... 4 /*音量调大时,若要超过SafeMediaVolume时,系统会弹出对话框给予确认*/ 5 if ((direction == AudioManager.ADJUST_RAISE) && 6 !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { 7 Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex); 8 mVolumePanel.postDisplaySafeVolumeWarning(flags); 9 } else if (streamState.adjustIndex(direction * step, device)) { 10 sendMsg(mAudioHandler, 11 MSG_SET_DEVICE_VOLUME, /*需要处理的Message值*/ 12 SENDMSG_QUEUE, 13 device, 14 0, 15 streamState, 16 0); 17 } 18 } 19 int index = mStreamStates[streamType].getIndex(device); 20 sendVolumeUpdate(streamType, oldIndex, index, flags); /*通知上层更新Volume*/ 21 }
在adjustStreamVolume 中会通过sendMsg的方式来将调节音量的事件加入到消息列队SENDMSG_QUENE中,当轮寻到该Message时,系统会调用handleMessage函数来处理该Message,此时该处对应的Message为MSG_SET_DEVICE_VOLUME。
1 public void handleMessage(Message msg) { 2 3 switch (msg.what) { 4 5 case MSG_SET_DEVICE_VOLUME: 6 setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1); 7 break; 8 9 case MSG_SET_ALL_VOLUMES: 10 setAllVolumes((VolumeStreamState) msg.obj); 11 break; 12 13 ... ... 14 } 15 }
可以发现当msg.what = MSG_SET_DEVICE_VOLUME时,会进到setDeviceVolume函数中,继续往下分析:
1 private void setDeviceVolume(VolumeStreamState streamState, int device) { 2 3 // Apply volume 4 streamState.applyDeviceVolume(device); 5 6 // Apply change to all streams using this one as alias 7 ... ... 8 9 // Post a persist volume msg 10 ... ... 11 }
applyDeviceVolume就是将音量Volume设置到对应的设备Device上,继续往下分析:
1 public void applyDeviceVolume(int device) { 2 int index; 3 if (isMuted()) { 4 index = 0; 5 } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && 6 mAvrcpAbsVolSupported) { 7 index = (mIndexMax + 5)/10; 8 } else { 9 index = (getIndex(device) + 5)/10; 10 } 11 AudioSystem.setStreamVolumeIndex(mStreamType, index, device); 12 }
此处VolumeIndex就是对应UI界面调节音量时,音量所处在的位置下标。在AudioService.java中定义了每种音频流对应的Max-Index,在AudioManager.java中定义了每种音频流在第一次刷机后默认的Index。
Step_3.此时得到音量的下标Index后,会调用AudioSystem.java中的setStreamVolumeIndex函数中来得到此时音量的放大倍数。通过JNI层调用到AudioSystem.cpp文件中的 setStreamVolumeIndex中。
1 status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream, 2 int index, 3 audio_devices_t device) 4 { 5 const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); 6 if (aps == 0) return PERMISSION_DENIED; 7 return aps->setStreamVolumeIndex(stream, index, device); 8 }
setStreamVolumeIndex函数中比较简单,通过StrongPointer来与AudioPolicyService建立联系,将AudioSystem中的setStreamVolumeIndex操作移到aps中完成。下面进入到AudioPolicyService.cpp文件中的setStreamVolumeIndex继续分析:
1 status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, 2 int index, 3 audio_devices_t device) 4 { 5 ... ... 6 if (mpAudioPolicy->set_stream_volume_index_for_device) { 7 return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy, 8 stream, 9 index, 10 device); 11 } else { 12 return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index); 13 } 14 }
Step_4.AudioPolicyService.cpp作为bn端,其对应的bp端为AudioPolicyManagerBase.cpp。在当前函数的if语句中判断AudioPolicyManagerBase.cpp文件中是否存在setStreamVolumeIndexForDevice函数,条件成立则会选择setStreamVolumeIndexForDevice作为函数入口端;否则选择setStreamVolumeIndex作为函数入口。现在进入AudioPolicyManagerBase.cpp中文件中完成最后的分析:
1 status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, 2 int index, 3 audio_devices_t device) 4 { 5 ... ... 6 // compute and apply stream volume on all outputs according to connected device 7 status_t status = NO_ERROR; 8 for (size_t i = 0; i < mOutputs.size(); i++) { 9 audio_devices_t curDevice = 10 getDeviceForVolume(mOutputs.valueAt(i)->device()); 11 if ((device == AUDIO_DEVICE_OUT_DEFAULT) || (device == curDevice)) { 12 status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice); 13 if (volStatus != NO_ERROR) { 14 status = volStatus; 15 } 16 } 17 } 18 return status; 19 }
继续调用checkAndSetVolume函数:
1 status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, 2 int index, 3 audio_io_handle_t output, 4 audio_devices_t device, 5 int delayMs, 6 bool force) 7 { 8 9 // do not change actual stream volume if the stream is muted 10 ... ... 11 // do not change in call volume if bluetooth is connected and vice versa 12 ... ... 13 audio_devices_t checkedDevice = (device == AUDIO_DEVICE_NONE) ? mOutputs.valueFor(output)->device() : device; 14 float volume = computeVolume(stream, index, checkedDevice); 15 16 ... ... 17 mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);/*将得到的volume应用到对应的output中*/ 18 }
在checkAndSetVolume中可以知道volume是通过computeVolume得到的。继续向下分析:
float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_devices_t device) { ... ... volume = volIndexToAmpl(device, streamDesc, index); ... ... return volume; }
终于到了最后volIndexToAmpl,从函数名就可以知道该函数的作用是通过volIndex得到音量放大倍数。
float AudioPolicyManagerBase::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, int indexInUi) { device_category deviceCategory = getDeviceCategory(device); const VolumeCurvePoint *curve = streamDesc.mVolumeCurve[deviceCategory]; // the volume index in the UI is relative to the min and max volume indices for this stream type int nbSteps = 1 + curve[VOLMAX].mIndex - curve[VOLMIN].mIndex; int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin); // find what part of the curve this index volume belongs to, or if it‘s out of bounds int segment = 0; if (volIdx < curve[VOLMIN].mIndex) { // out of bounds return 0.0f; } else if (volIdx < curve[VOLKNEE1].mIndex) { segment = 0; } else if (volIdx < curve[VOLKNEE2].mIndex) { segment = 1; } else if (volIdx <= curve[VOLMAX].mIndex) { segment = 2; } else { // out of bounds return 1.0f; } // linear interpolation in the attenuation table in dB float decibels = curve[segment].mDBAttenuation + ((float)(volIdx - curve[segment].mIndex)) * ( (curve[segment+1].mDBAttenuation - curve[segment].mDBAttenuation) / ((float)(curve[segment+1].mIndex - curve[segment].mIndex)) ); float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) return amplification; }
其中curve代表在用设备(如SPEAKER、HEADSET & EARPIECE)播放音频流(如SYSTEM、MUSIC、ALARM、RING & TTS等)时的音量曲线,最后decibles & amplification就是我们需要求的值,其中decibles代表某一音节所对应的dB值,而amplification则是由dB值转化得到的音量放大倍数。这样整个音量调节过程到此就算完成了,具体的计算分析会放在后面继续分析。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。