Bluetooth MIDIでSystem Exclusive

Bluetooth MIDIでMacとArduino, WindowsとArduinoを接続する記事はリンクを参照して下さい。 Bluetooth MIDIのモジュールは、RedBearLab.comの Bluetooth 4.0 Low Energy BLEシールド v2.1を使っています。

MacがBluetooh MIDIでSystem Exclusiveを送信する際、どのようなフォーマットで送信するかを確認します。Arduino のプログラムは以下の通りです。Bluetoothを受け取って、シリアルに書き出しています。

#include "RBL_nRF8001.h"

void setup() {
  Serial.begin(115200);
  //BLE initialize
  ble_set_pins(9, 8);
  ble_set_name("BlueMIDI");
  ble_begin();
}

void loop() {
  uint8_t c[20];
  uint8_t cnt=0;
  uint8_t i;

  while ( ble_available() )
  {
    c[cnt]=ble_read(); // Receive MIDI Packet
    cnt++;
  }
  if(cnt){
    for(i=0;i<cnt;i++){
      Serial.print(c[i],HEX);
      Serial.print(" ");
    }
    Serial.println(" ");
  }
  ble_do_events();
 }
System Exclusiveのフォーマット

System Exclusiveの送信は Send and Receive MIDIを使います。 Audio MIDI設定で、MIDIの設定を見て見ると以下のようにBluetooth MIDI Deviceが接続されています。

Send and Receive MIDI でDeviceポートにBLIDInoを選択します。

送信ボックスに以下ののような9バイト(F0とF7を加えると11バイト)のSystem Exclusiveを書き込みます。

Sendボタンをクリックすると、Arduinoは次のようにパケットを受信します。

BD BD F0 10 11 12 13 14 15 16 17 18 BD F7 
1 BD Timestamp High
2 BD Timestamp Low
3 F0 System Exclusive start
4-12 10 - 18 System Exclusive 9 byte
5 BD Timestamp Low
6 F7 System Exclusive end

次に32バイト(F0とF7を加えると34バイト)のSystem Exclusiveを書き込みます。

Sendボタンを押すとArduinoは以下のような2つのパケットを受信します。

1st packet:
B6 F4 F0 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20  
2nd packet:
B6 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F F4 F7  

2nd packetの先頭のB6はTimestamp Lowのみが送信されています。


1st packet
1 B6 Timestamp High
2 F4 Timestamp Low
3 F0 System Exclusive start
4-20 10 - 20 System Exclusive 17 byte
2nd packet
1 B6 Timestamp Low
2-17 21 - 2F System Exclusive 15 byte
18 F4 Timestamp Low
19 F7 System Exclusive end


System Exclusive受信実験

以下、System Exclusive受信のアルゴリズムを作成するために、 いくつかSystem Exclusiveメッセージの長さを変えて送信を試してみます。

System Exclusiveメッセージが15バイト(F0とF7を加えると17バイト)、先頭のタイムスタンプ2バイトと、 F7の手前の1バイトの計20バイト以内であれば、1つのパケットで送信されます。

送信:
F0 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E F7
受信:
BD FE F0 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E FE F7

System Exclusiveメッセージが16バイト(F0とF7を加えると18バイト)、 1つめのパケットには、先頭のタイムスタンプ2バイトを含めて19バイト、 2つめのパケットには、先頭のタイムスタンプ2バイトとF7が転送されます。

送信:
F0 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F F7
受信:
1st packet:
A1 97 F0 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
2nd packet:
A1 97 F7

System Exclusiveメッセージが17バイト(F0とF7を加えると19バイト)、 1つめのパケットには、先頭のタイムスタンプ2バイトを含めて20バイト、 2つめのパケットには、先頭のタイムスタンプ2バイトとF7が転送されます。

送信:
F0 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 F7
受信:
1st packet:
87 BF F0 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20
2nd packet:
87 BF F7

System Exclusiveメッセージが18バイト(F0とF7を加えると20バイト)、 1つめのパケットには、先頭のタイムスタンプ2バイトを含めて20バイト、 2つめのパケットには、先頭のタイムスタンプ1バイトと、1バイトSystem Exclusive さらにタイムスタンプ1バイトとのF7が転送されます。

送信:
F0 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 F7
受信:
1st packet:
A2 B9 F0 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20  
A2 21 B9 F7

System Realtime メッセージの受信実験

System Realtimeメッセージには以下の6種類があります。すべて1バイトのメッセージです。

F8 タイミングクロック
FA スタート
FB コンティニュー
FC ストップ
FE アクティブセンシング
FF システムリセット

この中で、よく使われるのはF8(タイミングクロック)とFE(アクティブセンシング)の2つです。 スタート、コンティニュー、ストップはシーケンサーの制御用です。

下の表に送信メッセージと受信パケットを示します。送信側のメッセージをSend and Receiveの 送信ボックスに(複数メッセージの場合もそのまま)記載しています。 受信側の青文字はタイムスタンプです。 タイムスタンプの値は毎回違う値になります。

送信 受信
F8 8D A5 F8
FE A6 96 FE
F8 FE AE AF F8 AF FE
90 40 7F F8 A4 EF 90 40 7F EF F8
F8 90 40 7F 9C 94 F8 94 90 40 7F
F0 10 12 13 F7 F8 96 C6 F0 10 12 13 C6 F7 C6 F8

System Realtimeメッセージといずれかのメッセージが同時に送信される場合、 System Realtimeメッセージは1つのパケットで送られるわけでは無く 1バイトのタイムスタンプを挟んで、1つのパケットで送信されています。

F0 10 12 F8 13 F7 A0 8B F0 10 12 8B F7 8B F8 13 8B F7

ただし、System Exclusiveのデータの間に、F8を挟み込むと、挟み込まれたところでSystem Exclusiveが中断され、 F7が送信されます。やってはいけない、ということかと思います。


System Realtime メッセージの受信アルゴリズム

Bluetoothパケットを受け取って、どのように処理すればMIDIを抽出できるか、アルゴリズムを考えてみます。 通常のチャンネルメッセージでは、パケットの先頭は必ず2バイトのタイムスタンプとなっています。しかし、 上の実験のように、パケットにSystem Exclusiveの途中から入っている時、 先頭の2バイトでは無く、1バイトがタイムスタンプとな場合があります。 これにより、アルゴリズムの先頭で、System Exclusiveの途中かどうかを判断しないといけないのでは と考えました。System Exclusiveの途中である場合は、次のアルゴリズムの丸で囲った"A"へ移行します。

また、最初のMIDI Statusを判断する際、Channel MessageとSystem Realtime Messageはそれぞれ下 で処理されます。System Exclusiveである場合は丸で囲った"B"へ移行します。

一つのパケットに複数のメッセージが入っている場合、メッセージの間には1バイトのタイムスタンプが挿入されているはずなので パケットの最後に達していない時にはこれを取得するようにしています。