一、音量相关概念

1. 相关术语解释

track volume : 单个App设置音量时设置的是这个,它只影响本App的音量。
stream volume :设置某一stream的音量,Android系统中支持10种stream。
stream volume alias:设置的是同一组stream的音量,比如使用某个音量调节滑动条设置的音量。比如设置媒体音,所有App的媒体音都受到影响(但是电话音,
闹钟音不受影响)。
master volume :设置它等于设置所有的stream volume和track volume。它可以写到声卡里面去,控制所有声音的音量。也可以不写到声卡里面去,而是作为一个乘数因子来影响所有的音量。

2. 华为Honor8音量设置

设置–>声音–>音量,设置界面列出了铃声、媒体、闹钟、通话,四个设置滚动条,称为四个stream type,四组。
Android系统中有10种stream,在system/core/include/system/audio.h中定义。但把这10种stream分成组,属于同一组的stream具有相同的别名(alias)。
一个音量调节滑动条具有一个alias,具有相同alias的stream都会受到这个滑动条的影响。

Android音频(9)——音量调节-风君雪科技博客

3. 声音播放的两种路径

(1)MixerThread
对于MixerThread(多个App共用一个声卡进行混音的的),APP对音量的设置不会影响到声卡的硬件音量,而只会影响APP的音频数据的幅值(变小或放大),
这些音频数据最终被混合后传给声卡。多个APP本身的音量设置互不影响。

(2)DirectOutputThread
对于DirectOutputThread(对于HDMI的,单个音频应用程序独占使用一个声卡的),同一时间里只有一个APP、只有一个AudioTrack使用它,
所以该AudioTrack的音量可以被DirectOutputThread直接用来设置硬件音量,这种声卡使用的不多。

若audio_policy.conf中的output的参数信息(会被解析成一个output profile)中有”flags AUDIO_OUTPUT_FLAG_DIRECT”就表示这个声卡可以
被某个App独占。这个App就会以DirectOutputThread的形式来使用这个声卡。

4. APP设置音量时互不影响, 这是AudioTrack volume

5. stream volume
可以引申出来: 各种stream的音量也可以单独设置、互不影响。比如”音乐音量”不应该影响到”来电振铃”、”闹钟”、”通话”的音量。

6. 有的手机音量控制界面有5种滑动条,用于设置某种类型的声音音量,但是Android系统创建AudioTrack时可以指定10种stream type,
必须分组,在Android源码中称之为”别名”, 即alias。
比如在电话中, 以下5种stream的alias都是STREAM_RING,那么对应的滑动条即可控制这5种stream的音量。
STREAM_SYSTEM
STREAM_RING
STREAM_NOTIFICATION
STREAM_SYSTEM_ENFORCED
STREAM_DTMF

6. 无论是AudioTrack volume、stream volume, 都是单独设置. master volume 可以设置所有的AudioTrack volume和stream volume,也可
直接用来控制声卡的寄存器。

7. 混音:
app1: data1_mix = data1_in * master_volume * stream1_volume * AudioTrack1_volume

app2: data2_mix = data2_in * master_volume * stream2_volume * AudioTrack2_volume

混合在一起: data_mix = data1_mix + data2_mix 然后把混合后的数据写给硬件。

二、AudioFlinger层调节音量流程

1. AudioFlinger层调节音量流程
a. AudioFlinger对master volume, stream volume的初始化与设置
b. PlaybackThread对master volume, stream volume的初始化与设置
c. AudioTrack volume的设置
d. 这3种音量的使用

2. AudioFlinger类中有关成员:
stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
//存储master volume
float mMasterVolume;
//存储是否静音
bool mMasterMute;

2. playbackThread类中:
stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1]; //为DuplicatingThread的OutputTrack多出一项
bool mMasterMute;
float mMasterVolume; //来源于AudioFlinger中的同名的变量

3. AudioTrack类中(App端)
float mVolume[2]; //两项,分别表示App设置的左右声道的音量

4. stream volume和audioTreack中的volume只是软件上的处理,masterVolue中保存的值若HAL提供了相应的写函数就会写给硬件。

5. DuplicatingThread可以用于在两个声卡上播放出同样的声音。

6. 加载HAL时设置为初始化值

AudioFlinger::loadHwModule(const char *name) //AudioFlinger.cpp
    loadHwModule_l(name);
        //调用HAL的open函数,得到一个audio_hw_device_t
        audio_hw_device_t *dev;
        load_audio_interface(name, &dev);
            //if_name来自audio_policy.conf,是"primary",AUDIO_HARDWARE_MODULE_ID是"audio"
            //最后组合成的名字就是: audio.primary.tiny4412.so
            hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
            audio_hw_device_open(mod, dev);
        //若HAL提供了get_master_volume就获取硬件的值赋给mMasterVolume
        mMasterVolume = dev->get_master_volume(dev, &mv)
        //若HAL提供了get_master_mute就获取硬件的值赋给mMasterMute
        mMasterMute = dev->get_master_mute(dev, &mm)
        //若存在对应的函数则调用设置
        dev->set_master_volume(dev, mMasterVolume)
        dev->set_master_mute(dev, mMasterMute)

audio_hw_device_t里面有masterVolume的存取函数:
typedef struct audio_hw_device audio_hw_device_t;
int (*set_master_mute)(struct audio_hw_device *dev, bool mute);
int (*get_master_mute)(struct audio_hw_device *dev, bool *mute);


AudioFlinger中还提供了函数设置MasterVolume和MasterMute
AudioFlinger::setMasterVolume(float value)
AudioFlinger::setMasterMute(bool muted)

