——副标题:给音频软件硬件工程师的提示

原文:Programming Volume Controls - dr-lex.be

因为很多程序员缺乏对人类听觉系统的常识,或者只是因为太懒了,很多音频软件都存在一个很烦的问题。当你使用这些音频软件的音量控制的时候,会觉得痛不欲生。如果你有机会参与到音频软件的开发中去,那么请仔细阅读下面这些文本,把它提到的知识烧进自己的脑子里去,甚至跟身边的人奔走相告!

太长不看

  • 音量推子是非线性的

线性的音量控制器真的是烦死人了!因为人类对响度的感知是对数的!这也是为什么所有的音频设备都用dB来标示它的音量控制或者增益控制。

对于一个相对的振幅

来说(即
,其中
是当前值,
是参考值),它对应的分贝值是
。正的分贝值表示放大,而负的分贝值表示衰减。

将振幅乘以特定的数值,那么对应的分贝值则增加了一定的数值。

此外,dB(A)经常被用来衡量人类感知到的绝对响度。人类平均可以感知到的最小的声响,被标记为0dB(A),一个“安静”的房间大概会有±30dB(A)的声响。

  • 一个音量控制器不应该是基于百分比的

因为百分比即是线性的。

但是如果这个百分比表示的是dB值的百分比,那就还可以接收。比如0%表示-60dB而100%表示0dB。

  • 一个理想的推子应该符合指数曲线

这条曲线的公式为

对于这条曲线来说,它的最小值代表“最安静”(对消费级产品来说是30dB(A)),而其最大值对应了该音频产品的最大响度。

有个问题是,你在很多情况下只能去猜测用户使用的是什么产品。所以,除非你是在做一个有明确的标准的高端产品,其实你是可以做一些猜测或者近似的。

一个很好用的估计值是用户拥有60dB的可调整范围。

  • 表1给出了一些参数a和b的经验值

表1中给出了在不同响度范围下,参数

可设置的经验值供参考。

其中,x是滑块的位置,其取值范围为

则是作用于波形上的系数,对波形的振幅进行缩放。

大概率上,你会使用的是60dB范围上的参数。

如果你想要让推子推到最小的时候,得到完全不输出信号,那么你可以在接近0的地方给函数设置一个线性的滚降(roll-off)

表1:方程中a和b的值
  • 不连续的控制器

如果你的音量控制器是离散的,比如说,用鼠标点击按钮、或者鼠标滚轮来控制,那么最好确保每两“格”音量之间的区别在1~3dB之间。

小于1dB的音量变化难以被察觉到,而3dB以上的变化则太过于粗糙了。2dB是一个蛮不错的选择。

  • 近似曲线

如果出于某些原因,你不想做指数运算,那你可以用一条所需的运算量低得多的幂次曲线来代替。

对于典型的60dB范围,这是一条4次曲线。换句话说,让波形振幅乘以系数

表1中也给出了不同动态范围下适用的近似曲线。虽然这些曲线不如对数曲线那么完美,但是再怎么说都好过你直接塞个线性的音量推子进去!

本文比较长,正文的前半部分都在讲关于分贝的基础知识。如果你只想看关于音量推子的部分,可以跳过前半部分,从“寻找理想的曲线”一节开始阅读。

为什么万恶的线性的音量控制器是邪恶的

当今的大部分音频软件都会带有控制音量的推子或者旋钮,其目的是模仿传统的音频硬件。不幸的是,大部分的这些软件控制器上都会有一点让人屁眼疼。那就是它们是线!性!的!

你也许会问:一个线性的推子犯了什么错?一端是0,一端是100,中间呈线性增长,不是很棒吗?答案是不不不不不。

你可以这样子试试看:打开一个音频软件,播一首你喜欢的曲子。把音量旋钮打到最大,然后稍稍搓动它。接着,把音量调到最小,然后稍稍搓动它。

如果这时候你听到,在音量最大的那一端基本没什么音量变化,而在最小的那一端疯狂变化。那么这个控制器十有八九就是线性的控制器了![1]

(如果你身边的播放器都做了正确的事情,你可以看看译者补充的这个视频感受下区别。)

https://www.zhihu.com/video/1227903653497257984

这种邪恶甚至已经渗透到一些硬件上了!Velleman卖过一款可以拆卸组装的图形均衡器K4302,在我1995年买到的版本上,搭载的推子就是线性的,也不知道现在修正了没有。
甚至iMac G3上的音量控制器也是线性的。
恐怕这些只是众多使用了线性推子的硬件中的一角。

