最近在做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函数为例:

 public void handleKeyDown(KeyEvent event, int stream) {
int keyCode = event.getKeyCode();
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP: /*KeyEvent 在KeyEvent.java中定义*/
case KeyEvent.KEYCODE_VOLUME_DOWN: int flags = FLAG_SHOW_UI | FLAG_VIBRATE; if (mUseMasterVolume) {
adjustMasterVolume(
keyCode == KeyEvent.KEYCODE_VOLUME_UP
? ADJUST_RAISE
: ADJUST_LOWER,
flags);
} else {
adjustSuggestedStreamVolume(
keyCode == KeyEvent.KEYCODE_VOLUME_UP
? ADJUST_RAISE
: ADJUST_LOWER,
stream,
flags);
}
break;
       ... ...
}
}

  其中是否进入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函数。

     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
IAudioService service = getService();
try {
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
mContext.getOpPackageName());
}
  ... ...
      }
}

  Step_2.在adjustSuggestedStreamVolume函数中首先会通过binder机制得到AudioService,并将音量控制过程转入到AudioService.java中。

     public void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage) {
... ...
/*音量调大时,若要超过SafeMediaVolume时,系统会弹出对话框给予确认*/
if ((direction == AudioManager.ADJUST_RAISE) &&
!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);
mVolumePanel.postDisplaySafeVolumeWarning(flags);
} else if (streamState.adjustIndex(direction * step, device)) {
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME, /*需要处理的Message值*/
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
}
int index = mStreamStates[streamType].getIndex(device);
sendVolumeUpdate(streamType, oldIndex, index, flags); /*通知上层更新Volume*/
}

  在adjustStreamVolume 中会通过sendMsg的方式来将调节音量的事件加入到消息列队SENDMSG_QUENE中,当轮寻到该Message时,系统会调用handleMessage函数来处理该Message,此时该处对应的Message为MSG_SET_DEVICE_VOLUME。

        public void handleMessage(Message msg) {

             switch (msg.what) {

                 case MSG_SET_DEVICE_VOLUME:
setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
break; case MSG_SET_ALL_VOLUMES:
setAllVolumes((VolumeStreamState) msg.obj);
break;   ... ...
}
}

  可以发现当msg.what =  MSG_SET_DEVICE_VOLUME时,会进到setDeviceVolume函数中,继续往下分析:

         private void setDeviceVolume(VolumeStreamState streamState, int device) {

             // Apply volume
streamState.applyDeviceVolume(device); // Apply change to all streams using this one as alias
     ... ... // Post a persist volume msg
     ... ...
}

  applyDeviceVolume就是将音量Volume设置到对应的设备Device上,继续往下分析:

         public void applyDeviceVolume(int device) {
int index;
if (isMuted()) {
index = 0;
} else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
mAvrcpAbsVolSupported) {
index = (mIndexMax + 5)/10;
} else {
index = (getIndex(device) + 5)/10;
}
AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
}

  此处VolumeIndex就是对应UI界面调节音量时,音量所处在的位置下标。在AudioService.java中定义了每种音频流对应的Max-Index,在AudioManager.java中定义了每种音频流在第一次刷机后默认的Index。

  Step_3.此时得到音量的下标Index后,会调用AudioSystem.java中的setStreamVolumeIndex函数中来得到此时音量的放大倍数。通过JNI层调用到AudioSystem.cpp文件中的 setStreamVolumeIndex中。

 status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == ) return PERMISSION_DENIED;
return aps->setStreamVolumeIndex(stream, index, device);
}

  setStreamVolumeIndex函数中比较简单,通过StrongPointer来与AudioPolicyService建立联系,将AudioSystem中的setStreamVolumeIndex操作移到aps中完成。下面进入到AudioPolicyService.cpp文件中的setStreamVolumeIndex继续分析:

 status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
... ...
if (mpAudioPolicy->set_stream_volume_index_for_device) {
return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy,
stream,
index,
device);
} else {
return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index);
}
}

  Step_4.AudioPolicyService.cpp作为bn端,其对应的bp端为AudioPolicyManagerBase.cpp。在当前函数的if语句中判断AudioPolicyManagerBase.cpp文件中是否存在setStreamVolumeIndexForDevice函数,条件成立则会选择setStreamVolumeIndexForDevice作为函数入口端;否则选择setStreamVolumeIndex作为函数入口。现在进入AudioPolicyManagerBase.cpp中文件中完成最后的分析:

 status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream,
int index,
audio_devices_t device)
{
... ...
// compute and apply stream volume on all outputs according to connected device
status_t status = NO_ERROR;
for (size_t i = ; i < mOutputs.size(); i++) {
audio_devices_t curDevice =
getDeviceForVolume(mOutputs.valueAt(i)->device());
if ((device == AUDIO_DEVICE_OUT_DEFAULT) || (device == curDevice)) {
status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice);
if (volStatus != NO_ERROR) {
status = volStatus;
}
}
}
return status;
}

继续调用checkAndSetVolume函数:

 status_t AudioPolicyManagerBase::checkAndSetVolume(int stream,
int index,
audio_io_handle_t output,
audio_devices_t device,
int delayMs,
bool force)
{ // do not change actual stream volume if the stream is muted
... ...
// do not change in call volume if bluetooth is connected and vice versa
... ...
audio_devices_t checkedDevice = (device == AUDIO_DEVICE_NONE) ? mOutputs.valueFor(output)->device() : device;
float volume = computeVolume(stream, index, checkedDevice); ... ...
  mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);/*将得到的volume应用到对应的output中*/
}

  在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 = + 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 = ;
