arduino
MIDI通信
サーチ…
前書き
このトピックの目的は、プロトコルの操作方法を示し、より複雑なアプリケーションに必要な便利な機能を徐々に追加する基本的なMIDIプログラムを紹介することです。
MIDI THRUの例
MIDI Thruはシンプルで簡単にテストできます。正しく動作すると、ArduinoプロジェクトをMIDI INからMIDI OUTの2つのMIDIデバイスの間にインストールすることができ、2つのデバイスが一緒に動作することを確認することができます。レイテンシを測定する機能を持っている場合は、シリアルバッファのキャプチャと再送信の指示が増えます。
// This is a simple MIDI THRU. Everything in, goes right out.
// This has been validate on an Arduino UNO and a Olimex MIDI Shield
boolean byteReady;
unsigned char midiByte;
void setup() {
// put your setup code here, to run once:
// Set MIDI baud rate:
Serial.begin(31250);
byteReady = false;
midiByte = 0;
}
// The Loop that always gets called...
void loop() {
if (byteReady) {
byteReady = false;
Serial.write(midiByte);
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
midiByte = (unsigned char)Serial.read();
byteReady = true;
}
}
キューを使用したMIDIスルー
// This is a more complex MIDI THRU. This version uses a queue. Queues are important because some
// MIDI messages can be interrupted for real time events. If you are generating your own messages,
// you may need to stop your message to let a "real time" message through and then resume your message.
#define QUEUE_DEPTH 128
// Queue Logic for storing messages
int headQ = 0;
int tailQ = 0;
unsigned char tx_queue[QUEUE_DEPTH];
void setup() {
// put your setup code here, to run once:
// Set MIDI baud rate:
Serial.begin(31250);
}
// getQDepth checks for roll over. Folks have told me this
// is not required. Feel free to experiment.
int getQDepth() {
int depth = 0;
if (headQ < tailQ) {
depth = QUEUE_DEPTH - (tailQ - headQ);
} else {
depth = headQ - tailQ;
}
return depth;
}
void addQueue (unsigned char myByte) {
int depth = 0;
depth = getQDepth();
if (depth < (QUEUE_DEPTH-2)) {
tx_queue[headQ] = myByte;
headQ++;
headQ = headQ % QUEUE_DEPTH; // Always keep the headQ limited between 0 and 127
}
}
unsigned char deQueue() {
unsigned char myByte;
myByte = tx_queue[tailQ];
tailQ++;
tailQ = tailQ % QUEUE_DEPTH; // Keep this tailQ contained within a limit
// Now that we dequeed the byte, it must be sent.
return myByte;
}
void loop() {
if (getQDepth>0) {
Serial.write(deQueue());
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
addQueue((unsigned char)Serial.read());;
}
}
MIDIクロック生成
// This is a MiDI clk generator. This takes a #defined BPM and
// makes the appropriate clk rate. The queue is used to let other messages
// through, but allows a clock to go immediately to reduce clock jitter
#define QUEUE_DEPTH 128
#define BPM 121
#define MIDI_SYSRT_CLK 0xF8
// clock tracking and calculation
unsigned long lastClock;
unsigned long captClock;
unsigned long clk_period_us;
// Queue Logic for storing messages
int headQ = 0;
int tailQ = 0;
unsigned char tx_queue[QUEUE_DEPTH];
void setup() {
// Set MIDI baud rate:
Serial.begin(31250);
clk_period_us = 60000000 / (24 * BPM);
lastClock = micros();
}
// getQDepth checks for roll over. Folks have told me this
// is not required. Feel free to experiment.
int getQDepth() {
int depth = 0;
if (headQ < tailQ) {
depth = QUEUE_DEPTH - (tailQ - headQ);
} else {
depth = headQ - tailQ;
}
return depth;
}
void addQueue (unsigned char myByte) {
int depth = 0;
depth = getQDepth();
if (depth < (QUEUE_DEPTH-2)) {
tx_queue[headQ] = myByte;
headQ++;
headQ = headQ % QUEUE_DEPTH; // Always keep the headQ limited between 0 and 127
}
}
unsigned char deQueue() {
unsigned char myByte;
myByte = tx_queue[tailQ];
tailQ++;
tailQ = tailQ % QUEUE_DEPTH; // Keep this tailQ contained within a limit
// Now that we dequeed the byte, it must be sent.
return myByte;
}
void loop() {
captClock = micros();
if (lastClock > captClock) {
// we have a roll over condition - Again, maybe we don't need to do this.
if (clk_period_us <= (4294967295 - (lastClock - captClock))) {
// Add a the ideal clock period for this BPM to the last measurement value
lastClock = lastClock + clk_period_us;
// Send a clock, bypasing the transmit queue
Serial.write(MIDI_SYSRT_CLK);
}
} else if (clk_period_us <= captClock-lastClock) {
// Basically the same two commands above, but not within a roll over check
lastClock = lastClock + clk_period_us;
// Send a clock, bypasing the transmit queue
Serial.write(MIDI_SYSRT_CLK);
}
if (getQDepth>0) {
Serial.write(deQueue());
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
addQueue((unsigned char)Serial.read());;
}
}
定義されたMIDIメッセージ
一般に、MIDIプロトコルは「メッセージ」に分類されます。メッセージには4つの一般的なクラスがあります。
- チャンネル音声
- チャンネルモード
- システムコモン
- システムのリアルタイムメッセージ
メッセージは0x80より上のバイト値で始まります。 0x7F以下の値はデータと見なされます。 127が単一のMIDIデータバイトにエンコードできる最大値を意味することを効果的に意味します。大きな値をエンコードするには、2つ以上のMIDIデータバイトが必要です。
メッセージは、中断することなく開始から終了までに送信されなければならないことを指摘しておきましょう。EXCEPT ...システムのリアルタイムメッセージ。メッセージの真ん中に挿入できる1バイトです。
チャネル音声メッセージ
ステータスD7..D0 | データバイト | 説明 |
---|---|---|
1000nnnn | 0kkkkkkk 0vvvvvvv | ノートオフイベント。このメッセージは、ノートがリリース(終了)されたときに送信されます。 (kkkkkkk)はキー(注)番号です。 (vvvvvvv)は速度です。 |
1001nnnn | 0kkkkkkk 0vvvvvvv | イベント時。このメッセージは、メモが押されたときに送信されます(開始)。 (kkkkkkk)はキー(注)番号です。 (vvvvvvv)は速度です。 |
1010nnnn | 0kkkkkkk 0vvvvvvv | ポリフォニックキー(アフタータッチ)。このメッセージは、「ボトムアウト」した後でキーを押し下げることによって、最も頻繁に送信されます。 (kkkkkkk)はキー(注)番号です。 (vvvvvvv)は圧力値です。 |
1011nnnn | 0ccccccc 0vvvvvvv | コントロールの変更。このメッセージは、コントローラの値が変更されたときに送信されます。コントローラには、ペダルやレバーなどのデバイスが含まれています。コントローラ番号120〜127は、「チャネルモードメッセージ」(下記)として予約されています。 (ccccccc)はコントローラ番号(0-119)です。 (vvvvvvv)はコントローラの値(0-127)です。 |
1100nnnn | 0ppppppp | プログラムの変更。このメッセージは、パッチ番号が変更されたときに送信されます。 (ppppppp)は新しいプログラム番号です。 |
1101nnnn | 0vvvvvvv | チャンネル圧力(アフタータッチ)。このメッセージは、「ボトムアウト」した後でキーを押し下げることによって、最も頻繁に送信されます。このメッセージは、ポリフォニックアフタータッチとは異なります。このメッセージを使用して、(現在押されているすべてのキーの)最大の単一の圧力値を送信します。 (vvvvvvv)は圧力値です。 |
1110nnnn | 0lllllll 0mmmmmmm | ピッチベンドの変更。このメッセージは、ピッチベンド(通常はホイールまたはレバー)の変化を示すために送信されます。ピッチベンドは、14ビットの値によって測定される。センター(ピッチ変更なし)は2000Hです。感度は受信機の関数ですが、RPN 0(lllllll)を使用して設定することができます。最下位7ビットです。 (mmmmmmm)が最上位7ビットです。 |
チャネルモードメッセージ
ステータスD7..D0 | データバイト | 説明 |
---|---|---|
1011nnnn | 0ccccccc 0vvvvvvv | チャネルモードメッセージ。これはコントロールチェンジ(上記)と同じコードですが、予約されたコントローラ番号120-127を使用してモードコントロールと特別なメッセージを実装します。コマンドは次のとおりです。 |
オール・サウンド・オフ。オール・サウンド・オフが受信されると、すべてのオシレーターはオフになり、音量エンベロープはできるだけ早くゼロに設定されます。 c = 120、v = 0:オール・サウンド・オフ | ||
すべてのコントローラをリセットします。 Reset All Controllersを受信すると、すべてのコントローラの値がデフォルト値にリセットされます。 (デフォルトの具体的な推奨事例を参照)。 | ||
c = 121、v = x:特定の推奨プラクティスで許可されていない限り、値はゼロでなければならない。 | ||
ローカルコントロール。ローカルコントロールがオフの場合、特定のチャンネルのすべてのデバイスがMIDIで受信したデータにのみ応答します。再生されたデータなどは無視されます。 Local Control Onは、通常のコントローラの機能を復元します。 | ||
c = 122、v = 0:ローカル制御オフ | ||
c = 122、v = 127:ローカルコントロールオン | ||
すべてのノートオフ。オールノートオフが受信されると、すべてのオシレーターがオフになります。 | ||
c = 123、v = 0:すべてのノートオフ(実際のモードコマンドの説明については、テキストを参照してください) | ||
c = 124、v = 0:オムニモードオフ | ||
c = 125、v = 0:オムニモードオン | ||
c = 126、v = M:Mono Mode On(Poly Off)ここで、Mはチャンネル数(Omni Off)または0(Omni On) | ||
c = 127、v = 0:ポリモードオン(モノオフ)(注:この4つのメッセージでもオールノートオフになります) |
システム共通メッセージ
ステータスD7..D0 | データバイト | 説明 |
---|---|---|
11110000 | 0iiiiiii [0iiiiiii 0iiiiiii] 0ddddddd --- --- 0ddddddd 11110111 | システム独占。このメッセージタイプは、製造業者が独自のメッセージ(バルクダンプ、パッチパラメータ、およびその他の非仕様データなど)を作成することを可能にし、追加のMIDI仕様メッセージを作成するためのメカニズムを提供します。メーカーのIDコード(MMAまたはAMEIによって割り当てられる)は、1バイト(0iiiiiii)または3バイト(0iiiiiii 0iiiiiii 0iiiiiii)です。 1バイトのIDのうち2つは、ユニバーサル・エクスクルーシブ・メッセージと呼ばれるエクステンション用に予約されています。デバイスがIDコードを独自のもの(またはサポートされているユニバーサルメッセージ)として認識すると、デバイスは残りのメッセージ(0ddddddd)をリスンします。それ以外の場合、メッセージは無視されます。 (注:リアルタイムメッセージのみがシステムエクスクルーシブでインターリーブされる場合があります)。 |
11110001 | 0nnndddd | MIDIタイムコードクォータフレーム。 nnn =メッセージタイプdddd =値 |
11110010 | 0lllllll 0mmmmmmm | ソング位置ポインタ。これは、曲の開始からMIDIビートの数(1ビート= 6 MIDIクロック)を保持する内部の14ビットレジスタです。 lはLSB、mはMSBです。 |
11110011 | 0sssssss | ソングセレクト。ソング・セレクトは、再生するシーケンスまたはソングを指定します。 |
11110100 | 未定義。 (予約済み) | |
11110101 | 未定義。 (予約済み) | |
11110110 | 要求を調整する。 Tune Requestを受信すると、すべてのアナログシンセサイザーはオシレーターを調整する必要があります。 | |
11110111 | 排他的な終わり。 System Exclusiveダンプを終了するために使用されます(上記を参照)。 |
システムのリアルタイムメッセージ
ステータスD7..D0 | データバイト | 説明 |
---|---|---|
11111000 | タイミングクロック。同期が必要な場合は4分音符ごとに24回送信されます(本文を参照)。 | |
11111001 | 未定義。 (予約済み) | |
11111010 | 開始。現在のシーケンスの再生を開始します。 (このメッセージにはタイミング・クロックが続きます)。 | |
11111011 | 持続する。シーケンスが停止した時点で続行します。 | |
11111100 | やめる。現在のシーケンスを停止します。 | |
11111101 | 未定義。 (予約済み) | |
11111110 | アクティブセンシング。このメッセージは、接続が生きていることを受信者に伝えるために繰り返し送信されることを意図しています。このメッセージの使用はオプションです。最初に受信すると、レシーバは300ms(最大)ごとに別のActive Sensingメッセージを受信すると予想し、そうでない場合は接続が切断されたとみなします。終了時には、レシーバーはすべてのボイスをオフにし、通常の(非アクティブセンシング)動作に戻ります。 | |
11111111 | リセット。システムのすべてのレシーバをパワーアップ状態にリセットします。これは控えめに、好ましくは手動制御下で行うべきである。特に、起動時には送信しないでください。 |
Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow