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;
}
}
|