pdをやりなおす(2)

効率的なプログラミング

#N canvas 0 0 450 300 12; #N canvas 0 0 450 300 hoge 0; #X obj 97 46 inlet; #X obj 232 468 dac~; #X obj 233 416 *~; #X obj 251 181 line~; #X obj 98 367 osc~; #X msg 251 142 1 \, 0 400; #X obj 115 242 expr~ pow($v1 \, 8); #X obj 96 289 *~; #X floatatom 97 93 5 0 0 0 - - -; #X obj 97 196 osc~; #X obj 97 329 *~; #X obj 150 289 * 8; #X connect 0 0 8 0; #X connect 2 0 1 0; #X connect 2 0 1 1; #X connect 3 0 6 0; #X connect 3 0 2 1; #X connect 4 0 2 0; #X connect 5 0 3 0; #X connect 6 0 7 1; #X connect 7 0 10 0; #X connect 8 0 9 0; #X connect 8 0 11 0; #X connect 8 0 5 0; #X connect 9 0 7 0; #X connect 10 0 4 0; #X connect 11 0 10 1; #X restore 193 149 pd hoge; #X floatatom 193 104 5 0 0 0 - - -; #X connect 1 0 0 0;


ナンバーボックスと「pd hoge」しか見えないが、実は先程のパッチと同じ機能を持つ。
「pd hoge」を右クリック>Openすると中のサブパッチを見ることができる。


パッチの一部をサブパッチとして小分けにするとプログラミングがしやすくなる。
サブパッチはpdオブジェクトで作成する。引数のhogeは適当な名前である。
サブパッチへの入力はinlet、inlet~、出力はoutlet、outlet~で行なう。

#N canvas 0 0 450 300 12; #N canvas 0 0 1024 692 out 0; #X obj 173 339 dac~; #X obj 174 287 *~; #X obj 191 241 lop~ 5; #X obj 114 241 inlet~; #X obj 191 198 inlet; #X connect 1 0 0 0; #X connect 1 0 0 1; #X connect 2 0 1 1; #X connect 3 0 1 0; #X connect 4 0 2 0; #X restore 174 181 pd out; #X obj 263 51 vsl 15 101 0 1 0 0 empty empty empty 0 -9 0 10 -262130 -1 -1 0 1; #N canvas 0 0 560 558 hoge 0; #X obj 97 46 inlet; #X obj 233 416 *~; #X obj 251 181 line~; #X obj 98 367 osc~; #X msg 251 142 1 \, 0 400; #X obj 115 242 expr~ pow($v1 \, 8); #X obj 96 289 *~; #X floatatom 97 93 5 0 0 0 - - -; #X obj 97 196 osc~; #X obj 97 329 *~; #X obj 150 289 * 8; #X obj 232 468 outlet~; #X connect 0 0 7 0; #X connect 1 0 11 0; #X connect 2 0 5 0; #X connect 2 0 1 1; #X connect 3 0 1 0; #X connect 4 0 2 0; #X connect 5 0 6 1; #X connect 6 0 9 0; #X connect 7 0 8 0; #X connect 7 0 10 0; #X connect 7 0 4 0; #X connect 8 0 6 0; #X connect 9 0 3 0; #X connect 10 0 9 1; #X restore 173 133 pd hoge; #X floatatom 173 88 5 0 0 0 - - -; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 2 0;


音量制御の部分を「pd out」にまとめた。

#N canvas 0 0 450 300 12; #N canvas 0 0 1024 692 out 0; #X obj 173 339 dac~; #X obj 174 287 *~; #X obj 191 241 lop~ 5; #X obj 191 198 inlet; #X obj 175 158 receive~ moge; #X connect 1 0 0 0; #X connect 1 0 0 1; #X connect 2 0 1 1; #X connect 3 0 2 0; #X connect 4 0 1 0; #X restore 230 169 pd out; #X obj 230 52 vsl 15 101 0 1 0 0 empty empty empty 0 -9 0 10 -262130 -1 -1 0 1; #N canvas 0 0 560 558 hoge 0; #X obj 97 46 inlet; #X obj 233 416 *~; #X obj 251 181 line~; #X obj 98 367 osc~; #X msg 251 142 1 \, 0 400; #X obj 115 242 expr~ pow($v1 \, 8); #X obj 96 289 *~; #X floatatom 97 93 5 0 0 0 - - -; #X obj 97 196 osc~; #X obj 97 329 *~; #X obj 150 289 * 8; #X obj 232 468 send~ moge; #X connect 0 0 7 0; #X connect 1 0 11 0; #X connect 2 0 5 0; #X connect 2 0 1 1; #X connect 3 0 1 0; #X connect 4 0 2 0; #X connect 5 0 6 1; #X connect 6 0 9 0; #X connect 7 0 8 0; #X connect 7 0 10 0; #X connect 7 0 4 0; #X connect 8 0 6 0; #X connect 9 0 3 0; #X connect 10 0 9 1; #X restore 155 169 pd hoge; #X floatatom 155 124 5 0 0 0 - - -; #X connect 1 0 0 0; #X connect 3 0 2 0;


