ムライボックス (8) --- ソフトウェア (3)
C 言語で記述したプログラムの断片を示しながら、もう少し具体的な処理の説明をします。
まず、処理で使う変数です。
uint16_t r_mask; // "running" bit mask uint8_t c; // MIDI byte from MIDI-IN uint8_t midi_ch; // MIDI ch / sys msg type uint16_t bm; // mask bitmap for "OR" gate
変数「r_mask」は、「ランニング」ビットマスク・パターンで、永続的に有効な値を保持する必要があります。
「グローバル変数」あるいは (スコープとしての意味ではなく) 記憶クラスとしての「static」属性を宣言した変数とします。
その他の変数は、関数が実行されている間だけ有効な「ローカル変数」で構いません。
「c」は MIDI-IN から入力されたバイト・データを (一時的に) 保持する変数、「midi_ch」は、チャネル・メッセージの場合のチャネル番号、およびシステム・メッセージの場合のメッセージ種別を表す値を (一時的に) 保持する変数、「bm」は外部ハードウェアの「OR」ゲートに出力するマスク・パターンのビットマップを (一時的に) 保持する変数です。
まず、変数 c に MIDI-IN から入力したバイトが格納されているとして、その処理の最初の部分は次のようになります。
midi_ch = (0x0f & c); // extract MIDI ch / sys msg type bm = r_mask; // use "running" port mask
入力されたバイト c がチャネル・メッセージのステータス・バイトの場合には、チャネル番号部分を書き換えてしまうので、あらかじめ元のチャネル番号部分を抽出して変数 midi_ch に控えておきます。
最も最近に出現したステータス・バイトで書き換えられたランニング・ビットマスクが変数 r_mask に記憶されているので、変数 bm に値を読み出しておきます。
このあと、MIDI バイト c の値により処理が変わります。
c の b7 が「0」の場合はデータ・バイトであり、その場合にはすでに必要な処理が済んでいるので、c および bm について操作はせず、そのまま出力処理に移ります。
c の b7 が「1」の場合はステータス・バイトであり、その場合には、チャネル・メッセージとシステム・メッセージとで扱いを変えます。 (2017 年 12 月 11 日追記: 「ステータス・バイト」とすべきところを「データ・バイト」と表記していた誤りを修正しました)
まず、チャネル・メッセージは、ステータス・バイトの値が 0x80 〜 0xef の範囲ですから、
if (0xf0 > c) { // ch mode message [0x80..0xef] bm = mask_tab[midi_ch]; // get port mask pattern r_mask = bm; // set "running" port mask pattern c = ((c & 0xf0) | remap_tab[midi_ch]); // remap MIDI channel }
ビット・マスクはチャネル・メッセージ用のテーブルである mask_tab を参照して変数 bm に代入します。 それと同時に、ランニング・ビットマスク r_mask にも記憶しておきます。
リマップ・テーブル remap_tab を参照して得たリマップ後のチャネル番号を、強制的に MIDI バイト c の下位 4 ビットに格納します。
システム・メッセージの処理は下のようになります。 上のプログラムの if 文の else 節となっています。
else { // system message [0xf0..0xff] bm = sysmsg_tab[midi_ch]; // escaping for system realtime if (0xf8 > c) { // system common msg [0xf0..0xf7] r_mask = bm; // set "running" port mask pattern } // if (0xf8 > c) { ... } // if (0xf0 > c) {} else { ...
システム・メッセージにはチャネル番号の概念はないので、その書き換えは行なわず、ビットマスク・パターンだけを扱います。
ビットマスク・パターンはシステム・メッセージ用のテーブル sysmsg_tab[] を参照して、システム・コモン・メッセージ (0xf0 〜 0xf7)、システム・リアルタイム・メッセージ (0xf8 〜 0xff) の計 16 種のメッセージを共通に扱います。
そして、システム・コモン・メッセージ (0xf0 〜 0xf7) では、チャネル・メッセージと同様にランニング・ビットマスク r_mask にも記憶しておきます。
システム・リアルタイム・メッセージ (0xf8 〜 0xff) では、r_mask は変化させず、「今回限り」の値である bm にだけビットマスクを代入しています。
以上で MIDI バイトの種別に応じた処理が終了したので、その後は共通の出力処理に入ります。
for (i = 0; i < N_PORTS; i++) { if (0x01 & bm) { // port active (*write_func_p[i])(c); // port output } // if bm >>= 1; } // for (i = 0; ...
これは以前示した「PSoC5LP 富豪版」の例ですが、「富豪版」なので出力ポートの数だけ送信 UART があり、他のポートのタイミングを考慮する必要がないので、単にビットマスクをシフトして 1 ビットごとに参照しながら UART へ出力するかどうか決めているだけです。
「エコノミー版」では出力タイミングを監視しながら出力しなければならないので、割り込み/ポーリング/バッファ等の構成によりプログラムは変わってきます。