ムライボックス (17) --- ソフトウェア (9)

XG Display Bitmap Data システム・エクスクルーシブ・メッセージにより、リマップ/ビットマスク・テーブルを MIDI シーケンス・データを使って書き換える手段を得ることができましたが、そのメッセージデータを作成する (ホスト PC 上で実行する) ユーティリティーを作りました。
テーブルを定義している C ソース・プログラムの初期値を記述している部分のテキストを標準入力に喰わせると、システム・エクスクルーシブ・メッセージに変換して出力します。
そのプログラム・リストを下に示します。
Windows 10 (バージョン 1709) の WSL (Windows Subsystem for Linux) 上の Ubuntu 16.04.3 LTS 上の gcc 5.4.0 で作成しています。

// gen_murai_sysx.c : generate system exclusive message
//                    of remap/bitmask table loading
//                    for MuraiBOX

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

char buf[65536] = " ";
char line_buf[4096];
int  vcode  = 0;
int  hcode  = 0;
// default device number = 16 (1 origin)
int  dev_no = (16-1);
// default = hex output mode
int  bin_flag  = 0; 
// default = no prefix for hex
int  pref_flag = 0; 

#define SYSX_BUF_LEN (7 + (3*16) + 1)

// array of bitmap data
int d[16];

uint8_t sysx[SYSX_BUF_LEN] = {
  0xf0,  // Start Of Exclusive
  0x43,  // YAMAHA ID
  0x10,  // device number
  0x4c,  // XG model ID
  0x07,  // XG Display Bitmap Data
}; // uint8_t sysx[]

// optional argument handling
void options(int argc, char * const argv[])
{
  int optc;
  
  if (1 < argc) {
    while (-1 != (optc = getopt(argc, argv, 
                                "BbD:d:H:h:PpV:v:"))){
      switch (optc) {
        case 'B': case 'b': // binary output flag
		  bin_flag ^= 0x01; // flip bin_flag
		  break;
        case 'D': case 'd': // device number
          dev_no = atoi(optarg);
          if (0  > dev_no) { dev_no = 0;  }
          if (15 < dev_no) { dev_no = 15; }
          break;
        case 'P': case 'p': // hex prefix flag
		  pref_flag ^= 0x01; // flip pref_flag
		  break;
        case 'V': case 'v': // vertical code
          vcode = atoi(optarg);
          if (0 > vcode) { vcode = 0; }
          if (7 < vcode) { vcode = 7; }
          break;
        case 'H': case 'h': // horizontal code
          hcode = atoi(optarg);
          if (0  > hcode) { hcode = 0;  }
          if (15 < hcode) { hcode = 15; }
          break;
        default:
          break;
      } // switch (optc) { ...
    } // while (-1 != ... ) { ...
  } // if (0 < argc) { ...
} // void options()

int main(int argc, char * const argv[])
{
  char *p;
  uint16_t bm;
  
  options(argc, argv);
  while (!feof(stdin)) {
    char c;
    fgets(line_buf, sizeof(line_buf), stdin);
    for (int i = 0; i < strlen(line_buf); i++) {
      c = line_buf[i];
      if ( (',' == c) || ('{' == c) ||
           ('=' == c) || (0x20 > c) ) { 
        line_buf[i] = ' '; // replace by white space
      } // if ( (',' == c) || ... ) { ...
    } // for (int i = 0; ...
// look for 1-line comment "// ..."
    p = strstr(line_buf, "//"); 
    if (NULL != p) {
      *p = 0; // trim comment
    } // if (NULL != p) { ...
// look for 1-line comment "/* ... */"
    p = strstr(line_buf, "/*");
    if (NULL != p) {
      *p = 0; // trim comment
    } // if (NULL != p) { ...
    strcat(buf, line_buf);
  } // while (!feof(stdin)) { ...
  sscanf(buf, "%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
              &(d[0]),  &(d[1]), 
              &(d[2]),  &(d[3]), 
              &(d[4]),  &(d[5]), 
              &(d[6]),  &(d[7]), 
              &(d[8]),  &(d[9]), 
              &(d[10]), &(d[11]), 
              &(d[12]), &(d[13]), 
              &(d[14]), &(d[15]));
  for (int i = 0; i < 16; i++) {
    bm = (uint16_t)d[i];
// split 16 bit width bitmap to 7:7:2
    sysx[i+7]    = (0x7f & (bm >> 9));
    sysx[i+7+16] = (0x7f & (bm >> 2));
    sysx[i+7+32] = (0x60 & (bm << 5));
  } // for (int i = 0; ...
  sysx[2] = (0x10 | dev_no); // device number 
  sysx[5] = (0x70 & (vcode << 4)) 
          | (0x0f & hcode); // v/h code 
  sysx[SYSX_BUF_LEN-1] = 0xf7; // End Of Exclusive
  for (int i = 0; i < SYSX_BUF_LEN; i++) {
    if (bin_flag) { // binary mode
      printf("%c", sysx[i]); // binary output 
    } else { // hex mode
      printf("%s%.2x ", (pref_flag ? "0x" : ""), sysx[i]);
	} // if (bin_flag) { ...
  } // for (int i = 0; ...
} // int main()

