Java Sound APIを使ってMIDIファイルを再生する方法（使用するMIDIデバイスを指定する）

#はじめに
[前回の記事](https://qiita.com/absol_abcX/items/e9c230b808e522154ae5)の続きです。

#概要
前回のMIDIファイル再生サンプルに、使用するMIDIシンセサイザー選択機能を実装します。
以下、実際の実行画面です。

```
[0] Gervill
[1] LoopBe Internal MIDI
[2] Microsoft MIDI Mapper
[3] CoolSoft MIDIMapper
[4] Microsoft GS Wavetable Synth
[5] VirtualMIDISynth #1
[6] LoopBe Internal MIDI
[7] Real Time Sequencer

使用するMIDIデバイスの番号を入力 >> 4
[Microsoft GS Wavetable Synth]を開いています...

MIDIファイルのパス >> sample.mid
再生中...
適当に文字列を入力すると終了します。
>>a
停止
```
ちなみに、
０番目の「Gervill」というデバイスがJavaのライブラリが提供するMIDIシンセサイザーです。
デフォルトでは、こちらのデバイスが指定されます。

今回使用している「Microsoft GS Wavetable Synth」は、Windowsで標準搭載されているシンセサイザーです。

#サンプルコード
```java:SamplePlayer.java
import java.io.File;
import java.io.IOException;
import java.util.Scanner;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;

public class SamplePlayer {

    public static void main(String[] args) {
        // MIDIデータを再生するハードウェア/ソフトウェア・デバイスのインスタンス。
        Sequencer sequencer = null;

        Scanner scanner = new Scanner(System.in);

        try {
            // Sequencerインスタンスを取得する。
            sequencer = MidiSystem.getSequencer(false);

            // デバイスを開き、リソースを獲得する。
            sequencer.open();

            // 使用可能なMIDIデバイスの一覧を取得する。
    		MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
    		for (int i=0; i<infos.length; i++) {
    			String line = String.format("[%s] %s", i, infos[i].getName());
    			System.out.println(line);
    		}
    		System.out.println();

    		int index = 0;
    		while (true) {
    			// コンソール入力から使用するMIDIデバイスのインデックスを取得する。
	    		System.out.print("使用するMIDIデバイスの番号を入力 >> ");
	    		String str = scanner.next();

	    		try {
	    			index = Integer.parseInt(str);
	    		}
	    		catch (NumberFormatException nfe) {
	    			continue;
	    		}
	    		break;
	    	}

    		// システムに目的のデバイスを表すデバイス情報を持った、MIDIデバイスを要求する。
    		MidiDevice device = MidiSystem.getMidiDevice(infos[index]);

    		System.out.println("[" + device.getDeviceInfo().getName() + "]を開いています...");

    		// MIDIデバイスを開く。
    		if (device.isOpen() == false) {
    			device.open();
    		}

    		// シーケンサのMIDI OUTとMIDIデバイスのMIDI INを接続する。
			Receiver receiver = device.getReceiver();
			sequencer.getTransmitter().setReceiver(receiver);
        }
        catch (MidiUnavailableException e) {
            e.printStackTrace();
        }
        System.out.println();

        // コンソール入力からMIDIファイルのパスを取得する。
        System.out.print("MIDIファイルのパス >> ");
        String path = scanner.next();

        try {
            // MIDIファイルからMIDIデータ(Sequenceオブジェクト)を取得。
            File file = new File(path);
            Sequence sequence = MidiSystem.getSequence(file);

            // 取得したMIDIデータをシーケンサに設定する。
            sequencer.setSequence(sequence);
        }
        catch (InvalidMidiDataException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }

        // シーケンサー再生
        sequencer.start();
        System.out.println("再生中...");

        // キー入力待ち
        System.out.println("適当に文字列を入力すると終了します。");
        System.out.print(">>");
        scanner.next();
        scanner.close();

        // シーケンサー停止
        sequencer.stop();
        System.out.println("停止");

        // シーケンサーを閉じ、使用していたリソース解放する。
        sequencer.close();
    }
}
```
#解説
###使用可能なデバイス情報一覧を取得
使用可能な全MIDIデバイス情報のオブジェクト配列を取得します。

```java
// 使用可能なMIDIデバイスの一覧を取得する。
MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
```

###デバイスのオブジェクト取得
取得したデバイス情報のオブジェクトを使用して、getMidiDeviceを呼び出すことにより、対応するデバイスを取得できます。
取得したデバイスで、openを呼び出すことで操作可能にします。

```java
// システムに目的のデバイスを表すデバイス情報を持った、MIDIデバイスを要求する。
MidiDevice device = MidiSystem.getMidiDevice(infos[index]);

// MIDIデバイスを開く。
if (device.isOpen() == false) {
   device.open();
}
```

###シーケンサーとデバイスを接続
MIDIデバイスのクラスが提供するインターフェースには、以下の2つがあります。

| インターフェース名     | 概要 |一般的に使用されているもの|
|:-----------------|:-------------------------|:------------------|
| Transmitter     | MIDIデータの送信を行う。 | シーケンサー・MIDI入力機器等 |
| Receiver        | MIDIデータの受信を行う。 | シンセサイザー・MIDI出力機器 |

今回は、シーケンサのMIDI送信ポート(Transmitter)に、指定したMIDIデバイスのMIDI受信ポート(Receiver)を接続します。

```java
// シーケンサのMIDI OUTとMIDIデバイスのMIDI INを接続する。
Receiver receiver = device.getReceiver();
sequencer.getTransmitter().setReceiver(receiver);
```
そうすると、だいたいこのような構成イメージになります。![デバイス構成.png](https://qiita-image-store.s3.amazonaws.com/0/344334/d5119e80-4bd2-1cec-40de-c666a310a247.png)

以上で、デバイスの接続は完了です。