android 音量调节

安卓音频数据的最终音量由三部分组成,分别是master volume(全局音量,对整个系统所有的音频数据生效),stream volume(流音量,只针对特定类型的音频数据生效)和track volume(track音量,只针对某个audiotrack的数据生效)。

音频数据音量大小公式:

final_volume= master_volume * stream_volume * track_volume;playbackthread负责这个具体值的计算并设置到audiomixer中生效。

其中master_volume,stream_volume和track_volume都是百分比,1表示音量调到最大;

音量最大分贝是0db,表示没有衰减,也就是音源音量;

stream volume

VolumeStreamState(audioservice用来管理系统音量引入的概念)

android系统定义了11种Stream(从0到10),每个stream都用VolumeStreamState来封装:

markup 复制代码
VolumeStreamState[] mStreamStates = new int[] {0,1,2,3,4,5,6,7,8,9,10};//0,1,2...这些数字表示stream类型,分别对应default,voice call,ring等;
markup 复制代码
VolumeStreamState{//用来管理一个流类型所有的音量信息
    /*stream类型*/
    int mStreamType;
 
    /*volume最小索引,只有0和1*/
    int mIndexMin;
 
    /*volume最大索引,7,15等*/
    int mIndexMax;//
 
    boolean mIsMuted;
 
    /*VolumeStreamState的名字,用来对系统setting进行查询和持久化*/
    String mVolumeIndexSettingName;
 
    int mObservedDevices;
 
    /*map中的key为device,value为音量值bvolume*/
    SparseIntArray mIndexMap = new SparseIntArray(8);
 
    /*当音量发生改变时,发送广播AudioManager.VOLUME_CHANGED_ACTION*/
    Intent mVolumeChanged;
 
    Intent mStreamDevicesChanged;
}

alias流别名

android系统定义了11种Stream(从0到10),如果用一个数组来表示,它们与mStreamStates数组中的元素一一对应:

markup 复制代码
int[] STREAM_VOLUME_DEFAULT = new int[] {0,1,2,3,4,5,6,7,8,9,10};//系统默认

android定义了这么多的streamtype,但目前android设备的并不支持这么多的stream,比如点击手机音量键调节某一个Stream音量时,android系统只会出现5个滑动条,也就是手机设备只有5类stream,又比如机顶盒只支持music stream这1类,所以对于不同的平台,需要将这些stream进行分组,把具有相同属性stream分为一类,在Android源码中称之为"别名", 即alias;

下面就是android源码11种stream的分组结果:

markup 复制代码
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};//默认

以手机为例,手机只支持2,3,4,5,6(0暂时忽略)这5种stream,由于手机的3,9,10归类到了3,也就是别名alias为3,所以当手机调节3,9,10这3种stream时,实际上调节的是3(music stream)。

系统提供的调节stream volume的api有2个,分别是adjustVolume()和 setStreamVolume(),我们看adjustVolume():

markup 复制代码
adjustVolume()
//java层:
//AudioService.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 = streamState.getIndex(device);//得到该device在当前stream上的音量
 
        int step;
        step = rescaleIndex(10, streamType, streamTypeAlias);//音量步进转化
        /*安全音量相关*/
        ......
        
        if(streamState.adjustIndex()){//调节音量,设置新的index值并发送音量改变的广播
            sendMsg(...,MSG_SET_DEVICE_VOLUME,...);//设置index到底层,并且将index保存到系统settings
                setDeviceVolume();
                    streamState.applyDeviceVolume_syncVSS(device);//设置index到底层,一直想不明白VSS是个啥缩写,今天突然明白了是VolumeStreamState的首字母缩写!!!!!!!!!!!!!!!!!!!!!!!1
                        int index;
                        index = ......;//确定index的最终值
                        AudioSystem.setStreamVolumeIndex(mStreamType, index, device);//将index设置到底层
                
                    for(......){//处理mStreamVolumeAlias相关
                        mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
                    }
            
                    sendMsg(...,MSG_SET_DEVICE_VOLUME,...);//将index保存到系统settings
                        persistVolume();
                            System.putIntForUser(......);//将index保存到系统settings
        }
 
        sendVolumeUpdate(streamType, oldIndex, index, flags);//刷新音量条(UI)
 
 
        //看看streamState.adjustIndex()
        streamState.adjustIndex()
            setIndex()//设置新的index值并发送音量改变的广播
                oldIndex = getIndex(device);//volume改变之前的index
                mIndexMap.put(device, index);//保存volume改变之后的index
                changed = oldIndex != index;//如果volume改变前后的index不相同
 
                for(...){//处理alias相关
                    VolumeStreamState aliasStreamState = mStreamStates[streamType];
                    aliasStreamState.setIndex(scaledIndex, device, caller);
                }
 
                if(changed){//当音量发生改变时,发送广播AudioManager.VOLUME_CHANGED_ACTION
                    sendBroadcastToAll(mVolumeChanged);
                }

//native层

//AudioSystem.java

AudioSystem.setStreamVolumeIndex()

//AudioSystem.cpp

AudioSystem::setStreamVolumeIndex()

//AudioPolicyManager.cpp

AudioPolicyManager::setStreamVolumeIndex()

for (size_t i = 0; i < mOutputs.size(); i++) {

checkAndSetVolume();//设置每个输出设备的音量

float volumeDb = computeVolume(stream, index, device);//计算音量

volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index);

//VolumeCurve.cpp

VolumeCurve::volIndexToDb()//根据音量曲线计算出音量

outputDesc->setVolume(volumeDb, stream, device, delayMs, force);

//AudioOutputDescriptor.cpp

SwAudioOutputDescriptor::setVolume()

bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force);

AudioOutputDescriptor::setVolume()

mCurVolume[stream] = volume;

mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);

//AudioPolicyService.cpp

AudioPolicyService::setStreamVolume()

mAudioCommandThread->volumeCommand()//AudioPolicyService::AudioCommandThread::volumeCommand()

sp command = new AudioCommand();

command->mCommand = SET_VOLUME;

sendCommand(command, delayMs);//AudioPolicyService::AudioCommandThread::sendCommand()

insertCommand_l(command, delayMs);//插入命令,执行AudioPolicyService::AudioCommandThread::threadLoop()

AudioSystem::setStreamVolume();//AudioSystem::setStreamVolume()

//AudioSystem.cpp

af->setStreamVolume(stream, value, output);//AudioFlinger::setStreamVolume()

//AudioFlinger.cpp

VolumeInterface *volumeInterface = getVolumeInterface_l(output);

volumeInterface->setStreamVolume(stream, value);//AudioFlinger::PlaybackThread::setStreamVolume()

//Threads.cpp

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数据最终的音量

...

}

音量曲线:我们从上面的代码中可以看到在audioservice中音量都是整形的index。而用于音量算法处理的是DB。音量曲线的作用就是将index转化为对应的DB 。这个音量曲线是可以通过配置文件修改。配置文件会定义一些点,audiopolicymanager会根据配置文件的点进行拟合,获得一条从最小index到最大index隐射到-∞DB到0DB的曲线。

auditorack音量设置:

markup 复制代码
//AudioTrack.cpp
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)));
        //AudioTrackShared.h
        /*mCblk表示共享内存的头部,也就是说这个音量值会保存到共享内存的头部*/
        mCblk->mVolumeLR = volumeLR;
 
播放声音时需要AudioMixer进行混音,继续分析AudioFlinger::MixerThread::prepareTracks_l():
//Threads.cpp
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);
    }
相关推荐
阿巴斯甜9 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker9 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952710 小时前
Andorid Google 登录接入文档
android
黄林晴11 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android