SDL と ONScripter


... の bug fix と最適化な話。

以下では Zaurus 用 cross build に関するコマンドラインは暗黙のうちに

% PATH=/opt/Embedix/tools/arm-linux/bin/:${PATH}
% export CC=arm-linux-gcc
% export CXX=arm-linux-g++
% export QTDIR=/opt/Qtopia/sharp
% export QPEDIR=/opt/Qtopia/sharp
してあるものとする。っつーか、時々忘れる ...

SDL 1.2.5

libSDL の stable は 1.2.5 ということなんで、これを使う。 つーか、qtopia support が stable に入ったのは 1.2.5 からなので stable を使うなら 1.2.5 が必須になる。

PC の場合、本家に rpm があるのでそのまま使うこともできるが、SL-A300 用に make するまえに PC 向けにも make してみた ── はずなんだが、すでにログ残ってないので再現できない ... Debian patch (for 1.2.4) が上手くあたらなかったので、結局 ./configure --prefix=/usr && make && make install して Debian の libSDL 1.2.4 に上書きしただけだったかも。

ここから本題。
SDL はアプリ側から要求された sound buffer (fragment) をカーネルに対して 2 本分しか要求しない。 レイテンシを小さくするにはバッファは少ないほうがいいから、 音が途切れない最小限度になっている。 そして ONScripter は SDL に対して 4K samples/ch (16bit PCM で 8kB/ch) のバッファを要求する。 44100 sampling/s で 90ms (x2) の演奏時間にあたるが、 プロセスが 200ms ほど瞬断する機会の多い SL-A300 にとって、この量は絶対的に足りない。
Sample 数基準なのでモノラルにしよーが 8bit PCM にしよーが全然関係ないことに注意。 モノラル 8bit PCM で鳴らす時は 4kB しか確保しないのでやっぱり足りない。

一方、カーネル側は上限 16kB の fragment を総計で 128kB まで持てる。 そもそもデフォルトでは 8kB の fragment を 8 本、計 64kB 使ってるのに、 これでも足りるとか足りないとかの瀬戸際だ (演奏時間 370ms; ぎりぎりで足りてるが、 例えば mp3 プレイヤーと公平にラウンドロビンされる重負荷プロセスが別に一つあると音が飛ぶはずだ)。

というわけで、SDL 側で (2 つでなく) 18 本のバッファをカーネルに要求するようにした。 ふつーならカーネル側で上限の 128kB まで丸められる。 (18 という数字に他意はない。16 足すのがソースの文面的にラクだっただけ ...)
... 手持ちのカーネルだと何気に 512kB まで確保できるようになってるので、320kB 確保する。 CBR の mp3 なら 128kB でも足りるが、 負荷変動が極端に大きい midi では 160kB では足りず、300kB ですら不足気味、 てな話はまたこんど。

上のは ogapee さんの OK patch も込み。
% cd SDL-1.2.5
% zcat ../SDL-1.2.5-k1.diff.gz | patch -p1
% ./configure  --host=i686-linux --target=strongarm-linux --enable-video-qtopia \
      --disable-arts --disable-esd --disable-alsa \
      --disable-nasm --disable-joystic \
      --disable-video-x11 --without-x --disable-video-dga --disable-dga \
      --disable-video-photon --disable-video-fbcon --disable-video-directfb \
      --disable-video-ggi --disable-video-svga --disable-video-aalib --disable-video-dummy \
      --disable-video-opengl \
      --enable-dlopen \
      --disable-diskaudio --disable-mintaudio --disable-nas --disable-cdrom --disable-debug
% make
... いや、ここまで configure で偏執的に disable しなくてもいいのかもしれないが。

母艦環境でそのまんま make install する訳にもいかんので、 libSDL-1.2.so.0.0.5, libSDLmain.a/opt/Embedix/tools/arm-linux/lib/ に持ち込む。libSDLmain.a/opt/Qtopia/sharp/lib/ のほうが良いんかな? まあいいか。

