一、音量相关概念
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都会受到这个滑动条的影响。
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