FM音源プログラム (33) -- データ構造
オペレータを組み合わせて構成した、1音の「発音単位」のことを、ここでは「ユニット」(unit) と呼ぶことにします。
FM音源チップでは「チャンネル」と呼ばれており、一般的な音源の用語としては「ボイス」になります。
1音に複数の発音機構を割り当て、複数の音色を重ねることを「レイヤード・ボイス」と言ったりします。
MIDI チャンネルごとの設定情報も含め、オペレータ/ユニット/チャンネル単位でデータ構造をまとめていますので、その説明をしたいと思います。
オペレータ・プロパティ
まず、オペレータですが、前に説明したように、主にピッチ関係の変数と、EG 関係の変数で構成されています。
「オペレータ・プロパティ・タイプ」の意で、op_prop_t という構造体の型を定義してあります。
/* operator property definition */ typedef struct { uint32_t phacc; // phase accumulator uint32_t ph_inc; // phase increment (pitch) int32_t mod_in; // phase modulation input int32_t egacc; // EG accumulator int32_t attacc; // EG attack accumulator int32_t eginc; // EG acc increment int32_t eginit; // EG acc init value int32_t ol_lin; // output level in lin int32_t tl_lin; // total level in linear int32_t sl_lin; // sustail level in linear int32_t tl_lb; // TL + KSL + VEL in log2 int32_t sl_lb; // sustain level in log2 int32_t vol_lb; // VOL + EXP in log2 int32_t am_lb; // AM in log2 int16_t egrate; // EG rate int16_t opout; // wave output of this op op_vparam_t vp; // voice parameters uint8_t egmode; // EG mode uint8_t last_egm; // last EG mode uint8_t rof; // KSR offset (0..15) int8_t gate_q; // key-ON gate queue int8_t damp_q; // damper queue } op_prop_t;
これは、ADuC7026 版、V850 版の 32 ビット RISC マイコンの場合の定義で、8 ビット変数で用が足りる場合でも、アクセスの高速化のために 32 ビット変数を主に使用しています。
8 ビットや 16 ビット変数を使うと RAM は節約できますが、32 ビット・レジスタへのゼロ拡張や符号拡張の命令が必要になり、動作速度の点では不利になります。
通常は、オペレータ・プロパティへのポインタを渡しますから、
void func(op_prop_t *op_p)
{
...
op_p->phacc += op_p->ph_inc;
...
}
のように、ポインタを利用して構造体メンバを参照する場合の記法の「->」を使って各メンバにアクセスします。
phacc, ph_inc, mod_in は、ピッチ/位相変調関係の変数です。
オペレータ関数をアセンブラで書く場合にオフセット値が求めやすいように、構造体先頭から配置してあります。
egacc から egrate までと egmode から damp_q までは EG 関係の変数です。
変数がリニア値の場合には、変数名の末尾に「_lin」を、lb 値の場合には「_lb」を付けてあります。
オペレータの音色パラメタは、下に示すような構造体のメンバに格納して利用しています。
ROM 上のパックされたデータからの抽出/展開は、MIDI メッセージのプログラム・チェンジを受け取ったタイミングで行っています。
/* operator decoded voice parameter structure */ typedef struct op_vparam_st { uint8_t ksr; // key scaling rate uint8_t egt; // EG type uint8_t vib; // vibrato flag uint8_t mult; // freq multiplier (1..30) uint8_t dr; // EG delay rate uint8_t rr; // EG release rate uint16_t sl; // EG sustain level uint8_t ar; // EG attack rate uint8_t ksl; // key scaling level uint16_t tl; // EG total level uint8_t wavesel; // wave select uint8_t am; // AM flag uint8_t dam; // AM depth uint8_t dvb; // vibrato depth uint8_t carrf; // carrier flag uint8_t op_num; // Operator number int16_t (*op_func_p)(void *o_p); } op_vparam_t, *op_vparam_p;
ユニット・プロパティ
ユニット・プロパティ・タイプは次のように定義されています。
/* unit propery definition */ typedef struct { uint32_t ph_inc; // f-number in linear format int32_t note_f; // current note number with fraction int32_t porta_delta; // portamento acc increment (signed) int32_t m_note_f; // target note number with fraction int waveout; // waveform output of this unit int vel_lb; // MIDI velocity in log2 int exp_lb; // MIDI ch vol * MIDI expression in log2 int16_t ks6_0dB; // Level key scaling value (6.0dB/oct) uint8_t on_cnt; // multiple keyon counter uint8_t midi_note; // MIDI note number (0..127) uint8_t midi_vel; // MIDI velocity (0..127) uint8_t oct; // effective octave (0..10) uint8_t keycode; // effective keycode (0..11) int8_t gate; // key-ON gate flag int8_t damper; // Hold1/Damper flag int8_t porta_flag;// portamento flag uint8_t hold; // CC#64 hold1/dumper flag (0..1) uint8_t ksn; // key scaling number uint8_t fb; // modulator feedback depth uint8_t lfo; // LFO setting uint8_t alg; // algorithm uint8_t midi_ch; // MIDI ch assigned to this unit unit_vparam_t uv; // unit voice parameter (fb/LFO/alg) op_prop_t op_prop[2]; // property for operator 0, 1 int voice_param_change; // voice parameter change flag int LFO_param_change; // LFO parameter change flag } unit_prop_t;
LFO 周波数やフィードバック、アルゴリズムはユニットのプロパティとして扱っています。
1 ユニット 2 オペレータ構成は変化しないので、ユニット・プロパティのメンバとして、オペレータ・プロパティの実体を置いています。
チャンネル・プロパティ
チャンネル・プロパティ・タイプを次に示します。
ここで、「チャンネル」と言っていますが、「パート」と言った方が正確です。
当初は、同時発音数 3 程度で、MIDI チャンネルの割り当ても固定だったので、「チャンネル」としたのですが、現在は MIDI チャンネル割り当て変更機能も実装したので、「チャンネル」では実状に合わなくなっています。
ここには MIDI チャンネルごとの設定情報を集めてあります。
プログラム・チェンジで選ばれた音色データの ROM 上の表現そのもののコピー、およびそれを展開したデータの両者を保持しています。
発音ユニットへの音色データ・ロードは、展開ずみ音色データをユニット・プロパティの音色データ部へブロック転送するだけです。
FM音源チップでは、チップ全体で LFO はひとつですが、チャンネルごとに LFO を設けた方がロジックとしては簡単になるので、FM音源プログラムでは、LFO はチャンネルのプロパティとしています。
/* MIDI channel (part) property */ typedef struct { int32_t bend_f; // pitch bend (b31..b16:int, b15..b0:frac) int32_t tune_f; // ch tune (b31..b16:int, b15..b0:frac) int32_t porta_inc; // portamento increment int32_t vLFO_f; // vibrato LFO output in fractional note format uint16_t aLFO; // AM (tremolo) LFO output in log2 format int16_t waveout; // wave output int16_t vol_lb; // MIDI volume in log2 int16_t exp_lb; // MIDI expression in log2 int16_t MD_bend; // MIDI pitch bend (-8196..8195) int16_t coarse_tune; // coarse tune (-64..63) int16_t fine_tune; // fine tune (-255..255) uint LFO_ph_acc; // LFO phase accumulator uint LFO_ph_inc; // LFO phase acc increment uint16_t MD_RPN_ML; // combined RPN MSB:LSB (0x0000..0x7f7f) uint16_t MD_NRPN_ML; // combined NRPN MSB:LSB (0x0000..0x7f7f) uint16_t MD_mod_range; // MIDI RPN#5 uint8_t MD_bend_range; // MIDI RPN#0 uint8_t MD_coarse_tune;// MIDI RPN#1 uint8_t MD_fine_tune; // MIDI RPN#2 uint8_t MD_progno; // MIDI program number uint8_t MD_note; // MIDI note number uint8_t MD_velocity; // MIDI velocity uint8_t MD_modulation; // MIDI CC#1 uint8_t MD_porta_time; // MIDI CC#5 uint8_t MD_volume; // MIDI CC#7 uint8_t MD_pan; // MIDI CC#10 uint8_t MD_expression; // MIDI CC#11 uint8_t MD_hold1; // MIDI CC#64 uint8_t MD_porta_sw; // MIDI CC#65 uint8_t MD_legato; // MIDI CC#68 int8_t MD_timbre; // MIDI CC#71 int8_t MD_brightness; // MIDI CC#74 int8_t MD_vib_rate; // MIDI CC#76 int8_t MD_vib_depth; // MIDI CC#77 int8_t MD_vib_delay; // MIDI CC#78 uint8_t MD_porta_src; // MIDI CC#84 uint8_t MD_rev_send; // MIDI CC#91 uint8_t MD_dent_MSB; // MIDI CC#6 uint8_t MD_dent_LSB; // MIDI CC#38 uint8_t LFO_rate; // 2-bit coded vibrato rate uint8_t LFO_depth; // 2-bit coded vibrato depth uint8_t last_on_note; // MIDI note number of most recently ON uint8_t last_off_note; // MIDI note number of most recently OFF uint8_t midi_ch; // MIDI ch number uint8_t in_use[1]; // assigned unit number table int8_t so_tune_ofs[12]; // MIDI scale/octave tuning offset op_vparam_t ov[2]; // operator voice parameters unit_vparam_t uv; // unit voice parameters struct voicedata vdat; // MA-2 format voice data } ch_prop_t;