# cp src/.libs/libSDL-1.2.so.0.0.5  /opt/Embedix/tools/arm-linux/lib/
# cp src/main/libSDLmain.a          /opt/Embedix/tools/arm-linux/lib/
# cd /opt/Embedix/tools/arm-linux/lib/
# ln -sf libSDL-1.2.so.0.0.5 libSDL-1.2.so.0
# ln -sf libSDL-1.2.so.0.0.5 libSDL.so
# cd /opt/Embedix/tools/arm-linux/include
# ln -sf /usr/include/SDL SDL

レイテンシはというと痛烈に悪化したはずだが ... libmad のロード時間が長くてそっちに埋もれてる。 Sound の mixing は sdl_mixer が担当し、sdl 本体の側は一本になった pcm stream が流れるだけなので いわゆる音ずれに変化はない(と思う、これが問題になりうるアプリ持ってない)。 立ち上げ/停止時間のずれだけがこの層での問題だ。

SDL_mixer 1.2.4

Sound の mixing を担当する他、 timidity 0.2 由来の midi デコーダ を内部にもつ。

(あまりに重すぎてさっさと)整数演算化されてしまった mp3 の演奏と違い、 Timidity++ は整数演算化されていないため、FPU を持たない StrongARM/XScale にとって midi は mp3 よりも格段に重い。 にもかかわらず onscripter という高負荷環境で midi が無事鳴るのは何故か ... という疑問があったんだが、 いにしえの timidity 0.2 時代はまだ浮動小数点演算が致命的になるほどには使われていないのだな。 同人アプリ (プロの script では環境依存性の高い midi を BGM には使ってないはずだ) の軽負荷 midi ならなんとか鳴ると。納得。

とはいえ、configuration (timidity/config.h) に謎な点があるので、ちょっと変えておく。 なお結果として良くなるか悪くなるかは環境次第で保証の限りではない :-)

DEFAULT_RATE
デフォルトは 32000 だが、SL-A300 はサンプリングレート 32kHz をサポートしない。 サポートしてないどころか カーネルのバグのせいで 32kHz を指定すると誤動作する -_-; 正しく動くのは 8, 11.025, 16, 22.05, 44.1, 48kHz のみで、 22050 にでもしておく。なお、SL-5000 系のほうは 32kHz でも動作する。 ずるいっつーか、さりげなくダウングレードされてるっつーか。
ONScripter 的には 22.05kHz を明示的に指定してるので、この部分には関係ない。
LOOKUP_SINE
sin() の計算を table lookup にする。 デフォルトではオフになっている。sin() の計算なんつーものをリアルタイムでさせるべきでない。もちろん入れておく。
PRECALC_LOOPS
loop の計算の一部を loop の前に出す。 ソースが読み辛かったので外したら 1 割ほどデコードが速くなった。 たしかに「入れるか外すか好きにしろ」って書いてあるけど ...
以下は触らなかった項。参考まで。
FAST_DECAY
Timidity++ のデフォルトは FAST_DECAY を外したものだが、 sdl_mixer ではデフォルトで有効になっている。 音質的には外したいが、外すとデコード時間が 2 割かた延びるんよね。 sdl_mixer は重負荷環境で使うことがほとんどだろーから、外す度胸は無かった。
USE_LDEXP
浮動小数点のシフト(2 の冪) に ldexp() を使う。 専用の関数のほうが速いかと思ったら、劇的に遅くなった :-)
そーいや hard-float だから、関数レベルで細かい面倒みてるわきゃーなかった。
% cd SDL_mixer-1.2.4
% zcat SDL_mixer-1.2.4-k1.diff.gz | patch -p1
% ./configure  --host=i686-linux --target=strongarm-linux \
   --disable-music-mp3 --disable-music-ogg --disable-music-native-midi \
   --disable-music-cmd --disable-music-mod --disable-x
% make

make するとラストで

/usr/lib/libSDL.so: could not read symbols: Invalid operation
と言って落ちる。 途中で SDL の config をとる sdl-config --libs が native 環境向けの -L/usr/lib -lSDL -lpthread と答えるためだが、 ... これはしょーがない。手で -L/usr/lib を剥ぎ取って link しなおした。