ふたつのサブパッチは結線していないが、先程の例と同じ機能を持つ。
send~、receive~を使って信号を渡している。これらはサブパッチに限らずどこでも使える。
引数に同じ名前を持つsend~とreceive~で信号が流れる。
同じ名前を持つsend~が複数あるとエラーとなる。


次の例は予め三つのpdファイルを作成しておく。


(hoge.pd)

#N canvas 0 0 449 595 12; #X obj 107 56 inlet; #X obj 243 426 *~; #X obj 261 191 line~; #X obj 108 377 osc~; #X msg 261 152 1 \, 0 400; #X obj 125 252 expr~ pow($v1 \, 8); #X obj 106 299 *~; #X floatatom 107 103 5 0 0 0 - - -; #X obj 107 206 osc~; #X obj 107 339 *~; #X obj 160 299 * 8; #X obj 243 476 outlet~; #X connect 0 0 7 0; #X connect 1 0 11 0; #X connect 2 0 5 0; #X connect 2 0 1 1; #X connect 3 0 1 0; #X connect 4 0 2 0; #X connect 5 0 6 1; #X connect 6 0 9 0; #X connect 7 0 8 0; #X connect 7 0 10 0; #X connect 7 0 4 0; #X connect 8 0 6 0; #X connect 9 0 3 0; #X connect 10 0 9 1;


(out.pd)

#N canvas 0 0 450 300 12; #X obj 160 233 dac~; #X obj 161 181 *~; #X obj 178 135 lop~ 5; #X obj 178 92 inlet; #X obj 162 52 inlet~; #X connect 1 0 0 0; #X connect 1 0 0 1; #X connect 2 0 1 1; #X connect 3 0 2 0; #X connect 4 0 1 0; #X coords 0 0 1 1 85 60 0;


(main.pd)

#N canvas 0 0 450 300 12; #X obj 219 52 vsl 15 101 0 1 0 0 empty empty empty 0 -9 0 10 -262130 -1 -1 0 1; #X floatatom 156 84 5 0 0 0 - - -; #X obj 156 129 hoge; #X obj 201 172 out; #X connect 0 0 3 1; #X connect 1 0 2 0; #X connect 2 0 3 0;


main.pdを開くと、やはり先程と同じ機能のパッチであるが、
先程までサブパッチとしていたものを外部パッチとして呼び出している。
外部パッチとして独立させることで、同じ機能を他のパッチでも使い回せるようになる。


(mohe.pd)

#N canvas 0 0 449 595 12; #X obj 107 56 inlet; #X obj 243 426 *~; #X obj 261 191 line~; #X obj 108 377 osc~; #X msg 261 152 1 \, 0 400; #X obj 125 252 expr~ pow($v1 \, 8); #X obj 106 299 *~; #X floatatom 107 103 5 0 0 0 - - -; #X obj 107 206 osc~; #X obj 107 339 *~; #X obj 243 476 outlet~; #X obj 160 299 * \$1; #X connect 0 0 7 0; #X connect 1 0 10 0; #X connect 2 0 5 0; #X connect 2 0 1 1; #X connect 3 0 1 0; #X connect 4 0 2 0; #X connect 5 0 6 1; #X connect 6 0 9 0; #X connect 7 0 8 0; #X connect 7 0 4 0; #X connect 7 0 11 0; #X connect 8 0 6 0; #X connect 9 0 3 0; #X connect 11 0 9 1;


(main.pd)

#N canvas 0 0 450 300 12; #X obj 223 58 vsl 15 101 0 1 0 0 empty empty empty 0 -9 0 10 -262130 -1 -1 0 1; #X floatatom 160 95 5 0 0 0 - - -; #X obj 160 140 mohe 20; #X obj 205 183 out; #X connect 0 0 3 1; #X connect 1 0 2 0; #X connect 2 0 3 0;


外部パッチに引数を与えることで、同じパッチに異なるふるまいをさせることができる。
mohe.pdに「* $1」とあるが、この$1で引数を受け取っている。
引数が複数の場合は、$2、$3と続く。

カウンタ

1ずつ加算する処理(インクリメント)はプログラミングでは多用されるが、
pdでは以下のようにして行なう。いわゆるカウンタと呼ばれる。

#N canvas 0 0 450 300 12; #X obj 173 89 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 173 134 i; #X obj 221 134 + 1; #X floatatom 173 180 5 0 0 0 - - -; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 1 0 3 0; #X connect 2 0 1 1;


一番上のスイッチはbangと呼ぶ。クリックや、数値等の値などを受け取ることでbangメッセージを発する。
bangは様々なオブジェクトが動作するきっかけに使われる。


iオブジェクトは整数を扱うintの省略形表記である。pdのオブジェクトにはこうした省略形がいくつかある。


この奇妙なパッチをよく観察してみる。
intの出力は+に入力され1を加算される。+の出力はintの右インレットに戻される。
intの値はbangをクリックするたびに1ずつ加算する。


