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 5918 deletions
#N canvas 430 37 555 619 10;
#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
14 -228856 -66577 0;
#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header vcf~ 3 12 0 18 -204280
-1 0;
#X obj 0 376 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
-228856 -1 0;
#N canvas 485 226 494 409 META 0;
#X text 12 165 LIBRARY internal;
#X text 12 205 WEBSITE http://crca.ucsd.edu/~msp/;
#X text 12 25 LICENSE SIBSD;
#X text 12 185 AUTHOR Miller Puckette;
#X text 12 245 HELP_PATCH_AUTHORS Updated for Pd version 0.35. Jonathan
Wilkes revised the patch to conform to the PDDP template for Pd version
0.42.;
#X text 12 5 KEYWORDS signal filter;
#X text 12 45 DESCRIPTION voltage-controlled bandpass filter;
#X text 12 65 INLET_0 signal;
#X text 12 105 INLET_2 float;
#X text 12 85 INLET_1 signal;
#X text 12 125 OUTLET_0 signal;
#X text 12 145 OUTLET_1 signal;
#X text 12 225 RELEASE_DATE 1997;
#X restore 500 597 pd META;
#X obj 0 468 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
13 -228856 -1 0;
#X obj 0 525 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
0 13 -228856 -1 0;
#X obj 0 566 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
0 13 -228856 -1 0;
#N canvas 216 524 428 109 Related_objects 0;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X obj 22 43 bp~;
#X text 8 2 [vcf~] Related Objects;
#X restore 102 597 pd Related_objects;
#X obj 78 385 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X obj 78 477 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X obj 78 413 cnv 17 3 17 empty \$0-pddp.cnv.let.1 1 5 9 0 16 -228856
-162280 0;
#X obj 501 9 vcf~;
#X obj 78 440 cnv 17 3 17 empty \$0-pddp.cnv.let.2 2 5 9 0 16 -228856
-162280 0;
#X text 98 439 float;
#X text 98 476 signal;
#X text 98 384 signal;
#X text 98 412 signal;
#X obj 261 141 sig~;
#X text 68 141 test signal;
#X text 125 235 amp in (db);
#X text 116 96 test frequency;
#X text 248 233 amp out (db);
#X text 230 96 center frequency;
#X text 344 140 q;
#X floatatom 146 120 5 0 0 0 - - -, f 5;
#X floatatom 261 120 5 0 0 0 - - -, f 5;
#X obj 146 141 osc~;
#X floatatom 308 141 5 0 0 0 - - -, f 5;
#X obj 146 190 env~ 8192;
#X obj 244 190 env~ 8192;
#X floatatom 146 212 5 0 0 0 - - -, f 5;
#X floatatom 244 212 5 0 0 0 - - -, f 5;
#X obj 244 168 vcf~ 1;
#X text 293 167 optional argument to initialize q;
#X text 11 23 voltage-controlled bandpass filter;
#X text 167 412 - center frequency \, which may change continuously
in time.;
#X text 167 439 - change the "Q \, " or filter sharpness.;
#X text 168 384 - the incoming signal to be filtered;
#X text 168 543 - [vcf~] takes one argument to initialize q.;
#X text 80 543 1) float;
#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
;
#X obj 478 58 pddp/dsp;
#X text 168 476 - the filtered signal (real part).;
#X obj 78 500 cnv 17 3 17 empty \$0-pddp.cnv.let.1 1 5 9 0 16 -228856
-162280 0;
#X text 98 499 signal;
#X text 168 499 - the filtered signal (imaginary part).;
#X text 76 283 [vcf~] is like [bp~] except that it takes an audio signal
to set center frequency \, which may thus change continuously in time.
The "Q" or filter sharpness is still only set by messages. More expensive
than bp~ in CPU time but more powerful too.;
#X connect 17 0 32 1;
#X connect 24 0 26 0;
#X connect 25 0 17 0;
#X connect 26 0 28 0;
#X connect 26 0 32 0;
#X connect 27 0 32 2;
#X connect 28 0 30 0;
#X connect 29 0 31 0;
#X connect 32 0 29 0;
#N canvas 432 36 558 455 10;
#X obj 1 1 cnv 8 100 60 empty empty vdial=vdl 20 20 1 18 -262144 -1109
0;
#X text 13 390 (c) musil@iem.kug.ac.at;
#X text 55 403 IEM KUG;
#X text 132 122 click properties to;
#X text 120 133 modify geometry \, colors \, etc.;
#X obj 159 261 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144
-1 -1;
#X obj 21 54 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
-1;
#X msg 41 319 \$1;
#X floatatom 41 341 4 0 0 0 - - -, f 4;
#X obj 41 363 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
-1;
#X obj 86 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
1;
#X obj 86 317 route 0 1 2 3 4 5 6 7 8 9;
#X msg 194 92 set \$1;
#X floatatom 194 71 4 0 9 0 - - -, f 4;
#X floatatom 44 54 4 0 9 0 - - -, f 4;
#X msg 91 41 7 0 -5.44;
#X msg 95 63 3 3 4.55;
#X obj 103 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
1;
#X obj 120 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
1;
#X obj 137 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
1;
#X obj 154 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
1;
#X obj 171 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
1;
#X obj 188 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
1;
#X obj 205 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
1;
#X obj 222 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
1;
#X obj 239 338 tgl 12 0 empty empty empty 8 -8 0 10 -262144 -1 -1 0
1;
#X obj 79 355 print;
#X floatatom 183 287 4 0 0 0 - - -, f 4;
#X msg 183 261 \$1;
#X msg 158 192 set \$1;
#X floatatom 158 171 4 0 9 0 - - -, f 4;
#X text 125 355 UP- \, DOWN- \, LEFT- or RIGHT-key;
#X text 124 366 for moving selected gui-objects;
#N canvas 230 247 699 530 edit 0;
#X obj 42 198 f;
#X msg 20 177 bang;
#X floatatom 58 176 3 63 156 0 - - -, f 3;
#X floatatom 93 198 3 -20 37 0 - - -, f 3;
#X obj 42 221 pack 0 0;
#X text 120 198 y-label;
#X text 86 176 x-label;
#X floatatom 270 187 3 8 50 0 - - -, f 3;
#X text 297 187 size;
#X obj 286 293 f;
#X msg 264 272 bang;
#X floatatom 302 271 3 -10 10 0 - - -, f 3;
#X floatatom 337 293 3 -10 10 0 - - -, f 3;
#X obj 286 316 pack 0 0;
#X obj 300 412 f;
#X msg 278 391 bang;
#X floatatom 316 390 3 20 60 0 - - -, f 3;
#X floatatom 351 412 3 100 200 0 - - -, f 3;
#X obj 300 435 pack 0 0;
#X text 330 271 x-delta;
#X text 364 293 y-delta;
#X text 344 390 x-position;
#X text 378 412 y-position;
#X obj 62 313 f;
#X msg 40 292 bang;
#X floatatom 78 291 3 0 2 0 - - -, f 3;
#X floatatom 113 313 3 4 36 0 - - -, f 3;
#X obj 62 336 pack 0 0;
#X text 106 291 font;
#X text 142 313 height;
#X text 504 293 no init;
#X text 475 348 init value on loadbang;
#X floatatom 482 228 5 2 20 0 - - -, f 5;
#X text 491 417 changing-behavior;
#X text 526 228 number of buttons;
#X obj 47 104 pack 0 0 0;
#X obj 47 76 f;
#X msg 24 28 bang;
#X floatatom 63 26 3 0 29 0 - - -, f 3;
#X floatatom 79 46 3 0 29 0 - - -, f 3;
#X floatatom 112 62 3 0 29 0 - - -, f 3;
#X text 91 26 background;
#X text 106 46 front-color;
#X text 140 63 label-color;
#X msg 285 25 back;
#X msg 285 45 front;
#X msg 285 65 label;
#X msg 247 25 bang;
#N canvas 15 207 606 448 RGB_____________ 0;
#X obj 97 56 inlet;
#X obj 262 53 inlet;
#X obj 339 55 inlet;
#X obj 405 56 inlet;
#X obj 97 270 bang;
#X msg 77 295 0;
#X msg 104 295 1;
#X obj 146 268 bang;
#X msg 132 295 0;
#X msg 160 295 1;
#X obj 196 269 bang;
#X msg 187 295 0;
#X msg 214 295 1;
#X obj 265 313 spigot;
#X obj 312 313 spigot;
#X obj 359 313 spigot;
#X obj 249 385 outlet;
#X text 93 33 select;
#X text 267 28 red;
#X text 337 30 green;
#X text 409 30 blue;
#X obj 405 102 t b f;
#X obj 339 160 +;
#X obj 339 185 t b f;
#X obj 339 216 +;
#X obj 296 385 outlet;
#X obj 343 385 outlet;
#X obj 28 180 loadbang;
#X obj 97 135 route back front label bang;
#X obj 343 362 f;
#X obj 296 361 f;
#X obj 249 361 f;
#X obj 262 79 * -65536;
#X obj 339 80 * -256;
#X obj 405 80 * -1;
#X obj 339 247 - 1;
#X obj 235 168 t b b b b;
#X connect 0 0 28 0;
#X connect 1 0 32 0;
#X connect 2 0 33 0;
#X connect 3 0 34 0;
#X connect 4 0 5 0;
#X connect 4 0 6 0;
#X connect 5 0 14 1;
#X connect 5 0 15 1;
#X connect 6 0 13 1;
#X connect 7 0 8 0;
#X connect 7 0 9 0;
#X connect 8 0 13 1;
#X connect 8 0 15 1;
#X connect 9 0 14 1;
#X connect 10 0 11 0;
#X connect 10 0 12 0;
#X connect 11 0 13 1;
#X connect 11 0 14 1;
#X connect 12 0 15 1;
#X connect 13 0 31 1;
#X connect 14 0 30 1;
#X connect 15 0 29 1;
#X connect 21 0 22 0;
#X connect 21 1 22 1;
#X connect 22 0 23 0;
#X connect 23 0 24 0;
#X connect 23 1 24 1;
#X connect 24 0 35 0;
#X connect 27 0 6 0;
#X connect 28 0 4 0;
#X connect 28 1 7 0;
#X connect 28 2 10 0;
#X connect 28 3 36 0;
#X connect 29 0 26 0;
#X connect 30 0 25 0;
#X connect 31 0 16 0;
#X connect 32 0 24 0;
#X connect 33 0 22 0;
#X connect 34 0 21 0;
#X connect 35 0 15 0;
#X connect 35 0 14 0;
#X connect 35 0 13 0;
#X connect 36 0 31 0;
#X connect 36 1 30 0;
#X connect 36 2 29 0;
#X connect 36 3 35 0;
#X restore 285 86 pd RGB_____________;
#X floatatom 327 55 3 0 255 0 - - -, f 3;
#X floatatom 370 55 3 0 255 0 - - -, f 3;
#X floatatom 413 56 3 0 255 0 - - -, f 3;
#X text 34 0 preset-colors;
#X text 296 -3 RGB-colors;
#X text 327 37 red;
#X text 363 36 green;
#X text 411 36 blue;
#X msg 47 125 \; foo12_rcv color \$1 \$2 \$3;
#X msg 42 246 \; foo12_rcv label_pos \$1 \$2;
#X msg 62 361 \; foo12_rcv label_font \$1 \$2;
#X msg 34 423 \; foo12_rcv label blabla;
#X msg 34 459 \; foo12_rcv label vdial_0_9;
#X msg 300 460 \; foo12_rcv pos \$1 \$2;
#X msg 286 341 \; foo12_rcv delta \$1 \$2;
#X msg 270 216 \; foo12_rcv size \$1;
#X msg 483 50 \; foo12_rcv send foo12a_snd;
#X msg 483 88 \; foo12_rcv send foo12_snd;
#X msg 482 171 \; foo12a_rcv receive foo12_rcv;
#X msg 483 133 \; foo12_rcv receive foo12a_rcv;
#X msg 482 254 \; foo12_rcv number \$1;
#X msg 483 312 \; foo12_rcv init 0;
#X msg 485 366 \; foo12_rcv init 1;
#X msg 490 436 \; foo12_rcv single_change;
#X msg 490 470 \; foo12_rcv double_change;
#X connect 0 0 4 0;
#X connect 1 0 0 0;
#X connect 2 0 0 1;
#X connect 3 0 4 1;
#X connect 4 0 58 0;
#X connect 7 0 64 0;
#X connect 9 0 13 0;
#X connect 10 0 9 0;
#X connect 11 0 9 1;
#X connect 12 0 13 1;
#X connect 13 0 63 0;
#X connect 14 0 18 0;
#X connect 15 0 14 0;
#X connect 16 0 14 1;
#X connect 17 0 18 1;
#X connect 18 0 62 0;
#X connect 23 0 27 0;
#X connect 24 0 23 0;
#X connect 25 0 23 1;
#X connect 26 0 27 1;
#X connect 27 0 59 0;
#X connect 32 0 69 0;
#X connect 35 0 57 0;
#X connect 36 0 35 0;
#X connect 37 0 36 0;
#X connect 38 0 36 1;
#X connect 39 0 35 1;
#X connect 40 0 35 2;
#X connect 44 0 48 0;
#X connect 45 0 48 0;
#X connect 46 0 48 0;
#X connect 47 0 48 0;
#X connect 48 0 35 0;
#X connect 48 1 35 1;
#X connect 48 2 35 2;
#X connect 49 0 48 1;
#X connect 50 0 48 2;
#X connect 51 0 48 3;
#X restore 267 222 pd edit;
#X obj 221 11 x_all_guis aaa bbb ccc ddd eee fff ggg hhh iii;
#X text 30 415 graz \, austria 2002;
#X text 223 401 updated for Pd version 0.35;
#X text 144 11 gui-vdial:;
#X obj 79 110 vdl 25 1 0 8 foo12_snd foo12_rcv vdial_0_9 20 -8 192
10 -99865 -262144 -260818 0;
#X obj 352 36 vdl 15 1 0 8 iii iii empty 20 8 192 8 -262144 -1 -1 0
;
#X obj 158 213 s foo12_rcv;
#X obj 159 239 r foo12_snd;
#X text 225 418 KEYWORDS deprecated;
#X connect 6 0 38 0;
#X connect 7 0 8 0;
#X connect 8 0 9 0;
#X connect 11 0 10 0;
#X connect 11 1 17 0;
#X connect 11 2 18 0;
#X connect 11 3 19 0;
#X connect 11 4 20 0;
#X connect 11 5 21 0;
#X connect 11 6 22 0;
#X connect 11 7 23 0;
#X connect 11 8 24 0;
#X connect 11 9 25 0;
#X connect 12 0 38 0;
#X connect 13 0 12 0;
#X connect 14 0 38 0;
#X connect 15 0 38 0;
#X connect 16 0 38 0;
#X connect 28 0 27 0;
#X connect 29 0 40 0;
#X connect 30 0 29 0;
#X connect 38 0 11 0;
#X connect 38 0 26 0;
#X connect 38 0 7 0;
#X connect 41 0 5 0;
#X connect 41 0 28 0;
#N canvas 430 33 555 619 10;
#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
14 -228856 -66577 0;
#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header vd~ 3 12 0 18 -204280
-1 0;
#X obj 0 369 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
-228856 -1 0;
#N canvas 486 282 494 344 META 0;
#X text 12 115 LIBRARY internal;
#X text 12 155 WEBSITE http://crca.ucsd.edu/~msp/;
#X text 12 25 LICENSE SIBSD;
#X text 12 135 AUTHOR Miller Puckette;
#X text 12 195 HELP_PATCH_AUTHORS Updated for Pd version 0.33. Jonathan
Wilkes revised the patch to conform to the PDDP template for Pd version
0.42.;
#X text 12 5 KEYWORDS signal nonlocal time;
#X text 12 45 DESCRIPTION read a signal from a delay line at a variable
delay time;
#X text 12 75 INLET_0 signal;
#X text 12 95 OUTLET_0 signal;
#X text 12 175 RELEASE_DATE 1997;
#X restore 500 597 pd META;
#X obj 0 444 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
13 -228856 -1 0;
#X obj 0 497 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
0 13 -228856 -1 0;
#X obj 0 538 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
0 13 -228856 -1 0;
#N canvas 218 514 428 109 Related_objects 0;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X obj 22 43 delwrite~;
#X obj 92 43 delread~;
#X text 8 2 [vd~] Related Objects;
#X obj 159 44 delay;
#X restore 102 597 pd Related_objects;
#X obj 78 378 cnv 17 3 55 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X obj 78 453 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X obj 324 138 sig~;
#X text 98 377 signal;
#X text 98 397 float;
#X text 98 416 list;
#X text 11 23 reads a signal from a delay line at a variable delay
time;
#X text 165 343 NOTE: [vd~] uses 4-point-interpolation;
#X text 98 452 signal;
#X text 168 452 - the signal from the corresponding [delwrite~] object
is interpolated and output after the delay specified by the incoming
signal.;
#X text 168 515 - the name of the corresponding [delwrite~] object
;
#X text 168 416 - a list is truncated to the first element.;
#X text 168 377 - incoming signal (delay time in ms).;
#X text 168 397 - delay time in ms.;
#X text 80 515 1) symbol atom;
#X obj 101 160 delwrite~ vd~help_example 1000;
#X obj 324 160 vd~ vd~help_example;
#X text 98 67 [vd~] implements a 4-point interpolating delay tap from
a corresponding delwrite~ object. The delay in milliseconds of the
tap is specified by the incoming signal.;
#X floatatom 324 115 5 0 0 0 - - -, f 5;
#X obj 324 230 snapshot~;
#X obj 333 204 metro 150;
#X obj 333 182 loadbang;
#X floatatom 324 252 5 0 0 0 - - -, f 5;
#X floatatom 101 139 5 0 0 0 - - -, f 5;
#X msg 369 115 1000;
#X text 98 274 The delay time is always at least one sample and at
most the length of the delay line (specified by [delwrite~]). In addition
\, in case the [delwrite~] runs later in the DSP loop than the [vd~]
\, the delay is constrained below by one vector length (64 samples.)
;
#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
;
#X obj 480 53 pddp/dsp;
#X obj 98 543 pddp/pddplink ../3.audio.examples/G03.delay.variable.pd
-text doc/3.audio.examples/G03.delay.variable.pd;
#X obj 98 558 pddp/pddplink ../3.audio.examples/G04.control.blocksize.pd
-text doc/3.audio.examples/G04.control.blocksize.pd;
#X obj 98 573 pddp/pddplink ../3.audio.examples/G05.execution.order.pd
-text doc/3.audio.examples/G05.execution.order.pd;
#X obj 519 12 vd~;
#X text 484 5;
#X connect 10 0 24 0;
#X connect 24 0 27 0;
#X connect 26 0 10 0;
#X connect 27 0 30 0;
#X connect 28 0 27 0;
#X connect 29 0 28 0;
#X connect 31 0 23 0;
#X connect 32 0 10 0;
#N canvas 431 35 555 619 10;
#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
14 -228856 -66577 0;
#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header vline~ 3 12 0 18
-204280 -1 0;
#X obj 0 361 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
-228856 -1 0;
#N canvas 484 278 494 344 META 0;
#X text 12 145 LIBRARY internal;
#X text 12 185 WEBSITE http://crca.ucsd.edu/~msp/;
#X text 12 25 LICENSE SIBSD;
#X text 12 165 AUTHOR Miller Puckette;
#X text 12 225 HELP_PATCH_AUTHORS Updated for version 0.33. Jonathan
Wilkes revised the patch to conform to the PDDP template for Pd version
0.42.;
#X text 12 5 KEYWORDS signal time conversion;
#X text 12 45 DESCRIPTION high-precision audio ramp generator;
#X text 12 65 INLET_0 float list stop;
#X text 12 85 INLET_1 float;
#X text 12 105 INLET_2 float;
#X text 12 125 OUTLET_0 signal;
#X text 12 205 RELEASE_DATE 1997;
#X restore 500 597 pd META;
#X obj 0 499 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
13 -228856 -1 0;
#X obj 0 541 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
0 13 -228856 -1 0;
#X obj 0 568 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
0 13 -228856 -1 0;
#X text 98 545 (none);
#N canvas 223 521 428 109 Related_objects 0;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X obj 22 43 line~;
#X obj 62 43 line;
#X text 8 2 [vline~] Related Objects;
#X restore 102 597 pd Related_objects;
#X obj 78 370 cnv 17 3 70 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X text 98 369 float;
#X text 98 393 list;
#X obj 78 508 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X obj 78 448 cnv 17 3 17 empty \$0-pddp.cnv.let.1 1 5 9 0 16 -228856
-162280 0;
#X text 98 447 float;
#X obj 495 11 vline~;
#X text 11 23 high-precision audio ramp generator;
#X text 168 393 - a list to the first inlet is distributed among the
inlets of [vline~].;
#X text 98 426 stop;
#X obj 78 471 cnv 17 3 17 empty \$0-pddp.cnv.let.2 2 5 9 0 16 -228856
-162280 0;
#X text 98 470 float;
#X text 98 507 signal;
#X text 168 507 - the output is a linear ramp whose levels and timing
are determined by the messages sent to the inlet(s).;
#X obj 31 271 snapshot~;
#X floatatom 31 294 5 0 0 0 - - -, f 5;
#X obj 41 244 metro 100;
#X msg 31 69 1 1000;
#X msg 59 174 stop;
#X msg 51 113 0;
#X obj 31 200 vline~;
#X text 86 67 ramp up;
#X msg 40 91 0 1000;
#X text 86 90 ramp down;
#X text 60 133 ramp up \, jump down \, ramp up again;
#X msg 54 152 1 1000 \, 0 0 1000 \, 1 1000 1000;
#X text 86 112 jump down;
#X text 147 181 The [vline~] object \, like [line~] \, generates linear
ramps whose levels and timing are determined by messages you send it.
The messages consist of a target value \, a time interval (zero if
not supplied) \, and an initial delay (also zero if not supplied.)
Ramps may start and stop between audio samples \, in which case the
output is interpolated accordingly.;
#X text 147 276 Any number of future ramps may be scheduled and [vline~]
will remember them and execute them in order. They must be specified
in increasing order of initial delay however \, since a segment cancels
all planned segments at any future time.;
#X text 168 369 - target value.;
#X text 168 426 - the "stop" message freezes [vline~] at its current
value.;
#X text 167 447 - time value.;
#X text 167 470 - initial delay.;
#X obj 41 222 loadbang;
#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
;
#X obj 474 57 pddp/dsp;
#X obj 98 572 pddp/pddplink ../3.audio.examples/C04.control.to.signal.pd
-text doc/3.audio.examples/C04.control.to.signal.pd;
#X connect 23 0 24 0;
#X connect 25 0 23 0;
#X connect 26 0 29 0;
#X connect 27 0 29 0;
#X connect 28 0 29 0;
#X connect 29 0 23 0;
#X connect 31 0 29 0;
#X connect 34 0 29 0;
#X connect 42 0 25 0;
#N canvas 430 32 555 619 10;
#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
14 -228856 -66577 0;
#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header vslider 3 12 0 18
-204280 -1 0;
#X obj 0 306 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
-228856 -1 0;
#N canvas 485 240 494 387 META 0;
#X text 12 135 LIBRARY internal;
#X text 12 175 WEBSITE http://crca.ucsd.edu/~msp/;
#X text 12 45 LICENSE SIBSD;
#X text 12 155 AUTHOR Miller Puckette;
#X text 12 215 HELP_PATCH_AUTHORS This help patch was updated for Pd
version 0.35 test 28 by Dave Sabine as part of a project called pddp
proposed by Krzysztof Czaja to build comprehensive documentation for
Pd. Jonathan Wilkes revised the patch to conform to the PDDP template
for Pd version 0.42.;
#X text 12 25 KEYWORDS control storage GUI nonlocal;
#X text 12 65 DESCRIPTION vertical gui-slider;
#X text 12 5 ALIAS vsl;
#X text 12 85 INLET_0 float bang size range log init steady receive
send label label_pos label_font color pos delta;
#X text 12 115 OUTLET_0 float;
#X text 12 195 RELEASE_DATE 1997;
#X restore 500 597 pd META;
#X obj 0 436 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
13 -228856 -1 0;
#X obj 0 490 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
0 13 -228856 -1 0;
#X obj 0 574 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
0 13 -228856 -1 0;
#X obj 78 315 cnv 17 3 106 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#N canvas 209 494 428 129 Related_objects 0;
#X obj 20 53 x_all_guis bng tgl vsl hsl hradio vradio vu cnv nbx;
#X text 18 29 All iem-gui objects:;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X text 8 2 [vslider] Related Objects;
#X restore 100 597 pd Related_objects;
#X obj 78 445 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X text 98 314 float;
#X text 98 445 float;
#X text 54 259 Use the UP- \, DOWN- \, LEFT- or RIGHT-key to move a
selected gui-object. Right-click and choose "Properties" to modify
its appearance.;
#X text 168 445 - when the slider value is changed by clicking \, dragging
\, or sending a float to the inlet \, the new slider value is sent
to the outlet.;
#N canvas 53 19 464 608 Setting_slider_properties_through_messages
0;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X floatatom 12 189 1 0 1 0 - #0-soc -, f 1;
#X text 324 35 no init;
#X text 9 170 steady-on-click;
#X text 11 309 label;
#X msg 11 329 label blabla;
#X msg 324 54 init 0;
#X msg 12 222 steady \$1;
#X msg 235 74 lin;
#X msg 245 106 log;
#X text 231 35 linear/;
#X text 231 50 logarithmical;
#X text 333 74 init value;
#X text 334 89 on loadbang;
#X text 235 170 change send name;
#X text 108 170 change receive name;
#X obj 122 245 s z_rcv;
#X msg 110 192 receive z_rcv;
#X msg 21 381 label mySlider;
#X msg 122 222 receive hsl_rcv;
#X msg 237 193 send hsl1_snd;
#X msg 246 222 send hsl_snd;
#N canvas 234 255 161 292 init 0;
#X obj 16 11 loadbang;
#X obj 16 42 f \$0;
#X msg 16 73 \; \$1-xpos 391 \; \$1-ypos 200 \; \$1-xlabel -2 \; \$1-ylabel
-8 \; \$1-soc 1 \; \$1-font-size 10 \; \$1-width 15 \; \$1-height 128
\; \$1-bottom 0 \; \$1-top 127 \; \$1-bg 0 \; \$1-front 12 \; \$1-label
12;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X restore 400 586 pd init;
#X msg 334 110 init 1;
#X msg 12 106 size \$1 \$2;
#X text 61 57 1.width;
#X text 61 78 2.height;
#X text 173 57 1.bottom;
#X text 173 78 2.top;
#X text 121 35 output-range;
#X msg 124 106 range \$1 \$2;
#X text 242 308 font properties;
#X msg 244 380 label_font \$1 \$2;
#X text 293 330 1.font(0-2);
#X text 293 351 2.height;
#X text 120 309 label position;
#X msg 122 380 label_pos \$1 \$2;
#X text 171 330 1.x-offset;
#X text 171 351 2.y-offset;
#X obj 375 184 cnv 15 67 172 empty empty empty 20 12 0 14 -1 -66577
0;
#X obj 376 185 cnv 15 65 170 empty empty empty 20 12 0 14 -261682 -66577
0;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-width empty
0 -8 0 12 -262144 -1 -1 15 256 0;
#X obj 103 124 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-height empty
0 -8 0 12 -262144 -1 -1 128 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 12 56 pd flash-time;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-bottom empty
0 -8 0 12 -262144 -1 -1 0 256 0;
#X obj 103 124 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-top empty 0
-8 0 12 -262144 -1 -1 127 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 124 56 pd flash-time;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-xlabel empty
0 -8 0 12 -262144 -1 -1 -2 256 0;
#X obj 103 124 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-ylabel empty
0 -8 0 12 -262144 -1 -1 -8 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 122 329 pd flash-time;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 0 2 0 0 \$0-dummy \$0-font empty 0 -8 0 12
-262144 -1 -1 0 256 0;
#X obj 103 124 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-font-size empty
0 -8 0 12 -262144 -1 -1 10 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 244 329 pd flash-time;
#X text 9 449 preset-colors;
#X text 105 449 RGB-colors;
#X msg 12 555 color \$1 \$2 \$3;
#N canvas 364 205 306 317 flash-time 0;
#X obj 69 196 t b a;
#X obj 33 262 outlet;
#X obj 33 237 pack 0 0 0;
#X obj 120 197 t b a;
#X obj 103 104 nbx 3 14 0 29 0 0 \$0-dummy \$0-bg empty 0 -8 0 12 -262144
-1 -1 0 256 0;
#X obj 103 124 nbx 3 14 0 29 0 0 \$0-dummy \$0-front empty 0 -8 0 12
-262144 -1 -1 12 256 0;
#X obj 103 144 nbx 3 14 0 29 0 0 \$0-dummy \$0-label empty 0 -8 0 12
-262144 -1 -1 12 256 0;
#X connect 0 0 2 0;
#X connect 0 1 2 1;
#X connect 2 0 1 0;
#X connect 3 0 2 0;
#X connect 3 1 2 2;
#X connect 4 0 2 0;
#X connect 5 0 0 0;
#X connect 6 0 3 0;
#X coords 0 -1 1 1 48 62 2 100 100;
#X restore 12 470 pd flash-time;
#N canvas 0 0 306 317 flash-time 0;
#X obj 96 521 outlet;
#X obj 162 230 t b f;
#X obj 96 268 +;
#X obj 96 293 t b f;
#X obj 96 324 +;
#X obj 19 208 * -65536;
#X obj 96 208 * -256;
#X obj 162 208 * -1;
#X obj 96 355 - 1;
#X obj 96 385 list prepend 0;
#X obj 96 410 route 0 1 2;
#X obj 96 497 pack 0 0 0;
#X obj 115 456 t b a;
#X obj 168 455 t b a;
#X obj 104 124 vradio 15 1 0 3 empty empty empty 0 -8 0 10 -262144
-1 -1 0;
#X text 120 120 1.bg;
#X text 120 137 2.front;
#X text 120 153 3.label;
#X obj 103 105 nbx 3 14 0 255 0 0 \$0-dummy \$0-r empty 0 -8 0 10 -262144
-258113 -1 0 256 0;
#X obj 133 105 nbx 3 14 0 255 0 0 \$0-dummy \$0-g empty 0 -8 0 10 -262144
-13381 -1 0 256 0;
#X obj 163 105 nbx 3 14 0 255 0 0 \$0-dummy \$0-b empty 0 -8 0 10 -262144
-4160 -1 0 256 0;
#X connect 1 0 2 0;
#X connect 1 1 2 1;
#X connect 2 0 3 0;
#X connect 3 0 4 0;
#X connect 3 1 4 1;
#X connect 4 0 8 0;
#X connect 5 0 4 0;
#X connect 6 0 2 0;
#X connect 7 0 1 0;
#X connect 8 0 9 0;
#X connect 9 0 10 0;
#X connect 10 0 11 0;
#X connect 10 1 12 0;
#X connect 10 2 13 0;
#X connect 11 0 0 0;
#X connect 12 0 11 0;
#X connect 12 1 11 1;
#X connect 13 0 11 0;
#X connect 13 1 11 2;
#X connect 14 0 9 1;
#X connect 18 0 5 0;
#X connect 19 0 6 0;
#X connect 20 0 7 0;
#X coords 0 -1 1 1 102 74 2 100 100;
#X restore 109 470 pd flash-time;
#X text 61 472 1.bg;
#X text 61 492 2.front;
#X text 61 513 3.label;
#X text 318 449 delta;
#X text 218 449 position;
#X msg 219 541 pos \$1 \$2;
#X msg 319 541 delta \$1 \$2;
#X obj 319 518 list;
#X obj 319 471 bng 20 250 50 0 empty empty empty 17 7 0 10 -262144
-1 -1;
#X text 398 470 1.x-delta;
#X text 398 492 2.y-delta;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-xpos empty 0
-8 0 12 -262144 -1 -1 391 256 0;
#X obj 103 124 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-ypos empty 0
-8 0 12 -262144 -1 -1 200 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 219 471 pd flash-time;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 -20 20 0 0 \$0-dummy \$0-xdelta empty 0 -8
0 12 -262144 -1 -1 0 256 0;
#X obj 103 124 nbx 3 14 -20 20 0 0 \$0-dummy \$0-ydelta empty 0 -8
0 12 -262144 -1 -1 0 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 348 471 pd flash-time;
#X floatatom 392 337 5 0 0 0 - - -, f 5;
#X text 11 36 dimensions;
#X text 267 470 1.x-pos;
#X text 267 492 2.y-pos;
#X obj 12 135 s vsl_rcv;
#X obj 124 135 s vsl_rcv;
#X obj 235 135 s vsl_rcv;
#X obj 324 135 s vsl_rcv;
#X obj 12 270 s vsl_rcv;
#X obj 110 270 s vsl_rcv;
#X obj 237 270 s vsl_rcv;
#X obj 11 409 s vsl_rcv;
#X obj 122 409 s vsl_rcv;
#X obj 244 405 s vsl_rcv;
#X obj 12 580 s vsl_rcv;
#X obj 219 566 s vsl_rcv;
#X obj 319 566 s vsl_rcv;
#X obj 391 200 vsl 15 128 0 127 0 0 vsl_snd vsl_rcv mySlider -2 -8
0 10 -262144 -33289 -33289 0 1;
#X text 8 2 [vslider] Set Properties through Messages;
#X connect 1 0 7 0;
#X connect 5 0 74 0;
#X connect 6 0 70 0;
#X connect 7 0 71 0;
#X connect 8 0 69 0;
#X connect 9 0 69 0;
#X connect 17 0 72 0;
#X connect 18 0 74 0;
#X connect 19 0 16 0;
#X connect 20 0 73 0;
#X connect 21 0 73 0;
#X connect 23 0 70 0;
#X connect 24 0 67 0;
#X connect 30 0 68 0;
#X connect 32 0 76 0;
#X connect 36 0 75 0;
#X connect 41 0 24 0;
#X connect 42 0 30 0;
#X connect 43 0 36 0;
#X connect 44 0 32 0;
#X connect 47 0 77 0;
#X connect 48 0 47 0;
#X connect 49 0 47 0;
#X connect 55 0 78 0;
#X connect 56 0 79 0;
#X connect 57 0 56 0;
#X connect 58 0 57 0;
#X connect 61 0 55 0;
#X connect 62 0 57 1;
#X connect 80 0 63 0;
#X restore 101 406 pd Setting_slider_properties_through_messages;
#X obj 57 213 ftom;
#X floatatom 57 237 9 0 0 0 - - -, f 9;
#X floatatom 104 213 9 0 0 0 - - -, f 9;
#X floatatom 57 84 9 0 0 0 - - -, f 9;
#X obj 57 106 mtof;
#X text 97 177 (0.01 pixels);
#X text 99 148 shift-click & drag;
#X text 99 163 for fine-tuning;
#X obj 57 132 vsl 15 73 55 3520 1 1 goo4_snd goo4_rcv log.freq. 20
6 0 10 -261681 -260818 -90881 2244 1;
#X text 380 170 names. Right-click and;
#X text 380 155 send and receive names.;
#X text 379 186 choose "Properties" to;
#X text 380 200 set them \, or change them;
#X text 380 214 with messages (see below);
#X floatatom 250 216 4 0 0 0 - - -, f 4;
#X msg 309 69 set \$1;
#X floatatom 270 47 4 0 0 0 - - -, f 4;
#X obj 250 240 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144
-1 -1;
#X obj 250 47 bng 15 250 50 0 empty empty empty 8 -8 0 10 -262144 -1
-1;
#X floatatom 309 48 4 0 0 0 - - -, f 4;
#X floatatom 310 191 4 0 0 0 - - -, f 4;
#X floatatom 309 100 4 0 0 0 - - -, f 4;
#X obj 250 102 vsl 15 101 100 300 0 1 foo3_snd foo3_rcv empty 8 -8
0 10 -225280 -1109 -1 5350 1;
#X obj 309 143 s foo3_rcv;
#X obj 310 169 r foo3_snd;
#X msg 309 121 set \$1;
#X obj 294 240 print;
#N canvas 276 200 290 224 once 0;
#X obj 38 24 inlet;
#X obj 38 91 outlet;
#X obj 38 69 t a b;
#X obj 38 47 spigot 1;
#X obj 97 47 0;
#X connect 0 0 3 0;
#X connect 2 0 1 0;
#X connect 2 1 4 0;
#X connect 3 0 2 0;
#X connect 4 0 3 1;
#X restore 294 216 pd once;
#X text 98 374 Special messages can be sent to [vslider] to change
its properties:;
#N canvas 65 102 428 434 vsl-creation-arguments 0;
#X text 22 226 For example \, click the msg-box to dynamically create
a hsl:;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X text 8 2 [vslider] Creation Arguments and Dynamic Patching;
#X obj 23 290 s pd-vsl-creation-arguments;
#X text 21 38 "vsl" can be called with 18 creation arguments. These
are not "optional" - all parameters must be filled or the gui-bng will
not instantiate correctly.;
#X text 21 85 vsl creation arguments: xSize ySize rangeBtm rangeTop
Logarithmic Init sendName receiveName Label labelXOff labelYOff Font#
fontSize bgColor foregroundColor lblColor sliderPosition steadyOnClick
;
#X text 22 143 note: sliderPosition is not the same as the initialization
value. To dynamically instantiate an hsl that initializes with a desired
value \, save an hsl in a patch and view it in a text editor to get
the right value for sliderPosition. Or \, refer to hslider_set in g_vslider.c
for conversion between sliderPosition and slider value.;
#X msg 23 248 obj 150 335 vsl 75 75 -1 1 0 1 send receive Merope 15
-9 0 12 7 13 21 4400 0;
#X connect 7 0 3 0;
#X restore 170 551 pd vsl-creation-arguments;
#X text 379 139 vslider has settable;
#X text 11 21 vertical gui-slider;
#X text 98 494 18;
#X text 168 494 - creating a [vslider] object without arguments sets
default values for its gui-properties. See the subpatch below to learn
how to dynamically instantiate a [bng] object with custom properties:
;
#X text 168 314 - a float updates the slider value and is sent to the
outlet.;
#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
;
#X text 98 356 list;
#X text 168 356 - a list is truncated to the first element.;
#X text 98 339 bang;
#X text 168 339 - output the current slider value.;
#X text 429 11 [vslider] or [vsl];
#X connect 15 0 16 0;
#X connect 18 0 19 0;
#X connect 19 0 23 0;
#X connect 23 0 15 0;
#X connect 23 0 17 0;
#X connect 29 0 32 0;
#X connect 30 0 37 0;
#X connect 31 0 37 0;
#X connect 33 0 37 0;
#X connect 34 0 30 0;
#X connect 36 0 40 0;
#X connect 37 0 29 0;
#X connect 37 0 42 0;
#X connect 39 0 35 0;
#X connect 40 0 38 0;
#X connect 42 0 41 0;
#N canvas 429 34 558 622 10;
#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
14 -228856 -66577 0;
#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header vu 3 12 0 18 -204280
-1 0;
#X obj 0 287 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
-228856 -1 0;
#N canvas 488 283 494 344 META 0;
#X text 12 155 LIBRARY internal;
#X text 12 25 LICENSE SIBSD;
#X text 12 175 AUTHOR Thomas Musil;
#X text 12 195 WEBSITE;
#X text 12 5 KEYWORDS control GUI nonlocal;
#X text 12 45 DESCRIPTION gui-vu-meter-display;
#X text 12 65 INLET_0 float list bang size scale receive label label_pos
label_font color pos delta;
#X text 12 95 INLET_1 float;
#X text 12 115 OUTLET_0 float;
#X text 12 135 OUTLET_1 float;
#X text 12 215 RELEASE_DATE 2002;
#X text 12 235 HELP_PATCH_AUTHORS Jonathan Wilkes revised the patch
to conform to the PDDP template for Pd version 0.42.;
#X restore 500 597 pd META;
#X obj 0 435 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
13 -228856 -1 0;
#X obj 0 489 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
0 13 -228856 -1 0;
#X obj 0 572 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
0 13 -228856 -1 0;
#N canvas 207 509 428 126 Related_objects 0;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X text 8 2 [vu] Related Objects;
#X obj 22 91 x_all_guis bng tgl vsl hsl hradio vradio vu cnv nbx;
#X text 20 67 All iem-gui objects:;
#X obj 23 43 env~;
#X restore 92 597 pd Related_objects;
#X obj 78 296 cnv 17 3 90 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X text 98 295 float;
#X obj 78 444 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X obj 78 396 cnv 17 3 30 empty \$0-pddp.cnv.let.1 1 5 9 0 16 -228856
-162280 0;
#X text 98 312 list;
#X text 98 396 float;
#X text 98 412 list;
#X text 168 312 - a 2-item list will be distributed to the two inlets
;
#X text 98 444 float;
#X obj 78 466 cnv 17 3 17 empty \$0-pddp.cnv.let.1 1 5 9 0 16 -228856
-162280 0;
#X text 98 466 float;
#N canvas 76 117 428 416 vu-creation-arguments 0;
#X obj 25 237 s pd-vu-creation-arguments;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X text 8 2 [vu] Creation Arguments;
#X text 22 163 For example \, click the msg-box to dynamically create
a [vu]:;
#X text 23 98 [vu] creation arguments: xSize ySize receiveName Label
labelXOff labelYOff font# fontSize bgColor labelColor Scale Unknown(0)
;
#X text 22 38 [vu] can be called with 12 creation arguments. These
are not "optional" - all parameters must be filled or the gui-bng will
not instantiate correctly.;
#X msg 25 195 obj 160 280 vu 15 120 fff Elektra -15 -12 0 12 26 12
0 0;
#X connect 6 0 0 0;
#X restore 170 549 pd vu-creation-arguments;
#N canvas 75 10 429 610 Changing_GUI_Properties 0;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X floatatom 130 62 1 0 1 0 - #0-scale -, f 1;
#X text 124 38 display scale;
#X msg 130 116 scale \$1;
#X obj 130 141 s vu_rcv;
#X text 213 38 change receive name;
#X obj 215 141 s vu_rcv;
#X msg 215 62 receive vu12_rcv;
#X obj 231 114 s vu12_rcv;
#X msg 231 89 receive vu_rcv;
#X msg 339 61 label blabla;
#X obj 339 141 s vu_rcv;
#X text 339 38 label;
#N canvas 378 326 151 256 init 0;
#X obj 16 11 loadbang;
#X obj 16 42 f \$0;
#X msg 16 73 \; \$1-xpos 330 \; \$1-ypos 245 \; \$1-scale 1 \; \$1-xlabel
-1 \; \$1-ylabel -8 \; \$1-label 12 \; \$1-font-size 10 \; \$1-width
15 \; \$1-height 120 \; \$1-font 0 \; \$1-bg 12 \; \$1-bg 12;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X restore 352 404 pd init;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-width empty
0 -8 0 12 -262144 -1 -1 15 256 0;
#X obj 103 124 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-height empty
0 -8 0 12 -262144 -1 -1 120 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 22 59 pd flash-time;
#X text 19 38 dimensions;
#X text 71 61 1.width;
#X text 71 81 2.height;
#X msg 22 116 size \$1 \$2;
#X obj 22 141 s vu_rcv;
#X text 20 175 label position;
#X text 152 175 font properties;
#X msg 22 250 label_pos \$1 \$2;
#X msg 154 250 label_font \$1 \$2;
#X text 203 199 1.font(0-2);
#X text 203 220 2.height;
#X text 71 198 1.x-offset;
#X text 71 219 2.y-offset;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-xlabel empty
0 -8 0 12 -262144 -1 -1 -1 256 0;
#X obj 103 124 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-ylabel empty
0 -8 0 12 -262144 -1 -1 -8 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 22 197 pd flash-time;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 0 2 0 0 \$0-dummy \$0-font empty 0 -8 0 12
-262144 -1 -1 0 256 0;
#X obj 103 124 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-font-size empty
0 -8 0 12 -262144 -1 -1 10 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 154 197 pd flash-time;
#X text 22 310 preset-colors;
#X text 131 310 RGB-colors;
#N canvas 364 205 169 292 flash-time 0;
#X obj 69 196 t b a;
#X obj 33 262 outlet;
#X obj 103 104 nbx 3 14 0 29 0 0 \$0-dummy \$0-bg empty 0 -8 0 12 -262144
-1 -1 12 256 0;
#X obj 103 124 nbx 3 14 0 29 0 0 \$0-dummy \$0-label empty 0 -8 0 12
-262144 -1 -1 12 256 0;
#X obj 33 237 pack 0 0;
#X connect 0 0 4 0;
#X connect 0 1 4 1;
#X connect 2 0 4 0;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 22 332 pd flash-time;
#N canvas 0 0 306 317 flash-time 0;
#X obj 96 521 outlet;
#X obj 162 230 t b f;
#X obj 96 268 +;
#X obj 96 293 t b f;
#X obj 96 324 +;
#X obj 19 208 * -65536;
#X obj 96 208 * -256;
#X obj 162 208 * -1;
#X obj 96 355 - 1;
#X obj 96 385 list prepend 0;
#X obj 115 456 t b a;
#X obj 104 124 vradio 15 1 0 2 empty empty empty 0 -8 0 10 -262144
-1 -1 0;
#X text 120 120 1.bg;
#X obj 103 105 nbx 3 14 0 255 0 0 \$0-dummy \$0-r empty 0 -8 0 10 -262144
-258113 -1 0 256 0;
#X obj 133 105 nbx 3 14 0 255 0 0 \$0-dummy \$0-g empty 0 -8 0 10 -262144
-13381 -1 0 256 0;
#X obj 163 105 nbx 3 14 0 255 0 0 \$0-dummy \$0-b empty 0 -8 0 10 -262144
-4160 -1 0 256 0;
#X text 120 137 2.label;
#X obj 96 410 route 0 1;
#X obj 96 497 pack 0 0;
#X connect 1 0 2 0;
#X connect 1 1 2 1;
#X connect 2 0 3 0;
#X connect 3 0 4 0;
#X connect 3 1 4 1;
#X connect 4 0 8 0;
#X connect 5 0 4 0;
#X connect 6 0 2 0;
#X connect 7 0 1 0;
#X connect 8 0 9 0;
#X connect 9 0 17 0;
#X connect 10 0 18 0;
#X connect 10 1 18 1;
#X connect 11 0 9 1;
#X connect 13 0 5 0;
#X connect 14 0 6 0;
#X connect 15 0 7 0;
#X connect 17 0 18 0;
#X connect 17 1 10 0;
#X connect 18 0 0 0;
#X coords 0 -1 1 1 102 58 2 100 100;
#X restore 132 332 pd flash-time;
#X text 71 334 1.bg;
#X text 71 354 2.label;
#X msg 22 407 color \$1 \$2;
#X text 153 463 delta;
#X text 21 463 position;
#X msg 22 560 pos \$1 \$2;
#X msg 154 560 delta \$1 \$2;
#X obj 154 536 list;
#X obj 154 485 bng 20 250 50 0 empty empty empty 17 7 0 10 -262144
-1 -1;
#X text 70 484 1.x-position;
#X text 70 506 2.y-position;
#X text 233 484 1.x-delta;
#X text 233 506 2.y-delta;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-xpos empty 0
-8 0 12 -262144 -1 -1 330 256 0;
#X obj 103 124 nbx 3 14 -1e+37 1e+37 0 0 \$0-dummy \$0-ypos empty 0
-8 0 12 -262144 -1 -1 245 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 22 485 pd flash-time;
#N canvas 366 206 174 259 flash-time 0;
#X obj 68 180 pack;
#X obj 103 149 t b a;
#X obj 68 205 outlet;
#X obj 103 104 nbx 3 14 -20 20 0 0 \$0-dummy \$0-xdelta empty 0 -8
0 12 -262144 -1 -1 0 256 0;
#X obj 103 124 nbx 3 14 -20 20 0 0 \$0-dummy \$0-ydelta empty 0 -8
0 12 -262144 -1 -1 0 256 0;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 1 1 0 1;
#X connect 3 0 0 0;
#X connect 4 0 1 0;
#X coords 0 -1 1 1 48 42 2 100 100;
#X restore 183 485 pd flash-time;
#X obj 296 213 cnv 15 102 177 empty empty empty 20 12 0 14 -1 -66577
0;
#X obj 297 214 cnv 15 100 175 empty empty empty 20 12 0 14 -261682
-66577 0;
#X obj 22 275 s vu_rcv;
#X obj 154 275 s vu_rcv;
#X obj 22 432 s vu_rcv;
#X obj 154 585 s vu_rcv;
#X obj 22 585 s vu_rcv;
#X text 6 2 [vu] Changing Properties through Messages;
#X msg 349 114 label my-vu;
#X obj 330 245 vu 15 120 vu_rcv my-vu -1 -8 0 10 -33289 -33289 1 0
;
#X connect 1 0 3 0;
#X connect 3 0 4 0;
#X connect 7 0 6 0;
#X connect 9 0 8 0;
#X connect 10 0 11 0;
#X connect 14 0 18 0;
#X connect 18 0 19 0;
#X connect 22 0 51 0;
#X connect 23 0 52 0;
#X connect 28 0 22 0;
#X connect 29 0 23 0;
#X connect 32 0 36 0;
#X connect 33 0 36 0;
#X connect 36 0 53 0;
#X connect 39 0 55 0;
#X connect 40 0 54 0;
#X connect 41 0 40 0;
#X connect 42 0 41 0;
#X connect 47 0 39 0;
#X connect 48 0 41 1;
#X connect 57 0 11 0;
#X restore 172 375 pd Changing_GUI_Properties;
#X text 97 345 see the following subpatch for messages that change
the gui-properties of [vu]:;
#X text 168 493 - creating a [vu] without any arguments will set default
values for all gui-properties. See the subpatch below to learn how
to dynamically instantiate a [vu] object with custom properties:;
#X text 170 239 Use the UP- \, DOWN- \, LEFT- or RIGHT-key to move
a selected gui-object. Right-click and choose "Properties" to modify
its appearance.;
#X text 341 111 and receive names. Right-;
#X text 341 126 click and choose;
#X text 340 142 "Properties" to set them \,;
#X text 341 156 or change them with messages;
#X text 341 170 (see the subpatch below);
#X floatatom 259 90 7 -110 20 1 dB - -, f 7;
#X obj 42 48 tgl 15 1 empty empty empty 8 -8 0 10 -262144 -1 -1 0 1
;
#X obj 43 125 vu 15 120 foo7_rcv vu-meter 60 0 0 10 -1 -355 1 0;
#X floatatom 42 259 6 0 0 1 dB - -, f 6;
#X floatatom 104 259 6 0 0 1 dB - -, f 6;
#X text 108 68 <list> of rms \, peak;
#X obj 259 197 s foo7_rcv;
#X obj 259 168 pack 0 0;
#X floatatom 277 113 7 -110 20 1 dB - -, f 7;
#X obj 277 138 t b f;
#X floatatom 52 90 7 -110 20 1 dB - -, f 7;
#X floatatom 120 90 7 -110 20 1 dB - -, f 7;
#N canvas 44 136 290 278 source 0;
#X obj 40 95 random 102;
#X obj 40 171 - 101;
#X obj 40 205 pack 0 0;
#X obj 40 45 metro 300;
#X obj 40 69 t b b;
#X obj 133 95 random 20;
#X obj 40 117 t f f;
#X obj 91 147 +;
#X obj 91 172 - 101;
#X obj 40 21 inlet;
#X obj 40 250 outlet;
#X obj 100 249 s fff;
#X obj 159 249 s vu_rcv;
#X connect 0 0 6 0;
#X connect 1 0 2 0;
#X connect 2 0 10 0;
#X connect 2 0 11 0;
#X connect 2 0 12 0;
#X connect 3 0 4 0;
#X connect 4 0 0 0;
#X connect 4 1 5 0;
#X connect 5 0 7 1;
#X connect 6 0 1 0;
#X connect 6 1 7 0;
#X connect 7 0 8 0;
#X connect 8 0 2 1;
#X connect 9 0 3 0;
#X restore 42 68 pd source;
#X text 340 95 [vu] has settable send;
#X text 11 23 gui-vu-meter-display;
#X text 98 493 12;
#X text 168 295 - rms level.;
#X text 168 396 - peak-level.;
#X text 168 412 - a list will be truncated to the first element.;
#X text 168 444 - rms level.;
#X text 168 466 - peak-level.;
#X text 98 328 bang;
#X text 168 328 - a bang will output the current values of the inlets.
;
#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
;
#X connect 29 0 36 0;
#X connect 30 0 41 0;
#X connect 31 0 32 0;
#X connect 31 1 33 0;
#X connect 36 0 35 0;
#X connect 37 0 38 0;
#X connect 38 0 36 0;
#X connect 38 1 36 1;
#X connect 39 0 31 0;
#X connect 40 0 31 1;
#X connect 41 0 31 0;
#N canvas 427 34 555 619 10;
#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
14 -228856 -66577 0;
#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header wrap~ 3 12 0 18
-204280 -1 0;
#X obj 0 428 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
-228856 -1 0;
#N canvas 491 283 494 344 META 0;
#X text 12 105 LIBRARY internal;
#X text 12 145 WEBSITE http://crca.ucsd.edu/~msp/;
#X text 12 25 LICENSE SIBSD;
#X text 12 125 AUTHOR Miller Puckette;
#X text 12 185 HELP_PATCH_AUTHORS Updated for Pd version 0.33. Jonathan
Wilkes revised the patch to conform to the PDDP template for Pd version
0.42.;
#X text 12 5 KEYWORDS signal;
#X text 12 45 DESCRIPTION remainder modulo 1;
#X text 12 65 INLET_0 signal;
#X text 12 85 OUTLET_0 signal;
#X text 12 165 RELEASE_DATE 1997;
#X restore 500 597 pd META;
#X obj 0 503 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
13 -228856 -1 0;
#X obj 0 540 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
0 13 -228856 -1 0;
#X obj 0 568 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
0 13 -228856 -1 0;
#X text 98 544 (none);
#N canvas 214 518 428 109 Related_objects 0;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X obj 22 43 wrap;
#X text 8 2 [wrap~] Related Objects;
#X restore 102 597 pd Related_objects;
#X obj 78 437 cnv 17 3 55 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X obj 78 512 cnv 17 3 17 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X obj 509 10 wrap~;
#X obj 94 243 metro 500;
#X floatatom 78 144 0 0 0 0 - - -;
#X floatatom 78 290 0 0 0 0 - - -;
#X obj 78 267 snapshot~;
#X obj 78 170 sig~;
#X obj 78 194 wrap~;
#X obj 94 218 loadbang;
#X text 111 144 <- shift-drag here to get non-integers to try;
#X text 98 436 signal;
#X text 98 457 float;
#X text 98 477 list;
#X text 168 477 - a list will be truncated to the first item.;
#X text 98 511 signal;
#X text 11 23 remainder modulo 1;
#X text 168 436 - the incoming signal.;
#X text 168 457 - a float will automatically be converted to a signal.
;
#X text 74 317 [wrap~] gives the difference between the input and the
largest integer not exceeding it (for positive numbers this is the
fractional part).;
#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
;
#X obj 482 55 pddp/dsp;
#X connect 12 0 15 0;
#X connect 13 0 16 0;
#X connect 15 0 14 0;
#X connect 16 0 17 0;
#X connect 17 0 15 0;
#X connect 18 0 12 0;
#N canvas 431 36 555 619 10;
#X obj 0 595 cnv 15 552 21 empty \$0-pddp.cnv.footer empty 20 12 0
14 -228856 -66577 0;
#X obj 0 0 cnv 15 552 40 empty \$0-pddp.cnv.header writesf~ 3 12 0
18 -204280 -1 0;
#X obj 0 267 cnv 3 550 3 empty \$0-pddp.cnv.inlets inlets 8 12 0 13
-228856 -1 0;
#N canvas 488 285 494 344 META 0;
#X text 12 105 LIBRARY internal;
#X text 12 145 WEBSITE http://crca.ucsd.edu/~msp/;
#X text 12 25 LICENSE SIBSD;
#X text 12 125 AUTHOR Miller Puckette;
#X text 12 185 HELP_PATCH_AUTHORS Updated for Pd version 0.37. Jonathan
Wilkes revised the patch to conform to the PDDP template for Pd version
0.42.;
#X text 12 5 KEYWORDS signal;
#X text 12 45 DESCRIPTION write audio signals to a soundfile;
#X text 12 65 INLET_0 signal open start stop print;
#X text 12 85 INLET_N signal;
#X text 12 165 RELEASE_DATE 1997;
#X restore 500 597 pd META;
#X obj 0 498 cnv 3 550 3 empty \$0-pddp.cnv.outlets outlets 8 12 0
13 -228856 -1 0;
#X obj 0 526 cnv 3 550 3 empty \$0-pddp.cnv.argument arguments 8 12
0 13 -228856 -1 0;
#X obj 0 567 cnv 3 550 3 empty \$0-pddp.cnv.more_info more_info 8 12
0 13 -228856 -1 0;
#N canvas 216 514 428 109 Related_objects 0;
#X obj 1 1 cnv 15 425 20 empty \$0-pddp.cnv.subheading empty 3 12 0
14 -204280 -1 0;
#X text 8 2 [writesf~] Related Objects;
#X obj 22 43 readsf~;
#X obj 80 43 soundfiler;
#X restore 102 597 pd Related_objects;
#X obj 78 276 cnv 17 3 183 empty \$0-pddp.cnv.let.0 0 5 9 0 16 -228856
-162280 0;
#X obj 78 465 cnv 17 3 17 empty \$0-pddp.cnv.let.n n 5 9 0 16 -228856
-162280 0;
#X obj 486 10 writesf~;
#X text 98 502 (none);
#X msg 77 179 print;
#X msg 9 103 bang;
#X msg 76 135 start;
#X msg 78 157 stop;
#X obj 9 134 del 1000;
#X obj 67 223 writesf~ 2;
#X msg 67 65 open /tmp/foo.wav;
#X obj 81 201 osc~ 440;
#X msg 67 111 open -bytes 4 /tmp/foo.wav;
#X text 176 64 create a new 16-bit soundfile;
#X text 233 88 create 24-bit soundfile;
#X text 232 111 create 32-bit floating-point soundfile;
#X msg 67 88 open -bytes 3 /tmp/foo.wav;
#X text 11 23 write audio signals to a soundfile;
#X text 98 275 signal;
#X text 98 292 open;
#X text 98 415 start;
#X text 98 430 stop;
#X text 98 445 print;
#X text 98 464 signal;
#X text 167 464 - an additional inlet is created for each channel specified
by the creation argument.;
#X text 202 328 -wave \, -nextstep \, -aiff;
#X text 203 343 -big \, -little (nextstep only!);
#X text 203 358 -bytes <2 \, 3 \, or 4>;
#X text 203 374 -rate <sample rate>;
#X text 167 389 (setting sample rate will affect the soundfile header
but the file will _not_ be resampled.);
#X text 168 292 - the "open" message creates a new soundfile that can
be written to. The "open" message may take flag-style arguments as
follows:;
#X text 168 415 - start streaming audio to disk.;
#X text 168 430 - stop streaming audio to disk.;
#X text 149 206 The soundfile is 2- or 3-byte fixed point ("pcm") or
4-byte floating-point. The soundfile format is determined by the file
extent ("foo.wav" \, "foo.aiff" \, or "foo.snd").;
#X text 149 138 [writesf~] creates a subthread whose task is to write
audio streams to disk. You need not provide any disk access time between
"open" and "start" \, but between "stop" and the next "open" you must
give the object time to flush all the output to disk.;
#X text 168 445 - print debugging information.;
#X text 168 275 - the incoming signal is written to the file.;
#X text 168 544 - the creation argument is the number of channels (1
to 64).;
#X text 80 544 1) float;
#X obj 4 597 pddp/pddplink all_about_help_patches.pd -text Usage Guide
;
#X obj 478 54 pddp/dsp;
#X connect 12 0 17 0;
#X connect 13 0 14 0;
#X connect 13 0 16 0;
#X connect 14 0 17 0;
#X connect 15 0 17 0;
#X connect 16 0 15 0;
#X connect 18 0 17 0;
#X connect 19 0 17 0;
#X connect 20 0 17 0;
#X connect 24 0 17 0;
#N canvas 428 44 290 271 10;
#X obj 23 31 bng 15 250 50 0 \$1 \$1 empty 20 8 0 8 -262144 -1 -1;
#X obj 23 63 tgl 15 1.04858e+06 \$2 \$2 empty 20 8 0 8 -262144 -1 -1
0 1;
#X obj 22 95 vsl 15 128 0 127 0 1.04858e+06 \$3 \$3 empty 20 8 0 8
-262144 -1 -1 0 1;
#X obj 65 30 hsl 128 15 0 127 0 0 \$4 \$4 empty 20 8 0 8 -262144 -1
-1 0 1;
#X obj 63 63 hdl 15 1 2.6624e+06 8 \$5 \$5 empty 20 8 192 8 -262144
-1 -1 0;
#X obj 62 99 vu 15 120 \$6 empty 35 8 0 8 -66577 -1 1 0;
#X obj 115 99 cnv 15 100 60 \$7 \$7 \$7 20 12 0 14 -233017 -66577 1.04858e+06
;
#X obj 41 308 inlet;
#X obj 41 334 outlet;
#X obj 227 30 vdl 15 1 4.79232e+06 8 \$9 \$9 empty 20 8 192 8 -262144
-1 -1 0;
#X obj 116 176 nbx 5 14 -1e+37 1e+37 0 0 \$8 \$8 empty 45 7 0 10 -262144
-1 -1 0 256 0;
% 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
/*
* HOWTO write an External for Pure data
* (c) 2001-2006 IOhannes m zmölnig zmoelnig[AT]iem.at
*
* this is the source-code for the first example in the HOWTO
* it creates an object that prints "Hello world!" whenever it
* gets banged.
*
* for legal issues please see the file LICENSE.txt
*/
/**
* include the interface to Pd
*/
#include "m_pd.h"
/**
* define a new "class"
*/
static t_class *helloworld_class;
/**
* this is the dataspace of our new object
* we don't need to store anything,
* however the first (and only) entry in this struct
* is mandatory and of type "t_object"
*/
typedef struct _helloworld {
t_object x_obj;
} t_helloworld;
/**
* this method is called whenever a "bang" is sent to the object
* the name of this function is arbitrary and is registered to Pd in the
* helloworld_setup() routine
*/
void helloworld_bang(t_helloworld *x)
{
/*
* post() is Pd's version of printf()
* the string (which can be formatted like with printf()) will be
* output to wherever Pd thinks it has too (pd's console, the stderr...)
* it automatically adds a newline at the end of the string
*/
post("Hello world !!");
}
/**
* this is the "constructor" of the class
* this method is called whenever a new object of this class is created
* the name of this function is arbitrary and is registered to Pd in the
* helloworld_setup() routine
*/
void *helloworld_new(void)
{
/*
* call the "constructor" of the parent-class
* this will reserve enough memory to hold "t_helloworld"
*/
t_helloworld *x = (t_helloworld *)pd_new(helloworld_class);
/*
* return the pointer to the class - this is mandatory
* if you return "0", then the object-creation will fail
*/
return (void *)x;
}
/**
* define the function-space of the class
* within a single-object external the name of this function is special
*/
void helloworld_setup(void) {
/* create a new class */
helloworld_class = class_new(gensym("helloworld"), /* the object's name is "helloworld" */
(t_newmethod)helloworld_new, /* the object's constructor is "helloworld_new()" */
0, /* no special destructor */
sizeof(t_helloworld), /* the size of the data-space */
CLASS_DEFAULT, /* a normal pd object */
0); /* no creation arguments */
/* attach functions to messages */
/* here we bind the "helloworld_bang()" function to the class "helloworld_class()" -
* it will be called whenever a bang is received
*/
class_addbang(helloworld_class, helloworld_bang);
}
# Makefile
# (c) 2006 IOhannes m zmlnig
# just use the Makefile in ../example1/
# (so i only have to maintain one Makefile)
include ../example1/Makefile
/*
* HOWTO write an External for Pure data
* (c) 2001-2006 IOhannes m zmölnig zmoelnig[AT]iem.at
*
* this is the source-code for the second example in the HOWTO
* it creates an object that increments and outputs a counter
* whenever it gets banged.
*
* for legal issues please see the file LICENSE.txt
*/
/**
* include the interface to Pd
*/
#include "m_pd.h"
/**
* define a new "class"
*/
static t_class *counter_class;
/**
* this is the dataspace of our new object
* the first (mandatory) "t_object"
* and a variable that holds the current counter value
*/
typedef struct _counter {
t_object x_obj;
t_int i_count;
} t_counter;
/**
* this method is called whenever a "bang" is sent to the object
* a reference to the class-dataspace is given as argument
* this enables us to do something with the data (e.g. increment the counter)
*/
void counter_bang(t_counter *x)
{
/*
* convert the current counter value to floating-point to output it later
*/
t_float f=x->i_count;
/* increment the counter */
x->i_count++;
/* send the old counter-value to the 1st outlet of the object */
outlet_float(x->x_obj.ob_outlet, f);
}
/**
* this is the "constructor" of the class
* we have one argument of type floating-point (as specified below in the counter_setup() routine)
*/
void *counter_new(t_floatarg f)
{
t_counter *x = (t_counter *)pd_new(counter_class);
/* set the counter value to the given argument */
x->i_count=f;
/* create a new outlet for floating-point values */
outlet_new(&x->x_obj, &s_float);
return (void *)x;
}
/**
* define the function-space of the class
*/
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0,
sizeof(t_counter),
CLASS_DEFAULT,
A_DEFFLOAT, 0); /* the object takes one argument which is a floating-point and defaults to 0 */
/* call a function when object gets banged */
class_addbang(counter_class, counter_bang);
}
# Makefile
# (c) 2006 IOhannes m zmlnig
# just use the Makefile in ../example1/
# (so i only have to maintain one Makefile)
include ../example1/Makefile
/*
* HOWTO write an External for Pure data
* (c) 2001-2006 IOhannes m zmölnig zmoelnig[AT]iem.at
*
* this is the source-code for the third example in the HOWTO
* it creates an object that increments and outputs a counter
* whenever it gets banged.
* the counter value can be "set" to a special value, or "reset" to a default
* an upper and lower boundary can be specified: whenever the counter crosses
* such boundary a "bang" is emitted at the 2nd outlet and the counter value wraps
*
* for legal issues please see the file LICENSE.txt
*/
/**
* include the interface to Pd
*/
#include "m_pd.h"
/**
* define a new "class"
*/
static t_class *counter_class;
/**
* this is the dataspace of our new object
* the first element is the mandatory "t_object"
* then we have all sort of variables for the
* actual counter value, the step-size and the counting boundaries
* finally we have 2 "t_outlet" elements so we can send data
* to a "named" outlet.
*/
typedef struct _counter {
t_object x_obj; /* mandatory t_object */
t_int i_count; /* the current counter value */
t_float step; /* step size;
* this is "float" because of the passive inlet we are using */
t_int i_down, i_up; /* lower and upper boundary */
t_outlet *f_out, *b_out; /* outlets */
} t_counter;
/**
* this method is called whenever a "bang" is sent to the object
*/
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;
/* we crossed the upper boundary, so we send a bang out of
* the 2nd outlet (which is x->b_out)
*/
outlet_bang(x->b_out);
} else if (x->i_count < x->i_down) {
x->i_count = x->i_up;
outlet_bang(x->b_out);
}
}
/* output the current counter value at the 1st outlet (which is x->f_out) */
outlet_float(x->f_out, f);
}
/**
* this is called whenever a "reset" message is sent to the inlet of the object
* since the "reset" message has no arguments (as declared in counter_setup())
* we only get a reference to the class-dataspace
*/
void counter_reset(t_counter *x)
{
x->i_count = x->i_down;
}
/**
* this is called whenever a "set" message is sent to the inlet of the object
* since the "set" message has one floating-point argument (as declared in counter_setup())
* we get a reference to the class-dataspace and the value
*/
void counter_set(t_counter *x, t_floatarg f)
{
x->i_count = f;
}
/**
* this is called whenever a "bound" message is sent to the inlet of the object
* note that in counter_new(), we rewrite a list to the 2nd inlet
* to a "bound" message to the 1st inlet
*/
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;
}
/**
* this is the "constructor" of the class
* we expect a variable number of arguments to this object
* symbol "s" is the name of the object itself
* the arguments are given as a t_atom array of argc elements.
*/
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;
/* depending on the number of arguments we interprete them differently */
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;
/* create a new active inlet for this object
* a message with the selector "list" that is sent
* to this inlet (it is the 2nd inlet from left),
* will be appear to be the same message but with the selector "bound"
* at the 1st inlet.
* the method for "bound" messages is given in counter_setup()
*/
inlet_new(&x->x_obj, &x->x_obj.ob_pd,
gensym("list"), gensym("bound"));
/* create a passive inlet inlet (it will be the 2rd inlet from left)
* whenever a floating point number is sent to this inlet,
* its value will be immediately stored in "x->step"
* no function will be called
*/
floatinlet_new(&x->x_obj, &x->step);
/* create a new outlet which will output floats
* we store a reference to this outlet in x->f_out
* so we are able to send data to this very outlet
*/
x->f_out = outlet_new(&x->x_obj, &s_float);
/* create a new outlet which will output bangs */
x->b_out = outlet_new(&x->x_obj, &s_bang);
return (void *)x;
}
/**
* define the function-space of the class
*/
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_GIMME, /* an arbitrary number of arguments
* which are of arbitrary type */
0);
/* call a function when a "bang" message appears on the first inlet */
class_addbang (counter_class, counter_bang);
/* call a function when a "reset" message (without arguments) appears on the first inlet */
class_addmethod(counter_class,
(t_method)counter_reset, gensym("reset"), 0);
/* call a function when a "set" message with one float-argument (defaults to 0)
* appears on the first inlet */
class_addmethod(counter_class,
(t_method)counter_set, gensym("set"),
A_DEFFLOAT, 0);
/* call a function when a "bound" message with 2 float-argument (both default to 0)
* appears on the first inlet
* this is used for "list" messages which appear on the 2nd inlet
* the magic is done in counter_new()
*/
class_addmethod(counter_class,
(t_method)counter_bound, gensym("bound"),
A_DEFFLOAT, A_DEFFLOAT, 0);
/* set the name of the help-patch to "help-counter"(.pd) */
class_sethelpsymbol(counter_class, gensym("help-counter"));
}
# Makefile
# (c) 2006 IOhannes m zmlnig
# just use the Makefile in ../example1/
# (so i only have to maintain one Makefile)
include ../example1/Makefile