デフォルトでは、"0x" プリフィクスなしの 2 桁 16 進出力ですが、"-b" オプションでバイナリ出力、"-p" オプションで "0x" プリフィクス付きの出力になります。
また、"-v n" オプションでテーブルの種別を "n" にします。 (デフォルトではリマップ・テーブルを表す "0")
入力としては、たとえば、

//uint16_t remap_tab[16] = {
  0,  // ch 1,  no remap
  1,  // ch 2,  no remap
  2,  // ch 3,  no remap 
  3,  // ch 4,  no remap 
  4,  // ch 5,  no remap 
  5,  // ch 6,  no remap 
  6,  // ch 7,  no remap 
  7,  // ch 8,  no remap 
  8,  // ch 9,  no remap 
  9,  // ch 10, (drum ch), no remap 
  10, // ch 11, no remap 
  11, // ch 12, no remap 
  12, // ch 13, no remap 
  13, // ch 14, no remap 
  14, // ch 15, no remap 
  15  // ch 16, no remap 
}; // uint16_t remap_tab[]

のように、変数名などの部分はコメント・アウトして、初期化数値の部分だけを有効にします。 この例では「すべてリマップしない」設定のリマップ・テーブルになっています。

f0 43 1f 4c 07 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 01 01 01 01 02 02 02 02 03 03 03 03 
00 20 40 60 00 20 40 60 00 20 40 60 00 20 40 60 
f7 


出力は上のようになります。 
見やすくするために後から手動で改行を挿入しています。
これをシステム・エクスクルーシブ・メッセージとして音源に送り、ディスプレイに表示させると左の写真のようになります。
ビットマスク・テーブルの例を下に示します。 これは Arduino 用の 7 ポート出力に対するものです。

//uint16_t mask_tab[16] = {
  0x0001, // ch 1  --> port 1
  0x0002, // ch 2  --> port 2
  0x0004, // ch 3  --> port 3
  0x0008, // ch 4  --> port 4
  0x0010, // ch 5  --> port 5
  0x0020, // ch 6  --> port 6
  0x0040, // ch 7  --> port 7
  0x0040, // ch 8  --> port 7
  0x0040, // ch 9  --> port 7
  0xffff, // ch 10 --> all port (broadcast drum ch) 
  0x0040, // ch 11 --> port 7
  0x0040, // ch 12 --> port 7
  0x0040, // ch 13 --> port 7
  0x0040, // ch 14 --> port 7
  0x0040, // ch 15 --> port 7
  0x0040  // ch 16 --> port 7
}; // uint16_t mask_tab[]

"-v 1" オプションを付けて実行した結果は、

f0 43 1f 4c 07 10 00 
00 00 00 00 00 00 00 00 00 7f 00 00 00 00 00 00 
00 00 01 02 04 08 10 10 10 7f 10 10 10 10 10 10 
20 40 00 00 00 00 00 00 00 60 00 00 00 00 00 00 
f7 


これを表示させると左の写真のようになります。
ただし、表示させるためにビットマスク・テーブルを表す 6 バイト目の「10」を「00」に書き換えています。
システムメッセージ・テーブルの例を下に示します。
これは各チップ共通です。

//uint16_t sysmsg_tab[16] = {
  0xffff, // 0xF0, Start of Exclusive (arbitrary)
  0xffff, // 0xF1, MTC Quarter frame (2 byte)
  0xffff, // 0xF2, Song Position Pointer (3 byte)
  0xffff, // 0xF3, Song Select (2 byte)
  0x0000, // 0xF4, undefined
  0x0000, // 0xF5, undefined
  0xffff, // 0xF6, Tune Request (obsolete)
  0xffff, // 0xF7, End of Exclusive
  0xffff, // 0xF8, MIDI Clock
  0x0000, // 0xF9, undefined
  0xffff, // 0xFA, Start
  0xffff, // 0xFB, Continue
  0xffff, // 0xFC, Stop
  0x0000, // 0xFD, undefined
  0xffff, // 0xFE, Active sensing
  0x0000  // 0xFF, System Reset (obsolete)
}; // uint16_t sysmsg_tab[]

"-v 2" オプションを付けて実行した結果は、

f0 43 1f 4c 07 20 00 
7f 7f 7f 7f 00 00 7f 7f 7f 00 7f 7f 7f 00 7f 00 
7f 7f 7f 7f 00 00 7f 7f 7f 00 7f 7f 7f 00 7f 00 
60 60 60 60 00 00 60 60 60 00 60 60 60 00 60 00 
f7 


これを表示させると左の写真のようになります。
ただし、表示させるためにシステムメッセージ・テーブルを表す 6 バイト目の「20」を「00」に書き換えています。