FM音源プログラム (34) -- ユニット

ユニットを処理する unit() 関数は、「unit.c」ファイル中で定義されています。
unit() 関数では、まず、フィードバック処理を行い、次に、アルゴリズムの指定に従って、オペレータのモジュレータ/キャリアの処理を行って、キャリア出力を結果として返します。
実際のコードを下に示します。

int16_t unit(unit_prop_t *u_p)
{
  int16_t w0, w1;
  op_prop_t *o_p = &(u_p->op_prop[0]);
  if (OFF == o_p[1].egmode) {
    o_p->phacc = 0;
    o_p[1].phacc = 0;
    o_p->opout = 0;
  }
  if (u_p->uv.fb) w1 = o_p->opout; // fb = 1..7
  else            w1 = 0;          // fb = 0
  o_p->mod_in = (int32_t)w1 << (8+(u_p->uv.fb));
  w0 = (*o_p->vp.op_func_p)(o_p); // OP0 mod/carr
  o_p++;  // advance op. prop. pointer to OP1
  if (1 == u_p->uv.alg) { // ALG == 1, parallel 
    o_p->mod_in = 0;
    w1 = (*o_p->vp.op_func_p)(o_p);  
    return(w0 + w1);
  } else { // ALG == 0, series 
    o_p->mod_in = ((int32_t)w0 << 17);
    w1 = (*o_p->vp.op_func_p)(o_p);   
    return(w1);
  }  
} // int16_t unit() 

最初の if 文では、キャリア・オペレータの OP1 の egmode が OFF、つまり、EG のレベルが -96 dB より小さくなったオフ状態を判定して、出力およびフェーズ・アキュムレータをゼロに強制しています。

その次は、フィードバック量 fb の値によって、ゼロあるいは前回の OP0 の出力値をシフトしたものを OP0 自身の変調入力にフィードバックして OP0 の処理をします。
そして、アルゴリズムを表す alg 変数を見て、1 なら並列型、0 なら直列型の構成で OP1 の処理をします。
ユニットも、サンプリング周期内に同時発音数の回数だけ実行されますから、効率化を考慮する必要があります。
その点も含め、現在の unit() 関数には

  • フィードバックや、モジュレータ出力をキャリアの変調入力に入れる部分のビットシフト量が「ハードコード」されている
  • 毎回 alg 変数を調べて処理を変えている
  • オペレータ出力値の保存は、フィードバックがある OP0 だけでいいのに、両方のオペレータで保存している

などの改善すべき点があります。