PureData(4)

去年RubyKaigiが開かれた場所の近くで、楽しい集まり が、あるようだ。今年のRubyKaigiは、筑波だそうだけど、遠いので敬遠かな。 どうせ遠い所なら、島根はどうだ。ゆっくり温泉に浸かりながら、まったりと旅行気分。 こういう所なら、行く鹿。とにかく、筑波は中途半端な所だ象。

で、楽しい集まりなんだけど、コストパフォーマンスが悪そうなので、敬遠。どうせやる なら、丸一日ぐらいやってください。文句を言うだけで終わってしまってはもったいないので、 ビューテフルコードとRWHの STMの章を読み直している。ビューテフルコードの方が、 自分にとっては興味を持てたな。ありがとう。

でもHaskell界、暫く目を離していたら、こんな事に。なんだかなあ。楽しようとして苦労を背負い込むって いつものパターンか。gem しかり。でもgemがちゃんと動いたら rubyのコレクターになれるかも。

From: [838] デフォルトの名無しさん <sage>
Date: 2010/04/11(日) 15:49:53

Cabalがエラりまくる
なんだよこれ3パッケージに1つはエラーでビルド失敗するわ
________________________________________

From: [839] デフォルトの名無しさん <sage>
Date: 2010/04/11(日) 15:55:10

Cabalはパッケージマネージャではないから仕方ない
________________________________________
From: [851] デフォルトの名無しさん <sage>
Date: 2010/04/11(日) 19:10:39

諸君らが愛してくれたCabalは死んだ。何故だ!
________________________________________

From: [852] デフォルトの名無しさん <sage>
Date: 2010/04/11(日) 19:12:51

>>851
研究のためのモルモットに過ぎなかったから

8.8.8.8

ネットをうろうろしてたら、面白い番号に出会った。中国人の好きそうな縁起の良い数字が 並んでいて、覚えやすい。

[sakae@cdr ~]$ traceroute 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 64 hops max, 40 byte packets
  ....
 6  r1-0311.otemachi.net.bbx.ad.jp (218.40.50.169)  16.854 ms  16.718 ms  15.864 ms
 7  d48i015.bbx.ad.jp (218.40.48.15)  18.029 ms  20.953 ms  21.741 ms
 8  AS15169.ix.jpix.ad.jp (210.171.224.96)  41.473 ms  17.205 ms  17.685 ms
 9  209.85.241.64 (209.85.241.64)  19.988 ms  18.002 ms
    209.85.241.68 (209.85.241.68)  16.903 ms
10  209.85.250.86 (209.85.250.86)  50.111 ms
    209.85.250.90 (209.85.250.90)  47.845 ms
    209.85.250.86 (209.85.250.86)  48.210 ms
11  209.85.250.101 (209.85.250.101)  46.152 ms
    209.85.250.103 (209.85.250.103)  48.634 ms
    209.85.243.23 (209.85.243.23)  49.180 ms
12  209.85.241.154 (209.85.241.154)  52.951 ms
    209.85.241.162 (209.85.241.162)  60.301 ms
    209.85.241.154 (209.85.241.154)  48.035 ms
13  google-public-dns-a.google.com (8.8.8.8)  53.744 ms  47.798 ms  52.154 ms
[sakae@cdr ~]$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=52 time=56.324 ms
64 bytes from 8.8.8.8: icmp_seq=1 ttl=52 time=51.411 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=52 time=50.767 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=52 time=52.923 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=52 time=52.208 ms
^C
--- 8.8.8.8 ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 50.767/52.727/56.324/1.940 ms

分かる人は早速、/etc/resolv.confに追加しておきましょう。私はちょくちょくOSを インストールするんだけど、その度に設定値を忘れて、右往左往してるんだ。次回 からは迷わないぞ。

mainがたくさん

PureDataのソースをWebから見られるようにしたのはいいのだが、mainが5つもあって ちょっとびっくりだ。どれが本体への入り口なんだろう?

