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" />
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
總結
以上是生活随笔為你收集整理的Android音频(9)——音量调节的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【krpano】krpano xml资源
- 下一篇: 荣耀90将在印度上市 价格提前曝光 对比