PureData(3)

Windows7上で、いろいろなOSを動かしている。私は、FreeBSDとArchLinuxとopensolaris ぐらいだけど、この間会った人は、これにNetBSD,OpenBSD,とかPlan9まで入れて動かして いるとか。おかげで、HDDが手狭になったので、大きい物の換装したそうだ。好きな人は とことんやるなあ。

まあ、Plan9を、らしく使うには、ファイルサーバー用、計算サーバー用 ログイン用という具合に3つぐらいマシンを用意するのが正しい使い方らしいので 大容量のHDDもうなずけるな。

そんな事より、ちと問題発生。ArchLinuxで、awesomeを使ってて

       Mod4 + l
           Increase master width factor by 5%.

上記のようなキーコンビネーションを実行すると、こやつをWindows7が食べちゃうんだ。 どうなるかと言うと、Windows7に画面ロックがかかっちゃう。よっていつまで たっても、ArchLinux側にキーコンビネーションが渡ってこない。

この際だから、Windows7のショートカットとか言う、キーコンビネーションを調べて みるかな。使えそうなのがあればマウスでぐりぐりよりはいいな。最近流行の、 指でグリグリ (あれジェスチャーって言うんでしたっけ?)は、おいらのマシンは対応してないんで。

えっと、困った時のF1キーか。Windowsヘルプとサポートか。ショートカットで検索と。 おお、ずばりのショートカットキーってのが出てきた。Windowsロゴキーのショートカット なのがあるな。田の字のマークと言うか旗みたいなキーは、ロゴキーって言うのか。 おいらは、四葉のクローバーキーとか、だえもんキーの方が好きだぞ。どこかに、それ らしいシールでもないかなあ。

で、ふむふむと、キーコンビネーションを見ている。まるで、emacsのkey-mapを指に 叩き込む作業と一緒だな。emacsのそれには文句を言うくせに、ロゴキーのやつに 文句を言わんと言う事は、Windows寄りの人は、ショートカットなんて使ってないんだ。

エアロスナップとかシェイクとかピークとか、みんなりんごさんの真似かな。

Tシャツとincanter

先月某所で、Tシャツを頂いてきた。太っ腹な事にたくさんあるから好きなだけ、 お持ち帰りくださいとの事だった。慎ましい私は、勿論一枚だけ頂いた。

某ロゴ入りのやつである。知っている人は、ははーん、知らない人はこの際覚えて くださいねっていう物である。まあ、広告塔宜しくお願いしますねって期待が込め られているのだろう。

λTシャツとか、最近では ここにリンク がある Tシャツ が、いいなあと思っています。 それにしても、ClojureでRもどきとは、恐れ入りました。

折角なので、incanter とやらをやってみる。最近はjreの新しいのが出たけど、FreeBSD上の 古いやつでも動くか確認してみる。

