Android音频(9)——音量调节
一、音量相關(guān)概念
1. 相關(guān)術(shù)語解釋
track volume : 單個(gè)App設(shè)置音量時(shí)設(shè)置的是這個(gè),它只影響本App的音量。
stream volume :設(shè)置某一stream的音量,Android系統(tǒng)中支持10種stream。
stream volume alias:設(shè)置的是同一組stream的音量,比如使用某個(gè)音量調(diào)節(jié)滑動(dòng)條設(shè)置的音量。比如設(shè)置媒體音,所有App的媒體音都受到影響(但是電話音,
鬧鐘音不受影響)。
master volume :設(shè)置它等于設(shè)置所有的stream volume和track volume。它可以寫到聲卡里面去,控制所有聲音的音量。也可以不寫到聲卡里面去,而是作為一個(gè)乘數(shù)因子來影響所有的音量。
2. 華為Honor8音量設(shè)置
設(shè)置-->聲音-->音量,設(shè)置界面列出了鈴聲、媒體、鬧鐘、通話,四個(gè)設(shè)置滾動(dòng)條,稱為四個(gè)stream type,四組。
Android系統(tǒng)中有10種stream,在system/core/include/system/audio.h中定義。但把這10種stream分成組,屬于同一組的stream具有相同的別名(alias)。
一個(gè)音量調(diào)節(jié)滑動(dòng)條具有一個(gè)alias,具有相同alias的stream都會(huì)受到這個(gè)滑動(dòng)條的影響。
3. 聲音播放的兩種路徑
(1)MixerThread
對(duì)于MixerThread(多個(gè)App共用一個(gè)聲卡進(jìn)行混音的的),APP對(duì)音量的設(shè)置不會(huì)影響到聲卡的硬件音量,而只會(huì)影響APP的音頻數(shù)據(jù)的幅值(變小或放大),
這些音頻數(shù)據(jù)最終被混合后傳給聲卡。多個(gè)APP本身的音量設(shè)置互不影響。
(2)DirectOutputThread
對(duì)于DirectOutputThread(對(duì)于HDMI的,單個(gè)音頻應(yīng)用程序獨(dú)占使用一個(gè)聲卡的),同一時(shí)間里只有一個(gè)APP、只有一個(gè)AudioTrack使用它,
所以該AudioTrack的音量可以被DirectOutputThread直接用來設(shè)置硬件音量,這種聲卡使用的不多。
若audio_policy.conf中的output的參數(shù)信息(會(huì)被解析成一個(gè)output profile)中有"flags AUDIO_OUTPUT_FLAG_DIRECT"就表示這個(gè)聲卡可以
被某個(gè)App獨(dú)占。這個(gè)App就會(huì)以DirectOutputThread的形式來使用這個(gè)聲卡。
4. APP設(shè)置音量時(shí)互不影響, 這是AudioTrack volume
5. stream volume
可以引申出來: 各種stream的音量也可以單獨(dú)設(shè)置、互不影響。比如"音樂音量"不應(yīng)該影響到"來電振鈴"、"鬧鐘"、"通話"的音量。
6. 有的手機(jī)音量控制界面有5種滑動(dòng)條,用于設(shè)置某種類型的聲音音量,但是Android系統(tǒng)創(chuàng)建AudioTrack時(shí)可以指定10種stream type,
必須分組,在Android源碼中稱之為"別名", 即alias。
比如在電話中, 以下5種stream的alias都是STREAM_RING,那么對(duì)應(yīng)的滑動(dòng)條即可控制這5種stream的音量。
STREAM_SYSTEM
STREAM_RING
STREAM_NOTIFICATION
STREAM_SYSTEM_ENFORCED
STREAM_DTMF
6. 無論是AudioTrack volume、stream volume, 都是單獨(dú)設(shè)置. master volume 可以設(shè)置所有的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 然后把混合后的數(shù)據(jù)寫給硬件。
二、AudioFlinger層調(diào)節(jié)音量流程
1. AudioFlinger層調(diào)節(jié)音量流程
a. AudioFlinger對(duì)master volume, stream volume的初始化與設(shè)置
b. PlaybackThread對(duì)master volume, stream volume的初始化與設(shè)置
c. AudioTrack volume的設(shè)置
d. 這3種音量的使用
2. AudioFlinger類中有關(guān)成員:
stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
//存儲(chǔ)master volume
float mMasterVolume;
//存儲(chǔ)是否靜音
bool mMasterMute;
2. playbackThread類中:
stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1]; //為DuplicatingThread的OutputTrack多出一項(xiàng)
bool mMasterMute;
float mMasterVolume; //來源于AudioFlinger中的同名的變量
3. AudioTrack類中(App端)
float mVolume[2]; //兩項(xiàng),分別表示App設(shè)置的左右聲道的音量
4. stream volume和audioTreack中的volume只是軟件上的處理,masterVolue中保存的值若HAL提供了相應(yīng)的寫函數(shù)就會(huì)寫給硬件。
5. DuplicatingThread可以用于在兩個(gè)聲卡上播放出同樣的聲音。
6. 加載HAL時(shí)設(shè)置為初始化值
AudioFlinger::loadHwModule(const char *name) //AudioFlinger.cpp
loadHwModule_l(name);
//調(diào)用HAL的open函數(shù),得到一個(gè)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)
//若存在對(duì)應(yīng)的函數(shù)則調(diào)用設(shè)置
dev->set_master_volume(dev, mMasterVolume)
dev->set_master_mute(dev, mMasterMute)
audio_hw_device_t里面有masterVolume的存取函數(shù):
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中還提供了函數(shù)設(shè)置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();
//對(duì)于數(shù)組的每一項(xiàng)都執(zhí)行
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);
//僅僅是把這個(gè)數(shù)據(jù)記錄在mVolumeLR域中而已。Cblk就是共享內(nèi)存的頭部。
mCblk->mVolumeLR = volumeLR; //AudioTrackShared.h
7. App中的AudioTrack與SurfaceFlinger中的mTracks中的對(duì)應(yīng)項(xiàng)通過共享內(nèi)存進(jìn)行通信,這個(gè)mProxy就是共享內(nèi)存的代理類。
8. App去設(shè)置音量只需要執(zhí)行AudioTrack::setVolume就可以了。它會(huì)把設(shè)置的值放在自己的私有成員里面,也會(huì)放到共享內(nèi)存的頭部。
9. 低16bit是左聲道數(shù)據(jù),高16bit是右聲道數(shù)據(jù)
10. AudioMixer中的音量如何保存:音量有整數(shù)表示方式也有float表示方式,float表示方式是未來的發(fā)展趨勢(shì)
三、音量鍵和Setting界面調(diào)節(jié)音量流程
1. 對(duì)于seekBar控件,當(dāng)滑動(dòng)滑動(dòng)條的時(shí)候,onProgressRefresh(AbsSeekBar.java)就會(huì)被調(diào)用,通過seekBar設(shè)置音量的兩種方法:
① 重寫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界面調(diào)節(jié)音量流程
a. 音量鍵處理流程
音量鍵:
如果APP沒有重寫它的處理函數(shù),音量鍵的處理將交給 PhoneFallbackEventHandler 來處理,它會(huì)調(diào)用AudioService.adjustSuggestedStreamVolume
調(diào)整"推薦的流"的音量
a.1 如何獲得"推薦的流": stream = getActiveStreamType(...)
a.2 音量設(shè)置的"alias"如何起作用:
set volume for stream; // 設(shè)置"推薦的流"的音量
if (mStreamVolumeAlias[other stream] == stream)
set volume for other stream; // 設(shè)置同屬一個(gè)alias的其他流的音量
對(duì)于不同的設(shè)備(電話、TV、平板), mStreamVolumeAlias指向不同的數(shù)組
a.3 怎么設(shè)置流的音量:
設(shè)置audioflinger中的數(shù)組: mStreamTypes[stream].volume = value;
設(shè)置PlaybackThread中的數(shù)組: mStreamTypes[stream].volume = value;
b. 音量滑動(dòng)條處理流程
b.1 通過下面文件定義音量滑動(dòng)條:
packages/apps/Settings/res/xml/notification_settings.xml
該文件定義了多個(gè)VolumeSeekBarPreference,每個(gè)VolumeSeekBarPreference要跟一個(gè)SeekBar綁定
b.2 在VolumeSeekBarPreference的綁定函數(shù)onBindView中,設(shè)置了對(duì)應(yīng)的SeekBar的SeekBarChangeListener (一個(gè)SeekBarVolumizer對(duì)象)
b.3 當(dāng)SeekBar被滑動(dòng)時(shí), 它的onProgressRefresh被調(diào)用,該函數(shù)會(huì)調(diào)用 mOnSeekBarChangeListener.onProgressChanged
b.4 mOnSeekBarChangeListener.onProgressChanged去設(shè)置音量
b.5 每一個(gè)SeekBar對(duì)應(yīng)一個(gè)stream,滑動(dòng)SeekBar時(shí)會(huì)設(shè)置該stream的音量,也會(huì)去設(shè)置同屬一個(gè)alias(同一分組)的其他stream的音量
c. 兩者最終都會(huì)調(diào)用AudioService.java的代碼發(fā)出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設(shè)置模塊音量調(diào)節(jié)流程:https://blog.csdn.net/fireness/article/details/46738643
Android音量調(diào)節(jié)的實(shí)現(xiàn)(RingtoneManager和RingerVolumePreference):https://blog.csdn.net/liranke/article/details/6683000
android設(shè)置中拖動(dòng)音量條調(diào)節(jié)音量流程(android5.1):https://blog.csdn.net/qq_28534581/article/details/77337599
Android 音量控制流程分析:https://blog.csdn.net/kehyuanyu/article/details/49153223
總結(jié)
以上是生活随笔為你收集整理的Android音频(9)——音量调节的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【krpano】krpano xml资源
- 下一篇: 荣耀90将在印度上市 价格提前曝光 对比