aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--abstractions/pdp_conv_emboss.pd7
-rw-r--r--abstractions/pdp_qt_control.pd147
-rw-r--r--abstractions/pdp_qtloop2~.pd190
-rw-r--r--abstractions/pdp_xv_keycursor.pd77
-rwxr-xr-xbuildall18
-rw-r--r--debug/teststuff.c29
-rw-r--r--doc/misc/devdoc.html167
-rw-r--r--doc/misc/layers.txt222
-rw-r--r--doc/misc/overview.html102
-rw-r--r--doc/misc/todo.jme4
-rw-r--r--doc/objects/pdp_netsend.pd62
-rw-r--r--doc/objects/pdp_rawin.pd28
-rw-r--r--doc/objects/pdp_rawout.pd28
-rw-r--r--include/pdp_debug.h16
-rw-r--r--include/pdp_mem.h46
-rw-r--r--include/pdp_net.h197
-rw-r--r--include/pdp_pd.h7
-rw-r--r--include/pdp_post.h29
-rw-r--r--include/pdp_symbol.h136
-rw-r--r--include/pdp_xvideo.h78
-rw-r--r--modules/generic/pdp_rawin.c299
-rw-r--r--modules/generic/pdp_rawout.c320
-rw-r--r--modules/generic/pdp_udp_receive.c203
-rw-r--r--modules/generic/pdp_udp_send.c336
-rw-r--r--opengl/doc/examples/arm.pd40
-rw-r--r--opengl/doc/examples/example01.pd257
-rw-r--r--opengl/doc/examples/example02.pd46
-rw-r--r--opengl/doc/examples/example03.pd65
-rw-r--r--opengl/doc/examples/example04.pd25
-rw-r--r--opengl/doc/examples/example05.pd41
-rw-r--r--opengl/doc/examples/example06.pd85
-rw-r--r--opengl/doc/examples/example07.pd25
-rw-r--r--opengl/doc/examples/example08.pd94
-rw-r--r--opengl/doc/examples/example09.pd90
-rw-r--r--opengl/doc/examples/example10.pd39
-rw-r--r--opengl/doc/examples/example11.pd77
-rw-r--r--opengl/doc/examples/example12.pd90
-rw-r--r--opengl/doc/examples/example13.pd81
-rw-r--r--opengl/doc/examples/example14.pd66
-rw-r--r--opengl/doc/examples/example15.pd126
-rw-r--r--opengl/doc/examples/example16.pd107
-rw-r--r--opengl/doc/objects/3dp_for.pd91
-rw-r--r--puredata/CONTENTS10
-rw-r--r--puredata/Makefile12
-rw-r--r--puredata/pdp_base.c415
-rw-r--r--puredata/pdp_comm.c367
-rw-r--r--puredata/pdp_compat.c57
-rw-r--r--puredata/pdp_control.c186
-rw-r--r--puredata/pdp_dpd_base.c270
-rw-r--r--puredata/pdp_forthproc.c_bak807
-rw-r--r--puredata/pdp_imagebase.c79
-rw-r--r--puredata/pdp_queue.c386
-rw-r--r--puredata/pdp_ut.c262
-rw-r--r--scaf/test/test_pdp_ca2.pd88
-rw-r--r--system/X11/pdp_xvideo.c201
-rw-r--r--system/kernel/CONTENTS7
-rw-r--r--system/kernel/pdp_debug.c21
-rw-r--r--system/kernel/pdp_mem.c129
-rw-r--r--system/kernel/pdp_packet2.c623
-rw-r--r--system/kernel/pdp_post.c48
-rw-r--r--system/kernel/pdp_symbol.c196
-rw-r--r--system/net/Makefile11
-rw-r--r--system/net/pdp_net.c685
63 files changed, 9007 insertions, 46 deletions
diff --git a/abstractions/pdp_conv_emboss.pd b/abstractions/pdp_conv_emboss.pd
index 427ee45..5346bde 100644
--- a/abstractions/pdp_conv_emboss.pd
+++ b/abstractions/pdp_conv_emboss.pd
@@ -1,4 +1,4 @@
-#N canvas 222 642 591 233 10;
+#N canvas 172 505 641 290 10;
#X obj 30 55 inlet;
#X obj 29 206 outlet;
#X obj 29 93 pdp_conv;
@@ -15,6 +15,7 @@ an emboss effect using the convolution mask (-1 0 0 \; 0 0 0 \; 0 0
1) the right inlet sets the chroma offset.;
#X msg 218 60 vmask 0 0 1;
#X msg 108 60 vmask -1 0 0;
+#X msg 108 128 chanmask 1;
#X connect 0 0 2 0;
#X connect 0 0 5 0;
#X connect 2 0 3 0;
@@ -31,4 +32,6 @@ an emboss effect using the convolution mask (-1 0 0 \; 0 0 0 \; 0 0
#X connect 10 0 6 2;
#X connect 12 0 5 0;
#X connect 13 0 2 0;
-#X connect 13 0 7 0;
+#X connect 13 0 14 0;
+#X connect 14 0 6 0;
+#X connect 14 0 7 0;
diff --git a/abstractions/pdp_qt_control.pd b/abstractions/pdp_qt_control.pd
new file mode 100644
index 0000000..7bb2de8
--- /dev/null
+++ b/abstractions/pdp_qt_control.pd
@@ -0,0 +1,147 @@
+#N canvas 287 151 577 553 10;
+#X obj 390 48 inlet;
+#X text 315 10 nb of frames;
+#X text 313 23 connect pdp_qt's 3rd outlet here;
+#X obj 390 99 s \$0-frames;
+#X obj 8 9 inlet;
+#X msg 150 186 pp;
+#X msg 53 85 set l;
+#X msg 104 87 set pp;
+#X msg 160 87 set r;
+#N canvas 821 623 335 233 randplay 0;
+#X obj 82 122 random;
+#X obj 111 96 r \$0-frames;
+#X msg 73 81 bang;
+#X obj 69 39 inlet;
+#X obj 82 153 outlet;
+#X text 154 24 random frame playback;
+#X connect 0 0 4 0;
+#X connect 1 0 0 1;
+#X connect 2 0 0 0;
+#X connect 3 0 2 0;
+#X restore 377 288 pd randplay;
+#N canvas 418 263 365 227 loopplay 0;
+#X obj 63 79 f 0;
+#X obj 63 101 + 1;
+#X obj 63 147 mod;
+#X obj 70 121 r \$0-frames;
+#X msg 62 59 bang;
+#X obj 62 30 inlet;
+#X obj 63 182 outlet;
+#X text 166 33 normal looped playback;
+#X connect 0 0 1 0;
+#X connect 1 0 2 0;
+#X connect 2 0 0 1;
+#X connect 2 0 6 0;
+#X connect 3 0 2 1;
+#X connect 4 0 0 0;
+#X connect 5 0 4 0;
+#X restore 59 284 pd loopplay;
+#X obj 4 35 route bang;
+#N canvas 253 244 365 227 rloopplay 0;
+#X obj 63 79 f 0;
+#X obj 63 147 mod;
+#X obj 96 129 r \$0-frames;
+#X msg 62 59 bang;
+#X obj 62 30 inlet;
+#X obj 63 182 outlet;
+#X obj 63 101 + 1;
+#X obj 133 79 r \$0-frames;
+#X obj 132 102 - 1;
+#X text 166 33 reverse looped playback;
+#X connect 0 0 6 0;
+#X connect 1 0 0 1;
+#X connect 1 0 5 0;
+#X connect 2 0 1 1;
+#X connect 3 0 0 0;
+#X connect 4 0 3 0;
+#X connect 6 0 1 0;
+#X connect 7 0 8 0;
+#X connect 8 0 6 1;
+#X restore 152 284 pd rloopplay;
+#X msg 215 87 set rl;
+#N canvas 0 0 450 300 addjitter 0;
+#X obj 150 65 inlet;
+#X obj 177 244 outlet;
+#X obj 259 66 inlet;
+#X obj 259 90 abs;
+#X obj 225 173 r \$0-frames;
+#X obj 186 201 mod;
+#X obj 259 113 + 1;
+#X obj 211 139 random 1;
+#X text 32 26 add jitter. sort of. something like the nervous effect.
+;
+#X obj 160 115 t f b;
+#X obj 185 169 +;
+#X connect 0 0 9 0;
+#X connect 2 0 3 0;
+#X connect 3 0 6 0;
+#X connect 4 0 5 1;
+#X connect 5 0 1 0;
+#X connect 6 0 7 1;
+#X connect 7 0 10 1;
+#X connect 9 0 10 0;
+#X connect 9 1 7 0;
+#X connect 10 0 5 0;
+#X restore 148 395 pd addjitter;
+#X obj 150 426 outlet;
+#X obj 53 60 route loop pingpong random rloop nervous;
+#X obj 95 419 print;
+#N canvas 751 514 434 327 pingpongplay 1;
+#X obj 117 73 r \$0-frames;
+#X msg 62 59 bang;
+#X obj 62 30 inlet;
+#X obj 56 243 outlet;
+#X text 120 13 pingpong (palindrome) looped playback;
+#X obj 118 121 * 2;
+#X obj 55 191 -;
+#X obj 55 213 abs;
+#X obj 63 79 f 0;
+#X floatatom 113 203 5 0 0 0 - - -;
+#X obj 58 108 + 1;
+#X obj 62 141 mod;
+#X obj 117 98 - 1;
+#X connect 0 0 12 0;
+#X connect 1 0 8 0;
+#X connect 2 0 1 0;
+#X connect 5 0 11 1;
+#X connect 6 0 7 0;
+#X connect 7 0 3 0;
+#X connect 7 0 9 0;
+#X connect 8 0 10 0;
+#X connect 10 0 11 0;
+#X connect 11 0 6 0;
+#X connect 11 0 8 1;
+#X connect 12 0 5 0;
+#X connect 12 0 6 1;
+#X restore 249 284 pd pingpongplay;
+#X obj 150 217 route l rl pp r;
+#X text 60 12 left input: metro and control;
+#X text 49 465 loop modes: loop (normal) \, rloop (reverse loop) \,
+pingpong (palindrome) \, random. send a nervous <amount> message to
+add some random jitter.;
+#X obj 390 75 abs;
+#X connect 0 0 22 0;
+#X connect 4 0 11 0;
+#X connect 5 0 19 0;
+#X connect 6 0 5 0;
+#X connect 7 0 5 0;
+#X connect 8 0 5 0;
+#X connect 9 0 14 0;
+#X connect 10 0 14 0;
+#X connect 11 0 5 0;
+#X connect 11 1 16 0;
+#X connect 12 0 14 0;
+#X connect 13 0 5 0;
+#X connect 14 0 15 0;
+#X connect 16 0 6 0;
+#X connect 16 1 7 0;
+#X connect 16 2 8 0;
+#X connect 16 3 13 0;
+#X connect 16 4 14 1;
+#X connect 18 0 14 0;
+#X connect 19 0 10 0;
+#X connect 19 1 12 0;
+#X connect 19 2 18 0;
+#X connect 19 3 9 0;
+#X connect 22 0 3 0;
diff --git a/abstractions/pdp_qtloop2~.pd b/abstractions/pdp_qtloop2~.pd
new file mode 100644
index 0000000..237f60c
--- /dev/null
+++ b/abstractions/pdp_qtloop2~.pd
@@ -0,0 +1,190 @@
+#N canvas 526 20 677 778 10;
+#X obj 82 83 inlet;
+#X obj 255 82 inlet;
+#X obj 103 506 pdp_loop;
+#X obj 154 702 outlet;
+#X obj 18 473 r \$0-loopin;
+#X obj 24 145 s \$0-filename;
+#X text 60 59 bang or hot frame;
+#X text 142 727 packet outlet;
+#X msg 132 137 bang;
+#X obj 82 111 route open bang;
+#X obj 103 554 pdp_convert image/YCrCb/*;
+#X obj 294 214 s \$0-playaudioframe;
+#X obj 294 237 outlet;
+#X obj 132 375 mod;
+#X obj 178 345 r \$0-nbframes;
+#X obj 418 363 outlet;
+#X obj 495 137 outlet~;
+#X obj 581 133 outlet~;
+#N canvas 98 558 711 881 more_logic 0;
+#X obj 215 518 pdp_qt;
+#X obj 246 772 symbol \$0-L;
+#X obj 340 773 symbol \$0-R;
+#X msg 246 797 dump \$1 0;
+#X msg 340 797 dump \$1 1;
+#X msg 140 473 open \$1;
+#X obj 21 367 symbol;
+#X obj 216 729 t f b;
+#X obj 232 490 r \$0-qtin;
+#X obj 340 833 s \$0-qtin;
+#X obj 245 318 s \$0-loopin;
+#X obj 21 338 r \$0-filename;
+#X obj 215 692 s \$0-loopin;
+#X text 337 227 new movie loaded \, nb of frames received here;
+#X text 268 730 dump audio;
+#X text 374 288 set loop size;
+#X obj 215 463 until;
+#X text 262 465 dump frames;
+#X obj 215 258 t f f;
+#X msg 245 289 size \$1 \, record;
+#X text 374 303 start recording;
+#X obj 199 435 f 0;
+#X obj 252 551 s \$0-nbframes;
+#X obj 215 227 r \$0-nbframes;
+#X msg 21 494 DONE;
+#X obj 21 671 print pdp_qtloop~;
+#X obj 215 661 pdp_convert bitmap/yv12/*;
+#X obj 215 348 t f f;
+#X obj 233 601 pack 0 0;
+#X obj 21 393 t b b b s;
+#X msg 233 627 decoding frame \$1 of \$2;
+#X obj 233 576 + 1;
+#X text 407 661 images are stored in 12bpp;
+#X obj 217 173 outlet~;
+#X obj 340 172 outlet~;
+#X obj 217 106 * 1920;
+#X obj 217 41 r \$0-playaudioframe;
+#X text 215 18 restart audio playback from frame #;
+#X text 277 106 samplerate / framerate;
+#X obj 32 32 table \$0-L;
+#X obj 32 56 table \$0-R;
+#N canvas 367 318 450 300 fracframemodulo 0;
+#X obj 93 43 inlet;
+#X obj 93 274 outlet;
+#X obj 93 97 /;
+#X obj 109 67 r \$0-nbframes;
+#X obj 123 147 int;
+#X obj 93 122 t f f;
+#X obj 93 179 -;
+#X obj 109 220 r \$0-nbframes;
+#X obj 93 248 *;
+#X connect 0 0 2 0;
+#X connect 2 0 5 0;
+#X connect 3 0 2 1;
+#X connect 4 0 6 1;
+#X connect 5 0 6 0;
+#X connect 5 1 4 0;
+#X connect 6 0 8 0;
+#X connect 7 0 8 1;
+#X connect 8 0 1 0;
+#X restore 217 68 pd fracframemodulo;
+#X obj 216 144 tabreadmix~ \$0-L;
+#X obj 340 143 tabreadmix~ \$0-R;
+#X obj 518 73 inlet;
+#X connect 0 0 26 0;
+#X connect 0 1 31 0;
+#X connect 0 2 22 0;
+#X connect 1 0 3 0;
+#X connect 2 0 4 0;
+#X connect 3 0 9 0;
+#X connect 4 0 9 0;
+#X connect 5 0 0 0;
+#X connect 6 0 29 0;
+#X connect 7 1 1 0;
+#X connect 7 1 2 0;
+#X connect 8 0 0 0;
+#X connect 11 0 6 0;
+#X connect 16 0 0 0;
+#X connect 18 0 27 0;
+#X connect 18 1 19 0;
+#X connect 19 0 10 0;
+#X connect 21 0 16 0;
+#X connect 23 0 18 0;
+#X connect 24 0 25 0;
+#X connect 26 0 12 0;
+#X connect 27 0 21 1;
+#X connect 27 0 28 1;
+#X connect 28 0 30 0;
+#X connect 29 0 24 0;
+#X connect 29 1 7 0;
+#X connect 29 2 21 0;
+#X connect 29 3 5 0;
+#X connect 30 0 25 0;
+#X connect 31 0 28 0;
+#X connect 35 0 42 0;
+#X connect 35 0 43 0;
+#X connect 36 0 41 0;
+#X connect 41 0 35 0;
+#X connect 42 0 33 0;
+#X connect 43 0 34 0;
+#X connect 44 0 43 1;
+#X connect 44 0 42 1;
+#X restore 495 104 pd more_logic;
+#X obj 132 170 f;
+#X obj 132 287 t f f;
+#X text 240 61 cold frame;
+#X obj 162 375 mod;
+#X obj 162 319 + 1;
+#X obj 118 613 pdp_route;
+#X obj 154 661 pdp_mix;
+#X text 273 257 frame nb outlet;
+#X msg 176 461 0;
+#X msg 208 461 1;
+#X obj 176 488 f;
+#X text 215 408 get current and next frame from pdp_loop;
+#X obj 132 219 int;
+#X obj 206 267 -;
+#X obj 132 196 t f f f;
+#X obj 132 249 t f f;
+#X text 277 317 crossfade fraction;
+#X obj 206 317 s \$0-frac;
+#X obj 198 631 r \$0-frac;
+#X obj 206 292 * -1;
+#X obj 103 408 t f b;
+#X obj 162 408 t f b;
+#X text 397 385 nb frames outlet;
+#X text 249 19 like pdp_qtloop~ but uses tabreadmix~ from creb;
+#X obj 497 77 inlet;
+#X text 479 57 grain size;
+#X connect 0 0 9 0;
+#X connect 1 0 19 1;
+#X connect 2 0 10 0;
+#X connect 4 0 2 0;
+#X connect 8 0 19 0;
+#X connect 9 0 5 0;
+#X connect 9 1 8 0;
+#X connect 9 2 19 0;
+#X connect 10 0 24 0;
+#X connect 13 0 39 0;
+#X connect 14 0 13 1;
+#X connect 14 0 22 1;
+#X connect 14 0 15 0;
+#X connect 18 0 16 0;
+#X connect 18 1 17 0;
+#X connect 19 0 33 0;
+#X connect 20 0 13 0;
+#X connect 20 1 23 0;
+#X connect 22 0 40 0;
+#X connect 23 0 22 0;
+#X connect 24 0 25 0;
+#X connect 24 1 25 1;
+#X connect 25 0 3 0;
+#X connect 27 0 29 0;
+#X connect 28 0 29 0;
+#X connect 29 0 24 1;
+#X connect 31 0 34 0;
+#X connect 32 0 38 0;
+#X connect 33 0 31 0;
+#X connect 33 1 32 1;
+#X connect 33 2 12 0;
+#X connect 33 2 11 0;
+#X connect 34 0 20 0;
+#X connect 34 1 32 0;
+#X connect 37 0 25 2;
+#X connect 38 0 36 0;
+#X connect 39 0 2 0;
+#X connect 39 1 27 0;
+#X connect 40 0 2 0;
+#X connect 40 1 28 0;
+#X connect 43 0 18 0;
diff --git a/abstractions/pdp_xv_keycursor.pd b/abstractions/pdp_xv_keycursor.pd
new file mode 100644
index 0000000..a10a708
--- /dev/null
+++ b/abstractions/pdp_xv_keycursor.pd
@@ -0,0 +1,77 @@
+#N canvas 135 108 708 682 10;
+#X obj 19 103 print;
+#X obj 19 67 pdp_xv;
+#X obj 111 168 route keypress;
+#X obj 367 215 table \$0-x;
+#X obj 87 394 tabwrite \$0-x;
+#X obj 396 335 tabread \$0-x;
+#X obj 486 335 tabread \$0-y;
+#X obj 409 383 pack s 0 0;
+#X obj 183 392 tabwrite \$0-y;
+#X obj 77 260 route motion;
+#X obj 88 367 unpack 0 0;
+#X obj 444 215 table \$0-y;
+#X obj 87 339 spigot;
+#X msg 157 311 0;
+#X msg 125 312 1;
+#X obj 157 288 route keyrelease;
+#X msg 385 360 movecursor;
+#X msg 409 412 \$1 \$2 \$3;
+#X obj 18 468 outlet;
+#X obj 17 128 t a a;
+#X obj 403 277 t b f f;
+#X obj 88 426 outlet;
+#X obj 152 423 outlet;
+#X obj 267 428 outlet;
+#X obj 239 231 t f f;
+#X obj 409 438 s \$0-control;
+#X obj 19 40 r \$0-control;
+#X obj 20 16 inlet;
+#X text 182 53 a keyboard/mouse controller using pdp_xv;
+#X text 180 76 hold a key while moving the mouse to update the x/y
+controller value. hint: turn off keyboard autoropeat using "xset r
+off" in a terminal window.;
+#X text 252 454 scan code;
+#X text 82 450 x coord;
+#X text 151 449 y coord;
+#X text 9 494 plain events;
+#X obj 450 119 inlet;
+#X obj 450 143 unpack 0 0 0;
+#X obj 382 175 tabwrite \$0-x;
+#X obj 480 175 tabwrite \$0-y;
+#X connect 1 0 0 0;
+#X connect 1 0 19 0;
+#X connect 2 0 24 0;
+#X connect 2 1 9 0;
+#X connect 5 0 7 1;
+#X connect 6 0 7 2;
+#X connect 7 0 17 0;
+#X connect 9 0 12 0;
+#X connect 9 1 15 0;
+#X connect 10 0 4 0;
+#X connect 10 0 21 0;
+#X connect 10 1 8 0;
+#X connect 10 1 22 0;
+#X connect 12 0 10 0;
+#X connect 13 0 12 1;
+#X connect 14 0 12 1;
+#X connect 15 0 13 0;
+#X connect 16 0 7 0;
+#X connect 17 0 25 0;
+#X connect 19 0 18 0;
+#X connect 19 1 2 0;
+#X connect 20 0 16 0;
+#X connect 20 1 5 0;
+#X connect 20 2 6 0;
+#X connect 24 0 4 1;
+#X connect 24 0 8 1;
+#X connect 24 0 23 0;
+#X connect 24 0 14 0;
+#X connect 24 1 20 0;
+#X connect 26 0 1 0;
+#X connect 27 0 1 0;
+#X connect 34 0 35 0;
+#X connect 35 0 36 0;
+#X connect 35 1 37 0;
+#X connect 35 2 37 1;
+#X connect 35 2 36 1;
diff --git a/buildall b/buildall
new file mode 100755
index 0000000..155633f
--- /dev/null
+++ b/buildall
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# a build script to build and configure the entire pdp distro
+# might need some editing to work for you
+
+
+# build the stable stuff
+autoconf && ./configure --enable-mmx --prefix=/home/tom/pd && make install
+
+cd scaf
+autoconf && ./configure && make install
+cd ..
+
+
+# build the experimental stuff
+
+make -C opengl
+
diff --git a/debug/teststuff.c b/debug/teststuff.c
new file mode 100644
index 0000000..1718e57
--- /dev/null
+++ b/debug/teststuff.c
@@ -0,0 +1,29 @@
+/* some test stuff */
+
+#if 0
+
+ {
+ t_pdp_list *l = pdp_list_from_cstring("(een 2 3. (1 1 1 1 1))", 0);
+ t_pdp_list *s = pdp_list_from_cstring("(symbol int float (int int ...))", 0);
+ //PDP_ASSERT(0);
+ pdp_list_print(l);
+ pdp_list_print(s);`
+ post("%d", pdp_tree_check_syntax(l, s));
+ exit(1);
+ }
+#endif
+
+#if 0
+
+ {
+ char *c = "(test 1 2 (23 4)) ( 1 [asdflkj; las;dlfkj;a sdf]) (een (zes (ze)ven ())) [";
+ while (*c){
+ t_pdp_list *l = pdp_list_from_cstring(c, &c);
+ if (l) pdp_list_print(l);
+ else{
+ post("parse error: remaining input: %s", c); break;
+ }
+ }
+ exit(1);
+ }
+#endif
diff --git a/doc/misc/devdoc.html b/doc/misc/devdoc.html
new file mode 100644
index 0000000..5ce1f83
--- /dev/null
+++ b/doc/misc/devdoc.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <title>PDP Developer Documentation</title>
+ </head>
+
+ <body>
+ <h1>PDP Developer Documentation</h1>
+
+ <h2>Introduction</h2>
+
+ <p>There is not yet much developer information, partly because pdp is not that big and since the goals are
+ not completely clear yet, a lot will probably change on the inside in the future. I believe it is
+ not too hard to figure out how it works, once you get started somewhere. This document is a minimalistic
+ attempt to provide that starting point. For full prototypes see the header files. I suggest you have a look at the pdp_base base class, and some simple
+ modules: pdp_add, pdp_noise and pdp_gain for examples.
+
+ <h2> PDP architecture </h2>
+ <p> Architecture is a big word, but pdp is organized as modules. A packet pool module (a reuse pool memory manager),
+ a packet class, a processing queue module, a high level type conversion module, an image packet class, and some
+ low level modules for image type conversion, image resampling and all sorts of other image processing. Besides that
+ there are 2 extension libraries: pdp_scaf, a cellular automata extension and pdp_opengl, a 3d rendering extension.
+ These are separate because of portability issues. The different pdp_* externs in the main pdp library use the
+ core modules' functionality to minimize code duplication. I'm relatively happy with how it fits together,
+ but some things need to change for future plans. Most objects are written in the object oriented c style of pd.
+ To prevent namespace conflicts, (almost) all routines start with the pdp_ prefix. The second name is the name of the
+ object or module they belong to. The first argument is always a pointer to an object or an integer (for packets).
+
+
+ <h2> PD ties </h2>
+ <p> PDP is written as an extension for PD. One of the goals of pdp is to evolve to a separate library that can
+ be reused in other software. The architecture will be split into two parts. A pd-independent part (the packet classes,
+ the packet pool, the type conversion system and the forth system) and a part with pd specific stuff (the process queue and interfaces to the
+ pd system like the base classes and the pd communication protocol). In order to do this the packet class will probably
+ evolve to a proper object model, supporting run time attribute binding (inspired by the python object model).
+
+ <p>There are some things that put a stamp on the current pdp design. Most importantly pd's processor object model and
+ communication protocol. (i.e. the fact that pd only supports unidirectional messaging creates the awkward concept
+ of a "passing packet" to eliminate excessive data copying.)
+
+ <p> In pd, the pdp messaging protocol is implemented as pd messages. The protocol is however 3 phase.
+ With a read only register phase, a read/write register phase and a process phase. This functionality
+ is part of the base class or the forth processor object. The dpd protocol is entirely different,
+ and is used in the opengl library. It is
+ not based on parallel dataflow but serial context passing.
+
+ <h2> Packets </h2>
+ <p> PDP introduces a new atom: the data packet. This can contain all kinds of data. Images (16bit/8bit), cellular
+ automata (1bit), matrices (real/complex float/double), opengl textures and 3d rendering contexts. Packets
+ are stored in a pool to ensure fast reuse, and to enable sharing. The paradigm is centered around a
+ combination of an object oriented approach and a dataflow approach.
+ <p>The methods operating on packets
+ (pdp_packet_*) are mainly for administrative purposes: memory management (construction, registering, copying)
+ and getting or setting info.
+ <p>All processing is done in the pd modules. Processors can be defined using
+ the forth scripting language, but this is still experimental. The forth system can be accessed
+ from the guile library.
+ <p> There is a central mechanism for packet type conversion. This is to facilitate the combination of different
+ media types. Whenever a packet class is constructed (i.e. in an extension library), a number of conversion
+ routines should be defined to convert the added type to one or some of the main pdp types.
+
+
+
+
+
+
+ <h2>PDP API Overview</h2>
+
+ The pdp public api contains only a single class: the packet. (The internal api has more classes, that can be used
+ too if necessary, but i won't document them.) A packet is a class in pdp. The table below lists the supported methods.
+ The first argument of a call is a packet id.
+
+ <TABLE border = "1">
+ <TR><TH colspan = "2">pdp_packet_*
+ <TR><TD>new <TD>construct a raw packet (depreciated)
+ <TR><TD>new_* <TD>construct packet of specific type/subtype/...
+ <TR><TD>mark_unused <TD>release
+ <TR><TD>mark_passing <TD>conditional release (release on first copy ro/rw)
+ <TR><TD>copy_ro <TD>readonly (shared) copy
+ <TR><TD>copy_rw <TD>private copy
+ <TR><TD>clone_rw <TD>private copy (copies only meta data, not the content)
+ <TR><TD>header <TD>get the raw header (t_pdp *)
+ <TR><TD>data <TD>get the raw data (void *)
+ <TR><TD>pass_if_valid <TD>send a packet to pd outlet, if it is valid, and mark unused
+ <TR><TD>replace_if_valid <TD>delete packet and replace with new one, if new is valid
+ <TR><TD>copy_ro_or_drop <TD>copy readonly, or don't copy if dest slot is full + send drop notify
+ <TR><TD>copy_rw_or_drop <TD>same, but private copy
+ <TR><TD>get_description <TD>retrieve type info
+ <TR><TD>convert_ro <TD>same as copy_ro, but with an automatic conversion matching a type template
+ <TR><TD>convert_rw <TD>same as convert_ro, but producing a private copy
+ </TABLE>
+
+
+ <p>The pool object methods. All the packets are stored in a central packet pool.
+
+ <TABLE border = "1">
+ <TR><TH colspan = "2">pdp_pool_*
+ <TR><TD>collect_garbage <TD>manually free all unused resources in packet pool
+ </TABLE>
+
+ <p>The process queue object methods. PDP supports a separate processing thread.
+
+ <TABLE border = "1">
+ <TR><TH colspan = "2"> pdp_queue_*
+ <TR><TD>add <TD>add a process method + callback
+ <TR><TD>finish <TD>wait until a specific task is done
+ <TR><TD>wait <TD>wait until processing queue is done
+ </TABLE>
+
+ <p>The control methods. General pdp control messages.
+
+ <TABLE border = "1">
+ <TR><TH colspan = "2"> pdp_control_*
+ <TR><TD>notify_drop <TD>notify that a packet has been dropped
+ </TABLE>
+
+ <p> The type mediator methods.
+ <TABLE border = "1">
+ <TR><TH colspan = "2"> pdp_type_*
+ <TR><TD>description_match <TD>check if two type templates match
+ <TR><TD>register_conversion <TD>register a type conversion program
+
+
+</TABLE>
+
+
+ <p>NOTE: it is advised to derive your module from the pdp base class defined in pdp_base.h
+ instead of communicating directly with the pdp core
+
+
+
+ <h2>pdp_base class</h2>
+ If you want to write a pdp extern, you can derive it from the pdp_base class, instead of t_object.
+ This class abstracts a lot of the hassle of writing ordinary (inplace) packet processors. The base
+ allows you to register process callbacks. There are 3 kinds of callbacks: preproc, process and postproc.
+ The preproc method is called inside the pd thread. This can be used to setup some things that can only
+ be done inside the pd thread. The process method should do most of the work, and is called from the
+ pdp processing thread if it is enabled, after the preproc method is finished. You can't use most
+ of pd's calls in this method. The postproc method is called
+ from the pd thread after the process method is finished, and can be used to send data to pd outlets. Simple
+ packet processors only need the process method (packet input/output is handled by the pdp_base class).
+
+ <h2>pdp_imageproc_* modules</h2>
+ Most of the image processing code is organized as planar 16 bit signed processors.
+ This is crude and oversimplified, but it helps to keep the code size small and fast
+ at the same time (platform dependent assembly code is reduced to a bare minimum). These
+ routines can be used to build higher level image processing objects that are more (cache)
+ efficient than an abstraction using separate pdp modules. If you plan to write your own image
+ processing routines, you can use the pdp_imageproc_dispatch_ routine to support all 16bit image
+ types at once (greyscale, subsampled YCrCb, multichannel planar). This requires you write the
+ image processing routine as a planar (greyscale) processor using the pdp_imageproc_
+ interface. (see pdp_imageproc.h)
+
+ <h2>pdp_llconv call</h2>
+ Low level image conversion routines. (operating on raw data buffers). You probably won't need this,
+ since the high level type conversion (pdp_packet_convert_ro/rw) covers most of its functionality.
+
+
+
+ <hr>
+ <address><a href="mailto:pdp@zzz.kotnet.org">Tom Schouten</a></address>
+<!-- Created: Mon Apr 28 15:35:12 CEST 2003 -->
+<!-- hhmts start -->
+Last modified: Fri Sep 19 04:52:12 CEST 2003
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/doc/misc/layers.txt b/doc/misc/layers.txt
new file mode 100644
index 0000000..a02e481
--- /dev/null
+++ b/doc/misc/layers.txt
@@ -0,0 +1,222 @@
+pdp 0.13 design layers + components
+-----------------------------------
+
+from version 0.13 onwards, pdp is no longer just a pd plugin but a
+standalone unix library (libpdp). this documents is an attempt to
+describe the design layers.
+
+A. PD INTERFACE
+---------------
+
+on the top level, libpdp is interfaced to pd using a glue layer which
+consists of
+
+1. pdp/dpd protocols for pd
+2. process queue
+3. base classes for pdp/dpd
+4. some small utility pd objects
+5. pd specific interfaces to part of pdp core
+6. pdp_console
+7. pd object interface to packet forth (pdp object)
+
+
+1. is the same as previous versions to ensure backwards compatibility in
+pd with previous pdp modules and extensions that are written as pd
+externs or external libs. this includes parts of pdp that are not yet
+migrated to libpdp (some of them are very pd specific and will not be
+moved to libpdp), and pidip. if you intend to write new modules, it is
+encouraged to use the new forth based api, so your code can be part of
+libpdp to use it in other image processing applications.
+
+2. is considered a pd part. it implements multithreading of pdp inside
+pd. multithreading is considered a host interface part, since it usually
+requires special code.
+
+3. the base classes (pd objects) for pdp image processing remain part of
+the pd<->pdp layer. the reason is the same as 1. a lot of the original
+pd style pdp is written as subclasses of the pdp_base, pdp_image_base,
+dpd_base and pdp_3dp_base classes. if you need to write pd specific
+code, it is still encouraged to use these apis, since they eliminate a
+lot of red tape involving the pdp protocol. a disadvantage is that this
+api is badly documented, and the basic api (1.) is a lot simpler to
+learn and documented. 3dp is supposed to merge to the new forth api,
+along with the image/video processing code.
+
+4. is small enough to be ignored here
+
+5. includes interfaces to thread system and type conversion system +
+some pd specific stuff using 1. or 3.
+
+6. the console interface to the pdp core, basicly a console for a
+forth like language called packet forth which is pdp's main scripting
+language. it's inteded for develloping and testing pdp but it can be
+used to write controllers for pd/pdp/... too. this is based on 1.
+
+7. is the main link between the new libpdp and pd. it is used to
+instantiate pdp processors in pd which are written in the packet forth.
+i.e. to create a mixer, you instantiate a [pdp mix] object, which would
+be the same as the previous [pdp_mix] object. each [pdp] object creates
+a forth environment, which is initialized by calling a forth init
+method. [pdp mix] would call the forth word init_mix to create the local
+environment for the mix object. wrappers will be included for backward
+compatibility when the image processing code is moved to libpdp.
+
+
+B. PDP SYSTEM CODE
+------------------
+
+1. basic building blocks: symbol, list, memory manager
+2. packet implementation (packet class and reuse queue)
+3. packet type conversion system
+4. os interface (X11, net, png, ...)
+5. packet forth
+6. additional libraries
+
+
+1. pdp makes intensive use of symbols and lists (trees, stacks, queues).
+pdp's namespace is built on the symbol implementation. a lot of other
+code uses the list
+
+2. the pdp packet model is very simple. basicly nothing more than
+constructors (new, copy, clone), destructors (mark_unused (for reuse
+later), delete). there is no real object model for processors. this is a
+conscious decision. processor objects are implemented as packet forth
+processes with object state stored in process data space. this is enough
+to interface the functionality present in packet forth code to any
+arbitrary object oriented language or system.
+
+3. each packet type can register conversion methods to other types. the
+type conversion system does the casting. this is not completely finished
+yet (no automatic multistage casting yet) but most of it is in place and
+usable. no types without casts.
+
+4. os specific modules for input/output. not much fun here..
+
+5. All of pdp is glued together with a scripting language called packet
+forth. This is a "high level" forth dialect that can operate on floats,
+ints, symbols, lists, trees and packets. It is a "fool proof" forth,
+which is polymorphic and relatively robust to user errors (read: it
+should not crash or cause memory leaks when experimenting). It is
+intended to serve as a packet level glue language, so it is not very
+efficient for scalar code. This is usually not a problem, since packet
+operations (esp. image processing) are much more expensive than a this
+thin layer of glue connecting them.
+
+All packet operations can be accessed in the forth. If you've ever
+worked with HP's RPN calculators, you can use packet forth. The basic
+idea is to write objects in packet forth that can be used in pd or in
+other image processing applications. For more information on packet
+forth, see the code (pdp_forth.h, pdp_forth.c and words.def)
+
+6. opengl lib, based on dpd (3.) which will be moved to packet forth
+words and the cellular automata lib, which will be moved to
+vector/slice forth later.
+
+
+C. LOW LEVEL CODE
+-----------------
+
+All the packet processing code is (will be) exported as packet forth
+words. This section is about how the code exported by those words is
+structured.
+
+C.1 IMAGE PROESSING: VECTOR FORTH
+
+Eventually, image operations need to be implemented, and in order
+to do this efficiently, both codewize (good modularity) as execution speed
+wize, i've opted for another forth. DSP and forth seem to mix well, once
+you get the risc pipeline issues out of the way. And, as a less rational
+explanation, forth has this magic kind of feel, something like art..
+well, whatever :)
+
+As opposed to the packet forth, this is a "real" lowlevel forth
+optimized for performance. Its reason of being is the solution of 3
+problems: image processing code factoring, quasi optimal processor
+pipeline & instruction usage, and locality of reference for maximum
+cache performance. Expect crashes when you start experimenting with
+this. It's really nothing more than a fancy macro assembler. It has no
+safety belts. Chuck Moore doctrine..
+
+The distinction between the two forths is at first sight not a good
+example of minimizing glue layers. I.e. both systems (packet script
+forth and low level slice forth) are forths in essence, requiring
+(partial) design duplication. Both implementations are however
+significantly different which justified this design duplication.
+
+Apart from the implementation differences, the purpose of both languages
+is not the same. This requires the designs of both languages to be
+different in some respect. So, with the rule of "do everything right
+once" in mind, this small remark tries to justify the fact that forth !=
+forth.
+
+The only place where apparent design correspondence (the language model)
+is actually used is in the interface between the packet forth and the
+slice forth.
+
+The base forth is an ordinary minimal 32bit (or machine word
+lenght) subroutine threaded forth, with a native code kernel for
+i386/mmx, a portable c code kernel and room for more native code kernels
+(i.e i386/sse/sse2/3dnow, altivec, dsp, ...) Besides support for native
+machine words bit ints and pointers, no floats, since they clash with
+mmx, are not needed for the fixed point image type, and can be
+implemented using other vector instructions when needed), support for
+slices and a separate "vector stack".
+
+Vectors are the native machine vectors, i.e. 64bit for mmx/3dnow,
+128bit for sse/sse2, or anything else. The architecture is supposed to
+be open. (I've been thinking to add a block forth, operating on 256bit
+blocks to eliminate pipeline issues). Blocks are just blocks of vectors
+which are used as a basic loop unrolling data size grain for solving
+pipeline operations in slice processing words.
+
+Slices are just arrays of blocks. In the mmx forth kernel, they can
+represent 4 scanlines or a 4 colour component scanline, depending on how
+they are fed from packet data. Slices can be anything, but right now,
+they're just scanlines. The forth kernel has a very simple and efficient
+(branchless) reference count based memory allocator for slices. This
+slice allocator is also stack based which ensures locality of reference:
+a new allocated slice is the last deallocated slice.
+
+The reason for this obsession with slices is that a lot of video
+effects can be chained on the slice level (scanline or bunch of
+scanlines), which improves speed through more locality of reference. In
+concreto intermediates are not flushed to slower memory. The same
+principles can be used to do audio dsp, but that's for later.
+
+The mmx forth kernel is further factored down following another
+virtual machine paradigm. After doing some profiling, i came to the
+conclusion that the only, single paradigm way of writing efficient
+vector code on today's machines is multiple accumulators to avoid
+pipeline stalls. The nice thing about image processing is that it
+parallellizes easily. Hence the slice/block thing. This leads to the
+1-operand virtual machine concept for the mmx slice operations. The
+basic data size is one 4x4 pixel block (16bit pixels), which is
+implemented as asm macros in mmx-sliceops-macro.s and used in
+mmx-sliceops-code.s to build slice operations. The slice operations are
+built out of macro instructions for this 256bit or 512bit, 2 or 1
+register virtual machine which has practically no pipeline delays
+between its instructions.
+
+Since the base of sliceforth is just another forth, it could be that
+(part of) 3dp will be implemented in this lowlevel forth too, if
+performance dictates it. It's probably simpler to do it in the lowlevel
+forth than the packet forth anyway, in the form of cwords.
+
+C.2: MATRIX PROCESSING: LIBGSL
+
+All matrix processing packet forth words are (will be) basicly wrappers
+around gsl library calls. Very straightforward.
+
+C.3: OPENGL STUFF
+
+The same goes for opengl. The difference here is that it uses the dpd
+protocol in pdp, which is more like the Gem way of doing things. The
+reason for this is that, although i've tried hard to avoid it, opengl
+seems to dictate a drawing context based, instead of an object based way
+of working. So processing is context (accumulator) based. Packet forth
+will probably get some object oriented, or context oriented feel when
+this is implemented.
+
+
+
+
diff --git a/doc/misc/overview.html b/doc/misc/overview.html
new file mode 100644
index 0000000..6eb0e70
--- /dev/null
+++ b/doc/misc/overview.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>Pure Data Packet</title></head>
+<body>
+
+<h1>Pure Data Packet</h1>
+
+
+<h2>Introduction</h2>
+
+<p>Pure Data Packet (PDP) is an extension library for the computer music
+program <a href="http://www.pure-data.org">Pure Data</a> (PD), by <a href =
+"http://www-crca.ucsd.edu/~msp/software.html">Miller Puckette</a> and
+others. Its goal is to provide a way to use arbitrary data types (data
+packets) as messages that can be passed around inside PD, along side the
+standard PD numbers and symbol types. In short it puts any data object on
+the same level as a float or a symbol.
+
+<p>PDP runs on Linux and OSX. The OSX version depends on <a
+href="http://fink.sourceforge.net/">Fink</a>, which is not in the "point &
+click" stage yet, so setting it up will require some efford. There is no
+windows version. The reason for this is simple: i don't use windows myself.
+Porting would require writing code for input/output and getting the
+libraries PDP depends on to work. If anyone is willing to do this, just let
+me know. PDP can run without X Window, using SDL.
+
+<p> Currently, PDP's focus is on images and video, but there is no reason it
+should stay like that. There is limited support for matrix processing
+included in the main library (like Jitter or Gridflow). There is an
+extension library for 1D and 2D binary cellular automata, opengl rendering
+(like Gem). Some plans include audio buffers (like Vasp), ascii packets,
+text buffers, ... Finally there's a library that enables you to connect a
+scheme interpreter (guile) to PD/PDP. For more image processing objects,
+have a look at Yves Degoyon's <a
+href="http://ydegoyon.free.fr/pidip.html">PiDiP</a> library.
+
+<h2>Getting Started</h2>
+
+If you're used to working with PD, the the documentation and example
+patches should be enough to get you started. Have a look at the README file
+in the distribution to find out how to compile and setup. The file
+doc/reference.txt contains a list of objects. If you have installed PDP
+properly, you can just press the right mouse button on an object and select
+help to get a help patch. If this doesn't work, look in the directory
+doc/objects for a collection of help patches. The directory doc/examples
+contains some more demos. The directory doc/objects contains two
+abstractions that are used to setup the input and output in the help
+patches. You might want to cut and connect some wires to use the
+input/output setup that works for you.
+
+<h2>Packets and Types</h2>
+
+<p> PDP is centered around the concept of packets and operations on
+packets. There are several types of packets. The default type for most
+objects is <code><b>image/YCrCb/320x240</b></code>. This is a single video
+frame, encoded in the internal 16bit YUV format, measuring 320 by 240
+pixels. Another image type is the grayscale image
+<code><b>image/grey/320x240</b></code>. Important notes: All image processing objects that
+combine two or more packets need to be fed with the same packet types, i.e.
+encoding (YCrCb/grey) and dimensions need to be the same. Image dimensions need to be a
+multiple of <code><b>8x8</b></code>.
+
+<p> The
+<code><b>bitmap/*/*</b></code> type is another image representation type
+supporting several encodings. I.e. <code><b>bitmap/rgb/*</b></code>,
+<code><b>bitmap/rgba/*</b></code>, <code><b>bitmap/yv12/*</b></code>, ...
+
+This type cannot be processed directly by most of the image processing
+objects, but it can be used to store in delay lines, or to send over the
+network. It's main use is to support all kinds of input/output devices, and
+opengl textures, without introducing too many conversions, but it can serve
+as a space and bandwidth saver too (especially
+<code><b>bitmap/yv12/*</b></code>).
+
+<p> One of the interesting
+features in PD is the possibility of connecting everything with everything.
+If you want to generalize this to all kinds of media objects, the complexity
+of managing the different types starts to grow quite fast. Therefore PDP has
+a type conversion system that can take care of most of the conversions
+using the <code><b>[pdp_convert]</b></code> object. You can manually convert
+packets to a certain type by specifying a type template as a creation
+argument. I.e. <code><b>[pdp_convert image/grey/*]</b></code> will convert
+any packet to a greyscale image. Most of the conversion will become
+automatic later on.
+
+<p> An example: You can use the basic PDP library together with the
+cellular automata library and the opengl rendering library to use a cellular
+automaton as an input to a video processing chain. You can convert the
+processed image to a texture that can be applied to a 3d object, which then
+can be drawn to the screen, captured as a texture, converted back to an
+image, which can then be converted to a sound, processed and converted back
+to an image, etc... You get the point. The possibilities are endless.
+
+
+
+ <hr>
+ <address><a href="mailto:pdp@zzz.kotnet.org">Tom Schouten</a></address>
+<!-- Created: Thu Apr 24 22:21:03 CEST 2003 -->
+<!-- hhmts start -->
+Last modified: Thu Sep 25 20:51:44 CEST 2003
+<!-- hhmts end -->
+ </body>
+</html>
diff --git a/doc/misc/todo.jme b/doc/misc/todo.jme
new file mode 100644
index 0000000..2ce317d
--- /dev/null
+++ b/doc/misc/todo.jme
@@ -0,0 +1,4 @@
+todo list of jme@off.net
+------------------------
+- a packet to trigger packet generator instead of bang
+ o the created packet has the same format as the incoming packet
diff --git a/doc/objects/pdp_netsend.pd b/doc/objects/pdp_netsend.pd
new file mode 100644
index 0000000..8828c17
--- /dev/null
+++ b/doc/objects/pdp_netsend.pd
@@ -0,0 +1,62 @@
+#N canvas 236 47 523 395 10;
+#X obj 174 74 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 155 43 metro 40;
+#X obj 155 18 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X obj 167 171 pdp_convert bitmap/yv12/*;
+#X floatatom 238 20 5 0 0 0 - - -;
+#X msg 263 132 sleepgrain \$1;
+#X floatatom 263 112 5 0 0 0 - - -;
+#X floatatom 308 74 5 0 0 0 - - -;
+#X msg 308 94 udpsize \$1;
+#X obj 295 44 hsl 128 15 1024 60000 1 0 empty empty empty -2 -6 0 8
+-262144 -1 -1 4000 1;
+#X msg 339 161 sleep \$1;
+#X obj 379 136 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X floatatom 327 200 5 0 0 0 - - -;
+#X msg 327 220 sleepperiod \$1;
+#X msg 16 13 connect acer 7777;
+#X obj 124 212 pdp_netsend;
+#X msg 324 15 1472;
+#X msg 211 81 dim 160 120;
+#X msg 212 62 dim 320 240;
+#X msg 18 44 connect localhost 7777;
+#X obj 65 247 pdp_netreceive 7777;
+#X obj 78 275 pdp_xv;
+#X obj 123 136 pdp_route;
+#X obj 203 130 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X floatatom 281 248 5 0 0 0 - - -;
+#X msg 281 268 timeout \$1;
+#X obj 154 103 pdp_v4l;
+#X obj 132 275 pdp_xv;
+#X text 97 334 this is still experimental (SIGSEGVs ahead);
+#X connect 0 0 26 0;
+#X connect 1 0 26 0;
+#X connect 2 0 1 0;
+#X connect 3 0 15 0;
+#X connect 4 0 1 1;
+#X connect 5 0 15 0;
+#X connect 6 0 5 0;
+#X connect 7 0 8 0;
+#X connect 8 0 15 0;
+#X connect 9 0 7 0;
+#X connect 10 0 15 0;
+#X connect 11 0 10 0;
+#X connect 12 0 13 0;
+#X connect 13 0 15 0;
+#X connect 14 0 15 0;
+#X connect 16 0 8 0;
+#X connect 17 0 26 0;
+#X connect 18 0 26 0;
+#X connect 19 0 15 0;
+#X connect 20 0 21 0;
+#X connect 22 0 15 0;
+#X connect 22 1 3 0;
+#X connect 23 0 22 1;
+#X connect 24 0 25 0;
+#X connect 25 0 15 0;
+#X connect 26 0 22 0;
+#X connect 26 0 27 0;
diff --git a/doc/objects/pdp_rawin.pd b/doc/objects/pdp_rawin.pd
new file mode 100644
index 0000000..6dcc342
--- /dev/null
+++ b/doc/objects/pdp_rawin.pd
@@ -0,0 +1,28 @@
+#N canvas 504 520 687 380 10;
+#X msg 137 68 open /tmp/otherpipe;
+#X msg 437 157 open;
+#X text 169 111 set type (how to interpret raw data);
+#X obj 75 307 pdp_help_output;
+#X text 476 157 open default pipe;
+#X text 177 196 creation args: <pipe> <type>;
+#X obj 405 218 print done;
+#X text 270 244 2nd outlet: bang if pipe is closed;
+#X text 271 260 connect to [open< to ensure pipe stays open;
+#X text 283 69 open any pipe for reading;
+#X msg 152 90 close;
+#X text 198 90 close pipe;
+#X obj 437 137 spigot;
+#X obj 473 116 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X msg 171 129 type bitmap/rgb/320x240;
+#X obj 137 174 pdp_rawin /tmp/pipe image/grey/320x240;
+#X text 102 19 pdp_rawin: read raw data from a pipe (or file);
+#X connect 0 0 15 0;
+#X connect 1 0 15 0;
+#X connect 10 0 15 0;
+#X connect 12 0 1 0;
+#X connect 13 0 12 1;
+#X connect 14 0 15 0;
+#X connect 15 0 3 0;
+#X connect 15 1 6 0;
+#X connect 15 1 12 0;
diff --git a/doc/objects/pdp_rawout.pd b/doc/objects/pdp_rawout.pd
new file mode 100644
index 0000000..0f231bf
--- /dev/null
+++ b/doc/objects/pdp_rawout.pd
@@ -0,0 +1,28 @@
+#N canvas 254 556 687 380 10;
+#X msg 132 63 open /tmp/otherpipe;
+#X msg 177 145 open;
+#X text 216 145 open default pipe;
+#X obj 132 219 print done;
+#X text 117 266 connect to [open< to ensure pipe stays open;
+#X text 278 64 open any pipe for reading;
+#X msg 147 85 close;
+#X text 193 85 close pipe;
+#X obj 177 125 spigot;
+#X obj 230 124 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X text 134 15 pdp_rawout: write raw data to a pipe (or file);
+#X text 293 169 creation args: <pipe>;
+#X obj 132 169 pdp_rawout /tmp/pipe;
+#X obj 15 124 pdp_help_input;
+#X obj 15 98 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X text 116 250 outlet: bang if pipe is closed;
+#X connect 0 0 12 0;
+#X connect 1 0 12 0;
+#X connect 6 0 12 0;
+#X connect 8 0 1 0;
+#X connect 9 0 8 1;
+#X connect 12 0 3 0;
+#X connect 12 0 8 0;
+#X connect 13 0 12 0;
+#X connect 14 0 13 0;
diff --git a/include/pdp_debug.h b/include/pdp_debug.h
new file mode 100644
index 0000000..a5e48fc
--- /dev/null
+++ b/include/pdp_debug.h
@@ -0,0 +1,16 @@
+#ifndef __PDP_DEBUG_H_
+#define __PDP_DEBUG_H_
+
+#include "pdp_config.h" // needed for PDP_DEBUG define
+
+void pdp_assert_hook (char *condition, char *file, int line);
+
+
+
+#if PDP_DEBUG
+#define PDP_ASSERT(x) if (!(x)) {pdp_assert_hook(#x, __FILE__, __LINE__);}
+#else
+#define PDP_ASSERT(x)
+#endif
+
+#endif //__PDP_DEBUG_H_
diff --git a/include/pdp_mem.h b/include/pdp_mem.h
new file mode 100644
index 0000000..3301655
--- /dev/null
+++ b/include/pdp_mem.h
@@ -0,0 +1,46 @@
+/*
+ * Pure Data Packet header file: memory allocation
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _PDP_MEM_H_
+#define _PDP_MEM_H_
+
+#include <pthread.h>
+
+/* a wrapper around malloc and free to keep track of pdp's memory usage */
+void *pdp_alloc(int size);
+void pdp_dealloc(void *stuff);
+
+
+/* fast allocator object (for lists and atoms) */
+#define PDP_FASTALLOC_BLOCK_ELEMENTS 4096
+typedef struct _pdp_fastalloc
+{
+ unsigned int atom_size;
+ unsigned int block_elements;
+ pthread_mutex_t mut;
+ struct _fastalloc *freelist;
+
+} t_pdp_fastalloc;
+
+void *pdp_fastalloc_new_atom(t_pdp_fastalloc *x);
+void pdp_fastalloc_save_atom(t_pdp_fastalloc *x, void *atom);
+t_pdp_fastalloc *pdp_fastalloc_new(unsigned int size);
+
+#endif
diff --git a/include/pdp_net.h b/include/pdp_net.h
new file mode 100644
index 0000000..ddc3f7f
--- /dev/null
+++ b/include/pdp_net.h
@@ -0,0 +1,197 @@
+#ifndef __PDP_UDP_H_
+#define __PDP_UDP_H_
+
+/*
+ * Pure Data Packet header: UDP protocol for raw packets
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+
+ This file contains the specification of the pdp UDP transport protocol.
+ It is a very basic binary protocol, not very fool proof.
+
+ The protocol:
+
+ A header packet is transmitted first. This contains mime type information,
+ and the size of and number of packets to be received.
+
+ The connection id:
+
+ Currently it is just a random number from the libc rand() function
+ this should be accurate enough.
+
+
+*/
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* some defs */
+#define MAX_UDP_PACKET 1472
+#define RESEND_MAX_CHUNKS ((MAX_UDP_PACKET - sizeof(t_pdp_udp_header))/sizeof(unsigned int))
+
+#define PDP_UDP_VERSION 1
+
+/* a pdp udp packet is prepended with this header */
+
+typedef struct _pdp_udp_header
+{
+ char signature[4]; /* must be "PDP" */;
+ unsigned int version; /* protocol version */
+ unsigned int connection_id; /* the 'connection' id */
+ unsigned int sequence_number; /* sequence number. negative: control packet: body contains meta info */
+
+} t_pdp_udp_header;
+
+/* the data part for a new connection */
+
+#define PDP_UDP_NEW -1 /* start a new transmission */
+typedef struct _pdp_udp_new
+{
+ unsigned int data_size; /* the size of the packets */
+ unsigned int nb_chunks; /* number of chunks in pdp to be transmitted */
+ unsigned int chunk_size; /* the maximum chunk size */
+ char type[0]; /* the packet type */
+
+ // the tail part is the mime type, for creation and reassembly
+} t_pdp_udp_newpacket;
+
+#define PDP_UDP_DONE -2 /* transmission is done */
+
+#define PDP_UDP_RESEND -3 /* request retransmission of certain chunks. empty: transmission ok */
+#define PDP_UDP_ACKNEW -4 /* acknowledge reception of new packet header */
+
+
+/* receiver and sender classes (transport layer) */
+
+#define PDP_UDP_BUFSIZE 0xF000
+
+/* RECEIVER */
+typedef struct _pdp_udp_receiver
+{
+
+ // buffer for receiving
+ t_pdp_udp_header x_header; //pdp over udp header
+ char x_buf[PDP_UDP_BUFSIZE]; //send buffer
+ unsigned int x_zero_terminator; // to prevent runaway strings
+ unsigned int x_buf_size; //size of the received data in the buffer (excluding the header)
+
+ // buffer for sending
+ t_pdp_udp_header x_resend_header; // header of the resend packet
+ unsigned int x_resend_chunks[RESEND_MAX_CHUNKS]; // body contains the chunks to resend
+ unsigned int x_resend_udp_packet_size;
+
+ // transmission info
+ unsigned int x_connection_id;
+ unsigned int x_nb_chunks;
+ unsigned int x_chunk_size;
+ unsigned int *x_chunk_list;
+ char *x_data_type;
+ unsigned int x_data_size;
+ void *x_data;
+ struct sockaddr_in x_source_socket;
+ int x_sslen;
+ int x_receive_finished;
+ int x_packet_transferred;
+
+ int x_socket; //socket used for sending
+ struct sockaddr_in x_sa; //address struct
+
+} t_pdp_udp_receiver;
+
+/* setup */
+t_pdp_udp_receiver *pdp_udp_receiver_new(int port);
+void pdp_udp_receiver_free(t_pdp_udp_receiver *x);
+
+/* reset connection (wait for new packet) */
+void pdp_udp_receiver_reset(t_pdp_udp_receiver *x);
+
+/* receive, returns 1 on success, 0 on timeout, -1 on error */
+int pdp_udp_receiver_receive(t_pdp_udp_receiver *x, unsigned int timeout_ms);
+
+/* get meta & data */
+char *pdp_udp_receiver_type(t_pdp_udp_receiver *x);
+unsigned int pdp_udp_receiver_size(t_pdp_udp_receiver *x);
+void *pdp_udp_receiver_data(t_pdp_udp_receiver *x);
+
+
+
+/* SENDER */
+typedef struct _pdp_udp_sender
+{
+ // desired udp packet size
+ unsigned int x_udp_payload_size;
+
+ // current packet && communication info
+ unsigned int x_connection_id;
+ char *x_data_type;
+ void *x_data;
+ unsigned int x_data_size;
+ unsigned int x_chunk_size;
+ unsigned int *x_chunk_list;
+ unsigned int x_nb_chunks;
+ unsigned int x_chunk_list_size;
+
+ // connection data
+ int x_socket; //socket used for sending
+ struct sockaddr_in x_sa; //address struct
+ unsigned int x_sleepgrain_us; //pause between sends (the poor man's flow control) (0 == no sleep)
+ unsigned int x_sleep_count;
+ unsigned int x_sleep_period;
+ unsigned int x_timeout_us;
+
+ // temp buffer for sending
+ t_pdp_udp_header x_header;
+ char x_buf[PDP_UDP_BUFSIZE];
+ unsigned int x_buf_size;
+
+ // temp buffer for receiving
+ t_pdp_udp_header x_resend_header;
+ unsigned int x_resend_chunks[RESEND_MAX_CHUNKS];
+ unsigned int x_resend_items;
+
+
+} t_pdp_udp_sender;
+
+/* some flow control variables */
+void pdp_udp_sender_timeout_us(t_pdp_udp_sender *x, unsigned int timeout_us);
+void pdp_udp_sender_sleepgrain_us(t_pdp_udp_sender *x, unsigned int sleepgrain_us);
+void pdp_udp_sender_sleepperiod(t_pdp_udp_sender *x, unsigned int sleepperiod);
+void pdp_udp_sender_udp_packet_size(t_pdp_udp_sender *x, unsigned int udp_packet_size);
+
+/* setup */
+t_pdp_udp_sender *pdp_udp_sender_new(void);
+void pdp_udp_sender_free(t_pdp_udp_sender *x);
+
+/* connect */
+void pdp_udp_sender_connect(t_pdp_udp_sender *x, char *host, unsigned int port);
+
+/* send, returns 1 on success, 0 on error */
+int pdp_udp_sender_send(t_pdp_udp_sender *x, char* type, unsigned int size, void *data);
+
+
+
+#endif
diff --git a/include/pdp_pd.h b/include/pdp_pd.h
new file mode 100644
index 0000000..e200c1e
--- /dev/null
+++ b/include/pdp_pd.h
@@ -0,0 +1,7 @@
+/* pdp_pd.h wrapper */
+
+#ifndef _M_PD_H_
+#define _M_PD_H_
+#include "m_pd.h"
+#endif
+
diff --git a/include/pdp_post.h b/include/pdp_post.h
new file mode 100644
index 0000000..05b5143
--- /dev/null
+++ b/include/pdp_post.h
@@ -0,0 +1,29 @@
+
+/*
+ * Pure Data Packet header file. pdp logging.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _PDP_POST_H_
+#define _PDP_POST_H_
+
+/* write a message to log (console) */
+void pdp_post_n(char *fmt, ...);
+void pdp_post(char *fmt, ...);
+
+#endif
diff --git a/include/pdp_symbol.h b/include/pdp_symbol.h
new file mode 100644
index 0000000..fe3137a
--- /dev/null
+++ b/include/pdp_symbol.h
@@ -0,0 +1,136 @@
+/*
+ * Pure Data Packet system implementation. : symbol and namespace stuff
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _PDP_SYMBOL_
+#define _PDP_SYMBOL_
+
+
+/* pdp's symbols are derived from pd's symbols
+ there is one symbol hash. each symbol has
+ a meaning in several name spaces.
+
+ * forth words
+ * type description lists (for accelerating type matching)
+
+
+*/
+
+#include "pdp_list.h"
+
+
+
+
+/* the pdp symbol type */
+typedef struct _pdp_symbol
+{
+ /* next */
+ struct _pdp_symbol *s_next;
+
+ /* the symbol name */
+ char *s_name;
+
+ /* forth symbol->atom */
+ struct _pdp_atom s_forth;
+
+ /* packet handling cache */
+ struct _pdp_list *s_type; // a parsed type description: a/b/c -> (a,b,c)
+ struct _pdp_list *s_reusefifo; // packet pool fifo for this type
+
+
+} t_pdp_symbol;
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* namespace stuff */
+int pdp_symbol_set_typelist(t_pdp_symbol *s, struct _pdp_list *typelist);
+
+/* get symbol from char */
+t_pdp_symbol *pdp_gensym(char *s);
+
+/* iterate over all symbols */
+typedef void (*t_pdp_symbol_iterator)(t_pdp_symbol *s);
+void pdp_symbol_apply_all(t_pdp_symbol_iterator ir);
+
+// don't use these directly, use the macros
+extern t_pdp_symbol _pdp_sym_wildcard;
+extern t_pdp_symbol _pdp_sym_float;
+extern t_pdp_symbol _pdp_sym_int;
+extern t_pdp_symbol _pdp_sym_symbol;
+extern t_pdp_symbol _pdp_sym_packet;
+extern t_pdp_symbol _pdp_sym_pointer;
+extern t_pdp_symbol _pdp_sym_list;
+extern t_pdp_symbol _pdp_sym_invalid;
+extern t_pdp_symbol _pdp_sym_question_mark;
+extern t_pdp_symbol _pdp_sym_atom;
+extern t_pdp_symbol _pdp_sym_null;
+extern t_pdp_symbol _pdp_sym_quote_start;
+extern t_pdp_symbol _pdp_sym_quote_end;
+extern t_pdp_symbol _pdp_sym_return;
+extern t_pdp_symbol _pdp_sym_nreturn;
+extern t_pdp_symbol _pdp_sym_defstart;
+extern t_pdp_symbol _pdp_sym_defend;
+extern t_pdp_symbol _pdp_sym_if;
+extern t_pdp_symbol _pdp_sym_then;
+extern t_pdp_symbol _pdp_sym_local;
+extern t_pdp_symbol _pdp_sym_forth;
+extern t_pdp_symbol _pdp_sym_call;
+extern t_pdp_symbol _pdp_sym_push;
+extern t_pdp_symbol _pdp_sym_pop;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+// these symbols are used a lot in critical parts
+// optimize later
+
+#define PDP_SYM_WILDCARD &_pdp_sym_wildcard
+#define PDP_SYM_FLOAT &_pdp_sym_float
+#define PDP_SYM_INT &_pdp_sym_int
+#define PDP_SYM_SYMBOL &_pdp_sym_symbol
+#define PDP_SYM_PACKET &_pdp_sym_packet
+#define PDP_SYM_POINTER &_pdp_sym_pointer
+#define PDP_SYM_LIST &_pdp_sym_list
+#define PDP_SYM_INVALID &_pdp_sym_invalid
+#define PDP_SYM_QUESTION_MARK &_pdp_sym_question_mark
+#define PDP_SYM_ATOM &_pdp_sym_atom
+#define PDP_SYM_NULL &_pdp_sym_null
+#define PDP_SYM_QUOTE_START &_pdp_sym_quote_start
+#define PDP_SYM_QUOTE_END &_pdp_sym_quote_end
+#define PDP_SYM_RETURN &_pdp_sym_return
+#define PDP_SYM_NRETURN &_pdp_sym_nreturn
+#define PDP_SYM_DEF_START &_pdp_sym_defstart
+#define PDP_SYM_DEF_END &_pdp_sym_defend
+#define PDP_SYM_IF &_pdp_sym_if
+#define PDP_SYM_THEN &_pdp_sym_then
+#define PDP_SYM_LOCAL &_pdp_sym_local
+#define PDP_SYM_FORTH &_pdp_sym_forth
+#define PDP_SYM_CALL &_pdp_sym_call
+#define PDP_SYM_PUSH &_pdp_sym_push
+#define PDP_SYM_POP &_pdp_sym_pop
+
+#endif
+
diff --git a/include/pdp_xvideo.h b/include/pdp_xvideo.h
new file mode 100644
index 0000000..c395605
--- /dev/null
+++ b/include/pdp_xvideo.h
@@ -0,0 +1,78 @@
+
+/*
+ * Pure Data Packet header file: xwindow glue code
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+// x stuff
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+
+// image formats for communication with the X Server
+#define FOURCC_YV12 0x32315659 /* YV12 YUV420P */
+#define FOURCC_YUV2 0x32595559 /* YUV2 YUV422 */
+#define FOURCC_I420 0x30323449 /* I420 Intel Indeo 4 */
+
+
+
+/* xvideo class */
+typedef struct _pdp_xvideo
+{
+
+ t_pdp_xdisplay *xdpy;
+ t_pdp_xwindow *xwin;
+ //Display *dpy;
+ //int screen;
+ //Window win;
+
+
+
+ int xv_format;
+ int xv_port;
+
+ XvImage *xvi;
+ unsigned char *data;
+ unsigned int width;
+ unsigned int height;
+ int last_encoding;
+
+ int initialized;
+
+} t_pdp_xvideo;
+
+
+/* cons */
+void pdp_xvideo_init(t_pdp_xvideo *x);
+t_pdp_xvideo *pdp_xvideo_new(void);
+
+/* des */
+void pdp_xvideo_cleanup(t_pdp_xvideo* x);
+void pdp_xvideo_free(t_pdp_xvideo* x);
+
+
+/* open an xv port (and create XvImage) */
+int pdp_xvideo_open_on_display(t_pdp_xvideo *x, t_pdp_xdisplay *d);
+
+/* close xv port (and delete XvImage */
+void pdp_xvideo_close(t_pdp_xvideo* x);
+
+/* display a packet */
+void pdp_xvideo_display_packet(t_pdp_xvideo *x, t_pdp_xwindow *w, int packet);
diff --git a/modules/generic/pdp_rawin.c b/modules/generic/pdp_rawin.c
new file mode 100644
index 0000000..28ef8fb
--- /dev/null
+++ b/modules/generic/pdp_rawin.c
@@ -0,0 +1,299 @@
+/*
+ * Pure Data Packet module. packet forth console
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <fcntl.h>
+#include "pdp_pd.h"
+#include "pdp_debug.h"
+#include "pdp_list.h"
+#include "pdp_comm.h"
+#include "pdp_post.h"
+#include "pdp_packet.h"
+
+
+#define PERIOD 1.0f
+#define D if (1)
+
+
+
+
+/* raw input from a unix pipe */
+
+typedef struct rawin_struct
+{
+ /* pd */
+ t_object x_obj;
+ t_outlet *x_outlet;
+ t_outlet *x_sync_outlet;
+ t_clock *x_clock;
+
+ /* comm */
+ t_pdp_list *x_queue; // packet queue
+
+ /* thread */
+ pthread_mutex_t x_mut;
+ pthread_attr_t x_attr;
+ pthread_t x_thread;
+
+ /* sync */
+ int x_giveup; // 1-> terminate reader thread
+ int x_active; // 1-> reader thread is launched
+ int x_done; // 1-> reader thread has exited
+
+ /* config */
+ t_symbol *x_pipe;
+ t_pdp_symbol *x_type;
+
+} t_rawin;
+
+
+static inline void lock(t_rawin *x){pthread_mutex_lock(&x->x_mut);}
+static inline void unlock(t_rawin *x){pthread_mutex_unlock(&x->x_mut);}
+
+static void rawin_close(t_rawin *x);
+static void tick(t_rawin *x)
+{
+ /* send all packets in queue to outlet */
+ lock(x);
+ while (x->x_queue->elements){
+ outlet_pdp_atom(x->x_outlet, x->x_queue->first);
+ pdp_list_pop(x->x_queue); // pop stale reference
+ }
+ unlock(x);
+ clock_delay(x->x_clock, PERIOD);
+
+ /* check if thread is done */
+ if (x->x_done) rawin_close(x);
+
+}
+
+static void move_current_to_queue(t_rawin *x, int packet)
+{
+ lock(x);
+ pdp_list_add_back(x->x_queue, a_packet, (t_pdp_word)packet);
+ unlock(x);
+}
+
+static void *rawin_thread(void *y)
+{
+ int pipe;
+ int packet = -1;
+ t_rawin *x = (t_rawin *)y;
+ int period_sec;
+ int period_usec;
+
+
+ //D pdp_post("pipe: %s", x->x_pipe->s_name);
+ //D pdp_post("type: %s", x->x_type->s_name);
+
+ /* open pipe */
+ if (-1 == (pipe = open(x->x_pipe->s_name, O_RDONLY|O_NONBLOCK))){
+ perror(x->x_pipe->s_name);
+ goto exit;
+ }
+
+ /* main loop (packets) */
+ while(1){
+ void *data = 0;
+ int left = -1;
+
+ /* create packet */
+ if (-1 != packet){
+ pdp_post("WARNING: deleting stale packet");
+ pdp_packet_mark_unused(packet);
+ }
+ packet = pdp_factory_newpacket(x->x_type);
+ if (-1 == packet){
+ pdp_post("ERROR: can't create packet. type = %s", x->x_type->s_name);
+ goto exit;
+ }
+
+ /* fill packet */
+ data = pdp_packet_data(packet);
+ left = pdp_packet_data_size(packet);
+ // D pdp_post("packet %d, data %x, size %d", packet, data, left);
+
+ /* inner loop: pipe reads */
+ while(left){
+
+ fd_set inset;
+ struct timeval tv = {0,10000};
+
+ /* check if we need to stop */
+ if (x->x_giveup){
+ pdp_packet_mark_unused(packet);
+ goto close;
+ }
+ /* select, with timeout */
+ FD_ZERO(&inset);
+ FD_SET(pipe, &inset);
+ if (-1 == select(pipe+1, &inset, NULL,NULL, &tv)){
+ pdp_post("select error");
+ goto close;
+ }
+
+ /* if ready, read, else retry */
+ if (FD_ISSET(pipe, &inset)){
+ int bytes = read(pipe, data, left);
+ if (!bytes){
+ /* if no bytes are read, pipe is closed */
+ goto close;
+ }
+ data += bytes;
+ left -= bytes;
+ }
+ }
+
+ /* move to queue */
+ move_current_to_queue(x, packet);
+ packet = -1;
+
+
+
+ }
+
+ close:
+ /* close pipe */
+ close(pipe);
+
+
+ exit:
+ x->x_done = 1;
+ return 0;
+}
+
+
+
+static void rawin_type(t_rawin *x, t_symbol *type)
+{
+ x->x_type = pdp_gensym(type->s_name);
+}
+
+static void rawin_open(t_rawin *x, t_symbol *pipe)
+{
+ /* save pipe name if not empty */
+ if (pipe->s_name[0]) {x->x_pipe = pipe;}
+
+ if (x->x_active) {
+ pdp_post("already open");
+ return;
+ }
+ /* start thread */
+ x->x_giveup = 0;
+ x->x_done = 0;
+ pthread_create(&x->x_thread, &x->x_attr, rawin_thread , x);
+ x->x_active = 1;
+}
+
+static void rawin_close(t_rawin *x)
+{
+
+ if (!x->x_active) return;
+
+ /* stop thread: set giveup + wait */
+ x->x_giveup = 1;
+ pthread_join(x->x_thread, NULL);
+ x->x_active = 0;
+
+ /* notify */
+ outlet_bang(x->x_sync_outlet);
+ pdp_post("connection to %s closed", x->x_pipe->s_name);
+
+
+
+
+
+}
+
+static void rawin_free(t_rawin *x)
+{
+ rawin_close(x);
+ clock_free(x->x_clock);
+ pdp_tree_strip_packets(x->x_queue);
+ pdp_tree_free(x->x_queue);
+}
+
+t_class *rawin_class;
+
+
+static void *rawin_new(t_symbol *pipe, t_symbol *type)
+{
+ t_rawin *x;
+
+ pdp_post("%s %s", pipe->s_name, type->s_name);
+
+ /* allocate & init */
+ x = (t_rawin *)pd_new(rawin_class);
+ x->x_outlet = outlet_new(&x->x_obj, &s_anything);
+ x->x_sync_outlet = outlet_new(&x->x_obj, &s_anything);
+ x->x_clock = clock_new(x, (t_method)tick);
+ x->x_queue = pdp_list_new(0);
+ x->x_active = 0;
+ x->x_giveup = 0;
+ x->x_done = 0;
+ x->x_type = pdp_gensym("image/YCrCb/320x240"); //default
+ x->x_pipe = gensym("/tmp/pdpraw"); // default
+ pthread_attr_init(&x->x_attr);
+ pthread_mutex_init(&x->x_mut, NULL);
+ clock_delay(x->x_clock, PERIOD);
+
+ /* args */
+ rawin_type(x, type);
+ if (pipe->s_name[0]) x->x_pipe = pipe;
+
+ return (void *)x;
+
+}
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_rawin_setup(void)
+{
+ int i;
+
+ /* create a standard pd class: [pdp_rawin pipe type] */
+ rawin_class = class_new(gensym("pdp_rawin"), (t_newmethod)rawin_new,
+ (t_method)rawin_free, sizeof(t_rawin), 0, A_DEFSYMBOL, A_DEFSYMBOL, A_NULL);
+
+ /* add global message handler */
+ class_addmethod(rawin_class, (t_method)rawin_type, gensym("type"), A_SYMBOL, A_NULL);
+ class_addmethod(rawin_class, (t_method)rawin_open, gensym("open"), A_DEFSYMBOL, A_NULL);
+ class_addmethod(rawin_class, (t_method)rawin_close, gensym("close"), A_NULL);
+
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/generic/pdp_rawout.c b/modules/generic/pdp_rawout.c
new file mode 100644
index 0000000..e1e9edf
--- /dev/null
+++ b/modules/generic/pdp_rawout.c
@@ -0,0 +1,320 @@
+/*
+ * Pure Data Packet module. packet forth console
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include "pdp_pd.h"
+#include "pdp_debug.h"
+#include "pdp_list.h"
+#include "pdp_comm.h"
+#include "pdp_post.h"
+#include "pdp_packet.h"
+
+
+#define D if (1)
+#define MAX_QUEUESIZE 4
+#define PIPE_BLOCKSIZE 4096
+
+
+
+
+/* raw input from a unix pipe */
+
+typedef struct rawout_struct
+{
+ /* pd */
+ t_object x_obj;
+ //t_outlet *x_outlet;
+ t_outlet *x_sync_outlet;
+
+ /* comm */
+ t_pdp_list *x_queue; // packet queue
+
+ /* thread */
+ pthread_mutex_t x_mut;
+ pthread_attr_t x_attr;
+ pthread_t x_thread;
+
+ /* sync */
+ int x_giveup; // 1-> terminate writer thread
+ int x_active; // 1-> writer thread is launched
+ int x_done; // 1-> writer thread has exited
+
+ /* config */
+ t_symbol *x_pipe;
+ t_pdp_symbol *x_type;
+
+} t_rawout;
+
+
+static inline void lock(t_rawout *x){pthread_mutex_lock(&x->x_mut);}
+static inline void unlock(t_rawout *x){pthread_mutex_unlock(&x->x_mut);}
+
+static void rawout_close(t_rawout *x);
+static void pdp_in(t_rawout *x, t_symbol *s, t_float f)
+{
+ /* save packet to pdp queue, if size is smaller than maxsize */
+ if (s == S_REGISTER_RO){
+ if (x->x_queue->elements < MAX_QUEUESIZE){
+ int p = (int)f;
+ p = pdp_packet_copy_ro(p);
+ if (p != -1){
+ lock(x);
+ pdp_list_add_back(x->x_queue, a_packet, (t_pdp_word)p);
+ unlock(x);
+ }
+ }
+ else {
+ pdp_post("pdp_rawout: dropping packet: (queue full)", MAX_QUEUESIZE);
+ }
+
+ }
+
+ /* check if thread is done */
+ if (x->x_done) rawout_close(x);
+
+}
+
+
+
+static void *rawout_thread(void *y)
+{
+ int pipe;
+ int packet = -1;
+ t_rawout *x = (t_rawout *)y;
+ int period_sec;
+ int period_usec;
+ sigset_t sigvec; /* signal handling */
+
+ /* ignore pipe signal */
+ sigemptyset(&sigvec);
+ sigaddset(&sigvec,SIGPIPE);
+ pthread_sigmask(SIG_BLOCK, &sigvec, 0);
+
+ //D pdp_post("pipe: %s", x->x_pipe->s_name);
+ //D pdp_post("type: %s", x->x_type->s_name);
+
+ /* open pipe */
+ if (-1 == (pipe = open(x->x_pipe->s_name, O_WRONLY|O_NONBLOCK))){
+ perror(x->x_pipe->s_name);
+ goto exit;
+ }
+
+ /* main loop (packets) */
+ while(1){
+ void *data = 0;
+ int left = -1;
+
+ /* try again if queue is empty */
+ if (!x->x_queue->elements){
+ /* check if we need to stop */
+ if (x->x_giveup){
+ goto close;
+ }
+ else {
+ usleep(1000.0f); // sleep before polling again
+ continue;
+ }
+ }
+ /* get packet from queue */
+ lock(x);
+ packet = pdp_list_pop(x->x_queue).w_packet;
+ unlock(x);
+
+ /* send packet */
+ data = pdp_packet_data(packet);
+ left = pdp_packet_data_size(packet);
+
+ /* inner loop: pipe reads */
+ while(left){
+
+ fd_set outset;
+ struct timeval tv = {0,10000};
+
+ /* check if we need to stop */
+ if (x->x_giveup){
+ pdp_packet_mark_unused(packet);
+ goto close;
+ }
+
+ /* select, with timeout */
+ FD_ZERO(&outset);
+ FD_SET(pipe, &outset);
+ if (-1 == select(pipe+1, NULL, &outset, NULL, &tv)){
+ pdp_post("select error");
+ goto close;
+ }
+
+ /* if ready, read, else retry */
+ if (FD_ISSET(pipe, &outset)){
+ int bytes = write(pipe, data, left);
+ /* handle errors */
+ if (bytes <= 0){
+ perror(x->x_pipe->s_name);
+ if (bytes != EAGAIN) goto close;
+ }
+ /* or update pointers */
+ else{
+ data += bytes;
+ left -= bytes;
+ //pdp_post("left %d", left);
+ }
+ }
+ else {
+ //pdp_post("retrying write");
+ }
+ }
+
+ /* discard packet */
+ pdp_packet_mark_unused(packet);
+
+
+ }
+
+ close:
+ /* close pipe */
+ close(pipe);
+
+
+ exit:
+ x->x_done = 1;
+ return 0;
+}
+
+
+
+static void rawout_type(t_rawout *x, t_symbol *type)
+{
+ x->x_type = pdp_gensym(type->s_name);
+}
+
+static void rawout_open(t_rawout *x, t_symbol *pipe)
+{
+ /* save pipe name if not empty */
+ if (pipe->s_name[0]) {x->x_pipe = pipe;}
+
+ if (x->x_active) {
+ pdp_post("already open");
+ return;
+ }
+ /* start thread */
+ x->x_giveup = 0;
+ x->x_done = 0;
+ pthread_create(&x->x_thread, &x->x_attr, rawout_thread , x);
+ x->x_active = 1;
+}
+
+static void rawout_close(t_rawout *x)
+{
+
+ if (!x->x_active) return;
+
+ /* stop thread: set giveup + wait */
+ x->x_giveup = 1;
+ pthread_join(x->x_thread, NULL);
+ x->x_active = 0;
+
+ /* notify */
+ outlet_bang(x->x_sync_outlet);
+ pdp_post("connection to %s closed", x->x_pipe->s_name);
+
+
+
+
+
+}
+
+static void rawout_free(t_rawout *x)
+{
+ rawout_close(x);
+ pdp_tree_strip_packets(x->x_queue);
+ pdp_tree_free(x->x_queue);
+}
+
+t_class *rawout_class;
+
+
+static void *rawout_new(t_symbol *pipe, t_symbol *type)
+{
+ t_rawout *x;
+
+ pdp_post("%s %s", pipe->s_name, type->s_name);
+
+ /* allocate & init */
+ x = (t_rawout *)pd_new(rawout_class);
+ //x->x_outlet = outlet_new(&x->x_obj, &s_anything);
+ x->x_sync_outlet = outlet_new(&x->x_obj, &s_anything);
+ x->x_queue = pdp_list_new(0);
+ x->x_active = 0;
+ x->x_giveup = 0;
+ x->x_done = 0;
+ x->x_type = pdp_gensym("image/YCrCb/320x240"); //default
+ x->x_pipe = gensym("/tmp/pdpraw"); // default
+ pthread_attr_init(&x->x_attr);
+ pthread_mutex_init(&x->x_mut, NULL);
+
+ /* args */
+ rawout_type(x, type);
+ if (pipe->s_name[0]) x->x_pipe = pipe;
+
+ return (void *)x;
+
+}
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_rawout_setup(void)
+{
+ int i;
+
+ /* create a standard pd class: [pdp_rawout pipe type] */
+ rawout_class = class_new(gensym("pdp_rawout"), (t_newmethod)rawout_new,
+ (t_method)rawout_free, sizeof(t_rawout), 0, A_DEFSYMBOL, A_DEFSYMBOL, A_NULL);
+
+ /* add global message handler */
+ class_addmethod(rawout_class, (t_method)pdp_in,
+ gensym("pdp"), A_SYMBOL, A_FLOAT, A_NULL);
+
+ class_addmethod(rawout_class, (t_method)rawout_type, gensym("type"), A_SYMBOL, A_NULL);
+ class_addmethod(rawout_class, (t_method)rawout_open, gensym("open"), A_DEFSYMBOL, A_NULL);
+ class_addmethod(rawout_class, (t_method)rawout_close, gensym("close"), A_NULL);
+
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/generic/pdp_udp_receive.c b/modules/generic/pdp_udp_receive.c
new file mode 100644
index 0000000..3d42466
--- /dev/null
+++ b/modules/generic/pdp_udp_receive.c
@@ -0,0 +1,203 @@
+/*
+ * Pure Data Packet module.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/* this module sends receives an udp packet stream and converts to pdp packet */
+
+#include "pdp_net.h"
+#include "pdp.h"
+#include "pdp_resample.h"
+
+#define D if(0)
+
+typedef struct pdp_udp_receive_struct
+{
+
+ t_object x_obj;
+ t_float x_f;
+
+ /* receiver object */
+ t_pdp_udp_receiver *x_receiver;
+
+
+ /* thread vars */
+ pthread_attr_t x_attr;
+ pthread_t x_thread;
+ int x_exit_thread;
+
+ /* packet queue */
+ int x_index;
+ int x_packet[2];
+
+ /* polling clock */
+ t_clock *x_clock;
+ /* outlet */
+ t_outlet *x_outlet0;
+
+} t_pdp_udp_receive;
+
+
+static void clock_tick(t_pdp_udp_receive *x)
+{
+ /* poll for new packet */
+
+ pdp_pass_if_valid(x->x_outlet0, &x->x_packet[!x->x_index]);
+ clock_delay(x->x_clock, 1.0f);
+}
+
+
+
+
+static void *receive_thread(void *threaddata)
+{
+ t_pdp_udp_receive *x = (t_pdp_udp_receive *)threaddata;
+ t_pdp *pdp_header = 0;
+ void *pdp_data = 0;
+ int tmp_packet = -1;
+ char *type = 0;
+ unsigned int size = 0;
+
+ /* listen for packets */
+ while (!x->x_exit_thread){
+
+
+ switch(pdp_udp_receiver_receive(x->x_receiver, 100)){
+ case -1:
+ /* error */
+ goto exit;
+ case 0:
+ /* timeout */
+ continue;
+ case 1:
+ /* data ready */
+ break;
+ }
+
+ /* create a new packet */
+ type = pdp_udp_receiver_type(x->x_receiver);
+ tmp_packet = pdp_factory_newpacket(pdp_gensym(type));
+ pdp_header = pdp_packet_header(tmp_packet);
+ pdp_data = pdp_packet_data(tmp_packet);
+
+ /* check if we were able to create the pdp packet */
+ if (!(pdp_header && pdp_data)){
+ post("pdp_netreceive: can't create packet (type %s)", type);
+ pdp_udp_receiver_reset(x->x_receiver);
+ continue;
+ }
+
+ /* check size */
+ size = pdp_udp_receiver_size(x->x_receiver);
+ if ((pdp_header->size - PDP_HEADER_SIZE) != size){
+ pdp_packet_mark_unused(tmp_packet);
+ tmp_packet = -1;
+ post("pdp_netreceive: invalid packet size %d (pdp packet size = %d)",
+ size, pdp_header->size - PDP_HEADER_SIZE);
+ continue;
+ }
+
+ /* copy the data */
+ memcpy(pdp_data, pdp_udp_receiver_data(x->x_receiver), size);
+
+ /* copy the packet into queue */
+ x->x_index ^= 1;
+ pdp_packet_mark_unused(x->x_packet[x->x_index]);
+ x->x_packet[x->x_index] = tmp_packet;
+
+
+ }
+
+ exit:
+ post("thread exiting");
+ return 0;
+}
+
+
+static void pdp_udp_receive_free(t_pdp_udp_receive *x)
+{
+ int i;
+ void* retval;
+ x->x_exit_thread = 1; // wait for thread to finish
+ pthread_join(x->x_thread, &retval);
+
+ pdp_udp_receiver_free(x->x_receiver);
+
+ pdp_packet_mark_unused(x->x_packet[0]);
+ pdp_packet_mark_unused(x->x_packet[1]);
+
+}
+
+t_class *pdp_udp_receive_class;
+
+
+
+void *pdp_udp_receive_new(t_floatarg fport)
+{
+ int i;
+ int port;
+ struct hostent *hp;
+
+ t_pdp_udp_receive *x = (t_pdp_udp_receive *)pd_new(pdp_udp_receive_class);
+
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_packet[0] = -1;
+ x->x_packet[1] = -1;
+ x->x_index = 0;
+
+ port = (fport == 0.0f) ? 7777 : fport;
+ x->x_receiver = pdp_udp_receiver_new(port);
+
+ /* setup thread stuff & create thread */
+ x->x_exit_thread = 0;
+ pthread_attr_init(&x->x_attr);
+ pthread_attr_setschedpolicy(&x->x_attr, SCHED_OTHER);
+ pthread_create(&x->x_thread, &x->x_attr, receive_thread, x);
+
+
+ /* setup the clock */
+ x->x_clock = clock_new(x, (t_method)clock_tick);
+ clock_delay(x->x_clock, 0);
+
+ post("pdp_netreceive: WARNING: experimental object");
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_udp_receive_setup(void)
+{
+
+
+ pdp_udp_receive_class = class_new(gensym("pdp_netreceive"), (t_newmethod)pdp_udp_receive_new,
+ (t_method)pdp_udp_receive_free, sizeof(t_pdp_udp_receive), 0, A_DEFFLOAT, A_NULL);
+
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/generic/pdp_udp_send.c b/modules/generic/pdp_udp_send.c
new file mode 100644
index 0000000..cb55ad1
--- /dev/null
+++ b/modules/generic/pdp_udp_send.c
@@ -0,0 +1,336 @@
+/*
+ * Pure Data Packet module.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+
+/* this module sends a pure packet out as an udp packet stream */
+
+#include "pdp_net.h"
+#include "pdp.h"
+#include "pdp_resample.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <netdb.h>
+
+#define DD if(0) // print DROP debug info
+#define D if(0) // print extra connection debug info
+#define V if(0) // be verbose (parameter setting feedback)
+
+typedef struct pdp_udp_send_struct
+{
+
+ t_object x_obj;
+ t_float x_f;
+
+ /* sender object */
+ t_pdp_udp_sender *x_sender;
+
+ /* pthread vars */
+ pthread_mutex_t x_mut;
+ pthread_cond_t x_cond_data_ready;
+ pthread_cond_t x_cond_send_done;
+ pthread_t x_thread;
+ int x_exit_thread;
+
+ // drop info
+ unsigned int x_drop;
+
+ t_outlet *x_outlet0;
+
+ // packet queue
+ int x_nb_packets;
+ int x_read_packet;
+ int x_write_packet;
+ int *x_packet;
+
+
+} t_pdp_udp_send;
+
+
+
+
+
+/* some synchro code */
+
+static int _wait_for_feeder(t_pdp_udp_send *x)
+{
+
+ /* only use locking when there is no data */
+ if (x->x_packet[x->x_read_packet] == -1){
+
+ /* signal sending is done */
+ pthread_mutex_lock(&x->x_mut);
+ pthread_cond_signal(&x->x_cond_send_done);
+
+ /* wait until there is an item in the queue */
+ while((x->x_packet[x->x_read_packet] == -1) && (!x->x_exit_thread)){
+ pthread_cond_wait(&x->x_cond_data_ready, &x->x_mut);
+ }
+ pthread_mutex_unlock(&x->x_mut);
+
+ /* check if we need to stop the thread */
+ if (x->x_exit_thread) return 0;
+
+ }
+
+ return !x->x_exit_thread;
+}
+
+static void _signal_sender(t_pdp_udp_send *x)
+{
+
+ pthread_mutex_lock(&x->x_mut);
+ pthread_cond_signal(&x->x_cond_data_ready);
+ pthread_mutex_unlock(&x->x_mut);
+}
+
+static void _wait_until_done(t_pdp_udp_send *x)
+{
+ pthread_mutex_lock(&x->x_mut);
+ while (x->x_packet[x->x_read_packet] != -1){
+ pthread_cond_wait(&x->x_cond_send_done, &x->x_mut);
+ }
+ pthread_mutex_unlock(&x->x_mut);
+}
+
+
+static void _remove_packet_from_queue(t_pdp_udp_send *x)
+{
+
+}
+
+
+
+
+
+static void *send_thread(void *threaddata)
+{
+ t_pdp_udp_send *x = (t_pdp_udp_send *)threaddata;
+
+ /* main thread loop */
+
+ /* get a pdp packet from queue */
+ /* send header packet and make sure it has arrived */
+ /* send a chunk burst */
+ /* send done packet and get the resend list */
+ /* repeat until send list is empty */
+
+ while (_wait_for_feeder(x)){
+ t_pdp *header;
+ void *data;
+
+ /* check if we have a valid pdp packet */
+ if ((!(header = pdp_packet_header(x->x_packet[x->x_read_packet])))
+ ||(!(data = pdp_packet_data(x->x_packet[x->x_read_packet])))
+ ||(0 == header->desc)) goto remove; /* nothing to transmit */
+
+ /* send it */
+ pdp_udp_sender_send(x->x_sender,
+ header->desc->s_name,
+ header->size - PDP_HEADER_SIZE, data);
+
+
+ remove:
+ /* remove packet from queue */
+ pdp_packet_mark_unused(x->x_packet[x->x_read_packet]);
+ x->x_packet[x->x_read_packet] = -1;
+ x->x_read_packet++;
+ x->x_read_packet %= x->x_nb_packets;
+
+ }
+ return 0;
+}
+
+
+static void pdp_udp_send_input_0(t_pdp_udp_send *x, t_symbol *s, t_floatarg f)
+{
+
+ int p = (int)f;
+ int my_p;
+ int transferred = 0;
+
+ if (s== gensym("register_ro")){
+
+
+ // check if packet can be stored in the queue
+ // this is possible if the current write location does not contain a packet
+
+ if (x->x_packet[x->x_write_packet] == -1){
+
+ // get the packet outside of the lock
+ my_p = pdp_packet_copy_ro(p);
+
+
+ // add to queue (do we really need to lock here?>
+ //pthread_mutex_lock(&x->x_mut); // LOCK
+ x->x_packet[x->x_write_packet] = my_p;
+ x->x_write_packet++;
+ x->x_write_packet %= x->x_nb_packets;
+ transferred = 1;
+ //pthread_mutex_unlock(&x->x_mut); // UNLOCK
+ }
+
+ // signal sender if transfer succeded
+ if (transferred) _signal_sender(x);
+
+ // else send a float indicating the number of drops so far
+ else{
+ x->x_drop++;
+ //outlet_float(x->x_outlet0, (float)x->x_drop);
+
+ DD post ("pdp_netsend: DROP: queue full");
+ }
+ }
+}
+
+
+
+/* some flow control hacks */
+
+static void pdp_udp_send_timeout(t_pdp_udp_send *x, float f)
+{
+ if (f < 0.0f) f = 0.0f;
+ pdp_udp_sender_timeout_us(x->x_sender, 1000.0f * f);
+}
+
+
+static void pdp_udp_send_sleepgrain(t_pdp_udp_send *x, float f)
+{
+ if (f < 0.0f) f = 0.0f;
+ pdp_udp_sender_sleepgrain_us(x->x_sender, 1000.0f * f);
+}
+
+static void pdp_udp_send_sleepperiod(t_pdp_udp_send *x, float f)
+{
+ if (f < 0.0f) f = 0.0f;
+ pdp_udp_sender_sleepperiod(x->x_sender, f);
+}
+
+
+static void pdp_udp_send_udpsize(t_pdp_udp_send *x, float f)
+{
+ if (f < 0.0f) f = 0.0f;
+ pdp_udp_sender_udp_packet_size(x->x_sender, f);
+}
+
+static void pdp_udp_send_connect(t_pdp_udp_send *x, t_symbol *shost, t_float fport)
+{
+ unsigned int port;
+ struct hostent *hp;
+
+ /* suspend until sending thread is finished */
+ _wait_until_done(x);
+
+ /* set target address */
+ port = (fport == 0.0f) ? 7777 : fport;
+ if (shost == gensym("")) shost = gensym("127.0.0.1");
+
+ /* connect */
+ pdp_udp_sender_connect(x->x_sender, shost->s_name, port);
+
+}
+
+
+static void pdp_udp_send_free(t_pdp_udp_send *x)
+{
+ int i;
+ void* retval;
+ _wait_until_done(x); // send all remaining packets
+ x->x_exit_thread = 1; // .. and wait for thread to finish
+ _signal_sender(x);
+ pthread_join(x->x_thread, &retval);
+
+ pdp_udp_sender_free(x->x_sender);
+
+
+ for (i=0; i<x->x_nb_packets; i++) pdp_packet_mark_unused(x->x_packet[i]);
+ pdp_dealloc(x->x_packet);
+
+}
+
+t_class *pdp_udp_send_class;
+
+
+
+void *pdp_udp_send_new(void)
+{
+ int i;
+ pthread_attr_t attr;
+
+ t_pdp_udp_send *x = (t_pdp_udp_send *)pd_new(pdp_udp_send_class);
+
+ x->x_sender = pdp_udp_sender_new();
+
+ //x->x_outlet0 = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_nb_packets = 4;
+ x->x_packet = malloc(sizeof(int)*x->x_nb_packets);
+ for (i=0; i<x->x_nb_packets; i++) x->x_packet[i] = -1;
+ x->x_read_packet = 0;
+ x->x_write_packet = 0;
+
+ x->x_drop = 0;
+
+
+
+ /* setup thread stuff & create thread */
+ x->x_exit_thread = 0;
+ pthread_mutex_init(&x->x_mut, NULL);
+ pthread_cond_init(&x->x_cond_data_ready, NULL);
+ pthread_cond_init(&x->x_cond_send_done, NULL);
+ pthread_attr_init(&attr);
+ //pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
+ pthread_create(&x->x_thread, &attr, send_thread, x);
+ post("pdp_netsend: WARNING: experimental object");
+
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_udp_send_setup(void)
+{
+
+ pdp_udp_send_class = class_new(gensym("pdp_netsend"), (t_newmethod)pdp_udp_send_new,
+ (t_method)pdp_udp_send_free, sizeof(t_pdp_udp_send), 0, A_NULL);
+
+
+ class_addmethod(pdp_udp_send_class, (t_method)pdp_udp_send_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_udp_send_class, (t_method)pdp_udp_send_sleepgrain, gensym("sleepgrain"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_udp_send_class, (t_method)pdp_udp_send_sleepperiod, gensym("sleepperiod"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_udp_send_class, (t_method)pdp_udp_send_udpsize, gensym("udpsize"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_udp_send_class, (t_method)pdp_udp_send_timeout, gensym("timeout"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_udp_send_class, (t_method)pdp_udp_send_connect, gensym("connect"), A_SYMBOL, A_FLOAT, A_NULL);
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/opengl/doc/examples/arm.pd b/opengl/doc/examples/arm.pd
new file mode 100644
index 0000000..36a8f48
--- /dev/null
+++ b/opengl/doc/examples/arm.pd
@@ -0,0 +1,40 @@
+#N canvas 475 534 572 380 10;
+#X obj 43 10 inlet;
+#X obj 43 227 outlet;
+#X obj 43 141 3dp_push;
+#X obj 208 200 3dp_view transx 0.5;
+#X obj 43 113 3dp_view rotz;
+#X obj 260 11 inlet;
+#X obj 208 174 3dp_view scalex \$1;
+#X obj 43 186 3dp_view transx \$1;
+#X obj 260 275 r texture;
+#X obj 43 83 3dp_view roty;
+#X obj 129 61 r roty;
+#X obj 329 91 r scale;
+#X obj 43 37 3dp_draw cube 1;
+#X obj 143 11 r cubesize;
+#X obj 208 334 3dp_draw torus 0.25 0.5 6;
+#X obj 208 256 spigot;
+#X obj 245 232 r drawtorus;
+#X obj 276 307 r torusr1;
+#X obj 353 307 r torusr2;
+#X text 375 27 draw one arm segment;
+#X connect 0 0 12 0;
+#X connect 2 0 7 0;
+#X connect 2 1 6 0;
+#X connect 3 0 15 0;
+#X connect 4 0 2 0;
+#X connect 5 0 4 1;
+#X connect 6 0 3 0;
+#X connect 7 0 1 0;
+#X connect 8 0 14 1;
+#X connect 9 0 4 0;
+#X connect 10 0 9 1;
+#X connect 11 0 6 1;
+#X connect 11 0 7 1;
+#X connect 12 0 9 0;
+#X connect 13 0 12 2;
+#X connect 15 0 14 0;
+#X connect 16 0 15 1;
+#X connect 17 0 14 2;
+#X connect 18 0 14 3;
diff --git a/opengl/doc/examples/example01.pd b/opengl/doc/examples/example01.pd
new file mode 100644
index 0000000..969ee17
--- /dev/null
+++ b/opengl/doc/examples/example01.pd
@@ -0,0 +1,257 @@
+#N canvas 426 142 799 779 10;
+#X floatatom 126 37 5 0 0 0 - - -;
+#X obj 56 20 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X msg 83 19 stop;
+#X floatatom 360 431 5 0 0 0 - - -;
+#X obj 59 103 3dp_push;
+#X floatatom 672 189 5 0 0 0 - - -;
+#X obj 546 244 3dp_view transx 3;
+#X obj 546 270 3dp_light;
+#X obj 612 97 f;
+#X floatatom 641 98 5 0 0 0 - - -;
+#X floatatom 669 370 5 0 0 0 - - -;
+#X obj 182 491 arm 3;
+#X obj 182 514 arm 3;
+#X obj 182 537 arm 3;
+#X obj 182 467 arm 3;
+#X floatatom 360 455 5 0 0 0 - - -;
+#X floatatom 359 478 5 0 0 0 - - -;
+#X floatatom 358 501 5 0 0 0 - - -;
+#X floatatom 358 524 5 0 0 0 - - -;
+#X obj 182 584 arm 3;
+#X obj 182 607 arm 3;
+#X obj 182 630 arm 3;
+#X obj 182 560 arm 3;
+#X floatatom 358 548 5 0 0 0 - - -;
+#X floatatom 358 571 5 0 0 0 - - -;
+#X floatatom 358 594 5 0 0 0 - - -;
+#X obj 59 224 3dp_view roty;
+#X obj 284 449 * 1;
+#X obj 284 589 * -1;
+#X obj 182 653 arm 3;
+#X floatatom 358 617 5 0 0 0 - - -;
+#X obj 284 635 * -1.5;
+#X obj 663 686 s roty;
+#X floatatom 615 611 5 0 0 0 - - -;
+#X floatatom 671 585 5 0 0 0 - - -;
+#X obj 673 616 s scale;
+#X floatatom 359 388 5 0 0 0 - - -;
+#X obj 284 473 * -1.01;
+#X obj 284 496 * 0.99;
+#X obj 284 519 * -1.01;
+#X obj 284 542 * 2.1;
+#X obj 284 566 * -1.7;
+#X obj 182 425 3dp_draw cube 1.4;
+#X obj 182 809 3dp_draw cube 1.4;
+#X msg 597 536 4;
+#X obj 59 151 3dp_view transz -3;
+#X obj 546 216 3dp_view roty 54;
+#X obj 669 392 s cubesize;
+#X msg 360 345 3.15;
+#X msg 126 17 20;
+#X obj 284 612 * 0.11;
+#X floatatom 672 220 5 0 0 0 - - -;
+#X msg 612 72 0;
+#X obj 342 311 * 1;
+#X obj 59 201 3dp_view rotx;
+#X floatatom 164 187 5 0 0 0 - - -;
+#X floatatom 358 641 5 0 0 0 - - -;
+#X obj 182 700 arm 3;
+#X obj 182 724 arm 3;
+#X obj 182 748 arm 3;
+#X obj 182 677 arm 3;
+#X floatatom 359 664 5 0 0 0 - - -;
+#X floatatom 359 688 5 0 0 0 - - -;
+#X floatatom 360 712 5 0 0 0 - - -;
+#X obj 284 706 * -1;
+#X obj 182 771 arm 3;
+#X floatatom 360 735 5 0 0 0 - - -;
+#X obj 283 753 * -1.5;
+#X obj 284 659 * 2.1;
+#X obj 284 682 * -1.7;
+#X obj 283 730 * 0.11;
+#X obj 9 334 3dp_push;
+#X obj 182 399 3dp_view transz;
+#X floatatom 282 369 5 0 0 0 - - -;
+#X obj 131 371 3dp_view transz;
+#X obj 231 338 * -1;
+#X msg 282 341 2;
+#X obj 564 401 s drawtorus;
+#X obj 564 374 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X obj 674 496 s torusr1;
+#X floatatom 672 473 5 0 0 0 - - -;
+#X floatatom 667 419 5 0 0 0 - - -;
+#X obj 669 442 s torusr2;
+#X msg 564 349 1;
+#X obj 597 645 *;
+#X obj 59 126 3dp_push;
+#X obj 9 364 3dp_push;
+#X obj 9 437 3dp_view rotx;
+#X floatatom 96 416 5 0 0 0 - - -;
+#X obj 9 471 3dp_draw sphere 30 40;
+#X obj 9 593 3dp_snap;
+#X obj 473 487 / 1000;
+#X floatatom 473 461 5 0 0 0 - - -;
+#X obj 430 8 loadbang;
+#X obj 430 31 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 59 77 3dp_windowcontext;
+#X obj 59 274 3dp_push;
+#X obj 110 303 pdp_t p b;
+#X obj 9 307 pdp_t p b;
+#X msg 349 252 400;
+#X msg 311 252 -400;
+#X obj 342 287 +;
+#X msg 473 434 3;
+#X text 544 189 light source;
+#X obj 59 248 3dp_view scale 0.4;
+#X obj 640 157 s counter;
+#X obj 245 169 r counter;
+#X text 694 98 speed;
+#X obj 59 54 metro 20;
+#X obj 238 207 * 0.05;
+#X obj 9 570 spigot;
+#X obj 76 546 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 612 122 + 1;
+#X text 28 521 texture feedback;
+#X text 486 751 "no-bots in no-sphere";
+#X text 459 768 a double dance of 13 segments;
+#X text 549 734 ---;
+#X text 549 787 ---;
+#X obj 59 176 3dp_mouserotate;
+#X connect 0 0 108 1;
+#X connect 1 0 108 0;
+#X connect 2 0 108 0;
+#X connect 3 0 27 1;
+#X connect 4 0 85 0;
+#X connect 4 1 46 0;
+#X connect 5 0 46 1;
+#X connect 6 0 7 0;
+#X connect 8 0 112 0;
+#X connect 9 0 112 1;
+#X connect 10 0 47 0;
+#X connect 11 0 12 0;
+#X connect 12 0 13 0;
+#X connect 13 0 22 0;
+#X connect 14 0 11 0;
+#X connect 15 0 37 1;
+#X connect 16 0 38 1;
+#X connect 17 0 39 1;
+#X connect 18 0 40 1;
+#X connect 19 0 20 0;
+#X connect 20 0 21 0;
+#X connect 21 0 29 0;
+#X connect 22 0 19 0;
+#X connect 23 0 41 1;
+#X connect 24 0 28 1;
+#X connect 25 0 50 1;
+#X connect 26 0 104 0;
+#X connect 27 0 37 0;
+#X connect 27 0 14 1;
+#X connect 28 0 50 0;
+#X connect 28 0 20 1;
+#X connect 29 0 60 0;
+#X connect 30 0 31 1;
+#X connect 31 0 29 1;
+#X connect 31 0 68 0;
+#X connect 33 0 84 1;
+#X connect 34 0 35 0;
+#X connect 36 0 42 2;
+#X connect 36 0 43 2;
+#X connect 37 0 38 0;
+#X connect 37 0 11 1;
+#X connect 38 0 39 0;
+#X connect 38 0 12 1;
+#X connect 39 0 40 0;
+#X connect 39 0 13 1;
+#X connect 40 0 41 0;
+#X connect 40 0 22 1;
+#X connect 41 0 28 0;
+#X connect 41 0 19 1;
+#X connect 42 0 14 0;
+#X connect 44 0 34 0;
+#X connect 45 0 118 0;
+#X connect 46 0 6 0;
+#X connect 48 0 36 0;
+#X connect 48 0 102 0;
+#X connect 48 0 76 0;
+#X connect 48 0 83 0;
+#X connect 49 0 0 0;
+#X connect 50 0 31 0;
+#X connect 50 0 21 1;
+#X connect 51 0 6 1;
+#X connect 52 0 8 0;
+#X connect 53 0 27 0;
+#X connect 54 0 26 0;
+#X connect 55 0 54 1;
+#X connect 56 0 68 1;
+#X connect 57 0 58 0;
+#X connect 58 0 59 0;
+#X connect 59 0 65 0;
+#X connect 60 0 57 0;
+#X connect 61 0 69 1;
+#X connect 62 0 64 1;
+#X connect 63 0 70 1;
+#X connect 64 0 70 0;
+#X connect 64 0 58 1;
+#X connect 65 0 43 0;
+#X connect 66 0 67 1;
+#X connect 67 0 65 1;
+#X connect 68 0 69 0;
+#X connect 68 0 60 1;
+#X connect 69 0 64 0;
+#X connect 69 0 57 1;
+#X connect 70 0 67 0;
+#X connect 70 0 59 1;
+#X connect 71 0 86 0;
+#X connect 71 1 74 0;
+#X connect 72 0 42 0;
+#X connect 73 0 72 1;
+#X connect 73 0 75 0;
+#X connect 74 0 42 0;
+#X connect 75 0 74 1;
+#X connect 76 0 73 0;
+#X connect 78 0 77 0;
+#X connect 80 0 79 0;
+#X connect 81 0 82 0;
+#X connect 83 0 78 0;
+#X connect 84 0 32 0;
+#X connect 85 0 45 0;
+#X connect 86 0 87 0;
+#X connect 87 0 89 0;
+#X connect 88 0 87 1;
+#X connect 89 0 110 0;
+#X connect 90 1 89 1;
+#X connect 91 0 33 0;
+#X connect 92 0 91 0;
+#X connect 93 0 94 0;
+#X connect 94 0 1 0;
+#X connect 94 0 48 0;
+#X connect 95 0 4 0;
+#X connect 95 1 118 1;
+#X connect 96 0 98 0;
+#X connect 96 1 97 0;
+#X connect 97 0 72 0;
+#X connect 97 1 100 0;
+#X connect 98 0 71 0;
+#X connect 98 1 99 0;
+#X connect 99 0 101 0;
+#X connect 100 0 101 0;
+#X connect 101 0 53 0;
+#X connect 102 0 92 0;
+#X connect 102 0 44 0;
+#X connect 104 0 96 0;
+#X connect 106 0 109 0;
+#X connect 108 0 95 0;
+#X connect 108 0 8 0;
+#X connect 109 0 26 1;
+#X connect 110 0 90 0;
+#X connect 111 0 110 1;
+#X connect 112 0 8 1;
+#X connect 112 0 84 0;
+#X connect 112 0 101 1;
+#X connect 112 0 105 0;
+#X connect 118 0 54 0;
diff --git a/opengl/doc/examples/example02.pd b/opengl/doc/examples/example02.pd
new file mode 100644
index 0000000..d5023d9
--- /dev/null
+++ b/opengl/doc/examples/example02.pd
@@ -0,0 +1,46 @@
+#N canvas 696 306 480 535 10;
+#X obj 102 73 3dp_windowcontext;
+#X obj 102 36 metro 20;
+#X obj 102 122 3dp_push;
+#X obj 102 11 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 56 40 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X msg 253 32 cursor \$1;
+#X obj 253 12 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X obj 102 209 3dp_mouserotate;
+#X obj 153 322 3dp_draw cube 1;
+#X obj 265 139 3dp_view transxyz -4 3 4;
+#X obj 102 270 3dp_push;
+#X obj 102 348 3dp_push;
+#X obj 102 425 3dp_push;
+#X obj 153 296 3dp_view transx 2;
+#X obj 153 374 3dp_view transy 2;
+#X obj 153 451 3dp_view transz 2;
+#X obj 153 400 3dp_draw cube 1;
+#X obj 153 477 3dp_draw cube 1;
+#X obj 102 506 3dp_draw dodeca 1;
+#X text 25 231 use the mouse to rotate the model (but not the light
+source since it is rendered before the rotation is applied.);
+#X obj 265 161 3dp_light;
+#X connect 0 0 2 0;
+#X connect 0 1 7 1;
+#X connect 1 0 0 0;
+#X connect 2 0 7 0;
+#X connect 2 1 9 0;
+#X connect 3 0 1 0;
+#X connect 4 0 0 0;
+#X connect 5 0 0 0;
+#X connect 6 0 5 0;
+#X connect 7 0 10 0;
+#X connect 9 0 20 0;
+#X connect 10 0 11 0;
+#X connect 10 1 13 0;
+#X connect 11 0 12 0;
+#X connect 11 1 14 0;
+#X connect 12 0 18 0;
+#X connect 12 1 15 0;
+#X connect 13 0 8 0;
+#X connect 14 0 16 0;
+#X connect 15 0 17 0;
diff --git a/opengl/doc/examples/example03.pd b/opengl/doc/examples/example03.pd
new file mode 100644
index 0000000..48d714b
--- /dev/null
+++ b/opengl/doc/examples/example03.pd
@@ -0,0 +1,65 @@
+#N canvas 382 102 480 535 10;
+#X obj 102 73 3dp_windowcontext;
+#X obj 102 36 metro 20;
+#X obj 102 139 3dp_push;
+#X obj 261 185 3dp_light;
+#X obj 102 11 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 56 40 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X msg 253 32 cursor \$1;
+#X obj 253 12 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 102 209 3dp_mouserotate;
+#X obj 261 163 3dp_view transxyz -4 3 4;
+#X obj 102 427 arm 3;
+#X obj 371 230 loadbang;
+#X obj 102 333 pdp_t p p p p;
+#X obj 102 304 pdp_t p p p p;
+#X floatatom 199 386 5 0 0;
+#X obj 318 378 s roty;
+#X floatatom 318 327 5 0 0;
+#X obj 371 378 s drawtorus;
+#X obj 371 355 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X obj 102 256 pdp_t p b;
+#X obj 212 273 f 0;
+#X obj 302 352 *;
+#X obj 183 409 *;
+#X obj 212 307 + 0.1;
+#X obj 102 105 3dp_view transz -5;
+#X text 41 471 it is possible to send the rendercontext to the same
+rendering/transforming chain multiple times. (in other words: 3dp objects
+do not support fanout \, but they do support fanin.);
+#X connect 0 0 24 0;
+#X connect 0 1 8 1;
+#X connect 1 0 0 0;
+#X connect 2 0 8 0;
+#X connect 2 1 9 0;
+#X connect 4 0 1 0;
+#X connect 5 0 0 0;
+#X connect 6 0 0 0;
+#X connect 7 0 6 0;
+#X connect 8 0 19 0;
+#X connect 9 0 3 0;
+#X connect 11 0 18 0;
+#X connect 12 0 10 0;
+#X connect 12 1 10 0;
+#X connect 12 2 10 0;
+#X connect 12 3 10 0;
+#X connect 13 0 12 0;
+#X connect 13 1 12 0;
+#X connect 13 2 12 0;
+#X connect 13 3 12 0;
+#X connect 14 0 22 1;
+#X connect 16 0 21 1;
+#X connect 18 0 17 0;
+#X connect 19 0 13 0;
+#X connect 19 1 20 0;
+#X connect 20 0 23 0;
+#X connect 21 0 15 0;
+#X connect 22 0 10 1;
+#X connect 23 0 20 1;
+#X connect 23 0 22 0;
+#X connect 23 0 21 0;
+#X connect 24 0 2 0;
diff --git a/opengl/doc/examples/example04.pd b/opengl/doc/examples/example04.pd
new file mode 100644
index 0000000..5d22270
--- /dev/null
+++ b/opengl/doc/examples/example04.pd
@@ -0,0 +1,25 @@
+#N canvas 288 311 566 284 10;
+#X obj 54 156 3dp_windowcontext;
+#X obj 54 43 metro 40;
+#X obj 54 19 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X obj 54 76 t b b;
+#X obj 134 120 pdp_convert texture/*/*;
+#X obj 54 183 3dp_mouserotate;
+#X floatatom 180 202 5 0 0 0 - - -;
+#X msg 134 68 open /dev/video1;
+#X obj 54 227 3dp_draw sphere 5;
+#X text 132 18 convert pdp image packets to textures and map them on
+a sphere;
+#X obj 134 93 pdp_v4l;
+#X connect 0 0 5 0;
+#X connect 0 1 5 1;
+#X connect 1 0 3 0;
+#X connect 2 0 1 0;
+#X connect 3 0 0 0;
+#X connect 3 1 10 0;
+#X connect 4 0 8 1;
+#X connect 5 0 8 0;
+#X connect 6 0 8 2;
+#X connect 7 0 10 0;
+#X connect 10 0 4 0;
diff --git a/opengl/doc/examples/example05.pd b/opengl/doc/examples/example05.pd
new file mode 100644
index 0000000..7996355
--- /dev/null
+++ b/opengl/doc/examples/example05.pd
@@ -0,0 +1,41 @@
+#N canvas 352 162 732 325 10;
+#X obj 54 43 metro 40;
+#X obj 54 19 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 314 120 pdp_convert texture/*/*;
+#X floatatom 343 212 5 0 0;
+#X obj 286 247 3dp_draw sphere 5;
+#X obj 314 95 pdp_plasma;
+#X obj 408 48 loadbang;
+#X msg 408 70 dim 512 256;
+#X obj 314 65 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 181 144 3dp_windowcontext;
+#X obj 181 203 3dp_view roty;
+#X floatatom 267 176 5 0 0;
+#X obj 31 144 3dp_windowcontext;
+#X obj 31 203 3dp_view roty;
+#X floatatom 117 176 5 0 0;
+#X obj 286 278 3dp_draw cube 2;
+#X obj 336 164 pdp_del 3;
+#X text 414 164 <- textures can be stored in a delay line;
+#X text 133 9 convert pdp image packets to textures and map them on
+a sphere and cube. create 2 independent viewing windows.;
+#X connect 0 0 9 0;
+#X connect 0 0 12 0;
+#X connect 1 0 0 0;
+#X connect 2 0 4 1;
+#X connect 2 0 16 0;
+#X connect 3 0 4 2;
+#X connect 4 0 15 0;
+#X connect 5 0 2 0;
+#X connect 6 0 7 0;
+#X connect 7 0 5 0;
+#X connect 8 0 5 0;
+#X connect 9 0 10 0;
+#X connect 10 0 4 0;
+#X connect 11 0 10 1;
+#X connect 12 0 13 0;
+#X connect 13 0 4 0;
+#X connect 14 0 13 1;
+#X connect 16 0 15 1;
diff --git a/opengl/doc/examples/example06.pd b/opengl/doc/examples/example06.pd
new file mode 100644
index 0000000..8d8595a
--- /dev/null
+++ b/opengl/doc/examples/example06.pd
@@ -0,0 +1,85 @@
+#N canvas 516 361 634 497 10;
+#X obj 23 119 metro 40;
+#X obj 23 89 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 360 397 pdp_convert texture/*/*;
+#X obj 23 387 3dp_mouserotate;
+#X obj 155 255 3dp_light 0;
+#X obj 23 193 3dp_push;
+#X floatatom 269 198 5 0 0;
+#X obj 23 161 3dp_windowcontext;
+#X obj 360 299 pdp_convert image/grey/*;
+#X obj 360 184 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X msg 478 163 random;
+#X msg 479 186 rule gameoflife;
+#X msg 478 114 rule fire;
+#X obj 360 270 pdp_ca;
+#X obj 360 371 pdp_motion_phase;
+#X floatatom 479 343 5 0 0;
+#X obj 478 88 loadbang;
+#X msg 479 322 0.33;
+#X obj 328 473 3dp_draw sphere 2.6;
+#X floatatom 463 434 5 0 0;
+#X obj 155 227 3dp_view transz 5;
+#X floatatom 480 237 5 0 0;
+#X msg 517 322 1;
+#X obj 360 347 pdp_gain;
+#X floatatom 411 323 5 0 0;
+#X msg 478 139 dim 256 128;
+#X obj 179 393 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 328 419 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X obj 23 416 3dp_toggle depth_test 0;
+#X obj 179 445 3dp_toggle blend_add 1;
+#X msg 480 213 1;
+#X obj 23 316 3dp_mode texture;
+#X floatatom 281 310 5 0 0;
+#X obj 156 339 3dp_view scale 1;
+#X floatatom 280 340 5 0 0;
+#X obj 156 366 3dp_view rotz;
+#X text 141 197 move light source;
+#X text 143 294 transform texture coords;
+#X text 31 12 blending with depth test disabled using 3dp_toggle object.
+together with a cellular automata texture (you need pdp_scaf for this)
+and texture coordinate transformation.;
+#X connect 0 0 7 0;
+#X connect 0 0 9 0;
+#X connect 1 0 0 0;
+#X connect 2 0 18 1;
+#X connect 3 0 28 0;
+#X connect 5 0 31 0;
+#X connect 5 1 20 0;
+#X connect 6 0 20 1;
+#X connect 7 0 5 0;
+#X connect 7 1 3 1;
+#X connect 8 0 23 0;
+#X connect 9 0 13 0;
+#X connect 10 0 13 0;
+#X connect 11 0 13 0;
+#X connect 12 0 13 0;
+#X connect 12 0 25 0;
+#X connect 13 0 8 0;
+#X connect 14 0 2 0;
+#X connect 15 0 14 1;
+#X connect 16 0 12 0;
+#X connect 17 0 15 0;
+#X connect 19 0 18 2;
+#X connect 20 0 4 0;
+#X connect 21 0 13 2;
+#X connect 22 0 15 0;
+#X connect 23 0 14 0;
+#X connect 24 0 23 1;
+#X connect 25 0 13 0;
+#X connect 25 0 10 0;
+#X connect 26 0 28 1;
+#X connect 27 0 29 1;
+#X connect 28 1 29 0;
+#X connect 29 1 18 0;
+#X connect 30 0 21 0;
+#X connect 31 0 3 0;
+#X connect 31 1 33 0;
+#X connect 32 0 33 1;
+#X connect 33 0 35 0;
+#X connect 34 0 35 1;
diff --git a/opengl/doc/examples/example07.pd b/opengl/doc/examples/example07.pd
new file mode 100644
index 0000000..cd3618c
--- /dev/null
+++ b/opengl/doc/examples/example07.pd
@@ -0,0 +1,25 @@
+#N canvas 400 454 637 428 10;
+#X obj 18 131 3dp_windowcontext;
+#X obj 18 159 3dp_push;
+#X obj 201 70 metro 40;
+#X obj 201 42 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 201 99 pdp_v4l;
+#X obj 201 131 pdp_convert texture/*/*;
+#X floatatom 258 265 5 0 0;
+#X obj 18 218 3dp_view scalex 1.33333;
+#X obj 18 187 3dp_mouserotate;
+#X text 15 10 using a square scaled by an aspect ratio to construct
+an an alternative pdp_glx;
+#X obj 144 290 3dp_draw square 8;
+#X connect 0 0 1 0;
+#X connect 0 1 8 1;
+#X connect 1 0 8 0;
+#X connect 2 0 0 0;
+#X connect 2 0 4 0;
+#X connect 3 0 2 0;
+#X connect 4 0 5 0;
+#X connect 5 0 10 1;
+#X connect 6 0 10 2;
+#X connect 7 0 10 0;
+#X connect 8 0 7 0;
diff --git a/opengl/doc/examples/example08.pd b/opengl/doc/examples/example08.pd
new file mode 100644
index 0000000..a5f7e9d
--- /dev/null
+++ b/opengl/doc/examples/example08.pd
@@ -0,0 +1,94 @@
+#N canvas 96 78 756 667 10;
+#X obj 18 131 3dp_windowcontext;
+#X obj 18 159 3dp_push;
+#X obj 327 80 metro 40;
+#X obj 327 52 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 327 224 pdp_v4l;
+#X obj 431 463 pdp_convert texture/*/*;
+#X floatatom 488 554 5 0 0;
+#X obj 18 215 3dp_view scalex 1.33333;
+#X obj 18 189 3dp_mouserotate;
+#X floatatom 83 308 5 0 0;
+#X msg 83 287 10;
+#X obj 69 428 3dp_view rotx;
+#X obj 69 475 3dp_view roty;
+#X obj 69 523 3dp_view rotz;
+#X obj 155 398 random 360;
+#X obj 155 453 random 360;
+#X obj 155 500 random 360;
+#X obj 155 371 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 18 366 3dp_push;
+#X text 132 305 nb of squares;
+#X floatatom 220 369 5 0 0;
+#X obj 431 309 pdp_plasma;
+#X obj 431 256 loadbang;
+#X obj 327 192 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 397 279 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X msg 417 55 200;
+#X floatatom 510 364 5 0 0;
+#X msg 378 55 40;
+#X obj 327 126 spigot;
+#X obj 385 106 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X obj 431 395 pdp_gain 0.3;
+#X obj 18 333 3dp_for 10;
+#X text 21 12 another example of accumulated blending combined with
+a 3dp_for object to render multiple randomly rotated squares.;
+#X msg 386 186 open /dev/video1;
+#X obj 18 246 3dp_blend;
+#X text 411 106 switch between video textures and stills;
+#X msg 386 160 open /dev/video0;
+#X text 87 246 <-- click for more info;
+#X msg 431 281 dim 64 64 \, bang;
+#X obj 548 228 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 548 256 metro 2000;
+#X obj 374 583 3dp_draw square 8;
+#X connect 0 0 1 0;
+#X connect 0 1 8 1;
+#X connect 1 0 8 0;
+#X connect 2 0 0 0;
+#X connect 2 0 28 0;
+#X connect 3 0 2 0;
+#X connect 4 0 30 0;
+#X connect 5 0 41 1;
+#X connect 6 0 41 2;
+#X connect 7 0 34 0;
+#X connect 8 0 7 0;
+#X connect 9 0 31 1;
+#X connect 10 0 9 0;
+#X connect 11 0 12 0;
+#X connect 12 0 13 0;
+#X connect 13 0 41 0;
+#X connect 14 0 11 1;
+#X connect 15 0 12 1;
+#X connect 16 0 13 1;
+#X connect 17 0 14 0;
+#X connect 17 0 15 0;
+#X connect 17 0 16 0;
+#X connect 18 1 11 0;
+#X connect 20 0 14 1;
+#X connect 20 0 15 1;
+#X connect 20 0 16 1;
+#X connect 21 0 30 0;
+#X connect 22 0 38 0;
+#X connect 23 0 4 0;
+#X connect 24 0 21 0;
+#X connect 25 0 2 1;
+#X connect 26 0 30 1;
+#X connect 27 0 2 1;
+#X connect 28 0 23 0;
+#X connect 29 0 28 1;
+#X connect 30 0 5 0;
+#X connect 31 0 18 0;
+#X connect 31 1 17 0;
+#X connect 33 0 4 0;
+#X connect 34 0 31 0;
+#X connect 36 0 4 0;
+#X connect 38 0 21 0;
+#X connect 39 0 40 0;
+#X connect 40 0 21 0;
diff --git a/opengl/doc/examples/example09.pd b/opengl/doc/examples/example09.pd
new file mode 100644
index 0000000..3333d3a
--- /dev/null
+++ b/opengl/doc/examples/example09.pd
@@ -0,0 +1,90 @@
+#N canvas 96 78 756 667 10;
+#X obj 18 111 3dp_windowcontext;
+#X obj 18 139 3dp_push;
+#X obj 18 169 3dp_mouserotate;
+#X floatatom 141 370 5 0 0;
+#X msg 141 349 10;
+#X obj 127 490 3dp_view rotx;
+#X obj 127 537 3dp_view roty;
+#X obj 127 585 3dp_view rotz;
+#X obj 213 460 random 360;
+#X obj 213 515 random 360;
+#X obj 213 562 random 360;
+#X obj 213 433 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 76 428 3dp_push;
+#X floatatom 278 431 5 0 0;
+#X obj 76 395 3dp_for 10;
+#X obj 18 226 3dp_blend;
+#X text 87 226 <-- click for more info;
+#X obj 18 365 3dp_color;
+#X floatatom 32 311 5 0 0;
+#X floatatom 78 311 5 0 0;
+#X floatatom 123 311 5 0 0;
+#X floatatom 167 311 5 0 0;
+#X obj 415 193 3dp_view transz 5;
+#X obj 415 217 3dp_light;
+#X obj 18 78 metro 40;
+#X obj 18 50 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X floatatom 529 166 5 0 0;
+#X obj 32 252 vsl 15 50 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 2200 1;
+#X obj 78 251 vsl 15 50 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 2100 1;
+#X obj 123 251 vsl 15 50 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 3800 1;
+#X obj 169 250 vsl 15 50 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 1100 1;
+#X text 209 275 in this blending mode \, alpha is interpreted as a
+color gain;
+#X floatatom 515 137 5 0 0;
+#X floatatom 144 178 5 0 0;
+#X obj 127 633 3dp_draw tetra 4;
+#X text 14 13 similar to as example08.;
+#X obj 415 168 3dp_view roty 5;
+#X obj 18 199 3dp_view scale 4;
+#X msg 278 406 18;
+#X msg 515 112 75;
+#X connect 0 0 1 0;
+#X connect 0 1 2 1;
+#X connect 1 0 2 0;
+#X connect 1 1 36 0;
+#X connect 2 0 37 0;
+#X connect 3 0 14 1;
+#X connect 4 0 3 0;
+#X connect 5 0 6 0;
+#X connect 6 0 7 0;
+#X connect 7 0 34 0;
+#X connect 8 0 5 1;
+#X connect 9 0 6 1;
+#X connect 10 0 7 1;
+#X connect 11 0 8 0;
+#X connect 11 0 9 0;
+#X connect 11 0 10 0;
+#X connect 12 1 5 0;
+#X connect 13 0 8 1;
+#X connect 13 0 9 1;
+#X connect 13 0 10 1;
+#X connect 14 0 12 0;
+#X connect 14 1 11 0;
+#X connect 15 0 17 0;
+#X connect 17 1 14 0;
+#X connect 18 0 17 1;
+#X connect 19 0 17 2;
+#X connect 20 0 17 3;
+#X connect 21 0 17 4;
+#X connect 22 0 23 0;
+#X connect 24 0 0 0;
+#X connect 25 0 24 0;
+#X connect 26 0 22 1;
+#X connect 27 0 18 0;
+#X connect 28 0 19 0;
+#X connect 29 0 20 0;
+#X connect 30 0 21 0;
+#X connect 32 0 36 1;
+#X connect 33 0 37 1;
+#X connect 36 0 22 0;
+#X connect 37 0 15 0;
+#X connect 38 0 13 0;
+#X connect 39 0 32 0;
diff --git a/opengl/doc/examples/example10.pd b/opengl/doc/examples/example10.pd
new file mode 100644
index 0000000..2216fb3
--- /dev/null
+++ b/opengl/doc/examples/example10.pd
@@ -0,0 +1,39 @@
+#N canvas 592 340 647 499 10;
+#X obj 231 344 pdp_xv;
+#X obj 54 72 metro 40;
+#X obj 54 47 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 54 140 3dp_push;
+#X obj 248 158 3dp_view transz 5;
+#X floatatom 362 126 5 0 0;
+#X obj 248 182 3dp_light 0;
+#X obj 54 169 3dp_mouserotate;
+#X obj 54 111 3dp_windowcontext;
+#X obj 231 311 pdp_blur;
+#X obj 285 291 hsl 128 15 0 1 0 1 empty empty empty -2 -6 0 8 -262144
+-1 -1 8100 1;
+#X text 104 7 connect 3dp and basic pdp image processing with 3dp_snap
+;
+#X text 105 26 3dp_snap defaults to texture packets \, but you can
+specify the desired type as a creation argument;
+#X obj 107 377 pdp_convert texture/*/*;
+#X obj 54 412 3dp_draw cube 10;
+#X text 247 233 <- specify packet type and capture area;
+#X obj 54 200 3dp_draw cube 2;
+#X obj 54 234 3dp_snap image/*/* 320 240;
+#X connect 1 0 8 0;
+#X connect 2 0 1 0;
+#X connect 3 0 7 0;
+#X connect 3 1 4 0;
+#X connect 4 0 6 0;
+#X connect 5 0 4 1;
+#X connect 7 0 16 0;
+#X connect 8 0 3 0;
+#X connect 8 1 7 1;
+#X connect 9 0 13 0;
+#X connect 9 0 0 0;
+#X connect 10 0 9 1;
+#X connect 13 0 14 1;
+#X connect 16 0 17 0;
+#X connect 17 0 14 0;
+#X connect 17 1 9 0;
diff --git a/opengl/doc/examples/example11.pd b/opengl/doc/examples/example11.pd
new file mode 100644
index 0000000..c902408
--- /dev/null
+++ b/opengl/doc/examples/example11.pd
@@ -0,0 +1,77 @@
+#N canvas 542 122 669 675 10;
+#X obj 23 70 metro 40;
+#X obj 23 40 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 23 517 3dp_mouserotate;
+#X obj 171 520 3dp_light 0;
+#X obj 23 444 3dp_push;
+#X obj 171 492 3dp_view transz 5;
+#X obj 23 112 3dp_windowcontext;
+#X obj 205 311 3dp_snap;
+#X obj 205 213 3dp_draw clear;
+#X obj 23 406 3dp_draw clear;
+#X obj 60 347 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 23 157 3dp_subcontext 64 64;
+#X obj 23 380 spigot;
+#X text 384 210 <- clear it;
+#X text 380 309 <- snap to texture;
+#X obj 205 261 3dp_view rota 1 1 0;
+#X floatatom 333 236 5 0 0;
+#X text 69 10 multipass rendering to texture using a subcontext (subwindow)
+;
+#X text 381 285 <- draw a wireframe sphere;
+#X text 383 235 <- rotate around (1 \, 1 \, 0);
+#X text 135 406 <- clear the window;
+#X obj 205 286 3dp_draw wsphere 3 10 10;
+#X obj 323 429 3dp_draw clear;
+#X obj 323 405 3dp_view reset;
+#X obj 323 507 3dp_draw wdodeca;
+#X obj 323 532 3dp_snap;
+#X obj 22 613 3dp_draw cube 10;
+#X obj 360 348 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X obj 323 381 spigot;
+#X text 381 346 <- enable/disable second pass;
+#X text 87 345 <- enable/disable third pass;
+#X text 443 404 <- reset context;
+#X text 442 427 <- clear context;
+#X text 442 507 <- draw wire dodecahedron;
+#X text 441 532 <- snap to texture;
+#X obj 323 483 3dp_view rota 1 1 0;
+#X floatatom 451 458 5 0 0;
+#X text 501 457 <- rotate around (1 \, 1 \, 0);
+#X obj 23 562 3dp_draw cube 2;
+#X text 204 142 NOTE: the part you use as a subcontext must be visible
+for the texture snap to work.;
+#X text 203 91 use the lower left 64x64 part of the window as a subcontext
+(right outlet) the left outlet is a new full window context reset to
+default.;
+#X connect 0 0 6 0;
+#X connect 1 0 0 0;
+#X connect 2 0 38 0;
+#X connect 4 0 2 0;
+#X connect 4 1 5 0;
+#X connect 5 0 3 0;
+#X connect 6 0 11 0;
+#X connect 6 1 2 1;
+#X connect 7 0 28 0;
+#X connect 7 1 38 1;
+#X connect 8 0 15 0;
+#X connect 9 0 4 0;
+#X connect 10 0 12 1;
+#X connect 11 0 12 0;
+#X connect 11 1 8 0;
+#X connect 12 0 9 0;
+#X connect 15 0 21 0;
+#X connect 16 0 15 4;
+#X connect 21 0 7 0;
+#X connect 22 0 35 0;
+#X connect 23 0 22 0;
+#X connect 24 0 25 0;
+#X connect 25 1 26 1;
+#X connect 27 0 28 1;
+#X connect 28 0 23 0;
+#X connect 35 0 24 0;
+#X connect 36 0 35 4;
+#X connect 38 0 26 0;
diff --git a/opengl/doc/examples/example12.pd b/opengl/doc/examples/example12.pd
new file mode 100644
index 0000000..14f8afd
--- /dev/null
+++ b/opengl/doc/examples/example12.pd
@@ -0,0 +1,90 @@
+#N canvas 529 191 669 751 10;
+#X obj 55 68 metro 40;
+#X obj 55 38 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 55 515 3dp_mouserotate;
+#X obj 184 514 3dp_light 0;
+#X obj 55 442 3dp_push;
+#X obj 184 486 3dp_view transz 5;
+#X obj 55 110 3dp_windowcontext;
+#X obj 343 481 3dp_snap;
+#X obj 55 404 3dp_draw clear;
+#X obj 436 272 3dp_light 0;
+#X obj 436 244 3dp_view transxyz 5 5 0;
+#X obj 285 226 3dp_push;
+#X obj 343 449 3dp_draw sphere 7 10 10;
+#X text 69 10 another multipass rendering example;
+#X obj 55 576 3dp_draw cube 7;
+#X obj 55 155 3dp_subcontext 256 256;
+#X obj 285 260 3dp_mouserotate;
+#X obj 343 413 3dp_draw icosa 1;
+#X obj 285 342 3dp_color;
+#X obj 343 387 3dp_view scale 1;
+#X floatatom 450 358 5 0 0;
+#N canvas 0 0 291 420 coord_to_color 0;
+#X obj 97 85 route drag;
+#X obj 97 147 *;
+#X obj 134 146 *;
+#X obj 97 174 +;
+#X obj 97 281 sqrt;
+#X obj 97 225 - 1;
+#X obj 97 249 * -1;
+#X obj 97 114 unpack 0 0;
+#X obj 98 45 inlet;
+#X obj 53 337 outlet;
+#X obj 181 332 outlet;
+#X obj 116 336 outlet;
+#X connect 0 0 7 0;
+#X connect 1 0 3 0;
+#X connect 2 0 3 1;
+#X connect 3 0 5 0;
+#X connect 4 0 11 0;
+#X connect 5 0 6 0;
+#X connect 6 0 4 0;
+#X connect 7 0 1 1;
+#X connect 7 0 1 0;
+#X connect 7 0 9 0;
+#X connect 7 1 2 1;
+#X connect 7 1 2 0;
+#X connect 7 1 10 0;
+#X connect 8 0 0 0;
+#X restore 304 305 pd coord_to_color;
+#X obj 453 337 hsl 128 15 1 3 0 1 empty empty empty -2 -6 0 8 -262144
+-1 -1 7900 1;
+#X obj 285 196 3dp_draw clear;
+#X obj 92 622 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 55 662 3dp_screenshot /tmp/screenshot.png;
+#X msg 227 71 dim 1023 768;
+#X connect 0 0 6 0;
+#X connect 1 0 0 0;
+#X connect 2 0 14 0;
+#X connect 4 0 2 0;
+#X connect 4 1 5 0;
+#X connect 5 0 3 0;
+#X connect 6 0 15 0;
+#X connect 6 1 2 1;
+#X connect 6 1 21 0;
+#X connect 6 1 16 1;
+#X connect 7 1 12 1;
+#X connect 7 1 14 1;
+#X connect 8 0 4 0;
+#X connect 10 0 9 0;
+#X connect 11 0 16 0;
+#X connect 11 1 10 0;
+#X connect 12 0 7 0;
+#X connect 14 0 25 0;
+#X connect 15 0 8 0;
+#X connect 15 1 23 0;
+#X connect 16 0 18 0;
+#X connect 17 0 12 0;
+#X connect 18 1 19 0;
+#X connect 19 0 17 0;
+#X connect 20 0 19 1;
+#X connect 21 0 18 1;
+#X connect 21 1 18 2;
+#X connect 21 2 18 3;
+#X connect 22 0 20 0;
+#X connect 23 0 11 0;
+#X connect 24 0 25 0;
+#X connect 26 0 6 0;
diff --git a/opengl/doc/examples/example13.pd b/opengl/doc/examples/example13.pd
new file mode 100644
index 0000000..a8c5f75
--- /dev/null
+++ b/opengl/doc/examples/example13.pd
@@ -0,0 +1,81 @@
+#N canvas 478 168 669 751 10;
+#X obj 55 68 metro 40;
+#X obj 55 38 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 55 110 3dp_windowcontext;
+#X obj 262 479 3dp_snap;
+#X obj 355 270 3dp_light 0;
+#X obj 355 242 3dp_view transxyz 5 5 0;
+#X obj 204 224 3dp_push;
+#X obj 262 447 3dp_draw sphere 7 10 10;
+#X obj 204 258 3dp_mouserotate;
+#X obj 262 411 3dp_draw icosa 1;
+#X obj 204 340 3dp_color;
+#X obj 262 385 3dp_view scale 1;
+#X floatatom 369 356 5 0 0;
+#N canvas 0 0 291 420 coord_to_color 0;
+#X obj 97 85 route drag;
+#X obj 97 147 *;
+#X obj 134 146 *;
+#X obj 97 174 +;
+#X obj 97 281 sqrt;
+#X obj 97 225 - 1;
+#X obj 97 249 * -1;
+#X obj 97 114 unpack 0 0;
+#X obj 98 45 inlet;
+#X obj 53 337 outlet;
+#X obj 181 332 outlet;
+#X obj 116 336 outlet;
+#X connect 0 0 7 0;
+#X connect 1 0 3 0;
+#X connect 2 0 3 1;
+#X connect 3 0 5 0;
+#X connect 4 0 11 0;
+#X connect 5 0 6 0;
+#X connect 6 0 4 0;
+#X connect 7 0 1 1;
+#X connect 7 0 1 0;
+#X connect 7 0 9 0;
+#X connect 7 1 2 1;
+#X connect 7 1 2 0;
+#X connect 7 1 10 0;
+#X connect 8 0 0 0;
+#X restore 223 303 pd coord_to_color;
+#X obj 372 335 hsl 128 15 1 3 0 1 empty empty empty -2 -6 0 8 -262144
+-1 -1 7900 1;
+#X obj 204 194 3dp_draw clear;
+#X text 209 658 <- stretch the texture to cover the entire window (click
+for info);
+#X text 119 13 fixed resolution processing (independent of the display
+window size) using multipass rendering;
+#X floatatom 210 631 5 0 0;
+#X obj 55 658 3dp_display_texture;
+#X text 338 480 <- snap the result to a texture;
+#X text 228 154 <- create a subcontext to do some drawing;
+#X text 402 76 see also;
+#X obj 405 97 3dp_fixedsizewindowcontext 64 64;
+#X obj 55 155 3dp_subcontext 320 240;
+#X connect 0 0 2 0;
+#X connect 1 0 0 0;
+#X connect 2 0 24 0;
+#X connect 2 1 13 0;
+#X connect 2 1 8 1;
+#X connect 3 1 7 1;
+#X connect 3 1 19 1;
+#X connect 5 0 4 0;
+#X connect 6 0 8 0;
+#X connect 6 1 5 0;
+#X connect 7 0 3 0;
+#X connect 8 0 10 0;
+#X connect 9 0 7 0;
+#X connect 10 1 11 0;
+#X connect 11 0 9 0;
+#X connect 12 0 11 1;
+#X connect 13 0 10 1;
+#X connect 13 1 10 2;
+#X connect 13 2 10 3;
+#X connect 14 0 12 0;
+#X connect 15 0 6 0;
+#X connect 18 0 19 2;
+#X connect 24 0 19 0;
+#X connect 24 1 15 0;
diff --git a/opengl/doc/examples/example14.pd b/opengl/doc/examples/example14.pd
new file mode 100644
index 0000000..fa0cabb
--- /dev/null
+++ b/opengl/doc/examples/example14.pd
@@ -0,0 +1,66 @@
+#N canvas 586 145 669 674 10;
+#X obj 67 71 3dp_windowcontext;
+#X obj 67 46 metro 40;
+#X obj 67 23 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X obj 67 155 3dp_blend;
+#X obj 176 423 3dp_view transxyz;
+#X msg 226 296 reset;
+#X obj 197 374 *;
+#X obj 304 375 *;
+#X floatatom 320 347 5 0 0;
+#X obj 274 154 vsl 15 30 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 700 1;
+#X obj 316 154 vsl 15 30 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 1100 1;
+#X obj 295 154 vsl 15 30 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 1200 1;
+#X obj 337 154 vsl 15 30 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 400 1;
+#X obj 125 253 3dp_push;
+#X obj 67 95 3dp_mouserotate;
+#X obj 67 124 3dp_view scale 1;
+#X floatatom 206 99 5 0 0;
+#X obj 209 79 hsl 128 15 0.2 5 1 1 empty empty empty -2 -6 0 8 -262144
+-1 -1 7000 1;
+#X obj 323 326 hsl 128 15 0.1 2 1 1 empty empty empty -2 -6 0 8 -262144
+-1 -1 2100 1;
+#X obj 67 187 3dp_color;
+#X obj 226 223 route press3;
+#X text 329 223 <- right click mouse to reset;
+#X text 211 253 <- remove 3dp_push object to accumulate the translations
+;
+#X obj 197 326 randomwalk2D 100;
+#X obj 125 224 3dp_for 100;
+#X text 287 130 R G B I;
+#X obj 176 460 3dp_draw cube 1;
+#X text 227 13 using 3dp_for and the randomwalk2D abstraction to create
+random walking blended cubes.;
+#X connect 0 0 14 0;
+#X connect 0 1 14 1;
+#X connect 0 1 20 0;
+#X connect 1 0 0 0;
+#X connect 2 0 1 0;
+#X connect 3 0 19 0;
+#X connect 4 0 26 0;
+#X connect 5 0 23 0;
+#X connect 6 0 4 1;
+#X connect 7 0 4 2;
+#X connect 8 0 7 1;
+#X connect 8 0 6 1;
+#X connect 9 0 19 1;
+#X connect 10 0 19 3;
+#X connect 11 0 19 2;
+#X connect 12 0 19 4;
+#X connect 13 1 4 0;
+#X connect 14 0 15 0;
+#X connect 15 0 3 0;
+#X connect 16 0 15 1;
+#X connect 17 0 16 0;
+#X connect 18 0 8 0;
+#X connect 19 1 24 0;
+#X connect 20 0 5 0;
+#X connect 23 0 6 0;
+#X connect 23 1 7 0;
+#X connect 24 0 13 0;
+#X connect 24 1 23 0;
diff --git a/opengl/doc/examples/example15.pd b/opengl/doc/examples/example15.pd
new file mode 100644
index 0000000..281c91a
--- /dev/null
+++ b/opengl/doc/examples/example15.pd
@@ -0,0 +1,126 @@
+#N canvas 269 234 933 697 10;
+#X obj 67 46 metro 40;
+#X obj 67 23 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 67 224 3dp_blend;
+#X obj 176 673 3dp_view transxyz;
+#X msg 260 342 reset;
+#X obj 197 497 *;
+#X obj 304 498 *;
+#X obj 272 218 vsl 15 30 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 1000 1;
+#X obj 314 218 vsl 15 30 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 0 1;
+#X obj 293 218 vsl 15 30 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 100 1;
+#X obj 335 218 vsl 15 30 0 1 0 1 empty empty empty 0 -8 0 8 -262144
+-1 -1 200 1;
+#X obj 125 304 3dp_push;
+#X obj 67 119 3dp_mouserotate;
+#X obj 67 176 3dp_view scale 1;
+#X floatatom 241 145 5 0 0 0 - - -;
+#X obj 244 125 hsl 128 15 0.2 5 1 1 empty empty empty -2 -6 0 8 -262144
+-1 -1 3800 1;
+#X obj 523 456 hsl 128 15 0.01 2 1 1 empty empty empty -2 -6 0 8 -262144
+-1 -1 7900 1;
+#X obj 67 251 3dp_color;
+#X obj 402 231 route press3;
+#X text 211 304 <- remove 3dp_push object to accumulate the translations
+;
+#X obj 197 392 randomwalk2D 100;
+#X obj 125 275 3dp_for 100;
+#X text 285 185 R G B I;
+#X obj 197 533 smoothupdate 100;
+#X obj 304 567 smoothupdate 100;
+#X obj 524 490 hsl 128 15 0.01 1 1 1 empty empty empty -2 -6 0 8 -262144
+-1 -1 1300 1;
+#X obj 67 93 3dp_push;
+#X obj 476 111 3dp_view transz 5;
+#X obj 476 171 3dp_light;
+#X obj 219 351 s i;
+#X obj 250 511 r i;
+#X obj 357 543 r i;
+#X obj 419 716 smoothupdate 100;
+#X obj 472 692 r i;
+#X obj 197 325 t f f b;
+#X obj 419 639 random 100;
+#X obj 419 662 - 50;
+#X obj 529 675 hsl 128 15 0.01 1 1 1 empty empty empty -2 -6 0 8 -262144
+-1 -1 600 1;
+#X obj 419 688 / 10;
+#X msg 418 424 reset;
+#X obj 148 201 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 67 200 pdp_route;
+#X obj 176 765 3dp_draw torus 1 2 5 5;
+#X obj 176 709 pdp_route;
+#X obj 265 711 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 194 741 3dp_draw cube 2;
+#X text 315 344 <- reset random walk;
+#X text 532 554 <- reset smoothed points to origin;
+#X text 227 13 like example14 but with coordinate smoothing and lighting
+;
+#X text 505 246 <- route mouse buttons;
+#X obj 481 265 route press2;
+#X text 666 453 <- walk radius;
+#X text 667 488 <- walk smoothing;
+#X obj 67 70 3dp_fixedsizewindowcontext 320 240;
+#X floatatom 199 250 5 0 0 0 - - -;
+#X connect 0 0 53 0;
+#X connect 1 0 0 0;
+#X connect 2 0 17 0;
+#X connect 3 0 43 0;
+#X connect 4 0 20 0;
+#X connect 5 0 23 0;
+#X connect 6 0 24 0;
+#X connect 7 0 17 1;
+#X connect 8 0 17 3;
+#X connect 9 0 17 2;
+#X connect 10 0 17 4;
+#X connect 11 1 3 0;
+#X connect 12 0 13 0;
+#X connect 13 0 41 0;
+#X connect 14 0 13 1;
+#X connect 15 0 14 0;
+#X connect 16 0 6 1;
+#X connect 16 0 5 1;
+#X connect 17 1 21 0;
+#X connect 18 0 4 0;
+#X connect 18 1 50 0;
+#X connect 20 0 5 0;
+#X connect 20 1 6 0;
+#X connect 21 0 11 0;
+#X connect 21 1 34 0;
+#X connect 23 0 3 1;
+#X connect 24 0 3 2;
+#X connect 25 0 23 2;
+#X connect 25 0 24 2;
+#X connect 26 0 12 0;
+#X connect 26 1 27 0;
+#X connect 27 0 28 0;
+#X connect 30 0 23 1;
+#X connect 31 0 24 1;
+#X connect 32 0 3 3;
+#X connect 33 0 32 1;
+#X connect 34 0 20 0;
+#X connect 34 1 29 0;
+#X connect 34 2 35 0;
+#X connect 35 0 36 0;
+#X connect 36 0 38 0;
+#X connect 37 0 32 2;
+#X connect 38 0 32 0;
+#X connect 39 0 23 0;
+#X connect 39 0 24 0;
+#X connect 39 0 32 0;
+#X connect 40 0 41 1;
+#X connect 41 0 2 0;
+#X connect 41 1 17 0;
+#X connect 43 0 42 0;
+#X connect 43 1 45 0;
+#X connect 44 0 43 1;
+#X connect 50 0 39 0;
+#X connect 53 0 26 0;
+#X connect 53 1 12 1;
+#X connect 53 1 18 0;
+#X connect 54 0 21 1;
diff --git a/opengl/doc/examples/example16.pd b/opengl/doc/examples/example16.pd
new file mode 100644
index 0000000..e9495aa
--- /dev/null
+++ b/opengl/doc/examples/example16.pd
@@ -0,0 +1,107 @@
+#N canvas 196 0 772 525 10;
+#X obj 57 25 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 57 153 3dp_blend;
+#X floatatom 101 237 5 0 0 0 - - -;
+#X floatatom 149 237 5 0 0 0 - - -;
+#X floatatom 199 236 5 0 0 0 - - -;
+#X floatatom 247 236 5 0 0 0 - - -;
+#X obj 167 364 3dp_push;
+#X obj 218 434 3dp_view transxyz;
+#X msg 276 354 normal;
+#X obj 422 255 f;
+#X floatatom 484 264 5 0 0 0 - - -;
+#X msg 422 220 0;
+#X obj 57 177 pdp_t p b;
+#X obj 392 185 t b b;
+#X msg 591 238 0.2;
+#X msg 484 239 0;
+#X obj 57 72 3dp_windowcontext;
+#X obj 392 112 route press3;
+#X obj 57 49 metro 40;
+#X obj 167 328 3dp_for 100;
+#X obj 227 125 3dp_view transz 5;
+#X obj 227 159 3dp_light;
+#X obj 57 96 3dp_push;
+#X obj 422 289 + 0.2;
+#X obj 497 112 loadbang;
+#X msg 519 238 0.01;
+#N canvas 0 0 577 190 coordinates 0;
+#X obj 51 100 *;
+#X obj 51 72 elbat 100;
+#X obj 165 99 *;
+#X obj 165 71 elbat 100;
+#X obj 276 97 *;
+#X obj 276 69 elbat 100;
+#X obj 51 19 inlet;
+#X obj 125 18 inlet;
+#X text 354 69 <- table read abstraction;
+#X obj 51 130 outlet;
+#X obj 165 127 outlet;
+#X obj 276 125 outlet;
+#X connect 0 0 9 0;
+#X connect 1 0 0 0;
+#X connect 2 0 10 0;
+#X connect 3 0 2 0;
+#X connect 4 0 11 0;
+#X connect 5 0 4 0;
+#X connect 6 0 1 0;
+#X connect 6 0 3 0;
+#X connect 6 0 5 0;
+#X connect 7 0 0 1;
+#X connect 7 0 2 1;
+#X connect 7 0 4 1;
+#X restore 239 403 pd coordinates;
+#X msg 392 136 bang;
+#X text 459 219 <- reset scaling to 0 (infinite density);
+#X text 469 290 <- increment scaling for each new frame;
+#X text 340 354 <- compute a new normal distributed set of points;
+#X text 506 371 (velocity vectors);
+#X text 545 264 <- diffusion speed;
+#X msg 559 238 0.1;
+#X text 182 12 explosion using 100 spheres with normal distributed
+velocity vectors;
+#X floatatom 367 441 5 0 0 0 - - -;
+#X obj 57 125 3dp_mouserotate;
+#X obj 57 278 3dp_color 0.1 0.1 0.1 0.27;
+#X obj 218 478 3dp_draw sphere 1 8 8;
+#X connect 0 0 18 0;
+#X connect 1 0 12 0;
+#X connect 2 0 37 1;
+#X connect 3 0 37 2;
+#X connect 4 0 37 3;
+#X connect 5 0 37 4;
+#X connect 6 1 7 0;
+#X connect 7 0 38 0;
+#X connect 8 0 26 0;
+#X connect 9 0 23 0;
+#X connect 10 0 23 1;
+#X connect 11 0 9 0;
+#X connect 12 0 37 0;
+#X connect 12 1 9 0;
+#X connect 13 0 8 0;
+#X connect 13 1 11 0;
+#X connect 14 0 10 0;
+#X connect 15 0 10 0;
+#X connect 16 0 22 0;
+#X connect 16 1 17 0;
+#X connect 16 1 36 1;
+#X connect 17 0 27 0;
+#X connect 18 0 16 0;
+#X connect 19 0 6 0;
+#X connect 19 1 26 0;
+#X connect 20 0 21 0;
+#X connect 22 0 36 0;
+#X connect 22 1 20 0;
+#X connect 23 0 9 1;
+#X connect 23 0 26 1;
+#X connect 24 0 13 0;
+#X connect 25 0 10 0;
+#X connect 26 0 7 1;
+#X connect 26 1 7 2;
+#X connect 26 2 7 3;
+#X connect 27 0 13 0;
+#X connect 33 0 10 0;
+#X connect 35 0 38 2;
+#X connect 36 0 1 0;
+#X connect 37 1 19 0;
diff --git a/opengl/doc/objects/3dp_for.pd b/opengl/doc/objects/3dp_for.pd
new file mode 100644
index 0000000..ccaf22d
--- /dev/null
+++ b/opengl/doc/objects/3dp_for.pd
@@ -0,0 +1,91 @@
+#N canvas 545 167 607 718 10;
+#X obj 22 40 3dp_windowcontext;
+#X obj 22 15 metro 40;
+#X obj 22 -5 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 22 67 3dp_push;
+#X obj 180 126 3dp_light;
+#X floatatom 301 85 5 0 0 0 - - -;
+#X obj 22 97 3dp_mouserotate;
+#X floatatom 151 228 5 0 0 0 - - -;
+#X obj 180 104 3dp_view transz 10;
+#X floatatom 81 165 5 0 0 0 - - -;
+#X obj 23 191 3dp_for 3;
+#X obj 23 251 3dp_view transx 1.5;
+#X floatatom 130 280 5 0 0 0 - - -;
+#X obj 23 302 3dp_view roty 45;
+#X obj 23 389 3dp_draw sphere 1;
+#X obj 81 365 + 1;
+#X obj 81 343 *;
+#X floatatom 129 327 5 0 0 0 - - -;
+#X text 135 159 3dp_for sends a rendering context trough a chain multiple
+times. the second outlet is the current number \, starting from zero
+\, and can be used to change the parameters parameters of the chain.
+(i.e. by reading from a table);
+#X text 222 252 all the geometry operations are accumulative \,;
+#X text 222 266 if you don't want that \, insert a 3dp_push object:
+;
+#X obj 314 313 3dp_for 3;
+#X obj 314 336 3dp_push;
+#X obj 365 400 3dp_draw cube 0.5;
+#X obj 23 140 3dp_push;
+#X obj 365 374 3dp_view transy;
+#X obj 465 343 * 1;
+#X floatatom 481 319 5 0 0 0 - - -;
+#X obj 86 569 pdp_t p p p;
+#X obj 86 606 3dp_view transy 1;
+#X obj 86 630 3dp_draw cube 0.5;
+#X obj 177 465 3dp_for 3;
+#X text 72 492 so \, in short \,;
+#X text 313 492 is equivalent to;
+#X obj 177 491 3dp_view transy 1;
+#X obj 177 515 3dp_draw cube 0.5;
+#X obj 354 539 3dp_view transy 1;
+#X obj 354 563 3dp_draw cube 0.5;
+#X obj 354 589 3dp_view transy 1;
+#X obj 354 613 3dp_draw cube 0.5;
+#X obj 354 638 3dp_view transy 1;
+#X obj 354 662 3dp_draw cube 0.5;
+#X text 256 596 and;
+#X obj 180 84 3dp_view roty;
+#X floatatom 266 66 5 0 0 0 - - -;
+#X connect 0 0 3 0;
+#X connect 0 1 6 1;
+#X connect 1 0 0 0;
+#X connect 2 0 1 0;
+#X connect 3 0 6 0;
+#X connect 3 1 43 0;
+#X connect 5 0 8 1;
+#X connect 6 0 24 0;
+#X connect 7 0 11 1;
+#X connect 8 0 4 0;
+#X connect 9 0 10 1;
+#X connect 10 0 11 0;
+#X connect 10 1 16 0;
+#X connect 11 0 13 0;
+#X connect 12 0 13 1;
+#X connect 13 0 14 0;
+#X connect 15 0 14 2;
+#X connect 16 0 15 0;
+#X connect 17 0 16 1;
+#X connect 21 0 22 0;
+#X connect 21 1 26 0;
+#X connect 22 1 25 0;
+#X connect 24 0 10 0;
+#X connect 24 1 21 0;
+#X connect 25 0 23 0;
+#X connect 26 0 25 1;
+#X connect 27 0 26 1;
+#X connect 28 0 29 0;
+#X connect 28 1 29 0;
+#X connect 28 2 29 0;
+#X connect 29 0 30 0;
+#X connect 31 0 34 0;
+#X connect 34 0 35 0;
+#X connect 36 0 37 0;
+#X connect 37 0 38 0;
+#X connect 38 0 39 0;
+#X connect 39 0 40 0;
+#X connect 40 0 41 0;
+#X connect 43 0 8 0;
+#X connect 44 0 43 1;
diff --git a/puredata/CONTENTS b/puredata/CONTENTS
new file mode 100644
index 0000000..19eeaaa
--- /dev/null
+++ b/puredata/CONTENTS
@@ -0,0 +1,10 @@
+base pdp base pd object
+image_base image processing base pd object
+dpd_base bucket base pd object
+comm pdp communication protocol in pd
+compat legacy pdp stuff
+control control pdp from within pd
+fp object interface to the forth system
+forthconsole console interface to the forth system
+queue processing queue and synchro stuff
+ut some utility pd objects
diff --git a/puredata/Makefile b/puredata/Makefile
new file mode 100644
index 0000000..11c78ec
--- /dev/null
+++ b/puredata/Makefile
@@ -0,0 +1,12 @@
+
+OBJECTS = pdp_base.o pdp_imagebase.o pdp_dpd_base.o pdp_ut.o pdp_queue.o pdp_comm.o \
+ pdp_control.o pdp_compat.o $(PDP_PDMOD)
+
+
+include ../Makefile.config
+
+all: $(OBJECTS)
+
+clean:
+ rm -f *~
+ rm -f *.o
diff --git a/puredata/pdp_base.c b/puredata/pdp_base.c
new file mode 100644
index 0000000..194134e
--- /dev/null
+++ b/puredata/pdp_base.c
@@ -0,0 +1,415 @@
+/*
+ * Pure Data Packet base class implementation.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/*
+
+ This file contains the pdp base class object.
+ This is really nothing more than an attempt to stay away from c++
+ as far as possible, while having some kind of base class functionality
+ for pdp (tucking away the communication & thread protocol).
+
+*/
+
+#include "pdp_base.h"
+#include <stdarg.h>
+
+
+static void pdp_base_debug(t_pdp_base *b, t_floatarg f)
+{
+ int i;
+ post("debug");
+ post("inlets: %d", b->b_inlets);
+ post("\tpacket\tnext_packet");
+ for (i=0; i<b->b_inlets; i++)
+ post("\t%d\t%d", b->b_packet[i], b->b_packet_next[i]);
+ //post("outlets: %d", b->b_inlets);
+}
+
+static void pdp_base_thread(t_pdp_base *b, t_floatarg f)
+{
+ int i = (int)f;
+ if ((i == 0) || (i == 1)) b->b_thread_enabled = i;
+}
+
+static void pdp_base_process(t_pdp_base *b)
+{
+
+ if (b->b_process_method)
+ (*b->b_process_method)(b);
+}
+
+/* this method is called after the thread has finished processing */
+static void pdp_base_postprocess(t_pdp_base *b)
+{
+ /* call the derived class postproc callback if there is any */
+ if (b->b_postproc_method)
+ (*b->b_postproc_method)(b);
+
+ /* unregister (mark unused) packet and propagate if packet is valid */
+ if (b->b_outlet[0])
+ pdp_pass_if_valid(b->b_outlet[0], &b->b_packet[0]);
+}
+
+
+/* move the passive packets in place */
+void pdp_base_movepassive(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ int i;
+
+ /* if a cold packet was received in the meantime
+ swap it in, else keep the old one */
+ for (i=1; i<b->b_inlets; i++){
+ pdp_replace_if_valid(&b->b_packet[i], &b->b_packet_next[i]);
+ }
+
+
+}
+
+/* the standard bang method */
+void pdp_base_bang(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ int i;
+
+ /* if pdp thread is still processing, do nothing */
+ if (-1 != b->b_queue_id) return;
+
+ /* move packets in place */
+ pdp_base_movepassive(x);
+
+
+ /* if there is a preproc method defined, call it inside
+ the pd thread. (mainly for allocations) */
+ if (b->b_preproc_method)
+ (*b->b_preproc_method)(b);
+
+ /* check if we need to use pdp queue */
+ if (b->b_thread_enabled){
+
+ /* add the process method and callback to the process queue */
+ pdp_procqueue_add(b->b_q, b, pdp_base_process, pdp_base_postprocess, &b->b_queue_id);
+ }
+ else{
+ /* call both methods directly */
+ pdp_base_process(b);
+ pdp_base_postprocess(b);
+ }
+}
+
+/* hot packet input handler */
+void pdp_base_input_hot(t_pdp_base *b, t_symbol *s, t_floatarg f)
+{
+
+ int p = (int)f;
+
+ /* dont register if active inlet is disabled */
+ if (!b->b_active_inlet_enabled) return;
+
+ /* register the packet (readonly or read/write)
+ or drop it if we have an active packet
+ if type template is not null, packet will be converted */
+
+
+ if (b->b_active_inlet_readonly){
+ if (s == S_REGISTER_RO){
+ if (b->b_type_template[0]){
+ pdp_packet_convert_ro_or_drop(&b->b_packet[0], p, b->b_type_template[0]);
+ }
+ else{
+ pdp_packet_copy_ro_or_drop(&b->b_packet[0], p);
+ }
+ }
+ }
+ else{
+ if (s == S_REGISTER_RW) {
+ if (b->b_type_template[0]){
+ pdp_packet_convert_rw_or_drop(&b->b_packet[0], p, b->b_type_template[0]);
+ }
+ else{
+ pdp_packet_copy_rw_or_drop(&b->b_packet[0], p);
+ }
+ }
+ }
+
+ /* start processing if there is an active packet to process
+ and the processing method is not active */
+
+ if ((s == S_PROCESS) && (-1 != b->b_packet[0]) && (-1 == b->b_queue_id)){
+ pdp_base_bang(b);
+ }
+ //if ((pdp_sym_prc() == s) && (-1 != b->b_packet[0]) && (!b->b_dropped)) pdp_base_bang(b);
+
+}
+
+/* cold packet input handlers */
+void pdp_base_input_cold(t_pdp_base *b, t_symbol *s, int ac, t_atom *av)
+{
+
+ int p;
+ int i;
+ char msg[] = "pdp1";
+ char *c;
+
+ int inlet;
+
+ //post("pdp_base_input_cold: got packet");
+
+ /* do cheap tests first */
+ if (ac != 2) return;
+ if (av[0].a_type != A_SYMBOL) return;
+ if (av[0].a_w.w_symbol != S_REGISTER_RO) return;
+ if (av[1].a_type != A_FLOAT) return;
+ p = (int)av[1].a_w.w_float;
+
+
+ /* check if it's a pdp message
+ and determine inlet */
+ for (i=1; i<MAX_NB_PDP_BASE_INLETS; i++){
+ if (s == gensym(msg)){
+ inlet = i;
+ goto found;
+ }
+ else{
+ msg[3]++;
+ }
+ }
+ return;
+
+
+ found:
+
+ /* store the packet and trow away
+ the old one, if there is any */
+
+ pdp_packet_copy_ro_or_drop(&b->b_packet_next[inlet], p);
+}
+
+
+void pdp_base_set_process_method(void *x, t_pdp_method m)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_process_method = m;
+}
+
+void pdp_base_set_preproc_method(void *x, t_pdp_method m)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_preproc_method = m;
+}
+
+
+void pdp_base_set_postproc_method(void *x, t_pdp_method m)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_postproc_method = m;
+}
+
+
+void pdp_base_queue_wait(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ pdp_procqueue_wait(b->b_q);
+}
+
+void pdp_base_set_queue(void *x, t_pdp_procqueue *q)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ pdp_base_queue_wait(x);
+ b->b_q = q;
+}
+
+t_pdp_procqueue *pdp_base_get_queue(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ return b->b_q;
+}
+
+void pdp_base_setup(t_class *c)
+{
+
+ /* add pdp base class methods */
+ class_addmethod(c, (t_method)pdp_base_thread, gensym("thread"), A_FLOAT, A_NULL);
+ class_addmethod(c, (t_method)pdp_base_debug, gensym("debug"), A_NULL);
+
+ /* hot packet handler */
+ class_addmethod(c, (t_method)pdp_base_input_hot, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+
+ /* cold packet handler */
+ class_addanything(c, (t_method)pdp_base_input_cold);
+}
+
+/* pdp base instance constructor */
+void pdp_base_init(void *x)
+{
+ int i;
+ t_pdp_base *b = (t_pdp_base *)x;
+
+ b->b_channel_mask = -1;
+
+ for(i=0; i<MAX_NB_PDP_BASE_INLETS; i++){
+ b->b_packet[i] = -1;
+ b->b_packet_next[i] = -1;
+ b->b_type_template[i] = 0;
+ }
+
+ b->b_queue_id = -1;
+ //b->b_dropped = 0;
+ b->b_process_method = 0;
+ b->b_preproc_method = 0;
+ b->b_inlets = 1;
+ b->b_outlets = 0;
+ b->b_active_inlet_enabled = 1;
+ b->b_active_inlet_readonly = 0;
+ b->b_thread_enabled = 1;
+
+ // default queue is pdp queue
+ b->b_q = pdp_queue_get_queue();
+
+}
+
+/* base instance destructor */
+void pdp_base_free(void *x)
+{
+ int i;
+ t_pdp_base *b = (t_pdp_base *)x;
+ /* remove process method from queue before deleting data */
+ pdp_procqueue_finish(b->b_q, b->b_queue_id);
+
+ /* delete stuff */
+ for(i=0; i<MAX_NB_PDP_BASE_INLETS; i++){
+ pdp_packet_mark_unused(b->b_packet[i]);
+ pdp_packet_mark_unused(b->b_packet_next[i]);
+ }
+
+}
+
+void pdp_base_readonly_active_inlet(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_active_inlet_readonly = 1;
+}
+
+void pdp_base_disable_active_inlet(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_active_inlet_enabled = 0;
+}
+
+
+/* add an inlet */
+void pdp_base_add_pdp_inlet(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ char s[] = "pdp0";
+ s[3] += b->b_inlets;
+
+ if (b->b_inlets < MAX_NB_PDP_BASE_INLETS){
+ inlet_new(&b->x_obj, &b->x_obj.ob_pd, gensym("pdp"), gensym(s));
+ b->b_inlets++;
+ }
+ else {
+ post("pdp_base_add_pdp_inlet: only %d pdp inlets allowed. ignoring.", MAX_NB_PDP_BASE_INLETS);
+ }
+}
+
+
+/* add an outlet: only one allowed */
+t_outlet *pdp_base_add_pdp_outlet(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ t_outlet *outlet = outlet_new(&b->x_obj, &s_anything);
+
+
+ if (b->b_outlets < MAX_NB_PDP_BASE_OUTLETS){
+ b->b_outlet[b->b_outlets] = outlet;
+ b->b_outlets++;
+ }
+
+ return outlet;
+
+}
+
+void pdp_base_set_packet(void *x, int inlet, int packet)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+
+ if (inlet < b->b_inlets){
+ //post("%d %d", b->b_packet[inlet], b->b_packet_next[inlet]);
+ pdp_packet_mark_unused(b->b_packet[inlet]);
+ b->b_packet[inlet] = packet;
+ }
+}
+
+
+int pdp_base_get_packet(void *x, int inlet)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+
+ if (inlet < b->b_inlets){
+ //post("%d %d", b->b_packet[inlet], b->b_packet_next[inlet]);
+ return (b->b_packet[inlet]);
+ }
+
+ return -1;
+}
+
+int pdp_base_move_packet(void *x, int inlet)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ int p;
+
+ if (inlet < b->b_inlets){
+ p = b->b_packet[inlet];
+ b->b_packet[inlet] = -1;
+ return (p);
+ }
+
+ return -1;
+}
+
+
+
+t_object *pdp_base_get_object(void *x)
+{
+ return (t_object *)x;
+}
+
+void pdp_base_add_gen_inlet(void *x, t_symbol *from, t_symbol *to)
+{
+ t_object *o = (t_object *)x;
+ inlet_new(o, &o->ob_pd, from, to);
+}
+
+void pdp_base_disable_thread(void *x)
+{
+
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_thread_enabled = 0;
+}
+
+void pdp_base_set_type_template(void *x, int inlet, t_pdp_symbol *type_template)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ if (inlet < b->b_inlets){
+ b->b_type_template[inlet] = type_template;
+ }
+}
diff --git a/puredata/pdp_comm.c b/puredata/pdp_comm.c
new file mode 100644
index 0000000..4c67659
--- /dev/null
+++ b/puredata/pdp_comm.c
@@ -0,0 +1,367 @@
+/*
+ * Pure Data Packet system implementation.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* this file contains misc communication (packet) methods for pd */
+
+
+#include <stdio.h>
+#include "pdp_pd.h"
+#include "pdp_internals.h"
+#include "pdp_packet.h"
+#include "pdp_comm.h"
+#include "pdp_type.h"
+#include "pdp_control.h"
+#include "pdp_mem.h"
+#include "pdp_queue.h" // for notify drop: fix this (should be from pdp_control.h)
+#include "pdp_debug.h"
+
+/* all symbols are C style */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+/* interface to pd system:
+ pdp/dpd communication protocol in pd
+ pd <-> pdp atom and list conversion */
+
+ /* NOTE: when using the outlet_pdp methods, the packet
+ is no longer read/write, but can be readonly */
+
+
+ /* NOTE: since 0.13 the passing packet is no more.
+ in order to limit copying. processors should always register ro,
+ and replace with writable when packet needs to be written in the process method */
+
+
+/* send a packet to an outlet */
+void outlet_pdp_register(t_outlet *out, int packetid)
+{
+ t_atom atom[2];
+
+ SETFLOAT(atom+1, (float)packetid);
+
+ /* during the following phase,
+ objects can register a ro copy */
+
+ SETSYMBOL(atom+0, S_REGISTER_RO);
+ outlet_anything(out, S_PDP, 2, atom);
+
+ /* DEPRECIATED: objects can register a rw copy
+ but this will always copy the packet. it is better
+ to perform a pdp_packet_replace_with_writable operation during the process step */
+
+ SETSYMBOL(atom+0, S_REGISTER_RW);
+ outlet_anything(out, S_PDP, 2, atom);
+
+}
+/* send a packet to an outlet */
+void outlet_pdp_process(t_outlet *out)
+{
+ t_atom atom[2];
+
+ /* set a dummy invalid packet.
+ this is for uniform pdp messages in pd, for ease of routing. */
+ SETFLOAT(atom+1, (float)-1);
+
+ /* during the process phase, objects can perform pdp_packet_replace_with_writable
+ and process the packet data */
+ SETSYMBOL(atom+0, S_PROCESS);
+ outlet_anything(out, S_PDP, 2, atom);
+
+}
+
+/* for compat */
+void outlet_pdp(t_outlet *out, int packetid)
+{
+ outlet_pdp_register(out, packetid);
+ outlet_pdp_process(out);
+}
+
+/* send an accumulation packet to an outlet */
+void outlet_dpd(t_outlet *out, int packetid)
+{
+ t_atom atom[2];
+
+ SETFLOAT(atom+1, (float)packetid);
+
+ SETSYMBOL(atom+0, S_INSPECT);
+ outlet_anything(out, S_DPD, 2, atom);
+
+ SETSYMBOL(atom+0, S_ACCUMULATE);
+ outlet_anything(out, S_DPD, 2, atom);
+
+}
+
+/* unregister a packet and send it to an outlet */
+void
+
+pdp_packet_pass_if_valid(t_outlet *outlet, int *packet_ptr)
+{
+ t_pdp *header = pdp_packet_header(*packet_ptr);
+ if (header){
+
+ /* send register phase */
+ outlet_pdp_register(outlet, *packet_ptr);
+
+ /* unregister */
+ pdp_packet_mark_unused(*packet_ptr);
+ *packet_ptr = -1;
+
+ /* send process phase */
+ outlet_pdp_process(outlet);
+
+ }
+}
+
+void
+pdp_packet_replace_if_valid(int *dpacket, int *spacket)
+{
+ if (-1 != *spacket){
+ pdp_packet_mark_unused(*dpacket);
+ *dpacket = *spacket;
+ *spacket = -1;
+ }
+
+}
+
+
+int
+pdp_packet_copy_ro_or_drop(int *dpacket, int spacket)
+{
+ int drop = 0;
+ if (*dpacket == -1) *dpacket = pdp_packet_copy_ro(spacket);
+ else {
+ /* send a notification there is a dropped packet */
+ pdp_control_notify_drop(spacket);
+ drop = 1;
+ }
+ return drop;
+}
+
+
+int
+pdp_packet_copy_rw_or_drop(int *dpacket, int spacket)
+{
+ int drop = 0;
+ if (*dpacket == -1) *dpacket = pdp_packet_copy_rw(spacket);
+ else {
+ /* send a notification there is a dropped packet */
+ pdp_control_notify_drop(spacket);
+ drop = 1;
+ }
+ return drop;
+}
+
+int
+pdp_packet_convert_ro_or_drop(int *dpacket, int spacket, t_pdp_symbol *template)
+{
+ int drop = 0;
+
+ if (!template) return pdp_packet_copy_ro_or_drop(dpacket, spacket);
+
+ if (*dpacket == -1) *dpacket = pdp_packet_convert_ro(spacket, template);
+ else {
+ /* send a notification there is a dropped packet */
+ pdp_control_notify_drop(spacket);
+ drop = 1;
+ }
+ return drop;
+}
+
+
+int
+pdp_packet_convert_rw_or_drop(int *dpacket, int spacket, t_pdp_symbol *template)
+{
+ int drop = 0;
+
+ if (!template) return pdp_packet_copy_rw_or_drop(dpacket, spacket);
+
+ if (*dpacket == -1) *dpacket = pdp_packet_convert_rw(spacket, template);
+ else {
+ /* send a notification there is a dropped packet */
+ pdp_control_notify_drop(spacket);
+ drop = 1;
+ }
+ return drop;
+}
+
+
+/* send a pdp list to a pd outlet. packets are not copied but passed! */
+void outlet_pdp_atom(t_outlet *out, t_pdp_atom *a)
+{
+ int packet = -1;
+ if (!a) return;
+ switch(a->t){
+ case a_float:
+ outlet_float(out, a->w.w_float);
+ return;
+ case a_int:
+ outlet_float(out, (float)a->w.w_int);
+ return;
+ case a_symbol:
+ outlet_symbol(out, gensym(a->w.w_symbol->s_name));
+ return;
+ case a_list:
+ outlet_pdp_list(out, a->w.w_list);
+ return;
+ case a_packet:
+ pdp_packet_pass_if_valid(out, &a->w.w_packet);
+ return;
+ default:
+ return;
+ }
+}
+
+void outlet_pdp_list(t_outlet *out, struct _pdp_list *l)
+{
+ int elements;
+ t_atom *atomlist;
+ t_pdp_atom *pdp_a;
+ t_atom *pd_a;
+ t_symbol *pd_selector;
+
+ if (!l) return;
+ switch(l->elements){
+ case 0: /* bang */
+ outlet_bang(out);
+ return;
+ case 1: /* atom */
+ outlet_pdp_atom(out, l->first);
+ return;
+ default: /* proper list*/
+ elements = l->elements;
+
+ /* allocate list */
+ atomlist = pdp_alloc(sizeof (t_atom) * l->elements);
+ pd_a = atomlist;
+ pdp_a = l->first;
+
+ /* setup selector */
+ if (pdp_a->t != a_symbol){
+ pd_selector = gensym("list");
+ }
+ else {
+ pd_selector = gensym(pdp_a->w.w_symbol->s_name);
+ elements--;
+ pdp_a = pdp_a->next;
+ }
+
+ /* setup atoms */
+ while (pdp_a){
+ switch(pdp_a->t){
+ case a_float:
+ SETFLOAT(pd_a, pdp_a->w.w_float);
+ break;
+ case a_int:
+ SETFLOAT(pd_a, (float)pdp_a->w.w_int);
+ break;
+ case a_symbol:
+ SETSYMBOL(pd_a, gensym(pdp_a->w.w_symbol->s_name));
+ break;
+ default:
+ SETSYMBOL(pd_a, gensym("invalid"));
+ break;
+ }
+
+ pdp_a = pdp_a->next;
+ pd_a++;
+ }
+
+ /* send out */
+ outlet_anything(out, pd_selector, elements, atomlist);
+
+
+
+ /* clean up */
+ pdp_dealloc(atomlist);
+
+ }
+
+
+}
+
+
+void pd_atom_to_pdp_atom(t_atom *pdatom, t_pdp_atom *pdpatom)
+{
+ switch (pdatom->a_type){
+ case A_FLOAT:
+ pdpatom->t = a_float;
+ pdpatom->w.w_float = pdatom->a_w.w_float;
+ break;
+ case A_SYMBOL:
+ pdpatom->t = a_symbol;
+ pdpatom->w.w_symbol = pdp_gensym(pdatom->a_w.w_symbol->s_name);
+ break;
+ default:
+ pdpatom->t = a_undef;
+ break;
+ }
+}
+
+
+
+/* some "accelerated" pd symbols */
+t_symbol s_pdp = {"pdp", 0, 0};
+t_symbol s_register_ro = {"register_ro", 0, 0};
+t_symbol s_register_rw = {"register_rw", 0, 0};
+t_symbol s_process = {"process", 0, 0};
+t_symbol s_dpd = {"dpd", 0, 0};
+t_symbol s_inspect = {"inspect", 0, 0};
+t_symbol s_accumulate = {"accumulate", 0, 0};
+t_symbol s_chanmask = {"chanmask", 0, 0};
+
+// internal pd method
+t_symbol *dogensym(char *s, t_symbol *oldsym);
+static void _addsym(t_symbol *s)
+{
+
+ /* don't kill me for this one..
+ if the symbol is already defined and used, .. well, that's a problem
+ but right now it seems a reasonable hack */
+
+ t_symbol *sret = dogensym(s->s_name, s);
+ if (s != sret){
+ post("PDP INIT ERROR: pd symbol clash adding symbol %s: new=%08x old=%08x", s->s_name, s, sret);
+ post("try loading pdp before other libraries");
+ }
+}
+
+void
+pdp_pdsym_setup(void)
+{
+
+ _addsym(&s_pdp);
+ _addsym(&s_register_ro);
+ _addsym(&s_register_rw);
+ _addsym(&s_process);
+ _addsym(&s_dpd);
+ _addsym(&s_inspect);
+ _addsym(&s_accumulate);
+ _addsym(&s_chanmask);
+
+}
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_compat.c b/puredata/pdp_compat.c
new file mode 100644
index 0000000..e7bc0c2
--- /dev/null
+++ b/puredata/pdp_compat.c
@@ -0,0 +1,57 @@
+/*
+ * Pure Data Packet system implementation. Compatibility routines.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* this file contains misc communication methods */
+
+#include <stdio.h>
+
+#include "pdp_pd.h"
+#include "pdp_comm.h"
+#include "pdp_internals.h"
+
+/* all symbols are C style */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+
+void
+pdp_pass_if_valid(t_outlet *outlet, int *packet)
+{
+ pdp_packet_pass_if_valid(outlet, packet);
+}
+
+void
+pdp_replace_if_valid(int *dpacket, int *spacket)
+{
+ pdp_packet_replace_if_valid(dpacket, spacket);
+
+}
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_control.c b/puredata/pdp_control.c
new file mode 100644
index 0000000..0b49fd9
--- /dev/null
+++ b/puredata/pdp_control.c
@@ -0,0 +1,186 @@
+/*
+ * Pure Data Packet system implementation: control object
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/* this is an actual pd class that is used for communication with the
+ pdp framework */
+
+#include "pdp_internals.h"
+#include "pdp_control.h"
+#include "pdp_packet.h"
+#include <stdio.h>
+
+/* all symbols are C style */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+
+static long dropped_packets;
+
+static t_class* pdp_control_class;
+
+
+/* pdp control instance data */
+
+struct _pdp_control;
+typedef struct _pdp_control
+{
+ t_object x_obj;
+ t_outlet *x_outlet0;
+ struct _pdp_control *x_next;
+
+} t_pdp_control;
+
+
+
+static t_pdp_control *pdp_control_list;
+
+static void pdp_control_info(t_pdp_control *x)
+{
+}
+
+static void pdp_control_collectgarbage(t_pdp_control *x)
+{
+ int nb_packets_freed = pdp_pool_collect_garbage();
+ post("pdp_control: freed %d packets", nb_packets_freed);
+
+}
+
+static void pdp_control_set_mem_limit(t_pdp_control *x, t_floatarg f)
+{
+ int limit = (int)f;
+ if (limit < 0) limit = 0;
+ pdp_pool_set_max_mem_usage(limit);
+ if (limit) post("pdp_control: set memory limit to %d bytes", limit);
+ else post("pdp_control: disabled memory limit");
+
+}
+
+static void pdp_control_thread(t_pdp_control *x, t_floatarg f)
+{
+ int t = (int)f;
+
+ if (t){
+ post("pdp_control: pdp is now using its own processing thread");
+ pdp_queue_use_thread(1);
+ }
+ else {
+ post("pdp_control: pdp is now using the main pd thread");
+ pdp_queue_use_thread(0);
+ }
+}
+
+
+static void pdp_control_send_drop_message(t_pdp_control *x)
+{
+ t_atom atom[1];
+ t_symbol *s = gensym("pdp_drop");
+
+ SETFLOAT(atom+0, (float)dropped_packets);
+ outlet_anything(x->x_outlet0, s, 1, atom);
+}
+
+
+static void pdp_control_free(t_pdp_control *x)
+{
+ /* remove from linked list */
+ t_pdp_control *curr = pdp_control_list;
+ if (pdp_control_list == x) pdp_control_list = x->x_next;
+ else while (curr){
+ if (curr->x_next == x) {
+ curr->x_next = x->x_next;
+ break;
+ }
+ else {
+ curr = curr->x_next;
+ }
+
+ }
+}
+
+
+static void *pdp_control_new(void)
+{
+ t_pdp_control *x = (t_pdp_control *)pd_new(pdp_control_class);
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_anything);
+
+ /* add to list */
+ x->x_next = pdp_control_list;
+ pdp_control_list = x;
+ return x;
+}
+
+/************************* class methods ***************************************/
+
+
+void pdp_control_addmethod(t_method m, t_symbol *s)
+{
+ class_addmethod(pdp_control_class, m, s, A_GIMME, A_NULL);
+}
+
+void pdp_control_setup(void)
+{
+
+ pdp_control_list = 0;
+ dropped_packets = 0;
+
+ /* setup pd class data */
+ pdp_control_class = class_new(gensym("pdp_control"), (t_newmethod)pdp_control_new,
+ (t_method)pdp_control_free, sizeof(t_pdp_control), 0, A_NULL);
+
+
+ class_addmethod(pdp_control_class, (t_method)pdp_control_info, gensym("info"), A_NULL);
+ class_addmethod(pdp_control_class, (t_method)pdp_control_thread, gensym("thread"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_control_class, (t_method)pdp_control_collectgarbage, gensym("collectgarbage"), A_NULL);
+ class_addmethod(pdp_control_class, (t_method)pdp_control_set_mem_limit, gensym("memlimit"), A_FLOAT, A_NULL);
+}
+
+
+
+void pdp_control_notify_broadcast(t_pdp_control_method_notify *notify)
+{
+ t_pdp_control *curr = pdp_control_list;
+ while (curr){
+ (*notify)(curr);
+ curr = curr->x_next;
+ }
+}
+
+
+
+/************************* notify class methods *************************/
+
+void pdp_control_notify_drop(int packet)
+{
+ dropped_packets++;
+
+ /* send drop notify to controller class instances */
+ pdp_control_notify_broadcast(pdp_control_send_drop_message);
+ //post("dropped packet");
+}
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_dpd_base.c b/puredata/pdp_dpd_base.c
new file mode 100644
index 0000000..371b99e
--- /dev/null
+++ b/puredata/pdp_dpd_base.c
@@ -0,0 +1,270 @@
+/*
+ * Pure Data Packet module. DPD base class implementation.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include "pdp_dpd_base.h"
+#include "pdp_internals.h"
+
+
+#define THIS(b) t_pdp_dpd_base *b = (t_pdp_dpd_base *)x
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+
+/* PRIVATE METHODS */
+
+
+
+
+/* dpd packet context input handler */
+static void _pdp_dpd_base_context_input(t_pdp_dpd_base *b, t_symbol *s, t_floatarg f)
+{
+
+ int p = (int)f;
+ int i;
+
+ //post ("pdp_dpd_base_context_input: got %s %d", s->s_name, p);
+
+ /* sources/sinks have active inlet disabled */
+ if (b->b_dpd_active_inlet_disabled) return;
+
+ /* handle inspect message */
+ if (s == S_INSPECT){
+
+ /* store packet for inspector */
+ b->b_context_packet = p;
+
+ /* add inspector to pdp queue
+ this is special: it doesn't use a command object */
+ pdp_dpd_base_queue_command(b, b, b->b_inspector_method, b->b_inspector_callback, 0);
+ }
+
+ /* handle accumulate message */
+ if (s == S_ACCUMULATE){
+
+ /* store context for accumulator methods */
+ b->b_context_packet = p;
+
+ /* call bang */
+ pdp_dpd_base_bang(b);
+
+
+ }
+
+}
+
+/* default command object (returns self) */
+void *_pdp_dpd_base_get_command_object(void *x){return x;}
+
+/* PUBLIC METHODS */
+
+
+void pdp_dpd_base_queue_command(void *x, void *c, t_pdp_method process,
+ t_pdp_method callback, int *id)
+{
+ THIS(b);
+ t_pdp_procqueue *q = pdp_base_get_queue(x);
+ pdp_procqueue_add(q, c, process, callback, id);
+
+}
+
+/* bang method (propagate context to outlet) : it is not registered as a pd message by default ! */
+void pdp_dpd_base_bang(void *x)
+{
+ THIS(b);
+ int i, id;
+ void *cobj;
+
+ /* move passive pdp packets in place */
+ pdp_base_movepassive(x);
+
+ /* get command object (or use self) */
+ cobj = b->b_command_factory_method ? (b->b_command_factory_method)(b) : b;
+ //post(" command object is %x. object is %x", cobj, b);
+
+
+ /* queue acc method & propagate for all outlets */
+ for (i=b->b_nb_context_outlets; i--;){
+
+
+ /* propagate the context packet to the outlet */
+ if (b->b_outlet_enable[i]){
+ pdp_dpd_base_queue_command(x, cobj, b->b_accum_method[i], b->b_accum_callback[i], 0);
+ outlet_dpd(b->b_context_outlet[i], b->b_context_packet);
+ }
+ else{
+ //post("outlet %d disabled", i);
+ }
+ }
+
+ /* queue cleanup method */
+ if (b->b_cleanup_method)
+ //pdp_procqueue_add(b->b_q, b, b->b_cleanup_method, 0, &b->b_cleanup_queue_id);
+ pdp_dpd_base_queue_command(x, cobj, b->b_cleanup_method, b->b_cleanup_callback, 0);
+
+ /* send communication complete notify */
+ if (b->b_complete_notify)
+ (b->b_complete_notify)(x);
+
+}
+
+/* get/set context packet */
+int pdp_dpd_base_get_context_packet(void *x){
+ THIS(b);
+ return b->b_context_packet;
+}
+int pdp_dpd_base_move_context_packet(void *x){
+ THIS(b);
+ int p = b->b_context_packet;
+ b->b_context_packet = -1;
+ return p;
+}
+
+void pdp_dpd_base_set_context_packet(void *x, int p){
+ THIS(b);
+ pdp_packet_mark_unused(b->b_context_packet);
+ b->b_context_packet = p;
+}
+
+/* add a cleanup callback (called after all propagation is finished) for sources/sinks */
+void pdp_dpd_base_add_cleanup(void *x, t_pdp_method cleanup_method, t_pdp_method cleanup_callback)
+{
+ THIS(b);
+ b->b_cleanup_method = cleanup_method;
+ b->b_cleanup_callback = cleanup_callback;
+ //b->b_cleanup_queue_id = -1;
+}
+
+/* add a inspector callback */
+void pdp_dpd_base_add_inspector(void *x, t_pdp_method inspector_method)
+{
+ THIS(b);
+ b->b_inspector_method = inspector_method;
+ //b->b_inspector_queue_id = -1;
+}
+
+/* add a context outlet */
+t_outlet *pdp_dpd_base_add_outlet(void *x, t_pdp_method accum_method, t_pdp_method accum_callback)
+{
+ THIS(b);
+ int i = b->b_nb_context_outlets;
+ if (i < PDP_DPD_MAX_CONTEXT_OUTLETS){
+ b->b_context_outlet[i] = outlet_new((t_object *)b, &s_anything);
+ b->b_outlet_enable[i] = 1;
+ b->b_accum_method[i] = accum_method;
+ b->b_accum_callback[i] = accum_callback;
+ //b->b_accum_queue_id[i] = -1;
+ b->b_nb_context_outlets++;
+ return b->b_context_outlet[i];
+ }
+ else{
+ post("pdp_dpd_base_add_outlet: no more free outlet slots");
+ return 0;
+ }
+
+}
+
+
+/* destructor */
+void pdp_dpd_base_free(void *x)
+{
+ THIS(b);
+
+ /* free base */
+ pdp_base_free(b);
+}
+
+
+void pdp_dpd_base_disable_active_inlet(void *x)
+{
+ THIS(b);
+ b->b_dpd_active_inlet_disabled = 1;
+}
+
+
+
+void pdp_dpd_base_enable_outlet(void *x, int outlet, int toggle)
+{
+ THIS(b);
+ if (outlet >=0 && outlet < PDP_DPD_MAX_CONTEXT_OUTLETS){
+ b->b_outlet_enable[outlet] = toggle;
+ }
+
+}
+
+
+void pdp_dpd_base_register_complete_notify(void *x, t_pdp_method method)
+{
+ THIS(b);
+ b->b_complete_notify = method;
+}
+
+void pdp_dpd_base_register_command_factory_method(void *x, t_pdp_newmethod command_factory_method)
+{
+ THIS(b);
+ b->b_command_factory_method = command_factory_method;
+}
+
+
+/* init method */
+void pdp_dpd_base_init(void *x)
+{
+ THIS(b);
+
+ /* super init */
+ pdp_base_init(b);
+
+ /* disable pdp messages on active inlet (dpd messages are used as sync) */
+ pdp_base_disable_active_inlet(b);
+
+ /* init data */
+ b->b_nb_context_outlets = 0;
+ b->b_context_packet = -1;
+ b->b_cleanup_method = 0;
+ //b->b_cleanup_queue_id = -1;
+ b->b_inspector_method = 0;
+ //b->b_inspector_queue_id = -1;
+ b->b_dpd_active_inlet_disabled = 0;
+
+ // default notify == none
+ b->b_complete_notify = 0;
+
+ // default command object getter
+ b->b_command_factory_method = 0;
+
+}
+
+
+void pdp_dpd_base_setup(t_class *class)
+{
+
+ pdp_base_setup(class);
+ class_addmethod(class, (t_method)_pdp_dpd_base_context_input, gensym("dpd"), A_SYMBOL, A_FLOAT, A_NULL);
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_forthproc.c_bak b/puredata/pdp_forthproc.c_bak
new file mode 100644
index 0000000..e6517d5
--- /dev/null
+++ b/puredata/pdp_forthproc.c_bak
@@ -0,0 +1,807 @@
+/*
+ * Pure Data Packet module.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* TODO: ADD THREAD SUPPORT */
+
+/*
+
+this is the pd interface to forth processes / processors.
+in time, it should become the only interface between the processing
+functionality in pdp, and pd objects representing this functionality
+
+example object definitions:
+
+a forth process is a virtual machine operating on a private
+rotation stack, as found in hp rpn calcs (this is the machine state vector)
+the tick operation leaves the stack size and relative position of
+the elements invariant
+
+this is a very crude and simple framework. it is an experiment to
+see how far this forth thing can be pushed to solve the problem
+of dataflow processing in a simple way, by cutting out the fat.
+yes, chuck moore's ideas are viral..
+
+a forth process has a setup code that will construct an initial stack template
+
+an object definition is a list of
+- list of symbolic input to stack location mappings (first = active)
+- list of symbolic output to stack location mappings
+- stack template init code (bootblock)
+- process code
+
+the stack is the machine memory
+
+the mapping between pdp's forth processors and pd object
+is handled by pdmaps. these can probably be reused to build other
+object oriented interfaces to the forth processors
+
+*/
+
+
+/* pdp forthprocs support thread processing.
+
+ there is only one stack, which
+ serves as the machine state and input/output storage.
+
+ when processing in thread, the input/output event
+ queue is used, when processing directly pd events
+ are inserted into the stack directly and the queues
+ are bypassed
+
+*/
+
+
+#include <pthread.h>
+#include "pdp_pd.h"
+#include "pdp_comm.h"
+#include "pdp_packet.h"
+#include "pdp_mem.h"
+#include "pdp_forth.h"
+#include "pdp_pdmap.h"
+#include "pdp_queue.h"
+#include "pdp_debug.h"
+
+
+
+/* this object instantiates a forth processor */
+
+typedef struct forthproc_struct
+{
+ t_object x_obj;
+ t_pdp_list *x_processor; // the processor definition
+ t_pdp_list *x_pdmap; // the pd port mappings
+ t_pdp_list *x_program; // the forth program
+
+
+ t_pdp_list *x_stack; // the state stack
+ t_pdp_list *x_stack_template; // a "fake" stack serving as a type template
+
+ t_pdp_list *x_input_events; // current input event list
+
+ t_pdp_list *x_queue_input; // queue of input event lists, event = (atom index, word)
+ t_pdp_list *x_queue_output; // queue of output event lists, event = (outlet ptr, word)
+
+ t_pdp_procqueue *x_q; // process queue
+
+
+ pthread_mutex_t x_mut; // queue mutex
+
+ int x_nb_outlets; // number of pd outlets
+ t_outlet **x_outlet;
+
+ int x_thread; // use thread processing or not
+
+ t_symbol *x_protocol; // protocol used (pdp or dpd)
+
+ /* pdp image legacy */
+ int x_chanmask;
+
+ /* dpd */
+ int x_dpd_packet;
+ t_outlet *x_dpd_outlet;
+
+} t_forthproc;
+
+
+static inline void lock(t_forthproc *x){pthread_mutex_lock(&x->x_mut);}
+static inline void unlock(t_forthproc *x){pthread_mutex_unlock(&x->x_mut);}
+
+
+/* send an atom to an outlet */
+static void send_pdp_atom_to_outlet(t_outlet *out, t_pdp_atom *a)
+{
+ outlet_pdp_atom(out, a);
+}
+
+/* output stack contents to outlet or output event list */
+static void output_from_stack(t_forthproc *x)
+{
+ t_pdp_list *outsym = pdp_forth_pdmap_outlist(x->x_pdmap);
+
+ /* create an event list if we're in thread mode */
+ t_pdp_list *eventlist = x->x_thread ? pdp_list_new(0) : 0;
+
+ static void _do_outlet(int index, t_pdp_atom *pname, t_pdp_list *eventlist){
+ t_outlet *out = x->x_outlet[index];
+ t_pdp_atom *a = pdp_forth_processor_stackatom_from_outputname(
+ x->x_processor, x->x_stack, pname->w.w_symbol);
+
+ PDP_ASSERT(a);
+
+ /* bang in reverse order by using head recursion */
+ if (pname->next) _do_outlet(index+1, pname->next, eventlist);
+
+ /* send the atom to the outlet if no event list */
+ if (!eventlist){
+ send_pdp_atom_to_outlet(out, a);
+ }
+ /* or add it to the event list */
+ else {
+ t_pdp_list *ev = pdp_list_new(2);
+ pdp_list_set_0(ev, a_pointer,
+ (t_pdp_word)(void*)out); // store outlet ptr
+ pdp_list_set_1(ev, a->t, a->w); // store atom
+ pdp_list_add_back(eventlist, a_list,
+ (t_pdp_word)ev); // store event in list
+
+ /* if it was a packet, clear the stacks reference */
+ if (a->t == a_packet) a->w.w_packet = -1;
+ }
+
+ }
+
+ _do_outlet(0, outsym->first, eventlist);
+
+ /* add eventlist to output event queue */
+ if (eventlist){
+
+ lock(x);
+ pdp_list_add_back(x->x_queue_output, a_list,
+ (t_pdp_word)eventlist);
+ unlock(x);
+ }
+}
+
+/* legacy hack: setup channel mask for image processing */
+static void setup_chanmask(t_forthproc *x)
+{
+ if (x->x_chanmask != -1){
+ t_pdp_symbol *pname = pdp_forth_pdmap_get_pname(x->x_pdmap, pdp_gensym("pdp"));
+ t_pdp_atom *a = pdp_forth_processor_stackatom_from_inputname(x->x_processor, x->x_stack, pname);
+ int *packet;
+ if (a && a->t == a_packet){
+ packet = &a->w.w_packet;
+ pdp_packet_replace_with_writable(packet); // make sure it's a private copy
+ pdp_packet_image_set_chanmask(*packet, x->x_chanmask);
+ //post("chanmask set to %d", x->x_chanmask);
+ }
+ }
+}
+
+static void exec_program(t_forthproc *x)
+{
+ int error;
+ setup_chanmask(x);
+ if (e_ok != (error = pdp_forth_execute_def(x->x_stack, x->x_program))){
+ post("error %d (%s) executing forth processor",
+ error, pdp_forth_word_error(error));
+ post("PROGRAM:");
+ pdp_list_print(x->x_program);
+ post("STACK:");
+ pdp_list_print(x->x_stack);
+ post("");
+
+ /* delete stack and create a new one */
+ pdp_tree_strip_packets(x->x_stack);
+ pdp_tree_free(x->x_stack);
+ x->x_stack = pdp_forth_processor_setup_stack(x->x_processor);
+
+
+ }
+
+
+}
+
+
+static void thread_exec(t_forthproc *x)
+{
+ t_pdp_list *event_list;
+ t_pdp_atom *a;
+
+ /* get input event list from input event queue */
+ PDP_ASSERT(x->x_queue_input->elements);
+ lock(x);
+ event_list = pdp_list_pop(x->x_queue_input).w_list;
+ unlock(x);
+
+ /* add input events to state stack */
+ for (a=event_list->first; a; a=a->next){
+ int index = a->w.w_list->first->w.w_int;
+ t_pdp_atom *src = a->w.w_list->first->next;
+
+ t_pdp_atom *dest = x->x_stack->first;
+ while (index--) dest = dest->next;
+
+ PDP_ASSERT(dest->t == src->t);
+
+ switch(src->t){
+
+ /* copy pure atoms */
+ case a_float:
+ case a_int:
+ case a_symbol:
+ dest->w = src->w;
+ break;
+
+ /* move reference atoms */
+ case a_packet:
+ pdp_packet_mark_unused(dest->w.w_packet);
+ dest->w = src->w;
+ src->w.w_packet = -1;
+ break;
+
+ /* ignored */
+ case a_pointer:
+ case a_list:
+ default:
+ break;
+ }
+ }
+
+
+
+ /* free event list */
+ pdp_tree_free(event_list);
+
+ /* run the process */
+ exec_program(x);
+
+ /* send output events to output event queue */
+ output_from_stack(x);
+
+}
+
+static void thread_output(t_forthproc *x)
+{
+ t_pdp_list *event_list;
+ t_pdp_atom *a;
+
+ /* get output event list from output event queue */
+ PDP_ASSERT(x->x_queue_output->elements);
+ lock(x);
+ event_list = pdp_list_pop(x->x_queue_output).w_list;
+ unlock(x);
+
+ /* send */
+ for (a=event_list->first; a; a=a->next){
+ t_outlet *outlet = a->w.w_list->first->w.w_pointer;
+ t_pdp_atom *outatom = a->w.w_list->first->next;
+ PDP_ASSERT(outlet);
+ PDP_ASSERT(outatom);
+ send_pdp_atom_to_outlet(outlet, outatom);
+ }
+
+ /* free event list */
+ pdp_tree_strip_packets(event_list);
+ pdp_tree_free(event_list);
+
+}
+
+/* handle dpd packet passing */
+static void dpd_output(t_forthproc *x)
+{
+ //post("checking dpd");
+ //post("protocol: %s, outlet: %08x, packet: %d", x->x_protocol->s_name, x->x_dpd_outlet, x->x_dpd_packet);
+ if ((x->x_protocol != S_DPD)
+ || (!x->x_dpd_outlet)
+ || (x->x_dpd_packet == -1)) return;
+
+ /* send the dpd packet to the special (first) outlet */
+ //post("sending dpd");
+ outlet_dpd(x->x_dpd_outlet, x->x_dpd_packet);
+ x->x_dpd_packet = -1;
+
+}
+
+
+/* this method is called after
+ an active event is received */
+
+static void run(t_forthproc *x)
+{
+
+ /* NO THREAD:
+ no brainer: execute and output in one go */
+
+ if (!x->x_thread){
+
+ /* run the word */
+ exec_program(x);
+
+ /* output stuff */
+ output_from_stack(x);
+ dpd_output(x);
+ }
+
+ /* THREAD:
+ start queueing operations
+
+ this is a bit harder since we need to make a copy of the machine state (stack)
+ before we send off a command to process it in the queue
+
+ this case is handled separately, because processing without thread is obviously
+ more efficient regarding memory usage and locality of reference.
+
+ */
+
+ else {
+
+ t_pdp_list *newstack;
+
+ /* compared to the previous approach, no 'automatic' dropping is applied
+ for forth processors. the input and output queues are of indefinite length.
+
+ dropping is the responsability of the sender or a special object
+ that uses the processing queue to synchronize (see 3dp_windowcontext)
+
+ this allows for pipelining.
+
+ a drawback is that feedback is a problem with this approach,
+ but it already was with the previous one.
+
+ the only exception to the dropping rule is when the procqueue is full
+
+ */
+
+
+ /* make sure process queue is not full */
+ if (pdp_procqueue_full(x->x_q)){
+
+ post("forthproc: WARNING: procqueue is full. dropping input events.");
+
+ /* clear the input event list */
+ pdp_tree_strip_packets(x->x_input_events);
+ pdp_tree_free(x->x_input_events);
+ x->x_input_events = pdp_list_new(0);
+
+ /* exit */
+ return;
+ }
+
+ /* add collected input events to input event queue, and create
+ a new empty input event list */
+ lock(x);
+ pdp_list_add_back(x->x_queue_input, a_list, (t_pdp_word)x->x_input_events);
+ x->x_input_events = pdp_list_new(0);
+ unlock(x);
+
+ /* queue the process method & callback */
+ pdp_procqueue_add(x->x_q, x, thread_exec, thread_output, 0);
+
+ /* how to handle dpd packets?
+ they both have direct and indirect output
+ let's try this: the dpd input is always passed on directly.
+ the other outputs are just like pdp outputs */
+
+ dpd_output(x);
+
+ }
+}
+
+
+static int handle_special_message(t_forthproc *x, t_symbol *s, int argc, t_atom *argv)
+{
+ /* handle the chanmask message. this is a legacy thingy */
+ if (s == S_CHANMASK){
+ if ((argc == 1) && (argv->a_type == A_FLOAT)){
+ x->x_chanmask = (int)argv->a_w.w_float;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* pd message handler:
+ receives a pd message and stores an event in the input queue
+ or directly on the stack */
+
+static void handle_pd_message(t_forthproc *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int active = 0;
+ int index;
+ int i;
+ t_pdp_atom ta;
+ t_pdp_symbol *message_id;
+ t_pdp_symbol *pname;
+
+
+ /* get the param name from the received symbol */
+ message_id = pdp_gensym(s->s_name);
+ pname = pdp_forth_pdmap_get_pname(x->x_pdmap, message_id);
+
+ /* if the parameter name is null, it should be interpreted
+ as a active */
+ if (pname == PDP_SYM_NULL){
+ run(x);
+ return;
+ }
+
+
+ /* get the stack atom index */
+ index = pdp_forth_processor_map_inputname_to_stackindex(x->x_processor, pname);
+ t_pdp_atom *stack_atom;
+
+ if (index < 0){
+ /* message is not in the pdmap: check any special messages */
+ if (!handle_special_message(x, s, argc, argv))
+ post("got invalid msg %s", s->s_name);
+ return;
+ }
+
+ /* get stack atom
+ if thread processing is on, get an atom from the template stack
+ because the real stack is in an indeterminate state */
+ i=index;
+ stack_atom = x->x_thread ? x->x_stack_template->first : x->x_stack->first;
+ while (i--) stack_atom = stack_atom->next;
+
+
+ /* store the type */
+ ta.t = stack_atom->t;
+
+ /* check if it is an active inlet
+ only floats, symbols, bangs, pdp and dpd messages can be active,
+ the others will be translated to different selectors */
+
+ if ((&s_float == s)
+ ||(&s_bang == s)
+ ||(&s_symbol == s)
+ ||(S_PDP == s)
+ ||(S_DPD == s)) active = 1;
+
+ /* interprete the anything message according to expected type (ta.t)
+ and put the result in the input event queue w */
+
+ switch(ta.t){
+
+ case a_float:
+ if ((argc != 1) || argv[0].a_type != A_FLOAT) post("bad float msg");
+ else ta.w.w_float = argv[0].a_w.w_float;
+ break;
+
+ case a_int:
+ if ((argc != 1) || argv[0].a_type != A_FLOAT) post("bad float msg");
+ else ta.w.w_int = (int)argv[0].a_w.w_float;
+ break;
+
+ case a_symbol:
+ if ((argc != 1) || argv[0].a_type != A_SYMBOL) post("bad symbol msg");
+ else ta.w.w_symbol = pdp_gensym(argv[0].a_w.w_symbol->s_name);
+
+ case a_list:
+ post("a_list: not supported yet");
+ break;
+
+ case a_packet:
+ if ((argc != 2)
+ || argv[0].a_type != A_SYMBOL
+ || argv[1].a_type != A_FLOAT) post ("bad pdp msg");
+ else{
+ t_symbol *command = argv[0].a_w.w_symbol;
+ int packet = (int)argv[1].a_w.w_float;
+
+
+ /* PDP */
+ /* register the pd packet readonly by default: stack owns a ro copy.
+ the forth words should convert a packet to rw if they need to
+ (we can't tell here) */
+ if (command == S_REGISTER_RO){
+ ta.w.w_packet = pdp_packet_copy_ro(packet);
+ }
+
+ /* DPD: does not work when multiple context outlets are involved */
+
+ /* register readonly just like pdp packets. but for dpd context
+ processors it's understood that the packet can be modified.
+ and store the reference to pass it along, if it's an active dpd packet */
+ else if (command == S_ACCUMULATE){
+
+ ta.w.w_packet = pdp_packet_copy_ro(packet);
+
+ if (s == S_DPD){ // only store main (left) inlet's dpd messages
+ x->x_dpd_packet = ta.w.w_packet;
+ }
+ }
+
+ else {
+ /* if it's not a register_ro (pdp) phase, or an accumulate (dpd) phase,
+ we're not going to do anything with the paket */
+ ta.w.w_packet = -1;
+ }
+
+ /* only the pdp process message or dpd accumulate message can be active */
+ if ((command != S_PROCESS)
+ && (command != S_ACCUMULATE)) active = 0;
+
+ }
+ break;
+ default:
+ post("unknown");
+ return;
+
+ }
+
+ /* check if we need to store the atom into the processor stack
+ directly or should put it in the input event queue */
+
+ if (!x->x_thread){
+ /* handle packets */
+ if (ta.t == a_packet){
+
+ /* only copy if valid (if it was valid and it's a register_ro phase */
+ if (ta.w.w_packet != -1){
+ pdp_packet_mark_unused(stack_atom->w.w_packet);
+ stack_atom->w = ta.w;
+ }
+ }
+ /* handle other atoms: store directly */
+ else{
+ stack_atom->w = ta.w;
+ }
+ }
+ else {
+
+ /* don't store invalid packets */
+ if (!(ta.t == a_packet && ta.w.w_packet == -1)){
+
+ /* store atom + location in event list */
+ t_pdp_list *ev = pdp_list_new(2);
+ pdp_list_set_0(ev, a_int, (t_pdp_word)index); // store atom location
+ pdp_list_set_1(ev, ta.t, ta.w); // store atom
+ pdp_list_add_back(x->x_input_events, a_list,
+ (t_pdp_word)ev); // store event in list
+
+ }
+ }
+
+
+ /* run the processor if it was an active input */
+ if (active) run(x);
+
+
+}
+
+
+
+/* OTHER METHODS */
+/* these serve as a replacement for the pdp_base class
+ the most prominent messages are:
+ - debug
+ - chanmask
+ - thread on/off/default on a per object basis
+*/
+
+
+
+/* MEM & INIT */
+
+static void forthproc_free(t_forthproc *x)
+{
+ /* wait for thread processing to finish */
+ pdp_procqueue_flush(x->x_q);
+
+ /* free state stack + template stack */
+ pdp_tree_strip_packets(x->x_stack);
+ pdp_tree_free(x->x_stack);
+ pdp_tree_free(x->x_stack_template);
+
+
+ /* free input event list */
+ pdp_tree_strip_packets(x->x_input_events);
+ pdp_tree_free(x->x_input_events);
+
+ /* free input/output event queues */
+ pdp_tree_strip_packets(x->x_queue_input);
+ pdp_tree_strip_packets(x->x_queue_output);
+ pdp_tree_free(x->x_queue_input);
+ pdp_tree_free(x->x_queue_output);
+
+ /* delete outlet pointer array */
+ if (x->x_outlet) pdp_dealloc(x->x_outlet);
+}
+
+t_class *forthproc_class;
+
+
+
+static void *forthproc_new(t_symbol *protocol, int argc, t_atom *argv)
+{
+ t_forthproc *x;
+ t_pdp_atom *a;
+ t_symbol *procname;
+ int i;
+
+ /* get processor name */
+ if ((argc < 1) || (argv[0].a_type != A_SYMBOL)) return 0;
+ procname = argv[0].a_w.w_symbol;
+ argc--;
+ argv++;
+
+
+ /* allocate */
+ x = (t_forthproc *)pd_new(forthproc_class);
+
+ /* init */
+ x->x_stack = 0;
+ x->x_stack_template = 0;
+ x->x_outlet = 0;
+ x->x_pdmap = 0;
+ x->x_processor = 0;
+ x->x_thread = 1;
+ x->x_program = 0;
+ x->x_chanmask = -1;
+ x->x_dpd_outlet = 0;
+ x->x_dpd_packet = -1;
+
+ /* get the protocol */
+ x->x_protocol = protocol;
+ //post("forthproc using protocol: %s", protocol->s_name);
+
+ /* input event list */
+ x->x_input_events = pdp_list_new(0);
+
+ /* input/output event queues */
+ x->x_queue_input = pdp_list_new(0);
+ x->x_queue_output = pdp_list_new(0);
+
+ /* default queue is pdp queue */
+ x->x_q = pdp_queue_get_queue();
+
+
+
+ /* get the pd mapper */
+ x->x_pdmap = pdp_forth_pdmap_getbyname(pdp_gensym(procname->s_name));
+ if (!x->x_pdmap) goto error;
+
+ /* get the processor */
+ x->x_processor = pdp_forth_pdmap_get_processor(x->x_pdmap);
+ if (!x->x_processor) goto error;
+
+ /* get the program */
+ x->x_program = x->x_processor->first->next->next->next->w.w_list;
+
+ /* create state stack template and remove packets
+ so they don't consume resources */
+ x->x_stack_template = pdp_forth_processor_setup_stack(x->x_processor);
+ pdp_tree_strip_packets(x->x_stack_template);
+
+ /* create the state stack */
+ x->x_stack = pdp_forth_processor_setup_stack(x->x_processor);
+
+
+ /* create additional inlets from description */
+ for (a = pdp_forth_pdmap_inlist(x->x_pdmap)->first; a; a=a->next){
+ t_pdp_symbol *message_id = a->w.w_symbol;
+ t_symbol *dsym = gensym(message_id->s_name);
+ t_symbol *ssym = 0;
+
+ t_pdp_symbol *pname = pdp_forth_pdmap_get_pname(x->x_pdmap, message_id);
+ t_pdp_atom *sa = pdp_forth_processor_stackatom_from_inputname(x->x_processor, x->x_stack, pname);
+ if (sa) {
+ switch(sa->t){
+ case a_float:
+ case a_int:
+ ssym = &s_float; break;
+ case a_packet:
+ ssym = S_PDP; break;
+ default:
+ post("unsupported type on stack"); break;
+ }
+ }
+
+ /* add inlet */
+ if (ssym){
+ //post("adding %s inlet %s (forth processor param %s)",
+ // ssym->s_name, dsym->s_name, pname->s_name);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, ssym, dsym);
+ }
+ else {
+ post("error adding inlet");
+ }
+ }
+
+ /* create outlets */
+ if (x->x_protocol == S_DPD){
+ x->x_dpd_outlet = outlet_new(&x->x_obj, &s_anything);
+ }
+
+ x->x_nb_outlets = pdp_forth_pdmap_outlist(x->x_pdmap)->elements;
+ if (x->x_nb_outlets){
+ x->x_outlet = pdp_alloc(x->x_nb_outlets * sizeof(*x->x_outlet));
+ for (i=0; i<x->x_nb_outlets; i++){
+ x->x_outlet[i] = outlet_new(&x->x_obj, &s_anything);
+ }
+ }
+
+ /* interpret arguments */
+ //pdp_list_print(pdp_forth_pdmap_arglist(x->x_pdmap));
+ for(a = pdp_forth_pdmap_arglist(x->x_pdmap)->first;
+ a && argc;
+ a=a->next, argv++, argc--){
+
+ t_pdp_atom *sa = pdp_forth_processor_stackatom_from_inputname
+ (x->x_processor, x->x_stack, a->w.w_symbol);
+ if (!sa) {
+ post("parameter %s not found", a->w.w_symbol->s_name);
+ continue;
+ }
+ //post("loading parameter %s", a->w.w_symbol->s_name);
+ /* handle symbols */
+ if((sa->t == a_symbol) && (argv->a_type == A_SYMBOL))
+ sa->w.w_symbol = pdp_gensym(argv->a_w.w_symbol->s_name);
+ /* handle floats */
+ else if (argv->a_type == A_FLOAT){
+ switch(sa->t){
+ case a_float: sa->w.w_float = argv->a_w.w_float; break;
+ case a_int: sa->w.w_int = (int)argv->a_w.w_float; break;
+ default: break;
+ }
+ }
+
+ }
+
+
+ /* finished */
+ return (void *)x;
+
+ error:
+
+ post ("error creating forth processor %s", procname->s_name);
+ forthproc_free(x);
+ return 0;
+
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_forthproc_setup(void)
+{
+ int i;
+
+ /* create a standard pd class */
+ forthproc_class = class_new(gensym("pdp"), (t_newmethod)forthproc_new,
+ (t_method)forthproc_free, sizeof(t_forthproc), 0, A_GIMME, A_NULL);
+ class_addcreator((t_newmethod)forthproc_new, gensym("dpd"), A_GIMME, A_NULL);
+
+ /* add global message handler */
+ class_addanything(forthproc_class, (t_method)handle_pd_message);
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_imagebase.c b/puredata/pdp_imagebase.c
new file mode 100644
index 0000000..f9634e1
--- /dev/null
+++ b/puredata/pdp_imagebase.c
@@ -0,0 +1,79 @@
+/*
+ * Pure Data Packet image processor base class implementation.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/*
+
+ This file contains the pdp image base class object.
+*/
+
+#include "pdp_imagebase.h"
+#include <stdarg.h>
+
+
+static void pdp_imagebase_chanmask(t_pdp_base *b, t_floatarg f)
+{
+ int i = (int)f;
+ if (i < 0) i = -1;
+ b->b_channel_mask = i;
+}
+
+void pdp_imagebase_setup(t_class *c)
+{
+ /* parent class setup */
+ pdp_base_setup(c);
+
+ /* add pdp base class methods */
+ class_addmethod(c, (t_method)pdp_imagebase_chanmask, gensym("chanmask"), A_FLOAT, A_NULL);
+
+}
+
+/* pdp base instance constructor */
+void pdp_imagebase_init(void *x)
+{
+ int i;
+ t_pdp_imagebase *b = (t_pdp_imagebase *)x;
+
+ /* init super */
+ pdp_base_init(x);
+
+ /* convert all active incoming packet types to image */
+ pdp_base_set_type_template(x, 0, pdp_gensym("image/*/*"));
+
+ /* default chanmask == all */
+ b->b_channel_mask = -1;
+
+}
+
+/* base instance destructor */
+void pdp_imagebase_free(void *x)
+{
+ /* free super */
+ pdp_base_free(x);
+
+}
+
+/* chanmask getter */
+u32 pdp_imagebase_get_chanmask(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ return b->b_channel_mask;
+}
+
diff --git a/puredata/pdp_queue.c b/puredata/pdp_queue.c
new file mode 100644
index 0000000..b66289a
--- /dev/null
+++ b/puredata/pdp_queue.c
@@ -0,0 +1,386 @@
+/*
+ * Pure Data Packet - processor queue module.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+
+/*
+ this is a the processor queue pdp system module
+ it receives tasks from objects that are schedules to
+ be computed in another thread. the object is signalled back
+ when the task is completed, using a polling mechanism
+ based on a pd clock.
+
+ the queue object can be reused. the pdp system however only
+ has one instance (one pdp queue. pdp remains a serial program, though
+ it can run in a separate thread)
+
+ */
+
+
+#include <string.h>
+
+#include "pdp_queue.h"
+#include "pdp_mem.h"
+
+
+#define D if (0)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define PDP_QUEUE_LOGSIZE 10
+#define PDP_QUEUE_DELTIME 10.0f
+
+
+/* there are 3 synchro methods, which can be used i.e. to ensure
+ all processing is done before shared resources are freed.
+
+ all 3 wait for the processing thread to finish, and
+
+ _wait: leaves callback queue untouched
+ _finish: clears the queue_id item in the callback queue
+ _flush: waits for thread and calls callbacks
+ and loops until callback list is empty
+
+*/
+
+
+
+/********************* general purpose pd process queue class *********************/
+
+void pdp_procqueue_wait(t_pdp_procqueue *q)
+{
+ D post("pdp_procqueue_wait(%x): waiting for pdp_queue_thread to finish processing", q);
+ pthread_mutex_lock(&q->mut);
+ while(((q->curr - q->head) & q->mask) != 0){
+
+ pthread_cond_wait(&q->cond_processingdone, &q->mut);
+ }
+ pthread_mutex_unlock(&q->mut);
+ D post("pdp_procqueue_wait(%x): pdp_procqueue_thread has finished processing", q);
+
+}
+void pdp_procqueue_finish(t_pdp_procqueue *q, int index)
+{
+
+ if (-1 == index) {
+ //post("pdp_pq_remove: index == -1");
+ return;
+ }
+ /* wait for processing thread to finish*/
+ pdp_procqueue_wait(q);
+
+ /* invalidate callback at index */
+ q->q[index & q->mask].x_callback = 0;
+ q->q[index & q->mask].x_queue_id = 0;
+
+}
+
+static void pdp_procqueue_callback (t_pdp_procqueue *q);
+
+void pdp_procqueue_flush(t_pdp_procqueue *q)
+{
+ /* wait once */
+ pdp_procqueue_wait(q);
+
+ do {
+
+ /* process callbacks and wait again
+ in case the callbacks introduced new tasks */
+ pdp_procqueue_callback(q);
+ pdp_procqueue_wait(q);
+
+ }
+ /* repeat if callback list is not empty */
+ while ((q->curr - q->head) & q->mask);
+
+ D post("pdp_procqueue_flush: done");
+}
+
+static void pdp_procqueue_signal_processor(t_pdp_procqueue *q)
+{
+
+ //NOTE: uncommenting these post statements causes a libc crash
+ //in mutex lock in putc
+ //D post("pdp_procqueue_signal_processor(%x): signalling process thread", q);
+ pthread_mutex_lock(&q->mut);
+ pthread_cond_signal(&q->cond_dataready);
+ pthread_mutex_unlock(&q->mut);
+ //D post("pdp_procqueue_signal_processor(%x): signalling done", q);
+
+
+}
+
+static void pdp_procqueue_wait_for_feeder(t_pdp_procqueue *q)
+{
+
+
+ /* only use locking when there is no data */
+ if(((q->curr - q->head) & q->mask) == 0){
+
+ /* signal processing done */
+ D post("pdp_procqueue_wait_for_feeder(%x): signalling processing is done", q);
+ pthread_mutex_lock(&q->mut);
+ pthread_cond_signal(&q->cond_processingdone);
+
+ /* wait until there is an item in the queue */
+ while(((q->curr - q->head) & q->mask) == 0){
+ pthread_cond_wait(&q->cond_dataready, &q->mut);
+ }
+
+ pthread_mutex_unlock(&q->mut);
+ D post("pdp_procqueue_wait_for_feeder(%x): waiting done", q);
+
+ }
+}
+
+
+int pdp_procqueue_full(t_pdp_procqueue *q)
+{
+ return (1 == ((q->tail - q->head) & q->mask));
+}
+
+
+void pdp_procqueue_add(t_pdp_procqueue *q, void *owner, void *process, void *callback, int *queue_id)
+{
+ int i;
+
+ /* if processing is in not in thread, just call the funcs */
+ if (!q->use_thread){
+ D post("pdp_procqueue_add(%q): calling processing routine directly", q);
+ if (queue_id) *queue_id = -1;
+ if (process) ((t_pdpmethod) process)(owner);
+ if (callback) ((t_pdpmethod) callback)(owner);
+ return;
+ }
+
+
+ /* if queue is full, print an error message and return */
+ if (pdp_procqueue_full(q)) {
+ post("pdp_procqueue_add: WARNING: processing queue (%x) is full.\n", q);
+ post("pdp_procqueue_add: WARNING: tail %08x, head %08x (%08x), mask %08x.\n", q->tail, q->head, q->head & q->mask, q->mask);
+ post("pdp_procqueue_add: WARNING: skipping process method, calling callback directly.\n");
+ if (queue_id) *queue_id = -1;
+ if (callback) ((t_pdpmethod) callback)(owner);
+ return;
+ //exit(1);
+ }
+
+ /* schedule method in thread queue */
+ i = q->head & q->mask;
+ q->q[i].x_owner = owner;
+ q->q[i].x_process = process;
+ q->q[i].x_callback = callback;
+ q->q[i].x_queue_id = queue_id;
+ if (queue_id) *queue_id = i;
+ //post("pdp_queue_add: added method to queue, index %d", i);
+
+
+ // increase the packet count
+ q->packets++;
+
+ // move head forward
+ q->head++;
+
+ pdp_procqueue_signal_processor(q);
+
+}
+
+
+/* processing thread */
+static void *pdp_procqueue_thread(void *vq)
+{
+ t_pdp_procqueue *q = (t_pdp_procqueue *)vq;
+
+ D post("pdp_procqueue_thread(%x): thread started", q);
+
+ while(1){
+ t_process_queue_item *p;
+
+
+ D post("pdp_procqueue_thread(%x): waiting for feeder", q);
+
+ /* wait until there is data available */
+ pdp_procqueue_wait_for_feeder(q);
+
+
+ D post("pdp_procqueue_thread(%x): processing %d", q, q->curr & q->mask);
+
+
+ /* call the process routine */
+ p = &q->q[q->curr & q->mask];
+ if (p->x_process)
+ (p->x_process)(p->x_owner);
+
+ /* advance */
+ q->curr++;
+
+
+ }
+ return 0;
+}
+
+
+/* call back all the callbacks */
+static void pdp_procqueue_callback (t_pdp_procqueue *q)
+{
+
+ /* call callbacks for finished packets */
+ while(0 != ((q->curr - q->tail) & q->mask))
+ {
+ int i = q->tail & q->mask;
+ /* invalidate queue id */
+ if(q->q[i].x_queue_id) *q->q[i].x_queue_id = -1;
+ /* call callback */
+ if(q->q[i].x_callback) (q->q[i].x_callback)(q->q[i].x_owner);
+ //else post("pdp_pq_tick: callback %d is disabled",i );
+ q->tail++;
+ }
+
+}
+
+/* the clock method */
+static void pdp_procqueue_tick (t_pdp_procqueue *q)
+{
+ /* do work */
+ //if (!(ticks % 1000)) post("pdp tick %d", ticks);
+
+ if (!q->use_thread) return;
+
+ /* call callbacks */
+ pdp_procqueue_callback(q);
+
+ /* increase counter */
+ q->ticks++;
+
+ /* set clock for next update */
+ clock_delay(q->pdp_clock, q->deltime);
+}
+
+
+
+void pdp_procqueue_use_thread(t_pdp_procqueue* q, int t)
+{
+ /* if thread usage is being disabled,
+ wait for thread to finish processing first */
+ if (t == 0) {
+ pdp_procqueue_wait(q);
+ q->use_thread = 0;
+ pdp_procqueue_callback(q);
+ clock_unset(q->pdp_clock);
+ }
+ else {
+ clock_unset(q->pdp_clock);
+ clock_delay(q->pdp_clock, q->deltime);
+ q->use_thread = 1;
+ }
+
+}
+
+void pdp_procqueue_init(t_pdp_procqueue *q, double milliseconds, int logsize)
+{
+ pthread_attr_t attr;
+ int size = 1 << logsize;
+
+ /* setup pdp queue processor object */
+ q->ticks = 0;
+ q->deltime = milliseconds;
+
+ /* setup queue data */
+ q->mask = size - 1;
+ q->head = 0;
+ q->tail = 0;
+ q->curr = 0;
+ q->q = pdp_alloc(size * sizeof(t_process_queue_item));
+ memset(q->q, 0, size * sizeof(t_process_queue_item));
+
+ /* enable threads */
+ q->use_thread = 1;
+
+ /* setup synchro stuff */
+ pthread_mutex_init(&q->mut, NULL);
+ pthread_cond_init(&q->cond_dataready, NULL);
+ pthread_cond_init(&q->cond_processingdone, NULL);
+
+
+ /* allocate the clock */
+ q->pdp_clock = clock_new(q, (t_method)pdp_procqueue_tick);
+
+ /* set the clock */
+ clock_delay(q->pdp_clock, 0);
+
+ /* start processing thread */
+
+ /* glibc doc says SCHED_OTHER is default,
+ but it seems not to be when initiated from a RT thread
+ so we explicitly set it here */
+ pthread_attr_init (&attr);
+ //pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
+ pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
+
+ D post("pdp_procqueue_init(%x): starting thread", q);
+ pthread_create(&q->thread_id, &attr, pdp_procqueue_thread, (void *)q);
+ D post("pdp_procqueue_init(%x): back in pd thread", q);
+
+ /* wait for processing thread to finish */
+ //pdp_procqueue_wait(q);
+
+ /* set default disable/enable thread here */
+ //post("pdp_queue: THREAD PROCESSING ON BY DEFAULT!!");
+ pdp_procqueue_use_thread(q,0);
+
+}
+
+
+
+
+/* the (static) pdp queue object */
+static t_pdp_procqueue pdp_queue;
+
+
+/* get the default queue */
+t_pdp_procqueue *pdp_queue_get_queue(void){return &pdp_queue;}
+
+
+#if 1
+/* default pdp queue shortcut methods */
+void pdp_queue_wait() {pdp_procqueue_wait(&pdp_queue);}
+void pdp_queue_finish(int index) { pdp_procqueue_finish(&pdp_queue, index);}
+void pdp_queue_add(void *owner, void *process, void *callback, int *queue_id) {
+ pdp_procqueue_add(&pdp_queue, owner, process, callback, queue_id);
+}
+void pdp_queue_use_thread(int t) {pdp_procqueue_use_thread(&pdp_queue, t);}
+void pdp_queue_setup(void){
+ pdp_procqueue_init(&pdp_queue, PDP_QUEUE_DELTIME, PDP_QUEUE_LOGSIZE);
+ pdp_procqueue_use_thread(&pdp_queue,0);
+}
+#endif
+
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_ut.c b/puredata/pdp_ut.c
new file mode 100644
index 0000000..a1369e3
--- /dev/null
+++ b/puredata/pdp_ut.c
@@ -0,0 +1,262 @@
+/*
+ * Pure Data Packet - Utility toolkit objects.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/* This file contains some small utility pd objects that make working with
+ pdp objects a lot easier. Mainly as glue to be used in the abstractions
+ in the distro. */
+
+
+#include "pdp_pd.h"
+#include <math.h>
+
+/* this object does an add, scale, clip operation */
+
+t_class *pdp_ut_addscaleclip_class;
+
+typedef struct pdp_ut_addscaleclip_struct
+{
+ t_object x_obj;
+ t_outlet *x_outlet0;
+ t_float x_min;
+ t_float x_max;
+ t_float x_offset;
+ t_float x_scale;
+} t_pdp_ut_addscaleclip;
+
+
+static void pdp_ut_addscaleclip_float(t_pdp_ut_addscaleclip *x, t_floatarg f)
+{
+ f += x->x_offset;
+ f *= x->x_scale;
+ f = (f < x->x_min) ? x->x_min : f;
+ f = (f > x->x_max) ? x->x_max : f;
+ outlet_float(x->x_outlet0, f);
+}
+
+static void pdp_ut_addscaleclip_free(t_pdp_ut_addscaleclip *x){}
+
+void *pdp_ut_addscaleclip_new(t_floatarg offset, t_floatarg scale, t_floatarg min, t_floatarg max)
+{
+ t_pdp_ut_addscaleclip *x = (t_pdp_ut_addscaleclip *)pd_new(pdp_ut_addscaleclip_class);
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_float);
+ x->x_offset = offset;
+ x->x_scale = scale;
+ x->x_min = min;
+ x->x_max = max;
+ return (void *)x;
+}
+
+void pdp_ut_addscaleclip_setup(void)
+{
+ pdp_ut_addscaleclip_class = class_new(gensym("pdp_ut_addscaleclip"), (t_newmethod)pdp_ut_addscaleclip_new,
+ (t_method)pdp_ut_addscaleclip_free, sizeof(t_pdp_ut_addscaleclip), 0,
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addfloat(pdp_ut_addscaleclip_class, pdp_ut_addscaleclip_float);
+}
+
+
+/* pdp_ut_logmap does a logarithmic parameter mapping [0->1] x -> min(max/min)^x max an add, scale, clip operation */
+/* pdp_ut_logmap_comp does x -> min(max/min)^(1-x) */
+/* pdp_ut_linmap dos x -> min + (max - min * x */
+
+t_class *pdp_ut_linmap_class;
+t_class *pdp_ut_logmap_class;
+t_class *pdp_ut_logmap_comp_class;
+
+typedef struct pdp_ut_map_struct
+{
+ t_object x_obj;
+ t_outlet *x_outlet0;
+ t_float x_min;
+ t_float x_max;
+} t_pdp_ut_map;
+
+
+static void pdp_ut_logmap_float(t_pdp_ut_map *x, t_floatarg f)
+{
+ f = (f < 0.0f) ? 0.0f : f;
+ f = (f > 1.0f) ? 1.0f : f;
+
+ f = x->x_min * pow((x->x_max / x->x_min), f);
+
+ outlet_float(x->x_outlet0, f);
+}
+
+static void pdp_ut_linmap_float(t_pdp_ut_map *x, t_floatarg f)
+{
+ f = (f < 0.0f) ? 0.0f : f;
+ f = (f > 1.0f) ? 1.0f : f;
+
+ f = x->x_min + ((x->x_max - x->x_min) * f);
+
+ outlet_float(x->x_outlet0, f);
+}
+
+static void pdp_ut_logmap_comp_float(t_pdp_ut_map *x, t_floatarg f)
+{
+ f = (f < 0.0f) ? 0.0f : f;
+ f = (f > 1.0f) ? 1.0f : f;
+
+ f = x->x_min * pow((x->x_max / x->x_min), (1.0f - f));
+
+ outlet_float(x->x_outlet0, f);
+}
+
+static void pdp_ut_map_free(t_pdp_ut_map *x){}
+
+
+void pdp_ut_map_init(t_pdp_ut_map *x, t_floatarg min, t_floatarg max)
+{
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_float);
+ x->x_min = min;
+ x->x_max = max;
+}
+
+void *pdp_ut_logmap_new(t_floatarg min, t_floatarg max)
+{
+ t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_logmap_class);
+ pdp_ut_map_init(x, min, max);
+ return (void *)x;
+}
+
+void *pdp_ut_linmap_new(t_floatarg min, t_floatarg max)
+{
+ t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_linmap_class);
+ pdp_ut_map_init(x, min, max);
+ return (void *)x;
+}
+
+void *pdp_ut_logmap_comp_new(t_floatarg min, t_floatarg max)
+{
+ t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_logmap_comp_class);
+ pdp_ut_map_init(x, min, max);
+ return (void *)x;
+}
+
+void pdp_ut_logmap_setup(void)
+{
+ pdp_ut_logmap_class = class_new(gensym("pdp_ut_logmap"), (t_newmethod)pdp_ut_logmap_new,
+ (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0,
+ A_FLOAT, A_FLOAT, A_NULL);
+ class_addfloat(pdp_ut_logmap_class, pdp_ut_logmap_float);
+}
+
+void pdp_ut_logmap_comp_setup(void)
+{
+ pdp_ut_logmap_comp_class = class_new(gensym("pdp_ut_logmap_comp"), (t_newmethod)pdp_ut_logmap_comp_new,
+ (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0,
+ A_FLOAT, A_FLOAT, A_NULL);
+ class_addfloat(pdp_ut_logmap_comp_class, pdp_ut_logmap_comp_float);
+}
+
+void pdp_ut_linmap_setup(void)
+{
+ pdp_ut_linmap_class = class_new(gensym("pdp_ut_linmap"), (t_newmethod)pdp_ut_linmap_new,
+ (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0,
+ A_FLOAT, A_FLOAT, A_NULL);
+ class_addfloat(pdp_ut_linmap_class, pdp_ut_linmap_float);
+}
+
+
+
+t_class *pdp_ut_rgb2ycrcb_class;
+
+typedef struct pdp_ut_rgb2ycrcb
+{
+ t_object x_obj;
+ t_outlet *x_outlet_luma;
+ t_outlet *x_outlet_chroma_red;
+ t_outlet *x_outlet_chroma_blue;
+
+ t_float x_red, x_green, x_blue;
+
+} t_pdp_ut_rgb2ycrcb;
+
+
+static void pdp_ut_rgb2ycrcb_bang (t_pdp_ut_rgb2ycrcb* x)
+{
+
+ float luma = 0.299f * x->x_red + 0.587f * x->x_green + 0.114f * x->x_blue;
+ float chroma_red = (x->x_red - luma) * 0.713f;
+ float chroma_blue = (x->x_blue - luma) * 0.565f;
+
+ outlet_float(x->x_outlet_chroma_blue, chroma_blue);
+ outlet_float(x->x_outlet_chroma_red, chroma_red);
+ outlet_float(x->x_outlet_luma, luma);
+
+}
+
+
+static void pdp_ut_rgb2ycrcb_red (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_red = f; pdp_ut_rgb2ycrcb_bang(x);}
+static void pdp_ut_rgb2ycrcb_green (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_green = f; pdp_ut_rgb2ycrcb_bang(x);}
+static void pdp_ut_rgb2ycrcb_blue (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_blue = f; pdp_ut_rgb2ycrcb_bang(x);}
+
+
+
+static void pdp_ut_rgb2ycrcb_free (t_pdp_ut_rgb2ycrcb* x) {}
+static void* pdp_ut_rgb2ycrcb_new(void)
+{
+ t_pdp_ut_rgb2ycrcb *x = (t_pdp_ut_rgb2ycrcb *)pd_new(pdp_ut_rgb2ycrcb_class);
+
+
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("green"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("blue"));
+
+ x->x_outlet_luma = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_chroma_red = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_chroma_blue = outlet_new(&x->x_obj, &s_float);
+
+ x->x_red = 0.0f;
+ x->x_green = 0.0f;
+ x->x_blue = 0.0f;
+
+
+ return (void *)x;
+}
+
+void pdp_ut_rgb2ycrcb_setup(void)
+{
+ pdp_ut_rgb2ycrcb_class = class_new(gensym("pdp_ut_rgb2ycrcb"), (t_newmethod)pdp_ut_rgb2ycrcb_new,
+ (t_method)pdp_ut_rgb2ycrcb_free, sizeof(t_pdp_ut_rgb2ycrcb), 0, A_NULL);
+ class_addfloat(pdp_ut_rgb2ycrcb_class, pdp_ut_rgb2ycrcb_red);
+ class_addmethod(pdp_ut_rgb2ycrcb_class, (t_method)pdp_ut_rgb2ycrcb_green, gensym("green"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_ut_rgb2ycrcb_class, (t_method)pdp_ut_rgb2ycrcb_blue, gensym("blue"), A_FLOAT, A_NULL);
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void pdp_ut_setup(void)
+{
+ pdp_ut_addscaleclip_setup();
+ pdp_ut_logmap_setup();
+ pdp_ut_logmap_comp_setup();
+ pdp_ut_linmap_setup();
+ pdp_ut_rgb2ycrcb_setup();
+}
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/scaf/test/test_pdp_ca2.pd b/scaf/test/test_pdp_ca2.pd
index 8af44a8..9561d9d 100644
--- a/scaf/test/test_pdp_ca2.pd
+++ b/scaf/test/test_pdp_ca2.pd
@@ -31,7 +31,6 @@
#X obj 481 167 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
1;
#X msg 526 55 ca 1024 512;
-#X msg 105 4 open /home/tom/pd/packet/scaf/modules/carules.scafo;
#X obj 426 294 pdp_ca2image;
#X obj 104 228 pdp_image2ca;
#X obj 136 155 metro 40;
@@ -82,16 +81,17 @@
#X msg 38 374 thread \$1;
#X obj 32 349 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
;
+#X msg 105 4 open /home/tom/pd/packet/scaf/rules/carules.scafo;
#X connect 0 0 2 0;
#X connect 1 0 0 0;
#X connect 2 0 3 0;
-#X connect 3 0 30 0;
+#X connect 3 0 29 0;
#X connect 4 0 3 0;
#X connect 5 0 3 0;
#X connect 6 0 5 0;
#X connect 7 0 5 0;
-#X connect 9 1 44 0;
-#X connect 9 2 29 0;
+#X connect 9 1 43 0;
+#X connect 9 2 73 0;
#X connect 10 0 9 0;
#X connect 11 0 3 0;
#X connect 12 0 3 0;
@@ -99,8 +99,8 @@
#X connect 14 0 3 0;
#X connect 15 0 3 2;
#X connect 16 0 3 0;
-#X connect 17 0 31 0;
-#X connect 17 0 63 1;
+#X connect 17 0 30 0;
+#X connect 17 0 62 1;
#X connect 18 0 17 0;
#X connect 19 0 3 0;
#X connect 20 0 3 0;
@@ -112,43 +112,43 @@
#X connect 26 0 3 0;
#X connect 27 0 15 0;
#X connect 28 0 3 0;
-#X connect 29 0 3 0;
-#X connect 30 0 62 0;
-#X connect 31 0 3 0;
-#X connect 32 0 17 0;
-#X connect 33 0 32 0;
-#X connect 34 0 32 0;
-#X connect 36 0 35 0;
-#X connect 40 0 3 0;
-#X connect 41 0 43 0;
-#X connect 42 0 3 0;
-#X connect 43 0 13 0;
-#X connect 43 1 42 0;
-#X connect 44 0 3 0;
-#X connect 45 0 31 1;
+#X connect 29 0 61 0;
+#X connect 30 0 3 0;
+#X connect 31 0 17 0;
+#X connect 32 0 31 0;
+#X connect 33 0 31 0;
+#X connect 35 0 34 0;
+#X connect 39 0 3 0;
+#X connect 40 0 42 0;
+#X connect 41 0 3 0;
+#X connect 42 0 13 0;
+#X connect 42 1 41 0;
+#X connect 43 0 3 0;
+#X connect 44 0 30 1;
+#X connect 45 0 3 0;
#X connect 46 0 3 0;
-#X connect 47 0 3 0;
-#X connect 48 0 45 0;
-#X connect 49 0 17 0;
-#X connect 50 0 31 0;
-#X connect 51 0 50 0;
-#X connect 52 0 50 0;
-#X connect 53 0 50 0;
-#X connect 54 0 50 0;
-#X connect 55 0 50 0;
+#X connect 47 0 44 0;
+#X connect 48 0 17 0;
+#X connect 49 0 30 0;
+#X connect 50 0 49 0;
+#X connect 51 0 49 0;
+#X connect 52 0 49 0;
+#X connect 53 0 49 0;
+#X connect 54 0 49 0;
+#X connect 55 0 56 0;
#X connect 56 0 57 0;
-#X connect 57 0 58 0;
-#X connect 57 0 58 1;
-#X connect 59 0 5 1;
-#X connect 60 0 3 0;
-#X connect 62 0 63 0;
-#X connect 63 0 8 0;
-#X connect 64 0 63 2;
-#X connect 65 0 32 1;
-#X connect 66 0 69 0;
-#X connect 67 0 68 0;
-#X connect 70 0 3 0;
-#X connect 71 0 4 0;
-#X connect 71 0 70 0;
-#X connect 72 0 66 0;
-#X connect 73 0 72 0;
+#X connect 56 0 57 1;
+#X connect 58 0 5 1;
+#X connect 59 0 3 0;
+#X connect 61 0 62 0;
+#X connect 62 0 8 0;
+#X connect 63 0 62 2;
+#X connect 64 0 31 1;
+#X connect 65 0 68 0;
+#X connect 66 0 67 0;
+#X connect 69 0 3 0;
+#X connect 70 0 4 0;
+#X connect 70 0 69 0;
+#X connect 71 0 65 0;
+#X connect 72 0 71 0;
+#X connect 73 0 3 0;
diff --git a/system/X11/pdp_xvideo.c b/system/X11/pdp_xvideo.c
new file mode 100644
index 0000000..dab060d
--- /dev/null
+++ b/system/X11/pdp_xvideo.c
@@ -0,0 +1,201 @@
+/*
+ * Pure Data Packet system module. - x window glue code (fairly tied to pd and pdp)
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+// this code is fairly tied to pd and pdp. serves mainly as reusable glue code
+// for pdp_xv, pdp_glx, pdp_3d_windowcontext, ...
+
+#include <string.h>
+
+#include "pdp_xwindow.h"
+#include "pdp_xvideo.h"
+#include "pdp_post.h"
+#include "pdp_packet.h"
+
+#define D if(0)
+
+
+
+
+/************************************* PDP_XVIDEO ************************************/
+
+static void pdp_xvideo_create_xvimage(t_pdp_xvideo *xvid, int width, int height)
+{
+ int i;
+ long size;
+
+ //post("pdp_xvideo_create_xvimage");
+
+ xvid->width = width;
+ xvid->height = height;
+ size = (xvid->width * xvid->height + (((xvid->width>>1)*(xvid->height>>1))<<1));
+ //post("create xvimage %d %d", xvid->width, xvid->height);
+ xvid->data = (unsigned char *)pdp_alloc(size);
+ for (i=0; i<size; i++) xvid->data[i] = i;
+ xvid->xvi = XvCreateImage(xvid->xdpy->dpy, xvid->xv_port, xvid->xv_format, (char *)xvid->data, xvid->width, xvid->height);
+ xvid->last_encoding = -1;
+ if ((!xvid->xvi) || (!xvid->data)) pdp_post ("ERROR CREATING XVIMAGE");
+ //pdp_post("created xvimag data:%x xvi:%x",xvid->data,xvid->xvi);
+
+}
+
+static void pdp_xvideo_destroy_xvimage(t_pdp_xvideo *xvid)
+{
+ if(xvid->data) pdp_dealloc(xvid->data);
+ if (xvid->xvi) XFree(xvid->xvi);
+ xvid->xvi = 0;
+ xvid->data = 0;
+}
+
+void pdp_xvideo_display_packet(t_pdp_xvideo *xvid, t_pdp_xwindow *xwin, int packet)
+{
+ t_pdp *header = pdp_packet_header(packet);
+ void *data = pdp_packet_data(packet);
+ t_bitmap * bm = pdp_packet_bitmap_info(packet);
+ unsigned int width, height, encoding, size, nbpixels;
+
+ /* some checks: only display when initialized and when pacet is bitmap YV12 */
+ if (!xvid->initialized) return;
+ if (!header) return;
+ if (!bm) return;
+
+ width = bm->width;
+ height = bm->height;
+ encoding = bm->encoding;
+ size = (width * height + (((width>>1)*(height>>1))<<1));
+ nbpixels = width * height;
+
+ if (PDP_BITMAP != header->type) return;
+ if (PDP_BITMAP_YV12 != encoding) return;
+
+ /* check if xvimage needs to be recreated */
+ if ((width != xvid->width) || (height != xvid->height)){
+ //pdp_post("pdp_xv: replace image");
+ pdp_xvideo_destroy_xvimage(xvid);
+ pdp_xvideo_create_xvimage(xvid, width, height);
+ }
+
+ /* copy the data to the XvImage buffer */
+ memcpy(xvid->data, data, size);
+
+ /* display */
+ XvPutImage(xvid->xdpy->dpy,xvid->xv_port, xwin->win,xwin->gc,xvid->xvi,
+ 0,0,xvid->width,xvid->height, 0,0,xwin->winwidth,xwin->winheight);
+ XFlush(xvid->xdpy->dpy);
+
+
+
+}
+
+
+
+void pdp_xvideo_close(t_pdp_xvideo* xvid)
+{
+ if (xvid->initialized){
+ if (xvid->xvi) pdp_xvideo_destroy_xvimage(xvid);
+ XvUngrabPort(xvid->xdpy->dpy, xvid->xv_port, CurrentTime);
+ xvid->xv_port = 0;
+ xvid->xdpy = 0;
+ xvid->last_encoding = -1;
+ xvid->initialized = false;
+ }
+}
+
+void pdp_xvideo_cleanup(t_pdp_xvideo* xvid)
+{
+ // close xvideo port (and delete XvImage)
+ pdp_xvideo_close(xvid);
+
+ // no more dynamic data to free
+
+}
+
+void pdp_xvideo_free(t_pdp_xvideo* xvid){
+ pdp_xvideo_cleanup(xvid);
+ pdp_dealloc(xvid);
+}
+
+void pdp_xvideo_init(t_pdp_xvideo *xvid)
+{
+
+ xvid->xdpy = 0;
+
+ xvid->xv_format = FOURCC_YV12;
+ xvid->xv_port = 0;
+
+ xvid->width = 320;
+ xvid->height = 240;
+
+ xvid->data = 0;
+ xvid->xvi = 0;
+
+ xvid->initialized = 0;
+ xvid->last_encoding = -1;
+
+}
+t_pdp_xvideo *pdp_xvideo_new(void)
+{
+ t_pdp_xvideo *xvid = pdp_alloc(sizeof(*xvid));
+ pdp_xvideo_init(xvid);
+ return xvid;
+}
+
+int pdp_xvideo_open_on_display(t_pdp_xvideo *xvid, t_pdp_xdisplay *d)
+{
+ unsigned int ver, rel, req, ev, err, i, j;
+ unsigned int adaptors;
+ int formats;
+ XvAdaptorInfo *ai;
+
+ if (xvid->initialized) return 1;
+ if (!d) return 0;
+ xvid->xdpy = d;
+
+ if (Success != XvQueryExtension(xvid->xdpy->dpy,&ver,&rel,&req,&ev,&err)) return 0;
+
+ /* find + lock port */
+ if (Success != XvQueryAdaptors(xvid->xdpy->dpy,DefaultRootWindow(xvid->xdpy->dpy),&adaptors,&ai))
+ return 0;
+ for (i = 0; i < adaptors; i++) {
+ if ((ai[i].type & XvInputMask) && (ai[i].type & XvImageMask)) {
+ for (j=0; j < ai[i].num_ports; j++){
+ if (Success != XvGrabPort(xvid->xdpy->dpy,ai[i].base_id+j,CurrentTime)) {
+ //fprintf(stderr,"INFO: Xvideo port %ld on adapter %d: is busy, skipping\n",ai[i].base_id+j, i);
+ }
+ else {
+ xvid->xv_port = ai[i].base_id + j;
+ goto breakout;
+ }
+ }
+ }
+ }
+
+
+ breakout:
+
+ XFree(ai);
+ if (0 == xvid->xv_port) return 0;
+ pdp_post("pdp_xvideo: grabbed port %d on adaptor %d", xvid->xv_port, i);
+ xvid->initialized = 1;
+ pdp_xvideo_create_xvimage(xvid, xvid->width, xvid->height);
+ return 1;
+}
+
+
diff --git a/system/kernel/CONTENTS b/system/kernel/CONTENTS
new file mode 100644
index 0000000..c2f7c8c
--- /dev/null
+++ b/system/kernel/CONTENTS
@@ -0,0 +1,7 @@
+debug debug stuff
+forth the forth system
+list the list implementation
+mem memory allocation stuf
+packet the packet memory manager
+type the type handling and conversion system
+symbol symbol implementation, with namespaces for forth, types, classes, ...
diff --git a/system/kernel/pdp_debug.c b/system/kernel/pdp_debug.c
new file mode 100644
index 0000000..07f6541
--- /dev/null
+++ b/system/kernel/pdp_debug.c
@@ -0,0 +1,21 @@
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#include "pdp_post.h"
+
+int pdp_debug_sigtrap_on_assert;
+
+
+void pdp_assert_hook (char *condition, char *file, int line)
+{
+ pdp_post("PDP_ASSERT (%s) failed in file %s, line %u. ", condition, file, line);
+ pdp_post("%s.\n", pdp_debug_sigtrap_on_assert ? "sending SIGTRAP" : "continuing");
+
+ if (pdp_debug_sigtrap_on_assert) kill(getpid(), SIGTRAP);
+}
+
+
+void pdp_debug_setup(void)
+{
+ pdp_debug_sigtrap_on_assert = 1;
+}
diff --git a/system/kernel/pdp_mem.c b/system/kernel/pdp_mem.c
new file mode 100644
index 0000000..33822ef
--- /dev/null
+++ b/system/kernel/pdp_mem.c
@@ -0,0 +1,129 @@
+/*
+ * Pure Data Packet system file: memory allocation
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include "pdp_mem.h"
+#include "pdp_debug.h"
+
+
+/* malloc wrapper that calls garbage collector */
+void *pdp_alloc(int size)
+{
+ void *ptr = malloc(size);
+
+ PDP_ASSERT(ptr);
+
+ return ptr;
+
+ //TODO: REPAIR THIS
+ //post ("malloc failed in a pdp module: running garbage collector.");
+ //pdp_pool_collect_garbage();
+ //return malloc(size);
+}
+
+
+void pdp_dealloc(void *stuff)
+{
+ free (stuff);
+}
+
+
+/* fast atom allocation object
+ well, this is not too fast yet, but will be later
+ when it suports linux futexes or atomic operations */
+
+//#include <pthread.h>
+
+/* private linked list struct */
+typedef struct _fastalloc
+{
+ struct _fastalloc * next;
+} t_fastalloc;
+
+
+
+
+static void _pdp_fastalloc_lock(t_pdp_fastalloc *x){pthread_mutex_lock(&x->mut);}
+static void _pdp_fastalloc_unlock(t_pdp_fastalloc *x){pthread_mutex_unlock(&x->mut);}
+
+static void _pdp_fastalloc_refill_freelist(t_pdp_fastalloc *x)
+{
+ t_fastalloc *atom;
+ unsigned int i;
+
+ PDP_ASSERT(x->freelist == 0);
+
+ /* get a new block
+ there is no means of freeing the data afterwards,
+ this is a fast implementation with the tradeoff of data
+ fragmentation "memory leaks".. */
+
+ x->freelist = pdp_alloc(x->block_elements * x->atom_size);
+
+ /* link all atoms together */
+ atom = x->freelist;
+ for (i=0; i<x->block_elements-1; i++){
+ atom->next = (t_fastalloc *)(((char *)atom) + x->atom_size);
+ atom = atom->next;
+ }
+ atom->next = 0;
+
+}
+
+void *pdp_fastalloc_new_atom(t_pdp_fastalloc *x)
+{
+ t_fastalloc *atom;
+
+ _pdp_fastalloc_lock(x);
+
+ /* get an atom from the freelist
+ or refill it and try again */
+ while (!(atom = x->freelist)){
+ _pdp_fastalloc_refill_freelist(x);
+ }
+
+ /* delete the element from the freelist */
+ x->freelist = x->freelist->next;
+ atom->next = 0;
+
+ _pdp_fastalloc_unlock(x);
+
+ return (void *)atom;
+
+}
+void pdp_fastalloc_save_atom(t_pdp_fastalloc *x, void *atom)
+{
+ _pdp_fastalloc_lock(x);
+ ((t_fastalloc *)atom)->next = x->freelist;
+ x->freelist = (t_fastalloc *)atom;
+ _pdp_fastalloc_unlock(x);
+}
+
+t_pdp_fastalloc *pdp_fastalloc_new(unsigned int size)
+{
+ t_pdp_fastalloc *x = pdp_alloc(sizeof(*x));
+ if (size < sizeof(t_fastalloc)) size = sizeof(t_fastalloc);
+ x->freelist = 0;
+ x->atom_size = size;
+ x->block_elements = PDP_FASTALLOC_BLOCK_ELEMENTS;
+ pthread_mutex_init(&x->mut, NULL);
+ return x;
+}
+
diff --git a/system/kernel/pdp_packet2.c b/system/kernel/pdp_packet2.c
new file mode 100644
index 0000000..3717a77
--- /dev/null
+++ b/system/kernel/pdp_packet2.c
@@ -0,0 +1,623 @@
+/*
+ * Pure Data Packet system implementation: Packet Manager
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include "pdp_post.h"
+#include "pdp_packet.h"
+#include "pdp_mem.h"
+#include "pdp_list.h"
+#include "pdp_type.h"
+#include "pdp_debug.h"
+
+
+/* packet implementation. contains class and packet (instance) handling
+
+ some notes on packet operations.
+ copy ro/rw and unregister are relatively straightforward
+ packet creation can be done in 2 ways in this interface:
+ create + reuse
+ however, these methods should only be called by specific factory
+ methods, so the user should only create packets using pdp_factory_newpacket
+
+ reuse or create is thus the responsability of the factory methods for
+ each packet type (class) implementation
+
+
+*/
+
+
+/* NOTE:
+ the packet pool methods are called within the pool locks. this probably
+ needs to change, because it will cause deadlocks for container packets (fobs) */
+
+
+/* new implementation: probably just a minor adjustment: add the reuse fifo attached
+ to type desc symbol name
+ need to check and possibly eliminate hacks for non-pure packets
+
+ pdp_packet_new:
+ LOCK
+ 1. check reuse fifo
+ 2. empty -> create packet+return (search array)
+ 3. element -> check if type is correct, yes->pop+return, no->goto 1.
+ UNLOCK
+ 4. wakeup
+
+ pdp_packet_mark_unused
+
+ 1. check refcount. if > 1 dec + exit
+ 2. if 1 put packet to sleep
+ 3. dec refcount
+ 4. add to reuse fifo (no fifo -> create)
+
+ pdp_packet_delete: analogous to mark_unused
+ pdp_packet_copy_ro/rw: analogous to new
+
+*/
+
+
+/* the pool */
+#define PDP_INITIAL_POOL_SIZE 64
+static int pdp_pool_size;
+static t_pdp** pdp_pool;
+
+/* mutex: protects the pool and reuse lists attached to symbols */
+static pthread_mutex_t pdp_pool_mutex;
+#define LOCK pthread_mutex_lock (&pdp_pool_mutex)
+#define UNLOCK pthread_mutex_unlock (&pdp_pool_mutex)
+
+/* the list of classes */
+static t_pdp_list *class_list;
+
+/* debug */
+void
+pdp_packet_print_debug(int packet)
+{
+ t_pdp *h = pdp_packet_header(packet);
+ pdp_post("debug info for packet %d", packet);
+ if (!h){
+ pdp_post("invalid packet");
+ }
+ else{
+ pdp_post ("\ttype: %d", h->type);
+ pdp_post ("\tdesc: %s", h->desc ? h->desc->s_name : "unknown");
+ pdp_post ("\tsize: %d", h->size);
+ pdp_post ("\tflags: %x", h->flags);
+ pdp_post ("\tusers: %d", h->users);
+ pdp_post ("\tclass: %x", h->theclass);
+ }
+}
+
+
+
+/* setup methods */
+
+void
+pdp_packet_setup(void)
+{
+
+ pdp_pool_size = PDP_INITIAL_POOL_SIZE;
+ pdp_pool = (t_pdp **)pdp_alloc(PDP_INITIAL_POOL_SIZE * sizeof(t_pdp *));
+ bzero(pdp_pool, pdp_pool_size * sizeof(t_pdp *));
+ class_list = pdp_list_new(0);
+ pthread_mutex_init(&pdp_pool_mutex, NULL);
+}
+
+/* class methods */
+t_pdp_class *pdp_class_new(t_pdp_symbol *type, t_pdp_factory_method create){
+ t_pdp_class *c = (t_pdp_class *)pdp_alloc(sizeof(t_pdp_class));
+ memset(c, 0, sizeof(t_pdp_class));
+ c->create = create;
+ c->type = type; // set type
+ pdp_list_add(class_list, a_pointer, (t_pdp_word)((void *)c));
+ return c;
+}
+
+/* the packet factory */
+int pdp_factory_newpacket(t_pdp_symbol *type)
+{
+ int p;
+ t_pdp_class *c;
+ t_pdp_atom *a = class_list->first;
+
+ /* try to reuse first
+ THINK: should this be the responsability of the type specific constructors,
+ or should a packet allways be reusable (solution: depends on what the cleanup method returns??)
+ */
+ p = pdp_packet_reuse(type);
+ if (-1 != p) return p;
+
+
+ /* call class constructor */
+ while(a){
+ c = (t_pdp_class *)(a->w.w_pointer);
+ if (c->type && pdp_type_description_match(type, c->type)){
+ //pdp_post("method %x, type %s", c->create, type->s_name);
+ return (c->create) ? (*c->create)(type) : -1;
+ }
+ a = a->next;
+ }
+ return -1;
+}
+
+static void
+_pdp_pool_expand_nolock(void){
+ int i;
+
+ /* double the size */
+ int new_pool_size = pdp_pool_size << 1;
+ t_pdp **new_pool = (t_pdp **)pdp_alloc(new_pool_size * sizeof(t_pdp *));
+ bzero(new_pool, new_pool_size * sizeof(t_pdp *));
+ memcpy(new_pool, pdp_pool, pdp_pool_size * sizeof(t_pdp *));
+ pdp_dealloc(pdp_pool);
+ pdp_pool = new_pool;
+ pdp_pool_size = new_pool_size;
+}
+
+
+
+
+/* private _pdp_packet methods */
+
+/* packets can only be created and destroyed using these 2 methods */
+/* it updates the mem usage and total packet count */
+
+static void
+_pdp_packet_dealloc_nolock(t_pdp *p)
+{
+ /* free memory */
+ pdp_dealloc (p);
+}
+
+static t_pdp*
+_pdp_packet_alloc_nolock(unsigned int datatype, unsigned int datasize)
+{
+ unsigned int totalsize = datasize + PDP_HEADER_SIZE;
+ t_pdp *p = (t_pdp *)pdp_alloc(totalsize);
+ if (p){
+ memset(p, 0, PDP_HEADER_SIZE); //initialize header to 0
+ p->type = datatype;
+ p->size = totalsize;
+ p->users = 1;
+ }
+ return p;
+}
+
+
+/* create a new packet and expand pool if necessary */
+static int
+_pdp_packet_create_nolock(unsigned int datatype, unsigned int datasize)
+{
+ int p = 0;
+ while(1){
+ for (; p < pdp_pool_size; p++){
+ if (!pdp_pool[p]){
+ /* found slot to store packet*/
+ t_pdp *header = _pdp_packet_alloc_nolock(datatype, datasize);
+ if (!header) return -1; // error allocating packet
+ pdp_pool[p] = header;
+ return p;
+ }
+ }
+ /* no slot found, expand pool */
+ _pdp_pool_expand_nolock();
+ }
+}
+
+
+void
+pdp_packet_destroy(void)
+{
+ int i = 0;
+ /* dealloc all the data in object stack */
+ pdp_post("DEBUG: pdp_packet_destroy: clearing object pool.");
+ while ((i < pdp_pool_size) && (pdp_pool[i])) _pdp_packet_dealloc_nolock(pdp_pool[i++]);
+}
+
+
+
+
+
+
+
+
+/* public pool operations: have to be thread safe so each entry point
+ locks the mutex */
+
+
+/* create a new packet.
+ this should only be used by type specific factory methods, and only if the
+ reuse method fails, since it will always create a new packet */
+int
+pdp_packet_create(unsigned int datatype, unsigned int datasize /*without header*/)
+{
+ int packet;
+ LOCK;
+ packet = _pdp_packet_create_nolock(datatype, datasize);
+ UNLOCK;
+ return packet;
+}
+
+
+/* return a new packet.
+ it tries to reuse a packet based on
+ 1. matching data size
+ 2. abscence of destructor (which SHOULD mean there are no enclosed references)
+
+ it obviously can't use the reuse fifo tagged to a symbolic type description
+
+ ALWAYS USE pdp_packet_reuse BEFORE calling pdp_packet_new if possible
+ use both ONLY IN CONSTRUCTORS !!!
+
+ use pdp_packet_factory to create packets as a "user"
+
+ this is a summary of all internal packet creation mechanisms:
+
+ -> pdp_packet_reuse, which uses symbolic type descriptions, and should work for all packet types
+ it returns an initialized container (meta = correct, data = garbage)
+
+ -> pdp_packet_new, which only works for non-pure packets, and reuses packets based on data type
+ it returns a pure packet (meta + data = garbage)
+
+ -> pdp_packet_create, like pdp_packet_new, only it always creates a new packet
+
+
+
+*/
+
+int
+pdp_packet_new(unsigned int datatype, unsigned int datasize)
+{
+ t_pdp *header;
+ int packet;
+ LOCK;
+ for (packet = 0; packet < pdp_pool_size; packet++){
+ header = pdp_pool[packet];
+ /* check data size */
+ if (header
+ && header->users == 0
+ && header->size == datasize + PDP_HEADER_SIZE
+ && !(header->theclass && header->theclass->cleanup)){
+
+ /* ok, got one. initialize */
+ memset(header, 0, PDP_HEADER_SIZE);
+ header->users = 1;
+ header->type = datatype;
+ header->size = datasize + PDP_HEADER_SIZE;
+
+ UNLOCK; //EXIT1
+ return packet;
+ }
+ }
+
+ /* no usable non-pure packet found, create a new one */
+
+ UNLOCK; //EXIT2
+ return pdp_packet_create(datatype, datasize);
+
+
+
+}
+
+
+/* internal method to add a packet to a packet type
+ description symbol's unused packet fifo */
+void
+_pdp_packet_save_nolock(int packet)
+{
+ t_pdp *header = pdp_packet_header(packet);
+ t_pdp_symbol *s;
+ PDP_ASSERT(header);
+ PDP_ASSERT(header->users == 0);
+ PDP_ASSERT(header->desc);
+ s = header->desc;
+ if (!s->s_reusefifo) s->s_reusefifo = pdp_list_new(0);
+ pdp_list_add(s->s_reusefifo, a_packet, (t_pdp_word)packet);
+}
+
+/* this will revive a packet matching a certain type description
+ no wildcards are allowed */
+int
+pdp_packet_reuse(t_pdp_symbol *type_description)
+{
+ int packet = -1;
+ t_pdp *header = 0;
+ t_pdp_list *l = 0;
+ LOCK;
+ if (!type_description || !(l = type_description->s_reusefifo)) goto exit;
+ while(l->elements){
+ packet = pdp_list_pop(l).w_packet;
+ header = pdp_packet_header(packet);
+
+ /* check if reuse fifo is consistent (packet unused + correct type)
+ packet could be deleted and replaced with another one, or
+ revived without the index updated (it's a "hint cache") */
+
+ if (header->users == 0){
+ /* check if type matches */
+ if (pdp_type_description_match(header->desc, type_description)){
+ header->users++; // revive
+ goto exit;
+ }
+ /* if not, add the packet to the correct reuse fifo */
+ else{
+ _pdp_packet_save_nolock(packet);
+ }
+ }
+
+ /* remove dangling refs */
+ header = 0;
+ packet = -1;
+ }
+
+ exit:
+ UNLOCK;
+ if (header && header->theclass && header->theclass->wakeup){
+ header->theclass->wakeup(header); // revive if necessary
+ }
+ return packet;
+}
+
+/* find all unused packets in pool, marked as used (to protect from other reapers)
+ and return them as a list. non-pure packets are not revived */
+
+
+
+
+
+/* this returns a copy of a packet for read only access.
+ (increases refcount of the packet -> packet will become readonly if it was
+ writable, i.e. had rc=1 */
+
+int
+pdp_packet_copy_ro(int handle)
+{
+ t_pdp* header;
+
+ if (header = pdp_packet_header(handle)){
+ PDP_ASSERT(header->users); // consistency check
+ LOCK;
+ header->users++; // increment reference count
+ UNLOCK;
+ }
+ else handle = -1;
+ return handle;
+}
+
+/* clone a packet: create a new packet with the same
+ type as the source packet */
+
+int
+pdp_packet_clone_rw(int handle)
+{
+ t_pdp* header;
+ int new_handle = -1;
+
+
+ if (header = pdp_packet_header(handle)){
+ /* consistency checks */
+ PDP_ASSERT(header->users);
+ PDP_ASSERT(header->desc);
+
+ /* first try to reuse old packet */
+ new_handle = pdp_packet_reuse(header->desc);
+
+ /* if this failed, create a new one using the central packet factory method */
+ if (-1 == new_handle) new_handle = pdp_factory_newpacket(header->desc);
+ }
+
+ return new_handle;
+}
+
+/* return a copy of a packet (clone + copy data) */
+int
+pdp_packet_copy_rw(int handle)
+{
+ t_pdp *header, *new_header;
+ int new_handle = -1;
+
+ if (!(header = pdp_packet_header(handle))) return -1;
+
+ /* check if we are allowed to copy */
+ if (header->flags & PDP_FLAG_DONOTCOPY) return -1;
+
+ /* get target packet */
+ new_handle = pdp_packet_clone_rw(handle);
+ if (-1 == new_handle) return -1;
+ new_header = pdp_packet_header(new_handle);
+
+ /* if there is a copy method, use that one */
+ if (header->theclass && header->theclass->copy){
+ header->theclass->copy(header, new_header);
+ }
+
+ /* otherwize copy the data verbatim */
+ else {
+ memcpy(pdp_packet_data(new_handle),
+ pdp_packet_data(handle),
+ pdp_packet_data_size(handle));
+ }
+
+ return new_handle;
+
+}
+
+
+/* decrement refcount */
+void pdp_packet_mark_unused(int handle)
+{
+ t_pdp *header;
+ if (!(header = pdp_packet_header(handle))) return;
+
+ PDP_ASSERT(header->users); // consistency check
+
+ LOCK;
+
+ /* just decrement refcount */
+ if (header->users > 1){
+ header->users--;
+ }
+
+ /* put packet to sleep if refcount 1->0 */
+ else {
+ if (header->theclass && header->theclass->sleep){
+ /* call sleep method (if any) outside of lock
+ while the packet is still alive, so it won't be
+ acclaimed by another thread */
+ UNLOCK;
+ header->theclass->sleep(header);
+ LOCK;
+ }
+ /* clear refcount & save in fifo for later use */
+ header->users = 0;
+ if (header->desc) // sleep could have destructed packet..
+ _pdp_packet_save_nolock(handle);
+ }
+
+ UNLOCK;
+}
+
+
+
+/* delete a packet. rc needs to be == 1 */
+void pdp_packet_delete(int handle)
+{
+ t_pdp *header;
+ header = pdp_packet_header(handle);
+ PDP_ASSERT(header);
+ PDP_ASSERT(header->users == 1); // consistency check
+
+ LOCK;
+
+ if (header->theclass && header->theclass->cleanup){
+ /* call cleanup method (if any) outside of lock
+ while the packet is still alive, so it won't be
+ acclaimed by another thread */
+ UNLOCK;
+ header->theclass->cleanup(header);
+ LOCK;
+ }
+
+ /* delete the packet */
+ pdp_pool[handle] = 0;
+ _pdp_packet_dealloc_nolock(header);
+
+
+ UNLOCK;
+}
+
+
+
+
+
+
+
+/* public data access methods */
+
+t_pdp*
+pdp_packet_header(int handle)
+{
+ if ((handle >= 0) && (handle < pdp_pool_size)) return pdp_pool[handle];
+ else return 0;
+}
+
+void*
+pdp_packet_subheader(int handle)
+{
+ t_pdp* header = pdp_packet_header(handle);
+ if (!header) return 0;
+ return (void *)(&header->info.raw);
+}
+
+void*
+pdp_packet_data(int handle)
+{
+ t_pdp *h;
+ if ((handle >= 0) && (handle < pdp_pool_size))
+ {
+ h = pdp_pool[handle];
+ if (!h) return 0;
+ return (char *)(h) + PDP_HEADER_SIZE;
+ }
+ else return 0;
+}
+
+int
+pdp_packet_data_size(int handle)
+{
+ t_pdp *h;
+ if ((handle >= 0) && (handle < pdp_pool_size))
+ {
+ h = pdp_pool[handle];
+ if (!h) return 0;
+ return h->size - PDP_HEADER_SIZE;
+ }
+ else return 0;
+}
+
+
+
+
+int pdp_packet_writable(int packet) /* returns true if packet is writable */
+{
+ t_pdp *h = pdp_packet_header(packet);
+ if (!h) return 0;
+ return (h->users == 1);
+}
+
+void pdp_packet_replace_with_writable(int *packet) /* replaces a packet with a writable copy */
+{
+ int new_p;
+ if (!pdp_packet_writable(*packet)){
+ new_p = pdp_packet_copy_rw(*packet);
+ pdp_packet_mark_unused(*packet);
+ *packet = new_p;
+ }
+
+}
+
+/* pool stuff */
+
+int
+pdp_pool_collect_garbage(void)
+{
+ pdp_post("ERROR: garbage collector not implemented");
+ return 0;
+}
+
+void
+pdp_pool_set_max_mem_usage(int max)
+{
+ pdp_post("ERROR: mem limit not implemented");
+}
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/system/kernel/pdp_post.c b/system/kernel/pdp_post.c
new file mode 100644
index 0000000..fb761d0
--- /dev/null
+++ b/system/kernel/pdp_post.c
@@ -0,0 +1,48 @@
+
+/*
+ * Pure Data Packet system file. pdp logging.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include "pdp_post.h"
+
+/* list printing should be moved here too */
+
+/* write a message to log (console) */
+void pdp_post_n(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+void pdp_post(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ putc('\n', stderr);
+}
+
+
diff --git a/system/kernel/pdp_symbol.c b/system/kernel/pdp_symbol.c
new file mode 100644
index 0000000..32e9e33
--- /dev/null
+++ b/system/kernel/pdp_symbol.c
@@ -0,0 +1,196 @@
+/*
+ * Pure Data Packet system implementation. : code implementing pdp's namespace (symbols)
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <string.h>
+#include <pthread.h>
+#include "pdp_symbol.h"
+#include "pdp_list.h"
+#include "pdp_debug.h"
+
+// some extra prototypes
+void *pdp_alloc(int size);
+void pdp_dealloc(void *data);
+
+// the symbol hash mutex
+static pthread_mutex_t pdp_hash_mutex;
+
+#define HASHSIZE 1024
+static t_pdp_symbol *pdp_symhash[HASHSIZE];
+
+
+#define LOCK pthread_mutex_lock(&pdp_hash_mutex)
+#define UNLOCK pthread_mutex_unlock(&pdp_hash_mutex)
+
+
+static void _pdp_symbol_init(t_pdp_symbol *s)
+{
+ memset(s, 0, sizeof(*s));
+ s->s_forth.t = a_undef;
+}
+
+
+/* shamelessly copied from pd src and made thread safe */
+t_pdp_symbol *_pdp_dogensym(char *s, t_pdp_symbol *oldsym)
+{
+ t_pdp_symbol **sym1, *sym2;
+ unsigned int hash1 = 0, hash2 = 0;
+ int length = 0;
+ char *s2 = s;
+ while (*s2)
+ {
+ hash1 += *s2;
+ hash2 += hash1;
+ length++;
+ s2++;
+ }
+ sym1 = pdp_symhash + (hash2 & (HASHSIZE-1));
+
+ /* lock hash */
+ LOCK;
+
+ while (sym2 = *sym1)
+ {
+ if (!strcmp(sym2->s_name, s)) goto gotit;
+ sym1 = &sym2->s_next;
+ }
+ if (oldsym){
+ sym2 = oldsym;
+ }
+ else
+ {
+ sym2 = (t_pdp_symbol *)pdp_alloc(sizeof(*sym2));
+ _pdp_symbol_init(sym2);
+ sym2->s_name = pdp_alloc(length+1);
+ sym2->s_next = 0;
+ strcpy(sym2->s_name, s);
+ }
+ *sym1 = sym2;
+
+ gotit:
+
+ /* unlock hash */
+ UNLOCK;
+ return (sym2);
+}
+
+t_pdp_symbol *pdp_gensym(char *s)
+{
+ return(_pdp_dogensym(s, 0));
+}
+
+
+/* connect a parsed typelist to a symbol type name
+ 1 = succes, 0 = error (symbol already connected) */
+int pdp_symbol_set_typelist(t_pdp_symbol *s, t_pdp_list *typelist)
+{
+ int status = 0;
+ LOCK;
+ if (!s->s_type){
+ s->s_type = typelist;
+ status = 1;
+ }
+ UNLOCK;
+ return status;
+}
+
+
+void pdp_symbol_apply_all(t_pdp_symbol_iterator it)
+{
+ int i;
+ for (i=0; i<HASHSIZE; i++){
+ t_pdp_symbol *s;
+ for (s = pdp_symhash[i]; s; s=s->s_next){
+ it(s);
+ }
+
+ }
+}
+
+t_pdp_symbol _pdp_sym_wildcard;
+t_pdp_symbol _pdp_sym_float;
+t_pdp_symbol _pdp_sym_int;
+t_pdp_symbol _pdp_sym_symbol;
+t_pdp_symbol _pdp_sym_packet;
+t_pdp_symbol _pdp_sym_pointer;
+t_pdp_symbol _pdp_sym_invalid;
+t_pdp_symbol _pdp_sym_list;
+t_pdp_symbol _pdp_sym_question_mark;
+t_pdp_symbol _pdp_sym_atom;
+t_pdp_symbol _pdp_sym_null;
+t_pdp_symbol _pdp_sym_quote_start;
+t_pdp_symbol _pdp_sym_quote_end;
+t_pdp_symbol _pdp_sym_return;
+t_pdp_symbol _pdp_sym_nreturn;
+t_pdp_symbol _pdp_sym_defstart;
+t_pdp_symbol _pdp_sym_defend;
+t_pdp_symbol _pdp_sym_if;
+t_pdp_symbol _pdp_sym_then;
+t_pdp_symbol _pdp_sym_local;
+t_pdp_symbol _pdp_sym_forth;
+t_pdp_symbol _pdp_sym_call;
+t_pdp_symbol _pdp_sym_push;
+t_pdp_symbol _pdp_sym_pop;
+
+static void _sym(char *name, t_pdp_symbol *s)
+{
+ t_pdp_symbol *realsym;
+ _pdp_symbol_init(s);
+ s->s_name = name;
+ realsym = _pdp_dogensym(name, s);
+ PDP_ASSERT(realsym == s); // if this fails, the symbol was already defined
+}
+
+void pdp_symbol_setup(void)
+{
+ // create mutexes
+ pthread_mutex_init(&pdp_hash_mutex, NULL);
+
+ // init symbol hash
+ memset(pdp_symhash, 0, HASHSIZE * sizeof(t_pdp_symbol *));
+
+ // setup predefined symbols (those that have direct pointer access for speedup)
+ _sym("*", &_pdp_sym_wildcard);
+ _sym("float", &_pdp_sym_float);
+ _sym("int", &_pdp_sym_int);
+ _sym("symbol", &_pdp_sym_symbol);
+ _sym("packet", &_pdp_sym_packet);
+ _sym("pointer", &_pdp_sym_pointer);
+ _sym("invalid", &_pdp_sym_invalid);
+ _sym("list", &_pdp_sym_list);
+ _sym("?", &_pdp_sym_question_mark);
+ _sym("atom", &_pdp_sym_atom);
+ _sym("null", &_pdp_sym_null);
+ _sym("[", &_pdp_sym_quote_start);
+ _sym("]", &_pdp_sym_quote_end);
+ _sym("ret", &_pdp_sym_return);
+ _sym("nret", &_pdp_sym_nreturn);
+ _sym(":", &_pdp_sym_defstart);
+ _sym(";", &_pdp_sym_defend);
+ _sym("if", &_pdp_sym_if);
+ _sym("then", &_pdp_sym_then);
+ _sym("local", &_pdp_sym_local);
+ _sym("forth", &_pdp_sym_forth);
+ _sym("call", &_pdp_sym_call);
+ _sym("push", &_pdp_sym_push);
+ _sym("pop", &_pdp_sym_pop);
+
+}
+
+
diff --git a/system/net/Makefile b/system/net/Makefile
new file mode 100644
index 0000000..53d1d61
--- /dev/null
+++ b/system/net/Makefile
@@ -0,0 +1,11 @@
+
+OBJECTS = pdp_net.o
+
+
+include ../../Makefile.config
+
+all: $(OBJECTS)
+
+clean:
+ rm -f *~
+ rm -f *.o
diff --git a/system/net/pdp_net.c b/system/net/pdp_net.c
new file mode 100644
index 0000000..04c97d6
--- /dev/null
+++ b/system/net/pdp_net.c
@@ -0,0 +1,685 @@
+
+#include "pdp_net.h"
+#include "pdp_debug.h"
+#include "pdp_post.h"
+#include "pdp_mem.h"
+
+#define D if (0) // DEBUG MSG
+#define DD if (0) // DROP DEBUG MSG
+
+/* shared internals */
+
+static int _is_udp_header(t_pdp_udp_header *header, unsigned int size)
+{
+ if (size < sizeof(t_pdp_udp_header)) return 0;
+ if (strcmp(header->signature, "PDP")) return 0;
+ if (PDP_UDP_VERSION != header->version) return 0;
+ return 1;
+}
+
+static void _make_udp_header(t_pdp_udp_header *header)
+{
+ strcpy(header->signature, "PDP");
+ header->version = PDP_UDP_VERSION;
+}
+
+
+
+
+/* R E C E I V E R */
+
+
+/* INTERNALS */
+
+static void _send_packet(t_pdp_udp_receiver *x)
+{
+ _make_udp_header(&x->x_resend_header);
+ PDP_ASSERT(x->x_resend_udp_packet_size <= sizeof(t_pdp_udp_header) + sizeof(x->x_resend_chunks));
+
+ /* send the packet */
+ if (-1 == sendto (x->x_socket, &x->x_resend_header, x->x_resend_udp_packet_size, 0,
+ (struct sockaddr *)&x->x_source_socket, x->x_sslen)){
+ pdp_post("pdp_netreceive: send failed");
+ }
+}
+
+static void _send_ack_new(t_pdp_udp_receiver *x)
+{
+ /* setup resend header */
+ x->x_resend_header.connection_id = x->x_connection_id;
+ x->x_resend_header.sequence_number = PDP_UDP_ACKNEW;
+ x->x_resend_udp_packet_size = sizeof(t_pdp_udp_header);
+
+ _send_packet(x);
+
+}
+
+
+static int _handle_PDP_UDP_NEW(t_pdp_udp_receiver *x)
+{
+ /* we've got a PDP_UDP_NEW packet, so prepare to receive the data */
+ t_pdp_udp_newpacket *np = (t_pdp_udp_newpacket *)x->x_buf;
+
+
+ //pdp_post("conn_id = %x", x->x_header.connection_id);
+ //pdp_post("size = %d", np->data_size);
+ //pdp_post("nb_chunks = %d", np->nb_chunks);
+ //pdp_post("chunk_size = %d", np->chunk_size);
+ //pdp_post("type = %s", np->type);
+
+ /* check if it is a resend of the PDP_UDP_NEW packet (if NEW_ACK didn't get through)
+ if not, prepare for reception */
+
+ if (x->x_connection_id != x->x_header.connection_id){
+
+
+ /* prepare for reception : TODO add some more checks here */
+
+ // setup type info
+ if (x->x_data_type) pdp_dealloc (x->x_data_type);
+ x->x_data_type = pdp_alloc(1 + strlen(np->type));
+ strcpy(x->x_data_type, np->type);
+
+ // setup data buffer
+ x->x_data_size = np->data_size;
+ if (x->x_data) pdp_dealloc (x->x_data);
+ x->x_data = pdp_alloc(x->x_data_size);
+ memset(x->x_data, 0, x->x_data_size); // clear for debug
+
+ // setup connection info
+ x->x_connection_id = x->x_header.connection_id;
+ x->x_nb_chunks = np->nb_chunks;
+ x->x_chunk_size = np->chunk_size;
+
+ /* setup chunk list */
+ if (x->x_chunk_list) pdp_dealloc(x->x_chunk_list);
+ x->x_chunk_list = pdp_alloc(sizeof(unsigned int)*x->x_nb_chunks);
+ memset(x->x_chunk_list, 0, sizeof(unsigned int)*x->x_nb_chunks);
+
+ x->x_receive_finished = 0; // we're in a receiving state
+ x->x_packet_transferred = 0; // we didn't pass the packet yet
+ }
+
+ /* send ACK */
+ _send_ack_new(x);
+
+
+
+ return 1;
+}
+
+static void _handle_PDP_UDP_DONE(t_pdp_udp_receiver *x)
+{
+ unsigned int chunk;
+ unsigned int missing;
+ unsigned int i;
+ unsigned int resend_packet_size;
+
+
+ /* check the connection id */
+ if (x->x_connection_id != x->x_header.connection_id) return;
+
+ /* determine how many packets are missing */
+ missing = 0;
+ for (i=0; i<x->x_nb_chunks; i++)
+ if (!x->x_chunk_list[i]) missing++;
+
+ D pdp_post ("last packet %x had %d/%d dropped chunks", x->x_connection_id, missing, x->x_nb_chunks);
+
+
+ /* build the resend request (chunk list )*/
+ if (missing > RESEND_MAX_CHUNKS) missing = RESEND_MAX_CHUNKS;
+ chunk = 0;
+ i = missing;
+ while(i--){
+ while (x->x_chunk_list[chunk]) chunk++; // find next missing chunk
+ x->x_resend_chunks[i] = chunk++; // store it in list
+ }
+
+ /* set the packet size to include the list */
+ x->x_resend_udp_packet_size = sizeof(t_pdp_udp_header)
+ + missing * sizeof(unsigned int);
+
+ /* setup resend header */
+ strcpy((char *)&x->x_resend_header, "PDP");
+ x->x_resend_header.version = PDP_UDP_VERSION;
+ x->x_resend_header.connection_id = x->x_connection_id;
+ x->x_resend_header.sequence_number = PDP_UDP_RESEND;
+
+ D pdp_post("pdp_netreceive: sending RESEND response for %u chunks", missing);
+
+ /* send out */
+ _send_packet(x);
+
+ /* indicate we're done if there's no chunks missing */
+ if (!missing) x->x_receive_finished = 1;
+
+}
+
+
+static int _handle_UDP_DATA(t_pdp_udp_receiver *x)
+{
+ unsigned int seq = x->x_header.sequence_number;
+ unsigned int offset = x->x_chunk_size * seq;
+
+ /* ignore the packet if we're not expecting it */
+ if ((!x->x_connection_id) || (x->x_connection_id != x->x_header.connection_id)){
+ //pdp_post("pdp_netreceive: got invalid data packet: transmission id %x is not part of current transmisson %x",
+ // x->x_header.connection_id, x->x_connection_id);
+ return 0;
+ }
+
+ /* check if it is valid */
+ if (seq >= x->x_nb_chunks){
+ pdp_post("pdp_netreceive: got invalid data packet: sequence number %u out of bound (nb_chunks=%u)",
+ seq, x->x_nb_chunks);
+ return 0;
+ }
+
+ /* final check */
+ PDP_ASSERT(offset + x->x_buf_size <= x->x_data_size);
+
+ /* write & log it */
+ memcpy(x->x_data + offset, x->x_buf, x->x_buf_size);
+ x->x_chunk_list[seq] = 1;
+ return 1;
+
+}
+
+/* INTERFACE */
+
+/* setup */
+t_pdp_udp_receiver *pdp_udp_receiver_new(int port)
+{
+ t_pdp_udp_receiver *x = pdp_alloc(sizeof(*x));
+ memset(x, 0, sizeof(*x));
+
+ /* init */
+ x->x_data = 0;
+ x->x_data_type = 0;
+ x->x_data_size = 0;
+ x->x_chunk_list = 0;
+ x->x_receive_finished = 0;
+ x->x_packet_transferred = 0;
+ x->x_zero_terminator = 0;
+
+ x->x_socket = socket(PF_INET, SOCK_DGRAM, 0);
+ x->x_connection_id = 0; /* zero for bootstrap (0 == an invalid id) */
+ x->x_sslen = sizeof(struct sockaddr_in);
+
+ /* bind socket */
+ x->x_sa.sin_port = htons(port);
+ x->x_sa.sin_addr.s_addr = 0;
+ if (-1 != bind (x->x_socket, (struct sockaddr *)&x->x_sa,
+ sizeof(struct sockaddr_in))) return x;
+
+ /* suicide if find failed */
+ else {
+ pdp_dealloc(x);
+ return 0;
+ }
+}
+void pdp_udp_receiver_free(t_pdp_udp_receiver *x)
+{
+ if (!x) return;
+ if (x->x_socket != 1) close (x->x_socket);
+ if (x->x_data) pdp_dealloc(x->x_data);
+ if (x->x_data_type) pdp_dealloc (x->x_data_type);
+ if (x->x_chunk_list) pdp_dealloc (x->x_chunk_list);
+}
+
+void pdp_udp_receiver_reset(t_pdp_udp_receiver *x)
+{
+ x->x_connection_id = 0;
+}
+
+
+/* receive loop, returns 1 on success, -1 on error, 0 on timeout */
+int pdp_udp_receiver_receive(t_pdp_udp_receiver *x, unsigned int timeout_ms)
+{
+ /* listen for packets */
+
+ unsigned int size;
+ struct timeval tv = {0,1000 * timeout_ms};
+ fd_set inset;
+ FD_ZERO(&inset);
+ FD_SET(x->x_socket, &inset);
+ switch(select (x->x_socket+1, &inset, NULL, NULL, &tv)){
+ case -1:
+ return -1; /* select error */
+ case 0:
+ return 0; /* select time out */
+ default:
+ break; /* data ready */
+ }
+
+ /* this won't block, since there's data available */
+ if (-1 == (int)(size = recvfrom(x->x_socket, (void *)&x->x_header,
+ PDP_UDP_BUFSIZE+sizeof(x->x_header), 0,
+ (struct sockaddr *)&x->x_source_socket, &x->x_sslen))) return -1;
+
+ /* store the data size of the packet */
+ x->x_buf_size = size - sizeof(t_pdp_udp_header);
+
+ /* parse the udp packet */
+ if (_is_udp_header(&x->x_header, size)){
+
+ /* it is a control packet */
+ if ((int)x->x_header.sequence_number < 0){
+
+ switch (x->x_header.sequence_number){
+ case PDP_UDP_NEW:
+ _handle_PDP_UDP_NEW(x);
+ break;
+
+ case PDP_UDP_DONE:
+ _handle_PDP_UDP_DONE(x);
+
+ /* check if we got a complete packet
+ and signal arrival if we haven't done this already */
+ if (x->x_receive_finished && !x->x_packet_transferred){
+ x->x_packet_transferred = 1;
+ return 1; // data complete, please receive
+ }
+ break;
+
+ default:
+ pdp_post("got unknown msg");
+ break;
+ }
+ }
+
+ /* it is a data packet */
+ else {
+ _handle_UDP_DATA(x);
+ }
+
+
+ }
+
+ else {
+ pdp_post("pdp_netreceive: got invalid UDP packet (size = %d)", size);
+ }
+
+ return 0; //no major event, please poll again
+
+}
+
+/* get meta & data */
+char *pdp_udp_receiver_type(t_pdp_udp_receiver *x){return x->x_data_type;}
+unsigned int pdp_udp_receiver_size(t_pdp_udp_receiver *x){return x->x_data_size;}
+void *pdp_udp_receiver_data(t_pdp_udp_receiver *x){return x->x_data;}
+
+
+/* S E N D E R */
+
+/* INTERNALS */
+
+static void _sleep(t_pdp_udp_sender *x)
+{
+ int sleep_period = x->x_sleep_period;
+
+ if (sleep_period) {
+ if (!x->x_sleep_count++) usleep(x->x_sleepgrain_us);
+ x->x_sleep_count %= sleep_period;
+ }
+}
+
+static void _send(t_pdp_udp_sender *x)
+{
+ //post("sending %u data bytes", x->x_buf_size);
+
+ _make_udp_header(&x->x_header);
+
+ PDP_ASSERT (x->x_buf_size <= PDP_UDP_BUFSIZE);
+
+ if (-1 == sendto (x->x_socket, &x->x_header, x->x_buf_size + sizeof(t_pdp_udp_header),
+ 0, (struct sockaddr *)&x->x_sa, sizeof(struct sockaddr_in)))
+ pdp_post("pdp_netsend: send FAILED");
+
+ _sleep(x);
+
+}
+
+
+static void _prepare_for_new_transmission(t_pdp_udp_sender *x, char *type, unsigned int size, void *data)
+{
+ unsigned int i;
+
+ /* setup data for transmission */
+ x->x_data_type = type;
+ x->x_data_size = size;
+ x->x_data = data;
+ x->x_chunk_size = x->x_udp_payload_size;
+ x->x_nb_chunks = (x->x_data_size - 1) / x->x_chunk_size + 1;
+
+ /* generate a connection id (non-zero) */
+ while (!(x->x_connection_id = rand()));
+
+ /* setup chunk list to contain all chunks */
+ if (x->x_chunk_list) free (x->x_chunk_list);
+ x->x_chunk_list_size = x->x_nb_chunks;
+ x->x_chunk_list = malloc(sizeof(unsigned int)*x->x_chunk_list_size);
+ for (i=0; i<x->x_chunk_list_size; i++) x->x_chunk_list[i] = i;
+
+}
+
+static void _send_header_packet(t_pdp_udp_sender *x)
+{
+ t_pdp_udp_newpacket *np = (t_pdp_udp_newpacket *)x->x_buf; /* buf contains the PDP_UDP_NEW body */
+
+ /* init packet */
+ x->x_header.sequence_number = PDP_UDP_NEW;
+ x->x_header.connection_id = x->x_connection_id;
+ np->data_size = x->x_data_size;
+ np->nb_chunks = x->x_nb_chunks;
+ np->chunk_size = x->x_chunk_size;
+ strcpy(np->type, x->x_data_type);
+ x->x_buf_size = sizeof(*np) + strlen(np->type) + 1;
+ PDP_ASSERT(x->x_buf_size <= PDP_UDP_BUFSIZE);
+
+ /* send the packet */
+ _send(x);
+}
+
+/* saend the chunks in the chunk list */
+static void _send_chunks(t_pdp_udp_sender *x){
+ unsigned int i;
+ unsigned int count = 0;
+
+ /* send chunks: this requires header is setup ok (sig,ver,connid)*/
+ for (i=0; i<x->x_chunk_list_size; i++){
+ unsigned int offset;
+ unsigned int current_chunk_size;
+ unsigned int seq = x->x_chunk_list[i];
+
+ PDP_ASSERT(seq < x->x_nb_chunks);
+ x->x_header.sequence_number = seq; // store chunk number
+
+ /* get current chunk offset */
+ offset = seq * x->x_chunk_size;
+ PDP_ASSERT(offset < x->x_data_size);
+
+
+ /* get current chunk size */
+ current_chunk_size = (offset + x->x_chunk_size > x->x_data_size) ?
+ (x->x_data_size - offset) : x->x_chunk_size;
+ x->x_buf_size = current_chunk_size;
+ PDP_ASSERT(x->x_buf_size <= PDP_UDP_BUFSIZE);
+
+ /* copy chunk to transmission buffer & send */
+ PDP_ASSERT(offset + current_chunk_size <= x->x_data_size);
+ memcpy(x->x_buf, x->x_data + offset, current_chunk_size);
+
+
+ /* send the chunk */
+ _send(x);
+ count++;
+
+ }
+ D pdp_post("sent %d chunks, id=%x", count,x->x_connection_id);
+}
+
+/* send a DONE packet */
+static void _send_done(t_pdp_udp_sender *x){
+ x->x_header.sequence_number = PDP_UDP_DONE;
+ x->x_buf_size = 0;
+ _send(x);
+}
+static int _receive_packet(t_pdp_udp_sender *x, int desired_type)
+/* 0 == timeout, -1 == error, 1 == got packet */
+{
+ unsigned int size;
+ int type;
+
+ struct timeval tv;
+ fd_set inset;
+ int sr;
+
+
+ while (1){
+ int retval;
+
+ /* wait for incoming */
+ tv.tv_sec = 0;
+ tv.tv_usec = x->x_timeout_us;
+ FD_ZERO(&inset);
+ FD_SET(x->x_socket, &inset);
+ switch (select (x->x_socket+1, &inset, NULL, NULL, &tv)){
+ case -1:
+ return -1; /* select error */
+ case 0:
+ return 0; /* select time out */
+ default:
+ break; /* data ready */
+ }
+
+ /* read packet */
+ if (-1 == (int)(size = recv(x->x_socket, (void *)&x->x_resend_header, MAX_UDP_PACKET, 0))){
+ pdp_post("pdp_netsend: error while reading from socket");
+ return -1;
+ }
+
+ /* check if it is a valid PDP_UDP packet */
+ if (!_is_udp_header(&x->x_resend_header, size)){
+ pdp_post("pdp_netsend: ignoring invalid UDP packet (size = %u)", size);
+ continue;
+ }
+
+
+ /* check connection id */
+ if (x->x_connection_id != x->x_resend_header.connection_id){
+ D pdp_post("pdp_netsend: ignoring ghost packet id=%x, current id=%x",
+ x->x_resend_header.connection_id, x->x_connection_id);
+ continue;
+ }
+
+ /* check type */
+ type = x->x_resend_header.sequence_number;
+ if (type != desired_type) continue;
+
+
+ /* setup data buffer for known packets */
+ switch(type){
+ case PDP_UDP_RESEND:
+ x->x_resend_items = (size - sizeof(t_pdp_udp_header)) / sizeof(unsigned int);
+ break;
+ default:
+ break;
+ }
+
+ return 1;
+ }
+
+}
+
+/* get the resend list */
+static int _need_resend(t_pdp_udp_sender *x) {
+
+ int retries = 3;
+ int retval;
+ while (retries--){
+
+ /* send a DONE msg */
+ _send_done(x);
+
+ /* wait for ACK */
+ switch(_receive_packet(x, PDP_UDP_RESEND)){
+ case 0:
+ /* timeout, retry */
+ continue;
+ case -1:
+ /* error */
+ goto move_on;
+
+ default:
+ /* got PDP_UDP_RESEND packet: setup resend list */
+ if (x->x_resend_items > x->x_nb_chunks){
+ pdp_post("pdp_netsend: WARNING: chunk list size (%d) is too big, ignoring RESEND request",
+ x->x_resend_items);
+ x->x_resend_items = 0;
+ continue;
+ }
+ x->x_chunk_list_size = x->x_resend_items;
+
+ memcpy(x->x_chunk_list, x->x_resend_chunks, sizeof(unsigned int) * x->x_resend_items);
+ D pdp_post("got RESEND request for %d chunks (id %x)", x->x_resend_items,x->x_connection_id);
+
+ return x->x_chunk_list_size > 0;
+ }
+
+ }
+
+ /* timeout */
+ move_on:
+ x->x_chunk_list_size = 0;
+ return 0;
+
+
+}
+
+
+/* INTERFACE */
+
+
+/* some flow control hacks */
+
+void pdp_udp_sender_timeout_us(t_pdp_udp_sender *x, unsigned int timeout_us)
+{
+ x->x_timeout_us = timeout_us;
+}
+
+
+void pdp_udp_sender_sleepgrain_us(t_pdp_udp_sender *x, unsigned int sleepgrain_us)
+{
+ x->x_sleepgrain_us = sleepgrain_us;
+}
+
+void pdp_udp_sender_sleepperiod(t_pdp_udp_sender *x, unsigned int sleepperiod)
+{
+ x->x_sleep_period = sleepperiod;
+}
+
+
+void pdp_udp_sender_udp_packet_size(t_pdp_udp_sender *x, unsigned int udp_packet_size)
+{
+ int i = (int)udp_packet_size - sizeof(t_pdp_udp_header);
+ if (i < 1024) i = 1024;
+ if (i > PDP_UDP_BUFSIZE) i = PDP_UDP_BUFSIZE;
+ x->x_udp_payload_size = i;
+}
+
+void pdp_udp_sender_connect(t_pdp_udp_sender *x, char *host, unsigned int port)
+{
+ struct hostent *hp;
+
+ hp = gethostbyname(host);
+ if (!hp){
+ pdp_post("pdp_udp_sender: host %s not found", host);
+ }
+ else{
+ /* host ok, setup address */
+ x->x_sa.sin_family = AF_INET;
+ x->x_sa.sin_port = htons(port);
+ memcpy((char *)&x->x_sa.sin_addr, (char *)hp->h_addr, hp->h_length);
+
+ /* create the a socket if necessary */
+ if (x->x_socket == -1){
+ if (-1 == (x->x_socket = socket(PF_INET, SOCK_DGRAM, 0))){
+ pdp_post("pdp_udp_sender: can't create socket");
+ }
+ if (1){
+ int on = 1;
+ if (setsockopt(x->x_socket,SOL_SOCKET,SO_BROADCAST,(char *)&on,sizeof(on))<0)
+ pdp_post("pdp_udp_sender: can't set broadcast flag");
+ }
+ }
+ }
+}
+
+/* setup */
+t_pdp_udp_sender *pdp_udp_sender_new(void)
+{
+ t_pdp_udp_sender *x = pdp_alloc(sizeof(*x));
+ memset(x,0,sizeof(*x));
+
+ x->x_chunk_list = 0;
+
+ /* no connection */
+ x->x_socket = -1;
+
+
+ /* set flow control */
+ pdp_udp_sender_timeout_us(x, 50000);
+ x->x_sleep_count = 0;
+ pdp_udp_sender_sleepgrain_us(x, 0);
+ pdp_udp_sender_sleepperiod(x, 50);
+ pdp_udp_sender_udp_packet_size(x, 1472); //optimal udp packet size (ip: 1500 = 28 + 1472)
+
+
+ return x;
+}
+
+void pdp_udp_sender_free(t_pdp_udp_sender *x)
+{
+ int i;
+ void* retval;
+ if (x->x_socket != -1) close(x->x_socket);
+ if (x->x_chunk_list) free (x->x_chunk_list);
+}
+
+/* send, returns 1 on success, 0 on error */
+int pdp_udp_sender_send(t_pdp_udp_sender *x, char* type, unsigned int size, void *data)
+{
+
+ /* SEND A PACKET */
+
+ /* get the type and data from caller */
+ /* send header packet and make sure it has arrived */
+ /* send a chunk burst */
+ /* send done packet and get the resend list */
+ /* repeat until send list is empty */
+
+
+ int hs_retry = 5; // retry count for initial handshake
+ int rs_retry = 5; // retry count for resends
+
+ /* check if we have a target */
+ if (-1 == x->x_socket) goto transerror;
+
+ /* setup internal state */
+ _prepare_for_new_transmission(x,type,size,data);
+
+ /* handshake a new transmission */
+ do {
+ if (!(hs_retry--)) break;
+ // pdp_post("handshake retry %d for packet %x", hscount, x->x_connection_id);
+ _send_header_packet(x);
+ } while (!_receive_packet(x, PDP_UDP_ACKNEW));
+
+
+ /* exit if no handshake was possible */
+ if (hs_retry < 0){
+ DD pdp_post("pdp_netsend: DROP: receiver does not accept new transmission");
+ goto transerror;
+ }
+
+ /* transmission loop */
+ do {
+ if (!(rs_retry--)) break;
+ _send_chunks(x);
+ } while (_need_resend(x));
+
+ /* exit if transmission was not successful */
+ if (rs_retry < 0){
+ DD pdp_post("pdp_netsend: DROP: receiver did not confirm reception");
+ goto transerror;
+ }
+
+ /* send successful */
+ return 1;
+
+ transerror:
+ /* transmission error */
+ return 0;
+}