[sakae@fb ~/incanter]$ bin/clj
Clojure 1.2.0-master-SNAPSHOT
user=> (use '(incanter core stats charts))
nil
user=> (view (histogram (sample-normal 1000)))
#<ChartFrame org.jfree.chart.ChartFrame[frame0,0,2,500x400,layout=java.awt.BorderLayout,title=Incanter Plot,resizable,normal,defaultCloseOperation=DISPOSE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,477x400,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]>
user=>

user=> (view (function-plot sin -4 4))
#<ChartFrame org.jfree.chart.ChartFrame[frame1,0,2,500x400,layout=java.awt.BorderLayout,title=Incanter Plot,resizable,normal,defaultCloseOperation=DISPOSE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,477x400,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777675,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]>
user=> (doc sd)
-------------------------
incanter.stats/sd
([x])

  Returns the sample standard deviation of the data, x. Equivalent to
  R's sd function.

  Examples:
    (sd (sample-normal 100))

  References:
    http://incanter.org/docs/parallelcolt/api/cern/jet/stat/tdouble/DoubleDescriptive.html
    http://en.wikipedia.org/wiki/Standard_deviation

nil

古いのでもちゃんと動いたよ。今後の進歩が楽しみだな。ちゃんとRを継承してる ようだし。アンチョコ・シートも提供されてるし。

どんな部品類を使っているかは Incanter dependencies に説明があったよ。Java資産を使うっていいね。ちなみにHellowWorld代わりに 作ったヒストグラムを作成するアプリは、16Mのサイズになった。これを 富豪とみるか、どこでも動く便利なやつと見るかは、さまざまなだなあ。 incanterの万能表示関数関数っぽいのも有るしな。

user=> (doc view)
-------------------------
incanter.core/view
nil
   This is a general 'view' function. When given an Incanter matrix/dataset
    or a Clojure numeric collection, it will display it in a Java Swing
    JTable. When given an Incanter chart object, it will display it in a new
    window. When given a URL string, it will open the location with the
    platform's default web browser.

    When viewing charts, a :width (default 500) and :height (default 400)
    option can be provided.

    When viewing an incanter.processing sketch, set the :exit-on-close option
    to true (default is false) to kill the animation processes when you
    close the window (this will also kill your REPL or Swank server),
    otherwise those processing will continue to run in the background.

    Examples:

      (use '(incanter core stats datasets charts))

      ;; view matrices
      (def rand-mat (matrix (sample-normal 100) 4))
      (view rand-mat)

      ;; view numeric collections
      (view [1 2 3 4 5])
      (view (sample-normal 100))

      ;; view Incanter datasets
      (view (get-dataset :iris))

      ;; convert dataset to matrix, changing Species names to numeric codes
      (view (to-matrix (get-dataset :iris)))

      ;; convert dataset to matrix, changing Species names to dummy variables
      (view (to-matrix (get-dataset :iris) :dummies true))

      ;; view a chart
      (view (histogram (sample-normal 1000)))

      ;; view a URL
      (view "http://incanter.org")

      ;; view a PNG file
      (save (histogram (sample-normal 1000)) "/tmp/norm_hist.png")
      (view "file:///tmp/norm_hist.png")

PureDataのファイルフォーマット

前に書いたけど、Pdのパッチファイルをリバースエンジニアリングする話、やっぱり 本格的にやっている人が居た。manuals/Pd/Pd-File-Format.htmlに出ていた。 やっぱり、正式なドキュメントが無いのでHackしたとある。

    * A
    * N
          o canvas
    * X
          o array
          o connect
          o coords
          o floatatom
          o msg
          o obj
                + bng
                + tgl
                + nbx
                + vsl
                + hsl
                + vradio
                + hradio
                + vu
                + cnv
          o [name]
          o pd [name]
          o restore
          o symbolatom
          o text

最初の段のA,N,Xには頭に#つくあれだ。2段目以降はそのままタグとして 使われるのか。下記は、いっぱいある中から、数字の表現を引っ張ってきたものだ。

floatatom
Description:
	Defines a number box
Syntax:
	#X floatatom [x_pos] [y_pos] [width] [lower_limit] [upper_limit] [label_pos] [label] [receive] [send];\r\n
Parameters:
	[x_pos] - horizontal position within window
	[y_pos] - vertical position within window
	[width] - number of digits
	[lower_limit] - minimal value
	[upper_limit] - maximum value
	[label_pos] - label position relative to floatatom position. 0 = left, 1 = right, 2 = top, 3 = bottom
	[label] - floatatom label/name
	[receive] - receive symbol name
	[send] - send symbol name
Example:
	#X floatatom 32 26 5 0 0 0 - - -;
Remarks:
	
When the value of [upper_limit] minus the value of [lower_limit] is less than one, or the [width] attribute is set to one, PureData resets these values both to zero.
Floatatom and symbolatom are the only elements that uses "-" characters to indicate that no value has been assigned to its attributes [label], [receive] and [send].

こちらから頂いてきた patchファイル、随分容量が大きいなと思って開いてみたら、Array用のデータが ごっそりと内蔵されてました。

HOWTO write an External for puredata

なんて言うページも有ったので試してみる。やっぱり最初は、helloworld つう事で。

#include "m_pd.h"

static t_class *helloworld_class;

typedef struct _helloworld {
  t_object  x_obj;
} t_helloworld;

void helloworld_bang(t_helloworld *x)
{
  post("Hello world !!");
}

void *helloworld_new(void)
{
  t_helloworld *x = (t_helloworld *)pd_new(helloworld_class);

  return (void *)x;
}

void helloworld_setup(void) {
  helloworld_class = class_new(gensym("helloworld"),
        (t_newmethod)helloworld_new,
        0, sizeof(t_helloworld),
        CLASS_DEFAULT, 0);
  class_addbang(helloworld_class, helloworld_bang);
}

bang信号がきたら、Hello world って叫ぶやつらしい。ちゃんとクラスの入れ物を 定義してから、メソッド定義、クラスの作成用関数、そしてPd側から一回呼ばれる setup関数を定義するんか。複雑なのはJava譲りだい。

こうして、helloworld.c の解説は載っているんだけど、コンパイル手順とかは 周知の事実らしくどこにも解説が無い。しょうがないので、拡張Pdのソースを 取り寄せてきて、門前の小僧習わぬ経を詠む してみる。何と言っても、ソースが真実だから。

ああ、今勝手に手が動いて、ソースが真実 と打っちゃったけど、これ、なかなか 良いフレーズじゃないですか。著作権を放棄しますから、みなさんご自由にお使い ください。

さて、何処に入門しようか? 3秒考えて、/home/sakae/Pd-0.41.4-extended/externals/mjlib にした。ここは、以前morseでお世話になった所だ。makefile.linuxを眺めて、 SOURCESに仲間入りさせて貰い、

[sakae@arch mjlib]$ make -f makefile.linux
gcc -c -o helloworld.o -g -O2 -DHAVE_LIBC=1 -DHAVE_LIBM=1 -DHAVE_LIBPTHREAD=1 -DSTDC_HEADERS=1 -DHAVE_FCNTL_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_UNISTD_H=1 -DTIME_WITH_SYS_TIME=1 -DHAVE_UNISTD_H=1 -DHAVE_GETPAGESIZE=1 -DHAVE_MMAP=1 -DHAVE_SELECT=1 -DHAVE_SOCKET=1 -DHAVE_STRERROR=1  -DPD_VERSION_MINOR=32 -I./include -I../src  -DVERSION=\"0.1\" -DPD helloworld.c
gcc -o helloworld.pd_linux -g -O2 -DHAVE_LIBC=1 -DHAVE_LIBM=1 -DHAVE_LIBPTHREAD=1 -DSTDC_HEADERS=1 -DHAVE_FCNTL_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_UNISTD_H=1 -DTIME_WITH_SYS_TIME=1 -DHAVE_UNISTD_H=1 -DHAVE_GETPAGESIZE=1 -DHAVE_MMAP=1 -DHAVE_SELECT=1 -DHAVE_SOCKET=1 -DHAVE_STRERROR=1  -DPD_VERSION_MINOR=32 -I./include -I../src  -export_dynamic  -shared  -DVERSION=\"0.1\" -DPD helloworld.o
/usr/bin/ld: warning: cannot find entry symbol xport_dynamic; defaulting to 00000460
rm helloworld.o

いろいろ、-Dオプションが付いてくるけど、まあそれは無視してもいいんだな。 肝は、-shardオプションっぽい。

[sakae@arch mjlib]$ file helloworld.pd_linux
helloworld.pd_linux: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, not stripped
[sakae@arch mjlib]$ file morse.pd_linux
morse.pd_linux: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped

どうやら、先輩と遜色なく出来たぽい。stripされてないのは、まだ信用ならんから だろうか? 一応中身を見ておく。

[sakae@arch mjlib]$ ldd ./helloworld.pd_linux
        linux-gate.so.1 =>  (0xb7886000)
        libc.so.6 => /lib/libc.so.6 (0xb772d000)
        /lib/ld-linux.so.2 (0xb7887000)

つぎは、こやつを、/usr/lib/pd/extraへ送り込めばいいかと。送り込んだ後

#N canvas 51 29 450 300 10;
#X obj 171 55 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1;
#X obj 171 114 helloworld;
#X connect 0 0 1 0;

実験してみた。ちゃんと、メインコンソールに、Hellow worldって出てきたよ。 これって、ひょっとしてrubyの拡張ライブラリーを作るより優しいね。

この後、2例程解説があって、次はpd's message-systemの説明になった。 大事なのは、浮動小数点形式の数字とシンボルとか。シンボルは検索の効率化 のためにHashに登録されると。gensym関数は、登録と検索をやってくれる。

次はpd-typesの説明があり、続いて大事な大事な m-pd.hの説明が始まっている。 そうか、これを理解出来れば、いいんだな。

そして大航海へ

ここまで来たら、ソースの海に溺れてみるしかないな。ソースの海を航海するには 羅針盤ならぬ、強力なツールが必要だ。そう、いつもお世話になるglobal。

[sakae@arch src]$ pwd
/home/sakae/Pd-0.41.4-extended/pd/src
[sakae@arch src]$ gtags
[sakae@arch src]$ htags -sanohITt 'Welcome to PureData source tour!'
[sakae@arch src]$ cd HTML
[sakae@arch HTML]$ w3m index.html

さあ、6万行(97個のファイル)のソースの海原へ!!

海原には親潮とか黒潮とかの流れがあるように、ソースも接頭文字によって グループ化されてました。何処に潜るかは、目標を決めてからですかね。

d*.c
にょろが付くオブジェクト関連(dsp)
g*.c
グラフィック関連
m*.c
mはmainを表す? それとも、message関係者なんでしょうか?
s*.c
soundとmidi関連
t*.c
Tcl/Tkとのインターフェース関連
u*.c
TCP/IP関連
x*.c
普通のオブジェクト関連