MIDI出力

DSC09522


iPhoneでOSC送信するよ、ふふん」とか言いつつ作りかけて飽きて盛大に放置しつつ、業務的にとある用途で必要になったのでMIDI送信をお試し。
なんだかんだで既製品なDAW側のコントロールプロトコルMIDIだったりするので、今更だけど作ってみる。
電源供給も含めてUSB-MIDIがスマートいいんだけど、素人がいきなりそこに突撃するのは無謀すぎるのでMIDI I/Oを経由する普通のMIDIコントローラ的なことをやってみる。


DSC09523


先ずは10年くらい死蔵してたUnitor8mkIIを引っ張り出してきた。
最新のドライバーがLogic8にしか付属しないっていうAppleの嫌がらせのEmagicの不遇のせいでちょっと苦戦したけど、どこからともなく拾ってきたver1.9で普通に動いるっぽい。
結局Unitor8は謎の不調により戦線離脱したので上の写真はamt8。大差ないけど。


で、MIDIインターフェイスArduinoMIDIケーブルで結線するけど、5PINのDINコネクタを使う。でも使うのは3つ。

2 GND
4 +5V
5 MIDIout

どれがPINxやねんって場合はこのへん参照で。
5Vには220Ωの抵抗が必要な以外は難しい要素も無く、コネクタをブレッドボードに刺すべくピンヘッダをハンダ付け。
MIDIメッセージの送信はUARTなので、DINソケットのPIN5はArudinoのPIN1(TX)につなぐ。
あとはMIDIコンっぽくするために可変抵抗とかタクトスイッチとかを用意してハード側は準備完了。


DSC09507


まずは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メッセージの仕様がハッキリしてないと辛いので調べる。


DSC09508


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送信おわり。