ツクールMVでmidiを鳴らす

ツクールMVでは、ゲーム内のBGMとしてmidiを鳴らすことができなくなってしまったのです。残念。
AndroidやiOSにも対応させるとなると、ファイルの他に音源を別に用意しなくてはならないmidiをサポートすることは難しかったんだろうな……。
けれども、midiファイルは容量がとても軽かったり、ループさせるのが簡単という他の形式には無い利点があるのです。PC版ツクールシリーズではVXAceまでずっと対応していたこともあり、素材がとっても多いのもグッド。これが使えないのはもったいない。というわけで……

#鳴らぬなら、鳴らしてみようmidiファイル

ちょっと検索してみると、javascriptを使ってmidiを鳴らす[MIDI.js](https://galacticmilk.com/midi-js/)（リンク先音量注意）というライブラリがあった。というわけで、これを使ってツクールMV用プラグインを作ってみよう。

まずは[githubにあるExample](https://github.com/mudcube/MIDI.js/blob/master/examples/Basic.html)と、ググったら出てきた[記事](http://qiita.com/Suna/items/68fa9ae40721cd7c55da)を参考にしつつ、midiファイルを読み込んで再生するプラグインを目指す。名前は……シンプルにMVMidiでいいや。

##外部スクリプトの追加
MIDI.jsを使うには、いくつかスクリプトを読み込む必要がある。githubからダウンロードした/incと/jsを入れたフォルダをjs/plugin下に置いておいて、動的に読み込ませればいいか……。

```javascript:MVMidi.js
      //スクリプトを追加する
      var s = new Array();
      //polyfill
      s.push(document.createElement('script'));
      s[s.length - 1].type = 'text/javascript';
      s[s.length - 1].src = "./js/plugins/MVMidi/inc/shim/Base64.js";
      s[s.length - 1].onload = function(){
        console.log("loaded Base64");
      }
```
こんな感じにscriptタグを作って配列に入れていく。
これを必要なファイルの分だけ繰り返して……

```javascript:MVMidi.js
var firstScript = document.getElementsByTagName('script')[0];
      for (var i = 0; i < s.length; i++){
        firstScript.parentNode.insertBefore(s[i], firstScript);
      }
```

最後に一気にまとめて追加、と。

ここまでの処理を一つの関数にまとめておけば、ゲーム開始時に一度だけ実行すればいい関数が出来る。一応何回呼び出しても無限にスクリプトが追加される事態にはならないようにしたけど……。

#midiを鳴らす
必要なものを入れたらさっそくmidiファイルを再生させてみよう。さっきのページを参考にしながら……

```javascript:MVMidi.js
//再生する
    var midiFile = './audio/bgm/' + midi;

    MIDI.loadPlugin({
      soundfontUrl: "http://gleitz.github.io/midi-js-soundfonts/FluidR3_GM/",

      instrument: "acoustic_grand_piano",
		onprogress: function(state, progress) {
			console.log(state, progress);
		},
		onsuccess: function() {

      //再生
      var player = MIDI.Player;
      player.loadFile(midiFile, MIDI.Player.start, function(){}, function(){
        console.log("midi file couldn't be loaded");
        return false;
      });
		}
    });
```

あ、書いてないけどmidiという変数は引数から取ってきたファイル名です。audio/bgm下に入れることを想定してます。あとでちゃんとファイルの有無のチェック入れておかなきゃね……。

MIDI.loadPluginの解説。
これはmidiファイルそのものをロードするのではなく、サウンドフォントなど再生に必要なものをロードするためのものみたいですね。
まずはサウンドフォントの指定。MIDI.jsの作者さんがMIDI.js用にリファインしたサウンドフォントをgithubに上げているのでそれをそのまま使うことに。
本当はローカルファイルに置いたほうがいいんだろうけど~~落とすのめんどくさかった~~。

instrumentは使う楽器を指定して読み込む。ここで使う楽器を全部指定しなきゃいけないのか……そう思っていた時期がわたしにもありました。後述。

onprogressはコンソール上に状態を表示するだけ。
で、ロードが成功するとonsuccessが呼ばれて実際にmidiファイルの再生を行う。

実際に再生をするのはMIDI.Player.loadFile。引数はmidiファイル、ファイルのロードに成功した時に呼び出す関数、ファイルのロードに失敗した時に呼び出す関数の三つ。成功した時に呼び出す関数にMIDI.Player.startを設定しておくと、ロードが終わったら自動的に再生してくれるってことですね。便利。

ここまで書いたら再生できるはず！いくぞー！

##が、しかし
音が鳴らない……(´・ω・｀)

サウンドフォントもロードして、ちゃんとMIDI.Player.startも実行されているみたいなのに、どうして？
色々とデバッグしてみたら、どうやら再生時間が０になっているっぽい。そんなバカな。

さっきの2つのページをよーく見てみると……あ、読み込んでないスクリプトがある……。/inc/jasmid下のファイル全部読み込んでなかった……。どうやらこのフォルダに入っているのがmidiファイルの読み込みをする部分らしい。そりゃちゃんと鳴らないわ……。 ~~エラー吐いてくれと思わないこともない~~

追加して今度こそ。いくぞー！

おお鳴った。感動。とうとうMVでもmidiが鳴らせた……。
でも聞いてみると少しおかしい。楽器がピアノなのは読み込んだのがそれしかないからいいとして、音が少ないような。

midiファイルの中身を見てみると、どうもチャンネル１の音しか鳴っていないようだ。なんとかして全てのチャンネルの音を鳴らせないものか……。

ググっていたら[こんなページ](https://github.com/mudcube/MIDI.js/issues/184)を発見。要するに、少し改造してやればいいらしい。一番下の投稿を参考に、player.jsのmidi.loadMidiFile内の（116行目あたり）

```javascript:player.js
instruments: midi.getFileInstruments(),
```
のコメントを削除して、midi.getFileInstruments内switch文の```case 'programChange'```（172行目あたり）の中に

```javascript:player.js
MIDI.programChange(event.channel, event.programNumber);
```
を追加。これでloadPlugin内でのinstrumentの指定が必要なくなった（もしかして元から要らなかったのかも……）。

さあ、これで再生してみると……。

##成功！

なんかテンポがおかしい気がするけど！ちゃんと鳴った！ちゃんと色んな音色で、全てのチャンネルの音が鳴っているぞ！

テンポがおかしいのは、loadPluginのonsuccess内でloadFile前に```MIDI.Player.BPM = 0;```を入れてやることであっさり解決。

##To Do
現状では再生する度にいちいちファイルを読み込んでいるので、再生するまでにえらく時間がかかる。また、せっかくmidiなのにループに対応していないのも残念。
音色に関しては、軽いサウンドフォントがあるとのことなのでそれを使えるようにすればインターネット環境が無い場合でもmidiファイルが再生できるようになる。再配布可能な軽いサウンドフォントがあればいいのだけれど。

それと、環境やmidiファイルによっては音飛びが激しいっぽい。自分の環境では7thCity.midがソフラン曲になってました。音もなんか変な感じだし……。

とにかく、もう少しいろいろいじってみよう。