FreeType 2.1.2

2.1.0, 2.1.1 と違い、2.1.2 は 2.0.9 と互換になる。「2.1.0 は使うな」という注意書きが ogapee さん家 にあるが、2.1.2 は無問題。バイナリレベルで直接置き換えてしまっていい。

さて、ONScripter にはもうひとつ注意書きがあった。TrueType font で漢字表示をする場合の:

MSゴシック (msgothic.ttc)
embedded bitmap を使っており、ゲームで 22pt 以下のフォントを要求すると正常に表示できません。
というやつだが。これは TT_CONFIG_OPTION_EMBEDDED_BITMAPS#undef すれば直る:
diff -u include/freetype/config/ftoption.h~  include/freetype/config/ftoption.h 
--- include/freetype/config/ftoption.h~  Sat Nov  9 22:59:17 2002
+++ include/freetype/config/ftoption.h   Sun Nov 10 00:09:17 2002
@@ -311,7 +311,7 @@
   /* embedded bitmaps in all formats using the SFNT module (namely         */
   /* TrueType & OpenType).                                                 */
   /*                                                                       */
-#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+#undef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
 
 
   /*************************************************************************/

... のはいいんだが、make 自体がめんどくさい。つーか、バグってるよーな気がすんだが。

% cd freetype-2.1.2
% ./configure --host=i686-linux --target=strongarm-linux
% OBJ_DIR=. make
... で build できることになってるが、なってない。configure した直後に
diff -u unix-cc.mk~ unix-cc.mk
--- unix-cc.mk~ Sat Nov  9 22:47:22 2002
+++ unix-cc.mk  Sat Nov  9 22:49:56 2002
@@ -4,7 +4,7 @@
 CC      := arm-linux-gcc
 
 ifndef LIBTOOL
-  LIBTOOL := $(BUILD)/libtool
+  LIBTOOL := libtool
 endif
とする必要があった。

補記。... まあ、みりゃわかるが、

% ./configure --host=strongarm-linux
% OBJ_DIR=. LIBTOOL=libtool make
とすればいいらしい。でも最後のリンクでひっかかるけど (link に arm-linux-gcc でなく gcc 使ってる。どっかで決め打ちしてる ...)。

ちなみに、RPM にはパッケージがあるが、Debian は sid も 2.0.9 である。 パッケージ化に挑戦したが、なんでパッケージされてないかを理解しただけだった ... make の構造がびみょーな具合にちがってて上手く直らん。

SDL_ttf 2.0.5

Debian のは古い。ascent/descent の解釈が間違ってるので、使ってはいけない。 それではと本家の RPM を alien で直して使おうとすると、この SDL-ttf ってば freetype が内部にとりこまれていて、しかも上述の EMBEDDED BITMAPS のバグ持ちなので文字化けする。
したがって PC 用 Zaurus 用ともども自前で make する。

% configure --host=i686-linux --target=arm-linux --without-x
% make

SDL_image 1.2.2

ogapee さん家のバイナリで何の問題もなし。 せめて一つくらいはバイナリ使いたいぞってことで、rebuild すらしてないので、書くこともなし。 PC では Debian woody の 1.2.1 をそのまま使ってこちらも問題なし。

なお、ogapee さん家(および zaurus-ja で) 配付されてるライブラリバイナリは strip されてないので、 てきとーに strip しておいたほうがいい。SDL_image の場合で言えばサイズが半分になる。 libSDL 本体だと 1/4 位になるので、やっとかんとメモリ or SD がもったいない。

% ls -l libSDL_image-1.2.so.0.1.1
-rwxr-xr-x    1 kensyu    users      188569 Sep  9 12:01 libSDL_image-1.2.so.0.1.1
% arm-linux-strip libSDL_image-1.2.so.0.1.1
% ls -al libSDL_image-1.2.so.0.1.1
-rwxr-xr--    1 kensyu    users       91500 Nov 10 18:16 libSDL_image-1.2.so.0.1.1