除了上面提到的,线性的音量控制器还会导致这些症状:

  • 如果你从最小的音量网上推,只要推一点点就已经太大声了
  • 从差不多中间开始,到最大音量,几乎没什么变化
  • 如果一个高灵敏度的耳机和低输出的音响接到同一台电脑上,基本不可能对耳机进行微调。因为要拖动很大很大的距离,才能让音响出现音量的改变

种种这些问题,就会让用户觉得你的产品难用,但是又找不出为什么,最后只能烦到大骂:

这些音量推子……简直就像一个个秤砣!

问题出在哪里?

所以线性的音量推子到底怎么了?其实是因为我们对声音的感知是对数的。

横轴:振幅;纵轴:感知响度

也就是说,相同的振幅变化,在振幅本身就小的时候,我们感知到的变化程度要比振幅本身比较大的时候更大。
这种特性让我们能既能听到振幅很小的声音,也能听到振幅超大的声音。或者说,我们的听觉覆盖了很大的动态范围。

而放到音量控制上,线性的推子给出的音量变化,在我们听来是呈对数变化的,所以会觉得怪怪的。上图就是一条对数曲线。横轴上标记了两个完全一样长度的区域(也即线性的音量推子)。而纵轴则显示了听觉上的音量变化。在音量较小的那段,变化的程度要比较大的那端大。

为了得到一个“真正的”音量推子,我们只需要让推子的输出是指数的。因为

,这样一来,主观感知上的音量变化就是线性的了,而这才是我们想要的。

在下文中,我假设音量推子和整个音频系统,都工作在

的值域上。且用
来表示音量推子的位置,
来表示于波形数据相乘的系数。

寻找理想的曲线

指数曲线有两个很烦的属性:

  • 一是只有自变量是负无穷时,它的值才是0

不过这个问题不大,因为我们的耳朵并没有拥有无限的敏感度。我们只需要知道一些实用的动态范围就行了。这部分在下面再展开讲。

  • 二是指数函数的一般形式

    的图像,即使确定了两个点之后,还是无法定下具体的三个参数。

所以在音量控制的场景中,我们省去

这个参数,因为耳朵无法感知到直流偏移(译者注:感觉这个解释不是很好,加上
应该是整体的音量都提升或衰减,是可以被感知的)。这样一来,就可以通过两个点来确定一条具体的曲线了。

我们要得到的曲线,一定会经过

这个点。于是可以得到

剩下的问题就是,确定控制曲线形状的

值。更小的值生成的曲线更加陡峭,而更大的值生成的曲线则更加平缓。

你也许会尝试把第二个点选择为

,但是不应该是这样的。就像刚刚说的,指数函数在自变量是0的时候可不是0。不过这也不是大问题,因为我们耳朵的对数响应曲线,会在一个非零响度的地方达到0,也即听阈

(The Hearing Threshold)

一般的环境中,都存在一定的背景噪声。如果一个声音的响度低于这个背景噪声的响度,就可以是不可闻的了。最大的问题是,即使不同人的听阈大致上是一致的,但是不同的音响系统在相同的输入信号下,输出的响度却和大量的参数相关。

为了得到合适的

值,我们需要更多的信息。如果我们想要让用户在调节音量时的感觉完全是线性的,我们就需要知道一个音频系统在最大音量时的响度是多少。但是很显然,这个问题没有什么实用性。除非你是给特定的硬件系统开发软件,否则这个问题很简单,就是没有确切的答案。

所以我们就需要做一些假设。这里插一个题外话,就是“响度”是如何被测量的。

声强的测量

因为人类的听系统具有对数的响应曲线,人类定义了一个特殊的单位,以Graham Bell的名字命名的“贝尔(Bel)”,来衡量声音的大小。但是,贝尔的跨度太大了,所以我们经常接触到的单位,是贝尔缩小十倍得到的“分贝”,用dB来表示。1贝尔=10分贝。使用dB作为单位时,可以以绝对标度或者相对标度来计算。

当使用绝对标度时,我们得到的是测量的声音对于平均人群来说有多大声,用声压级(Sound Pressure Level, SPL)表示。这个标度有几种不同的变体,最常见的是dB(A)。

要使用dB(A)来度量一个声音,首先这个声音要通过一个关于平均人群的频率响应曲线的滤波器。接着,经过一个以10为低的对数变化,最终再乘以10. 这里不会讲到再深入的细节去了,因为这些在这篇文章里已经够用了。

你应该知道的一点是,0dB(A)代表了平均人群能感知到的最小响度,也即听阈。而在一个安静的环境里,通常来说都会有大约30dB(A)的背景噪声。处在一个0dB(A)的环境中,其实反而是一种很奇怪的体验。
人类所能听到的最大响度,大概是120dB(A)。一支传统的管弦乐团演奏音乐时的响度大约时94dB(A)。

