Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • jwilkes/purr-data
  • aggraef/purr-data
  • samthursfield/purr-data
  • prakhar/purr-data
  • yadu05/purr-data
  • NegiAkash890/purr-data
  • prateekpardeshi/purr-data
  • Shruti3004/purr-data
  • hidimpu/purr-data
  • Atseosi/purr-data
  • piyushjasaiwal/purr-data
  • deveshprasad/purr-data
  • skm_7/purr-data
  • sankt/purr-data
  • ashim_tom/purr-data
  • dineshsoni02/purr-data
  • chaitanya1-coder/purr-data
  • Nitish0007/purr-data
  • nitin/purr-data
  • shuvam09/purr-data
  • gabrielabittencourt/purr-data
  • sivasai/purr-data
  • flachyjoe/purr-data
  • ishankaler/purr-data
  • prateek/purr-data
  • RukshanJS/purr-data
  • rajatshrm648/purr-data
  • Srashti/purr-data
  • Paarth/purr-data
  • AniruddhaGawali/purr-data
  • brittneyjuliet/purr-data
  • prakharagarwal1/purr-data
  • Shreyanshpaliwalcmsmn/purr-data
  • k_amrut/purr-data
  • AyushAnand/purr-data
  • Va16hav07/purr-data
36 results
Show changes
Showing
with 0 additions and 5112 deletions
#N canvas 271 55 464 710 10;
#X floatatom 243 167 5 0 0 0 - - -;
#X symbolatom 291 166 10 0 0 0 - - -;
#X obj 196 187 bng 15 250 50 0 empty empty bang -6 23 1 12 -262144
-1 -1;
#X obj 196 143 route bang float symbol list;
#X obj 243 187 bng 15 250 50 0 empty empty float -4 23 1 12 -262144
-1 -1;
#X obj 291 187 bng 15 250 50 0 empty empty symbol -12 23 1 12 -262144
-1 -1;
#X obj 339 187 bng 15 250 50 0 empty empty list -2 23 1 12 -262144
-1 -1;
#X obj 387 187 bng 15 250 50 0 empty empty undefined -18 23 1 12 -262144
-1 -1;
#X text 24 35 There are no 0- or 1-element lists. These are immediately
converted to other types:;
#X msg 195 75 list;
#X msg 202 96 list 2;
#X msg 207 115 list word;
#X floatatom 11 147 5 0 0 0 - - -;
#X symbolatom 77 147 10 0 0 0 - - -;
#X obj 11 165 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 77 165 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X text 19 244 Therefore \, the definition of list is: a series of
elements with a selector of "list" and 2 or more elements. (But don't
forget! A series whose element is a float has an implied "list" selector!)
;
#X obj 123 373 select;
#X msg 157 343 list;
#X msg 122 344 bang;
#X msg 247 343 list;
#X msg 212 344 bang;
#X obj 213 373 print;
#X msg 73 429 3;
#X msg 106 428 list 3;
#X obj 59 471 *;
#X msg 223 429 3;
#X msg 256 428 list 3;
#X obj 208 481 *~;
#X text 19 311 These two disagree about 0 element lists:;
#X text 19 401 These two disagree about 1 element lists:;
#X obj 140 597 route 1;
#X msg 140 575 1 3;
#X obj 125 622 *~;
#X text 24 528 [route] doesn't seem to automatically convert 1 element
lists:;
#X connect 0 0 4 0;
#X connect 1 0 5 0;
#X connect 3 0 2 0;
#X connect 3 1 0 0;
#X connect 3 2 1 0;
#X connect 3 3 6 0;
#X connect 3 4 7 0;
#X connect 9 0 3 0;
#X connect 9 0 13 0;
#X connect 9 0 12 0;
#X connect 10 0 3 0;
#X connect 10 0 12 0;
#X connect 11 0 3 0;
#X connect 11 0 13 0;
#X connect 12 0 14 0;
#X connect 13 0 15 0;
#X connect 18 0 17 1;
#X connect 19 0 17 1;
#X connect 20 0 22 0;
#X connect 21 0 22 0;
#X connect 23 0 25 1;
#X connect 24 0 25 1;
#X connect 26 0 28 1;
#X connect 27 0 28 1;
#X connect 31 0 33 1;
#X connect 32 0 31 0;
#N canvas 220 136 605 423 10;
#X obj 190 245 print message;
#X msg 190 58 list x.wav 44100;
#X msg 223 118 x.wav 44100;
#X msg 189 159 read \$1 \$2;
#X msg 202 79 44100 x.wav;
#X text 119 33 message arguments only work with lists.;
#X text 318 56 declared list;
#X text 314 79 implied list;
#X text 307 118 undefined list;
#X text 77 68 these work -->;
#X text 68 120 this does not -->;
#X connect 1 0 3 0;
#X connect 2 0 3 0;
#X connect 3 0 0 0;
#X connect 4 0 3 0;
#N canvas 238 303 753 580 10;
#X obj 63 224 float;
#X floatatom 63 252 5 0 0 0 - - -;
#X obj 102 224 + 1;
#X obj 62 274 bng 15 250 50 0 empty empty empty 0 -6 0 10 -262144 -1
-1;
#X obj 35 166 bang;
#X obj 81 167 bang;
#X obj 59 124 bng 15 250 50 0 empty empty empty 0 -6 0 10 -262144 -1
-1;
#X obj 220 232 float;
#X floatatom 220 260 5 0 0 0 - - -;
#X obj 259 232 + 1;
#X obj 219 282 bng 15 250 50 0 empty empty empty 0 -6 0 10 -262144
-1 -1;
#X obj 224 123 bng 15 250 50 0 empty empty empty 0 -6 0 10 -262144
-1 -1;
#X obj 180 165 delay 1000;
#X obj 261 166 delay 1000;
#X msg 90 199 0;
#X text 118 199 reset;
#X msg 247 208 0;
#X text 275 208 reset;
#X obj 550 231 float;
#X floatatom 550 259 5 0 0 0 - - -;
#X obj 589 231 + 1;
#X obj 549 281 bng 15 250 50 0 empty empty empty 0 -6 0 10 -262144
-1 -1;
#X obj 554 122 bng 15 250 50 0 empty empty empty 0 -6 0 10 -262144
-1 -1;
#X obj 510 164 delay 1000;
#X obj 591 165 delay 1000;
#X msg 577 207 0;
#X text 605 207 reset;
#X obj 449 171 bang;
#X text 80 68 counts twice per bang button click:;
#X text 451 70 but this counts once:;
#X obj 102 445 float;
#X floatatom 102 473 5 0 0 0 - - -;
#X obj 141 445 + 1;
#X obj 101 495 bng 15 250 50 0 empty empty empty 0 -6 0 10 -262144
-1 -1;
#X obj 106 336 bng 15 250 50 0 empty empty empty 0 -6 0 10 -262144
-1 -1;
#X msg 129 421 0;
#X text 157 421 reset;
#X obj 62 378 metro 1000;
#X obj 143 379 metro 1000;
#X text 317 334 The first [delay] triggers the execution \, which ultimately
sends a bang to the second [delay] \, which hasn't fired yet. When
the second [delay] receives that bang \, it resets its timer. Therefore
\, the second [delay] never fires.;
#X connect 0 0 1 0;
#X connect 0 0 2 0;
#X connect 1 0 3 0;
#X connect 2 0 0 1;
#X connect 4 0 0 0;
#X connect 5 0 0 0;
#X connect 6 0 4 0;
#X connect 6 0 5 0;
#X connect 7 0 8 0;
#X connect 7 0 9 0;
#X connect 8 0 10 0;
#X connect 9 0 7 1;
#X connect 11 0 12 0;
#X connect 11 0 13 0;
#X connect 12 0 7 0;
#X connect 13 0 7 0;
#X connect 14 0 0 1;
#X connect 16 0 7 1;
#X connect 18 0 19 0;
#X connect 18 0 20 0;
#X connect 18 0 27 0;
#X connect 19 0 21 0;
#X connect 20 0 18 1;
#X connect 22 0 23 0;
#X connect 22 0 24 0;
#X connect 23 0 18 0;
#X connect 24 0 18 0;
#X connect 25 0 18 1;
#X connect 27 0 22 0;
#X connect 30 0 31 0;
#X connect 30 0 32 0;
#X connect 31 0 33 0;
#X connect 32 0 30 1;
#X connect 34 0 37 0;
#X connect 34 0 38 0;
#X connect 35 0 30 1;
#X connect 37 0 30 0;
#X connect 38 0 30 0;
#N canvas 793 278 453 550 10;
#X obj 122 255 route 1 bla;
#X msg 118 113 list bla hu;
#X obj 114 293 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144
-1 -1;
#X obj 149 297 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144
-1 -1;
#X obj 197 296 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144
-1 -1;
#X msg 86 85 bla hu;
#X msg 159 147 1 two;
#X msg 203 147 list 1 two;
#X text 32 22 mixing different data types in one route object is not
supported and thus leads to unexpected behaviour.;
#X msg 169 220 0;
#X text 197 218 <- unexpectedly matches 'bla';
#X text 134 83 <- unexpectedly not matching;
#X text 200 113 unexpectedly matches 'bla';
#X connect 0 0 2 0;
#X connect 0 1 3 0;
#X connect 0 2 4 0;
#X connect 1 0 0 0;
#X connect 5 0 0 0;
#X connect 6 0 0 0;
#X connect 7 0 0 0;
#X connect 9 0 0 0;
#N canvas 414 90 494 642 10;
#X obj 334 314 route symbol;
#X obj 336 334 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 413 335 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 331 361 route symbol;
#X obj 333 381 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 410 382 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X msg 318 269 test;
#X msg 357 269 symbol test;
#X obj 58 331 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 135 331 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 55 378 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 132 378 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X msg 89 266 bang;
#X obj 56 311 route bang;
#X obj 53 358 route bang;
#X text 14 5 routing based on reserved words:;
#X obj 188 331 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 265 331 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 185 378 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 262 378 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 186 311 route float;
#X obj 183 358 route float;
#X msg 170 266 1;
#X msg 209 266 float 12;
#X obj 181 542 route list;
#X obj 181 561 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 249 562 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 168 580 route list;
#X obj 168 599 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 236 600 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X msg 184 493 list 1 two 3;
#X msg 167 473 1 two 3;
#X msg 200 513 list one 2 three;
#X msg 365 289 symbol;
#X msg 113 484 list;
#X msg 224 287 float test;
#X text 26 245 These all output the as same atom type that is routed:
;
#X msg 15 266 bang test;
#X obj 71 131 route 1;
#X msg 71 183 2 3 4;
#X obj 71 164 prepend set;
#X msg 71 100 1 2 3 4;
#X msg 218 183 is not a list;
#X obj 218 164 prepend set;
#X obj 218 131 route this;
#X msg 218 100 this is not a list;
#X text 21 50 [route] has three modes \, first is float \, second is
symbol \, and third is data type. In the first two modes \, the first
element of the set is stripped off by [route].;
#X text 26 206 In the third mode \, [route] outputs the same atom type
(bang->bang \, float->float \, symbol->symbol).;
#X text 26 419 Lists do not behave the same with [route list] even
though it is interpreting incoming lists \, not just routing by keyword
\, as in the second symbol mode.;
#X msg 174 164 set;
#X text 157 164 re;
#X text 164 24 "bang" \, "float" \, "symbol" \, and "list";
#X connect 0 0 1 0;
#X connect 0 0 3 0;
#X connect 0 1 2 0;
#X connect 3 0 4 0;
#X connect 3 1 5 0;
#X connect 6 0 0 0;
#X connect 7 0 0 0;
#X connect 12 0 13 0;
#X connect 13 0 8 0;
#X connect 13 0 14 0;
#X connect 13 1 9 0;
#X connect 14 0 10 0;
#X connect 14 1 11 0;
#X connect 20 0 16 0;
#X connect 20 0 21 0;
#X connect 20 1 17 0;
#X connect 21 0 18 0;
#X connect 21 1 19 0;
#X connect 22 0 20 0;
#X connect 23 0 20 0;
#X connect 24 0 25 0;
#X connect 24 0 27 0;
#X connect 24 1 26 0;
#X connect 27 0 28 0;
#X connect 27 1 29 0;
#X connect 30 0 24 0;
#X connect 31 0 24 0;
#X connect 32 0 24 0;
#X connect 33 0 0 0;
#X connect 34 0 24 0;
#X connect 35 0 20 0;
#X connect 37 0 13 0;
#X connect 38 0 40 0;
#X connect 40 0 39 0;
#X connect 41 0 38 0;
#X connect 43 0 42 0;
#X connect 44 0 43 0;
#X connect 45 0 44 0;
#X connect 49 0 42 0;
#X connect 49 0 39 0;
#N canvas 514 341 621 440 10;
#X obj 57 205 select;
#X msg 52 108 symbol twenty;
#X msg 7 109 23;
#X msg 99 179 symbol twenty;
#X obj 57 250 bng 25 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 93 250 bng 25 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X msg 86 158 23;
#X msg 402 108 symbol twenty;
#X msg 357 109 23;
#X msg 509 179 symbol twenty;
#X obj 407 250 bng 25 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 493 250 bng 25 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X msg 496 158 23;
#X obj 407 205 select twenty;
#X msg 218 108 symbol twenty;
#X msg 177 109 23;
#X msg 289 179 symbol twenty;
#X obj 217 250 bng 25 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 253 250 bng 25 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X msg 276 158 23;
#X obj 217 205 select 23;
#X text 68 59 a [select] without a creation argument cannot use a symbol
selector (you get an error in the Pd window);
#X connect 0 0 4 0;
#X connect 0 1 5 0;
#X connect 1 0 0 0;
#X connect 2 0 0 0;
#X connect 3 0 0 1;
#X connect 6 0 0 1;
#X connect 7 0 13 0;
#X connect 8 0 13 0;
#X connect 9 0 13 1;
#X connect 12 0 13 1;
#X connect 13 0 10 0;
#X connect 13 1 11 0;
#X connect 14 0 20 0;
#X connect 15 0 20 0;
#X connect 16 0 20 1;
#X connect 19 0 20 1;
#X connect 20 0 17 0;
#X connect 20 1 18 0;
#N canvas 366 31 565 405 10;
#X msg 168 46 anything 1 two three;
#X msg 180 66 one 2 3;
#X obj 167 308 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 362 305 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X msg 190 114 list one 2 3;
#X msg 191 136 1 2 3;
#X obj 167 285 trigger anything bang float list pointer symbol;
#X obj 424 306 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 491 307 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 296 305 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 230 305 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 35 362 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 111 362 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 137 362 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 164 362 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 85 362 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 59 362 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 35 340 trigger a b f l p s;
#X text 48 20 how [trigger] interprets and converts data can be tricky:
;
#X text 315 59 "undefined lists";
#X text 288 127 lists;
#X msg 199 175 4;
#X msg 202 196 float 234;
#X text 280 181 floats;
#X msg 208 229 symbol blah;
#X text 303 242 symbols;
#X msg 188 84 asdf;
#X msg 208 251 symbol four;
#X connect 0 0 6 0;
#X connect 1 0 6 0;
#X connect 4 0 6 0;
#X connect 5 0 6 0;
#X connect 6 0 2 0;
#X connect 6 0 17 0;
#X connect 6 1 10 0;
#X connect 6 2 9 0;
#X connect 6 3 3 0;
#X connect 6 4 7 0;
#X connect 6 5 8 0;
#X connect 17 0 11 0;
#X connect 17 1 16 0;
#X connect 17 2 15 0;
#X connect 17 3 12 0;
#X connect 17 4 13 0;
#X connect 17 5 14 0;
#X connect 21 0 6 0;
#X connect 22 0 6 0;
#X connect 24 0 6 0;
#X connect 26 0 6 0;
#X connect 27 0 6 0;
#N canvas 242 94 461 496 10;
#X floatatom 161 180 5 0 0 0 - - -;
#X symbolatom 209 179 10 0 0 0 - - -;
#X obj 114 200 bng 15 250 50 0 empty empty bang -6 23 1 12 -262144
-1 -1;
#X obj 114 156 route bang float symbol list;
#X obj 161 200 bng 15 250 50 0 empty empty float -4 23 1 12 -262144
-1 -1;
#X obj 209 200 bng 15 250 50 0 empty empty symbol -12 23 1 12 -262144
-1 -1;
#X obj 257 200 bng 15 250 50 0 empty empty list -2 23 1 12 -262144
-1 -1;
#X obj 305 200 bng 15 250 50 0 empty empty undefined -18 23 1 12 -262144
-1 -1;
#X msg 151 83 lots of pie;
#X floatatom 191 405 5 0 0 0 - - -;
#X symbolatom 259 404 10 0 0 0 - - -;
#X obj 124 425 bng 15 250 50 0 empty empty bang -6 23 1 12 -262144
-1 -1;
#X obj 191 425 bng 15 250 50 0 empty empty float -4 23 1 12 -262144
-1 -1;
#X obj 259 425 bng 15 250 50 0 empty empty symbol -12 23 1 12 -262144
-1 -1;
#X obj 328 425 bng 15 250 50 0 empty empty list -2 23 1 12 -262144
-1 -1;
#X msg 156 104 is this a list 2?;
#X text 144 61 [route] says these are not lists:;
#X msg 147 29 list lots of pie;
#X text 268 30 a list;
#X obj 123 381 trigger bang float symbol list;
#X msg 124 274 list lots of pie;
#X text 251 274 a list;
#X msg 158 330 lots of pie;
#X msg 158 349 is this a list 2?;
#X text 15 316 [trigger] says these are not floats \, lists \, symbols
or lists;
#X connect 0 0 4 0;
#X connect 1 0 5 0;
#X connect 3 0 2 0;
#X connect 3 1 0 0;
#X connect 3 2 1 0;
#X connect 3 3 6 0;
#X connect 3 4 7 0;
#X connect 8 0 3 0;
#X connect 9 0 12 0;
#X connect 10 0 13 0;
#X connect 15 0 3 0;
#X connect 17 0 3 0;
#X connect 19 0 11 0;
#X connect 19 1 9 0;
#X connect 19 2 10 0;
#X connect 19 3 14 0;
#X connect 20 0 19 0;
#X connect 22 0 19 0;
#X connect 23 0 19 0;
#N canvas 312 241 798 505 10;
#X floatatom 80 298 5 0 0 0 - - -;
#X symbolatom 128 297 10 0 0 0 - - -;
#X obj 33 318 bng 15 250 50 0 empty empty bang -6 23 1 12 -262144 -1
-1;
#X obj 33 274 route bang float symbol list;
#X obj 80 318 bng 15 250 50 0 empty empty float -4 23 1 12 -262144
-1 -1;
#X obj 128 318 bng 15 250 50 0 empty empty symbol -12 23 1 12 -262144
-1 -1;
#X obj 176 318 bng 15 250 50 0 empty empty list -2 23 1 12 -262144
-1 -1;
#X obj 224 318 bng 15 250 50 0 empty empty undefined -18 23 1 12 -262144
-1 -1;
#X msg 32 21 bang;
#X obj 46 40 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X msg 72 31 element;
#X obj 68 54 bang;
#X msg 81 96 float bang;
#X text 164 96 invalid;
#X msg 86 118 symbol bang;
#X text 173 118 not a bang \, but a symbol;
#X msg 92 142 list bang;
#X msg 94 169 list bang bang bang;
#X text 163 141 not a bang \, but a symbol;
#X text 235 170 not a bang \, but a list;
#X msg 87 203 bang bang bang;
#X text 195 203 this is a bang;
#X msg 91 226 bang 1 2;
#X msg 92 247 bang one two;
#X text 160 226 this is a bang;
#X text 191 247 this is a bang;
#X msg 422 27 bang;
#X obj 436 46 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X msg 469 43 element;
#X obj 447 74 bang;
#X msg 471 102 float bang;
#X text 554 102 invalid;
#X msg 476 124 symbol bang;
#X text 563 124 not a bang \, but a symbol;
#X msg 482 148 list bang;
#X msg 484 175 list bang bang bang;
#X text 553 147 not a bang \, but a symbol;
#X text 625 176 not a bang \, but a list;
#X msg 477 209 bang bang bang;
#X text 585 209 this is a bang;
#X msg 481 232 bang 1 2;
#X msg 482 253 bang one two;
#X text 550 232 this is a bang;
#X text 581 253 this is a bang;
#X obj 423 280 print;
#X text 482 74 this turns [element( into a bang;
#X msg 47 392 list bang;
#X obj 47 416 list trim;
#X obj 47 440 print;
#X text 124 414 another way to convert a list to a bang;
#X msg 77 77 list;
#X connect 0 0 4 0;
#X connect 1 0 5 0;
#X connect 3 0 2 0;
#X connect 3 1 0 0;
#X connect 3 2 1 0;
#X connect 3 3 6 0;
#X connect 3 4 7 0;
#X connect 8 0 3 0;
#X connect 9 0 3 0;
#X connect 10 0 11 0;
#X connect 11 0 3 0;
#X connect 12 0 3 0;
#X connect 14 0 3 0;
#X connect 16 0 3 0;
#X connect 17 0 3 0;
#X connect 20 0 3 0;
#X connect 22 0 3 0;
#X connect 23 0 3 0;
#X connect 26 0 44 0;
#X connect 27 0 44 0;
#X connect 28 0 29 0;
#X connect 29 0 44 0;
#X connect 30 0 44 0;
#X connect 32 0 44 0;
#X connect 34 0 44 0;
#X connect 35 0 44 0;
#X connect 38 0 44 0;
#X connect 40 0 44 0;
#X connect 41 0 44 0;
#X connect 46 0 47 0;
#X connect 47 0 48 0;
#X connect 50 0 3 0;
#N canvas 146 67 812 610 10;
#X msg 48 49 symbol pie;
#X msg 67 101 pie;
#X text 127 49 a symbol;
#X floatatom 96 297 5 0 0 0 - - -;
#X symbolatom 144 296 10 0 0 0 - - -;
#X obj 49 317 bng 15 250 50 0 empty empty bang -6 23 1 12 -262144 -1
-1;
#X obj 49 273 route bang float symbol list;
#X obj 96 317 bng 15 250 50 0 empty empty float -4 23 1 12 -262144
-1 -1;
#X obj 144 317 bng 15 250 50 0 empty empty symbol -12 23 1 12 -262144
-1 -1;
#X obj 192 317 bng 15 250 50 0 empty empty list -2 23 1 12 -262144
-1 -1;
#X obj 240 317 bng 15 250 50 0 empty empty undefined -18 23 1 12 -262144
-1 -1;
#X msg 86 220 lots of pie;
#X obj 142 538 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 233 538 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X msg 370 96 symbol pie;
#X msg 387 142 pie;
#X text 449 96 a symbol;
#X floatatom 438 299 5 0 0 0 - - -;
#X symbolatom 504 298 10 0 0 0 - - -;
#X obj 371 319 bng 15 250 50 0 empty empty bang -6 23 1 12 -262144
-1 -1;
#X obj 438 319 bng 15 250 50 0 empty empty float -4 23 1 12 -262144
-1 -1;
#X obj 504 319 bng 15 250 50 0 empty empty symbol -12 23 1 12 -262144
-1 -1;
#X obj 572 319 bng 15 250 50 0 empty empty list -2 23 1 12 -262144
-1 -1;
#X text 28 566 [select] requires a atom type selector in order to understand
the data (the float selector is implied);
#X text 54 79 [route] says these are not symbols:;
#X msg 74 121 word;
#X msg 91 241 is this a list 2?;
#X text 79 198 [route] says these are not lists:;
#X msg 82 166 list lots of pie;
#X text 203 167 a list;
#X msg 397 161 word;
#X msg 414 189 list lots of pie;
#X text 541 189 a list;
#X msg 415 214 lots of pie;
#X msg 416 238 is this a list 2?;
#X msg 474 444 symbol pie;
#X msg 505 497 pie;
#X msg 541 497 word;
#X symbolatom 473 554 10 0 0 0 - - -;
#X obj 473 571 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X text 616 486 (check Pd Window):;
#X text 482 474 symbolatom says these are not symbols;
#X obj 370 275 trigger bang float symbol list anything;
#X obj 639 319 bng 15 250 50 0 empty empty anything -15 23 1 12 -262144
-1 -1;
#X text 390 122 [trigger] says these are not symbols:;
#X text 360 16 The Pd window reports that it 'can only convert "s"
to "b" or "a"'. So it considers the non-symbols of type "s" since they
are converted only to "bang" and "anything". But type "s" is not the
same as "symbol" because the symbol is converted to all of the types.
;
#X msg 132 473 element;
#X msg 140 492 symbol element;
#X obj 141 517 select element;
#X obj 71 538 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 119 538 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
-1;
#X obj 70 517 select 1;
#X text 190 472 not a symbol or a float;
#X text 15 444 [select] considers [element( as something other than
a symbol or a float:;
#X msg 433 512 bang;
#X symbolatom 60 417 10 0 0 0 - - -;
#X msg 60 367 symbol bang;
#X text 148 368 this is a symbol \, not a bang;
#X msg 76 390 symbol 5;
#X text 145 391 this is a blank symbol;
#X msg 377 365 symbol bang;
#X text 465 366 this is a symbol \, not a bang;
#X msg 393 388 symbol 5;
#X text 462 389 this is a blank symbol;
#X obj 376 416 print;
#X connect 0 0 6 0;
#X connect 1 0 6 0;
#X connect 3 0 7 0;
#X connect 4 0 8 0;
#X connect 6 0 5 0;
#X connect 6 1 3 0;
#X connect 6 2 4 0;
#X connect 6 3 9 0;
#X connect 6 4 10 0;
#X connect 11 0 6 0;
#X connect 14 0 42 0;
#X connect 15 0 42 0;
#X connect 17 0 20 0;
#X connect 18 0 21 0;
#X connect 25 0 6 0;
#X connect 26 0 6 0;
#X connect 28 0 6 0;
#X connect 30 0 42 0;
#X connect 31 0 42 0;
#X connect 33 0 42 0;
#X connect 34 0 42 0;
#X connect 35 0 38 0;
#X connect 36 0 38 0;
#X connect 37 0 38 0;
#X connect 38 0 39 0;
#X connect 42 0 19 0;
#X connect 42 1 17 0;
#X connect 42 2 18 0;
#X connect 42 3 22 0;
#X connect 42 4 43 0;
#X connect 46 0 48 0;
#X connect 46 0 51 0;
#X connect 47 0 48 0;
#X connect 48 0 12 0;
#X connect 48 1 13 0;
#X connect 51 0 49 0;
#X connect 51 1 50 0;
#X connect 54 0 38 0;
#X connect 56 0 55 0;
#X connect 58 0 55 0;
#X connect 60 0 64 0;
#X connect 62 0 64 0;
# Doxyfile 1.3.4
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = pd
PROJECT_NUMBER =
OUTPUT_DIRECTORY =
OUTPUT_LANGUAGE = English
USE_WINDOWS_ENCODING = NO
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = NO
STRIP_FROM_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
DETAILS_AT_TOP = NO
INHERIT_DOCS = YES
DISTRIBUTE_GROUP_DOC = NO
TAB_SIZE = 8
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
SUBGROUPING = YES
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = YES
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = YES
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ../../pd/src
FILE_PATTERNS =
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS =
EXCLUDE_PATTERNS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE =
IMAGE_PATH =
INPUT_FILTER =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = NO
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = pd-html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = YES
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4wide
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = NO
USE_PDFLATEX = NO
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = NO
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
UML_LOOK = NO
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DOT_IMAGE_FORMAT = png
DOT_PATH =
DOTFILE_DIRS =
MAX_DOT_GRAPH_WIDTH = 1024
MAX_DOT_GRAPH_HEIGHT = 1024
MAX_DOT_GRAPH_DEPTH = 0
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO
#N canvas 26 165 1002 607 10;
#X obj 351 386 cnv 15 69 16 empty empty 69x16 2 12 0 9 -128992 -66577
0;
#X obj 351 355 cnv 15 78 20 empty empty 78x20 2 12 0 9 -128992 -66577
0;
#X obj 695 387 cnv 15 70 17 empty empty 70x17 2 12 0 9 -128992 -66577
0;
#X text 161 389 Mac OS X;
#X text 160 357 Windows;
#X text 161 417 GNU/Linux;
#X obj 775 387 cnv 15 70 17 empty empty 70x17 2 12 0 9 -128992 -66577
0;
#X obj 865 387 cnv 15 60 15 empty empty 60x15 2 12 0 9 -128992 -66577
0;
#X obj 520 173 cnv 15 100 60 empty empty courier10 20 12 0 8 -233017
-66577 0;
#X msg 539 177 courier10;
#X obj 650 103 cnv 15 100 60 empty empty courier10 20 12 0 9 -233017
-66577 0;
#X msg 670 105 courier10;
#X text 651 124 Windows/0.39.2-extended-test7;
#X obj 695 357 cnv 15 60 17 empty empty 60x17 2 12 0 9 -128992 -66577
0;
#X obj 775 357 cnv 15 60 17 empty empty 60x17 2 12 0 9 -128992 -66577
0;
#X obj 351 416 cnv 15 87 19 empty empty 87x19 2 12 0 9 -128992 -66577
0;
#X text 17 314 these represent the size of this message box:;
#X msg 346 313 courier10;
#X text 15 1 the fonts line up on these platforms:;
#X obj 139 35 cnv 15 100 60 empty empty courier10 20 12 0 9 -233017
-66577 0;
#X msg 157 41 courier10;
#X text 350 335 0.39.2-extended-test7;
#X text 697 335 0.39-2;
#X text 777 334 0.40-2;
#X text 857 334 devel 0.39-1test1;
#X obj 501 386 cnv 15 69 16 empty empty 69x16 2 12 0 9 -128992 -66577
0;
#X text 500 335 0.38.4-extended;
#X obj 612 387 cnv 15 70 17 empty empty 70x17 2 12 0 9 -128992 -66577
0;
#X text 614 335 0.38-4;
#X text 18 444 these represent the size of this comment:;
#X obj 140 240 cnv 15 100 60 empty empty courier10 20 12 0 10 -233017
-66577 0;
#X msg 158 243 courier10;
#X obj 232 500 cnv 7 52 7 empty empty 52x7 2 15 0 11 -128992 -66577
0;
#X text 163 499 Mac OS X;
#X text 162 467 Windows;
#X text 163 527 GNU/Linux;
#X obj 784 500 cnv 7 63 8 empty empty 63x8 2 15 0 11 -128992 -66577
0;
#X obj 506 500 cnv 7 62 7 empty empty 62x7 2 15 0 11 -128992 -66577
0;
#X obj 20 170 cnv 15 100 60 empty empty courier10 20 12 0 12 -233017
-66577 0;
#X msg 39 174 courier10;
#X text 47 212 12 pt;
#X text 176 284 10pt;
#X text 686 140 9 pt;
#X obj 20 35 cnv 15 100 60 empty empty courier10 20 12 0 12 -233017
-66577 0;
#X msg 39 39 courier10;
#X text 47 77 12 pt;
#X text 40 64 0.38-4;
#X text 40 199 0.40-2;
#X text 139 64 devel 0.39-1test1;
#X text 32 16 ---------- Mac OS X ----------;
#X obj 614 500 cnv 7 63 8 empty empty 63x8 2 15 0 11 -128992 -66577
0;
#X text 435 442 ----------------------------------------------------------
;
#X text 445 312 ----------------------------------------------------------
;
#X obj 699 500 cnv 7 63 8 empty empty 63x8 2 15 0 11 -128992 -66577
0;
#X obj 21 102 cnv 15 100 60 empty empty courier10 20 12 0 12 -233017
-66577 0;
#X msg 40 106 courier10;
#X text 48 144 12 pt;
#X text 41 131 0.39-2;
#X text 141 78 9 pt (closest);
#X obj 870 500 cnv 6 53 6 empty empty 53x6 2 15 0 11 -128992 -66577
0;
#X obj 520 103 cnv 15 100 60 empty empty courier10 20 12 0 8 -233017
-66577 0;
#X msg 539 107 courier10;
#X text 548 124 0.39-2;
#X text 542 16 ---------- Windows ----------;
#X text 528 140 8 pt (closest);
#X text 549 197 0.40-2;
#X obj 699 470 cnv 5 51 5 empty empty 51x5 2 15 0 11 -128992 -66577
0;
#X text 526 213 8 pt (closest);
#X obj 784 470 cnv 5 51 5 empty empty 51x5 2 15 0 11 -128992 -66577
0;
#X text 296 443 courier10;
#X obj 269 102 cnv 15 100 60 empty empty courier10 20 12 0 8 -233017
-66577 0;
#X msg 288 106 courier10;
#X text 297 123 0.39-2;
#X text 272 16 ---------- GNU/Linux ----------;
#X obj 381 242 cnv 15 100 60 empty empty courier10 20 12 0 8 -233017
-66577 0;
#X msg 399 246 courier10;
#X text 382 263 0.39.2-extended-RC1;
#X text 417 279 8 pt;
#X text 307 139 8 pt;
#X obj 695 414 cnv 15 69 17 empty empty 69x17 2 12 0 9 -128992 -66577
0;
#X obj 700 530 cnv 7 61 8 empty empty 61x8 2 15 0 11 -128992 -66577
0;
#X obj 233 416 cnv 15 62 17 empty empty 62x17 2 12 0 9 -128992 -66577
0;
#X obj 380 103 cnv 15 100 60 empty empty courier8 20 12 0 8 -233017
-66577 0;
#X msg 399 106 courier10;
#X text 416 140 8 pt;
#X text 381 124 0.39.2-extended-test7;
#X obj 355 529 cnv 7 80 10 empty empty 80x10 2 15 0 11 -128992 -66577
0;
#X obj 269 170 cnv 15 100 60 empty empty courier10 20 12 0 10 -233017
-66577 0;
#X msg 290 175 courier10;
#X text 298 194 0.40-2;
#X text 275 210 10 pt (closest);
#X obj 785 530 cnv 7 61 8 empty empty 61x8 2 15 0 11 -128992 -66577
0;
#N canvas 0 22 466 290 instructions 0;
#X text 18 45 If the sizing was all working nicely \, the fonts would
line up exactly on the top section when both are set to 10 point since
they are the same font. That is what we are trying to achieve. But
that doesn't happen very much. So here are the three tests \, from
the top to bottom: - top: adjust the font size in the canvas until
it matches the message box. Move the message box over the canvas font
so it lines up it exactly. Mark down the font size in the canvas. If
you can't get an exact match \, write (closest) - middle: copy and
paste one of the canvases with the dimensions in it (e.g. 69x16). Take
this canvas and make it fit exactly over the [courier10( message box.
then put it in the right place in the grid. - bottom: copy and paste
one of the canvases with the dimensions in it (e.g. 53x6). Take this
canvas and make it fit exactly over the comment "courier1)". then put
it in the correct place in the grid.;
#X obj 364 7 import cyclone;
#X restore 849 28 pd instructions;
#X obj 782 150 comment 173 10 helvetica ? 0 0 0 0 adjust the font size
in the canvas until it matches the message box. Move the message box
over the canvas font so it lines up it exactly. Mark down the font
size in the canvas. If you can't get an exact match \, write (closest)
;
#X obj 13 340 comment 132 10 helvetica ? 0 0 0 0 copy and paste one
of the canvases with the dimensions in it (e.g. 69x16). Take this canvas
and make it fit exactly over the [courier10( message box. then put
it in the right place in the grid.;
#X obj 13 480 comment 132 10 helvetica ? 0 0 0 0 copy and paste one
of the canvases with the dimensions in it (e.g. 53x6). Take this canvas
and make it fit exactly over the comment "courier10". then put it in
the correct place in the grid.;
#X obj 354 499 cnv 7 63 8 empty empty 63x8 2 15 0 11 -128992 -66577
0;
#X obj 140 170 cnv 15 100 60 empty empty courier10 20 12 0 12 -233017
-66577 0;
#X msg 159 174 courier10;
#X text 167 212 12 pt;
#X text 140 196 0.39.2-ext-test7;
#N canvas 285 122 610 587 get-fontsize 0;
#X obj 45 79 cnv 10 68 16 empty \$0-cnv 68x16 2 30 0 11 -260818 -128992
0;
#X obj 146 165 pack 0 0;
#X floatatom 146 80 5 0 0 0 - - -;
#X obj 176 139 t b a;
#X floatatom 176 119 5 0 0 0 - - -;
#X obj 146 235 s \$0-cnv;
#X msg 45 79 courier10;
#X msg 146 207 vis_size \$1 \$2 \, label \$1x$2;
#X text 144 261 setting label this way only works with pd >= 0-40;
#X obj 50 319 cnv 10 55 8 empty \$0-cnv2 55x8 2 30 0 11 -260818 -128992
0;
#X obj 152 387 pack 0 0;
#X floatatom 152 322 5 0 0 0 - - -;
#X obj 182 361 t b a;
#X floatatom 182 341 5 0 0 0 - - -;
#X msg 152 449 vis_size \$1 \$2 \, label \$1x$2;
#X text 150 503 setting label this way only works with pd >= 0-40;
#X obj 152 477 s \$0-cnv2;
#X text 48 316 courier10;
#X obj 148 45 hsl 128 15 40 80 0 0 empty empty empty -2 -8 0 10 -262144
-1 -1 0 1;
#X obj 145 61 int;
#X obj 188 85 hsl 128 15 5 20 0 0 empty empty empty -2 -8 0 10 -262144
-1 -1 0 1;
#X obj 185 101 int;
#X obj 154 285 hsl 128 15 40 80 0 0 empty empty empty -2 -8 0 10 -262144
-1 -1 0 1;
#X obj 151 301 int;
#X obj 194 305 hsl 128 15 5 20 0 0 empty empty empty -2 -8 0 10 -262144
-1 -1 0 1;
#X obj 191 321 int;
#X connect 1 0 7 0;
#X connect 2 0 1 0;
#X connect 3 0 1 0;
#X connect 3 1 1 1;
#X connect 4 0 3 0;
#X connect 7 0 5 0;
#X connect 10 0 14 0;
#X connect 11 0 10 0;
#X connect 12 0 10 0;
#X connect 12 1 10 1;
#X connect 13 0 12 0;
#X connect 14 0 16 0;
#X connect 18 0 19 0;
#X connect 19 0 2 0;
#X connect 20 0 21 0;
#X connect 21 0 4 0;
#X connect 22 0 23 0;
#X connect 23 0 11 0;
#X connect 24 0 25 0;
#X connect 25 0 13 0;
#X restore 852 60 pd get-fontsize;
#X obj 520 240 cnv 15 100 60 empty empty courier10 20 12 0 10 -233017
-66577 0;
#X msg 538 244 courier10;
#X text 556 284 10pt;
#X text 520 265 0.39-2-extended-rc2;
#X obj 233 359 cnv 15 62 17 empty empty 62x17 2 12 0 9 -128992 -66577
0;
#X obj 232 470 cnv 7 52 7 empty empty 52x7 2 15 0 11 -128992 -66577
0;
#X obj 233 389 cnv 15 62 17 empty empty 62x17 2 12 0 9 -128992 -66577
0;
#X obj 270 240 cnv 15 100 60 empty empty courier10 20 12 0 10 -233017
-66577 0;
#X msg 288 244 courier10;
#X text 306 284 10pt;
#X text 270 265 0.39-2-extended-rc2;
#X text 140 265 0.39-2-extended-rc2;
#X obj 232 530 cnv 7 52 7 empty empty 52x7 2 15 0 11 -128992 -66577
0;
#X text 221 334 0.39.2-extended-rc2;
File deleted
File deleted
File deleted
% format latexg -*- latex -*-
\documentclass[12pt, a4paper,austrian, titlepage]{article}
%% HOWTO write an external for Pd
%% Copyright (c) 2001-2006 by IOhannes m zmölnig
%%
%% Permission is granted to copy, distribute and/or modify this document
%% under the terms of the GNU Free Documentation License, Version 1.2
%% or any later version published by the Free Software Foundation;
%% with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
%% Texts. A copy of the license is included in the LICENSE.txt file.
%sprache
\usepackage[latin1]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{babel}
% add hypertext support (fine for latex2html)
\usepackage{html}
% add landscape support (for rotating text through 90deg)
\usepackage{lscape}
%\begin{latexonly}
% pdf kompatibilität
\newif\ifpdf
\ifx\pdfoutput\undefined
\pdffalse % we are not running PDFLatex
\else
\pdfoutput=1 % yes, we are running PDFLatex
\pdftrue
\fi
\latexhtml{
\ifpdf
\usepackage[pdftex]{graphicx}
\pdfcompresslevel=9
\else
\usepackage{graphicx}
\fi
}{
\usepackage{graphicx}
}
\title{
HOWTO \\
write an External \\
for {\em Pure data}
}
\author{
johannes m zmölnig \\
\\
{\em
\latexhtml{institut für elektronische musik und akustik}
{\htmladdnormalink{institut für elektronische musik und akustik}{http://iem.at}}
}
}
\date{}
\begin {document}
\maketitle
\hyphenation{Echt-zeit}
\hyphenation{Computer-musik-program-men}
\hyphenation{Echt-zeit-Computer-musik-pro-gramm}
\begin{abstract}
Pd ist ein graphisches Computermusiksystem in der Tradition von IRCAMs {\em ISPW-max}.
Obwohl eine Fülle von Funktionen von Pd selbst zur Verfügung gestellt
werden, stößt man doch manchmal an die Grenzen dessen,
das mit diesen Primitiven und ihren Kombinationen möglich ist.
Deswegen bietet Pd die Möglichkeit, eigene Primitive (``objects'', Objekte) in komplexen
Programmiersprachen wie {\tt C/C++} zu erstellen.
In diesem Dokument soll beschrieben werden, wie man solche Primitive mit Hilfe der
Sprache {\tt C}, in der auch Pd selbst realisiert wurde, schreibt.
\end{abstract}
\vfill
\newpage
\tableofcontents
\vfill
\newpage
\section{Voraussetzungen und Begriffsbestimmungen}
Pd bezieht sich auf das graphische Echtzeit-Computermusikprogramm von
Miller~S.~Puckette.
{\em Pure data}.
Zum Verständnis dieses Dokumentes wird der Umgang mit Pd sowie
Verständnis von Programmiertechniken, insbesondere {\tt C} vorausgesetzt.
Zum Schreiben von eigenen Primitiven wird weiters ein {\tt C}-Compiler,
der dem {\tt ANSI-C}-Standard genügt, notwendig sein.
Solche Compiler sind beispielsweise der {\em Gnu C-Compiler} (gcc) auf linux-Systemen oder
{\em Visual-C++} auf Windows-Systemen.
\subsection{Klassen, Instanzen und Objekte}
Pd ist in der Programmiersprache {\tt C} geschrieben.
Allerdings ist Pd auf Grund seiner graphischen Natur ein {\em objektorientiertes} System.
Da {\tt C} die Verwendung von Klassen nicht sehr gut unterstützt, ist der resultierende
Quellcode nicht so elegant wie er zum Beispiel unter {\tt C++} wäre.
Der Ausdruck {\em Klasse} bezieht sich in diesem Dokument auf die Realisierung eines
Konzeptes, bei dem Daten und Manipulatoren eine Einheit bilden.
Konkrete {\em Instanzen einer Klasse} sind {\em Objekte}.
\subsection{Internals, Externals und Libraries}
Um Begriffsverwirrungen von vorneherein auszuschließen, seien hier kurz die Ausdrücke
{\em Internal}, {\em External} und {\em Library} erklärt.
\paragraph{Internal}
Ein {\em Internal} ist eine Klasse, die in Pd eingebaut ist.
Viele Primitive wie ``+'', ``pack'' oder ``sig\~\/`` sind {\em Internals}
\paragraph{External}
Ein {\em External} ist eine Klasse, die nicht in Pd eingebaut ist und erst zur Laufzeit
nachgeladen wird.
Sind sie einmal im Speicher von Pd, so sind {\em Externals} nicht mehr von {\em Internals} zu
unterscheiden.
\paragraph{Library}
Eine {\em Library} bezeichnet eine Sammlung von {\em Externals},
die gemeinsam in eine Binärdatei kompiliert werden.
{\em Library}-Dateien müssen eine betriebssystemabhängige Namenskonvention einhalten:
\begin{tabular}{c||c|c|c}
Bibliothek&linux&irix&Win32 \\
\hline
{\tt my\_lib}&{\tt my\_lib.pd\_linux}&{\tt my\_lib.pd\_irix}&
{\tt my\_lib.dll}\\
\end{tabular}
Die einfachste Form einer {\em Library} beinhaltet genau ein {\em External},
das den selben Name trägt, wie auch die {\em Library}
Im Gegensatz zu Externals können {\em Libraries} mit bestimmten Befehlen
von Pd importiert werden.
Ist eine {\em Library} importiert worden,
so sind alle {\em Externals}, die sie beinhaltet,
in den Speicher geladen und stehen als Objekte zur Verfügung.
Pd stellt zwei Methoden zur Verfügung, um {\em Libraries} zu laden:
\begin{itemize}
\item mit der commandline-Option ``{\tt -lib my\_lib}''
\item durch Kreieren eines Objektes ``{\tt my\_lib}''
\end{itemize}
Die erste Methode lädt die {\em Library} sofort beim Starten von Pd.
Dies ist die zu bevorzugende Methode für {\em Libraries},
die mehrere {\em Externals} beinhalten.
Die zweite Methode ist für {\em Libraries} zu bevorzugen, die genau
ein {\em External} mit dem selben Namen beinhalten.
Bei der zweiten Methode wird zuerst geprüft, ob eine Klasse namens ``my\_lib'' bereits
in den Speicher geladen ist.
Ist dies nicht der Fall\footnote
{Ist eine solche Klasse bereits im Speicher, wird ein
Objekt namens ``my\_lib'' instanziiert und der Vorgang bricht ab.
Es wird also keine neue {\em Library} geladen.
Man kann daher keine {\em Libraries} mit bereits verwendeten Klassennamen,
wie zum Beispiel ``abs'', laden.}
so werden alle Pfade untersucht,
ob darin eine Datei namens ``{\tt my\_lib.pd\_linux}''\footnote{
oder einer anderen betriebssystemabhängigen Dateinamenerweiterung (s.o.)}
existiert.
Wird eine solche Datei gefunden, so werden alle in ihr enthaltenen {\em Externals}
in den Speicher geladen.
Danach wird nachgesehen, ob nun eine Klasse namens ``my\_lib''
als (neu geladenes) {\em External} im Speicher existiert.
Ist dies der Fall, so wird eine Instanz dieser Klasse geschaffen.
Ansonsten wird eine Fehlermeldung ausgegeben, die Instanziierung ist gescheitert.
\section{mein erstes External: {\tt helloworld}}
Wie das beim Erlernen von Programmiersprachen so üblich ist,
beginnen wir mit ``Hello world''.
Ein Objekt soll geschaffen werden, dass jedesmal, wenn es
mit ``bang'' getriggert wird, die Zeile ``Hello world!!'' auf
die Standardausgabe schreibt.
\subsection{die Schnittstelle zu Pd}
Um ein Pd-External zu schreiben, braucht man eine wohldefinierte Schnittstelle.
Diese wird in der Datei ``m\_pd.h'' zur Verfügung gestellt.
\begin{verbatim}
#include "m_pd.h"
\end{verbatim}
\subsection{eine Klasse und ihr Datenraum}
Als nächstes muß eine neue Klasse vorbereitet und der
Datenraum für diese Klasse definiert werden.
\begin{verbatim}
static t_class *helloworld_class;
typedef struct _helloworld {
t_object x_obj;
} t_helloworld;
\end{verbatim}
\verb+hello_worldclass+ wird der Zeiger auf die neue Klasse.
Die Struktur \verb+t_helloworld+ (vom Typ \verb+_helloworld+)
stellt den Datenraum der Klasse dar.
Ein unverzichtbares Element ist dabei eine Variable des Type \verb+t_object+.
In ihr werden interne Objekteigenschaften abgelegt, wie zum Beispiel
die Größe der Objekt-Box bei der graphischen Darstellung, aber auch
Daten über Inlets und Outlets.
\verb+t_object+ muss der erste Eintrag in die Struktur sein !
Da bei einer einfachen ``Hello world''-Anwendung keine Variablen gebraucht werden,
ist die Struktur ansonsten leer.
\subsection{Methodenraum}
Zu einer Klasse gehören neben einem Datenraum auch ein Satz von
Manipulatoren (Methoden) mit denen diese Daten manipuliert werden können.
Wird eine Message an eine Instanz unserer Klasse geschickt,
so wird eine Methoden aufgerufen.
Diese Mehtoden, die die Schnittstelle zum Messagesystem von Pd bilden,
haben grundsätzlich kein Rückgabeargument, sind also vom Typ \verb+void+.
\begin{verbatim}
void helloworld_bang(t_helloworld *x)
{
post("Hello world !!");
}
\end{verbatim}
Diese Methode hat ein Übergabeargument vom Typ \verb+t_helloworld+,
sodass wir also unseren Datenraum manipulieren könnten.
Da wir nur ``Hello world!'' ausgeben wollen (und ausserdem unser Datenraum
recht spärlich ist), verzichten wir auf eine Manipulation.
Mit dem Befehl \verb+post(char *c,...)+ wird eine Meldung an die Standardausgabe
geschickt.
Ein Zeilenumbruch wird automatisch angehängt.
Ansonsten funktioniert \verb+post()+ gleich wie der {\tt C}-Befehl \verb+printf()+.
\subsection{Generierung einer neuen Klasse}
Um eine neue Klasse zu generieren, müssen Angaben über
den Datenraum und den Methodenraum dieser Klasse
beim Laden einer Library an Pd übergeben werden.
Wird eine neue Library ``my\_lib'' geladen,
so versucht Pd eine Funktion ``my\_lib\_setup()'' aufzurufen.
Diese Funktion (oder von ihr aufgerufene Funktionen) teilt Pd mit,
welche Eigenschaften die neuen Klassen haben.
Sie wird nur einmal, beim Laden der Library aufgerufen.
\begin{verbatim}
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);
}
\end{verbatim}
\paragraph{class\_new}
Der Befehl \verb+class_new+ kreiert eine neue Klasse und gibt einen Zeiger auf diesen
Prototyp zurück.
Das erste Argument ist der symbolische Name der Klasse.
Die nächsten beiden Argumente definieren Konstruktor und Destruktor der Klasse.
Wenn in einen Pd-Patch ein Objekt kreiert wird,
instanziiert der Konstruktor \verb+(t_newmethod)helloworld_new+ diesses Objekt
und initialisiert den Datenraum.
Wird ein Pd-Patch geschlossen oder ein Objekt daraus entfernt,
so gibt der Destruktor, wenn notwendig, dynamisch reservierten Speicher wieder frei.
Der Speicherplatz für den Datenraum selbst wird von Pd automatisch freigegeben.
Deshalb kann in diesem Beispiel auf einen Destruktor verzichtet werden,
folglich wird dieses Argument auf ``0'' gesetzt.
Damit Pd genug Speicher für den Datenraum allozieren und wieder freigeben kann,
wird die Größe dieser Datenstruktur als viertes Argument übergeben.
Das fünfte Argument bestimmt, wie Klasseninstanzen graphisch dargestellt werden und
ob sie mit anderen Objekten verknüpfbar sind.
Der Standardwert \verb+CLASS_DEFAULT+ (oder einfacher: ``0'') bezieht sich auf
ein Objekt mit mindestens einem Inlet.
Würde man keinen Eingang wollen (wie zum Beispiel beim Internal ``receive''),
so kann man diesen Wert auf \verb+CLASS_NOINLET+ setzen.
Die restlichen Argumente definieren die Übergabeargumente eines Objektes und deren Typ.
Bis zu sechs numerische und symbolische Objektargumente können in beliebiger Reihenfolge
mit \verb+A_DEFFLOAT+ und \verb+A_DEFSYMBOL+ angegeben werden.
Sollen mehr Argumente übergeben werden oder die Atomtyp-Reihenfolge flexibler sein,
so bietet \verb+A_GIMME+ die Übergabe einer beliebigen Liste von Atomen.
Die Objektargumentliste wird mit ``0'' terminiert.
In unserem Beispiel sind also keine Übergabeargumente für die Klasse vorgesehen.
\paragraph{class\_addbang}
Jetzt muss zur Klasse noch ein Methodenraum hinzugefügt werden.
Mit \verb+class_addbang+ wird der durch das erste Argument definierten Klasse
eine Methode für eine ``bang''-Message hinzuzugefügt.
Diese Methode ist das zweite Argument.
\subsection{Konstruktor: Instanziierung eines Objektes}
Jedesmal, wenn in einem Pd-Patch ein Objekt einer Klasse kreiert wird,
schafft der mit \verb+class_new+ angegebene Konstruktor eine neue Instanz der Klasse.
Der Konstruktor ist immer vom Typ \verb+void *+
\begin{verbatim}
void *helloworld_new(void)
{
t_helloworld *x = (t_helloworld *)pd_new(helloworld_class);
return (void *)x;
}
\end{verbatim}
Die Übergabeargumente der Konstruktorfunktion hängen von den mit
\verb+class_new+ angegebenen Objektargumenten ab.
\begin{tabular}{l|l}
\verb+class_new+-Argument&Konstruktorargument\\
\hline
\verb+A_DEFFLOAT+&\verb+t_floatarg f+ \\
\verb+A_DEFSYMBOL+&\verb+t_symbol *s+ \\
\verb+A_GIMME+&\verb+t_symbol *s, int argc, t_atom *argv+
\end{tabular}
Da in diesem Beispiel keine Objektargumente existieren, hat auch
der Konstruktor keine.
Die Funktion \verb+pd_new+ reserviert Speicher für den Datenraum, initialisiert
die objektinternen Variablen und gibt einen Zeiger auf den Datenraum zurück.
Der Typ-Cast auf den Datenraum ist notwendig.
Normalerweise würden im Konstruktor auch die Objektvariablen initialisiert werden.
In diesem Beispiel ist dies aber nicht notwendig.
Der Konstruktor muss einen Zeiger auf den instanziierten Datenraum zurückgeben.
\subsection{der Code: \tt helloworld}
\begin{verbatim}
#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);
}
\end{verbatim}
\section{ein komplexes External: {\tt counter}}
Als nächstes soll ein einfacher Zähler als External geschrieben werden.
Ein ``bang''-Trigger soll den aktuellen Zählerstand am Outlet ausgeben
und anschließend um 1 erhöhen.
Diese Klasse unterscheidet sich nicht sonderlich von der vorherigen,
ausser dass nun eine interne Variable ``Zählerstand'' benötigt
wird und das Ergebnis nicht mehr auf die Standardausgabe geschrieben sondern
als Message zu einem Outlet geschickt wird.
\subsection{Variablen eines Objektes}
Ein Zähler braucht natürlich eine Zustandsvariable,
in der der aktueller Zählerstand gespeichert ist.
Solche zum Objekt gehörigen Zustandsvariablen werden im Datenraum abgelegt.
\begin{verbatim}
typedef struct _counter {
t_object x_obj;
t_int i_count;
} t_counter;
\end{verbatim}
Die Ganzzahlvariable \verb+i_count+ beschreibt den Zählerstand.
Natürlich könnte man sie auch als Gleitkommawert realisieren,
doch traditionell werden Zähler ganzzahlig ausgeführt.
\subsection{Übergabeargumente}
Für einen Zähler ist es durchaus sinnvoll, wenn man den Startwert festlegen kann.
Hier soll der Startwert dem Objekt bei der Kreation übergeben werden.
\begin{verbatim}
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addbang(counter_class, counter_bang);
}
\end{verbatim}
Es ist also ein Argument zur Funktion \verb+class_new+ hinzugekommen:
\verb+A_DEFFLOAT+ teilt mit, dass das Objekt ein Übergabeargument
vom Typ \verb+t_floatarg+ hat.
\subsection{Konstruktor}
Dem Konstruktor kommen nun mehrere neue Aufgaben zu.
Zum ersten muss eine Variable initialisiert werden,
zum anderen muss auch ein Outlet für das Objekt geschaffen werden.
\begin{verbatim}
void *counter_new(t_floatarg f)
{
t_counter *x = (t_counter *)pd_new(counter_class);
x->i_count=f;
outlet_new(&x->x_obj, &s_float);
return (void *)x;
}
\end{verbatim}
Die Konstruktorfunktion hat jetzt ein Argument fom Typ \verb+t_floatarg+, wie es in
der Setup-Routine \verb+class_new+ deklariert worden ist.
Dieses Argument initialisiert den Zähler.
Einer neuer Outlet wird mit der Funktion \verb+outlet_new+ geschaffen.
Das erste Argument ist ein Zeiger auf die Objektinterna,
in denen der neue Ausgang geschaffen wird.
Das zweite Argument ist eine symbolische Typbeschreibung des Ausgangs.
Da der Zähler numerische Werte ausgeben soll, ist er vom Typ ``float''.
Sollte der Ausgang für Messages mit verschiedenen Selectoren verwendet werden,
so ist dieser Wert ``0''.
\verb+outlet_new+ gibt einen Zeiger auf den neuen Outlet zurück und speichert diesen
Zeiger in der \verb+t_object+-Variablen \verb+x_obj.ob_outlet+.
Wird nur ein Outlet verwendet, muss daher der Zeiger nicht extra im Datenraum gespeichert
werden.
Werden mehrere Outlets verwendet, so müssen diese Zeiger im Datenraum gespeichert werden.
\subsection{die Zählermethode}
Bei einem Triggerevent soll der alte Zählerstand ausgegeben und um eins inkrementiert werden.
\begin{verbatim}
void counter_bang(t_counter *x)
{
t_float f=x->i_count;
x->i_count++;
outlet_float(x->x_obj.ob_outlet, f);
}
\end{verbatim}
Die Funktion \verb+outlet_float+ gibt an dem Outlet, auf den das erste Argument verweist,
eine Gleitkommazahl (zweites Argument) aus.
Hier wird zuerst der Zählerstand in eine Gleitkomma-Buffervariable gespeichert.
Danach wird er inkrementiert und dann wird erst die Buffervariable ausgegeben.
Was auf den ersten Blick unnötig erscheint, macht bei näherer Betrachtung Sinn:
Die Buffervariable wurde gleich als \verb+t_float+ realisiert,
da sich \verb+outlet_float+ sowieso einen Gleitkommawert erwartet
und ein Cast unvermeidlich ist.
Würde der Zählerstand zuerst an den Outlet geschickt werden und
danach erst inkrementiert werden, würde dies unter Umständen zu einem etwas seltsamen
Verhalten führen.
Wenn nämlich der Zählerausgang wieder an den Inlet zurückgeführt würde, der
Zähler sich also selbst triggerte, so würde die Zählermethode erneut
aufgerufen, ohne dass der Zählerstand inkrementiert worden wäre.
Dies ist im Allgemeinen aber unerwünscht.
Man kann übrigens das gleiche Ergebnis wie hier mit nur einer einzigen Zeile erreichen,
doch sieht man das {\em Reentrant}-Problem dann nicht sehr gut.
\subsection{der Code: \tt counter}
\begin{verbatim}
#include "m_pd.h"
static t_class *counter_class;
typedef struct _counter {
t_object x_obj;
t_int i_count;
} t_counter;
void counter_bang(t_counter *x)
{
t_float f=x->i_count;
x->i_count++;
outlet_float(x->x_obj.ob_outlet, f);
}
void *counter_new(t_floatarg f)
{
t_counter *x = (t_counter *)pd_new(counter_class);
x->i_count=f;
outlet_new(&x->x_obj, &s_float);
return (void *)x;
}
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addbang(counter_class, counter_bang);
}
\end{verbatim}
\section{ein komplexeres External: \tt counter}
Man kann natürlich auch einen einfache Zähler ein bißchen komplexer gestalten.
Es wäre zum Beispiel sinnvoll,
wenn der Zählerstand auf einen Startwert zurückgesetzt werden könnte,
wenn man Start- und Endwert bestimmen könnte und auch die Schrittweite variabel wäre.
Bei jedem Zählerüberlauf soll ein zweiter Outlet eine ``bang''-Message schicken und der
Zähler auf den Startwert zurückgesetzt werden.
\subsection{erweiterter Datenraum}
\begin{verbatim}
typedef struct _counter {
t_object x_obj;
t_int i_count;
t_float step;
t_int i_down, i_up;
t_outlet *f_out, *b_out;
} t_counter;
\end{verbatim}
Der Datenraum wurde also erweitert um Variablen für Schrittweite und Start- bzw. Stopwert.
Weiters werden Zeiger auf zwei Outlets zur Verfügung gestellt.
\subsection{Erweiterung der Klasse}
Da nun die Klassenobjekte verschiedene Messages, wie ``set'' und ``reset'',
verstehen können sollen, mussen der Methodenraum entsprechend erweitert werden.
\begin{verbatim}
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_GIMME, 0);
\end{verbatim}
Der Klassengenerator \verb+class_new+ ist um das Objektübergabeargument
\verb+A_GIMME+ erweitert.
Damit kann eine dynamische Anzahl von Argumenten bei der Objektinstanziierung
verwaltet werden.
\begin{verbatim}
class_addmethod(counter_class,
(t_method)counter_reset,
gensym("reset"), 0);
\end{verbatim}
\verb+class_addmethod+ fügt einer Klasse eine Methode mit für einen
beliebigen Selector hinzu.
Das erste Argument ist die Klasse,
zu der die Methode (zweites Argument) hinzugefügt wird.
Das dritte Argument ist der symbolische Selector,
der mit der Methode assoziiert wird.
Die restlichen ``0''-terminierten Argumente
beschreiben die Atomliste, die dem Selector folgt.
\begin{verbatim}
class_addmethod(counter_class,
(t_method)counter_set, gensym("set"),
A_DEFFLOAT, 0);
class_addmethod(counter_class,
(t_method)counter_bound, gensym("bound"),
A_DEFFLOAT, A_DEFFLOAT, 0);
\end{verbatim}
Eine Methode für den Selector ``set'', gefolgt von einem numerischen Wert,
wird hinzugefügt.
Für den Selector ``bound'', gefolgt von zwei numerischen Werten,
wird ebenfalls eine Methode zur Klasse hinzugefügt.
\begin{verbatim}
class_sethelpsymbol(counter_class, gensym("help-counter"));
\end{verbatim}
Clickt man mit der rechten Maustaste auf ein Pd-Objekt,
so kann man sich einen Hilfe-Patch für die zugehörige Objektklasse anzeigen lasse.
Standardmäßig wird ist dies ein Patch mit dem symbolischen Klassennamen
im Verzeichnis ``{\em doc/5.reference/}'' gesucht.
Mit dem Befehl \verb+class_sethelpsymbol+ kann ein alternativer Patch angegeben werden.
\subsection{Konstruktion von In- und Outlets}
Bei der Objektkreation sollten dem Objekt verschiedene Argumente übergeben
werden.
\begin{verbatim}
void *counter_new(t_symbol *s, int argc, t_atom *argv)
\end{verbatim}
Durch die Argumentendeklaration in der \verb+class_new+-Funktion
mit \verb+A_GIMME+, werden dem Konstruktor folgende Argumente
übergeben:
\begin{tabular}{c|l}
\verb+t_symbol *s+ & der symbolische Namen,\\
& mit dem das Objekt kreiert wurde \\
\verb+int argc+ & die Anzahl, der dem Objekt übergebenen Argumente\\
\verb+t_atom *argv+ & ein Zeiger auf eine Liste von {\tt argc} Atomen
\end{tabular}
\begin{verbatim}
t_float f1=0, f2=0;
x->step=1;
switch(argc){
default:
case 3:
x->step=atom_getfloat(argv+2);
case 2:
f2=atom_getfloat(argv+1);
case 1:
f1=atom_getfloat(argv);
break;
case 0:
break;
}
if (argc<2)f2=f1;
x->i_down = (f1<f2)?f1:f2;
x->i_up = (f1>f2)?f1:f2;
x->i_count=x->i_down;
\end{verbatim}
Werden drei Argumente übergeben, so sollten dies {\em untere Zählergrenze},
{\em obere Zählergrenze} und {\em Schrittgröße} sein.
Werden nur zwei Argumente übergeben,
so wird die Schrittgröße standardmäßig auf ``1'' gesetzt.
Bei nur einem Argument, sei dies der {\em Startwert} des Zählers,
die {\em Schrittgröße} sei ``1''.
\begin{verbatim}
inlet_new(&x->x_obj, &x->x_obj.ob_pd,
gensym("list"), gensym("bound"));
\end{verbatim}
Die Funktion \verb+inlet_new+ erzeugt einen neuen ``aktiven'' Inlet.
``Aktiv'' heißt, dass eine Klassenmethode ausgeführt wird,
wenn eine Message in den einen ``aktiven'' Inlet geschickt wird.
Von der Software-Architektur her ist der erste Inlet immer ``aktiv''.
Die ersten beiden Argumente der \verb+inlet_new+-Funktion
sind Zeiger auf die Objektinterna und die graphische Darstellung des Objektes.
Der symbolische Selector, der durch das dritte Argument spezifiziert wird,
wird für diesen Inlet durch einen anderen symbolischen Selector (viertes Argument)
substituiert.
Durch die Substitution von Selectoren kann eine Message
an einem bestimmten rechten Eingang wie eine Message mit einem bestimmten Selector
am linken Eingang betrachtet werden.
Dies bedeutet
\begin{itemize}
\item Der substituierende Selector muss mit \verb+class_addmethod+ angegeben werden.
\item Man kann einen bestimmten rechten Eingang simulieren,
indem man dem ersten Eingang eine Message mit dem Selector dieses Eingangs schickt.
\item Es ist nicht möglich, einem rechten Eingang Methoden für mehr als einen Selector
zuzuweisen. Insbesondere ist es nicht möglich, ihm eine allgemeine Methode
für einen beliebigen Selector zuzuweisen.
\end{itemize}
\begin{verbatim}
floatinlet_new(&x->x_obj, &x->step);
\end{verbatim}
\verb+floatinlet_new+ generiert einen ``passiven'' Inlet für numerische Werte.
``Passive'' Eingänge erlauben, dass ein Speicherplatz bestimmten Typs im
Variablenraum des Objektes von außen direkt beschrieben werden kann.
Dadurch ist zum Beispiel eine Abfrage nach illegalen Eingaben nicht möglich.
Das erste Argument ist dabei ein Zeiger auf die interne Objektinfrastruktur.
Das zweite Argument ist ein Zeiger auf den Speicherplatz, auf den geschrieben wird.
Es können ``passive'' Eingänge für numerische (Gleitkomma\footnote{
Deswegen ist der {\tt step}-Wert des Klassendatenraums als {\tt t\_float} realisiert.})
-Werte, symbolische Werte und Pointer geschaffen werden.
\begin{verbatim}
x->f_out = outlet_new(&x->x_obj, &s_float);
x->b_out = outlet_new(&x->x_obj, &s_bang);
\end{verbatim}
Die von \verb+outlet_new+ zurückgegebenen Zeiger auf die geschaffenen Outlets,
müssen im Klassendatenraum gespeichert werden,
damit sie später von den Ausgaberoutinen angesprochen werden.
Die Reihenfolge der Generierung von In- und Outlets ist wichtig,
da sie der Reihenfolge der Ein- und Ausgänge der graphischen Repräsentation
des Objektes entsprechen.
\subsection{erweiterter Methodenraum}
Der Methode für die ``bang''-Message muss natürlich der komplexeren Zählerstruktur
genüge tun.
\begin{verbatim}
void counter_bang(t_counter *x)
{
t_float f=x->i_count;
t_int step = x->step;
x->i_count+=step;
if (x->i_down-x->i_up) {
if ((step>0) && (x->i_count > x->i_up)) {
x->i_count = x->i_down;
outlet_bang(x->b_out);
} else if (x->i_count < x->i_down) {
x->i_count = x->i_up;
outlet_bang(x->b_out);
}
}
outlet_float(x->f_out, f);
}
\end{verbatim}
Die einzelnen Outlets werden von den \verb+outlet_...+-Funktionen über
die Zeiger auf diese Ausgänge identifiziert.
Die übrigen Methoden müssen noch implementiert werden:
\begin{verbatim}
void counter_reset(t_counter *x)
{
x->i_count = x->i_down;
}
void counter_set(t_counter *x, t_floatarg f)
{
x->i_count = f;
}
void counter_bound(t_counter *x, t_floatarg f1, t_floatarg f2)
{
x->i_down = (f1<f2)?f1:f2;
x->i_up = (f1>f2)?f1:f2;
}
\end{verbatim}
\subsection{der Code: \tt counter}
\begin{verbatim}
#include "m_pd.h"
static t_class *counter_class;
typedef struct _counter {
t_object x_obj;
t_int i_count;
t_float step;
t_int i_down, i_up;
t_outlet *f_out, *b_out;
} t_counter;
void counter_bang(t_counter *x)
{
t_float f=x->i_count;
t_int step = x->step;
x->i_count+=step;
if (x->i_down-x->i_up) {
if ((step>0) && (x->i_count > x->i_up)) {
x->i_count = x->i_down;
outlet_bang(x->b_out);
} else if (x->i_count < x->i_down) {
x->i_count = x->i_up;
outlet_bang(x->b_out);
}
}
outlet_float(x->f_out, f);
}
void counter_reset(t_counter *x)
{
x->i_count = x->i_down;
}
void counter_set(t_counter *x, t_floatarg f)
{
x->i_count = f;
}
void counter_bound(t_counter *x, t_floatarg f1, t_floatarg f2)
{
x->i_down = (f1<f2)?f1:f2;
x->i_up = (f1>f2)?f1:f2;
}
void *counter_new(t_symbol *s, int argc, t_atom *argv)
{
t_counter *x = (t_counter *)pd_new(counter_class);
t_float f1=0, f2=0;
x->step=1;
switch(argc){
default:
case 3:
x->step=atom_getfloat(argv+2);
case 2:
f2=atom_getfloat(argv+1);
case 1:
f1=atom_getfloat(argv);
break;
case 0:
break;
}
if (argc<2)f2=f1;
x->i_down = (f1<f2)?f1:f2;
x->i_up = (f1>f2)?f1:f2;
x->i_count=x->i_down;
inlet_new(&x->x_obj, &x->x_obj.ob_pd,
gensym("list"), gensym("bound"));
floatinlet_new(&x->x_obj, &x->step);
x->f_out = outlet_new(&x->x_obj, &s_float);
x->b_out = outlet_new(&x->x_obj, &s_bang);
return (void *)x;
}
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_GIMME, 0);
class_addbang (counter_class, counter_bang);
class_addmethod(counter_class,
(t_method)counter_reset, gensym("reset"), 0);
class_addmethod(counter_class,
(t_method)counter_set, gensym("set"),
A_DEFFLOAT, 0);
class_addmethod(counter_class,
(t_method)counter_bound, gensym("bound"),
A_DEFFLOAT, A_DEFFLOAT, 0);
class_sethelpsymbol(counter_class, gensym("help-counter"));
}
\end{verbatim}
\section{ein Signal-External: {\tt pan\~\/}}
Signalklassen sind normale Klassen, die zusätzlich Methoden
für Signale bereitstellen.
Alle Methoden und Konzepte die mit normalen Objektklassen realisierbar sind,
sind also auch mit Signalklassen zuverwirklichen.
Per Konvention enden die symbolischen Namen mit einer Tilde \~\/.
Anhand einer Klasse ``pan\~\/`` soll demonstriert werden wie Signalklassen geschrieben
werden können.
Ein Signal am linken Inlet wird mit einem Signal am zweiten Inlet gemischt.
Der Mischungsgrad wird als \verb+t_float+-Message an einen dritten Eingang festgelegt.
\subsection{Variablen einer Signalklasse}
Da eine Signalklasse nur eine erweiterte normale Klasse ist,
gibt es keine prinzipielle Unterschiede zwischen den Datenräumen.
\begin{verbatim}
typedef struct _pan_tilde {
t_object x_obj;
t_sample f_pan;
t_float f;
} t_pan_tilde;
\end{verbatim}
Es wird nur eine Variable für den {\em Mischfaktor} der Panningfunktion benötigt.
Die Variable \verb+f+ wird gebraucht, falls kein Signal am Signalinlet liegt.
Wird dann an diesen Signalinlet ein numerischer Wert als Message geschickt,
so ersetzt dieser das Signal und wird in der Variable \verb+f+ gespeichert.
\subsection{Signalklassen}
\begin{verbatim}
void pan_tilde_setup(void) {
pan_tilde_class = class_new(gensym("pan~"),
(t_newmethod)pan_tilde_new,
0, sizeof(t_pan_tilde),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addmethod(pan_tilde_class,
(t_method)pan_tilde_dsp, gensym("dsp"), 0);
CLASS_MAINSIGNALIN(pan_tilde_class, t_pan_tilde, f);
}
\end{verbatim}
Jeder Signalklasse muss eine Methode für die Signalverarbeitung zugeordnet werden.
Wenn die Audioengine von Pd gestartet wird, wird allen Objekten eine
Message mit dem Selector ``\verb+dsp+'' geschickt.
Alle Klassen, die eine Methode für die ``dsp''-Message haben, sind Signalklassen.
Signalklassen, die Signal-Inlets zur Verfügung stellen wollen,
müssen dies mit dem \verb+CLASS_MAINSIGNALIN+-Makro anmelden.
Dadurch ist der erste Inlet als Signalinlet deklariert.
\verb+t_float+-Messages können nicht mehr an einen solchen Eingang
gesendet werden.
Das erste Argument des Makros ist ein Zeiger auf die Signalklasse.
Das zweite Argument ist der Typ des Datenraums der Klasse.
Das dritte Argument ist eine Dummy-Variable aus dem Datenraum, die gebraucht wird,
um bei nicht vorhandenen Signalen am Signalinlet diese durch \verb+t_float+-Messages
einfach ersetzen zu können.
\subsection{Konstruktion von Signal-In- und Outlets}
\begin{verbatim}
void *pan_tilde_new(t_floatarg f)
{
t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class);
x->f_pan = f;
inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
floatinlet_new (&x->x_obj, &x->f_pan);
outlet_new(&x->x_obj, &s_signal);
return (void *)x;
}
\end{verbatim}
Zusätzliche Signal-Eingänge werden normal mit der Routine \verb+inlet_new+
hinzugefügt.
Die letzen beiden Argumente sind dann jeweils ein Verweis auf den symbolischen Selector
``signal'' in der lookup-Tabelle.
Signal-Outlets werden ebenfalls wie Message-Outlets generiert, deren Outlet mit dem
Selector ``signal'' versehen ist.
\subsection{DSP-Methode}
Wenn die Audio-Engine von Pd eingeschalten wird,
so teilen ihr alle Signal-Objekte mit,
welche Methode von ihrer Klasse zur digitalen Signalverarbeitung herangezogen werden soll.
Die ``DSP''-Methode hat als Argumente einen Zeiger auf den Klassendatenraum und
einen Zeiger auf ein Array von Signalen.
Die Signale im Array sind so angeordnet, dass sie am graphischen Objekt
im Uhrzeigersinn gelesen werden.\footnote{
Sofern linke und rechte Ein- und Ausgangssignale vorhanden sind, gilt also:
Zuerst kommt das linke Eingangssignal, danach die rechten Eingangssignale;
nach den rechten Ausgangssignalen kommt das linke Ausgangssignal.
}
\begin{verbatim}
void pan_tilde_dsp(t_pan_tilde *x, t_signal **sp)
{
dsp_add(pan_tilde_perform, 5, x,
sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
}
\end{verbatim}
\verb+dsp_add+ fügt eine ``Perform''-Routine (erstes Argument) zum DSP-Baum hinzu.
Das zweite Argument ist die Anzahl der nachfolgenden Zeiger auf diverse Variablen.
Welche Zeiger auf welche Variablen übergeben werden, unterliegt keiner Beschränkung.
sp[0] bezeichnet hier das erste Eingangssignal, sp[1] das zweite Eingangssignal,
sp[3] das Ausgangssignal.
Die Struktur \verb+t_signal+ enthält einen Zeiger auf den
zugehörigen Signalvektor \verb+.s_vec+ (ein Array von Samples \verb+t_sample+),
sowie die Länge dieses Signalvektors \verb+.s_n+.
Da innerhalb eines Patches alle Signalvektoren die gleiche Länge haben,
genügt es, die Länge eines dieser Vektoren abzufragen.
\subsection{perform-Routine}
Die perform-Routine ist das eigentliche DSP-Herzstück einer Signalklasse.
Ihr wird ein Zeiger auf ein Integer-Array übergeben.
In diesem Array sind die Zeiger gespeichert, die mit \verb+dsp_add+ übergeben wurden.
Sie müssen auf ihren ursprünglichen Typ zurückgecastet werden.
Die perform-Routine muß einen Zeiger auf Integer zurückgeben, der hinter den
Speicherplatz zeigt, in dem die eigenen Zeiger gespeichert sind.
Dies bedeutet, dass das Rückgabeargument gleich dem Übergabeargument plus der
Anzahl der eigenen Zeigervariablen (wie sie als zweites Argument in
\verb+dsp_add+ angegeben wurde) plus eins.
\begin{verbatim}
t_int *pan_tilde_perform(t_int *w)
{
t_pan_tilde *x = (t_pan_tilde *)(w[1]);
t_sample *in1 = (t_sample *)(w[2]);
t_sample *in2 = (t_sample *)(w[3]);
t_sample *out = (t_sample *)(w[4]);
int n = (int)(w[5]);
t_sample f_pan = (x->f_pan<0)?0.0:(x->f_pan>1)?1.0:x->f_pan;
while (n--) *out++ = (*in1++)*(1-f_pan)+(*in2++)*f_pan;
return (w+6);
}
\end{verbatim}
In der \verb+while+-Schleife wird jedes Sample der Signalvektoren einzeln
abgearbeitet.
Eine Optimierungsroutine bei der Erstellung des DSP-Baumes wird darauf geachtet,
keine unnötigen Kopieroperationen durchzuführen.
Es kann daher geschehen, dass ein Eingangs- und ein Ausgangssignal an der
gleichen Stelle im Speicher stehen.
Es ist daher in solchem Falle darauf zu achten,
dass nicht in das Ausgangssignal geschrieben wird,
bevor dort das Eingangssignal ausgelesen wurde.
\subsection{der Code: \tt pan\~\/}
\begin{verbatim}
#include "m_pd.h"
static t_class *pan_tilde_class;
typedef struct _pan_tilde {
t_object x_obj;
t_sample f_pan;
t_sample f;
} t_pan_tilde;
t_int *pan_tilde_perform(t_int *w)
{
t_pan_tilde *x = (t_pan_tilde *)(w[1]);
t_sample *in1 = (t_sample *)(w[2]);
t_sample *in2 = (t_sample *)(w[3]);
t_sample *out = (t_sample *)(w[4]);
int n = (int)(w[5]);
t_sample f_pan = (x->f_pan<0)?0.0:(x->f_pan>1)?1.0:x->f_pan;
while (n--) *out++ = (*in1++)*(1-f_pan)+(*in2++)*f_pan;
return (w+6);
}
void pan_tilde_dsp(t_pan_tilde *x, t_signal **sp)
{
dsp_add(pan_tilde_perform, 5, x,
sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
}
void *pan_tilde_new(t_floatarg f)
{
t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class);
x->f_pan = f;
inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
floatinlet_new (&x->x_obj, &x->f_pan);
outlet_new(&x->x_obj, &s_signal);
return (void *)x;
}
void pan_tilde_setup(void) {
pan_tilde_class = class_new(gensym("pan~"),
(t_newmethod)pan_tilde_new,
0, sizeof(t_pan_tilde),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addmethod(pan_tilde_class,
(t_method)pan_tilde_dsp, gensym("dsp"), 0);
CLASS_MAINSIGNALIN(pan_tilde_class, t_pan_tilde, f);
}
\end{verbatim}
\vfill
\newpage
\begin{appendix}
\section{das Message-System von \em pd}
Nicht-Audio-Daten werden über ein Message-System verteilt.
Jede Message besteht aus einem ``Selector'' und einer Liste von Atomen.
\subsection{Atome}
Es gibt drei Arten von Atomen:
\begin{itemize}
\item {\em A\_FLOAT}: ein numerischer Wert (Gleitkommazahl)
\item {\em A\_SYMBOL}: ein symbolischer Wert (String)
\item {\em A\_POINTER}: ein Zeiger
\end{itemize}
Numerische Werte werden immer als Floating-Point-Werte (\verb+double+) dargestellt,
auch wenn es sich um Ganzzahlwerte handelt.
Jedes Symbol wird aus Performancegründen in einer lookup-Tabelle abgelegt.
Der Befehl \verb+gensym+ speichert, wenn nötig,
einen String in dieser Symboltabelle und gibt seine Addresse in der Tabelle zurück.
Atome vom Typ {\em A\_POINTER} haben in der Praxis
(für einfache Externals) eher untergeordnete Bedeutung.
Der Typ eines Atoms \verb+a+ wird im Strukturelement \verb+a.a_type+ gespeichert.
\subsection{Selectoren}
Der Selector ist ein Symbol und bestimmt, welchen Typ eine Message hat.
Es gibt fünf vordefinierte Selectoren:
\begin{itemize}
\item ``{\tt bang}'' bezeichnet ein Triggerevent.
Die Message besteht nur aus dem Selector und enthält keine Liste von Atomen.
\item ``{\tt float}'' bezeichnet einen numerischen Wert. Die Liste enthält nur ein Atom.
\item ``{\tt symbol}'' bezeichnet einen symbolischen Wert. Die Liste enthält nur ein Atom.
\item ``{\tt pointer}'' bezeichnet einen Zeiger. Die Liste enthält nur ein Atom.
\item ``{\tt list}'' bezeichnet eine Liste von mehreren Atomen.
\end{itemize}
Da die Symbole für diese Selectoren relativ häufig verwendet werden,
kann man deren Symboltabellen-Adresse auch direkt,
ohne den Umweg über \verb+gensym+ abfragen:
\begin{tabular}{l||l|l}
Selector&lookup-Routine&lookup-Addresse\\
\hline
\tt bang &\verb+gensym("bang")+ & \verb+&s_bang+ \\
\tt float &\verb+gensym("float")+ & \verb+&s_float+ \\
\tt symbol &\verb+gensym("symbol")+ & \verb+&s_symbol+ \\
\tt pointer &\verb+gensym("pointer")+ & \verb+&s_pointer+ \\
\tt list &\verb+gensym("list")+ & \verb+&s_list+ \\
--- (Signal) &\verb+gensym("signal")+&\verb+&s_symbol+
\end{tabular}
Es können auch andere Selectoren verwendet werden,
doch muss dann die Empfängerklasse entweder selbst eine Methode
für diesen Selector zur verfügung stellen,
oder eine Methode für ``anything'', also jeden beliebigen Selector, anbieten.
Messages die ohne Selector sofort mit einem Zahlenwert beginnen, werden automatisch
entweder als numerischer Wert (nur ein Atom) oder als Liste (mehrere Atome) erkannt.
Zum Beispiel sind also die Messages ``\verb+12.429+'' und ``\verb+float 12.429+'' ident.
Ebenfalls ident sind auch die Listen-Messages
``\verb+list 1 kleines Haus+'' und ``\verb+1 kleines Haus+''.
\section{Pd-Typen}
Da Pd auf mehreren Plattformen benutzt wird,
werden viele gewöhnliche Variablentypen, wie \verb|int|, neu definiert.
Um portablen Code zu schreiben ist es daher angebracht, die von Pd bereitgestellten
Typen zu verwenden.
Weiters gibt es viele vordefinierte Typen,
die das Leben des Programmierers vereinfachen sollten.
Pd-Typen beginnen im Allgemeinen mit \verb|t_|.
\begin{tabular}{c|l}
Pd-Type & Beschreibung \\
\hline\hline
\verb+t_atom+& Atom \\
\verb+t_float+ & Gleitkomma-Zahl \\
\verb+t_symbol+ & Symbol \\
\verb+t_gpointer+ & Zeiger (auf graphische Objekte) \\
\hline
\verb+t_int+ & Ganzzahl \\
\verb+t_signal+ & Struktur auf ein Signal \\
\verb+t_sample+ & Audio-Signalwert (Gleitkomma)\\
\verb+t_outlet+ & Outlet eines Objekts \\
\verb+t_inlet+ & Inlet eines Objekts \\
\verb+t_object+ & Objekt-Interna \\
\hline
\verb+t_class+ & eine Pd-Klasse \\
\verb+t_method+ & Zeiger auf Klassenmethode \\
\verb+t_newmethod+ & Zeiger auf Klasseninstanziierungsmethode (new-Routine) \\
\end{tabular}
\section{Wichtige Funktionen aus ``m\_pd.h''}
\subsection{Funktionen: Atome}
\subsubsection{SETFLOAT}
\begin{verbatim}
SETFLOAT(atom, f)
\end{verbatim}
Dieses Makro setzt den Typ von \verb+atom+ auf \verb+A_FLOAT+
und setzt den numerischen Wert dieses Atoms auf \verb+f+.
\subsubsection{SETSYMBOL}
\begin{verbatim}
SETSYMBOL(atom, s)
\end{verbatim}
Dieses Makro setzt den Typ von \verb+atom+ auf \verb+A_SYMBOL+
und setzt den symbolischen Wert dieses Atoms auf \verb+s+.
\subsubsection{SETPOINTER}
\begin{verbatim}
SETPOINTER(atom, pt)
\end{verbatim}
Dieses Makro setzt den Typ von \verb+atom+ auf \verb+A_POINTER+
und setzt den Zeiger-Wert dieses Atoms auf \verb+pt+.
\subsubsection{atom\_getfloat}
\begin{verbatim}
t_float atom_getfloat(t_atom *a);
\end{verbatim}
Wenn der Typ des Atoms \verb+a+ \verb+A_FLOAT+ ist, wird dessen numerischer Wert,
ansonsten ``0.0'' zurückgegeben.
\subsubsection{atom\_getfloatarg}
\begin{verbatim}
t_float atom_getfloatarg(int which, int argc, t_atom *argv)
\end{verbatim}
Wenn das Atom,
das in der Atomliste \verb+argv+ mit der Länge \verb+argc+ an der Stelle \verb+which+
zu finden ist,
vom Typ \verb+A_FLOAT+ ist, wird dessen numerischer Wert,
ansonsten ``0.0'' zurückgegeben.
\subsubsection{atom\_getint}
\begin{verbatim}
t_int atom_getint(t_atom *a);
\end{verbatim}
Wenn der Typ des Atoms \verb+a+ \verb+A_FLOAT+ ist, wird dessen numerischer
Wert als Ganzzahlwert, ansonsten ``0'' zurückgegeben.
\subsubsection{atom\_getsymbol}
\begin{verbatim}
t_symbol atom_getsymbol(t_atom *a);
\end{verbatim}
Wenn der Typ des Atoms \verb+a+ \verb+A_SYMBOL+ ist, wird ein Zeiger
auf dessen Symbol ansonsten auf das Symbol ``float'' zurückgegeben.
\subsubsection{atom\_gensym}
\begin{verbatim}
t_symbol *atom_gensym(t_atom *a);
\end{verbatim}
Wenn der Typ des Atoms \verb+a+ \verb+A_SYMBOL+ ist, wird ein Zeiger
auf dessen Symbol zurückgegeben.
Atome anderen Typs werden zuerst ``sinnvoll'' in Strings umgewandelt.
Diese Strings werden, falls nötig, in die Symbol-Tabelle eingetragen.
Die Zeiger auf das Symbol wird zurückgegeben.
\subsubsection{atom\_string}
\begin{verbatim}
void atom_string(t_atom *a, char *buf, unsigned int bufsize);
\end{verbatim}
Konvertiert ein Atom \verb+a+ in einen {\tt C}-String \verb+buf+.
Der char-Buffer muss selbst reserviert und seine Länge in \verb+bufsize+ angegeben werden.
\subsubsection{gensym}
\begin{verbatim}
t_symbol *gensym(char *s);
\end{verbatim}
Prüft, ob für den C-String \verb+*s+ bereits ein Eintrag in der Symbol-lookup-Tabelle
vorhanden ist.
Ist noch kein Eintrag vorhanden, so wird einer angelegt.
Ein Zeiger auf das Symbol in der Tabelle wird zurückgegeben.
\subsection{Funktionen: Klassen}
\subsubsection{class\_new}
\begin{verbatim}
t_class *class_new(t_symbol *name,
t_newmethod newmethod, t_method freemethod,
size_t size, int flags,
t_atomtype arg1, ...);
\end{verbatim}
Generiert eine neue Klasse mit dem symbolischen Namen \verb+name+.
\verb+newmethod+ ist eine Konstruktorfunktion,
die eine Instanz der Klasse konstruiert und einen Zeiger auf diese Instanz zurückgibt.
Wird manuell dynamischer Speicher reserviert,
so muss dieser bei Zerstörung eines Objektes
mit der Destruktormethode \verb+freemethod+ (kein Rückgabeargument)
wieder freigegeben werden.
\verb+size+ ist statische die Größe des Klassendatenraumes,
die mit der Funktion \verb+sizeof(t_mydata)+ berechnet werden kann.
\verb+flags+ bestimmen das Aussehen des graphischen Objektes.
Eine beliebige Kombination folgender Flags ist möglich:
\begin{tabular}{l|l}
Flag&Bedeutung\\
\hline
\verb+CLASS_DEFAULT+ &Ein normales Objekt mit einem Inlet \\
\verb+CLASS_PD+ & \em Objekte ohne Graphikdarstellung\\
\verb+CLASS_GOBJ+ & \em reine Graphikobjekte (wie Arrays, Graphen,...)\\
\verb+CLASS_PATCHABLE+ & \em normales Objekt (mit einem Inlet) \\
\verb+CLASS_NOINLET+ & Der standardmäßige Inlet wird unterdrückt \\
\end{tabular}
Flags, deren Bedeutung {\em kursiv} gedruckt ist,
haben geringe Bedeutung beim Schreiben von Externals.
Die restlichen Argumente \verb+arg1,...+ definieren
die Typen die Übergabeargumente bei der Objektkreation.
Höchstens sechs typgeprüfte Argumente können einem Objekt übergeben werden.
Die Argumententypeliste wird ``0'' terminiert.
Mögliche Argumententypen sind:
\begin{tabular}{l|l}
\verb+A_DEFFLOAT+ & ein numerischer Wert \\
\verb+A_DEFSYMBOL+ & ein symbolischer Wert \\
\verb+A_GIMME+ & eine Atomliste beliebiger Länge und Typen \\
\end{tabular}
Sollten mehr als sechs Argumente übergeben werden, muss man
\verb+A_GIMME+ verwenden und eine händische Typprüfung durchführen.
\subsubsection{class\_addmethod}
\begin{verbatim}
void class_addmethod(t_class *c, t_method fn, t_symbol *sel,
t_atomtype arg1, ...);
\end{verbatim}
Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+ für
eine Message mit dem Selector \verb+sel+ hinzu.
Die restlichen Argumente \verb+arg1,...+ definieren
die Typen der Atomliste die dem Selector folgt.
Höchstens sechs typgeprüfte Argumente angegeben werden.
Sollten mehr als sechs Argumente übergeben werden, muss man
\verb+A_GIMME+ verwenden und eine händische Typprüfung durchführen.
Die Argumententypeliste wird ``0'' terminiert.
Mögliche Argumententypen sind:
\begin{tabular}{l|l}
\verb+A_DEFFLOAT+ & ein numerischer Wert \\
\verb+A_DEFSYMBOL+ & ein symbolischer Wert \\
\verb+A_POINTER+ & eine Zeiger \\
\verb+A_GIMME+ & eine Atomliste beliebiger Länge und Typen \\
\end{tabular}
\subsubsection{class\_addbang}
\begin{verbatim}
void class_addbang(t_class *c, t_method fn);
\end{verbatim}
Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+
für eine ``bang''-Message hinzu.
Die ``bang''-Methode hat als Übergabeargument einen Zeiger auf den Klassendatenraum:
\verb+void my_bang_method(t_mydata *x);+
\subsubsection{class\_addfloat}
\begin{verbatim}
void class_addfloat(t_class *c, t_method fn);
\end{verbatim}
Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+
für eine ``float''-Message hinzu.
Die ``float''-Methode hat als Übergabeargument einen Zeiger auf den Klassendatenraum und
ein Gleitkommaargument:
\verb+void my_float_method(t_mydata *x, t_floatarg f);+
\subsubsection{class\_addsymbol}
\begin{verbatim}
void class_addsymbol(t_class *c, t_method fn);
\end{verbatim}
Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+
für eine ``symbol''-Message hinzu.
Die ``symbol''-Methode hat als Übergabeargument einen Zeiger auf den Klassendatenraum und
einen Zeiger auf das übergebene Symbol:
\verb+void my_symbol_method(t_mydata *x, t_symbol *s);+
\subsubsection{class\_addpointer}
\begin{verbatim}
void class_addpointer(t_class *c, t_method fn);
\end{verbatim}
Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+
für eine ``pointer''-Message hinzu.
Die ``pointer''-Methode hat als Übergabeargument einen Zeiger
auf den Klassendatenraum und einen Zeiger auf einen Pointer:
\verb+void my_pointer_method(t_mydata *x, t_gpointer *pt);+
\subsubsection{class\_addlist}
\begin{verbatim}
void class_addlist(t_class *c, t_method fn);
\end{verbatim}
Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+
für eine ``list''-Message hinzu.
Die ``list''-Methode hat als Übergabeargument neben einem Zeiger
auf den Klassendatenraum einen Zeiger auf das Selectorsymbol
(immer \verb+&s_list+),
die Anzahl der Atome in der Liste sowie einen Zeiger auf die Atomliste:
\verb+void my_list_method(t_mydata *x,+
\verb+ t_symbol *s, int argc, t_atom *argv);+
%\begin{verbatim}
%void my_list_method(t_mydata *x,
% t_symbol *s, int argc, t_atom *argv);
%\end{verbatim}
\subsubsection{class\_addanything}
\begin{verbatim}
void class_addanything(t_class *c, t_method fn);
\end{verbatim}
Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+
für eine beliebige Message hinzu.
Die anything-Methode hat als Übergabeargument neben einem Zeiger
auf den Klassendatenraum einen Zeiger auf das Selectorsymbol,
die Anzahl der Atome in der Liste sowie einen Zeiger auf die Atomliste:
\verb+void my_any_method(t_mydata *x,+
\verb+ t_symbol *s, int argc, t_atom *argv);+
%\begin{verbatim}
%void my_any_method(t_mydata *x,
% t_symbol *s, int argc, t_atom *argv);
%\end{verbatim}
\subsubsection{class\_addcreator}
\begin{verbatim}
void class_addcreator(t_newmethod newmethod, t_symbol *s,
t_atomtype type1, ...);
\end{verbatim}
Fügt zu einem Konstruktor \verb+newmethod+ ein zum Klassennamen alternatives
Kreatorsymbol \verb+s+ hinzu.
Dadurch können Objekte mit dem richtigen Klassennamen und einem Aliasnamen
(zum Beispiel eine Abkürzung, wie das Internal ``float'' bzw. ``f'') kreiert werden.
Die ``0''-terminierte Typenliste entspricht der von \verb+class_new+.
\subsubsection{class\_sethelpsymbol}
\begin{verbatim}
void class_sethelpsymbol(t_class *c, t_symbol *s);
\end{verbatim}
Clickt man mit der rechten Maustaste auf ein Pd-Objekt,
so kann man sich einen Hilfe-Patch für die zugehörige Objektklasse anzeigen lasse.
Standardmäßig wird ist dies ein Patch mit dem symbolischen Klassennamen
im Verzeichnis ``{\em doc/5.reference/}'' gesucht.
Für die Klasse, auf die \verb+c+ zeigt, wird der Name des Hilfepatches auf den
symbolischen Wert \verb+s+ geändert.
Dadurch können sich mehrere verwandte Klassen einen Hilfepatch teilen.
Pfadangaben erfolgen relativ zum Standardhilfepfad {\em doc/5.reference/}.
\subsubsection{pd\_new}
\begin{verbatim}
t_pd *pd_new(t_class *cls);
\end{verbatim}
Generiert eine neue Instanz der Klasse \verb+cls+ und gibt einen Zeiger auf diese
Instanz zurück.
\subsection{Funktionen: In- und Outlets}
Alle Inlet- und Outletroutinen benötigen eine Referenz auf die Objektinterna
der Klasseninstanz.
Die notwendige Variable vom Typ \verb+t_object+ im Datenraum wird bei der
Objektinstanziierung initialisiert.
Diese Variable muß als \verb+owner+-Objekt den Inlet- und Outletroutinen übergeben werden.
\subsubsection{inlet\_new}
\begin{verbatim}
t_inlet *inlet_new(t_object *owner, t_pd *dest,
t_symbol *s1, t_symbol *s2);
\end{verbatim}
Generiert einen zusätzlichen ``aktiven'' Inlet des Objektes, auf das \verb+owner+ zeigt.
\verb+dest+ zeigt im Allgemeinen auf ``\verb+owner.ob_pd+''.
Der Selector \verb+s1+ am neuen Inlet, wird durch den Selector \verb+s2+ substituiert.
Tritt also eine Message mit dem Selector \verb+s1+ am neuen Inlet auf,
wird die Klassenmethode für den Selector \verb+s2+ ausgeführt.
Dies bedeutet
\begin{itemize}
\item Der substituierende Selector muss mit \verb+class_addmethod+ angegeben werden.
\item Man kann einen bestimmten rechten Eingang simulieren,
indem man dem ersten Eingang eine Message mit dem Selector dieses Eingangs schickt.
Verwendet man ein leeres Symbol (\verb+gensym("")+) als Selector,
so erreicht man, dass der rechte Eingang nicht über den ersten angesprochen werden kann.
\item Es ist nicht möglich, einem rechten Eingang Methoden für mehr als einen Selector
zuzuweisen. Insbesondere ist es nicht möglich, ihm eine allgemeine Methode
für einen beliebigen Selector zuzuweisen.
\end{itemize}
\subsubsection{floatinlet\_new}
\begin{verbatim}
t_inlet *floatinlet_new(t_object *owner, t_float *fp);
\end{verbatim}
Schafft einen neuen ``passiven'' Eingang für das Objekt, auf das \verb+owner+ zeigt,
der es erlaubt, einen numerischen Wert von außen direkt auf einen
Speicherplatz \verb+fp+ zu schreiben, ohne eine eigene Methode aufzurufen.
\subsubsection{symbolinlet\_new}
\begin{verbatim}
t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp);
\end{verbatim}
Schafft einen neuen ``passiven'' Eingang für das Objekt, auf das \verb+owner+ zeigt,
der es erlaubt, einen symbolischen Wert von außen direkt auf einen
Speicherplatz \verb+sp+ zu schreiben, ohne eine eigene Methode aufzurufen.
\subsubsection{pointerinlet\_new}
\begin{verbatim}
t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp);
\end{verbatim}
Schafft einen neuen ``passiven'' Eingang für das Objekt, auf das \verb+owner+ zeigt,
der es erlaubt, einen Zeigerwert von außen direkt auf einen
Speicherplatz \verb+gp+ zu schreiben, ohne eine eigene Methode aufzurufen.
\subsubsection{outlet\_new}
\begin{verbatim}
t_outlet *outlet_new(t_object *owner, t_symbol *s);
\end{verbatim}
Generiert einen neuen Ausgang für das Objekt, auf das \verb+owner+ zeigt.
Das Symbol, auf das \verb+s+ zeigt, zeigt den Typ des Ausgangs an.
\begin{tabular}{c|l||l}
Symbolwert & Symboladresse & Outlet-Typus \\
\hline\hline
``bang'' & \verb+&s_bang+ & Message (Bang)\\
``float'' & \verb+&s_float+ & Message (Float)\\
``symbol'' & \verb+&s_symbol+ & Message (Symbol) \\
``pointer'' & \verb+&s_gpointer+ & Message (List)\\
``list'' & \verb+&s_list+ & Message \\
--- & 0 & Message \\
\hline
``signal'' & \verb+&s_signal+ & Signal \\
\end{tabular}
Zwischen den verschiedenen Message-Outlet-Typen gibt es keinen Unterschied.
Allerdings macht es den Code leichter lesbar,
wenn schon bei der Outlet-Generierung angezeigt wird, wozu der Ausgang verwendet wird.
Für allgemeine Message-Outlets verwendet man einen ``0''-Pointer.
Variablen vom Typ \verb+t_object+ stellen einen Zeiger auf einen Outlet zur Verfügung.
Bei der Generierung eines neuen Outlets,
wird seine Addresse in der Objektvariablen \verb+(*owner).ob_outlet+ gespeichert.
Werden mehrere Message-Ausgänge benötigt, müssen die Outletzeiger,
die von \verb+outlet_new+ zurückgegeben werden, manuell im Datenraum gespeichert werden,
um die jeweiligen Ausgänge ansprechen zu können.
\subsubsection{outlet\_bang}
\begin{verbatim}
void outlet_bang(t_outlet *x);
\end{verbatim}
Gibt am Outlet, auf den \verb+x+ zeigt, eine ``bang''-Message aus.
\subsubsection{outlet\_float}
\begin{verbatim}
void outlet_float(t_outlet *x, t_float f);
\end{verbatim}
Gibt am Outlet, auf den \verb+x+ zeigt, eine ``float''-Message mit dem
numerischen Wert \verb+f+ aus.
\subsubsection{outlet\_symbol}
\begin{verbatim}
void outlet_symbol(t_outlet *x, t_symbol *s);
\end{verbatim}
Gibt am Outlet, auf den \verb+x+ zeigt, eine ``symbol''-Message mit dem
symbolischen Wert von \verb+s+ aus.
\subsubsection{outlet\_pointer}
\begin{verbatim}
void outlet_pointer(t_outlet *x, t_gpointer *gp);
\end{verbatim}
Gibt am Outlet, auf den \verb+x+ zeigt, eine ``pointer''-Message mit dem
Zeiger \verb+gp+ aus.
\subsubsection{outlet\_list}
\begin{verbatim}
void outlet_list(t_outlet *x,
t_symbol *s, int argc, t_atom *argv);
\end{verbatim}
Gibt am Outlet, auf den \verb+x+ zeigt, eine ``list''-Message mit
\verb+argc+ Atomen aus.
\verb+argv+ zeigt auf das erste Atom der Liste.
Unabhängig davon, auf welches Symbol \verb+s+ zeigt, wird der Selector
``list'' der Liste vorangestellt.
Aus Lesbarkeitsgründen sollte man aber trotzdem einen Zeiger auf das
Symbol ``list'' (\verb+gensym("list")+ oder \verb+&s_list+) angeben.
\subsubsection{outlet\_anything}
\begin{verbatim}
void outlet_anything(t_outlet *x,
t_symbol *s, int argc, t_atom *argv);
\end{verbatim}
Gibt am Outlet, auf den \verb+x+ zeigt, eine Message mit
dem Selector, auf den \verb+s+ zeigt, aus.
Dem Selector folgen \verb+argc+ Atome.
\verb+argv+ zeigt auf das erste Atom dieser Liste.
\subsection{Funktionen: DSP}
Soll eine Klasse Methoden zur digitalen Signalsverarbeitung zur Verfügung stellen,
so muss ihr eine Methode für den Selector ``dsp'' hinzugefügt werden.
Wird die Audio-Engine gestartet, so werden alle Objekte, die eine ``dsp''-Methode
zur Verfügung stellen, als Instanzen von Signalklassen identifiziert.
\paragraph{DSP-Methode}
\begin{verbatim}
void my_dsp_method(t_mydata *x, t_signal **sp)
\end{verbatim}
In der ``dsp''-Methode wird mit der Funktion \verb+dsp_add+ die
Klassenroutine für Signalverarbeitung in den DSP-Baum eingebunden.
Neben dem eigenen Datenraum \verb+x+, wird auch ein Array von Signalen übergeben.
Die Signale im Array sind so angeordnet, dass sie am graphischen Objekt
im Uhrzeigersinn gelesen werden.
Sofern je zwei Ein- und Ausgangssignale vorhanden sind, gilt also:
\begin{tabular}{c|r}
Zeiger & auf Signal \\
\hline\hline
sp[0] & linkes Eingangssignal \\
sp[1] & rechtes Eingangssignal \\
sp[2] & rechtes Ausgangssignal \\
sp[3] & linkes Ausgangssignal \\
\end{tabular}
Die Signalstruktur enthält unter anderem:
\begin{tabular}{c|l}
Strukturelement & Bedeutung \\
\hline
\verb+s_n+ & Länge des Signalvektors \\
\verb+s_vec+ & Zeiger auf den Signalvektor \\
\end{tabular}
Der Signalvektor ist ein Array auf Samples vom Typ \verb+t_sample+.
\paragraph{Perform-Routine}
\begin{verbatim}
t_int *my_perform_routine(t_int *w)
\end{verbatim}
Der Perform-Routine die mit \verb+class_add+ in den DSP-Baum eingefügt wurde,
wird ein Zeiger \verb+w+ auf ein (Integer-)Array übergeben.
In diesem Array sind die Zeiger gespeichert, die mit \verb+dsp_add+ übergeben wurden.
Sie müssen auf ihren ursprünglichen Typ zurückgecastet werden.
Der erste Zeiger ist an der Stelle \verb+w[1]+ gespeichert !!!
Die perform-Routine muß einen Zeiger auf Integer zurückgeben, der hinter den
Speicherplatz zeigt, in dem die eigenen Zeiger gespeichert sind.
Dies bedeutet, dass das Rückgabeargument gleich dem Übergabeargument plus der
Anzahl der eigenen Zeigervariablen (wie sie als zweites Argument in
\verb+dsp_add+ angegeben wurde) plus eins.
\subsubsection{CLASS\_MAINSIGNALIN}
\begin{verbatim}
CLASS_MAINSIGNALIN(<class_name>, <class_data>, <f>);
\end{verbatim}
Das Makro \verb+CLASS_MAINSIGNALIN+ meldet an, dass die Klasse
Signal-Inlets brauchts.
Das erste Argument des Makros ist ein Zeiger auf die Signalklasse.
Das zweite Argument ist der Typ des Datenraums der Klasse.
Das dritte Argument ist eine (Dummy-)Gleitkomma-Variable aus dem Datenraum,
die gebraucht wird, um bei nicht vorhandenen Signalen am Signalinlet,
``float''-Messages wie Signale behandeln zu können.
An so kreierten Signaleingängen können daher keine zusätzlichen ``float''-Messages
geschickt werden.
\subsubsection{dsp\_add}
\begin{verbatim}
void dsp_add(t_perfroutine f, int n, ...);
\end{verbatim}
Fügt dem DSP-Baum eine Perform-Routine \verb+f+ hinzu,
die jeden DSP-Zyklus neu aufgerufen wird.
Das zweite Argument \verb+n+ legt die Anzahl der nachfolgenden Zeigerargumente fest.
Welche Zeiger auf welche Variablen übergeben werden, unterliegt keiner Beschränkung.
Sinnvoll sind im Allgemeinen Zeiger auf den Datenraum und auf die Signalvektoren.
Auch die Länge der Signalvektoren sollte übergeben werden,
um effektiv Signale manipulieren zu können.
\subsubsection{sys\_getsr}
\begin{verbatim}
float sys_getsr(void);
\end{verbatim}
Gibt die Abtastrate des Systems zurück.
\subsection{Funktion: Memory}
\subsubsection{getbytes}
\begin{verbatim}
void *getbytes(size_t nbytes);
\end{verbatim}
Reserviert \verb+nbytes+ Bytes und gibt einen Zeiger auf den reservierten Speicher zurück.
\subsubsection{copybytes}
\begin{verbatim}
void *copybytes(void *src, size_t nbytes);
\end{verbatim}
Kopiert \verb+nbytes+ Bytes von \verb+*src+ in einen neu alloziierten Speicher.
Die Addresse dieses Speichers wird zurückgegeben.
\subsubsection{freebytes}
\begin{verbatim}
void freebytes(void *x, size_t nbytes);
\end{verbatim}
Gibt \verb+nbytes+ Bytes an der Addresse \verb+*x+ frei.
\subsection{Funktionen: Ausgabe}
\subsubsection{post}
\begin{verbatim}
void post(char *fmt, ...);
\end{verbatim}
Schreibt einen {\tt C}-String auf den Standarderror (Shell).
\subsubsection{error}
\begin{verbatim}
void error(char *fmt, ...);
\end{verbatim}
Schreibt einen {\tt C}-String als Fehlermeldung auf den Standarderror (Shell).
Das Objekt, das die Fehlermeldung ausgegeben hat, wird markiert und
ist über das Pd-Menü {\em Find->Find last error} identifizierbar.
\end{appendix}
\end{document}
% format latexg -*- latex -*-
\documentclass[12pt, a4paper,english,titlepage]{article}
%% HOWTO write an external for pd
%% Copyright (c) 2001-2006 by IOhannes m zmölnig
%%
%% Permission is granted to copy, distribute and/or modify this document
%% under the terms of the GNU Free Documentation License, Version 1.2
%% or any later version published by the Free Software Foundation;
%% with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
%% Texts. A copy of the license is included in the LICENSE.txt file.
\usepackage[latin1]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{babel}
\title{
HOWTO \\
write an External \\
for {\em Pure Data}
}
\author{
johannes m zmölnig \\
\\
{\em institute of electronic music and acoustics\footnote{http://iem.at}}
}
\date{}
\begin{document}
\maketitle
\begin{abstract}
Pd is a graphical real-time computer-music system that follows the tradition of
IRCAMs {\em ISPW-max}.
Although plenty of functions are built into Pd,
it is sometimes a pain or simply impossible to create a patch with a certain
functionality out of the given primitives and combinations of these.
Therefore, Pd can be extended with self made primitives (``objects'')
that are written in complex programming-languages, like {\tt C/C++}.
This document aims to explain, how to write such primitives in {\tt C},
the popular language that was used to realize Pd.
\end{abstract}
\vfill
\newpage
\tableofcontents
\vfill
\newpage
\section{definitions and prerequisites}
Pd refers to the graphical real-time computer-music environment {\em Pure Data}
by Miller~S.~Puckette.
To fully understand this document, it is necessary to
be acquainted with Pd and to
have a general understanding of programming techniques especially in {\tt C}.
To write externals yourself, a {\tt C}-compiler that supports the
{\tt ANSI-C}-Standard, like the {\em Gnu C-compiler} (gcc) on linux-systems or
{\em Visual-C++} on windos-plattforms, will be necessary.
\subsection{classes, instances, objects}
Pd is written in the programming-language {\tt C}.
Due to its graphical nature, Pd is a {\em object-oriented} system.
Unfortunately {\tt C} does not support very well the use of classes.
Thus the resulting source-code is not as elegant as {\tt C++}-code would be, for instance.
In this document, the expression {\em class} refers to the realisation of a concept
combining data and manipulators on this data.
Concrete {\em instances of a class} are called {\em objects}.
\subsection{internals, externals und libraries}
To avoid confusion of ideas, the expressions {\em internal}, {\em external} and
{\em library} should be explained here.
\paragraph{Internal}
An {\em internal} is a class that is built into Pd.
Plenty of primitives, such as ``+'', ``pack'' or ``sig\~\/'' are {\em internals}.
\paragraph{External}
An {\em external} is a class that is not built into Pd but is loaded at runtime.
Once loaded into Pd's memory, {\em externals} cannot be distinguished from
{\em internals} any more.
\paragraph{Library}
A {\em library} is a collection of {\em externals} that are compiled into a
single binary-file.
{\em Library}-files have to follow a system dependent naming convention:
\begin{tabular}{c||c|c|c}
library & linux&irix&Win32 \\
\hline
{\tt my\_lib}&{\tt my\_lib.pd\_linux}&{\tt my\_lib.pd\_irix}&
{\tt my\_lib.dll}\\
\end{tabular}
The simplest form of a {\em library} includes exactly one {\em external}
bearing the same name as the {\em library}.
Unlike {\em externals}, {\em libraries} can be imported by Pd with special operations.
After a {\em library} has been imported,
all included {\em externals} have been loaded into memory and are available as objects.
Pd supports to modes to import {\em libraries}:
\begin{itemize}
\item via the command line-option ``{\tt -lib my\_lib}''
\item by creating an object ``{\tt my\_lib}''
\end{itemize}
The first method loads a {\em library} when Pd is started.
This method is preferably used for {\em libraries} that contain several {\em externals}.
The other method should be used for {\em libraries} that contain exactly
one {\em external} bearing the same name.
Pd checks first, whether a class named ``my\_lib'' is already loaded.
If this is not the case\footnote{
If a class ``my\_lib'' is already existent, an object ``my\_lib'' will be instantiated
and the procedure is done.
Thus, no {\em library} has been loaded.
Therefore no {\em library} that is named like an already used class-name like, say, ``abs'',
can be loaded.}, all paths are searched for a file called
``{\tt my\_lib.pd\_linux}''\footnote{or another system-dependent filename-extensions (s.a.)}.
If such file is found, all included {\em externals} are loaded into memory by calling a
routine \verb+my_lib_setup()+.
After loading, a class ``my\_lib'' is (again) looked for as a (newly loaded) {\em external}.
If so, an instance of this class is created, else the instantiation fails and an error is
printed.
Anyhow, all {\em external}-classes declared in the {\em library} are loaded by now.
\section{my first external: {\tt helloworld}}
Usually the first attempt learning a programming-language is a ``hello world''-application.
In our case, an object class should be created, that prints the line ``hello world!!'' to
the standard error every time it is triggered with a ``bang''-message.
\subsection{the interface to Pd}
To write a Pd-external a well-defined interface is needed.
This is provided in the header-file ``m\_pd.h''.
\begin{verbatim}
#include "m_pd.h"
\end{verbatim}
\subsection{a class and its data space}
First a new class has to be prepared and the data space for this class has to be defined.
\begin{verbatim}
static t_class *helloworld_class;
typedef struct _helloworld {
t_object x_obj;
} t_helloworld;
\end{verbatim}
\verb+hello_worldclass+ is going to be a pointer to the new class.
The structure \verb+t_helloworld+ (of the type \verb+_helloworld+) is
the data space of the class.
An absolutely necessary element of the data space is a variable of the type
\verb+t_object+, which is used to store internal object-properties like
the graphical presentation of the object or data about inlets and outlets.
\verb+t_object+ has to be the first entry in the structure !
Because a simple ``hello world''-application needs no variables,
the structure is empty apart from the \verb+t_object+.
\subsection{method space}
Apart from the data space, a class needs a set of manipulators (methods) to
manipulate the data with.
If a message is sent to an instance of our class, a method is called.
These methods are the interfaces to the message system of Pd.
On principal they have no return argument and are therefore are of the
type \verb+void+.
\begin{verbatim}
void helloworld_bang(t_helloworld *x)
{
post("Hello world !!");
}
\end{verbatim}
This method has an argument of the type \verb+t_helloworld+,
which would enable us to manipulate the data space.
Since we only want to output ``Hello world!''
(and, by the way, our data space is quite sparse),
we renounce a manipulation.
The command \verb+post(char *c,...)+ sends a string to the standard error.
A carriage return is added automatically.
Apart from this, the \verb+post+-command works like the {\tt C}-command \verb+printf()+.
\subsection{generation of a new class}
To generate a new class, information of the data space and the method space of this class,
have to be passed to Pd when a library is loaded.
On loading a new library ``my\_lib'',
Pd tries to call a function ``my\_lib\_setup()''.
This function (or functions called by it)
declares the new classes and their properties.
It is only called once, when the library is loaded.
If the function-call fails (e.g., because no function of the specified name is present)
no external of the library will be loaded.
\begin{verbatim}
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);
}
\end{verbatim}
\paragraph{class\_new}
The function \verb+class_new+ creates a new class and returns a pointer to this prototype.
The first argument is the symbolic name of the class.
The next two arguments define the constructor and destructor of the class.
Whenever a class object is created in a Pd-patch,
the class-constructor \verb+(t_newmethod)helloworld_new+ instantiates the object
and initialises the data space.
Whenever an object is destroyed
(either by closing the containing patch or by deleting the object from the patch)
the destructor frees the dynamically reserved memory.
The allocated memory for the static data space is automatically reserved and freed.
Therefore we do not have to provide a destructor in this example, the argument
is set to ``0''.
To enable Pd to reserve and free enough memory for the static data space,
the size of the data structure has to be passed as the fourth argument.
The fifth argument has influence on the graphical representation of the class objects.
The default-value is \verb+CLASS_DEFAULT+ or simply ``0''.
The remaining arguments define the arguments of an object and its type.
Up to six numeric and symbolic object-arguments can be defined via
\verb+A_DEFFLOAT+ and \verb+A_DEFSYMBOL+.
If more arguments are to be passed to the object
or if the order of atom types should by more flexible,
\verb+A_GIMME+ can be used for passing an arbitrary list of atoms.
The list of object-arguments is terminated by ``0''.
In this example we have no object-arguments at all for the class.
\paragraph{class\_addbang}
We still have to add a method space to the class.
\verb+class_addbang+ adds a method for a ``bang''-message to the class that is
defined in the first argument.
The added method is defined in the second argument.
\subsection{constructor: instantiation of an object}
Each time, an object is created in a Pd-patch, the
constructor that is defined with the \verb+class_new+-command,
generates a new instance of the class.
The constructor has to be of type \verb+void *+.
\begin{verbatim}
void *helloworld_new(void)
{
t_helloworld *x = (t_helloworld *)pd_new(helloworld_class);
return (void *)x;
}
\end{verbatim}
The arguments of the constructor-method depend on the object-arguments
defined with \verb+class_new+.
\begin{tabular}{l|l}
\verb+class_new+-argument&constructor-argument\\
\hline
\verb+A_DEFFLOAT+&\verb+t_floatarg f+ \\
\verb+A_DEFSYMBOL+&\verb+t_symbol *s+ \\
\verb+A_GIMME+&\verb+t_symbol *s, int argc, t_atom *argv+
\end{tabular}
Because there are no object-arguments for our ``hello world''-class,
the constructor has anon too.
The function \verb+pd_new+ reserves memory for the data space,
initialises the variables that are internal to the object and
returns a pointer to the data space.
The type-cast to the data space is necessary.
Normally, the constructor would initialise the object-variables.
However, since we have none, this is not necessary.
The constructor has to return a pointer to the instantiated data space.
\subsection{the code: \tt helloworld}
\begin{verbatim}
#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);
}
\end{verbatim}
\section{a simple external: {\tt counter}}
Now we want to realize a simple counter as an external.
A ``bang''-trigger outputs the counter-value on the outlet and
afterwards increases the counter-value by 1.
This class is similar to the previous one,
but the data space is extended by a variable ``counter'' and the
result is written as a message to an outlet instead of
a string to the standard error.
\subsection{object-variables}
Of course, a counter needs a state-variable to store the actual counter-value.
State-variables that belong to class instances belong to the data space.
\begin{verbatim}
typedef struct _counter {
t_object x_obj;
t_int i_count;
} t_counter;
\end{verbatim}
The integer variable \verb+i_count+ stores the counter-value.
\subsection{object-arguments}
It is quite useful for a counter, if a initial value can be defined by the user.
Therefore this initial value should be passed to the object at creation-time.
\begin{verbatim}
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addbang(counter_class, counter_bang);
}
\end{verbatim}
So we have an additional argument in the function \verb+class_new+:
\verb+A_DEFFLOAT+ tells Pd, that the object needs one argument of the
type \verb+t_floatarg+.
If no argument is passed, this will default to ``0''.
\subsection{constructor}
The constructor has some new tasks.
On the one hand, a variable value has to be initialised,
on the other hand, an outlet for the object has to be created.
\begin{verbatim}
void *counter_new(t_floatarg f)
{
t_counter *x = (t_counter *)pd_new(counter_class);
x->i_count=f;
outlet_new(&x->x_obj, &s_float);
return (void *)x;
}
\end{verbatim}
The constructor-method has one argument of type \verb+t_floatarg+ as declared
in the setup-routine by \verb+class_new+.
This argument is used to initialise the counter.
A new outlet is created with the function \verb+outlet_new+.
The first argument is a pointer to the interna of the object
the new outlet is created for.
The second argument is a symbolic description of the outlet-type.
Since out counter should output numeric values it is of type ``float''.
\verb+outlet_new+ returns a pointer to the new outlet and saves this very pointer
in the \verb+t_object+-variable \verb+x_obj.ob_outlet+.
If only one outlet is used, the pointer need not additionally be stored in the data space.
If more than one outlets are used, the pointers have to be stored in the data space,
because the \verb+t_object+-variable can only hold one outlet pointer.
\subsection{the counter method}
When triggered, the counter value should be sent to the outlet
and afterwards be incremented by 1.
\begin{verbatim}
void counter_bang(t_counter *x)
{
t_float f=x->i_count;
x->i_count++;
outlet_float(x->x_obj.ob_outlet, f);
}
\end{verbatim}
The function \verb+outlet_float+ sends a floating-point-value (second argument) to the outlet
that is specified by the first argument.
We first store the counter in a floating point-buffer.
Afterwards the counter is incremented and not before that the buffer variable is sent
to the outlet.
What appears to be unnecessary on the first glance, makes sense after further
inspection:
The buffer variable has been realized as \verb+t_float+,
since \verb+outlet_float+ expects a floating point-value and a typecast is
inevitable.
If the counter value was sent to the outlet before being incremented,
this could result in an unwanted (though well defined) behaviour:
If the counter-outlet directly triggered its own inlet,
the counter-method would be called although the counter value was not yet incremented.
Normally this is not what we want.
The same (correct) result could of course be obtained with a single line,
but this would obscure the {\em reentrant}-problem.
\subsection{the code: \tt counter}
\begin{verbatim}
#include "m_pd.h"
static t_class *counter_class;
typedef struct _counter {
t_object x_obj;
t_int i_count;
} t_counter;
void counter_bang(t_counter *x)
{
t_float f=x->i_count;
x->i_count++;
outlet_float(x->x_obj.ob_outlet, f);
}
void *counter_new(t_floatarg f)
{
t_counter *x = (t_counter *)pd_new(counter_class);
x->i_count=f;
outlet_new(&x->x_obj, &s_float);
return (void *)x;
}
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addbang(counter_class, counter_bang);
}
\end{verbatim}
\section{a complex external: \tt counter}
The simple counter of the previous chapter can easily be extended to more complexity.
It might be quite useful to be able to reset the counter to an initial value,
to set upper and lower boundaries and to control the step-width.
Each overrun should send a ``bang''-Message to a second outlet and reset the counter to
the initial value.
\subsection{extended data space}
\begin{verbatim}
typedef struct _counter {
t_object x_obj;
t_int i_count;
t_float step;
t_int i_down, i_up;
t_outlet *f_out, *b_out;
} t_counter;
\end{verbatim}
The data space has been extended to hold variables for step width and
upper and lower boundaries.
Furthermore pointers for two outlets have been added.
\subsection{extension of the class}
The new class objects should have methods for different messages,
like ``set'' and ``reset''.
Therefore the method space has to be extended too.
\begin{verbatim}
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_GIMME, 0);
\end{verbatim}
The class generator \verb+class_new+ has been extended by the argument \verb+A_GIMME+.
This enables a dynamic number of arguments to be passed at the instantiation of the object.
\begin{verbatim}
class_addmethod(counter_class,
(t_method)counter_reset,
gensym("reset"), 0);
\end{verbatim}
\verb+class_addmethod+ adds a method for an arbitrary selector to an class.
The first argument is the class the method (second argument) will be added to.
The third argument is the symbolic selector that should be associated with the method.
The remaining ``0''-terminated arguments describe the list of atoms that
follows the selector.
\begin{verbatim}
class_addmethod(counter_class,
(t_method)counter_set, gensym("set"),
A_DEFFLOAT, 0);
class_addmethod(counter_class,
(t_method)counter_bound, gensym("bound"),
A_DEFFLOAT, A_DEFFLOAT, 0);
\end{verbatim}
A method for ``set'' followed by a numerical value is added,
as well as a method for the selector ``bound'' followed by two numerical values.
\begin{verbatim}
class_sethelpsymbol(counter_class, gensym("help-counter"));
\end{verbatim}
If a Pd-object is right-clicked, a help-patch describing the object-class can be opened.
By default, this patch is located in the directory ``{\em doc/5.reference/}'' and
is named like the symbolic class name.
An alternative help-patch can be defined with the
\verb+class_sethelpsymbol+-command.
\subsection{construction of in- and outlets}
When creating the object, several arguments should be passed by the user.
\begin{verbatim}
void *counter_new(t_symbol *s, int argc, t_atom *argv)
\end{verbatim}
Because of the declaration of arguments in the \verb+class_new+-function
with \verb+A_GIMME+,
the constructor has following arguments:
\begin{tabular}{c|l}
\verb+t_symbol *s+ & the symbolic name,\\
& that was used for object creation \\
\verb+int argc+ & the number of arguments passed to the object\\
\verb+t_atom *argv+ & a pointer to a list of {\tt argc} atoms
\end{tabular}
\begin{verbatim}
t_float f1=0, f2=0;
x->step=1;
switch(argc){
default:
case 3:
x->step=atom_getfloat(argv+2);
case 2:
f2=atom_getfloat(argv+1);
case 1:
f1=atom_getfloat(argv);
break;
case 0:
break;
}
if (argc<2)f2=f1;
x->i_down = (f1<f2)?f1:f2;
x->i_up = (f1>f2)?f1:f2;
x->i_count=x->i_down;
\end{verbatim}
If three arguments are passed, these should be the {\em lower boundary},
the {\em upper boundary} and the {\em step width}.
If only two arguments are passed, the step-width defaults to ``1''.
If only one argument is passed, this should be the {\em initial value} of the counter with
step-width of ``1''.
\begin{verbatim}
inlet_new(&x->x_obj, &x->x_obj.ob_pd,
gensym("list"), gensym("bound"));
\end{verbatim}
The function \verb+inlet_new+ creates a new ``active'' inlet.
``Active'' means, that a class-method is called each time
a message is sent to an ``active'' inlet.
Due to the software-architecture, the first inlet is always ``active''.
The first two arguments of the \verb+inlet_new+-function are
pointers to the interna of the object and to the graphical presentation of the object.
The symbolic selector that is specified by the third argument is to be
substituted by another symbolic selector (fourth argument) for this inlet.
Because of this substitution of selectors,
a message on a certain right inlet can be treated as a message with
a certain selector on the leftmost inlet.
This means:
\begin{itemize}
\item The substituting selector has to be declared by \verb+class_addmethod+
in the setup-routine.
\item It is possible to simulate a certain right inlet, by sending a message with
this inlet's selector to the leftmost inlet.
\item It is not possible to add methods for more than one selector to a right inlet.
Particularly it is not possible to add a universal method for arbitrary selectors to
a right inlet.
\end{itemize}
\begin{verbatim}
floatinlet_new(&x->x_obj, &x->step);
\end{verbatim}
\verb+floatinlet_new+ generates a new ``passive'' inlet for numerical values.
``Passive'' inlets allow parts of the data space-memory to be written directly
from outside.
Therefore it is not possible to check for illegal inputs.
The first argument is a pointer to the internal infrastructure of the object.
The second argument is the address in the data space-memory,
where other objects can write too.
``Passive'' inlets can be created for pointers, symbolic or
numerical (floating point\footnote{
That's why the {\tt step}-width of the class\/data space is realized as {\tt t\_float}.})
values.
\begin{verbatim}
x->f_out = outlet_new(&x->x_obj, &s_float);
x->b_out = outlet_new(&x->x_obj, &s_bang);
\end{verbatim}
The pointers returned by \verb+outlet_new+ have to be saved in the class\/data space
to be used later by the outlet-routines.
The order of the generation of inlets and outlets is important,
since it corresponds to the order of inlets and outlets in the
graphical representation of the object.
\subsection{extended method space}
The method for the ``bang''-message has to full fill the more complex tasks.
\begin{verbatim}
void counter_bang(t_counter *x)
{
t_float f=x->i_count;
t_int step = x->step;
x->i_count+=step;
if (x->i_down-x->i_up) {
if ((step>0) && (x->i_count > x->i_up)) {
x->i_count = x->i_down;
outlet_bang(x->b_out);
} else if (x->i_count < x->i_down) {
x->i_count = x->i_up;
outlet_bang(x->b_out);
}
}
outlet_float(x->f_out, f);
}
\end{verbatim}
Each outlet is identified by the \verb+outlet_...+-functions via the
pointer to this outlets.
The remaining methods still have to be implemented:
\begin{verbatim}
void counter_reset(t_counter *x)
{
x->i_count = x->i_down;
}
void counter_set(t_counter *x, t_floatarg f)
{
x->i_count = f;
}
void counter_bound(t_counter *x, t_floatarg f1, t_floatarg f2)
{
x->i_down = (f1<f2)?f1:f2;
x->i_up = (f1>f2)?f1:f2;
}
\end{verbatim}
\subsection{the code: \tt counter}
\begin{verbatim}
#include "m_pd.h"
static t_class *counter_class;
typedef struct _counter {
t_object x_obj;
t_int i_count;
t_float step;
t_int i_down, i_up;
t_outlet *f_out, *b_out;
} t_counter;
void counter_bang(t_counter *x)
{
t_float f=x->i_count;
t_int step = x->step;
x->i_count+=step;
if (x->i_down-x->i_up) {
if ((step>0) && (x->i_count > x->i_up)) {
x->i_count = x->i_down;
outlet_bang(x->b_out);
} else if (x->i_count < x->i_down) {
x->i_count = x->i_up;
outlet_bang(x->b_out);
}
}
outlet_float(x->f_out, f);
}
void counter_reset(t_counter *x)
{
x->i_count = x->i_down;
}
void counter_set(t_counter *x, t_floatarg f)
{
x->i_count = f;
}
void counter_bound(t_counter *x, t_floatarg f1, t_floatarg f2)
{
x->i_down = (f1<f2)?f1:f2;
x->i_up = (f1>f2)?f1:f2;
}
void *counter_new(t_symbol *s, int argc, t_atom *argv)
{
t_counter *x = (t_counter *)pd_new(counter_class);
t_float f1=0, f2=0;
x->step=1;
switch(argc){
default:
case 3:
x->step=atom_getfloat(argv+2);
case 2:
f2=atom_getfloat(argv+1);
case 1:
f1=atom_getfloat(argv);
break;
case 0:
break;
}
if (argc<2)f2=f1;
x->i_down = (f1<f2)?f1:f2;
x->i_up = (f1>f2)?f1:f2;
x->i_count=x->i_down;
inlet_new(&x->x_obj, &x->x_obj.ob_pd,
gensym("list"), gensym("bound"));
floatinlet_new(&x->x_obj, &x->step);
x->f_out = outlet_new(&x->x_obj, &s_float);
x->b_out = outlet_new(&x->x_obj, &s_bang);
return (void *)x;
}
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_GIMME, 0);
class_addbang (counter_class, counter_bang);
class_addmethod(counter_class,
(t_method)counter_reset, gensym("reset"), 0);
class_addmethod(counter_class,
(t_method)counter_set, gensym("set"),
A_DEFFLOAT, 0);
class_addmethod(counter_class,
(t_method)counter_bound, gensym("bound"),
A_DEFFLOAT, A_DEFFLOAT, 0);
class_sethelpsymbol(counter_class, gensym("help-counter"));
}
\end{verbatim}
\section{a signal-external: {\tt pan\~\/}}
Signal classes are normal Pd-classes, that offer additional methods for signals.
All methods and concepts that can be realized with normal object classes can
therefore be realized with signal classes too.
Per agreement, the symbolic names of signal classes end with a tilde \~\/.
The class ``pan\~\/'' shall demonstrate, how signal classes are written.
A signal on the left inlet is mixed with a signal on the second inlet.
The mixing-factor between 0 and 1 is defined via a \verb+t_float+-message
on a third inlet.
\subsection{variables of a signal class}
Since a signal-class is only an extended normal class,
there are no principal differences between the data spaces.
\begin{verbatim}
typedef struct _pan_tilde {
t_object x_obj;
t_sample f_pan;
t_float f;
} t_pan_tilde;
\end{verbatim}
Only one variable \verb+f_pan+ for the {\em mixing-factor} of the panning-function is needed.
The other variable \verb+f+ is needed whenever a signal-inlet is needed too.
If no signal but only a float-message is present at a signal-inlet, this
variable is used to automatically convert the float to signal.
\subsection{signal-classes}
\begin{verbatim}
void pan_tilde_setup(void) {
pan_tilde_class = class_new(gensym("pan~"),
(t_newmethod)pan_tilde_new,
0, sizeof(t_pan_tilde),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addmethod(pan_tilde_class,
(t_method)pan_tilde_dsp, gensym("dsp"), 0);
CLASS_MAINSIGNALIN(pan_tilde_class, t_pan_tilde, f);
}
\end{verbatim}
A method for signal-processing has to be provided by each signal class.
Whenever Pd's audio engine is started, a message with the selector ``dsp''
is sent to each object.
Each class that has a method for the ``dsp''-message is recognised as signal class.
Signal classes that want to provide signal-inlets have to
declare this via the \verb+CLASS_MAINSIGNALIN+-macro.
This enables signals at the first (default) inlet.
If more than one signal-inlet is needed, they have to be created explicitly
in the constructor-method.
Inlets that are declared as signal-inlets cannot provide
methods for \verb+t_float+-messages any longer.
The first argument of the macro is a pointer to the signal class.
The second argument is the type of the class's data space.
The last argument is a dummy-variable out of the data space that is needed
to replace non-existing signal at the signal-inlet(s) with \verb+t_float+-messages.
\subsection{construction of signal-inlets and -outlets}
\begin{verbatim}
void *pan_tilde_new(t_floatarg f)
{
t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class);
x->f_pan = f;
inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
floatinlet_new (&x->x_obj, &x->f_pan);
outlet_new(&x->x_obj, &s_signal);
return (void *)x;
}
\end{verbatim}
Additional signal-inlets are added like other inlets with the routine \verb+inlet_new+.
The last two arguments are references to the symbolic selector ``signal''
in the lookup-table.
Signal-outlets are also created like normal (message-)outlets,
by setting the outlet-selector to ``signal''.
\subsection{DSP-methods}
Whenever Pd's audio engine is turned on,
all signal-objects declare their perform-routines that are to be added to the DSP-tree.
The ``dsp''-method has two arguments, the pointer to the class-data space, and
a pointer to an array of signals.
The signals are arranged in the array in such way,
that they are ordered in a clockwise way in the graphical representation of the
object.\footnote{
If both left and right in- and out-signals exist, this means:
First is the leftmost in-signal followed by the right in-signals;
after the right out-signals, finally there comes the leftmost out-signal.}
\begin{verbatim}
void pan_tilde_dsp(t_pan_tilde *x, t_signal **sp)
{
dsp_add(pan_tilde_perform, 5, x,
sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
}
\end{verbatim}
\verb+dsp_add+ adds a {\em perform}-routine (as declared in the first argument)
to the DSP-tree.
The second argument is the number of the following pointers to diverse variables.
Which pointers to which variables are passed is not limited.
Here, sp[0] is the first in-signal, sp[1] represents the second in-signal and
sp[3] points to the out-signal.
The structure \verb+t_signal+ contains a pointer to the
its signal-vector \verb+().s_vec+ (an array of samples of type \verb+t_sample+),
and the length of this signal-vector \verb+().s_n+.
Since all signal vectors of a patch (not including it's sub-patches) are of the same length,
it is sufficient to get the length of one of these vectors.
\subsection{perform-routine}
The perform-routine is the DSP-heart of each signal class.
A pointer to an integer-array is passed to it.
This array contains the pointers, that were passed via \verb+dsp_add+,
which must be casted back to their real type.
The perform-routine has to return a pointer to integer,
that points to the address behind the stored pointers of the routine.
This means, that the return argument equals the
argument of the perform-routine plus
the number of pointer variables (as declared as the second argument
of \verb+dsp_add+) plus one.
\begin{verbatim}
t_int *pan_tilde_perform(t_int *w)
{
t_pan_tilde *x = (t_pan_tilde *)(w[1]);
t_sample *in1 = (t_sample *)(w[2]);
t_sample *in2 = (t_sample *)(w[3]);
t_sample *out = (t_sample *)(w[4]);
int n = (int)(w[5]);
t_sample f_pan = (x->f_pan<0)?0.0:(x->f_pan>1)?1.0:x->f_pan;
while (n--) *out++ = (*in1++)*(1-f_pan)+(*in2++)*f_pan;
return (w+6);
}
\end{verbatim}
Each sample of the signal vectors is read and manipulated in the \verb+while+-loop.
Optimisation of the DSP-tree tries to avoid unnecessary copy-operations.
Therefore it is possible, that in- and out-signal are located
at the same address in the memory.
In this case, the programmer has to be careful not to write into the out-signal
before having read the in-signal to avoid overwriting data that is not yet saved.
\subsection{the code: \tt pan\~\/}
\begin{verbatim}
#include "m_pd.h"
static t_class *pan_tilde_class;
typedef struct _pan_tilde {
t_object x_obj;
t_sample f_pan;
t_sample f;
} t_pan_tilde;
t_int *pan_tilde_perform(t_int *w)
{
t_pan_tilde *x = (t_pan_tilde *)(w[1]);
t_sample *in1 = (t_sample *)(w[2]);
t_sample *in2 = (t_sample *)(w[3]);
t_sample *out = (t_sample *)(w[4]);
int n = (int)(w[5]);
t_sample f_pan = (x->f_pan<0)?0.0:(x->f_pan>1)?1.0:x->f_pan;
while (n--) *out++ = (*in1++)*(1-f_pan)+(*in2++)*f_pan;
return (w+6);
}
void pan_tilde_dsp(t_pan_tilde *x, t_signal **sp)
{
dsp_add(pan_tilde_perform, 5, x,
sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n);
}
void *pan_tilde_new(t_floatarg f)
{
t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class);
x->f_pan = f;
inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
floatinlet_new (&x->x_obj, &x->f_pan);
outlet_new(&x->x_obj, &s_signal);
return (void *)x;
}
void pan_tilde_setup(void) {
pan_tilde_class = class_new(gensym("pan~"),
(t_newmethod)pan_tilde_new,
0, sizeof(t_pan_tilde),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addmethod(pan_tilde_class,
(t_method)pan_tilde_dsp, gensym("dsp"), 0);
CLASS_MAINSIGNALIN(pan_tilde_class, t_pan_tilde, f);
}
\end{verbatim}
\vfill
\newpage
\begin{appendix}
\section{Pd's message-system}
Non-audio-data are distributed via a message-system.
Each message consists of a ``selector'' and a list of atoms.
\subsection{atoms}
There are three kinds of atoms:
\begin{itemize}
\item {\em A\_FLOAT}: a numerical value (floating point)
\item {\em A\_SYMBOL}: a symbolic value (string)
\item {\em A\_POINTER}: a pointer
\end{itemize}
Numerical values are always floating point-values (\verb+t_float+),
even if they could be displayed as integer values.
Each symbol is stored in a lookup-table for reasons of performance.
The command \verb+gensym+ looks up a string in the lookup-table and
returns the address of the symbol.
If the string is not yet to be found in the table,
a new symbol is added.
Atoms of type {\em A\_POINTER} are not very important (for simple externals).
The type of an atom \verb+a+ is stored in the structure-element \verb+a.a_type+.
\subsection{selectors}
The selector is a symbol that defines the type of a message.
There are five predefined selectors:
\begin{itemize}
\item ``{\tt bang}'' labels a trigger event.
A ``bang''-message consists only of the selector and contains no lists of atoms.
\item ``{\tt float}'' labels a numerical value.
The list of a ``float''-Message contains one single atom of type {\em A\_FLOAT}
\item ``{\tt symbol}'' labels a symbolic value.
The list of a ``symbol''-Message contains one single atom of type {\em A\_SYMBOL}
\item ``{\tt pointer}'' labels a pointer value.
The list of a ``pointer''-Message contains one single atom of type {\em A\_POINTER}
\item ``{\tt list}'' labels a list of one or more atoms of arbitrary type.
\end{itemize}
Since the symbols for these selectors are used quite often,
their address in the lookup-table can be queried directly,
without having to use \verb+gensym+:
\begin{tabular}{l||l|l}
selector&lookup-routine&lookup-address\\
\hline
\tt bang &\verb+gensym("bang")+ & \verb+&s_bang+ \\
\tt float &\verb+gensym("float")+ & \verb+&s_float+ \\
\tt symbol &\verb+gensym("symbol")+ & \verb+&s_symbol+ \\
\tt pointer &\verb+gensym("pointer")+ & \verb+&s_pointer+ \\
\tt list &\verb+gensym("list")+ & \verb+&s_list+ \\
--- (signal) &\verb+gensym("signal")+&\verb+&s_symbol+
\end{tabular}
Other selectors can be used as well.
The receiving class has to provide a method for a specifique selector
or for ``anything'', which is any arbitrary selector.
Messages that have no explicit selector and start with a numerical value,
are recognised automatically either as ``float''-message (only one atom) or as
``list''-message (several atoms).
For example, messages ``\verb+12.429+'' and ``\verb+float 12.429+'' are identical.
Likewise, the messages ``\verb+list 1 for you+'' is identical to ``\verb+1 for you+''.
\section{Pd-types}
Since Pd is used on several platforms,
many ordinary types of variables, like \verb|int|, are re-defined.
To write portable code, it is reasonable to use types provided by Pd.
Apart from this there are many predefined types,
that should make the life of the programmer simpler.
Generally, Pd-types start with \verb|t_|.
\begin{tabular}{c|l}
Pd-type & description \\
\hline\hline
\verb+t_atom+& atom \\
\verb+t_float+ & floating point value\\
\verb+t_symbol+ & symbol \\
\verb+t_gpointer+ & pointer (to graphical objects) \\
\hline
\verb+t_int+ & integer value \\
\verb+t_signal+ & structure of a signal \\
\verb+t_sample+ & audio signal-value (floating point)\\
\verb+t_outlet+ & outlet of an object \\
\verb+t_inlet+ & inlet of an object \\
\verb+t_object+ & object-interna \\
\hline
\verb+t_class+ & a Pd-class \\
\verb+t_method+ & class-method \\
\verb+t_newmethod+ & pointer to a constructor (new-routine) \\
\end{tabular}
\section{important functions in ``m\_pd.h''}
\subsection{functions: atoms}
\subsubsection{SETFLOAT}
\begin{verbatim}
SETFLOAT(atom, f)
\end{verbatim}
This macro sets the type of \verb+atom+ to \verb+A_FLOAT+
and stores the numerical value \verb+f+ in this atom.
\subsubsection{SETSYMBOL}
\begin{verbatim}
SETSYMBOL(atom, s)
\end{verbatim}
This macro sets the type of \verb+atom+ to \verb+A_SYMBOL+
and stores the symbolic pointer \verb+s+ in this atom.
\subsubsection{SETPOINTER}
\begin{verbatim}
SETPOINTER(atom, pt)
\end{verbatim}
This macro sets the type of \verb+atom+ to \verb+A_POINTER+
and stores the pointer \verb+pt+ in this atom.
\subsubsection{atom\_getfloat}
\begin{verbatim}
t_float atom_getfloat(t_atom *a);
\end{verbatim}
If the type of the atom \verb+a+ is \verb+A_FLOAT+,
the numerical value of this atom else ``0.0'' is returned.
\subsubsection{atom\_getfloatarg}
\begin{verbatim}
t_float atom_getfloatarg(int which, int argc, t_atom *argv)
\end{verbatim}
If the type of the atom -- that is found at in the atom-list
\verb+argv+ with the length \verb+argc+ at the place \verb+which+ --
is \verb+A_FLOAT+, the numerical value of this atom else ``0.0'' is returned.
\subsubsection{atom\_getint}
\begin{verbatim}
t_int atom_getint(t_atom *a);
\end{verbatim}
If the type of the atom \verb+a+ is \verb+A_FLOAT+,
its numerical value is returned as integer else ``0'' is returned.
\subsubsection{atom\_getsymbol}
\begin{verbatim}
t_symbol atom_getsymbol(t_atom *a);
\end{verbatim}
If the type of the atom \verb+a+ is \verb+A_SYMBOL+,
a pointer to this symbol is returned, else a null-pointer ``0'' is returned.
\subsubsection{atom\_gensym}
\begin{verbatim}
t_symbol *atom_gensym(t_atom *a);
\end{verbatim}
If the type of the atom \verb+a+ is \verb+A_SYMBOL+,
a pointer to this symbol is returned.
Atoms of a different type, are ``reasonably'' converted into a string.
This string is -- on demand -- inserted into the symbol-table.
A pointer to this symbol is returned.
\subsubsection{atom\_string}
\begin{verbatim}
void atom_string(t_atom *a, char *buf, unsigned int bufsize);
\end{verbatim}
Converts an atom \verb+a+ into a {\tt C}-string \verb+buf+.
The memory to this char-Buffer has to be reserved manually and
its length has to be declared in \verb+bufsize+.
\subsubsection{gensym}
\begin{verbatim}
t_symbol *gensym(char *s);
\end{verbatim}
Checks, whether the C-string \verb+*s+ has already been inserted into the symbol-table.
If no entry exists, it is created.
A pointer to the symbol is returned.
\subsection{functions: classes}
\subsubsection{class\_new}
\begin{verbatim}
t_class *class_new(t_symbol *name,
t_newmethod newmethod, t_method freemethod,
size_t size, int flags,
t_atomtype arg1, ...);
\end{verbatim}
Generates a class with the symbolic name \verb+name+.
\verb+newmethod+ is the constructor that creates an instance of the class and
returns a pointer to this instance.
If memory is reserved dynamically, this memory has to be freed by the
destructor-method \verb+freemethod+ (without any return argument),
when the object is destroyed.
\verb+size+ is the static size of the class-data space,
that is returned by \verb+sizeof(t_mydata)+.
\verb+flags+ define the presentation of the graphical object.
A (more or less arbitrary) combination of following objects is possible:
\begin{tabular}{l|l}
flag&description\\
\hline
\verb+CLASS_DEFAULT+ & a normal object with one inlet \\
\verb+CLASS_PD+ & \em object (without graphical presentation) \\
\verb+CLASS_GOBJ+ & \em pure graphical object (like arrays, graphs,...)\\
\verb+CLASS_PATCHABLE+ & \em a normal object (with one inlet) \\
\verb+CLASS_NOINLET+ & the default inlet is suppressed \\
\end{tabular}
Flags the description of which is printed in {\em italic}
are of small importance for writing externals.
The remaining arguments \verb+arg1,...+ define the
types of object-arguments passed at the creation of a class-object.
A maximum of six type checked arguments can be passed to an object.
The list of argument-types are terminated by ``0''.
Possible types of arguments are:
\begin{tabular}{l|l}
\verb+A_DEFFLOAT+ & a numerical value \\
\verb+A_DEFSYMBOL+ & a symbolical value \\
\verb+A_GIMME+ & a list of atoms of arbitrary length and types \\
\end{tabular}
If more than six arguments are to be passed,
\verb+A_GIMME+ has to be used and a manual type-check has to be made.
\subsubsection{class\_addmethod}
\begin{verbatim}
void class_addmethod(t_class *c, t_method fn, t_symbol *sel,
t_atomtype arg1, ...);
\end{verbatim}
Adds a method \verb+fn+ for a selector \verb+sel+ to a class \verb+c+.
The remaining arguments \verb+arg1,...+ define the
types of the list of atoms that follow the selector.
A maximum of six type-checked arguments can be passed.
If more than six arguments are to be passed,
\verb+A_GIMME+ has to be used and a manual type-check has to be made.
The list of arguments is terminated by ``0''.
Possible types of arguments are:
\begin{tabular}{l|l}
\verb+A_DEFFLOAT+ & a numerical value \\
\verb+A_DEFSYMBOL+ & a symbolical value \\
\verb+A_POINTER+ & a pointer \\
\verb+A_GIMME+ & a list of atoms of arbitrary length and types \\
\end{tabular}
\subsubsection{class\_addbang}
\begin{verbatim}
void class_addbang(t_class *c, t_method fn);
\end{verbatim}
Adds a method \verb+fn+ for ``bang''-messages to the class \verb+c+.
The argument of the ``bang''-method is a pointer to the class-data space:
\verb+void my_bang_method(t_mydata *x);+
\subsubsection{class\_addfloat}
\begin{verbatim}
void class_addfloat(t_class *c, t_method fn);
\end{verbatim}
Adds a method \verb+fn+ for ``float''-messages to the class \verb+c+.
The arguments of the ``float''-method is a pointer to the class-data space and
a floating point-argument:
\verb+void my_float_method(t_mydata *x, t_floatarg f);+
\subsubsection{class\_addsymbol}
\begin{verbatim}
void class_addsymbol(t_class *c, t_method fn);
\end{verbatim}
Adds a method \verb+fn+ for ``symbol''-messages to the class \verb+c+.
The arguments of the ``symbol''-method is a pointer to the class-data space and
a pointer to the passed symbol:
\verb+void my_symbol_method(t_mydata *x, t_symbol *s);+
\subsubsection{class\_addpointer}
\begin{verbatim}
void class_addpointer(t_class *c, t_method fn);
\end{verbatim}
Adds a method \verb+fn+ for ``pointer''-messages to the class \verb+c+.
The arguments of the ``pointer''-method is a pointer to the class-data space and
a pointer to a pointer:
\verb+void my_pointer_method(t_mydata *x, t_gpointer *pt);+
\subsubsection{class\_addlist}
\begin{verbatim}
void class_addlist(t_class *c, t_method fn);
\end{verbatim}
Adds a method \verb+fn+ for ``list''-messages to the class \verb+c+.
The arguments of the ``list''-method are -- apart from a pointer to the class-data space --
a pointer to the selector-symbol (always \verb+&s_list+),
the number of atoms and a pointer to the list of atoms:
\verb+void my_list_method(t_mydata *x,+
\verb+ t_symbol *s, int argc, t_atom *argv);+
\subsubsection{class\_addanything}
\begin{verbatim}
void class_addanything(t_class *c, t_method fn);
\end{verbatim}
Adds a method \verb+fn+ for an arbitrary message to the class \verb+c+.
The arguments of the anything-method are -- apart from a pointer to the class-data space --
a pointer to the selector-symbol,
the number of atoms and a pointer to the list of atoms:
\verb+void my_any_method(t_mydata *x,+
\verb+ t_symbol *s, int argc, t_atom *argv);+
\subsubsection{class\_addcreator}
\begin{verbatim}
void class_addcreator(t_newmethod newmethod, t_symbol *s,
t_atomtype type1, ...);
\end{verbatim}
Adds a creator-symbol \verb+s+, alternative to the symbolic class name,
to the constructor \verb+newmethod+.
Thus, objects can be created either by their ``real'' class name or
an alias-name (p.e. an abbreviation, like the internal ``float'' resp. ``f'').
The ``0''-terminated list of types corresponds to that of \verb+class_new+.
\subsubsection{class\_sethelpsymbol}
\begin{verbatim}
void class_sethelpsymbol(t_class *c, t_symbol *s);
\end{verbatim}
If a Pd-object is right-clicked, a help-patch for the corresponding object class
can be opened.
By default this is a patch with the symbolic class name in the
directory ``{\em doc/5.reference/}''.
The name of the help-patch for the class that is pointed to by \verb+c+
is changed to the symbol \verb+s+.
Therefore, several similar classes can share a single help-patch.
Path-information is relative to the default help path {\em doc/5.reference/}.
\subsubsection{pd\_new}
\begin{verbatim}
t_pd *pd_new(t_class *cls);
\end{verbatim}
Generates a new instance of the class \verb+cls+ and
returns a pointer to this instance.
\subsection{functions: inlets and outlets}
All routines for inlets and outlets need a reference to the object-interna of
the class-instance.
When instantiating a new object,
the necessary data space-variable of the \verb+t_object+-type is initialised.
This variable has to be passed as the \verb+owner+-object to the
various inlet- and outlet-routines.
\subsubsection{inlet\_new}
\begin{verbatim}
t_inlet *inlet_new(t_object *owner, t_pd *dest,
t_symbol *s1, t_symbol *s2);
\end{verbatim}
Generates an additional ``active'' inlet for the object
that is pointed at by \verb+owner+.
Generally, \verb+dest+ points at ``\verb+owner.ob_pd+''.
The selector \verb+s1+ at the new inlet is substituted by the selector \verb+s2+.
If a message with selector \verb+s1+ appears at the new inlet,
the class-method for the selector \verb+s2+ is called.
This means
\begin{itemize}
\item The substituting selector has to be declared by \verb+class_addmethod+
in the setup-routine.
\item It is possible to simulate a certain right inlet, by sending a message with
this inlet's selector to the leftmost inlet.
Using an empty symbol (\verb+gensym("")+) as selector
makes it impossible to address a right inlet via the leftmost one.
\item It is not possible to add methods for more than one selector to a right inlet.
Particularly it is not possible to add a universal method for arbitrary selectors to
a right inlet.
\end{itemize}
\subsubsection{floatinlet\_new}
\begin{verbatim}
t_inlet *floatinlet_new(t_object *owner, t_float *fp);
\end{verbatim}
Generates a new ``passive'' inlet for the object that is pointed at by \verb+owner+.
This inlet enables numerical values to be written directly into the
memory \verb+fp+, without calling a dedicated method.
\subsubsection{symbolinlet\_new}
\begin{verbatim}
t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp);
\end{verbatim}
Generates a new ``passive'' inlet for the object that is pointed at by \verb+owner+.
This inlet enables symbolic values to be written directly into the
memory \verb+*sp+, without calling a dedicated method.
\subsubsection{pointerinlet\_new}
\begin{verbatim}
t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp);
\end{verbatim}
Generates a new ``passive'' inlet for the object that is pointed at by \verb+owner+.
This inlet enables pointer to be written directly into the
memory \verb+gp+, without calling a dedicated method.
\subsubsection{outlet\_new}
\begin{verbatim}
t_outlet *outlet_new(t_object *owner, t_symbol *s);
\end{verbatim}
Generates a new outlet for the object that is pointed at by \verb+owner+.
The Symbol \verb+s+ indicates the type of the outlet.
\begin{tabular}{c|l||l}
symbol & symbol-address & outlet-type \\
\hline\hline
``bang'' & \verb+&s_bang+ & message (bang)\\
``float'' & \verb+&s_float+ & message (float)\\
``symbol'' & \verb+&s_symbol+ & message (symbol) \\
``pointer'' & \verb+&s_gpointer+ & message (pointer)\\
``list'' & \verb+&s_list+ & message (list) \\
--- & 0 & message \\
\hline
``signal'' & \verb+&s_signal+ & signal \\
\end{tabular}
There are no real differences between outlets of the various message-types.
At any rate, it makes code more easily readable,
if the use of outlet is shown at creation-time.
For outlets for any messages a null-pointer is used.
Signal-outlet must be declared with \verb+&s_signal+.
Variables if the type \verb+t_object+ provide pointer to one outlet.
Whenever a new outlet is generated, its address is stored in the
object variable \verb+(*owner).ob_outlet+.
If more than one message-outlet is needed,
the outlet-pointers that are returned by \verb+outlet_new+
have to be stored manually in the data space
to address the given outlets.
\subsubsection{outlet\_bang}
\begin{verbatim}
void outlet_bang(t_outlet *x);
\end{verbatim}
Outputs a ``bang''-message at the outlet specified by \verb+x+.
\subsubsection{outlet\_float}
\begin{verbatim}
void outlet_float(t_outlet *x, t_float f);
\end{verbatim}
Outputs a ``float''-message with the numeric value \verb+f+
at the outlet specified by \verb+x+.
\subsubsection{outlet\_symbol}
\begin{verbatim}
void outlet_symbol(t_outlet *x, t_symbol *s);
\end{verbatim}
Outputs a ``symbol''-message with the symbolic value \verb+s+
at the outlet specified by \verb+x+.
\subsubsection{outlet\_pointer}
\begin{verbatim}
void outlet_pointer(t_outlet *x, t_gpointer *gp);
\end{verbatim}
Outputs a ``pointer''-message with the pointer \verb+gp+
at the outlet specified by \verb+x+.
\subsubsection{outlet\_list}
\begin{verbatim}
void outlet_list(t_outlet *x,
t_symbol *s, int argc, t_atom *argv);
\end{verbatim}
Outputs a ``list''-message at the outlet specified by \verb+x+.
The list contains \verb+argc+ atoms.
\verb+argv+ points to the first element of the atom-list.
Independent of the symbol \verb+s+, the selector ``list'' will precede
the list.
To make the code more readable,
\verb+s+ should point to the symbol list
(either via \verb+gensym("list")+ or via \verb+&s_list+)
\subsubsection{outlet\_anything}
\begin{verbatim}
void outlet_anything(t_outlet *x,
t_symbol *s, int argc, t_atom *argv);
\end{verbatim}
Outputs a message at the outlet specified by \verb+x+.
The message-selector is specified with \verb+s+.
It is followed by \verb+argc+ atoms.
\verb+argv+ points to the first element of the atom-list.
\subsection{functions: DSP}
If a class should provide methods for digital signal-processing,
a method for the selector ``dsp'' (followed by no atoms)
has to be added to this class
Whenever Pd's audio engine is started,
all objects that provide a ``dsp''-method are identified as instances of signal classes.
\paragraph{DSP-method}
\begin{verbatim}
void my_dsp_method(t_mydata *x, t_signal **sp)
\end{verbatim}
In the ``dsp''-method a class method for signal-processing
is added to the DSP-tree by the function \verb+dsp_add+.
Apart from the data space \verb+x+ of the object,
an array of signals is passed.
The signals in the array are arranged in such a way,
that they can be read in the graphical representation of the object
clockwisely.
In case there are both two in- and out-signals, this means:
\begin{tabular}{c|r}
pointer & to signal \\
\hline\hline
sp[0] & left in-signal \\
sp[1] & right in-signal \\
sp[2] & right out-signal \\
sp[3] & left out-signal \\
\end{tabular}
The signal structure contains apart from other things:
\begin{tabular}{c|l}
structure-element & description \\
\hline
\verb+s_n+ & length of the signal vector \\
\verb+s_vec+ & pointer to the signal vector \\
\end{tabular}
The signal vector is an array of samples of type \verb+t_sample+.
\paragraph{perform-routine}
\begin{verbatim}
t_int *my_perform_routine(t_int *w)
\end{verbatim}
A pointer \verb+w+ to an array (of integer) is passed to
the perform-routine that is inserted into the DSP-tree by \verb+class_add+.
In this array the pointers that are passed via \verb+dsp_add+ are stored.
These pointers have to be casted back to their original type.
The first pointer is stored at \verb+w[1]+ !!!
The perform-routine has to return a pointer to integer,
that points directly behind the memory, where the object's pointers are stored.
This means, that the return-argument equals the routine's argument \verb+w+
plus the number of used pointers
(as defined in the second argument of \verb+dsp_add+) plus one.
\subsubsection{CLASS\_MAINSIGNALIN}
\begin{verbatim}
CLASS_MAINSIGNALIN(<class_name>, <class_data>, <f>);
\end{verbatim}
The macro \verb+CLASS_MAINSIGNALIN+ declares,
that the class will use signal-inlets.
The first macro-argument is a pointer to the signal-class.
The second argument is the type of the class-data space.
The third argument is a (dummy-)floating point-variable of the data space,
that is needed to automatically convert ``float''-messages into signals
if no signal is present at the signal-inlet.
No ``float''-methods can be used for signal-inlets, that are created this way.
\subsubsection{dsp\_add}
\begin{verbatim}
void dsp_add(t_perfroutine f, int n, ...);
\end{verbatim}
Adds the perform-routine \verb+f+ to the DSP-tree.
The perform-routine is called at each DSP-cycle.
The second argument\verb+n+ defines the number of following pointer-arguments
Which pointers to which data are passes is not limited.
Generally, pointers to the data space of the object and to the
signal-vectors are reasonable.
The length of the signal-vectors should also be passed to manipulate signals effectively.
\subsubsection{sys\_getsr}
\begin{verbatim}
float sys_getsr(void);
\end{verbatim}
Returns the sampler ate of the system.
\subsection{functions: memory}
\subsubsection{getbytes}
\begin{verbatim}
void *getbytes(size_t nbytes);
\end{verbatim}
Reserves \verb+nbytes+ bytes and returns a pointer to the allocated memory.
\subsubsection{copybytes}
\begin{verbatim}
void *copybytes(void *src, size_t nbytes);
\end{verbatim}
Copies \verb+nbytes+ bytes from \verb+*src+ into a newly allocated memory.
The address of this memory is returned.
\subsubsection{freebytes}
\begin{verbatim}
void freebytes(void *x, size_t nbytes);
\end{verbatim}
Frees \verb+nbytes+ bytes at address \verb+*x+.
\subsection{functions: output}
\subsubsection{post}
\begin{verbatim}
void post(char *fmt, ...);
\end{verbatim}
Writes a {\tt C}-string to the standard error (shell).
\subsubsection{error}
\begin{verbatim}
void error(char *fmt, ...);
\end{verbatim}
Writes a {\tt C}-string as an error-message to the standard error (shell).
The object that has output the error-message is marked and
can be identified via the Pd-menu {\em Find->Find last error}.
\end{appendix}
\end{document}
"HOWTO write Externals for Pure data" by IOhannes m zmölnig
This work consists of a HOWTO-text (possibly in various translations) and
accompanying material (example source code files, Makefiles,...)
Any source-code examples within the HOWTO-text are regarded as accompanying material.
1. License for the HOWTO-text:
==============================
Copyright (c) 2001-2006 IOhannes m zmölnig.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
Texts. A copy of the license is included in the section entitled "GNU
Free Documentation License" of this document.
2. License for the accompanying material (examples,...)
=======================================================
This software is copyrighted by IOhannes m zmölnig. The following
terms (the "Standard Improved BSD License") apply to all files associated with
the software unless explicitly disclaimed in individual files:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
3. The name of the author may not be used to endorse or promote
products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
=======================================================
=======================================================
=======================================================
GNU Free Documentation License
Version 1.2, November 2002
Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
0. PREAMBLE
The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
1. APPLICABILITY AND DEFINITIONS
This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
2. VERBATIM COPYING
You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and you may publicly display copies.
3. COPYING IN QUANTITY
If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
4. MODIFICATIONS
You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
* A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
* B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
* C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
* D. Preserve all the copyright notices of the Document.
* E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
* F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
* G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
* H. Include an unaltered copy of this License.
* I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
* J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
* K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
* L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
* M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
* N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
* O. Preserve any Warranty Disclaimers.
If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
5. COMBINING DOCUMENTS
You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements."
6. COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
7. AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
8. TRANSLATION
Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
9. TERMINATION
You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
10. FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
HOWTO_EN=HOWTO-externals-en
HOWTO_DE=HOWTO-externals-de
HOWTO_EXAMPLES=example1 example2 example3 example4
HTMLDIR_EN=HOWTO
HTMLDIR_DE=HOWTO-de
LATEX=latex
DVIPS=dvips
DVIPDF=dvipdf
LATEX2HTML=latex2html
default: en_pdf
TARGETS: default \
en_ps en_pdf en_html de_ps de_pdf de_html ps pdf html \
clean cleaner distclean \
examples $(HOWTO_EXAMPLES)
.PHONY: $(TARGETS)
en_ps: $(HOWTO_EN).ps
en_pdf: $(HOWTO_EN).pdf
en_html:
mkdir -p ${HTMLDIR_EN}
$(LATEX2HTML) -dir $(HTMLDIR_EN) -split 4 $(HOWTO_EN).tex
de_ps: $(HOWTO_DE).ps
de_pdf: $(HOWTO_DE).pdf
de_html:
mkdir -p ${HTMLDIR_DE}
$(LATEX2HTML) -dir $(HTMLDIR_DE) -split 4 $(HOWTO_DE).tex
ps: en_ps de_ps
pdf: en_pdf de_pdf
html: en_html de_html
clean:
-rm -f *.aux *.log *.toc *.dvi *~
cleaner: clean
-rm -f *.ps *.pdf
-rm -rf $(HTMLDIR_EN) $(HTMLDIR_DE)
distclean: cleaner
@for d in ${HOWTO_EXAMPLES}; do ${MAKE} -C $$d clean; done
%.dvi:
$(LATEX) $*.tex
$(LATEX) $*.tex
%.ps: %.dvi
$(DVIPS) $*.dvi
%.pdf: %.dvi
$(DVIPDF) $*.dvi
examples: $(HOWTO_EXAMPLES)
echo made examples
$(HOWTO_EXAMPLES):
$(MAKE) -C $@
# Makefile
# (c) 2006 IOhannes m zmlnig
# path to pd
## change this according to your setup!
PDROOT=../../../../pd
#PDROOT=/home/zmoelnig/src/pd/
# here we find the sources of pd (and evtl. the pd.lib)
PDSRCDIR=$(PDROOT)/src
PDLIBDIR=$(PDROOT)/bin
# this is the filename-extension
# people have to specify it at the cmdline: eg "make pd_linux"
EXTENSION=$(MAKECMDGOALS)
# if no filename-extension is supplied by the user
# try to guess one, based on what "uname" tells us
UNAME := $(shell uname -s)
ifeq ($(UNAME),Linux)
DEFAULTEXTENSION= pd_linux
else
ifeq ($(UNAME),Darwin)
DEFAULTEXTENSION= pd_darwin
else
ifeq (MINGW,$(findstring MINGW,$(UNAME)))
DEFAULTEXTENSION= pd_nt
else
ifeq ($(UNAME),IRIX)
UNAMEV := $(shell uname -R)
ifeq (6.,$(findstring 6.,$(UNAMEV)))
DEFAULTEXTENSION= pd_irix6
else
DEFAULTEXTENSION= pd_irix5
endif
else
DEFAULTEXTENSION=help
endif
endif
endif
endif
# if no extension is given, call "make" again with a guessed extension
auto:
make $(DEFAULTEXTENSION)
# just a stupid fallback
help:
@echo "choose one command: make pd_linux (linux), make pd_darwin (osX), make pd_irix5 (IRIX5), make pd_irix6 (IRIX6), make dll (MSVC), make pd_nt (MinWG)"
# delete old build files
clean:
-rm -f *.dll *.pd_* *.o *.obj *~
# we want to compile all C-files we find in the current directory
SOURCES=$(sort $(filter %.c, $(wildcard *.c)))
# each C-files maps will become an external with the given filename-extension
TARGETS=$(SOURCES:.c=.$(EXTENSION))
# ----------------------- Linux -----------------------
pd_linux: $(TARGETS)
LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer -fPIC \
-Wall -W -Wshadow -Wstrict-prototypes -Werror \
-Wno-unused -Wno-parentheses -Wno-switch
LINUXLDFLAGS = -export_dynamic -shared -lc -lm
LINUXINCLUDE = -I$(PDSRCDIR)
%.pd_linux: %.c
$(CC) $(LINUXLDFLAGS) $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.pd_linux $*.c
strip --strip-unneeded $*.pd_linux
# ----------------------- Mac OSX -----------------------
pd_darwin: $(TARGETS)
DARWINCFLAGS = -DPD -O2 -Wall -W -Wshadow -Wstrict-prototypes \
-Wno-unused -Wno-parentheses -Wno-switch
DARWININCLUDE = -I$(PDSRCDIR)
DARWINLDFLAGS = -bundle -undefined suppress -flat_namespace
%.pd_darwin: %.c
$(CC) $(DARWINCFLAGS) $(DARWININCLUDE) $(DARWINLDFLAGS) -o $*.pd_darwin $*.c
# ----------------------- IRIX 5.x -----------------------
pd_irix5: $(TARGETS)
SGICFLAGS5 = -o32 -DPD -DSGI -O2
SGIINCLUDE = -I$(PDSRCDIR)
SGILDFLAGS = -elf -shared -rdata_shared
%.pd_irix5: %.c
$(CC) $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c
$(LD) $(SGILDFLAGS) -o $*.pd_irix5 $*.o
rm $*.o
# ----------------------- IRIX 6.x -----------------------
pd_irix6: $(TARGETS)
SGICFLAGS6 = -DPD -DSGI -n32 \
-OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
-Ofast=ip32
%.pd_irix6: %.c
$(CC) $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c
$(LD) $(SGILDFLAGS) -o $*.pd_irix6 $*.o
rm $*.o
# ----------------------- NT -----------------------
dll: $(TARGETS)
PDNTCFLAGS = /W3 /WX /DPD /DNT /D__WIN32__ /DMSW /nologo
VC="C:\Programme\Microsoft Visual Studio\Vc98"
PDNTINCLUDE = /I. /I$(PDROOT)\tcl\include /I$(PDSRCDIR)\src /I$(VC)\include
PDNTLDIR = $(VC)\lib
PDNTLIB = $(PDNTLDIR)\libc.lib \
$(PDNTLDIR)\oldnames.lib \
$(PDNTLDIR)\kernel32.lib \
$(PDLIBDIR)\pd.lib
%.dll: %.c
cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c
link /dll /export:$*_setup $*.obj $(PDNTLIB)
pd_nt: $(TARGETS)
MINGWCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer \
-Wall -W -Wshadow -Wstrict-prototypes -Werror \
-Wno-unused -Wno-parentheses -Wno-switch -mms-bitfields
MINGWLDFLAGS = -export_dynamic -shared -lm -lkernel32 -lcoldname -lcrtdll -lpd -L$(PDLIBDIR)
MINGWINCLUDE = -I$(PDSRCDIR)
%.pd_nt: %.c
$(CC) $(MINGWLDFLAGS) $(MINGWCFLAGS) $(MINGWINCLUDE) -o $*.dll $*.c