このpd独特の仕組みを理解するには以下の例を見てみる。

#N canvas 0 0 450 300 12; #X obj 85 149 i; #X floatatom 85 190 5 0 0 0 - - -; #X floatatom 60 106 5 0 0 0 - - -; #X floatatom 103 106 5 0 0 0 - - -; #X obj 196 149 i; #X floatatom 196 190 5 0 0 0 - - -; #X floatatom 214 106 5 0 0 0 - - -; #X obj 181 106 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X floatatom 324 190 5 0 0 0 - - -; #X floatatom 276 84 5 0 0 0 - - -; #X floatatom 324 84 5 0 0 0 - - -; #X obj 324 118 t b i; #X obj 324 149 +; #X text 90 231 [1]; #X text 203 231 [2]; #X text 313 231 [3]; #X connect 0 0 1 0; #X connect 2 0 0 0; #X connect 3 0 0 1; #X connect 4 0 5 0; #X connect 6 0 4 1; #X connect 7 0 4 0; #X connect 9 0 12 0; #X connect 10 0 11 0; #X connect 11 0 12 0; #X connect 11 1 12 1; #X connect 12 0 8 0;


数値等の値を扱ういくつかのオブジェクトには、インレットの優先順位がある。
その場合、左をhot、右をcoldと呼ぶ
[1]の例では左のナンバーの値はすぐにintから出力される一方、右のナンバーの値は出力されない。
しかしintの中には右のナンバーの値が格納されている。


[2]の例では、左インレットにbangを入力することで、右インレットから格納された値が、intから出力される。
ふたつのインレットを使って、いかなる時も足し算ができるようにするには[3]のようにしなければならない。


tはtrigger、bはbangの省略形。
このtriggerオブジェクトは右側のアウトレットから順に、引数で指定された形式(ここではintとbang)で出力する。
(なおpdでの数値等の出力順は必ず右から左である)
つまり最初にtriggerに入力された数値、次にbangを出力する。
bangを受け取った+は即座に足し算の結果を返す。
先程のカウンタも、インクリメントの結果をbangに応じて出力していた。


ふたたびカウンタに戻る。

#N canvas 0 0 450 300 12; #X obj 178 137 i; #X obj 226 137 + 1; #X floatatom 178 231 5 0 0 0 - - -; #X obj 178 58 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 178 97 metro 100; #X connect 0 0 1 0; #X connect 0 0 2 0; #X connect 1 0 0 1; #X connect 3 0 4 0; #X connect 4 0 0 0;


metroは引数で指定された時間間隔でbangを繰り返し出力する。
「metro 100」は100ms(0.1秒)間隔でbangを出力する。
一番上のトグルをオンにすることで動作が始まる。

#N canvas 0 0 450 300 12; #X obj 190 133 i; #X obj 238 133 + 1; #X floatatom 190 227 5 0 0 0 - - -; #X obj 190 54 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 190 93 metro 100; #X obj 190 180 % 16; #X connect 0 0 1 0; #X connect 0 0 5 0; #X connect 1 0 0 1; #X connect 3 0 4 0; #X connect 4 0 0 0; #X connect 5 0 2 0;


カウンタの出力に%をつなげる。
%とはモジュロ演算と呼ばれるもので、その結果、数値は一定の範囲での繰り返しになる。

#N canvas 0 0 667 300 12; #X floatatom 69 248 5 0 0 0 - - -; #X obj 324 106 vsl 15 101 0 1 0 0 empty empty empty 0 -9 0 10 -262130 -1 -1 0 1; #X obj 201 111 * 1000; #X obj 201 151 mohe 20; #X obj 69 198 tabread array1; #N canvas 0 0 450 300 (subpatch) 0; #X array array1 16 float 3; #A 0 0.435721 0.0785732 0.707154 0.178575 0.378578 0.100002 0.0500014 0.0642873 0.0357155 0.557152 0.814299 0.107145 0.0642876 0.0285727 0.257148 0.164289; #X coords 0 1 16 0 200 140 1; #X restore 404 64 graph; #X obj 69 104 i; #X obj 117 104 + 1; #X obj 69 25 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 69 64 metro 100; #X obj 69 151 % 16; #X obj 306 229 out; #X connect 1 0 11 1; #X connect 2 0 3 0; #X connect 3 0 11 0; #X connect 4 0 0 0; #X connect 4 0 2 0; #X connect 6 0 7 0; #X connect 6 0 10 0; #X connect 7 0 6 1; #X connect 8 0 9 0; #X connect 9 0 6 0; #X connect 10 0 4 0;


これまでの例を総合して簡単なシーケンサを作成。
array1と書かれたテーブルは配列オブジェクトである。


先程のカウンタの値をインデックスとしてtabreadに与えることで、配列の値を読み出している。
配列テーブルは複数のスライダの集まりのように操作できる。
線をドラッグするとフレーズが変化する(ドラッグには少々コツが要る)。