需要注意的是,由于dB的对数运算,将一个声音的功率放大10倍,其实相当于增加了10dB(A).

在几乎所有的物理度量中,都会使用到相对标度。相对标度可以表达两个不同的信号或物理量之间的差距。
相对标度的符号就是简单的dB。其计算方法与你要计算的物理量是振幅还是功率有关。

对于功率值来说,其公式为

代表相对的功率(即两个功率的比值)

对于振幅来说,其公式为

产生这种区别的原因是,功率正比于振幅的平方。在对数运算中,平方可以提取到对数运算符的外面,变成系数2.

理论上来说,绝对标度和相对标度不能真正地互换。当我们对一个90dB(A)的声音衰减20dB,我们其实无法保证得到的声音就是精确的70dB。(译者不太理解这一点)

寻找理想的曲线(第二部分)

在我们学习了这么多关于分贝的度量的知识之后,我们可以继续刚刚寻找两个参数的问题了。我们要确保这条曲线能给人们带来接近线性的主观感受。

我们先不用考虑低于30dB(A)的部分,因为在大部分环境里就已经有这么大的背景噪声了。所以我们可以将30dB(A)作为其阈值。

然后我们继续假设用户的设备最大可以产生90dB(A)的声音。这其实已经是一个蛮大的音量了,大部分人都不会想要让自己长时间暴露在大于90dB(A)的环境中的。也许手机、电脑、平板的内置喇叭都达不到这个水平,但是耳机耳塞和Hi-Fi, PA等音箱系统是可以的。

现在我们确定下我们的曲线中的两个点了,也就是

。常用的音量推子控制的都是衰减的值。如果我们把这条曲线放到相对坐标轴里,得到的点就是
了。为了让计算更加直观,可以给它添加一个60dB的偏移,得到
两个点。从振幅上看,60dB是0dB的
倍。

由于

,
,那么a的值就是简单的
了。

现在我们得到的曲线,应该具有不错的实用性,而且在大多数情况下都能令人满意了。

理论上讲,推子推到最小的位置应该对应到30dB(A),也就是会被环境噪声遮掩掉的水平。尽管没有必要强制让这个点的输出是0,但是实际上我们还是希望它对应到0。

因为人们都会期望当音量调到最小时,声音设备不再有输出,并且我们前面做的估计工作也不可能完全准确。一个最简单的解决方法就是加一个判断条件

if(x==0) ampl=0;

如果需要更平滑地过渡到0,也可以这样子:

if(x < 0.1) ampl *= x*10;

表1给出了按照前面所讲的思路设计的,不同动态范围下理想曲线的

的值。(这里的动态范围就是最大响度和背景噪声的差值)

你可以在你的代码里直接使用这个指数方程。如果你不知道用户的设备所能达到的最大响度是多少,就猜一猜吧。上面说到的30到90,就是一个不错的猜测。

不过这种猜测永远也不可能准确,因为dB(A)也与播放的声音的种类有关。

即使我们算出来的曲线和实际要求有一定的偏差,也比一条愚蠢的线性曲线好不知道多少倍。更别说是用了刚刚说到的平滑的滚降之后的效果了。

寻找不是特别理想但是仍然有不错的效果的曲线

有些程序员可不想就为了你一个音量腿子,调用一整个数学库!所以,我们可以折衷一下,找到一条接近指数曲线,但是又不至于那么麻烦的曲线。

下图绘制出了三条曲线:线性曲线(嫌弃,略略略)、60dB指数曲线(红)和

(蓝)。

就像你的眼睛看到的,蓝色的曲线跟红色的曲线还蛮接近的,而且你还可以看到线性的曲线错得多么离谱。

四次幂曲线的方程,每次运算只需要做三次乘法(如果你愿意多写一行代码,只做两次乘法也可),而且它经过零点。已经很优秀了吧。

我在一些设备上尝试应用了4次幂曲线,它给人的感觉十分自然,所以我强烈向你安利它。你也许也会觉得5次幂曲线更好,这取决于你自己了。

如果最大音量没有那么大,那你可以用一条不那么弯的曲线

,或者如果你的动态范围更大,你也可以选择比
更弯的曲线。比如对于90dB动态范围的系统来说,
才是比较好的近似。不过需要用到这么弯的曲线的系统其实不多。

表1的最后一列给出了各个动态范围下的曲线近似方程。你也可以在下面的图表里看看这些曲线的近似情况(下方的表格以dB作为纵坐标的单位)。

