ディジタル信号処理による信号発生とエイリアス(13)

D-Number

FM音源チップのデータシートでは、F-Number レジスタの名前の由来については、特に説明されていません。
この F-Number は周波数 (ピッチ) に比例する量ですから、「F」は Frequency の F から取ったものと見て間違いないでしょう。
ここで、F-Number とペアになる概念として、(勝手に) 「D-Number」を定義したいと思います。
D は Division (除算)、Divider (分周器)、あるいは Divisor (除数) の D です。
f を F-Number、d を D-Number として
\qquad \qquad \qquad f \cdot \,d \,=\,定数
で定義します。 つまり、D-Number は F-Number の逆数に比例する数となります。
F-Number は、波形の周波数に比例する数でしたから、D-Number は、波形の周期に比例する数となります。
ここで、特に、定数=2^{\small N} の場合、
\qquad \qquad \qquad f \cdot d =2^{\small N}
を考えます。
フェーズ・アキュムレータのビット幅を N とし、f の表現は、それに見合った、N ビットの固定小数点表現で、フェーズ・アキュムレータに加算する値そのものとします。

両辺の 2 の対数を取って移項すると、
\qquad \qquad \qquad \log_2(d) = N - \log_2(f)
F-Number f は周波数に比例する値ですから、\log_2(f) は何かと言えば、その周波数の音程ドメインでの表現になります。
\log_2(\cdot ) の整数部はオクターブを表し、小数部はオクターブ内の音程を表します。
整数部を添え字 o、小数部を添え字 m で表現し、
\qquad \qquad \qquad \log_2(f) = f_o + f_m
\qquad \qquad \qquad \log_2(d) = d_o + d_m
と表すことにします。
そうすると、前の式は、
\qquad \qquad \qquad d_o + d_m = (N-1) + 1 - f_o - f_m
となります。
整数部どうし、小数部どうしを、それぞれ等しいとおいて、ふたつの式に分けると、
\qquad \qquad \qquad d_o = (N-1) - f_o
\qquad \qquad \qquad d_m = 1 - f_m
となります。
f_o\, , \; f_m の組から、実際の f を求めるには、
\qquad \qquad \qquad f = 2^{f_o} \,\times\, 2^{f_m}
を計算するわけですが、f_o は整数なので、2 のべき乗は、単にビットシフトするだけとなります。
小数部の f_m については、F-NUMBER テーブルを引いて求めていることを以前説明しました。
fd の小数部に関する式を見ると、0 \le f_m \lt 1.0 である f_m に対して、1 - f_m d_m が求められることを表現していますから、結局、F-NUMBER テーブルを、fd では、それぞれ逆向きに参照すれば良いことが分かります。
もう少し具体的に言うと、

  • F-NUMBER テーブルのエントリ数が NSIZE
  • f_m に相当するインデクス値が k でテーブルの参照が fnum[k] で表される

場合、d_m に対する参照は、fnum[NSIZE - k] となります。
ただし、この場合、 k=0 で fnum[NSIZE] を参照することになり、テーブルの領域外をアクセスすることになります。
fnum[NSIZE] に対するエントリを追加し、NSIZE+1 エントリの表にすれば解決できますが、16 ビット値のテーブルを、fnum[0] = 0x8000 から始めると fnum[NSIZE] = 0x10000 としなければならず、16 ビットにはおさまらなくなります。
少し誤差が加わることを承知の上で、fnum[NSIZE-k] の代わりに fnum[NSIZE-1-k] を使えば、テーブルサイズを増やさなくてすみます。
結局、ランプ波形サンプリングに相当する波形を発生させる方式*1に必要とされる D-Number
\qquad \qquad \qquad d = \frac{2^{\small N}}{f}
の計算は、通常の音程ドメインから F-NUMBER を求める計算に追加する形で、除算なしで実行できます。

*1:これを「ディジタル・リワインド方式」と呼ぶことにします