MIDI出力
「iPhoneでOSC送信するよ、ふふん」とか言いつつ作りかけて飽きて盛大に放置しつつ、業務的にとある用途で必要になったのでMIDI送信をお試し。
なんだかんだで既製品なDAW側のコントロールのプロトコルはMIDIだったりするので、今更だけど作ってみる。
電源供給も含めてUSB-MIDIがスマートいいんだけど、素人がいきなりそこに突撃するのは無謀すぎるのでMIDI I/Oを経由する普通のMIDIコントローラ的なことをやってみる。
先ずは10年くらい死蔵してたUnitor8mkIIを引っ張り出してきた。
最新のドライバーがLogic8にしか付属しないっていうAppleの嫌がらせのEmagicの不遇のせいでちょっと苦戦したけど、どこからともなく拾ってきたver1.9で普通に動いるっぽい。
結局Unitor8は謎の不調により戦線離脱したので上の写真はamt8。大差ないけど。
で、MIDIインターフェイスとArduinoはMIDIケーブルで結線するけど、5PINのDINコネクタを使う。でも使うのは3つ。
2 | GND |
4 | +5V |
5 | MIDIout |
どれがPINxやねんって場合はこのへん参照で。
5Vには220Ωの抵抗が必要な以外は難しい要素も無く、コネクタをブレッドボードに刺すべくピンヘッダをハンダ付け。
MIDIメッセージの送信はUARTなので、DINソケットのPIN5はArudinoのPIN1(TX)につなぐ。
あとはMIDIコンっぽくするために可変抵抗とかタクトスイッチとかを用意してハード側は準備完了。
まずはPlaygroundにある MIDILibraryを使ってみる。
いい加減なソースを晒しとく。
#include <Debounce.h> #include <MIDI.h> #define VOLUME 0 #define SWITCH 4 #define LED 13 int currentVolume; int vol; int currentSwitchState; int switchState; Debounce debouncer = Debounce(20 , SWITCH); void setup() { pinMode(SWITCH,INPUT); pinMode(LED,OUTPUT); currentVolume = 0; vol = 0; currentSwitchState = 0; switchState = 0; MIDI.begin(); } void loop() { currentVolume = analogRead(VOLUME) / 8; if (currentVolume != vol) sendCC(currentVolume); vol = currentVolume; debouncer.update(); currentSwitchState = debouncer.read(); if (currentSwitchState != switchState) sendNOTE(currentSwitchState); switchState = currentSwitchState; } void sendCC(int ctlVal) { MIDI.send(CC, ctlVal, 1, 1); delay(100); } void sendNOTE(boolean on) { if (on) MIDI.send(NoteOn, 42, 127, 2); else MIDI.send(NoteOff, 42, 0, 2); delay(100); }
Debounceはソフトウェア的なチャタリング対策をちゃちゃっとやってくれるライブラリ。
試しに使ってみた。コンストラクタの第2引数はミリ秒。あとは見たまま。
可変抵抗の読み取り値を8で割ってるのは、MIDIのData Byteが0〜127までだから。
で送信は
void MIDI_Class::send (
byte type,
byte data1,
byte data2,
byte channel
)
にしかるべき値を渡すだけ。
引数はこんな感じ。
type | MIDI Statusの種類(とかCCとか) |
data1 | NoteOn/OffならNote Number、CCならControl Numberとか |
data2 | velocityとかControll Valueとか |
channel | MIDIチャンネル |
2ByteのMIDIメッセージの場合のdata2は0で。Channel PressureとProgram Changeだけだけど。
MIDI Statusの定義は以下。
#define NoteOff 0 #define NoteOn 1 #define ATPoly 2 #define CC 3 #define PC 4 #define ATCanal 5 #define PitchBend 6 #define System 7
詳しくはMIDI.hか Reference参照で。
SystemはSystemExclusiveね。今時そんなの使う人居ないと思うけど。
で、MIDI_Class::sendが中で何やってるかっちゅうと、
UART.print(type); UART.print(data1, BYTE); UART.print(data2, BYTE);
なので、細かいことを無視したら
Serial.print(type, BYTE); Serial.print(data1, BYTE); Serial.print(data2, BYTE);
てやってもいいはず。
でも、そうなるとMIDIメッセージの仕様がハッキリしてないと辛いので調べる。
MIDIメッセージは1ByteのStatus Byteと任意のData Byteで構成されてる。
で、Data Byteは0〜127が割り当てられてて、それ以降はStatus Byteになってる。
Data ByteがNote Numberだったら
Octave | C | C# | D | D# | E | F | F# | G | G# | A | A# | B |
---|---|---|---|---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
0 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 12 | 22 | 23 |
1 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
と9オクターブのG(127)まで続く。
Status Byteは全部は書き出せないけど大体こんな感じ。
10進数 | 16進数 | Status | Data1 | Data2 |
---|---|---|---|---|
128〜143 | 80〜8F | Note off | Note Number | Velocity |
144〜159 | 90〜9F | Note on | Note Number | Velocity) |
160〜175 | A0〜AF | After Touch | Note Number | Key Pressure |
176〜191 | B0〜BF | Control Change | Control Number | Value |
192〜207 | C0〜CF | Program Change | Program Number | |
208〜223 | D0〜DF | Channel Pressure | Pressure | |
224〜239 | E0〜EF | Pitch Bend | LSB(0〜127) | MSB(0〜127) |
242 | F2 | Song Position | LSB | MSB |
243 | F3 | Song Select | 0〜127 | |
250 | FA | START | ||
252 | FC | STOP | ||
254 | FE | Active Sensing | ||
255 | FF | Reset |
MIDIチャンネルは16chまであるんで、NoteOffからPBまではStatus Byteが各チャンネルそれぞれに割り当ててある。
例えば、3chのCをNoteOnする場合は
146 45 127
なので、
Serial.print(0x92, BYTE); Serial.print(0x2d, BYTE); Serial.print(0x7f, BYTE);
とか。
MIDIの受信側はMAXでささっと作って動作確認。
ちゃんと動作をチェックするならmidiparseオブジェクトとか使うんだろうけど、そういうのはもっと作り込んだときってことで。
さ、これでMIDI送信おわり。