Android Audio System線性音量和對數音量的轉換

來源:互聯網
上載者:User

Android的音頻系統的代碼中,應用程式對每個音頻流的音量做出調整後,最終會轉換為一個係數K,所有的音頻資料在輸出到硬體之前,都要乘以係數K,只要應用程式發出調整音量的調用,中介層的Audio System就會重新計算係數K的值。對應用程式來說,音量控制通常都是按照線性進行調整的,比如對於具有15級音量的音頻流來說,我們預期每級的音量變化都是相當的,也就是說:從第5級調到第6級,和從第7級調到第8級,我們期望人耳可以感覺到同樣大小的音量變化。但是,在Android的代碼中,我們看到了計算係數K的公式,它相當奇怪,代碼位於frameworks/base/media/libmedia/audiosystem.cpp中:

/*****************************************************************************************************/
聲明:本博內容均由http://blog.csdn.net/droidphone原創,轉載請註明出處,謝謝!
/*****************************************************************************************************/

// convert volume steps to natural log scale// change this value to change volume scalingstatic const float dBPerStep = 0.50f;// shouldn't need to touch thesestatic const float dBConvert = -dBPerStep * 2.302585093f / 20.0f;static const float dBConvertInverse = 1.0f / dBConvert;float AudioSystem::linearToLog(int volume){    // float v = volume ? exp(float(100 - volume) * dBConvert) : 0;    // LOGD("linearToLog(%d)=%f", volume, v);    // return v;    return volume ? exp(float(100 - volume) * dBConvert) : 0;}int AudioSystem::logToLinear(float volume){    // int v = volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0;    // LOGD("logTolinear(%d)=%f", v, volume);    // return v;    return volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0;}

要理解上面代碼中的公式,我們先要瞭解人耳的聲心理學模型。根據人耳的聲心理學的研究,人耳對聲音大小的感知程度並不是線性,而是呈對數關係。對數形式的單位是dB,在音頻領域,通常我們會定義一個標準電平V0,那麼電平X的轉換公式是:

dB=20log(X/V0);

例如:我們給喇叭輸出滿負荷最大音量時的電平是1V,如果有15級音量,如果按線性進行調整,1/15 = 66.6mV,我們就得到每級音量的調整量是:

66.6mV,133.2mV,200mV,......,866.8mV,933.4mV,1000mV;

如果按照這個步長進行調整,人耳感覺到的音量變化就不是連續的。

另一種方式是按對數進行調整,在數字音頻領域,通常0dB代表最大音量,0dB意味著不對資料進行任何的變換處理,輸出等於輸入,所以20log(V0/V0)=20log(1)=0dB。這意味著最大音量以下的dB值為一個負數,現在我們把1V認為是0dB,最低音量是-28dB,那麼對應15級音量的dB值就是:

-28dB,-26dB,-24dB,......,-4dB,-2dB,0dB;

對應的電平值是( 使用公式Vx=10^(dB/20)*V0 ):

39mV,50mV,63mV,......,630mV,794mV,1000mV;

                                      線性音量和對數音量的調整曲線

回到Android的代碼中,它也使用了對數的調節方式,它先是定義了每次調節音量的步長值為0.5dB:

static const float dBPerStep = 0.50f;

然後他定義了一個計算用的中間常數:

static const float dBConvert = -dBPerStep * 2.302585093f / 20.0f;

這個一開始有點難於理解,尤其是奇怪的係數:2.302585093。所有這些定義都是為了得到用於與音頻資料相乘的係數K,Android中有多種音頻流,每種音頻流的預設音量大小步數都不一樣,有的是7步,有的是5步,有的是15步,為了便於計算的統一,計算前都會先把相應的步數映射為0-100步之間,因為步長已經定義為0.5dB,所以各級音量對應的dB數如下:

音量層級 0 1 2 3 ...... 97 98 99 100
dB數 mute -49.5dB -49dB -48.5dB ...... -1.5dB 1.0dB 0.5dB 0dB

很顯然,知道了音量為哪個步數層級後,相應的dB值也會知道,那麼我們要做的就是把dB值轉換為係數K值,K值實際上就是公式dB=20log(X/V0)中的比值:X/V0,根據此公式反推,音量層級為volume對應的K值:

(1)          dB = -dBPerStep * ( 100 - volume );

又因為:

(2)          dB/20 = log(Vx/V0) = log(K);

把(1)式代入(2)式:

(3)          -dBPerStep * ( 100 - volume ) / 20 = log(K);

為了得到K,兩邊取以10為底的指數:

(4)           10 ^ ( -dBPerStep * ( 100 - volume ) / 20 ) = 10 ^ ( log(K) );

(5)            K = 10 ^ ( log(K) ) = 10 ^ ( -dBPerStep * ( 100 - volume ) / 20 ) ;

(6)             K = 10 ^ ( dBConvert * ( 100 - volume ) ) ;      // 令:dBConvert = -dBPerStep  / 20;

使用(6)式即可得到係數K,需要計算以10為底的冪,可是這與Android使用的計算公式有些差異,Andrioid使用的公式是:

(7)            exp(float(100 - volume) * dBConvert);

這是因為它沒有使用以10為底的冪運算,而是使用以自然常數e為底的冪運算,因為:

(8)           ln( 10)  = 2.302585093;

我們把dBConvert 重新定義為-dBPerStep * 2.302585093/ 20後,式子(6)和式子(7)實際上是完全等價的。也就是說:

(9)           e^2.302585093 = e^ln(10) = 10;

這下終於知道2.302585093這個奇怪數位來曆啦!!從代碼的注釋中,我們可以知道,只要改變dBPerStep的大小,就可以決定系統的最小音量了:

最小音量 = -99 * dBPerStep;預設情況下是-49.5dB,K值為:0.00334965439;

至於最大軟體數字音量,就是0dB,不能改變,要改就修改底層的音頻驅動的硬體音量吧!!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.