MAD 0.14.2b

ふつー使われている FPM_ARM 版よりも ASO_IMDCT を #define して作りなおした版のほうがごくわずか負荷が小さい。 44.1kHz サンプリングの 128kbps な mp3 に対して 前者のデコード時間が演奏時間の 25.4 〜 25.8%、後者が 24.3 〜 24.5%。 ちなみに整数演算化 mpg123 だと数秒かかる前処理の時間を除いて 32.1% 〜 32.5% くらい。

gcc の最適化オプションはいずれも -O2 のみ。 これ以外に余計なことしても速くなることはないどころか遅くなることが多かった。

めもらんだむでは間違えてるほど微細な違いだし、 わざわざ rebuild するほどではないけど、いちお。

この ASO_IMDCT 版 libmad は今回のパッケージには入ってない。もともと入ってないみたいだし。

ONScripter

本家でバイナリも配付されてるが、内部的に SDL_ttf 経由で freetype のバグに触ってるらしく、直した freetype を使って make しなおさないと文字が化ける。

ただ、この時のバケ方がちょっと楽しいので、一度みておいてもいいかもしんない。

「ゲームで 22pt 以下のフォントを要求すると正常に表示できません」
というのが #define TT_CONFIG_OPTION_EMBEDDED_BITMAPS してある freetype との組合せでの ふつーの化け方だが、 「#define 以下略環境」で make した onscripter を 「#undef 左に同じ環境」で動かすと、
「ゲームで 22pt を越えるフォントを要求すると正常に表示できません」
てな状態になる :-)
この逆、つまり #undef 以下略で make した onscripter を #define で動かすとこちらはふつーに(?) 化ける。つまらん。

閑話休題。 libSDL が Qtopia 対応した場合、main() は libSDLmain 内のを使い、 それがアプリの SDL_main() を呼び出す形になる。 Qtopia アプリケーションは C++ で書かれており、 libSDL の Qtopia interface (の一部)を担当する src/main/SDL_Qtopia_main.cc もまた C++ で書かれているが、それが呼び出す SDL_main() は C linkage になっている。

一方、onscripter を普通に make すると SDL_main() が C++ linkage になる。 このため、最後の link のところで名前を発見できずに失敗する。 ... libSDL の Qtopia 対応は stable には 1.2.5 から入ったばっかりなんだが、 CVS の時代はまだ C++ linkage だったりするのかしらん。

補足: じつはちゃんと C linkage だそうだ。
... で、じつはパッチなしで C linkage になってるはずだった。 すまん。インクルードファイルの一部が 1.2.4 のままで、 SDLmain の C linkage 宣言が入ってなかった mOm

例によって sdl-config で止まるのが煩いので、上では sdl-config も外した。 cross 環境前提の Makefile.ARMLinux からは取ってしまっていいはずだ。
% cd onscripter-0.beta+20021104
% zcat ../onscripter-0.beta+20021104-k1.diff.gz | patch -p1
% make -f Makefile.ARMLinux

ついで。SDL のところで sound buffer について触れた。 SDL 側を触るのでなく ONScripterLabel.cpp などにある #define DEFAULT_AUDIOBUF 4096 を適当にふやすのが本筋なのだろうが、もともと buffer は 1 本あたり 16kB までで、 現状ですでに上限に達している (4096samples x 2bytes/sample x 2ch) ので、 これを増やしても音切れにはたぶん関係ない
... というのは誤解で、fragment は 4096 x 2ch の 8kB にセットされてるみたい (16bit PCM だからといって倍にしてない)。 限界の倍の 8192 にしておくのも手だが、でも結局 SDL 側に手を入れないと足りないか。 16kB x 2 = 32kB じゃねぇ。

スクリーンショット

手持ちの SL-A300 のメモリは 62MB あるわけで、もちろん swap なしでふつーに動く。 逆に timidity-patch_1.0.0_arm.ipk が SD カードの存在を前提として直接 /mnt/card/ にほうりこんでくるので、 /dev/mmcda1 が小さいマシンでは溢れた。あう。

