FPGA 版 FM 音源 (56) -- YMF297 (OPN3/OPL3) 測定 (22) -- EG シミュレーション・プログラム (2)

エンベロープ計算関数の「EG_module()」の宣言部分を下に示します。

int EG_module(uint16_t *eg_mem_p, int fs_en, 
              int kon_rise, int kon_fall, int egt, 
              int ar, int dr, int sl, int rr, 
              int rof)

複数タイム・スロット対応になっており、時分割で複数のエンベロープを生成することができます。
「スロット・クロック」ごとに関数を呼び出して、関数の返り値として EG アキュムレータの値を得ます。
EG アキュムレータ (9 ビット) および EG ステート (3 ビット) の計 12 ビットのデータは各スロットごとに確保しておいたメモリに収納する必要があり、関数の第 1 引数の「eg_mem_p」でメモリへのポインタを渡します。
関数内では最初にポインタの指すメモリから値を読み込んで処理を行い、最後に結果をポインタの指すメモリに書き戻しています。
EG クロック・プリスケーラはサンプリング・クロックごとにインクリメントする必要がありますが、そのタイミングを第 2 引数の「fs_en」を「1」にして渡すことで通知します。
kon_rize / kon_fall は、それぞれ KON (Key ON) の立ち上がりと立ち下がりを表す 1 クロック幅のパルスに相当します。
ひとつの発音チャネルを構成する 2 つあるいは 4 つのオペレータには、共通の KON 信号が与えられるので、オペレータ側 (スロット側) ではなく、発音チャネル・コントロール側で KON のトランジェントを検出したほうがメモリを節約できます。
EGT = 0 のパーカッシブ・モードの場合には kon_rize 信号のみでトリガすることができます。
ar / dr / sl / rr の引数は、それぞれ対応する発音パラメタ・レジスタの値であり、計算したいスロットの設定値を渡します。
当然、eg_mem_p で渡すメモリのスロット番号と揃える必要があります。
「rof」は「Rate offset」の意味で、直接の音色パラメタではありませんが、F-Number のオクターブ指定である「BLOCK」の値と KSR (Rate Key Scaling) パラメタの値により値が決まり、0 〜 3 あるいは 0 〜 15 のオフセット値となります。
2 スロット対応版のプログラムを PSoC5LP 上にインプリメントし、SPDIF のステレオ 2 チャネルに乗せて出力したものをチャプチャした結果を下に示します。

上半分が L ch、下半分が R ch で、左右で別々のエンベロープ設定になっています。
メイン関数の断片を下に示します。

#define N_SLOT (2)
typedef uint16_t uint16_slot_t[N_SLOT];
typedef int16_t  int16_slot_t[N_SLOT];

  uint16_slot_t eg_mem, eg_out, lin_out;
  int16_slot_t  ar, dr, sl, rr, rof, egt;
  int i, k;

  ... <中略> ...

  for (i = 0; i < 4096; i++) {
    for (k = 0; k < N_SLOT; k++) {
      eg_out[k] = EG_module(&(eg_mem[k]), (0 == k), 
                           (1 == i), (3072 == i), egt[k], 
                           ar[k], dr[k], sl[k], rr[k], 
                           rof[k]);
// convert lb to linear value            
      lin_out[k]  = lb2lin(0, (eg_out[k] << 3));
    } // for (k = 0; ...

  ...  < SPDIF 出力 > ...

  } // for (i = 0; ...