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);
    }
相关推荐
阿木看源码4 小时前
AndroidStudio无法识别连接夜神模拟器
android
沧海一树5 小时前
WanAndroid 项目迁移 KMP
android·ios
hardWork_yulu5 小时前
Android 媒体(音乐)播放通知栏
android·音视频·媒体
tangweiguo030519876 小时前
Kotlin when 表达式完全指南:从基础到高级的12种实战用法
android·开发语言·kotlin
tangweiguo030519876 小时前
Android 简化图片加载与显示——使用Coil和Kotlin封装高效工具类
android·开发语言·kotlin
漫步企鹅7 小时前
【漏洞修复】为了修复ARM64 Android10系统的第三方库漏洞,将ARM64 Android16的系统库直接拷贝到Android10系统如何?
android·漏洞·修复·系统库
二流小码农9 小时前
鸿蒙开发:如何实现文本跑马灯效果
android·ios·harmonyos
二流小码农9 小时前
鸿蒙开发:单一手势实现长按事件
android·ios·harmonyos
二流小码农10 小时前
鸿蒙开发:信息标记组件
android·ios·harmonyos
利维亚的杰洛特10 小时前
【Android15 ShellTransitions】(九)结束动画+Android原生ANR问题分析
android