Android音频(9)——音量调节
一、音量相关概念
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" />
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
posted on 2019-05-29 19:59 Hello-World3 阅读(12369) 评论(0) 编辑 收藏 举报