由于人耳对声音的听感具指数曲线型,也就是对小音量时比较敏感,随着声音的加大其听感随之变的不敏感,其变化近似指数函数曲线的形式。为了使听感变的近似直线的变化,人们在实践中就采用了音量变化近似对数式曲线型的电位器来实现这个目的。对比法产生音量控制曲线与最终扬声器输出的声压有关,当然您也可以根据扬声器的输出功率来进行比对,但功率终究不如电压来的方便。音量调节框的UI滑动条的刻度是线性的,这样就给我们生成音量控制曲线打下了很好的对比基础。下面我们就来通过一个音量调节的场景来分析Android是如何控制音量的。
首先,我们按音量调节键使得media音量逐级增加到最大。STREAM_MUSIC流的音量分为15级,通过AudioManger的handleKeyDown函数调用adjustSuggestedStreamVol
// 在intel的CherryTrail平台的android 6.0 版本上,这个函数位置是:/frameworks/av/services/audiopolicy/engineconfigurable/src/Stream.cpp 里
- 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;//计算预置的曲线区间的范围,这里是(1-100) -
ALOGI("VOLUME vol indexInUi=%d, nbSteps=%d, mIndexMin=%d, mIndexMax=%d",indexInUi,nbSteps,streamDesc.mIndexMin,streamDesc.mIndexMax); -
int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / -
(streamDesc.mIndexMax - streamDesc.mIndexMin);//(由传进来的UIIndex计算百分比的index,比如现在是第一级 100*(1-0)/(15-0)=6) -
-
// 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; -
} -
//第一极6是在区间VOLKNEE1之间,其区间表是在AudioPolicyManager初始化的时候就已经加载,因此它对应的segment为0 -
// 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)) ); -
//计算衰减分贝数 curve[0].db + 该区间每一级index对应的db*index数 -
float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) - //由指数公式计算出音量amplification
db = 20log(V/Vmax) linearToLog Vmax是一个参考值 -
ALOGI("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f", -
curve[segment].mIndex, volIdx, -
curve[segment+1].mIndex, -
curve[segment].mDBAttenuation, -
decibels, -
curve[segment+1].mDBAttenuation, -
amplification); -
-
return amplification; -
}
音量刻度 | 1 | 33 | 66 | 100 |
输出衰减量db | -49.5 | -33.5 | -17.0 | 0.0 |
表1-1 default volume curve
音量刻度 | 1 | 20 | 60 | 100 |
输出衰减量db | -58.0 | -40.0 | -17.0 | 0.0 |
表1-2 default media volume curve
音量刻度 | 1 | 20 | 60 | 100 |
输出衰减量db | -56.0 | -34.0 | -11.0 | 0.0 |
表1-3 speaker media volume curve
音量刻度 | 1 | 33 | 66 | 100 |
输出衰减量db | -29.7 | -20.1 | -10.2 | 0.0 |
表1-4 speaker sonification volumecurve
音量刻度 | 1 | 33 | 66 | 100 |
输出衰减量db | -24.0 | -18.0 | -12.0 | -6.0 |
表1-5 default system volume curve
音量刻度 | 1 | 33 | 66 | 100 |
输出衰减量db | -30.0 | -26.0 | -22.0 | -18.0 |
表1-6 headset system volume curve
总结:通过调节音量键这一调节音量的场景,从java层,media本地层,AudioFlinger服务层,硬件抽象层等四层分析音量调节函数是如何完成一个音量调节任务的。总结了从线性UI的Index如何转化为对数关系的人耳的听觉转换的公式,以及预置的区间表,根据不同的硬件,我们可以自己预置适当的区间表,使得音量曲线更符合我们的听感。
在intel的 CherryTrail平台上,是用volume.pfw文件来配置这个三段音量dB值对应曲线的。
位置是:/device/intel/common/audio/parameter-framework/Settings/Policy/volumes.pfw