将线性音频分布转换为 logarithmic/perceptual 分布?

Convert linear audio frequency distribution to logarithmic/perceptual distribution?

我正在为脚本创建一些音频提示,并将 运行 保持在相同的问题中:

人类的感知是对数的。如果我想将一系列声音分成 10 个相等的部分,我不能只从最高音调减去最低音调然后除以 10。这些步骤在感知上是不准确的。

那么,在处理输出范围为 20-20000 (kHz) 的函数时,是否有一个基本的数学函数可以合理地近似将这些数字转换为他们的感知等价物——同时仍在 20-20000 范围内映射?

示例:

我正在处理以 kHz 为范围的 "beep" 命令。

我从我自己的系统测试中知道,这个命令在被测系统上的功能范围是 38 到 9900Hz。

因此,如果我要创建一个音频倒计时以通过 100 个项目,我会将 9900-38 / 100 划分为每个项目的 98.62Hz 范围。如果我要创建一个音频倒计时,我会简单地创建一个从最大范围开始的蜂鸣声序列,每次蜂鸣声减少 98.62Hz。

但这听起来并不准确。大多数时间都花在高音频率上。没有足够的时间花在低音上。

我确实设法为我的白噪声发生器修复了这个问题,但我无法对音频倒计时应用同样类型的修复。 (主要是因为白噪声发生器有很大的出错余地。)在那种情况下,我只是简单地将赫兹除以 10。但我这样做是随机的。它给了我想要的效果,但没有任何一种一致的映射函数:

        if  %@RANDOM[1,10] gt 6 (set BEEP_FREQUENCY_TEMP=%@FLOOR[%@EVAL[BEEP_FREQUENCY_TEMP / 10]] 
        if  %@RANDOM[1,10] gt 9 (set BEEP_FREQUENCY_TEMP=%@FLOOR[%@EVAL[BEEP_FREQUENCY_TEMP / 10]] 
        if  %@RANDOM[1,10] gt 8 (set BEEP_FREQUENCY_TEMP=%@FLOOR[%@EVAL[BEEP_FREQUENCY_TEMP / 10]]

你能看出我什至不知道怎么问我的问题吗?

您需要使用对数间距:

// start - start frequency
// stop - stop frequency
// n - the point which you wish to compute (zero based)
// N - the number of points over which to divide the frequency
// range.
double logspace(double start, double stop, int n, int N)
{
    return start * pow(stop/start, n/(double)(N-1));
}

例如:

logspace(20, 200000, 0, 4) = 20
logspace(20, 200000, 1, 4) = 200
logspace(20, 200000, 2, 4) = 2000
logspace(20, 200000, 3, 4) = 20000

请注意 logspace(x,y,0,N) 可能不完全等于 x 并且 logspace(x,y,N-1,N) 可能不完全等于 y。这是由于浮点精度和舍入误差。

如您所知,从一个音调到高一个八度的音调会使它的频率加倍。从那里再增加一个八度音阶。所以你得到的是指数以二为底的指数线性增加(每八度增加 1,或每半音增加 1/12):

f = 440Hz · 2x

现在将可能的 x 值范围划分为规则的区间,您应该完成了。要将频率 f 转换为音调 x,您需要计算一个对数,即

x = log2(f / 440Hz) = ln(f / 440Hz)/ln(2)

我最后查找了一个包含所有与音符匹配的频率的列表,制作了一个包含所有这些频率的文本文件,然后以我想要的任何间隔简单地迭代它。例如,如果我想要一个指示 10% 上传进度的注释,它会获取总行数,并抓取文件中 10% 的行。这解决了使我的音频倒计时 声音 线性的问题,即使这样做所需的频率不是线性的。

查找table。

不优雅,但有效。