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

メインループの中では、MIDI/シリアル MIDI から 1 バイト受信するたびに MIDI メッセージのデコード関数である mididec() を呼び出しています。
mididec() 関数では、そのデータの到着で MIDI メッセージとして完成した場合には、それをデコードし、その処理をする関数を呼び出すところまでを実行します。
まだ MIDI メッセージが完成していない、途中のデータの場合には、内部状態を更新するだけでリターンします。
メッセージを構成するデータが全て揃ってからデコードを開始するので、レイテンシーについては大きくなります。
mididec() 関数では、渡されたデータがステータスバイトならば

  • システム・リアルタイム・メッセージの除去/処理
  • システム・エクスクルーシブ・メッセージの組み立て開始と終了
  • ランニング・ステータス処理

を行っています。
データバイトの場合は md_data_byte() 関数を呼び出して処理します。
システム・リアルタイム・メッセージは、他のメッセージの途中に割り込んで送信されることもあるので、特別扱いをします。
多くは、単に読み飛ばすだけですが、スタート/コンティニュー/ストップ (0xfa/0xfb/0xfc) は内蔵デモ曲のシーケンサのコントロールに使っているので処理します。 といっても、シーケンサで使っているフラグをセットするだけです。
ランニング・ステータスについては、便宜的に全てのメッセージがランニング・ステータスであるかのように扱います。
まず、普通にステータスバイトが到着するとランニング・ステータス用の変数 MS_run_stat に格納し、1 バイト受信したことを変数 MD_bcnt に記録するだけで、それ以上は何もしません。
データバイトが到着した段階で、md_data_byte() 関数を呼び出し、そこで初めて MS_run_stat 変数を参照して、このデータバイトが何のメッセージであるかを判断します。
つまり、データバイト処理の段階では、ステータスバイトが直前に受信したものであるのか、ランニング・ステータスなのかは、全く区別せずに処理します。
実は MD_bcnt の値で区別はできるのですが、あえて同じに扱っているといった方が正確です。
この方法では、ランニング・ステータスが許されていないメッセージについても、エラーと判定せず、ランニング・ステータスとして扱ってしまいます。
ランニング・ステータスを許さないメッセージを処理した後に、「ランニング・ステータスなし」という設定をすれば回避はできますが、現在は特に気にせず、そのままにしてあります。
ステータスバイトがシステム・エクスクルーシブの開始バイト (0xf0) であった場合には、エクスクルーシブ・メッセージをバッファ sysx_buf[] にコピーする準備をします。
バッファのポインタ sysx_cnt を 1 にして、先頭の 0xf0 をコピーしておきます。
sysx_cnt はシステム・エクスクルーシブ処理中を表すフラグにもなっています。
実際のエクスクルーシブ・メッセージのコピーは md_data_byte() 関数の中で行われます。
ステータスバイトがシステム・エクスクルーシブの終了バイト (EOX: 0xf7) であった場合には、エクスクルーシブ・メッセージの終了処理をします。
受信したメッセージがバッファサイズより大きくて、バッファに入り切らなかった場合には、メッセージを破棄して、エクスクルーシブは実行しません。
バッファに正常にコピーできていれば、システム・エクスクルーシブのデコーダ関数 sysx_dec() を呼び出し、システム・エクスクルーシブ処理中フラグをクリアします。
sysx_dec() では、デコード後に処理関数を呼び出す所まで実行するので、その処理関数の実行に長時間要するものでは、音が途切れる可能性があります。