ジャバでMIDIコントローラを扱うメモ

最近VJ的なパフォーマンス用ソフトを作ったりしていたのですが、その過程でMIDIコントローラを使いたいと思い、評判が良くて、手ごろな値段のKorg nanoKONTROL2を購入しました。
f:id:soma_arc:20150825191203j:plain
ジャバは標準でMIDIが扱えますが、MIDIコントローラを扱う情報が少なく、見つけづらかったのでメモしておきます。

ジャバのMIDI関連に関しては以下のブログエントリが詳しいです。
http://blog.bonar.jp/entry/20090322/1237711377
このエントリでは自作Receiverを定義して、MIDIキーボードから受け取ったMIDIの演奏情報にフィルターをかけるようなことをしているみたいですね。

このサイトだけで大体は分かったのですが、コントローラにReceiverを登録する際にひっかかりました。僕の環境では以下のような機器が検出されていたのですが、僕は最初、External MIDI Portの方に接続しようとして失敗していました。nanoKONTROL2が二つ検出されているのがよくわかりませんが、External MIDI Portの方にはReceiverを登録できないみたいですね。

Gervill -- Software MIDI Synthesizer
nanoKONTROL2 -- No details available
Microsoft MIDI Mapper -- Windows MIDI_MAPPER
Microsoft GS Wavetable Synth -- Internal software synthesizer
nanoKONTROL2 -- External MIDI Port
Real Time Sequencer -- Software sequencer

とりあえず以下のstackoverflowの回答のコードを使えば検出され、使用できるMIDI機器全てにレシーバーを追加することができます。
http://stackoverflow.com/questions/6937760/java-getting-input-from-midi-keyboard
検出されたMIDI信号はReceiverのsend関数で処理されます。僕はとりあえず以下のような関数を用意しました

public void send(MidiMessage message, long timeStamp) {
	if (message instanceof ShortMessage) {
		ShortMessage sm = ((ShortMessage)message);
		switch(sm.getCommand()) {
		case ShortMessage.CONTROL_CHANGE:
			System.out.println(sm.getChannel());
			System.out.println(sm.getData1());
			System.out.println(sm.getData2());
			break;
		case ShortMessage.NOTE_ON:
			System.out.println("note on");
			break;
		default:
			System.out.println("default");
			break;
		}
	}
	System.out.println("midi received");
}

ShortMessageは信号の種類ですが、MIDIコントローラでは全てCONTROL_CHANGEになるみたいです。getData1でMIDIコントローラにおけるつまみやボタンのid、getData2でコントローラの0から127までの値が取得できます。
ちなみにボタンは押されたときに127、指が離されたときに0が返るようになっています。

残念なことにidからはどれがつまみでどれがスライダーかはわからないので自分で調べる
必要があるみたいですね。
nanoKONTROL2に関しては自分で調べて以下のようなEnumを定義しておきました。
このIDをキーにして処理を呼び出すようにすればよいのではないでしょうか。

public interface MidiController {
	public int getId();
}
public enum KorgNanoControl2 implements MidiController{
	SLIDER1(0), SLIDER2(1), SLIDER3(2), SLIDER4(3), SLIDER5(4), SLIDER6(5), SLIDER7(6), SLIDER8(7),
	KNOB1(16), KNOB2(17), KNOB3(18), KNOB4(19), KNOB5(20), KNOB6(21), KNOB7(22), KNOB8(23),
	BUTTON_S1(32), BUTTON_S2(33), BUTTON_S3(34), BUTTON_S4(35), BUTTON_S5(36), BUTTON_S6(37), BUTTON_S7(38), BUTTON_S8(39),
	BUTTON_M1(48), BUTTON_M2(49), BUTTON_M3(50), BUTTON_M4(51), BUTTON_M5(52), BUTTON_M6(53), BUTTON_M7(54), BUTTON_M8(55),
	BUTTON_R1(64), BUTTON_R2(65), BUTTON_R3(66), BUTTON_R4(67), BUTTON_R5(68), BUTTON_R6(69), BUTTON_R7(70), BUTTON_R8(71),
	BUTTON_PREV_TRACK(58), BUTTON_NEXT_TRACK(59),
	BUTTON_CYCLE(46), BUTTON_MARKER_SET(60), BUTTON_MARKER_PREV(61), BUTTON_MARKER_NEXT(62),
	BUTTON_BACKWARD(43), BUTTON_FORWARD(44),BUTTON_STOP(42), BUTTON_START(41), BUTTON_REC(45);

	private final int id;

	private KorgNanoControl2(final int id){
		this.id = id;
	}

	@Override
	public int getId(){
		return id;
	}
}

実際に操作に使用してみると、結構便利で面白いです。しかし、描画するもののパラメータに0から127までの値をうまく対応付ける事が難しいですね。