main               30 s_entry.c      int main(int argc, char **argv)
main               15 s_watchdog.c   int main(int argc, char **argv)
main               44 t_main.c       main(int argc, char **argv)
main               51 u_pdreceive.c  int main(int argc, char **argv)
main               29 u_pdsend.c     int main(int argc, char **argv)

入り口って明示してるやつが本流かなあ? ソースを辿って行くのもちょっと かったるいので、debgerで追いかけれるようにしてみるかな。こういう時は FreeBSDでしょと思って、ソースを展開し、configureしたんだが、Tcl/Tkが 見つからんとか言われたよ。

configure --help しても、Tcl/Tkを指定するオプションも特に無いみたいだし。目を 凝らして画面を見てたら、なんとなく使えそうな環境変数に目が行った。

  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
              nonstandard directory <lib dir>
  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
              headers in a nonstandard directory <include dir>

CPPFLAGS='-I/usr/local/include/tcl8.5 ...' LDFLAGS=-L/usr/local/lib ./configure

とか指定してあげたら、無事に第一関門を通過した。次はmake工程なんだけど、alloca.h が無いと言ってコンパイルが停止。そんなヘッダーファイルはFreeBSDには無いんよ。 ソースを読んで、ちと書き換えた。そしたら少しは先に進んだけど、また同じような問題が。 嫌気が差してしまったので、portsシステムに任せてしまう事にした。

ports/audio/pd の中に行って、make configure 。 こうすると、portsの提供者が ソースにpatchを宛て、configureまで行った状態になる。どんなパッチが当たっているか、 参考のために見ておく。

[sakae@cdr ~/pd-0.42-5/src]$ ls *orig
m_conf.c.orig           s_audio_oss.c.orig      s_stuff.h.orig
m_glob.c.orig           s_file.c.orig           t_tkcmd.c.orig
m_sched.c.orig          s_inter.c.orig          x_list.c.orig
s_audio.c.orig          s_main.c.orig
[sakae@cdr ~/pd-0.42-5/src]$ ls *bak
configure.bak*          makefile.in.bak
m_binbuf.c.bak          u_main.tk.bak

色々な手直しありがとうございます。丁寧に差分を取ると、いろいろな解決方法が見えて くるな。上のalloca.hは、stdlib.hで代用してたぞ。

次は、gdbを使えるように、-O2オプションを潰して -gに変更。make時のログを取りつつ makeを実行。生成物が binの中に出来上がった。

[sakae@cdr ~/pd-0.42-5/src]$ ls ../bin
pd*             pd-watchdog*    pdreceive*
pd-gui*         pd.tk           pdsend*

pd-guiって何物ぞ? 何から出来ているがログを確認してみる。

 :
cc -g -pipe -fno-strict-aliasing -DPD   -I/usr/local/include -I/usr/local/include/tcl8.5 -I/usr/local/include/tk8.5   -DDL_OPEN -DPA_USE_OSS -DUNIX -DUNISTD  -DUSEAPI_OSS  -fno-strict-aliasing  -DINSTALL_PREFIX=\"/usr/local\" -I/usr/local/include -I/usr/local/include/tcl8.5 -I/usr/local/include/tk8.5   -DDL_OPEN -DPA_USE_OSS -DUNIX -DUNISTD   -DUSEAPI_OSS  -fno-strict-aliasing  -c -o ../obj/t_tkcmd.o t_tkcmd.c
cd ../obj; cc  -L/usr/local/lib -pthread -Wl,-export-dynamic -o ../bin/pd-gui t_main.o t_tkcmd.o -ltk85 -ltcl85
cp u_main.tk ../bin/pd.tk

t_*.c が集まって pd-gui になるのか pd.tk ってのは、Tcl/Tkのスクリプトだな。そう 言えば、メインメニューのHelpからhtmlを選ぶと、lynx が立ち上がってきたけど、ここに 埋め込まれているのかな?

