現在の timidity は浮動小数点の演算部分の負荷が重く、
StrongARM/XScale では事実上動かない。
というわけで整数演算化したもの。
たいして面白いネタでもないんで、最後の timidity.cfg の注意書きまですっとばしてもいい。
gnugo の整数化にくらべりゃ timidity なんて ...
% apt-get source timidity % cd timidity-2.10.4 % ./configure --without-x --enable-audio=oss --disable-dynamic --disable-gtktest --host=arm-linux % make CC=arm-linux-gcc... で終り。で、これを A300 に持っていって音色ファイル整えて適当に鳴らすと、 鳴るには鳴るが切れ切れになる。ためしに
# time timidity -Ow piano.mid -o /dev/nullなどとして midi → wav 変換時間を測るとだいたい演奏時間の 12 倍ほどかかる。 そら鳴らんだろう ...
% make clean % make CC=arm-linux-gcc CFLAGS='-pg'として prof 対応させたものを A300 に持ち込む。A300 側で、
# timidity -Ow piano.mid -o /dev/nullとして gmon.out というファイルを得る。これを母艦に持ち帰り、
% gprof timidity gmon.out > timidity-arm.profとしてプロファイルを手に入れる。こんな感じ:
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
49.53 1002.27 1002.27 4261 0.24 0.24 do_ch_reverb
32.69 1663.80 661.53 18684 0.04 0.04 set_ch_reverb
5.52 1775.54 111.74 3423868 0.00 0.00 apply_envelope_to_amp
4.99 1876.56 101.02 63577 0.00 0.00 rs_bidir
2.18 1920.69 44.13 65156 0.00 0.00 rs_loop
1.35 1948.08 27.39 56558 0.00 0.00 mix_mystery_signal
... 以下略 ...
一方、PC 上の timidity では:
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
45.56 9.81 9.81 50833 0.19 0.19 rs_bidir
17.60 13.60 3.79 57571 0.07 0.07 rs_loop
6.64 15.03 1.43 33458 0.04 0.05 mix_mystery_signal
5.11 16.13 1.10 4261 0.26 0.26 do_ch_reverb
5.02 17.21 1.08 18653 0.06 0.06 set_ch_reverb
4.60 18.20 0.99 33373 0.03 0.04 mix_single_signal
... 以下略 ...
rs_bidir, rs_loop が主要な負荷であることが正しい姿であるとするなら、 A300 で do_ch_reverb, set_ch_reverb, apply_envelope_to_amp の 3 関数が重いのは異常だ。 とくに do_ch_reverb, set_ch_reverb の二つで変換時間の 82% を消費しているが、 これは、この二つがもし (PC でのように) 10% 程度に収まるなら、 A300 の変換時間は 1/3 に縮まることを意味する。... これでもまだ重い (演奏時間の 4 倍ほどになる計算だ) が、 それくらいになればサンプリングレートを減らすなりして対応可能だろう (てゆーか、対応可能であってくれなければ困る)。
#define REV_INP_LEV 0.55
void set_ch_reverb(register int32 *sbuffer, int32 n, int level)
{
register int32 i;
FLOAT_T send_level = (FLOAT_T)level * (REV_INP_LEV/127.0);
for(i = 0; i < n; i++) {
direct_buffer[i] += sbuffer[i];
effect_buffer[i] += sbuffer[i] * send_level;
}
}
これがむちゃくちゃ重いってんだから、浮動小数点の計算が重いとしか考えよーがない。
つーか、こんな計算に FLOAT_T (= double) を使うんだから、
すっかり FPU 前提のリソースに慣れてしまってるな。
こいつの内部計算は FLOAT_T だが、 引数と結果 (direct_buffer, effect_buffer) は整数なので、整数化は容易:
#define REV_INP_LEV 0.55
void set_ch_reverb(register int32 *sbuffer, int32 n, int level)
{
register int32 i;
int32 send_level = level / (127.0 / REV_INP_LEV);
for(i = 0; i < n; i++) {
direct_buffer[i] += sbuffer[i];
effect_buffer[i] += sbuffer[i] * send_level;
}
}
send_level の値域的に REV_INP_LEV = 0.548 ... くらいの計算をしてることに相当する
(うろおぼえ)。
do_ch_reverb のほうは長いので略。こちらも 引数と結果は整数で、浮動小数点演算はほぼ関数内部に閉じた問題なので固定小数点化は 各変数の値域に注意するだけでいい。おおむね整数部 29bit、小数部 3bit にしたんだったかな。
apply_envelope_to_amp は global なデータを触ってるので、こいつの固定小数点化は あっちこっちに影響がでる。しかも値域が色々で、変数名の互換性ゼロ。 ともあれ 3 つとも固定小数点演算化して prof とると:
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
28.26 59.14 59.14 42408 1.39 1.39 rs_bidir
20.86 102.79 43.65 1953529 0.02 0.02 apply_envelope_to_amp
8.36 120.29 17.50 42061 0.42 0.42 rs_loop
6.01 132.87 12.58 27385 0.46 1.40 mix_mystery_signal
5.72 144.84 11.97 3604 3.32 3.32 do_ch_reverb
5.43 156.20 11.36 11879 0.96 1.63 rs_vib_loop
4.76 166.16 9.96 15845 0.63 0.63 set_ch_reverb
... 以下略 ...
これで変換時間は演奏時間の 2 倍強。
あとは SDL の最適化の時と同じく
(つーか、順番的にはこちらが先) LOOKUP_SINE などの調整で
サンプリングレート 22.05 kHz 時に演奏時間の 5 割に追いこんだ。
なお、サンプリングレート 44.1kHz 時だと変換時間は演奏時間の9 割に達する。 なんとかリアルタイム演奏可能にみえるが、あくまで平均レートでの話であって 瞬間的に間に合わないことがある。 44.1kHz レートで演奏させるなら十分に大きいバッファを用意しなければならない。
理由は単純だった。timidity++ で計算時間の 80% 以上を占めた do_ch_reverb, set_ch_reverb の 2 関数が SDL-mixer の timidity には存在しない!
... そーか、かつて(SDL-mixer のは timidity 0.2 由来)
は timidity が reverb をサポートしてない時代もあったのだな ... 当然か。
ためしに Timidity++ でも do_ch_reverb, set_reverb を外して演奏させる:
# timidity -Breverb=0 piano0.midと、かなり軽くなった。
% ./configure --without-x --enable-audio=oss --disable-dynamic --disable-gtktest \
--enable-ncurses --enable-network --host=arm-linux --prefix=/home/QtPalmtop
で make している。つまり、UI としては ncurses のものだけ入っている。
S-lang I/F くらいはすぐに入れられるけど、なんか無駄な抵抗という気がするので入れてない。
qte 対応くらいは考えてたが、
qte アプリの立ち上がりのあまりの遅さと、新 ROM で qpe が
quickexec 対応されるらしいという噂を聞いて、とりあえず Qt I/F は新 ROM 待ち。
なお、timidity++ 本来のデフォルトのレートは 32kHz だが、このレートは A300 はサポートしてないうえにトラブルを起こす。 上のバイナリではデフォルトレートは 22.05kHz になっている。
ogapee さんのところ の timidity-patch_1.0.1-sd_arm.ipk を使う場合で言えば、 MIDIパッチファイル自体は /mnt/card/QtPalmtop/etc/timidity/ 下に置かれ、 /home/QtPalmtop/etc/timidity.cfg から /home/QtPalmtop/etc/timidity/ として参照されることになっているはずだ。 この timidity.cfg を /etc/ にコピーしておく。
Debian の timidity-patches を使う場合は /usr/share/timidity/ 下に置かれ、 /etc/timidity.cfg から参照される。こちらはそのまま使える。
ちなみに。 Debian のが展開後 12MB, ogapee さん家のが 18MB, 自分とこの PC で使ってる eawpats12 に至っては 38MB と とてつもなくでかい。
手元の A300 ではソフト工房乾から Timidi95 の 3MB音色ファイル + gm差分 を使っている。こちらはあわせて 5MB ほどだ。 3MB の標準ファイルだけではさすがに品質がちと悲しいが、5MB のほうはそこそこである。 Debian のを使うくらいなら ogapee さん家の 18MB のほうが遥かに良いし、 できれば timidity のデファクトスタンダード化してしまった eawpats12 のがいいにきまってるが、 ... さすがにそんなでかいのを SD に入れるくらいなら midi なんて使わず PC で mp3 化してから SD にもってくるってば。
また、バッファオプションを "-B 20,12" をデフォルトに ── つまり 20 x 4096sample/sec のバッファをカーネルにセットするようにした。 オプションなしで動かしても音が切れることはないと思う。
% diff timidity.h~ timidity.h 163c172 < #define CSPLINE_INTERPOLATION --- > /* #define CSPLINE_INTERPOLATION */ 184c193 < #define SMOOTH_MIXING --- > /* #define SMOOTH_MIXING */
デフォルト出力はやっぱり 22kHz になっている。C7x0 では 44kHz を指定 (-s 44100) したほうがおそらく軽い。
なお、prof 出力は↓な感じ。
time seconds seconds calls ms/call ms/call name
53.85 238.92 238.92 8867384 0.03 0.03 apply_envelope_to_amp
15.16 306.17 67.25 19203 3.50 3.50 do_ch_reverb
9.49 348.28 42.11 111978 0.38 1.48 mix_mystery_signal
7.45 381.33 33.05 112021 0.30 1.40 mix_single_signal
6.25 409.04 27.71 38400 0.72 0.72 set_ch_reverb
2.68 420.91 11.87 4467 2.66 2.66 s32tos16
...
軽くしたはずの apply_envelope_to_amp が重い。なんでやねん。
これで SL-A300 の 48kHz 時で CPU usage 80% 弱、 SL-C750 の 48kHz 時で CPU usage 30% 弱、22kHz で 20% ほど ── 既出だがなるほど C750 の 22kHz では音が切れることがあるな。カーネル内バッファの使い方が下手?
つーとこで、ようやく使ってた timidity 2.10 を 2.11 のに移行(今頃かい)。