(まさか2日ばかり遅れた程度でこのAdvent Calendarごっこが終わるはずがない。というわけで何事もなかったかのように続きを書くことにしよう。)
最初にXWTで作成したバージョンは、描画まわりが著しく不安定だったので、その原因を追及しなければならなくなった。不安定の原因は描画パフォーマンスにありそうだった。MIDIファイルの演奏はもちろんmp3などのデコード処理に比べると軽量だが、リアルタイムで処理すべきメッセージは少なくない。ひとつのチャネルでノートオンを実行する前に、エクスプレッションなどCC/RPN/NRPNの変更命令が入り、それからノートオンが処理される。場合によってはノートオフとノートオンは同じタイミングで必要になる(ゲートタイムが無い音の場合)。
描画命令は、それぞれのMIDIメッセージを受信したタイミングで行われていた。実際には、XwtではCanvasオブジェクトに対してQueueDraw()というメソッドを呼び出し、それ自体は描画スレッドに描画イベントをトリガーするよう指令を送る。これを受信したスレッドが、OnDraw()を呼び出す、という流れだ。(実際には、QueueDraw()の実装はBackendのフレームワークのメッセージをディスパッチするだけだし、メッセージはそのフレームワーク側から発生したイベントにフックされたXwtのBackendのイベントが、必要に応じてユーザーコードからフックされたイベントを呼び出しているだけだ。)
このQueueDraw()の呼び出しは、何も考えずに実装されたコードでは、一度に処理するMIDIメッセージにつきひとつ存在したので、16鍵盤合わせておそらくこれだけでFPS…framesと言うべきか…はある。真面目に描画命令がダブルバッファリングされている必要がある。
とは言え…本来ならこれはGtk#のレベルで解決していて、従ってXwtのレベルでも解決しているはずの問題だ。gtk+は2.10の時点でデフォルトでダブルバッファリングを行うようなコードが入っているはずだ。ただ、XwtがGtk#と何かしら異なる仕組みをもっているかもしれないし、ダブルバッファリングが有効になった上でこのような体たらくなのかもしれない。
仕方ないので、Xwtはいったん放棄して、Gtk#バージョンのブランチを作って開発することにした。Gtk#用のコードは、gtk-sharpブランチにpushしてある。
gtk#版の開発は、それなりにスムーズに行われた。まだUIのコードがほとんど無かったということもあるけど、実際にはMVCの亜種のような設計にしていたからだ。コード中にはViewModelという名前のクラスが存在する。それらはいずれのGUIフレームワークにも依存しない形になっている。なぜそのような設計になっているかというと、もちろんGUIフレームワークを簡単に切り替えて検証できるようにするためだ。
具体的には、PointやColorのような型を独自に定義して、各ViewModelクラスが座標周りの数値を保持している。スケーリングに優しい設計を行っているわけではない。16鍵盤という画面の構成上、かなり幅広く領域を確保する必要があり、それを可能にするために要素が10ピクセル以下のものが多く、単純な拡大縮小計算で何とかなる気がしないためだ。
また、現状このアプリケーションは visualizer であり、ユーザー入力を受け付けるものではないため、いわゆるMVVMのようなデータバインディングやUIコンポーネントの状態変化によるイベント通知を行っているわけではない。(入力を受け付けたとしても、Canvas全体に対する入力として処理せざるを得ないだろう。) 現状はとりあえず困っていない。Viewの状態変化により大きく影響するのは、演奏状態や演奏制御命令に応じる部分の方が大きい。
もっとも、そうは言っても、このアプリケーションはほとんどがViewに属する部分で出来ているので、ViewModelのようなものを切り離しても、それなりの作業量・コード差分にはなった。
ともあれ、Gtk#版は起動するようになった。そこで確認できたことは、Gtk#でも手動で GdkWindowの
BeginPaintRect() / EndPaintRect() を呼び出した上でContextへの描画処理を行わなければ重い、ということだった。具体的には https://github.com/atsushieno/xmdsp/blob/gtk-sharp/Xmdsp/View.Xwt/KeyboardList.cs#L90 が決め手となって、描画パフォーマンスの問題が一気に解決したわけである。
どうやらGtk#であれば少なくとも現状のモデル(C#/自作MIDIレイヤー/クロスプラットフォーム)で動作するものが作れそうだとわかった。次は、これをXwtのコードベースで実現できるようにする話を書く。