aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/help-pdp_background.pd41
-rw-r--r--doc/help-pdp_binary.pd97
-rw-r--r--doc/help-pdp_cropper.pd64
-rw-r--r--doc/help-pdp_dilate.pd91
-rw-r--r--doc/help-pdp_disintegration.pd61
-rw-r--r--doc/help-pdp_distance.pd72
-rw-r--r--doc/help-pdp_erode.pd91
-rw-r--r--doc/help-pdp_hitandmiss.pd97
-rw-r--r--doc/help-pdp_theorin~.pd14
-rw-r--r--doc/help-pdp_theorout~.pd118
-rw-r--r--doc/rs_pdp_theorin~.pd115
-rw-r--r--modules/pdp_background.c188
-rw-r--r--modules/pdp_binary.c364
-rw-r--r--modules/pdp_cropper.c291
-rw-r--r--modules/pdp_dilate.c281
-rw-r--r--modules/pdp_disintegration.c280
-rw-r--r--modules/pdp_distance.c337
-rw-r--r--modules/pdp_erode.c281
-rw-r--r--modules/pdp_hitandmiss.c386
-rw-r--r--modules/pdp_theorin~.c970
-rw-r--r--modules/pdp_theorout~.c884
-rw-r--r--patches/cutandpaste.pd114
-rw-r--r--patches/morphology/help-closing.pd98
-rw-r--r--patches/morphology/help-opening.pd98
-rw-r--r--patches/morphology/help-skeletization.pd113
-rw-r--r--patches/morphology/help-thickening.pd100
-rw-r--r--patches/morphology/help-thinning.pd95
27 files changed, 5741 insertions, 0 deletions
diff --git a/doc/help-pdp_background.pd b/doc/help-pdp_background.pd
new file mode 100644
index 0000000..62112cd
--- /dev/null
+++ b/doc/help-pdp_background.pd
@@ -0,0 +1,41 @@
+#N canvas 237 21 712 664 10;
+#X obj 212 155 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 173 154 stop;
+#X obj 201 189 metro 70;
+#X obj 410 486 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 410 538 pdp_control;
+#X msg 410 511 thread \$1;
+#X floatatom 410 599 5 0 0 0 - - -;
+#X obj 410 570 route pdp_drop;
+#X obj 247 263 pdp_background;
+#X obj 218 304 pdp_convert image/YCrCb/*;
+#X msg 143 189 bang;
+#X floatatom 273 229 5 0 0 0 - - -;
+#X text 280 214 Red;
+#X floatatom 321 228 5 0 0 0 - - -;
+#X floatatom 373 228 5 0 0 0 - - -;
+#X text 321 213 Green;
+#X text 375 212 Blue;
+#X obj 308 96 pdp_colorgrid pdp_colorgrid1 256 0 256 50 0 50 0 1 1
+10 10 409 121;
+#X text 258 392 pdp_background : a simple background generator;
+#X text 259 409 written by Yves Degoyon ( ydegoyon@free.fr );
+#X obj 218 339 pdp_xv;
+#X connect 0 0 2 0;
+#X connect 1 0 2 0;
+#X connect 2 0 8 0;
+#X connect 3 0 5 0;
+#X connect 4 0 7 0;
+#X connect 5 0 4 0;
+#X connect 7 0 6 0;
+#X connect 8 0 9 0;
+#X connect 9 0 20 0;
+#X connect 10 0 8 0;
+#X connect 11 0 8 1;
+#X connect 13 0 8 2;
+#X connect 14 0 8 3;
+#X connect 17 0 11 0;
+#X connect 17 1 13 0;
+#X connect 17 2 14 0;
diff --git a/doc/help-pdp_binary.pd b/doc/help-pdp_binary.pd
new file mode 100644
index 0000000..f74c13c
--- /dev/null
+++ b/doc/help-pdp_binary.pd
@@ -0,0 +1,97 @@
+#N canvas 416 0 781 666 10;
+#X obj 342 90 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 197 162 loop \$1;
+#X obj 198 140 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0
+1;
+#X msg 444 70 open \$1;
+#X obj 443 46 openpanel;
+#X obj 428 29 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 390 125 5 0 0 0 - - -;
+#X msg 299 91 stop;
+#X obj 397 94 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 331 161 metro 70;
+#X obj 326 193 pdp_yqt;
+#X obj 26 263 pdp_v4l;
+#X obj 35 232 metro 70;
+#X obj 80 198 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 37 199 stop;
+#X msg 122 230 open /dev/video;
+#X floatatom 303 338 5 0 0 0 - - -;
+#X floatatom 312 359 5 0 0 0 - - -;
+#X floatatom 329 380 5 0 0 0 - - -;
+#X floatatom 334 402 5 0 0 0 - - -;
+#X text 296 631 written by Yves Degoyon ( ydegoyon@free.fr );
+#X floatatom 343 427 5 0 0 0 - - -;
+#X text 382 401 X coordinate of cursor ( pick );
+#X text 391 426 Y coordinate of cursor ( pick );
+#X msg 89 341 pick;
+#X text 57 323 Pick up the color;
+#X floatatom 356 450 5 0 0 0 - - -;
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X msg 118 379 setcur \$1 \$2;
+#X text 6 378 Set the cursor;
+#X floatatom 155 524 5 0 0 0 - - -;
+#X floatatom 205 525 5 0 0 0 - - -;
+#X floatatom 252 524 5 0 0 0 - - -;
+#X text 297 614 pdp_binary : image binarizer;
+#X text 122 554 Components of selected color;
+#X text 167 542 Y;
+#X text 218 542 U;
+#X text 262 542 V;
+#X text 402 449 Tolerance ( default = 55 );
+#X text 350 337 Y component ( default : 200 );
+#X text 357 358 U component ( default : -1 );
+#X text 374 379 V component ( default : -1 );
+#X obj 110 459 pdp_binary ----;
+#X obj 312 264 pdp_glx;
+#X obj 311 290 route press drag release;
+#X msg 335 234 cursor 1;
+#X obj 78 504 pdp_glx;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 46 0;
+#X connect 10 0 47 0;
+#X connect 10 0 49 0;
+#X connect 11 0 46 0;
+#X connect 11 0 47 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 16 0 46 1;
+#X connect 17 0 46 2;
+#X connect 18 0 46 3;
+#X connect 19 0 46 4;
+#X connect 21 0 46 5;
+#X connect 24 0 46 0;
+#X connect 26 0 46 6;
+#X connect 27 0 29 0;
+#X connect 28 0 31 0;
+#X connect 29 0 28 0;
+#X connect 31 0 30 0;
+#X connect 32 0 24 0;
+#X connect 32 0 46 0;
+#X connect 46 0 50 0;
+#X connect 46 1 34 0;
+#X connect 46 2 35 0;
+#X connect 46 3 36 0;
+#X connect 47 0 48 0;
+#X connect 48 0 32 0;
+#X connect 49 0 47 0;
diff --git a/doc/help-pdp_cropper.pd b/doc/help-pdp_cropper.pd
new file mode 100644
index 0000000..7088b9c
--- /dev/null
+++ b/doc/help-pdp_cropper.pd
@@ -0,0 +1,64 @@
+#N canvas 237 21 712 664 10;
+#X obj 217 367 pdp_xv;
+#X obj 268 64 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 123 136 loop \$1;
+#X obj 124 114 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1
+1;
+#X msg 150 62 open \$1;
+#X obj 149 38 openpanel;
+#X obj 134 21 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 316 99 5 0 0 0 - - -;
+#X msg 225 65 stop;
+#X obj 257 135 metro 70;
+#X obj 252 167 pdp_yqt;
+#X obj 369 162 pdp_v4l;
+#X obj 378 131 metro 70;
+#X obj 423 97 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 380 98 stop;
+#X msg 465 129 open /dev/video;
+#X obj 414 352 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 414 404 pdp_control;
+#X msg 414 377 thread \$1;
+#X floatatom 414 465 5 0 0 0 - - -;
+#X obj 414 436 route pdp_drop;
+#X obj 217 299 pdp_cropper;
+#X text 187 505 pdp_cropper : crop a video;
+#X text 185 520 ( useful for cut and paste : see the cutandpaste.pd
+patch );
+#X text 186 534 written by Yves Degoyon ( ydegoyon@free.fr );
+#X floatatom 378 218 5 0 0 0 - - -;
+#X text 387 201 X1;
+#X floatatom 423 217 5 0 0 0 - - -;
+#X text 432 200 X2;
+#X floatatom 337 239 5 0 0 0 - - -;
+#X text 315 239 Y1;
+#X floatatom 338 259 5 0 0 0 - - -;
+#X text 316 259 Y2;
+#X connect 1 0 9 0;
+#X connect 2 0 10 0;
+#X connect 3 0 2 0;
+#X connect 4 0 10 0;
+#X connect 5 0 4 0;
+#X connect 6 0 5 0;
+#X connect 7 0 9 1;
+#X connect 8 0 9 0;
+#X connect 9 0 10 0;
+#X connect 10 0 21 0;
+#X connect 11 0 21 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 16 0 18 0;
+#X connect 17 0 20 0;
+#X connect 18 0 17 0;
+#X connect 20 0 19 0;
+#X connect 21 0 0 0;
+#X connect 25 0 21 1;
+#X connect 27 0 21 2;
+#X connect 29 0 21 3;
+#X connect 31 0 21 4;
diff --git a/doc/help-pdp_dilate.pd b/doc/help-pdp_dilate.pd
new file mode 100644
index 0000000..2f5a0a2
--- /dev/null
+++ b/doc/help-pdp_dilate.pd
@@ -0,0 +1,91 @@
+#N canvas 381 0 781 666 10;
+#X obj 341 20 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 196 92 loop \$1;
+#X obj 197 70 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 453 93 open \$1;
+#X obj 452 69 openpanel;
+#X obj 437 52 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 389 55 5 0 0 0 - - -;
+#X msg 298 21 stop;
+#X obj 396 24 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 330 91 metro 70;
+#X obj 325 123 pdp_yqt;
+#X obj 25 193 pdp_v4l;
+#X obj 34 162 metro 70;
+#X obj 79 128 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 36 129 stop;
+#X msg 121 160 open /dev/video;
+#X text 296 540 written by Yves Degoyon ( ydegoyon@free.fr );
+#X msg 31 236 pick;
+#X floatatom 248 262 5 0 0 0 - - -;
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X msg 72 237 setcur \$1 \$2;
+#X floatatom 174 328 5 0 0 0 - - -;
+#X floatatom 224 329 5 0 0 0 - - -;
+#X floatatom 271 328 5 0 0 0 - - -;
+#X text 294 261 Tolerance ( default = 55 );
+#X obj 138 289 pdp_binary ----;
+#X obj 41 369 route press drag release;
+#X msg 42 311 cursor 1;
+#X obj 148 476 pdp_glx;
+#X floatatom 250 370 5 0 0 0 - - -;
+#X text 296 369 Number of passes ( default = 1 );
+#X obj 41 342 pdp_glx;
+#X floatatom 271 396 5 0 0 0 - - -;
+#X floatatom 292 420 5 0 0 0 - - -;
+#X text 317 395 Kernel width ( default = 3 );
+#X text 338 418 Kernel height ( default = 3 );
+#X obj 149 437 pdp_dilate ----;
+#X text 297 523 pdp_dilate : morphology : dilation;
+#X obj 54 478 pdp_xor;
+#X obj 55 508 pdp_glx;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 29 0;
+#X connect 11 0 29 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 17 0 29 0;
+#X connect 18 0 29 6;
+#X connect 19 0 21 0;
+#X connect 20 0 23 0;
+#X connect 21 0 20 0;
+#X connect 23 0 22 0;
+#X connect 24 0 17 0;
+#X connect 24 0 29 0;
+#X connect 29 0 35 0;
+#X connect 29 0 31 0;
+#X connect 29 0 40 0;
+#X connect 29 0 42 0;
+#X connect 29 1 25 0;
+#X connect 29 2 26 0;
+#X connect 29 3 27 0;
+#X connect 30 0 24 0;
+#X connect 31 0 35 0;
+#X connect 33 0 40 1;
+#X connect 35 0 30 0;
+#X connect 36 0 40 2;
+#X connect 37 0 40 3;
+#X connect 40 0 32 0;
+#X connect 40 0 42 1;
+#X connect 42 0 43 0;
diff --git a/doc/help-pdp_disintegration.pd b/doc/help-pdp_disintegration.pd
new file mode 100644
index 0000000..b58d0a2
--- /dev/null
+++ b/doc/help-pdp_disintegration.pd
@@ -0,0 +1,61 @@
+#N canvas 381 0 781 666 10;
+#X obj 341 20 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 196 92 loop \$1;
+#X obj 197 70 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 453 93 open \$1;
+#X obj 452 69 openpanel;
+#X obj 437 52 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 389 55 5 0 0 0 - - -;
+#X msg 298 21 stop;
+#X obj 396 24 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 330 91 metro 70;
+#X obj 325 123 pdp_yqt;
+#X obj 25 193 pdp_v4l;
+#X obj 34 162 metro 70;
+#X obj 79 128 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 36 129 stop;
+#X msg 121 160 open /dev/video;
+#X text 296 540 written by Yves Degoyon ( ydegoyon@free.fr );
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X floatatom 239 270 5 0 0 0 - - -;
+#X floatatom 287 311 5 0 0 0 - - -;
+#X text 297 522 pdp_disintegration : piksels summing-up and averaging
+;
+#X obj 138 337 pdp_disintegration ----;
+#X text 285 267 Number of passes ( default = 3 );
+#X text 333 310 Reduction factor ( default = 5 );
+#X text 281 282 ( disintegration is here );
+#X obj 137 376 pdp_xv;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 25 0;
+#X connect 11 0 25 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 17 0 19 0;
+#X connect 18 0 21 0;
+#X connect 19 0 18 0;
+#X connect 21 0 20 0;
+#X connect 22 0 25 1;
+#X connect 23 0 25 2;
+#X connect 25 0 29 0;
diff --git a/doc/help-pdp_distance.pd b/doc/help-pdp_distance.pd
new file mode 100644
index 0000000..3451c85
--- /dev/null
+++ b/doc/help-pdp_distance.pd
@@ -0,0 +1,72 @@
+#N canvas 375 16 781 666 10;
+#X obj 341 20 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 196 92 loop \$1;
+#X obj 197 70 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 453 93 open \$1;
+#X obj 452 69 openpanel;
+#X obj 437 52 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 389 55 5 0 0 0 - - -;
+#X msg 298 21 stop;
+#X obj 396 24 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 330 91 metro 70;
+#X obj 325 123 pdp_yqt;
+#X obj 25 193 pdp_v4l;
+#X obj 34 162 metro 70;
+#X obj 79 128 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 36 129 stop;
+#X msg 121 160 open /dev/video;
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X text 297 554 http://www.cs.cf.ac.uk/User/Paul.Rosin/resources/sdt/
+;
+#X text 296 538 inspired by Paul Rosin;
+#X text 297 570 PD-fication by Yves Degoyon ( ydegoyon@free.fr );
+#X text 297 521 pdp_distance : chamfer34 distance transform;
+#X obj 108 400 pdp_distance ----;
+#X obj 31 328 pdp_glx;
+#X floatatom 211 317 5 0 0 0 - - -;
+#X text 254 315 Coefficient 1;
+#X floatatom 237 337 5 0 0 0 - - -;
+#X floatatom 264 359 5 0 0 0 - - -;
+#X floatatom 281 382 5 0 0 0 - - -;
+#X text 280 335 Coefficient 2;
+#X text 307 357 Coefficient 3;
+#X text 324 380 Coefficient 4;
+#X obj 108 441 pdp_glx;
+#X obj 113 289 pdp_grey ----;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 36 0;
+#X connect 11 0 36 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 16 0 18 0;
+#X connect 17 0 20 0;
+#X connect 18 0 17 0;
+#X connect 20 0 19 0;
+#X connect 25 0 35 0;
+#X connect 27 0 25 1;
+#X connect 29 0 25 2;
+#X connect 30 0 25 3;
+#X connect 31 0 25 4;
+#X connect 36 0 25 0;
+#X connect 36 0 26 0;
diff --git a/doc/help-pdp_erode.pd b/doc/help-pdp_erode.pd
new file mode 100644
index 0000000..c2ad22e
--- /dev/null
+++ b/doc/help-pdp_erode.pd
@@ -0,0 +1,91 @@
+#N canvas 381 0 781 666 10;
+#X obj 341 20 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 196 92 loop \$1;
+#X obj 197 70 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0 1
+;
+#X msg 453 93 open \$1;
+#X obj 452 69 openpanel;
+#X obj 437 52 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 389 55 5 0 0 0 - - -;
+#X msg 298 21 stop;
+#X obj 396 24 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 330 91 metro 70;
+#X obj 325 123 pdp_yqt;
+#X obj 25 193 pdp_v4l;
+#X obj 34 162 metro 70;
+#X obj 79 128 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 36 129 stop;
+#X msg 121 160 open /dev/video;
+#X text 296 540 written by Yves Degoyon ( ydegoyon@free.fr );
+#X msg 31 236 pick;
+#X floatatom 248 262 5 0 0 0 - - -;
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X msg 72 237 setcur \$1 \$2;
+#X floatatom 174 328 5 0 0 0 - - -;
+#X floatatom 224 329 5 0 0 0 - - -;
+#X floatatom 271 328 5 0 0 0 - - -;
+#X text 294 261 Tolerance ( default = 55 );
+#X obj 138 289 pdp_binary ----;
+#X obj 41 369 route press drag release;
+#X msg 42 311 cursor 1;
+#X text 297 523 pdp_erode : morphology : erosion;
+#X obj 148 476 pdp_glx;
+#X floatatom 250 370 5 0 0 0 - - -;
+#X text 296 369 Number of passes ( default = 1 );
+#X obj 41 342 pdp_glx;
+#X floatatom 271 396 5 0 0 0 - - -;
+#X floatatom 292 420 5 0 0 0 - - -;
+#X text 317 395 Kernel width ( default = 3 );
+#X text 338 418 Kernel height ( default = 3 );
+#X obj 148 437 pdp_erode ----;
+#X obj 63 477 pdp_xor;
+#X obj 64 509 pdp_glx;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 29 0;
+#X connect 11 0 29 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 17 0 29 0;
+#X connect 18 0 29 6;
+#X connect 19 0 21 0;
+#X connect 20 0 23 0;
+#X connect 21 0 20 0;
+#X connect 23 0 22 0;
+#X connect 24 0 17 0;
+#X connect 24 0 29 0;
+#X connect 29 0 36 0;
+#X connect 29 0 31 0;
+#X connect 29 0 41 0;
+#X connect 29 0 42 0;
+#X connect 29 1 25 0;
+#X connect 29 2 26 0;
+#X connect 29 3 27 0;
+#X connect 30 0 24 0;
+#X connect 31 0 36 0;
+#X connect 34 0 41 1;
+#X connect 36 0 30 0;
+#X connect 37 0 41 2;
+#X connect 38 0 41 3;
+#X connect 41 0 33 0;
+#X connect 41 0 42 1;
+#X connect 42 0 43 0;
diff --git a/doc/help-pdp_hitandmiss.pd b/doc/help-pdp_hitandmiss.pd
new file mode 100644
index 0000000..5b54cce
--- /dev/null
+++ b/doc/help-pdp_hitandmiss.pd
@@ -0,0 +1,97 @@
+#N canvas 381 0 781 666 10;
+#X obj 341 20 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 196 92 loop \$1;
+#X obj 197 70 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 453 93 open \$1;
+#X obj 452 69 openpanel;
+#X obj 437 52 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 389 55 5 0 0 0 - - -;
+#X msg 298 21 stop;
+#X obj 396 24 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 330 91 metro 70;
+#X obj 325 123 pdp_yqt;
+#X obj 25 193 pdp_v4l;
+#X obj 34 162 metro 70;
+#X obj 79 128 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 36 129 stop;
+#X msg 121 160 open /dev/video;
+#X text 306 611 written by Yves Degoyon ( ydegoyon@free.fr );
+#X msg 31 236 pick;
+#X floatatom 248 262 5 0 0 0 - - -;
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X msg 72 237 setcur \$1 \$2;
+#X floatatom 174 328 5 0 0 0 - - -;
+#X floatatom 224 329 5 0 0 0 - - -;
+#X floatatom 271 328 5 0 0 0 - - -;
+#X text 294 261 Tolerance ( default = 55 );
+#X obj 138 289 pdp_binary ----;
+#X obj 41 369 route press drag release;
+#X msg 42 311 cursor 1;
+#X floatatom 250 370 5 0 0 0 - - -;
+#X text 296 369 Number of passes ( default = 1 );
+#X floatatom 271 396 5 0 0 0 - - -;
+#X floatatom 292 420 5 0 0 0 - - -;
+#X text 317 395 Kernel width ( default = 3 );
+#X text 338 418 Kernel height ( default = 3 );
+#X obj 143 501 pdp_hitandmiss ----;
+#X text 307 594 pdp_hitandmiss : morphology : hit and miss;
+#X msg 307 456 kernel 1 1 1 1 1 1 1 1 1;
+#X text 515 454 Change the kernel;
+#X text 514 468 ( this one is equivalent to erosion );
+#X text 515 485 The default one is :;
+#X text 570 501 -1 1 -1;
+#X text 578 516 0 1 1;
+#X text 578 529 0 0 -1;
+#X text 517 547 ( -1 here means unused );
+#X obj 41 342 pdp_glx;
+#X msg 308 484 kernel -1 1 -1 0 1 1 0 0 -1;
+#X obj 143 540 pdp_glx;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 29 0;
+#X connect 11 0 29 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 17 0 29 0;
+#X connect 18 0 29 6;
+#X connect 19 0 21 0;
+#X connect 20 0 23 0;
+#X connect 21 0 20 0;
+#X connect 23 0 22 0;
+#X connect 24 0 17 0;
+#X connect 24 0 29 0;
+#X connect 29 0 31 0;
+#X connect 29 0 38 0;
+#X connect 29 0 48 0;
+#X connect 29 1 25 0;
+#X connect 29 2 26 0;
+#X connect 29 3 27 0;
+#X connect 30 0 24 0;
+#X connect 31 0 48 0;
+#X connect 32 0 38 1;
+#X connect 34 0 38 2;
+#X connect 35 0 38 3;
+#X connect 38 0 50 0;
+#X connect 40 0 38 0;
+#X connect 48 0 30 0;
+#X connect 49 0 38 0;
diff --git a/doc/help-pdp_theorin~.pd b/doc/help-pdp_theorin~.pd
new file mode 100644
index 0000000..19d133a
--- /dev/null
+++ b/doc/help-pdp_theorin~.pd
@@ -0,0 +1,14 @@
+#N canvas 259 178 509 391 10;
+#X obj 156 158 dac~;
+#X text 51 309 written by Yves Degoyon (ydegoyon@free.fr);
+#X text 236 112 <-- everything is in this box;
+#X text 265 127 where the block size is redefined;
+#X text 265 141 this is necessary for an;
+#X text 266 154 ( acceptable? ) audio decoding;
+#X obj 395 221 loadbang;
+#X msg 395 251 \; pd dsp 1;
+#X text 51 295 pdp_theorin~ : theora threaded file reader;
+#X obj 128 113 rs_pdp_theorin~;
+#X connect 6 0 7 0;
+#X connect 9 0 0 0;
+#X connect 9 1 0 1;
diff --git a/doc/help-pdp_theorout~.pd b/doc/help-pdp_theorout~.pd
new file mode 100644
index 0000000..0fe1b31
--- /dev/null
+++ b/doc/help-pdp_theorout~.pd
@@ -0,0 +1,118 @@
+#N canvas 5 16 986 661 10;
+#X obj 255 34 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 117 65 loop \$1;
+#X obj 117 40 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0 1
+;
+#X msg 278 48 open \$1;
+#X obj 278 24 openpanel;
+#X obj 278 3 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 293 75 5 0 0 0 - - -;
+#X msg 212 35 stop;
+#X obj 215 91 metro 70;
+#X obj 18 191 pdp_v4l;
+#X obj 27 160 metro 70;
+#X obj 72 126 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 29 127 stop;
+#X msg 92 161 open /dev/video;
+#X text 112 622 written by Yves Degoyon ( ydegoyon@free.fr );
+#X msg 383 294 stop;
+#X msg 382 268 start;
+#X text 424 269 Start recording;
+#X floatatom 159 534 5 0 0 0 - - -;
+#X obj 213 124 pdp_yqt;
+#X text 424 296 Stop recording;
+#X obj 832 499 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 832 551 pdp_control;
+#X msg 832 524 thread \$1;
+#X floatatom 832 612 5 0 0 0 - - -;
+#X obj 832 583 route pdp_drop;
+#X obj 119 300 adc~;
+#X text 326 197 <---- audio connections;
+#X text 383 224 ==== ACTIONS ========;
+#X text 384 327 ==== VIDEOS SETTINGS ========;
+#X text 383 403 ==== AUDIO SETTINGS ========;
+#X obj 142 200 pdp_xv;
+#X obj 36 255 pdp_affine;
+#X floatatom 68 229 5 0 0 0 - - -;
+#X obj 36 287 pdp_xv;
+#X text 113 605 pdp_theorout~ : records a/v theora encoded file;
+#X msg 382 242 open /tmp/output.ogg;
+#X text 534 244 Open a theora/vorbis file before any operations;
+#X obj 308 124 pdp_theorin~;
+#X obj 378 32 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X msg 378 82 open \$1;
+#X obj 378 54 openpanel;
+#X obj 357 167 dac~;
+#X text 612 296 Warning : change audio and video settings;
+#X msg 386 347 videoquality \$1;
+#X msg 387 375 videobitrate \$1;
+#X text 560 377 Video bitrate ( kbps ) : [45 \, 2000];
+#X text 558 350 Video quality : [0 \, 63] \, default 16;
+#X text 727 392 default 96;
+#X msg 385 424 audioquality \$1;
+#X msg 386 452 audiobitrate \$1;
+#X text 729 468 default 32;
+#X text 563 453 Audio bitrate ( kbps ) : [8 \, 2000];
+#X text 562 426 Audio quality : [-0.1 \, 1.0] \, default 0.5;
+#X floatatom 504 349 5 0 0 0 - - -;
+#X floatatom 505 376 5 0 0 0 - - -;
+#X floatatom 506 424 5 0 0 0 - - -;
+#X floatatom 506 453 5 0 0 0 - - -;
+#X text 682 310 before starting the recording;
+#X text 682 324 ( or restart it );
+#X obj 159 504 pdp_theorout~;
+#X connect 0 0 8 0;
+#X connect 1 0 19 0;
+#X connect 2 0 1 0;
+#X connect 3 0 19 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 8 1;
+#X connect 7 0 8 0;
+#X connect 8 0 19 0;
+#X connect 9 0 32 0;
+#X connect 10 0 9 0;
+#X connect 11 0 10 0;
+#X connect 12 0 10 0;
+#X connect 13 0 9 0;
+#X connect 15 0 60 0;
+#X connect 16 0 60 0;
+#X connect 19 0 31 0;
+#X connect 19 0 60 0;
+#X connect 19 4 42 0;
+#X connect 19 4 60 0;
+#X connect 19 5 42 1;
+#X connect 19 5 60 1;
+#X connect 21 0 23 0;
+#X connect 22 0 25 0;
+#X connect 23 0 22 0;
+#X connect 25 0 24 0;
+#X connect 26 0 60 0;
+#X connect 26 1 60 1;
+#X connect 32 0 34 0;
+#X connect 32 0 60 0;
+#X connect 33 0 32 1;
+#X connect 36 0 60 0;
+#X connect 38 0 31 0;
+#X connect 38 0 60 0;
+#X connect 38 1 42 0;
+#X connect 38 1 60 0;
+#X connect 38 2 42 1;
+#X connect 38 2 60 1;
+#X connect 39 0 41 0;
+#X connect 40 0 38 0;
+#X connect 41 0 40 0;
+#X connect 44 0 60 0;
+#X connect 45 0 60 0;
+#X connect 49 0 60 0;
+#X connect 50 0 60 0;
+#X connect 54 0 44 0;
+#X connect 55 0 45 0;
+#X connect 56 0 49 0;
+#X connect 57 0 50 0;
+#X connect 60 0 18 0;
diff --git a/doc/rs_pdp_theorin~.pd b/doc/rs_pdp_theorin~.pd
new file mode 100644
index 0000000..cc1a74b
--- /dev/null
+++ b/doc/rs_pdp_theorin~.pd
@@ -0,0 +1,115 @@
+#N canvas 127 47 872 636 10;
+#X text 452 600 written by Yves Degoyon (ydegoyon@free.fr);
+#X floatatom 240 549 5 0 0 0 - - -;
+#X text 289 550 Number of video frames decoded;
+#X msg 250 409 priority \$1;
+#X floatatom 339 410 5 0 0 0 - - -;
+#X text 385 433 ( optional \, if you know what you're doing );
+#X obj 134 543 outlet~;
+#X text 390 405 Set the priority of decoding thread;
+#X obj 247 143 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1
+-1;
+#X msg 250 379 audio \$1;
+#X obj 319 381 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X obj 322 350 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X msg 248 350 thread \$1;
+#X obj 378 350 loadbang;
+#X msg 343 350 1;
+#X text 446 350 Activate threading ( default : on );
+#X floatatom 256 526 5 0 0 0 - - -;
+#X floatatom 280 506 5 0 0 0 - - -;
+#X obj 339 257 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X obj 395 255 loadbang;
+#X msg 360 255 1;
+#X msg 248 256 autoplay \$1;
+#X text 463 255 Activate auto play mode ( default : on );
+#X msg 248 287 bang;
+#X msg 388 289 bang;
+#X floatatom 429 290 5 0 0 0 - - -;
+#X text 481 288 In manual mode \, read next frame ( autoplay = off
+);
+#X obj 324 289 metro 70;
+#X msg 249 318 loop \$1;
+#X obj 311 319 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X obj 367 317 loadbang;
+#X msg 332 317 1;
+#X text 435 317 Activate loop mode for files ( default : on );
+#X msg 285 288 stop;
+#X obj 111 430 pdp_theorin~;
+#X msg 248 222 close;
+#X text 326 224 Close the current file;
+#X text 325 164 Open a theora video file;
+#X msg 247 193 open \$1;
+#X text 303 528 File framerate;
+#X text 327 507 End of file reached;
+#X text 452 586 pdp_theorin~ : threaded theora file reader;
+#X text 11 163 Seek file ( in kilobytes );
+#X obj 72 543 outlet~;
+#X text 387 418 ([0 \, 20 ] default : 1 );
+#X obj 247 165 openpanel;
+#X text 444 380 Activate decoding of audio ( default : on );
+#X obj 377 380 loadbang;
+#X msg 342 380 1;
+#X obj 67 60 block~ 512;
+#X text 12 179 warning : there's a big delay;
+#X text 11 192 and risks of desynchronisation;
+#X text 25 220 seekable for now );
+#X text 11 207 ( sorry \, theora is not really;
+#X floatatom 309 455 5 0 0 0 - - -;
+#X text 354 455 File size ( in kbs );
+#X obj 39 496 pdp_xv;
+#X obj 78 259 vsl 15 128 0 2083 0 0 empty filesize empty 0 -8 0 8 -262144
+-1 -1 0 1;
+#X msg 310 473 \; filesize range 0 \$1;
+#X floatatom 59 411 5 0 0 0 - - -;
+#X obj 691 39 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X obj 691 91 pdp_control;
+#X msg 691 64 thread \$1;
+#X floatatom 691 152 5 0 0 0 - - -;
+#X obj 691 123 route pdp_drop;
+#X connect 3 0 34 0;
+#X connect 4 0 3 0;
+#X connect 8 0 45 0;
+#X connect 9 0 34 0;
+#X connect 10 0 9 0;
+#X connect 11 0 12 0;
+#X connect 12 0 34 0;
+#X connect 13 0 14 0;
+#X connect 14 0 11 0;
+#X connect 18 0 21 0;
+#X connect 19 0 20 0;
+#X connect 20 0 18 0;
+#X connect 21 0 34 0;
+#X connect 23 0 34 0;
+#X connect 24 0 27 0;
+#X connect 25 0 27 1;
+#X connect 27 0 34 0;
+#X connect 28 0 34 0;
+#X connect 29 0 28 0;
+#X connect 30 0 31 0;
+#X connect 31 0 29 0;
+#X connect 33 0 27 0;
+#X connect 34 0 56 0;
+#X connect 34 1 43 0;
+#X connect 34 2 6 0;
+#X connect 34 3 1 0;
+#X connect 34 4 16 0;
+#X connect 34 5 17 0;
+#X connect 34 6 54 0;
+#X connect 35 0 34 0;
+#X connect 38 0 34 0;
+#X connect 45 0 38 0;
+#X connect 47 0 48 0;
+#X connect 48 0 10 0;
+#X connect 54 0 58 0;
+#X connect 57 0 34 1;
+#X connect 57 0 59 0;
+#X connect 60 0 62 0;
+#X connect 61 0 64 0;
+#X connect 62 0 61 0;
+#X connect 64 0 63 0;
diff --git a/modules/pdp_background.c b/modules/pdp_background.c
new file mode 100644
index 0000000..130253d
--- /dev/null
+++ b/modules/pdp_background.c
@@ -0,0 +1,188 @@
+/*
+ * PiDiP module.
+ * Copyright (c) by Yves Degoyon (ydegoyon@free.fr)
+ *
+ * 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.h"
+#include "yuv.h"
+#include "time.h"
+#include "sys/time.h"
+
+#define DEFAULT_RED_VALUE 255
+#define DEFAULT_GREEN_VALUE 255
+#define DEFAULT_BLUE_VALUE 255
+#define DEFAULT_WIDTH 320
+#define DEFAULT_HEIGHT 240
+
+typedef struct pdp_background_struct
+{
+ t_object x_obj;
+
+ t_outlet *x_outlet0;
+
+ t_int x_packet0;
+
+ t_int x_colorR;
+ t_int x_colorG;
+ t_int x_colorB;
+ t_int x_colorY;
+ t_int x_colorU;
+ t_int x_colorV;
+
+ t_int x_width;
+ t_int x_height;
+
+} t_pdp_background;
+
+static void pdp_background_bang(t_pdp_background *x)
+{
+
+ t_pdp *header;
+ unsigned char *data;
+ unsigned char *pY, *pU, *pV;
+ t_int px, py;
+
+ x->x_packet0 = pdp_packet_new_bitmap_yv12( x->x_width, x->x_height );
+
+ data = (char *)pdp_packet_data(x->x_packet0);
+ pY = data;
+ pV = data+(x->x_width*x->x_height);
+ pU = data+(x->x_width*x->x_height)+((x->x_width*x->x_height)>>2);
+
+ memset( pY, (unsigned char)x->x_colorY, x->x_width*x->x_height );
+ memset( pV, (unsigned char)x->x_colorV, (x->x_width*x->x_height>>2) );
+ memset( pU, (unsigned char)x->x_colorU, (x->x_width*x->x_height>>2) );
+
+ pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0);
+
+}
+
+static void pdp_background_dim(t_pdp_background *x, t_floatarg fwidth, t_floatarg fheight)
+{
+ if ( ( (t_int)fwidth>0 ) && ( (t_int) fheight>0 ) )
+ {
+ if ( ((t_int)fwidth)%8 == 0 )
+ {
+ x->x_width = (t_int)fwidth;
+ }
+ else
+ {
+ x->x_width = (t_int)fwidth + (8-((t_int)fwidth)%8); // align on 8
+ }
+ if ( ((t_int)fheight)%8 == 0 )
+ {
+ x->x_height = (t_int)fheight;
+ }
+ else
+ {
+ x->x_height = (t_int)fheight + (8-((t_int)fheight)%8); // align on 8
+ }
+ }
+}
+
+static void pdp_background_red(t_pdp_background *x, t_floatarg fred)
+{
+ if ( ( (t_int)fred>=0 ) && ( (t_int) fred <= 255 ) )
+ {
+ x->x_colorR = (t_int) fred;
+ x->x_colorY = yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ x->x_colorU = yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ x->x_colorV = yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ }
+}
+
+static void pdp_background_green(t_pdp_background *x, t_floatarg fgreen)
+{
+ if ( ( (t_int)fgreen>=0 ) && ( (t_int) fgreen <= 255 ) )
+ {
+ x->x_colorG = (t_int) fgreen;
+ x->x_colorY = yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ x->x_colorU = yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ x->x_colorV = yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ }
+}
+
+static void pdp_background_blue(t_pdp_background *x, t_floatarg fblue)
+{
+ if ( ( (t_int)fblue>=0 ) && ( (t_int) fblue <= 255 ) )
+ {
+ x->x_colorB = (t_int) fblue;
+ x->x_colorY = yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ x->x_colorU = yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ x->x_colorV = yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ }
+}
+
+static void pdp_background_free(t_pdp_background *x)
+{
+ // nothing to do
+}
+
+t_class *pdp_background_class;
+
+void *pdp_background_new(void)
+{
+ t_pdp_background *x = (t_pdp_background *)pd_new(pdp_background_class);
+
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("red"));
+ 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_outlet0 = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_colorR = DEFAULT_RED_VALUE;
+ x->x_colorG = DEFAULT_GREEN_VALUE;
+ x->x_colorB = DEFAULT_BLUE_VALUE;
+
+ x->x_colorY = yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ x->x_colorU = yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+ x->x_colorV = yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB );
+
+ x->x_width = DEFAULT_WIDTH;
+ x->x_height = DEFAULT_HEIGHT;
+
+ x->x_packet0 = -1;
+
+ return (void *)x;
+}
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_background_setup(void)
+{
+ pdp_background_class = class_new(gensym("pdp_background"), (t_newmethod)pdp_background_new,
+ (t_method)pdp_background_free, sizeof(t_pdp_background), 0, A_NULL);
+
+ class_addmethod(pdp_background_class, (t_method)pdp_background_bang, gensym("bang"), A_NULL);
+ class_addmethod(pdp_background_class, (t_method)pdp_background_red, gensym("red"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_background_class, (t_method)pdp_background_green, gensym("green"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_background_class, (t_method)pdp_background_blue, gensym("blue"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_background_class, (t_method)pdp_background_dim, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL);
+ class_sethelpsymbol( pdp_background_class, gensym("pdp_background.pd") );
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_binary.c b/modules/pdp_binary.c
new file mode 100644
index 0000000..5ce943c
--- /dev/null
+++ b/modules/pdp_binary.c
@@ -0,0 +1,364 @@
+/*
+ * PiDiP module
+ * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr )
+ *
+ * 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 object is an image binarizer, based on some (y,u,v) setting
+ */
+
+#include "pdp.h"
+#include "yuv.h"
+#include <math.h>
+#include <stdio.h>
+
+static char *pdp_binary_version = "pdp_binary: a image binarizer version 0.1 written by Yves Degoyon (ydegoyon@free.fr)";
+
+typedef struct pdp_binary_struct
+{
+ t_object x_obj;
+
+ t_int x_packet0;
+ t_int x_packet1;
+ t_int x_queue_id;
+ t_int x_dropped;
+
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+ t_int x_colorY; // YUV components of binary mask
+ t_int x_colorU;
+ t_int x_colorV;
+ t_int x_cursX; // X position of the cursor
+ t_int x_cursY; // Y position of the cursor
+ t_int x_tolerance; // tolerance
+ short int *x_frame; // keep a copy of current frame for picking color
+
+ t_outlet *x_pdp_output; // output packets
+ t_outlet *x_Y; // output Y component of selected color
+ t_outlet *x_U; // output U component of selected color
+ t_outlet *x_V; // output V component of selected color
+
+} t_pdp_binary;
+
+static void pdp_binary_setcur(t_pdp_binary *x, t_floatarg fpx, t_floatarg fpy )
+{
+ if ( (fpx>=0.0) && (fpx<=1.0) && (fpy>=0.0) && (fpy<=1.0) )
+ {
+ x->x_cursX = fpx*(t_float)x->x_vwidth;
+ x->x_cursY = fpy*(t_float)x->x_vheight;
+ }
+}
+
+static void pdp_binary_y(t_pdp_binary *x, t_floatarg fy )
+{
+ if ( fy <= 255. )
+ {
+ x->x_colorY = (t_int)fy;
+ outlet_float( x->x_Y, x->x_colorY );
+ }
+}
+
+static void pdp_binary_u(t_pdp_binary *x, t_floatarg fu )
+{
+ if ( fu <= 255. )
+ {
+ x->x_colorU = (t_int)fu;
+ outlet_float( x->x_U, x->x_colorU );
+ }
+}
+
+static void pdp_binary_v(t_pdp_binary *x, t_floatarg fv )
+{
+ if ( fv < 255 )
+ {
+ x->x_colorV = (t_int)fv;
+ outlet_float( x->x_V, x->x_colorV );
+ }
+}
+
+static void pdp_binary_cursx(t_pdp_binary *x, t_floatarg fx )
+{
+ if ( ( fx >= 0 ) && ( fx < x->x_vwidth) )
+ {
+ x->x_cursX = (int)fx;
+ }
+}
+
+static void pdp_binary_cursy(t_pdp_binary *x, t_floatarg fy )
+{
+ if ( ( fy >= 0 ) && ( fy < x->x_vheight) )
+ {
+ x->x_cursY = (int)fy;
+ }
+}
+
+static void pdp_binary_tolerance(t_pdp_binary *x, t_floatarg ftolerance )
+{
+ if ( ftolerance >= 0 )
+ {
+ x->x_tolerance = (int)ftolerance;
+ }
+}
+
+static void pdp_binary_pick(t_pdp_binary *x)
+{
+ if ( x->x_frame && ( x->x_cursX > 0 ) && ( x->x_cursX < x->x_vwidth )
+ && ( x->x_cursY > 0 ) && ( x->x_cursY < x->x_vheight ) )
+ {
+ // post( "pdp_binary : picking up color : x=%d y=%d", x->x_cursX, x->x_cursY );
+ x->x_colorY = x->x_frame[ x->x_cursY*x->x_vwidth+x->x_cursX ];
+ x->x_colorV = x->x_frame[ x->x_vsize + (x->x_cursY>>1)*(x->x_vwidth>>1)+(x->x_cursX>>1) ];
+ x->x_colorU = x->x_frame[ x->x_vsize + (x->x_vsize>>2) + (x->x_cursY>>1)*(x->x_vwidth>>1)+(x->x_cursX>>1) ];
+ x->x_colorY = (x->x_colorY)>>7;
+ x->x_colorV = (x->x_colorV>>8)+128;
+ x->x_colorU = (x->x_colorU>>8)+128;
+ outlet_float( x->x_Y, x->x_colorY );
+ outlet_float( x->x_V, x->x_colorV );
+ outlet_float( x->x_U, x->x_colorU );
+ }
+}
+
+static void pdp_binary_allocate(t_pdp_binary *x)
+{
+ x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+
+ if ( !x->x_frame )
+ {
+ post( "pdp_binary : severe error : cannot allocate buffer !!! ");
+ return;
+ }
+}
+
+static void pdp_binary_free_ressources(t_pdp_binary *x)
+{
+ if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+}
+
+static void pdp_binary_process_yv12(t_pdp_binary *x)
+{
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ short int *data = (short int *)pdp_packet_data(x->x_packet0);
+ t_pdp *newheader = pdp_packet_header(x->x_packet1);
+ short int *newdata = (short int *)pdp_packet_data(x->x_packet1);
+ t_int i;
+ t_int px=0, py=0;
+ t_int y=0, u=0, v=0;
+ short int *pfY, *pfU, *pfV;
+ t_int diff;
+
+ /* allocate all ressources */
+ if ( ( (int)header->info.image.width != x->x_vwidth ) ||
+ ( (int)header->info.image.height != x->x_vheight ) )
+ {
+ pdp_binary_free_ressources( x );
+ x->x_vwidth = header->info.image.width;
+ x->x_vheight = header->info.image.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+ pdp_binary_allocate( x );
+ post( "pdp_binary : reallocated buffers" );
+ }
+
+ memcpy(x->x_frame, data, (x->x_vsize + (x->x_vsize>>1))<<1 );
+
+ // post( "pdp_binary : newheader:%x", newheader );
+
+ newheader->info.image.encoding = header->info.image.encoding;
+ newheader->info.image.width = x->x_vwidth;
+ newheader->info.image.height = x->x_vheight;
+
+ // binarize
+ pfY = data;
+ pfV = data+x->x_vsize;
+ pfU = data+x->x_vsize+(x->x_vsize>>2);
+ for ( py=0; py<x->x_vheight; py++ )
+ {
+ for ( px=0; px<x->x_vwidth; px++ )
+ {
+ y = (*pfY)>>7;
+ v = ((*pfV)>>8)+128;
+ u = ((*pfU)>>8)+128;
+
+ // post( "pdp_binary : y=%d, u=%d, v=%d", y, u, v );
+
+ diff = 0;
+ if ( x->x_colorY >= 0 )
+ {
+ diff += abs(y-x->x_colorY );
+ }
+ if ( x->x_colorV >= 0 )
+ {
+ diff += abs(v-x->x_colorV );
+ }
+ if ( x->x_colorU >=0 )
+ {
+ diff += abs(u-x->x_colorU );
+ }
+
+ if ( diff <= x->x_tolerance )
+ {
+ *(newdata+(py*x->x_vwidth+px)) = 0xff<<7;
+ }
+ else
+ {
+ *(newdata+(py*x->x_vwidth+px)) = 0;
+ }
+
+ pfY++;
+ if ( (px%2==0) && (py%2==0) )
+ {
+ pfU++;pfV++;
+ }
+ }
+ }
+
+ memset( newdata+x->x_vsize, 0x0, (x->x_vsize>>1)<<1 );
+
+ return;
+}
+
+static void pdp_binary_sendpacket(t_pdp_binary *x)
+{
+ /* release the packet */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+
+ /* unregister and propagate if valid dest packet */
+ pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1);
+}
+
+static void pdp_binary_process(t_pdp_binary *x)
+{
+ int encoding;
+ t_pdp *header = 0;
+
+ /* check if image data packets are compatible */
+ if ( (header = pdp_packet_header(x->x_packet0))
+ && (PDP_IMAGE == header->type)){
+
+ /* pdp_binary_process inputs and write into active inlet */
+ switch(pdp_packet_header(x->x_packet0)->info.image.encoding)
+ {
+
+ case PDP_IMAGE_YV12:
+ x->x_packet1 = pdp_packet_clone_rw(x->x_packet0);
+ pdp_queue_add(x, pdp_binary_process_yv12, pdp_binary_sendpacket, &x->x_queue_id);
+ break;
+
+ case PDP_IMAGE_GREY:
+ // should write something to handle these one day
+ // but i don't use this mode
+ break;
+
+ default:
+ /* don't know the type, so dont pdp_binary_process */
+ break;
+
+ }
+ }
+
+}
+
+static void pdp_binary_input_0(t_pdp_binary *x, t_symbol *s, t_floatarg f)
+{
+ /* if this is a register_ro message or register_rw message, register with packet factory */
+
+ if (s== gensym("register_rw"))
+ {
+ x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") );
+ }
+
+ if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped))
+ {
+ pdp_binary_process(x);
+ }
+}
+
+static void pdp_binary_free(t_pdp_binary *x)
+{
+ int i;
+
+ pdp_packet_mark_unused(x->x_packet0);
+ pdp_binary_free_ressources( x );
+}
+
+t_class *pdp_binary_class;
+
+void *pdp_binary_new(void)
+{
+ int i;
+
+ t_pdp_binary *x = (t_pdp_binary *)pd_new(pdp_binary_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("Y"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("U"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("V"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cursx"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cursy"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("tolerance"));
+
+ x->x_pdp_output = outlet_new(&x->x_obj, &s_anything);
+ x->x_Y = outlet_new(&x->x_obj, &s_float);
+ x->x_U = outlet_new(&x->x_obj, &s_float);
+ x->x_V = outlet_new(&x->x_obj, &s_float);
+
+ x->x_colorY = 200;
+ x->x_colorU = -1;
+ x->x_colorV = -1;
+
+ x->x_packet0 = -1;
+ x->x_packet1 = -1;
+
+ x->x_cursX = -1;
+ x->x_cursY = -1;
+ x->x_tolerance = 55;
+
+ x->x_vwidth = -1;
+ x->x_vheight = -1;
+ x->x_vsize = -1;
+ x->x_frame = NULL;
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_binary_setup(void)
+{
+ // post( pdp_binary_version );
+ pdp_binary_class = class_new(gensym("pdp_binary"), (t_newmethod)pdp_binary_new,
+ (t_method)pdp_binary_free, sizeof(t_pdp_binary), 0, A_NULL);
+
+ class_addmethod(pdp_binary_class, (t_method)pdp_binary_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_binary_class, (t_method)pdp_binary_y, gensym("Y"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_binary_class, (t_method)pdp_binary_u, gensym("U"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_binary_class, (t_method)pdp_binary_v, gensym("V"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_binary_class, (t_method)pdp_binary_cursx, gensym("cursx"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_binary_class, (t_method)pdp_binary_cursy, gensym("cursy"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_binary_class, (t_method)pdp_binary_pick, gensym("pick"), A_NULL);
+ class_addmethod(pdp_binary_class, (t_method)pdp_binary_tolerance, gensym("tolerance"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_binary_class, (t_method)pdp_binary_setcur, gensym("setcur"), A_DEFFLOAT, A_DEFFLOAT, A_NULL);
+ class_sethelpsymbol( pdp_binary_class, gensym("pdp_binary.pd") );
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_cropper.c b/modules/pdp_cropper.c
new file mode 100644
index 0000000..f1c3004
--- /dev/null
+++ b/modules/pdp_cropper.c
@@ -0,0 +1,291 @@
+/*
+ * PiDiP module.
+ * Copyright (c) by Yves Degoyon (ydegoyon@free.fr)
+ *
+ * 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 object is a video cropper
+ * Written by Yves Degoyon
+ */
+
+
+
+#include "pdp.h"
+#include <math.h>
+
+static char *pdp_cropper_version = "pdp_cropper: a video cropper, version 0.1, written by Yves Degoyon (ydegoyon@free.fr)";
+
+typedef struct pdp_cropper_struct
+{
+ t_object x_obj;
+ t_float x_f;
+
+ t_outlet *x_outlet0;
+ t_int x_packet0;
+ t_int x_packet1;
+ t_int x_dropped;
+ t_int x_queue_id;
+
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+ t_int x_csizex;
+ t_int x_csizey;
+ t_int x_csizev;
+ unsigned int x_encoding;
+
+ int x_cropx1;
+ int x_cropx2;
+ int x_cropy1;
+ int x_cropy2;
+
+} t_pdp_cropper;
+
+static void pdp_cropper_cropx1(t_pdp_cropper *x, t_floatarg fcropx1 )
+{
+ if ( ( fcropx1>=0 ) && ( fcropx1<x->x_vwidth ) )
+ {
+ x->x_cropx1 = (t_int)fcropx1;
+ }
+}
+
+static void pdp_cropper_cropx2(t_pdp_cropper *x, t_floatarg fcropx2 )
+{
+ if ( ( fcropx2>=0 ) && ( fcropx2<x->x_vwidth ) )
+ {
+ x->x_cropx2 = (t_int)fcropx2;
+ }
+}
+
+static void pdp_cropper_cropy1(t_pdp_cropper *x, t_floatarg fcropy1 )
+{
+ if ( ( fcropy1>=0 ) && ( fcropy1<x->x_vheight ) )
+ {
+ x->x_cropy1 = (t_int)fcropy1;
+ }
+}
+
+static void pdp_cropper_cropy2(t_pdp_cropper *x, t_floatarg fcropy2 )
+{
+ if ( ( fcropy2>=0 ) && ( fcropy2<x->x_vheight ) )
+ {
+ x->x_cropy2 = (t_int)fcropy2;
+ }
+}
+
+static void pdp_cropper_process_yv12(t_pdp_cropper *x)
+{
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ short int *data = (short int *)pdp_packet_data(x->x_packet0);
+ t_pdp *newheader = NULL;
+ short int *newdata = NULL;
+ int i;
+
+ int px, py;
+ short int *pY, *pU, *pV;
+ short int *pnY, *pnU, *pnV;
+ int minx, maxx;
+ int miny, maxy;
+
+ /* allocate all ressources */
+ if ( ( (t_int)header->info.image.width != x->x_vwidth ) ||
+ ( (t_int)header->info.image.height != x->x_vheight ) )
+ {
+ x->x_vwidth = header->info.image.width;
+ x->x_vheight = header->info.image.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+ if ( ( x->x_cropx1 <0 ) || ( x->x_cropx1 >= x->x_vwidth ) ) x->x_cropx1 = 0;
+ if ( ( x->x_cropx2 <0 ) || ( x->x_cropx2 >= x->x_vwidth ) ) x->x_cropx2 = x->x_vwidth-1;
+ if ( ( x->x_cropy1 <0 ) || ( x->x_cropy1 >= x->x_vheight ) ) x->x_cropy1 = 0;
+ if ( ( x->x_cropy2 <0 ) || ( x->x_cropy2 >= x->x_vheight ) ) x->x_cropy2 = x->x_vheight-1;
+ }
+
+ x->x_csizex = abs ( x->x_cropx2 - x->x_cropx1 );
+ if ( x->x_csizex%8 != 0 ) x->x_csizex = x->x_csizex + (8-(x->x_csizex%8)); // align on 8
+ x->x_csizey = abs ( x->x_cropy2 - x->x_cropy1 );
+ if ( x->x_csizey%8 != 0 ) x->x_csizey = x->x_csizey + (8-(x->x_csizey%8)); // align on 8
+ if ( x->x_csizex == 0 ) x->x_csizex = 8;
+ if ( x->x_csizey == 0 ) x->x_csizey = 8;
+ // post( "pdp_cropper : new image %dx%d", x->x_csizex, x->x_csizey );
+
+ x->x_csizev = x->x_csizex*x->x_csizey;
+ x->x_packet1 = pdp_packet_new_image_YCrCb( x->x_csizex, x->x_csizey );
+ newheader = pdp_packet_header(x->x_packet1);
+ newdata = (short int *)pdp_packet_data(x->x_packet1);
+
+ newheader->info.image.encoding = header->info.image.encoding;
+ newheader->info.image.width = x->x_csizex;
+ newheader->info.image.height = x->x_csizey;
+
+ pY = data;
+ pU = (data+x->x_vsize);
+ pV = (data+x->x_vsize+(x->x_vsize>>2));
+ pnY = newdata;
+ pnU = (newdata+x->x_csizev);
+ pnV = (newdata+x->x_csizev+(x->x_csizev>>2));
+
+ if ( x->x_cropx1<x->x_cropx2 )
+ {
+ minx = x->x_cropx1;
+ maxx = x->x_cropx2;
+ }
+ else
+ {
+ minx = x->x_cropx2;
+ maxx = x->x_cropx1;
+ }
+
+ if ( x->x_cropy1<x->x_cropy2 )
+ {
+ miny = x->x_cropy1;
+ maxy = x->x_cropy2;
+ }
+ else
+ {
+ miny = x->x_cropy2;
+ maxy = x->x_cropy1;
+ }
+
+ for(py=miny; py<maxy; py++)
+ {
+ for(px=minx; px<maxx; px++)
+ {
+ *(pnY+(py-miny)*x->x_csizex+(px-minx)) = *(pY+py*x->x_vwidth+px);
+ if ( (py%2==0) && (px%2==0) )
+ {
+ *(pnU+((py-miny)>>1)*(x->x_csizex>>1)+((px-minx)>>1)) = *(pU+(py>>1)*(x->x_vwidth>>1)+(px>>1));
+ *(pnV+((py-miny)>>1)*(x->x_csizex>>1)+((px-minx)>>1)) = *(pV+(py>>1)*(x->x_vwidth>>1)+(px>>1));
+ }
+ }
+ }
+
+ return;
+}
+
+static void pdp_cropper_sendpacket(t_pdp_cropper *x)
+{
+ /* release the packet */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+
+ /* unregister and propagate if valid dest packet */
+ pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet1);
+}
+
+static void pdp_cropper_process(t_pdp_cropper *x)
+{
+ int encoding;
+ t_pdp *header = 0;
+
+ /* check if image data packets are compatible */
+ if ( (header = pdp_packet_header(x->x_packet0))
+ && (PDP_IMAGE == header->type)){
+
+ /* pdp_cropper_process inputs and write into active inlet */
+ switch(pdp_packet_header(x->x_packet0)->info.image.encoding){
+
+ case PDP_IMAGE_YV12:
+ pdp_queue_add(x, pdp_cropper_process_yv12, pdp_cropper_sendpacket, &x->x_queue_id);
+ break;
+
+ case PDP_IMAGE_GREY:
+ // pdp_cropper_process_packet(x);
+ break;
+
+ default:
+ /* don't know the type, so dont pdp_cropper_process */
+ break;
+
+ }
+ }
+}
+
+static void pdp_cropper_input_0(t_pdp_cropper *x, t_symbol *s, t_floatarg f)
+{
+ /* if this is a register_ro message or register_rw message, register with packet factory */
+
+ if (s== gensym("register_rw"))
+ {
+ x->x_dropped =
+ pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") );
+ }
+
+ if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped))
+ {
+ /* add the process method and callback to the process queue */
+ pdp_cropper_process(x);
+ }
+}
+
+static void pdp_cropper_free(t_pdp_cropper *x)
+{
+ int i;
+
+ pdp_queue_finish(x->x_queue_id);
+ pdp_packet_mark_unused(x->x_packet0);
+}
+
+t_class *pdp_cropper_class;
+
+void *pdp_cropper_new(void)
+{
+ int i;
+
+ t_pdp_cropper *x = (t_pdp_cropper *)pd_new(pdp_cropper_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("x1"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("x2"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("y1"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("y2"));
+
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_packet0 = -1;
+ x->x_packet1 = -1;
+ x->x_queue_id = -1;
+
+ x->x_cropx1=-1;
+ x->x_cropx2=-1;
+ x->x_cropy1=-1;
+ x->x_cropy2=-1;
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_cropper_setup(void)
+{
+// post( pdp_cropper_version );
+ pdp_cropper_class = class_new(gensym("pdp_cropper"), (t_newmethod)pdp_cropper_new,
+ (t_method)pdp_cropper_free, sizeof(t_pdp_cropper), 0, A_NULL);
+
+ class_addmethod(pdp_cropper_class, (t_method)pdp_cropper_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_cropper_class, (t_method)pdp_cropper_cropx1, gensym("x1"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_cropper_class, (t_method)pdp_cropper_cropx2, gensym("x2"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_cropper_class, (t_method)pdp_cropper_cropy1, gensym("y1"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_cropper_class, (t_method)pdp_cropper_cropy2, gensym("y2"), A_FLOAT, A_NULL);
+ class_sethelpsymbol( pdp_cropper_class, gensym("pdp_cropper.pd") );
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_dilate.c b/modules/pdp_dilate.c
new file mode 100644
index 0000000..062f452
--- /dev/null
+++ b/modules/pdp_dilate.c
@@ -0,0 +1,281 @@
+/*
+ * PiDiP module
+ * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr )
+ *
+ * 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 object is a morphological operator for dilation using as a structuring element a WxH square
+ */
+
+#include "pdp.h"
+#include "yuv.h"
+#include <math.h>
+#include <stdio.h>
+
+static char *pdp_dilate_version = "pdp_dilate: morphology : dilation version 0.1 written by Yves Degoyon (ydegoyon@free.fr)";
+
+typedef struct pdp_dilate_struct
+{
+ t_object x_obj;
+
+ t_int x_packet0;
+ t_int x_packet1;
+ t_int x_queue_id;
+ t_int x_dropped;
+
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+ t_int x_kernelw; // width of the (square) kernel
+ t_int x_kernelh; // height of the square kernel
+ t_int x_nbpasses; // number of passes
+ short int *x_frame; // keep a copy of current frame for transformations
+
+ t_outlet *x_pdp_output; // output packets
+
+} t_pdp_dilate;
+
+static void pdp_dilate_nbpasses(t_pdp_dilate *x, t_floatarg fpasses )
+{
+ if ( fpasses>=1.)
+ {
+ x->x_nbpasses = (t_int)fpasses;
+ }
+}
+
+static void pdp_dilate_kernelw(t_pdp_dilate *x, t_floatarg fkernelw )
+{
+ if ( fkernelw>=0.)
+ {
+ x->x_kernelw = (t_int)fkernelw;
+ }
+}
+
+static void pdp_dilate_kernelh(t_pdp_dilate *x, t_floatarg fkernelh )
+{
+ if ( fkernelh>=0.)
+ {
+ x->x_kernelh = (t_int)fkernelh;
+ }
+}
+
+static void pdp_dilate_allocate(t_pdp_dilate *x)
+{
+ x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+
+ if ( !x->x_frame )
+ {
+ post( "pdp_dilate : severe error : cannot allocate buffer !!! ");
+ return;
+ }
+}
+
+static void pdp_dilate_free_ressources(t_pdp_dilate *x)
+{
+ if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+}
+
+static void pdp_dilate_process_yv12(t_pdp_dilate *x)
+{
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ short int *data = (short int *)pdp_packet_data(x->x_packet0);
+ t_pdp *newheader = pdp_packet_header(x->x_packet1);
+ short int *newdata = (short int *)pdp_packet_data(x->x_packet1);
+ t_int i;
+ t_int px=0, py=0;
+ short int *pfY, *pfU, *pfV;
+ t_int ppx, ppy, ix, iy, pn, kx, ky;
+
+ // allocate all ressources
+ if ( ( (int)header->info.image.width != x->x_vwidth ) ||
+ ( (int)header->info.image.height != x->x_vheight ) )
+ {
+ pdp_dilate_free_ressources( x );
+ x->x_vwidth = header->info.image.width;
+ x->x_vheight = header->info.image.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+ pdp_dilate_allocate( x );
+ post( "pdp_dilate : reallocated buffers" );
+ }
+
+ // post( "pdp_dilate : newheader:%x", newheader );
+
+ newheader->info.image.encoding = header->info.image.encoding;
+ newheader->info.image.width = x->x_vwidth;
+ newheader->info.image.height = x->x_vheight;
+
+ memcpy( newdata, data, x->x_vsize+(x->x_vsize>>1)<<1 );
+ memcpy( x->x_frame, data, x->x_vsize+(x->x_vsize>>1)<<1 );
+
+ // dilate (supposedly) binary image by using a 3x3 square as a structuring element
+ pfY = x->x_frame;
+ pfV = x->x_frame+x->x_vsize;
+ pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2);
+ kx = (x->x_kernelw/2);
+ ky = (x->x_kernelh/2);
+ for ( pn=0; pn<x->x_nbpasses; pn++ )
+ {
+ memcpy( x->x_frame, newdata, x->x_vsize+(x->x_vsize>>1)<<1 );
+ for ( py=0; py<x->x_vheight; py++ )
+ {
+ for ( px=0; px<x->x_vwidth; px++ )
+ {
+ if ( *(pfY+py*x->x_vwidth+px) == 0 )
+ {
+ for (ix=-kx; ix<=kx; ix++)
+ {
+ ppx=px+ix;
+ if ( (ppx>=0) && (ppx<x->x_vwidth) )
+ {
+ for (iy=-ky; iy<=ky; iy++)
+ {
+ ppy=py+iy;
+ if ( (ppy>=0) && (ppy<x->x_vheight) )
+ {
+ if( *(pfY+ppy*x->x_vwidth+ppx) == ((255)<<7) )
+ {
+ *(newdata+py*x->x_vwidth+px) = ((255)<<7);
+ break;
+ }
+ }
+ }
+ if ( *(newdata+py*x->x_vwidth+px) == ((255)<<7) ) break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+static void pdp_dilate_sendpacket(t_pdp_dilate *x)
+{
+ /* release the packet */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+
+ /* unregister and propagate if valid dest packet */
+ pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1);
+}
+
+static void pdp_dilate_process(t_pdp_dilate *x)
+{
+ int encoding;
+ t_pdp *header = 0;
+
+ /* check if image data packets are compatible */
+ if ( (header = pdp_packet_header(x->x_packet0))
+ && (PDP_IMAGE == header->type)){
+
+ /* pdp_dilate_process inputs and write into active inlet */
+ switch(pdp_packet_header(x->x_packet0)->info.image.encoding)
+ {
+
+ case PDP_IMAGE_YV12:
+ x->x_packet1 = pdp_packet_clone_rw(x->x_packet0);
+ pdp_queue_add(x, pdp_dilate_process_yv12, pdp_dilate_sendpacket, &x->x_queue_id);
+ break;
+
+ case PDP_IMAGE_GREY:
+ // should write something to handle these one day
+ // but i don't use this mode
+ break;
+
+ default:
+ /* don't know the type, so dont pdp_dilate_process */
+ break;
+
+ }
+ }
+
+}
+
+static void pdp_dilate_input_0(t_pdp_dilate *x, t_symbol *s, t_floatarg f)
+{
+ /* if this is a register_ro message or register_rw message, register with packet factory */
+
+ if (s== gensym("register_rw"))
+ {
+ x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") );
+ }
+
+ if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped))
+ {
+ pdp_dilate_process(x);
+ }
+}
+
+static void pdp_dilate_free(t_pdp_dilate *x)
+{
+ int i;
+
+ pdp_packet_mark_unused(x->x_packet0);
+ pdp_dilate_free_ressources( x );
+}
+
+t_class *pdp_dilate_class;
+
+void *pdp_dilate_new(void)
+{
+ int i;
+
+ t_pdp_dilate *x = (t_pdp_dilate *)pd_new(pdp_dilate_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("nbpasses"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelw"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelh"));
+
+ x->x_pdp_output = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_packet0 = -1;
+ x->x_packet1 = -1;
+ x->x_vwidth = -1;
+ x->x_vheight = -1;
+ x->x_vsize = -1;
+ x->x_frame = NULL;
+ x->x_kernelw = 3;
+ x->x_kernelh = 3;
+
+ x->x_nbpasses = 1;
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_dilate_setup(void)
+{
+ // post( pdp_dilate_version );
+ pdp_dilate_class = class_new(gensym("pdp_dilate"), (t_newmethod)pdp_dilate_new,
+ (t_method)pdp_dilate_free, sizeof(t_pdp_dilate), 0, A_NULL);
+
+ class_addmethod(pdp_dilate_class, (t_method)pdp_dilate_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_dilate_class, (t_method)pdp_dilate_nbpasses, gensym("nbpasses"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_dilate_class, (t_method)pdp_dilate_kernelw, gensym("kernelw"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_dilate_class, (t_method)pdp_dilate_kernelh, gensym("kernelh"), A_FLOAT, A_NULL);
+ class_sethelpsymbol( pdp_dilate_class, gensym("pdp_dilate.pd") );
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_disintegration.c b/modules/pdp_disintegration.c
new file mode 100644
index 0000000..93eeec7
--- /dev/null
+++ b/modules/pdp_disintegration.c
@@ -0,0 +1,280 @@
+/*
+ * PiDiP module
+ * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr )
+ *
+ * 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 object is a piksels sum-up operator
+ */
+
+#include "pdp.h"
+#include "yuv.h"
+#include <math.h>
+#include <stdio.h>
+
+static char *pdp_disintegration_version = "pdp_disintegration: piksels sum-up version 0.1 written by Yves Degoyon (ydegoyon@free.fr)";
+
+typedef struct pdp_disintegration_struct
+{
+ t_object x_obj;
+
+ t_int x_packet0;
+ t_int x_packet1;
+ t_int x_queue_id;
+ t_int x_dropped;
+
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+ t_int x_nbpasses; // number of passes
+ t_int x_reductor; // fraction reductor
+ short int *x_frame; // keep a copy of current frame for transformations
+
+ t_outlet *x_pdp_output; // output packets
+
+} t_pdp_disintegration;
+
+static void pdp_disintegration_nbpasses(t_pdp_disintegration *x, t_floatarg fpasses )
+{
+ if ( fpasses>=1.)
+ {
+ x->x_nbpasses = (t_int)fpasses;
+ }
+}
+
+static void pdp_disintegration_reductor(t_pdp_disintegration *x, t_floatarg freductor )
+{
+ if ( freductor>=1.)
+ {
+ x->x_reductor = (t_int)freductor;
+ }
+}
+
+static void pdp_disintegration_allocate(t_pdp_disintegration *x)
+{
+ x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+
+ if ( !x->x_frame )
+ {
+ post( "pdp_disintegration : severe error : cannot allocate buffer !!! ");
+ return;
+ }
+}
+
+static void pdp_disintegration_free_ressources(t_pdp_disintegration *x)
+{
+ if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+}
+
+static void pdp_disintegration_process_yv12(t_pdp_disintegration *x)
+{
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ short int *data = (short int *)pdp_packet_data(x->x_packet0);
+ t_pdp *newheader = pdp_packet_header(x->x_packet1);
+ short int *newdata = (short int *)pdp_packet_data(x->x_packet1);
+ t_int i;
+ t_int px=0, py=0;
+ short int *pfY, *pfU, *pfV;
+ t_int ppx, ppy, ix, iy, pn;
+ t_int nvalue;
+
+ // allocate all ressources
+ if ( ( (int)header->info.image.width != x->x_vwidth ) ||
+ ( (int)header->info.image.height != x->x_vheight ) )
+ {
+ pdp_disintegration_free_ressources( x );
+ x->x_vwidth = header->info.image.width;
+ x->x_vheight = header->info.image.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+ pdp_disintegration_allocate( x );
+ post( "pdp_disintegration : reallocated buffers" );
+ }
+
+ // post( "pdp_disintegration : newheader:%x", newheader );
+
+ newheader->info.image.encoding = header->info.image.encoding;
+ newheader->info.image.width = x->x_vwidth;
+ newheader->info.image.height = x->x_vheight;
+
+ memcpy( newdata, data, x->x_vsize+(x->x_vsize>>1)<<1 );
+ memcpy( x->x_frame, data, x->x_vsize+(x->x_vsize>>1)<<1 );
+
+ pfY = x->x_frame;
+ pfV = x->x_frame+x->x_vsize;
+ pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2);
+
+ // foreground piksels are now 1
+ for ( py=0; py<x->x_vheight; py++ )
+ {
+ for ( px=0; px<x->x_vwidth; px++ )
+ {
+ if ( *(pfY+py*x->x_vwidth+px) == ((255)<<7) )
+ {
+ *(pfY+py*x->x_vwidth+px) = ((1)<<7);
+ }
+ }
+ }
+
+ for ( pn=0; pn<x->x_nbpasses; pn++ )
+ {
+ memcpy( x->x_frame, newdata, x->x_vsize+(x->x_vsize>>1)<<1 );
+ for ( py=0; py<x->x_vheight; py++ )
+ {
+ for ( px=0; px<x->x_vwidth; px++ )
+ {
+ nvalue = 0;
+ for (ix=-1; ix<=1; ix++)
+ {
+ ppx=px+ix;
+ if ( (ppx>=0) && (ppx<x->x_vwidth) )
+ {
+ for (iy=-1; iy<=1; iy++)
+ {
+ ppy=py+iy;
+ if ( (ppy>=0) && (ppy<x->x_vheight) )
+ {
+ nvalue += *(pfY+ppy*x->x_vwidth+ppx);
+ }
+ }
+ }
+ }
+ if ( nvalue > ((255)<<7)*9 )
+ {
+ *(newdata+py*x->x_vwidth+px) = ((255)<<7);
+ }
+ else
+ {
+ *(newdata+py*x->x_vwidth+px) = nvalue/x->x_reductor;
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+static void pdp_disintegration_sendpacket(t_pdp_disintegration *x)
+{
+ /* release the packet */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+
+ /* unregister and propagate if valid dest packet */
+ pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1);
+}
+
+static void pdp_disintegration_process(t_pdp_disintegration *x)
+{
+ int encoding;
+ t_pdp *header = 0;
+
+ /* check if image data packets are compatible */
+ if ( (header = pdp_packet_header(x->x_packet0))
+ && (PDP_IMAGE == header->type)){
+
+ /* pdp_disintegration_process inputs and write into active inlet */
+ switch(pdp_packet_header(x->x_packet0)->info.image.encoding)
+ {
+
+ case PDP_IMAGE_YV12:
+ x->x_packet1 = pdp_packet_clone_rw(x->x_packet0);
+ pdp_queue_add(x, pdp_disintegration_process_yv12, pdp_disintegration_sendpacket, &x->x_queue_id);
+ break;
+
+ case PDP_IMAGE_GREY:
+ // should write something to handle these one day
+ // but i don't use this mode
+ break;
+
+ default:
+ /* don't know the type, so dont pdp_disintegration_process */
+ break;
+
+ }
+ }
+
+}
+
+static void pdp_disintegration_input_0(t_pdp_disintegration *x, t_symbol *s, t_floatarg f)
+{
+ /* if this is a register_ro message or register_rw message, register with packet factory */
+
+ if (s== gensym("register_rw"))
+ {
+ x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") );
+ }
+
+ if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped))
+ {
+ pdp_disintegration_process(x);
+ }
+}
+
+static void pdp_disintegration_free(t_pdp_disintegration *x)
+{
+ int i;
+
+ pdp_packet_mark_unused(x->x_packet0);
+ pdp_disintegration_free_ressources( x );
+}
+
+t_class *pdp_disintegration_class;
+
+void *pdp_disintegration_new(void)
+{
+ int i;
+
+ t_pdp_disintegration *x = (t_pdp_disintegration *)pd_new(pdp_disintegration_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("nbpasses"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("reductor"));
+
+ x->x_pdp_output = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_packet0 = -1;
+ x->x_packet1 = -1;
+ x->x_vwidth = -1;
+ x->x_vheight = -1;
+ x->x_vsize = -1;
+ x->x_frame = NULL;
+ x->x_nbpasses = 3;
+ x->x_reductor = 5;
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_disintegration_setup(void)
+{
+ // post( pdp_disintegration_version );
+ pdp_disintegration_class = class_new(gensym("pdp_disintegration"), (t_newmethod)pdp_disintegration_new,
+ (t_method)pdp_disintegration_free, sizeof(t_pdp_disintegration), 0, A_NULL);
+
+ class_addmethod(pdp_disintegration_class, (t_method)pdp_disintegration_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_disintegration_class, (t_method)pdp_disintegration_nbpasses, gensym("nbpasses"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_disintegration_class, (t_method)pdp_disintegration_reductor, gensym("reductor"), A_FLOAT, A_NULL);
+ class_sethelpsymbol( pdp_disintegration_class, gensym("pdp_disintegration.pd") );
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_distance.c b/modules/pdp_distance.c
new file mode 100644
index 0000000..34b3ddf
--- /dev/null
+++ b/modules/pdp_distance.c
@@ -0,0 +1,337 @@
+/*
+ * PiDiP module
+ * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr )
+ *
+ * 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 object is a salience distance operator
+ * ( inspired by Paul Rosin, 91, http://www.cs.cf.ac.uk/User/Paul.Rosin/resources/sdt/ )
+ */
+
+#include "pdp.h"
+#include "yuv.h"
+#include <math.h>
+#include <stdio.h>
+
+static char *pdp_distance_version = "pdp_distance: morphology : distance operator version 0.1 written by Yves Degoyon (ydegoyon@free.fr)";
+
+typedef struct pdp_distance_struct
+{
+ t_object x_obj;
+
+ t_int x_packet0;
+ t_int x_packet1;
+ t_int x_queue_id;
+ t_int x_dropped;
+
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+ short int *x_frame; // keep a copy of current frame for transformations
+
+ t_int x_coeff1;
+ t_int x_coeff2;
+ t_int x_coeff3;
+ t_int x_coeff4;
+
+ t_outlet *x_pdp_output; // output packets
+
+} t_pdp_distance;
+
+static void pdp_distance_allocate(t_pdp_distance *x)
+{
+ x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+
+ if ( !x->x_frame )
+ {
+ post( "pdp_distance : severe error : cannot allocate buffer !!! ");
+ return;
+ }
+}
+
+static void pdp_distance_coeff1(t_pdp_distance *x, t_floatarg fcoeff1 )
+{
+ x->x_coeff1 = (int) fcoeff1;
+}
+
+static void pdp_distance_coeff2(t_pdp_distance *x, t_floatarg fcoeff2 )
+{
+ x->x_coeff2 = (int) fcoeff2;
+}
+
+static void pdp_distance_coeff3(t_pdp_distance *x, t_floatarg fcoeff3 )
+{
+ x->x_coeff3 = (int) fcoeff3;
+}
+
+static void pdp_distance_coeff4(t_pdp_distance *x, t_floatarg fcoeff4 )
+{
+ x->x_coeff4 = (int) fcoeff4;
+}
+
+static void pdp_distance_free_ressources(t_pdp_distance *x)
+{
+ if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+}
+
+static void pdp_distance_process_yv12(t_pdp_distance *x)
+{
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ short int *data = (short int *)pdp_packet_data(x->x_packet0);
+ t_pdp *newheader = pdp_packet_header(x->x_packet1);
+ short int *newdata = (short int *)pdp_packet_data(x->x_packet1);
+ t_int i;
+ t_int px=0, py=0;
+ short int *pfY, *pfU, *pfV;
+ t_int nvalues, values[5], ival, mval;
+
+ // allocate all ressources
+ if ( ( (int)header->info.image.width != x->x_vwidth ) ||
+ ( (int)header->info.image.height != x->x_vheight ) )
+ {
+ pdp_distance_free_ressources( x );
+ x->x_vwidth = header->info.image.width;
+ x->x_vheight = header->info.image.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+ pdp_distance_allocate( x );
+ post( "pdp_distance : reallocated buffers" );
+ }
+
+ // post( "pdp_distance : newheader:%x", newheader );
+
+ newheader->info.image.encoding = header->info.image.encoding;
+ newheader->info.image.width = x->x_vwidth;
+ newheader->info.image.height = x->x_vheight;
+
+ memcpy( newdata, data, x->x_vsize+(x->x_vsize>>1)<<1 );
+ memcpy( x->x_frame, data, x->x_vsize+(x->x_vsize>>1)<<1 );
+
+ pfY = x->x_frame;
+ pfV = x->x_frame+x->x_vsize;
+ pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2);
+
+ // thresholding
+ for ( py=0; py<x->x_vheight; py++ )
+ {
+ for ( px=0; px<x->x_vwidth; px++ )
+ {
+ if ( *(pfY+py*x->x_vwidth+px) > ((128)<<7) )
+ {
+ *(pfY+py*x->x_vwidth+px) = ((128)<<7);
+ }
+ }
+ }
+
+ // forward pass
+ for ( py=0; py<x->x_vheight; py++ )
+ {
+ for ( px=0; px<x->x_vwidth; px++ )
+ {
+ nvalues = 0;
+ if ( ((px-1)>=0) && ((py-1)>=0) )
+ {
+ values[nvalues] = *(pfY+(py-1)*x->x_vwidth+(px-1)) + (x->x_coeff1<<7);
+ nvalues++;
+ }
+ if ( (py-1)>=0 )
+ {
+ values[nvalues] = *(pfY+(py-1)*x->x_vwidth+px) + (x->x_coeff2<<7);
+ nvalues++;
+ }
+ if ( ((px+1)<x->x_vwidth) && ((py-1)>=0) )
+ {
+ values[nvalues] = *(pfY+(py-1)*x->x_vwidth+(px+1)) + (x->x_coeff3<<7);
+ nvalues++;
+ }
+ if ( (px-1)>=0 )
+ {
+ values[nvalues] = *(pfY+py*x->x_vwidth+(px-1)) + (x->x_coeff4<<7);
+ nvalues++;
+ }
+ values[nvalues] = *(pfY+py*x->x_vwidth+px);
+ nvalues++;
+
+ mval = values[0];
+ for (ival=0; ival<nvalues; ival++)
+ {
+ if (values[ival]<mval) mval=values[ival];
+ }
+ *(x->x_frame+py*x->x_vwidth+px)=mval;
+ }
+ }
+
+ // backward pass
+ for ( py=x->x_vheight-1; py>=0; py-- )
+ {
+ for ( px=x->x_vwidth-1; px>=0; px-- )
+ {
+ nvalues = 0;
+ if ( ((px-1)>=0) && ((py+1)<x->x_vheight) )
+ {
+ values[nvalues] = *(pfY+(py+1)*x->x_vwidth+(px-1)) + (x->x_coeff1<<7);
+ nvalues++;
+ }
+ if ( (py+1)<x->x_vheight )
+ {
+ values[nvalues] = *(pfY+(py+1)*x->x_vwidth+px) + (x->x_coeff2<<7);
+ nvalues++;
+ }
+ if ( ((px+1)<x->x_vwidth) && ((py+1)<x->x_vheight) )
+ {
+ values[nvalues] = *(pfY+(py+1)*x->x_vwidth+(px+1)) + (x->x_coeff3<<7);
+ nvalues++;
+ }
+ if ( (px+1)<x->x_vwidth )
+ {
+ values[nvalues] = *(pfY+py*x->x_vwidth+(px+1)) + (x->x_coeff4<<7);
+ nvalues++;
+ }
+ values[nvalues] = *(pfY+py*x->x_vwidth+px);
+ nvalues++;
+
+ mval = values[0];
+ for (ival=0; ival<nvalues; ival++)
+ {
+ if (values[ival]<mval) mval=values[ival];
+ }
+ *(x->x_frame+py*x->x_vwidth+px)=mval;
+ }
+ }
+
+ memcpy( newdata, x->x_frame, x->x_vsize+(x->x_vsize>>1)<<1 );
+
+ return;
+}
+
+static void pdp_distance_sendpacket(t_pdp_distance *x)
+{
+ /* release the packet */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+
+ /* unregister and propagate if valid dest packet */
+ pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1);
+}
+
+static void pdp_distance_process(t_pdp_distance *x)
+{
+ int encoding;
+ t_pdp *header = 0;
+
+ /* check if image data packets are compatible */
+ if ( (header = pdp_packet_header(x->x_packet0))
+ && (PDP_IMAGE == header->type)){
+
+ /* pdp_distance_process inputs and write into active inlet */
+ switch(pdp_packet_header(x->x_packet0)->info.image.encoding)
+ {
+
+ case PDP_IMAGE_YV12:
+ x->x_packet1 = pdp_packet_clone_rw(x->x_packet0);
+ pdp_queue_add(x, pdp_distance_process_yv12, pdp_distance_sendpacket, &x->x_queue_id);
+ break;
+
+ case PDP_IMAGE_GREY:
+ // should write something to handle these one day
+ // but i don't use this mode
+ break;
+
+ default:
+ /* don't know the type, so dont pdp_distance_process */
+ break;
+
+ }
+ }
+
+}
+
+static void pdp_distance_input_0(t_pdp_distance *x, t_symbol *s, t_floatarg f)
+{
+ /* if this is a register_ro message or register_rw message, register with packet factory */
+
+ if (s== gensym("register_rw"))
+ {
+ x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") );
+ }
+
+ if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped))
+ {
+ pdp_distance_process(x);
+ }
+}
+
+static void pdp_distance_free(t_pdp_distance *x)
+{
+ int i;
+
+ pdp_packet_mark_unused(x->x_packet0);
+ pdp_distance_free_ressources( x );
+}
+
+t_class *pdp_distance_class;
+
+void *pdp_distance_new(void)
+{
+ int i;
+
+ t_pdp_distance *x = (t_pdp_distance *)pd_new(pdp_distance_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("coeff1"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("coeff2"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("coeff3"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("coeff4"));
+
+ x->x_pdp_output = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_packet0 = -1;
+ x->x_packet1 = -1;
+ x->x_vwidth = -1;
+ x->x_vheight = -1;
+ x->x_vsize = -1;
+ x->x_frame = NULL;
+
+ x->x_coeff1 = 4;
+ x->x_coeff2 = 3;
+ x->x_coeff3 = 4;
+ x->x_coeff4 = 3;
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_distance_setup(void)
+{
+ // post( pdp_distance_version );
+ pdp_distance_class = class_new(gensym("pdp_distance"), (t_newmethod)pdp_distance_new,
+ (t_method)pdp_distance_free, sizeof(t_pdp_distance), 0, A_NULL);
+
+ class_addmethod(pdp_distance_class, (t_method)pdp_distance_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_distance_class, (t_method)pdp_distance_coeff1, gensym("coeff1"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_distance_class, (t_method)pdp_distance_coeff2, gensym("coeff2"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_distance_class, (t_method)pdp_distance_coeff3, gensym("coeff3"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_distance_class, (t_method)pdp_distance_coeff4, gensym("coeff4"), A_DEFFLOAT, A_NULL);
+ class_sethelpsymbol( pdp_distance_class, gensym("pdp_distance.pd") );
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_erode.c b/modules/pdp_erode.c
new file mode 100644
index 0000000..a843308
--- /dev/null
+++ b/modules/pdp_erode.c
@@ -0,0 +1,281 @@
+/*
+ * PiDiP module
+ * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr )
+ *
+ * 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 object is a morphological operator for erosion using as a structuring element a WxH square
+ */
+
+#include "pdp.h"
+#include "yuv.h"
+#include <math.h>
+#include <stdio.h>
+
+static char *pdp_erode_version = "pdp_erode: morphology : erosion version 0.1 written by Yves Degoyon (ydegoyon@free.fr)";
+
+typedef struct pdp_erode_struct
+{
+ t_object x_obj;
+
+ t_int x_packet0;
+ t_int x_packet1;
+ t_int x_queue_id;
+ t_int x_dropped;
+
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+ t_int x_kernelw; // width of the (square) kernel
+ t_int x_kernelh; // height of the square kernel
+ t_int x_nbpasses; // number of passes
+ short int *x_frame; // keep a copy of current frame for transformations
+
+ t_outlet *x_pdp_output; // output packets
+
+} t_pdp_erode;
+
+static void pdp_erode_nbpasses(t_pdp_erode *x, t_floatarg fpasses )
+{
+ if ( fpasses>=1.)
+ {
+ x->x_nbpasses = (t_int)fpasses;
+ }
+}
+
+static void pdp_erode_kernelw(t_pdp_erode *x, t_floatarg fkernelw )
+{
+ if ( fkernelw>=0.)
+ {
+ x->x_kernelw = (t_int)fkernelw;
+ }
+}
+
+static void pdp_erode_kernelh(t_pdp_erode *x, t_floatarg fkernelh )
+{
+ if ( fkernelh>=0.)
+ {
+ x->x_kernelh = (t_int)fkernelh;
+ }
+}
+
+static void pdp_erode_allocate(t_pdp_erode *x)
+{
+ x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+
+ if ( !x->x_frame )
+ {
+ post( "pdp_erode : severe error : cannot allocate buffer !!! ");
+ return;
+ }
+}
+
+static void pdp_erode_free_ressources(t_pdp_erode *x)
+{
+ if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+}
+
+static void pdp_erode_process_yv12(t_pdp_erode *x)
+{
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ short int *data = (short int *)pdp_packet_data(x->x_packet0);
+ t_pdp *newheader = pdp_packet_header(x->x_packet1);
+ short int *newdata = (short int *)pdp_packet_data(x->x_packet1);
+ t_int i;
+ t_int px=0, py=0;
+ short int *pfY, *pfU, *pfV;
+ t_int ppx, ppy, ix, iy, pn, kx, ky;
+
+ // allocate all ressources
+ if ( ( (int)header->info.image.width != x->x_vwidth ) ||
+ ( (int)header->info.image.height != x->x_vheight ) )
+ {
+ pdp_erode_free_ressources( x );
+ x->x_vwidth = header->info.image.width;
+ x->x_vheight = header->info.image.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+ pdp_erode_allocate( x );
+ post( "pdp_erode : reallocated buffers" );
+ }
+
+ // post( "pdp_erode : newheader:%x", newheader );
+
+ newheader->info.image.encoding = header->info.image.encoding;
+ newheader->info.image.width = x->x_vwidth;
+ newheader->info.image.height = x->x_vheight;
+
+ memcpy( newdata, data, x->x_vsize+(x->x_vsize>>1)<<1 );
+ memcpy( x->x_frame, data, x->x_vsize+(x->x_vsize>>1)<<1 );
+
+ // erode (supposedly) binary image by using a 3x3 square as a structuring element
+ pfY = x->x_frame;
+ pfV = x->x_frame+x->x_vsize;
+ pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2);
+ kx = (x->x_kernelw/2);
+ ky = (x->x_kernelh/2);
+ for ( pn=0; pn<x->x_nbpasses; pn++ )
+ {
+ memcpy( x->x_frame, newdata, x->x_vsize+(x->x_vsize>>1)<<1 );
+ for ( py=0; py<x->x_vheight; py++ )
+ {
+ for ( px=0; px<x->x_vwidth; px++ )
+ {
+ if ( *(pfY+py*x->x_vwidth+px) != 0 )
+ {
+ for (ix=-kx; ix<=kx; ix++)
+ {
+ ppx=px+ix;
+ if ( (ppx>=0) && (ppx<x->x_vwidth) )
+ {
+ for (iy=-ky; iy<=ky; iy++)
+ {
+ ppy=py+iy;
+ if ( (ppy>=0) && (ppy<x->x_vheight) )
+ {
+ if( *(pfY+ppy*x->x_vwidth+ppx) != ((255)<<7) )
+ {
+ *(newdata+py*x->x_vwidth+px) = 0;
+ break;
+ }
+ }
+ }
+ if ( *(newdata+py*x->x_vwidth+px) == 0 ) break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+static void pdp_erode_sendpacket(t_pdp_erode *x)
+{
+ /* release the packet */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+
+ /* unregister and propagate if valid dest packet */
+ pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1);
+}
+
+static void pdp_erode_process(t_pdp_erode *x)
+{
+ int encoding;
+ t_pdp *header = 0;
+
+ /* check if image data packets are compatible */
+ if ( (header = pdp_packet_header(x->x_packet0))
+ && (PDP_IMAGE == header->type)){
+
+ /* pdp_erode_process inputs and write into active inlet */
+ switch(pdp_packet_header(x->x_packet0)->info.image.encoding)
+ {
+
+ case PDP_IMAGE_YV12:
+ x->x_packet1 = pdp_packet_clone_rw(x->x_packet0);
+ pdp_queue_add(x, pdp_erode_process_yv12, pdp_erode_sendpacket, &x->x_queue_id);
+ break;
+
+ case PDP_IMAGE_GREY:
+ // should write something to handle these one day
+ // but i don't use this mode
+ break;
+
+ default:
+ /* don't know the type, so dont pdp_erode_process */
+ break;
+
+ }
+ }
+
+}
+
+static void pdp_erode_input_0(t_pdp_erode *x, t_symbol *s, t_floatarg f)
+{
+ /* if this is a register_ro message or register_rw message, register with packet factory */
+
+ if (s== gensym("register_rw"))
+ {
+ x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") );
+ }
+
+ if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped))
+ {
+ pdp_erode_process(x);
+ }
+}
+
+static void pdp_erode_free(t_pdp_erode *x)
+{
+ int i;
+
+ pdp_packet_mark_unused(x->x_packet0);
+ pdp_erode_free_ressources( x );
+}
+
+t_class *pdp_erode_class;
+
+void *pdp_erode_new(void)
+{
+ int i;
+
+ t_pdp_erode *x = (t_pdp_erode *)pd_new(pdp_erode_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("nbpasses"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelw"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelh"));
+
+ x->x_pdp_output = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_packet0 = -1;
+ x->x_packet1 = -1;
+ x->x_vwidth = -1;
+ x->x_vheight = -1;
+ x->x_vsize = -1;
+ x->x_frame = NULL;
+ x->x_kernelw = 3;
+ x->x_kernelh = 3;
+
+ x->x_nbpasses = 1;
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_erode_setup(void)
+{
+ // post( pdp_erode_version );
+ pdp_erode_class = class_new(gensym("pdp_erode"), (t_newmethod)pdp_erode_new,
+ (t_method)pdp_erode_free, sizeof(t_pdp_erode), 0, A_NULL);
+
+ class_addmethod(pdp_erode_class, (t_method)pdp_erode_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_erode_class, (t_method)pdp_erode_nbpasses, gensym("nbpasses"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_erode_class, (t_method)pdp_erode_kernelw, gensym("kernelw"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_erode_class, (t_method)pdp_erode_kernelh, gensym("kernelh"), A_FLOAT, A_NULL);
+ class_sethelpsymbol( pdp_erode_class, gensym("pdp_erode.pd") );
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_hitandmiss.c b/modules/pdp_hitandmiss.c
new file mode 100644
index 0000000..0664605
--- /dev/null
+++ b/modules/pdp_hitandmiss.c
@@ -0,0 +1,386 @@
+/*
+ * PiDiP module
+ * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr )
+ *
+ * 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 object is a morphological operator for hit and miss agorithm
+ * using as a structuring element a WxH square
+ */
+
+#include "pdp.h"
+#include "yuv.h"
+#include <math.h>
+#include <stdio.h>
+
+static char *pdp_hitandmiss_version = "pdp_hitandmiss: morphology : hit&miss version 0.1 written by Yves Degoyon (ydegoyon@free.fr)";
+
+typedef struct pdp_hitandmiss_struct
+{
+ t_object x_obj;
+
+ t_int x_packet0;
+ t_int x_packet1;
+ t_int x_queue_id;
+ t_int x_dropped;
+
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+ t_int x_kernelw; // width of the (square) kernel
+ t_int x_kernelh; // height of the square kernel
+ char *x_kdata; // kernel data
+ t_int x_nbpasses; // number of passes
+ short int *x_frame; // keep a copy of current frame for transformations
+
+ t_outlet *x_pdp_output; // output packets
+
+} t_pdp_hitandmiss;
+
+static void pdp_hitandmiss_nbpasses(t_pdp_hitandmiss *x, t_floatarg fpasses )
+{
+ if ( fpasses>=1.)
+ {
+ x->x_nbpasses = (t_int)fpasses;
+ }
+}
+
+static void pdp_hitandmiss_kernelw(t_pdp_hitandmiss *x, t_floatarg fkernelw )
+{
+ t_int oldw, minw;
+ char *nkdata;
+
+ if ( fkernelw>=0.)
+ {
+ oldw = x->x_kernelw;
+ x->x_kernelw = (t_int)fkernelw;
+
+ nkdata= (char*) malloc( x->x_kernelw*x->x_kernelh );
+
+ minw = (x->x_kernelw>oldw?oldw:x->x_kernelw );
+ memcpy( nkdata, x->x_kdata, minw*x->x_kernelh );
+
+ if ( x->x_kdata )
+ {
+ free( x->x_kdata );
+ }
+ x->x_kdata = nkdata;
+ }
+
+}
+
+static void pdp_hitandmiss_kernelh(t_pdp_hitandmiss *x, t_floatarg fkernelh )
+{
+ t_int oldh, minh;
+ char *nkdata;
+
+ if ( fkernelh>=0.)
+ {
+ oldh = x->x_kernelh;
+ x->x_kernelh = (t_int)fkernelh;
+
+ nkdata= (char*) malloc( x->x_kernelw*x->x_kernelh );
+
+ minh = (x->x_kernelh>oldh?oldh:x->x_kernelh );
+ memcpy( nkdata, x->x_kdata, x->x_kernelw*minh );
+
+ if ( x->x_kdata )
+ {
+ free( x->x_kdata );
+ }
+ x->x_kdata = nkdata;
+ }
+}
+
+static void pdp_hitandmiss_kdata(t_pdp_hitandmiss *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_int nbvalues, ivalue, iv;
+
+ if ( argc > x->x_kernelw*x->x_kernelh )
+ {
+ nbvalues = x->x_kernelh*x->x_kernelw;
+ post( "pdp_hitandmiss : too many data for the kernel, it should be %dx%d", x->x_kernelw, x->x_kernelh );
+ post( "pdp_hitandmiss : truncated to %d", nbvalues );
+ }
+ else if ( argc < x->x_kernelw*x->x_kernelh )
+ {
+ nbvalues = argc;
+ post( "pdp_hitandmiss : too few data for the kernel, it should be %dx%d", x->x_kernelw, x->x_kernelh );
+ post( "pdp_hitandmiss : padding with -1 (as if not used)" );
+ for ( iv=argc; iv<x->x_kernelw*x->x_kernelh; iv++ )
+ {
+ x->x_kdata[iv] = -1;
+ }
+ }
+ else
+ {
+ nbvalues = argc;
+ }
+
+ for ( iv=0; iv<nbvalues; iv++ )
+ {
+ if ( argv[iv].a_type != A_FLOAT )
+ {
+ post( "pdp_hitandmiss : wrong data (%d), unchanged", iv );
+ }
+ else
+ {
+ ivalue = (t_int) argv[iv].a_w.w_float;
+ if ( ( ivalue != 0 ) && ( ivalue != 1 ) && ( ivalue != -1 ) )
+ {
+ post( "pdp_hitandmiss : wrong data (%d), unchanged", iv );
+ }
+ else
+ {
+ x->x_kdata[iv] = ivalue;
+ }
+ }
+ }
+}
+
+static void pdp_hitandmiss_allocate(t_pdp_hitandmiss *x)
+{
+ x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+
+ if ( !x->x_frame )
+ {
+ post( "pdp_hitandmiss : severe error : cannot allocate buffer !!! ");
+ return;
+ }
+}
+
+static void pdp_hitandmiss_free_ressources(t_pdp_hitandmiss *x)
+{
+ if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 );
+}
+
+static void pdp_hitandmiss_process_yv12(t_pdp_hitandmiss *x)
+{
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ short int *data = (short int *)pdp_packet_data(x->x_packet0);
+ t_pdp *newheader = pdp_packet_header(x->x_packet1);
+ short int *newdata = (short int *)pdp_packet_data(x->x_packet1);
+ t_int i;
+ t_int px=0, py=0;
+ short int *pfY, *pfU, *pfV;
+ t_int ppx, ppy, ix, iy, pn, kx, ky;
+ short int pvalue;
+
+ // allocate all ressources
+ if ( ( (int)header->info.image.width != x->x_vwidth ) ||
+ ( (int)header->info.image.height != x->x_vheight ) )
+ {
+ pdp_hitandmiss_free_ressources( x );
+ x->x_vwidth = header->info.image.width;
+ x->x_vheight = header->info.image.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+ pdp_hitandmiss_allocate( x );
+ post( "pdp_hitandmiss : reallocated buffers" );
+ }
+
+ // post( "pdp_hitandmiss : newheader:%x", newheader );
+
+ newheader->info.image.encoding = header->info.image.encoding;
+ newheader->info.image.width = x->x_vwidth;
+ newheader->info.image.height = x->x_vheight;
+
+ memcpy( newdata, data, x->x_vsize+(x->x_vsize>>1)<<1 );
+ memcpy( x->x_frame, data, x->x_vsize+(x->x_vsize>>1)<<1 );
+
+ // hit and miss (supposedly) binary image by using a 3x3 square as a structuring element
+ pfY = x->x_frame;
+ pfV = x->x_frame+x->x_vsize;
+ pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2);
+ kx = (x->x_kernelw/2);
+ ky = (x->x_kernelh/2);
+ for ( pn=0; pn<x->x_nbpasses; pn++ )
+ {
+ memcpy( x->x_frame, newdata, x->x_vsize+(x->x_vsize>>1)<<1 );
+ for ( py=0; py<x->x_vheight; py++ )
+ {
+ for ( px=0; px<x->x_vwidth; px++ )
+ {
+ *(newdata+py*x->x_vwidth+px) = ((255)<<7);
+ for (ix=-kx; ix<=kx; ix++)
+ {
+ ppx=px+ix;
+ if ( (ppx>=0) && (ppx<x->x_vwidth) )
+ {
+ for (iy=-ky; iy<=ky; iy++)
+ {
+ ppy=py+iy;
+ if ( (ppy>=0) && (ppy<x->x_vheight) )
+ {
+ if ( x->x_kdata[(ky+iy)*x->x_kernelw+(kx+ix)] == 1 )
+ {
+ pvalue = ((255)<<7);
+ }
+ else if ( x->x_kdata[(ky+iy)*x->x_kernelw+(kx+ix)] == 0 )
+ {
+ pvalue = 0;
+ }
+ else // unused bit in the kernel, usually -1
+ {
+ continue;
+ }
+ if( *(pfY+ppy*x->x_vwidth+ppx) != pvalue )
+ {
+ *(newdata+py*x->x_vwidth+px) = 0;
+ break;
+ }
+ }
+ }
+ if ( *(newdata+py*x->x_vwidth+px) == 0 ) break;
+ }
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+static void pdp_hitandmiss_sendpacket(t_pdp_hitandmiss *x)
+{
+ /* release the packet */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+
+ /* unregister and propagate if valid dest packet */
+ pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1);
+}
+
+static void pdp_hitandmiss_process(t_pdp_hitandmiss *x)
+{
+ int encoding;
+ t_pdp *header = 0;
+
+ /* check if image data packets are compatible */
+ if ( (header = pdp_packet_header(x->x_packet0))
+ && (PDP_IMAGE == header->type)){
+
+ /* pdp_hitandmiss_process inputs and write into active inlet */
+ switch(pdp_packet_header(x->x_packet0)->info.image.encoding)
+ {
+
+ case PDP_IMAGE_YV12:
+ x->x_packet1 = pdp_packet_clone_rw(x->x_packet0);
+ pdp_queue_add(x, pdp_hitandmiss_process_yv12, pdp_hitandmiss_sendpacket, &x->x_queue_id);
+ break;
+
+ case PDP_IMAGE_GREY:
+ // should write something to handle these one day
+ // but i don't use this mode
+ break;
+
+ default:
+ /* don't know the type, so dont pdp_hitandmiss_process */
+ break;
+
+ }
+ }
+
+}
+
+static void pdp_hitandmiss_input_0(t_pdp_hitandmiss *x, t_symbol *s, t_floatarg f)
+{
+ /* if this is a register_ro message or register_rw message, register with packet factory */
+
+ if (s== gensym("register_rw"))
+ {
+ x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") );
+ }
+
+ if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped))
+ {
+ pdp_hitandmiss_process(x);
+ }
+}
+
+static void pdp_hitandmiss_free(t_pdp_hitandmiss *x)
+{
+ int i;
+
+ pdp_packet_mark_unused(x->x_packet0);
+ pdp_hitandmiss_free_ressources( x );
+}
+
+t_class *pdp_hitandmiss_class;
+
+void *pdp_hitandmiss_new(void)
+{
+ int i;
+
+ t_pdp_hitandmiss *x = (t_pdp_hitandmiss *)pd_new(pdp_hitandmiss_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("nbpasses"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelw"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelh"));
+
+ x->x_pdp_output = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_packet0 = -1;
+ x->x_packet1 = -1;
+ x->x_vwidth = -1;
+ x->x_vheight = -1;
+ x->x_vsize = -1;
+ x->x_frame = NULL;
+ x->x_kernelw = 3;
+ x->x_kernelh = 3;
+ x->x_kdata = (char *)malloc( x->x_kernelw*x->x_kernelh );
+ x->x_kdata[0] = -1;
+ x->x_kdata[1] = 1;
+ x->x_kdata[2] = -1;
+ x->x_kdata[3] = 0;
+ x->x_kdata[4] = 1;
+ x->x_kdata[5] = 1;
+ x->x_kdata[6] = 0;
+ x->x_kdata[7] = 0;
+ x->x_kdata[8] = -1;
+ // that is :
+ // | -1 1 -1 |
+ // | 0 1 1 |
+ // | 0 0 -1 |
+
+ x->x_nbpasses = 1;
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_hitandmiss_setup(void)
+{
+ // post( pdp_hitandmiss_version );
+ pdp_hitandmiss_class = class_new(gensym("pdp_hitandmiss"), (t_newmethod)pdp_hitandmiss_new,
+ (t_method)pdp_hitandmiss_free, sizeof(t_pdp_hitandmiss), 0, A_NULL);
+
+ class_addmethod(pdp_hitandmiss_class, (t_method)pdp_hitandmiss_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_hitandmiss_class, (t_method)pdp_hitandmiss_nbpasses, gensym("nbpasses"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_hitandmiss_class, (t_method)pdp_hitandmiss_kernelw, gensym("kernelw"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_hitandmiss_class, (t_method)pdp_hitandmiss_kernelh, gensym("kernelh"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_hitandmiss_class, (t_method)pdp_hitandmiss_kdata, gensym("kernel"), A_GIMME, A_NULL);
+ class_sethelpsymbol( pdp_hitandmiss_class, gensym("pdp_hitandmiss.pd") );
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_theorin~.c b/modules/pdp_theorin~.c
new file mode 100644
index 0000000..2a7c72b
--- /dev/null
+++ b/modules/pdp_theorin~.c
@@ -0,0 +1,970 @@
+/*
+ * PiDiP module.
+ * Copyright (c) by Yves Degoyon (ydegoyon@free.fr)
+ *
+ * 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 object is a theora file decoder object
+ * It uses libtheora and some of it code samples ( copyright xiph.org )
+ * Copyleft by Yves Degoyon ( ydegoyon@free.fr )
+ */
+
+
+#include "pdp.h"
+#include "yuv.h"
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include <theora/theora.h> /* theora stuff */
+#include <vorbis/codec.h> /* vorbis stuff */
+
+#define VIDEO_BUFFER_SIZE (1024*1024)
+#define MAX_AUDIO_PACKET_SIZE (64 * 1024)
+#define MIN_AUDIO_SIZE (64*1024)
+
+#define DEFAULT_CHANNELS 1
+#define DEFAULT_WIDTH 320
+#define DEFAULT_HEIGHT 240
+#define DEFAULT_FRAME_RATE 25
+#define END_OF_STREAM 20
+#define MIN_PRIORITY 0
+#define DEFAULT_PRIORITY 1
+#define MAX_PRIORITY 20
+
+#define THEORA_NUM_HEADER_PACKETS 3
+
+static char *pdp_theorin_version = "pdp_theorin~: version 0.1, a theora file reader ( ydegoyon@free.fr).";
+
+typedef struct pdp_theorin_struct
+{
+ t_object x_obj;
+ t_float x_f;
+
+ t_int x_packet0;
+ t_int x_dropped;
+
+ t_pdp *x_header;
+ unsigned char *x_data;
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+
+ t_outlet *x_pdp_out; // output decoded pdp packets
+ t_outlet *x_outlet_left; // left audio output
+ t_outlet *x_outlet_right; // right audio output
+ t_outlet *x_outlet_nbframes; // number of frames emitted
+ t_outlet *x_outlet_framerate; // real framerate
+ t_outlet *x_outlet_endoffile; // for signaling the end of the file
+ t_outlet *x_outlet_filesize; // for informing of the file size
+
+ pthread_t x_decodechild; // file decoding thread
+ t_int x_usethread; // flag to activate decoding in a thread
+ t_int x_autoplay; // flag to autoplay the file ( default = true )
+ t_int x_nextimage; // flag to play next image in manual mode
+ t_int x_priority; // priority of decoding thread
+
+ char *x_filename;
+ FILE *x_infile; // file descriptor
+ t_int x_decoding; // decoding flag
+ t_int x_theorainit; // flag for indicating that theora is initialized
+ t_int x_videoready; // video ready flag
+ t_int x_newpicture; // new picture flag
+ t_int x_newpictureready;// new picture ready flag
+ t_int x_loop; // looping flag ( default = on )
+ t_int x_notpackets; // number of theora packets decoded
+ t_int x_novpackets; // number of vorbis packets decoded
+ t_int x_endoffile; // end of the file reached
+ t_int x_nbframes; // number of frames emitted
+ t_int x_framerate; // framerate
+ t_int x_samplerate; // audio sample rate
+ t_int x_audiochannels; // audio channels
+ t_int x_blocksize; // audio block size
+ t_int x_audioon; // audio buffer filling flag
+ t_int x_reading; // file reading flag
+ t_int x_cursec; // current second
+ t_int x_secondcount; // number of frames received in the current second
+ struct timeval x_starttime; // reading starting time
+
+ /* vorbis/theora structures */
+ ogg_sync_state x_sync_state; // ogg sync state
+ ogg_page x_ogg_page; // ogg page
+ ogg_packet x_ogg_packet; // ogg packet
+ ogg_stream_state x_statev; // vorbis stream state
+ ogg_stream_state x_statet; // theora stream state
+ theora_info x_theora_info; // theora info
+ theora_comment x_theora_comment; // theora comment
+ theora_state x_theora_state; // theora state
+ vorbis_info x_vorbis_info; // vorbis info
+ vorbis_dsp_state x_dsp_state; // vorbis dsp state
+ vorbis_block x_vorbis_block; // vorbis block
+ vorbis_comment x_vorbis_comment; // vorbis comment
+ yuv_buffer x_yuvbuffer; // yuv buffer
+
+ /* audio structures */
+ t_int x_audio; // flag to activate the decoding of audio
+ t_float x_audio_inl[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for float audio decoded from ogg */
+ t_float x_audio_inr[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for float audio decoded from ogg */
+ t_int x_audioin_position; // writing position for incoming audio
+
+} t_pdp_theorin;
+
+static void pdp_theorin_priority(t_pdp_theorin *x, t_floatarg fpriority )
+{
+ if ( ( x->x_priority >= MIN_PRIORITY ) && ( x->x_priority <= MAX_PRIORITY ) )
+ {
+ x->x_priority = (int)fpriority;
+ }
+}
+
+static void pdp_theorin_threadify(t_pdp_theorin *x, t_floatarg fusethread )
+{
+ if ( ( fusethread == 0 ) || ( fusethread == 1 ) )
+ {
+ x->x_usethread = (int)fusethread;
+ }
+}
+
+static void pdp_theorin_audio(t_pdp_theorin *x, t_floatarg faudio )
+{
+ if ( ( faudio == 0. ) || ( faudio == 1. ) )
+ {
+ x->x_audio = (int)faudio;
+ }
+}
+
+static void pdp_theorin_autoplay(t_pdp_theorin *x, t_floatarg fautoplay )
+{
+ if ( ( fautoplay == 0. ) || ( fautoplay == 1. ) )
+ {
+ x->x_autoplay = (int)fautoplay;
+ }
+}
+
+static void pdp_theorin_loop(t_pdp_theorin *x, t_floatarg floop )
+{
+ if ( ( floop == 0. ) || ( floop == 1. ) )
+ {
+ x->x_loop = (int)floop;
+ }
+}
+
+static void pdp_theorin_bang(t_pdp_theorin *x)
+{
+ if ( x->x_nextimage == 1 )
+ {
+ // post( "pdp_theorin~ : banging too fast, previous image is not decoded yet... ignored" );
+ return;
+ }
+ x->x_nextimage = 1;
+}
+
+static t_int pdp_theorin_get_buffer_from_file(FILE *in, ogg_sync_state *oy)
+{
+ char *buffer;
+ t_int bytes;
+
+ buffer=ogg_sync_buffer(oy,4096);
+ bytes=fread(buffer,1,4096,in);
+ ogg_sync_wrote(oy,bytes);
+ return(bytes);
+}
+
+static t_int pdp_theorin_queue_page(t_pdp_theorin *x)
+{
+ if(x->x_notpackets) ogg_stream_pagein(&x->x_statet, &x->x_ogg_page);
+ if(x->x_novpackets) ogg_stream_pagein(&x->x_statev, &x->x_ogg_page);
+ return 0;
+}
+
+static t_int pdp_theorin_decode_packet(t_pdp_theorin *x)
+{
+ int ret, count, maxsamples, samples, si=0, sj=0;
+ float **pcm;
+ struct timespec mwait;
+ struct timeval ctime;
+ long long tplaying;
+ long long ttheoretical;
+ unsigned char *pY, *pU, *pV;
+ unsigned char *psY, *psU, *psV;
+ t_int px, py;
+
+ // post( "pdp_theorin~ : decode packet" );
+
+ if ( !x->x_reading ) return -1;
+
+ while ( x->x_novpackets && !x->x_audioon )
+ {
+ /* if there's pending, decoded audio, grab it */
+ if((ret=vorbis_synthesis_pcmout(&x->x_dsp_state, &pcm))>0)
+ {
+ if (x->x_audio)
+ {
+ maxsamples=(3*MAX_AUDIO_PACKET_SIZE-x->x_audioin_position);
+ samples=(ret<maxsamples)?ret:maxsamples;
+
+ if ( x->x_audioin_position + samples*x->x_audiochannels < 3*MAX_AUDIO_PACKET_SIZE )
+ {
+ memcpy( (void*)&x->x_audio_inl[x->x_audioin_position], pcm[0], samples*sizeof(t_float) );
+ memcpy( (void*)&x->x_audio_inr[x->x_audioin_position], pcm[1], samples*sizeof(t_float) );
+ x->x_audioin_position = ( x->x_audioin_position + samples ) % (4*MAX_AUDIO_PACKET_SIZE);
+ }
+ else
+ {
+ post( "pdp_theorin~ : audio overflow : packet ignored...");
+ x->x_audioin_position = 0;
+ }
+ if ( ( x->x_audioin_position > MIN_AUDIO_SIZE ) && (!x->x_audioon) )
+ {
+ x->x_audioon = 1;
+ // post( "pdp_theorin~ : audio on (audioin=%d)", x->x_audioin_position );
+ }
+ // tell vorbis how many samples were read
+ // post( "pdp_theorin~ : got %d audio samples (audioin=%d)", samples, x->x_audioin_position );
+ vorbis_synthesis_read(&x->x_dsp_state, samples);
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ {
+ // no pending audio: is there a pending packet to decode?
+ if( ogg_stream_packetout(&x->x_statev, &x->x_ogg_packet)>0 )
+ {
+ if(vorbis_synthesis(&x->x_vorbis_block, &x->x_ogg_packet)==0)
+ {
+ vorbis_synthesis_blockin(&x->x_dsp_state, &x->x_vorbis_block);
+ }
+ }
+ else /* we need more data; suck in another page */
+ {
+ break;
+ }
+ }
+ }
+
+ if ( !x->x_newpictureready && !x->x_newpicture )
+ {
+ while(x->x_notpackets && !x->x_videoready)
+ {
+ // theora is one in, one out...
+ if(ogg_stream_packetout(&x->x_statet, &x->x_ogg_packet)>0)
+ {
+ theora_decode_packetin(&x->x_theora_state, &x->x_ogg_packet);
+ // post( "pdp_theorin~ : got one video frame" );
+ x->x_videoready=1;
+ }
+ else
+ {
+ // post( "pdp_theorin~ : no more video frame (frames=%d)", x->x_nbframes );
+ if ( x->x_nbframes > 0 )
+ {
+ if ( !x->x_loop )
+ {
+ x->x_endoffile = 1;
+ }
+ else
+ {
+ // restart a new loop
+ if ( gettimeofday(&x->x_starttime, NULL) == -1)
+ {
+ post("pdp_theorin~ : could not set start time" );
+ }
+ }
+ x->x_nbframes = 0;
+ x->x_audioin_position = 0; // reset audio
+ x->x_audioon = 0;
+ }
+ break;
+ }
+ }
+
+ if ( x->x_videoready )
+ {
+ theora_decode_YUVout(&x->x_theora_state, &x->x_yuvbuffer);
+
+ // create a new pdp packet from PIX_FMT_YUV420P image format
+ x->x_vwidth = x->x_yuvbuffer.y_width;
+ x->x_vheight = x->x_yuvbuffer.y_height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+ x->x_packet0 = pdp_packet_new_bitmap_yv12( x->x_vwidth, x->x_vheight );
+ // post( "pdp_theorin~ : allocated packet %d", x->x_packet0 );
+ x->x_header = pdp_packet_header(x->x_packet0);
+ x->x_data = (unsigned char*) pdp_packet_data(x->x_packet0);
+
+ x->x_header->info.image.encoding = PDP_BITMAP_YV12;
+ x->x_header->info.image.width = x->x_vwidth;
+ x->x_header->info.image.height = x->x_vheight;
+
+ pY = x->x_data;
+ pV = x->x_data+x->x_vsize;
+ pU = x->x_data+x->x_vsize+(x->x_vsize>>2);
+
+ psY = x->x_yuvbuffer.y;
+ psU = x->x_yuvbuffer.u;
+ psV = x->x_yuvbuffer.v;
+
+ for ( py=0; py<x->x_vheight; py++)
+ {
+ memcpy( (void*)pY, (void*)psY, x->x_vwidth );
+ pY += x->x_vwidth;
+ psY += x->x_yuvbuffer.y_stride;
+ if ( py%2==0 )
+ {
+ memcpy( (void*)pU, (void*)psU, (x->x_vwidth>>1) );
+ memcpy( (void*)pV, (void*)psV, (x->x_vwidth>>1) );
+ pU += (x->x_vwidth>>1);
+ pV += (x->x_vwidth>>1);
+ psU += x->x_yuvbuffer.uv_stride;
+ psV += x->x_yuvbuffer.uv_stride;
+ }
+ }
+ if ( !x->x_autoplay )
+ {
+ x->x_newpicture = 1;
+ }
+ else
+ {
+ x->x_newpictureready = 1;
+ }
+ }
+ }
+
+ if ( x->x_newpictureready )
+ {
+ if ( gettimeofday(&ctime, NULL) == -1)
+ {
+ post("pdp_theorin~ : could not read time" );
+ }
+
+ tplaying = ( ctime.tv_sec-x->x_starttime.tv_sec )*1000 +
+ ( ctime.tv_usec-x->x_starttime.tv_usec )/1000;
+ ttheoretical = ((x->x_nbframes)*1000 )/x->x_framerate;
+ // post( "pdp-theorin~ : %d playing since : %lldms ( theory : %lldms )",
+ // x->x_nbframes, tplaying, ttheoretical );
+
+ if ( ttheoretical <= tplaying )
+ {
+ x->x_newpicture = 1;
+ x->x_newpictureready = 0;
+ }
+
+ }
+
+ // check end of file
+ if(!x->x_videoready && feof(x->x_infile))
+ {
+ if ( x->x_loop )
+ {
+ if ( fseek( x->x_infile, 0x0, SEEK_SET ) < 0 )
+ {
+ post( "pdp_theorin~ : could not reset file." );
+ perror( "fseek" );
+ }
+ }
+ }
+
+ // read more data in
+ if( ( x->x_audioin_position < MIN_AUDIO_SIZE ) || ( !x->x_newpicture && !x->x_newpictureready ) )
+ {
+ ret=pdp_theorin_get_buffer_from_file(x->x_infile, &x->x_sync_state);
+ // post( "pdp_theorin~ : read %d bytes from file", ret );
+ while( ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page)>0 )
+ {
+ pdp_theorin_queue_page(x);
+ }
+ }
+
+ x->x_videoready = 0;
+
+ return 0;
+
+}
+
+static void *pdp_decode_file(void *tdata)
+{
+ t_pdp_theorin *x = (t_pdp_theorin*)tdata;
+ struct sched_param schedprio;
+ t_int pmin, pmax, p1;
+ struct timespec twait;
+
+ twait.tv_sec = 0;
+ twait.tv_nsec = 5000000; // 5 ms
+
+ schedprio.sched_priority = sched_get_priority_min(SCHED_FIFO) + x->x_priority;
+ if ( sched_setscheduler(0, SCHED_FIFO, &schedprio) == -1)
+ {
+ post("pdp_theorin~ : couldn't set priority for decoding thread.");
+ }
+
+ while ( x->x_decodechild )
+ {
+ if ( ( x->x_reading ) && ( ( x->x_autoplay ) || ( x->x_nextimage == 1 ) ) )
+ {
+ x->x_decoding = 1;
+
+ // decode incoming packets
+ if ( x->x_reading ) pdp_theorin_decode_packet( x );
+ nanosleep( &twait, NULL );
+ x->x_nextimage = -1;
+ }
+ else
+ {
+ x->x_decoding = 0;
+ nanosleep( &twait, NULL ); // nothing to do, just wait
+ }
+ }
+
+ x->x_decoding = 0;
+ post("pdp_theorin~ : decoding child exiting." );
+ return NULL;
+}
+
+static void pdp_theorin_close(t_pdp_theorin *x)
+{
+ t_int ret, i, count=0;
+ struct timespec twait;
+
+ twait.tv_sec = 0;
+ twait.tv_nsec = 10000000; // 10 ms
+
+ if ( x->x_infile == NULL )
+ {
+ post("pdp_theorin~ : close request but no file is played ... ignored" );
+ return;
+ }
+
+ if ( x->x_reading )
+ {
+ x->x_newpicture = 0;
+ x->x_reading = 0;
+ post("pdp_theorin~ : waiting end of decoding..." );
+ while ( x->x_decoding ) nanosleep( &twait, NULL );
+
+ if ( fclose( x->x_infile ) < 0 )
+ {
+ post( "pdp_theorin~ : could not close input file" );
+ perror( "fclose" );
+ }
+ x->x_infile = NULL;
+
+ if ( x->x_notpackets > 0 )
+ {
+ ogg_stream_clear(&x->x_statet);
+ theora_clear(&x->x_theora_state);
+ theora_comment_clear(&x->x_theora_comment);
+ theora_info_clear(&x->x_theora_info);
+ }
+
+ if ( x->x_novpackets > 0 )
+ {
+ ogg_stream_clear(&x->x_statev);
+ vorbis_block_clear(&x->x_vorbis_block);
+ vorbis_dsp_clear(&x->x_dsp_state);
+ vorbis_comment_clear(&x->x_vorbis_comment);
+ vorbis_info_clear(&x->x_vorbis_info);
+ }
+
+ }
+
+ x->x_notpackets = 0;
+ x->x_novpackets = 0;
+ x->x_endoffile = 0;
+ x->x_nbframes = 0;
+ x->x_decoding = 0;
+ x->x_theorainit = 0;
+
+ x->x_videoready = 0;
+ x->x_newpicture = 0;
+
+ x->x_nbframes = 0;
+ outlet_float( x->x_outlet_nbframes, x->x_nbframes );
+ x->x_framerate = 0;
+ outlet_float( x->x_outlet_framerate, x->x_framerate );
+}
+
+static void pdp_theorin_open(t_pdp_theorin *x, t_symbol *s)
+{
+ t_int ret, i;
+ pthread_attr_t decode_child_attr;
+ ogg_stream_state o_tempstate;
+ struct stat fileinfos;
+
+ if ( x->x_infile != NULL )
+ {
+ post("pdp_theorin~ : open request but a file is open ... closing" );
+ pdp_theorin_close(x);
+ }
+
+ if ( x->x_filename ) free( x->x_filename );
+ x->x_filename = (char*) malloc( strlen( s->s_name ) + 1 );
+ strcpy( x->x_filename, s->s_name );
+ post( "pdp_theorin~ : opening file : %s", x->x_filename );
+
+ if ( ( x->x_infile = fopen(x->x_filename,"r") ) == NULL )
+ {
+ post( "pdp_theorin~ : unable to open file >%s<", x->x_filename );
+ return;
+ }
+
+ ogg_sync_init(&x->x_sync_state);
+
+ // init supporting Vorbis structures needed in header parsing
+ vorbis_info_init(&x->x_vorbis_info);
+ vorbis_comment_init(&x->x_vorbis_comment);
+
+ // init supporting Theora structures needed in header parsing
+ theora_comment_init(&x->x_theora_comment);
+ theora_info_init(&x->x_theora_info);
+
+ // parse headers
+ while( !x->x_theorainit )
+ {
+ if ( ( ret = pdp_theorin_get_buffer_from_file(x->x_infile, &x->x_sync_state) )==0) break;
+
+ while( ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page) > 0 )
+ {
+ /* is this a mandated initial header? If not, stop parsing */
+ if(!ogg_page_bos(&x->x_ogg_page))
+ {
+ pdp_theorin_queue_page(x);
+ x->x_theorainit = 1;
+ break;
+ }
+
+ ogg_stream_init(&o_tempstate, ogg_page_serialno(&x->x_ogg_page));
+ ogg_stream_pagein(&o_tempstate, &x->x_ogg_page);
+ ogg_stream_packetout(&o_tempstate, &x->x_ogg_packet);
+
+ /* identify the codec: try theora */
+ if(!x->x_notpackets &&
+ theora_decode_header(&x->x_theora_info, &x->x_theora_comment, &x->x_ogg_packet)>=0)
+ {
+ /* it is theora */
+ memcpy(&x->x_statet, &o_tempstate, sizeof(o_tempstate));
+ x->x_notpackets=1;
+ }else
+ if(!x->x_novpackets &&
+ vorbis_synthesis_headerin(&x->x_vorbis_info, &x->x_vorbis_comment, &x->x_ogg_packet)>=0){
+ memcpy(&x->x_statev, &o_tempstate, sizeof(o_tempstate));
+ x->x_novpackets=1;
+ }else{
+ /* whatever it is, we don't care about it */
+ ogg_stream_clear(&o_tempstate);
+ }
+ }
+ }
+
+ // we're expecting more header packets.
+ while( (x->x_notpackets && x->x_notpackets<3) || (x->x_novpackets && x->x_novpackets<3) )
+ {
+ // look for further theora headers
+ while(x->x_notpackets && (x->x_notpackets<3) &&
+ (ret=ogg_stream_packetout(&x->x_statet, &x->x_ogg_packet)))
+ {
+ if( ret<0 )
+ {
+ post("pdp_theorin~ : error parsing theora stream headers\n");
+ x->x_theorainit = 0;
+ return;
+ }
+ if( theora_decode_header(&x->x_theora_info, &x->x_theora_comment, &x->x_ogg_packet) )
+ {
+ post("pdp_theorin~ : error parsing theora stream headers\n");
+ x->x_theorainit = 0;
+ return;
+ }
+ x->x_notpackets++;
+ if(x->x_notpackets==3) break;
+ }
+
+ /* look for more vorbis header packets */
+ while(x->x_novpackets && (x->x_novpackets<3) &&
+ (ret=ogg_stream_packetout(&x->x_statev, &x->x_ogg_packet)))
+ {
+ if(ret<0)
+ {
+ post("pdp_theorin~ : error parsing theora stream headers\n");
+ x->x_theorainit = 0;
+ return;
+ }
+ if( vorbis_synthesis_headerin(&x->x_vorbis_info, &x->x_vorbis_comment, &x->x_ogg_packet) )
+ {
+ post("pdp_theorin~ : error parsing theora stream headers\n");
+ x->x_theorainit = 0;
+ return;
+ }
+ x->x_novpackets++;
+ if(x->x_novpackets==3) break;
+ }
+
+ if(ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page)>0)
+ {
+ pdp_theorin_queue_page(x);
+ }
+ else
+ {
+ if( (ret=pdp_theorin_get_buffer_from_file(x->x_infile, &x->x_sync_state))==0 )
+ {
+ post("pdp_theorin~ : end of file while parsing headers\n");
+ x->x_theorainit = 0;
+ return;
+ }
+ }
+ }
+ post( "pdp_theorin~ : parsed headers ok." );
+
+ // initialize decoders
+ if( x->x_notpackets )
+ {
+ theora_decode_init(&x->x_theora_state, &x->x_theora_info);
+ x->x_framerate = (t_int)x->x_theora_info.fps_numerator/x->x_theora_info.fps_denominator;
+ post("pdp_theorin~ : stream %x is theora %dx%d %d fps video.",
+ x->x_statet.serialno,
+ x->x_theora_info.width,x->x_theora_info.height,
+ x->x_framerate);
+ if(x->x_theora_info.width!=x->x_theora_info.frame_width ||
+ x->x_theora_info.height!=x->x_theora_info.frame_height)
+ {
+ post("pdp_theorin~ : frame content is %dx%d with offset (%d,%d).",
+ x->x_theora_info.frame_width, x->x_theora_info.frame_height,
+ x->x_theora_info.offset_x, x->x_theora_info.offset_y);
+ }
+ x->x_vwidth = x->x_theora_info.width;
+ x->x_vheight = x->x_theora_info.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+
+ switch(x->x_theora_info.colorspace)
+ {
+ case OC_CS_UNSPECIFIED:
+ /* nothing to report */
+ break;;
+ case OC_CS_ITU_REC_470M:
+ post("pdp_theorin~ : encoder specified ITU Rec 470M (NTSC) color.");
+ break;;
+ case OC_CS_ITU_REC_470BG:
+ post("pdp_theorin~ : encoder specified ITU Rec 470BG (PAL) color.");
+ break;;
+ default:
+ post("pdp_theorin~ : warning: encoder specified unknown colorspace (%d).",
+ x->x_theora_info.colorspace);
+ break;;
+ }
+ }
+ else
+ {
+ // tear down the partial theora setup
+ theora_info_clear(&x->x_theora_info);
+ theora_comment_clear(&x->x_theora_comment);
+ post("pdp_theorin~ : could not initialize theora decoder.");
+ x->x_theorainit = 0;
+ return;
+ }
+
+ if( x->x_novpackets )
+ {
+ vorbis_synthesis_init(&x->x_dsp_state, &x->x_vorbis_info);
+ vorbis_block_init(&x->x_dsp_state, &x->x_vorbis_block);
+ x->x_audiochannels = x->x_vorbis_info.channels;
+ x->x_samplerate = x->x_vorbis_info.rate;
+ post("pdp_theorin~ : ogg logical stream %x is vorbis %d channel %d Hz audio.",
+ x->x_statev.serialno,
+ x->x_audiochannels, x->x_samplerate);
+ }
+ else
+ {
+ /* tear down the partial vorbis setup */
+ vorbis_info_clear(&x->x_vorbis_info);
+ vorbis_comment_clear(&x->x_vorbis_comment);
+ post("pdp_theorin~ : could not initialize vorbis decoder.");
+ // x->x_theorainit = 0;
+ // return;
+ x->x_audio = 0;
+ }
+ // everything seems to be ready
+ x->x_reading = 1;
+
+ if ( x->x_usethread && ( x->x_decodechild == 0 ) )
+ {
+ x->x_decodechild = 1; // trick & treets
+ // launch decoding thread
+ if ( pthread_attr_init( &decode_child_attr ) < 0 )
+ {
+ post( "pdp_theorin~ : could not launch decoding thread" );
+ perror( "pthread_attr_init" );
+ pthread_exit(NULL);
+ }
+ if ( pthread_create( &x->x_decodechild, &decode_child_attr, pdp_decode_file, x ) < 0 )
+ {
+ post( "pdp_theorin~ : could not launch decoding thread" );
+ perror( "pthread_create" );
+ pthread_exit(NULL);
+ }
+ else
+ {
+ // post( "pdp_theorin~ : decoding thread %d launched", (int)x->x_decodechild );
+ }
+ }
+
+ if ( stat( x->x_filename, &fileinfos ) < 0 )
+ {
+ post("pdp_theorin~ : couldn't get file informations" );
+ perror( "stat" );
+ }
+ else
+ {
+ outlet_float( x->x_outlet_filesize, (fileinfos.st_size)/1024 );
+ }
+
+ if ( gettimeofday(&x->x_starttime, NULL) == -1)
+ {
+ post("pdp_theorin~ : could not set start time" );
+ }
+
+ x->x_nbframes = 0;
+ x->x_endoffile = -1;
+
+ return;
+}
+
+static void pdp_theorin_frame_cold(t_pdp_theorin *x, t_floatarg kbytes)
+{
+ int pos = (int)kbytes;
+ int ret;
+
+ if (x->x_infile==NULL) return;
+
+ pdp_theorin_open(x, gensym(x->x_filename));
+
+ // it's very approximative, we're are positioning the file on the number of requested kilobytes
+ if ( fseek( x->x_infile, pos*1024, SEEK_SET ) < 0 )
+ {
+ post( "pdp_theorin~ : could not set file at that position (%d kilobytes)", pos );
+ perror( "fseek" );
+ return;
+ }
+ // post( "pdp_theorin~ : file seeked at %d kilobytes", pos );
+}
+
+ /* decode the audio buffer */
+static t_int *pdp_theorin_perform(t_int *w)
+{
+ t_float *out1 = (t_float *)(w[1]); // left audio inlet
+ t_float *out2 = (t_float *)(w[2]); // right audio inlet
+ t_pdp_theorin *x = (t_pdp_theorin *)(w[3]);
+ int n = (int)(w[4]); // number of samples
+ struct timeval etime;
+ t_int sn;
+
+ // decode a packet if not in thread mode
+ if ( !x->x_usethread && x->x_reading )
+ {
+ pdp_theorin_decode_packet( x );
+ }
+
+ x->x_blocksize = n;
+
+ // just read the buffer
+ if ( x->x_audioon )
+ {
+ sn=0;
+ while (n--)
+ {
+ *(out1)=x->x_audio_inl[ sn ];
+ if ( x->x_audiochannels == 1 )
+ {
+ *(out2) = *(out1);
+ sn++;
+ }
+ if ( x->x_audiochannels == 2 )
+ {
+ *(out2)=x->x_audio_inr[ sn++ ];
+ }
+ out1++;
+ out2++;
+ }
+ memcpy( &x->x_audio_inl[0], &x->x_audio_inl[sn], (x->x_audioin_position-sn)*sizeof(t_float) );
+ memcpy( &x->x_audio_inr[0], &x->x_audio_inr[sn], (x->x_audioin_position-sn)*sizeof(t_float) );
+ x->x_audioin_position-=sn;
+ // post( "pdp_theorin~ : audio in position : %d", x->x_audioin_position );
+ if ( x->x_audioin_position <= sn )
+ {
+ x->x_audioon = 0;
+ // post( "pdp_theorin~ : audio off ( audioin : %d, channels=%d )",
+ // x->x_audioin_position, x->x_audiochannels );
+ }
+ }
+ else
+ {
+ // post("pdp_theorin~ : no available audio" );
+ while (n--)
+ {
+ *(out1++) = 0.0;
+ *(out2++) = 0.0;
+ }
+ }
+
+ // check if the framerate has been exceeded
+ if ( gettimeofday(&etime, NULL) == -1)
+ {
+ post("pdp_theorin~ : could not read time" );
+ }
+ if ( etime.tv_sec != x->x_cursec )
+ {
+ x->x_cursec = etime.tv_sec;
+ if (x->x_reading) outlet_float( x->x_outlet_framerate, x->x_secondcount );
+ x->x_secondcount = 0;
+ }
+
+ // output image if there's a new one decoded
+ if ( x->x_newpicture )
+ {
+ pdp_packet_pass_if_valid(x->x_pdp_out, &x->x_packet0);
+ x->x_newpicture = 0;
+
+ // update streaming status
+ x->x_nbframes++;
+ x->x_secondcount++;
+ outlet_float( x->x_outlet_nbframes, x->x_nbframes );
+
+ }
+ if ( x->x_endoffile == 1 ) // only once
+ {
+ outlet_float( x->x_outlet_endoffile, x->x_endoffile );
+ x->x_endoffile = 0;
+ }
+ if ( x->x_endoffile == -1 ) // reset
+ {
+ x->x_endoffile = 0;
+ outlet_float( x->x_outlet_endoffile, x->x_endoffile );
+ }
+
+ return (w+5);
+}
+
+static void pdp_theorin_dsp(t_pdp_theorin *x, t_signal **sp)
+{
+ dsp_add(pdp_theorin_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
+}
+
+static void pdp_theorin_free(t_pdp_theorin *x)
+{
+ int i;
+
+ if ( x->x_decodechild )
+ {
+ x->x_decodechild = 0;
+ }
+
+ if ( x->x_reading )
+ {
+ pdp_theorin_close(x);
+ }
+
+ post( "pdp_theorin~ : freeing object" );
+}
+
+t_class *pdp_theorin_class;
+
+void *pdp_theorin_new(void)
+{
+ int i;
+
+ t_pdp_theorin *x = (t_pdp_theorin *)pd_new(pdp_theorin_class);
+
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("frame_cold"));
+
+ x->x_pdp_out = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_outlet_left = outlet_new(&x->x_obj, &s_signal);
+ x->x_outlet_right = outlet_new(&x->x_obj, &s_signal);
+
+ x->x_outlet_nbframes = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_framerate = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_endoffile = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_filesize = outlet_new(&x->x_obj, &s_float);
+
+ x->x_packet0 = -1;
+ x->x_decodechild = 0;
+ x->x_decoding = 0;
+ x->x_theorainit = 0;
+ x->x_usethread = 1;
+ x->x_priority = DEFAULT_PRIORITY;
+ x->x_framerate = DEFAULT_FRAME_RATE;
+ x->x_nbframes = 0;
+ x->x_samplerate = 0;
+ x->x_audio = 1;
+ x->x_audiochannels = 0;
+ x->x_audioin_position = 0;
+ x->x_videoready = 0;
+ x->x_newpicture = 0;
+ x->x_newpictureready = 0;
+ x->x_endoffile = 0;
+ x->x_notpackets = 0;
+ x->x_novpackets = 0;
+ x->x_blocksize = MIN_AUDIO_SIZE;
+ x->x_autoplay = 1;
+ x->x_loop = 1;
+ x->x_nextimage = 0;
+ x->x_infile = NULL;
+ x->x_reading = 0;
+
+ memset( &x->x_audio_inl[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(t_float) );
+ memset( &x->x_audio_inr[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(t_float) );
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_theorin_tilde_setup(void)
+{
+ // post( pdp_theorin_version );
+ pdp_theorin_class = class_new(gensym("pdp_theorin~"), (t_newmethod)pdp_theorin_new,
+ (t_method)pdp_theorin_free, sizeof(t_pdp_theorin), 0, A_NULL);
+
+ class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_dsp, gensym("dsp"), A_NULL);
+ class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_open, gensym("open"), A_SYMBOL, A_NULL);
+ class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_close, gensym("close"), A_NULL);
+ class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_priority, gensym("priority"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_audio, gensym("audio"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_autoplay, gensym("autoplay"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_loop, gensym("loop"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_threadify, gensym("thread"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_bang, gensym("bang"), A_NULL);
+ class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_frame_cold, gensym("frame_cold"), A_FLOAT, A_NULL);
+ class_sethelpsymbol( pdp_theorin_class, gensym("pdp_theorin~.pd") );
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_theorout~.c b/modules/pdp_theorout~.c
new file mode 100644
index 0000000..96d610a
--- /dev/null
+++ b/modules/pdp_theorout~.c
@@ -0,0 +1,884 @@
+/*
+ * PiDiP module.
+ * Copyright (c) by Yves Degoyon <ydegoyon@free.fr>
+ *
+ * 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 object is a theora file encoder object
+ * It uses libtheora and some of it code samples ( copyright xiph.org )
+ * Copyleft by Yves Degoyon ( ydegoyon@free.fr )
+ *
+ */
+
+
+#include "pdp.h"
+#include <math.h>
+#include <time.h>
+#include <sys/time.h>
+#include <theora/theora.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisenc.h>
+
+#define DEFAULT_FRAME_RATE 25
+#define MIN_VIDEO_QUALITY 0
+#define MAX_VIDEO_QUALITY 63
+#define DEFAULT_VIDEO_QUALITY 16
+#define MIN_VIDEO_BITRATE 45
+#define MAX_VIDEO_BITRATE 2000
+#define DEFAULT_VIDEO_BITRATE 96
+#define MIN_AUDIO_QUALITY -0.1
+#define MAX_AUDIO_QUALITY 1.0
+#define DEFAULT_AUDIO_QUALITY 0.5
+#define MIN_AUDIO_BITRATE 8
+#define MAX_AUDIO_BITRATE 2000
+#define DEFAULT_AUDIO_BITRATE 32
+
+#define DEFAULT_CHANNELS 2
+#define DEFAULT_BITS 8
+#define MAX_AUDIO_PACKET_SIZE (128 * 1024)
+// streams hard-coded serial numbers
+#define STREAMV_SNO 0x987654
+#define STREAMA_SNO 0x456789
+
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+static char *pdp_theorout_version = "pdp_theorout~: version 0.1, a theora video/audio recording object, written by ydegoyon@free.fr";
+
+typedef struct pdp_theorout_struct
+{
+ t_object x_obj;
+ t_float x_f;
+
+ t_outlet *x_outlet0;
+ t_int x_packet0;
+ t_int x_packet1;
+ t_int x_dropped;
+ t_int x_queue_id;
+
+ t_int x_vwidth;
+ t_int x_tvwidth; /* theora 16 pixels aligned width value */
+ t_int x_vheight;
+ t_int x_tvheight; /* theora 16 pixels aligned height value */
+ t_int x_vsize;
+
+ FILE *x_tfile;
+ t_int x_framerate;
+ t_int x_newfile;
+ t_int x_einit;
+ t_int x_recflag;
+ t_int x_enduprec;;
+ t_int x_frameswritten;
+ t_int x_frames;
+ struct timeval x_tstart;
+ struct timeval x_tzero;
+ struct timeval x_tcurrent;
+ struct timeval x_tlastrec;
+
+ /* vorbis/theora structures */
+ ogg_page x_ogg_page; // ogg page
+ ogg_packet x_ogg_packet; // ogg packet
+ ogg_stream_state x_statev; // vorbis stream state
+ ogg_stream_state x_statet; // theora stream state
+ theora_info x_theora_info; // theora info
+ theora_comment x_theora_comment; // theora comment
+ theora_state x_theora_state; // theora state
+ vorbis_info x_vorbis_info; // vorbis info
+ vorbis_dsp_state x_dsp_state; // vorbis dsp state
+ vorbis_block x_vorbis_block; // vorbis block
+ vorbis_comment x_vorbis_comment; // vorbis comment
+ yuv_buffer x_yuvbuffer; // yuv buffer
+
+ t_int x_akbps; // audio bit rate
+ t_int x_vkbps; // video bit rate
+ t_float x_aquality; // audio quality
+ t_int x_vquality; // video quality
+ t_int x_abytesout; // audio bytes written
+ t_int x_vbytesout; // video bytes written
+
+ /* audio structures */
+ t_float **x_audio_buf; /* buffer for incoming audio */
+ t_int x_audioin_position; // writing position for incoming audio
+ t_int x_channels; // audio channels
+ t_int x_samplerate; // audio sample rate
+ t_int x_bits; // audio bits
+
+} t_pdp_theorout;
+
+ /* allocate internal ressources */
+static void pdp_theorout_allocate(t_pdp_theorout *x)
+{
+ int ret;
+
+ x->x_yuvbuffer.y_width=x->x_vwidth;
+ x->x_yuvbuffer.y_height=x->x_vheight;
+ x->x_yuvbuffer.y_stride=x->x_vwidth;
+
+ x->x_yuvbuffer.uv_width=x->x_vwidth>>1;
+ x->x_yuvbuffer.uv_height=x->x_vheight>>1;
+ x->x_yuvbuffer.uv_stride=x->x_vwidth>>1;
+
+ x->x_yuvbuffer.y = (char *)malloc( x->x_yuvbuffer.y_width * x->x_yuvbuffer.y_height );
+ x->x_yuvbuffer.u = (char *)malloc( x->x_yuvbuffer.uv_width * x->x_yuvbuffer.uv_height );
+ x->x_yuvbuffer.v = (char *)malloc( x->x_yuvbuffer.uv_width * x->x_yuvbuffer.uv_height );
+}
+
+ /* free internal ressources */
+static void pdp_theorout_free_ressources(t_pdp_theorout *x)
+{
+ if ( x->x_yuvbuffer.y ) free( x->x_yuvbuffer.y );
+ if ( x->x_yuvbuffer.u ) free( x->x_yuvbuffer.u );
+ if ( x->x_yuvbuffer.v ) free( x->x_yuvbuffer.v );
+}
+
+ /* initialize the encoder */
+static void pdp_theorout_init_encoder(t_pdp_theorout *x)
+{
+ t_int ret;
+
+ x->x_einit=0;
+
+ // init streams
+ ogg_stream_init(&x->x_statet, STREAMA_SNO);
+ ogg_stream_init(&x->x_statev, STREAMV_SNO);
+
+ theora_info_init(&x->x_theora_info);
+ x->x_theora_info.width=x->x_tvwidth;
+ x->x_theora_info.height=x->x_tvheight;
+ x->x_theora_info.frame_width=x->x_vwidth;
+ x->x_theora_info.frame_height=x->x_vheight;
+ x->x_theora_info.offset_x=(x->x_tvwidth-x->x_vwidth)>>1;
+ x->x_theora_info.offset_y=(x->x_tvheight-x->x_vheight)>>1;
+ x->x_theora_info.fps_numerator=x->x_framerate;
+ x->x_theora_info.fps_denominator=1;
+ x->x_theora_info.aspect_numerator=x->x_vwidth;
+ x->x_theora_info.aspect_denominator=x->x_vheight;
+ x->x_theora_info.colorspace=OC_CS_UNSPECIFIED;
+ x->x_theora_info.target_bitrate=x->x_vkbps;
+ x->x_theora_info.quality=x->x_vquality;
+
+ x->x_theora_info.dropframes_p=0;
+ x->x_theora_info.quick_p=1;
+ x->x_theora_info.keyframe_auto_p=1;
+ x->x_theora_info.keyframe_frequency=64;
+ x->x_theora_info.keyframe_frequency_force=64;
+ x->x_theora_info.keyframe_data_target_bitrate=x->x_vkbps*1.5;
+ x->x_theora_info.keyframe_auto_threshold=80;
+ x->x_theora_info.keyframe_mindistance=8;
+ x->x_theora_info.noise_sensitivity=1;
+
+ theora_encode_init(&x->x_theora_state,&x->x_theora_info);
+
+ vorbis_info_init(&x->x_vorbis_info);
+
+ if(x->x_aquality > -0.1)
+ {
+ ret = vorbis_encode_init_vbr(&x->x_vorbis_info, x->x_channels, x->x_samplerate, x->x_aquality);
+ }
+ else
+ {
+ ret = vorbis_encode_init(&x->x_vorbis_info, x->x_channels, x->x_samplerate, -1, x->x_akbps, -1);
+ }
+
+ if (ret)
+ {
+ post( "pdp_theorout~ : could not initialize vorbis encoder" );
+ x->x_einit=0;
+ return;
+ }
+
+ vorbis_comment_init(&x->x_vorbis_comment);
+ vorbis_analysis_init(&x->x_dsp_state,&x->x_vorbis_info);
+ vorbis_block_init(&x->x_dsp_state,&x->x_vorbis_block);
+
+ post( "pdp_theorout~ : encoder initialized." );
+ x->x_einit=1;
+
+}
+
+static void pdp_theorout_write_headers(t_pdp_theorout *x)
+{
+ t_int ret;
+ ogg_packet aheader, aheadercomm, aheadercode;
+
+ if ( !x->x_einit )
+ {
+ post( "pdp_theorout~ : trying to write headers but encoder is not initialized." );
+ return;
+ }
+
+ if ( x->x_tfile == NULL )
+ {
+ post( "pdp_theorout~ : trying to write headers but no file is opened." );
+ return;
+ }
+
+ theora_encode_header(&x->x_theora_state, &x->x_ogg_packet);
+ ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet);
+ if(ogg_stream_pageout(&x->x_statet, &x->x_ogg_page)!=1)
+ {
+ post( "pdp_theorout~ : ogg encoding error." );
+ return;
+ }
+ if ( ( ret = fwrite(x->x_ogg_page.header, 1, x->x_ogg_page.header_len, x->x_tfile) ) <= 0 )
+ {
+ post( "pdp_theorout~ : could not write headers (ret=%d).", ret );
+ perror( "fwrite" );
+ return;
+ }
+ if ( ( ret = fwrite(x->x_ogg_page.body, 1, x->x_ogg_page.body_len, x->x_tfile) ) <= 0 )
+ {
+ post( "pdp_theorout~ : could not write headers (ret=%d).", ret );
+ perror( "fwrite" );
+ return;
+ }
+
+ theora_comment_init(&x->x_theora_comment);
+ theora_encode_comment(&x->x_theora_comment, &x->x_ogg_packet);
+ ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet);
+ theora_encode_tables(&x->x_theora_state, &x->x_ogg_packet);
+ ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet);
+
+ vorbis_analysis_headerout(&x->x_dsp_state, &x->x_vorbis_comment,
+ &aheader,&aheadercomm,&aheadercode);
+ ogg_stream_packetin(&x->x_statev,&aheader);
+
+ if(ogg_stream_pageout(&x->x_statev, &x->x_ogg_page)!=1)
+ {
+ post( "pdp_theorout~ : ogg encoding error." );
+ return;
+ }
+ if ( ( ret = fwrite(x->x_ogg_page.header, 1, x->x_ogg_page.header_len, x->x_tfile) ) <= 0 )
+ {
+ post( "pdp_theorout~ : could not write headers (ret=%d).", ret );
+ perror( "fwrite" );
+ return;
+ }
+ if ( ( ret = fwrite(x->x_ogg_page.body, 1, x->x_ogg_page.body_len, x->x_tfile) ) <= 0 )
+ {
+ post( "pdp_theorout~ : could not write headers (ret=%d).", ret );
+ perror( "fwrite" );
+ return;
+ }
+
+ // remaining vorbis header packets
+ ogg_stream_packetin(&x->x_statev, &aheadercomm);
+ ogg_stream_packetin(&x->x_statev, &aheadercode);
+
+ // flush all the headers
+ while(1)
+ {
+ ret = ogg_stream_flush(&x->x_statet, &x->x_ogg_page);
+ if(ret<0){
+ post( "pdp_theorout~ : ogg encoding error." );
+ return;
+ }
+ if(ret==0)break;
+ if ( ( ret = fwrite(x->x_ogg_page.header, 1, x->x_ogg_page.header_len, x->x_tfile) ) <= 0 )
+ {
+ post( "pdp_theorout~ : could not write headers (ret=%d).", ret );
+ perror( "fwrite" );
+ return;
+ }
+ if ( ( ret = fwrite(x->x_ogg_page.body, 1, x->x_ogg_page.body_len, x->x_tfile) ) <= 0 )
+ {
+ post( "pdp_theorout~ : could not write headers (ret=%d).", ret );
+ perror( "fwrite" );
+ return;
+ }
+ }
+
+ while(1)
+ {
+ ret = ogg_stream_flush(&x->x_statev, &x->x_ogg_page);
+ if(ret<0){
+ post( "pdp_theorout~ : ogg encoding error." );
+ return;
+ }
+ if(ret==0)break;
+ if ( ( ret = fwrite(x->x_ogg_page.header, 1, x->x_ogg_page.header_len, x->x_tfile) ) <= 0 )
+ {
+ post( "pdp_theorout~ : could not write headers (ret=%d).", ret );
+ perror( "fwrite" );
+ return;
+ }
+ if ( ( ret = fwrite(x->x_ogg_page.body, 1, x->x_ogg_page.body_len, x->x_tfile) ) <= 0 )
+ {
+ post( "pdp_theorout~ : could not write headers (ret=%d).", ret );
+ perror( "fwrite" );
+ return;
+ }
+ }
+}
+
+ /* terminate the encoding process */
+static void pdp_theorout_shutdown_encoder(t_pdp_theorout *x)
+{
+ ogg_stream_clear(&x->x_statev);
+ vorbis_block_clear(&x->x_vorbis_block);
+ vorbis_dsp_clear(&x->x_dsp_state);
+ vorbis_comment_clear(&x->x_vorbis_comment);
+ vorbis_info_clear(&x->x_vorbis_info);
+ ogg_stream_clear(&x->x_statet);
+ theora_clear(&x->x_theora_state);
+}
+
+
+ /* close a video file */
+static void pdp_theorout_close(t_pdp_theorout *x)
+{
+ int ret;
+
+ if ( x->x_tfile )
+ {
+ if ( fclose( x->x_tfile ) < 0 )
+ {
+ post( "pdp_theorout~ : could not close output file" );
+ perror( "fclose" );
+ }
+ x->x_tfile = NULL;
+ }
+}
+
+ /* open a new video file */
+static void pdp_theorout_open(t_pdp_theorout *x, t_symbol *sfile)
+{
+ t_int ret=0;
+
+ // close previous video file if existing
+ pdp_theorout_close(x);
+
+ if ( x->x_recflag ) {
+ x->x_recflag = 0;
+ }
+ x->x_frameswritten = 0;
+
+ if ( ( x->x_tfile = fopen( sfile->s_name, "w+" ) ) == NULL )
+ {
+ post( "pdp_theorout~ : could not open output file" );
+ perror( "fopen" );
+ return;
+ }
+ else
+ {
+ post( "pdp_theorout~ : opened >%s<", sfile->s_name);
+ }
+ x->x_newfile = 1;
+
+}
+
+ /* start recording */
+static void pdp_theorout_start(t_pdp_theorout *x)
+{
+ if ( !x->x_tfile )
+ {
+ post("pdp_theorout~ : start received but no file has been opened ... ignored.");
+ return;
+ }
+
+ if ( x->x_recflag == 1 )
+ {
+ post("pdp_theorout~ : start received but recording is started ... ignored.");
+ return;
+ }
+
+ if ( gettimeofday(&x->x_tstart, NULL) == -1)
+ {
+ post("pdp_theorout~ : could not set start time" );
+ }
+
+ x->x_recflag = 1;
+ pdp_theorout_init_encoder( x );
+ pdp_theorout_write_headers( x );
+ post("pdp_theorout~ : start recording at %d frames/second", x->x_framerate);
+}
+
+ /* stop recording */
+static void pdp_theorout_stop(t_pdp_theorout *x)
+{
+ if ( !x->x_tfile )
+ {
+ post("pdp_theorout~ : stop received but no file has been opened ... ignored.");
+ return;
+ }
+
+ if ( x->x_recflag == 0 )
+ {
+ post("pdp_theorout~ : stop received but recording is stopped ... ignored.");
+ return;
+ }
+
+ x->x_recflag = 0;
+
+ // record last packet
+ x->x_enduprec = 1;
+
+}
+
+ /* set video bitrate */
+static void pdp_theorout_vbitrate(t_pdp_theorout *x, t_floatarg vbitrate )
+{
+ if ( ( (t_int) vbitrate < MIN_VIDEO_BITRATE ) || ( (t_int) vbitrate > MAX_VIDEO_BITRATE ) )
+ {
+ post( "pdp_theorout~ : wrong video bitrate %d : should be in [%d,%d] kbps",
+ (t_int) vbitrate, MIN_VIDEO_BITRATE, MAX_VIDEO_BITRATE );
+ return;
+ }
+ x->x_vkbps = (t_int) vbitrate;
+}
+
+ /* set audio bitrate */
+static void pdp_theorout_abitrate(t_pdp_theorout *x, t_floatarg abitrate )
+{
+ if ( ( (t_int) abitrate < MIN_AUDIO_BITRATE ) || ( (t_int) abitrate > MAX_AUDIO_BITRATE ) )
+ {
+ post( "pdp_theorout~ : wrong audio bitrate %d : should be in [%d,%d] kbps",
+ (t_int) abitrate, MIN_AUDIO_BITRATE, MAX_AUDIO_BITRATE );
+ return;
+ }
+ x->x_akbps = (t_int) abitrate;
+}
+
+ /* set video quality */
+static void pdp_theorout_vquality(t_pdp_theorout *x, t_floatarg vquality )
+{
+ if ( ( (t_int) vquality < MIN_VIDEO_QUALITY ) || ( (t_int) vquality > MAX_VIDEO_QUALITY ) )
+ {
+ post( "pdp_theorout~ : wrong video quality %d : should be in [%d,%d]",
+ (t_int) vquality, MIN_VIDEO_QUALITY, MAX_VIDEO_QUALITY );
+ return;
+ }
+ x->x_vquality = (t_int) vquality;
+}
+
+ /* set audio quality */
+static void pdp_theorout_aquality(t_pdp_theorout *x, t_floatarg aquality )
+{
+ if ( ( (t_int) aquality < MIN_AUDIO_QUALITY ) || ( (t_int) aquality > MAX_AUDIO_QUALITY ) )
+ {
+ post( "pdp_theorout~ : wrong audio quality %d : should be in [%d,%d]",
+ (t_int) aquality, MIN_AUDIO_QUALITY, MAX_AUDIO_QUALITY );
+ return;
+ }
+ x->x_aquality = (t_int) aquality;
+}
+
+ /* store audio data in PCM format in a buffer for now */
+static t_int *pdp_theorout_perform(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]); // left audio inlet
+ t_float *in2 = (t_float *)(w[2]); // right audio inlet
+ t_pdp_theorout *x = (t_pdp_theorout *)(w[3]);
+ int n = (int)(w[4]); // number of samples
+ t_float fsample;
+ t_int isample, i;
+
+ if ( x->x_recflag )
+ {
+ // just fills the buffer
+ while (n--)
+ {
+ fsample=*(in1++);
+ if (fsample > 1.0) { fsample = 1.0; }
+ if (fsample < -1.0) { fsample = -1.0; }
+ x->x_audio_buf[0][x->x_audioin_position]=fsample;
+ fsample=*(in2++);
+ if (fsample > 1.0) { fsample = 1.0; }
+ if (fsample < -1.0) { fsample = -1.0; }
+ x->x_audio_buf[1][x->x_audioin_position]=fsample;
+ x->x_audioin_position=(x->x_audioin_position+1)%(MAX_AUDIO_PACKET_SIZE);
+ if ( x->x_audioin_position == MAX_AUDIO_PACKET_SIZE-1 )
+ {
+ post( "pdp_theorout~ : reaching end of audio buffer" );
+ }
+ }
+ }
+
+ return (w+5);
+}
+
+static void pdp_theorout_dsp(t_pdp_theorout *x, t_signal **sp)
+{
+ dsp_add(pdp_theorout_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
+}
+
+static void pdp_theorout_process_yv12(t_pdp_theorout *x)
+{
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ unsigned char *data = (unsigned char *)pdp_packet_data(x->x_packet0);
+ t_int i, ret;
+ t_int px, py;
+ char *pY, *pU, *pV;
+ struct timeval trec;
+ t_int nbaudiosamples, nbusecs, nbrecorded;
+ t_float fframerate=0.0;
+ t_int precflag;
+ ogg_page apage;
+ ogg_page vpage;
+ t_float **vbuffer;
+ double videotime, audiotime;
+
+ if ( ( (int)(header->info.image.width) != x->x_vwidth ) ||
+ ( (int)(header->info.image.height) != x->x_vheight ) ||
+ ( x->x_newfile ) )
+ {
+ precflag = x->x_recflag;
+ x->x_recflag = 0;
+ pdp_theorout_free_ressources( x );
+ pdp_theorout_shutdown_encoder( x );
+ x->x_vwidth = header->info.image.width;
+ x->x_vheight = header->info.image.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+ x->x_tvwidth=((x->x_vwidth + 15) >>4)<<4;
+ x->x_tvheight=((x->x_vheight + 15) >>4)<<4;
+ pdp_theorout_allocate( x );
+ if ( x->x_tzero.tv_sec != 0 )
+ {
+ pdp_theorout_init_encoder( x );
+ pdp_theorout_write_headers( x );
+ }
+ x->x_recflag = precflag;
+ x->x_newfile = 0;
+ }
+
+ if ( x->x_tzero.tv_sec == 0 )
+ {
+ if ( gettimeofday(&x->x_tzero, NULL) == -1)
+ {
+ post("pdp_theorout~ : could get initial time" );
+ }
+ }
+
+ x->x_frames++;
+
+ // calculate current framerate
+ if ( gettimeofday(&x->x_tcurrent, NULL) == -1)
+ {
+ post("pdp_theorout~ : could get current time" );
+ }
+
+ // calculate frame rate if it hasn't been set
+ if ( ( x->x_tcurrent.tv_sec - x->x_tzero.tv_sec ) > 0 )
+ {
+ x->x_framerate = x->x_frames / ( x->x_tcurrent.tv_sec - x->x_tzero.tv_sec );
+ }
+ else
+ {
+ x->x_framerate = DEFAULT_FRAME_RATE;
+ }
+
+ if ( x->x_frameswritten == 0 )
+ {
+ if ( gettimeofday(&x->x_tlastrec, NULL) == -1)
+ {
+ post("pdp_theorout~ : could set start time" );
+ }
+ }
+
+ pY = x->x_yuvbuffer.y;
+ memcpy( (void*)pY, (void*)&data[0], x->x_vsize );
+ pV = x->x_yuvbuffer.v;
+ memcpy( (void*)pV, (void*)&data[x->x_vsize], (x->x_vsize>>2) );
+ pU = x->x_yuvbuffer.u;
+ memcpy( (void*)pU, (void*)&data[x->x_vsize+(x->x_vsize>>2)], (x->x_vsize>>2) );
+
+ if ( x->x_tfile && x->x_recflag && !x->x_enduprec)
+ {
+
+ if ( ( ret = theora_encode_YUVin( &x->x_theora_state, &x->x_yuvbuffer ) ) != 0 )
+ {
+ post( "pdp_theorout~ : could not encode yuv image (ret=%d).", ret );
+ }
+ else
+ {
+ // stream one packet
+ theora_encode_packetout(&x->x_theora_state, 0, &x->x_ogg_packet);
+ ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet);
+ // post( "pdp_theorout~ : new (theora) ogg packet : bytes:%ld, bos:%ld, eos:%ld, no:%lld",
+ // x->x_ogg_packet.bytes, x->x_ogg_packet.b_o_s,
+ // x->x_ogg_packet.e_o_s, x->x_ogg_packet.packetno );
+
+ while( ( ret = ogg_stream_pageout(&x->x_statet, &vpage) ) >0 )
+ {
+ videotime = theora_granule_time(&x->x_theora_state, ogg_page_granulepos(&vpage));
+ x->x_vbytesout+=fwrite(vpage.header, 1, vpage.header_len, x->x_tfile );
+ x->x_vbytesout+=fwrite(vpage.body, 1, vpage.body_len, x->x_tfile );
+ }
+ }
+
+ // calculate the number of audio samples to output
+ if ( gettimeofday(&trec, NULL) == -1)
+ {
+ post("pdp_theorout~ : could set stop time" );
+ }
+ // calculate time diff in micro seconds
+ nbusecs = ( trec.tv_usec - x->x_tlastrec.tv_usec ) +
+ ( trec.tv_sec - x->x_tlastrec.tv_sec )*1000000;
+ nbaudiosamples = (sys_getsr()*1000000)/nbusecs;
+ memcpy( &x->x_tlastrec, &trec, sizeof( struct timeval) );
+
+ if ( x->x_audioin_position > nbaudiosamples )
+ {
+ nbrecorded = nbaudiosamples;
+ }
+ else
+ {
+ nbrecorded = x->x_audioin_position;
+ }
+
+ vbuffer=vorbis_analysis_buffer( &x->x_dsp_state, nbrecorded );
+ memcpy( (void*)&vbuffer[0][0], (void*)&x->x_audio_buf[0][0], nbrecorded*sizeof( t_float ) );
+ memcpy( (void*)&vbuffer[1][0], (void*)&x->x_audio_buf[1][0], nbrecorded*sizeof( t_float ) );
+
+ vorbis_analysis_wrote( &x->x_dsp_state, nbrecorded);
+
+ while(vorbis_analysis_blockout( &x->x_dsp_state, &x->x_vorbis_block)==1)
+ {
+
+ // analysis, assume we want to use bitrate management
+ vorbis_analysis( &x->x_vorbis_block, NULL);
+ vorbis_bitrate_addblock( &x->x_vorbis_block );
+
+ // weld packets into the bitstream
+ while(vorbis_bitrate_flushpacket( &x->x_dsp_state, &x->x_ogg_packet))
+ {
+ ogg_stream_packetin( &x->x_statev, &x->x_ogg_packet);
+ }
+
+ }
+
+ while( ogg_stream_pageout( &x->x_statev, &apage) >0 )
+ {
+ audiotime = vorbis_granule_time(&x->x_dsp_state, ogg_page_granulepos(&apage));
+ x->x_abytesout+=fwrite(apage.header, 1, apage.header_len, x->x_tfile );
+ x->x_abytesout+=fwrite(apage.body, 1, apage.body_len, x->x_tfile );
+ }
+
+ memcpy( &x->x_audio_buf[0][0], &x->x_audio_buf[0][nbrecorded],
+ ( x->x_audioin_position-nbrecorded ) * sizeof( t_float ) );
+ memcpy( &x->x_audio_buf[1][0], &x->x_audio_buf[1][nbrecorded],
+ ( x->x_audioin_position-nbrecorded ) * sizeof( t_float ) );
+ x->x_audioin_position -= nbrecorded;
+ // post ( "pdp_theorout~ : recorded %d samples.", nbrecorded );
+
+ x->x_frameswritten++;
+
+ }
+
+ if ( x->x_tfile && x->x_enduprec )
+ {
+ x->x_enduprec = 0;
+ post( "pdp_theorout~ : ending up recording." );
+ x->x_frameswritten++;
+
+ if ( ( ret = theora_encode_YUVin( &x->x_theora_state, &x->x_yuvbuffer ) ) != 0 )
+ {
+ post( "pdp_theorout~ : could not encode yuv image (ret=%d).", ret );
+ }
+ else
+ {
+ // stream one packet
+ theora_encode_packetout(&x->x_theora_state, 1, &x->x_ogg_packet);
+ ogg_stream_packetin( &x->x_statet, &x->x_ogg_packet);
+
+ while( ( ret = ogg_stream_pageout( &x->x_statet, &vpage) ) > 0 )
+ {
+ videotime = theora_granule_time(&x->x_theora_state, ogg_page_granulepos(&vpage));
+ x->x_vbytesout+=fwrite(vpage.header, 1, vpage.header_len, x->x_tfile );
+ x->x_vbytesout+=fwrite(vpage.body, 1, vpage.body_len, x->x_tfile );
+ }
+ }
+
+ // end up audio stream
+ vorbis_analysis_wrote( &x->x_dsp_state, 0);
+
+ while(vorbis_analysis_blockout( &x->x_dsp_state, &x->x_vorbis_block)==1)
+ {
+ // analysis, assume we want to use bitrate management
+ vorbis_analysis( &x->x_vorbis_block, NULL);
+ vorbis_bitrate_addblock( &x->x_vorbis_block);
+
+ // weld packets into the bitstream
+ while(vorbis_bitrate_flushpacket( &x->x_dsp_state, &x->x_ogg_packet))
+ {
+ ogg_stream_packetin( &x->x_statev, &x->x_ogg_packet);
+ }
+ }
+
+ while( ogg_stream_pageout( &x->x_statev, &apage) >0 )
+ {
+ audiotime = vorbis_granule_time(&x->x_dsp_state, ogg_page_granulepos(&apage));
+ x->x_abytesout+=fwrite(apage.header, 1, apage.header_len, x->x_tfile );
+ x->x_abytesout+=fwrite(apage.body, 1, apage.body_len, x->x_tfile );
+ }
+
+ post("pdp_theorout~ : stop recording");
+
+ pdp_theorout_shutdown_encoder( x );
+ pdp_theorout_close(x);
+ }
+
+ return;
+}
+
+static void pdp_theorout_killpacket(t_pdp_theorout *x)
+{
+ /* release the packet */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+}
+
+static void pdp_theorout_process(t_pdp_theorout *x)
+{
+ int encoding;
+ t_pdp *header = 0;
+
+ /* check if image data packets are compatible */
+ if ( (header = pdp_packet_header(x->x_packet0))
+ && (PDP_BITMAP == header->type)){
+
+ /* pdp_theorout_process inputs and write into active inlet */
+ switch(pdp_packet_header(x->x_packet0)->info.image.encoding)
+ {
+
+ case PDP_BITMAP_YV12:
+ if ( x->x_tfile && x->x_recflag )
+ {
+ outlet_float( x->x_obj.ob_outlet, x->x_frameswritten );
+ }
+ pdp_queue_add(x, pdp_theorout_process_yv12, pdp_theorout_killpacket, &x->x_queue_id);
+ break;
+
+ default:
+ /* don't know the type, so dont pdp_theorout_process */
+ break;
+
+ }
+ }
+
+}
+
+static void pdp_theorout_input_0(t_pdp_theorout *x, t_symbol *s, t_floatarg f)
+{
+ /* if this is a register_ro message or register_rw message, register with packet factory */
+
+ if (s== gensym("register_rw"))
+ {
+ x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("bitmap/yv12/*") );
+ }
+
+ if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped))
+ {
+ /* add the process method and callback to the process queue */
+ pdp_theorout_process(x);
+ }
+
+}
+
+static void pdp_theorout_free(t_pdp_theorout *x)
+{
+ int i;
+
+ pdp_queue_finish(x->x_queue_id);
+ pdp_packet_mark_unused(x->x_packet0);
+ // close video file if existing
+ pdp_theorout_close(x);
+ for ( i=0; i<x->x_channels; i++)
+ {
+ if ( x->x_audio_buf[i] ) freebytes( x->x_audio_buf[i], MAX_AUDIO_PACKET_SIZE*sizeof(t_float) );
+ }
+ if ( x->x_audio_buf ) freebytes( x->x_audio_buf, x->x_channels*sizeof(t_float*) );
+
+}
+
+t_class *pdp_theorout_class;
+
+void *pdp_theorout_new(void)
+{
+ t_int i;
+
+ t_pdp_theorout *x = (t_pdp_theorout *)pd_new(pdp_theorout_class);
+ inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal"));
+ outlet_new (&x->x_obj, &s_float);
+
+ x->x_packet0 = -1;
+ x->x_packet1 = -1;
+ x->x_queue_id = -1;
+
+ x->x_tfile = NULL;
+ x->x_yuvbuffer.y = NULL;
+ x->x_yuvbuffer.u = NULL;
+ x->x_yuvbuffer.v = NULL;
+
+ /* audio defaults */
+ x->x_samplerate = sys_getsr();
+ x->x_channels = DEFAULT_CHANNELS;
+ x->x_bits = DEFAULT_BITS;
+
+ x->x_framerate = DEFAULT_FRAME_RATE;
+ x->x_vkbps = DEFAULT_VIDEO_BITRATE;
+ x->x_vquality = DEFAULT_VIDEO_QUALITY;
+ x->x_akbps = DEFAULT_AUDIO_BITRATE;
+ x->x_aquality = DEFAULT_AUDIO_QUALITY;
+
+ x->x_audio_buf = (t_float**) getbytes( x->x_channels*sizeof(t_float*) );
+ for ( i=0; i<x->x_channels; i++)
+ {
+ x->x_audio_buf[i] = (t_float*) getbytes( MAX_AUDIO_PACKET_SIZE*sizeof(t_float) );
+ }
+
+ x->x_newfile = 0;
+ x->x_frames = 0;
+ x->x_frameswritten = 0;
+
+ x->x_tzero.tv_sec = 0;
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_theorout_tilde_setup(void)
+{
+ // post( pdp_theorout_version );
+ pdp_theorout_class = class_new(gensym("pdp_theorout~"), (t_newmethod)pdp_theorout_new,
+ (t_method)pdp_theorout_free, sizeof(t_pdp_theorout), 0, A_NULL);
+
+ CLASS_MAINSIGNALIN(pdp_theorout_class, t_pdp_theorout, x_f );
+ class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_dsp, gensym("dsp"), 0);
+ class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_open, gensym("open"), A_SYMBOL, A_NULL);
+ class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_close, gensym("close"), A_NULL);
+ class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_start, gensym("start"), A_NULL);
+ class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_stop, gensym("stop"), A_NULL);
+ class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_abitrate, gensym("audiobitrate"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_vbitrate, gensym("videobitrate"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_aquality, gensym("audioquality"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_vquality, gensym("videoquality"), A_FLOAT, A_NULL);
+ class_sethelpsymbol( pdp_theorout_class, gensym("pdp_theorout~.pd") );
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/patches/cutandpaste.pd b/patches/cutandpaste.pd
new file mode 100644
index 0000000..bbb0283
--- /dev/null
+++ b/patches/cutandpaste.pd
@@ -0,0 +1,114 @@
+#N canvas 237 21 712 664 10;
+#X obj 164 55 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 71 103 loop \$1;
+#X obj 71 81 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 55 55 open \$1;
+#X obj 55 31 openpanel;
+#X obj 55 9 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1 -1
+;
+#X floatatom 216 56 5 0 0 0 - - -;
+#X msg 125 53 stop;
+#X obj 157 92 metro 70;
+#X obj 152 124 pdp_yqt;
+#X obj 263 113 pdp_v4l;
+#X obj 263 87 metro 70;
+#X obj 303 56 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 264 55 stop;
+#X msg 326 87 open /dev/video;
+#X obj 582 496 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 582 548 pdp_control;
+#X msg 582 521 thread \$1;
+#X floatatom 582 609 5 0 0 0 - - -;
+#X obj 582 580 route pdp_drop;
+#X obj 176 274 pdp_cropper;
+#X floatatom 336 230 5 0 0 0 - - -;
+#X floatatom 381 229 5 0 0 0 - - -;
+#X floatatom 295 251 5 0 0 0 - - -;
+#X floatatom 294 272 5 0 0 0 - - -;
+#X obj 257 501 pack f f;
+#X floatatom 258 462 5 0 0 0 - - -;
+#X floatatom 308 462 5 0 0 0 - - -;
+#X obj 298 481 t b f;
+#X msg 257 525 offset 2 \$1 \$2;
+#X msg 308 439 100;
+#X obj 152 169 pdp_scale 320 240;
+#X msg 338 249 20;
+#X msg 381 208 180;
+#X obj 41 605 pdp_xv;
+#X msg 492 213 bang;
+#X text 532 213 Crop!;
+#X msg 258 440 34;
+#X msg 336 209 100;
+#X msg 337 271 100;
+#X obj 353 448 loadbang;
+#X obj 176 360 pdp_canvas 320 240 2;
+#X obj 371 323 pdp_background;
+#X msg 489 322 0;
+#X obj 525 322 loadbang;
+#X obj 176 395 pdp_rotate;
+#X floatatom 257 384 5 0 0 0 - - -;
+#X obj 41 545 pdp_ocanvas 320 240 2;
+#X msg 258 548 alpha 1 0.3;
+#X msg 258 569 alpha 2 0.7;
+#X connect 0 0 8 0;
+#X connect 1 0 9 0;
+#X connect 2 0 1 0;
+#X connect 3 0 9 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 8 1;
+#X connect 7 0 8 0;
+#X connect 8 0 9 0;
+#X connect 8 0 42 0;
+#X connect 9 0 31 0;
+#X connect 10 0 31 0;
+#X connect 11 0 10 0;
+#X connect 12 0 11 0;
+#X connect 13 0 11 0;
+#X connect 14 0 10 0;
+#X connect 15 0 17 0;
+#X connect 16 0 19 0;
+#X connect 17 0 16 0;
+#X connect 19 0 18 0;
+#X connect 20 0 41 2;
+#X connect 21 0 20 1;
+#X connect 22 0 20 2;
+#X connect 23 0 20 3;
+#X connect 24 0 20 4;
+#X connect 25 0 29 0;
+#X connect 26 0 25 0;
+#X connect 27 0 28 0;
+#X connect 28 0 25 0;
+#X connect 28 1 25 1;
+#X connect 29 0 41 0;
+#X connect 30 0 27 0;
+#X connect 31 0 20 0;
+#X connect 31 0 47 1;
+#X connect 32 0 23 0;
+#X connect 33 0 22 0;
+#X connect 35 0 33 0;
+#X connect 35 0 38 0;
+#X connect 35 0 32 0;
+#X connect 35 0 39 0;
+#X connect 37 0 26 0;
+#X connect 38 0 21 0;
+#X connect 39 0 24 0;
+#X connect 40 0 37 0;
+#X connect 40 0 30 0;
+#X connect 40 0 48 0;
+#X connect 40 0 49 0;
+#X connect 41 0 45 0;
+#X connect 42 0 41 1;
+#X connect 43 0 42 3;
+#X connect 43 0 42 2;
+#X connect 43 0 42 1;
+#X connect 44 0 43 0;
+#X connect 45 0 47 2;
+#X connect 46 0 45 1;
+#X connect 47 0 34 0;
+#X connect 48 0 47 0;
+#X connect 49 0 47 0;
diff --git a/patches/morphology/help-closing.pd b/patches/morphology/help-closing.pd
new file mode 100644
index 0000000..de4f8a8
--- /dev/null
+++ b/patches/morphology/help-closing.pd
@@ -0,0 +1,98 @@
+#N canvas 381 0 781 666 10;
+#X obj 341 20 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 196 92 loop \$1;
+#X obj 197 70 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0 1
+;
+#X msg 453 93 open \$1;
+#X obj 452 69 openpanel;
+#X obj 437 52 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 389 55 5 0 0 0 - - -;
+#X msg 298 21 stop;
+#X obj 396 24 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 6900 1;
+#X obj 330 91 metro 70;
+#X obj 325 123 pdp_yqt;
+#X obj 25 193 pdp_v4l;
+#X obj 34 162 metro 70;
+#X obj 79 128 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 36 129 stop;
+#X msg 121 160 open /dev/video;
+#X text 301 612 written by Yves Degoyon ( ydegoyon@free.fr );
+#X msg 31 236 pick;
+#X floatatom 248 262 5 0 0 0 - - -;
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X msg 72 237 setcur \$1 \$2;
+#X floatatom 174 328 5 0 0 0 - - -;
+#X floatatom 224 329 5 0 0 0 - - -;
+#X floatatom 271 328 5 0 0 0 - - -;
+#X text 294 261 Tolerance ( default = 55 );
+#X obj 138 289 pdp_binary ----;
+#X obj 41 369 route press drag release;
+#X msg 42 311 cursor 1;
+#X floatatom 250 370 5 0 0 0 - - -;
+#X text 296 369 Number of passes ( default = 1 );
+#X obj 41 342 pdp_glx;
+#X floatatom 271 396 5 0 0 0 - - -;
+#X floatatom 292 420 5 0 0 0 - - -;
+#X text 317 395 Kernel width ( default = 3 );
+#X text 337 417 Kernel height ( default = 3 );
+#X floatatom 265 450 5 0 0 0 - - -;
+#X text 311 449 Number of passes ( default = 1 );
+#X text 302 594 morphology : opening;
+#X obj 148 550 pdp_glx;
+#X obj 61 508 pdp_xor;
+#X obj 61 547 pdp_glx;
+#X obj 148 437 pdp_dilate ----;
+#X obj 148 509 pdp_erode ----;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 29 0;
+#X connect 11 0 29 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 17 0 29 0;
+#X connect 18 0 29 6;
+#X connect 19 0 21 0;
+#X connect 20 0 23 0;
+#X connect 21 0 20 0;
+#X connect 23 0 22 0;
+#X connect 24 0 17 0;
+#X connect 24 0 29 0;
+#X connect 29 0 34 0;
+#X connect 29 0 31 0;
+#X connect 29 0 43 0;
+#X connect 29 0 45 0;
+#X connect 29 1 25 0;
+#X connect 29 2 26 0;
+#X connect 29 3 27 0;
+#X connect 30 0 24 0;
+#X connect 31 0 34 0;
+#X connect 32 0 45 1;
+#X connect 34 0 30 0;
+#X connect 35 0 45 2;
+#X connect 35 0 46 2;
+#X connect 36 0 45 3;
+#X connect 36 0 46 3;
+#X connect 39 0 46 1;
+#X connect 43 0 44 0;
+#X connect 45 0 46 0;
+#X connect 46 0 42 0;
+#X connect 46 0 43 1;
diff --git a/patches/morphology/help-opening.pd b/patches/morphology/help-opening.pd
new file mode 100644
index 0000000..660a59e
--- /dev/null
+++ b/patches/morphology/help-opening.pd
@@ -0,0 +1,98 @@
+#N canvas 381 0 781 666 10;
+#X obj 341 20 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 196 92 loop \$1;
+#X obj 197 70 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 453 93 open \$1;
+#X obj 452 69 openpanel;
+#X obj 437 52 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 389 55 5 0 0 0 - - -;
+#X msg 298 21 stop;
+#X obj 396 24 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 330 91 metro 70;
+#X obj 325 123 pdp_yqt;
+#X obj 25 193 pdp_v4l;
+#X obj 34 162 metro 70;
+#X obj 79 128 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 36 129 stop;
+#X msg 121 160 open /dev/video;
+#X text 301 612 written by Yves Degoyon ( ydegoyon@free.fr );
+#X msg 31 236 pick;
+#X floatatom 248 262 5 0 0 0 - - -;
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X msg 72 237 setcur \$1 \$2;
+#X floatatom 174 328 5 0 0 0 - - -;
+#X floatatom 224 329 5 0 0 0 - - -;
+#X floatatom 271 328 5 0 0 0 - - -;
+#X text 294 261 Tolerance ( default = 55 );
+#X obj 138 289 pdp_binary ----;
+#X obj 41 369 route press drag release;
+#X msg 42 311 cursor 1;
+#X floatatom 250 370 5 0 0 0 - - -;
+#X text 296 369 Number of passes ( default = 1 );
+#X obj 41 342 pdp_glx;
+#X floatatom 271 396 5 0 0 0 - - -;
+#X floatatom 292 420 5 0 0 0 - - -;
+#X text 317 395 Kernel width ( default = 3 );
+#X text 337 417 Kernel height ( default = 3 );
+#X obj 148 437 pdp_erode ----;
+#X floatatom 265 450 5 0 0 0 - - -;
+#X text 311 449 Number of passes ( default = 1 );
+#X text 302 594 morphology : opening;
+#X obj 148 509 pdp_dilate ----;
+#X obj 148 550 pdp_glx;
+#X obj 61 508 pdp_xor;
+#X obj 61 547 pdp_glx;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 29 0;
+#X connect 11 0 29 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 17 0 29 0;
+#X connect 18 0 29 6;
+#X connect 19 0 21 0;
+#X connect 20 0 23 0;
+#X connect 21 0 20 0;
+#X connect 23 0 22 0;
+#X connect 24 0 17 0;
+#X connect 24 0 29 0;
+#X connect 29 0 34 0;
+#X connect 29 0 31 0;
+#X connect 29 0 39 0;
+#X connect 29 0 45 0;
+#X connect 29 1 25 0;
+#X connect 29 2 26 0;
+#X connect 29 3 27 0;
+#X connect 30 0 24 0;
+#X connect 31 0 34 0;
+#X connect 32 0 39 1;
+#X connect 34 0 30 0;
+#X connect 35 0 39 2;
+#X connect 35 0 43 2;
+#X connect 36 0 39 3;
+#X connect 36 0 43 3;
+#X connect 39 0 43 0;
+#X connect 40 0 43 1;
+#X connect 43 0 44 0;
+#X connect 43 0 45 1;
+#X connect 45 0 46 0;
diff --git a/patches/morphology/help-skeletization.pd b/patches/morphology/help-skeletization.pd
new file mode 100644
index 0000000..342f6cb
--- /dev/null
+++ b/patches/morphology/help-skeletization.pd
@@ -0,0 +1,113 @@
+#N canvas 368 0 781 666 10;
+#X obj 341 20 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 196 92 loop \$1;
+#X obj 197 70 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 453 93 open \$1;
+#X obj 452 69 openpanel;
+#X obj 437 52 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 389 55 5 0 0 0 - - -;
+#X msg 298 21 stop;
+#X obj 396 24 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 330 91 metro 70;
+#X obj 325 123 pdp_yqt;
+#X obj 25 193 pdp_v4l;
+#X obj 34 162 metro 70;
+#X obj 79 128 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 36 129 stop;
+#X msg 121 160 open /dev/video;
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X obj 114 339 pdp_distance ----;
+#X obj 287 289 pdp_glx;
+#X text 297 552 morphology : skeletization;
+#X text 297 570 written by Yves Degoyon ( ydegoyon@free.fr );
+#X floatatom 270 471 5 0 0 0 - - -;
+#X floatatom 150 510 5 0 0 0 - - -;
+#X floatatom 200 511 5 0 0 0 - - -;
+#X floatatom 247 510 5 0 0 0 - - -;
+#X text 270 443 Tolerance ( default = 55 );
+#X obj 114 471 pdp_binary ----;
+#X floatatom 110 445 5 0 0 0 - - -;
+#X text 157 443 Luma component;
+#X obj 138 413 pdp_glx;
+#X obj 347 470 loadbang;
+#X obj 114 289 pdp_binary ----;
+#X floatatom 228 257 5 0 0 0 - - -;
+#X text 275 256 Tolerance ( default = 55 );
+#X obj 114 543 pdp_glx;
+#X obj 114 384 pdp_gain;
+#X floatatom 176 363 5 0 0 0 - - -;
+#X msg 229 362 2;
+#X obj 264 362 loadbang;
+#X obj 11 445 loadbang;
+#X msg 76 444 200;
+#X msg 313 471 55;
+#X msg 116 240 pick;
+#X msg 115 262 setcur \$1 \$2;
+#X floatatom 148 317 5 0 0 0 - - -;
+#X floatatom 189 317 5 0 0 0 - - -;
+#X floatatom 236 317 5 0 0 0 - - -;
+#X obj 287 315 route press drag release;
+#X msg 227 232 cursor 1;
+#X obj 344 161 pdp_glx;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 35 0;
+#X connect 10 0 52 0;
+#X connect 10 0 53 0;
+#X connect 11 0 35 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 16 0 18 0;
+#X connect 17 0 20 0;
+#X connect 18 0 17 0;
+#X connect 20 0 19 0;
+#X connect 21 0 39 0;
+#X connect 22 0 51 0;
+#X connect 25 0 30 6;
+#X connect 30 0 38 0;
+#X connect 30 1 26 0;
+#X connect 30 2 27 0;
+#X connect 30 3 28 0;
+#X connect 31 0 30 1;
+#X connect 34 0 45 0;
+#X connect 35 0 22 0;
+#X connect 35 0 21 0;
+#X connect 35 1 48 0;
+#X connect 35 2 49 0;
+#X connect 35 3 50 0;
+#X connect 36 0 35 6;
+#X connect 39 0 33 0;
+#X connect 39 0 30 0;
+#X connect 40 0 39 1;
+#X connect 41 0 40 0;
+#X connect 42 0 41 0;
+#X connect 43 0 44 0;
+#X connect 44 0 31 0;
+#X connect 45 0 25 0;
+#X connect 46 0 35 0;
+#X connect 47 0 46 0;
+#X connect 47 0 35 0;
+#X connect 51 0 47 0;
+#X connect 52 0 22 0;
+#X connect 52 0 53 0;
+#X connect 53 0 51 0;
diff --git a/patches/morphology/help-thickening.pd b/patches/morphology/help-thickening.pd
new file mode 100644
index 0000000..4bb41ca
--- /dev/null
+++ b/patches/morphology/help-thickening.pd
@@ -0,0 +1,100 @@
+#N canvas 381 0 781 666 10;
+#X obj 341 20 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 196 92 loop \$1;
+#X obj 197 70 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 453 93 open \$1;
+#X obj 452 69 openpanel;
+#X obj 437 52 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 389 55 5 0 0 0 - - -;
+#X msg 298 21 stop;
+#X obj 396 24 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 330 91 metro 70;
+#X obj 325 123 pdp_yqt;
+#X obj 25 193 pdp_v4l;
+#X obj 34 162 metro 70;
+#X obj 79 128 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 36 129 stop;
+#X msg 121 160 open /dev/video;
+#X text 306 611 written by Yves Degoyon ( ydegoyon@free.fr );
+#X msg 31 236 pick;
+#X floatatom 248 262 5 0 0 0 - - -;
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X msg 72 237 setcur \$1 \$2;
+#X floatatom 174 328 5 0 0 0 - - -;
+#X floatatom 224 329 5 0 0 0 - - -;
+#X floatatom 271 328 5 0 0 0 - - -;
+#X text 294 261 Tolerance ( default = 55 );
+#X obj 41 369 route press drag release;
+#X msg 42 311 cursor 1;
+#X text 296 369 Number of passes ( default = 1 );
+#X floatatom 271 396 5 0 0 0 - - -;
+#X floatatom 292 420 5 0 0 0 - - -;
+#X text 317 395 Kernel width ( default = 3 );
+#X text 338 418 Kernel height ( default = 3 );
+#X obj 143 501 pdp_hitandmiss ----;
+#X obj 41 342 pdp_glx;
+#X obj 63 576 pdp_glx;
+#X obj 138 289 pdp_binary ----;
+#X text 307 594 morphology : thinning;
+#X floatatom 247 368 5 0 0 0 - - -;
+#X msg 329 494 kernel -1 1 -1 0 1 1 0 0 -1;
+#X msg 327 471 kernel 1 1 -1 1 0 -1 1 -1 0;
+#X obj 327 444 loadbang;
+#X obj 64 550 pdp_or;
+#X obj 152 560 pdp_xor;
+#X obj 152 589 pdp_glx;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 39 0;
+#X connect 11 0 39 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 17 0 39 0;
+#X connect 18 0 39 6;
+#X connect 19 0 21 0;
+#X connect 20 0 23 0;
+#X connect 21 0 20 0;
+#X connect 23 0 22 0;
+#X connect 24 0 17 0;
+#X connect 24 0 39 0;
+#X connect 29 0 24 0;
+#X connect 30 0 37 0;
+#X connect 32 0 36 2;
+#X connect 33 0 36 3;
+#X connect 36 0 45 1;
+#X connect 37 0 29 0;
+#X connect 39 0 30 0;
+#X connect 39 0 36 0;
+#X connect 39 0 37 0;
+#X connect 39 0 45 0;
+#X connect 39 0 46 1;
+#X connect 39 1 25 0;
+#X connect 39 2 26 0;
+#X connect 39 3 27 0;
+#X connect 41 0 36 1;
+#X connect 42 0 36 0;
+#X connect 43 0 36 0;
+#X connect 44 0 43 0;
+#X connect 45 0 38 0;
+#X connect 45 0 46 0;
+#X connect 46 0 47 0;
diff --git a/patches/morphology/help-thinning.pd b/patches/morphology/help-thinning.pd
new file mode 100644
index 0000000..5b49da0
--- /dev/null
+++ b/patches/morphology/help-thinning.pd
@@ -0,0 +1,95 @@
+#N canvas 381 0 781 666 10;
+#X obj 341 20 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 196 92 loop \$1;
+#X obj 197 70 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 453 93 open \$1;
+#X obj 452 69 openpanel;
+#X obj 437 52 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 389 55 5 0 0 0 - - -;
+#X msg 298 21 stop;
+#X obj 396 24 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 330 91 metro 70;
+#X obj 325 123 pdp_yqt;
+#X obj 25 193 pdp_v4l;
+#X obj 34 162 metro 70;
+#X obj 79 128 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 36 129 stop;
+#X msg 121 160 open /dev/video;
+#X text 306 611 written by Yves Degoyon ( ydegoyon@free.fr );
+#X msg 31 236 pick;
+#X floatatom 248 262 5 0 0 0 - - -;
+#X obj 594 197 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 594 249 pdp_control;
+#X msg 594 222 thread \$1;
+#X floatatom 594 310 5 0 0 0 - - -;
+#X obj 594 281 route pdp_drop;
+#X msg 72 237 setcur \$1 \$2;
+#X floatatom 174 328 5 0 0 0 - - -;
+#X floatatom 224 329 5 0 0 0 - - -;
+#X floatatom 271 328 5 0 0 0 - - -;
+#X text 294 261 Tolerance ( default = 55 );
+#X obj 41 369 route press drag release;
+#X msg 42 311 cursor 1;
+#X text 296 369 Number of passes ( default = 1 );
+#X floatatom 271 396 5 0 0 0 - - -;
+#X floatatom 292 420 5 0 0 0 - - -;
+#X text 317 395 Kernel width ( default = 3 );
+#X text 338 418 Kernel height ( default = 3 );
+#X obj 143 501 pdp_hitandmiss ----;
+#X obj 41 342 pdp_glx;
+#X obj 63 576 pdp_glx;
+#X obj 138 289 pdp_binary ----;
+#X text 307 594 morphology : thinning;
+#X floatatom 247 368 5 0 0 0 - - -;
+#X msg 329 494 kernel -1 1 -1 0 1 1 0 0 -1;
+#X obj 64 551 pdp_xor;
+#X msg 327 471 kernel -1 -1 -1 -1 1 -1 1 1 1;
+#X obj 326 444 loadbang;
+#X connect 0 0 9 0;
+#X connect 1 0 10 0;
+#X connect 2 0 1 0;
+#X connect 3 0 10 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 10 0;
+#X connect 10 0 39 0;
+#X connect 11 0 39 0;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 11 0;
+#X connect 17 0 39 0;
+#X connect 18 0 39 6;
+#X connect 19 0 21 0;
+#X connect 20 0 23 0;
+#X connect 21 0 20 0;
+#X connect 23 0 22 0;
+#X connect 24 0 17 0;
+#X connect 24 0 39 0;
+#X connect 29 0 24 0;
+#X connect 30 0 37 0;
+#X connect 32 0 36 2;
+#X connect 33 0 36 3;
+#X connect 36 0 43 1;
+#X connect 37 0 29 0;
+#X connect 39 0 30 0;
+#X connect 39 0 36 0;
+#X connect 39 0 37 0;
+#X connect 39 0 43 0;
+#X connect 39 1 25 0;
+#X connect 39 2 26 0;
+#X connect 39 3 27 0;
+#X connect 41 0 36 1;
+#X connect 42 0 36 0;
+#X connect 43 0 38 0;
+#X connect 44 0 36 0;
+#X connect 45 0 44 0;