4日目のMidiPlayerは、パフォーマンスや同期性に問題はあるものの、それなりにすんなり動かすことができ、他の部分をいろいろやっつけることで、とりあえず鍵盤をちらちらと演奏表示できるMIDIプレイヤのプロトタイプのようなものは出来た。何とか1週間でできた。コードはgithubに置いた。
ファイル操作は外部アプリケーション(OI File Manager)をインストールしてIntentを使って通信することで実現した。これが微妙にハマりどころで、結果がonActivityResult()で返ってくるよりも、startActivityForResult()のダイアログ呼び出しの次のコード行が先に実行されたりする(要するに別スレッドで行われている)。バグなのかもしれないが、これでは別ウィンドウを開いて手動でスレッド同期するのと同様の煩雑な手順が必要になる。
ちなみに、Intentで必要になる外部コンポーネントについて、他人にインストールさせる手段はあるようなのだけど、まだ分かっていない。
スレッドモデルがCLIとJavaでは大分違って、特にCLIでいうWaitHandleの通知まわりは、状態のチェックが厳格で(たとえばThread.wait()していないところにThread.notify()することができない)、いろいろ移植での問題が発見された。今でも演奏のための最低限の部分は何とか動いてはいるものの、一時停止や停止が動かないなど、不安定な部分が少なからず残っているようだ。さすがにMidiPlayerを安定的に動くようにさせるには至らなかった。
あと、MidiPlayerでイベントごとに描画をコールバックするようにしたら、さすがに描画が追いつかなくなってきたようだ。というより、800x480の画面では遅延だらけで話にならない。アプリケーションの遅延の原因が描画にあると気付いたのは、HT03Aなど古い端末でも動かせるようにしようと思ってのことだったのだけど、そのやっつけ作業が終わって動かしてみたらほとんど遅延が無いことに気付いたのだった。
現在は、SurfaceViewのプロトタイプサンプルにあった、別スレッドで描画の更新を行うモデルをそのまま残してあって、その中で画面全体をCanvas.drawBitmap()で更新するのだけど、必要な部分だけ毎回lockCanvas()して更新した方が良いのかもしれない。各種の最適化方法を試した方が良さそうだ。
現在は、JetPlayerによるMIDIの再生とMidiPlayerによる画面の更新という、2つの演奏ドライバが併走している状態なのだけど、描画の遅延はここで非同期の問題をもたらしている。とはいえ、本来はMidiPlayerが遅延を吸収できるべきところだ。現在は遅延を一切考慮せずひたすらThread.sleep()しているので(これはC#版からそうだ)、定期的に現在時刻からの差分を吸収するように直す必要がある。
これとは別に、SmfReaderにもパフォーマンス上の問題があって、これは純粋に自分のライブラリの問題なのだけど、これは別途プロファイルしてみないと分からない。C#からJavaに移植する過程で、さまざまなbyte->short変換を追加せざるを得なかったのだけど、それが問題になっている部分もあるかもしれない。まあもともと処理効率の良いライブラリだったわけでもないが...
アプリケーションのパッケージングなどは、全てeclipse上で行っていたので、その下回りで何が行われているかはよく分かっていない。Android開発そのものに大きな興味があるわけではないので、その辺を見てみようという気は今のところ無い。
ところで、Intentを使ってアプリケーション間通信を行えるというところで気付いたのだけど、エディタ上でMMLを記述して、それをandroidmono経由でmugeneを呼び出してSMFに変換するようなことが出来るのではないかと思った。UI方面は実質的にまだバインディングが存在しないので、C#で全体を書き直すのは現時点では無理だと思うが、そういういじり方でJavaコードを減らしていくのは、悪くないアプローチかもしれない。ちなみに、Javaコードは書きたくなかったものの、Jythonなどを使うとSMFライブラリのパフォーマンスに問題が生じそうだったので、今回は回避した。
とりあえず今日で一連のandroid hackingネタは終わり。
Leave a comment