FM音源プログラム (31) -- MIDI デコーダ(5)

NRPN/RPN デコーダ

NRPN (Non-Registered Parameter Number) と RPN (Registered Parameter Number) のパラメタ番号の指定は、NRPN/RPN それぞれ別のコントロール・チェンジに割り当てられています。
MSB / LSB の指定の順番には、特に規定はなく、どちらが先でも構いません。
値の指定については、データ・エントリ MSB (CC#6)、データエントリ LSB (CC#38) を NRPN/RPN 共通に使います。
パラメタ番号の MSB と LSB が共に 0x7F の場合は、「指定なし」を表しています。
また、NPRN 番号を指定すると RPN は指定なし (RPN null) に、RPN を指定すると NRPN は指定なし (NRPN null) の状態に強制されます。
パラメタ番号の指定の段階では、番号を記録しておくだけで、それ以外の処理は行いません。
データエントリがあって、初めて目的の動作を行います。
MSB/LSB の指定方法は一般的なルールに従い、MSB の入力で LSB 値はクリアされ、LSB 値の入力では MSB に影響を与えません。
したがって、MSB, LSB 両方を指定する必要がある場合には、MSB, LSB の順に指定する必要があります。
md_cc_func() 関数では、データ・エントリ MSB (CC#6)、データエントリ LSB (CC#38) の処理部分で decoode_nrpn()、decode_rpn() を両方呼び出します。
データエントリ関係は、

  • データ・エントリ MSB (CC#6)
  • データエントリ LSB (CC#38)
  • データ・インクリメント (CC#96)
  • データ・デクリメント (CC#97)

の4種類がありますから、decode_nrpn(), decode_rpn() 関数の引数として、MSB は 0x100、LSB は 0、インクリメントは +1、デクリメントは -1 を渡して区別をしています。
NRPN/RPN のデコードで一番の問題は、パラメタ番号は 14 ビット幅あって、指定可能な領域は広大なのに、実際に定義されている数は少なくて、全体から見ると、まばらなことです。
パラメタ番号をインデクスとするテーブルを作ることは、有効なエントリが、まばらにしか存在しないので効率的ではありません。
RPN は「Registered」の名前通りに、AMEI (Association of Musical Electronics Industry) および MMA (MIDI Manufacturers Association) が登録したパラメタに限られますから、数は少数であり、急激に増えたりはしません。
現在、定義されているのは、次の 6 種です。

RPN番号 機能
RPN#0000 Pitch Bend Sensitivity
RPN#0001 Channel Fine Tune
RPN#0002 Channel Coarse Tune
RPN#0003 Tuning Program Select
RPN#0004 Tuning Bank Select
RPN#0005 Modulation Sensitivity

ここで、RPN 番号は MSB、LSB それぞれの 7 ビット数値を 16 進 2 桁で表現して、連続して並べてあります。
RPN については、この 6 種だけですから、6 エントリの表に、各機能を実現する関数のアドレスを登録しておくことにしました。
RPN#0003, RPN#0004 の機能はサポートしていません。
NRPN については、次の 6 種のみサポートしています。

NRPN番号 機能
NRPN#0108 Vibrato Rate
NRPN#0109 Vibrato Depth
NRPN#010A Vibrato Delay
NRPN#0163 EG Attack Time
NRPN#0164 EG Decay Time
NRPN#0166 EG Release Time

これらのパラメタは「Non-Registered」とは言いながら、YAMAHA の音源でも、Roland の音源でもサポートされています。
これらは、コントロール・チェンジでも同様の機能があって、実装が簡単なものを選んでいます。
NRPN#010A は、デコードはしていますが、機能は実装していません。
NRPN は勝手に定義して使えるものですが、FM音源プログラムでは、独自の NRPN はひとつも定義していません。
NRPN のデコードは、まず NRPN 番号の MSB をインデクスとして、表を引きます。
その表には、LSB をデコードするための関数へのポインタが登録されています。
ポインタの値が NULL でなければ、そのポインタ経由で関数を呼び出して LSB のデコードを続けます。
表のエントリ数は 3 個用意してありますが、LSB デコード関数の実体があるのは MSB = 0x01 の場合だけです。