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 だけでいいのに、両方のオペレータで保存している
などの改善すべき点があります。