if (volIdx < curve[VOLMIN].mIndex) { // out of bounds
return 0.0f;
} else if (volIdx < curve[VOLKNEE1].mIndex) {
segment = ;
} else if (volIdx < curve[VOLKNEE2].mIndex) {
segment = ;
} else if (volIdx <= curve[VOLMAX].mIndex) {
segment = ;
} 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+].mDBAttenuation -
curve[segment].mDBAttenuation) /
((float)(curve[segment+].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值转化得到的音量放大倍数。这样整个音量调节过程到此就算完成了,具体的计算分析会放在后面继续分析。

  

Android 4.4 音量调节流程分析(一)的更多相关文章

  1. Android 4.4 音量调节流程分析(二)

    之前在Android 4.4 音量调节流程分析(一)里已经有简单的分析音量控制的流程,今天想接着继续分析下音量大小计算的方法.对于任一播放文件而言其本身都有着固定大小的音量Volume_Max,而在A ...

  2. Android 7.1 屏幕旋转流程分析

    Android 7.1   屏幕旋转流程分析 一.概述 Android屏幕的旋转在framework主要涉及到三个类,结构如图 PhoneWindowManager:为屏幕的横竖屏转换的管理类. Wi ...

  3. Android之 MTP框架和流程分析

    概要 本文的目的是介绍Android系统中MTP的一些相关知识.主要的内容包括:第1部分 MTP简介            对Mtp协议进行简单的介绍.第2部分 MTP框架            介绍 ...

  4. Android系统之LK启动流程分析(一)

    1.前言 LK是Little Kernel的缩写,在Qualcomm平台的Android系统中普遍采用LK作为bootloader,它是一个开源项目,LK是整个系统的引导部分,所以不是独立存在的,但是 ...

  5. Android视图状态及重绘流程分析,带你一步步深入了解View(三)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17045157 在前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程, ...

  6. Android SeekBar实现音量调节

    SeekBar可以通过滑块的位置来标识数值----而且拖动条允许用户拖动滑块来改变值,因此拖动条通常用于对系统的某种数值进行调节,比如调节音量等. SeekBar允许用户改变拖动条的滑块外观,改变滑块 ...

  7. Android N wifi auto connect流程分析

    定义 当有两个或者两个以上的已经保存的无线网络可以连接时,系统通过选择算法来选择一个最优网络. 在Android L,wifi的自动重连机制是由WifiAutoJoinController 类来实现, ...

  8. Android wpa_supplicant 四次握手 流程分析

    记录wpa_supplicant四次握手的过程. 相关log:https://www.cnblogs.com/helloworldtoyou/p/9633603.html 接收到第一次握手,会设置一个 ...

  9. Cocos2d-x3.3RC0的Android编译Activity启动流程分析

    本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...

随机推荐

  1. winform 递归循环阻止机构

    private void GetTree() { DataTable dt = new DataTable(); var sql = @" select OUID,ParentOUID,OU ...

  2. WinForm------如何打开子窗体的同时关闭父窗体

    方法: 如何打开子窗体的同时关闭父窗体 this.Hide(); new Frm_Management().ShowDialog(); this.Close();

  3. &lt; APT 攻击&gt;看起来是 .PPT 附件,竟是 .SCR !!

    趋势科技以前在2013年下半年度目标攻击综合报告里指出,发现了好几起APT攻击-高级持续性渗透攻击 (Advanced Persistent Threat, APT) /目标攻击相关的攻击活动. 趋势 ...

  4. 第十四篇:Apriori 关联分析算法原理分析与代码实现

    前言 想必大家都听过数据挖掘领域那个经典的故事 - "啤酒与尿布" 的故事. 那么,具体是怎么从海量销售信息中挖掘出啤酒和尿布之间的关系呢? 这就是关联分析所要完成的任务了. 本文 ...

  5. Echarts中axislabel文字过长导致显示不全或重叠

    最近在使用Echarts的时候,遇到点问题就是xAxis文字过长导致x轴的文字显示不全. 解决方案如下: 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HT ...

  6. mysql5.6的二进制包安装

    author: headsen chen data :2018-06-08  16:21:43 1. 创建存放软件文件夹 # cd / #mkdir a 2.下载MySQL5.6二进制包 cd a w ...

  7. dao---service---action分层结构

    此文转载于http://blog.csdn.net/jay198746/article/details/4698709 之前有看过一些ssh2中采用dao---service---action分层结构 ...

  8. 修改\Servlet和Jsp模板的方法

    1.在MyEclipse6.6\myeclipse\eclipse\plugins下找到com.genuitec.eclipse.wizards 2.templates\替换Servlet.java ...

  9. android开发时程序出现崩溃问题

    原因:项目目录下的:src与gen中的包名不一致时,出现问题 <manifest xmlns:android="http://schemas.android.com/apk/res/a ...

  10. 剑指Offer——按之字形顺序打印二叉树

    题目描述: 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. 分析: 我们都知道二叉树的层次遍历用的是队 ...