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

OPL3 の音源チップでは、リニア周波数的な表現である F-NUMBER、BLOCK の組でピッチ情報を指定します。
一方、KSL (Level Key Scale) では、1 オクターブ当り何 dB という形で出力レベルに補正が加えられます。
この変換は、下の表にしたがって、チップ内部で行われます。

OCT
FNUM
0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 3.000 6.000 9.000
2 0 0 0 0 3.000 6.000 9.000 12.000
3 0 0 0 1.875 4.875 7.875 10.875 13.875
4 0 0 0 3.000 6.000 9.000 12.000 15.000
5 0 0 1.125 4.125 7.125 10.125 13.125 16.125
6 0 0 1.875 4.875 7.875 10.875 13.875 16.875
7 0 0 2.625 5.625 8.625 11.625 14.625 17.625
8 0 0 3.000 6.000 9.000 12.000 15.000 18.000
9 0 0.750 3.750 6.750 9.750 12.750 15.750 18.750
10 0 1.125 4.125 7.125 10.125 13.125 16.125 19.125
11 0 1.500 4.500 7.500 10.500 13.500 16.500 19.500
12 0 1.875 4.875 7.875 10.875 13.875 16.875 19.875
13 0 2.250 5.250 8.250 11.250 14.250 17.250 20.250
14 0 2.625 5.625 8.625 11.625 14.625 17.625 20.625
15 0 3.000 6.000 9.000 12.000 15.000 18.000 21.000


この表は「YMF715x (OPL3-SA3) -Register Description Document-」(以下「rege」と略記) の Page 7 に掲載されているものと同じです。
表の数値は、オペレータの出力レベルに追加する減衰量を dB 単位で表現したものです。
「FNUM」 は 10 ビット幅の F-NUMBER の上位 4 ビット、つまり b9..b6 を抜き出したものです。 「OCT」 は前に出てきた 「BLOCK」 のことで、表現上の統一が取れていませんが、「rege」の表現に従いました。
この表は KSL=1 の 3 dB/oct の場合で、KSL=2 の 1.5 dB/oct の場合は数値を半分に、KSL=3 の 6 dB/oct の場合は数値を倍にしたものになります。
最初にこの表を見たときには何のことか良く分かりませんでしたが、考えてみたら、実質的には対数変換の表であることが分かりました。
式で表すと、ほほ、
{\rm val} = 3.0 \times ( \log_2({\rm FNUM}) - 4 + {\rm OCT} )
になります。 「3.0」の部分は KSL の値によって「1.5」または「6.0」になります。
「ほぼ」と言ったのは、表の数値と一致させるためには、以下の条件を満たす必要があるからです。

  • FNUM=7, 9 に対しては +0.5 した 7.5, 9.5 を計算に使う
  • FNUM=14, 15 に対しては +1 した 15, 16 を計算に使う
  • log2 の値は小数点以下 3 ビット精度に丸める
  • 結果がマイナスの場合は 0 で置き換える

これらの条件も考慮した、「rege」の表と同等の出力が得られる C プログラムを示します。

#include <stdio.h>
#include <math.h> // 'log2()' defined

void main()
{
  int fnum, oct, i;
  double fn, ksl, db_per_oct = 3.0;
  printf("  OCT");
  for (oct = 0; oct < 8; oct++) {
    printf("%8d ", oct);
  } // for (oct
  printf("\n FNUM\n");
  for (fnum = 0; fnum < 16; fnum++) {
    printf("%4d ", fnum);
    fn = (13 < fnum) ? fnum+1 : fnum;
    if ((7 == fnum)|(9 == fnum)) fn += 0.5;
    for (oct = 0; oct < 8; oct++) {
      ksl = log2(fn + 1e-9) - 4.0 + oct;
      i = ksl * 8 + 0.5;
      if (i < 0) i = 0;
      ksl = db_per_oct * i / 8.0;
      printf("%8.3f ", ksl);
    } // for (oct
    printf("\n");
  } // for (fnum
} // main()

OPL3 ハードウェアを忠実にエミュレートするなら、この表も実装しなければなりません。
しかし、FM音源プログラムでは、「音程ドメイン」の一連の計算の中で KSL 補正値を求めることとし、この表は実装しませんでした。
ビブラート、ピッチベンド、ポルタメントなど、ピッチ情報に影響を及ぼす要素は全て「音程ドメイン」で計算して一本化し、F-NUMBER に集約して扱うことにしました。
次回以降で、この「音程ドメイン」での計算について説明します。