在推子比较低的位置上,由于幂次曲线降低到0(负无穷分贝)的速度要比指数曲线快很多,所以这部分的近似比较不理想。放到dB坐标中来看这个差距还是蛮大的。不过看看线性曲线的表现,就会觉得这点代价还是可以接受的了。

7次幂曲线在高达120dB动态范围的情况下保持了不错的近似,不过大部分情况下你都不应该制造可以发出100dB(A)声响的设备,免得收到因为使用你的设备而损伤了听力的诉讼。

后记

人类能够分辨出的最小的音量区别,大概是1dB,或者说10%。如果你想要做一个离散的音量控制器,比如通过点击“+-”符号来调整音量的控制器,那就最好让每次点击调整都能被感觉到。

但是你也最好不要让每次点击的变化太大,如果你的控制器跨度太大了,用户体验也不好。

一个比较推荐的经验值每次调整2dB,且最多不要超过3dB。

在GNOME一个版本的音量调整中,步长居然达到了5dB。整个网站都在抱怨这个事情。

我有时候会收到别人的邮件,问我应该怎么设置一个被设计成使用dB作为单位的音量控制器。有人依然觉得它们应该对它进行非线性变换。不不不!这种情况下你只需要设定你需要的范围和步长就行了!

举个例子,有些音量控制器给你提供了120dB的动态范围,但是如果用不上那么多,你可以把它限制到上半部分的60dB就好了。有些地方可能会同时提供衰减和增益,至于具体怎么用,你就自己多加注意啦!

有些人没有把这些对数啦,感知啦理解好,有可能会出现一些奇奇怪怪的想法。

比如说:“一个98dB(A)的声音太吵了!如果我把它减小到95dB(A),那它就只剩下原来一半的能量,只有一半吵了!”

又对又错。能量确实是只剩下一半了(振幅衰减至

倍),但是主观感知只减少了3dB,也就是只比能感受到的变化多那么一点点。因为这就是分贝这个单位本身被提出来的意义呀!在这种情况下,功率对半减少了,对听力的损伤可没有减少多少。

上面讲的这么多,不只是适用于推子,也适用于旋钮(虽然在软件里很少见,但是大部分的硬件都用的电位器。这些电位器也都具有指数的响应曲线)和各种情况。甚至在均衡器里也是这样,即使它们的每个推子都只控制频谱中的一部分频率成分。

除了在要求极高的情况下,其实音量控制并不总是十分精确的。总之,这篇文章的中心就是,音量控制器应该做成指数响应的,或者,至少让它的响应曲线长得别差太多!

关于频率控制和频率分析

这部分是小得多的问题了,因为大部分应用都不需要在用户端处理频率。

人类对音高的感知也不是线性的,所以如果你需要对频率进行操作,也不要用线性的曲线!你必不想听到一台按照线性尺度进行调音的钢琴!

这不止在声音合成的领域有关,如果你要做声音信号分析,也会用到这些知识。如果你想要做一个频谱分析仪,如果没有特殊要求,那么它的频率轴就应该是对数的。如果你把它做成线性的,那么大部分低频都会被挤压到小小的一块空间里,而高频成分会在一大块空间里分散开来。

即使我们听觉的频率范围,上限达到了20kHz,但是大概2kHz的位置开始,我们就会觉得音高已经很高了。音乐中主要的乐器都集中在2kHz以下。而语音信号中,超过4kHz的成分已经不多了(电话就会用一个滤波器滤去这以上的频率成分)。而这些高频成分,如果在线性坐标下,会占掉近80%的空间!

不过,要生成一个对数坐标的频谱可不容易。FFT是线性的,唯一的办法就是在做完FFT后,再调整坐标轴,以对数的形式去展示数据。而这会导致低频的分辨率低得可怜,而高频成分又远超所需的分辨率。为了解决这个问题,你可以在超高的频率分辨率下来做FFT,使得低频区域获得足够的分辨率。但是这样做又会损失掉高频部分的时间分辨率。也有其他的解决办法,比如通过一系列滤波器后,对不同频率的声音分别采取不同的FFT大小。不过这样一来,各个频率成分的时间分辨率又难以统一了。

原文遵守CC BY 4.0协议

参考

  1. ^一个有意思的例子。2012年左右,BBC嵌入到新闻文章的视频播放器的音量控制器,可以达到11,然而在10调到11的时候,是完全听不出区别的。甚至从8调到11,能听出来的区别也很小。因为它是线性的