『夏色の影』からメニュー

上はMA−リンクから『夏色の影』。 ちなみに文字は msgothic である。

スクリーンショットとして PC なりから観ると綺麗だが、 液晶の色の品質がさいてーなので 文市さん家の写真のように実物はかなり濁る。 画像のサイズを変換するなりするときについでにガンマいじるとかすればいいのかもしれないが、 どーにもならないのかもしれない ...
SL-A300 向けのまともなイメージビューアを誰かが作ったら それからガンマパラメータ借りてくればいーや、とか ...

音に関しては mp3 や wav は楽勝だが、やはり midi が凄まじく重たい。 たしかに音切れこそないが、「切れてないだけ」という状態に近い。 手持ちの SL-A300 はカーネルも libc もメモリサイズもノーマルと大きく違うので ノーマルでの状況が見えないんだが、ほんとーにまともに midi 動いているんだろか。 どっかで設定しくったかな〜?

それと、これは onscripter for PC でもそうだが、フォントの陰影などの修飾がイマイチで、 フォントが背景に埋もれる傾向にある。freetype の責任かな? PDA での表示が VGA で絵を作ってから QVGA に縮小している関係で PDA では特に潰れやすい。 QVGA の絵を作ったあとで、 そのサイズにあわせてグリフを freetype から取得して修飾すべきだと思うが、 そこまでコード読んでない。

もひとつついで。上記『夏色の影』は現在もともとの意味で終了する方法がない。

[killmenu] is not support yet!!
と出る。外から入って kill してやるか、 HOME キーで Qtopia に戻って konsole なりから kill する必要がある。 つまり (仕様上効かないはずの) HOME キーがちゃんと効くんだけど ... そういうもんか?

パッケージ

そのうちまともなやつが本家(or zaurus-ja) からでてくるだろうが、それまでのサンプルとして。 無保証っつーか、眠い頭で packaging したので動くかどーかも無保証:

なお rebuild しなかったものについては、本家元ネタ (onscripter-lib_1.0.2_arm.ipk,onscripter_beta-20021104_arm.ipk) からコピーしたものを入れている。

追記

その 1.

MIDI のパッチファイルは /usr/local/lib/timidity/timidity.cfg でなく、 /etc/timidity.cfg を参照するようになっている。 ... 従来通りカレントディレクトリに timidity.cfg を置くのでもかまわないはずだけど。

その 2.

ライブラリ(onscripter-lib_1.0.2k1) はこれでよさげだが、 本体(onscripter_beta-20021104k1) のほうは起動しないらしい (libbz2 の責任だそうな。そーいや libbz2 も自前でmake しなおしてる ...)。 ついでに本家の onscripter バイナリと上記ライブラリの組で文字化けは起きないみたいなんで、 本体のほーは要らなさそーである。
しかし ... レポート早いねぇ(驚) ...

その 2'

本家バイナリは libbz2, libmad を static link しているらしい。 上のバイナリは dynamic link になっているので通常の環境では libbz, libmad が足りないと言われる。どうも libbz2, libmad を packaging してるとこがないみたいなんで、ふつー入ってないはずだ。
というわけで libbz, libmad のパッケージを作ってみた。

これで上記の本体バイナリも動くはずだ。てゆうか動いてくれ ...

その 2''.

本家 に新バイナリが上がってきた。確認したところ本家レポートにあるように msgothic などでは文字が化ける。 化け方が「大きい文字が化ける」形っぽいのでライブラリのほうは #undef された形だが onscripter で #define されてしまってる(というかなんというか) らしい ── libqte が freetype くわえこんでて、こいつとバッティングしてるとのこと。なるほど ...

の組合せでは msgothic がバケないことを確認しておいた。

その 2'''.

本家の新バイナリ(onscripter_beta 20021113 + onscripter-lib 1.0.3) にて、msgothic が化けないことを確認。 これで上のバイナリはぜんぶ用済みとなると。
おつかれさまでした > ogapee さん & あやちさん。


[日記へ] [目次へ]