什么算法可以用于音频音量级别?

32
假设我有一个可以在0到1之间滑动的滑块。 SoundTransform.volume 也在0(静音)和1(最大音量)之间变化,但是如果我使用线性函数,比如SoundTransform.volume = slider.volume ,结果并不令人满意 - 感受是在滑块的较低一半音量急剧变化,而在上半部分几乎没有变化。我真的没有研究过人类耳朵,但我曾经听说过人类感知是对数的,或者类似的东西。我应该使用什么算法来设置 SoundTransform.volume

感觉是音量在滑块的下半部分急剧变化,而在上半部分几乎没有任何变化。 - evilpenguin
8个回答

31

总体而言,人类感知是对数的,当涉及到亮度等事物时也是如此。这使我们能够察觉到环境中微小的“输入信号”变化,或者换句话说:始终感知可感知物理量的变化与其值的关系...

因此,您应该将音量按指数增长进行修改,就像这样:

y = (Math.exp(x)-1)/(Math.E-1)

你也可以尝试其他进制:

y = (Math.pow(base,x)-1)/(base-1)

base值越大,效果越强,体积开始增长的速度越慢,在最后增长的速度越快...

一个稍微简单的方法,可以给你相似的结果(因为你只在0和1之间,所以近似相当简单),就是对原始值进行指数运算,如下:

y = Math.pow(x, exp);

exp大于1时,其效果是输出(例如在您的情况下是音量)开始变化较慢,然后越来越快,这与指数函数非常相似... exp越大,效果就越强烈...


9

人类听觉是对数的,所以您需要一个指数函数(反函数)来应用于滑块的线性输出。我不知道人类听觉更接近于ln还是log

对于Ln:

e^x

日志记录:

10^x

你也可以尝试使用其他进制。然后,您需要缩放输出,使其覆盖可用值的范围。

更新

经过一些研究,似乎基数2是合适的,因为幂与压力的平方有关。如果有人知道更好的方法,请纠正我。

我认为你想要的是:

v' = 2^v.a^v - 1
a  = ( 2^(log2(m+1)/n) )/2

v代表您的线性输入值,范围在0..n之间 v'代表您的对数值,范围在0..m之间

第一个方程中的-1是为了给您一个从0开始而不是1的输出范围(因为k^0=1)。

m+1是为了补偿这个,所以您得到的是0..m而不是0..m+1

当然,您可以根据需要进行调整。


1
e^x和10^x本质上具有相同的缩放方式。实际上它们只是一个常量因子的区别。试着玩一下缩放因子,忽略基数。 - ReaperUnreal
3
它们不是一个恒定的因子:e^x = (e/10)^x . 10^x。 - Draemon
将正方形中的2带入似乎对我来说非常有趣,但因果关系似乎有点薄弱...这意味着当一个音源以恒定音量、线性速度向你移动时,你所感知到的变化也是线性的...我个人认为,我并不会像那样感知它,但这可能是多普勒效应造成的...此外,在自然界中很少有恒定音量的声源,所以我不明白为什么进化会选择这个特定的指数...但谁知道呢^^ - back2dos
@ReaperUnreal:你在想对数:ln(x) = log(x)/log(e) - BlueRaja - Danny Pflughoeft

5
听觉很复杂,感知的响度会根据频率、样本持续时间和个人而异。因此,这不能通过数学方法解决,而是通过尝试各种控制函数并选择“感觉”最佳的函数来解决。
你是否发现目前在范围的低端变化控制对听感音量影响较小,但在范围的上端音量迅速增加?或者你听到相反的情况,低端的音量变化过快,高端不够?或者你想在中等水平上更精细地控制音量?
增加低音量灵敏度:
SoundTransform.volume = Math.sin(x * Math.PI / 2);

提高高容量灵敏度:

SoundTransform.volume = (Math.pow(base,x) - 1)/(base-1);

或者

SoundTransform.volume = Math.pow(x, base);

当基数>1时,请尝试不同的值并观察其效果。或者更激进地说,可以使用90度的圆弧:

SoundTransform.volume = 1 - Math.sqrt(1-(x * x));

其中x是slider.volume,取值范围在0到1之间。

请务必告诉我们你的进展情况!


2

Android的音频框架已经可以通过使用分贝来调节音量,用户可以设置从1到7的铃声音量或从1到15的音乐音量。

音量计算公式如下:

用户通过线性调用音量设置API,但是获得的振幅是指数级别的。如下图所示:

enter image description here


2

1

3db的增加意味着你将音量加倍,但人耳需要大约6db的增加才能感知到音量的加倍。

然而,严格的对数曲线虽然准确地模拟了人类对音量的感知,但存在一个可用性问题。

当人们想要大声的音量时,旋钮在上端变得过于敏感,使得很难找到“合适”的音量。

你可能以前遇到过这个问题……7太软,8太响,而1-3在背景噪音中听不见。

因此,我建议使用对数刻度,但在低端设置底限,在顶部设置软膝以允许更线性的响应,特别是在旋钮的“响亮”部分。

哦,还要确保旋钮可以调到11。 ;)


这是一个众所周知的原则吗?它有名称或标准吗?我知道线性刻度是错误/常见的错误(安静音量太敏感),而对数/指数刻度存在您描述的问题(响亮音量过于敏感)。苹果耳机插孔和其他成熟的电子产品使用介于两者之间的某种方法(例如,大声说话每2dB一步,小声说话每3dB一步)。 - aandroyd

0
这是一个用于计算分贝对数刻度的JavaScript函数。输入为百分比(0.00至1.00)和最大值(我的实现使用12db)。
中点设置为0.5,相当于0db。
当百分比为零时,输出为负无穷大。
function percentageToDb(p, max) {
     return max * (1 - (Math.log(p) / Math.log(0.5)));
};

0
人类的耳朵确实以递增的对数尺度感知声音,因此,通常用于测量声学强度的单位是分贝(实际上用于各种强度和功率,而不仅仅是声音,并且也恰好是一个无量纲单位)。参考电平0 dB通常设置为人类听力的下限,每增加10分贝相当于功率增加了10倍。
但是,请注意,您应该先与其他人核实并查看他们的想法,以防万一;对您来说听起来奇怪的可能并不奇怪。如果他们同意您的看法,那么就按指数进行操作,但如果您处于少数派,则可能只是您自己的耳朵有问题。
编辑:请忽略我之前的第三段。如果您决定按指数进行操作,请参考back2dos的答案。

有许多类型的函数,其中f(0) = 0且f(1) = 1。 - Matt Howells
确实,如果我当时真的计算出来了(使用f(x) = a * 10^x - b,其中f(0) = 0且f(1) = 1),我就会意识到这一点。相反,我太粗心了,甚至没有尝试计算它。 - JAB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接