摘要:本文介紹了android音量的控制曲線的計算方法。
由於人耳對聲音的聽感具指數曲線型,也就是對小音量時比較敏感,隨着聲音的加大其聽感隨之變的不敏感,其變化近似指數函數曲線的形式。為了使聽感變的近似直線的變化,人們在實踐中就采用了音量變化近似對數式曲線型的電位器來實現這個目的。對比法產生音量控制曲線與最終揚聲器輸出的聲壓有關,當然您也可以根據揚聲器的輸出功率來進行比對,但功率終究不如電壓來的方便。音量調節框的UI滑動條的刻度是線性的,這樣就給我們生成音量控制曲線打下了很好的對比基礎。下面我們就來通過一個音量調節的場景來分析Android是如何控制音量的。
首先,我們按音量調節鍵使得media音量逐級增加到最大。STREAM_MUSIC流的音量分為15級,通過AudioManger的handleKeyDown函數調用adjustSuggestedStreamVolume設置,一路找下去,發現在AudioService中adjustSuggestedStreamVolume然后調用adjustStreamVolume,通過消息MSG_SET_SYSTEM_VOLUME(4.1中MSG_SET_DEVICE_VOLUME)調用setSystemVolume(4.1中setDeviceVolume),轉到AudioSystem中的setStreamVolumeIndex,再通過jni層調用本地層的AudioSystem調用AudioPolicymanagerService,最后到AudiopolicyManagerBase的setStreamVolumeIndex,接下來的由checkAndSetVolume調用computeVolume,馬上就要到真相大白的時候了。volIndexToAmpl函數時真正計算音量的地方,我們一起來分析這個函數
1 float AudioPolicyManagerBase::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, 2 int indexInUi) 3 { 4 device_category deviceCategory = getDeviceCategory(device); 5 const VolumeCurvePoint *curve = streamDesc.mVolumeCurve[deviceCategory]; 6 7 // the volume index in the UI is relative to the min and max volume indices for this stream type 8 int nbSteps = 1 + curve[VOLMAX].mIndex - 9 curve[VOLMIN].mIndex;//計算預置的曲線區間的范圍,這里是(1-100) 10 ALOGI("VOLUME vol indexInUi=%d, nbSteps=%d, mIndexMin=%d, mIndexMax=%d",indexInUi,nbSteps,streamDesc.mIndexMin,streamDesc.mIndexMax); 11 int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / 12 (streamDesc.mIndexMax - streamDesc.mIndexMin);//(由傳進來的UIIndex計算百分比的index,比如現在是第一級 100*(1-0)/(15-0)=6) 13 14 // find what part of the curve this index volume belongs to, or if it's out of bounds 15 int segment = 0; 16 if (volIdx < curve[VOLMIN].mIndex) { // out of bounds 17 return 0.0f; 18 } else if (volIdx < curve[VOLKNEE1].mIndex) { 19 segment = 0; 20 } else if (volIdx < curve[VOLKNEE2].mIndex) { 21 segment = 1; 22 } else if (volIdx <= curve[VOLMAX].mIndex) { 23 segment = 2; 24 } else { // out of bounds 25 return 1.0f; 26 } 27 //第一極6是在區間VOLKNEE1之間,其區間表是在AudioPolicyManager初始化的時候就已經加載,因此它對應的segment為0 28 // linear interpolation in the attenuation table in dB 29 float decibels = curve[segment].mDBAttenuation + 30 ((float)(volIdx - curve[segment].mIndex)) * 31 ( (curve[segment+1].mDBAttenuation - 32 curve[segment].mDBAttenuation) / 33 ((float)(curve[segment+1].mIndex - 34 curve[segment].mIndex)) ); 35 //計算衰減分貝數 curve[0].db + 該區間每一級index對應的db*index數 36 float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) 37//由指數公式計算出音量amplification db = 20log(V/Vmax) linearToLog Vmax是一個參考值 38 ALOGI("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f", 39 curve[segment].mIndex, volIdx, 40 curve[segment+1].mIndex, 41 curve[segment].mDBAttenuation, 42 decibels, 43 curve[segment+1].mDBAttenuation, 44 amplification); 45 46 return amplification; 47 }
然后通過以上函數計算后得到一個0-1之間的float音量值,最后通過mpClientInterface->setStreamVolume設置到audioflinger中的mStreamTypes[stream].value,在prepareTracks_l中將音量值傳入到audiomixer中混合。至此音量調節的全過程介紹完畢,下面六個附表是在AudioPolicyManagerBase中預置的六個音量曲線db分布表
音量刻度 | 1 | 33 | 66 | 100 |
輸出衰減量db | -49.5 | -33.5 | -17.0 | 0.0 |
表1-1 default volume curve
音量刻度 | 1 | 20 | 60 | 100 |
輸出衰減量db | -58.0 | -40.0 | -17.0 | 0.0 |
表1-2 default media volume curve
音量刻度 | 1 | 20 | 60 | 100 |
輸出衰減量db | -56.0 | -34.0 | -11.0 | 0.0 |
表1-3 speaker media volume curve
音量刻度 | 1 | 33 | 66 | 100 |
輸出衰減量db | -29.7 | -20.1 | -10.2 | 0.0 |
表1-4 speaker sonification volume curve
音量刻度 | 1 | 33 | 66 | 100 |
輸出衰減量db | -24.0 | -18.0 | -12.0 | -6.0 |
表1-5 default system volume curve
音量刻度 | 1 | 33 | 66 | 100 |
輸出衰減量db | -30.0 | -26.0 | -22.0 | -18.0 |
表1-6 headset system volume curve
總結:通過調節音量鍵這一調節音量的場景,從java層,media本地層,AudioFlinger服務層,硬件抽象層等四層分析音量調節函數是如何完成一個音量調節任務的。總結了從線性UI的Index如何轉化為對數關系的人耳的聽覺轉換的公式,以及預置的區間表,根據不同的硬件,我們可以自己預置適當的區間表,使得音量曲線更符合我們的聽感。