Android Audio音量設(shè)置原理流程分析
簡(jiǎn)介
本篇文章主要介紹Android音量設(shè)置從App應(yīng)用層到framework層執(zhí)行流程,以及相關(guān)的細(xì)節(jié)和原理分析,建議在閱讀此文章前去看博主的混音理論篇的聲音的音量屬性和聲音相關(guān)公式的推導(dǎo)章節(jié),這對(duì)閱讀時(shí)理解音量等級(jí)、音量分貝計(jì)算有著很大的益處;如果閱讀時(shí)發(fā)現(xiàn)文章有錯(cuò)誤,歡迎指出!
音量設(shè)置總流程
音量設(shè)置總流程如下:
App在設(shè)置音量時(shí),通常使用AudioManager的以下兩個(gè)接口:
- setStreamVolume(int streamType, int index, int flags)
index:音量等級(jí),通常在0~31這個(gè)設(shè)置范圍,音量可以突變?cè)O(shè)置,如上次音量為1,下次設(shè)置音量為5 - adjustStreamVolume(int streamType, int direction, int flags)
direction:音量調(diào)整方向ADJUST_LOWER,ADJUST_RAISE,ADJUST_SAME,類似于每次只加/減一,勻速調(diào)整
最后,音量設(shè)置是針對(duì)音頻流類型來設(shè)置的,而streamType是系統(tǒng)規(guī)定(在AudioSystem中)的音頻流類型,如下:
public static final int STREAM_DEFAULT = -1;
public static final int STREAM_VOICE_CALL = 0;
public static final int STREAM_SYSTEM = 1;
public static final int STREAM_RING = 2;
public static final int STREAM_MUSIC = 3;
public static final int STREAM_ALARM = 4;
public static final int STREAM_NOTIFICATION = 5;
public static final int STREAM_BLUETOOTH_SCO = 6;
/** Used to identify the volume of audio streams for enforced system sounds in certain
* countries (e.g camera in Japan) */
@UnsupportedAppUsage
public static final int STREAM_SYSTEM_ENFORCED = 7;
/** Used to identify the volume of audio streams for DTMF tones */
public static final int STREAM_DTMF = 8;
/** Used to identify the volume of audio streams exclusively transmitted through the
* speaker (TTS) of the device */
public static final int STREAM_TTS = 9;
/** Used to identify the volume of audio streams for accessibility prompts */
public static final int STREAM_ACCESSIBILITY = 10;
AudioService中音量處理
在分析AudioService的setStreamVolume和adjustStreamVolume函數(shù)之前,需要先掌握AudioService內(nèi)部的幾個(gè)知識(shí)點(diǎn),這對(duì)我們分析音量設(shè)置極為重要;
- mUseFixedVolume和mFixedVolumeDevices 固定音量
- mSafeMediaVolumeDevices安全音量設(shè)備
- STREAM_VOLUME_ALIAS 簡(jiǎn)稱音頻音量別名
- VolumeStreamState 是AudioService的一個(gè)內(nèi)部類
AudioService內(nèi)部知識(shí)點(diǎn)理解
mUseFixedVolume和mFixedVolumeDevices固定音量
mUseFixedVolume狀態(tài)標(biāo)志來源于:framework的config.xml內(nèi)的配置字com.android.internal.R.bool.config_useFixedVolume,如果true,則所有的設(shè)備都不可進(jìn)行音量調(diào)節(jié),音量值為max值
mFixedVolumeDevices:是一個(gè)數(shù)組,里面是輸出設(shè)備out device集合,在此范圍內(nèi)的設(shè)備不可進(jìn)行音量調(diào)節(jié),音量值取值max
mSafeMediaVolumeDevices安全音量設(shè)備
所謂安全音量設(shè)備,它也是一個(gè)輸出設(shè)備集合數(shù)組,包含了耳機(jī)相關(guān)輸出設(shè)備;在改變音量時(shí)或插入耳機(jī)設(shè)備時(shí),防止音量突變過大,對(duì)人耳造成傷害;如手機(jī)上music的音量設(shè)置到最大值,當(dāng)我們插上耳機(jī)戴上時(shí),如果音量仍不變保持最大輸出,聽力可能會(huì)受損,這時(shí)這個(gè)安全音量設(shè)備就其作用了,插入耳機(jī)時(shí)音量值變?yōu)榘踩袅恐担Wo(hù)我們的聽力
音頻音量別名
音頻音量別名是一個(gè)數(shù)組,在AudioService中定義,數(shù)組內(nèi)部還是由streamType組成,如下:
private final int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
AudioSystem.STREAM_ALARM, // STREAM_ALARM
AudioSystem.STREAM_NOTIFICATION, // STREAM_NOTIFICATION
AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_RING, // STREAM_DTMF
AudioSystem.STREAM_MUSIC, // STREAM_TTS
AudioSystem.STREAM_MUSIC // STREAM_ACCESSIBILITY
};
如何獲取某個(gè)streamType的音頻音量別名呢?
數(shù)組取值,如STREAM_VOLUME_ALIAS_DEFAULT[STREAM_TTS]得到的別名是AudioSystem.STREAM_MUSIC;
那它有什么作用呢?
就是在設(shè)置音量時(shí),將不同音頻流進(jìn)行分組來處理他們的音量;因?yàn)樵诓煌O(shè)備上,不同類型音頻流管理一致,如Android TV設(shè)備,STREAM_MUSIC和STREAM_RING音量管理方式是一樣的
VolumeStreamState
概念
它是AudioService內(nèi)部的音量狀態(tài)管理類,以streamType為單位,每一種音頻流,都會(huì)創(chuàng)建一個(gè)VolumeStreamState內(nèi)部對(duì)象,App應(yīng)用層設(shè)置某個(gè)streamType的音量時(shí),就會(huì)調(diào)用它對(duì)應(yīng)的VolumeStreamState對(duì)象來處理音量,分析一下它的創(chuàng)建過程,如下:
private void createStreamStates() {
// 一共11個(gè)types,定義在Audioystem中
int numStreamTypes = AudioSystem.getNumStreamTypes();
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
//相當(dāng)于mStreamStates實(shí)實(shí)在在有numStreamTypes個(gè),但是內(nèi)部的name可能是相同的
for (int i = 0; i < numStreamTypes; i++) {
//mStreamVolumeAlias數(shù)組保存了每個(gè)streamType的int值,VOLUME_SETTINGS_INT數(shù)組字符串stream的名稱
streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS_INT[mStreamVolumeAlias[i]], i);
}
......
}
- getNumStreamTypes獲取streamType的數(shù)量11個(gè),創(chuàng)建11個(gè)VolumeStreamState對(duì)象
- i其實(shí)就對(duì)應(yīng)streamType,mStreamVolumeAlias[i]就是取streamType的音頻音量別名,VOLUME_SETTINGS_INT就是轉(zhuǎn)換為字符串名字
VolumeStreamState構(gòu)造方法
private class VolumeStreamState {
private final int mStreamType;
private int mIndexMin; //最小音量等級(jí)
private int mIndexMax; //最大音量等級(jí)
private boolean mIsMuted; //是否靜音
private String mVolumeIndexSettingName;
private int mObservedDevices;
//保存了每個(gè)device的音量等級(jí)index值
private final SparseIntArray mIndexMap = new SparseIntArray(8);
private final Intent mVolumeChanged;
private final Intent mStreamDevicesChanged;
//settingName來源于Settings.java中的VOLUME_SETTINGS_INT數(shù)組,如字符串volume_music、volume_voice等
private VolumeStreamState(String settingName, int streamType) {
mVolumeIndexSettingName = settingName;
mStreamType = streamType;
/* 默認(rèn)是min-0 max-31,但是在AudioService構(gòu)造方法中,已經(jīng)獲取了每個(gè)streamType的indexMin和indexMax;
* ,所以這里MIN_STREAM_VOLUME和MAX_STREAM_VOLUME是實(shí)際值,而不是默認(rèn)值;
* 乘10擴(kuò)大倍數(shù),防止浮點(diǎn)型計(jì)算*/
mIndexMin = MIN_STREAM_VOLUME[streamType] * 10;
mIndexMax = MAX_STREAM_VOLUME[streamType] * 10;
//實(shí)質(zhì)是設(shè)置VolumeCurves對(duì)象的mIndexMin和mIndexMax成員
AudioSystem.initStreamVolume(streamType, mIndexMin / 10, mIndexMax / 10);
readSettings();
.......
}
}
- 以上這種VolumeStreamState創(chuàng)建對(duì)象有11個(gè),不同的VolumeStreamState對(duì)應(yīng),其內(nèi)部的mVolumeIndexSettingName可能有相同的,但是streamType是唯一的;
- mIndexMin和mIndexMax表示此類streamType音量等級(jí)調(diào)節(jié)的范圍,不能超過此范圍,這個(gè)值min和max其實(shí)來源于音量曲線文件中的配置,<indexMin>和<indexMax>標(biāo)簽的值如下:
<volumeGroup>
<name>oem_traffic_anouncement</name>
<indexMin>0</indexMin>
<indexMax>40</indexMax>
<volume deviceCategory="DEVICE_CATEGORY_SPEAKER">
<point>0,-4200</point>
<point>33,-2800</point>
<point>66,-1400</point>
<point>100,0</point>
</volume>
</volumeGroup>
那它是怎么獲取得到這個(gè)值,建議先看看我這邊文章Audio解析strategy配置文件,解析strategy配置文件,得到strategy與volumeGroup數(shù)據(jù)的連接關(guān)系;從streamType獲取到attribute,在從attribute獲取到volumeGroup,然后就拿到volumeGroup的indexMin和indexMax了。
3. mIndexMap是map結(jié)構(gòu),key對(duì)應(yīng)device,value對(duì)應(yīng)音量等級(jí)index,device對(duì)應(yīng)了AudioSystem.DEVICE_OUT_ALL許多設(shè)備,如DEVICE_OUT_EARPIECE、DEVICE_OUT_SPEAKER等等,也就是說每個(gè)每個(gè)VolumeStreamState保存了所有輸出設(shè)備的音量等級(jí)index
VolumeStreamState之read_settings
讀取配置,也就是為VolumeStreamState的部分成員賦值,具體代碼如下:
public void readSettings() {
synchronized (mSettingsLock) {
synchronized (VolumeStreamState.class) {
// force maximum volume on all streams if fixed volume property is set
if (mUseFixedVolume) {
//DEVICE_OUT_DEFAULT應(yīng)該是默認(rèn)設(shè)備的默認(rèn)音量
mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
return;
}
// do not read system stream volume from settings: this stream is always aliased
// to another stream type and its volume is never persisted. Values in settings can
// only be stale values
if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
(mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
//如果stream是系統(tǒng)類型,則從讀取系統(tǒng)中此類型的默認(rèn)音量,默認(rèn)都是12
int index = 10 * AudioSystem.DEFAULT_STREAM_VOLUME[mStreamType];
if (mCameraSoundForced) {
index = mIndexMax;
}
//此streamType下,無論任何輸出設(shè)備,音量都是固定值
mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
return;
}
}
}
synchronized (VolumeStreamState.class) {
//DEVICE_OUT_ALL是所有設(shè)備的輸出或的結(jié)果,每個(gè)設(shè)備device占一個(gè)bit,如A設(shè)備為0x01 B設(shè)備0x02 C設(shè)備0x04這樣依次,或之后的結(jié)果互不沖突
int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
for (int i = 0; remainingDevices != 0; i++) {
//依次取出每一個(gè)device設(shè)備
int device = (1 << i);
//如果device和remainingevices相與為0,說明這個(gè)device沒有在所有設(shè)備的集合中,因此也沒必要走下面的流程
if ((device & remainingDevices) == 0) {
continue;
}
//從remainingDevices設(shè)備集合中剔除掉當(dāng)前這個(gè)device
remainingDevices &= ~device;
// retrieve current volume for device
// if no volume stored for current stream and device, use default volume if default
// device, continue otherwise
int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
AudioSystem.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
int index;
//不是有效的名字,null或空字符
if (!hasValidSettingsName()) {
index = defaultIndex;
} else {
//為這個(gè)的device拿到自己的字符串名字,是一個(gè)組合名字,stream類型家輸出設(shè)備,如stream_music_speaker、stream_music_earpiece等
String name = getSettingNameForDevice(device);
//通過ContentResolver方式獲取上次保存的index
index = Settings.System.getIntForUser(
mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
}
if (index == -1) {
continue;
}
//getValidIndex確保index在indexMin和indexMax范圍之間的合法值
mIndexMap.put(device, getValidIndex(10 * index));
}
}
}
- 如果配置了mUseFixedVolume固定音量,則mIndexMap中所有設(shè)備的音量等級(jí)取值indexMax,返回
- AudioSystem.DEVICE_OUT_ALL包含了所有輸出設(shè)備,依次遍歷每個(gè)設(shè)備,將每個(gè)設(shè)備的音量等級(jí)index設(shè)置到mIndexMap中去;如果當(dāng)前的mVolumeIndexSettingName不合法,音量等級(jí)為默認(rèn)值12,反之則通過Settings類,利用contentResolver機(jī)制從數(shù)據(jù)庫中獲取上次的音量等級(jí);
小結(jié)
最后,在AudioService中,內(nèi)部的對(duì)象實(shí)例連接圖如下:
adjustStreamVolume
protected void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid) {
if (mUseFixedVolume) {
return;
}
.......
boolean isMuteAdjust = isMuteAdjust(direction);
if (isMuteAdjust && !isStreamAffectedByMute(streamType)) {
return;
}
.........
//獲取別名
int streamTypeAlias = mStreamVolumeAlias[streamType];
//獲取volumeStreamState
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
//獲取stream的輸出設(shè)備
final int device = getDeviceForStream(streamTypeAlias);
//獲取此stream的這個(gè)輸出設(shè)備的音量等級(jí)index
int aliasIndex = streamState.getIndex(device);
boolean adjustVolume = true;
int step;
.......
// reset any pending volume command
synchronized (mSafeMediaVolumeStateLock) {
mPendingVolumeCommand = null;
}
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
((device & mFixedVolumeDevices) != 0)) {
flags |= AudioManager.FLAG_FIXED_VOLUME;
//media safe安全狀態(tài)是否激活,并且device又屬于安全設(shè)備范圍headset headphone
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
(device & mSafeMediaVolumeDevices) != 0) {
//獲取耳機(jī)設(shè)備的最大安全音量,防止聲音突變對(duì)耳朵造成傷害
step = safeMediaVolumeIndex(device);
} else {
//不在safe狀態(tài)或不屬于safedevice,就拿它的最大音量等級(jí)
step = streamState.getMaxIndex();
}
if (aliasIndex != 0) {
aliasIndex = step;
}
} else {
//又可能上層streamType和streamTypeAlias不一樣,這種情況在不同設(shè)備上,比如TV設(shè)備,music、voice對(duì)應(yīng)的alias都屬于music
step = rescaleIndex(10, streamType, streamTypeAlias);
}
//flag和uiSoundStreamType符合就要處理RingMode,uiSound就是獲取Stream_system的別名
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
(streamTypeAlias == getUiSoundsStreamType())) {
//獲取當(dāng)前ringmode模式
int ringerMode = getRingerModeInternal();
// 如果已經(jīng)是vibrator震動(dòng)模式,就沒必要再次設(shè)置震動(dòng)了
if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
flags &= ~AudioManager.FLAG_VIBRATE;
}
final int result = checkForRingerModeChange(aliasIndex, direction, step,
streamState.mIsMuted, callingPackage, flags);
adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
flags |= AudioManager.FLAG_SHOW_SILENT_HINT;
}
// If suppressing a volume down adjustment in vibrate mode, display the UI hint
if ((result & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
flags |= AudioManager.FLAG_SHOW_VIBRATE_HINT;
}
}
// If the ringer mode or zen is muting the stream, do not change stream unless
// it'll cause us to exit dnd
if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
adjustVolume = false;
}
int oldIndex = mStreamStates[streamType].getIndex(device);
//不等于ADJUST_SAME說明聲音要作出改變,如何改變就是要看他的direction
if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);
if (isMuteAdjust) {
boolean state;
//就是與之前的mute取反向操作
if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
state = !streamState.mIsMuted;
} else {
state = direction == AudioManager.ADJUST_MUTE;
}
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
//設(shè)置是否mute
setSystemAudioMute(state);
}
for (int stream = 0; stream < mStreamStates.length; stream++) {
//滿足if條件的可能有多個(gè),因?yàn)樵趧?chuàng)建之處的時(shí)候就創(chuàng)建了多個(gè)
if (streamTypeAlias == mStreamVolumeAlias[stream]) {
if (!(readCameraSoundForced()
&& (mStreamStates[stream].getStreamType()
== AudioSystem.STREAM_SYSTEM_ENFORCED))) {
//設(shè)置某個(gè)streamType的mute狀態(tài)
mStreamStates[stream].mute(state);
}
}
}
} else if ((direction == AudioManager.ADJUST_RAISE) &&
!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
mVolumeController.postDisplaySafeVolumeWarning(flags);
} else if (((device & mFullVolumeDevices) == 0)
&& (streamState.adjustIndex(direction * step, device, caller)
|| streamState.mIsMuted)) {
// Post message to set system volume (it in turn will post a
// message to persist).
if (streamState.mIsMuted) {
// Unmute the stream if it was previously muted
if (direction == AudioManager.ADJUST_RAISE) {
// unmute immediately for volume up
streamState.mute(false);
} else if (direction == AudioManager.ADJUST_LOWER) {
if (mIsSingleVolume) {
sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,
streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY);
}
}
}
//發(fā)送mute
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
......
}
- 獲取streamType的別名alias和音量狀態(tài)管理類VolumeStreamState,獲取當(dāng)前streamType在底層鏈路中的輸出設(shè)備device,獲取過程相對(duì)復(fù)雜,主要是通過streamType,依次找到attribute、strategy,根據(jù)路由策略找到輸出設(shè)備device,詳細(xì)過程可參考Audio播放音頻 — 建立播放通道
- 獲取這個(gè)device的音量等級(jí)index,其中需要判斷該device是否屬于fixvolume、safemedia設(shè)備中,是的話音量等級(jí)就取該設(shè)備的safeVolume,否則就取10,并且要進(jìn)行音量等級(jí)轉(zhuǎn)換rescaleIndex,因?yàn)閟treamType和他的alias別名不同,也就是說要按照alias的音量管理來處理音量,所以需要將音量等級(jí)進(jìn)行轉(zhuǎn)換
- 處理RingMode模式,針對(duì)mute靜音情況時(shí),是開啟震動(dòng)vibrator還是保持靜音slience
- 處理音量調(diào)節(jié)加減等工作
代碼中有注釋,不太明白的看注釋!
2: rescaleIndex音量等級(jí)轉(zhuǎn)換
顧名思義,就是將音量等級(jí)index從一個(gè)stream類型轉(zhuǎn)換到另一個(gè)stream類型
private int rescaleIndex(int index, int srcStream, int dstStream) {
int srcRange =
mStreamStates[srcStream].getMaxIndex() - mStreamStates[srcStream].getMinIndex();
int dstRange =
mStreamStates[dstStream].getMaxIndex() - mStreamStates[dstStream].getMinIndex();
if (srcRange == 0) {
Log.e(TAG, "rescaleIndex : index range should not be zero");
return mStreamStates[dstStream].getMinIndex();
}
/* 這個(gè)實(shí)質(zhì)就是等比例換算,將在srcStream的音量等級(jí)index轉(zhuǎn)換到dstStream的上去,
* 還加了一個(gè)0.5,為啥加呢?感覺沒啥用,這都是int型,最后得到的結(jié)果相當(dāng)于沒有加0.5
* 估計(jì)是向上取整*/
return mStreamStates[dstStream].getMinIndex()
+ ((index - mStreamStates[srcStream].getMinIndex()) * dstRange + srcRange / 2)
/ srcRange;
}
以上代碼關(guān)鍵理解點(diǎn)在于return這句,srcRange代表srcStream可調(diào)整的音量等級(jí)范圍,假如[1, 10],同理dstRange,有可能是[10, 20],如傳入的srcStream的index音量等級(jí)是5,對(duì)應(yīng)到dstStream是都少呢?
因?yàn)檫@里所屬音量等級(jí)范疇,屬于線性的,還沒到分貝非線性范疇,所以是可以用等比例法進(jìn)行換算,為了簡(jiǎn)化字符,將mStreamStates[dstStream].getMinIndex()以
d
s
t
.
m
i
n
dst.min
dst.min,將return返回值記為
x
x
x,則以上return表達(dá)式變?yōu)椋?br>
x
=
d
s
t
.
m
i
n
+
(
i
n
d
e
x
?
s
r
c
.
m
i
n
)
?
d
s
t
R
a
n
g
e
+
s
r
c
R
a
n
g
e
2
s
r
c
R
a
n
g
e
x = dst.min + \frac{(index - src.min)*dstRange + \frac{srcRange}{2}}{srcRange}
x=dst.min+srcRange(index?src.min)?dstRange+2srcRange??
等式變化:
x
?
d
s
t
.
m
i
n
?
0.5
i
n
d
e
x
?
s
r
c
.
m
i
n
=
d
s
t
R
a
n
g
e
s
r
c
R
a
n
g
e
\frac{x - dst.min - 0.5}{index - src.min} = \frac{dstRange}{srcRange}
index?src.minx?dst.min?0.5?=srcRangedstRange?
最終求值的
x
x
x不就是我們要的等比例值嗎?+0.5估計(jì)是為了向上取整
4: 處理音量調(diào)節(jié)加減等工作
這個(gè)步驟主要有三個(gè)if條件處理,如下:
if (isMuteAdjust) {
處理mute和unmute情況
}else if((direction == AudioManager.ADJUST_RAISE) &&
!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)){
忽略此步
}else if(((device & mFullVolumeDevices) == 0) &&
(streamState.adjustIndex(direction * step, device, caller)
|| streamState.mIsMuted)){
處理音量加減情況
}
- 第一個(gè)條件處理策略:
isMuteAdjust的取值在:
private boolean isMuteAdjust(int adjust) {
return adjust == AudioManager.ADJUST_MUTE || adjust == AudioManager.ADJUST_UNMUTE
|| adjust == AudioManager.ADJUST_TOGGLE_MUTE;
}
AudioManager.ADJUST_MUTE : 靜默
AudioManager.ADJUST_UNMUTE : 取消靜默
AudioManager.ADJUST_TOGGLE_MUTE : 如果之前是靜默這次就非靜默,反之亦是
此類情況的處理策略:
//就是與之前的mute取反向操作
if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
state = !streamState.mIsMuted;
} else {
state = direction == AudioManager.ADJUST_MUTE;
}
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
//設(shè)置是否mute
setSystemAudioMute(state);
}
for (int stream = 0; stream < mStreamStates.length; stream++) {
//以別名為準(zhǔn),streamType對(duì)應(yīng)的別名相同的音量管理類volumeStreamState都要處理
if (streamTypeAlias == mStreamVolumeAlias[stream]) {
//沒有強(qiáng)制或type情況
if (!(readCameraSoundForced()
&& (mStreamStates[stream].getStreamType()
== AudioSystem.STREAM_SYSTEM_ENFORCED))) {
//設(shè)置某個(gè)streamType的mute狀態(tài)
mStreamStates[stream].mute(state);
}
}
}
根據(jù)direction確定是mute還是非mute,最后要調(diào)用alias別名相同的VolumeStreamState對(duì)象mute方法處理:
public void mute(boolean state) {
boolean changed = false;
synchronized (VolumeStreamState.class) {
//需要設(shè)置的state和之前的mISMuted是否相同,相同就沒必要再次重復(fù)
if (state != mIsMuted) {
changed = true;
mIsMuted = state;
//sendMsg參數(shù)MSG_SET_ALL_VOLUMES=what,SENDMSG_QUEUE代表這個(gè)消息是發(fā)、不發(fā)、替換策略,不會(huì)放入到消息體中,第三個(gè)就會(huì)放到消息里面去
sendMsg(mAudioHandler,
MSG_SET_ALL_VOLUMES,
SENDMSG_QUEUE,
0,
0,
this, 0);
}
}
......
}
音量調(diào)整幾乎是按照此模式來執(zhí)行的,拿到streamType的音量管理類VolumeStreamState,發(fā)送handle消息到AudioHandler,最后在走回VolumeStreamState自身的音量方法處理,所以VolumeStreamState封裝了音量處理的大部分方法
MSG_SET_ALL_VOLUMES消息處理依次調(diào)用:setAllVolumes --> applyAllVolumes,直接看最后的方法:
public void applyAllVolumes() {
final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported();
synchronized (VolumeStreamState.class) {
// apply device specific volumes first
int index;
for (int i = 0; i < mIndexMap.size(); i++) {
final int device = mIndexMap.keyAt(i);
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
if (mIsMuted) {
index = 0;
//為什么index要加5個(gè)等級(jí)呢?調(diào)整音量
} else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
isAvrcpAbsVolSupported) {
index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if ((device & mFullVolumeDevices) != 0) {
index = (mIndexMax + 5)/10;
} else if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
index = (mIndexMax + 5)/10;
} else {
index = (mIndexMap.valueAt(i) + 5)/10;
}
setStreamVolumeIndex(index, device);
}
}
// apply default volume last: by convention , default device volume will be used
// by audio policy manager if no explicit volume is present for a given device type
if (mIsMuted) {
index = 0;
} else {
index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
}
setStreamVolumeIndex(index, AudioSystem.DEVICE_OUT_DEFAULT);
}
}
i. 因?yàn)檫@里處理的mute和unmute,所以針對(duì)的所有的設(shè)備,故會(huì)遍歷mIndexMap中的所有device,依次為其調(diào)整音量;如果是mute,index就是0;unmute則為之前保存的音量加5除10,不清楚為何要加5? 除10好理解因?yàn)閂olumeStreamState內(nèi)的index整體都擴(kuò)大10倍了,setStreamVolumeIndex向下底層設(shè)置音量時(shí)要縮小10倍
ii. 不論怎么處理,最后都是調(diào)用setStreamVolumeIndex方法,走到這個(gè)方法后,后續(xù)的方法基本和AudioService不會(huì)有交集了,所以這里暫不往下分析,把AudioService分析完成。
最后,這里總結(jié)就是: mute就是音量等級(jí)index設(shè)置為0,unmute就是從mIndexMap中獲取之前保存的音量等級(jí)index,并加5除10,最后調(diào)用setStreamVolumeIndex向下設(shè)置音量
- 第三個(gè)條件處理策略:
關(guān)鍵點(diǎn)在于條件中的判斷streamState.adjustIndex就已經(jīng)把音量等級(jí)index設(shè)置到indexMap集合中去了
if (((device & mFullVolumeDevices) == 0)
&& (streamState.adjustIndex(direction * step, device, caller)
|| streamState.mIsMuted))
direction取值有:ADJUST_LOWER(-1)、ADJUST_RAISE(1)、ADJUST_SAME(0),還有幾個(gè)mute和unmute取值,但是那種情況不會(huì)進(jìn)入到這里,而step又是前面10轉(zhuǎn)換而來的,也就是index這里每次以10/-10向上或向下進(jìn)行調(diào)整,進(jìn)入adjustIndex方法:
public boolean adjustIndex(int deltaIndex, int device, String caller) {
//之前的音量加上這次變化的音量10/-10
return setIndex(getIndex(device) + deltaIndex, device, caller);
}
public int getIndex(int device) {
synchronized (VolumeStreamState.class) {
int index = mIndexMap.get(device, -1);
if (index == -1) {
// there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
index = mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
}
return index;
}
}
//設(shè)在音量等級(jí)
public boolean setIndex(int index, int device, String caller) {
boolean changed;
int oldIndex;
synchronized (mSettingsLock) {
synchronized (VolumeStreamState.class) {
oldIndex = getIndex(device);
index = getValidIndex(index); //index必須在min和max等級(jí)范圍中
if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
index = mIndexMax;
}
//將新的音量等級(jí)index設(shè)置到map中
mIndexMap.put(device, index);
changed = oldIndex != index; //音量等級(jí)是否有變化
final boolean isCurrentDevice = (device == getDeviceForStream(mStreamType));
final int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
final VolumeStreamState aliasStreamState = mStreamStates[streamType];
//跳過自己 尋找alias別名與自己相同 且也擁有此設(shè)備device的volumestate,并為其設(shè)置index
if (streamType != mStreamType &&
mStreamVolumeAlias[streamType] == mStreamType &&
(changed || !aliasStreamState.hasIndexForDevice(device))) {
final int scaledIndex = rescaleIndex(index, mStreamType, streamType);
//mStreamStates數(shù)組中,其他不同流類型streamType,但是映射到alias里面流相同的設(shè)置相同的device一下,
aliasStreamState.setIndex(scaledIndex, device, caller);
if (isCurrentDevice) {
aliasStreamState.setIndex(scaledIndex,
getDeviceForStream(streamType), caller);
}
}
}
// Mirror changes in SPEAKER ringtone volume on SCO when
if (changed && mStreamType == AudioSystem.STREAM_RING
&& device == AudioSystem.DEVICE_OUT_SPEAKER) {
for (int i = 0; i < mIndexMap.size(); i++) {
int otherDevice = mIndexMap.keyAt(i);
//為什么要把sco相關(guān)的device音量也修改了呢?
if ((otherDevice & AudioSystem.DEVICE_OUT_ALL_SCO) != 0) {
mIndexMap.put(otherDevice, index);
}
}
}
}
}
return change;
}
細(xì)節(jié)看代碼中的注釋,最主要的是把新的音量等級(jí)設(shè)置到indexMap集合中,同時(shí)再遍歷所有VolumeStreamState對(duì)象,別名和自己相同的對(duì)象,也要為其設(shè)置音量等級(jí),為什么呢?
我的理解,因?yàn)橄嗤瑂treamType,他們的輸出device可能也是一樣的,所以它的音量也應(yīng)該要改變才對(duì);
后續(xù)的第三個(gè)條件中的處理,也是根據(jù)direction判斷是raise還是lower音量,然后發(fā)送sendMsg對(duì)應(yīng)的消息,進(jìn)行處理,同條件if一是一樣的,最后也是調(diào)用setStreamVolumeIndex向下設(shè)置音量。
setStreamVolume
設(shè)置過程同adjustStreamVolume相差無幾,在此就略過,如果需要可以找我要一份相關(guān)添加注釋的代碼,里面有詳細(xì)的批注解答!
AudioService章節(jié)總結(jié)
1)利用設(shè)置的音頻流類型streamType找到對(duì)應(yīng)的音量狀態(tài)管理類VolumeStreamState,此類集合的音量管理的核心方法,內(nèi)部有一個(gè)map集合保存了所有輸出設(shè)備及其對(duì)應(yīng)的音量等級(jí)index,最終也是調(diào)整這個(gè)index
2)同時(shí)使用streamType根據(jù)strategy找到對(duì)應(yīng)的輸出設(shè)備,為此設(shè)備device設(shè)置index
3)一旦某個(gè)VolumeStreamState音量做出了調(diào)整,則需要把別名相同的另一個(gè)VolumeStreamState也要進(jìn)行音量調(diào)整
4)其他就是要處理RingMode、fix固定音量和safe安全音量的問題
5)音量等級(jí)設(shè)置好后,就是向下調(diào)用setStreamVolumeIndex方法,進(jìn)行音量等級(jí)到分貝的處理
AudioPolicyManager音量調(diào)整
記住,上面AudioService最后向下調(diào)用setStreamVolumeIndex函數(shù)傳遞的重要參數(shù)是:
1)輸出設(shè)備device; 2)音量等級(jí)index;
分析AudioPolicyManager模塊的音量設(shè)置之前建議先閱讀Audio解析strategy配置文件文章,因?yàn)檫@塊涉及了很多strategy策略、VolumeCurves音量曲線概念,以及他們之間的依賴連接關(guān)系;
setStreamVolumeIndex
status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
auto attributes = mEngine->getAttributesForStreamType(stream);
ALOGV("%s: stream %s attributes=%s", __func__,
toString(stream).c_str(), toString(attributes).c_str());
return setVolumeIndexForAttributes(attributes, index, device);
}
這塊代碼主要是將streamType轉(zhuǎn)換到他對(duì)應(yīng)的屬性attribute,然后調(diào)用setVolumeIndexForAttributes函數(shù)繼續(xù)往下設(shè)置,那streamType怎么轉(zhuǎn)換得到的attribute呢?
答案就是系統(tǒng)啟動(dòng)時(shí)解析strategy和VolumeCurves配置文件得到的strategy和VolumeCurves的數(shù)據(jù)依賴關(guān)系,利用這個(gè)依賴關(guān)系得到的:
streamType–>productStrategy–>遍歷其內(nèi)部所有的attribute,若它內(nèi)部的streamType與傳入的streamType相等就返回attribute
插入一個(gè)知識(shí)點(diǎn)AudioOutputDescriptor
還記得AudioOutputDescriptor類嗎?在AudioPolicyManager初始化時(shí)每打開一個(gè)輸出通道,就會(huì)用mOutputs<id,SwAudioOutputDescriptor>保存一個(gè)此類,它是負(fù)責(zé)連接App應(yīng)用端和中間件AudioFlinger通道端的一個(gè)中間類,而下面介紹的知識(shí)點(diǎn)就是和應(yīng)用端相關(guān)的成員,如下:
using RoutingActivities = std::map<product_strategy_t, RoutingActivity>;
using VolumeActivities = std::map<VolumeSource, VolumeActivity>;
class AudioOutputDescriptor: public AudioPortConfig, public AudioIODescriptorInterface
//ClientMapHandler是std::map<audio_port_handle_t, sp<T>>類型,通過addClient往里面添加,那這個(gè)client就
//代表客戶端應(yīng)用層的AudioTrack
, public ClientMapHandler<TrackClientDescriptor>
{
RoutingActivities mRoutingActivities; /**< track routing activity on this ouput.*/
//std::map<int, class>類型,mVolumeActivities[1]會(huì)自動(dòng)構(gòu)建一個(gè)class對(duì)象,無需add或insert
VolumeActivities mVolumeActivities; /**< track volume activity on this ouput.*/
// The ActiveClients shows the clients that contribute to the @VolumeSource counts
// and may include upstream clients from a duplicating thread.
// Compare with the ClientMap (mClients) which are external AudioTrack clients of the
// output descriptor (and do not count internal PatchTracks).
TrackClientVector mActiveClients;
}
因?yàn)槎鄠€(gè)App應(yīng)用端可能在中間層都是使用的同一個(gè)通道,所以這個(gè)AudioOutputDescriptor就集成ClientMapHandler這個(gè)集合對(duì)象,父類ClientMapHandler內(nèi)有一個(gè)ActiveClients成員是保存客戶端client信息的,同樣在AudioOutputDescriptor也有一個(gè)mActiveClients也是保存客戶端信息的,他們二者有何不同,我也不是太清楚,哈哈!
mVolumeActivities: 是保存與客戶端音量方面的對(duì)象,音量加減時(shí)會(huì)用到
mRoutingActivities: 保存與客戶端路由方面的對(duì)象
client是如何被添加進(jìn)來的?
如下代碼:
void AudioOutputDescriptor::setClientActive(const sp<TrackClientDescriptor>& client, bool active)
{
//查找成功則返回一個(gè)指向指定元素的迭代器,查找失敗則返回end迭代器
auto clientIter = std::find(begin(mActiveClients), end(mActiveClients), client);
//首先判斷是否存在,不存在情況括號(hào)內(nèi)部是false,如果參數(shù)active是true,說明是第一次進(jìn)來,反之參數(shù)是false,則沒必要繼續(xù)往下
//同理,后半部分為true,就是存在情況下,同理
if (active == (clientIter != end(mActiveClients))) {
ALOGW("%s(%s): ignored active: %d, current stream count %d", __func__,
client->toShortString().c_str(), active,
mRoutingActivities.at(client->strategy()).getActivityCount());
return;
}
if (active) {
//尾巴添加
mActiveClients.push_back(client);
} else {
//刪除
mActiveClients.erase(clientIter);
}
const int delta = active ? 1 : -1;
// []這種方式相當(dāng)于直接創(chuàng)建一個(gè)內(nèi)部元素 delta是存活次數(shù)
mRoutingActivities[client->strategy()].changeActivityCount(delta);
mVolumeActivities[client->volumeSource()].changeActivityCount(delta);
// Handle non-client-specific activity ref count
int32_t oldGlobalActiveCount = mGlobalActiveCount;
if (!active && mGlobalActiveCount < 1) {
ALOGW("%s(%s): invalid deactivation with globalRefCount %d",
__func__, client->toShortString().c_str(), mGlobalActiveCount);
mGlobalActiveCount = 1;
}
mGlobalActiveCount += delta;
......
client->setActive(active);
}
如果客戶端App使用AudioTrack時(shí)播放音頻時(shí),就會(huì)為其尋找一個(gè)輸出通道,成功找到通道后就會(huì)調(diào)用此方法;
- 設(shè)置客戶端根據(jù)其active狀態(tài)來添加,首先檢查client是否存在,不存在且active是true就添加進(jìn)來
- 存活active就加入到mActiveClients,反之則移除
- 創(chuàng)建mRoutingActivities和mVolumeActivities對(duì)應(yīng)詞客戶端的元素,并賦予其存活次數(shù)
setVolumeIndexForAttributes
status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attributes,
int index,
audio_devices_t device)
{
// 從attributes中獲取其連接的volumeGroup
auto group = mEngine->getVolumeGroupForAttributes(attributes);
if (group == VOLUME_GROUP_NONE) {
ALOGD("%s: no group matching with %s", __FUNCTION__, toString(attributes).c_str());
return BAD_VALUE;
}
status_t status = NO_ERROR;
//從attribute獲取與之對(duì)應(yīng)的volumeGroup,在從volumeGroup中獲取到VolumeCurves
IVolumeCurves &curves = getVolumeCurves(attributes);
//static強(qiáng)轉(zhuǎn),子類向基類轉(zhuǎn)換安全,但反之沒有運(yùn)行時(shí)檢查,不安全
VolumeSource vs = toVolumeSource(group);
product_strategy_t strategy = mEngine->getProductStrategyForAttributes(attributes);
//將當(dāng)前的音量等級(jí)設(shè)置到volumeCurves的mIndexCur<device,index>成員中去
status = setVolumeCurveIndex(index, device, curves);
if (status != NO_ERROR) {
ALOGE("%s failed to set curve index for group %d device 0x%X", __func__, group, device);
return status;
}
audio_devices_t curSrcDevice;
auto curCurvAttrs = curves.getAttributes();
//取attrs的首節(jié)點(diǎn),并且根據(jù)attribute獲取當(dāng)前系統(tǒng)最新的一個(gè)輸出設(shè)備curSrcDevice
if (!curCurvAttrs.empty() && curCurvAttrs.front() != defaultAttr) {
auto attr = curCurvAttrs.front();
//獲取attr適合的device
curSrcDevice = mEngine->getOutputDevicesForAttributes(attr, nullptr, false).types();
} else if (!curves.getStreamTypes().empty()) {
auto stream = curves.getStreamTypes().front();
curSrcDevice = mEngine->getOutputDevicesForStream(stream, false).types();
} else {
ALOGE("%s: Invalid src %d: no valid attributes nor stream",__func__, vs);
return BAD_VALUE;
}
//多個(gè)設(shè)備的情況下,選擇一個(gè)
curSrcDevice = Volume::getDeviceForVolume(curSrcDevice);
//遍歷每一個(gè)輸出通道
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
//打開的設(shè)備
audio_devices_t curDevice = desc->devices().types();
if (curDevice & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
curDevice |= AUDIO_DEVICE_OUT_SPEAKER;
curDevice &= ~AUDIO_DEVICE_OUT_SPEAKER_SAFE;
}
bool applyVolume = false;
//此設(shè)備能進(jìn)行g(shù)ain增益控制,檢查desc的openDevice的device,查看device的配置標(biāo)簽gain對(duì)應(yīng)的AudioGains是否支持音量調(diào)節(jié)
if (desc->useHwGain()) {
//必須有對(duì)應(yīng)的volumegroup客戶端client存活
if (!(desc->isActive(toVolumeSource(group)) || isInCall())) {
continue;
}
for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) {
auto activeClients = desc->clientsList(true /*activeOnly*/, productStrategy,
false /*preferredDevice*/);
//無存活客戶端,不做音量調(diào)整
if (activeClients.empty()) {
continue;
}
bool isPreempted = false;
bool isHigherPriority = productStrategy < strategy;
for (const auto &client : activeClients) {
if (isHigherPriority && (client->volumeSource() != vs)) {
applyVolume = false;
isPreempted = true;
break;
}
// However, continue for loop to ensure no higher prio clients running on output
//client有一個(gè)符合音量曲線的設(shè)備
if (client->volumeSource() == vs) {
applyVolume = true;
}
}
if (isPreempted || applyVolume) {
break;
}
}
if (!applyVolume) {
continue; // next output
}
//最終會(huì)把index由音量等級(jí)轉(zhuǎn)化為分貝db,把分貝值設(shè)置到AudioutputDescriptor的VolumeActivities成員變量中去
status_t volStatus = checkAndSetVolume(curves, vs, index, desc, curDevice,
(vs == toVolumeSource(AUDIO_STREAM_SYSTEM)?
TOUCH_SOUND_FIXED_DELAY_MS : 0));
if (volStatus != NO_ERROR) {
status = volStatus;
}
continue;
}
......
if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
curSrcDevice |= device; //合并當(dāng)前設(shè)備與上層傳入設(shè)備
//getDeviceForVolume是選擇一個(gè)設(shè)備,與后者取交集,有交集說明和調(diào)節(jié)音量設(shè)備有關(guān)系
applyVolume = (Volume::getDeviceForVolume(curDevice) & curSrcDevice) != 0;
} else {
//檢查curves內(nèi)mIndexCur是否包含curSrcDevice
applyVolume = !curves.hasVolumeIndexForDevice(curSrcDevice);
}
if (applyVolume) {
//FIXME: workaround for truncated touch sounds
// delayed volume change for system stream to be removed when the problem is
// handled by system UI
status_t volStatus = checkAndSetVolume(
curves, vs, index, desc, curDevice,
((vs == toVolumeSource(AUDIO_STREAM_SYSTEM))?
TOUCH_SOUND_FIXED_DELAY_MS : 0));
if (volStatus != NO_ERROR) {
status = volStatus;
}
}
}
//group是attribute綁定的group,也就是streamType對(duì)應(yīng)的attribute
mpClientInterface->onAudioVolumeGroupChanged(group, 0 /*flags*/);
return status;
}
代碼很多,閱讀下來就做了幾件事:
- 以attribute屬性找到volumegroup、VolumeCurves和productStrategy,然后將device的音量等級(jí)設(shè)置到VolumeCurves的內(nèi)部成員mIndexCur<device,index>中去,原始的音量數(shù)據(jù)
- 以attribute按照系統(tǒng)當(dāng)前的音頻策略,找到當(dāng)前的輸出設(shè)備curDevice,再遍歷所有輸出通道m(xù)Outputs,看是哪個(gè)通道設(shè)備AudioOutputDescriptor在使用這個(gè)設(shè)備curDevice
- 簡(jiǎn)稱AudioOutputDescriptor保存的打開設(shè)備為Curdevice,如果這個(gè)Curdevice使用hwGain(硬件音量設(shè)置),也就是device-port標(biāo)簽配置了gain,name就去查找這個(gè)AudioOutputDescriptor的client是否有與之匹配的volumeGroup,與我們的attribute的volumeGroup相等,相等就說這個(gè)通道需要設(shè)置音量,調(diào)用checkAndSetVolume進(jìn)行設(shè)置
- 如果Curdevice沒有使用hwGain,那就需要看看這個(gè)Curdevice和我們傳入的設(shè)備device有沒有交集,有交集說明也需要對(duì)此通道進(jìn)行音量調(diào)整;沒有交集則查看音量曲線volumeCurves內(nèi)有沒有已經(jīng)設(shè)置過curSrcDevice的音量,沒有就需要設(shè)置音量,充滿疑問的是為啥沒有就要設(shè)置,應(yīng)該是有才會(huì)去設(shè)置,而且代碼寫在這個(gè)地方和外層的for循環(huán)遍歷mOutputs一點(diǎn)關(guān)系也沒有
到這一步,還是在確定哪些通道需要調(diào)整音量值?。?!
checkAndSetVolume
status_t AudioPolicyManager::checkAndSetVolume(IVolumeCurves &curves,
VolumeSource volumeSource,
int index,
const sp<AudioOutputDescriptor>& outputDesc,
audio_devices_t device,
int delayMs,
bool force)
{
// 查看其內(nèi)的VolumeActivities的muteCount是否大于0,大于0不能進(jìn)行音量調(diào)節(jié)
if (outputDesc->isMuted(volumeSource)) {
ALOGVV("%s: volume source %d muted count %d active=%d", __func__, volumeSource,
outputDesc->getMuteCount(volumeSource), outputDesc->isActive(volumeSource));
return NO_ERROR;
}
........
if (device == AUDIO_DEVICE_NONE) {
//devices返回的是open時(shí)傳入的設(shè)備
device = outputDesc->devices().types();
}
//計(jì)算音量值dB,device轉(zhuǎn)化為device_category,根據(jù)它在找到curve,傳入index獲取其分貝值
float volumeDb = computeVolume(curves, volumeSource, index, device);
if (outputDesc->isFixedVolume(device) ||
// Force VoIP volume to max for bluetooth SCO
((isVoiceVolSrc || isBtScoVolSrc) && (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
volumeDb = 0.0f;
}
//設(shè)置到其內(nèi)部成員volumeActivities里面去
outputDesc->setVolume(volumeDb, volumeSource, curves.getStreamTypes(), device, delayMs, force);
if (isVoiceVolSrc || isBtScoVolSrc) {
float voiceVolume;
// Force voice volume to max or mute for Bluetooth SCO as other attenuations are managed by the headset
if (isVoiceVolSrc) {
voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
} else {
voiceVolume = index == 0 ? 0.0 : 1.0;
}
if (voiceVolume != mLastVoiceVolume) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume;
}
}
return NO_ERROR;
}
以上代碼主要完成兩個(gè)任務(wù):
- 將音量等級(jí)index轉(zhuǎn)換為分貝db,computeVolume函數(shù)
- 將分貝值寫入到輸出設(shè)備outputDesc中去,如果是通話音量的話voice話,則調(diào)用setVoiceVolume直通hal層去調(diào)整音量
1. index轉(zhuǎn)dB過程依次調(diào)用
AudioPolicyManager::computeVolume -->> VolumeCurves::volIndexToDb(device_category deviceCat, int indexInUi) -->> VolumeCurve::VolIndexToDb(int indexInUi, int volIndexMin, int volIndexMax)
在VolumeCurves里面,是按map<device_category, volumecurve>存儲(chǔ)的,所以根據(jù)傳入deviceCat拿到VolumeCurve,然后在VolumeCurve里:
//indexUi是上層傳遞下來的音量等級(jí),max和min是可調(diào)整的最大最小值
float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const
{
ALOG_ASSERT(!mCurvePoints.isEmpty(), "Invalid volume curve");
if (volIndexMin < 0 || volIndexMax < 0) {
// In order to let AudioService initialize the min and max, convention is to use -1
return NAN;
}
if (indexInUi < volIndexMin) {
// an index of 0 means mute request when volIndexMin > 0
if (indexInUi == 0) {
ALOGV("VOLUME forcing mute for index 0 with min index %d", volIndexMin);
return VOLUME_MIN_DB;
}
ALOGV("VOLUME remapping index from %d to min index %d", indexInUi, volIndexMin);
indexInUi = volIndexMin;
} else if (indexInUi > volIndexMax) {
ALOGV("VOLUME remapping index from %d to max index %d", indexInUi, volIndexMax);
indexInUi = volIndexMax;
}
size_t nbCurvePoints = mCurvePoints.size();
// the volume index in the UI is relative to the min and max volume indices for this stream
//nbSteps總的音量等級(jí)范圍總共值 1~nbSteps,加一就是最小值從1開始,防止為0時(shí)是mute,特別注意,這個(gè)去就是index,不要理解成健值對(duì)的value了
int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex;
//nbSteps是總的音量等級(jí),后者是當(dāng)前設(shè)置音量index占可調(diào)整范圍(min,max)占比多少,乘以后就是映射到總的音量等級(jí)的映射值
int volIdx = (nbSteps * (indexInUi - volIndexMin)) / (volIndexMax - volIndexMin);
// Where would this volume index been inserted in the curve point
//只比教,不插入,從而知道對(duì)應(yīng)的分貝值
size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
if (indexInUiPosition >= nbCurvePoints) {
//use last point of table
return mCurvePoints[nbCurvePoints - 1].mAttenuationInMb / 100.0f;
}
if (indexInUiPosition == 0) {
if (indexInUiPosition != mCurvePoints[0].mIndex) {
return VOLUME_MIN_DB; // out of bounds
}
return mCurvePoints[0].mAttenuationInMb / 100.0f;
}
// linear interpolation in the attenuation table in dB
//這個(gè)求值原理就是,取插入前一個(gè)位置的分貝值作為基礎(chǔ),加上后部分的比例值,比例值采用插入前和后這兩個(gè)位置的間距比,計(jì)算出對(duì)應(yīng)的分貝值
//思考:因?yàn)榉重愂欠蔷€性的,這里采用比例換算出分貝合理嗎?
//我覺得應(yīng)該合理,首先基礎(chǔ)值是插入前一個(gè)位置的分貝,這個(gè)值是xml文件配置的,它是非線性的合理值,占據(jù)大部分;關(guān)鍵在于后部分的比例換算值是否合理,我覺得合理因?yàn)橐舱紦?jù)小部分值,人耳聽不出
float decibels = (mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f) +
((float)(volIdx - mCurvePoints[indexInUiPosition - 1].mIndex)) *
( ((mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f) -
(mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f)) /
((float)(mCurvePoints[indexInUiPosition].mIndex -
mCurvePoints[indexInUiPosition - 1].mIndex)) );
ALOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
mCurvePoints[indexInUiPosition - 1].mIndex, volIdx,
mCurvePoints[indexInUiPosition].mIndex,
((float)mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f), decibels,
((float)mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f));
if(indexInUi >=0 && indexInUi<32 && (volIndexMax==31))
decibels=VOLUME_DB[indexInUi];
//分貝值
return decibels;
}
這部分代碼比較好理解,重點(diǎn)要理解mCurvePoints來源于解析volume_conf文件如下:
<volume stream="AUDIO_STREAM_DTMF" deviceCategory="DEVICE_CATEGORY_HEADSET">
<point>1,-3000</point>
<point>33,-2600</point>
<point>66,-2200</point>
<point>100,-1800</point>
</volume>
mCurvePoints[x].mIndex就是第一列數(shù)據(jù)1、33、66、100,mAttenuationInMb就是分貝值-3000、-2600等,上述代碼實(shí)質(zhì)轉(zhuǎn)換index后進(jìn)行查表,因?yàn)樯厦鎮(zhèn)魅氲膇ndex范圍是0 ~ 31,而mCurvePoints[x].mIndex在1 ~ 100的范圍,所以要先使用等比例換將0 ~ 31范圍的值換算到1~100的范圍,然后使用插值法確定volIdx應(yīng)該插入到mCurvePoints的哪個(gè)位置,
1)如果indexInUiPosition落在0~最大值外,則取其最接近移除的值;
2) 值落0~最大值范圍內(nèi),就取前一個(gè)index位置的mAttenuationInMb,加上前后兩個(gè)位置的分貝值差值,乘以這個(gè)index在前后index占的百分比,計(jì)算如下:
記前后點(diǎn)分貝為pre.mb、aft.mb,同理index,插入為insert.index,這個(gè)insert.index落在pre.index和aft.index中間,那所求insert.index的分貝為:
M
B
=
p
r
e
.
m
b
+
i
n
s
e
r
t
.
i
n
d
e
x
?
p
r
e
.
i
n
d
e
x
a
f
t
.
i
n
d
e
x
?
p
r
e
.
i
n
d
e
x
?
(
a
f
t
.
m
b
?
p
r
e
.
m
b
)
MB = pre.mb + \frac{insert.index - pre.index}{aft.index - pre.index} * (aft.mb - pre.mb)
MB=pre.mb+aft.index?pre.indexinsert.index?pre.index??(aft.mb?pre.mb)
AudioPolicyManager小結(jié)
此部分作用主要是:
- 根據(jù)device、streamType找到attribute、strategy、volumeCurves音量曲線等
- 尋找哪些通道需要進(jìn)行音量調(diào)整,主要是根據(jù)attribute、客戶端client對(duì)應(yīng)的volumeGroup是否一致,以及AudioOutputDescriptor的device和上層傳入下來的device是否相同
- 找到AudioOutputDescriptor需要調(diào)整音量時(shí),然后利用attribute找到的VolumeCurves、volumePoints等,利用查表和等比例方法查到其對(duì)應(yīng)的分貝值
AudioOutputDescriptor音量調(diào)整
setVolume
bool AudioOutputDescriptor::setVolume(float volumeDb,
VolumeSource volumeSource,
const StreamTypeVector &/*streams*/,
audio_devices_t /*device*/,
uint32_t delayMs,
bool force)
{
// We actually change the volume if:
// - the float value returned by computeVolume() changed 與之前的音量不同
// - the force flag is set 強(qiáng)制標(biāo)記
if (volumeDb != getCurVolume(volumeSource) || force) {
ALOGV("%s for volumeSrc %d, volume %f, delay %d", __func__, volumeSource, volumeDb, delayMs);
//設(shè)置到VolumeActivities的db成員中,本類中會(huì)getCurVolume獲取當(dāng)前音量,并向AudioFlinger設(shè)置到回播線程中,根據(jù)streamType設(shè)置到對(duì)應(yīng)的stream上去
setCurVolume(volumeSource, volumeDb);
return true;
}
return false;
}
bool SwAudioOutputDescriptor::setVolume(float volumeDb,
VolumeSource vs, const StreamTypeVector &streamTypes,
audio_devices_t device,
uint32_t delayMs,
bool force)
{
StreamTypeVector streams = streamTypes;
if (!AudioOutputDescriptor::setVolume(volumeDb, vs, streamTypes, device, delayMs, force)) {
return false;
}
if (streams.empty()) {
streams.push_back(AUDIO_STREAM_MUSIC);
}
for (const auto& devicePort : devices()) {
// 設(shè)備相等,且支持gain硬件調(diào)整音量的去設(shè)置
if (device == devicePort->type() &&
devicePort->hasGainController(true) && isActive(vs)) {
ALOGV("%s: device %s has gain controller", __func__, devicePort->toString().c_str());
//將0dB轉(zhuǎn)換為功率值
float volumeAmpl = Volume::DbToAmpl(0);
//為此類型的軟件音量值設(shè)置0就是不發(fā)聲,
for (const auto &stream : streams) {
mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
}
//硬件音量更新
AudioGains gains = devicePort->getGains();
int gainMinValueInMb = gains[0]->getMinValueInMb();
int gainMaxValueInMb = gains[0]->getMaxValueInMb();
int gainStepValueInMb = gains[0]->getStepValueInMb();
int gainValueMb = ((volumeDb * 100)/ gainStepValueInMb) * gainStepValueInMb;
gainValueMb = std::max(gainMinValueInMb, std::min(gainValueMb, gainMaxValueInMb));
audio_port_config config = {};
devicePort->toAudioPortConfig(&config);
config.config_mask = AUDIO_PORT_CONFIG_GAIN;
config.gain.values[0] = gainValueMb;
//硬件音量設(shè)置
return mClientInterface->setAudioPortConfig(&config, 0) == NO_ERROR;
}
}
//上述走過硬件音量后,下面的都是軟件音量,獲取當(dāng)前音量并轉(zhuǎn)換為功率值ampl
float volumeAmpl = Volume::DbToAmpl(getCurVolume(vs));
if (hasStream(streams, AUDIO_STREAM_BLUETOOTH_SCO)) {
mClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volumeAmpl, mIoHandle, delayMs);
}
//設(shè)置功率值
for (const auto &stream : streams) {
ALOGV("%s output %d for volumeSource %d, volume %f, delay %d stream=%s", __func__,
mIoHandle, vs, volumeDb, delayMs, toString(stream).c_str());
mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
}
return true;
}
- 首先將音量值分貝Db設(shè)置到volumeActivities成員中,然后遍歷當(dāng)前打開的device
- 如果這個(gè)device支持gain硬件方式設(shè)置音量,就使用硬件音量調(diào)整setAudioPortConfig,此方法會(huì)調(diào)用到hal的set_audio_port_config指針函數(shù);否則就是軟件音量調(diào)整設(shè)置setStreamVolume
- 在進(jìn)行第2步時(shí),需要先把Db分貝轉(zhuǎn)換為功率值ampl,轉(zhuǎn)換方法如下:
Volume::DbToAmpl
static inline float DbToAmpl(float decibels)
{
//VOLUME_MIN_DB = -758估計(jì)在小的話人耳就聽不見了
if (decibels <= VOLUME_MIN_DB) {
return 0.0f;
}
//exp是以e為底的指數(shù)函數(shù),常數(shù) e 的值約為 2.718282;是由分貝算式推導(dǎo)過來的db = 20* ln(P1/P0);P0是基本功率
return exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
}
看不懂此公式的先閱讀混音理論篇的聲音的音量屬性和聲音相關(guān)公式的推導(dǎo)章節(jié),這里做個(gè)簡(jiǎn)單的推導(dǎo):
一般來說分貝公式為
20
log
?
P
1
P
0
20\log\frac{P_1}{P_0}
20logP0?P1??,其中
P
1
P_1
P1?是待測(cè)功率,
P
0
P_0
P0?是基礎(chǔ)功率,因?yàn)榉重愂菨M足對(duì)數(shù)特性,這里就用對(duì)數(shù)比值來確定分貝,這里我們的aosp代碼把
log
?
\log
log替換為對(duì)數(shù)
ln
?
\ln
ln函數(shù),基礎(chǔ)功率
P
0
P_0
P0?取值為10,在移植分貝
d
e
c
i
b
e
l
s
decibels
decibels的情況下,求取
P
1
P_1
P1?就好理解了,簡(jiǎn)化
d
e
c
i
b
e
l
s
decibels
decibels為
d
b
db
db,則推導(dǎo)公式:
d
b
=
20
?
ln
?
P
1
P
0
=
20
ln
?
P
1
10
db = 20* \ln\frac{P_1}{P_0} = 20\ln\frac{P_1}{10}
db=20?lnP0?P1??=20ln10P1??
d
b
20
=
ln
?
P
1
10
\frac{db}{20} = \ln\frac{P_1}{10}
20db?=ln10P1??
e
d
b
20
=
P
1
10
e^{\frac{db}{20}} = \frac{P_1}{10}
e20db?=10P1??
P
1
=
10
?
e
d
b
20
=
e
ln
?
10
?
e
d
b
20
=
e
ln
?
10
+
d
b
20
P_1 = 10*e^{\frac{db}{20}} = e^{\ln10} * e^{\frac{db}{20}} = e^{{\ln10} + {\frac{db}{20}}}
P1?=10?e20db?=eln10?e20db?=eln10+20db?
所以我認(rèn)為上面aosp的轉(zhuǎn)換代碼是錯(cuò)誤的,為什么它會(huì)寫成 ln ? 10 20 \frac{\ln{10}}{20} 20ln10?實(shí)在想不通!!!
-----------------------------------------------------理解更新--------------------------------------------------------------
-----------------------------------------------------理解更新--------------------------------------------------------------
接上買軟件音量設(shè)置方法setStreamVolume
AudioFlinger中音量處理
setStreamVolume
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output)
{
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
status_t status = checkStreamType(stream);
if (status != NO_ERROR) {
return status;
}
if (output == AUDIO_IO_HANDLE_NONE) {
return BAD_VALUE;
}
LOG_ALWAYS_FATAL_IF(stream == AUDIO_STREAM_PATCH && value != 1.0f,
"AUDIO_STREAM_PATCH must have full scale volume");
AutoMutex lock(mLock);
//從mPlaybackThreads集合中拿到一個(gè)回播線程實(shí)例
VolumeInterface *volumeInterface = getVolumeInterface_l(output);
if (volumeInterface == NULL) {
return BAD_VALUE;
}
//設(shè)置音量對(duì)應(yīng)功率值到playbackthread中的stream對(duì)應(yīng)的音量值去
volumeInterface->setStreamVolume(stream, value);
return NO_ERROR;
}
代碼很簡(jiǎn)單了,主要是通過句柄output拿到通道回播線程PlaybackThread,設(shè)置音量進(jìn)去就可以了文章來源:http://www.zghlxwxcb.cn/news/detail-618000.html
PlaybackThread中音量處理
setStreamVolume
void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
Mutex::Autolock _l(mLock);
mStreamTypes[stream].volume = value;
broadcast_l();
}
回播線程按照streamType類型集合保存了音量值,準(zhǔn)確說應(yīng)該是音量值對(duì)應(yīng)的功率值,在播放音頻或混音時(shí),回播線程會(huì)將音頻數(shù)據(jù)與音量數(shù)據(jù)相乘,最后將結(jié)果傳送到Hal去播放,至此,軟件音量調(diào)節(jié)過程就完結(jié)了!文章來源地址http://www.zghlxwxcb.cn/news/detail-618000.html
到了這里,關(guān)于Android Audio音量設(shè)置原理流程分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!