Kumakki MIDI Playerとは

私が思い付きで作ったMIDIプレイヤー。 C#WPFで作られていて、Black MIDIを読み込めるように軽量化重視で作ったやつ。 ある程度MIDIをプログラムで触れるようになって、これ頑張ればMIDIプレイヤーとか作れるんじゃね?って思って作り始めた。 私のこだわりとして、読み込んだ時のメモリ使用量を極限まで節約して、巨大なMIDIファイルでも読み込めるようにしてある。 一応22億ノーツのMIDIまではテストで読み込めることを確認。

MIDIファイルの読み込み

普通ならC#用のMIDIを扱うライブラリとかを使うと思うけど、私は一切使ってない。 普通のライブラリを使うと恐らく巨大なMIDIファイルは読み込めないだろう。 MIDIファイルの仕組みはだいたい理解したので、バイナリを直接読んで読み込む方式にした。 全てのイベントを取っているわけではないけど、最低限普通に再生できるレベルでは取ってるつもり。 読み込んだデータは必要最低限に絞ったクラスに格納し、それをMIDIの最小時間単位である1tickずつリストに格納している。

2種類の描画タイプ

最初に作ったのは128鍵の鍵盤をチャンネルごとに縦に16個並べ、発音中の音に合わせてトラックごとの色を塗って描画するというもの。 似たようなものだとVanbasco’s Karaoke PlayerやTom’s MIDI Player、Ultralight MIDI Playerなどがある。 音に合わせて光らせたり消したりするだけだから簡単そうだなと思い、これから作ることにした。 その後上からノーツが降ってくるピアノロールタイプのものを作った。 こちらは似たようなものだとPiano From AboveやSynthesia、Embers、Kiva、そして同じくUltralight MIDI Playerなどがある。 こちらは描画したピアノロールを動かさなければならず、ハードルは高そうだったが何とか作ることができた。

チャンネルごとに鍵盤を描画

1つ目のチャンネルごとに描画するタイプについて。 こちらは描画用の変数を用意していて、ノーツを再生するたびにこの変数を書き換えて、画面に反映させている。 intの3次元配列を用意し、1次元目をチャンネル、2次元目を音の高さ、3次元目をトラック番号として割り当てて管理している。 1フレームごとにこの変数を参照して描画するけど、毎回描画してると重いので前フレームの状態を保持して置いて、それと比べて変更があった個所のみ描画に反映させるようにしている。 結局すべての音が毎フレーム変わるような場合は重くなるし、それでも重くならないように最適化したからほぼ意味ないんだけどね。 同じ鍵盤が複数回押されてもいいように、データにはboolではなくintで管理している。 ノートオンで加算。ノートオフで減産し、途中で再生個所を変えた等でマイナスになった場合は強制的に0にしている。

ピアノロールで上から降ってくるタイプの描画

Black MIDI界隈のMIDI動画と言えばやっぱりこの方式だからどうしても作ってみたかったんだよね~。 ただ、こっちはチャンネルごとの描画に比べて圧倒的に1画面内の要素が多すぎるので、同じ方式ではとても厳しかった。 あっちは128x16で2048個の要素だったのに対し、こっちでは画面の縦ピクセル数x128とかいうとんでもない数になる。 流石に毎回これらを再生するたびに更新するわけにもいかないし、まだ再生してない部分のノーツとかも事前に確認しに行って描画する必要があったので流石にリアルタイムは無理だなってなった。 なので、こちらではあらかじめ描画用のデータを作ることにして、再生時は毎フレームそのデータをもとに現在描画するべきデータを求めて描画するすることによって何とか実現できた。 MIDIを読み込んだ時に専用の処理を流し、1tickごとにこの時点ではどのキーにどの色を描画するべきかをすべて計算、データとして持つというような仕組み。 再支持は現在のtick情報とノーツ速度を元に、各ピクセルにどのtickのノーツを描画するべきかを求め、配列から取り出して色を塗っている。 描画する際は、一括更新だと量が多くて重いし、1要素単位でも量が多くて重いという事態になってしまい、ちょうどいいバランスを探りながら区画分けして更新することで軽くなった。

一部のノーツを無視する機能

この機能があるMIDIプレイヤーはあんまり見かけたことないかも?(Kivaとかは確か負荷度合いで自動で発動とかはあった気がする) 特定のVelocity(音量)以下または特定のGate(長さ)以下、もしくはその両方を満たすときにノーツの発音を無視するというもの。 これにプラスして、完全に無視するモードと、描画だけはして発音はしないモードを切り替えられるようにしてある。 もともとは音を鳴らす側で使ってたOmni MIDIという仮想シンセサイザーに音量によって発音を無視する機能があったが、 これに長さも加えてMIDIプレイヤー側で実装出来たらいいなと思って作ってみた。 描画まで無視するのは流石にみたことなくて、重たいBlack MIDIでもまるでAudio MIDIのように再生することができる。 やっぱり重たいのは音の再生部分なので、発音を無視して描画だけするようにすれば、秒間数千万あるMIDIでも問題なく再生出来た。(流石にこれ動いたときは自分でもビビった)

その他細かいステータス表示

再生中はFPSやNPS(一秒間の発音数)、現在地点での合計ノーツ数、再生時間、再生tick数などを画面上部に表示させている。 Black MIDI扱うなら秒間ノーツ数とか合計ノーツ数は欲しいよね~って思って作った。

配布について

個人の趣味で作っただけなのと、どんなバグがあるかわかんないしメンテも面倒だなって思って配布する予定はなかった。 バージョン管理もろくにしてないし、不具合見つけても直すかは気分次第。 でもせっかくこういう解説ページ作ったなら配布してみるのもありかな?と思いとりあえず置くだけ置いてみる。

MIDIプレイヤーのダウンロード