android volume adjustment

Keywords: Android SQLite

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.


 

Posted by penkomitev on Thu, 14 Oct 2021 22:26:55 -0700