Volume adjustment is divided into three parts: master volume (hardware volume, control sound card), stream volume (stream volume) and track volume(app volume).
app volume formula:
app_mix = master_volume * stream_volume * track_volume;
Where master_volume,stream_volume and track_volume is a percentage, and 1 indicates that the volume is adjusted to the maximum;
The maximum decibel volume is 0db, which means there is no attenuation, that is, the sound source volume;
1. Stream volume (the volume controlled by the slider is the stream volume)
(1)VolumeStreamState
The android system defines 11 streams (from 0 to 10), and each stream is encapsulated with VolumeStreamState:
VolumeStreamState[] mStreamStates = new int[] {0,1,2,3,4,5,6,7,8,9,10};//0, 1, 2... These numbers represent the stream type, corresponding to default, voice call, ring, etc;
VolumeStreamState{//Used to manage all volume information of a stream type /*stream type*/ int mStreamType; /*volume Minimum, only 0 and 1*/ int mIndexMin; /*volume Maximum, 7, 15, etc*/ int mIndexMax;// boolean mIsMuted; /*VolumeStreamState The name of the, corresponding to the stream type*/ String mVolumeIndexSettingName; int mObservedDevices; /*map key in is device and value is volume value bvolume*/ SparseIntArray mIndexMap = new SparseIntArray(8); /*When the volume changes, the broadcast audiomanager.volume is sent_ CHANGED_ ACTION*/ Intent mVolumeChanged; Intent mStreamDevicesChanged; }
(2)alias
The android system defines 11 streams (from 0 to 10). If represented by an array, they correspond to the elements in the mStreamStates array one by one:
int[] STREAM_VOLUME_DEFAULT = new int[] {0,1,2,3,4,5,6,7,8,9,10};//System default
Android defines so many streams to facilitate future expansion and development, but in fact, Android devices do not support so many streams. For example, when you click the mobile phone volume key to adjust the volume of a stream, only five sliding bars will appear in the Android system, that is, there are only five types of streams for mobile devices, and for example, the set-top box only supports music stream, so for different platforms, These streams need to be grouped, and streams with the same attributes are divided into one category, which is called "alias" in the Android source code, that is, alias;
The following is the grouping result of 11 stream s of mobile phone / set-top box by android source code:
int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {0,2,2,3,4,5,6,2,2,3,3};//mobile phone int[] STREAM_VOLUME_ALIAS_TELEVISION = new int[] {3,3,3,3,3,3,3,3,3,3,3};//Set top box int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {0,2,2,3,4,2,6,2,2,3,3};//default
For mobile phones: 0 ----> 0 2 ----> 1,2,7,8 3 ----> 3,9,10 4 ----> 4 5 ----> 5 6 ----> 6 For set-top boxes: 3 ----> 0,1,2,3,4,5,6,7,8,9,10
Take the mobile phone as an example. The mobile phone only supports the five streams 2, 3, 4, 5 and 6 (0 is temporarily ignored). Since the 3, 9 and 10 of the mobile phone are classified as 3, that is, the alias alias is 3, when the mobile phone adjusts the three streams 3, 9 and 10, it actually adjusts 3(music stream).
(3) The system provides two APIs for regulating stream volume, namely adjustVolume() and setStreamVolume(). Take adjustVolume() as an example:
adjustVolume() //java layer: //AudioService.java adjustSuggestedStreamVolume() //Determine streamType final int streamType; if (mUserSelectedVolumeControlStream) { streamType = mVolumeControlStream; }else{ ...... } adjustStreamVolume() int streamTypeAlias = mStreamVolumeAlias[streamType];//Convert streamType to streamTypeAlias for the corresponding platform VolumeStreamState streamState = mStreamStates[streamTypeAlias]; final int device = getDeviceForStream(streamTypeAlias);//Get the current device int aliasIndex = streamState.getIndex(device);//Get the volume of the device on the current stream int step; step = rescaleIndex(10, streamType, streamTypeAlias);//Because different stream types have different volume adjustment ranges, rescaleIndex is used to change the volume value from the source stream type to the target stream type /*Safety volume related*/ ...... if(streamState.adjustIndex()){//Adjust the volume, set a new index value and send a broadcast of volume change sendMsg(...,MSG_SET_DEVICE_VOLUME,...);//Set the index to the bottom layer and save the index to the database setDeviceVolume(); streamState.applyDeviceVolume_syncVSS(device);//Set index to the bottom int index; index = ......;//Determine the final value of index AudioSystem.setStreamVolumeIndex(mStreamType, index, device);//Set index to the bottom layer for(......){//Handling mStreamVolumeAlias related mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice); } sendMsg(...,MSG_SET_DEVICE_VOLUME,...);//Save index to database persistVolume(); System.putIntForUser(......);//Save index to database } sendVolumeUpdate(streamType, oldIndex, index, flags);//Related to volumeui //Look at streamState.adjustIndex() streamState.adjustIndex() setIndex()//Set a new index value and send a broadcast of volume change oldIndex = getIndex(device);//index before volume change mIndexMap.put(device, index);//Save index after volume change changed = oldIndex != index;//If the index before and after volume change is different for(...){//Dealing with alias VolumeStreamState aliasStreamState = mStreamStates[streamType]; aliasStreamState.setIndex(scaledIndex, device, caller); } if(changed){//When the volume changes, the broadcast audiomanager.volume is sent_ CHANGED_ ACTION sendBroadcastToAll(mVolumeChanged); }
//native layer //AudioSystem.java AudioSystem.setStreamVolumeIndex() //AudioSystem.cpp AudioSystem::setStreamVolumeIndex() //AudioPolicyManager.cpp AudioPolicyManager::setStreamVolumeIndex() for (size_t i = 0; i < mOutputs.size(); i++) { checkAndSetVolume();//Set the volume of each output device float volumeDb = computeVolume(stream, index, device);//Calculate volume volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index); //VolumeCurve.cpp VolumeCurve::volIndexToDb()//Calculate the volume according to the volume curve 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<AudioCommand> command = new AudioCommand(); command->mCommand = SET_VOLUME; sendCommand(command, delayMs);//AudioPolicyService::AudioCommandThread::sendCommand() insertCommand_l(command, delayMs);//Insert the command and execute 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;//Save volume value broadcast_l();//Wake up PlaybackThread thread AudioFlinger::PlaybackThread::threadLoop() mMixerStatus = prepareTracks_l(&tracksToRemove);//Different types of Thread pair prepareTracks_l there are different implementations AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l() //This function is analyzed in track volume ...... }
(4) Volume curve
Different device s on each stream have a pair of volume relationship corresponding points, and these relationship points are connected to be the volume curve;
Because the human ear has an exponential curve type of hearing sense of sound, that is, it is more sensitive to small volume. With the increase of sound, its hearing sense becomes insensitive, and its change is similar to the form of exponential function curve. In order to make the change of listening sensation approximate to the change of straight line, people use the potentiometer with volume change approximate logarithmic curve to achieve this purpose in practice.
This paper summarizes the formula of how to convert the Index of linear UI into the auditory conversion of human ear with logarithmic relationship, as well as the preset interval table. According to different hardware, we can preset the appropriate interval table to make the volume curve more in line with our listening sense.
(5) Summary:
i) Adjust the volume of the stream. The java layer actually adjusts the index value of the device hung on the stream. Each device hung on the stream has a volume curve. The native layer converts the index value into the value in the volume curve:
|--> 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) how the "alias" of volume setting works:
set volume for stream; // Set the volume of recommended streams if (mStreamVolumeAlias[other stream] == stream) set volume for other stream; // Set the volume of other streams belonging to the same alias For different devices(Telephone TV,Flat), mStreamVolumeAlias Point to different arrays
iii) how to set the volume of the stream:
set up audioflinger Array in: mStreamTypes[stream].volume = value; set up PlaybackThread Array in: mStreamTypes[stream].volume = value;
2.track volume(APP sets the AudioTrack volume)
//AudioTrack.cpp AudioTrack::setVolume(float left, float right) /*The incoming volume value is saved in the mVolume array*/ mVolume[AUDIO_INTERLEAVE_LEFT] = left; mVolume[AUDIO_INTERLEAVE_RIGHT] = right; /*setVolumeLR Will assemble the values of the channel and the right channel into a number*/ mProxy->setVolumeLR(gain_minifloat_pack(gain_from_float(left), gain_from_float(right))); //AudioTrackShared.h /*mCblk Represents the header of shared memory, that is, the volume value will be saved to the header of shared memory*/ mCblk->mVolumeLR = volumeLR; Required when playing sound AudioMixer Mix and continue analysis AudioFlinger::MixerThread::prepareTracks_l(): //Threads.cpp AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l() /*Remove the hardware sound*/ float masterVolume = mMasterVolume; bool masterMute = mMasterMute; /*Take out all active Tracks*/ for (size_t i=0 ; i<count ; i++) { ...... //This is stream volume float typeVolume = mStreamTypes[track->streamType()].volume; float v = masterVolume * typeVolume; /*Take out the volume from the proxy, which is the volume saved through the head, which contains the volume of the left and right channels*/ gain_minifloat_packed_t vlr = proxy->getVolumeLR(); /*Extract left and right channel values*/ vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); /*Multiply by the previous V*/ vlf *= v * vh; vrf *= v * vh; /*Pass vlf and vrf to AudioMixer*/ mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf); mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf); }
That's the volume analysis.