了解 Voss-McCartney 粉红噪声生成算法

信息处理 噪音 算法
2022-02-23 01:32:08

我正在实现Voss-McCartney粉红噪声生成算法。

如果您点击上面的链接,您可以阅读:

来自 James McCartney 1999 年 9 月 2 日 21:00:30 -0600:

频谱的高端不是那么好。我在另一篇文章中预测的 sin(x)/x 形状的级联非常明显。纹波在 Fs/8 之前只有 2dB 左右,在 Fs/5 之前只有 4dB。在 Fs/4(sin(x)/x 零点之一)处,响应下降了大约 5dB,在 Fs/2 处有一个很深的零点。(这些数字有点粗略。更多的平均会有所帮助。)

您可以通过添加与其他振幅相同的白噪声发生器来稍微改善顶部八度音阶。其中填写图表如下:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x x x x x x x x x x x x x x x x
 x   x   x   x   x   x   x   x
   x       x       x       x
       x               x
               x

那里仍然会颠簸,但空值不会那么深。

如果我理解得很好,这个算法会通过添加不同频率的随机(白色?)噪声源来生成粉红噪声1

但是,我不完全理解上面引用中对“顶行”额外白噪声发生器的解释。有人可以澄清它如何/为什么改进算法吗?这是否使它成为音频应用中产生粉红噪声的好算法?特别是,我不应该丢弃第一个样本,直到所有“行”都混合到信号中(在上面引用的 ASCII 艺术中,这意味着丢弃 15 个第一个样本)?


1我不确定这里的措辞。如果我错了,请不要犹豫纠正我

2个回答

首先,让我们将 Voss 部分与 McCartney 分开。第一个生成随机数的 1/f 分布,功率与频率成反比——粉红噪声。McCartney 提出了一个改变,它提供了一个更平坦的计算负载。

沃斯的简要概述:

从随机数生成器开始,二进制计数器中的每一位对应一个。让我们考虑一个简单的三个案例:

000
001
010
011
100
101
110
111
(and continuing with rollover back to 000)

对于每个新的输出样本,计数器都会增加 1。有三个随机数生成器,每列一个。当一列中的数字发生变化时,其对应的生成器会产生一个新的随机数。否则,生成器将保持其先前的值。

作为最后一步,对于每个循环,将所有生成器相加以产生过程的输出。

计算负载不均衡。每隔一个周期只有一个发电机变化,而每四次所有三个都变化(011 -> 100,和 111 -> 000)。实用的粉色源需要更多的生成器,所以最坏的情况会变得更糟。

McCartney 的方法对流程进行了重新设计以实现均匀负载。

额外的想法:

我相信 Voss 的目标不是生成音频速率的粉红噪声信号,而是模仿缓慢自然变化的品质的粉红序列,包括语音和音乐的响度和音高变化。

我在 John Simonton Jr. 的关于计算机/合成器的友好故事中了解了该算法,他在其中引用了 Gardner 的文章和 Voss,并列出了他归因于 Voss 的算法:五,“四面骰子”——使用一个 5 位计数器,它的每个位变化都会生成一个随机整数值 0-3。输出是一个 0-15 的值,但具有变化的质量通常很小,但不太常见的是变化可能很大——有时掷骰子。

例如,您可以将其用作音阶的索引。这样,下一个音符通常会在音阶上接近最后一个音符,但有时会随着掷骰子的增加而发生更大的跳跃变化。大约 30 年前,我将这个算法用作我的 HyperMIDI 产品(HyperCard 的 MIDI 脚本)中包含的示例。对于这种类型的使用,McCartney 的负载平衡似乎达不到目的。

我没有研究音频噪声源的质量与计算,但是对白噪声使用 3 dB/倍频程滤波器的方法效果很好,它只需要几个极点和零。

因此,让我们看看您链接到的文章的作者在下面说了什么;输出样本位于顶行,是当时所有其他行的总和。

Output  /---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\
        \___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/

Row -1  /---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\/---\
        \___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/\___/

Row 0   /--------\/--------\/--------\/--------\/--------\/--------\/--------\/--------\/--------\
        \________/\________/\________/\________/\________/\________/\________/\________/\________/

Row 1   --------------\/------------------\/------------------\/------------------\/--------------
        ______________/\__________________/\__________________/\__________________/\______________

Row 2   ------------------------\/--------------------------------------\/------------------------
        ________________________/\______________________________________/\________________________

Row 3   --------------------------------------------\/--------------------------------------------
        ____________________________________________/\____________________________________________

Row 4   ------------------------------------------------------------------------------------\/----
        ____________________________________________________________________________________/\____

这意味着上图有多个不同的白色序列,它们只是偶尔改变——让我们将其形式化。仅从最上面的两行开始:

  • 第-1行只是白噪声
  • 第 0 行是白噪声,使用 2-sample-boxcar-filter/sample-and-hold 插值因子 2。这使噪声具有(混叠)正弦形状,本质上是低通形状

第 1…N 行做同样的事情,sincs 变窄了 2 倍。

考虑这个离散的PSD:

  • 第 -1 行具有恒定的离散 PSD
  • 第 0 行为其添加了 sinc(2f)² 形幂
  • 第 1 行为其添加了 sinc(4f)² 形功率
  • 等等

总而言之,我没有证据证明这会变成完美的粉红色,它可能不在有限的观察范围内,但直觉上认为接近 0 Hz,这些 sinc²s 的所有主瓣加起来,并且随着频率每增加一倍,您就会更接近更多 sinc² 的零点。

所提出的算法看起来并不那么优雅——产生好的(离散的)白(伪随机)噪声实际上对于更长的观察窗口来说是非常困难的(如果你想评估某物的质量,这就是你所需要的),因此,伪随机发生器¹以渐近两倍的采样率运行似乎比让它以采样率运行,然后使用近似于所需光谱形状的适当低通滤波器(在这种情况下,|H(f)|1f); 至少在现代 CPU 上,它们具有出色的 SIMD 指令(即针对运行滤波器进行了高度优化,而不是针对运行伪随机噪声生成器进行了优化),保持和累加许多噪声值与执行 FIR 之间的区别在于 FIR 需要将保持的值与常数相乘(滤波器抽头)——因为这通常可以在融合乘加运算中完成。

现在,在 ASIC 或 FPGA 上,情况可能会有所不同。如果噪声的幅度分布无关紧要(即除了均匀绘制、不相关的样本外,不需要添加任何东西),那么您实际上可以通过做“更简单”的事情来节省复杂性,即生成所需的逻辑运算,例如XOROSHIRO128** 的时钟频率很可能远高于一个好的 FIR 滤波器所需的乘法器。


¹您不需要多个生成器——您只需多问一个白色的;白色样本在每个子采样中都不相关!