音量调节分3个部分,分别是master volume(硬件音量,控制声卡),stream volume(流音量)和track volume(app音量)。
app音量大小公式:
app_mix = master_volume * stream_volume * track_volume;
其中master_volume,stream_volume和track_volume都是百分比,1表示音量调到最大;音量最大分贝是0db,表示没有衰减,也就是音源音量;
volume(使用滑动条控制的音量是stream音量)
(1)VolumeStreamState
android系统定义了11种Stream(从0到10),每个stream都用VolumeStreamState来封装:
VolumeStreamState[] mStreamStates = new int[] {0,1,2,3,4,5,6,7,8,9,10};//0,1,2...这些数字表示stream类型,分别对应default,voice call,ring等;
VolumeStreamState{//用来管理一个流类型所有的音量信息
/*stream类型*/
int mStreamType;
/*volume最小值,只有0和1*/
int mIndexMin;
/*volume最大值,7,15等*/
int mIndexMax;//
boolean mIsMuted;
/*VolumeStreamState的名字,和stream类型对应*/
String mVolumeIndexSettingName;
int mObservedDevices;
/*map中的key为device,value为音量值bvolume*/
SparseIntArray mIndexMap = new SparseIntArray(8);
/*当音量发生改变时,发送广播AudioManager.VOLUME_CHANGED_ACTION*/
Intent mVolumeChanged;
Intent mStreamDevicesChanged;
}
(2)alias
android系统定义了11种Stream(从0到10),如果用一个数组来表示,它们与mStreamStates数组中的元素一一对应:
int[] STREAM_VOLUME_DEFAULT = new int[] {0,1,2,3,4,5,6,7,8,9,10};//系统默认
android定义了这么多的stream,是为了方便以后扩展开发,但实际上android设备并不支持这么多的stream,比如点击手机音量键调节某一个Stream音量时,android系统只会出现5个滑动条,也就是手机设备只有5类stream,又比如机顶盒只支持music stream这1类,所以对于不同的平台,需要将这些stream进行分组,把具有相同属性stream分为一类,在Android源码中称之为"别名", 即alias;
下面就是android源码对手机/机顶盒11种stream的分组结果:
int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {0,2,2,3,4,5,6,2,2,3,3};//手机
int[] STREAM_VOLUME_ALIAS_TELEVISION = new int[] {3,3,3,3,3,3,3,3,3,3,3};//机顶盒
int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {0,2,2,3,4,2,6,2,2,3,3};//默认
对于手机:
0 ----> 0
2 ----> 1,2,7,8
3 ----> 3,9,10
4 ----> 4
5 ----> 5
6 ----> 6
对于机顶盒:
3 ----> 0,1,2,3,4,5,6,7,8,9,10
以手机为例,手机只支持2,3,4,5,6(0暂时忽略)这5种stream,由于手机的3,9,10归类到了3,也就是别名alias为3,所以当手机调节3,9,10这3种stream时,实际上调节的是3(music stream)。
(3)系统提供的调节stream volume的api有2个,分别是adjustVolume()和 setStreamVolume(),以adjustVolume()为例:
adjustVolume()
//java层:
//
adjustSuggestedStreamVolume()
//确定streamType
final int streamType;
if (mUserSelectedVolumeControlStream) {
streamType = mVolumeControlStream;
}else{
......
}
adjustStreamVolume()
int streamTypeAlias = mStreamVolumeAlias[streamType];//将streamType转化为对应平台的streamTypeAlias
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
final int device = getDeviceForStream(streamTypeAlias);//得到当前的device
int aliasIndex = (device);//得到该device在当前stream上的音量
int step;
step = rescaleIndex(10, streamType, streamTypeAlias);//由于不同的流类型的音量调节范围不同,rescaleIndex用于将音量值的变化量从源流类型变换到目标流类型下
/*安全音量相关*/
......
if(()){//调节音量,设置新的index值并发送音量改变的广播
sendMsg(...,MSG_SET_DEVICE_VOLUME,...);//设置index到底层,并且将index保存到数据库
setDeviceVolume();
streamState.applyDeviceVolume_syncVSS(device);//设置index到底层
int index;
index = ......;//确定index的最终值
(mStreamType, index, device);//将index设置到底层
for(......){//处理mStreamVolumeAlias相关
mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
}
sendMsg(...,MSG_SET_DEVICE_VOLUME,...);//将index保存到数据库
persistVolume();
(......);//将index保存到数据库
}
sendVolumeUpdate(streamType, oldIndex, index, flags);//跟volumeui相关
//看看()
()
setIndex()//设置新的index值并发送音量改变的广播
oldIndex = getIndex(device);//volume改变之前的index
(device, index);//保存volume改变之后的index
changed = oldIndex != index;//如果volume改变前后的index不相同
for(...){//处理alias相关
VolumeStreamState aliasStreamState = mStreamStates[streamType];
(scaledIndex, device, caller);
}
if(changed){//当音量发生改变时,发送广播AudioManager.VOLUME_CHANGED_ACTION
sendBroadcastToAll(mVolumeChanged);
}
//native层
//
()
//
AudioSystem::setStreamVolumeIndex()
//
AudioPolicyManager::setStreamVolumeIndex()
for (size_t i = 0; i < (); i++) {
checkAndSetVolume();//设置每个输出设备的音量
float volumeDb = computeVolume(stream, index, device);//计算音量
volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index);
//
VolumeCurve::volIndexToDb()//根据音量曲线计算出音量
outputDesc->setVolume(volumeDb, stream, device, delayMs, force);
//
SwAudioOutputDescriptor::setVolume()
bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force);
AudioOutputDescriptor::setVolume()
mCurVolume[stream] = volume;
mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);
//
AudioPolicyService::setStreamVolume()
mAudioCommandThread->volumeCommand()//AudioPolicyService::AudioCommandThread::volumeCommand()
sp<AudioCommand> command = new AudioCommand();
command->mCommand = SET_VOLUME;
sendCommand(command, delayMs);//AudioPolicyService::AudioCommandThread::sendCommand()
insertCommand_l(command, delayMs);//插入命令,执行AudioPolicyService::AudioCommandThread::threadLoop()
AudioSystem::setStreamVolume();//AudioSystem::setStreamVolume()
//
af->setStreamVolume(stream, value, output);//AudioFlinger::setStreamVolume()
//
VolumeInterface *volumeInterface = getVolumeInterface_l(output);
volumeInterface->setStreamVolume(stream, value);//AudioFlinger::PlaybackThread::setStreamVolume()
//
mStreamTypes[stream].volume = value;//保存volume值
broadcast_l();//唤醒PlaybackThread线程
AudioFlinger::PlaybackThread::threadLoop()
mMixerStatus = prepareTracks_l(&tracksToRemove);//不同类型的Thread对prepareTracks_l有不同的实现
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l()
//该函数在track volume中分析
......
}
(4)音量曲线
每个stream上面不同的device,都有一对音量关系对应点与之对应,这些关系点连接起来就是音量曲线;
由于人耳对声音的听感具指数曲线型,也就是对小音量时比较敏感,随着声音的加大其听感随之变的不敏感,其变化近似指数函数曲线的形式。为了使听感变的近似直线的变化,人们在实践中就采用了音量变化近似对数式曲线型的电位器来实现这个目的。
总结了从线性UI的Index如何转化为对数关系的人耳的听觉转换的公式,以及预置的区间表,根据不同的硬件,我们可以自己预置适当的区间表,使得音量曲线更符合我们的听感。
(5)总结:
i)调节stream的音量,java层实际上调节的是挂在stream上的device的index值,每个挂在stream上的device都有一个音量曲线,native层是将index值转化为音量曲线中的值:
|--> device1 (index) --> curve1
|--> device2 (index) --> curve2
stream1 --> |--> device3 (index) --> curve3
|--> device4 (index) --> curve4
|--> device5 (index) --> curve5
|--> device1 (index) --> curve6
|--> device2 (index) --> curve7
stream2 --> |--> device3 (index) --> curve8
|--> device4 (index) --> curve9
|--> device5 (index) --> curve10
ii)音量设置的"alias"如何起作用:
set volume for stream; // 设置"推荐的流"的音量
if (mStreamVolumeAlias[other stream] == stream)
set volume for other stream; // 设置同属一个alias的其他流的音量
对于不同的设备(电话、TV、平板), mStreamVolumeAlias指向不同的数组
iii)怎么设置流的音量:
设置audioflinger中的数组: mStreamTypes[stream].volume = value;
设置PlaybackThread中的数组: mStreamTypes[stream].volume = value;
volume(APP设置的是AudioTrack的音量)
//
AudioTrack::setVolume(float left, float right)
/*传入的音量值保存在mVolume数组中*/
mVolume[AUDIO_INTERLEAVE_LEFT] = left;
mVolume[AUDIO_INTERLEAVE_RIGHT] = right;
/*setVolumeLR会把做声道与右声道的值,组装成一个数*/
mProxy->setVolumeLR(gain_minifloat_pack(gain_from_float(left), gain_from_float(right)));
//
/*mCblk表示共享内存的头部,也就是说这个音量值会保存到共享内存的头部*/
mCblk->mVolumeLR = volumeLR;
播放声音时需要AudioMixer进行混音,继续分析AudioFlinger::MixerThread::prepareTracks_l():
//
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l()
/*取出硬件声音*/
float masterVolume = mMasterVolume;
bool masterMute = mMasterMute;
/*把所有活跃的Tracks取出来*/
for (size_t i=0 ; i<count ; i++) {
......
//这就是stream volume
float typeVolume = mStreamTypes[track->streamType()].volume;
float v = masterVolume * typeVolume;
/*从proxy中取出取出音量,其就是通过头部保存的音量,其含有左右声道的音量*/
gain_minifloat_packed_t vlr = proxy->getVolumeLR();
/*提取左右声道的值*/
vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
/*都与之前的V进行相乘*/
vlf *= v * vh;
vrf *= v * vh;
/*把vlf与vrf传入给AudioMixer*/
mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);
}
音量就分析到这里。