Android原生的音量调整都是调整软件音量,也就是对于解码后的pcm数据乘以一定的系数,范围0~1.0,常见的概念有:
Track volume : 单个AudioTrack的音量,使用时只影响该AudioTrack的音量,不会对其他AudioTrack造成影响,播完之后也就结束。
接口为:status_t AudioTrack::setVolume(float left, float right);
stream volume :设置某一stream的音量,这是系统中最常见的音量设置方式,通常都是设置AUDIO_STREAM_MUSIC类型即多媒体音量,而设置这项不会影响其他stream类型的音量,如按键音的AUDIO_STREAM_SYSTEM类型。
native的接口有:status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output);
master volume :设置它等于设置所有的stream volume和track volume。系统任何声音输出都会受到影响。而这个接口也保留了通过hal层直接设置硬件音量的可能性,直接设置硬件音量必然是会对系统所有声音有影响的
native的接口有: AudioSystem::setMasterVolume(float value);
所以一个输出的软件音量=master_volume * stream_volume * AudioTrack_volume,默认情况下master volume和Track volume都会是1.0,只会用到stream_volume。
master volume和Track volume的设置很简单,一般都是直接设置好自己想要设置的线性音量值,范围从0~1.0.
而stream volume涉及到音视频产品的音频曲线概念,有一套自己的规则,要分析stream volume的处理,AudioPolicyManager中的checkAndSetVolume函数是个高屋建瓴的入口,所有stream类型和不同的device的音量都是在这里进行计算并设置的。
Android P上的code如下,音量已经改成以db为单位计算了。
status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
int index,
const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device,
int delayMs,
bool force)
{
// do not change actual stream volume if the stream is muted
if (outputDesc->mMuteCount[stream] != 0) {
ALOGVV("checkAndSetVolume() stream %d muted count %d",
stream, outputDesc->mMuteCount[stream]);
return NO_ERROR;
}
audio_policy_forced_cfg_t forceUseForComm =
mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION);
// do not change in call volume if bluetooth is connected and vice versa
if ((stream == AUDIO_STREAM_VOICE_CALL && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) ||
(stream == AUDIO_STREAM_BLUETOOTH_SCO && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO)) {
ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
stream, forceUseForComm);
return INVALID_OPERATION;
}
if (device == AUDIO_DEVICE_NONE) {
device = outputDesc->device();
}
float volumeDb = computeVolume(stream, index, device);
if (outputDesc->isFixedVolume(device) ||
// Force VoIP volume to max for bluetooth SCO
((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) &&
(device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
volumeDb = 0.0f;
}
outputDesc->setVolume(volumeDb, stream, device, delayMs, force);
if (stream == AUDIO_STREAM_VOICE_CALL ||
stream == AUDIO_STREAM_BLUETOOTH_SCO) {
float voiceVolume;
// Force voice volume to max for bluetooth SCO as volume is managed by the headset
if (stream == AUDIO_STREAM_VOICE_CALL) {
voiceVolume = (float)index/(float)mVolumeCurves->getVolumeIndexMax(stream);
} else {
voiceVolume = 1.0;
}
if (voiceVolume != mLastVoiceVolume) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume;
}
}
return NO_ERROR;
}
computeVolume(stream, index, device)
根据当前的index计算出stream和device的当前音量,如果系统设置了fixed模式,意为软件音量不可改变,将固定为0db输出
float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
float volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index);
// handle the case of accessibility active while a ringtone is playing: if the ringtone is much
// louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch
// exploration of the dialer UI. In this situation, bring the accessibility volume closer to
// the ringtone volume
if ((stream == AUDIO_STREAM_ACCESSIBILITY)
&& (AUDIO_MODE_RINGTONE == mEngine->getPhoneState())
&& isStreamActive(AUDIO_STREAM_RING, 0)) {
const float ringVolumeDB = computeVolume(AUDIO_STREAM_RING, index, device);
return ringVolumeDB - 4 > volumeDB ? ringVolumeDB - 4 : volumeDB;
}
..............
}
将index转算出对应的db值
float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const
{
ALOG_ASSERT(!mCurvePoints.isEmpty(), "Invalid volume curve");
size_t nbCurvePoints = mCurvePoints.size();
// the volume index in the UI is relative to the min and max volume indices for this stream
int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex;
if (indexInUi < volIndexMin) {
ALOGV("VOLUME remapping index from %d to min index %d", indexInUi, volIndexMin);
indexInUi = volIndexMin;
} else if (indexInUi > volIndexMax) {
ALOGV("VOLUME remapping index from %d to max index %d", indexInUi, volIndexMax);
indexInUi = volIndexMax;
}
int volIdx = (nbSteps * (indexInUi - volIndexMin)) / (volIndexMax - volIndexMin);
// Where would this volume index been inserted in the curve point
size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
if (indexInUiPosition >= nbCurvePoints) {
//use last point of table
return mCurvePoints[nbCurvePoints - 1].mAttenuationInMb / 100.0f;
}
if (indexInUiPosition == 0) {
if (indexInUiPosition != mCurvePoints[0].mIndex) {
return VOLUME_MIN_DB; // out of bounds
}
return mCurvePoints[0].mAttenuationInMb / 100.0f;
}
// linear interpolation in the attenuation table in dB
float decibels = (mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f) +
((float)(volIdx - mCurvePoints[indexInUiPosition - 1].mIndex)) *
( ((mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f) -
(mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f)) /
((float)(mCurvePoints[indexInUiPosition].mIndex -
mCurvePoints[indexInUiPosition - 1].mIndex)) );
ALOGV("VOLUME mDeviceCategory %d, mStreamType %d vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
mDeviceCategory, mStreamType,
mCurvePoints[indexInUiPosition - 1].mIndex, volIdx,
mCurvePoints[indexInUiPosition].mIndex,
((float)mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f), decibels,
((float)mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f));
return decibels;
}
利用audio_policy_volumes.xml中的音量点配置来计算对应音量,举例如下我们计算该类型index为70时候的音量值:
<volume stream="AUDIO_STREAM_ALARM" deviceCategory="DEVICE_CATEGORY_SPEAKER">
<point>0,-2970</point>
<point>33,-2010</point>
<point>66,-1020</point>
<point>100,0</point>
</volume>
nbCurvePoints = mCurvePoints.size();解析完之后为4
nbSteps = 1+ 100 - 0 = 101;
volIdx = 70;
indexInUiPosition 表示当前index在表中的位置,70在66和100之间,那么计算就以66和100作为参考
音量计算公式为,:
float decibels = (mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f) +
((float)(volIdx - mCurvePoints[indexInUiPosition - 1].mIndex)) *
( ((mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f) -
(mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f)) /
((float)(mCurvePoints[indexInUiPosition].mIndex -
mCurvePoints[indexInUiPosition - 1].mIndex)) );
mCurvePoints[0].mAttenuationInMb = -2970db
mCurvePoints[1].mAttenuationInMb = -2010db;
mCurvePoints[2].mAttenuationInMb = -1020db;
mCurvePoints[3].mAttenuationInMb = 0db;
70对应的db为 decibels = -10.2 + (70 -66)*(0-(-10.2))/(100-66) = -9.27db;
音量计算的逻辑分析完毕,而处理的地方在audiomixer里面,还涉及混音和重采样,有时间再独立去分析。
其实我们完全可以把100个点完全填写完毕也是可以的,这样使用公式平均化也是个粗略的估算,很多产品是有比较严格的音频曲线标准,我们可以在xml里将100个点的对应音量全部给出来。