TL;DR
試行錯誤の末、fluidsynthをビルドするために、cerberoのソースに追加できるrecipeを作成した。
https://gist.github.com/atsushieno/a1c7af0537f1ce353e4e
やや冗長な導入
fluidsynthはLinux環境で使われる仮想MIDIシンセサイザのひとつだ。同様のソフトウェアにはtimidityやZynAddSubFxなどがある。
fluidsynthは、ALSAのMIDIデバイスとしても使用するのが一般的であろうと思われるが、C APIがそれなりに整備されていて(fluidsynth自体もCで書かれている)、プログラムから直接操作することも出来る。C APIが用意されていると、Cライブラリとのインターフェースを実現している、perl、python、node.JS、C#/.NETのような言語環境とも相性が良い。筆者は実際C#バインディングを開発したことがある。(自分では実際のアプリで使ったことがないのに、ユーザーから問い合わせや使用レポートを受け取ってもいる地雷ライブラリである。)
https://github.com/atsushieno/nfluidsynth
そんなfluidsynthを、今回はAndroid用にビルドしてみた。これがあまりストレートにはいかなかったので、それについて説明したい。
ちなみに、Androidの主なアプリケーション開発言語はJavaであり、Cのライブラリをビルドするだけでは、直ちに使用できるわけではなく、JNIを使用したJava用のエントリポイントをいろいろ作成してやらなければならないのが一般的だが、筆者はそもそもC#環境で使うことを念頭に置いている(そのための上記nfluidsynthである)ので、fluidsynthのAPIのそれぞれについてバインディングを作成するなどという面倒な作業は行っていない。Java環境にも.NETのP/Invokeに相当する機能を実現する仕組みはいくつかあって、有名なのはSun Microsystems(当時)が開発したJNAだろうが、筆者のお気に入りはBridjというライブラリである。これについては、筆者が数年前にAndroidサポートをcontributeした時に、詳しい解説を書いたので、興味のある人はそれを参照されたい。
http://d.hatena.ne.jp/atsushieno/20110628/p1
android-cmakeによるビルドの試行(おまけ)
さて、Android NDKを使用したネイティブライブラリのビルドにおいては、Android.mkという独自フォーマットのビルド記述ファイルが使用されるが、fluidsynthが主にサポートしているビルド環境はcmakeである。最新のstableバージョンであるcmake 3.2.*系列では、Androidビルドをサポートするための何かしらのモジュールファイルが追加されているが、それらはAndroid NDKのtoolchainを使用したビルドを実現するためのものではないようだ(必要なモジュールかもしれないが、cmake単体でNDKによるビルドをサポートするものではなさそうだ)。
cmakeには、拡張モジュールによってツールチェインの設定などを簡単に行える機構があり、Android NDKでもcmakeベースのプロジェクトをビルドできるように、この拡張モジュールを作成している人たちが何人かいるようだ。筆者がgithubで検索して最初に発見したのがこちらである。
https://github.com/taka-no-me/android-cmake
やったー!と思って、このREADMEにある通りfluidsynthをビルドしてみると…
$ cmake -DCMAKE_TOOLCHAIN_FILE=android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="armeabi-v7a with NEON" .
...
CMake Error at /usr/share/cmake-3.2/Modules/FindPackageHandleStandardArgs.cmake:138 (message):
Could NOT find Pthreads (missing: PTHREADS_LIBRARY)
上手く行かない。
このエラーメッセージでgoogle searchをかけてヒットする情報が既に古く、筆者がcmakeにも詳しいこともないので、結局これを解決する方法はよく分からなかった。また、後述する別の問題もあるので、cmakeによる解決はとりあえずあきらめた。
autotoolsによるビルドの試行
実は、fluidsynthにはautotoolsベースのビルドも用意されている。これを使えば、cmakeに頼る必要もないわけだ。
もちろん、autotoolsで得られるのはmakeベースのビルドであり、ndk-build一発で完了するAndroid.mkが得られるわけではない。しかし一般的なautotoolsベースのプロジェクトでは、クロスコンパイル用のビルドスクリプトが作成されるものだし、実際にautotoolsベースのプロジェクトをAndroid NDKでビルドするためのTipsは存在している。
https://warpedtimes.wordpress.com/2010/02/03/building-open-source-libraries-with-android-ndk/
ただ、flluidsynthのビルドに関しては、ひとつ厄介な問題があった。fluidsynthはglibを参照しているのである。このglibをAndroid用にビルドするのは、不可能ではないはずだが(glibを使用しているライブラリはAOSPの中に含まれているはずである)、glibパッケージの解決にはpkg-configが使用されるので面倒くさいし(クロスコンパイルでpkg-configの参照を解決するということは、INSTALL PREFIXとして指定できるような、対象ターゲット環境のディレクトリが必要になるはずだ)、普通のLinuxデスクトップ用のビルドと同様にというわけにはいかなそうだ。
(ただし、後で判明したことだが、fluidsynthのビルドでautogen.shを使用する場合は、pkg-configが存在しない環境でも動作するようだ。しかし、いずれにしろglibのビルドが必要になることに変わりはない)。
(ちなみに、Makefile.amに比較的短いビルドステップの記述を追加することでAndroid.mkを簡単に作成できるandrogenizerというプロジェクトも存在するが、いずれにしろglibの依存解決が必要になるので、通常のautotoolsビルドはあきらめた。)
cerberoによるビルド
今回のglibもそうだが、この「パッケージ参照を解決する仕組み」がネックになってビルドできないプロジェクトというのは、いくつもありそうだ。
Android NDKのtoolchainにpkg-configが含まれるだけでも、autotoolsベースのプロジェクトの取り込みがだいぶ楽になると思うし、個人的にもAndroid NDK用のビルドシステムを構築したほうが良いんじゃないかとは思っている。(実のところ、それで開発したビルドサーバのようなものを作成したこともあるのだが…)
ともあれ、Android NDKに無い以上、この種の多大な依存ライブラリをビルドできるような、代替の仕組みがどこかにあるのではないか。そう思って探してみたもののひとつがgstreamerである。gstreamerのビルドが存在するであろうことは、VLC for Androidが存在することから推測していた。
このgstreamerのAndroidビルドを実現しているのが、cerberoという、gstreamer独自のビルドシステムである。
http://docs.gstreamer.com/display/GstSDK/Building+from+source+using+Cerbero
このgstreamerは、主にGNOME環境で開発されてきたもので、GNOME関係の依存関係が多大に含まれる。先のページからリンクされているfreedesktopのgitサーバから、チェックアウトして、以下のコマンドを実行してみると、およそ180種類のライブラリやツールがビルドできることが分かる。
./cerbero-uninstalled list
glibはGNOMEでは非常にessentialなライブラリなので、もちろんこの中に含まれている。
そして最も重要なことに、cerberoはAndroidビルドをサポートしている。先のドキュメントページに、Androidビルドの説明が記述されている。以下のオプションを指定するだけで良い。
cerbero -c config/cross-android.cbc <command>
ちなみにcerberoではcmakeによるビルドもサポートされていることになっているが、Androidビルドについてはそれは当てはまらないようだ。前述のandroid-cmakeが期待通りに動作するようになれば、cerbero本体をハックしてandroid.toolchain.cmakeを追加するようにすることも可能かもしれない。
いずれにしろ、cerberoは掘り下げる価値がありそうだった。
cerberoにビルド可能なモジュールを追加する
さて、fluidsynthはgstreamerの一部ではないので、既にビルドできるパッケージには含まれていない。自前でビルドを追加する必要がある。モジュールのビルドは、「レシピ」を作成するかたちで追加できる。幸い、cerberoソースのrecipesディレクトリには大量のレシピが置かれているので、それらを参考にしつつコピペで新しく作ってしまえば、難しくはない。というわけで、以下のようなfluidsynth.recipeを作成した:
class Recipe(recipe.Recipe):
name = 'fluidsynth'
version = '1.1.6'
licenses = [License.LGPLv2]
deps = ['glib']
remotes = {'origin': 'git://git.code.sf.net/p/fluidsynth/code-git'}
commit = 'f11592'
srcdir = 'fluidsynth'
config_sh = './autogen.sh && CFLAGS=-DDEFAULT_SOUNDFONT=NULL ./configure'
files_libs = ['libfluidsynth']
files_lang = ['fluidsynth']
files_devel = [
'include/fluidsynth',
'fluidsynth.pc',
]
(https://gist.github.com/atsushieno/a1c7af0537f1ce353e4e にも同じ内容がある。)
実のところ、これはPythonで実行されるソースファイルにすぎない(cerberoはPythonで作成されている)。
内容は少々ハック気味なので、少し説明しておくと、config_sh
には ./configure
に相当するコマンドが記述される。fluidsynthの autogen.sh はなぜか一般的な autogen.sh と異なり configure を呼び出してくれないので(Makefileが期待通りに作成されずしばらくハマった)、両方を呼ぶ必要がある。そして、fluidsynthのビルドをそのまま通そうとすると、DEFAULT_SOUNDFONT
という変数が定義されていない、と怒られる。これは、たとえばUbuntuデスクトップ環境では /usr/share/sounds/sf2/FluidR3_GM.sf2
のようなファイルが用意されているのだけど、アプリケーションに組み込むしかないAndroid環境では、当然ながらデフォルトのサウンドフォントは用意できない。とりあえずこれは定義するしかない。
とりあえず、以上のファイルをrecipesディレクトリに追加して、
./cerbero-uninstalled -c config/cross-android.cbc buildone fluidsynth
で、$HOME/cerbero/dist/android_arm/lib
に libfluidsynth.so が作成されるはずである。
ちなみにcerberoで現状まともにビルドできるAndroidのモジュールは多くないかもしれない。筆者が試したところでは、atkが既にビルドできなかった(atkはgtkに含まれるかなりessentialなライブラリだ)。
未解決の問題点
このエントリは、Android NDKでfluidsynthをビルドするという部分についてのみまとめている。ビルドされたlibfluidsynthを使うところまでは、まだ確認できていない。
ビルドされたlibfluidsynthには、実のところ、fluidsynthのオーディオバックエンドが何一つ含まれていない(はずだ)。Androidにはossもalsaもjackもpulseaudioも無い。おそらくオーディオ出力に影響されない機能のみが使用できる状態だと思われる。
いずれにせよ、Linux用ライブラリらしい、パッケージ依存が含まれるものを、Android用にビルドしようと思ったら、このcerberoを使ってみると、簡単になることもあるのではないか。実際、WebRTC実装を含むforkなどが、github上に存在しているようである。
というわけで、cerberoの紹介は以上である。