计算音频数据音量_【翻译】线性的音量推子……简直像一个个秤砣!相关推荐

  1. java调节手机音量_玩法 | 手机音量太小?这样设置声音瞬间变大!

    原标题:玩法 | 手机音量太小?这样设置声音瞬间变大! 不管是用手机看电视还是打电话,倘若手机声音太小,听起来很吃力,会让人觉得很苦恼,遇到这种情况怎么办?按照以下方法设置看看,可以让你的手机音量瞬间 ...

  2. 功放板加开关音量_一种带音量开关功能的音响功放板的制作方法

    本实用新型涉及音响功放板领域,尤其涉及一种带音量开关功能的音响功放板. 背景技术: 随着时代的变化,人们对于技术的研究飞速,人们的生活中越来越多的工具,其中也缺少不了音响功放板这些设施,在人们使用的过 ...

  3. python增大音频音量_音频数据增强及python实现

    博客作者:凌逆战 博客地址:https://www.cnblogs.com/LXP-Never/p/13404523.html 音频时域波形具有以下特征:音调,响度,质量.我们在进行数据增强时,最好只 ...

  4. mfcc中的fft操作_简化音频数据:FFT,STFT和MFCC

    mfcc中的fft操作 What we should know about sound. Sound is produced when there's an object that vibrates ...

  5. 数据丢包怎么修复_一种网络传输中实时音频数据丢包恢复的方法与流程

    本发明涉及通信技术领域,具体涉及一种网络传输中实时音频数据丢包恢复的方法. 背景技术: 随着通信技术的发展,音频传输系统对实时性和准确性的要求越来越高.在网络的音频传输过程中,影响音频音质的主要因素是 ...

  6. PCM音频数据音量大小调节

    转载:http://blog.csdn.net/timsley/article/details/50683084 PCM音频数据增大或减小的原理主要是,将采样的数据乘上一个数字或者是除以一个数字,但要 ...

  7. ffpmpeg 音量_使用ffmpeg调整音频音量

    一.前言 记得在很多年前使用MP3.MP4播放器的时候,里面的音频和视频数据都是自己从别的地方拷贝下来的,因此其中的音频音量大小标准不一致,经常会出现上一首歌需要调整很大的设备音量,而下一首同样的音量 ...

  8. 怎么计算一组数据的波动_数据分析(一):数据描述统计

    一. 数据描述统计 看了一个纪录片 - The Joy Of Stats <统计的乐趣>,这虽然是一个关于数据统计分析的纪录短片,但短片中对于数据统计在实际应用场景中应用的效果还是很值得思 ...

  9. python音频 降噪_从视频中提取音频数据,然后应用傅里叶对音频降噪(python)...

    视频准备 QQ有热键 然后随便打开一个视频网站进行录屏 我选择B站 从视频中提取音频 需要安装包moviepy pip install moviepy 提取代码 from moviepy.editor ...

最新文章

  1. WSP框架:WEB组件的原理
  2. ubuntu和python快速换源
  3. npu算力如何计算_异构计算神器来了,它能带来性能革命吗
  4. 上传jar到本地仓库
  5. Post与Get传值读取方法
  6. MySQL 5.7临时表空间
  7. GPS从入门到放弃(十五)--- DCB差分码偏差
  8. 微信小程序 测试号 申请
  9. Java泛型_自定义泛型类
  10. 注册FaceBook和购买FaceBook小白号的区别
  11. Ping命令进行网络检测
  12. linux mint19内核,aria2的Linux Mint 19安装过程完整总结
  13. 转载-GNS3安装和使用教程(超详细)
  14. Windows10插了耳机电脑还是外放
  15. 代码 比较工具 在线
  16. 概率论与数理统计-离散型随机变量基础知识
  17. 互联网公司刻板印象合集:程序员都秃头,商务个个是人精
  18. 虚拟产品哪个平台引流比较好?虚拟产品有哪些平台可以引流
  19. 联通、电信、移动、积分兑换话费
  20. LinuxC实现文件夹及其文件的拷贝

热门文章

  1. 5、Linux系统的目录结构
  2. 【Socket网络编程】3.字节序转换函数htons、htonl ,地址转换函数inet_ntoa、inet_ntop、inet_pton、inet_addr
  3. [Google Guava] 8-区间
  4. Java设计模式透析之 —— 策略(Strategy)
  5. 解析IntelliJ IDEA内部设计
  6. MIT自然语言处理第五讲:最大熵和对数线性模型
  7. 编程之美-中国象棋将帅问题方法整理
  8. Ubuntu报错:sudo 无法解析的主机
  9. Create Custom Instruments
  10. Spring容器装饰者模式应用之实现业务类与服务类自由组合的解决方式