# open HTML docs from the menu using the OS-default HTML viewer
proc menu_openhtml {filename} {
    global pd_nt

    if {$pd_nt == 0} {
        foreach candidate \
            { gnome-open xdg-open sensible-browser iceweasel firefox mozilla \
              galeon konqueror netscape lynx } {
                  set browser [lindex [auto_execok $candidate] 0]
                  if {[string length $browser]} {
                         puts stderr [format "%s %s" $browser $filename]
                         exec -- sh -c [format "%s %s" $browser $filename] &
                         break
      :

初めてのTcl/Tkか。一杯あるブラウザーの候補をちょいと実行してみて、成功したのを 使うとな、何となく読めるものだな。それにしても、IEは候補にも入っていないので しょうか?私のWIndows7では、firefoxが上がってくるよ。FreeBSD用に、w3mを追加 しといた。スクリプト言語は楽でいいわい。

もう少し、状況証拠を調べておく。pdを起動して、どんなプロセスが上がってくるか見よう。 こんなメッセージがコンソールに出てた。

[sakae@cdr /usr/home/sakae/pd-0.42-5/src]$ ../bin/pd
Pd version 0.42-5
compiled 14:37:14 Apr 12 2010
port 5400
TCL_LIBRARY="../tcl/library" TK_LIBRARY="../tk/library"  "../bin/pd-gui" 5400
Waiting for connection request...
... connected
OSS: requested audio buffer size 8820 limited to 8192
OSS: issuing first ADC 'read' ... ...done.
pd_gui: pd process exited
closing audio...
... done.

この時のプロセス状況は次の通り。

[sakae@cdr ~/pd-0.42-5/src]$ ps awx
  :
 2991  p0  S+     0:02.07 ../bin/pd
 2992  p0  I+     0:00.55 ../bin/pd-gui 5400

pidの番号から、pdがpd-guiを起動してると思われる。すると、pd-guiで使われるスクリプトは、pd-guiで決め打ち だろうな。

[sakae@cdr ~/pd-0.42-5/src]$ grep 'pd\.tk' *.[ch]
s_inter.c:            sprintf(cmdbuf, "\"%s\" %s/pd.tk %d\n", filename, guidir, portno);
s_inter.c:        strcat(scriptbuf, "/" PDBINDIR "pd.tk\"");
t_tkcmd.c:    pdgui_evalfile("pd.tk");
[sakae@cdr ~/pd-0.42-5/src]$ grep pd-watchdog *.c
s_inter.c:            sprintf(cmdbuf, "%s/pd-watchdog\n", guidir);
s_watchdog.c:/* This file is compiled into the separate program, "pd-watchdog," which

どうやら、pd-watchdogとかpdsendは関係ないようだ。debugのお供用かな?(bin/から削除してもpdの動作に 不都合は無かった)pd-guiは、pd.tkのラッパーになって、pdとのネットワーク通信を 担当してるっぽい。推測がビンゴかどうか確認してみよう。

盗聴してみる

ネットワークの盗聴と言ったら、wiresharkで決まりなんだけど、生憎入っていない。 入れようとしたら、ports祭りの後遺症で、いろいろなライブラリーを更新してからで ないと入らない事が判明。昔懐かしいものでやってみる。

ちょっと余談になるけど、ArchLinuxにwiresharkを入れてみたんだけど、使うのを断念した。 だって、地獄の番犬(ケルベロス)を要求してくるんだもん。そんなのおいらは使わん。 tcpdumpも入れてみたけど、やはり使ってるライブラリーのミスマッチがあって起動せず。 Linuxにも、FreeBSDの/etc/libmap.confみたいな仕組みはないものか脳。

最初、lo0じゃ無い外に開かれたNICを使ったものだから、パケットをキャプチャ 出来なくて少々焦ったよ。

[sakae@cdr ~]$ sudo tcpdump -i lo0 -X port 5400
15:31:48.458385 IP localhost.58612 > localhost.5400: Flags [P.], ack 2031443132, win 8960, options [nop,nop,TS val 372195 ecr 2208906876], length 49
        0x0000:  4500 0065 0570 4000 4006 0000 7f00 0001  E..e.p@.@.......
        0x0010:  7f00 0001 e4f4 1518 dbb3 47c6 7915 5cbc  ..........G.y.\.
        0x0020:  8018 2300 fe59 0000 0101 080a 0005 ade3  ..#..Y..........
        0x0030:  83a9 3e7c 7064 206f 7065 6e20 6d79 7465  ..>|pd.open.myte
        0x0040:  7374 2e70 6420 2f75 7372 2f68 6f6d 652f  st.pd./usr/home/
        0x0050:  7361 6b61 652f 7064 2d30 2e34            sakae/pd-0.4
15:31:48.466074 IP localhost.5400 > localhost.58612: Flags [P.], ack 49, win 8960, options [nop,nop,TS val 2208915319 ecr 372195], length 174
        0x0000:  4500 00e2 0571 4000 4006 0000 7f00 0001  E....q@.@.......
        0x0010:  7f00 0001 1518 e4f4 7915 5cbc dbb3 47f7  ........y.\...G.
        0x0020:  8018 2300 fed6 0000 0101 080a 83a9 5f77  ..#..........._w
        0x0030:  0005 ade3 7064 746b 5f63 616e 7661 735f  ....pdtk_canvas_
        0x0040:  6e65 7720 2e78 3238 3332 6235 3430 2031  new..x2832b540.1
        0x0050:  3238 2031 3338 202b 302b 3020            28.138.+0+0.

上記は、mytest.pdと言うパッチを、メインパネルから開いた所である。port番号58612が pd-gui側になり、5400番は、pd側になる。pd-gui側から、開いたパッチのファイル名と、 そのdir名(全ては表示されてませんが)が渡っている。これは、下記pd.tkスクリプトの pd "pd open ..."そのもに一致している。

proc open_file {filename} {
    global pd_opendir
    set directory [string range $filename 0 [expr [string last / $filename] - 1]]
    set pd_opendir $directory
    set basename [string range $filename [expr [string last / $filename] + 1] end]
    if {[string last .pd $filename] >= 0} {
        pd "pd open [pdtk_enquote $basename] [pdtk_enquote $directory] ;"
    }
}

そして、そのパッチがpd側で開かれてパースされ、pd-gui側に、pdtk_canvas_newと言う コマンドを渡し、表示をお願いしてる。下記は、pd.tk側でキャンバス(ウィンドウ) を開く手続きになる。

############# pdtk_canvas_new -- create a new canvas ###############
proc pdtk_canvas_new {name width height geometry editable} {
    global pd_opendir
    global pd_tearoff
    global pd_nt
    global tcl_version

    toplevel $name -menu $name.m
        # if we're a mac, refuse to make window so big you can't get to
        # the resizing control
    if {$pd_nt == 2} {
        if {$width > [winfo screenwidth $name] - 80} {
            set width [expr [winfo screenwidth $name] - 80]
        }
        if {$height > [winfo screenheight $name] - 80} {
            set height [expr [winfo screenheight $name] - 80]
        }
    }

もう、pd.tk側には見るべき点はないので、このぐらいにしておこう。そうそう、このpd.tk スクリプト1本で、unix,ms,Macの3メジャーOSに対応している。

# set pd_nt (bad name) 0 for unix, 1 for microsoft, and 2 for Mac OSX.

bad nameって書いてあるけど、本当に悪い名前の代表だわい。WindowsNTを連想するもんなあ。 これはきっと、最初unix用に開発、そのうちにWindows用も追加。その時にフラグとして 付け加えたんだな。そのうちにMacでも動くようにした時、フラグを流用しちゃったんだ。 たった一行のコメントから、歴史が推測できて、面白いな。そう言えば、pdのソースの方にも、 愚痴が書いてあった。__APPLE__ ってのは、なんだかなあって。

本流の見所

折角、gdbで追えるようにしたので、ちょと本流を追いかけてみる。

s_main.cの中にある sys_main() が本流である。この中で、pd_init()は、是非詳細を 見ておくべきだろう。何たって、骨格が分かりますからね。

sys_startgui()で、pd-guiを起動してますね。そして舞台は、m_sched.cの m_mainloop()に 移り、最終的にその中の m_pollingscheduler()内をぐるぐる回って、スケジュールを 消化して行きます。(正確には、m_pollingscheduler内のwhileループですが)

スケジューラーったら、OSに出てくるやつですかね。ざっとこのルーチンを読んだ限りでは 一番優先度が高いのは、dacで次はmidi最後はguiとなっているようでした。また、ユーザーが 自分自身でスケジューラーを登録出来るようになってました。また、dacが動いている時は 常に信号データを供給しなければならないという宿命があるため(データが途切れると、 ノイズが入る等の問題が発生する)、そういう問題の察知のため、ミリセコンド単位で 監視してるようです。これ以上深入りすると、OS屋さんになっちゃうので、これぐらいに します。

次は、pd_bangに、ブレークポイントを置いて、止まった所でスタックダンプしてみました。

(gdb) bt
#0  pd_bang (x=0x2830aae0) at m_pd.c:269
#1  0x08094635 in outlet_bang (x=0x283550c0) at m_obj.c:331
#2  0x0807b783 in bng_bout2 (x=0x28359200) at g_bang.c:295
#3  0x0807b97c in bng_click (x=0x28359200, xpos=45, ypos=32, shift=0, ctrl=0, alt=0) at g_bang.c:338
#4  0x0807b9c0 in bng_newclick (z=0x28359200, glist=0x2832b540, xpix=45, ypix=32, shift=0, alt=0, dbl=0, doit=1) at g_bang.c:344
#5  0x08071505 in gobj_click (x=0x28359200, glist=0x2832b540, xpix=45, ypix=32, shift=0, alt=0, dbl=0, doit=1) at g_editor.c:108
#6  0x080743ca in canvas_doclick (x=0x2832b540, xpos=45, ypos=32, which=1, mod=0, doit=1) at g_editor.c:1269
#7  0x08074bb3 in canvas_mousedown (x=0x2832b540, xpos=45, ypos=32, which=1, mod=0) at g_editor.c:1427
#8  0x080934f1 in pd_typedmess (x=0x2832b540, s=0x283064a0, argc=0, argv=0xbfbfe77c) at m_class.c:744
#9  0x0806ee23 in guiconnect_anything (x=0x283560a0, s=0x283064a0, ac=4, av=0xbfbfe75c) at g_guiconnect.c:70
#10 0x080937cf in pd_typedmess (x=0x283560a0, s=0x283064a0, argc=4, argv=0xbfbfe75c) at m_class.c:765
#11 0x08096fba in binbuf_eval (x=0x28309658, target=0x283560a0, argc=0, argv=0x0) at m_binbuf.c:722
#12 0x0809db97 in socketreceiver_read (x=0x2830aa20, fd=8) at s_inter.c:546
#13 0x0809d1cd in sys_domicrosleep (microsec=0, pollem=1) at s_inter.c:184
#14 0x0809e31a in sys_pollgui () at s_inter.c:833
#15 0x0809af3c in m_pollingscheduler () at m_sched.c:496
#16 0x0809b09e in m_mainloop () at m_sched.c:572
#17 0x0809b8a3 in sys_main (argc=1, argv=0xbfbfeaa0) at s_main.c:315
#18 0x080a2082 in main (argc=Error accessing memory address 0x0: Bad address.) at s_entry.c:32

こんなのを手がかりに、ソースを追いかけていくと良いのかな? 結構、追いかけがいが ありそうで、ちとうんざりぎみです。