FM音源プログラム (16) -- オペレータ (9)

F-NUMBER 計算

以前示した F-NUMBER の計算式には、サンプリング周波数が含まれていますから、F-NUMBER の値はサンプリング周波数に依存します。
FM音源プログラムでは、サンプリング周波数の切り替え機能を持たせたので、何も対策しないと、 F-NUMBER テーブルが複数個必要になってしまいます。
そこで、F-NUMBER テーブルは十分細かい刻みを持つ1種類として、音程ドメインでサンプリング周波数に応じたオフセットを足しこんでから表を引くことにより、複数サンプリング周波数に対応します。
ここで使う F-NUMBER テーブルは、いわば正規化された形のもので、その最小値は MSB だけが「1」で残りのビットが「0」のものです。
たとえば、16 ビット幅の値とすると、最小値は 0x8000 で、最大値は 0xffff 以下になります。
このような値に選ぶ理由は、限られたビット幅で最大の精度を得るためです。
最適でない例として、たとえば、前に書いた OPL3 の例では、「C」の音に対して 346 = 0x15a、「B」 の音に対して 654 = 0x28e となっています。
「B」の音の F-NUMBER は 10 ビット幅ですが、「C」の音の F-NUMBER は 9 ビット幅です。 つまり、低いほうの音で F-NUMBER の精度を 1 ビット損していることになります。
FM音源プログラムで使っているのは、MIDI ノートの小数部 7 ビットに対応する 12×128=1536 エントリの 16 ビットデータの表です。 プログラムでは fnum128[] という配列になっています。
この F-NUMBER テーブルを引く前に、ピッチに関する要素を音程ドメインで集約します。

  • MIDI ノート (およびポルタメント・アキュムレータ兼用) (note_f)
  • チューニング関係 (tune_f, master_tune_f)
  • ピッチ・ベンド (bend_f)
  • モジュレーション (vLFO_f)
  • スケール/オクターブ・チューニング
  • サンプリング周波数によるオフセット

の寄与を加算した計算結果の整数部を、オクターブとオクターブ内の音程とに分割します。
ここで、ついうっかり

oct = targnote / 12;
k = targnote % 12;

などと書いてしまうと、ライブラリの除算ルーチンとリンクされ、実行速度が著しく低下してしまいます。(経験あり)
除算を避けるために、MIDI ノート値の整数部をインデクスとし、オクターブ値とオクターブ内の音程を値とする表を作っておきます。 プログラムでは octkey[] という配列になっています。

そして、実際のフェーズ・アキュムレータの増分値 phinc は、

phinc = fnum128[kk] << oct;

という形で求めて、オペレータに渡します。