AudioFlinger中的setStreamVolume
AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output)
    mStreamTypes[stream].volume = value;
    AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value) //Threads.cpp
        mStreamTypes[stream].volume = value;
        broadcast_l();

PlaybackThread中的初始值都是来自AudioFlinger
AudioFlinger::PlaybackThread::PlaybackThread()
    mMasterVolume = audioFlinger->masterVolume_l();
    mMasterMute = audioFlinger->masterMute_l();
    //对于数组的每一项都执行
    mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
    mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);

AudioTrack中的
AudioTrack::setVolume(float left, float right) //AudioTrack.cpp
    mVolume[AUDIO_INTERLEAVE_LEFT] = left;
    mVolume[AUDIO_INTERLEAVE_RIGHT] = right;
    //
    mProxy->setVolumeLR(gain_minifloat_pack(gain_from_float(left), gain_from_float(right)));
        //mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
        //仅仅是把这个数据记录在mVolumeLR域中而已。Cblk就是共享内存的头部。
        mCblk->mVolumeLR = volumeLR; //AudioTrackShared.h

7. App中的AudioTrack与SurfaceFlinger中的mTracks中的对应项通过共享内存进行通信,这个mProxy就是共享内存的代理类。

8. App去设置音量只需要执行AudioTrack::setVolume就可以了。它会把设置的值放在自己的私有成员里面,也会放到共享内存的头部。

9. 低16bit是左声道数据,高16bit是右声道数据

10. AudioMixer中的音量如何保存:音量有整数表示方式也有float表示方式,float表示方式是未来的发展趋势

三、音量键和Setting界面调节音量流程

1. 对于seekBar控件,当滑动滑动条的时候,onProgressRefresh(AbsSeekBar.java)就会被调用,通过seekBar设置音量的两种方法:
① 重写onProgressRefresh
② 添加Listener

2. seekBar是通过packages目录下的 notification_settings.xml 文件画出来的,packages/apps/Settings/res/xml/notification_settings.xml

<!-- Media volume -->
<com.android.settings.notification.VolumeSeekBarPreference
        android:key="media_volume"
        android:icon="@drawable/ic_audio_vol_24dp"
        android:title="@string/media_volume_option_title" />

<!-- Alarm volume -->
<com.android.settings.notification.VolumeSeekBarPreference
        android:key="alarm_volume"
        android:icon="@drawable/ic_audio_alarm_24dp"
        android:title="@string/alarm_volume_option_title" />

<!-- Ring volume -->
<com.android.settings.notification.VolumeSeekBarPreference
        android:key="ring_volume"
        android:icon="@drawable/ring_notif"
        android:title="@string/ring_volume_option_title" />

<!-- Notification volume -->
<com.android.settings.notification.VolumeSeekBarPreference
        android:key="notification_volume"
        android:icon="@drawable/ring_notif"
        android:title="@string/notification_volume_option_title" />

View Code

3. 音量键和Setting界面调节音量流程

a. 音量键处理流程
音量键: 
  如果APP没有重写它的处理函数,音量键的处理将交给 PhoneFallbackEventHandler 来处理,它会调用AudioService.adjustSuggestedStreamVolume
  调整"推荐的流"的音量

a.1 如何获得"推荐的流": stream = getActiveStreamType(...)

a.2 音量设置的"alias"如何起作用: 
     set volume for stream; // 设置"推荐的流"的音量
     
     if (mStreamVolumeAlias[other stream] == stream)
         set volume for other stream;  // 设置同属一个alias的其他流的音量
         
     对于不同的设备(电话、TV、平板), mStreamVolumeAlias指向不同的数组

a.3 怎么设置流的音量:
    设置audioflinger中的数组:   mStreamTypes[stream].volume = value;
    设置PlaybackThread中的数组: mStreamTypes[stream].volume = value;
    

b. 音量滑动条处理流程

b.1 通过下面文件定义音量滑动条:
packages/apps/Settings/res/xml/notification_settings.xml
   该文件定义了多个VolumeSeekBarPreference,每个VolumeSeekBarPreference要跟一个SeekBar绑定

b.2 在VolumeSeekBarPreference的绑定函数onBindView中,设置了对应的SeekBar的SeekBarChangeListener (一个SeekBarVolumizer对象)

b.3 当SeekBar被滑动时, 它的onProgressRefresh被调用,该函数会调用 mOnSeekBarChangeListener.onProgressChanged

b.4 mOnSeekBarChangeListener.onProgressChanged去设置音量

b.5 每一个SeekBar对应一个stream,滑动SeekBar时会设置该stream的音量,也会去设置同属一个alias(同一分组)的其他stream的音量
    
c. 两者最终都会调用AudioService.java的代码发出MSG:
            sendMsg(mAudioHandler,
                    MSG_SET_DEVICE_VOLUME,
                    SENDMSG_QUEUE,
                    device,
                    0,
                    streamState,
                    0);

参考:
Android官方setting文档:https://developer.android.google.cn/guide/topics/ui/settings

android5.0设置模块音量调节流程:https://blog.csdn.net/fireness/article/details/46738643

Android音量调节的实现(RingtoneManager和RingerVolumePreference):https://blog.csdn.net/liranke/article/details/6683000

android设置中拖动音量条调节音量流程(android5.1):https://blog.csdn.net/qq_28534581/article/details/77337599

Android 音量控制流程分析:https://blog.csdn.net/kehyuanyu/article/details/49153223