PCM音量控制(高級篇)
http://blog.jianchihu.net/pcm-volume-control.html
去年寫過一篇文章,有關PCM的音量控制:http://blog.jianchihu.net/pcm-volume-control.html。那時闡述了一些概念,對一些細節沒有詳細描述。因為有人問到使用對數關系調節音量時,增益系數如何確定,故開此篇文章。
聲學中的分貝
因為人耳的特性,我們對聲音的大小感知呈對數關系。所以我們通常用分貝描述聲音大小,分貝(decibel)是量度兩個相同單位之數量比例的單位,主要用於度量聲音強度,常用dB表示。聲學中,聲音的強度定義為聲壓。計算分貝值時采用20微帕斯卡為參考值(通常被認為是人類的最少聽覺響應值,大約是3米以外飛行的蚊子聲音)。這一參考值是人類對聲音能夠感知的閾值下限。聲壓是場量,因此使用聲壓計算分貝時使用下述版本的公式:
其中的pref是標準參考聲壓值20微帕。
分貝聲音變化範圍
在編程中,我們可以用以下公式計算兩個聲音之間的動態範圍,單位為分貝:
1 | dB = 20 * log(A1 / A2) |
其中 A1 和 A2 是兩個聲音的振幅,在程序中表示每個聲音樣本的大小。聲音采樣大小(也就是量化深度)為1bit時,動態範圍為0,因為只可能有一個振幅。采樣大小為8bit也就是一個字節時,最大振幅是最小振幅的 256 倍。因此,動態範圍是 48 分貝,計算公式如下:
dB = 20 * log(256)
48 分貝的動態範圍大約是一個安靜房間和一臺運行著電動割草機之間的區別。如果將聲音采樣大小增加一倍到16bit,產生的動態範圍則為 96 分貝,計算公式如下:
dB = 20 * log(65536)
這非常接近聽力最低閾值和產生痛感之間的區別,這個範圍被認為非常適合還原音樂。
了解了分貝的相關概念我們通過圖表說下為什麽要用對數關系描述聲音大小。
1)音量滑塊與聲音增幅大小線性變化。
上述左圖中,音量滑塊位置與聲音振幅為線性增長關系,右圖是我們人耳感受的音量大小與滑塊位置關系。可知,在左側移動相同距離的滑塊,感知到的聲音變化範圍很大,在右側接近聲音最大值移動相同距離滑塊,感知到的聲音大小變化就很小了。
2)音量滑塊與聲音振幅大小對數關系變化。
左圖中,音量滑塊位置與聲音振幅對數關系增長。右圖中無論哪個位置,移動相同距離滑塊,感知到的聲音變化都是相同的。
需要說明的是滑塊最小位置只是接近0,不能為0,因為對數函數y=logx中x>0。
windows系統中音量滑塊控制的聲音變化範圍
在最新版的windows系統中,音量滑塊控制的聲音變化範圍也是96分貝。如下表所示,是不同版本windows的音量範圍以及默認音量值。
從表中我們可以看到默認值都是0分貝,根據分貝公式:dB = 20 * log(A1 / A2),當A1,A2相等時,db為0。
程序實現
了解了分貝以及windows中音量滑塊是在哪個範圍變化,我們的程序實現起來也很簡單。
這裏我們規定音量大小變化範圍也是96分貝,每個聲音采樣大小為16位。對於分貝公式:dB = 20 * log(A1 / A2),我們取參考聲音振幅A2為原始聲音振幅,A1為調節後的聲音振幅大小。可知調節後的聲音:
1 | A1 = A2 * pow(10 , db/20) |
看過一篇文章說理想的聲音調節步長最好是2db,對於96db範圍,我們按2db步長進行分割,可以分成48份,這樣我們得到的聲音變化為[-96db,-94db,-92db,…-4db.-2db,0db],假設我們要調節一半音量大小,也就是-48db,由上述公式可知:調節後音量A1大小:
1 | A1 = A2 * pow(10 , -48/20) |
程序偽代碼如下,具體db大小與滑塊位置對應關系的實現這裏就不寫出:
1 2 3 4 5 6 7 8 9 10 11 12 13 | int16_t pcm[1024] = read in some pcm data; int32_t pcmval; float multiplier = pow(10,db/20); for (ctr = 0; ctr < 1024; ctr++) { pcmval = pcm[ctr] * multiplier; if (pcmval < 32767 && pcmval > -32768) { pcm[ctr] = pcmval } else if (pcmval > 32767) { pcm[ctr] = 32767; } else if (pcmval < -32768) { pcm[ctr] = -32768; } } |
PCM音量控制(高級篇)