diff options
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; +} |