fèng

遥か遠くの記憶の残滓

MIDIのシステムエクスクルーシブでバイナリを送る

  • 投稿日:
  • by
  • Category:

「すーぱーおくてっと!」でユーザー音色を使用できるようにするときに一番悩んだポイントだったので記事にしておく。ユーザー音色のデータをどうやってMIDIで転送するか、という話です。

MIDIの仕様の説明は省きます、気になる人はMIDI1.0の規格書が全文無料で公開されているので読んでください。分かりやすいし、MIDIパーサの実装例もあるので大変良い資料です。

さて、MIDIは上位1bit(MSB)をステータス/データの判別に使用しているため値として使用できるのは7bitです。しかし転送したいデータは8bit/1バイトなので足りません。そこで次の二つの方法が一般的に考えられます。

  • 7bitに収まるように転送するデータを工夫する
    • バイナリデータを送りたいのでこれは無理
    • ASCIIテキストであればできるような気もする(BASE64とか)
  • 8bitのデータを分割して収める
    • 今回はこっち

後者の方法を今回は取ります。ネットの海を眺めていると8bitデータを4bit/4bitに分割して送るスタイルが多いような気がしました。が、これは次の理由から使わないことにしました。

  • データの量が増えすぎる
    • 1音色のデータは最大1KBに達するため、単純にデータが2KBに膨れ上がり転送に時間がかかる
    • Windowsは一度に送れるMIDIメッセージの長さが決まっており(1回あたり256バイト)あんまり長くなると良くない(気がする)
  • 3bit分無駄になる
    • ステータス1bit+データ4bit=5bitとなり空白の3bitが生まれる
    • 通信ステータスとかの管理に使うという手もあるかもしれないが、3bitだと正直微妙
  • ありきたりで面白くない
    • (殴
    • 昔このフォーマットで実装したことがある

そこで今回はもっと効率の良い(そして実装がそこまで面倒ではない)方法を考えることにしました。

最初に提示したMIDI1.0の規格書を眺めているとちょうどいいフォーマットを見つけました。例えば次のようなデータがあったとします。

AAAAaaaa BBBBbbbb CCCCcccc DDDDdddd EEEEeeee FFFFffff GGGGgggg(7bytes)

まず、先ほど挙げた4bit/4bit分割の場合は次のようになります。

0000AAAA 0000aaaa 0000BBBB 0000bbbb 0000CCCC 0000cccc 0000DDDD 0000dddd 0000EEEE 0000eeee 0000FFFF 0000ffff 0000GGGG 0000gggg(14bytes)

14bytesになりました。

次に元データをこのような方法で変換してみます。

  • 最初のbyteに続く7bytesのsign-bit(MSB)を格納する
  • 続く7bytesのデータは下位7bitのみを格納する
  • データ長が7bytesの倍数でない場合にはsign-bitの格納部分に0埋めをする(データは0埋めしない)

すると次のようになります。

0ABCDEFG 0AAAaaaa 0BBBbbbb 0CCCcccc 0DDDdddd 0EEEeeee 0FFFffff 0GGGgggg(8bytes)

これによりデータ長は8bytesになり元データと比較して12%の増加で済みます。

大昔MIDIの規格書を穴が開くほど読んでいたころの微かな記憶をもとに思いつきましたが今見たら全く同じことが78ページに書かれていました。

これだけだと本当に規格書と書いていることが同じなので実装例を示しておきます。もっとうまく実装できると思うけどあくまで一例ってことで。チェックサムとかも入れていない、実際には入れたほうがいいかもしれない。

エンコード(8bit->7bit)

int encode(uint8_t* dest,uint8_t* src,size_t length){
int block = 0;
int counter = 0;
for (int i = 0; i < length; i++) {
if(counter % 7 == 0){
for(int j = 0;j < 7;j++){
if(src[counter + j] & 0x80){
dest[block * 8] |= (1 << (6 - j));
} else {
dest[block * 8] &= ~(1 << (6 - j));
}
}
block++;
}
dest[block + counter++] = src[i] & 0x7F;
}
return counter + block;
}

destには格納先の配列へのポインタ、srcは変換対象の配列へのポインタ、lengthは元データの長さ。ここで、destの配列長は十分な長さが事前に確保されていることが前提です。

なんか無限に実装うまくいかなくてしばらく頭を抱えていた。

デコード(7bit->8bit)

int decode(uint8_t* dest,uint8_t* src,size_t length){
int counter = 0;
for (int i = 0; i < length; i++) {
if (i % 8 == 0) {
/* Head */
for (int j = 0; j < 7; j++) {
if (src[i] & (1 << (6 - j))) {
dest[counter + j] = 0x80;
} else {
dest[counter + j] = 0x00;
}
}
} else {
dest[counter++] |= src[i];
}
}
return counter;
}

じつはデコードのほうが処理が簡単です。引数の意味はさっきと同じ。

サンプルコード

int main(void){
uint8_t original[] = {0x55,0xAA,0xFF,0x80,0x7F,0x55,0x52,0x34,0x7F};
uint8_t encoded[32];
uint8_t decoded[32];
printf("original Length:%d\n",sizeof(original));
printf("Original:");
for(int i = 0;i < sizeof(original);i++){
printf("%02X ",original[i]);
}
printf("\n");
int length = encode(encoded,original,sizeof(original));
printf("encoded Length:%d\n",length);
printf("Encoded :");
for(int i = 0;i < length;i++){
printf("%02X ",encoded[i]);
}
printf("\n");
length = decode(decoded,encoded,length);
printf("decoded Length:%d\n",length);

printf("Decoded :");
for(int i = 0;i < length;i++){
printf("%02X ",decoded[i]);
}
printf("\n");
return 0;
}

stdio.hとstdint.hをincludeする必要があります。単にエンコードしたデータをデコードしているだけ。

あとはこれをXGパラメータセットのデータ部分に乗せるだけで音色転送の処理は完了。バイナリデータが送れれば画像もサンプリング音声も送れるので夢が広がリング。特に画像はデータ量も多いので恩恵を受けやすいんじゃないかなと思います。

以上。

MIFES11を買った

  • 投稿日:
  • by
  • Category:

最近あんまり記事を書いてなかったなと思ったので何かしら書いてみる。ということで最近買ったエディタの話でも。

MIFESといえばDOS時代からある老舗エディタですが、僕が生まれたころにはとっくにDOSなんて淘汰されておりリアルタイムでは触ることはなかった。ただまぁここ数年PC-98に触れるようになりDOSも使ってきたがその時のエディタはVzだったのでここでも触る機会はなかった。

そんなこんなで、ふと某社のゲームエンジンの推奨開発環境がMIFESかサクラエディタだったことを思い出しMIFESの価格を見てみると結構高くて驚いた。秀丸エディタのライセンスが大体4,000円くらいなのに対してMIFES11は16,500円なのでかなりの高級品

見た感じ結構UIが良さげだったのと、最近10->11にバージョンアップしたらしく64bit対応を果たしたようだったのでこの機に買ってみることにした。

アウトライン表示とかはちゃんとやってくれるが、Dominoの定義ファイルだとちょくちょくエラーが出て何とも言えない感じ。

文章を書くにはよさそうだがプログラミングに向いているかといわれると結構微妙......というかVS Codeが無料で万能すぎるのが悪いだけな気もする。

まだ買ってからあまりきちんと使えていないので、正しく評価できるのは1年以上後かな。

もう一個、どうでも良い話題として、この記事からリッチテキストエディタではなくブロックエディタで書き始めた。

既に「すーぱーおくてっと!資料室」ではブロックエディタを使っていて、結構よかったので記事にもこれを使うことに。なぜかCloudflare Tunnel経由で画像をアップロードすると502が返ってきて使えないので画像が絡むときはローカルIP直叩きでログインしている。

3Dプリンタのあれこれ

  • 投稿日:
  • by
  • Category:

最近、PETGのフィラメントを使い始めたわけだがPLAと比べるとどうも扱いにくい印象。

試行錯誤してなんとか品質も安定してきたので少しまとめておく。

まず、ビルドプレート直だとまったく定着しない。マスキングテープ貼ったら良い感じになる。スティックのりは使ってないのでわからん。ただ、マスキングテープだと今度は食いつきがよすぎて剝がすのに苦労する。

次に、スライサは当初Curaを使ってチクチクパラメータを設定していたが結局プリンタ同梱のスライサのテンプレートを使うのが最適だとわかった。温度の微調整くらいはするが他はほぼ弄ってない。エクストルーダ225°C/ヒートベッド70°Cで運用している。

そして、重い腰を上げてOctoPiを導入したが、これもこれで微妙に面倒くさい。何が面倒くさいってRaspberryPiもプリンタもUSBがMicroBなところ。弊宅ではMiniBかTypeCがメインなのでMicroBのケーブルはほとんどない。探すのに30分くらいかかった。

インストールやらなんやらは比較的スムーズにできた、ただRaspberry Pi 3A+を使ったのが原因だと思うがカメラ映像のストリーミングがかなりもたつく。それ以外は特に不満無し、MicroSDをいちいち抜き差ししなくても良くなったので効率は多少上がったかもしれない。

結論、余計なことはしないほうが良い。

身も蓋もない話だけど実際そんな感じ。

日記20240301

  • 投稿日:
  • by
  • Category:

完全に腑抜けになってしまって諸々に手がつかない。

基板も届かないし何も進んでいない、おわり。

日記20240226

  • 投稿日:
  • by
  • Category:

今日の出来事

  • 労働

労働した(5.75h)。

PETGのフィラメントを使い始めたがなかなか難しい。スライサーによってもだいぶ仕上がりがちがうっぽい。Curaだと結構微妙な感じ。

大学の寮にはプリンタ持っていくの難しいので向こうで買いなおすのがよさそうである。

日記20240225

  • 投稿日:
  • by
  • Category:

今日の出来事

  • 労働

労働した(5.75h)。そして薄型PS2(SCPH-75000)を買った。実は30000番台を持っているのだがディスクの読み取りが若干怪しいのでこの機に買い替え。

とはいってもBBユニットが刺さらないのでバックアップの起動がかなり面倒になる。LANかUSBになるがそれはそれで遅すぎて使い物にならないと思われるので何か考えたいところ。直接IDEを引き出す方法もあるが熱対策とそもそもの手間が辛いので現実的ではないだろう。

日記20240224

  • 投稿日:
  • by
  • Category:

今日の出来事

  • 労働

労働した(5.75h)。今日からまた三連勤だ。

日記20240223

  • 投稿日:
  • by
  • Category:

今日の出来事

  • 知人と遊んだ

大須の佐古前装備と周辺のハドフを巡った。いろいろ買えてよかった。

日記20240222

  • 投稿日:
  • by
  • Category:

今日の出来事

  • 労働

労働した(5.75h)。明日は休みでまた3連勤。3月のシフトも入れられてしまった、もう休ませてくれ。

日記20240221

  • 投稿日:
  • by
  • Category:

今日の出来事

  • 労働

労働した(5.75h)。晩飯に寿司を食いに行った、ガッツリ食うタイプの人間ではないため実は安上がり。

自宅のメインPCにUbuntu22.04をインストールした。使用しているUSBオーディオ・インターフェイス(US-144 mkII)が使えないらしく大変困った。

思いつく対処方法としてはPCIe接続のSoundBlasterカードに乗り換えるか別のインターフェイスを導入するか。

後者は新しく買うことになるので面倒。無難にSBを載せたほうが良さそうである、が昔やろうとして微妙に躓いた記憶があるしRCAではなく3.5mmジャックなのも面倒くさい。暇なときにすることにする。