【MIDI】SysEx（システム・エクスクルーシブ）用のデータ変換ツール作成

# はじめに
MIDIのSystemExclusiveMessageの生成、解析を行う必要があったので変換ツールを作りました。
需要はあまりないような気もしますが、そのやり方をご紹介します。

# System Exclusive Messageのデータ仕様
MIDIの規格では、最上位ビットが1のデータは「ステータスバイト」と定められています。
そして、最上位ビットが0のデータのみが「データバイト」として扱われます。

MIDIメッセージの種類の一つであるSystemExclusiveMessageでは、任意のデータを送受信することができます。
このとき、最上位ビットが1となる0x80〜0xFFの値を送受信できるようにするため、データ変換が必要となります。

## データ変換仕様
例えば、こんな8バイトのデータを送受信したい場合を考えます。
`1a` 〜 `8h` は各ビットを指します。
![Timeline 1_01_00_31_13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/b324692c-5720-4501-82d4-b3aac7612a0c.jpeg)

### 一巡目
まず、8バイト目は次の処理に回されるので一旦退場してもらいます。
![MIDIのSysExMessage用バイナリ変換ツール1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/e098e165-cb37-4448-8b9e-c774d91e6652.gif)

そして、1〜7バイト目の下位7ビットをそれぞれ1バイト分後ろにずらします。
![MIDIのSysExMessage用バイナリ変換ツール2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/5c3ebb3e-4a21-4e0d-b939-dd8c6708e175.gif)

次に、最上位ビットを空けないといけないので、各バイトの最上位ビットを空いた1バイト目に並べます。
![MIDIのSysExMessage用バイナリ変換ツール3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/9abdc8ff-b27f-4404-9fb2-79d8098248ec.gif)

最上位ビットが空いたので `0` を入れて7バイト分のデータの変換は完了です。
![MIDIのSysExMessage用バイナリ変換ツール4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/a803322b-2953-4a27-bf70-6e771765c229.gif)

### 二巡目
続いて二巡目です。
退場してもらっていた元8バイト目を処理します。
処理するのはあと1バイト分のみなので、ここでは1バイト追加して合計2バイトのデータを生成します。

一巡目と同じように、元8バイト目の下位7ビットを1バイト分後ろにずらします。
![MIDIのSysExMessage用バイナリ変換ツール5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/b721bcbc-b52f-4d12-98d5-c75bfca36bdb.gif)

そして最上位ビットを空けるために、最上位ビットを一巡目と同じ形式で9バイト目に並べます。
![MIDIのSysExMessage用バイナリ変換ツール6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/66940a2a-1e14-4e6a-b7ec-abcb106ae3e5.gif)

最上位ビットと、9バイト目で余っている部分に `0` を入れてデータ変換完了です。
![MIDIのSysExMessage用バイナリ変換ツール7.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1605307/ad93fe00-3caf-4c61-9eb1-9403cb46eeaf.gif)


このように、7バイト分のバイト配列から、最上位ビットに `0` の入った8バイトのバイト配列を生成することを繰り返して変換する仕組みとなっています。


# 変換プログラム
仕様がわかったので、C#で変換プログラムを作りました。

## エンコード（8bit→7bit）

```cs:バイト配列をMIDI SysEx用の7bitのバイナリにエンコードする処理
var eightBitsStr = "48 65 6C 6C 6F 20 4D 49 44 49 21";
// ハイフンとスペースを除去
var eightBitsBytes = Convert.FromHexString(eightBitsStr.Replace(Environment.NewLine, string.Empty).Replace(" ", string.Empty));

var (sevenBitsByteLength, remainder) = Math.DivRem(eightBitsBytes.Length * 8, 7);
if (remainder > 0)
	sevenBitsByteLength++;

Span<byte> sevenBitsBytes = new byte[sevenBitsByteLength];

// 8bit文字7byte分→7bit文字8byte分に変換
for (int i = 0, j = 0; i < eightBitsBytes.Length; i += 7, j += 8) {
	ReadOnlySpan<byte> source = i + 7 <= eightBitsBytes.Length ? eightBitsBytes[i..(i + 7)] : eightBitsBytes[i..];
	Span<byte> dest = j + 8 <= sevenBitsByteLength ? sevenBitsBytes[j..(j + 8)] : sevenBitsBytes[j..];

	for (int k = 0; k < source.Length; k++) {
		// 各byteの再上位bitは1byte目に集める
		dest[0] |= (byte)((source[k] & 0x80) >> (k + 1));
		// 残りの7bitのみで1byte分を構成
		dest[k + 1] = (byte)(source[k] & 0x7F);
	}
}

// スペース区切りの文字列に変換
var sevenBitsStr = BitConverter.ToString(sevenBitsBytes.ToArray()).Replace('-', ' ');
```

## デコード (7bit→8bit)

```cs:MIDI SysEx用の7bitのバイナリをバイト配列にデコードする処理
var sevenBitsStr = "00 48 65 6C 6C 6F 20 4D 00 49 44 49 21";
// ハイフンとスペースを除去
var sevenBitsBytes = Convert.FromHexString(sevenBitsStr.Replace(Environment.NewLine, string.Empty).Replace(" ", string.Empty));

var (eightBitsByteLength, _) = Math.DivRem(sevenBitsBytes.Length * 7, 8);

Span<byte> eightBitsBytes = new byte[eightBitsByteLength];

// 7bit文字8byte分→8bit文字7byte分に変換
for (int i = 0, j = 0; i < sevenBitsBytes.Length; i += 8, j += 7) {
	ReadOnlySpan<byte> source = i + 8 <= sevenBitsBytes.Length ? sevenBitsBytes[i..(i + 8)] : sevenBitsBytes[i..];
	Span<byte> dest = j + 7 <= eightBitsByteLength ? eightBitsBytes[j..(j + 7)] : eightBitsBytes[j..];

	for (var k = 1; k < source.Length; k++) {
		// 1byte目の中から該当1bitを抜き出し
		var topBit = (byte)((source[0] & (0x80 >> k)) << k);
		// 抜き出した1bitを頭に付けて8bit分を構成
		dest[k - 1] = (byte)(source[k] | topBit);
	}
}

// スペース区切りの文字列に変換
var eightBitsStr = BitConverter.ToString(eightBitsBytes.ToArray()).Replace('-', ' ');
```

# 変換ツール公開
上記の変換処理を行うツールをWebアプリとして公開しました。

https://midi78.lilytech-lab.com/

もし必要な方がいれば（いるのかな…？）、ご自由にお使いください。

ソースコードはこちらに置いています。

https://github.com/lilytech-lab/MidiSevenBitsAndBytesConverter


# 紹介動画

この記事の内容と変換ツールの使い方を動画にまとめました。


<iframe width="560" height="315" src="https://www.youtube.com/embed/EpmxKj_oh4s?rel=0" frameborder="0" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>