テキトーなノイズ生成

ソフトシンセのプログラミングにおける、ノイズ(≒疑似乱数)を作る話。

(不真面目なネタ)

ノイズを出すだけなら、例えば -1, +1 の2値でもいいのですが、ここでは32bit整数の疑似乱数を作って適当に変換することを前提にしています。


用途が用途なので、メルセンヌ・ツイスターとかを持ち出す必要はないはず。できるだけ(CPUだけでなくメモリ消費も)軽い方がいい。

よく使われる(使われた)のは、線形帰還シフトレジスタ (linear-feedback shift register, LFSR) だと思う……けれど、今回は使わない。

「簡単な疑似乱数生成」の代表格の線形合同法 (linear congruential generator, LCG) は、前回の値 \(x_n\) から下のような式で次々に新しい値 \(x_{n+1}\) を求める。

$$x_{n+1} = (ax_n + c)\mod m$$

それで、この式には乗算 \((ax_n)\) と剰余 (mod) 演算があるので、シフトとXORだけで済むLFSRより遅いというのが一般的な話。

ここで、\(m\) は周期に関わるのでできるだけ大きく、加算すらケチるために \(c=0\)、\(a\) を(さすがに0, 1, 2ではダメなので)\(3\) にしてやると、

$$x_{n+1} = 3x_n\mod 2^{32}$$

初期値1とかで、奇数しか出てこないけれど、\(2^{30}\) 周期の疑似乱数のような何かが出来上がる。で、mod \(2^{32}\) は32bit整数のオーバーフローを無視するだけなので演算不要。つまり、前回の値を3倍にするだけ。掛け算が遅いなら (x << 1) + x みたいにすればシフト1回・加算1回で済む。LFSRより軽いのでは!

もうちょい何とかするなら、

$$x_{n+1} = (5x_n + 1)\mod 2^{32}$$

とでもすれば、インクリメント1回分の犠牲で \(2^{32}\) 通りの数値が全部出てくる。

で、これらを *.wav に吐き出して鳴らしてみたところ、\(3x_n\) のやつでもホワイトノイズとして違和感はない感じ(私の耳が悪いだけの可能性はある)。周波数スペクトルを見ると微妙に右肩下がりにも見えるけれど、太いノイズと言い張ればアリでしょう。

結論:現代の計算機環境において、そんなところをケチる必要はない。

まあ、今ならXorshiftとかがいいんじゃないですかね。