diff options
Diffstat (limited to 'modules')
50 files changed, 19552 insertions, 0 deletions
diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..996f668 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,18 @@ +current: all_modules + +include ../Makefile + +OBJECTS = pdp_intrusion.o pdp_yqt.o pdp_simura.o pdp_underwatch.o \ + pdp_vertigo.o pdp_yvu2rgb.o pdp_lens.o pdp_baltan.o \ + pdp_aging.o pdp_ripple.o pdp_warp.o pdp_rev.o \ + pdp_mosaic.o pdp_edge.o pdp_spiral.o pdp_radioactiv.o \ + pdp_warhol.o pdp_nervous.o pdp_quark.o pdp_spigot.o \ + pdp_rec~.o pdp_o.o pdp_i.o pdp_mgrid.o pdp_ctrack.o \ + pdp_cycle.o pdp_transform.o pdp_shagadelic.o \ + pdp_dice.o pdp_puzzle.o pdp_text.o pdp_form.o \ + pdp_compose.o pdp_cmap.o pdp_aa.o pdp_ascii.o \ + pdp_ffmpeg~.o pdp_live~.o pdp_segsnd~.o pdp_noquark.o \ + pdp_juxta.o pdp_capture.o pdp_smuck.o pdp_lumafilt.o \ + pdp_transition.o pdp_imgloader.o pdp_imgsaver.o + +all_modules: $(OBJECTS) diff --git a/modules/Makefile.in b/modules/Makefile.in new file mode 100644 index 0000000..996f668 --- /dev/null +++ b/modules/Makefile.in @@ -0,0 +1,18 @@ +current: all_modules + +include ../Makefile + +OBJECTS = pdp_intrusion.o pdp_yqt.o pdp_simura.o pdp_underwatch.o \ + pdp_vertigo.o pdp_yvu2rgb.o pdp_lens.o pdp_baltan.o \ + pdp_aging.o pdp_ripple.o pdp_warp.o pdp_rev.o \ + pdp_mosaic.o pdp_edge.o pdp_spiral.o pdp_radioactiv.o \ + pdp_warhol.o pdp_nervous.o pdp_quark.o pdp_spigot.o \ + pdp_rec~.o pdp_o.o pdp_i.o pdp_mgrid.o pdp_ctrack.o \ + pdp_cycle.o pdp_transform.o pdp_shagadelic.o \ + pdp_dice.o pdp_puzzle.o pdp_text.o pdp_form.o \ + pdp_compose.o pdp_cmap.o pdp_aa.o pdp_ascii.o \ + pdp_ffmpeg~.o pdp_live~.o pdp_segsnd~.o pdp_noquark.o \ + pdp_juxta.o pdp_capture.o pdp_smuck.o pdp_lumafilt.o \ + pdp_transition.o pdp_imgloader.o pdp_imgsaver.o + +all_modules: $(OBJECTS) diff --git a/modules/pdp_aa.c b/modules/pdp_aa.c new file mode 100644 index 0000000..ee159f9 --- /dev/null +++ b/modules/pdp_aa.c @@ -0,0 +1,324 @@ +/* + * 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 interface to aalib ( http://aa-project.sourceforge.net/aalib/ ) + * converting an image to ASCII art + * Written by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include "yuv.h" +#include <math.h> +#include <aalib.h> + +#define MAX_OPTIONS 20 +#define MAX_OPTION_LENGTH 20 + +static char *pdp_aa_version = "pdp_aa: version 0.1, ASCII art output written by ydegoyon@free.fr "; + +typedef struct pdp_aa_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + /* aalib structures */ + aa_context *x_context; // a lot of things and image data + aa_renderparams x_renderparams; // rendering parameters + char* x_driver; // name of driver + t_int x_render; // rendering option + + char **x_aa_options; // aa options passed as arguments + t_int x_nb_options; // number of aa options + +} t_pdp_aa; + +static void pdp_aa_allocate(t_pdp_aa *x) +{ + if ( !strcmp( x->x_driver, "X11" ) ) + { + x->x_context = aa_init(&X11_d, &aa_defparams, NULL); + } + else if ( !strcmp( x->x_driver, "slang" ) ) + { + x->x_context = aa_init(&slang_d, &aa_defparams, NULL); + } + else if ( !strcmp( x->x_driver, "stdout" ) ) + { + x->x_context = aa_init(&stdout_d, &aa_defparams, NULL); + } + else if ( !strcmp( x->x_driver, "stderr" ) ) + { + x->x_context = aa_init(&stderr_d, &aa_defparams, NULL); + } + else + { + post( "pdp_aa : unsupported driver : %s : using X11", x->x_driver ); + strcpy( x->x_driver, "X11" ); + pdp_aa_allocate( x ); + return; + } + + if (x->x_context == NULL) + { + post("pdp_aa : severe error : cannot initialize aalib !!!"); + return; + } + else + { + post("pdp_aa : initialized context"); + } + aa_setfont( x->x_context, &aa_font8 ); +} + +static void pdp_aa_free_ressources(t_pdp_aa *x) +{ + // if ( x->x_context ) aa_close( x->x_context ); // this crashes unfortunately +} + +static void pdp_aa_render(t_pdp_aa *x, t_floatarg frender) +{ + if ( ((int)frender == 0) || ((int)frender == 1) ) + { + x->x_render = (int)frender; + } +} + +static void pdp_aa_driver(t_pdp_aa *x, t_symbol *sdriver) +{ + if ( ( !strcmp( sdriver->s_name, "X11" ) ) || + ( !strcmp( sdriver->s_name, "slang" ) ) || + ( !strcmp( sdriver->s_name, "stdout" ) ) || + ( !strcmp( sdriver->s_name, "stderr" ) ) ) + { + strcpy( x->x_driver, sdriver->s_name ); + } + else + { + post( "pdp_aa : unsupported driver : %s : using X11", sdriver->s_name ); + strcpy( x->x_driver, "X11" ); + } + pdp_aa_free_ressources(x); + pdp_aa_allocate(x); +} + +static void pdp_aa_process_yv12(t_pdp_aa *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, pixsum; + t_int px, py, ppx, ppy; + t_int hratio, wratio; + + if ( ( (int)header->info.image.width != x->x_vwidth ) || + ( (int)header->info.image.height != x->x_vheight ) ) + { + pdp_aa_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_aa_allocate( x ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + if ( aa_imgwidth(x->x_context) < x->x_vwidth ) + { + wratio = x->x_vwidth / aa_imgwidth(x->x_context); + } + else + { + wratio = 1; + } + if ( aa_imgheight(x->x_context) < x->x_vheight ) + { + hratio = x->x_vheight / aa_imgheight(x->x_context); + } + else + { + hratio = 1; + } + + for(py=1; py<x->x_vheight; py+=hratio) + { + for(px=0; px<x->x_vwidth; px+=wratio) + { + pixsum = 0; + for ( ppy=0; ppy<hratio; ppy++ ) + { + for ( ppx=0; ppx<wratio; ppx++ ) + { + pixsum += (data[(py+ppy)*x->x_vwidth + (px+ppx)]>>7); + } + } + aa_putpixel(x->x_context, px/wratio, py/hratio, pixsum/(wratio*hratio)); + } + } + + if ( x->x_render ) + { + aa_fastrender(x->x_context, 0, 0, aa_scrwidth(x->x_context), aa_scrheight(x->x_context)); + aa_flush( x->x_context ); + } + + // post( "pdp_aa : ascii text : %s", x->x_context->textbuffer ); + + memcpy( newdata, data, (x->x_vsize+(x->x_vsize>>1))<<1 ); + + return; +} + +static void pdp_aa_sendpacket(t_pdp_aa *x) +{ + /* delete source 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_aa_process(t_pdp_aa *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_aa_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_aa_process_yv12, pdp_aa_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_aa_process */ + break; + + } + } + +} + +static void pdp_aa_input_0(t_pdp_aa *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_aa_process(x); + } + +} + +static void pdp_aa_free(t_pdp_aa *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_aa_free_ressources(x); + for (i=0; i<MAX_OPTIONS; i++) + { + if (x->x_aa_options[ i ]) freebytes( x->x_aa_options[ i ], MAX_OPTION_LENGTH ); + } + if (x->x_aa_options) freebytes( x->x_aa_options, MAX_OPTIONS*sizeof(char*) ); + if (x->x_driver) freebytes( x->x_driver, MAX_OPTION_LENGTH ); +} + +t_class *pdp_aa_class; + +void *pdp_aa_new(void) +{ + int i; + + t_pdp_aa *x = (t_pdp_aa *)pd_new(pdp_aa_class); + + 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_driver = (char*) getbytes( MAX_OPTION_LENGTH ); + strcpy( x->x_driver, "X11" ); + x->x_render = 1; + + // aa_setsupported( x->x_context, AA_EXTENDED ); + x->x_aa_options = (char **) getbytes( MAX_OPTIONS*sizeof(char*) ); + for (i=0; i<MAX_OPTIONS; i++) + { + x->x_aa_options[ i ] = (char*) getbytes( MAX_OPTION_LENGTH ); + } + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_aa_setup(void) +{ + post( pdp_aa_version ); + pdp_aa_class = class_new(gensym("pdp_aa"), (t_newmethod)pdp_aa_new, + (t_method)pdp_aa_free, sizeof(t_pdp_aa), 0, A_NULL); + + class_addmethod(pdp_aa_class, (t_method)pdp_aa_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_aa_class, (t_method)pdp_aa_driver, gensym("driver"), A_SYMBOL, A_NULL); + class_addmethod(pdp_aa_class, (t_method)pdp_aa_render, gensym("render"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_aging.c b/modules/pdp_aging.c new file mode 100644 index 0000000..f2317b2 --- /dev/null +++ b/modules/pdp_aging.c @@ -0,0 +1,380 @@ +/* + * 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 adaptation of aging effect from effectv + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +static char *pdp_aging_version = "pdp_aging: version 0.1, port of aging from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +#define PDP_AGING_MAX_SCRATCHES 100 +static unsigned int fastrand_val; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + +typedef struct _scratch +{ + t_int life; + t_int x; + t_int dx; + t_int init; +} scratch; + +typedef struct pdp_aging_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_area_scale; + t_int x_nb_scratches; + t_int x_dust_interval; + t_int x_pits_interval; + scratch x_scratches[PDP_AGING_MAX_SCRATCHES]; + +} t_pdp_aging; + +static void pdp_aging_area_scale(t_pdp_aging *x, t_floatarg fscale ) +{ + if ( (int) fscale > 1 ) + { + x->x_area_scale = (int)fscale; + } +} + +static void pdp_aging_scratches(t_pdp_aging *x, t_floatarg fscratches ) +{ + if ( ( (int)fscratches < PDP_AGING_MAX_SCRATCHES ) && ( (int)fscratches>=0) ) + { + x->x_nb_scratches = (int)fscratches; + } +} + +static void pdp_aging_coloraging(t_pdp_aging *x, short int *src, short int *dest) +{ + short int a, b; + int i; + + for(i=0; i<x->x_vsize; i++) + { + a = *src++; + b = (a & 0xfcfc)>>2; + *dest++ = a - b + 0x1818 + ((inline_fastrand()>>8)&0x1010); + } + // set all to b&w + for(i=x->x_vsize; i<( x->x_vsize + (x->x_vsize>>1) ); i++) + { + *dest++ = 0; + } +} + +static void pdp_aging_scratching(t_pdp_aging *x, short int *dest) +{ + int i, y, y1, y2; + short int *p, a, b; + const int width = x->x_vwidth; + const int height = x->x_vheight; + + for(i=0; i<x->x_nb_scratches; i++) + { + if(x->x_scratches[i].life) + { + x->x_scratches[i].x = x->x_scratches[i].x + x->x_scratches[i].dx; + if(x->x_scratches[i].x < 0 || x->x_scratches[i].x > width*256) + { + x->x_scratches[i].life = 0; + break; + } + p = dest + (x->x_scratches[i].x>>8); + if(x->x_scratches[i].init) + { + y1 = x->x_scratches[i].init; + x->x_scratches[i].init = 0; + } + else + { + y1 = 0; + } + x->x_scratches[i].life--; + if(x->x_scratches[i].life) + { + y2 = height; + } + else + { + y2 = inline_fastrand() % height; + } + for(y=y1; y<y2; y++) + { + a = *p & 0xfeff; + a += 0x2020; + b = a & 0x10100; + *p = a | (b - (b>>8)); + p += width; + } + } + else + { + if((inline_fastrand()&0xf0000000) == 0) + { + x->x_scratches[i].life = 2 + (inline_fastrand()>>27); + x->x_scratches[i].x = inline_fastrand() % (width * 256); + x->x_scratches[i].dx = ((int)inline_fastrand())>>23; + x->x_scratches[i].init = (inline_fastrand() % (height-1))+1; + } + } + } +} + +static void pdp_aging_dusts(t_pdp_aging *x, short int *dest) +{ + int dx[8] = { 1, 1, 0, -1, -1, -1, 0, 1}; + int dy[8] = { 0, -1, -1, -1, 0, 1, 1, 1}; + int i, j; + int dnum; + int d, len; + int px, py; + const int width = x->x_vwidth; + const int height = x->x_vheight; + + if(x->x_dust_interval == 0) + { + if((inline_fastrand()&0xf0000000) == 0) { + x->x_dust_interval = inline_fastrand()>>29; + } + return; + } + + dnum = x->x_area_scale*4 + (inline_fastrand()>>27); + for(i=0; i<dnum; i++) + { + px = inline_fastrand()%width; + py = inline_fastrand()%height; + d = inline_fastrand()>>29; + len = inline_fastrand()%x->x_area_scale + 5; + for(j=0; j<len; j++) { + dest[py*width + px] = 0x1010; + py += dy[d]; + px += dx[d]; + if(px<0 || px>=width) break; + if(py<0 || py>=height) break; + d = (d + inline_fastrand()%3 - 1) & 7; + } + } + x->x_dust_interval--; +} + +static void pdp_aging_pits(t_pdp_aging *x, short int *dest) +{ + int i, j; + int pnum, size, pnumscale; + int px, py; + const int width = x->x_vwidth; + const int height = x->x_vheight; + + pnumscale = x->x_area_scale * 2; + if(x->x_pits_interval) + { + pnum = pnumscale + (inline_fastrand()%pnumscale); + x->x_pits_interval--; + } + else + { + pnum = inline_fastrand()%pnumscale; + if((inline_fastrand()&0xf8000000) == 0) + { + x->x_pits_interval = (inline_fastrand()>>28) + 20; + } + } + for(i=0; i<pnum; i++) + { + px = inline_fastrand()%(width-1); + py = inline_fastrand()%(height-1); + size = inline_fastrand()>>28; + for(j=0; j<size; j++) + { + px = px + inline_fastrand()%3-1; + py = py + inline_fastrand()%3-1; + if(px<0 || px>=width) break; + if(py<0 || py>=height) break; + dest[py*width + px] = 0xc0c0; + } + } +} + +static void pdp_aging_process_yv12(t_pdp_aging *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); + int i; + + unsigned int totalnbpixels; + unsigned int u_offset; + unsigned int v_offset; + unsigned int totnbpixels; + short int *poy, *pou, *pov, *pny, *pnu, *pnv; + int px, py; + int noy, pos, nox; + int *p; + + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + totalnbpixels = x->x_vsize; + u_offset = x->x_vsize; + v_offset = x->x_vsize + (x->x_vsize>>2); + totnbpixels = x->x_vsize + (x->x_vsize>>1); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + pdp_aging_coloraging( x, data, newdata ); + pdp_aging_scratching( x, newdata ); + pdp_aging_pits( x, newdata ); + if ( x->x_area_scale > 1 ) pdp_aging_dusts( x, newdata ); + + return; +} + +static void pdp_aging_sendpacket(t_pdp_aging *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_aging_process(t_pdp_aging *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_aging_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_aging_process_yv12, pdp_aging_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_aging_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_aging_process */ + break; + + } + } + +} + +static void pdp_aging_input_0(t_pdp_aging *x, t_symbol *s, t_floatarg f) +{ + 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_aging_process(x); + + } + +} + +static void pdp_aging_free(t_pdp_aging *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + +} + +t_class *pdp_aging_class; + +void *pdp_aging_new(void) +{ + int i; + + t_pdp_aging *x = (t_pdp_aging *)pd_new(pdp_aging_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("area_scale")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("scratches")); + + 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_nb_scratches = 7; + x->x_area_scale=5; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_aging_setup(void) +{ +// post( pdp_aging_version ); + pdp_aging_class = class_new(gensym("pdp_aging"), (t_newmethod)pdp_aging_new, + (t_method)pdp_aging_free, sizeof(t_pdp_aging), 0, A_NULL); + + class_addmethod(pdp_aging_class, (t_method)pdp_aging_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_aging_class, (t_method)pdp_aging_area_scale, gensym("area_scale"), A_FLOAT, A_NULL); + class_addmethod(pdp_aging_class, (t_method)pdp_aging_scratches, gensym("scratches"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_aging.c~ b/modules/pdp_aging.c~ new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/modules/pdp_aging.c~ diff --git a/modules/pdp_ascii.c b/modules/pdp_ascii.c new file mode 100644 index 0000000..5d2dc50 --- /dev/null +++ b/modules/pdp_ascii.c @@ -0,0 +1,258 @@ +/* + * 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 ASCII art object replacing blocks with ASCII characters + * Written by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include "yuv.h" +#include "charmaps.h" +#include <math.h> + +static char *pdp_ascii_version = "pdp_ascii: version 0.1, ASCII art output written by ydegoyon@free.fr"; + +typedef struct pdp_ascii_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_int x_color; // rendering color option + t_int x_brightness; // added value for brightness + t_float x_ratio; // character to pixel ratio + +} t_pdp_ascii; + +static void pdp_ascii_color(t_pdp_ascii *x, t_floatarg fcolor) +{ + if ( ((int)fcolor == 0) || ((int)fcolor == 1) ) + { + x->x_color = (int)fcolor; + } +} + +static void pdp_ascii_ratio(t_pdp_ascii *x, t_floatarg fratio) +{ + if ( ( fratio > 0) && ( x->x_ratio < x->x_vwidth/2 ) ) + { + x->x_ratio = fratio; + } +} + +static void pdp_ascii_brightness(t_pdp_ascii *x, t_floatarg fbrightness) +{ + if ( ((int)fbrightness > 0) && ((int)fbrightness < 255) ) + { + x->x_brightness = (int)fbrightness; + } +} + +static void pdp_ascii_process_yv12(t_pdp_ascii *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, pixsum; + t_int px, py, ppx, ppy; + t_int rank, value; + t_int pwidth, pheight; + + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + memset( newdata, 0x00, (x->x_vsize+(x->x_vsize>>1))<<1 ); + + pwidth = (int) CHARWIDTH*x->x_ratio; + if (pwidth==0) pwidth=1; + if (pwidth>x->x_vwidth) return; + pheight = (int) CHARHEIGHT*x->x_ratio; + if (pheight==0) pheight=1; + if (pheight>x->x_vheight) return; + + for(py=1; py<x->x_vheight; py+=pheight) + { + for(px=0; px<x->x_vwidth; px+=pwidth) + { + pixsum = 0; + for ( ppy=0; ppy<pheight; ppy++ ) + { + for ( ppx=0; ppx<pwidth; ppx++ ) + { + pixsum += (data[(py+ppy)*x->x_vwidth + (px+ppx)]>>7); + } + } + rank = (pixsum/(pheight*pwidth))/2; // set the chosen character + for ( ppy=0; ppy<pheight; ppy++ ) + { + for ( ppx=0; ppx<pwidth; ppx++ ) + { + if ( ( px+ppx > x->x_vwidth ) || + ( py+ppy > x->x_vheight ) ) + { + break; + } + if ( charmaps[rank][((int)(ppy/x->x_ratio))*CHARWIDTH+((int)(ppx/x->x_ratio))] ) + { + value = ( (2*rank+x->x_brightness) > 255 ) ? 255 : (2*rank+x->x_brightness); + newdata[(py+ppy)*x->x_vwidth+(px+ppx)] = (value)<<7; + if ( x->x_color ) + { + newdata[x->x_vsize+((py+ppy)>>1)*(x->x_vwidth>>1)+((px+ppx)>>1)] = + data[x->x_vsize+((py+ppy)>>1)*(x->x_vwidth>>1)+((px+ppx)>>1)]; + newdata[x->x_vsize+(x->x_vsize>>2)+((py+ppy)>>1)*(x->x_vwidth>>1)+((px+ppx)>>1)] = + data[x->x_vsize+(x->x_vsize>>2)+((py+ppy)>>1)*(x->x_vwidth>>1)+((px+ppx)>>1)]; + } + } + } + } + } + } + + return; +} + +static void pdp_ascii_sendpacket(t_pdp_ascii *x) +{ + /* delete source 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_ascii_process(t_pdp_ascii *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_ascii_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_ascii_process_yv12, pdp_ascii_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_ascii_process */ + break; + + } + } + +} + +static void pdp_ascii_input_0(t_pdp_ascii *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_ascii_process(x); + } + +} + +static void pdp_ascii_free(t_pdp_ascii *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_ascii_class; + +void *pdp_ascii_new(void) +{ + int i; + + t_pdp_ascii *x = (t_pdp_ascii *)pd_new(pdp_ascii_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ratio")); + + 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_color = 1; + x->x_ratio = 1.; + x->x_brightness = 25; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_ascii_setup(void) +{ + post( pdp_ascii_version ); + pdp_ascii_class = class_new(gensym("pdp_ascii"), (t_newmethod)pdp_ascii_new, + (t_method)pdp_ascii_free, sizeof(t_pdp_ascii), 0, A_NULL); + + class_addmethod(pdp_ascii_class, (t_method)pdp_ascii_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_ascii_class, (t_method)pdp_ascii_color, gensym("color"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_ascii_class, (t_method)pdp_ascii_brightness, gensym("brightness"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_ascii_class, (t_method)pdp_ascii_ratio, gensym("ratio"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_baltan.c b/modules/pdp_baltan.c new file mode 100644 index 0000000..b75c074 --- /dev/null +++ b/modules/pdp_baltan.c @@ -0,0 +1,232 @@ +/* + * 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 adaptation of baltan effect from freej + * Originally written by Fukuchi Kentarou + * Adapted by Yves Degoyon + * Do not expect it to behave like effectv : well, it does things .... + */ + + + +#include "pdp.h" +#include <math.h> + +#define PLANES 32 + +#define STRIDE 8 + +typedef struct pdp_baltan_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_planebuf; + t_int x_plane; + t_int x_pixels; + t_int x_dfts; /* the factor */ + +} t_pdp_baltan; + +static void pdp_baltan_process_yv12(t_pdp_baltan *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); + + unsigned int w = header->info.image.width; + unsigned int h = header->info.image.height; + + unsigned int size = w*h; + unsigned int totalnbpixels = size; + unsigned int u_offset = size; + unsigned int v_offset = size + (size>>2); + unsigned int totnbpixels = size + (size>>1); + + t_int i, cf; + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = w; + newheader->info.image.height = h; + + /* allocate buffers if necessary */ + if ( ( x->x_planebuf == NULL ) || ( (int)size != x->x_pixels ) ) + { + if ( x->x_planebuf ) freebytes( x->x_planebuf, x->x_pixels*PLANES*sizeof(t_int) ); + + x->x_pixels = size; + x->x_planebuf = (t_int*)getbytes(x->x_pixels*PLANES*sizeof(t_int)); + post("pdp_baltan : allocated plane buffer (size=%d)", x->x_pixels*PLANES*sizeof(t_int) ); + bzero(x->x_planebuf, x->x_pixels*PLANES*sizeof(t_int)); + x->x_plane = 0; + if ( !x->x_planebuf ) + { + post( "pdp_baltan : serious error : unable to allocate buffers " ) ; + return; + } + } + + /* process data packet */ + for(i=0; i<x->x_pixels; i++) + { + *(x->x_planebuf+x->x_plane*x->x_pixels+i) = (data[i] & x->x_dfts)>>2; + } + + cf = x->x_plane & (STRIDE-1); + + for(i=0; i<x->x_pixels; i++) { + newdata[i] = *(x->x_planebuf+cf*x->x_pixels+i) + + *(x->x_planebuf+((cf+STRIDE)*x->x_pixels)+i) + + *(x->x_planebuf+((cf+2*STRIDE)*x->x_pixels)+i) + + *(x->x_planebuf+((cf+3*STRIDE)*x->x_pixels)+i); + *(x->x_planebuf+x->x_plane*x->x_pixels+i) = (newdata[i]&x->x_dfts)>>2; + } + + x->x_plane++; + x->x_plane = x->x_plane & (PLANES-1); + + /* leave the colors unchanged */ + for( i=size; i<(int)totnbpixels; i++) + { + newdata[i] = data[i]; + } + + return; +} + +static void pdp_baltan_sendpacket(t_pdp_baltan *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_baltan_process(t_pdp_baltan *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_baltan_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_baltan_process_yv12, pdp_baltan_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_baltan_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_baltan_process */ + break; + + } + } +} + +static void pdp_baltan_input_0(t_pdp_baltan *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_baltan_process(x); + + } +} + +static void pdp_baltan_dfts(t_pdp_baltan *x, t_floatarg fdfts ) +{ + x->x_dfts = (t_int)fdfts; +} + +static void pdp_baltan_free(t_pdp_baltan *x) +{ + if ( x->x_planebuf ) freebytes( x->x_planebuf, x->x_pixels*PLANES*sizeof(t_int) ); + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_baltan_class; + +void *pdp_baltan_new(void) +{ + int i; + + t_pdp_baltan *x = (t_pdp_baltan *)pd_new(pdp_baltan_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("dfts")); + + 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_planebuf = NULL; + x->x_pixels = 0; + x->x_dfts = 0xfcfcfc; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_baltan_setup(void) +{ + + + pdp_baltan_class = class_new(gensym("pdp_baltan"), (t_newmethod)pdp_baltan_new, + (t_method)pdp_baltan_free, sizeof(t_pdp_baltan), 0, A_NULL); + + class_addmethod(pdp_baltan_class, (t_method)pdp_baltan_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_baltan_class, (t_method)pdp_baltan_dfts, gensym("dfts"), A_DEFFLOAT, A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_capture.c b/modules/pdp_capture.c new file mode 100644 index 0000000..f7e9292 --- /dev/null +++ b/modules/pdp_capture.c @@ -0,0 +1,938 @@ +/* + * 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 lets you capture a portion of the screen + * and turn it into pdp packets + * ( inspired by ImageMagick code ) + */ + +#include "pdp.h" +#include "yuv.h" +#include <math.h> +#include <assert.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <magick/api.h> +#include <magick/magick.h> +#include <magick/xwindow.h> + +#define PDP_DISPLAY_LENGTH 1024 + +static char *pdp_capture_version = "pdp_capture: version 0.1, capture of screen written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_capture_struct +{ + t_object x_obj; + + t_outlet *x_outlet0; + t_int x_packet0; + short int *x_data; + t_pdp *x_header; + t_int x_displayopen; + + char *x_display; + t_int x_screen; + t_int x_x; + t_int x_y; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + Image *x_Ximage; + Display *x_dpy; +} t_pdp_capture; + +/***********************************************/ +/* this code is borrowed from ImageMagick */ +/* but not exported, sorry, guys and girls, */ +/* i only need that */ +/***********************************************/ + +static Window XMyGetSubwindow(t_pdp_capture *o, Display *display, Window window, int x, int y) +{ + Window source_window, target_window; + int status, x_offset, y_offset; + + assert(display != (Display *) NULL); + source_window=XRootWindow(display, o->x_screen); + if (window == (Window) NULL) + { + return(source_window); + } + target_window=window; + for ( ; ; ) + { + status=XTranslateCoordinates(display,source_window,window,x,y, &x_offset,&y_offset,&target_window); + if (status != 1) + { + break; + } + if (target_window == (Window) NULL) + { + break; + } + source_window=window; + window=target_window; + x=x_offset; + y=y_offset; + } + if (target_window == (Window) NULL) + { + target_window=window; + } + return(target_window); +} + +static Window XMyClientWindow(t_pdp_capture *o, Display *display,Window target_window) +{ + Atom state, type; + int format, status; + unsigned char *data; + unsigned long after, number_items; + Window client_window; + + assert(display != (Display *) NULL); + state=XInternAtom(display,"WM_STATE",1); + if (state == (Atom) NULL) + { + return(target_window); + } + type=(Atom) NULL; + status=XGetWindowProperty(display,target_window,state,0L,0L,0, (Atom) AnyPropertyType,&type,&format,&number_items,&after,&data); + if ((status == Success) && (type != (Atom) NULL)) + { + return(target_window); + } + client_window=XWindowByProperty(display,target_window,state); + if (client_window == (Window) NULL) + { + return(target_window); + } + return(client_window); +} + +static Image *XMyGetWindowImage(t_pdp_capture *o, Display *display,const Window window, const unsigned int level) +{ + typedef struct _ColormapInfo + { + Colormap colormap; + XColor *colors; + struct _ColormapInfo *next; + } ColormapInfo; + + typedef struct _WindowInfo + { + Window window, parent; + Visual *visual; + Colormap colormap; + XSegment bounds; + RectangleInfo crop_info; + } WindowInfo; + + IndexPacket index; + int display_height, display_width, id, status, x_offset, y_offset; + RectangleInfo crop_info; + register IndexPacket *indexes; + register int i; + static ColormapInfo *colormap_info = (ColormapInfo *) NULL; + static int max_windows = 0, number_windows = 0; + static WindowInfo *window_info; + Window child, root_window; + XWindowAttributes window_attributes; + + assert(display != (Display *) NULL); + status=XGetWindowAttributes(display,window,&window_attributes); + if ((status == 0) || (window_attributes.map_state != IsViewable)) + { + return((Image *) NULL); + } + root_window=XRootWindow(display, o->x_screen ); + (void) XTranslateCoordinates(display,window,root_window,0,0,&x_offset, &y_offset,&child); + crop_info.x=x_offset; + crop_info.y=y_offset; + crop_info.width=window_attributes.width; + crop_info.height=window_attributes.height; + if (crop_info.x < 0) + { + crop_info.width+=crop_info.x; + crop_info.x=0; + } + if (crop_info.y < 0) + { + crop_info.height+=crop_info.y; + crop_info.y=0; + } + display_width=XDisplayWidth(display, o->x_screen); + if ((int) (crop_info.x+crop_info.width) > display_width) + { + crop_info.width=display_width-crop_info.x; + } + display_height=XDisplayHeight(display, o->x_screen); + if ((int) (crop_info.y+crop_info.height) > display_height) + { + crop_info.height=display_height-crop_info.y; + } + if (number_windows >= max_windows) + { + max_windows+=1024; + if (window_info == (WindowInfo *) NULL) + { + window_info=(WindowInfo *) AcquireMemory(max_windows*sizeof(WindowInfo)); + } + else + { + ReacquireMemory((void **) &window_info,max_windows*sizeof(WindowInfo)); + } + } + if (window_info == (WindowInfo *) NULL) + { + post("pdp_capture : MemoryAllocationFailed : UnableToReadXImage"); + return((Image *) NULL); + } + id=number_windows++; + window_info[id].window=window; + window_info[id].visual=window_attributes.visual; + window_info[id].colormap=window_attributes.colormap; + window_info[id].bounds.x1=(short) crop_info.x; + window_info[id].bounds.y1=(short) crop_info.y; + window_info[id].bounds.x2=(short) (crop_info.x+(int) crop_info.width-1); + window_info[id].bounds.y2=(short) (crop_info.y+(int) crop_info.height-1); + crop_info.x-=x_offset; + crop_info.y-=y_offset; + + window_info[id].crop_info=crop_info; + if (level != 0) + { + unsigned int number_children; + Window *children; + + status=XQueryTree(display,window,&root_window,&window_info[id].parent, &children,&number_children); + for (i=0; i < id; i++) + { + if ((window_info[i].window == window_info[id].parent) && + (window_info[i].visual == window_info[id].visual) && + (window_info[i].colormap == window_info[id].colormap)) + { + if ((window_info[id].bounds.x1 <= window_info[i].bounds.x1) || + (window_info[id].bounds.x1 >= window_info[i].bounds.x2) || + (window_info[id].bounds.y1 <= window_info[i].bounds.y1) || + (window_info[id].bounds.y1 >= window_info[i].bounds.y2)) + { + number_windows--; + break; + } + } + } + if ((status == 1) && (number_children != 0)) + { + (void) XFree((void *) children); + } + } + if (level <= 1) + { + ColormapInfo *next; + Image *composite_image, *image; + int y; + register int j, x; + register PixelPacket *q; + register unsigned long pixel; + unsigned int import, number_colors; + XColor *colors; + XImage *ximage; + + image=(Image *) NULL; + for (id=0; id < number_windows; id++) + { + import=(window_info[id].bounds.x2 >= window_info[0].bounds.x1) && + (window_info[id].bounds.x1 <= window_info[0].bounds.x2) && + (window_info[id].bounds.y2 >= window_info[0].bounds.y1) && + (window_info[id].bounds.y1 <= window_info[0].bounds.y2); + for (j=0; j < id; j++) + { + if ((window_info[id].visual == window_info[j].visual) && + (window_info[id].colormap == window_info[j].colormap) && + (window_info[id].bounds.x1 >= window_info[j].bounds.x1) && + (window_info[id].bounds.y1 >= window_info[j].bounds.y1) && + (window_info[id].bounds.x2 <= window_info[j].bounds.x2) && + (window_info[id].bounds.y2 <= window_info[j].bounds.y2)) + { + import=0; + } + else + { + if ((window_info[id].visual != window_info[j].visual) || + (window_info[id].colormap != window_info[j].colormap)) + { + if ((window_info[id].bounds.x2 > window_info[j].bounds.x1) && + (window_info[id].bounds.x1 < window_info[j].bounds.x2) && + (window_info[id].bounds.y2 > window_info[j].bounds.y1) && + (window_info[id].bounds.y1 < window_info[j].bounds.y2)) + { + import=1; + } + } + } + } + if (!import) + { + continue; + } + // post( "pdp_capture : get image : %ld [%d,%d,%d,%d]", window_info[id].window, + // (int) window_info[id].crop_info.x, + // (int) window_info[id].crop_info.y, + // (unsigned int) window_info[id].crop_info.width, + // (unsigned int) window_info[id].crop_info.height ); + ximage=XGetImage(display,window_info[id].window, + (int) window_info[id].crop_info.x,(int) window_info[id].crop_info.y, + (unsigned int) window_info[id].crop_info.width, + (unsigned int) window_info[id].crop_info.height,AllPlanes,ZPixmap); + if (ximage == (XImage *) NULL) + { + continue; + } + number_colors=0; + colors=(XColor *) NULL; + if (window_info[id].colormap != (Colormap) NULL) + { + ColormapInfo *p; + + number_colors=window_info[id].visual->map_entries; + for (p=colormap_info; p != (ColormapInfo *) NULL; p=p->next) + { + if (p->colormap == window_info[id].colormap) + { + break; + } + } + if (p == (ColormapInfo *) NULL) + { + colors=(XColor *) AcquireMemory(number_colors*sizeof(XColor)); + if (colors == (XColor *) NULL) + { + XDestroyImage(ximage); + return((Image *) NULL); + } + if ((window_info[id].visual->storage_class != DirectColor) && + (window_info[id].visual->storage_class != TrueColor)) + { + for (i=0; i < (int) number_colors; i++) + { + colors[i].pixel=i; + colors[i].pad=0; + } + } + else + { + unsigned long blue, blue_bit, green, green_bit, red, red_bit; + + red=0; + green=0; + blue=0; + red_bit=window_info[id].visual->red_mask & + (~(window_info[id].visual->red_mask)+1); + green_bit=window_info[id].visual->green_mask & + (~(window_info[id].visual->green_mask)+1); + blue_bit=window_info[id].visual->blue_mask & + (~(window_info[id].visual->blue_mask)+1); + for (i=0; i < (int) number_colors; i++) + { + colors[i].pixel=red | green | blue; + colors[i].pad=0; + red+=red_bit; + if (red > window_info[id].visual->red_mask) + red=0; + green+=green_bit; + if (green > window_info[id].visual->green_mask) + green=0; + blue+=blue_bit; + if (blue > window_info[id].visual->blue_mask) + blue=0; + } + } + (void) XQueryColors(display,window_info[id].colormap,colors, (int) number_colors); + p=(ColormapInfo *) AcquireMemory(sizeof(ColormapInfo)); + if (p == (ColormapInfo *) NULL) + return((Image *) NULL); + p->colormap=window_info[id].colormap; + p->colors=colors; + p->next=colormap_info; + colormap_info=p; + } + colors=p->colors; + } + composite_image=AllocateImage((ImageInfo *) NULL); + if (composite_image == (Image *) NULL) + { + XDestroyImage(ximage); + return((Image *) NULL); + } + if ((window_info[id].visual->storage_class != TrueColor) && + (window_info[id].visual->storage_class != DirectColor)) + { + composite_image->storage_class=PseudoClass; + } + composite_image->columns=ximage->width; + composite_image->rows=ximage->height; + switch (composite_image->storage_class) + { + case DirectClass: + default: + { + register unsigned long color, index; + unsigned long blue_mask, blue_shift, green_mask, green_shift, red_mask, red_shift; + + red_mask=window_info[id].visual->red_mask; + red_shift=0; + while ((red_mask & 0x01) == 0) + { + red_mask>>=1; + red_shift++; + } + green_mask=window_info[id].visual->green_mask; + green_shift=0; + while ((green_mask & 0x01) == 0) + { + green_mask>>=1; + green_shift++; + } + blue_mask=window_info[id].visual->blue_mask; + blue_shift=0; + while ((blue_mask & 0x01) == 0) + { + blue_mask>>=1; + blue_shift++; + } + if ((number_colors != 0) && + (window_info[id].visual->storage_class == DirectColor)) + { + for (y=0; y < (long) composite_image->rows; y++) + { + q=SetImagePixels(composite_image,0,y, + composite_image->columns,1); + if (q == (PixelPacket *) NULL) + break; + for (x=0; x < (long) composite_image->columns; x++) + { + pixel=XGetPixel(ximage,x,y); + index=(pixel >> red_shift) & red_mask; + q->red=ScaleShortToQuantum(colors[index].red); + index=(pixel >> green_shift) & green_mask; + q->green=ScaleShortToQuantum(colors[index].green); + index=(pixel >> blue_shift) & blue_mask; + q->blue=ScaleShortToQuantum(colors[index].blue); + q++; + } + if (!SyncImagePixels(composite_image)) + break; + } + } + else + { + for (y=0; y < (long) composite_image->rows; y++) + { + q=SetImagePixels(composite_image,0,y, + composite_image->columns,1); + if (q == (PixelPacket *) NULL) + break; + for (x=0; x < (long) composite_image->columns; x++) + { + pixel=XGetPixel(ximage,x,y); + color=(pixel >> red_shift) & red_mask; + q->red=ScaleShortToQuantum((65535L*color)/red_mask); + color=(pixel >> green_shift) & green_mask; + q->green=ScaleShortToQuantum((65535L*color)/green_mask); + color=(pixel >> blue_shift) & blue_mask; + q->blue=ScaleShortToQuantum((65535L*color)/blue_mask); + q++; + } + if (!SyncImagePixels(composite_image)) + { + break; + } + } + } + break; + } + case PseudoClass: + { + if (!AllocateImageColormap(composite_image,number_colors)) + { + XDestroyImage(ximage); + DestroyImage(composite_image); + return((Image *) NULL); + } + for (i=0; i < (int) composite_image->colors; i++) + { + composite_image->colormap[colors[i].pixel].red= + ScaleShortToQuantum(colors[i].red); + composite_image->colormap[colors[i].pixel].green= + ScaleShortToQuantum(colors[i].green); + composite_image->colormap[colors[i].pixel].blue= + ScaleShortToQuantum(colors[i].blue); + } + for (y=0; y < (long) composite_image->rows; y++) + { + q=SetImagePixels(composite_image,0,y,composite_image->columns,1); + if (q == (PixelPacket *) NULL) + break; + indexes=GetIndexes(composite_image); + for (x=0; x < (long) composite_image->columns; x++) + { + index=(IndexPacket) XGetPixel(ximage,x,y); + indexes[x]=index; + *q++=composite_image->colormap[index]; + } + if (!SyncImagePixels(composite_image)) + { + break; + } + } + break; + } + } + XDestroyImage(ximage); + if (image == (Image *) NULL) + { + image=composite_image; + continue; + } + (void) XTranslateCoordinates(display,window_info[id].window,window,0,0, &x_offset,&y_offset,&child); + x_offset-=(int) crop_info.x; + if (x_offset < 0) + { + x_offset=0; + } + y_offset-=(int) crop_info.y; + if (y_offset < 0) + { + y_offset=0; + } + (void) CompositeImage(image,CopyCompositeOp,composite_image, x_offset,y_offset); + } + while (colormap_info != (ColormapInfo *) NULL) + { + next=colormap_info->next; + LiberateMemory((void **) &colormap_info->colors); + LiberateMemory((void **) &colormap_info); + colormap_info=next; + } + /* + Free resources and restore initial state. + */ + LiberateMemory((void **) &window_info); + window_info=(WindowInfo *) NULL; + max_windows=0; + number_windows=0; + colormap_info=(ColormapInfo *) NULL; + return(image); + } + return((Image *) NULL); +} + +/*************************************************/ +/* this code is adapted from ImageMagick */ +/* mainly because i don't want user interactions */ +/* and i want to chose the screen also */ +/*************************************************/ + +static void pdp_capture_do_capture(t_pdp_capture *x) +{ + Colormap *colormaps; + Image *image; + int number_colormaps, number_windows, status, X, Y; + RectangleInfo crop_info; + Window *children, client, prior_target, root, target; + XTextProperty window_name; + Window child; + XWindowAttributes window_attributes; + + /* + Open X server connection. + */ + if (!x->x_displayopen) + { + post( "pdp_capture : display not open : no capture" ); + return; + } + (void) XSetErrorHandler(XError); + crop_info.x=x->x_x; + crop_info.y=x->x_y; + crop_info.width=x->x_vwidth; + crop_info.height=x->x_vheight; + root=XRootWindow(x->x_dpy, x->x_screen); + target=(Window) NULL; + prior_target=target; + + target = XMyGetSubwindow(x, x->x_dpy,root,x->x_x,x->x_y); + + client=target; /* obsolete */ + if (target != root) + { + unsigned int d; + status=XGetGeometry(x->x_dpy,target,&root,&X,&X,&d,&d,&d,&d); + if (status != 0) + { + for ( ; ; ) + { + Window parent; + + status=XQueryTree(x->x_dpy,target,&root,&parent,&children,&d); + if (status && (children != (Window *) NULL)) + { + (void) XFree((char *) children); + } + if (!status || (parent == (Window) NULL) || (parent == root)) break; + target=parent; + } + client=XMyClientWindow(x, x->x_dpy, target); + target=client; + if (prior_target) target=prior_target; + } + } + status=XGetWindowAttributes(x->x_dpy,target,&window_attributes); + if (status == 0) + { + post( "pdp_capture : unable to read window attributes" ); + (void) XCloseDisplay(x->x_dpy); + return; + } + (void) XTranslateCoordinates(x->x_dpy,target,root,0,0,&X,&Y,&child); + crop_info.x=x->x_x; + crop_info.y=x->x_y; + crop_info.width=x->x_vwidth; + crop_info.height=x->x_vheight; + target=root; + + number_windows=0; + status=XGetWMColormapWindows(x->x_dpy,target,&children,&number_windows); + if ((status == 1) && (number_windows > 0)) + { + (void) XFree ((char *) children); + } + colormaps=XListInstalledColormaps(x->x_dpy,target,&number_colormaps); + if (number_colormaps > 0) + { + (void) XFree((char *) colormaps); + } + + image=XMyGetWindowImage(x, x->x_dpy, target, 1); + if (image == (Image *) NULL) + { + post( "pdp_capture : unable to read xwindow image" ); + } + else + { + Image *clone_image; + + clone_image=CloneImage(image,0,0,1,&image->exception); + if (clone_image != (Image *) NULL) + { + x->x_Ximage=CropImage(clone_image,&crop_info,&image->exception); + if (x->x_Ximage != (Image *) NULL) + { + DestroyImage(image); + DestroyImage(clone_image); + image=x->x_Ximage; + } + } + status=XGetWMName(x->x_dpy,target,&window_name); + } + return; +} + +static void pdp_capture_display(t_pdp_capture *x, t_symbol *s) +{ + if ( x->x_displayopen ) + { + if ( XCloseDisplay(x->x_dpy) == -1 ) + { + post( "pdp_capture : could not close display" ); + } + } + strcpy( x->x_display, s->s_name ); + if ( ( x->x_dpy = XOpenDisplay( x->x_display ) ) != NULL ) + { + x->x_displayopen = 1; + + x->x_vwidth=XDisplayWidth(x->x_dpy, x->x_screen); + x->x_vheight=XDisplayHeight(x->x_dpy, x->x_screen); + x->x_vsize=x->x_vwidth*x->x_vheight; + + } +} + +static void pdp_capture_screen(t_pdp_capture *x, t_floatarg fscreen) +{ + if ( (int)fscreen > 0 ) + { + x->x_screen = (int) fscreen; + } +} + +static void pdp_capture_x(t_pdp_capture *x, t_floatarg fx) +{ + t_int width; + t_int err; + + if (!x->x_displayopen) + { + post( "pdp_capture : display not open : not setting x" ); + return; + } + width = XDisplayWidth( x->x_dpy, x->x_screen ); + if ( ( (int)fx > 0 ) && ( (int)fx <= width ) ) + { + x->x_x = (int) fx; + if ( x->x_x + x->x_vwidth > width ) + { + x->x_vwidth = width - x->x_x; + x->x_vsize = x->x_vwidth * x->x_vheight; + } + } + else + { + post( "pdp_capture : x position out of range : [0, %d]", width ); + } +} + +static void pdp_capture_y(t_pdp_capture *x, t_floatarg fy) +{ + t_int height; + t_int err; + + if (!x->x_displayopen) + { + post( "pdp_capture : display not open : not setting y" ); + return; + } + height = XDisplayHeight( x->x_dpy, x->x_screen ); + if ( ( (int)fy > 0 ) && ( (int)fy <= height ) ) + { + x->x_y = (int) fy; + if ( x->x_y + x->x_vheight > height ) + { + x->x_vheight = height - x->x_y; + x->x_vsize = x->x_vwidth * x->x_vheight; + } + } + else + { + post( "pdp_capture : y position out of range : [0, %d]", height ); + } +} + +static void pdp_capture_width(t_pdp_capture *x, t_floatarg fwidth) +{ + t_int width; + t_int err; + + if (!x->x_displayopen) + { + post( "pdp_capture : display not open : not setting width" ); + return; + } + width = XDisplayWidth( x->x_dpy, x->x_screen ); + if ( ( (int)fwidth > 0 ) && ( (int)fwidth <= (width-x->x_x) ) ) + { + x->x_vwidth = (int) fwidth; + x->x_vsize = x->x_vwidth * x->x_vheight; + } + else + { + post( "pdp_capture : width out of range : [0, %d]", width-x->x_x ); + } +} + +static void pdp_capture_height(t_pdp_capture *x, t_floatarg fheight) +{ + t_int height; + t_int err; + + if (!x->x_displayopen) + { + post( "pdp_capture : display not open : not setting height" ); + return; + } + height = XDisplayWidth( x->x_dpy, x->x_screen ); + if ( ( (int)fheight > 0 ) && ( (int)fheight <= (height-x->x_y) ) ) + { + x->x_vheight = (int) fheight; + x->x_vsize = x->x_vwidth * x->x_vheight; + } + else + { + post( "pdp_capture : width out of range : [0, %d]", height-x->x_y ); + } +} + +static void pdp_capture_sendpacket(t_pdp_capture *x) +{ + /* unregister and propagate if valid dest packet */ + if (x->x_packet0 != -1 ) + { + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); + } +} + +static void pdp_capture_bang(t_pdp_capture *x) +{ + PixelPacket pixel; + short int *pY, *pU, *pV; + unsigned char y, u, v; + t_int px, py, r, g, b; + long number_pixels; + + // capture the image and output a PDP packet + pdp_capture_do_capture( x ); + + x->x_vwidth = x->x_Ximage->columns; + x->x_vheight = x->x_Ximage->rows; + x->x_vsize = x->x_vwidth*x->x_vheight; + + x->x_packet0 = pdp_packet_new_image_YCrCb( x->x_vwidth, x->x_vheight ); + x->x_data = (short int *)pdp_packet_data(x->x_packet0); + x->x_header = pdp_packet_header(x->x_packet0); + + x->x_header->info.image.encoding = PDP_IMAGE_YV12; + x->x_header->info.image.width = x->x_vwidth; + x->x_header->info.image.height = x->x_vheight; + + number_pixels=(long) GetPixelCacheArea(x->x_Ximage); + + // post( "pdp_capture : capture done : w=%d h=%d pixels=%ld", x->x_vwidth, x->x_vheight, number_pixels ); + + pY = x->x_data; + pV = x->x_data+x->x_vsize; + pU = x->x_data+x->x_vsize+(x->x_vsize>>2); + for ( py=0; py<x->x_vheight; py++ ) + { + for ( px=0; px<x->x_vwidth; px++ ) + { + pixel = GetOnePixel(x->x_Ximage, px, py); + // scale values + r = (pixel.red*255)/65535; + g = (pixel.green*255)/65535; + b = (pixel.blue*255)/65535; + // post( "pdp_capture : pixel : [%d, %d] : %d,%d,%d", px, py, r, g, b ); + y = yuv_RGBtoY(((r)<<16)+((g)<<8)+(b)); + u = yuv_RGBtoU(((r)<<16)+((g)<<8)+(b)); + v = yuv_RGBtoV(((r)<<16)+((g)<<8)+(b)); + + *(pY) = y<<7; + if ( (px%2==0) && (py%2==0) ) + { + *(pV) = (v-128)<<8; + *(pU) = (u-128)<<8; + } + pY++; + if ( (px%2==0) && (py%2==0) ) + { + pV++;pU++; + } + } + } + + // output the new packet + pdp_capture_sendpacket( x ); + + DestroyImage( x->x_Ximage ); +} + +static void pdp_capture_free(t_pdp_capture *x) +{ + int i; + + if ( x->x_packet0 != -1 ) + { + pdp_packet_mark_unused(x->x_packet0); + } + if ( x->x_displayopen ) + { + if ( XCloseDisplay(x->x_dpy) == -1 ) + { + post( "pdp_capture : could not close display" ); + } + } +} + +t_class *pdp_capture_class; + +void *pdp_capture_new(void) +{ + int i; + + t_pdp_capture *x = (t_pdp_capture *)pd_new(pdp_capture_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("x")); + 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("width")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("height")); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_packet0 = -1; + + x->x_display = (char *) malloc( PDP_DISPLAY_LENGTH ); + strcpy( x->x_display, ":0.0" ); + + x->x_displayopen = 0; + if ( ( x->x_dpy = XOpenDisplay( x->x_display ) ) != NULL ) + { + x->x_displayopen = 1; + + x->x_vwidth=XDisplayWidth(x->x_dpy, x->x_screen); + x->x_vheight=XDisplayWidth(x->x_dpy, x->x_screen); + x->x_vsize=x->x_vwidth*x->x_vheight; + + } + + x->x_screen = 0; + x->x_x = 0; + x->x_y = 0; + x->x_vwidth = 320; + x->x_vheight = 240; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_capture_setup(void) +{ + post( pdp_capture_version ); + pdp_capture_class = class_new(gensym("pdp_capture"), (t_newmethod)pdp_capture_new, + (t_method)pdp_capture_free, sizeof(t_pdp_capture), 0, A_NULL); + + class_addmethod(pdp_capture_class, (t_method)pdp_capture_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_capture_class, (t_method)pdp_capture_display, gensym("display"), A_SYMBOL, A_NULL); + class_addmethod(pdp_capture_class, (t_method)pdp_capture_screen, gensym("screen"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_capture_class, (t_method)pdp_capture_x, gensym("x"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_capture_class, (t_method)pdp_capture_y, gensym("y"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_capture_class, (t_method)pdp_capture_width, gensym("width"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_capture_class, (t_method)pdp_capture_height, gensym("height"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_cmap.c b/modules/pdp_cmap.c new file mode 100644 index 0000000..3d8524f --- /dev/null +++ b/modules/pdp_cmap.c @@ -0,0 +1,539 @@ +/* + * 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 color mapper that lets you change the colors within the image + */ + +#include "pdp.h" +#include "yuv.h" +#include <math.h> +#include <stdio.h> + +struct _rtext +{ + char *x_buf; + int x_bufsize; + int x_selstart; + int x_selend; + int x_active; + int x_dragfrom; + int x_height; + int x_drawnwidth; + int x_drawnheight; + t_text *x_text; + t_glist *x_glist; + char x_tag[50]; + struct _rtext *x_next; +}; + +#define t_rtext struct _rtext + +extern int rtext_width(t_rtext *x); +extern int rtext_height(t_rtext *x); +extern t_rtext *glist_findrtext(t_glist *gl, t_text *who); + +typedef struct _color +{ + t_int on; + t_int y,u,v; + t_int oy,ou,ov; + t_int tolerance; +} t_color; + +#define COLORHEIGHT 5 +#define DEFAULT_CAPACITY 10 + +static char *pdp_cmap_version = "pdp_cmap: a color mapper version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_cmap_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_dropped; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_int x_capacity; // number of mapped colors + t_int x_current; // current color + t_color *x_colors; // color substitution table + + t_int x_cursor; // show cursor or not + t_int x_luminosity; // use luminosity or not + + t_int x_colorR; // setable r + t_int x_colorG; // setable g + t_int x_colorB; // setable b + + t_int x_cursX; // X coordinate of cursor + t_int x_cursY; // Y coordinate of cursor + short int *x_frame; // keep a copy of current frame for picking color + + t_outlet *x_pdp_output; // output packets + + t_canvas *x_canvas; + +} t_pdp_cmap; + +static void pdp_cmap_draw_color(t_pdp_cmap *x, t_int r, t_int g, t_int b) +{ + t_int width, height; + char color[32]; + + sprintf( color, "#%.2X%.2X%.2X", r, g, b ); + width = rtext_width( glist_findrtext( (t_glist*)x->x_canvas, (t_text *)x ) ); + height = rtext_height( glist_findrtext( (t_glist*)x->x_canvas, (t_text *)x ) ); + sys_vgui(".x%x.c delete rectangle %xCOLOR\n", x->x_canvas, x ); + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill %s -tags %xCOLOR\n", + x->x_canvas, x->x_obj.te_xpix+width+5, x->x_obj.te_ypix, + x->x_obj.te_xpix+width+height+5, + x->x_obj.te_ypix+height, color, x ); +} + +static void pdp_cmap_r(t_pdp_cmap *x, t_floatarg fr ) +{ + if ( ( fr >= 0 ) && ( fr < 255 ) ) + { + x->x_colorR = (int)fr; + x->x_colors[x->x_current].y = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colors[x->x_current].u = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colors[x->x_current].v = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + pdp_cmap_draw_color( x, x->x_colorR, x->x_colorG, x->x_colorB ); + } +} + +static void pdp_cmap_g(t_pdp_cmap *x, t_floatarg fg ) +{ + if ( ( fg >= 0 ) && ( fg < 255 ) ) + { + x->x_colorG = (int)fg; + x->x_colors[x->x_current].y = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colors[x->x_current].u = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colors[x->x_current].v = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + pdp_cmap_draw_color( x, x->x_colorR, x->x_colorG, x->x_colorB ); + } +} + +static void pdp_cmap_b(t_pdp_cmap *x, t_floatarg fb ) +{ + if ( ( fb >= 0 ) && ( fb < 255 ) ) + { + x->x_colorB = (int)fb; + x->x_colors[x->x_current].y = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colors[x->x_current].u = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colors[x->x_current].v = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + pdp_cmap_draw_color( x, x->x_colorR, x->x_colorG, x->x_colorB ); + } +} + +static void pdp_cmap_cursx(t_pdp_cmap *x, t_floatarg fx ) +{ + if ( ( fx >= 0 ) && ( fx < x->x_vwidth) ) + { + x->x_cursX = (int)fx; + } +} + +static void pdp_cmap_cursy(t_pdp_cmap *x, t_floatarg fy ) +{ + if ( ( fy >= 0 ) && ( fy < x->x_vheight) ) + { + x->x_cursY = (int)fy; + } +} + +static void pdp_cmap_tolerance(t_pdp_cmap *x, t_floatarg ftolerance ) +{ + if ( ftolerance >= 0 ) + { + x->x_colors[x->x_current].tolerance = (int)ftolerance; + } +} + +static void pdp_cmap_luminosity(t_pdp_cmap *x, t_floatarg fluminosity ) +{ + if ( ( fluminosity == 0 ) || ( fluminosity == 1 ) ) + { + x->x_luminosity = (int)fluminosity; + } +} + +static void pdp_cmap_delete(t_pdp_cmap *x, t_floatarg fcolor ) +{ + if ( ( fcolor >= 0 ) && ( fcolor < x->x_capacity ) ) + { + x->x_colors[(int)fcolor].on = 0; + } +} + +static void pdp_cmap_clear(t_pdp_cmap *x) +{ + t_int ci; + + for ( ci=0; ci<x->x_capacity; ci++) + { + x->x_colors[ci].on = 0; + } + x->x_current = 0; +} + +static void pdp_cmap_resize(t_pdp_cmap *x, t_floatarg fnewsize ) +{ + t_color *colors; + t_int ci, csize; + + if ( (int) fnewsize<=0 ) return; + + // allocate new structures + colors = (t_color*) getbytes( fnewsize*sizeof(t_color) ); + + for ( ci=0; ci<fnewsize; ci++ ) + { + colors[ci].on = 0; + colors[ci].tolerance = 10; + } + + if ( fnewsize < x->x_capacity ) + { + post( "pdp_form : new size is too small : texts lost !!" ); + csize = fnewsize; + } + else + { + csize = x->x_capacity; + } + + // copy all values + for ( ci=0; ci<csize; ci++ ) + { + memcpy( &colors[ci], &x->x_colors[ci], sizeof( t_color ) ); + } + + // free old structures + if ( x->x_colors ) freebytes( x->x_colors, x->x_capacity*sizeof(t_color) ); + + // set new structures + x->x_colors = colors; + x->x_capacity = fnewsize; + x->x_current = 0; +} + +static void pdp_cmap_setcur(t_pdp_cmap *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_cmap_current(t_pdp_cmap *x, t_floatarg fcurrent ) +{ + if ( ( fcurrent >= 0 ) && ( fcurrent < x->x_capacity ) ) + { + x->x_current = (int)fcurrent; + post( "pdp_cmap : color index set to : %d", x->x_current ); + } +} + +static void pdp_cmap_cursor(t_pdp_cmap *x, t_floatarg fcursor ) +{ + if ( ( fcursor == 0 ) || ( fcursor == 1 ) ) + { + x->x_cursor = (int)fcursor; + } +} + +static void pdp_cmap_pick(t_pdp_cmap *x) +{ + t_int y,u,v; + + if ( x->x_frame && ( x->x_cursX > 0 ) && ( x->x_cursX < x->x_vwidth ) + && ( x->x_cursY > 0 ) && ( x->x_cursY < x->x_vheight ) ) + { + x->x_colors[x->x_current].oy = x->x_frame[ x->x_cursY*x->x_vwidth+x->x_cursX ]; + x->x_colors[x->x_current].ov = x->x_frame[ x->x_vsize+((x->x_cursY>>1)*(x->x_vwidth>>1)+(x->x_cursX>>1)) ]; + x->x_colors[x->x_current].ou = x->x_frame[ x->x_vsize+(x->x_vsize>>2)+((x->x_cursY>>1)*(x->x_vwidth>>1)+(x->x_cursX>>1)) ]; + y = (x->x_colors[x->x_current].oy)>>7; + v = (x->x_colors[x->x_current].ov>>8)+128; + u = (x->x_colors[x->x_current].ou>>8)+128; + x->x_colorR = yuv_YUVtoR( y, u, v ); + x->x_colorG = yuv_YUVtoG( y, u, v ); + x->x_colorB = yuv_YUVtoB( y, u, v ); + pdp_cmap_draw_color( x, x->x_colorR, x->x_colorG, x->x_colorB ); + x->x_colors[x->x_current].y = 255; + x->x_colors[x->x_current].u = 255; + x->x_colors[x->x_current].v = 255; + x->x_colors[x->x_current].on = 1; + } +} + +static void pdp_cmap_allocate(t_pdp_cmap *x) +{ + x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + + if ( !x->x_frame ) + { + post( "pdp_mgrid : severe error : cannot allocate buffer !!! "); + return; + } +} + +static void pdp_cmap_free_ressources(t_pdp_cmap *x) +{ + if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); +} + +static void pdp_cmap_process_yv12(t_pdp_cmap *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_int i, ci; + t_int px=0, py=0, ppx=0, ppy=0; + t_int y=0, u=0, v=0; + short int *pfY, *pfU, *pfV; + short int *poY, *poU, *poV; + t_int diff; + + /* allocate all ressources */ + if ( ( (int)header->info.image.width != x->x_vwidth ) || + ( (int)header->info.image.height != x->x_vheight ) ) + { + pdp_cmap_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_cmap_allocate( x ); + post( "pdp_cmap : reallocated buffers" ); + } + + memcpy(x->x_frame, data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + + // map colors + for ( ci=0; ci<x->x_capacity; ci++ ) + { + if ( x->x_colors[ci].on ) + { + pfY = data; + pfV = data+x->x_vsize; + pfU = data+x->x_vsize+(x->x_vsize>>2); + poY = x->x_frame; + poV = x->x_frame+x->x_vsize; + poU = x->x_frame+x->x_vsize+(x->x_vsize>>2); + for ( py=0; py<x->x_vheight; py++ ) + { + for ( px=0; px<x->x_vwidth; px++ ) + { + y = *poY; + v = *poV; + u = *poU; + + if ( x->x_luminosity ) + { + diff = (abs(y-x->x_colors[ci].oy)>>7)+(abs(u-x->x_colors[ci].ou)>>8)+(abs(v-x->x_colors[ci].ov)>>8); + } + else + { + diff = (abs(u-x->x_colors[ci].ou)>>8)+(abs(v-x->x_colors[ci].ov)>>8); + } + + if ( diff <= x->x_colors[ci].tolerance ) + { + // change color not luminosity + // *pfY = x->x_colors[ci].y; + *pfV = x->x_colors[ci].v; + *pfU = x->x_colors[ci].u; + } + + pfY++;poY++; + if ( (px%2==0) && (py%2==0) ) + { + pfU++;pfV++; + poU++;poV++; + } + } + } + } + } + + // draw cursor + if ( ( x->x_cursX > 0 ) && ( x->x_cursY > 0 ) && ( x->x_cursor ) ) + { + for ( px=(x->x_cursX-5); px<=(x->x_cursX+5); px++ ) + { + if ( ( px > 0 ) && ( px < x->x_vwidth ) ) + { + if ( ((*(data+x->x_cursY*x->x_vwidth+px))>>7) < 128 ) + { + *(data+x->x_cursY*x->x_vwidth+px) = 0xff<<7; + } + else + { + *(data+x->x_cursY*x->x_vwidth+px) = 0x00<<7; + } + } + } + for ( py=(x->x_cursY-5); py<=(x->x_cursY+5); py++ ) + { + if ( ( py > 0 ) && ( py < x->x_vheight ) ) + { + if ( ((*(data+py*x->x_vwidth+x->x_cursX))>>7) < 128 ) + { + *(data+py*x->x_vwidth+x->x_cursX) = 0xff<<7; + } + else + { + *(data+py*x->x_vwidth+x->x_cursX) = 0x00<<7; + } + } + } + } + + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet0); + + return; +} + +static void pdp_cmap_process(t_pdp_cmap *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_cmap_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding){ + + case PDP_IMAGE_YV12: + pdp_cmap_process_yv12(x); + 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_cmap_process */ + break; + + } + } + +} + +static void pdp_cmap_input_0(t_pdp_cmap *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_cmap_process(x); + + } +} + +static void pdp_cmap_free(t_pdp_cmap *x) +{ + int i; + + pdp_packet_mark_unused(x->x_packet0); + pdp_cmap_free_ressources( x ); +} + +t_class *pdp_cmap_class; + +void *pdp_cmap_new(void) +{ + t_int ci; + + t_pdp_cmap *x = (t_pdp_cmap *)pd_new(pdp_cmap_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("current")); + 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("R")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("G")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("B")); + 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_packet0 = -1; + + x->x_cursX = -1; + x->x_cursY = -1; + x->x_cursor = 1; + x->x_luminosity = 1; + + x->x_capacity = DEFAULT_CAPACITY; + x->x_current = 0; + x->x_colors = (t_color *) getbytes( x->x_capacity*sizeof(t_color) ); + for ( ci=0; ci<x->x_capacity; ci++) + { + x->x_colors[ci].on = 0; + x->x_colors[ci].tolerance = 10; + } + + x->x_canvas = canvas_getcurrent(); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_cmap_setup(void) +{ + post( pdp_cmap_version ); + pdp_cmap_class = class_new(gensym("pdp_cmap"), (t_newmethod)pdp_cmap_new, + (t_method)pdp_cmap_free, sizeof(t_pdp_cmap), 0, A_NULL); + + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_r, gensym("R"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_g, gensym("G"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_b, gensym("B"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_cursx, gensym("cursx"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_cursy, gensym("cursy"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_pick, gensym("pick"), A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_tolerance, gensym("tolerance"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_cursor, gensym("cursor"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_luminosity, gensym("luminosity"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_current, gensym("current"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_clear, gensym("clear"), A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_delete, gensym("delete"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_resize, gensym("resize"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_setcur, gensym("setcur"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_compose.c b/modules/pdp_compose.c new file mode 100644 index 0000000..ed2a5b3 --- /dev/null +++ b/modules/pdp_compose.c @@ -0,0 +1,459 @@ +/* + * 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 compositor mixing two sources + * idea expressed by liz + */ + +#include "pdp.h" +#include "yuv.h" +#include <math.h> +#include <stdio.h> + +struct _rtext +{ + char *x_buf; + t_int x_bufsize; + t_int x_selstart; + t_int x_selend; + t_int x_active; + t_int x_dragfrom; + t_int x_height; + t_int x_drawnwidth; + t_int x_drawnheight; + t_text *x_text; + t_glist *x_glist; + char x_tag[50]; + struct _rtext *x_next; +}; + +#define t_rtext struct _rtext + +extern int rtext_width(t_rtext *x); +extern int rtext_height(t_rtext *x); +extern t_rtext *glist_findrtext(t_glist *gl, t_text *who); + +#define COLORHEIGHT 5 + +static char *pdp_compose_version = "pdp_compose: a video compositor version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_compose_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_dropped; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + t_int x_colorR; // RGB components of mixing color + t_int x_colorG; + t_int x_colorB; + t_int x_colorY; // YUV components of mixing color + t_int x_colorU; + t_int x_colorV; + t_int x_tolerance; // tolerance + t_int x_cursX; // X coordinate of cursor + t_int x_cursY; // Y coordinate of cursor + t_int x_cursor; // cursor drawing flag + t_int x_luminosity; // flag to indicate if luminosity is used + short int *x_frame; // keep a copy of current frame for picking color + short int *x_right_frame; // 2nd video source + + t_outlet *x_pdp_output; // output packets + + t_canvas *x_canvas; + +} t_pdp_compose; + +static void pdp_compose_draw_color(t_pdp_compose *x) +{ + t_int width, height; + char color[32]; + + sprintf( color, "#%.2X%.2X%.2X", x->x_colorR, x->x_colorG, x->x_colorB ); + width = rtext_width( glist_findrtext( (t_glist*)x->x_canvas, (t_text *)x ) ); + height = rtext_height( glist_findrtext( (t_glist*)x->x_canvas, (t_text *)x ) ); + sys_vgui(".x%x.c delete rectangle %xCOLOR\n", x->x_canvas, x ); + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill %s -tags %xCOLOR\n", + x->x_canvas, x->x_obj.te_xpix+width+5, x->x_obj.te_ypix, + x->x_obj.te_xpix+width+height+5, + x->x_obj.te_ypix+height, color, x ); +} + +static void pdp_compose_setcur(t_pdp_compose *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_compose_r(t_pdp_compose *x, t_floatarg fr ) +{ + if ( ( fr >= 0 ) && ( fr < 255 ) ) + { + x->x_colorR = (int)fr; + x->x_colorY = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colorU = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colorV = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + pdp_compose_draw_color( x ); + } +} + +static void pdp_compose_g(t_pdp_compose *x, t_floatarg fg ) +{ + if ( ( fg >= 0 ) && ( fg < 255 ) ) + { + x->x_colorG = (int)fg; + x->x_colorY = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colorU = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colorV = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + pdp_compose_draw_color( x ); + } +} + +static void pdp_compose_b(t_pdp_compose *x, t_floatarg fb ) +{ + if ( ( fb >= 0 ) && ( fb < 255 ) ) + { + x->x_colorB = (int)fb; + x->x_colorY = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colorU = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colorV = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + pdp_compose_draw_color( x ); + } +} + +static void pdp_compose_cursor(t_pdp_compose *x, t_floatarg fcursor ) +{ + if ( ( (int)fcursor == 0 ) || ( (int)fcursor == 1 ) ) + { + x->x_cursor = (int)fcursor; + } +} + +static void pdp_compose_tolerance(t_pdp_compose *x, t_floatarg ftolerance ) +{ + if ( ftolerance >= 0 ) + { + x->x_tolerance = (int)ftolerance; + } +} + +static void pdp_compose_cursx(t_pdp_compose *x, t_floatarg fx ) +{ + if ( ( fx >= 0 ) && ( fx < x->x_vwidth) ) + { + x->x_cursX = (int)fx; + } +} + +static void pdp_compose_cursy(t_pdp_compose *x, t_floatarg fy ) +{ + if ( ( fy >= 0 ) && ( fy < x->x_vheight) ) + { + x->x_cursY = (int)fy; + } +} + +static void pdp_compose_luminosity(t_pdp_compose *x, t_floatarg fluminosity ) +{ + if ( ( fluminosity == 0 ) || ( fluminosity == 1 ) ) + { + x->x_luminosity = (int)fluminosity; + } +} + +static void pdp_compose_pick(t_pdp_compose *x) +{ + t_int y,u,v; + + if ( x->x_frame && ( x->x_cursX > 0 ) && ( x->x_cursX < x->x_vwidth ) + && ( x->x_cursY > 0 ) && ( x->x_cursY < x->x_vheight ) ) + { + 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) ]; + y = (x->x_colorY)>>7; + u = (x->x_colorU>>8)+128; + v = (x->x_colorV>>8)+128; + x->x_colorR = yuv_YUVtoR( y, u, v ); + x->x_colorG = yuv_YUVtoG( y, u, v ); + x->x_colorB = yuv_YUVtoB( y, u, v ); + pdp_compose_draw_color( x ); + } +} + +static void pdp_compose_allocate(t_pdp_compose *x) +{ + x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + x->x_right_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + + if ( !x->x_frame || !x->x_right_frame ) + { + post( "pdp_mgrid : severe error : cannot allocate buffer !!! "); + return; + } +} + +static void pdp_compose_free_ressources(t_pdp_compose *x) +{ + if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + if ( x->x_right_frame ) freebytes ( x->x_right_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); +} + +static void pdp_compose_process_yv12(t_pdp_compose *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_int i, cf; + t_int px=0, py=0, ppx=0, ppy=0, found=0, xcell=0, ycell=0; + t_int celldiff=0, cellwidth=0, cellheight=0; + t_int y=0, u=0, v=0; + t_int sum; + short int *pfY, *pfV, *pfU, *prY, *prV, *prU, *pdY, *pdV, *pdU; + + /* allocate all ressources */ + if ( ( (int)header->info.image.width != x->x_vwidth ) || + ( (int)header->info.image.height != x->x_vheight ) ) + { + pdp_compose_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_compose_allocate( x ); + post( "pdp_compose : reallocated buffers" ); + } + + memcpy(x->x_frame, data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + + // draw cursor + if ( ( x->x_cursor ) && ( x->x_cursX > 0 ) && ( x->x_cursY > 0 ) ) + { + for ( px=(x->x_cursX-5); px<=(x->x_cursX+5); px++ ) + { + if ( ( px > 0 ) && ( px < x->x_vwidth ) ) + { + if ( ((*(data+x->x_cursY*x->x_vwidth+px))>>7) < 128 ) + { + *(data+x->x_cursY*x->x_vwidth+px) = 0xff<<7; + } + else + { + *(data+x->x_cursY*x->x_vwidth+px) = 0x00<<7; + } + } + } + for ( py=(x->x_cursY-5); py<=(x->x_cursY+5); py++ ) + { + if ( ( py > 0 ) && ( py < x->x_vheight ) ) + { + if ( ((*(data+py*x->x_vwidth+x->x_cursX))>>7) < 128 ) + { + *(data+py*x->x_vwidth+x->x_cursX) = 0xff<<7; + } + else + { + *(data+py*x->x_vwidth+x->x_cursX) = 0x00<<7; + } + } + } + } + + pfY = x->x_frame; + pfV = x->x_frame+x->x_vsize; + pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2); + pdY = data; + pdV = data+x->x_vsize; + pdU = data+x->x_vsize+(x->x_vsize>>2); + prY = x->x_right_frame; + prV = x->x_right_frame+x->x_vsize; + prU = x->x_right_frame+x->x_vsize+(x->x_vsize>>2); + // track color + if ( x->x_colorR != -1 ) + { + for ( py=0; py<x->x_vheight; py++ ) + { + for ( px=0; px<x->x_vwidth; px++ ) + { + y = *pfY; + v = *pfV; + u = *pfU; + if ( x->x_luminosity ) + { + sum = (abs(y-x->x_colorY)>>7); + } + else + { + sum = (abs(y-x->x_colorY)>>7)+(abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + if ( sum <= x->x_tolerance ) + { + *pdY = *prY; + *pdV = *prV; + *pdU = *prU; + } + prY++;pdY++;pfY++; + if ( (px%2==0) && (py%2==0) ) + { + prU++; prV++; + pfU++; pfV++; + pdU++; pdV++; + } + } + } + } + + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet0); + + return; +} + +static void pdp_compose_process(t_pdp_compose *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_compose_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding){ + + case PDP_IMAGE_YV12: + pdp_compose_process_yv12(x); + 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_compose_process */ + break; + + } + } + +} + +static void pdp_compose_input_1(t_pdp_compose *x, t_symbol *s, t_floatarg f) +{ + short int *rightdata = (short int *)pdp_packet_data((int)f); + + if (s== gensym("register_rw")) memcpy(x->x_right_frame, rightdata, (x->x_vsize + (x->x_vsize>>1))<<1 ); +} + +static void pdp_compose_input_0(t_pdp_compose *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_compose_process(x); + + } +} + +static void pdp_compose_free(t_pdp_compose *x) +{ + int i; + + pdp_packet_mark_unused(x->x_packet0); + pdp_compose_free_ressources( x ); +} + +t_class *pdp_compose_class; + +void *pdp_compose_new(void) +{ + int i; + + t_pdp_compose *x = (t_pdp_compose *)pd_new(pdp_compose_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("pdp"), gensym("pdp1")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("R")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("G")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("B")); + 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_colorR = -1; + x->x_colorG = -1; + x->x_colorB = -1; + + x->x_colorY = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colorU = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colorV = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + + x->x_packet0 = -1; + + x->x_cursX = -1; + x->x_cursY = -1; + x->x_tolerance = 20; + x->x_luminosity = 1; + x->x_cursor = 1; + + x->x_canvas = canvas_getcurrent(); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_compose_setup(void) +{ + post( pdp_compose_version ); + pdp_compose_class = class_new(gensym("pdp_compose"), (t_newmethod)pdp_compose_new, + (t_method)pdp_compose_free, sizeof(t_pdp_compose), 0, A_NULL); + + class_addmethod(pdp_compose_class, (t_method)pdp_compose_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_input_1, gensym("pdp1"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_r, gensym("R"), A_FLOAT, A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_g, gensym("G"), A_FLOAT, A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_b, gensym("B"), A_FLOAT, A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_cursx, gensym("cursx"), A_FLOAT, A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_cursy, gensym("cursy"), A_FLOAT, A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_pick, gensym("pick"), A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_cursor, gensym("cursor"), A_FLOAT, A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_tolerance, gensym("tolerance"), A_FLOAT, A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_luminosity, gensym("luminosity"), A_FLOAT, A_NULL); + class_addmethod(pdp_compose_class, (t_method)pdp_compose_setcur, gensym("setcur"), A_FLOAT, A_FLOAT, A_NULL); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_ctrack.c b/modules/pdp_ctrack.c new file mode 100644 index 0000000..45e1ccb --- /dev/null +++ b/modules/pdp_ctrack.c @@ -0,0 +1,676 @@ +/* + * 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 color tracker that lets you follow the movement of objects + */ + +#include "pdp.h" +#include "yuv.h" +#include <math.h> +#include <stdio.h> + +struct _rtext +{ + char *x_buf; + int x_bufsize; + int x_selstart; + int x_selend; + int x_active; + int x_dragfrom; + int x_height; + int x_drawnwidth; + int x_drawnheight; + t_text *x_text; + t_glist *x_glist; + char x_tag[50]; + struct _rtext *x_next; +}; + +#define t_rtext struct _rtext + +extern int rtext_width(t_rtext *x); +extern int rtext_height(t_rtext *x); +extern t_rtext *glist_findrtext(t_glist *gl, t_text *who); + +#define COLORHEIGHT 5 + +static char *pdp_ctrack_version = "pdp_ctrack: a color tracker version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_ctrack_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_dropped; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + t_int x_colorR; // RGB components of tracked color + t_int x_colorG; + t_int x_colorB; + t_int x_colorY; // YUV components of tracked color + t_int x_colorU; + t_int x_colorV; + t_int x_tolerance; // tolerance + t_int x_luminosity; // use luminosity or not + t_int x_steady; // steady mode : the zone is searched around the cursor + t_int x_cursor; // show cursor or not + t_int x_showframe; // show frame or not + t_int x_cursX; // X coordinate of cursor + t_int x_cursY; // Y coordinate of cursor + short int *x_frame; // keep a copy of current frame for picking color + + t_outlet *x_pdp_output; // output packets + t_outlet *x_x1; // output x1 coordinate of block which has been detected + t_outlet *x_y1; // output y1 coordinate of block which has been detected + t_outlet *x_x2; // output x2 coordinate of block which has been detected + t_outlet *x_y2; // output y2 coordinate of block which has been detected + t_outlet *x_R; // output R component of selected color + t_outlet *x_G; // output R component of selected color + t_outlet *x_B; // output R component of selected color + + t_canvas *x_canvas; + +} t_pdp_ctrack; + +static void pdp_ctrack_draw_color(t_pdp_ctrack *x) +{ + t_int width, height; + char color[32]; + + sprintf( color, "#%.2X%.2X%.2X", x->x_colorR, x->x_colorG, x->x_colorB ); + width = rtext_width( glist_findrtext( (t_glist*)x->x_canvas, (t_text *)x ) ); + height = rtext_height( glist_findrtext( (t_glist*)x->x_canvas, (t_text *)x ) ); + sys_vgui(".x%x.c delete rectangle %xCOLOR\n", x->x_canvas, x ); + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill %s -tags %xCOLOR\n", + x->x_canvas, x->x_obj.te_xpix+width+5, x->x_obj.te_ypix, + x->x_obj.te_xpix+width+height+5, + x->x_obj.te_ypix+height, color, x ); +} + +static void pdp_ctrack_setcur(t_pdp_ctrack *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_ctrack_r(t_pdp_ctrack *x, t_floatarg fr ) +{ + if ( ( fr >= 0 ) && ( fr < 255 ) ) + { + x->x_colorR = (int)fr; + x->x_colorY = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colorU = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colorV = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + pdp_ctrack_draw_color( x ); + outlet_float( x->x_R, x->x_colorR ); + } +} + +static void pdp_ctrack_g(t_pdp_ctrack *x, t_floatarg fg ) +{ + if ( ( fg >= 0 ) && ( fg < 255 ) ) + { + x->x_colorG = (int)fg; + x->x_colorY = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colorU = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colorV = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + pdp_ctrack_draw_color( x ); + outlet_float( x->x_G, x->x_colorG ); + } +} + +static void pdp_ctrack_b(t_pdp_ctrack *x, t_floatarg fb ) +{ + if ( ( fb >= 0 ) && ( fb < 255 ) ) + { + x->x_colorB = (int)fb; + x->x_colorY = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colorU = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colorV = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + pdp_ctrack_draw_color( x ); + outlet_float( x->x_B, x->x_colorB ); + } +} + +static void pdp_ctrack_cursx(t_pdp_ctrack *x, t_floatarg fx ) +{ + if ( ( fx >= 0 ) && ( fx < x->x_vwidth) ) + { + x->x_cursX = (int)fx; + } +} + +static void pdp_ctrack_tolerance(t_pdp_ctrack *x, t_floatarg ftolerance ) +{ + if ( ftolerance >= 0 ) + { + x->x_tolerance = (int)ftolerance; + } +} + +static void pdp_ctrack_cursy(t_pdp_ctrack *x, t_floatarg fy ) +{ + if ( ( fy >= 0 ) && ( fy < x->x_vheight) ) + { + x->x_cursY = (int)fy; + } +} + +static void pdp_ctrack_luminosity(t_pdp_ctrack *x, t_floatarg fluminosity ) +{ + if ( ( fluminosity == 0 ) || ( fluminosity == 1 ) ) + { + x->x_luminosity = (int)fluminosity; + } +} + +static void pdp_ctrack_cursor(t_pdp_ctrack *x, t_floatarg fcursor ) +{ + if ( ( fcursor == 0 ) || ( fcursor == 1 ) ) + { + x->x_cursor = (int)fcursor; + } +} + +static void pdp_ctrack_frame(t_pdp_ctrack *x, t_floatarg fframe ) +{ + if ( ( fframe == 0 ) || ( fframe == 1 ) ) + { + x->x_showframe = (int)fframe; + } +} + +static void pdp_ctrack_steady(t_pdp_ctrack *x, t_floatarg fsteady ) +{ + if ( ( fsteady == 0 ) || ( fsteady == 1 ) ) + { + x->x_steady = (int)fsteady; + } +} + +static void pdp_ctrack_pick(t_pdp_ctrack *x) +{ + t_int y,u,v; + + if ( x->x_frame && ( x->x_cursX > 0 ) && ( x->x_cursX < x->x_vwidth ) + && ( x->x_cursY > 0 ) && ( x->x_cursY < x->x_vheight ) ) + { + 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) ]; + y = (x->x_colorY)>>7; + u = (x->x_colorU>>8)+128; + v = (x->x_colorV>>8)+128; + x->x_colorR = yuv_YUVtoR( y, u, v ); + outlet_float( x->x_R, x->x_colorR ); + x->x_colorG = yuv_YUVtoG( y, u, v ); + outlet_float( x->x_G, x->x_colorG ); + x->x_colorB = yuv_YUVtoB( y, u, v ); + outlet_float( x->x_B, x->x_colorB ); + pdp_ctrack_draw_color( x ); + } +} + +static void pdp_ctrack_allocate(t_pdp_ctrack *x) +{ + x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + + if ( !x->x_frame ) + { + post( "pdp_mgrid : severe error : cannot allocate buffer !!! "); + return; + } +} + +static void pdp_ctrack_free_ressources(t_pdp_ctrack *x) +{ + if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); +} + +static void pdp_ctrack_process_yv12(t_pdp_ctrack *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_int i, cf; + t_int px=0, py=0, ppx=0, ppy=0, found=0, xcell=0, ycell=0; + t_int y=0, u=0, v=0; + t_int x1=0, y1=0, x2=0, y2=0; + t_int X1=0, Y1=0, X2=0, Y2=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_ctrack_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_ctrack_allocate( x ); + post( "pdp_ctrack : reallocated buffers" ); + } + + memcpy(x->x_frame, data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + + // draw cursor + if ( ( x->x_cursX > 0 ) && ( x->x_cursY > 0 ) && ( x->x_cursor ) ) + { + for ( px=(x->x_cursX-5); px<=(x->x_cursX+5); px++ ) + { + if ( ( px > 0 ) && ( px < x->x_vwidth ) ) + { + if ( ((*(data+x->x_cursY*x->x_vwidth+px))>>7) < 128 ) + { + *(data+x->x_cursY*x->x_vwidth+px) = 0xff<<7; + } + else + { + *(data+x->x_cursY*x->x_vwidth+px) = 0x00<<7; + } + } + } + for ( py=(x->x_cursY-5); py<=(x->x_cursY+5); py++ ) + { + if ( ( py > 0 ) && ( py < x->x_vheight ) ) + { + if ( ((*(data+py*x->x_vwidth+x->x_cursX))>>7) < 128 ) + { + *(data+py*x->x_vwidth+x->x_cursX) = 0xff<<7; + } + else + { + *(data+py*x->x_vwidth+x->x_cursX) = 0x00<<7; + } + } + } + } + + // track color + pfY = x->x_frame; + pfV = x->x_frame+x->x_vsize; + pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2); + if ( x->x_colorR != -1 ) + { + for ( py=0; py<x->x_vheight; py++ ) + { + for ( px=0; px<x->x_vwidth; px++ ) + { + y = *pfY++; + v = *pfV; + u = *pfU; + + if ( x->x_luminosity ) + { + diff = (abs(y-x->x_colorY)>>7)+(abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + else + { + diff = (abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + + if ( diff <= x->x_tolerance ) + { + x1=x2=px; + y1=y2=py; + found=0; + + for (ppy=y1; ppy<x->x_vheight; ppy++) + { + found = 0; + for (ppx=x1; ppx<x->x_vwidth; ppx++) + { + y = x->x_frame[ ppy*x->x_vwidth+ppx ]; + v = x->x_frame[ x->x_vsize + (((ppy>>1)*(x->x_vwidth>>1))+(ppx>>1)) ]; + u = x->x_frame[ x->x_vsize + (x->x_vsize>>2) + (((ppy>>1)*(x->x_vwidth>>1))+(ppx>>1)) ]; + + if ( x->x_luminosity ) + { + diff = (abs(y-x->x_colorY)>>7)+(abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + else + { + diff = (abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + + if ( diff <= x->x_tolerance ) + { + if ( ppx > x2 ) x2 = ppx; + y2 = ppy; + found=1; + } + else + { + break; + } + } + if (!found) break; // a whole line without the looked-up color + } + // check further extensions + // to the bottom + for (ppx=x1; ppx<x2; ppx++) + { + for (ppy=y2; ppy<x->x_vheight; ppy++) + { + y = x->x_frame[ ppy*x->x_vwidth+ppx ]; + v = x->x_frame[ x->x_vsize + (((ppy>>1)*(x->x_vwidth>>1))+(ppx>>1)) ]; + u = x->x_frame[ x->x_vsize + (x->x_vsize>>2) + (((ppy>>1)*(x->x_vwidth>>1))+(ppx>>1)) ]; + + if ( x->x_luminosity ) + { + diff = (abs(y-x->x_colorY)>>7)+(abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + else + { + diff = (abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + + if ( diff <= x->x_tolerance ) + { + y2 = ppy; + } + else + { + break; + } + } + } + // to the left + for (ppy=y1; ppy<=y2; ppy++) + { + for (ppx=x1; ppx>0; ppx--) + { + y = x->x_frame[ ppy*x->x_vwidth+ppx ]; + v = x->x_frame[ x->x_vsize + (((ppy>>1)*(x->x_vwidth>>1))+(ppx>>1)) ]; + u = x->x_frame[ x->x_vsize + (x->x_vsize>>2) + (((ppy>>1)*(x->x_vwidth>>1))+(ppx>>1)) ]; + + if ( x->x_luminosity ) + { + diff = (abs(y-x->x_colorY)>>7)+(abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + else + { + diff = (abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + + if ( diff <= x->x_tolerance ) + { + x1 = ppx; + } + else + { + break; + } + } + } + // to the right + for (ppy=y1; ppy<=y2; ppy++) + { + for (ppx=x2; ppx<x->x_vwidth; ppx++) + { + y = x->x_frame[ ppy*x->x_vwidth+ppx ]; + v = x->x_frame[ x->x_vsize + (((ppy>>1)*(x->x_vwidth>>1))+(ppx>>1)) ]; + u = x->x_frame[ x->x_vsize + (x->x_vsize>>2) + (((ppy>>1)*(x->x_vwidth>>1))+(ppx>>1)) ]; + + if ( x->x_luminosity ) + { + diff = (abs(y-x->x_colorY)>>7)+(abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + else + { + diff = (abs(u-x->x_colorU)>>8)+(abs(v-x->x_colorV)>>8); + } + + if ( diff <= x->x_tolerance ) + { + x2 = ppx; + } + else + { + break; + } + } + } + + // select zone + if ( !x->x_steady ) + { + // track the biggest object + if ( abs(x2-x1)*abs(y2-y1) > abs(X2-X1)*abs(Y2-Y1) ) + { + X1=x1; X2=x2; Y1=y1; Y2=y2; + } + } + else + { + // check if the cursor is in selected zone + if ( (x->x_cursX>=x1) && (x->x_cursX<=x2) + && (x->x_cursY>=y1) && (x->x_cursY<=y2 ) ) + { + X1=x1; X2=x2; Y1=y1; Y2=y2; + // set new cursor position + x->x_cursX = ( X1+X2 )/2; + x->x_cursY = ( Y1+Y2 )/2; + } + } + + px=x2+1; py=y2; + // post( "pdp_ctrack : px=%d py=%d", px, py ); + } + + if ( (px%2==0) && (py%2==0) ) + { + pfU++;pfV++; + } + } + } + if ( X1!=0 || X2!=0 || Y1!=0 || Y2!=0 ) + { + outlet_float( x->x_x1, X1 ); + outlet_float( x->x_y1, Y1 ); + outlet_float( x->x_x2, X2 ); + outlet_float( x->x_y2, Y2 ); + + // draw the frame + if ( x->x_showframe ) + { + for (ppy=Y1; ppy<=Y2; ppy++) + { + if ( ((*(data+ppy*x->x_vwidth+X1))>>7) < 128 ) + { + *(data+ppy*x->x_vwidth+X1) = 0xff<<7; + } + else + { + *(data+ppy*x->x_vwidth+X1) = 0x00<<7; + } + if ( ((*(data+ppy*x->x_vwidth+X2))>>7) < 128 ) + { + *(data+ppy*x->x_vwidth+X2) = 0xff<<7; + } + else + { + *(data+ppy*x->x_vwidth+X2) = 0x00<<7; + } + } + for (ppx=X1; ppx<=X2; ppx++) + { + if ( ((*(data+Y1*x->x_vwidth+ppx))>>7) < 128 ) + { + *(data+Y1*x->x_vwidth+ppx) = 0xff<<7; + } + else + { + *(data+Y1*x->x_vwidth+ppx) = 0x00<<7; + } + if ( ((*(data+Y2*x->x_vwidth+ppx))>>7) < 128 ) + { + *(data+Y2*x->x_vwidth+ppx) = 0xff<<7; + } + else + { + *(data+Y2*x->x_vwidth+ppx) = 0x00<<7; + } + } + } + } + } + + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet0); + + return; +} + +static void pdp_ctrack_process(t_pdp_ctrack *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_ctrack_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding){ + + case PDP_IMAGE_YV12: + pdp_ctrack_process_yv12(x); + 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_ctrack_process */ + break; + + } + } + +} + +static void pdp_ctrack_input_0(t_pdp_ctrack *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_ctrack_process(x); + + } +} + +static void pdp_ctrack_free(t_pdp_ctrack *x) +{ + int i; + + pdp_packet_mark_unused(x->x_packet0); + pdp_ctrack_free_ressources( x ); +} + +t_class *pdp_ctrack_class; + +void *pdp_ctrack_new(void) +{ + int i; + + t_pdp_ctrack *x = (t_pdp_ctrack *)pd_new(pdp_ctrack_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("R")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("G")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("B")); + 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_x1 = outlet_new(&x->x_obj, &s_float); + x->x_y1 = outlet_new(&x->x_obj, &s_float); + x->x_x2 = outlet_new(&x->x_obj, &s_float); + x->x_y2 = outlet_new(&x->x_obj, &s_float); + x->x_R = outlet_new(&x->x_obj, &s_float); + x->x_G = outlet_new(&x->x_obj, &s_float); + x->x_B = outlet_new(&x->x_obj, &s_float); + + x->x_colorR = -1; + x->x_colorG = -1; + x->x_colorB = -1; + + x->x_colorY = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; + x->x_colorU = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + x->x_colorV = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; + + x->x_packet0 = -1; + + x->x_cursX = -1; + x->x_cursY = -1; + x->x_tolerance = 50; + x->x_luminosity = 1; + x->x_steady = 0; + x->x_cursor = 1; + x->x_showframe = 1; + + x->x_canvas = canvas_getcurrent(); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_ctrack_setup(void) +{ + post( pdp_ctrack_version ); + pdp_ctrack_class = class_new(gensym("pdp_ctrack"), (t_newmethod)pdp_ctrack_new, + (t_method)pdp_ctrack_free, sizeof(t_pdp_ctrack), 0, A_NULL); + + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_r, gensym("R"), A_FLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_g, gensym("G"), A_FLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_b, gensym("B"), A_FLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_cursx, gensym("cursx"), A_FLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_cursy, gensym("cursy"), A_FLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_pick, gensym("pick"), A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_tolerance, gensym("tolerance"), A_FLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_luminosity, gensym("luminosity"), A_FLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_steady, gensym("steady"), A_FLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_cursor, gensym("cursor"), A_FLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_frame, gensym("frame"), A_FLOAT, A_NULL); + class_addmethod(pdp_ctrack_class, (t_method)pdp_ctrack_setcur, gensym("setcur"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_cycle.c b/modules/pdp_cycle.c new file mode 100644 index 0000000..f2d0085 --- /dev/null +++ b/modules/pdp_cycle.c @@ -0,0 +1,246 @@ +/* + * 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 port of cycle effect from EffecTV + * Originally written by clifford smith <nullset@dookie.net> + * Pd-fication by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include <math.h> + +#define NEWCOLOR(c,o) ((c+o)%230) + +static char *pdp_cycle_version = "pdp_cycle: version 0.1, port of cycle from EffecTV by clifford smith, adapted by ydegoyon@free.fr "; + +typedef struct pdp_cycle_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_int x_cycley; // flag to activate y cycling + t_int x_cycleu; // flag to activate u cycling + t_int x_cyclev; // flag to activate v cycling + + t_int x_yoffset; + t_int x_uoffset; + t_int x_voffset; + +} t_pdp_cycle; + +static void pdp_cycle_cycley(t_pdp_cycle *x, t_floatarg fcycley ) +{ + if ( ( fcycley == 0 ) || ( fcycley == 1 ) ) + { + x->x_cycley = fcycley; + } +} + +static void pdp_cycle_cycleu(t_pdp_cycle *x, t_floatarg fcycleu ) +{ + if ( ( fcycleu == 0 ) || ( fcycleu == 1 ) ) + { + x->x_cycleu = fcycleu; + } +} + +static void pdp_cycle_cyclev(t_pdp_cycle *x, t_floatarg fcyclev ) +{ + if ( ( fcyclev == 0 ) || ( fcyclev == 1 ) ) + { + x->x_cyclev = fcyclev; + } +} + +static void pdp_cycle_process_yv12(t_pdp_cycle *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, py, y, u, v; + + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + x->x_yoffset += 1; + x->x_uoffset += 3; + x->x_voffset += 7; + + for(py=1; py<x->x_vheight; py++) + { + for(px=0; px<x->x_vwidth; px++) + { + if ( x->x_cycley ) + { + y = *(data+py*x->x_vwidth+px)>>7; + *(newdata+py*x->x_vwidth+px) = NEWCOLOR(y,x->x_yoffset)<<7; + } + if ( x->x_cycleu ) + { + u = ( *(data+x->x_vsize+(x->x_vsize>>2)+((py*x->x_vwidth>>2)+(px>>1))) >>8 ) + 128; + *(newdata+x->x_vsize+(x->x_vsize>>2)+((py*x->x_vwidth>>2)+(px>>1))) = (NEWCOLOR(u,x->x_uoffset)-128)<<8; + } + if ( x->x_cyclev ) + { + v = ( *(data+x->x_vsize+((py*x->x_vwidth>>2)+(px>>1))) >>8 ) + 128; + *(newdata+x->x_vsize+((py*x->x_vwidth>>2)+(px>>1))) = (NEWCOLOR(v,x->x_voffset)-128)<<8; + } + } + } + + return; +} + +static void pdp_cycle_sendpacket(t_pdp_cycle *x) +{ + /* delete source 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_cycle_process(t_pdp_cycle *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_cycle_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_cycle_process_yv12, pdp_cycle_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_cycle_process */ + break; + + } + } + +} + +static void pdp_cycle_input_0(t_pdp_cycle *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_cycle_process(x); + + } + +} + +static void pdp_cycle_free(t_pdp_cycle *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_cycle_class; + +void *pdp_cycle_new(void) +{ + int i; + + t_pdp_cycle *x = (t_pdp_cycle *)pd_new(pdp_cycle_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cycley")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cycleu")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cyclev")); + + 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_cycley = 1; + x->x_cycleu = 0; + x->x_cyclev = 0; + + x->x_yoffset = 0; + x->x_uoffset = 0; + x->x_voffset = 0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_cycle_setup(void) +{ +// post( pdp_cycle_version ); + pdp_cycle_class = class_new(gensym("pdp_cycle"), (t_newmethod)pdp_cycle_new, + (t_method)pdp_cycle_free, sizeof(t_pdp_cycle), 0, A_NULL); + + class_addmethod(pdp_cycle_class, (t_method)pdp_cycle_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cycle_class, (t_method)pdp_cycle_cycley, gensym("cycley"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cycle_class, (t_method)pdp_cycle_cycleu, gensym("cycleu"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cycle_class, (t_method)pdp_cycle_cyclev, gensym("cyclev"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_dice.c b/modules/pdp_dice.c new file mode 100644 index 0000000..ddb6007 --- /dev/null +++ b/modules/pdp_dice.c @@ -0,0 +1,345 @@ +/* + * 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 port of dice effect from EffecTV + * Originally written by clifford smith <nullset@dookie.net> + * Pd-fication by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include <math.h> + +#define DEFAULT_CUBE_BITS 4 +#define MAX_CUBE_BITS 5 +#define MIN_CUBE_BITS 0 + +typedef enum _pdp_dice_dir { + up = 0, + right = 1, + down = 2, + left = 3 +} t_pdp_dice_dir; + +static unsigned int fastrand_val; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + +static char *pdp_dice_version = "pdp_dice: version 0.1, port of dice from EffecTV by clifford smith, adapted by ydegoyon@free.fr "; + +typedef struct pdp_dice_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + char *x_dicemap; + + t_int x_cube_bits; + t_int x_cube_size; + t_int x_map_height; + t_int x_map_width; + +} t_pdp_dice; + +static void pdp_dice_create_map(t_pdp_dice *x) +{ + int px, py, i; + + if ( x->x_dicemap == NULL ) + { + post( "pdp_dice : tried to create map but it's not allocated" ); + return; + } + + x->x_map_height = x->x_vheight >> x->x_cube_bits; + x->x_map_width = x->x_vwidth >> x->x_cube_bits; + x->x_cube_size = 1 << x->x_cube_bits; + + i = 0; + for (py=0; py<x->x_map_height; py++) + { + for(px=0; px<x->x_map_width; px++) + { + x->x_dicemap[i] = (inline_fastrand() >> 24) & 0x03; + i++; + } + } + + return; +} + +static void pdp_dice_cubebits(t_pdp_dice *x, t_floatarg fcubebits ) +{ + if ( ( fcubebits >= MIN_CUBE_BITS ) || ( fcubebits <= MAX_CUBE_BITS ) ) + { + x->x_cube_bits = fcubebits; + pdp_dice_create_map(x); + } +} + +static void pdp_dice_free_ressources(t_pdp_dice *x) +{ + if ( x->x_dicemap ) freebytes( x->x_dicemap, x->x_vsize ); +} + +static void pdp_dice_allocate(t_pdp_dice *x) +{ + x->x_dicemap = (char *) getbytes( x->x_vsize ); +} + +static void pdp_dice_process_yv12(t_pdp_dice *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, iuv; + t_int mapx, mapy, mapi; + t_int base, baseuv, dx, dy, di, diuv; + + /* allocate all ressources */ + if ( ((int)header->info.image.width != x->x_vwidth) || + ((int)header->info.image.height != x->x_vheight) ) + { + pdp_dice_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_dice_allocate(x); + post( "pdp_dice : reallocated buffers" ); + pdp_dice_create_map(x); + post( "pdp_dice : initialized map" ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + mapi = 0; + for(mapy = 0; mapy < x->x_map_height; mapy++) + { + for(mapx = 0; mapx < x->x_map_width; mapx++) + { + base = (mapy << x->x_cube_bits) * x->x_vwidth + (mapx << x->x_cube_bits); + baseuv = (((mapy << x->x_cube_bits)>>1) * (x->x_vwidth>>1)) + ((mapx << x->x_cube_bits)>>1); + switch (x->x_dicemap[mapi]) + { + case up: + for (dy = 0; dy < x->x_cube_size; dy++) + { + i = base + dy * x->x_vwidth; + iuv = baseuv + ((dy>>1) * (x->x_vwidth>>1)); + for (dx = 0; dx < x->x_cube_size; dx++) + { + newdata[i] = data[i]; + newdata[x->x_vsize+iuv] = data[x->x_vsize+iuv]; + newdata[x->x_vsize+(x->x_vsize>>2)+iuv] = data[x->x_vsize+(x->x_vsize>>2)+iuv]; + i++; + if ( (dx%2==0) && (dy%2==0) ) iuv++; + } + } + break; + + case left: + for (dy = 0; dy < x->x_cube_size; dy++) + { + i = base + dy * x->x_vwidth; + iuv = baseuv + ((dy>>1) * (x->x_vwidth>>1)); + for (dx = 0; dx < x->x_cube_size; dx++) + { + di = base + (dx * x->x_vwidth) + (x->x_cube_size - dy - 1); + diuv = baseuv + ((dx>>1) * (x->x_vwidth>>1)) + ((x->x_cube_size - dy - 1)>>1); + newdata[di] = data[i]; + newdata[x->x_vsize+diuv] = data[x->x_vsize+iuv]; + newdata[x->x_vsize+(x->x_vsize>>2)+diuv] = data[x->x_vsize+(x->x_vsize>>2)+iuv]; + i++; + if ( (dx%2==0) && (dy%2==0) ) iuv++; + } + } + break; + + case down: + for (dy = 0; dy < x->x_cube_size; dy++) + { + di = base + dy * x->x_vwidth; + diuv = baseuv + ((dy>>1) * (x->x_vwidth>>1)); + i = base + (x->x_cube_size - dy - 1) * x->x_vwidth + x->x_cube_size; + iuv = baseuv + (((x->x_cube_size - dy - 1)>>1) * (x->x_vwidth>>1)) + (x->x_cube_size>>1); + for (dx = 0; dx < x->x_cube_size; dx++) + { + i--; + if ( dx%2==0) iuv--; + newdata[di] = data[i]; + newdata[x->x_vsize+diuv] = data[x->x_vsize+iuv]; + newdata[x->x_vsize+(x->x_vsize>>2)+diuv] = data[x->x_vsize+(x->x_vsize>>2)+iuv]; + di++; + if ( (dx%2==0) && (dy%2==0) ) iuv++; + } + } + break; + + case right: + for (dy = 0; dy < x->x_cube_size; dy++) + { + i = base + (dy * x->x_vwidth); + iuv = baseuv + ((dy>>1) * (x->x_vwidth>>1)); + for (dx = 0; dx < x->x_cube_size; dx++) + { + di = base + dy + (x->x_cube_size - dx - 1) * x->x_vwidth; + diuv = baseuv + (dy>>1) + (((x->x_cube_size - dx - 1)>>1) * (x->x_vwidth>>1)); + newdata[di] = data[i]; + newdata[x->x_vsize+diuv] = data[x->x_vsize+iuv]; + newdata[x->x_vsize+(x->x_vsize>>2)+diuv] = data[x->x_vsize+(x->x_vsize>>2)+iuv]; + i++; + if ( (dx%2==0) && (dy%2==0) ) iuv++; + } + } + break; + + default: + break; + } + mapi++; + } + } + + return; +} + +static void pdp_dice_sendpacket(t_pdp_dice *x) +{ + /* delete source 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_dice_process(t_pdp_dice *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_dice_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_dice_process_yv12, pdp_dice_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_dice_process */ + break; + + } + } + +} + +static void pdp_dice_input_0(t_pdp_dice *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_dice_process(x); + + } + +} + +static void pdp_dice_free(t_pdp_dice *x) +{ + int i; + + pdp_dice_free_ressources(x); + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_dice_class; + +void *pdp_dice_new(void) +{ + int i; + + t_pdp_dice *x = (t_pdp_dice *)pd_new(pdp_dice_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cubebits")); + + 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_cube_bits = DEFAULT_CUBE_BITS; + x->x_cube_size = 0; + x->x_map_height = 0; + x->x_map_width = 0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_dice_setup(void) +{ +// post( pdp_dice_version ); + pdp_dice_class = class_new(gensym("pdp_dice"), (t_newmethod)pdp_dice_new, + (t_method)pdp_dice_free, sizeof(t_pdp_dice), 0, A_NULL); + + class_addmethod(pdp_dice_class, (t_method)pdp_dice_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_dice_class, (t_method)pdp_dice_cubebits, gensym("cubebits"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_edge.c b/modules/pdp_edge.c new file mode 100644 index 0000000..89e3cd8 --- /dev/null +++ b/modules/pdp_edge.c @@ -0,0 +1,300 @@ +/* + * 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 adaptation of edge effect from effectv + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +static char *pdp_edge_version = "pdp_edge: version 0.1, port of edge from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_edge_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_mapw; + t_int x_maph; + t_int x_video_width_margin; + t_int *x_map; + +} t_pdp_edge; + +static void pdp_edge_allocate(t_pdp_edge *x) +{ + x->x_map = (t_int*) getbytes ( ( x->x_vwidth * x->x_vheight * sizeof (t_int) ) << 1 ); + bzero(x->x_map, ( x->x_vwidth * x->x_vheight * sizeof (t_int) ) << 1 ); +} + +static void pdp_edge_free_ressources(t_pdp_edge *x) +{ + if ( x->x_map ) freebytes ( x->x_map, ( x->x_vwidth * x->x_vheight * sizeof (t_int) ) << 1 ); +} + +static void pdp_edge_process_yv12(t_pdp_edge *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); + int i; + + t_int px, py; + t_int y, u, v; + t_int y0, u0, v0; + t_int y1, u1, v1; + t_int y2, u2, v2; + t_int y3, u3, v3; + short int *pdata, *pnewdata; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_edge_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; + x->x_mapw = x->x_vwidth >> 2; + x->x_maph = x->x_vheight >> 2; + x->x_video_width_margin = x->x_vwidth - ( x->x_mapw << 2 ); + pdp_edge_allocate(x); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + pdata = data + x->x_vwidth*4+4; + pnewdata = newdata + x->x_vwidth*4+4; + for(py=1; py<x->x_vheight-4; py+=4) + { + for(px=1; px<x->x_vwidth-4; px+=4) + { + /* difference between the current pixel and right neighbor. */ + y2 = (*(pdata+(py*x->x_vwidth+px))>>8) - (*(pdata+(py*x->x_vwidth+px)-4)>>8); + u2 = (*(pdata+x->x_vsize+(py*x->x_vwidth>>2)+(px>>1))>>8) - + (*(pdata+x->x_vsize+(py*x->x_vwidth>>2)+(px>>1)-2)>>8); + v2 = (*(pdata+x->x_vsize+(x->x_vsize>>2)+(py*x->x_vwidth>>2)+(px>>1))>>8) - + (*(pdata+x->x_vsize+(x->x_vsize>>2)+(py*x->x_vwidth>>2)+(px>>1)-2)>>8); + y2 *= y2; + u2 *= u2; + v2 *= v2; + y2 = y2>>5; /* To lack the lower bit for saturated addition, */ + u2 = u2>>5; /* devide the value with 32, instead of 16. It is */ + v2 = v2>>4; /* same as `v2 &= 0xfefeff' */ + if(y2>127) y2 = 127; + if(u2>127) u2 = 127; + if(v2>255) v2 = 255; + + /* difference between the current pixel and upper neighbor. */ + y3 = (*(pdata+(py*x->x_vwidth+px))>>8) - (*(pdata+(py*x->x_vwidth+px)-4*x->x_vwidth)>>8); + u3 = (*(pdata+x->x_vsize+(py*x->x_vwidth>>2)+(px>>1))>>8) - + (*(pdata+x->x_vsize+(py*x->x_vwidth>>2)+(px>>1)-2*x->x_vwidth)>>8); + v3 = (*(pdata+x->x_vsize+(x->x_vsize>>2)+(py*x->x_vwidth>>2)+(px>>1))>>8) - + (*(pdata+x->x_vsize+(x->x_vsize>>2)+(py*x->x_vwidth>>2)+(px>>1)-2*x->x_vwidth)>>8); + y3 *= y3; + u3 *= u3; + v3 *= v3; + y3 = y3>>5; /* To lack the lower bit for saturated addition, */ + u3 = u3>>5; /* devide the value with 32, instead of 16. It is */ + v3 = v3>>4; /* same as `v2 &= 0xfefeff' */ + if(y3>127) y3 = 127; + if(u3>127) u3 = 127; + if(v3>255) v3 = 255; + + y0 = (x->x_map[(py-1)*x->x_vwidth*2+px*2]>>17); + u0 = (x->x_map[(py-1)*x->x_vwidth*2+px*2]>>9)&0xff; + v0 = x->x_map[(py-1)*x->x_vwidth*2+px*2]&0xff; + y1 = (x->x_map[py*x->x_vwidth*2+(px-1)*2+1]>>17); + u1 = (x->x_map[py*x->x_vwidth*2+(px-1)*2+1]>>9)&0xff; + v1 = x->x_map[py*x->x_vwidth*2+(px-1)*2+1]&0xff; + x->x_map[py*x->x_vwidth*2+px*2] = (y2<<17)|(u2<<9)|v2; + x->x_map[py*x->x_vwidth*2+px*2+1] = (y3<<17)|(u3<<9)|v3; + y = y0 + y1; + u = y & 0x01010100; + *(pnewdata+(py*x->x_vwidth+px)) = (y | (u - (u>>8)))<<8; + *(pnewdata+x->x_vsize+(py*x->x_vwidth>>2)+(px>>1)) = (y | (u - (u>>8)))<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+(py*x->x_vwidth>>2)+(px>>1)) = (y | (u - (u>>8)))<<8; + y = y0 + y3; + u = y & 0x01010100; + *(pnewdata+(py*x->x_vwidth+px)+1) = (y | (u - (u>>8)))<<8; + *(pnewdata+x->x_vsize+(py*x->x_vwidth>>2)+(px>>1+1)) = (y | (u - (u>>8)))<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+(py*x->x_vwidth>>2)+(px>>1+1)) = (y | (u - (u>>8)))<<8; + *(pnewdata+(py*x->x_vwidth+px)+2) = y3<<8; + *(pnewdata+x->x_vsize+(py*x->x_vwidth>>2)+((px+2)>>1)) = u3<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+(py*x->x_vwidth>>2)+((px+2)>>1)) = v3<<8; + *(pnewdata+(py*x->x_vwidth+px)+3) = y3<<8; + *(pnewdata+x->x_vsize+(py*x->x_vwidth>>2)+((px+3)>>1)) = u3<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+(py*x->x_vwidth>>2)+((px+3)>>1)) = v3<<8; + y = y2 + y1; + u = y & 0x01010100; + *(pnewdata+(py*x->x_vwidth+px)+x->x_vwidth) = (y | (u - (u>>8)))<<8; + *(pnewdata+x->x_vsize+((py+1)*x->x_vwidth>>2)+(px>>1)) = (y | (u - (u>>8)))<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+((py+1)*x->x_vwidth>>2)+(px>>1)) = (y | (u - (u>>8)))<<8; + y = y2 + y3; + u = y & 0x01010100; + + *(pnewdata+(py*x->x_vwidth+px)+x->x_vwidth+1) = (y | (u - (u>>8)))<<8; + *(pnewdata+x->x_vsize+((py+1)*x->x_vwidth>>2)+((px+1)>>1)) = (y | (u - (u>>8)))<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+((py+1)*x->x_vwidth>>2)+((px+1)>>1)) = (y | (u - (u>>8)))<<8; + + *(pnewdata+(py*x->x_vwidth+px)+x->x_vwidth+2) = y3<<8; + *(pnewdata+x->x_vsize+((py+1)*x->x_vwidth>>2)+((px+2)>>1)) = u3<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+((py+1)*x->x_vwidth>>2)+((px+2)>>1)) = v3<<8; + + *(pnewdata+(py*x->x_vwidth+px)+x->x_vwidth+3) = y3<<8; + *(pnewdata+x->x_vsize+((py+1)*x->x_vwidth>>2)+((px+3)>>1)) = u3<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+((py+1)*x->x_vwidth>>2)+((px+3)>>1)) = v3<<8; + + *(pnewdata+(py*x->x_vwidth+px)+2*x->x_vwidth) = y2<<8; + *(pnewdata+x->x_vsize+((py+2)*x->x_vwidth>>2)+((px)>>1)) = u2<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+((py+2)*x->x_vwidth>>2)+((px)>>1)) = v2<<8; + + *(pnewdata+(py*x->x_vwidth+px)+2*x->x_vwidth+1) = y2<<8; + *(pnewdata+x->x_vsize+((py+2)*x->x_vwidth>>2)+((px+1)>>1)) = u2<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+((py+2)*x->x_vwidth>>2)+((px+1)>>1)) = v2<<8; + + *(pnewdata+(py*x->x_vwidth+px)+3*x->x_vwidth) = y2<<8; + *(pnewdata+x->x_vsize+((py+3)*x->x_vwidth>>2)+((px)>>1)) = u2<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+((py+3)*x->x_vwidth>>2)+((px)>>1)) = v2<<8; + + *(pnewdata+(py*x->x_vwidth+px)+3*x->x_vwidth+1) = y2<<8; + *(pnewdata+x->x_vsize+((py+3)*x->x_vwidth>>2)+((px+1)>>1)) = u2<<8; + *(pnewdata+x->x_vsize+(x->x_vsize>>2)+((py+3)*x->x_vwidth>>2)+((px+1)>>1)) = v2<<8; + } + } + + return; +} + +static void pdp_edge_sendpacket(t_pdp_edge *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_edge_process(t_pdp_edge *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_edge_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_edge_process_yv12, pdp_edge_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + break; + + default: + /* don't know the type, so dont pdp_edge_process */ + break; + + } + } + +} + +static void pdp_edge_input_0(t_pdp_edge *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_edge_process(x); + + } +} + +static void pdp_edge_free(t_pdp_edge *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_edge_free_ressources(x); +} + +t_class *pdp_edge_class; + +void *pdp_edge_new(void) +{ + int i; + + t_pdp_edge *x = (t_pdp_edge *)pd_new(pdp_edge_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_queue_id = -1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_edge_setup(void) +{ +// post( pdp_edge_version ); + pdp_edge_class = class_new(gensym("pdp_edge"), (t_newmethod)pdp_edge_new, + (t_method)pdp_edge_free, sizeof(t_pdp_edge), 0, A_NULL); + + class_addmethod(pdp_edge_class, (t_method)pdp_edge_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_ffmpeg~.c b/modules/pdp_ffmpeg~.c new file mode 100644 index 0000000..30e321f --- /dev/null +++ b/modules/pdp_ffmpeg~.c @@ -0,0 +1,740 @@ +/* + * Pure Data Packet module. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a streaming object towards a ffmeg server + * A lot of this object code is inspired by the excellent ffmpeg.c + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * The rest is written by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include <math.h> +#include <time.h> +#include <sys/time.h> +#include <avformat.h> + +#define VIDEO_BUFFER_SIZE (1024*1024) +#define MAX_AUDIO_PACKET_SIZE (128 * 1024) +#define AUDIO_PACKET_SIZE (2*1152) + +static char *pdp_ffmpeg_version = "pdp_ffmpeg~: version 0.1, a video streaming object (towards ffserver)"; + +typedef struct pdp_ffmpeg_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_dropped; + t_int x_queue_id; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_outlet *x_outlet_streaming; // indicates the action of streaming + t_outlet *x_outlet_nbframes; // number of frames emitted + t_outlet *x_outlet_nbframes_dropped; // number of frames dropped + + char *x_feedname; + t_int x_streaming; // streaming flag + t_int x_nbframes; // number of frames emitted + t_int x_nbframes_dropped; // number of frames dropped + t_int x_nbvideostreams; // number of video streams + t_int x_nbaudiostreams; // number of audio streams + t_int x_cursec; // current second + t_int *x_secondcount; // number of frames emitted in the current second ( per video stream ) + + /* AV data structures */ + AVFormatContext *x_avcontext; + AVFormatParameters x_avparameters; // unused but the call is necessary to allocate structures + AVPicture *x_final_picture; + AVPicture *x_formatted_picture; + AVPicture x_picture_format; + AVPicture x_picture_final; + ImgReSampleContext *x_img_resample_ctx; + uint8_t *x_video_buffer; + uint8_t *x_rdata; + uint8_t *x_buf1; + uint8_t *x_buf2; + + /* audio structures */ + short x_audio_buf[2*MAX_AUDIO_PACKET_SIZE]; /* buffer for incoming audio */ + short x_audio_enc_buf[2*MAX_AUDIO_PACKET_SIZE]; /* buffer for audio to be encoded */ + uint8_t x_audio_out[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for encoded audio */ + t_int x_audioin_position; // writing position for incoming audio + t_int x_audio_per_frame; // number of audio samples to transmit for each frame + ReSampleContext *x_audio_resample_ctx; // structures for audio resample + FifoBuffer *x_audio_fifo; // audio fifos ( one per codec ) + +} t_pdp_ffmpeg; + +static int pdp_ffmpeg_read_ffserver_streams(t_pdp_ffmpeg *x) +{ + int i, err; + AVFormatContext *ic; + + err = av_open_input_file(&ic, x->x_feedname, NULL, FFM_PACKET_SIZE, NULL); + if (err < 0) + { + return err; + } + + /* copy stream format */ + x->x_avcontext->nb_streams = ic->nb_streams; + x->x_nbvideostreams = 0; + x->x_nbaudiostreams = 0; + + for(i=0;i<ic->nb_streams;i++) + { + AVStream *st; + + st = av_mallocz(sizeof(AVFormatContext)); + memcpy(st, ic->streams[i], sizeof(AVStream)); + x->x_avcontext->streams[i] = st; + + if ( ic->streams[i]->codec.codec_type == CODEC_TYPE_UNKNOWN ) + { + post( "pdp_ffmpeg~ : stream #%d # type : unknown", i ); + } + if ( ic->streams[i]->codec.codec_type == CODEC_TYPE_AUDIO ) + { + post( "pdp_ffmpeg~ : stream #%d # type : audio # id : %d # bitrate : %d", + i, ic->streams[i]->codec.codec_id, ic->streams[i]->codec.bit_rate ); + post( "pdp_ffmpeg~ : sample rate : %d # channels : %d", + ic->streams[i]->codec.sample_rate, ic->streams[i]->codec.channels ); + x->x_nbaudiostreams++; + } + if ( ic->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO ) + { + post( "pdp_ffmpeg~ : stream #%d # type : video # id : %d # bitrate : %d", + i, ic->streams[i]->codec.codec_id, ic->streams[i]->codec.bit_rate ); + post( "pdp_ffmpeg~ : framerate : %d # width : %d # height : %d", + ic->streams[i]->codec.frame_rate/10000, ic->streams[i]->codec.width, ic->streams[i]->codec.height ); + x->x_nbvideostreams++; + } + } + + if ( x->x_secondcount ) free( x->x_secondcount ); + x->x_secondcount = (t_int*) malloc( x->x_nbvideostreams*sizeof(t_int) ); + memset( x->x_secondcount, 0x00, x->x_nbvideostreams*sizeof(t_int) ); + x->x_audio_fifo = (FifoBuffer*) malloc( x->x_nbaudiostreams*sizeof(FifoBuffer) ); + for ( i=0; i<x->x_nbaudiostreams; i++) + { + fifo_init(&x->x_audio_fifo[i], 2 * MAX_AUDIO_PACKET_SIZE); + } + + av_close_input_file(ic); + + return 0; +} + +static void pdp_ffmpeg_starve(t_pdp_ffmpeg *x) +{ + t_int ret, i; + + if (!x->x_streaming) + { + post("pdp_ffmpeg~ : close request but no feed is opened ... ignored" ); + return; + } + + x->x_streaming = 0; + outlet_float( x->x_outlet_streaming, x->x_streaming ); + x->x_nbframes = 0; + outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + x->x_nbframes_dropped = 0; + outlet_float( x->x_outlet_nbframes_dropped, x->x_nbframes_dropped ); + if ( x->x_buf1 ) free( x->x_buf1 ); + x->x_buf1 = NULL; + if ( x->x_buf2 ) free( x->x_buf2 ); + x->x_buf2 = NULL; + + if (x->x_img_resample_ctx) + { + img_resample_close(x->x_img_resample_ctx); + x->x_img_resample_ctx = NULL; + } + + if (x->x_audio_resample_ctx) + { + audio_resample_close(x->x_audio_resample_ctx); + x->x_audio_resample_ctx = NULL; + } + + if ( ( ret = url_fclose(&x->x_avcontext->pb) ) < 0 ) + { + post("pdp_ffmpeg~ : could not close stream (ret=%d)", ret ); + } + + for(i=0;i<x->x_avcontext->nb_streams;i++) + { + avcodec_close( &x->x_avcontext->streams[i]->codec ); + av_free( x->x_avcontext->streams[i] ); + } + +} + +static void pdp_ffmpeg_feed(t_pdp_ffmpeg *x, t_symbol *s) +{ + t_int ret, i; + + if (x->x_streaming) + { + post("pdp_ffmpeg~ : feed request but a feed is running ... ignored" ); + return; + } + + if ( !strstr( s->s_name, "http:" ) || !strstr( s->s_name, ".ffm" ) ) + { + post( "pdp_ffmpeg~ : wrong feed format <%s>: should be like http://localhost:8090/test.ffm", s->s_name ); + } + if ( x->x_feedname ) free( x->x_feedname ); + x->x_feedname = (char*) malloc( strlen( s->s_name ) + 1 ); + strcpy( x->x_feedname, s->s_name ); + + /* set output format */ + x->x_avcontext->oformat = guess_format(NULL, s->s_name, NULL); + + if ( ( ret = pdp_ffmpeg_read_ffserver_streams(x) ) < 0 ) + { + post( "pdp_ffmpeg~ : error encountered while reading feed informations :", ret ); + if ( ret == -1 ) post( "pdp_ffmpeg~ : unknown error" ); + if ( ret == -2 ) post( "pdp_ffmpeg~ : i/o error" ); + if ( ret == -3 ) post( "pdp_ffmpeg~ : number syntax expected in filename" ); + if ( ret == -4 ) post( "pdp_ffmpeg~ : invalid data found" ); + if ( ret == -5 ) post( "pdp_ffmpeg~ : not enough memory" ); + if ( ret == -6 ) post( "pdp_ffmpeg~ : unknown format" ); + + x->x_streaming = 0; + outlet_float( x->x_outlet_streaming, x->x_streaming ); + return; + } + + /* open the stream now */ + if (url_fopen(&x->x_avcontext->pb, s->s_name, URL_WRONLY) < 0) + { + post("pdp_ffmpeg~ : could not open stream '%s'", s->s_name); + x->x_streaming = 0; + outlet_float( x->x_outlet_streaming, x->x_streaming ); + return; + } + else + { + post("pdp_ffmpeg~ : opened stream '%s'", s->s_name); + } + + /* open each encoder */ + for(i=0;i<x->x_avcontext->nb_streams;i++) + { + AVCodec *codec; + codec = avcodec_find_encoder(x->x_avcontext->streams[i]->codec.codec_id); + if (!codec) + { + post("pdp_ffmpeg~ : unsupported codec for output stream #%d\n", i ); + x->x_streaming = 0; + outlet_float( x->x_outlet_streaming, x->x_streaming ); + return; + } + if (avcodec_open(&x->x_avcontext->streams[i]->codec, codec) < 0) + { + post("pdp_ffmpeg~ : error while opening codec for stream #%d - maybe incorrect parameters such as bit_rate, rate, width or height\n", i); + x->x_streaming = 0; + outlet_float( x->x_outlet_streaming, x->x_streaming ); + return; + } + else + { + post("pdp_ffmpeg~ : opened encoder for stream #%d", i); + } + } + + /* set parameters */ + if (av_set_parameters(x->x_avcontext, &x->x_avparameters) < 0) + { + post("pdp_ffmpeg~ : severe error : could not set encoding parameters" ); + x->x_streaming = 0; + outlet_float( x->x_outlet_streaming, x->x_streaming ); + return; + } + + /* write header */ + if (av_write_header(x->x_avcontext) < 0) + { + post("pdp_ffmpeg~ : could not write header (incorrect codec parameters ?)\n", i); + x->x_streaming = 0; + outlet_float( x->x_outlet_streaming, x->x_streaming ); + return; + } + + x->x_streaming = 1; + outlet_float( x->x_outlet_streaming, x->x_streaming ); + x->x_nbframes = 0; + outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + x->x_nbframes_dropped = 0; + outlet_float( x->x_outlet_nbframes_dropped, x->x_nbframes_dropped ); + +} + +static void pdp_ffmpeg_allocate(t_pdp_ffmpeg *x) +{ + x->x_rdata = (uint8_t*) getbytes( (x->x_vsize+(x->x_vsize>>1))*sizeof(uint8_t) ); +} + +static void pdp_ffmpeg_free_ressources(t_pdp_ffmpeg *x) +{ + if (x->x_rdata) freebytes( x->x_rdata, (x->x_vsize+(x->x_vsize>>1))*sizeof(uint8_t) ); +} + +static void pdp_ffmpeg_process_yv12(t_pdp_ffmpeg *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *newheader = 0; + short int *newdata = 0; + t_int newpacket = -1, i; + short int *pY, *pU, *pV; + uint8_t *pnY, *pnU, *pnV; + t_int px, py; + t_int svideoindex; + t_int saudioindex; + struct timeval etime; + t_int sizeout, size, encsize; + t_int framebytes; + t_int owidth, oheight; + short *pencbuf; + + /* allocate all ressources */ + if ( ((int)header->info.image.width != x->x_vwidth) || + ((int)header->info.image.height != x->x_vheight) ) + { + pdp_ffmpeg_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_ffmpeg_allocate(x); + } + + if ( x->x_streaming ) + { + AVPicture pdppict; + + pY = data; + pV = data+x->x_vsize; + pU = data+x->x_vsize+(x->x_vsize>>2); + pnY = x->x_rdata; + pnU = x->x_rdata+x->x_vsize; + pnV = x->x_rdata+x->x_vsize+(x->x_vsize>>2); + for(py=0; py<x->x_vheight; py++) + { + for(px=0; px<x->x_vwidth; px++) + { + *pnY = (uint8_t) (*(pY++)>>7); + if ( *pnY > 255 ) *pnY=255; + pnY++; + if ( (px%2==0) && (py%2==0) ) + { + *pnV = (uint8_t) (((*(pV++))>>8)+128); + if ( *pnV > 255 ) *pnV=255; + *pnU = (uint8_t) (((*(pU++))>>8)+128); + if ( *pnU > 255 ) *pnU=255; + pnU++;pnV++; + } + } + } + + if ( avpicture_fill(&pdppict, x->x_rdata, + PIX_FMT_YUV420P, + x->x_vwidth, + x->x_vheight) < 0 ) + { + post( "pdp_ffmpeg~ : could not build av picture" ); + } + + // output all video streams + svideoindex=0; + saudioindex=0; + for (i=0; i<x->x_avcontext->nb_streams; i++) + { + /* convert pixel format and size if needed */ + if ( x->x_avcontext->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO ) + { + t_int size; + + // check if the framerate has been exceeded + if ( gettimeofday(&etime, NULL) == -1) + { + post("pdp_ffmpeg~ : could not read time" ); + } + if ( etime.tv_sec != x->x_cursec ) + { + x->x_cursec = etime.tv_sec; + x->x_secondcount[ svideoindex ] = 0; + } + if ( x->x_secondcount[ svideoindex ] >= x->x_avcontext->streams[i]->codec.frame_rate/10000 ) + { + x->x_nbframes_dropped++; + continue; + } + + if ( x->x_avcontext->streams[i]->codec.pix_fmt != PIX_FMT_YUV420P ) + { + /* create temporary picture */ + size = avpicture_get_size(x->x_avcontext->streams[i]->codec.pix_fmt, + x->x_vwidth, x->x_vheight ); + if (!x->x_buf1) x->x_buf1 = (uint8_t*) malloc(size); + if (!x->x_buf1) + { + post ("pdp_ffmpeg~ : severe error : could not allocate image buffer" ); + return; + } + x->x_formatted_picture = &x->x_picture_format; + avpicture_fill(x->x_formatted_picture, x->x_buf1, + x->x_avcontext->streams[i]->codec.pix_fmt, + x->x_vwidth, x->x_vheight ); + + if (img_convert(x->x_formatted_picture, + x->x_avcontext->streams[i]->codec.pix_fmt, + &pdppict, PIX_FMT_YUV420P, + x->x_vwidth, x->x_vheight ) < 0) + { + post ("pdp_ffmpeg~ : error : image conversion failed" ); + } + } + else + { + x->x_formatted_picture = &pdppict; + } + + // post ( "pdp_ffmpeg~ : resampling [%d,%d] -> [%d,%d]", + // x->x_vwidth, x->x_vheight, + // x->x_avcontext->streams[i]->codec.width, + // x->x_avcontext->streams[i]->codec.height ); + if ( ( x->x_avcontext->streams[i]->codec.width < x->x_vwidth ) && + ( x->x_avcontext->streams[i]->codec.height < x->x_vheight ) ) + { + owidth = x->x_avcontext->streams[i]->codec.width; + oheight = x->x_avcontext->streams[i]->codec.height; + + if (x->x_img_resample_ctx) img_resample_close(x->x_img_resample_ctx); + x->x_img_resample_ctx = img_resample_full_init( + owidth, oheight, + x->x_vwidth, x->x_vheight, 0, 0, 0, 0); + + size = avpicture_get_size(x->x_avcontext->streams[i]->codec.pix_fmt, + owidth, oheight ); + if ( !x->x_buf2 ) + { + x->x_buf2 = (uint8_t*) malloc(size); + } + if (!x->x_buf2) + { + post ("pdp_ffmpeg~ : severe error : could not allocate image buffer" ); + return; + } + x->x_final_picture = &x->x_picture_final; + avpicture_fill(x->x_final_picture, x->x_buf2, + x->x_avcontext->streams[i]->codec.pix_fmt, + owidth, oheight ); + + img_resample(x->x_img_resample_ctx, x->x_final_picture, x->x_formatted_picture); + + } + else + { + x->x_final_picture = x->x_formatted_picture; + } + + // encode and send the picture + { + AVFrame aframe; + t_int fsize, ret; + + memset(&aframe, 0, sizeof(AVFrame)); + *(AVPicture*)&aframe= *x->x_final_picture; + + aframe.quality = x->x_avcontext->streams[i]->quality; + + fsize = avcodec_encode_video(&x->x_avcontext->streams[i]->codec, + x->x_video_buffer, VIDEO_BUFFER_SIZE, + &aframe); + if ( ( ret = av_write_frame( x->x_avcontext, i, x->x_video_buffer, fsize) ) < 0 ) + { + post ("pdp_ffmpeg~ : error : could not send frame : (ret=%d)", ret ); + return; + } + else + { + x->x_nbframes++; + x->x_secondcount[ svideoindex++ ]++; + } + } + } + + /* convert and stream audio data */ + if ( x->x_avcontext->streams[i]->codec.codec_type == CODEC_TYPE_AUDIO ) + { + // we assume audio is synchronized on next video stream + if ( ( (i+1) < x->x_avcontext->nb_streams ) && + ( x->x_avcontext->streams[i+1]->codec.codec_type == CODEC_TYPE_VIDEO ) ) + { + x->x_audio_per_frame = + // 2*( (int) sys_getsr() ) / ( x->x_avcontext->streams[i+1]->codec.frame_rate/10000); + AUDIO_PACKET_SIZE; + // post ("pdp_ffmpeg~ : transmit %d samples", x->x_audio_per_frame ); + } + else + { + post ("pdp_ffmpeg~ : can't stream audio : video stream is not found" ); + continue; + } + + if ( x->x_audioin_position > x->x_audio_per_frame ) + { + size = x->x_audioin_position; + if ( ( x->x_avcontext->streams[i]->codec.sample_rate != (int)sys_getsr() ) || + ( x->x_avcontext->streams[i]->codec.channels != 2 ) ) + { + if (x->x_audio_resample_ctx) audio_resample_close(x->x_audio_resample_ctx); + x->x_audio_resample_ctx = + audio_resample_init(x->x_avcontext->streams[i]->codec.channels, 2, + x->x_avcontext->streams[i]->codec.sample_rate, + (int)sys_getsr()); + sizeout = audio_resample(x->x_audio_resample_ctx, + x->x_audio_enc_buf, + x->x_audio_buf, + size / (x->x_avcontext->streams[i]->codec.channels * 2)); + pencbuf = (short*) &x->x_audio_enc_buf; + sizeout = sizeout * x->x_avcontext->streams[i]->codec.channels * 2; + } + else + { + pencbuf = (short*) &x->x_audio_buf; + sizeout = size; + } + + /* output resampled raw samples */ + fifo_write(&x->x_audio_fifo[saudioindex], (uint8_t*)pencbuf, sizeout, + &x->x_audio_fifo[saudioindex].wptr); + + framebytes = x->x_avcontext->streams[i]->codec.frame_size * 2 * + x->x_avcontext->streams[i]->codec.channels; + + while (fifo_read(&x->x_audio_fifo[saudioindex], (uint8_t*)pencbuf, framebytes, + &x->x_audio_fifo[saudioindex].rptr) == 0) + { + encsize = avcodec_encode_audio(&x->x_avcontext->streams[i]->codec, + (uint8_t*)&x->x_audio_out, sizeof(x->x_audio_out), + (short *)pencbuf); + av_write_frame(x->x_avcontext, i, x->x_audio_out, encsize); + } + saudioindex++; + } + } + } + x->x_audioin_position=0; + } + return; +} + +static void pdp_ffmpeg_killpacket(t_pdp_ffmpeg *x) +{ + /* delete source packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; +} + + /* store audio data in PCM format and stream it */ +static t_int *pdp_ffmpeg_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_ffmpeg *x = (t_pdp_ffmpeg *)(w[3]); + int n = (int)(w[4]); // number of samples + t_float fsample; + t_int isample, i; + + // just fills the buffer + while (n--) + { + fsample=*(in1++); + if (fsample > 1.0) { fsample = 1.0; } + if (fsample < -1.0) { fsample = -1.0; } + isample=(short) (32767.0 * fsample); + *(x->x_audio_buf+x->x_audioin_position)=isample; + x->x_audioin_position=(x->x_audioin_position+1)%(2*MAX_AUDIO_PACKET_SIZE); + if ( x->x_audioin_position == 2*MAX_AUDIO_PACKET_SIZE-1 ) + { + post( "pdp_ffmpeg~ : reaching end of audio buffer" ); + } + fsample=*(in2++); + if (fsample > 1.0) { fsample = 1.0; } + if (fsample < -1.0) { fsample = -1.0; } + isample=(short) (32767.0 * fsample); + *(x->x_audio_buf+x->x_audioin_position)=isample; + x->x_audioin_position=(x->x_audioin_position+1)%(2*MAX_AUDIO_PACKET_SIZE); + if ( x->x_audioin_position == 2*MAX_AUDIO_PACKET_SIZE-1 ) + { + post( "pdp_ffmpeg~ : reaching end of audio buffer" ); + } + } + + return (w+5); +} + +static void pdp_ffmpeg_dsp(t_pdp_ffmpeg *x, t_signal **sp) +{ + dsp_add(pdp_ffmpeg_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + +static void pdp_ffmpeg_process(t_pdp_ffmpeg *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_ffmpeg_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_ffmpeg_process_yv12, pdp_ffmpeg_killpacket, &x->x_queue_id); + outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + outlet_float( x->x_outlet_nbframes_dropped, x->x_nbframes_dropped ); + 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_ffmpeg_process */ + break; + + } + } + +} + +static void pdp_ffmpeg_input_0(t_pdp_ffmpeg *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_ffmpeg_process(x); + } + +} + +static void pdp_ffmpeg_free(t_pdp_ffmpeg *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_ffmpeg_free_ressources(x); + if (x->x_img_resample_ctx) + { + img_resample_close(x->x_img_resample_ctx); + x->x_img_resample_ctx = NULL; + } + if (x->x_audio_resample_ctx) + { + audio_resample_close(x->x_audio_resample_ctx); + x->x_audio_resample_ctx = NULL; + } + av_free_static(); +} + +t_class *pdp_ffmpeg_class; + +void *pdp_ffmpeg_new(void) +{ + int i; + + t_pdp_ffmpeg *x = (t_pdp_ffmpeg *)pd_new(pdp_ffmpeg_class); + inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); + + x->x_outlet_streaming = outlet_new(&x->x_obj, &s_float); + x->x_outlet_nbframes = outlet_new(&x->x_obj, &s_float); + x->x_outlet_nbframes_dropped = outlet_new(&x->x_obj, &s_float); + + x->x_packet0 = -1; + x->x_queue_id = -1; + x->x_nbframes = 0; + x->x_nbframes_dropped = 0; + x->x_img_resample_ctx = NULL; + x->x_audio_resample_ctx = NULL; + x->x_secondcount = NULL; + x->x_nbvideostreams = 0; + x->x_audioin_position = 0; + + x->x_rdata = NULL; + x->x_buf1 = NULL; + x->x_buf2 = NULL; + x->x_avcontext = av_mallocz(sizeof(AVFormatContext)); + x->x_video_buffer = av_malloc( VIDEO_BUFFER_SIZE ); + if ( !x->x_video_buffer || !x->x_avcontext ) + { + post( "pdp_ffmpeg~ : severe error : could not allocate video structures." ); + return NULL; + } + + // activate codecs + av_register_all(); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_ffmpeg_tilde_setup(void) +{ + post( pdp_ffmpeg_version ); + pdp_ffmpeg_class = class_new(gensym("pdp_ffmpeg~"), (t_newmethod)pdp_ffmpeg_new, + (t_method)pdp_ffmpeg_free, sizeof(t_pdp_ffmpeg), 0, A_NULL); + + CLASS_MAINSIGNALIN(pdp_ffmpeg_class, t_pdp_ffmpeg, x_f ); + class_addmethod(pdp_ffmpeg_class, (t_method)pdp_ffmpeg_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_ffmpeg_class, (t_method)pdp_ffmpeg_dsp, gensym("dsp"), 0); + class_addmethod(pdp_ffmpeg_class, (t_method)pdp_ffmpeg_feed, gensym("feed"), A_SYMBOL, A_NULL); + class_addmethod(pdp_ffmpeg_class, (t_method)pdp_ffmpeg_starve, gensym("starve"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_form.c b/modules/pdp_form.c new file mode 100644 index 0000000..5b92fe8 --- /dev/null +++ b/modules/pdp_form.c @@ -0,0 +1,608 @@ +/* + * 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 geometric forms generator object for PDP + * It uses imlib2 for all graphical operations + */ + +/* Listening to : + * Culturcide - Bruce + * This Heat - Independence + */ + +#include "pdp.h" +#include "yuv.h" +#include <math.h> +#include <ctype.h> +#include <Imlib2.h> // imlib2 is required + + +/* forms type */ +enum _form_type +{ + IMLIB_LINE, + IMLIB_RECTANGLE, + IMLIB_ELLIPSE +}; +typedef enum _form_type t_form_type; + +typedef struct _form +{ + t_form_type type; + t_int n1,n2,n3,n4; // numerical coordinates or rays + t_int r,g,b; +} t_form; + + +#define DEFAULT_CAPACITY 10 + +static char *pdp_form_version = "pdp_form: version 0.1 : forms rendering object written by ydegoyon@free.fr "; + +typedef struct pdp_form_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_form *x_forms; + t_int x_nbforms; + t_int x_current; + t_int x_capacity; + + /* imlib data */ + Imlib_Image x_image; + +} t_pdp_form; + + /* add a line */ +static void pdp_form_line(t_pdp_form *x, t_symbol *s, int argc, t_atom *argv) +{ + if ( x->x_nbforms >= x->x_capacity ) + { + post( "pdp_form : sorry, maximum capacity has been reached... try resize" ); + return; + } + + if ( argc < 4 ) + { + post( "pdp_form : error in the number of arguments ( minimum is 4 )", argc ); + return; + } + if ( argv[0].a_type != A_FLOAT || argv[1].a_type != A_FLOAT || + argv[2].a_type != A_FLOAT || argv[3].a_type != A_FLOAT ) { + post( "pdp_form : add : wrong arguments" ); + return; + } + + x->x_forms[x->x_nbforms].type = IMLIB_LINE; + x->x_forms[x->x_nbforms].n1 = (int)argv[0].a_w.w_float; + x->x_forms[x->x_nbforms].n2 = (int)argv[1].a_w.w_float; + x->x_forms[x->x_nbforms].n3 = (int)argv[2].a_w.w_float; + x->x_forms[x->x_nbforms].n4 = (int)argv[3].a_w.w_float; + + if ( (argc>=5) && (argv[4].a_type == A_FLOAT) ) + { + x->x_forms[x->x_nbforms].r = (int)argv[4].a_w.w_float; + } + if ( (argc>=6) && (argv[5].a_type == A_FLOAT) ) + { + x->x_forms[x->x_nbforms].g = (int)argv[5].a_w.w_float; + } + if ( (argc>=7) && (argv[6].a_type == A_FLOAT) ) + { + x->x_forms[x->x_nbforms].b = (int)argv[6].a_w.w_float; + } + + post( "pdp_form : added line [%d,%d,%d,%d] @ %d (r=%d g=%d b=%d)", + x->x_forms[x->x_nbforms].n1, x->x_forms[x->x_nbforms].n2, + x->x_forms[x->x_nbforms].n3, x->x_forms[x->x_nbforms].n4, x->x_nbforms, + x->x_forms[x->x_nbforms].r, x->x_forms[x->x_nbforms].g, x->x_forms[x->x_nbforms].b ); + + if ( x->x_current == -1 ) x->x_current = x->x_nbforms; + x->x_nbforms++; + +} + + /* add a rectangle */ +static void pdp_form_rectangle(t_pdp_form *x, t_symbol *s, int argc, t_atom *argv) +{ + if ( x->x_nbforms >= x->x_capacity ) + { + post( "pdp_form : sorry, maximum capacity has been reached... try resize" ); + return; + } + + if ( argc < 4 ) + { + post( "pdp_form : error in the number of arguments ( minimum is 4 )", argc ); + return; + } + if ( argv[0].a_type != A_FLOAT || argv[1].a_type != A_FLOAT || + argv[2].a_type != A_FLOAT || argv[3].a_type != A_FLOAT ) { + post( "pdp_form : add : wrong arguments" ); + return; + } + + x->x_forms[x->x_nbforms].type = IMLIB_RECTANGLE; + x->x_forms[x->x_nbforms].n1 = (int)argv[0].a_w.w_float; + x->x_forms[x->x_nbforms].n2 = (int)argv[1].a_w.w_float; + x->x_forms[x->x_nbforms].n3 = (int)argv[2].a_w.w_float; + x->x_forms[x->x_nbforms].n4 = (int)argv[3].a_w.w_float; + + if ( (argc>=5) && (argv[4].a_type == A_FLOAT) ) + { + x->x_forms[x->x_nbforms].r = (int)argv[4].a_w.w_float; + } + if ( (argc>=6) && (argv[5].a_type == A_FLOAT) ) + { + x->x_forms[x->x_nbforms].g = (int)argv[5].a_w.w_float; + } + if ( (argc>=7) && (argv[6].a_type == A_FLOAT) ) + { + x->x_forms[x->x_nbforms].b = (int)argv[6].a_w.w_float; + } + + post( "pdp_form : added rectangle [%d,%d,%d,%d] @ %d (r=%d g=%d b=%d)", + x->x_forms[x->x_nbforms].n1, x->x_forms[x->x_nbforms].n2, + x->x_forms[x->x_nbforms].n3, x->x_forms[x->x_nbforms].n4, x->x_nbforms, + x->x_forms[x->x_nbforms].r, x->x_forms[x->x_nbforms].g, x->x_forms[x->x_nbforms].b ); + + if ( x->x_current == -1 ) x->x_current = x->x_nbforms; + x->x_nbforms++; + +} + + /* add an ellipse */ +static void pdp_form_ellipse(t_pdp_form *x, t_symbol *s, int argc, t_atom *argv) +{ + if ( x->x_nbforms >= x->x_capacity ) + { + post( "pdp_form : sorry, maximum capacity has been reached... try resize" ); + return; + } + + if ( argc < 4 ) + { + post( "pdp_form : error in the number of arguments ( minimum is 4 )", argc ); + return; + } + if ( argv[0].a_type != A_FLOAT || argv[1].a_type != A_FLOAT || + argv[2].a_type != A_FLOAT || argv[3].a_type != A_FLOAT ) { + post( "pdp_form : add : wrong arguments" ); + return; + } + + x->x_forms[x->x_nbforms].type = IMLIB_ELLIPSE; + x->x_forms[x->x_nbforms].n1 = (int)argv[0].a_w.w_float; + x->x_forms[x->x_nbforms].n2 = (int)argv[1].a_w.w_float; + x->x_forms[x->x_nbforms].n3 = (int)argv[2].a_w.w_float; + x->x_forms[x->x_nbforms].n4 = (int)argv[3].a_w.w_float; + + if ( (argc>=5) && (argv[4].a_type == A_FLOAT) ) + { + x->x_forms[x->x_nbforms].r = (int)argv[4].a_w.w_float; + } + if ( (argc>=6) && (argv[5].a_type == A_FLOAT) ) + { + x->x_forms[x->x_nbforms].g = (int)argv[5].a_w.w_float; + } + if ( (argc>=7) && (argv[6].a_type == A_FLOAT) ) + { + x->x_forms[x->x_nbforms].b = (int)argv[6].a_w.w_float; + } + + post( "pdp_form : added ellipse [%d,%d,%d,%d] @ %d (r=%d g=%d b=%d)", + x->x_forms[x->x_nbforms].n1, x->x_forms[x->x_nbforms].n2, + x->x_forms[x->x_nbforms].n3, x->x_forms[x->x_nbforms].n4, x->x_nbforms, + x->x_forms[x->x_nbforms].r, x->x_forms[x->x_nbforms].g, x->x_forms[x->x_nbforms].b ); + + if ( x->x_current == -1 ) x->x_current = x->x_nbforms; + x->x_nbforms++; + +} + +static void pdp_form_current(t_pdp_form *x, t_floatarg fcurrent ) +{ + if ( ( fcurrent >= 0 ) && ( fcurrent < x->x_nbforms ) ) + { + // post( "pdp_form : current item set to %d", x->x_current ); + x->x_current = fcurrent; + } +} + +static void pdp_form_x1(t_pdp_form *x, t_floatarg fx ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbforms ) ) + { + x->x_forms[x->x_current].n1 = fx; + } +} + +static void pdp_form_y1(t_pdp_form *x, t_floatarg fy ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbforms ) ) + { + x->x_forms[x->x_current].n2 = fy; + } +} + +static void pdp_form_x2(t_pdp_form *x, t_floatarg fx ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbforms ) ) + { + x->x_forms[x->x_current].n3 = fx; + } +} + +static void pdp_form_y2(t_pdp_form *x, t_floatarg fy ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbforms ) ) + { + x->x_forms[x->x_current].n4 = fy; + } +} + +static void pdp_form_r(t_pdp_form *x, t_floatarg fr ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbforms ) ) + { + x->x_forms[x->x_current].r = fr; + } +} + +static void pdp_form_g(t_pdp_form *x, t_floatarg fg ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbforms ) ) + { + x->x_forms[x->x_current].g = fg; + } +} + +static void pdp_form_b(t_pdp_form *x, t_floatarg fb ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbforms ) ) + { + x->x_forms[x->x_current].b = fb; + } +} + +static void pdp_form_clear(t_pdp_form *x ) +{ + x->x_nbforms = 0; +} + +static void pdp_form_delete(t_pdp_form *x, t_floatarg fnum ) +{ + t_int i; + char *lostword; + + if ( ( fnum>0 ) && ( fnum<=x->x_nbforms ) ) + { + for ( i=(int)fnum; i<x->x_nbforms; i++ ) + { + memcpy( &x->x_forms[ i-1 ], &x->x_forms[ i ], sizeof( t_form ) ); + } + x->x_nbforms--; + } +} + +static void pdp_form_resize(t_pdp_form *x, t_floatarg fnewsize ) +{ + t_form *forms; + t_int i, csize; + + if ( (int) fnewsize<=0 ) return; + + // allocate new structures + forms = (t_form*) getbytes( fnewsize*sizeof(t_form) ); + + for ( i=0; i<fnewsize; i++ ) + { + forms[i].r = forms[i].g = forms[i].b = 255; + } + + if ( fnewsize < x->x_nbforms ) + { + post( "pdp_form : new size is too small : texts lost !!" ); + csize = fnewsize; + } + else + { + csize = x->x_nbforms; + } + + // copy all values + for ( i=0; i<csize; i++ ) + { + memcpy( &forms[i], &x->x_forms[i], sizeof( t_form ) ); + } + + // free old structures + if ( x->x_forms ) freebytes( x->x_forms, x->x_capacity*sizeof(t_form) ); + + // set new structures + x->x_forms = forms; + x->x_nbforms = csize; + x->x_capacity = fnewsize; + if ( x->x_nbforms > 0 ) + { + x->x_current = 0; + } + else + { + x->x_current = -1; + } +} + +static void pdp_form_allocate(t_pdp_form *x) +{ + x->x_image = imlib_create_image( x->x_vwidth, x->x_vheight ); + if ( x->x_image == NULL ) + { + post( "pdp_form : severe error : could not allocate image !!" ); + } + imlib_context_set_image(x->x_image); +} + +static void pdp_form_free_ressources(t_pdp_form *x) +{ + if ( x->x_image != NULL ) imlib_free_image(); +} + +static void pdp_form_process_yv12(t_pdp_form *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 ti; + t_int px, py; + unsigned char y, u, v; + DATA32 *imdata; + DATA32 bgcolor; + short int *pY, *pU, *pV; + + if ( ( (int)(header->info.image.width) != x->x_vwidth ) || + ( (int)(header->info.image.height) != x->x_vheight ) ) + { + pdp_form_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_form_allocate(x); + } + + 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 ); + + // draw all texts + imlib_image_clear(); + imlib_context_set_direction(IMLIB_TEXT_TO_ANGLE); + imdata = imlib_image_get_data(); + bgcolor = imdata[0]; + + for (ti=0; ti<x->x_nbforms; ti++) + { + imlib_context_set_color( x->x_forms[ti].r, x->x_forms[ti].g, x->x_forms[ti].b, 255 ); + + switch ( x->x_forms[ti].type ) + { + case IMLIB_LINE : + imlib_image_draw_line( x->x_forms[ti].n1, x->x_forms[ti].n2, x->x_forms[ti].n3, x->x_forms[ti].n4, 1); + break; + + case IMLIB_RECTANGLE : + imlib_image_draw_rectangle( x->x_forms[ti].n1, x->x_forms[ti].n2, + x->x_forms[ti].n3-x->x_forms[ti].n1, x->x_forms[ti].n4-x->x_forms[ti].n2 ); + imlib_image_fill_rectangle( x->x_forms[ti].n1, x->x_forms[ti].n2, + x->x_forms[ti].n3-x->x_forms[ti].n1, x->x_forms[ti].n4-x->x_forms[ti].n2 ); + break; + + case IMLIB_ELLIPSE : + imlib_image_draw_ellipse( x->x_forms[ti].n1, x->x_forms[ti].n2, x->x_forms[ti].n3, x->x_forms[ti].n4 ); + imlib_image_fill_ellipse( x->x_forms[ti].n1, x->x_forms[ti].n2, x->x_forms[ti].n3, x->x_forms[ti].n4 ); + break; + } + } + + pY = newdata; + pV = newdata+x->x_vsize; + pU = newdata+x->x_vsize+(x->x_vsize>>2); + for ( py=0; py<x->x_vheight; py++ ) + { + for ( px=0; px<x->x_vwidth; px++ ) + { + if ( imdata[py*x->x_vwidth+px] != bgcolor ) + { + y = yuv_RGBtoY(imdata[py*x->x_vwidth+px]); + u = yuv_RGBtoU(imdata[py*x->x_vwidth+px]); + v = yuv_RGBtoV(imdata[py*x->x_vwidth+px]); + + *(pY) = y<<7; + if ( (px%2==0) && (py%2==0) ) + { + *(pV) = (v-128)<<8; + *(pU) = (u-128)<<8; + } + } + pY++; + if ( (px%2==0) && (py%2==0) ) + { + pV++;pU++; + } + } + } + + return; +} + +static void pdp_form_sendpacket(t_pdp_form *x) +{ + /* delete source 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_form_process(t_pdp_form *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_form_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_form_process_yv12, pdp_form_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_form_process */ + break; + + } + } + +} + +static void pdp_form_input_0(t_pdp_form *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_form_process(x); + + } + +} + +static void pdp_form_free(t_pdp_form *x) +{ + int i; + + pdp_form_free_ressources(x); + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_form_class; + +void *pdp_form_new(void) +{ + int i; + + t_pdp_form *x = (t_pdp_form *)pd_new(pdp_form_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("current")); + 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("y1")); + 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("y2")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("r")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("g")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("b")); + + 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_image = NULL; + + x->x_capacity = DEFAULT_CAPACITY; + + x->x_forms = (t_form *) getbytes( x->x_capacity*sizeof(t_form) ); + + for ( i=0; i<x->x_capacity; i++ ) + { + x->x_forms[i].r = x->x_forms[i].g = x->x_forms[i].b = 255; + } + + x->x_nbforms = 0; + x->x_current = -1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_form_setup(void) +{ + + post( pdp_form_version ); + pdp_form_class = class_new(gensym("pdp_form"), (t_newmethod)pdp_form_new, + (t_method)pdp_form_free, sizeof(t_pdp_form), 0, A_NULL); + + class_addmethod(pdp_form_class, (t_method)pdp_form_input_0, gensym("pdp"), + A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_line, gensym("line"), A_GIMME, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_rectangle, gensym("rectangle"), A_GIMME, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_ellipse, gensym("ellipse"), A_GIMME, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_current, gensym("current"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_x1, gensym("x1"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_y1, gensym("y1"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_x2, gensym("x2"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_y2, gensym("y2"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_r, gensym("r"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_g, gensym("g"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_b, gensym("b"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_clear, gensym("clear"), A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_delete, gensym("delete"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_form_class, (t_method)pdp_form_resize, gensym("resize"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_i.c b/modules/pdp_i.c new file mode 100644 index 0000000..4ea749c --- /dev/null +++ b/modules/pdp_i.c @@ -0,0 +1,454 @@ + +/* + * 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 streaming receiver + * It receives PDP packets sent by a pdp_o object + */ + +#include <sys/types.h> +#include <string.h> +#include <sys/socket.h> +#include <errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/time.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <bzlib.h> // bz2 decompression routines +#include "pdp.h" +#include "pdp_streaming.h" + +typedef void (*t_fdpollfn)(void *ptr, int fd); +extern void sys_rmpollfn(int fd); +extern void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr); + +#define SOCKET_ERROR -1 +#define INPUT_BUFFER_SIZE 1048578 /* 1 M */ + +/* time-out used for select() call */ +static struct timeval ztout; + +static char *pdp_i_version = "pdp_i : a video stream receiver, written by ydegoyon@free.fr"; + +extern void sys_sockerror(char *s); + +void pdp_i_closesocket(int fd) +{ + if ( close(fd) < 0 ) + { + perror( "close" ); + } + else + { + post( "pdp_i : closed socket : %d", fd ); + } +} + +int pdp_i_setsocketoptions(int sockfd) +{ + int sockopt = 1; + if (setsockopt(sockfd, SOL_TCP, TCP_NODELAY, (const char*) &sockopt, sizeof(int)) < 0) + { + post("pdp_i : setsockopt TCP_NODELAY failed"); + perror( "setsockopt" ); + return -1; + } + else + { + post("pdp_i : TCP_NODELAY set"); + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int)) < 0) + { + post("pdp_i : setsockopt SO_REUSEADDR failed"); + perror( "setsockopt" ); + return -1; + } + else + { + post("pdp_i : setsockopt SO_REUSEADDR done."); + } + return 0; +} + +/* ------------------------ pdp_i ----------------------------- */ + +static t_class *pdp_i_class; + +typedef struct _pdp_i +{ + t_object x_obj; + t_int x_socket; + t_outlet *x_connection_status; + t_outlet *x_frames; + t_outlet *x_connectionip; + t_outlet *x_pdp_output; + t_int x_serversocket; + t_int x_framesreceived; // total number of frames received + + void *x_inbuffer; /* accumulation buffer for incoming frames */ + t_int x_inwriteposition; + t_int x_inbuffersize; + + /* PDP data structures */ + t_int x_packet; + t_pdp *x_header; + t_int x_vheight; + t_int x_vwidth; + t_int x_vsize; + t_int x_psize; + t_int x_hsize; + t_int x_bsize; + t_int x_bzsize; + short int *x_data; + unsigned char *x_hdata; // huffman coded data + unsigned char *x_ddata; // decompressed data + unsigned short *x_bdata; // previous data + +} t_pdp_i; + + /* huffman decoding */ +static int pdp_i_huffman(t_pdp_i *x, char *source, char *dest, t_int size, t_int *dsize) +{ + char *pcount=source; + char *pvalue=(source+1); + + *dsize=0; + while ( pcount < (source+size) ) + { + while ( (*pcount) > 0 ) + { + *(dest++)=*(pvalue); + *pcount-=1; + *dsize+=1; + } + pcount+=2; + pvalue+=2; + } + + // post( "pdp_i : dsize=%d", *dsize ); + return *dsize; +} + +static void pdp_i_free_ressources(t_pdp_i *x) +{ + if ( x->x_ddata ) freebytes( x->x_ddata, x->x_psize ); + if ( x->x_hdata ) freebytes( x->x_hdata, x->x_hsize ); + if ( x->x_bdata ) freebytes( x->x_hdata, x->x_bsize ); +} + +static void pdp_i_allocate(t_pdp_i *x) +{ + x->x_psize = x->x_vsize + (x->x_vsize>>1); + x->x_hsize = (x->x_vsize + (x->x_vsize>>1)); + x->x_bsize = (x->x_vsize + (x->x_vsize>>1))*sizeof(unsigned short); + x->x_ddata = (unsigned char*) getbytes(x->x_psize); + x->x_hdata = (unsigned char*) getbytes(x->x_hsize); + x->x_bdata = (unsigned short*) getbytes(x->x_bsize); + if ( !x->x_ddata || !x->x_hdata ) + { + post( "pdp_i : severe error : could not allocate buffer" ); + } +} + +static void pdp_i_recv(t_pdp_i *x) +{ + int ret, i; + t_hpacket *pheader; + + if ( ( ret = recv(x->x_socket, (void*) (x->x_inbuffer + x->x_inwriteposition), + (size_t)((x->x_inbuffersize-x->x_inwriteposition)), + MSG_NOSIGNAL) ) < 0 ) + { + post( "pdp_i : receive error" ); + perror( "recv" ); + return; + } + else + { + // post( "pdp_i : received %d bytes at %d on %d ( up to %d)", + // ret, x->x_inwriteposition, x->x_socket, + // x->x_inbuffersize-x->x_inwriteposition*sizeof( unsigned long) ); + + if ( ret == 0 ) + { + /* peer has reset connection */ + outlet_float( x->x_connection_status, 0 ); + pdp_i_closesocket( x->x_socket ); + sys_rmpollfn(x->x_socket); + x->x_socket = -1; + } + else + { + // check we don't overflow input buffer + if ( x->x_inwriteposition+ret >= x->x_inbuffersize ) + { + // post( "pdp_i : too much input...resetting" ); + x->x_inwriteposition=0; + return; + } + x->x_inwriteposition += ret; + if ( pheader = (t_hpacket*) strstr( (char*) x->x_inbuffer, PDP_PACKET_START ) ) + { + // check if a full packet is present + if ( x->x_inwriteposition >= (int)((char*)pheader - (char*)(x->x_inbuffer)) + (int)sizeof(t_hpacket) + (int)pheader->clength ) + { + if ( ( x->x_vwidth != pheader->width ) || + ( x->x_vheight != pheader->height ) ) + { + pdp_i_free_ressources(x); + x->x_vheight = pheader->height; + x->x_vwidth = pheader->width; + x->x_vsize = x->x_vheight*x->x_vwidth; + pdp_i_allocate(x); + post( "pdp_i : allocated buffers : vsize=%d : hsize=%d", x->x_vsize, x->x_hsize ); + } + + x->x_packet = pdp_packet_new_image_YCrCb( x->x_vwidth, x->x_vheight ); + x->x_header = pdp_packet_header(x->x_packet); + x->x_data = (short int *)pdp_packet_data(x->x_packet); + memcpy( x->x_data, x->x_bdata, x->x_bsize ); + + // post( "pdp_i : decompress %d in %d bytes", pheader->clength, x->x_hsize ); + x->x_bzsize = x->x_hsize; + + if ( ( ret = BZ2_bzBuffToBuffDecompress( (char*)x->x_hdata, + &x->x_bzsize, + (char *) pheader+sizeof(t_hpacket), + pheader->clength, + 0, 0 ) ) == BZ_OK ) + { + // post( "pdp_i : bz2 decompression (%d)->(%d)", pheader->clength, x->x_bzsize ); + + switch( pheader->encoding ) + { + case REGULAR : + memcpy( x->x_ddata, x->x_hdata, x->x_bzsize ); + break; + + case HUFFMAN : + pdp_i_huffman( x, x->x_hdata, x->x_ddata, x->x_bzsize, &x->x_psize ); + break; + } + + for ( i=0; i<x->x_vsize; i++ ) + { + if ( !strcmp( pheader->tag, PDP_PACKET_TAG ) ) + { + x->x_data[i] = x->x_ddata[i]<<7; + } + else + { + if ( x->x_ddata[i] != 0 ) + { + x->x_data[i] = x->x_ddata[i]<<7; + } + } + } + for ( i=x->x_vsize; i<(x->x_vsize+(x->x_vsize>>1)); i++ ) + { + if ( !strcmp( pheader->tag, PDP_PACKET_TAG ) ) + { + x->x_data[i] = (x->x_ddata[i])<<8; + } + else + { + if ( x->x_ddata[i] != 0 ) + { + x->x_data[i] = (x->x_ddata[i])<<8; + } + } + } + + x->x_header->info.image.encoding = PDP_IMAGE_YV12; + x->x_header->info.image.width = x->x_vwidth; + x->x_header->info.image.height = x->x_vheight; + + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet); + // post( "pdp_i : propagate packet : %d", x->x_packet ); + outlet_float( x->x_frames, ++x->x_framesreceived ); + } + else + { + post( "pdp_i : bz2 decompression failed (ret=%d)", ret ); + } + + memcpy( x->x_bdata, x->x_data, x->x_bsize ); + + // roll buffer + x->x_inwriteposition -= (int)((char*)pheader-(char*)(x->x_inbuffer)) + sizeof(t_hpacket) + pheader->clength; + memcpy( x->x_inbuffer, pheader+sizeof(t_hpacket) + pheader->clength, x->x_inwriteposition ); + } + } + } + + } +} + +static void pdp_i_acceptconnection(t_pdp_i *x) +{ + struct sockaddr_in incomer_address; + int sockaddrl = (int) sizeof( struct sockaddr ); + + int fd = accept(x->x_serversocket, (struct sockaddr*)&incomer_address, &sockaddrl ); + + if (fd < 0) { + post("pdp_i : accept failed"); + return; + } + + if ( x->x_socket > 0 ) + { + post( "pdp_i : accepting a new source : %s", inet_ntoa( incomer_address.sin_addr) ); + pdp_i_closesocket( x->x_socket ); + sys_rmpollfn(x->x_socket); + outlet_float( x->x_connection_status, 0 ); + } + + x->x_socket = fd; + x->x_framesreceived = 0; + sys_addpollfn(x->x_socket, (t_fdpollfn)pdp_i_recv, x); + post("pdp_i : new source : %s.", inet_ntoa( incomer_address.sin_addr )); + outlet_float( x->x_connection_status, 1 ); + outlet_float( x->x_frames, x->x_framesreceived ); + outlet_symbol( x->x_connectionip, gensym( inet_ntoa( incomer_address.sin_addr) ) ); + +} + + +static int pdp_i_startservice(t_pdp_i* x, int portno) +{ + struct sockaddr_in server; + int sockfd; + + /* create a socket */ + sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd < 0) + { + sys_sockerror("socket"); + return (0); + } + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + + /* assign server port number */ + server.sin_port = htons((u_short)portno); + post("listening to port number %d", portno); + + pdp_i_setsocketoptions(sockfd); + + /* name the socket */ + if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) { + sys_sockerror("bind"); + pdp_i_closesocket(sockfd); + return (0); + } + + if (listen(sockfd, 5) < 0) { + sys_sockerror("listen"); + pdp_i_closesocket(sockfd); + } + else + { + x->x_serversocket = sockfd; + sys_addpollfn(x->x_serversocket, (t_fdpollfn)pdp_i_acceptconnection, x); + } + + return 1; +} + +static void pdp_i_free(t_pdp_i *x) +{ + post( "pdp_i : free %x", x ); + if (x->x_serversocket > 0) { + post( "pdp_i : closing server socket" ); + pdp_i_closesocket(x->x_serversocket); + sys_rmpollfn(x->x_serversocket); + x->x_serversocket = -1; + } + if (x->x_socket > 0) { + post( "pdp_i : closing socket" ); + pdp_i_closesocket(x->x_socket); + sys_rmpollfn(x->x_socket); + x->x_socket = -1; + } + if ( x->x_inbuffer ) freebytes( x->x_inbuffer, x->x_inbuffersize ); + pdp_i_free_ressources( x ); +} + +static void *pdp_i_new(t_floatarg fportno) +{ + t_pdp_i *x; + int i; + + if ( fportno <= 0 || fportno > 65535 ) + { + post( "pdp_i : error : wrong portnumber : %d", (int)fportno ); + return NULL; + } + + x = (t_pdp_i *)pd_new(pdp_i_class); + x->x_pdp_output = outlet_new(&x->x_obj, &s_anything); + x->x_connection_status = outlet_new(&x->x_obj, &s_float); + x->x_frames = outlet_new(&x->x_obj, &s_float); + x->x_connectionip = outlet_new(&x->x_obj, &s_symbol); + + x->x_serversocket = -1; + + x->x_inbuffersize = INPUT_BUFFER_SIZE; + x->x_inbuffer = (char*) getbytes( x->x_inbuffersize ); + memset( x->x_inbuffer, 0x0, INPUT_BUFFER_SIZE ); + + if ( !x->x_inbuffer ) + { + post( "pdp_i : could not allocate buffer." ); + return NULL; + } + + x->x_inwriteposition = 0; + x->x_socket = -1; + x->x_packet = -1; + x->x_ddata = NULL; + x->x_hdata = NULL; + x->x_bdata = NULL; + + ztout.tv_sec = 0; + ztout.tv_usec = 0; + + post( "pdp_i : starting service on port %d", (int)fportno ); + pdp_i_startservice(x, (int)fportno); + + return (x); +} + + +void pdp_i_setup(void) +{ + post( pdp_i_version ); + pdp_i_class = class_new(gensym("pdp_i"), + (t_newmethod) pdp_i_new, (t_method) pdp_i_free, + sizeof(t_pdp_i), CLASS_NOINLET, A_DEFFLOAT, A_DEFFLOAT, A_NULL); + +} diff --git a/modules/pdp_imgloader.c b/modules/pdp_imgloader.c new file mode 100644 index 0000000..13cae1e --- /dev/null +++ b/modules/pdp_imgloader.c @@ -0,0 +1,300 @@ +/* + * 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 loads an image from a file and blends it with a video + * It uses imlib2 for all graphical operations + */ + +#include "pdp.h" +#include "yuv.h" +#include <math.h> +#include <ctype.h> +#include <Imlib2.h> // imlib2 is required + +static char *pdp_imgloader_version = "pdp_imgloader: version 0.1 : image loading object written by ydegoyon@free.fr "; + +typedef struct pdp_imgloader_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_int x_xoffset; // x offset of the image + t_int x_yoffset; // y offset of the image + + /* imlib data */ + Imlib_Image x_image; + DATA32 *x_imdata; + t_int x_iwidth; + t_int x_iheight; + + t_float x_blend; + +} t_pdp_imgloader; + + /* load an image */ +static void pdp_imgloader_load(t_pdp_imgloader *x, t_symbol *filename, t_floatarg fx, t_floatarg fy) +{ + Imlib_Load_Error imliberr; + + post( "pdp_imgloader : loading : %s", filename->s_name ); + + if ( x->x_image != NULL ) + { + imlib_free_image(); + } + x->x_image = imlib_load_image_with_error_return( filename->s_name, &imliberr ); + if ( imliberr != IMLIB_LOAD_ERROR_NONE ) + { + post( "pdp_imgloader : severe error : could not load image (err=%d)!!", imliberr ); + x->x_image = NULL; + return; + } + imlib_context_set_image(x->x_image); + x->x_imdata = imlib_image_get_data(); + x->x_iwidth = imlib_image_get_width(); + x->x_iheight = imlib_image_get_height(); + post( "pdp_imgloader : loaded : %s (%dx%d)", filename->s_name, x->x_iwidth, x->x_iheight ); + x->x_xoffset = (int) fx; + x->x_yoffset = (int) fy; +} + +static void pdp_imgloader_xoffset(t_pdp_imgloader *x, t_floatarg fx ) +{ + x->x_xoffset = (int) fx; +} + +static void pdp_imgloader_yoffset(t_pdp_imgloader *x, t_floatarg fy ) +{ + x->x_yoffset = (int) fy; +} + +static void pdp_imgloader_blend(t_pdp_imgloader *x, t_floatarg fblend ) +{ + if ( ( fblend > 0.0 ) && ( fblend < 1.0 ) ) + { + x->x_blend = fblend; + } +} + +static void pdp_imgloader_clear(t_pdp_imgloader *x ) +{ + if ( x->x_image != NULL ) + { + imlib_free_image(); + } + x->x_image = NULL; +} + +static void pdp_imgloader_process_yv12(t_pdp_imgloader *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 px, py; + t_float alpha, factor; + unsigned char y, u, v; + short int *pY, *pU, *pV; + + if ( ( (int)(header->info.image.width) != x->x_vwidth ) || + ( (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; + } + + 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 ); + + pY = newdata; + pV = newdata+x->x_vsize; + pU = newdata+x->x_vsize+(x->x_vsize>>2); + for ( py=0; py<x->x_vheight; py++ ) + { + for ( px=0; px<x->x_vwidth; px++ ) + { + if ( ( x->x_image != NULL ) + && (px >= x->x_xoffset) && ( px < x->x_xoffset + x->x_iwidth ) + && (py >= x->x_yoffset) && ( py < x->x_yoffset + x->x_iheight ) + ) + { + y = yuv_RGBtoY(x->x_imdata[(py-x->x_yoffset)*x->x_iwidth+(px-x->x_xoffset)]); + u = yuv_RGBtoU(x->x_imdata[(py-x->x_yoffset)*x->x_iwidth+(px-x->x_xoffset)]); + v = yuv_RGBtoV(x->x_imdata[(py-x->x_yoffset)*x->x_iwidth+(px-x->x_xoffset)]); + + + if ( imlib_image_has_alpha() ) + { + alpha = (x->x_imdata[(py-x->x_yoffset)*x->x_iwidth+(px-x->x_xoffset)] >> 24)/255; + } + else + { + alpha = 1.0; + } + factor = x->x_blend*alpha; + + *(pY) = (int)((1-factor)*(*(pY)) + factor*(y<<7)); + if ( (px%2==0) && (py%2==0) ) + { + *(pV) = (int)((1-factor)*(*(pV)) + factor*((v-128)<<8)); + *(pU) = (int)((1-factor)*(*(pU)) + factor*((u-128)<<8)); + } + } + pY++; + if ( (px%2==0) && (py%2==0) ) + { + pV++;pU++; + } + } + } + + return; +} + +static void pdp_imgloader_sendpacket(t_pdp_imgloader *x) +{ + /* delete source 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_imgloader_process(t_pdp_imgloader *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_imgloader_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_imgloader_process_yv12, pdp_imgloader_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_imgloader_process */ + break; + + } + } + +} + +static void pdp_imgloader_input_0(t_pdp_imgloader *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_imgloader_process(x); + + } + +} + +static void pdp_imgloader_free(t_pdp_imgloader *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_imgloader_class; + +void *pdp_imgloader_new(void) +{ + int i; + + t_pdp_imgloader *x = (t_pdp_imgloader *)pd_new(pdp_imgloader_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("xoffset")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("yoffset")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("blend")); + + 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_image = NULL; + + x->x_blend = 1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_imgloader_setup(void) +{ + + post( pdp_imgloader_version ); + pdp_imgloader_class = class_new(gensym("pdp_imgloader"), (t_newmethod)pdp_imgloader_new, + (t_method)pdp_imgloader_free, sizeof(t_pdp_imgloader), 0, A_NULL); + + class_addmethod(pdp_imgloader_class, (t_method)pdp_imgloader_input_0, gensym("pdp"), + A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_imgloader_class, (t_method)pdp_imgloader_load, gensym("load"), A_SYMBOL, A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_imgloader_class, (t_method)pdp_imgloader_clear, gensym("clear"), A_NULL); + class_addmethod(pdp_imgloader_class, (t_method)pdp_imgloader_xoffset, gensym("xoffset"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_imgloader_class, (t_method)pdp_imgloader_yoffset, gensym("yoffset"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_imgloader_class, (t_method)pdp_imgloader_blend, gensym("blend"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_imgsaver.c b/modules/pdp_imgsaver.c new file mode 100644 index 0000000..5f11098 --- /dev/null +++ b/modules/pdp_imgsaver.c @@ -0,0 +1,340 @@ +/* + * 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 saves a snaphot to a file + * Image type is specified by extension + * It uses imlib2 for all graphical operations + */ + +#include "pdp.h" +#include "yuv.h" +#include <math.h> +#include <ctype.h> +#include <pthread.h> +#include <Imlib2.h> // imlib2 is required + +static char *pdp_imgsaver_version = "pdp_imgsaver: version 0.1 : image snapshot object written by ydegoyon@free.fr "; + +typedef struct pdp_imgsaver_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_int x_save_pending; + + /* imlib data */ + Imlib_Image x_image; + DATA32 *x_imdata; + t_int x_iwidth; + t_int x_iheight; + + t_symbol *x_filename; + short int *x_datas; + pthread_t x_savechild; /* thread id for the saving child */ + +} t_pdp_imgsaver; + + /* do save the image */ +static void *pdp_imgsaver_do_save(void *tdata) +{ + Imlib_Load_Error imliberr; + t_pdp_imgsaver *x = (t_pdp_imgsaver*) tdata; + t_int px, py; + short int *pY, *pU, *pV; + unsigned char y, u, v; + + if ( ( x->x_vwidth == 0 ) || ( x->x_vheight == 0 ) ) + { + post( "pdp_imgsaver : tried to save an image but no video is playing" ); + x->x_save_pending = 0; + x->x_filename = NULL; + return NULL; + } + + x->x_image = imlib_create_image( x->x_vwidth, x->x_vheight ); + if ( x->x_image == NULL ) + { + post( "pdp_imgsaver : severe error : could not allocate image !!" ); + x->x_save_pending = 0; + x->x_filename = NULL; + return NULL; + } + imlib_context_set_image(x->x_image); + + x->x_imdata = imlib_image_get_data(); + x->x_iwidth = imlib_image_get_width(); + x->x_iheight = imlib_image_get_height(); + + pY = x->x_datas; + pV = x->x_datas+x->x_vsize; + pU = x->x_datas+x->x_vsize+(x->x_vsize>>2); + for ( py=0; py<x->x_iheight; py++ ) + { + for ( px=0; px<x->x_iwidth; px++ ) + { + y = *(pY)>>7; + v = (*(pV)>>8)+128; + u = (*(pU)>>8)+128; + x->x_imdata[ py*x->x_iwidth + px ] = yuv_YUVtoRGB( y, u, v ); + pY++; + if ( (px%2==0) && (py%2==0) ) + { + pV++;pU++; + } + } + } + + post( "pdp_imgsaver : saving image to : %s", x->x_filename->s_name ); + imlib_save_image_with_error_return(x->x_filename->s_name, &imliberr ); + if ( imliberr != IMLIB_LOAD_ERROR_NONE ) + { + post( "pdp_imgsaver : severe error : could not save image (err=%d)!!", imliberr ); + } + else + { + post( "pdp_imgsaver : saved to : %s", x->x_filename->s_name ); + } + + if ( x->x_image != NULL ) + { + imlib_free_image(); + x->x_image = NULL; + } + + x->x_save_pending = 0; + x->x_filename = NULL; + + post( "pdp_imgsaver : saving thread %d finished", x->x_savechild ); + x->x_savechild = 0; + + return NULL; + +} + + /* launched the thread to save the image */ +static void pdp_imgsaver_save(t_pdp_imgsaver *x, t_symbol *filename) +{ + pthread_attr_t save_child_attr; + + if ( x->x_save_pending ) + { + post( "pdp_imgsaver : a save operation is currently running...retry later" ); + return; + } + x->x_save_pending = 1; + x->x_filename = filename; + if ( x->x_image != NULL ) + { + imlib_free_image(); + x->x_image = NULL; + } + + // launch saving thread + if ( pthread_attr_init( &save_child_attr ) < 0 ) + { + post( "pdp_imgsaver : could not launch save thread" ); + perror( "pthread_attr_init" ); + return; + } + if ( pthread_attr_setdetachstate( &save_child_attr, PTHREAD_CREATE_DETACHED ) < 0 ) { + post( "pdp_imgsaver : could not launch save thread" ); + perror( "pthread_attr_setdetachstate" ); + return; + } + if ( pthread_create( &x->x_savechild, &save_child_attr, pdp_imgsaver_do_save, x ) < 0 ) { + post( "pdp_imgsaver : could not launch save thread" ); + perror( "pthread_create" ); + return; + } + else + { + post( "pdp_imgsaver : saving thread %d launched", (int)x->x_savechild ); + } +} + +static void pdp_imgsaver_allocate(t_pdp_imgsaver *x) +{ + x->x_datas = (short int *)getbytes((( x->x_vsize + (x->x_vsize>>1))<<1)); +} + +static void pdp_imgsaver_free_ressources(t_pdp_imgsaver *x) +{ + if ( x->x_datas ) freebytes( x->x_datas, (( x->x_vsize + (x->x_vsize>>1))<<1)); +} + +static void pdp_imgsaver_process_yv12(t_pdp_imgsaver *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 px, py; + t_float alpha, factor; + unsigned char y, u, v; + short int *pY, *pU, *pV; + + if ( ( (int)(header->info.image.width) != x->x_vwidth ) || + ( (int)(header->info.image.height) != x->x_vheight ) ) + { + pdp_imgsaver_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_imgsaver_allocate(x); + } + + 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 ); + if ( !x->x_save_pending ) + { + memcpy( x->x_datas, data, (x->x_vsize+(x->x_vsize>>1))<<1 ); + } + + return; +} + +static void pdp_imgsaver_sendpacket(t_pdp_imgsaver *x) +{ + /* delete source 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_imgsaver_process(t_pdp_imgsaver *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_imgsaver_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_imgsaver_process_yv12, pdp_imgsaver_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_imgsaver_process */ + break; + + } + } + +} + +static void pdp_imgsaver_input_0(t_pdp_imgsaver *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_imgsaver_process(x); + + } + +} + +static void pdp_imgsaver_free(t_pdp_imgsaver *x) +{ + int i; + + pdp_imgsaver_free_ressources(x); + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_imgsaver_class; + +void *pdp_imgsaver_new(void) +{ + int i; + + t_pdp_imgsaver *x = (t_pdp_imgsaver *)pd_new(pdp_imgsaver_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_datas = NULL; + x->x_queue_id = -1; + x->x_image = NULL; + + x->x_vwidth = 0; + x->x_vheight = 0; + + x->x_save_pending = 0; + x->x_filename = NULL; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_imgsaver_setup(void) +{ + + post( pdp_imgsaver_version ); + pdp_imgsaver_class = class_new(gensym("pdp_imgsaver"), (t_newmethod)pdp_imgsaver_new, + (t_method)pdp_imgsaver_free, sizeof(t_pdp_imgsaver), 0, A_NULL); + + class_addmethod(pdp_imgsaver_class, (t_method)pdp_imgsaver_input_0, gensym("pdp"), + A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_imgsaver_class, (t_method)pdp_imgsaver_save, gensym("save"), A_SYMBOL, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_intrusion.c b/modules/pdp_intrusion.c new file mode 100644 index 0000000..26c9ead --- /dev/null +++ b/modules/pdp_intrusion.c @@ -0,0 +1,428 @@ +/* + * 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 adaptation of hologram effect from effectv + * but, in these paranoid times, i found it funnier to rename it as intrusion + * because it can detect moving objects ( targets ?? ) + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define NB_IMAGES 4 +#define MAGIC_THRESHOLD 10 +static unsigned int fastrand_val; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + +static char *pdp_intrusion_version = "pdp_intrusion: version 0.1, inspired by hologram from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_intrusion_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; + + unsigned int x_noisepattern[256]; // noise pattern + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + unsigned int x_encoding; + short int *x_images[NB_IMAGES]; + t_int x_rtimage; + short int *x_diff; + short int *x_bdata; + int x_threshold; + int x_phase; + int x_loopcount; + int x_threshfreq; + +} t_pdp_intrusion; + +/* check if there is a real difference with background image */ +short int *pdp_intrusion_diff(t_pdp_intrusion *x, short int *src) +{ + int i; + int Y; + int Yb; + short int *p=NULL; + short int *pb=NULL; + short int *r=NULL; + int v; + + p = src; + pb = x->x_bdata; + r = x->x_diff; + for(i=0; i<(x->x_vsize); i++) { + Y = (*p); + Yb = (*pb); + *r = (Yb - Y); + p++; pb++; + r++; + } + + return x->x_diff; +} + +static void pdp_intrusion_threshold(t_pdp_intrusion *x, t_floatarg fthreshold ) +{ + if ( fthreshold>0 && fthreshold<255 ) + { + x->x_threshold = ((int)fthreshold ) << 8; + } +} + +static void pdp_intrusion_background(t_pdp_intrusion *x ) +{ + int i, j; + + if ( ( x->x_images[0] == NULL ) || + ( x->x_images[1] == NULL ) || + ( x->x_images[2] == NULL ) || + ( x->x_images[3] == NULL ) ) + { + post( "pdp_intrusion_background : no images available !! " ); + return; + } + post( "pdp_intrusion : setting background" ); + + memcpy( x->x_bdata, x->x_images[0], (( x->x_vsize + (x->x_vsize>>1))<<1)); + + for( i=1; i<NB_IMAGES; i++ ) + { + for ( j=0; j<(x->x_vsize+(x->x_vsize>>1)); j++ ) + { + x->x_bdata[j] = (x->x_bdata[j]&x->x_images[i][j])+((x->x_bdata[j]^x->x_images[i][j])>>1); + } + } +} + +static void pdp_intrusion_allocate(t_pdp_intrusion *x, t_int newsize) +{ + int i; + + for ( i=0; i<NB_IMAGES; i++ ) + { + if ( x->x_images[i] != NULL ) + { + freebytes( x->x_images[i], (x->x_vsize + (x->x_vsize>>1))<<1 ); + } + } + if ( x->x_diff != NULL ) + { + freebytes( x->x_diff, (x->x_vsize + (x->x_vsize>>1))<<1 ); + } + if ( x->x_bdata ) freebytes( x->x_bdata, (( x->x_vsize + (x->x_vsize>>1))<<1)); + + x->x_vsize = newsize; + for ( i=0; i<NB_IMAGES; i++ ) + { + x->x_images[i] = (short int*) getbytes((x->x_vsize + (x->x_vsize>>1))<<1); + } + x->x_diff = (short int*) getbytes((x->x_vsize + (x->x_vsize>>1))<<1); + x->x_bdata = (short int *)getbytes((( x->x_vsize + (x->x_vsize>>1))<<1)); +} + +static void pdp_intrusion_process_yv12(t_pdp_intrusion *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); + int i; + + unsigned int totalnbpixels; + unsigned int u_offset; + unsigned int v_offset; + unsigned int totnbpixels; + int px, py; + short int *diff; + short int *sy, *su, *sv, t; + short int *sby, *sbu, *sbv; + short int *sny, *snu, *snv; + int Y=0, U=0, V=0; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_intrusion_allocate(x, header->info.image.width*header->info.image.height ); + post( "pdp_intrusion : reallocating buffers" ); + } + + 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_encoding = header->info.image.encoding; + + totalnbpixels = x->x_vsize; + u_offset = x->x_vsize; + v_offset = x->x_vsize + (x->x_vsize>>2); + totnbpixels = x->x_vsize + (x->x_vsize>>1); + + newheader->info.image.encoding = x->x_encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + /* copy images if necessary */ + memcpy( x->x_images[x->x_rtimage], data, (( x->x_vsize + (x->x_vsize>>1))<<1)); + x->x_rtimage=(x->x_rtimage+1)%4; + + if ( !x->x_bdata ) return; + + /* check pixels which has changed */ + diff = pdp_intrusion_diff(x, data); + + sy = data; + su = (data+x->x_vsize); + sv = (data+x->x_vsize+(x->x_vsize>>2)); + sby = x->x_bdata; + sbu = (x->x_bdata+x->x_vsize); + sbv = (x->x_bdata+x->x_vsize+(x->x_vsize>>2)); + sny = newdata; + snu = (newdata+x->x_vsize); + snv = (newdata+x->x_vsize+(x->x_vsize>>2)); + + for(py=1; py<x->x_vheight; py++) + { + if(((py+x->x_phase) & 0x7f)<0x58) + { + for(px=0; px<x->x_vwidth; px++) + { + if ( sv >= data + x->x_vsize + (x->x_vsize>>1 ) ) break; + if(*diff > x->x_threshold) + { + t = x->x_noisepattern[inline_fastrand()>>24]; + Y = (*sy) + t; + U = (*su) + t; + V = (*sv) + t; + Y = (Y>>1)-100; + U = (U>>1)-100; + V = V>>2; + Y += ((*sby)>>1)+((rand()%255)<<8); + U += ((*sbu)>>1)+((rand()%255)<<8); + V += ((*sbv)>>1)+((rand()%255)<<8); + // clipping + if((Y>>8)<20) Y=20<<8; + if((U>>8)<-108) U=-108<<8; + if((V>>8)<-108) V=-108<<8; + if((Y>>8)>255) Y = 255; + if((U>>8)>128) U = 128; + if((V>>8)>128) V = 128; + *sny = Y; + *snu = U; + *snv = V; + } + else + { + *sny = *sy; + *snu = *su; + *snv = *sv; + } + diff++; sy++; sby++; sny++; + if ( ((px+1)%2==0) && ((py+1)%2==0)) + { + su++; sbu++; snu++; + sv++; sbv++; snv++; + } + } + } + else + { + for(px=0; px<x->x_vwidth; px++) + { + if ( sv >= data + x->x_vsize + (x->x_vsize>>1 ) ) break; + if(*diff > x->x_threshold){ + t = x->x_noisepattern[inline_fastrand()>>24]; + Y = (*sy) + t; + U = (*su) + t; + V = (*sv) + t; + Y = (Y>>1)-100; + U = (U>>1)-100; + V = V>>2; + Y += ((*sby)>>1)+((rand()%255)<<8); + U += ((*sbu)>>1)+((rand()%255)<<8); + V += ((*sbv)>>1)+((rand()%255)<<8); + if((Y>>8)<0) Y=0; + if((U>>8)<-128) U=-128<<8; + if((V>>8)<-128) V=-128<<8; + if((Y>>8)>255) Y = 255; + if((U>>8)>128) U = 128; + if((V>>8)>128) V = 128; + *sny = Y; + *snu = U; + *snv = V; + } else { + *sny = *sy; + *snu = *su; + *snv = *sv; + } + diff++; sy++; sby++; sny++; + if ( ((px+1)%2==0) && ((py+1)%2==0) ) + { + su++; sbu++; snu++; + sv++; sbv++; snv++; + } + } + } + } + + x->x_phase-=37; + + return; +} + +static void pdp_intrusion_sendpacket(t_pdp_intrusion *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_intrusion_process(t_pdp_intrusion *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_intrusion_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_intrusion_process_yv12, pdp_intrusion_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_intrusion_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_intrusion_process */ + break; + + } + } +} + +static void pdp_intrusion_input_0(t_pdp_intrusion *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_intrusion_process(x); + + } +} + +static void pdp_intrusion_free(t_pdp_intrusion *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + + for (i=0; i<NB_IMAGES; i++ ) + { + if ( x->x_images[i] ) freebytes( x->x_images[i], (x->x_vsize + (x->x_vsize>>1))<<1 ); + } + +} + +t_class *pdp_intrusion_class; + +void *pdp_intrusion_new(void) +{ + int i; + + t_pdp_intrusion *x = (t_pdp_intrusion *)pd_new(pdp_intrusion_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_bang, gensym("background")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("threshold")); + + 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_threshold = MAGIC_THRESHOLD<<8; + x->x_phase = 0; + x->x_bdata = NULL; + x->x_diff = NULL; + x->x_vsize = -1; + x->x_loopcount = 0; + x->x_threshfreq = 10; + + // initialize noise pattern + for(i=0; i<256; i++) + { + x->x_noisepattern[i] = (i * i * i / 40000)* i / 256; + } + + // initialize images + for(i=0; i<NB_IMAGES; i++) + { + x->x_images[i] = NULL; + } + x->x_rtimage=0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_intrusion_setup(void) +{ +// post( pdp_intrusion_version ); + pdp_intrusion_class = class_new(gensym("pdp_intrusion"), (t_newmethod)pdp_intrusion_new, + (t_method)pdp_intrusion_free, sizeof(t_pdp_intrusion), 0, A_NULL); + + class_addmethod(pdp_intrusion_class, (t_method)pdp_intrusion_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_intrusion_class, (t_method)pdp_intrusion_background, gensym("background"), A_NULL); + class_addmethod(pdp_intrusion_class, (t_method)pdp_intrusion_threshold, gensym("threshold"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_juxta.c b/modules/pdp_juxta.c new file mode 100644 index 0000000..e0b107a --- /dev/null +++ b/modules/pdp_juxta.c @@ -0,0 +1,319 @@ +/* + * 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 object allowing juxtaposition of frames from two inlets + * Written by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +static char *pdp_juxta_version = "pdp_juxta: version 0.1, frames juxtaposition, written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_juxta_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + t_int x_packet0; + t_int x_packet1; + t_int x_packet; + t_int x_dropped; + t_int x_queue_id; + + t_int x_vwidth0; + t_int x_vheight0; + t_int x_vsize0; + + t_int x_vwidth1; + t_int x_vheight1; + t_int x_vsize1; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + +} t_pdp_juxta; + +static void pdp_juxta_process_yv12(t_pdp_juxta *x) +{ + t_pdp *header0 = pdp_packet_header(x->x_packet0); + short int *data0 = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *header1 = pdp_packet_header(x->x_packet1); + short int *data1 = (short int *)pdp_packet_data(x->x_packet1); + t_pdp *header; + short int *data; + int i; + short int *poY, *poV, *poU, *p0Y, *p0V, *p0U, *p1Y, *p1V, *p1U; + + int px, py; + + if ( header0 ) + { + x->x_vwidth0 = header0->info.image.width; + x->x_vheight0 = header0->info.image.height; + x->x_vsize0 = x->x_vwidth0*x->x_vheight0; + } + else + { + x->x_vwidth0 = x->x_vheight0 = x->x_vsize0 = 0; + } + + if ( header1 ) + { + x->x_vwidth1 = header1->info.image.width; + x->x_vheight1 = header1->info.image.height; + x->x_vsize1 = x->x_vwidth1*x->x_vheight1; + } + else + { + x->x_vwidth1 = x->x_vheight1 = x->x_vsize1 = 0; + } + + x->x_vwidth = x->x_vwidth0 + x->x_vwidth1; + if ( x->x_vheight0 > x->x_vheight1 ) + { + x->x_vheight = x->x_vheight0; + } + else + { + x->x_vheight = x->x_vheight1; + } + x->x_vsize = x->x_vwidth*x->x_vheight; + // post( "pdp_juxta : resulting frame : %dx%d", x->x_vwidth, x->x_vheight ); + + x->x_packet = pdp_packet_new_image_YCrCb( x->x_vwidth, x->x_vheight ); + + header = pdp_packet_header(x->x_packet); + data = (short int *)pdp_packet_data(x->x_packet); + + header->info.image.encoding = PDP_IMAGE_YV12; + header->info.image.width = x->x_vwidth; + header->info.image.height = x->x_vheight; + + poY = data; + poV = data+x->x_vsize; + poU = data+x->x_vsize+(x->x_vsize>>2); + p0Y = data0; + p0V = data0+x->x_vsize0; + p0U = data0+x->x_vsize0+(x->x_vsize0>>2); + p1Y = data1; + p1V = data1+x->x_vsize1; + p1U = data1+x->x_vsize1+(x->x_vsize1>>2); + for ( py=0; py<x->x_vheight; py++ ) + { + for ( px=0; px<x->x_vwidth; px++ ) + { + if ( px < x->x_vwidth0 && p0Y ) + { + *poY = *p0Y; + *poV = *p0V; + *poU = *p0U; + poY++;p0Y++; + if ( (px%2==0) && (py%2==0) ) + { + poU++; poV++; + p0U++; p0V++; + } + } + if ( px >= x->x_vwidth0 && p1Y ) + { + *poY = *p1Y; + *poV = *p1V; + *poU = *p1U; + poY++;p1Y++; + if ( (px%2==0) && (py%2==0) ) + { + poU++; poV++; + p1U++; p1V++; + } + } + } + } + + return; +} + +static void pdp_juxta_sendpacket0(t_pdp_juxta *x) +{ + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet); +} + +static void pdp_juxta_sendpacket1(t_pdp_juxta *x) +{ + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet); +} + +static void pdp_juxta_process0(t_pdp_juxta *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_juxta_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_juxta_process_yv12, pdp_juxta_sendpacket0, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + break; + + default: + /* don't know the type, so dont pdp_juxta_process */ + break; + + } + } +} + +static void pdp_juxta_process1(t_pdp_juxta *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet1)) + && (PDP_IMAGE == header->type)){ + + /* pdp_juxta_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet1)->info.image.encoding){ + + case PDP_IMAGE_YV12: + pdp_queue_add(x, pdp_juxta_process_yv12, pdp_juxta_sendpacket1, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + break; + + default: + /* don't know the type, so dont pdp_juxta_process */ + break; + + } + } +} + +static void pdp_juxta_input_0(t_pdp_juxta *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")) + { + /* release the packet */ + if ( x->x_packet0 != -1 ) + { + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + } + 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_juxta_process0(x); + + } +} + +static void pdp_juxta_input_1(t_pdp_juxta *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")) + { + /* release the packet */ + if ( x->x_packet1 != -1 ) + { + pdp_packet_mark_unused(x->x_packet1); + x->x_packet1 = -1; + } + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet1, (int)f, pdp_gensym("image/YCrCb/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet1) && (!x->x_dropped)){ + + /* add the process method and callback to the process queue */ + pdp_juxta_process1(x); + + } +} + +static void pdp_juxta_free(t_pdp_juxta *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_packet_mark_unused(x->x_packet1); +} + +t_class *pdp_juxta_class; + +void *pdp_juxta_new(void) +{ + int i; + + t_pdp_juxta *x = (t_pdp_juxta *)pd_new(pdp_juxta_class); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("pdp"), gensym("pdp1")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("pdp"), gensym("pdp2")); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_packet = -1; + x->x_queue_id = -1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_juxta_setup(void) +{ + post( pdp_juxta_version ); + pdp_juxta_class = class_new(gensym("pdp_juxta"), (t_newmethod)pdp_juxta_new, + (t_method)pdp_juxta_free, sizeof(t_pdp_juxta), CLASS_NOINLET, A_NULL); + + class_addmethod(pdp_juxta_class, (t_method)pdp_juxta_input_0, gensym("pdp1"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_juxta_class, (t_method)pdp_juxta_input_1, gensym("pdp2"), A_SYMBOL, A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_lens.c b/modules/pdp_lens.c new file mode 100644 index 0000000..a46312a --- /dev/null +++ b/modules/pdp_lens.c @@ -0,0 +1,339 @@ +/* + * 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 adaptation of lens effect from effectv + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +static char *pdp_lens_version = "pdp_lens: version 0.1, port of lens from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_lens_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_float x_zoom; // zoom factor + t_int x_cx; // coordinates of lens center + t_int x_cy; // coordinates of lens center + t_int x_csize; // width of the lens + t_int x_xd; + t_int x_yd; + t_int x_mode; + t_int *x_lens; + t_int x_init; + +} t_pdp_lens; + +static void pdp_lens_preset(t_pdp_lens *x, t_int oldsize, t_int newsize) +{ + int px, py, r; + + if ( x->x_lens ) freebytes(x->x_lens, oldsize * oldsize * sizeof( t_int) ); + x->x_lens = (t_int *) getbytes( newsize * newsize * sizeof( t_int ) ); + r = x->x_csize / 2; + + /* it is sufficient to generate 1/4 of the lens and reflect this + * around; a sphere is mirrored on both the x and y axes */ + for (py = 0; py < r; py++) { + for (px = 0; px < r; px++) { + int ix, iy, offset, dist; + dist = px*px + py*py - r*r; + if(dist < 0) { + double shift = x->x_zoom/sqrt(x->x_zoom*x->x_zoom - dist); + ix = px * shift - px; + iy = py * shift - py; + } else { + ix = 0; + iy = 0; + } + offset = (iy * x->x_vwidth + ix); + x->x_lens[(r - py)*x->x_csize + r - px] = -offset; + x->x_lens[(r + py)*x->x_csize + r + px] = offset; + offset = (-iy * x->x_vwidth + ix); + x->x_lens[(r + py)*x->x_csize + r - px] = -offset; + x->x_lens[(r - py)*x->x_csize + r + px] = offset; + } + } +} + +static void pdp_lens_cliplens(t_pdp_lens *x) +{ + if (x->x_cy<0-(x->x_csize/2)+1)x->x_cy=0-(x->x_csize/2)+1; + if (x->x_cy>=x->x_vheight-x->x_csize/2-1)x->x_cy=x->x_vheight-x->x_csize/2-1; + + if (x->x_cx<0-(x->x_csize/2)+1) x->x_cx=0-x->x_csize/2+1; + if(x->x_cx>=x->x_vwidth-x->x_csize/2-1) x->x_cx=x->x_vwidth-x->x_csize/2-1; +} + +static void pdp_lens_zoom(t_pdp_lens *x, t_floatarg fzoom ) +{ + if ( fzoom>0 ) + { + x->x_zoom = fzoom; + if (x->x_zoom<5) x->x_zoom=5; + if (x->x_zoom>200) x->x_zoom=200; + pdp_lens_preset(x, x->x_csize, x->x_csize); + } +} + +static void pdp_lens_csize(t_pdp_lens *x, t_floatarg fcsize ) +{ + if ( fcsize>0 ) + { + x->x_csize = (int)fcsize; + if (x->x_csize>x->x_vheight) x->x_csize = x->x_vheight; + if (x->x_csize<3) x->x_csize = 3; + pdp_lens_preset(x, x->x_csize, x->x_csize); + pdp_lens_cliplens(x); + } +} + +static void pdp_lens_cy(t_pdp_lens *x, t_floatarg fcy ) +{ + if ( fcy>0 ) + { + x->x_cy = (int)fcy; + pdp_lens_cliplens(x); + } +} + +static void pdp_lens_cx(t_pdp_lens *x, t_floatarg fcx ) +{ + if ( fcx>0 ) + { + x->x_cx = (int)fcx; + pdp_lens_cliplens(x); + } +} + +static void pdp_lens_mode(t_pdp_lens *x, t_floatarg fmode ) +{ + if ( ( fmode == 0 ) || ( fmode == 1 ) ) + { + x->x_mode = (int)fmode; + } +} + +static void pdp_lens_process_yv12(t_pdp_lens *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); + int i; + + unsigned int totalnbpixels; + unsigned int u_offset; + unsigned int v_offset; + unsigned int totnbpixels; + short int *poy, *pou, *pov, *pny, *pnu, *pnv; + int px, py; + int noy, pos, posu, nox; + int *p; + + 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_init == -1 ) + { + pdp_lens_preset( x, x->x_csize, x->x_csize ); + x->x_init = 1; + } + + totalnbpixels = x->x_vsize; + u_offset = x->x_vsize; + v_offset = x->x_vsize + (x->x_vsize>>2); + totnbpixels = x->x_vsize + (x->x_vsize>>1); + + 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); + + p = x->x_lens; + poy = data; + pou = data + x->x_vsize; + pov = data + x->x_vsize + (x->x_vsize>>2); + pny = newdata; + pnu = newdata + x->x_vsize; + pnv = newdata + x->x_vsize + (x->x_vsize>>2); + for (py = 0; py < x->x_csize; py++) + { + for (px = 0; px < x->x_csize; px++) + { + noy=(py+x->x_cy); + nox=(px+x->x_cx); + if ((nox>=0)&&(noy>=0)&&(nox<x->x_vwidth)&&(noy<x->x_vheight)){ + pos = (noy * x->x_vwidth) + nox; + posu = ((noy>>1) * (x->x_vwidth>>1)) + (nox>>1); + if ( ( ( pos + *p )< x->x_vsize ) && ( pos < x->x_vsize ) ) + { + *(pny+pos) = *(poy + pos + *p); + *(pnu+posu) = *(pou + posu + *p ); + *(pnv+posu) = *(pov + posu + *p ); + } + } + p++; + } + } + + if (x->x_mode==1) + { + x->x_cx+= x->x_xd; x->x_cy+=x->x_yd; + if (x->x_cx > (x->x_vwidth - x->x_csize - 5) || x->x_cx < 5) x->x_xd = -x->x_xd; + if (x->x_cy > (x->x_vwidth - x->x_csize - 5) || x->x_cy < 5) x->x_yd = -x->x_yd; + } + + return; +} + +static void pdp_lens_sendpacket(t_pdp_lens *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_lens_process(t_pdp_lens *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_lens_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_lens_process_yv12, pdp_lens_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_lens_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_lens_process */ + break; + + } + } +} + +static void pdp_lens_input_0(t_pdp_lens *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_lens_process(x); + + } +} + +static void pdp_lens_free(t_pdp_lens *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_lens_class; + +void *pdp_lens_new(void) +{ + int i; + + t_pdp_lens *x = (t_pdp_lens *)pd_new(pdp_lens_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cx")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cy")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("csize")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("zoom")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("mode")); + + 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_cx = x->x_cy = 16; + x->x_xd = x->x_yd = 5; + x->x_csize = 150; + x->x_zoom = 30; + x->x_init = -1; + x->x_mode = 0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_lens_setup(void) +{ +// post( pdp_lens_version ); + pdp_lens_class = class_new(gensym("pdp_lens"), (t_newmethod)pdp_lens_new, + (t_method)pdp_lens_free, sizeof(t_pdp_lens), 0, A_NULL); + + class_addmethod(pdp_lens_class, (t_method)pdp_lens_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_lens_class, (t_method)pdp_lens_cx, gensym("cx"), A_FLOAT, A_NULL); + class_addmethod(pdp_lens_class, (t_method)pdp_lens_cy, gensym("cy"), A_FLOAT, A_NULL); + class_addmethod(pdp_lens_class, (t_method)pdp_lens_csize, gensym("csize"), A_FLOAT, A_NULL); + class_addmethod(pdp_lens_class, (t_method)pdp_lens_zoom, gensym("zoom"), A_FLOAT, A_NULL); + class_addmethod(pdp_lens_class, (t_method)pdp_lens_mode, gensym("mode"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_live~.c b/modules/pdp_live~.c new file mode 100644 index 0000000..aa8398d --- /dev/null +++ b/modules/pdp_live~.c @@ -0,0 +1,789 @@ +/* + * Pure Data Packet module. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a stream decoder object + * A lot of this object code is inspired by the excellent ffmpeg.c + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * The rest is written 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 <avformat.h> + +#define VIDEO_BUFFER_SIZE (1024*1024) +#define MAX_AUDIO_PACKET_SIZE (128 * 1024) +#define MIN_AUDIO_SIZE (64 * 1024) +#define AUDIO_PACKET_SIZE (2*1152) + +#define DEFAULT_CHANNELS 1 +#define DEFAULT_FRAME_RATE 25 +#define DEFAULT_WIDTH 320 +#define DEFAULT_HEIGHT 240 +#define DEFAULT_PRIORITY 0 + +static char *pdp_live_version = "pdp_live~: version 0.1, a video stream decoder ( ydegoyon@free.fr)."; + +typedef struct pdp_live_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_dropped; + + t_pdp *x_header; + short int *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_streaming; // indicates the action of streaming + t_outlet *x_outlet_nbframes; // number of frames emitted + + pthread_t x_connectchild; // thread used for connecting to a stream + pthread_t x_decodechild; // stream decoding thread + t_int x_usethread; // flag to activate decoding in a thread + t_int x_priority; // priority of decoding thread + + char *x_url; + t_int x_streaming; // streaming flag + 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_audioon; // enough audio data to start playing + struct timeval x_starttime; // streaming starting time + t_int x_cursec; // current second + t_int x_secondcount; // number of frames received in the current second + t_int x_nbvideostreams; // number of video streams + t_int x_nbaudiostreams; // number of audio streams + + /* AV data structures */ + AVFormatContext *x_avcontext; + AVFormatParameters x_avparameters; // unused but the call is necessary to allocate structures + AVPacket x_pkt; // packet received on the stream + AVPicture x_picture_decoded; + t_int x_newpicture; + + /* audio structures */ + t_int x_audio; // flag to activate the decoding of audio + short x_audio_buf[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for audio from stream*/ + short x_audio_in[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for resampled PCM audio */ + t_int x_audioin_position; // writing position for incoming audio + ReSampleContext *x_audio_resample_ctx; // structures for audio resample + +} t_pdp_live; + +static void pdp_live_priority(t_pdp_live *x, t_floatarg fpriority ) +{ + x->x_priority = (int)fpriority; +} + +static void pdp_live_threadify(t_pdp_live *x, t_floatarg fusethread ) +{ + if ( ( fusethread == 0 ) || ( fusethread == 1 ) ) + { + x->x_usethread = (int)fusethread; + } +} + +static void pdp_live_audio(t_pdp_live *x, t_floatarg faudio ) +{ + if ( ( faudio == 0.0 ) || ( faudio == 1 ) ) + { + x->x_audio = (int)faudio; + } +} + +static t_int pdp_live_decode_packet(t_pdp_live *x) +{ + t_int chunksize=0, length; + t_int audiosize, sizeout, imagesize, pictureok; + AVFrame frame; + uint8_t *pcktptr; + struct timeval etime; + + if ( !x->x_streaming ) + { + return -1; + } + + // read new packet on the stream + if (av_read_packet(x->x_avcontext, &x->x_pkt) < 0) + { + // post( "pdp_live~ : decoding thread : nothing to decode" ); + return -1; + } + // post( "pdp_live~ : read packet ( size=%d )", x->x_pkt.size ); + + if (x->x_pkt.stream_index >= x->x_avcontext->nb_streams) + { + post("pdp_live~ : stream received out of range !! "); + return 0; + } + + length = x->x_pkt.size; + pcktptr = x->x_pkt.data; + while (length > 0) + { + switch(x->x_avcontext->streams[x->x_pkt.stream_index]->codec.codec_type) + { + case CODEC_TYPE_AUDIO: + if ( !x->x_audio ) + { + av_free_packet(&x->x_pkt); + return 0; + } + chunksize = avcodec_decode_audio(&x->x_avcontext->streams[x->x_pkt.stream_index]->codec, + &x->x_audio_buf[0], &audiosize, + pcktptr, length); + if (chunksize < 0) + { + post("pdp_live~ : could not decode audio input (ret=%d)", chunksize ); + av_free_packet(&x->x_pkt); + continue; + } + // some bug in mpeg audio decoder gives + // data_size < 0, it seems they are overflows + if (audiosize <= 0) + { + /* no audio frame */ + pcktptr += chunksize; + length -= chunksize; + // post("pdp_live~ : audio overflow in decoder!"); + continue; + } + + // resample received audio + // post( "pdp_live~ : resampling from %dHz-%dch to %dHz-%dch (in position=%d)", + // x->x_avcontext->streams[x->x_pkt.stream_index]->codec.sample_rate, + // x->x_avcontext->streams[x->x_pkt.stream_index]->codec.channels, + // (int)sys_getsr(), 2, x->x_audioin_position ); + + x->x_audiochannels = x->x_avcontext->streams[x->x_pkt.stream_index]->codec.channels; + x->x_samplerate = x->x_avcontext->streams[x->x_pkt.stream_index]->codec.sample_rate; + if (x->x_audio_resample_ctx) audio_resample_close(x->x_audio_resample_ctx); + x->x_audio_resample_ctx = + audio_resample_init(DEFAULT_CHANNELS, + x->x_avcontext->streams[x->x_pkt.stream_index]->codec.channels, + (int)sys_getsr(), + x->x_avcontext->streams[x->x_pkt.stream_index]->codec.sample_rate); + + sizeout = audio_resample(x->x_audio_resample_ctx, + &x->x_audio_in[x->x_audioin_position], + &x->x_audio_buf[0], + audiosize/(x->x_avcontext->streams[x->x_pkt.stream_index]->codec.channels * sizeof(short))); + sizeout = sizeout * DEFAULT_CHANNELS; + + if ( ( x->x_audioin_position + sizeout ) < 3*MAX_AUDIO_PACKET_SIZE ) + { + x->x_audioin_position = x->x_audioin_position + sizeout; + } + else + { + post( "pdp_live~ : audio overflow : packet ignored..."); + } + if ( ( x->x_audioin_position > MIN_AUDIO_SIZE ) && (!x->x_audioon) ) + { + x->x_audioon = 1; + // post( "pdp_live~ : audio on" ); + } + break; + + case CODEC_TYPE_VIDEO: + + imagesize = (x->x_avcontext->streams[x->x_pkt.stream_index]->codec.width * + x->x_avcontext->streams[x->x_pkt.stream_index]->codec.height * 3) / 2; // yuv planar + + // do not believe the declared framerate + // x->x_framerate = x->x_avcontext->streams[x->x_pkt.stream_index]->codec.frame_rate / 10000; + + // calculate actual frame rate + if ( gettimeofday(&etime, NULL) == -1) + { + post("pdp_live~ : could not read time" ); + } + if ( ( etime.tv_sec - x->x_starttime.tv_sec ) > 0 ) + { + x->x_framerate = x->x_nbframes / ( etime.tv_sec - x->x_starttime.tv_sec ); + } + if ( x->x_framerate == 0 ) x->x_framerate = 1; + // post ("pdp_live~ : frame rate is %d", x->x_framerate ); + + chunksize = avcodec_decode_video( + &x->x_avcontext->streams[x->x_pkt.stream_index]->codec, + &frame, &pictureok, + pcktptr, length); + if ( x->x_avcontext->streams[x->x_pkt.stream_index]->codec.pix_fmt != PIX_FMT_YUV420P ) + { + post( "pdp_live~ : unsupported image format : %d", + x->x_avcontext->streams[x->x_pkt.stream_index]->codec.pix_fmt ); + pictureok = 0; + } + // post( "pdp_live~ : decoded new frame : type=%d format=%d (w=%d) (h=%d) (linesizes=%d,%d,%d,%d)", + // frame.pict_type, + // x->x_avcontext->streams[x->x_pkt.stream_index]->codec.pix_fmt, + // x->x_avcontext->streams[x->x_pkt.stream_index]->codec.width, + // x->x_avcontext->streams[x->x_pkt.stream_index]->codec.height, + // frame.linesize[0], + // frame.linesize[1], + // frame.linesize[2], + // frame.linesize[3] ); + x->x_picture_decoded = *(AVPicture*)&frame; + x->x_avcontext->streams[x->x_pkt.stream_index]->quality= frame.quality; + if (chunksize < 0) + { + av_free_packet(&x->x_pkt); + post("pdp_live~ : could not decode video frame (ret=%d)", chunksize ); + return 0; + } + if (!pictureok) + { + // no picture yet + pcktptr += chunksize; + length -= chunksize; + continue; + } + else + { + x->x_newpicture=1; + x->x_vwidth = x->x_avcontext->streams[x->x_pkt.stream_index]->codec.width; + x->x_vheight = x->x_avcontext->streams[x->x_pkt.stream_index]->codec.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + } + break; + } + pcktptr += chunksize; + length -= chunksize; + if ( !x->x_streaming ) break; + } + av_free_packet(&x->x_pkt); + + // post( "pdp_live~ : decoded one packet" ); + return 0; + +} + +static void *pdp_decode_stream_from_url(void *tdata) +{ + t_pdp_live *x = (t_pdp_live*)tdata; + struct sched_param schedprio; + t_int pmin, pmax; + struct timespec twait; + + twait.tv_sec = 0; + twait.tv_nsec = 10000000; // 10 ms + + schedprio.sched_priority = 0; + if ( sched_setscheduler(0,SCHED_OTHER,&schedprio) == -1) + { + post("pdp_live~ : couldn't set scheduler for decoding thread.\n"); + } + if ( setpriority( PRIO_PROCESS, 0, x->x_priority ) < 0 ) + { + post("pdp_live~ : couldn't set priority to %d for decoding thread.\n", x->x_priority ); + } + else + { + post("pdp_live~ : priority set to %d for thread %d.\n", x->x_priority, x->x_decodechild ); + } + + if ( ! (x->x_avcontext->iformat->flags & AVFMT_NOHEADER ) ) + { + if (x->x_avcontext->iformat->read_header(x->x_avcontext, &x->x_avparameters) < 0) + { + post( "pdp_live~ : couldn't read header" ); + } + post( "pdp_live~ : read header." ); + } + + while ( x->x_streaming ) + { + while ( x->x_newpicture ) nanosleep( &twait, NULL ); + + // decode incoming packets + if ( pdp_live_decode_packet( x ) < 0 ) + { + nanosleep( &twait, NULL ); // nothing to read, just wait + } + } + + post( "pdp_live~ : decoding thread %d exiting....", x->x_decodechild ); + x->x_decodechild = 0; + pthread_exit(NULL); +} + +static void *pdp_live_connect_to_url(void *tdata) +{ + int i, err; + t_pdp_live *x = (t_pdp_live*)tdata; + pthread_attr_t decode_child_attr; + + memset(&x->x_avparameters, 0, sizeof(AVFormatParameters)); + x->x_avparameters.sample_rate = sys_getsr(); + x->x_avparameters.channels = DEFAULT_CHANNELS; + x->x_avparameters.frame_rate = DEFAULT_FRAME_RATE; + x->x_avparameters.width = DEFAULT_WIDTH; + x->x_avparameters.height = DEFAULT_HEIGHT; + x->x_avparameters.image_format = PIX_FMT_YUV420P; + + post( "pdp_live~ : opening url : %s", x->x_url ); + err = av_open_input_file(&x->x_avcontext, x->x_url, x->x_avcontext->iformat, 0, &x->x_avparameters); + if (err < 0) + { + if ( err == -1 ) post( "pdp_live~ : unknown error" ); + if ( err == -2 ) post( "pdp_live~ : i/o error" ); + if ( err == -3 ) post( "pdp_live~ : number syntax expected in filename" ); + if ( err == -4 ) post( "pdp_live~ : invalid data found" ); + if ( err == -5 ) post( "pdp_live~ : not enough memory" ); + if ( err == -6 ) post( "pdp_live~ : unknown format ( stream not found? )" ); + x->x_connectchild = 0; + x->x_avcontext = av_mallocz(sizeof(AVFormatContext)); + pthread_exit(NULL); + } + /* If not enough info to get the stream parameters, we decode the + first frames to get it. (used in mpeg case for example) */ + err = av_find_stream_info(x->x_avcontext); + if (err < 0) + { + post( "pdp_live~ : %s: could not find codec parameters\n", x->x_url); + x->x_connectchild = 0; + av_close_input_file(x->x_avcontext); + x->x_avcontext = av_mallocz(sizeof(AVFormatContext)); + pthread_exit(NULL); + } + + // post( "pdp_live~ : stream reader : %x", x->x_avcontext->iformat ); + + /* copy stream format */ + x->x_nbvideostreams = 0; + x->x_nbaudiostreams = 0; + + for(i=0;i<x->x_avcontext->nb_streams;i++) + { + AVStream *st; + + if ( x->x_avcontext->streams[i]->codec.codec_type == CODEC_TYPE_UNKNOWN ) + { + post( "pdp_live~ : stream #%d # type : unknown", i ); + } + if ( x->x_avcontext->streams[i]->codec.codec_type == CODEC_TYPE_AUDIO ) + { + post( "pdp_live~ : stream #%d # type : audio # id : %d # bitrate : %d", + i, x->x_avcontext->streams[i]->codec.codec_id, x->x_avcontext->streams[i]->codec.bit_rate ); + post( "pdp_live~ : sample rate : %d # channels : %d", + x->x_avcontext->streams[i]->codec.sample_rate, x->x_avcontext->streams[i]->codec.channels ); + x->x_nbaudiostreams++; + } + if ( x->x_avcontext->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO ) + { + post( "pdp_live~ : stream #%d # type : video # id : %d # bitrate : %d", + i, x->x_avcontext->streams[i]->codec.codec_id, + x->x_avcontext->streams[i]->codec.bit_rate ); + post( "pdp_live~ : framerate : %d # width : %d # height : %d", + x->x_avcontext->streams[i]->codec.frame_rate/10000, + x->x_avcontext->streams[i]->codec.width, + x->x_avcontext->streams[i]->codec.height ); + x->x_nbvideostreams++; + } + } + + /* open each decoder */ + for(i=0;i<x->x_avcontext->nb_streams;i++) + { + AVCodec *codec; + post("pdp_live~ : opening decoder for stream #%d", i); + codec = avcodec_find_decoder(x->x_avcontext->streams[i]->codec.codec_id); + if (!codec) + { + post("pdp_live~ : unsupported codec for output stream #%d\n", i ); + x->x_streaming = 0; + x->x_connectchild = 0; + av_close_input_file(x->x_avcontext); + x->x_avcontext = av_mallocz(sizeof(AVFormatContext)); + pthread_exit(NULL); + } + if (avcodec_open(&x->x_avcontext->streams[i]->codec, codec) < 0) + { + post("pdp_live~ : error while opening codec for stream #%d - maybe incorrect parameters such as bit_rate, rate, width or height\n", i); + x->x_streaming = 0; + x->x_connectchild = 0; + av_close_input_file(x->x_avcontext); + x->x_avcontext = av_mallocz(sizeof(AVFormatContext)); + pthread_exit(NULL); + } + else + { + post("pdp_live~ : opened decoder for stream #%d", i); + } + } + + if ( gettimeofday(&x->x_starttime, NULL) == -1) + { + post("pdp_live~ : could not set start time" ); + } + x->x_streaming = 1; + x->x_nbframes = 0; + + x->x_connectchild = 0; + + if ( x->x_usethread ) + { + // launch decoding thread + if ( pthread_attr_init( &decode_child_attr ) < 0 ) + { + post( "pdp_live~ : could not launch decoding thread" ); + perror( "pthread_attr_init" ); + av_close_input_file(x->x_avcontext); + x->x_avcontext = av_mallocz(sizeof(AVFormatContext)); + pthread_exit(NULL); + } + if ( pthread_create( &x->x_decodechild, &decode_child_attr, pdp_decode_stream_from_url, x ) < 0 ) + { + post( "pdp_live~ : could not launch decoding thread" ); + perror( "pthread_create" ); + av_close_input_file(x->x_avcontext); + x->x_avcontext = av_mallocz(sizeof(AVFormatContext)); + pthread_exit(NULL); + } + else + { + post( "pdp_live~ : decoding thread %d launched", (int)x->x_decodechild ); + } + } + + pthread_exit(NULL); +} + +static void pdp_live_disconnect(t_pdp_live *x) +{ + t_int ret, i, count=0; + struct timespec twait; + + twait.tv_sec = 0; + twait.tv_nsec = 100000000; // 100 ms + + if (!x->x_streaming) + { + post("pdp_live~ : close request but no stream is played ... ignored" ); + return; + } + + if ( x->x_streaming ) + { + x->x_streaming = 0; + x->x_newpicture = 0; + post("pdp_live~ : waiting for the end of decoding thread..." ); + while ( x->x_decodechild && ( count < 100 ) ) + { + count++; + sleep( 1 ); + } + if ( x->x_decodechild ) + { + post("pdp_live~ : zombie thread, i guess" ); + } + post("pdp_live~ : closing input file..." ); + av_close_input_file(x->x_avcontext); + x->x_avcontext = av_mallocz(sizeof(AVFormatContext)); + } + + outlet_float( x->x_outlet_streaming, x->x_streaming ); + x->x_nbframes = 0; + outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + + if (x->x_audio_resample_ctx) + { + audio_resample_close(x->x_audio_resample_ctx); + x->x_audio_resample_ctx = NULL; + } + +} + +static void pdp_live_connect(t_pdp_live *x, t_symbol *s) +{ + t_int ret, i; + pthread_attr_t connect_child_attr; + + if ( ( x->x_streaming ) || ( x->x_connectchild != 0 ) ) + { + post("pdp_live~ : connection request but a connection is pending ... disconnecting" ); + pdp_live_disconnect(x); + } + + if ( x->x_url ) free( x->x_url ); + x->x_url = (char*) malloc( strlen( s->s_name ) + 1 ); + strcpy( x->x_url, s->s_name ); + + // launch connection thread + if ( pthread_attr_init( &connect_child_attr ) < 0 ) { + post( "pdp_live~ : could not launch connection thread" ); + perror( "pthread_attr_init" ); + return; + } + if ( pthread_attr_setdetachstate( &connect_child_attr, PTHREAD_CREATE_DETACHED ) < 0 ) { + post( "pdp_live~ : could not launch connection thread" ); + perror( "pthread_attr_setdetachstate" ); + return; + } + if ( pthread_create( &x->x_connectchild, &connect_child_attr, pdp_live_connect_to_url, x ) < 0 ) { + post( "pdp_live~ : could not launch connection thread" ); + perror( "pthread_create" ); + return; + } + else + { + post( "pdp_live~ : connection thread %d launched", (int)x->x_connectchild ); + } + + return; +} + + /* decode the stream to fill up buffers */ +static t_int *pdp_live_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_live *x = (t_pdp_live *)(w[3]); + int n = (int)(w[4]); // number of samples + short int *pY, *pU, *pV; + uint8_t *psY, *psU, *psV; + t_int pixRGB, px, py; + short sampleL, sampleR; + struct timeval etime; + t_int sn; + + // decode a packet if not in thread mode + if ( !x->x_usethread ) + { + pdp_live_decode_packet( x ); + } + + // just read the buffer + if ( x->x_audioon ) + { + sn=0; + n=n*DEFAULT_CHANNELS; + while (n--) + { + sampleL=x->x_audio_in[ sn++ ]; + *(out1) = ((t_float)sampleL)/32768.0; + if ( DEFAULT_CHANNELS == 1 ) + { + *(out2) = *(out1); + } + if ( DEFAULT_CHANNELS == 2 ) + { + sampleR=x->x_audio_in[ sn++ ]; + *(out2) = ((t_float)sampleR)/32768.0; + } + out1++; + out2++; + } + x->x_audioin_position-=sn; + memcpy( &x->x_audio_in[0], &x->x_audio_in[sn], 4*MAX_AUDIO_PACKET_SIZE-sn ); + // post( "pdp_live~ : audio in position : %d", x->x_audioin_position ); + if ( x->x_audioin_position <= sn ) + { + x->x_audioon = 0; + // post( "pdp_live~ : audio off" ); + } + } + else + { + // post("pdp_live~ : 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_live~ : could not read time" ); + } + if ( etime.tv_sec != x->x_cursec ) + { + x->x_cursec = etime.tv_sec; + x->x_secondcount = 0; + } + if ( x->x_secondcount >= x->x_framerate ) + { + // return (w+5); + } + + // output image if there's a new one decoded + if ( x->x_newpicture ) + { + // create a new pdp packet from PIX_FMT_YUV420P image format + x->x_packet0 = pdp_packet_new_image_YCrCb( x->x_vwidth, x->x_vheight ); + x->x_header = pdp_packet_header(x->x_packet0); + x->x_data = (short int *)pdp_packet_data(x->x_packet0); + + 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_picture_decoded.data[0]; + psU = x->x_picture_decoded.data[1]; + psV = x->x_picture_decoded.data[2]; + + for ( py=0; py<x->x_vheight; py++) + { + for ( px=0; px<x->x_vwidth; px++) + { + *(pY) = ( *(psY+px) << 7 ); + *(pV) = ( ((*(psV+(px>>1)))-128) << 8 ); + *(pU) = ( ((*(psU+(px>>1)))-128) << 8 ); + pY++; + if ( (px%2==0) && (py%2==0) ) + { + pV++; pU++; + } + } + psY += x->x_picture_decoded.linesize[0]; + if ( py%2==0 ) psU += x->x_picture_decoded.linesize[1]; + if ( py%2==0 ) psV += x->x_picture_decoded.linesize[2]; + } + + pdp_packet_pass_if_valid(x->x_pdp_out, &x->x_packet0); + + // update streaming status + outlet_float( x->x_outlet_streaming, x->x_streaming ); + x->x_nbframes++; + x->x_secondcount++; + outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + + x->x_newpicture = 0; + } + + return (w+5); +} + +static void pdp_live_dsp(t_pdp_live *x, t_signal **sp) +{ + dsp_add(pdp_live_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + +static void pdp_live_free(t_pdp_live *x) +{ + int i; + + if ( x->x_streaming ) + { + pdp_live_disconnect(x); + sleep(3); + } + post( "pdp_live~ : freeing object" ); + pdp_packet_mark_unused(x->x_packet0); + if (x->x_audio_resample_ctx) + { + audio_resample_close(x->x_audio_resample_ctx); + x->x_audio_resample_ctx = NULL; + } + av_free_static(); +} + +t_class *pdp_live_class; + +void *pdp_live_new(void) +{ + int i; + + t_pdp_live *x = (t_pdp_live *)pd_new(pdp_live_class); + + 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_streaming = outlet_new(&x->x_obj, &s_float); + x->x_outlet_nbframes = outlet_new(&x->x_obj, &s_float); + + x->x_packet0 = -1; + x->x_connectchild = 0; + x->x_decodechild = 0; + x->x_usethread = 1; + x->x_priority = DEFAULT_PRIORITY; + x->x_nbframes = 0; + x->x_framerate = DEFAULT_FRAME_RATE; + x->x_samplerate = 0; + x->x_audiochannels = 0; + x->x_cursec = 0; + x->x_secondcount = 0; + x->x_audio_resample_ctx = NULL; + x->x_nbvideostreams = 0; + x->x_audioin_position = 0; + x->x_newpicture = 0; + + x->x_avcontext = av_mallocz(sizeof(AVFormatContext)); + if ( !x->x_avcontext ) + { + post( "pdp_live~ : severe error : could not allocate video structures." ); + return NULL; + } + + // activate codecs + av_register_all(); + + memset( &x->x_audio_buf[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(short) ); + memset( &x->x_audio_in[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(short) ); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_live_tilde_setup(void) +{ + post( pdp_live_version ); + pdp_live_class = class_new(gensym("pdp_live~"), (t_newmethod)pdp_live_new, + (t_method)pdp_live_free, sizeof(t_pdp_live), 0, A_NULL); + + class_addmethod(pdp_live_class, (t_method)pdp_live_dsp, gensym("dsp"), 0); + class_addmethod(pdp_live_class, (t_method)pdp_live_connect, gensym("connect"), A_SYMBOL, A_NULL); + class_addmethod(pdp_live_class, (t_method)pdp_live_disconnect, gensym("disconnect"), A_NULL); + class_addmethod(pdp_live_class, (t_method)pdp_live_priority, gensym("priority"), A_FLOAT, A_NULL); + class_addmethod(pdp_live_class, (t_method)pdp_live_audio, gensym("audio"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_lumafilt.c b/modules/pdp_lumafilt.c new file mode 100644 index 0000000..62f1a87 --- /dev/null +++ b/modules/pdp_lumafilt.c @@ -0,0 +1,255 @@ +/* + * 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 effect filters some levels of luminosity + * Useful to isolate some objects + * Written by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define MAX_LUMA 256 + +static char *pdp_lumafilt_version = "pdp_lumafilt: version 0.1, written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_lumafilt_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_filter[MAX_LUMA]; // transform number + +} t_pdp_lumafilt; + +static void pdp_lumafilt_free_ressources(t_pdp_lumafilt *x) +{ + // nothing +} + +static void pdp_lumafilt_allocate(t_pdp_lumafilt *x) +{ + // nothing +} + +static void pdp_lumafilt_filter(t_pdp_lumafilt *x, t_floatarg fluma, t_floatarg fonoff ) +{ + if ( ( (int)fluma >= 0 ) && ( (int)fluma < MAX_LUMA ) ) + { + if ( ((int)fonoff == 0 ) || ((int)fonoff == 1 ) ) + { + x->x_filter[ (int)fluma ] = (int)fonoff; + } + } +} + +static void pdp_lumafilt_mfilter(t_pdp_lumafilt *x, t_floatarg flumas, t_floatarg flumae, t_floatarg fonoff ) +{ + t_int li; + + if ( ( (int)flumas >= 0 ) && ( (int)flumas < MAX_LUMA ) && + ( (int)flumae >= 0 ) && ( (int)flumae < MAX_LUMA ) && + ( flumas < flumae ) ) + { + if ( ((int)fonoff == 0 ) || ((int)fonoff == 1 ) ) + { + for ( li=(int)flumas; li<=(int)flumae; li++ ) + { + x->x_filter[ li ] = (int)fonoff; + } + } + } +} + +static void pdp_lumafilt_process_yv12(t_pdp_lumafilt *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 px, py, luma; + short int *pnY, *pnU, *pnV; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_lumafilt_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_lumafilt_allocate(x); + post( "pdp_lumafilt : reallocated buffers" ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + pnY = newdata; + pnV = newdata+x->x_vsize; + pnU = newdata+x->x_vsize+(x->x_vsize>>2); + + memcpy( newdata, data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + + for (py = 0; py < x->x_vheight; py++) { + for (px = 0; px < x->x_vwidth; px++) { + luma = (*(pnY)>>7); + if ( ( luma >0 ) && ( luma < MAX_LUMA ) ) // paranoid + { + if ( x->x_filter[luma] ) + { + *(pnY) = 0; + *(pnU) = 0; + *(pnV) = 0; + } + } + else + { + post( "pdp_lumafilt : luminosity value out of bounds : %d", luma ); + } + pnY++; + if ( (px%2==0) && (py%2==0) ) + { + *pnU++; *pnV++; + } + } + } + + return; +} + +static void pdp_lumafilt_sendpacket(t_pdp_lumafilt *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_lumafilt_process(t_pdp_lumafilt *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_lumafilt_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_lumafilt_process_yv12, pdp_lumafilt_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_lumafilt_process */ + break; + + } + } +} + +static void pdp_lumafilt_input_0(t_pdp_lumafilt *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_lumafilt_process(x); + } +} + +static void pdp_lumafilt_free(t_pdp_lumafilt *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_lumafilt_free_ressources(x); +} + +t_class *pdp_lumafilt_class; + +void *pdp_lumafilt_new(void) +{ + int fi; + + t_pdp_lumafilt *x = (t_pdp_lumafilt *)pd_new(pdp_lumafilt_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_queue_id = -1; + + for ( fi=0; fi<MAX_LUMA; fi++ ) + { + x->x_filter[fi] = 0; + } + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_lumafilt_setup(void) +{ +// post( pdp_lumafilt_version ); + pdp_lumafilt_class = class_new(gensym("pdp_lumafilt"), (t_newmethod)pdp_lumafilt_new, + (t_method)pdp_lumafilt_free, sizeof(t_pdp_lumafilt), 0, A_NULL); + + class_addmethod(pdp_lumafilt_class, (t_method)pdp_lumafilt_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_lumafilt_class, (t_method)pdp_lumafilt_filter, gensym("filter"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_lumafilt_class, (t_method)pdp_lumafilt_mfilter, gensym("mfilter"), A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_mgrid.c b/modules/pdp_mgrid.c new file mode 100644 index 0000000..3f3939c --- /dev/null +++ b/modules/pdp_mgrid.c @@ -0,0 +1,336 @@ +/* + * 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 motion detection grid + */ + +#include "pdp.h" +#include <math.h> + +#define DEFAULT_X_DIM 10 +#define DEFAULT_Y_DIM 10 +#define DEFAULT_THRESHOLD 20 +#define DEFAULT_COLOR 128 + +static char *pdp_mgrid_version = "pdp_mgrid: a motion detection grid version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_mgrid_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_dropped; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + short int *x_previous_frame; + t_int x_xdim; + t_int x_ydim; + t_int x_threshold; + short int x_color; + t_int x_firstimage; + + t_outlet *x_pdp_output; // output packets + t_outlet *x_xmotion; // output x coordinate of block which has been detected + t_outlet *x_ymotion; // output y coordinate of block which has been detected + + +} t_pdp_mgrid; + +static void pdp_mgrid_free_ressources(t_pdp_mgrid *x) +{ + if ( x->x_previous_frame ) freebytes ( x->x_previous_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); +} + +static void pdp_mgrid_allocate(t_pdp_mgrid *x) +{ + x->x_previous_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + + if ( !x->x_previous_frame ) + { + post( "pdp_mgrid : severe error : cannot allocate buffer !!! "); + return; + } +} + +static void pdp_mgrid_x_dim(t_pdp_mgrid *x, t_floatarg fxdim ) +{ + if ( x->x_previous_frame == NULL ) + { + post( "pdp_mgrid : tried to set grid dimension but image dimensions are unknown" ); + return; + } + if ( ( fxdim >= 0 ) && ( fxdim < (x->x_vwidth/3) ) ) + { + x->x_xdim = (int)fxdim; + } +} + +static void pdp_mgrid_y_dim(t_pdp_mgrid *x, t_floatarg fydim ) +{ + if ( x->x_previous_frame == NULL ) + { + post( "pdp_mgrid : tried to set grid dimension but image dimensions are unknown" ); + return; + } + + if ( ( fydim >= 0 ) && ( fydim < (x->x_vheight/3) ) ) + { + x->x_ydim = (int)fydim; + } +} + +static void pdp_mgrid_threshold(t_pdp_mgrid *x, t_floatarg fthreshold ) +{ + if ( fthreshold > 0 ) + { + x->x_threshold = (int)fthreshold; + } +} + +static void pdp_mgrid_color(t_pdp_mgrid *x, t_floatarg fcolor ) +{ + if ( ( fcolor >= 0 ) && ( fcolor < 255 ) ) + { + x->x_color = (int)fcolor; + } +} + +static void pdp_mgrid_process_yv12(t_pdp_mgrid *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_int i, cf; + t_int px=0, py=0, xcell=0, ycell=0; + t_int celldiff=0, cellwidth=0, cellheight=0; + t_int yindex=0, uindex=0, vindex=0; + + /* allocate all ressources */ + if ( ( (int)header->info.image.width != x->x_vwidth ) || + ( (int)header->info.image.height != x->x_vheight ) ) + { + pdp_mgrid_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_mgrid_allocate(x); + x->x_firstimage = 1; + post( "pdp_mgrid : reallocated buffers" ); + } + + // draw horizontal lines + if ( x->x_ydim > 0 ) + { + for(py=0; py<x->x_vheight; py+=(x->x_vheight/x->x_ydim)) + { + if ( py >= x->x_vheight ) break; + for(px=0; px<x->x_vwidth; px++) + { + data[py*x->x_vwidth+px] = + ( ( data[py*x->x_vwidth+px] >> 7 ) ^ x->x_color) << 7; + } + } + } + + // draw vertical lines + if ( x->x_xdim > 0 ) + { + for(px=0; px<x->x_vwidth; px+=(x->x_vwidth/x->x_xdim)) + { + if ( px >= x->x_vwidth ) break; + for(py=0; py<x->x_vheight; py++) + { + data[py*x->x_vwidth+px] = + ( ( data[py*x->x_vwidth+px] >> 7 ) ^ x->x_color) << 7; + } + } + } + + if ( !x->x_firstimage ) + { + // detect cell where a movement occurred + ycell=0; + celldiff=0; + if ( x->x_xdim > 0 ) + { + cellwidth=(x->x_vwidth/x->x_xdim); + } + else + { + cellwidth=x->x_vwidth; + } + if ( x->x_ydim > 0 ) + { + cellheight=(x->x_vheight/x->x_ydim); + } + else + { + cellheight=x->x_vheight; + } + for(xcell=0; xcell<x->x_xdim; xcell++) + { + for(ycell=0; ycell<x->x_ydim; ycell++) + { + celldiff=0; + for ( px=xcell*cellwidth; px<(xcell+1)*cellwidth; px++ ) + { + if ( px >= x->x_vwidth ) break; + for ( py=ycell*cellheight; py<(ycell+1)*cellheight; py++ ) + { + if ( py >= x->x_vheight ) break; + yindex = py*x->x_vwidth+px; + uindex = x->x_vsize + ((py*x->x_vwidth>>2)+(px>>1)); + vindex = x->x_vsize + (x->x_vsize>>2) + ((py*x->x_vwidth>>2)+(px>>1)); + + // this calculation although more accurate is heavy + // celldiff += sqrt( pow(((data[ yindex ]>>7) - (x->x_previous_frame[ yindex ]>>7)), 2) + // + pow(((data[ uindex ]>>8) - (x->x_previous_frame[ uindex ]>>8)), 2) + // + pow(((data[ vindex ]>>8) - (x->x_previous_frame[ vindex ]>>8)), 2)); + celldiff += abs(((data[ yindex ]>>7) - (x->x_previous_frame[ yindex ]>>7))) + + abs(((data[ uindex ]>>8) - (x->x_previous_frame[ uindex ]>>8))) + + abs(((data[ vindex ]>>8) - (x->x_previous_frame[ vindex ]>>8))); + } + } + if ( celldiff > x->x_threshold*cellwidth*cellheight ) + { + outlet_float(x->x_xmotion, xcell+1); + outlet_float(x->x_ymotion, ycell+1); + } + // post( "pdp_mgrid : cell [%d,%d] diff=%d", xcell, ycell, celldiff ); + } + } + } + else + { + x->x_firstimage = 0; + } + + memcpy(x->x_previous_frame, data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet0); + + return; +} + +static void pdp_mgrid_process(t_pdp_mgrid *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_mgrid_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding){ + + case PDP_IMAGE_YV12: + pdp_mgrid_process_yv12(x); + 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_mgrid_process */ + break; + + } + } +} + +static void pdp_mgrid_input_0(t_pdp_mgrid *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_mgrid_process(x); + + } +} + +static void pdp_mgrid_free(t_pdp_mgrid *x) +{ + int i; + + pdp_packet_mark_unused(x->x_packet0); + pdp_mgrid_free_ressources(x); +} + +t_class *pdp_mgrid_class; + +void *pdp_mgrid_new(void) +{ + int i; + + t_pdp_mgrid *x = (t_pdp_mgrid *)pd_new(pdp_mgrid_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("threshold")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("dimx")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("dimy")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("color")); + + x->x_pdp_output = outlet_new(&x->x_obj, &s_anything); + x->x_xmotion = outlet_new(&x->x_obj, &s_float); + x->x_ymotion = outlet_new(&x->x_obj, &s_float); + + x->x_packet0 = -1; + + x->x_previous_frame = NULL; + x->x_xdim = DEFAULT_X_DIM; + x->x_ydim = DEFAULT_Y_DIM; + x->x_threshold = DEFAULT_THRESHOLD; + x->x_color = DEFAULT_COLOR; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_mgrid_setup(void) +{ + post( pdp_mgrid_version ); + pdp_mgrid_class = class_new(gensym("pdp_mgrid"), (t_newmethod)pdp_mgrid_new, + (t_method)pdp_mgrid_free, sizeof(t_pdp_mgrid), 0, A_NULL); + + class_addmethod(pdp_mgrid_class, (t_method)pdp_mgrid_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_mgrid_class, (t_method)pdp_mgrid_threshold, gensym("threshold"), A_FLOAT, A_NULL); + class_addmethod(pdp_mgrid_class, (t_method)pdp_mgrid_x_dim, gensym("dimx"), A_FLOAT, A_NULL); + class_addmethod(pdp_mgrid_class, (t_method)pdp_mgrid_y_dim, gensym("dimy"), A_FLOAT, A_NULL); + class_addmethod(pdp_mgrid_class, (t_method)pdp_mgrid_color, gensym("color"), A_FLOAT, A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_mosaic.c b/modules/pdp_mosaic.c new file mode 100644 index 0000000..1fb3487 --- /dev/null +++ b/modules/pdp_mosaic.c @@ -0,0 +1,312 @@ +/* + * 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 adaptation of mosaic effect from effectv + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define MAGIC_THRESHOLD 30 +#define CENSOR_LEVEL 20 + +static char *pdp_mosaic_version = "pdp_mosaic: version 0.1, port of mosaic from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_mosaic_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_censor_level; + t_int x_ssize; + short int *x_diff; + short int *x_bdata; + t_int x_snapshot; + +} t_pdp_mosaic; + + +static void pdp_mosaic_ssize(t_pdp_mosaic *x, t_floatarg fssize ) +{ + if ( ( fssize > 1 ) && ( fssize < x->x_vwidth ) ) + { + x->x_ssize = (int)fssize; + } +} + +static void pdp_mosaic_level(t_pdp_mosaic *x, t_floatarg flevel ) +{ + if ( flevel > 0 ) + { + x->x_censor_level = (int)flevel; + } +} + +static void pdp_mosaic_background(t_pdp_mosaic *x ) +{ + x->x_snapshot = 1; +} + +static void pdp_mosaic_free_ressources(t_pdp_mosaic *x) +{ + if ( x->x_diff != NULL ) freebytes( x->x_diff, (x->x_vsize + (x->x_vsize>>1))<<1 ); + if ( x->x_bdata != NULL ) freebytes( x->x_bdata, (( x->x_vsize + (x->x_vsize>>1))<<1)); +} + +static void pdp_mosaic_allocate(t_pdp_mosaic *x) +{ + int i; + + x->x_diff = (short int*) getbytes((x->x_vsize + (x->x_vsize>>1))<<1); + x->x_bdata = (short int *) getbytes((( x->x_vsize + (x->x_vsize>>1))<<1)); + if( !x->x_bdata || ! x->x_diff ) { + post( "pdp_mosaic : severe error : cannot allocate buffers" ); + } + +} + +/* check if there is a real difference with background image */ +static void pdp_mosaic_diff(t_pdp_mosaic *x, short int *src) +{ + int i; + int Yy=0, Yu=0, Yv=0; + int Yby=0, Ybu=0, Ybv=0; + short int *p=NULL; + short int *pb=NULL; + short int *r=NULL; + int v; + + p = src; + pb = x->x_bdata; + r = x->x_diff; + for(i=0; i<(x->x_vsize); i++) + { + Yy = (*p); + Yu = (*(p+x->x_vsize+(i>>2))); + if ( x->x_vsize+(x->x_vsize>>2)+(i>>2) > x->x_vsize+(x->x_vsize>>1) ) + { + post ("pdp_mosaic : overflow : offset=%d limit=%d", x->x_vsize+(x->x_vsize>>2)+(i>>2), + x->x_vsize+(x->x_vsize>>1) ); + return; + } + Yv = (*(p+x->x_vsize+(x->x_vsize>>2)+(i>>2))); + Yby = (*pb); + Ybu = (*(pb+x->x_vsize+(i>>2))); + Ybv = (*(pb+x->x_vsize+(x->x_vsize>>2)+(i>>2))); + if ( !r ) { post( "pdp_mosaic : hey, buffers are not allocated !!" ); return; }; + *r = ( (Yy - Yby) + (Yu - Ybu) + (Yv - Ybv) ); + r++; + } + +} + +static void pdp_mosaic_process_yv12(t_pdp_mosaic *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); + int i; + + int px=0, py=0, xx, yy, y, u, v; + int count; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_mosaic_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_mosaic_allocate(x); + post( "pdp_mosaic : reallocated buffers" ); + } + + if ( x->x_bdata && x->x_snapshot ) + { + x->x_snapshot = 0; + memcpy( x->x_bdata, data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + pdp_mosaic_diff(x, data); + + memcpy( newdata, data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + + for(py=0; py<(x->x_vheight-x->x_ssize-1); py+=x->x_ssize) + { + for(px=0; px<(x->x_vwidth-x->x_ssize-1); px+=x->x_ssize) + { + count = 0; + for(yy=0; yy<x->x_ssize; yy++) + { + for(xx=0; xx<x->x_ssize; xx++) + { + count += *(x->x_diff + (py+yy)*x->x_vwidth + (px+xx) ); + count += *(x->x_diff + x->x_vsize + (((py+yy)*x->x_vwidth)>>2) + ((px+xx)>>1) ); + count += *(x->x_diff + x->x_vsize + (x->x_vsize>>2) + (((py+yy)*x->x_vwidth)>>2) + ((px+xx)>>1) ); + } + } + if(count > x->x_censor_level) + { + // post( "pdp_mosaic : censored" ); + y = *(data + (py+3)*x->x_vwidth + (px+3)); + u = *(data + x->x_vsize + (((py+3)*x->x_vwidth)>>2) + ((px+3)>>1) ); + v = *(data + x->x_vsize + (((py+3)*x->x_vwidth)>>2) + ((px+3)>>1) ); + for(yy=0; yy<x->x_ssize; yy++) + { + for(xx=0; xx<x->x_ssize; xx++) + { + *(newdata + (py+yy)*x->x_vwidth + (px+xx)) = y; + *(newdata + x->x_vsize + (((py+yy)*x->x_vwidth)>>2) + ((px+xx)>>1) ) = u; + *(newdata + x->x_vsize + (x->x_vsize>>2) + (((py+yy)*x->x_vwidth)>>2) + ((px+xx)>>1) ) = v; + } + } + } + } + } + + return; +} + +static void pdp_mosaic_sendpacket(t_pdp_mosaic *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_mosaic_process(t_pdp_mosaic *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_mosaic_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_mosaic_process_yv12, pdp_mosaic_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + break; + + default: + /* don't know the type, so dont pdp_mosaic_process */ + break; + + } + } +} + +static void pdp_mosaic_input_0(t_pdp_mosaic *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_mosaic_process(x); + } +} + +static void pdp_mosaic_free(t_pdp_mosaic *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_mosaic_free_ressources(x); +} + +t_class *pdp_mosaic_class; + +void *pdp_mosaic_new(void) +{ + int i; + + t_pdp_mosaic *x = (t_pdp_mosaic *)pd_new(pdp_mosaic_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_bang, gensym("background")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("level")); + + 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_vsize = -1; + x->x_snapshot = 1; + x->x_ssize = 8; // square size + x->x_censor_level = CENSOR_LEVEL; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_mosaic_setup(void) +{ +// post( pdp_mosaic_version ); + pdp_mosaic_class = class_new(gensym("pdp_mosaic"), (t_newmethod)pdp_mosaic_new, + (t_method)pdp_mosaic_free, sizeof(t_pdp_mosaic), 0, A_NULL); + + class_addmethod(pdp_mosaic_class, (t_method)pdp_mosaic_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_mosaic_class, (t_method)pdp_mosaic_background, gensym("background"), A_NULL); + class_addmethod(pdp_mosaic_class, (t_method)pdp_mosaic_level, gensym("level"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_nervous.c b/modules/pdp_nervous.c new file mode 100644 index 0000000..df76821 --- /dev/null +++ b/modules/pdp_nervous.c @@ -0,0 +1,283 @@ +/* + * 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 adaptation of warp effect from effectv + * Copyright (C) 2002 TANNENBAUM Edo + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define DEFAULT_PLANES 32 + +static int fastrand_val=0; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + + +static char *pdp_nervous_version = "pdp_nervous: version 0.1, port of nervous from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +static char* the_wave_table = NULL; + +typedef struct pdp_nervous_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_mode; + short int *x_buffer; + short int **x_planetable; + t_int x_planes; + t_int x_plane; + t_int x_stock; + t_int x_timer; + t_int x_stride; + t_int x_readplane; + +} t_pdp_nervous; + +static void pdp_nervous_mode(t_pdp_nervous *x, t_floatarg fmode ) +{ + if ( ( fmode == 0 ) || ( fmode == 1 ) ) + { + x->x_mode = (int)fmode; + } +} + +static void pdp_nervous_free_ressources(t_pdp_nervous *x) +{ + if ( x->x_buffer ) free ( x->x_buffer ); + if ( x->x_planetable ) free ( x->x_planetable ); +} + +static void pdp_nervous_allocate(t_pdp_nervous *x) +{ + int i; + + // allocate space for the frame buffers. A lot of memory is required - + // with the default settings, it totals nearly 5 megs. + x->x_buffer = (short int *) getbytes ( ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ) * 2 * x->x_planes); + x->x_planetable = (short int**) getbytes ( x->x_planes*sizeof(short int*) ); + + // set up the array of pointers to the frame buffers + for(i=0;i<x->x_planes;i++) + { + x->x_planetable[i] = &x->x_buffer[ ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ) * i]; + } + + if ( !x->x_buffer ) + { + post( "pdp_nervous : severe error : cannot allocate buffers !!! "); + return; + } +} + +static void pdp_nervous_planes(t_pdp_nervous *x, t_floatarg fplanes ) +{ + if ( ( fplanes > 1 ) && ( fplanes < 100 ) ) + { + pdp_nervous_free_ressources(x); + x->x_planes = (int)fplanes; + x->x_plane = x->x_planes-1; + x->x_readplane = 0; + x->x_stock = 0; + pdp_nervous_allocate(x); + } +} + +static void pdp_nervous_process_yv12(t_pdp_nervous *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); + int i; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_nervous_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; + x->x_plane = x->x_planes-1; + pdp_nervous_allocate(x); + post( "pdp_nervous : reallocated buffers" ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + memcpy(x->x_planetable[x->x_plane], data, ( x->x_vsize + (x->x_vsize>>1) ) << 1 ); + if(x->x_stock < x->x_planes) { + x->x_stock++; + } + + if(x->x_mode) + { + if(x->x_timer) + { + x->x_readplane = x->x_readplane + x->x_stride; + while(x->x_readplane < 0) x->x_readplane += x->x_stock; + while(x->x_readplane >= x->x_stock) x->x_readplane -= x->x_stock; + x->x_timer--; + } + else + { + x->x_readplane = inline_fastrand() % x->x_stock; + if ( x->x_readplane < 0 ) x->x_readplane = 0; + x->x_stride = inline_fastrand() % 5 - 2; + if(x->x_stride >= 0) x->x_stride++; + x->x_timer = inline_fastrand() % 6 + 2; + } + } + else + { + if(x->x_stock > 0) x->x_readplane = ( inline_fastrand() % x->x_stock ); + if ( x->x_readplane < 0 ) x->x_readplane = 0; + } + memcpy(newdata, x->x_planetable[x->x_readplane], ( x->x_vsize + ( x->x_vsize>>1) ) << 1 ); + x->x_plane = ( x->x_plane + 1 ) % x->x_planes; + + return; +} + +static void pdp_nervous_sendpacket(t_pdp_nervous *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_nervous_process(t_pdp_nervous *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_nervous_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_nervous_process_yv12, pdp_nervous_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_nervous_process */ + break; + + } + } +} + +static void pdp_nervous_input_0(t_pdp_nervous *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_nervous_process(x); + } +} + +static void pdp_nervous_free(t_pdp_nervous *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_nervous_free_ressources(x); +} + +t_class *pdp_nervous_class; + +void *pdp_nervous_new(void) +{ + int i; + + t_pdp_nervous *x = (t_pdp_nervous *)pd_new(pdp_nervous_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("planes")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("mode")); + + 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_mode = 0; + x->x_buffer = NULL; + x->x_planes = DEFAULT_PLANES; + x->x_readplane = 0; + x->x_stock = 0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_nervous_setup(void) +{ +// post( pdp_nervous_version ); + pdp_nervous_class = class_new(gensym("pdp_nervous"), (t_newmethod)pdp_nervous_new, + (t_method)pdp_nervous_free, sizeof(t_pdp_nervous), 0, A_NULL); + + class_addmethod(pdp_nervous_class, (t_method)pdp_nervous_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_nervous_class, (t_method)pdp_nervous_mode, gensym("mode"), A_FLOAT, A_NULL); + class_addmethod(pdp_nervous_class, (t_method)pdp_nervous_planes, gensym("planes"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_noquark.c b/modules/pdp_noquark.c new file mode 100644 index 0000000..399b11f --- /dev/null +++ b/modules/pdp_noquark.c @@ -0,0 +1,279 @@ +/* + * 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 adaptation of warp effect from effectv + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define DEFAULT_PLANES 16 +#define DEFAULT_TOLERANCE 5 + +static int fastrand_val=0; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + + +static char *pdp_noquark_version = "pdp_noquark: version 0.1, port of quark from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +static char* the_wave_table = NULL; + +typedef struct pdp_noquark_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; + short int *x_buffer; + short int **x_planetable; + t_int x_plane; + t_int x_planes; + t_int x_tolerance; + +} t_pdp_noquark; + +static void pdp_noquark_free_ressources(t_pdp_noquark *x) +{ + if ( x->x_buffer ) free ( x->x_buffer ); + if ( x->x_planetable ) free ( x->x_planetable ); +} + +static void pdp_noquark_allocate(t_pdp_noquark *x) +{ + int i; + + // allocate space for the frame buffers. A lot of memory is required - + // with the default settings, it totals nearly 5 megs. + x->x_buffer = (short int *) getbytes ( ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ) * 2 * x->x_planes); + x->x_planetable = (short int**) getbytes( x->x_planes*sizeof( short int* ) ); + + // set up the array of pointers to the frame buffers + for(i=0;i<x->x_planes;i++) + { + x->x_planetable[i] = &x->x_buffer[ ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ) * i]; + } + + if ( !x->x_buffer ) + { + post( "pdp_noquark : severe error : cannot allocate buffers !!! "); + return; + } +} + +static void pdp_noquark_planes(t_pdp_noquark *x, t_floatarg fplanes) +{ + if ( ( (int)fplanes > 1 ) && ( (int)fplanes < 100 ) ) + { + pdp_noquark_free_ressources(x); + x->x_planes = (int) fplanes; + x->x_plane=x->x_planes-1; + pdp_noquark_allocate(x); + } +} + +static void pdp_noquark_tolerance(t_pdp_noquark *x, t_floatarg ftolerance) +{ + if ( (int)ftolerance > 1 ) + { + x->x_tolerance = (int) ftolerance; + } +} + +static void pdp_noquark_process_yv12(t_pdp_noquark *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 px, py, cf, diff, rx, ry; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_noquark_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; + x->x_plane = x->x_planes-1; + pdp_noquark_allocate(x); + post( "pdp_noquark : reallocated buffers" ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + memcpy(x->x_planetable[x->x_plane], data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + for(py=0; py<x->x_vheight; py++) + { + for(px=0; px<x->x_vwidth; px++) + { + cf = (x->x_plane + (inline_fastrand()>>24))&(x->x_planes-1); + diff = (((x->x_planetable[cf])[py*x->x_vwidth+px] - data[py*x->x_vwidth+px])>>7) + + ((((x->x_planetable[cf])[x->x_vsize+((py>>1)*(x->x_vwidth>>1)+(px>>1))] - + data[x->x_vsize+((py>>1)*(x->x_vwidth>>1)+(px>>1))] )>>8)) + + ((((x->x_planetable[cf])[x->x_vsize+(x->x_vsize>>2)+((py>>1)*(x->x_vwidth>>1)+(px>>1))] - + data[x->x_vsize+(x->x_vsize>>2)+((py>>1)*(x->x_vwidth>>1)+(px>>1))] )>>8)); + if ( abs ( diff ) > x->x_tolerance ) + { + newdata[py*x->x_vwidth+px] = data[py*x->x_vwidth+px]; + newdata[x->x_vsize+((py>>1)*(x->x_vwidth>>1)+(px>>1))] = data[x->x_vsize+((py>>1)*(x->x_vwidth>>1)+(px>>1))]; + newdata[x->x_vsize+(x->x_vsize>>2)+((py>>1)*(x->x_vwidth>>1)+(px>>1))] = + data[x->x_vsize+(x->x_vsize>>2)+((py>>1)*(x->x_vwidth>>1)+(px>>1))]; + } + else + { + rx = inline_fastrand()&x->x_vwidth; + ry = inline_fastrand()&x->x_vheight; + newdata[py*x->x_vwidth+px] = data[ry*x->x_vwidth+rx]; + newdata[x->x_vsize+((py>>1)*(x->x_vwidth>>1)+(px>>1))] = + data[x->x_vsize+((ry>>1)*(x->x_vwidth>>1)+(rx>>1))]; + newdata[x->x_vsize+(x->x_vsize>>2)+((py>>1)*(x->x_vwidth>>1)+(px>>1))] = + data[x->x_vsize+(x->x_vsize>>2)+((ry>>1)*(x->x_vwidth>>1)+(rx>>1))]; + } + /* The reason why I use high order 8 bits is written in utils.c + (or, 'man rand') */ + } + } + x->x_plane--; + if ( x->x_plane < 0 ) x->x_plane = x->x_planes-1; + + return; +} + +static void pdp_noquark_sendpacket(t_pdp_noquark *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_noquark_process(t_pdp_noquark *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_noquark_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_noquark_process_yv12, pdp_noquark_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_noquark_process */ + break; + + } + } +} + +static void pdp_noquark_input_0(t_pdp_noquark *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_noquark_process(x); + } +} + +static void pdp_noquark_free(t_pdp_noquark *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_noquark_free_ressources(x); +} + +t_class *pdp_noquark_class; + +void *pdp_noquark_new(void) +{ + int i; + + t_pdp_noquark *x = (t_pdp_noquark *)pd_new(pdp_noquark_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("planes")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("tolerance")); + + 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_planes = DEFAULT_PLANES; + x->x_tolerance = DEFAULT_TOLERANCE; + + x->x_buffer = NULL; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_noquark_setup(void) +{ +// post( pdp_noquark_version ); + pdp_noquark_class = class_new(gensym("pdp_noquark"), (t_newmethod)pdp_noquark_new, + (t_method)pdp_noquark_free, sizeof(t_pdp_noquark), 0, A_NULL); + + class_addmethod(pdp_noquark_class, (t_method)pdp_noquark_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_noquark_class, (t_method)pdp_noquark_planes, gensym("planes"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_noquark_class, (t_method)pdp_noquark_tolerance, gensym("tolerance"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_o.c b/modules/pdp_o.c new file mode 100644 index 0000000..34b2b18 --- /dev/null +++ b/modules/pdp_o.c @@ -0,0 +1,589 @@ +/* + * 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 streaming emitter + * -- compressed with a very simple codec ( smoothing + huffman + bz2 ) + * It sends PDP packet to a pdp_i receiving object + */ + + +#include "pdp.h" +#include "pdp_streaming.h" +#include <math.h> +#include <time.h> +#include <unistd.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <quicktime/quicktime.h> +#include <quicktime/colormodels.h> +#include <bzlib.h> // bz2 compression routines + +#define DEFAULT_FRAME_RATE 25 + +static char *pdp_o_version = "pdp_o: version 0.1, a video stream emitter, written by ydegoyon@free.fr"; + +typedef struct pdp_o_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + t_int x_hsize; // size of huffman coded data + + t_int x_packet0; + t_int x_dropped; + t_int x_queue_id; + + t_int x_emitflag; + + /* connection data */ + int x_fd; // info about connection status + t_int x_framessent; + t_int x_framesdropped; + t_int x_secondcount; + t_int x_bandwidthcount; + t_int x_cursec; + t_int x_framerate; + t_int x_smoothing; + + t_hpacket x_hpacket; // packet header + + short int *x_previous_frame; + char *x_diff_frame; + char *x_hdata; // huffman coded data + char *x_cdata; // compressed data to be sent + + t_outlet *x_connection_status; // indicates status + t_outlet *x_frames; // outlet for the number of frames emitted + t_outlet *x_framesd; // outlet for the number of frames dropped + t_outlet *x_bandwidth; // outlet for bandwidth + +} t_pdp_o; + +static void pdp_o_free_ressources(t_pdp_o *x) +{ + if ( x->x_diff_frame ) freebytes( x->x_diff_frame, x->x_vsize + (x->x_vsize>>1) ); + if ( x->x_previous_frame ) freebytes( x->x_previous_frame, (x->x_vsize + (x->x_vsize>>1))<<1 ); + if ( x->x_cdata ) freebytes( x->x_cdata, (x->x_vsize + (x->x_vsize>>1))*1.01+600 ); // size is taken from bzlib manual + if ( x->x_hdata ) freebytes( x->x_hdata, ((x->x_vsize + (x->x_vsize>>1))<<1) ); // size is taken from bzlib manual +} + +static void pdp_o_allocate(t_pdp_o *x) +{ + x->x_diff_frame = (char*) getbytes( x->x_vsize + (x->x_vsize>>1) ); + memset( x->x_diff_frame, 0x00, x->x_vsize + (x->x_vsize>>1) ); + x->x_previous_frame = (short int*) getbytes( (x->x_vsize + (x->x_vsize>>1))<<1 ); + memset( x->x_previous_frame, 0x00, (x->x_vsize + (x->x_vsize>>1))<<1 ); + x->x_cdata = (char*) getbytes( (x->x_vsize + (x->x_vsize>>1))*1.01+600 ); + memset( x->x_cdata, 0x00, (x->x_vsize + (x->x_vsize>>1))*1.01+600 ); + x->x_hdata = (char*) getbytes( (x->x_vsize + (x->x_vsize>>1))<<1 ); + memset( x->x_hdata, 0x00, (x->x_vsize + (x->x_vsize>>1))<<1 ); + strcpy( x->x_hpacket.tag, PDP_PACKET_TAG ); +} + + /* disconnect from receiver */ +static void pdp_o_disconnect(t_pdp_o *x) +{ + int ret; + + if(x->x_fd >= 0) /* close socket */ + { + close(x->x_fd); + x->x_fd = -1; + outlet_float( x->x_connection_status, 0 ); + post("pdp_o : connection closed"); + } + +} + + /* set the emission frame rate */ +static void pdp_o_framerate(t_pdp_o *x, t_floatarg fframerate) +{ + if ( fframerate > 1 ) + { + x->x_framerate = (int)fframerate; + } +} + + /* set the smoothing factor */ +static void pdp_o_smoothing(t_pdp_o *x, t_floatarg fsmoothing) +{ + if ( ( fsmoothing >= 0 ) && ( fsmoothing < 255 ) ) + { + x->x_smoothing = (int)fsmoothing; + } +} + + /* smoothe image */ +static void pdp_o_smoothe(t_pdp_o *x, short int *source, t_int size ) +{ + t_int i; + char evalue, eevalue; + char value; + + if ( x->x_smoothing == 0 ) return; + + for(i=0;i<size;i++) + { + value = (source[i])>>7; + evalue = (value/x->x_smoothing)*x->x_smoothing; + eevalue = ((value/x->x_smoothing)+1)*x->x_smoothing; + + if ( abs( value - evalue ) < x->x_smoothing/2 ) + { + source[i] = evalue<<7; + } + else + { + source[i] = eevalue<<7; + } + } + + for(i=size;i<(size+(size>1));i++) + { + value = ((source[i])>>8)+128; + evalue = (value/x->x_smoothing)*x->x_smoothing; + eevalue = ((value/x->x_smoothing)+1)*x->x_smoothing; + + if ( abs( value - evalue ) < x->x_smoothing/2 ) + { + source[i] = (evalue)<<8; + } + else + { + source[i] = (eevalue)<<8; + } + } +} + + /* huffman coding */ +static int pdp_o_huffman(t_pdp_o *x, char *source, char *dest, t_int size, t_int *csize ) +{ + t_int i; + char value = source[0]; + char count = 0; + t_int tcount=0; + char *pcount=dest; + char *pvalue=dest+1; + + *(csize)=2; + dest++; + for( i=0; i<size; i++) + { + if ( (source[i] == value) && (count<127) ) + { + count++; + } + else + { + value=source[i]; + *(pcount)=count; + *(pvalue) = value; + tcount+=count; + count=1; + pcount+=2; + pvalue+=2; + *(csize)+=2; + } + } + *(pcount)=count; + tcount+=count; + + // huffman is no good for that image + if ( (*csize) >= size ) + { + *csize = size; + memcpy( dest, source, size ); + return REGULAR; + } + else + { + // post( "pdp_o : huffman : compression ratio %d/%d : %f (total count=%d)", size, (*csize), + // (t_float)size/(t_float)(*csize), tcount ); + return HUFFMAN; + } +} + + + /* connect to a receiver on <hostname> <port> */ +static void pdp_o_connect(t_pdp_o *x, t_symbol *shostname, t_floatarg fportno) +{ + struct sockaddr_in csocket; + struct hostent *hp; + int portno = fportno; /* get port from message box */ + + /* variables used for communication with the receiver */ + const char *buf = 0; + unsigned int len; + int sockfd; + int ret; + + // close previous connection if existing + pdp_o_disconnect(x); + + sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) + { + error("pdp_o : error while attempting to create socket"); + perror( "socket" ); + return; + } + + /* connect socket using hostname provided in command line */ + csocket.sin_family = AF_INET; + hp = gethostbyname(shostname->s_name); + if (hp == 0) + { + post("pdp_o : ip address of receiver could not be found"); + perror( "gethostbyname" ); + close(sockfd); + return; + } + memcpy((char *)&csocket.sin_addr, (char *)hp->h_addr, hp->h_length); + + /* assign client port number */ + csocket.sin_port = htons((unsigned short)fportno); + + /* try to connect. */ + post("pdp_o : connecting to port %d", (int)fportno); + if (connect(sockfd, (struct sockaddr *) &csocket, sizeof (csocket)) < 0) + { + error("mp3streamout~: connection failed!\n"); + perror( "connect" ); + close(sockfd); + return; + } + + x->x_fd = sockfd; + x->x_framessent = 0; + x->x_framesdropped = 0; + outlet_float( x->x_connection_status, 1 ); + post( "pdp_o : connected to receiver : %s:%d", shostname->s_name, (int)fportno ); + +} + + /* refresh means forcing the emission of a full frame */ +static void pdp_o_refresh(t_pdp_o *x) +{ + strcpy( x->x_hpacket.tag, PDP_PACKET_TAG ); + memset( x->x_previous_frame, 0x00, (x->x_vsize + (x->x_vsize>>1))<<1 ); +} + + /* start emitting */ +static void pdp_o_start(t_pdp_o *x) +{ + if ( x->x_emitflag == 1 ) { + post("pdp_o : start received but emission is started ... ignored."); + return; + } + + x->x_emitflag = 1; + post("pdp_o : emission started"); +} + + /* stop emitting */ +static void pdp_o_stop(t_pdp_o *x) +{ + if ( x->x_emitflag == 0 ) { + post("mp3write~: stop received but emission is stopped ... ignored."); + return; + } + + x->x_emitflag = 0; + + post("pdp_o : emission stopped"); +} + +static void pdp_o_process_yv12(t_pdp_o *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_int count, i, ret=0; + + /* setting video track */ + if ( x->x_emitflag ) + { + if ( ( (int)(header->info.image.width) != x->x_vwidth ) || + ( (int)(header->info.image.height) != x->x_vheight ) + ) + { + pdp_o_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_o_allocate(x); + } + + // smoothe image + pdp_o_smoothe(x, data, x->x_vsize ); + + for ( i=0; i<x->x_vsize; i++ ) + { + t_int downvalue; + + downvalue = (data[i]>>7); + if ( ( downvalue > 128 ) || + ( downvalue < -128 ) ) + { + // post( "pdp_o : y value out of range : %d", downvalue ); + } + if ( data[i] != x->x_previous_frame[i] ) + { + x->x_diff_frame[i] = (char)downvalue; + } + else + { + x->x_diff_frame[i] = 0; + } + } + for ( i=x->x_vsize; i<(x->x_vsize+(x->x_vsize>>1)); i++ ) + { + t_int downvalue; + + downvalue = (data[i]>>8); + if ( ( downvalue > 128 ) || + ( downvalue < -128 ) ) + { + // post( "pdp_o : y value out of range : %d", downvalue ); + } + if ( data[i] != x->x_previous_frame[i] ) + { + x->x_diff_frame[i] = (char)downvalue; + } + else + { + x->x_diff_frame[i] = 0; + } + } + + x->x_hpacket.width = x->x_vwidth; + x->x_hpacket.height = x->x_vheight; + if ( gettimeofday(&x->x_hpacket.etime, NULL) == -1) + { + post("pdp_o : could not set emit time" ); + } + if ( x->x_hpacket.etime.tv_sec != x->x_cursec ) + { + x->x_cursec = x->x_hpacket.etime.tv_sec; + x->x_secondcount = 0; + x->x_bandwidthcount = 0; + } + + // do not send the frame if too many frames + // have been sent in the current second + if ( x->x_secondcount < x->x_framerate ) + { + + // try a huffman coding + x->x_hpacket.encoding = pdp_o_huffman(x, x->x_diff_frame, x->x_hdata, x->x_vsize+(x->x_vsize>>1), &x->x_hsize ); + + x->x_hpacket.clength = (x->x_vsize+(x->x_vsize>>1))*1.01+600; + // compress the graphic data + if ( ( ret = BZ2_bzBuffToBuffCompress( x->x_cdata, + &x->x_hpacket.clength, + (char*) x->x_hdata, + x->x_hsize, + 9, 0, 0 ) ) == BZ_OK ) + { + // post( "pdp_o : bz2 compression (%d)->(%d)", x->x_hsize, x->x_hpacket.clength ); + + x->x_secondcount++; + + // memorize last emitted frame + memcpy( x->x_previous_frame, data, (x->x_vsize+(x->x_vsize>>1))<<1 ); + + // send header + count = send(x->x_fd, &x->x_hpacket, sizeof(x->x_hpacket), MSG_NOSIGNAL); + if(count < 0) + { + error("pdp_o : could not send encoded data to the peer (%d)", count); + perror( "send" ); + pdp_o_disconnect(x); + } + else + { + if((count > 0)&&(count != sizeof(x->x_hpacket))) + { + error("pdp_o : %d bytes skipped", sizeof(x->x_hpacket) - count); + } + x->x_bandwidthcount += count/1024; + } + + // send data + count = send(x->x_fd, x->x_cdata, x->x_hpacket.clength, MSG_NOSIGNAL); + if(count < 0) + { + error("pdp_o : could not send encoded data to the peer (%d)", count); + perror( "send" ); + pdp_o_disconnect(x); + } + else + { + if((count > 0)&&(count != (int)x->x_hpacket.clength)) + { + error("pdp_o : %d bytes skipped", x->x_hpacket.clength - count); + } + ++x->x_framessent; + x->x_bandwidthcount += count/1024; + } + + // unless after a refresh, next packets are diffs + strcpy( x->x_hpacket.tag, PDP_PACKET_DIFF ); + + } + else + { + post( "pdp_o : bz2 compression failed (ret=%d)", ret ); + } + } + else + { + ++x->x_framesdropped; + // post( "pdp_o : frames dropped ( framerate limit )" ); + } + } + + return; +} + +static void pdp_o_killpacket(t_pdp_o *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; +} + +static void pdp_o_process(t_pdp_o *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_o_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_o_process_yv12, pdp_o_killpacket, &x->x_queue_id); + outlet_float( x->x_framesd, x->x_framesdropped ); + outlet_float( x->x_frames, x->x_framessent ); + outlet_float( x->x_bandwidth, x->x_bandwidthcount ); + 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_o_process */ + break; + + } + } + +} + +static void pdp_o_input_0(t_pdp_o *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_o_process(x); + + } +} + +static void pdp_o_free(t_pdp_o *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + // close connection if existing + pdp_o_disconnect(x); +} + +t_class *pdp_o_class; + +void *pdp_o_new(void) +{ + int i; + + t_pdp_o *x = (t_pdp_o *)pd_new(pdp_o_class); + x->x_connection_status = outlet_new (&x->x_obj, &s_float); + x->x_frames = outlet_new (&x->x_obj, &s_float); + x->x_framesd = outlet_new (&x->x_obj, &s_float); + x->x_bandwidth = outlet_new (&x->x_obj, &s_float); + + x->x_packet0 = -1; + x->x_queue_id = -1; + + x->x_framessent = 0; + x->x_framerate = DEFAULT_FRAME_RATE; + x->x_smoothing = 0; + x->x_secondcount = 0; + x->x_bandwidthcount = 0; + x->x_cursec = 0; + x->x_fd = -1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_o_setup(void) +{ + post( pdp_o_version ); + pdp_o_class = class_new(gensym("pdp_o"), (t_newmethod)pdp_o_new, + (t_method)pdp_o_free, sizeof(t_pdp_o), 0, A_NULL); + + class_addmethod(pdp_o_class, (t_method)pdp_o_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_o_class, (t_method)pdp_o_connect, gensym("connect"), A_SYMBOL, A_FLOAT, A_NULL); + class_addmethod(pdp_o_class, (t_method)pdp_o_disconnect, gensym("disconnect"), A_NULL); + class_addmethod(pdp_o_class, (t_method)pdp_o_start, gensym("start"), A_NULL); + class_addmethod(pdp_o_class, (t_method)pdp_o_stop, gensym("stop"), A_NULL); + class_addmethod(pdp_o_class, (t_method)pdp_o_refresh, gensym("refresh"), A_NULL); + class_addmethod(pdp_o_class, (t_method)pdp_o_framerate, gensym("framerate"), A_FLOAT, A_NULL); + class_addmethod(pdp_o_class, (t_method)pdp_o_smoothing, gensym("smoothing"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_puzzle.c b/modules/pdp_puzzle.c new file mode 100644 index 0000000..0c0520f --- /dev/null +++ b/modules/pdp_puzzle.c @@ -0,0 +1,421 @@ +/* + * 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 port of puzzle effect from EffecTV + * Originally written by Fukuchi Kentaro <nullset@dookie.net> + * Pd-fication by Yves Degoyon ( ydegoyon@free.fr ) + * The origin of PuzzleTV is ``Video Puzzle'' by Suutarou in 1993. + * It runs on Fujitsu FM-TOWNS. + */ + + +#include "pdp.h" +#include <math.h> + +#define DEFAULT_BLOCK_NUMBER 5 + +static unsigned int fastrand_val; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + +static char *pdp_puzzle_version = "pdp_puzzle: version 0.1, port of puzzle from EffecTV by Fukuchi Kentaro, adapted by ydegoyon@free.fr "; + +typedef struct pdp_puzzle_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + /* puzzle parameters */ + t_int *x_blockpos; + t_int *x_blockoffset; + t_int *x_ublockoffset; + t_int *x_vblockoffset; + t_int x_nbblocks; + t_int x_blockwidth; + t_int x_blockheight; + t_int x_blockw; + t_int x_blockh; + t_int x_blocknum; + t_int x_spacepos; + t_int x_spacex; + t_int x_spacey; + +} t_pdp_puzzle; + +static void pdp_puzzle_init_tables(t_pdp_puzzle *x) +{ + t_int i, a, b, c; + + for(i=0; i<x->x_blocknum; i++) + { + x->x_blockpos[i] = i; + } + for(i=0; i<20*x->x_blockw; i++) + { + /* the number of shuffling times is a rule of thumb. */ + a = inline_fastrand()%(x->x_blocknum-1); + b = inline_fastrand()%(x->x_blocknum-1); + if(a == b) b = (b+1)%(x->x_blocknum-1); + c = x->x_blockpos[a]; + x->x_blockpos[a] = x->x_blockpos[b]; + x->x_blockpos[b] = c; + } + x->x_spacepos = x->x_blocknum-1; + x->x_spacex = x->x_blockw-1; + x->x_spacey = x->x_blockh-1; + + return; +} + +static void pdp_puzzle_up(t_pdp_puzzle *x ) +{ + t_int tmp, nextpos=-1; + + if(x->x_spacey>0) + { + nextpos = x->x_spacepos - x->x_blockw; + x->x_spacey--; + } + if(nextpos>=0) + { + tmp = x->x_blockpos[x->x_spacepos]; + x->x_blockpos[x->x_spacepos] = x->x_blockpos[nextpos]; + x->x_blockpos[nextpos] = tmp; + x->x_spacepos = nextpos; + } +} + +static void pdp_puzzle_down(t_pdp_puzzle *x ) +{ + t_int tmp, nextpos=-1; + + if(x->x_spacey<x->x_blockh-1) + { + nextpos = x->x_spacepos + x->x_blockw; + x->x_spacey++; + } + if(nextpos>=0) + { + tmp = x->x_blockpos[x->x_spacepos]; + x->x_blockpos[x->x_spacepos] = x->x_blockpos[nextpos]; + x->x_blockpos[nextpos] = tmp; + x->x_spacepos = nextpos; + } +} + +static void pdp_puzzle_left(t_pdp_puzzle *x ) +{ + t_int tmp, nextpos=-1; + + if(x->x_spacex>0) + { + nextpos = x->x_spacepos - 1; + x->x_spacex--; + } + if(nextpos>=0) + { + tmp = x->x_blockpos[x->x_spacepos]; + x->x_blockpos[x->x_spacepos] = x->x_blockpos[nextpos]; + x->x_blockpos[nextpos] = tmp; + x->x_spacepos = nextpos; + } +} + +static void pdp_puzzle_right(t_pdp_puzzle *x ) +{ + t_int tmp, nextpos=-1; + + if(x->x_spacex<x->x_blockw-1) + { + nextpos = x->x_spacepos + 1; + x->x_spacex++; + } + if(nextpos>=0) + { + tmp = x->x_blockpos[x->x_spacepos]; + x->x_blockpos[x->x_spacepos] = x->x_blockpos[nextpos]; + x->x_blockpos[nextpos] = tmp; + x->x_spacepos = nextpos; + } +} + +static void pdp_puzzle_free_ressources(t_pdp_puzzle *x) +{ + if ( x->x_blockpos ) freebytes( x->x_blockpos, x->x_blocknum*sizeof(int) ); + if ( x->x_blockoffset ) freebytes( x->x_blockoffset, x->x_blocknum*sizeof(int) ); + if ( x->x_ublockoffset ) freebytes( x->x_ublockoffset, x->x_blocknum*sizeof(int) ); + if ( x->x_vblockoffset ) freebytes( x->x_vblockoffset, x->x_blocknum*sizeof(int) ); +} + +static void pdp_puzzle_allocate(t_pdp_puzzle *x) +{ + t_int px, py; + + x->x_blockwidth = x->x_vwidth / x->x_nbblocks; + x->x_blockheight = x->x_vheight / x->x_nbblocks; + x->x_blockw = x->x_nbblocks; + x->x_blockh = x->x_nbblocks; + x->x_blocknum = x->x_blockw * x->x_blockh; + + x->x_blockpos = (int *) getbytes( x->x_blocknum*sizeof(int) ); + x->x_blockoffset = (int *) getbytes( x->x_blocknum*sizeof(int) ); + x->x_ublockoffset = (int *) getbytes( x->x_blocknum*sizeof(int) ); + x->x_vblockoffset = (int *) getbytes( x->x_blocknum*sizeof(int) ); + if( x->x_blockpos == NULL || x->x_blockoffset == NULL || + x->x_ublockoffset == NULL || x->x_vblockoffset == NULL ) + { + post( "pdp_puzzle : severe error : cannot allocate buffers !!! "); + return; + } + + for(py=0; py<x->x_blockh; py++) + { + for(px=0; px<x->x_blockw; px++) + { + x->x_blockoffset[py*x->x_blockw+px] = py*x->x_blockheight*x->x_vwidth + px*x->x_blockwidth; + x->x_vblockoffset[py*x->x_blockw+px] = x->x_vsize + (py*x->x_blockheight>>1)*(x->x_vwidth>>1) + (px*x->x_blockwidth>>1); + x->x_ublockoffset[py*x->x_blockw+px] = x->x_vsize + (x->x_vsize>>2) + (py*x->x_blockheight>>1)*(x->x_vwidth>>1) + (px*x->x_blockwidth>>1); + } + } +} + +static void pdp_puzzle_nbblocks(t_pdp_puzzle *x, t_floatarg fnbblocks ) +{ + if ( ( fnbblocks > 1 ) && ( fnbblocks < x->x_vwidth/10 ) ) + { + x->x_nbblocks = fnbblocks; + post( "pdp_puzzle : number of blocks set to : %d", x->x_nbblocks ); + pdp_puzzle_free_ressources(x); + pdp_puzzle_allocate(x); + pdp_puzzle_init_tables(x); + } +} + +static void pdp_puzzle_process_yv12(t_pdp_puzzle *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 px, py, xx, yy, i; + short int *pY, *qY, *pU, *pV, *qU, *qV; + + /* allocate all ressources */ + if ( ((int)header->info.image.width != x->x_vwidth) || + ((int)header->info.image.height != x->x_vheight) ) + { + pdp_puzzle_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_puzzle_allocate(x); + post( "pdp_puzzle : reallocated buffers" ); + pdp_puzzle_init_tables(x); + post( "pdp_puzzle : initialized tables" ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + i = 0; + for(py=0; py<x->x_blockh; py++) + { + for(px=0; px<x->x_blockw; px++) + { + pY = &data[x->x_blockoffset[x->x_blockpos[i]]]; + pU = &data[x->x_ublockoffset[x->x_blockpos[i]]]; + pV = &data[x->x_vblockoffset[x->x_blockpos[i]]]; + qY = &newdata[x->x_blockoffset[i]]; + qU = &newdata[x->x_ublockoffset[i]]; + qV = &newdata[x->x_vblockoffset[i]]; + if(x->x_spacepos == i) + { + for(yy=0; yy<x->x_blockheight; yy++) + { + for(xx=0; xx<x->x_blockwidth; xx++) + { + *(qY++) = 0; + if ( (xx%2==0) && (yy%2==0) ) + { + *(qU++) = 0; + *(qV++) = 0; + } + } + qY+=x->x_vwidth-x->x_blockwidth; + if ( yy%2==0 ) + { + qU+=(x->x_vwidth-x->x_blockwidth)>>1; + qV+=(x->x_vwidth-x->x_blockwidth)>>1; + } + } + } + else + { + for(yy=0; yy<x->x_blockheight; yy++) + { + for(xx=0; xx<x->x_blockwidth; xx++) + { + *(qY++) = *(pY++); + if ( (xx%2==0) && (yy%2==0) ) + { + *(qU++) = *(pU++); + *(qV++) = *(pV++); + } + } + qY+=x->x_vwidth-x->x_blockwidth; + pY+=x->x_vwidth-x->x_blockwidth; + if ( yy%2==0 ) + { + qU+=(x->x_vwidth-x->x_blockwidth)>>1; + pU+=(x->x_vwidth-x->x_blockwidth)>>1; + qV+=(x->x_vwidth-x->x_blockwidth)>>1; + pV+=(x->x_vwidth-x->x_blockwidth)>>1; + } + } + } + i++; + } + } + + return; +} + +static void pdp_puzzle_sendpacket(t_pdp_puzzle *x) +{ + /* delete source 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_puzzle_process(t_pdp_puzzle *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_puzzle_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_puzzle_process_yv12, pdp_puzzle_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_puzzle_process */ + break; + + } + } + +} + +static void pdp_puzzle_input_0(t_pdp_puzzle *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_puzzle_process(x); + + } + +} + +static void pdp_puzzle_free(t_pdp_puzzle *x) +{ + int i; + + pdp_puzzle_free_ressources(x); + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_puzzle_class; + +void *pdp_puzzle_new(void) +{ + int i; + + t_pdp_puzzle *x = (t_pdp_puzzle *)pd_new(pdp_puzzle_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("nbblocks")); + + 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_nbblocks = DEFAULT_BLOCK_NUMBER; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_puzzle_setup(void) +{ +// post( pdp_puzzle_version ); + pdp_puzzle_class = class_new(gensym("pdp_puzzle"), (t_newmethod)pdp_puzzle_new, + (t_method)pdp_puzzle_free, sizeof(t_pdp_puzzle), 0, A_NULL); + + class_addmethod(pdp_puzzle_class, (t_method)pdp_puzzle_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_puzzle_class, (t_method)pdp_puzzle_up, gensym("up"), A_NULL); + class_addmethod(pdp_puzzle_class, (t_method)pdp_puzzle_down, gensym("down"), A_NULL); + class_addmethod(pdp_puzzle_class, (t_method)pdp_puzzle_left, gensym("left"), A_NULL); + class_addmethod(pdp_puzzle_class, (t_method)pdp_puzzle_right, gensym("right"), A_NULL); + class_addmethod(pdp_puzzle_class, (t_method)pdp_puzzle_nbblocks, gensym("nbblocks"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_quark.c b/modules/pdp_quark.c new file mode 100644 index 0000000..4942ce4 --- /dev/null +++ b/modules/pdp_quark.c @@ -0,0 +1,268 @@ +/* + * 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 adaptation of warp effect from effectv + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define DEFAULT_PLANES 16 + +static int fastrand_val=0; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + + +static char *pdp_quark_version = "pdp_quark: version 0.1, port of quark from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +static char* the_wave_table = NULL; + +typedef struct pdp_quark_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; + short int *x_buffer; + short int **x_planetable; + t_int x_plane; + t_int x_planes; + t_int x_tolerance; + +} t_pdp_quark; + +static void pdp_quark_free_ressources(t_pdp_quark *x) +{ + if ( x->x_buffer ) free ( x->x_buffer ); + if ( x->x_planetable ) free ( x->x_planetable ); +} + +static void pdp_quark_allocate(t_pdp_quark *x) +{ + int i; + + // allocate space for the frame buffers. A lot of memory is required - + // with the default settings, it totals nearly 5 megs. + x->x_buffer = (short int *) getbytes ( ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ) * 2 * x->x_planes); + x->x_planetable = (short int**) getbytes( x->x_planes*sizeof( short int* ) ); + + // set up the array of pointers to the frame buffers + for(i=0;i<x->x_planes;i++) + { + x->x_planetable[i] = &x->x_buffer[ ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ) * i]; + } + + if ( !x->x_buffer ) + { + post( "pdp_quark : severe error : cannot allocate buffers !!! "); + return; + } +} + +static void pdp_quark_planes(t_pdp_quark *x, t_floatarg fplanes) +{ + if ( ( (int)fplanes > 1 ) && ( (int)fplanes < 100 ) ) + { + pdp_quark_free_ressources(x); + x->x_planes = (int) fplanes; + x->x_plane=x->x_planes-1; + pdp_quark_allocate(x); + } +} + +static void pdp_quark_tolerance(t_pdp_quark *x, t_floatarg ftolerance) +{ + if ( (int)ftolerance > 1 ) + { + x->x_tolerance = (int) ftolerance; + } +} + +static void pdp_quark_process_yv12(t_pdp_quark *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, cf, diff; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_quark_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; + x->x_plane = x->x_planes-1; + pdp_quark_allocate(x); + post( "pdp_quark : reallocated buffers" ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + memcpy(x->x_planetable[x->x_plane], data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + for(i=0; i<x->x_vsize+(x->x_vsize>>1); i++) + { + cf = (x->x_plane + (inline_fastrand()>>24))&(x->x_planes-1); + if ( i<x->x_vsize ) + { + diff = ((x->x_planetable[cf])[i] - data[i])>>7; + } + else + { + diff = (((x->x_planetable[cf])[i] - data[i] )>>8)+128; + } + if ( abs ( diff ) > x->x_tolerance ) + { + newdata[i] = (x->x_planetable[cf])[i]; + } + else + { + newdata[i] = data[i]; + } + /* The reason why I use high order 8 bits is written in utils.c + (or, 'man rand') */ + } + x->x_plane--; + if ( x->x_plane < 0 ) x->x_plane = x->x_planes-1; + + return; +} + +static void pdp_quark_sendpacket(t_pdp_quark *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_quark_process(t_pdp_quark *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_quark_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_quark_process_yv12, pdp_quark_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_quark_process */ + break; + + } + } +} + +static void pdp_quark_input_0(t_pdp_quark *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_quark_process(x); + } +} + +static void pdp_quark_free(t_pdp_quark *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_quark_free_ressources(x); +} + +t_class *pdp_quark_class; + +void *pdp_quark_new(void) +{ + int i; + + t_pdp_quark *x = (t_pdp_quark *)pd_new(pdp_quark_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("planes")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("tolerance")); + + 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_planes = DEFAULT_PLANES; + + x->x_buffer = NULL; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_quark_setup(void) +{ +// post( pdp_quark_version ); + pdp_quark_class = class_new(gensym("pdp_quark"), (t_newmethod)pdp_quark_new, + (t_method)pdp_quark_free, sizeof(t_pdp_quark), 0, A_NULL); + + class_addmethod(pdp_quark_class, (t_method)pdp_quark_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_quark_class, (t_method)pdp_quark_planes, gensym("planes"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_quark_class, (t_method)pdp_quark_tolerance, gensym("tolerance"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_radioactiv.c b/modules/pdp_radioactiv.c new file mode 100644 index 0000000..c39dd4b --- /dev/null +++ b/modules/pdp_radioactiv.c @@ -0,0 +1,516 @@ +/* + * 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 adaptation of radioactiv effect from effectv + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define COLORS 32 +#define MAGIC_THRESHOLD 40 +#define RATIO 0.95 +#define DELTA (255/(COLORS/2-1)) + +#define VIDEO_HWIDTH (x->x_buf_width/2) +#define VIDEO_HHEIGHT (x->x_buf_height/2) + +static char *pdp_radioactiv_version = "pdp_radioactiv: version 0.1, port of radioactiv effect from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_radioactiv_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; + unsigned char *x_blurzoombuf; + t_int *x_blurzoomx; + t_int *x_blurzoomy; + t_int x_buf_width_blocks; + t_int x_buf_width; + t_int x_buf_height; + t_int x_buf_area; + t_int x_buf_margin_right; + t_int x_buf_margin_left; + t_int x_palette[COLORS]; + t_int x_mode; /* 0=normal 1=strobe 2=strobe2 3=trigger */ + t_int x_snap_time; + t_int x_snap_interval; + short int *x_snapframe; + short int *x_diff; + short int *x_bdata; + t_int x_snapshot; + +} t_pdp_radioactiv; + +static void pdp_radioactiv_free_ressources(t_pdp_radioactiv *x) +{ + if ( x->x_blurzoombuf ) freebytes ( x->x_blurzoombuf, x->x_buf_area*2 ); + if ( x->x_blurzoomx ) freebytes ( x->x_blurzoomx, x->x_buf_width*sizeof(t_int) ); + if ( x->x_blurzoomy ) freebytes ( x->x_blurzoomy, x->x_buf_height*sizeof(t_int) ); + if ( x->x_snapframe ) freebytes ( x->x_snapframe, ( ( x->x_vsize + x->x_vsize>>1 ) << 1 ) ); + if ( x->x_diff ) freebytes( x->x_diff, (x->x_vsize + (x->x_vsize>>1))<<1 ); + if ( x->x_bdata ) freebytes( x->x_bdata, (( x->x_vsize + (x->x_vsize>>1))<<1)); +} + +static void pdp_radioactiv_allocate(t_pdp_radioactiv *x) +{ + int i; + + x->x_buf_width_blocks = (x->x_vwidth / 32); + x->x_buf_width = x->x_buf_width_blocks * 32; + x->x_buf_height = x->x_vheight; + x->x_buf_area = x->x_buf_width * x->x_buf_height; + x->x_buf_margin_left = (x->x_vwidth - x->x_buf_width)/2; + x->x_buf_margin_right = x->x_vwidth - x->x_buf_width - x->x_buf_margin_left; + + x->x_blurzoombuf = (unsigned char *) getbytes (x->x_buf_area*2); + x->x_blurzoomx = (t_int *) getbytes (x->x_buf_width*sizeof(t_int)); + x->x_blurzoomy = (t_int *) getbytes (x->x_buf_height*sizeof(t_int)); + x->x_snapframe = (short int *) getbytes ( ( ( x->x_vsize + x->x_vsize>>1 ) << 1 ) ); + x->x_diff = (short int*) getbytes((x->x_vsize + (x->x_vsize>>1))<<1); + x->x_bdata = (short int *) getbytes((( x->x_vsize + (x->x_vsize>>1))<<1)); + + if ( !x->x_blurzoombuf || !x->x_blurzoomx || !x->x_blurzoomy || + !x->x_snapframe || !x->x_diff || !x->x_bdata ) + { + post( "pdp_radioactiv : severe error : cannot allocate buffers !!!" ); + return; + } +} + +/* check if there is a real difference with background image */ +static void pdp_radioactiv_diff(t_pdp_radioactiv *x, short int *src) +{ + int i; + int Yy=0, Yu=0, Yv=0; + int Yby=0, Ybu=0, Ybv=0; + short int *p=NULL; + short int *pb=NULL; + short int *r=NULL; + int v; + + p = src; + pb = x->x_bdata; + r = x->x_diff; + for(i=0; i<(x->x_vsize); i++) + { + Yy = (*p); + Yu = (*(p+x->x_vsize+(i>>2))); + if ( x->x_vsize+(x->x_vsize>>2)+(i>>2) > x->x_vsize+(x->x_vsize>>1) ) + { + post ("pdp_mosaic : overflow : offset=%d limit=%d", x->x_vsize+(x->x_vsize>>2)+(i>>2), + x->x_vsize+(x->x_vsize>>1) ); + return; + } + Yv = (*(p+x->x_vsize+(x->x_vsize>>2)+(i>>2))); + Yby = (*pb); + Ybu = (*(pb+x->x_vsize+(i>>2))); + Ybv = (*(pb+x->x_vsize+(x->x_vsize>>2)+(i>>2))); + if ( !r ) { post( "pdp_mosaic : hey, buffers are not allocated !!" ); return; }; + *r = ( (Yy - Yby) + (Yu - Ybu) + (Yv - Ybv) ); + r++; + } + +} + +static void pdp_radioactiv_make_palette(t_pdp_radioactiv *x) +{ + int i; + + for(i=0; i<COLORS/2; i++) + { + x->x_palette[i] = i*DELTA; + } + for(i=0; i<COLORS/2; i++) + { + x->x_palette[i+COLORS/2] = 255 | (i*DELTA)<<16 | (i*DELTA)<<8; + } + for(i=0; i<COLORS; i++) + { + x->x_palette[i] = x->x_palette[i] & 0xfefeff; + } +} + +/* this table assumes that video_width is times of 32 */ +static void pdp_radioactiv_set_table(t_pdp_radioactiv *x) +{ + unsigned int bits; + int px, py, tx, ty, xx; + int ptr=0, prevptr=0; + + prevptr = (int)(0.5+RATIO*(-VIDEO_HWIDTH)+VIDEO_HWIDTH); + for(xx=0; xx<(x->x_buf_width_blocks); xx++) + { + bits = 0; + for(px=0; px<32; px++) + { + ptr = (int)(0.5+RATIO*((xx*32)+px-VIDEO_HWIDTH)+VIDEO_HWIDTH); + bits = bits>>1; + if(ptr != prevptr) bits |= 0x80000000; + prevptr = ptr; + } + x->x_blurzoomx[xx] = bits; + } + + ty = (int)(0.5+RATIO*(-VIDEO_HHEIGHT)+VIDEO_HHEIGHT); + tx = (int)(0.5+RATIO*(-VIDEO_HWIDTH)+VIDEO_HWIDTH); + xx=(int)(0.5+RATIO*(x->x_buf_width-1-VIDEO_HWIDTH)+VIDEO_HWIDTH); + x->x_blurzoomy[0] = ty * x->x_buf_width + tx; + prevptr = ty * x->x_buf_width + xx; + for(py=1; py<x->x_buf_height; py++) + { + ty = (int)(0.5+RATIO*(py-VIDEO_HHEIGHT)+VIDEO_HHEIGHT); + x->x_blurzoomy[py] = ty * x->x_buf_width + tx - prevptr; + prevptr = ty * x->x_buf_width + xx; + } +} + +static void pdp_radioactiv_mode(t_pdp_radioactiv *x, t_floatarg fmode ) +{ + if ( ( fmode > 0 ) || ( fmode < 4 ) ) + { + x->x_mode = (int)fmode; + if(x->x_mode == 3) + { + x->x_snap_time = 1; + } + else + { + x->x_snap_time = 0; + } + } +} + +static void pdp_radioactiv_snap_time(t_pdp_radioactiv *x, t_floatarg fsnaptime ) +{ + if ( fsnaptime > 0 ) + { + x->x_snap_time = (t_int) fsnaptime; + } +} + +static void pdp_radioactiv_snap_interval(t_pdp_radioactiv *x, t_floatarg fsnapinterval ) +{ + if ( fsnapinterval > 1 ) + { + x->x_snap_interval = (t_int) fsnapinterval; + } +} + +static void pdp_radioactiv_blur(t_pdp_radioactiv *x) +{ + int px, py; + int width; + unsigned char *p, *q; + unsigned char v; + + width = x->x_buf_width; + p = x->x_blurzoombuf + width + 1; + q = p + x->x_buf_area; + + for(py=x->x_buf_height-2; py>0; py--) + { + for(px=x->x_vwidth-2; px>0; px--) + { + v = (*(p-width) + *(p-1) + *(p+1) + *(p+width))/4 - 1; + if(v == 255) v = 0; + *q = v; + p++; + q++; + } + p += 2; + q += 2; + } +} + +static void pdp_radioactiv_zoom(t_pdp_radioactiv *x) +{ + int b, px, py; + unsigned char *p, *q; + int blocks, height; + int dx; + + p = x->x_blurzoombuf + x->x_buf_area; + q = x->x_blurzoombuf; + height = x->x_buf_height; + blocks = x->x_buf_width_blocks; + + for(py=0; py<height; py++) + { + p += x->x_blurzoomy[py]; + for(b=0; b<blocks; b++) + { + dx = x->x_blurzoomx[b]; + for(px=0; px<32; px++) + { + p += (dx & 1); + *q++ = *p; + dx = dx>>1; + } + } + } +} + +static void pdp_radioactiv_blurzoom(t_pdp_radioactiv *x) +{ + pdp_radioactiv_blur(x); + pdp_radioactiv_zoom(x); +} + +static void pdp_radioactiv_process_yv12(t_pdp_radioactiv *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); + int i; + + int px, py; + short int a, b; + unsigned char *p; + short int *diff, *src; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_radioactiv_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_radioactiv_allocate(x); + post( "pdp_radioactiv : reallocating buffers" ); + pdp_radioactiv_set_table(x); + post( "pdp_radioactiv : set table" ); + } + + 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_bdata && x->x_snapshot ) + { + x->x_snapshot = 0; + memcpy( x->x_bdata, data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + } + + 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); + + if(x->x_mode != 2 || x->x_snap_time <= 0) + { + pdp_radioactiv_diff(x, data); + if(x->x_mode == 0 || x->x_snap_time <= 0) + { + diff = x->x_diff + x->x_buf_margin_left; + p = x->x_blurzoombuf; + for(py=0; py<x->x_buf_height; py++) + { + for(px=0; px<x->x_buf_width; px++) + { + p[px] |= diff[px] >> 3; + } + diff += x->x_vwidth; + p += x->x_buf_width; + } + if( ( x->x_mode == 1 ) || ( x->x_mode == 2 )) + { + memcpy(x->x_snapframe, data, ( ( x->x_vsize + x->x_vsize>>1 ) << 1 ) ); + } + } + } + pdp_radioactiv_blurzoom(x); + + if( ( x->x_mode == 1 ) || ( x->x_mode == 2 )) + { + src = x->x_snapframe; + } + else + { + src = data; + } + + p = x->x_blurzoombuf; + for(py=0; py<x->x_vheight; py++) + { + for(px=0; px<x->x_buf_margin_left; px++) + { + *newdata++ = *src++; + } + for(px=0; px<x->x_buf_width; px++) + { + a = *src++ & 0xfeff; + b = x->x_palette[*p++]; + a += b; + b = a & 0x0100; + *newdata++ = a | (b - (b >> 8)); + } + for(px=0; px<x->x_buf_margin_right; px++) + { + *newdata++ = *src++; + } + } + + if( ( x->x_mode == 1 ) || ( x->x_mode == 2 ) ) + { + x->x_snap_time--; + if(x->x_snap_time < 0) { + x->x_snap_time = x->x_snap_interval; + } + } + + return; +} + +static void pdp_radioactiv_sendpacket(t_pdp_radioactiv *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_radioactiv_process(t_pdp_radioactiv *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_radioactiv_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_radioactiv_process_yv12, pdp_radioactiv_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_radioactiv_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_radioactiv_process */ + break; + + } + } +} + +static void pdp_radioactiv_input_0(t_pdp_radioactiv *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_radioactiv_process(x); + + } +} + +static void pdp_radioactiv_free(t_pdp_radioactiv *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_radioactiv_free_ressources(x); + +} + +t_class *pdp_radioactiv_class; + +void *pdp_radioactiv_new(void) +{ + int i; + + t_pdp_radioactiv *x = (t_pdp_radioactiv *)pd_new(pdp_radioactiv_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("mode")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("snaptime")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("snapinterval")); + + 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_mode = 0; /* 0=normal/1=strobe/2=strobe2/3=trigger */ + x->x_blurzoombuf = NULL; + x->x_snapframe = NULL; + x->x_snap_time = 0; + x->x_snap_interval = 3; + x->x_blurzoombuf = NULL; + x->x_blurzoomx = NULL; + x->x_blurzoomy = NULL; + x->x_snapframe = NULL; + x->x_diff = NULL; + + pdp_radioactiv_make_palette(x); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_radioactiv_setup(void) +{ +// post( pdp_radioactiv_version ); + pdp_radioactiv_class = class_new(gensym("pdp_radioactiv"), (t_newmethod)pdp_radioactiv_new, + (t_method)pdp_radioactiv_free, sizeof(t_pdp_radioactiv), 0, A_NULL); + + class_addmethod(pdp_radioactiv_class, (t_method)pdp_radioactiv_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_radioactiv_class, (t_method)pdp_radioactiv_mode, gensym("mode"), A_FLOAT, A_NULL); + class_addmethod(pdp_radioactiv_class, (t_method)pdp_radioactiv_snap_time, gensym("snaptime"), A_FLOAT, A_NULL); + class_addmethod(pdp_radioactiv_class, (t_method)pdp_radioactiv_snap_interval, gensym("snapinterval"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_rec~.c b/modules/pdp_rec~.c new file mode 100644 index 0000000..7f879d0 --- /dev/null +++ b/modules/pdp_rec~.c @@ -0,0 +1,705 @@ +/* + * 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 recording object + * It records its input in quicktime format + */ + + +#include "pdp.h" +#include <math.h> +#include <time.h> +#include <sys/time.h> +#include <quicktime/quicktime.h> +#include <quicktime/colormodels.h> + +#define DEFAULT_FRAME_RATE 25 +#define DEFAULT_CHANNELS 2 +#define DEFAULT_BITS 8 +#define DEFAULT_QUALITY 75 // from 1 to 100 +#define MAX_COMP_LENGTH 8 +#define MAX_AUDIO_PACKET_SIZE (128 * 1024) + +static char *pdp_rec_version = "pdp_rec~: version 0.1, a video/audio recording object, written by ydegoyon@free.fr"; + +typedef struct pdp_rec_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; + + quicktime_t *x_qtfile; + unsigned char **x_yuvpointers; + unsigned char *x_yuvbuffer; + t_int x_framerate; + t_int x_forced_framerate; + t_int x_jpeg_quality; + t_int x_newfile; + char *x_compressor; + t_int x_recflag; + t_int x_frameswritten; + struct timeval x_tstart; + struct timeval x_tstop; + struct timeval x_tlastrec; + + /* audio structures */ + int16_t **x_audio_buf; /* buffer for incoming audio */ + t_int x_audioin_position; // writing position for incoming audio + char *x_acompressor; // audio compressor + t_int x_channels; // audio channels + t_int x_samplerate; // audio sample rate + t_int x_bits; // audio bits + +} t_pdp_rec; + +static void pdp_rec_free_ressources(t_pdp_rec *x) +{ + if ( x->x_yuvpointers ) freebytes ( x->x_yuvpointers, 3*sizeof( unsigned char** ) ); + if ( x->x_yuvbuffer ) freebytes ( x->x_yuvbuffer, x->x_vsize + (x->x_vsize>>1) ); + +} + +static void pdp_rec_allocate(t_pdp_rec *x) +{ + int i; + + x->x_yuvpointers = (unsigned char**) getbytes ( 3*sizeof(unsigned char**) ); + x->x_yuvbuffer = (unsigned char*) getbytes ( x->x_vsize + (x->x_vsize>>1) ); + x->x_yuvpointers[0] = &x->x_yuvbuffer[0]; + x->x_yuvpointers[2] = &x->x_yuvbuffer[x->x_vsize]; + x->x_yuvpointers[1] = &x->x_yuvbuffer[x->x_vsize + (x->x_vsize>>2)]; +} + + /* set video track whenever width or height is changed */ +static void pdp_rec_set_video(t_pdp_rec *x) +{ + t_int ret; + + if ( !x->x_qtfile ) { + post( "pdp_rec~ : no video recording file is opened !!"); + return; + } + + if( ( ret = quicktime_set_video(x->x_qtfile, 1, x->x_vwidth, x->x_vheight, x->x_framerate, x->x_compressor) ) != 0) { + post( "pdp_rec~ : error setting video track ret=%d", ret ); + } else { + post( "pdp_rec~ : video track set" ); + } + + quicktime_set_copyright(x->x_qtfile, ""); + quicktime_set_name(x->x_qtfile, "Pdp output"); + quicktime_set_info(x->x_qtfile, "File created with PDP/PiDiP"); + +} + + /* set framerate */ +static void pdp_rec_set_framerate(t_pdp_rec *x) +{ + t_int ret; + + if ( !x->x_qtfile ) { + post( "pdp_rec~ : no video recording file is opened !!"); + return; + } + + quicktime_set_framerate(x->x_qtfile, (float)x->x_framerate ); + post( "pdp_rec~ : framerate set to : %d", x->x_framerate ); + +} + + /* set audio track */ +static void pdp_rec_set_audio(t_pdp_rec *x) +{ + t_int ret; + + if ( !x->x_qtfile ) { + post( "pdp_rec~ : no video recording file is opened !!"); + return; + } + + if( ( ret = quicktime_set_audio(x->x_qtfile, x->x_channels, x->x_samplerate, x->x_bits, x->x_acompressor ) ) == 0) + { + post( "pdp_rec~ : error setting audio track ret=%d", ret ); + post( "pdp_rec~ : params : samplerate=%d : compressor=%s : channels=%d : bits=%d", + x->x_samplerate, x->x_acompressor, x->x_channels, x->x_bits ); + } else { + post( "pdp_rec~ : %d audio track(s) allocated.", ret ); + } + +} + + /* set color model : it's hard coded : only one model supported */ +static void pdp_rec_set_cmodel(t_pdp_rec *x) +{ + t_int ret; + + if ( !x->x_qtfile ) { + post( "pdp_rec~ : no video recording file is opened !!"); + return; + } + + quicktime_set_cmodel(x->x_qtfile, BC_YUV420P ); + post( "pdp_rec~ : color model set" ); + +} + +static void pdp_rec_set_jpeg(t_pdp_rec *x) +{ + if ( !x->x_qtfile ) + { + post( "pdp_rec~ : set jpeg : no video recording file is opened !!"); + return; + } + + if ( strcmp( x->x_compressor, QUICKTIME_JPEG ) ) + { + post( "pdp_rec~ : set jpeg : the codec is not jpeg right now !!"); + return; + } + quicktime_set_jpeg( x->x_qtfile, x->x_jpeg_quality, 1 ); + post( "pdp_rec~ : jpeg quality factor set : %d", x->x_jpeg_quality ); +} + +static void pdp_rec_frame_rate(t_pdp_rec *x, t_floatarg frate ) +{ + if ( frate >= 1 ) + { + x->x_framerate = (int) frate; + x->x_forced_framerate = 1; + post( "pdp_rec~ : frame rate set to %d : open a new file to activate it", x->x_framerate ); + } +} + +static void pdp_rec_jpeg(t_pdp_rec *x, t_floatarg fjpeg ) +{ + if ( ( fjpeg >= 1 ) && ( fjpeg <= 100 )) + { + x->x_jpeg_quality = (int) fjpeg; + post( "pdp_rec~ : jpeg quality set : open a new file to activate it" ); + } +} + +static void pdp_rec_compressor(t_pdp_rec *x, t_symbol *scompressor ) +{ + char scomp[ MAX_COMP_LENGTH ]; + + // check compressor as defined in quicktime.h + if ( + strcmp( scompressor->s_name, "divx") + && strcmp( scompressor->s_name, "dv") + && strcmp( scompressor->s_name, "raw") + && strcmp( scompressor->s_name, "jpeg") + // && strcmp( scompressor->s_name, "png") // crashes with libquicktime 0.9.1 + // && strcmp( scompressor->s_name, "mjpa") // no output with libquicktime 0.9.1 + && strcmp( scompressor->s_name, "yuv2") + // && strcmp( scompressor->s_name, "yuv4") // crashes with libquicktime 0.9.1 + ) + { + post( "pdp_rec~ : unsupported codec : %s", scompressor->s_name ); + return; + } + + // map message names to libquicktime names + if ( !strcmp( scompressor->s_name, "divx") ) + { + strcpy( scomp, QUICKTIME_DIVX ); + } + if ( !strcmp( scompressor->s_name, "dv") ) + { + strcpy( scomp, QUICKTIME_DV ); + } + if ( !strcmp( scompressor->s_name, "raw") ) + { + strcpy( scomp, QUICKTIME_RAW ); + } + if ( !strcmp( scompressor->s_name, "jpeg") ) + { + strcpy( scomp, QUICKTIME_JPEG ); + } + if ( !strcmp( scompressor->s_name, "png") ) + { + strcpy( scomp, QUICKTIME_PNG ); + } + if ( !strcmp( scompressor->s_name, "mjpa") ) + { + strcpy( scomp, QUICKTIME_MJPA ); + } + if ( !strcmp( scompressor->s_name, "yuv2") ) + { + strcpy( scomp, QUICKTIME_YUV2 ); + } + if ( !strcmp( scompressor->s_name, "yuv4") ) + { + strcpy( scomp, QUICKTIME_YUV4 ); + } + + if ( x->x_compressor ) + { + freebytes( x->x_compressor, strlen( x->x_compressor )+1 ); + } + x->x_compressor = (char *) getbytes( strlen( scomp ) + 1 ); + strcpy( x->x_compressor, scomp ); + post( "pdp_rec~ : compressor set to %s : open a new file to activate it", scomp ); +} + + /* set audio compressor */ +static void pdp_rec_acompressor(t_pdp_rec *x, t_symbol *scompressor ) +{ + char scomp[ MAX_COMP_LENGTH ]; + + // check compressor as defined in quicktime.h + if ( + strcmp( scompressor->s_name, "twos") + // && strcmp( scompressor->s_name, "ima4") // produces a lot of errors ( libquicktime 0.9.1 ) + && strcmp( scompressor->s_name, "raw") + // && strcmp( scompressor->s_name, "ulaw") // produces a lot of errors ( libquicktime 0.9.1 ) + // && strcmp( scompressor->s_name, "ogg") // produces a lot of errors ( libquicktime 0.9.1 ) + ) + { + post( "pdp_rec~ : unsupported codec : %s", scompressor->s_name ); + return; + } + + // map message names to libquicktime names + if ( !strcmp( scompressor->s_name, "raw") ) + { + strcpy( scomp, QUICKTIME_RAW ); + } + if ( !strcmp( scompressor->s_name, "ima4") ) + { + strcpy( scomp, QUICKTIME_IMA4 ); + } + if ( !strcmp( scompressor->s_name, "twos") ) + { + strcpy( scomp, QUICKTIME_TWOS ); + } + if ( !strcmp( scompressor->s_name, "ulaw") ) + { + strcpy( scomp, QUICKTIME_ULAW ); + } + if ( !strcmp( scompressor->s_name, "ogg") ) + { + strcpy( scomp, QUICKTIME_VORBIS ); + } + + if ( x->x_compressor ) + { + freebytes( x->x_compressor, strlen( x->x_compressor )+1 ); + } + x->x_compressor = (char *) getbytes( strlen( scomp ) + 1 ); + strcpy( x->x_compressor, scomp ); + post( "pdp_rec~ : audio compressor set to %s : open a new file to activate it", scomp ); +} + + /* close a video file */ +static void pdp_rec_close(t_pdp_rec *x) +{ + int ret; + + if ( x->x_qtfile ) { + if( ( ret = quicktime_close(x->x_qtfile) ) != 0 ) { + post( "pdp_rec~ : error closing file ret=%d", ret ); + } else { + post( "pdp_rec~ : closed video file" ); + x->x_qtfile = NULL; + } + } +} + + /* open a new video file */ +static void pdp_rec_open(t_pdp_rec *x, t_symbol *sfile) +{ + t_int ret=0; + + // close previous video file if existing + pdp_rec_close(x); + + if ( x->x_recflag ) { + x->x_recflag = 0; + } + + if ( ( x->x_qtfile = quicktime_open(sfile->s_name, 0, 1) ) == NULL ) + { + error( "pdp_rec~ : cannot open >%s<", sfile->s_name); + error( "pdp_rec~ : ret=%d", ret ); + x->x_qtfile = NULL; + return; + } else { + x->x_frameswritten = 0; + post( "pdp_rec~ : opened >%s<", sfile->s_name); + x->x_newfile = 1; + } + +} + + /* start recording */ +static void pdp_rec_start(t_pdp_rec *x) +{ + if ( !x->x_qtfile ) { + post("pdp_rec~ : start received but no file has been opened ... ignored."); + return; + } + + if ( x->x_recflag == 1 ) { + post("pdp_rec~ : start received but recording is started ... ignored."); + return; + } + + if ( gettimeofday(&x->x_tstart, NULL) == -1) + { + post("pdp_rec~ : could not set start time" ); + } + + x->x_recflag = 1; + post("pdp_rec~ : start recording"); +} + + /* stop recording */ +static void pdp_rec_stop(t_pdp_rec *x) +{ + if ( !x->x_qtfile ) { + post("pdp_rec~ : stop received but no file has been opened ... ignored."); + return; + } + + if ( x->x_recflag == 0 ) { + post("pdp_rec~ : stop received but recording is stopped ... ignored."); + return; + } + + if ( gettimeofday(&x->x_tstop, NULL) == -1) + { + post("pdp_rec~ : could set stop time" ); + } + + // calculate frame rate if it hasn't been set + if ( !x->x_forced_framerate ) + { + if ( ( x->x_tstop.tv_sec - x->x_tstart.tv_sec ) > 0 ) + { + x->x_framerate = x->x_frameswritten / ( x->x_tstop.tv_sec - x->x_tstart.tv_sec ); + } + else + { + x->x_framerate = DEFAULT_FRAME_RATE; + } + } + + pdp_rec_set_framerate(x); + + x->x_recflag = 0; + pdp_rec_close(x); + + post("pdp_rec~ : stop recording"); +} + + /* store audio data in PCM format in a buffer for now */ +static t_int *pdp_rec_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_rec *x = (t_pdp_rec *)(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; } + isample=(short) (32767.0 * fsample); + x->x_audio_buf[0][x->x_audioin_position]=isample; + fsample=*(in2++); + if (fsample > 1.0) { fsample = 1.0; } + if (fsample < -1.0) { fsample = -1.0; } + isample=(short) (32767.0 * fsample); + x->x_audio_buf[1][x->x_audioin_position]=isample; + x->x_audioin_position=(x->x_audioin_position+1)%(2*MAX_AUDIO_PACKET_SIZE); + if ( x->x_audioin_position == 2*MAX_AUDIO_PACKET_SIZE-1 ) + { + post( "pdp_rec~ : reaching end of audio buffer" ); + } + } + + } + + return (w+5); +} + +static void pdp_rec_dsp(t_pdp_rec *x, t_signal **sp) +{ + dsp_add(pdp_rec_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + +static void pdp_rec_process_yv12(t_pdp_rec *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_int i, ret; + t_int px, py; + unsigned short *poy, *pou, *pov; + struct timeval trec; + t_int nbaudiosamples, nbusecs, nbrecorded; + t_float fframerate=0.0; + + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + /* setting video track */ + if ( x->x_qtfile && x->x_recflag ) + { + if ( ( (int)(header->info.image.width) != x->x_vwidth ) || + ( (int)(header->info.image.height) != x->x_vheight ) || + ( x->x_newfile ) ) + { + pdp_rec_free_ressources(x); + x->x_newfile = 0; + if ( x->x_qtfile ) { + pdp_rec_set_video(x); + pdp_rec_set_audio(x); + pdp_rec_set_cmodel(x); + if ( !strcmp( x->x_compressor, QUICKTIME_JPEG ) ) + { + pdp_rec_set_jpeg(x); + } + } + pdp_rec_allocate(x); + } + + if ( x->x_frameswritten == 0 ) + { + if ( gettimeofday(&x->x_tlastrec, NULL) == -1) + { + post("pdp_rec~ : could set stop time" ); + } + } + + for (i=0; i<x->x_vsize; i++) + { + x->x_yuvbuffer[i] = data[i]>>7; + } + for (i=x->x_vsize; i<(x->x_vsize+(x->x_vsize>>1)); i++) + { + x->x_yuvbuffer[i] = ((data[i]>>8)+128); + } + + if ( ( ret = quicktime_encode_video(x->x_qtfile, x->x_yuvpointers, 0) ) != 0 ) + { + post( "pdp_rec~ : error writing frame : ret=%d", ret ); + } + else + { + x->x_frameswritten++; + } + + // calculate the number of audio samples to output + if ( gettimeofday(&trec, NULL) == -1) + { + post("pdp_rec~ : 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; + } + + if ( ( ret = quicktime_encode_audio(x->x_qtfile, x->x_audio_buf, NULL, nbrecorded) ) != 0 ) + { + post( "pdp_rec~ : error writing audio data : ret=%d", ret ); + } + else + { + memcpy( &x->x_audio_buf[0][0], &x->x_audio_buf[0][nbrecorded], x->x_audioin_position-nbrecorded ); + memcpy( &x->x_audio_buf[1][0], &x->x_audio_buf[1][nbrecorded], x->x_audioin_position-nbrecorded ); + x->x_audioin_position -= nbrecorded; + // post ( "pdp_rec~ : recorded %d samples.", nbrecorded ); + } + } + + return; +} + +static void pdp_rec_killpacket(t_pdp_rec *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; +} + +static void pdp_rec_process(t_pdp_rec *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_rec_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding) + { + + case PDP_IMAGE_YV12: + if ( x->x_qtfile && x->x_recflag ) + { + outlet_float( x->x_obj.ob_outlet, x->x_frameswritten ); + } + pdp_queue_add(x, pdp_rec_process_yv12, pdp_rec_killpacket, &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_rec_process */ + break; + + } + } + +} + +static void pdp_rec_input_0(t_pdp_rec *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_rec_process(x); + } + +} + +static void pdp_rec_free(t_pdp_rec *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + // close video file if existing + pdp_rec_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(int16_t) ); + } + if ( x->x_audio_buf ) freebytes( x->x_audio_buf, x->x_channels*sizeof(int16_t*) ); + +} + +t_class *pdp_rec_class; + +void *pdp_rec_new(void) +{ + t_int i; + + t_pdp_rec *x = (t_pdp_rec *)pd_new(pdp_rec_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_qtfile = NULL; + x->x_framerate = DEFAULT_FRAME_RATE; + x->x_forced_framerate = 0; + x->x_compressor = (char*) getbytes( strlen(QUICKTIME_JPEG)+1 ); + strcpy( x->x_compressor, QUICKTIME_JPEG ); + + /* audio defaults */ + x->x_acompressor = (char*) getbytes( strlen(QUICKTIME_TWOS)+1 ); + strcpy( x->x_acompressor, QUICKTIME_TWOS ); + x->x_samplerate = sys_getsr(); + x->x_channels = DEFAULT_CHANNELS; + x->x_bits = DEFAULT_BITS; + + x->x_audio_buf = (int16_t**) getbytes( x->x_channels*sizeof(int16_t*) ); + for ( i=0; i<x->x_channels; i++) + { + x->x_audio_buf[i] = (int16_t*) getbytes( MAX_AUDIO_PACKET_SIZE*sizeof(int16_t) ); + } + + x->x_newfile = 0; + x->x_yuvbuffer = NULL; + x->x_yuvpointers = NULL; + x->x_jpeg_quality = DEFAULT_QUALITY; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_rec_tilde_setup(void) +{ + post( pdp_rec_version ); + pdp_rec_class = class_new(gensym("pdp_rec~"), (t_newmethod)pdp_rec_new, + (t_method)pdp_rec_free, sizeof(t_pdp_rec), 0, A_NULL); + + CLASS_MAINSIGNALIN(pdp_rec_class, t_pdp_rec, x_f ); + class_addmethod(pdp_rec_class, (t_method)pdp_rec_dsp, gensym("dsp"), 0); + class_addmethod(pdp_rec_class, (t_method)pdp_rec_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_rec_class, (t_method)pdp_rec_open, gensym("open"), A_SYMBOL, A_NULL); + class_addmethod(pdp_rec_class, (t_method)pdp_rec_close, gensym("close"), A_NULL); + class_addmethod(pdp_rec_class, (t_method)pdp_rec_frame_rate, gensym("framerate"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_rec_class, (t_method)pdp_rec_compressor, gensym("compressor"), A_SYMBOL, A_NULL); + class_addmethod(pdp_rec_class, (t_method)pdp_rec_acompressor, gensym("acompressor"), A_SYMBOL, A_NULL); + class_addmethod(pdp_rec_class, (t_method)pdp_rec_jpeg, gensym("jpeg"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_rec_class, (t_method)pdp_rec_start, gensym("start"), A_NULL); + class_addmethod(pdp_rec_class, (t_method)pdp_rec_stop, gensym("stop"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_rev.c b/modules/pdp_rev.c new file mode 100644 index 0000000..58f5753 --- /dev/null +++ b/modules/pdp_rev.c @@ -0,0 +1,261 @@ +/* + * 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 adaptation of rev effect from effectv + * (c)2002 Ed Tannenbaum + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +static char *pdp_rev_version = "pdp_rev: version 0.1, port of rev from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_rev_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_vgrabtime; + t_int x_vgrab; + t_int x_linespace; + t_int x_vscale; + t_int x_vcolor; + +} t_pdp_rev; + +static void pdp_rev_vgrabtime(t_pdp_rev *x, t_floatarg fvgrabtime ) +{ + if ( fvgrabtime > 1 ) + { + x->x_vgrabtime = (int)fvgrabtime; + } +} + +static void pdp_rev_linespace(t_pdp_rev *x, t_floatarg flinespace ) +{ + if ( flinespace > 1 ) + { + x->x_linespace = (int)flinespace; + } +} + +static void pdp_rev_vscale(t_pdp_rev *x, t_floatarg fvscale ) +{ + if ( fvscale > 1 ) + { + x->x_vscale = (int)fvscale; + } +} + +static void pdp_rev_color(t_pdp_rev *x, t_floatarg fvcolor ) +{ + if ( ( fvcolor > 0 ) && ( fvcolor < 0xffff ) ) + { + x->x_vcolor = ((int)fvcolor)<<8; + } +} + +void pdp_rev_vasulka(t_pdp_rev *x, short int *src, short int *dst, int srcx, int srcy, int dstx, int dsty, int w, int h) +{ + short int *cdst=dst+((dsty*x->x_vwidth)+dstx); + short int *nsrc, *nusrc, *nvsrc; + int py,px,Y,U,V,yval; + int offset; + + // draw the offset lines + for (py=srcy; py<h+srcy; py+=x->x_linespace) + { + for(px=srcx; px<=w+srcx; px++) + { + nsrc=src+(py*x->x_vwidth)+px; + nusrc=src+(((py*x->x_vwidth)+px)>>2)+x->x_vsize; + nvsrc=src+(((py*x->x_vwidth)+px)>>2)+x->x_vsize+(x->x_vsize>>2); + // Calc Y Value for curpix + Y = (*nsrc); + U = (*nusrc); + V = (*nvsrc); + yval = py - ((short)(Y + U + V) / x->x_vscale) ; + offset = px + yval * x->x_vwidth; + if(offset >= 0 && offset < x->x_vsize ) + { + cdst[offset]=x->x_vcolor; + cdst[x->x_vsize+(offset>>2)]=x->x_vcolor; + cdst[x->x_vsize+(x->x_vsize>>2)+(offset>>2)]=x->x_vcolor; + } + } + } +} + +static void pdp_rev_process_yv12(t_pdp_rev *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); + int i; + + unsigned int totalnbpixels; + + totalnbpixels = x->x_vsize; + + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + 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 ); + + x->x_vgrab++; + if (x->x_vgrab >= x->x_vgrabtime) + { + x->x_vgrab=0; + pdp_rev_vasulka(x, data, newdata, 0, 0, 0, 0, x->x_vwidth, x->x_vheight); + } + + return; +} + +static void pdp_rev_sendpacket(t_pdp_rev *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_rev_process(t_pdp_rev *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_rev_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_rev_process_yv12, pdp_rev_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + break; + + default: + /* don't know the type, so dont pdp_rev_process */ + break; + + } + } + +} + +static void pdp_rev_input_0(t_pdp_rev *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_rev_process(x); + } +} + +static void pdp_rev_free(t_pdp_rev *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_rev_class; + +void *pdp_rev_new(void) +{ + int i; + + t_pdp_rev *x = (t_pdp_rev *)pd_new(pdp_rev_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("vgrabtime")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("linespace")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("vscale")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("color")); + + 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_vgrabtime = 1; + x->x_vgrab = 0; + x->x_linespace = 6; + x->x_vscale = 50; + x->x_vcolor = 0xffff; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_rev_setup(void) +{ +// post( pdp_rev_version ); + pdp_rev_class = class_new(gensym("pdp_rev"), (t_newmethod)pdp_rev_new, + (t_method)pdp_rev_free, sizeof(t_pdp_rev), 0, A_NULL); + + class_addmethod(pdp_rev_class, (t_method)pdp_rev_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_rev_class, (t_method)pdp_rev_vgrabtime, gensym("vgrabtime"), A_FLOAT, A_NULL); + class_addmethod(pdp_rev_class, (t_method)pdp_rev_linespace, gensym("linespace"), A_FLOAT, A_NULL); + class_addmethod(pdp_rev_class, (t_method)pdp_rev_vscale, gensym("vscale"), A_FLOAT, A_NULL); + class_addmethod(pdp_rev_class, (t_method)pdp_rev_color, gensym("color"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_ripple.c b/modules/pdp_ripple.c new file mode 100644 index 0000000..4f54b30 --- /dev/null +++ b/modules/pdp_ripple.c @@ -0,0 +1,567 @@ +/* + * 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 adaptation of ripple effect from effectv + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define MAGIC_THRESHOLD 30 + +static unsigned int fastrand_val; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + +static int sqrtable[256]; +static int sqrt_init=1; +static const int point = 16; +static const int impact = 2; +static const int decay = 8; +static const int loopnum = 2; +static int period = 0; +static int rain_stat = 0; +static unsigned int drop_prob = 0; +static int drop_prob_increment = 0; +static int drops_per_frame_max = 0; +static int drops_per_frame = 0; +static int drop_power = 0; + +static char *pdp_ripple_version = "pdp_ripple: version 0.1, port of ripple from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_ripple_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_maph; + t_int x_mapw; + t_int x_mode; + t_int x_threshold; + t_int *x_map; + t_int *x_map1; + t_int *x_map2; + t_int *x_map3; + signed char *x_vtable; + short int *x_diff; + short int *x_bdata; + t_int x_snapshot; + +} t_pdp_ripple; + +static void pdp_ripple_mode(t_pdp_ripple *x, t_floatarg fmode ) +{ + if ( ( fmode == 0 ) || ( fmode == 1 ) ) + { + x->x_mode = (int)fmode; + } +} + +static void pdp_ripple_threshold(t_pdp_ripple *x, t_floatarg fthreshold ) +{ + x->x_threshold = (int)fthreshold; +} + +static void pdp_ripple_increment(t_pdp_ripple *x, t_floatarg fincrement ) +{ + drop_prob_increment = (int)fincrement; +} + +static void pdp_ripple_background(t_pdp_ripple *x ) +{ + x->x_snapshot = 1; +} + +static void pdp_ripple_free_ressources(t_pdp_ripple *x) +{ + if ( x->x_diff != NULL ) freebytes( x->x_diff, (x->x_vsize + (x->x_vsize>>1))<<1 ); + if ( x->x_bdata ) freebytes( x->x_bdata, (( x->x_vsize + (x->x_vsize>>1))<<1)); + if ( x->x_map ) freebytes(x->x_map, x->x_maph*x->x_mapw*3*sizeof(t_int)); + if ( x->x_vtable ) freebytes(x->x_vtable, x->x_maph*x->x_mapw*2*sizeof(signed char)); +} + +static void pdp_ripple_allocate(t_pdp_ripple *x) +{ + int i; + + x->x_diff = (short int*) getbytes((x->x_vsize + (x->x_vsize>>1))<<1); + x->x_bdata = (short int *)getbytes((( x->x_vsize + (x->x_vsize>>1))<<1)); + x->x_maph = x->x_vheight / 2 + 1; + x->x_mapw = x->x_vwidth / 2 + 1; + x->x_map = (int *)getbytes(x->x_maph*x->x_mapw*3*sizeof(t_int)); + x->x_vtable = (signed char *)getbytes(x->x_maph*x->x_mapw*2*sizeof(signed char)); + if( !x->x_map || x->x_vtable || !x->x_bdata || ! x->x_diff ) { + post( "pdp_ripple : severe error : cannot allocate buffers" ); + } + x->x_map1 = x->x_map; + x->x_map2 = x->x_map + x->x_maph * x->x_mapw; + x->x_map3 = x->x_map + x->x_mapw * x->x_maph * 2; + +} + +/* check if there is a real difference with background image */ +short int *pdp_ripple_diff(t_pdp_ripple *x, short int *src) +{ + int i; + int Y; + int Yb; + short int *p=NULL; + short int *pb=NULL; + short int *r=NULL; + int v; + + p = src; + pb = x->x_bdata; + r = x->x_diff; + for(i=0; i<(x->x_vsize); i++) { + Y = (*p); + Yb = (*pb); + *r = ( (Yb - Y) > x->x_threshold ) ? (Yb - Y) : 0; + p++; pb++; + r++; + } + + return x->x_diff; +} + +static void pdp_ripple_motion_detect(t_pdp_ripple *x, short int *src) +{ + short int *diff; + int width; + int *p, *q; + int px, py, h; + + diff = pdp_ripple_diff(x, src); + width = x->x_vwidth; + p = x->x_map1+x->x_mapw+1; + q = x->x_map2+x->x_mapw+1; + diff += width+2; + + for(py=x->x_maph-2; py>0; py--) + { + for(px=x->x_mapw-2; px>0; px--) + { + h = (int)*diff;// + (int)*(diff+1) + (int)*(diff+width) + (int)*(diff+width+1); + if(h>0) { + *p = h<<(point + impact - 8); + *q = *p; + } + p++; + q++; + diff += 2; + } + diff += width+2; + p+=2; + q+=2; + } +} + +static inline void pdp_ripple_drop(t_pdp_ripple *x, int power) +{ + int px, py; + int *p, *q; + + px = inline_fastrand()%(x->x_mapw-4)+2; + py = inline_fastrand()%(x->x_maph-4)+2; + p = x->x_map1 + py*x->x_mapw + px; + q = x->x_map2 + py*x->x_mapw + px; + *p = power; + *q = power; + *(p-x->x_mapw) = *(p-1) = *(p+1) = *(p+x->x_mapw) = power/2; + *(p-x->x_mapw-1) = *(p-x->x_mapw+1) = *(p+x->x_mapw-1) = *(p+x->x_mapw+1) = power/4; + *(q-x->x_mapw) = *(q-1) = *(q+1) = *(q+x->x_mapw) = power/2; + *(q-x->x_mapw-1) = *(q-x->x_mapw+1) = *(q+x->x_mapw-1) = *(p+x->x_mapw+1) = power/4; +} + +static void pdp_ripple_raindrop(t_pdp_ripple *x) +{ + int i; + + if(period == 0) + { + switch(rain_stat) + { + case 0: + period = (inline_fastrand()>>23)+100; + drop_prob = 0; + drop_prob_increment = 0x00ffffff/period; + drop_power = (-(inline_fastrand()>>28)-2)<<point; + drops_per_frame_max = 2<<(inline_fastrand()>>30); // 2,4,8 or 16 + rain_stat = 1; + break; + case 1: + drop_prob = 0x00ffffff; + drops_per_frame = 1; + drop_prob_increment = 1; + period = (drops_per_frame_max - 1) * 16; + rain_stat = 2; + break; + case 2: + period = (inline_fastrand()>>22)+1000; + drop_prob_increment = 0; + rain_stat = 3; + break; + case 3: + period = (drops_per_frame_max - 1) * 16; + drop_prob_increment = -1; + rain_stat = 4; + break; + case 4: + period = (inline_fastrand()>>24)+60; + drop_prob_increment = -(drop_prob/period); + rain_stat = 5; + break; + case 5: + default: + period = (inline_fastrand()>>23)+500; + drop_prob = 0; + rain_stat = 0; + break; + } + } + switch(rain_stat) + { + default: + case 0: + break; + case 1: + case 5: + if((inline_fastrand()>>8)<drop_prob) + { + pdp_ripple_drop(x, drop_power); + } + drop_prob += drop_prob_increment; + break; + case 2: + case 3: + case 4: + for(i=drops_per_frame/16; i>0; i--) + { + pdp_ripple_drop(x, drop_power); + } + drops_per_frame += drop_prob_increment; + break; + } + period--; +} + +static void pdp_ripple_process_yv12(t_pdp_ripple *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); + int i; + + unsigned int totalnbpixels; + unsigned int u_offset; + unsigned int v_offset; + unsigned int totnbpixels; + + int px, py; + int dx, dy; + int h, v; + int width, height; + int *p, *q, *r; + signed char *vp; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_ripple_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_ripple_allocate(x); + post( "pdp_ripple : reallocated buffers" ); + } + + if ( x->x_bdata && x->x_snapshot ) + { + x->x_snapshot = 0; + memcpy( x->x_bdata, data, (x->x_vsize + (x->x_vsize<<1))<<1 ); + } + + totalnbpixels = x->x_vsize; + u_offset = x->x_vsize; + v_offset = x->x_vsize + (x->x_vsize>>2); + totnbpixels = x->x_vsize + (x->x_vsize>>1); + + 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 ); + + if ( x->x_mode ) + { + pdp_ripple_motion_detect(x, data); + } + else + { + pdp_ripple_raindrop(x); + } + + /* simulate surface wave */ + width = x->x_mapw; + height = x->x_maph; + + /* This function is called only 30 times per second. To increase a speed + * of wave, iterates this loop several times. */ + for(i=loopnum; i>0; i--) + { + /* wave simulation */ + p = x->x_map1 + width + 1; + q = x->x_map2 + width + 1; + r = x->x_map3 + width + 1; + for(py=height-2; py>0; py--) + { + for(px=width-2; px>0; px--) + { + h = *(p-width-1) + *(p-width+1) + *(p+width-1) + *(p+width+1) + + *(p-width) + *(p-1) + *(p+1) + *(p+width) - (*p)*9; + h = h >> 3; + v = *p - *q; + v += h - (v >> decay); + *r = v + *p; + p++; + q++; + r++; + } + p += 2; + q += 2; + r += 2; + } + + /* low pass filter */ + p = x->x_map3 + width + 1; + q = x->x_map2 + width + 1; + for(py=height-2; py>0; py--) + { + for(px=width-2; px>0; px--) + { + h = *(p-width) + *(p-1) + *(p+1) + *(p+width) + (*p)*60; + *q = h >> 6; + p++; + q++; + } + p+=2; + q+=2; + } + + p = x->x_map1; + x->x_map1 = x->x_map2; + x->x_map2 = p; + } + + vp = x->x_vtable; + p = x->x_map1; + for(py=height-1; py>0; py--) + { + for(px=width-1; px>0; px--) + { + /* difference of the height between two voxel. They are twiced to + * emphasise the wave. */ + vp[0] = sqrtable[((p[0] - p[1])>>(point-1))&0xff]; + vp[1] = sqrtable[((p[0] - p[width])>>(point-1))&0xff]; + p++; + vp+=2; + } + p++; + vp+=2; + } + + height = x->x_vheight; + width = x->x_vwidth; + vp = x->x_vtable; + + /* draw refracted image. The vector table is stretched. */ + for(py=0; py<height; py+=2) + { + for(px=0; px<width; px+=2) + { + h = (int)vp[0]; + v = (int)vp[1]; + dx = px + h; + dy = py + v; + if(dx<0) dx=0; + if(dy<0) dy=0; + if(dx>=width) dx=width-1; + if(dy>=height) dy=height-1; + newdata[0] = data[dy*width+dx]; + + i = dx; + + dx = px + 1 + (h+(int)vp[2])/2; + if(dx<0) dx=0; + if(dx>=width) dx=width-1; + newdata[1] = data[dy*width+dx]; + + dy = py + 1 + (v+(int)vp[x->x_mapw*2+1])/2; + if(dy<0) dy=0; + if(dy>=height) dy=height-1; + newdata[width] = data[dy*width+i]; + + newdata[width+1] = data[dy*width+dx]; + newdata+=2; + vp+=2; + } + newdata += width; + vp += 2; + } + + return; +} + +static void pdp_ripple_sendpacket(t_pdp_ripple *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_ripple_process(t_pdp_ripple *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_ripple_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_ripple_process_yv12, pdp_ripple_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_ripple_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_ripple_process */ + break; + + } + } +} + +static void pdp_ripple_input_0(t_pdp_ripple *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_ripple_process(x); + } +} + +static void pdp_ripple_free(t_pdp_ripple *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_ripple_free_ressources(x); +} + +t_class *pdp_ripple_class; + +void *pdp_ripple_new(void) +{ + int i; + + t_pdp_ripple *x = (t_pdp_ripple *)pd_new(pdp_ripple_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("mode")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_bang, gensym("background")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("threshold")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("increment")); + + 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_mode = 0; + x->x_vsize = -1; + x->x_snapshot = 1; + x->x_threshold = MAGIC_THRESHOLD; + + if ( sqrt_init ) + { + sqrt_init = 0; + for(i=0; i<128; i++) { + sqrtable[i] = i*i; + } + for(i=1; i<=128; i++) { + sqrtable[256-i] = -i*i; + } + } + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_ripple_setup(void) +{ +// post( pdp_ripple_version ); + pdp_ripple_class = class_new(gensym("pdp_ripple"), (t_newmethod)pdp_ripple_new, + (t_method)pdp_ripple_free, sizeof(t_pdp_ripple), 0, A_NULL); + + class_addmethod(pdp_ripple_class, (t_method)pdp_ripple_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_ripple_class, (t_method)pdp_ripple_mode, gensym("mode"), A_FLOAT, A_NULL); + class_addmethod(pdp_ripple_class, (t_method)pdp_ripple_background, gensym("background"), A_NULL); + class_addmethod(pdp_ripple_class, (t_method)pdp_ripple_threshold, gensym("threshold"), A_FLOAT, A_NULL); + class_addmethod(pdp_ripple_class, (t_method)pdp_ripple_increment, gensym("increment"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_segsnd~.c b/modules/pdp_segsnd~.c new file mode 100644 index 0000000..f593af7 --- /dev/null +++ b/modules/pdp_segsnd~.c @@ -0,0 +1,412 @@ +/* + * 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 turns an image into sound + */ + +/* Listening to : + * The Deviants - Nothing Man + * 90 day men - My Trip To Venus + */ + +#include "pdp.h" +#include "yuv.h" +#include <math.h> +#include <ctype.h> +#include <Imlib2.h> // imlib2 is required + +static char *pdp_segsnd_version = "pdp_segsnd~: version 0.1 : turns an image into sound written by ydegoyon@free.fr "; + +typedef struct pdp_segsnd_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_x1; // coordinates of fixed segment + t_int x_y1; + t_int x_x2; + t_int x_y2; + t_int x_random; + + short int *x_data; + + /* imlib data */ + Imlib_Image x_image; + +} t_pdp_segsnd; + +static void pdp_segsnd_x1(t_pdp_segsnd *x, t_floatarg fx ) +{ + if ( ( fx >= 0 ) && ( fx < x->x_x2 ) ) + { + x->x_x1 = fx; + } +} + +static void pdp_segsnd_y1(t_pdp_segsnd *x, t_floatarg fy ) +{ + if ( ( fy >= 0 ) && ( fy < x->x_y2 ) ) + { + x->x_y1 = fy; + } +} + +static void pdp_segsnd_x2(t_pdp_segsnd *x, t_floatarg fx ) +{ + if ( ( fx >= x->x_x1 ) && ( fx < x->x_vwidth ) ) + { + x->x_x2 = fx; + } +} + +static void pdp_segsnd_y2(t_pdp_segsnd *x, t_floatarg fy ) +{ + if ( ( fy >= x->x_y1 ) && ( fy < x->x_vheight ) ) + { + x->x_y2 = fy; + } +} + +static void pdp_segsnd_random(t_pdp_segsnd *x, t_floatarg frand ) +{ + if ( ( frand == 0 ) || ( frand == 1 ) ) + { + x->x_random = frand; + } +} + +static void pdp_segsnd_allocate(t_pdp_segsnd *x) +{ + x->x_image = imlib_create_image( x->x_vwidth, x->x_vheight ); + if ( x->x_image == NULL ) + { + post( "pdp_form : severe error : could not allocate image !!" ); + } + imlib_context_set_image(x->x_image); + x->x_data = (short int *)getbytes((( x->x_vsize + (x->x_vsize>>1))<<1)); +} + +static void pdp_segsnd_free_ressources(t_pdp_segsnd *x) +{ + if ( x->x_image != NULL ) imlib_free_image(); + x->x_image = NULL; + if ( x->x_data ) freebytes( x->x_data, (( x->x_vsize + (x->x_vsize>>1))<<1)); + x->x_data = NULL; +} + +static void pdp_segsnd_process_yv12(t_pdp_segsnd *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 ti; + t_int px, py; + unsigned char y, u, v; + short int *pY, *pU, *pV; + DATA32 *imdata; + DATA32 bgcolor; + + if ( ( (int)(header->info.image.width) != x->x_vwidth ) || + ( (int)(header->info.image.height) != x->x_vheight ) ) + { + pdp_segsnd_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_segsnd_allocate(x); + } + + 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_data, data, ((x->x_vsize+(x->x_vsize>>1))<<1)); + + imlib_image_clear(); + imlib_context_set_direction(IMLIB_TEXT_TO_ANGLE); + imdata = imlib_image_get_data(); + bgcolor = imdata[0]; + + // post( "pdp_segsnd : x1=%d y1=%d x2=%d y2=%d", x->x_x1, x->x_y1, x->x_x2, x->x_y2 ); + if ( x->x_x1 != -1 ) + { + imlib_context_set_color( 255, 255, 255, 255 ); + imlib_image_draw_line( x->x_x1, x->x_y1, x->x_x2, x->x_y2, 1); + + pY = newdata; + pV = newdata+x->x_vsize; + pU = newdata+x->x_vsize+(x->x_vsize>>2); + for ( py=0; py<x->x_vheight; py++ ) + { + for ( px=0; px<x->x_vwidth; px++ ) + { + if ( imdata[py*x->x_vwidth+px] != bgcolor ) + { + y = yuv_RGBtoY(imdata[py*x->x_vwidth+px]); + u = yuv_RGBtoU(imdata[py*x->x_vwidth+px]); + v = yuv_RGBtoV(imdata[py*x->x_vwidth+px]); + + *(pY) = y<<7; + if ( (px%2==0) && (py%2==0) ) + { + *(pV) = (v-128)<<8; + *(pU) = (u-128)<<8; + } + } + pY++; + if ( (px%2==0) && (py%2==0) ) + { + pV++;pU++; + } + } + } + if ( x->x_random ) + { + x->x_x2 = ((t_float)rand()/RAND_MAX)*x->x_vwidth; + x->x_x1 = ((t_float)rand()/RAND_MAX)*x->x_x2; + x->x_y2 = ((t_float)rand()/RAND_MAX)*x->x_vheight; + x->x_y1 = ((t_float)rand()/RAND_MAX)*x->x_y2; + } + } + + return; +} + +static void pdp_segsnd_sendpacket(t_pdp_segsnd *x) +{ + /* delete source 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_segsnd_process(t_pdp_segsnd *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_segsnd_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_segsnd_process_yv12, pdp_segsnd_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_segsnd_process */ + break; + + } + } + +} + +static void pdp_segsnd_input_0(t_pdp_segsnd *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/*") ); + } + // post( "pdp_segsnd : action=%s dropped=%d", s->s_name, x->x_dropped ); + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + + /* add the process method and callback to the process queue */ + pdp_segsnd_process(x); + + } + +} + +static void pdp_segsnd_free(t_pdp_segsnd *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +static t_int *pdp_segsnd_perform(t_int *w) +{ + t_float *out = (t_float *)(w[1]); // audio generated sound + t_pdp_segsnd *x = (t_pdp_segsnd *)(w[2]); + t_int n = (int)(w[3]); + t_int npoints, xi, px, py; + t_float a=0; + + // set initial coordinates + if ( ( x->x_x1 == -1 ) && (x->x_vwidth != -1 )) + { + x->x_x1 = 10; + x->x_y1 = 10; + if ( 10+n > (x->x_vwidth-1) ) + { + x->x_x2 = x->x_vwidth-1; + } + else + { + x->x_x2 = 10+n; + } + if ( 10+n > (x->x_vheight-1) ) + { + x->x_y2 = x->x_vheight-1; + } + else + { + x->x_y2 = 10+n; + } + } + // post( "pdp_segsnd : x1=%d y1=%d x2=%d y2=%d", x->x_x1, x->x_y1, x->x_x2, x->x_y2 ); + + // output image data + if ( x->x_x1 == -1 ) + { + npoints = 0; + } + else if ( x->x_x2-x->x_x1 > n ) + { + npoints = n; + } + else + { + npoints = x->x_x2-x->x_x1; + } + if ( (x->x_x2-x->x_x1) > 0 ) + { + a = (x->x_y2-x->x_y1)/(x->x_x2-x->x_x1); + } + // post( "pdp_segsnd : npoints=%d a=%f", npoints, a ); + // read pixels + for (xi=0; xi<npoints; xi++) + { + px = x->x_x1 + xi; + py = x->x_y1 + (int)(a*xi); + *out = (((t_float)(x->x_data[py*x->x_vwidth+px]>>7))-127)/128.0; // scaled to -1 ... 1 + out++; + } + // fill up with zeros + for (xi=npoints; xi<n; xi++) + { + *out++ = 0.0; + } + + return (w+4); +} + +static void pdp_segsnd_dsp(t_pdp_segsnd *x, t_signal **sp) +{ + dsp_add(pdp_segsnd_perform, 3, sp[0]->s_vec, x, sp[0]->s_n); +} + +t_class *pdp_segsnd_class; + +void *pdp_segsnd_new(void) +{ + int i; + + t_pdp_segsnd *x = (t_pdp_segsnd *)pd_new(pdp_segsnd_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("y1")); + 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("y2")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("random")); + + // pdp output + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + // sound output + outlet_new (&x->x_obj, &s_signal); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_queue_id = -1; + + x->x_vwidth = -1; + x->x_vheight = -1; + + x->x_image = NULL; + x->x_data = NULL; + x->x_x1 = -1; + x->x_y1 = -1; + x->x_x2 = -1; + x->x_y2 = -1; + x->x_random = 0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_segsnd_tilde_setup(void) +{ + + post( pdp_segsnd_version ); + pdp_segsnd_class = class_new(gensym("pdp_segsnd~"), (t_newmethod)pdp_segsnd_new, + (t_method)pdp_segsnd_free, sizeof(t_pdp_segsnd), 0, A_NULL); + + class_addmethod(pdp_segsnd_class, (t_method)pdp_segsnd_dsp, gensym("dsp"), 0); + class_addmethod(pdp_segsnd_class, (t_method)pdp_segsnd_input_0, gensym("pdp"), + A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_segsnd_class, (t_method)pdp_segsnd_x1, gensym("x1"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_segsnd_class, (t_method)pdp_segsnd_y1, gensym("y1"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_segsnd_class, (t_method)pdp_segsnd_x2, gensym("x2"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_segsnd_class, (t_method)pdp_segsnd_y2, gensym("y2"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_segsnd_class, (t_method)pdp_segsnd_random, gensym("random"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_shagadelic.c b/modules/pdp_shagadelic.c new file mode 100644 index 0000000..dd24e03 --- /dev/null +++ b/modules/pdp_shagadelic.c @@ -0,0 +1,307 @@ +/* + * 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 port of shagadelic effect from EffecTV + * Originally written by Fukuchi Kentaro + * Pd-fication by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include <math.h> + +#define MAX_TABLES 6 +static unsigned int fastrand_val; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + +static char *pdp_shagadelic_version = "pdp_shagadelic: version 0.1, port of cycle from EffecTV by clifford smith, adapted by ydegoyon@free.fr "; + +typedef struct pdp_shagadelic_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + /* shagadelic parameters */ + char *x_ripple; + char *x_spiral; + unsigned char x_phase; + t_int x_rx, x_ry; + t_int x_bx, x_by; + t_int x_rvx, x_rvy; + t_int x_bvx, x_bvy; + short int x_mask; + +} t_pdp_shagadelic; + +static void pdp_shagadelic_mask(t_pdp_shagadelic *x, t_floatarg fmask ) +{ + if ( ( fmask >= 0 ) || ( fmask < 65536 ) ) + { + x->x_mask = fmask; + } +} + +static int pdp_shagadelic_map_from_table(t_pdp_shagadelic *x, t_int px, t_int py, t_int t) +{ + int xd,yd; + + yd = py + (inline_fastrand() >> 30)-2; + xd = px + (inline_fastrand() >> 30)-2; + if (xd > x->x_vwidth) { + xd-=1; + } + return (xd+yd*x->x_vwidth); +} + +static void pdp_shagadelic_init_tables(t_pdp_shagadelic *x) +{ + t_int px, py, i; + double xx, yy; + + i = 0; + for(py=0; py<x->x_vheight*2; py++) + { + yy = py - x->x_vheight; + yy *= yy; + for(px=0; px<x->x_vwidth*2; px++) + { + xx = px - x->x_vwidth; + x->x_ripple[i++] = ((unsigned int)(sqrt(xx*xx+yy)*8))&255; + } + } + i = 0; + for(py=0; py<x->x_vheight; py++) + { + yy = py - x->x_vheight/2; + for(px=0; px<x->x_vwidth; px++) + { + xx = px - x->x_vwidth/2; + x->x_spiral[i++] = ((unsigned int) ((atan2(xx, yy)/M_PI*256*9) + (sqrt(xx*xx+yy*yy)*5)))&255; + } + } + + x->x_rx = inline_fastrand()%x->x_vwidth; + x->x_ry = inline_fastrand()%x->x_vheight; + x->x_bx = inline_fastrand()%x->x_vwidth; + x->x_by = inline_fastrand()%x->x_vheight; +} + +static void pdp_shagadelic_free_ressources(t_pdp_shagadelic *x) +{ + if (x->x_ripple) freebytes( x->x_ripple, x->x_vsize*4 ); + if (x->x_spiral) freebytes( x->x_spiral, x->x_vsize ); +} + +static void pdp_shagadelic_allocate(t_pdp_shagadelic *x) +{ + x->x_ripple = (char *) getbytes( x->x_vsize*4 ); + x->x_spiral = (char *) getbytes( x->x_vsize ); +} + +static void pdp_shagadelic_process_yv12(t_pdp_shagadelic *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, py; + unsigned char y, u, v; + char *p_y, *p_u, *p_v; + + + /* allocate all ressources */ + if ( ((int)header->info.image.width != x->x_vwidth) || + ((int)header->info.image.height != x->x_vheight) ) + { + pdp_shagadelic_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_shagadelic_allocate(x); + post( "pdp_shagadelic : reallocated buffers" ); + pdp_shagadelic_init_tables(x); + post( "pdp_shagadelic : initialized tables" ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + p_y = &x->x_ripple[x->x_ry*x->x_vwidth*2 + x->x_rx]; + p_u = x->x_spiral; + p_v = &x->x_ripple[x->x_by*x->x_vwidth*2 + x->x_bx]; + + for(py=0; py<x->x_vheight; py++) + { + for(px=0; px<x->x_vwidth; px++) + { + y = (char)(*p_y+x->x_phase*2)>>7; + u = (char)(*p_u+x->x_phase*3)>>7; + v = (char)(*p_v-x->x_phase)>>7; + *(newdata+py*x->x_vwidth+px) = *(data) & (y<<7) & x->x_mask; + *(newdata+x->x_vsize+((py*x->x_vwidth+px)>>2)) = *(data) & ((u-128)<<8) & x->x_mask; + *(newdata+x->x_vsize+(x->x_vsize>>2)+((py*x->x_vwidth+px)>>2)) = *(data) & ((v-128)<<8) & x->x_mask; + p_y++; + p_u++; + p_v++; + data++; + } + p_y += x->x_vwidth; + p_v += x->x_vwidth; + } + + x->x_phase -= 8; + if((x->x_rx+x->x_rvx)<0 || (x->x_rx+x->x_rvx)>=x->x_vwidth) x->x_rvx =-x->x_rvx; + if((x->x_ry+x->x_rvy)<0 || (x->x_ry+x->x_rvy)>=x->x_vheight) x->x_rvy =-x->x_rvy; + if((x->x_bx+x->x_bvx)<0 || (x->x_bx+x->x_bvx)>=x->x_vwidth) x->x_bvx =-x->x_bvx; + if((x->x_by+x->x_bvy)<0 || (x->x_by+x->x_bvy)>=x->x_vheight) x->x_bvy =-x->x_bvy; + + x->x_rx += x->x_rvx; + x->x_ry += x->x_rvy; + x->x_bx += x->x_bvx; + x->x_by += x->x_bvy; + + return; +} + +static void pdp_shagadelic_sendpacket(t_pdp_shagadelic *x) +{ + /* delete source 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_shagadelic_process(t_pdp_shagadelic *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_shagadelic_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_shagadelic_process_yv12, pdp_shagadelic_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_shagadelic_process */ + break; + + } + } + +} + +static void pdp_shagadelic_input_0(t_pdp_shagadelic *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_shagadelic_process(x); + + } + +} + +static void pdp_shagadelic_free(t_pdp_shagadelic *x) +{ + int i; + + pdp_shagadelic_free_ressources(x); + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_shagadelic_class; + +void *pdp_shagadelic_new(void) +{ + int i; + + t_pdp_shagadelic *x = (t_pdp_shagadelic *)pd_new(pdp_shagadelic_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("mask")); + + 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_rvx = -2; + x->x_rvy = -2; + x->x_bvx = 2; + x->x_bvy = 2; + x->x_phase = 0; + x->x_mask = 0xffff; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_shagadelic_setup(void) +{ +// post( pdp_shagadelic_version ); + pdp_shagadelic_class = class_new(gensym("pdp_shagadelic"), (t_newmethod)pdp_shagadelic_new, + (t_method)pdp_shagadelic_free, sizeof(t_pdp_shagadelic), 0, A_NULL); + + class_addmethod(pdp_shagadelic_class, (t_method)pdp_shagadelic_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_shagadelic_class, (t_method)pdp_shagadelic_mask, gensym("mask"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_simura.c b/modules/pdp_simura.c new file mode 100644 index 0000000..04fd74b --- /dev/null +++ b/modules/pdp_simura.c @@ -0,0 +1,443 @@ +/* + * Pure Data Packet module. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is an adaptation of simura effect from freej + * Originally written by Fukuchi Kentarou + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +static char *pdp_simura_version = "pdp_simura: version 0.1, port of simura from freej ( Fukuchi Kentarou ), adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_simura_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; + + unsigned short int x_color; /* color for the mask */ + t_int x_mode; /* mirror mode */ + +} t_pdp_simura; + +static void pdp_simura_process_yv12(t_pdp_simura *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); + int newpacket = -1, i; + + unsigned int w = header->info.image.width; + unsigned int hw = w/2; + unsigned int hhw = w/4; + unsigned int h = header->info.image.height; + unsigned int hh = h/2; + unsigned int hhh = h/4; + + unsigned int size = w*h; + unsigned int totalnbpixels = size; + unsigned int u_offset = size; + unsigned int v_offset = size + (size>>2); + unsigned int totnbpixels = size + (size>>1); + + unsigned int px, py; + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = w; + newheader->info.image.height = h; + + switch ( x->x_mode ) + { + case 0 : + // y component + for(py=0; py<h; py++){ + for(px=0; px<w; px++){ + newdata[py*w+px] = data[py*w+px]; + } + } + // u component + for(py=0; py<hh; py++){ + for(px=0; px<hw; px++){ + newdata[u_offset+py*hw+px] = data[u_offset+py*hw+px] ^ x->x_color; + } + } + // v component + for(py=0; py<hh; py++){ + for(px=0; px<hw; px++){ + newdata[v_offset+py*hw+px] = data[v_offset+py*hw+px] ^ x->x_color; + } + } + break; + case 1 : + // y component + for(py=0; py<(hh); py++){ + for(px=0; px<w; px++){ + newdata[py*(w)+px] = data[py*(w)+px]; + newdata[((h)-py-1)*(w)+px] = data[py*(w)+px]; + } + } + // u component + for(py=0; py<(hhh); py++){ + for(px=0; px<hw; px++){ + newdata[u_offset+py*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+((hh)-py-1)*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + } + } + // v component + for(py=0; py<(hhh); py++){ + for(px=0; px<hw; px++){ + newdata[v_offset+py*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+((hh)-py-1)*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + } + } + break; + case 2 : + // y component + for(py=(hh); py<h; py++){ + for(px=0; px<w; px++){ + newdata[py*(w)+px] = data[py*(w)+px]; + newdata[((h)-py-1)*(w)+px] = data[py*(w)+px]; + } + } + // u component + for(py=(hhh); py<hh; py++){ + for(px=0; px<hw; px++){ + newdata[u_offset+py*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+((hh)-py-1)*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + } + } + // v component + for(py=(hhh); py<hh; py++){ + for(px=0; px<hw; px++){ + newdata[v_offset+py*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+((hh)-py-1)*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + } + } + break; + case 3 : + // y component + for(py=0; py<h; py++){ + for(px=0; px<(hw); px++){ + newdata[py*(w)+px] = data[py*(w)+px]; + newdata[py*(w)+((w)-px-1)] = data[py*(w)+px]; + } + } + // u component + for(py=0; py<hh; py++){ + for(px=0; px<(hhw); px++){ + newdata[u_offset+py*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+py*(hw)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + } + } + // v component + for(py=0; py<hh; py++){ + for(px=0; px<(hhw); px++){ + newdata[v_offset+py*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+py*(hw)+((hw)-px-1)] = data[v_offset+py*(hw)+px] ^ x->x_color; + } + } + break; + case 4 : + // y component + for(py=0; py<h; py++){ + for(px=(hw); px<w; px++){ + newdata[py*(w)+px] = data[py*(w)+px]; + newdata[py*(w)+((w)-px-1)] = data[py*(w)+px]; + } + } + // u component + for(py=0; py<hh; py++){ + for(px=(hhw); px<hw; px++){ + newdata[u_offset+py*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+py*(hw)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + } + } + // u component + for(py=0; py<hh; py++){ + for(px=(hhw); px<hw; px++){ + newdata[u_offset+py*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+py*(hw)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + } + } + break; + case 5 : + // y component + for(py=0; py<(hh); py++){ + for(px=0; px<(hw); px++){ + newdata[py*(w)+px] = data[py*(w)+px]; + newdata[py*(w)+((w)-px-1)] = data[py*(w)+px]; + newdata[((h)-py-1)*(w)+px] = data[py*(w)+px]; + newdata[((h)-py-1)*(w)+((w)-px-1)] = data[py*(w)+px]; + } + } + // u component + for(py=0; py<(hhh); py++){ + for(px=0; px<(hhw); px++){ + newdata[u_offset+py*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+py*(hw)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+((hh)-py-1)*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+((hh)-py-1)*(w)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + } + } + // v component + for(py=0; py<(hhh); py++){ + for(px=0; px<(hhw); px++){ + newdata[v_offset+py*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+py*(hw)+((hw)-px-1)] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+((hh)-py-1)*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+((hh)-py-1)*(hw)+((hw)-px-1)] = data[v_offset+py*(hw)+px] ^ x->x_color; + } + } + break; + case 6 : + // y component + for(py=0; py<(hh); py++){ + for(px=(hw); px<w; px++){ + newdata[py*(w)+px] = data[py*(w)+px]; + newdata[py*(w)+((w)-px-1)] = data[py*(w)+px]; + newdata[((h)-py-1)*(w)+px] = data[py*(w)+px]; + newdata[((h)-py-1)*(w)+((w)-px-1)] = data[py*(w)+px]; + } + } + // u component + for(py=0; py<(hhh); py++){ + for(px=(hhw); px<(hw); px++){ + newdata[u_offset+py*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+py*(hw)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+((hh)-py-1)*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+((hh)-py-1)*(hw)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + } + } + // v component + for(py=0; py<(hhh); py++){ + for(px=(hhw); px<(hw); px++){ + newdata[v_offset+py*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+py*(hw)+((hw)-px-1)] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+((hh)-py-1)*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+((hh)-py-1)*(hw)+((hw)-px-1)] = data[v_offset+py*(hw)+px] ^ x->x_color; + } + } + break; + case 7 : + // y component + for(py=(hh); py<(h); py++){ + for(px=0; px<(hw); px++){ + newdata[py*(w)+px] = data[py*(w)+px]; + newdata[py*(w)+((w)-px-1)] = data[py*(w)+px]; + newdata[((h)-py-1)*(w)+px] = data[py*(w)+px]; + newdata[((h)-py-1)*(w)+((w)-px-1)] = data[py*(w)+px]; + } + } + // u component + for(py=(hhh); py<(hh); py++){ + for(px=0; px<(hhw); px++){ + newdata[u_offset+py*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+py*(hw)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+((hh)-py-1)*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+((hh)-py-1)*(hw)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + } + } + // v component + for(py=(hhh); py<(hh); py++){ + for(px=0; px<(hhw); px++){ + newdata[v_offset+py*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+py*(hw)+((hw)-px-1)] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+((hh)-py-1)*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+((hh)-py-1)*(hw)+((hw)-px-1)] = data[v_offset+py*(hw)+px]^ x->x_color; + } + } + break; + case 8 : + // y component + for(py=(hh); py<h; py++){ + for(px=(hw); px<w; px++){ + newdata[py*(w)+px] = data[py*(w)+px]; + newdata[py*(w)+((w)-px-1)] = data[py*(w)+px]; + newdata[((h)-py-1)*(w)+px] = data[py*(w)+px]; + newdata[((h)-py-1)*(w)+((w)-px-1)] = data[py*(w)+px]; + } + } + // u component + for(py=(hhh); py<(hh); py++){ + for(px=(hhw); px<(hw); px++){ + newdata[u_offset+py*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+py*(hw)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+((hh)-py-1)*(hw)+px] = data[u_offset+py*(hw)+px] ^ x->x_color; + newdata[u_offset+((hh)-py-1)*(hw)+((hw)-px-1)] = data[u_offset+py*(hw)+px] ^ x->x_color; + } + } + // v component + for(py=(hhh); py<(hh); py++){ + for(px=(hhw); px<(hw); px++){ + newdata[v_offset+py*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+py*(hw)+((hw)-px-1)] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+((hh)-py-1)*(hw)+px] = data[v_offset+py*(hw)+px] ^ x->x_color; + newdata[v_offset+((hh)-py-1)*(hw)+((hw)-px-1)] = data[v_offset+py*(hw)+px] ^ x->x_color; + } + } + break; + } + // post( "pdp_simura : size=%d tsize=%d", size, (int)(size + (size>>1))<<1 ); + + /* delete source packet and replace with new packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = newpacket; + return; +} + +static void pdp_simura_sendpacket(t_pdp_simura *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_simura_process(t_pdp_simura *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_simura_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_simura_process_yv12, pdp_simura_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_simura_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_simura_process */ + break; + + } + } +} + +static void pdp_simura_input_0(t_pdp_simura *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_simura_process(x); + } +} + +static void pdp_simura_color(t_pdp_simura *x, t_floatarg fcolor ) +{ + if ( (int)fcolor >0 && (int)fcolor < 0xFFFF ) + { + x->x_color = (unsigned short int)fcolor; + } + else + { + post( "pdp_simura : wrong color %d", (int) fcolor ); + } +} + +static void pdp_simura_mode(t_pdp_simura *x, t_floatarg fmode ) +{ + if ( (int)fmode >=0 && (int)fmode <= 8 ) + { + x->x_mode = (int)fmode; + } + else + { + post( "pdp_simura : wrong mode : %d : must be 0<=mode<=8", (int)fmode ); + } +} + + +static void pdp_simura_free(t_pdp_simura *x) +{ + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_simura_class; + +void *pdp_simura_new(void) +{ + int i; + + t_pdp_simura *x = (t_pdp_simura *)pd_new(pdp_simura_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("color")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("mode")); + + 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_color = 0; + x->x_mode = 0; // no mirror + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_simura_setup(void) +{ +// post( pdp_simura_version ); + + pdp_simura_class = class_new(gensym("pdp_simura"), (t_newmethod)pdp_simura_new, + (t_method)pdp_simura_free, sizeof(t_pdp_simura), 0, A_NULL); + + class_addmethod(pdp_simura_class, (t_method)pdp_simura_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_simura_class, (t_method)pdp_simura_color, gensym("color"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_simura_class, (t_method)pdp_simura_mode, gensym("mode"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_smuck.c b/modules/pdp_smuck.c new file mode 100644 index 0000000..968bea4 --- /dev/null +++ b/modules/pdp_smuck.c @@ -0,0 +1,228 @@ +/* + * 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 adaptation of smuck effect from veejay + * But it it inspired by effectv's transform ( mode 5 ) + * Originally written by Niels Elburg + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define MAX_N 100 + +static int fastrand_val=0; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + +static char *pdp_smuck_version = "pdp_smuck: version 0.1, port of smuck from veejay( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_smuck_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_n; // transform number + +} t_pdp_smuck; + +static void pdp_smuck_free_ressources(t_pdp_smuck *x) +{ + // nothing +} + +static void pdp_smuck_allocate(t_pdp_smuck *x) +{ + // nothing +} + +static void pdp_smuck_n(t_pdp_smuck *x, t_floatarg fn ) +{ + if ( ( fn >= 0 ) && ( fn < MAX_N ) ) + { + x->x_n = fn; + } +} + +static void pdp_smuck_process_yv12(t_pdp_smuck *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 px, py, pxx, pyy; + short int *pnY, *pnU, *pnV; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_smuck_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_smuck_allocate(x); + post( "pdp_smuck : reallocated buffers" ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + pnY = newdata; + pnV = newdata+x->x_vsize; + pnU = newdata+x->x_vsize+(x->x_vsize>>2); + + for (py = 0; py < x->x_vheight; py++) { + for (px = 0; px < x->x_vwidth; px++) { + pyy = py + (inline_fastrand() >> x->x_n) - 2; + pxx = px + (inline_fastrand() >> x->x_n) - 2; + if (pxx > x->x_vwidth) + pxx = x->x_vwidth; + if ( pxx < 0 ) pxx = 0; + if (pyy > x->x_vheight) + pyy = x->x_vheight; + if ( pyy < 0 ) pyy = 0; + *pnY++ = *( data + pyy*x->x_vwidth + pxx ); + if ( (px%2==0) && (py%2==0) ) + { + *pnU++ = *( data + x->x_vsize + ( (pyy>>1)*(x->x_vwidth>>1) + (pxx>>2) ) ); + *pnV++ = *( data + x->x_vsize + (x->x_vsize>>2) + ( (pyy>>1)*(x->x_vwidth>>1) + (pxx>>2) ) ); + } + } + } + + return; +} + +static void pdp_smuck_sendpacket(t_pdp_smuck *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_smuck_process(t_pdp_smuck *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_smuck_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_smuck_process_yv12, pdp_smuck_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_smuck_process */ + break; + + } + } +} + +static void pdp_smuck_input_0(t_pdp_smuck *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_smuck_process(x); + } +} + +static void pdp_smuck_free(t_pdp_smuck *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_smuck_free_ressources(x); +} + +t_class *pdp_smuck_class; + +void *pdp_smuck_new(void) +{ + int i; + + t_pdp_smuck *x = (t_pdp_smuck *)pd_new(pdp_smuck_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("n")); + + 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_n = 30; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_smuck_setup(void) +{ +// post( pdp_smuck_version ); + pdp_smuck_class = class_new(gensym("pdp_smuck"), (t_newmethod)pdp_smuck_new, + (t_method)pdp_smuck_free, sizeof(t_pdp_smuck), 0, A_NULL); + + class_addmethod(pdp_smuck_class, (t_method)pdp_smuck_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_smuck_class, (t_method)pdp_smuck_n, gensym("n"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_spigot.c b/modules/pdp_spigot.c new file mode 100644 index 0000000..3b79bb1 --- /dev/null +++ b/modules/pdp_spigot.c @@ -0,0 +1,168 @@ +/* + * Pure Data Packet module. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is video packet routing utility + * Written by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include <math.h> + +static char *pdp_spigot_version = "pdp_spigot: version 0.1, a video packets routing utility"; + +typedef struct pdp_spigot_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + t_outlet *x_outlet1; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_int x_packet0; + t_int x_toggle; + +} t_pdp_spigot; + +static void pdp_spigot_toggle(t_pdp_spigot *x, t_floatarg ftoggle ) +{ + if ( ( ftoggle == 0 ) || ( ftoggle == 1 ) ) + { + x->x_toggle = ftoggle; + } +} + +static void pdp_spigot_process_packet(t_pdp_spigot *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *newheader = 0; + short int *newdata = 0; + t_int newpacket = -1, i; + + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + return; +} + +static void pdp_spigot_process(t_pdp_spigot *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_spigot_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding) + { + + case PDP_IMAGE_YV12: + pdp_spigot_process_packet(x); + 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_spigot_process */ + break; + + } + } + + /* propagate if valid */ + if(x->x_packet0 != -1){ + if ( x->x_toggle ) + { + pdp_packet_pass_if_valid(x->x_outlet1, &x->x_packet0); + } + else + { + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); + } + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + } + +} + +static void pdp_spigot_input_0(t_pdp_spigot *x, t_symbol *s, t_floatarg f) +{ + if (s == gensym("register_rw")){ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = pdp_packet_convert_rw((int)f, pdp_gensym("image/YCrCb/*") ); + } + else if (s == gensym("process")){ + pdp_spigot_process(x); + } +} + +static void pdp_spigot_free(t_pdp_spigot *x) +{ + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_spigot_class; + +void *pdp_spigot_new(void) +{ + int i; + + t_pdp_spigot *x = (t_pdp_spigot *)pd_new(pdp_spigot_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("toggle")); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_outlet1 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_spigot_setup(void) +{ +// post( pdp_spigot_version ); + pdp_spigot_class = class_new(gensym("pdp_spigot"), (t_newmethod)pdp_spigot_new, + (t_method)pdp_spigot_free, sizeof(t_pdp_spigot), 0, A_NULL); + + class_addmethod(pdp_spigot_class, (t_method)pdp_spigot_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_spigot_class, (t_method)pdp_spigot_toggle, gensym("toggle"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_spiral.c b/modules/pdp_spiral.c new file mode 100644 index 0000000..a97442b --- /dev/null +++ b/modules/pdp_spiral.c @@ -0,0 +1,519 @@ +/* + * 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 adaptation of warp effect from effectv + * copyright (c) 2001 Sam Mertens. + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define PLANE_POWER (4) // 2 exp 4 = 16 +#define WAVE_COUNT_POWER (3) // 2 exp 3 = 8 +#define WAVE_LENGTH_POWER (9) // 2 exp 9 = 512 + +#define PLANES (1 << PLANE_POWER) // 16 +#define PLANE_MASK (PLANES - 1) +#define PLANE_MAX (PLANES - 1) + +#define WAVE_COUNT (1 << WAVE_COUNT_POWER) // 8 +#define WAVE_MASK (WAVE_COUNT - 1) +#define WAVE_MAX (WAVE_COUNT - 1) + +#define WAVE_LENGTH (1 << WAVE_LENGTH_POWER) // 512 +#define WAVE_LENGTH_MASK (WAVE_LENGTH - 1) + + +#define WAVE_CONCENTRIC_A 0 +#define WAVE_SAWTOOTH_UP 1 +#define WAVE_SAWTOOTH_DOWN 2 +#define WAVE_TRIANGLE 3 + +#define WAVE_SINUS 4 +#define WAVE_CONCENTRIC_B 5 +#define WAVE_LENS 6 +#define WAVE_FLAT 7 + +/* The *_OFFSET predefines are just precalculations. There shouldn't normally +** be any need to change them. +*/ + +#define WAVE_CONCENTRIC_A_OFFSET (WAVE_CONCENTRIC_A * WAVE_LENGTH) +#define WAVE_SAW_UP_OFFSET (WAVE_SAWTOOTH_UP * WAVE_LENGTH) +#define WAVE_SAW_DOWN_OFFSET (WAVE_SAWTOOTH_DOWN * WAVE_LENGTH) +#define WAVE_TRIANGLE_OFFSET (WAVE_TRIANGLE * WAVE_LENGTH) + +#define WAVE_CONCENTRIC_B_OFFSET (WAVE_CONCENTRIC_B * WAVE_LENGTH) +#define WAVE_LENS_OFFSET (WAVE_LENS * WAVE_LENGTH) +#define WAVE_SINUS_OFFSET (WAVE_SINUS * WAVE_LENGTH) +#define WAVE_FLAT_OFFSET (WAVE_FLAT * WAVE_LENGTH) + +#define WAVE_ELEMENT_SIZE (sizeof(char)) +#define WAVE_TABLE_SIZE (WAVE_COUNT * WAVE_LENGTH * WAVE_ELEMENT_SIZE) + +#define FOCUS_INCREMENT_PRESET (M_PI/2.0) + +static char *pdp_spiral_version = "pdp_spiral: version 0.1, port of spiral from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +static char* the_wave_table = NULL; + +typedef struct pdp_spiral_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; + short int *x_buffer; + short int *x_planetable[PLANES]; + t_int x_plane; + t_int *x_depthmap; + t_int x_mode; + t_int x_focus_x; + t_int x_focus_y; + t_int x_cursor_state; + t_int x_cursor_local; + t_int x_toggle_xor; + t_int x_animate_focus; + t_int x_focus_interval; + t_int x_focus_counter; + unsigned int x_depth_shift; // Cheesy way to adjust intensity + t_int x_focus_radius; + double x_focus_degree; + double x_focus_increment; + +} t_pdp_spiral; + +static void pdp_spiral_define_waves(t_pdp_spiral *x) +{ + t_int i, w, iw; + double sinus_val = M_PI/2.0; + + if (NULL == the_wave_table) return; + + w = ((int)sqrt(x->x_vheight * x->x_vheight + x->x_vwidth * x->x_vwidth)); + for (i=0; i<WAVE_LENGTH; i++) + { + the_wave_table[WAVE_FLAT_OFFSET + i] = 0; + + the_wave_table[WAVE_SAW_UP_OFFSET + i] = i & PLANE_MASK; + the_wave_table[WAVE_SAW_DOWN_OFFSET + i] = PLANE_MAX - (i & PLANE_MASK); + if (i & PLANES) + { + the_wave_table[WAVE_TRIANGLE_OFFSET + i] = (~i) & PLANE_MASK; + } + else + { + the_wave_table[WAVE_TRIANGLE_OFFSET + i] = i & PLANE_MASK; + } + + iw = i / (w/(PLANES*2)); + + if (iw & PLANES) + { + the_wave_table[WAVE_CONCENTRIC_A_OFFSET + i] = (~iw) & PLANE_MASK; + } + else + { + the_wave_table[WAVE_CONCENTRIC_A_OFFSET + i] = iw & PLANE_MASK; + } + + the_wave_table[WAVE_CONCENTRIC_B_OFFSET + i] = (i*PLANES)/w; + the_wave_table[WAVE_LENS_OFFSET + i] = i >> 3; + the_wave_table[WAVE_SINUS_OFFSET + i] = ((PLANES/2) + + (int)((PLANES/2 - 1) * sin(sinus_val))) & PLANE_MASK; + sinus_val += M_PI/PLANES; + } + +} + +void pdp_spiral_create_map(t_pdp_spiral *x) +{ + t_int px, py, rel_x, rel_y, yy; + float x_ratio; + float y_ratio; + t_int v, i, wave_offset; + + if ( x->x_vsize == -1 ) + { + post( "pdp_spiral : create_map : no video data" ); + return; + } + + /* + ** The following code generates the default depth map. + */ + i = 0; + wave_offset = x->x_mode * WAVE_LENGTH; + + x_ratio = 320.0 / x->x_vwidth; + y_ratio = 240.0 / x->x_vheight; + + for (py=0; py<x->x_vheight; py++) + { + rel_y = (x->x_focus_y - py) * y_ratio; + yy = rel_y * rel_y; + + for(px=0; px<x->x_vwidth; px++) + { + rel_x = (x->x_focus_x - px) * x_ratio; + v = ((int)sqrt(yy + rel_x*rel_x)) & WAVE_LENGTH_MASK; + x->x_depthmap[i++] = the_wave_table[wave_offset + v] >> x->x_depth_shift; + } + } + + return; +} + +static void pdp_spiral_mode(t_pdp_spiral *x, t_floatarg fmode ) +{ + if ( ( fmode > 0 ) || ( fmode < WAVE_COUNT ) ) + { + x->x_mode = (int)fmode; + pdp_spiral_create_map(x); + } +} + +static void pdp_spiral_focus_x(t_pdp_spiral *x, t_floatarg ffocusx ) +{ + if ( ( ffocusx > 0 ) || ( ffocusx < x->x_vwidth ) ) + { + x->x_focus_x = (int)ffocusx; + pdp_spiral_create_map(x); + } +} + +static void pdp_spiral_focus_y(t_pdp_spiral *x, t_floatarg ffocusy ) +{ + if ( ( ffocusy > 0 ) || ( ffocusy < x->x_vwidth ) ) + { + x->x_focus_y = (int)ffocusy; + pdp_spiral_create_map(x); + } +} + +static void pdp_spiral_depth_shift(t_pdp_spiral *x, t_floatarg fdepthshift ) +{ + if ( ( fdepthshift > 0 ) || ( fdepthshift < 5 ) ) + { + x->x_depth_shift = (int)fdepthshift; + pdp_spiral_create_map(x); + } +} + +static void pdp_spiral_focus_interval(t_pdp_spiral *x, t_floatarg finterval ) +{ + if ( ( finterval > 0 ) || ( finterval < 60 ) ) + { + x->x_focus_interval = (int)finterval; + } +} + +static void pdp_spiral_focus_increment(t_pdp_spiral *x, t_floatarg fincrement ) +{ + if ( fincrement > 0 ) + { + x->x_focus_increment = (int)fincrement; + } +} + +static void pdp_spiral_toggle_xor(t_pdp_spiral *x, t_floatarg fxor ) +{ + if ( ( fxor ==0 ) || ( fxor == 1 ) ) + { + x->x_toggle_xor = (int)fxor; + } +} + +static void pdp_spiral_animate_focus(t_pdp_spiral *x, t_floatarg fafocus ) +{ + if ( ( fafocus ==0 ) || ( fafocus == 1 ) ) + { + x->x_animate_focus = (int)fafocus; + } +} + +static void pdp_spiral_free_ressources(t_pdp_spiral *x) +{ + if ( the_wave_table ) free ( the_wave_table ); + if ( x->x_buffer ) free ( x->x_buffer ); + if ( x->x_depthmap ) free ( x->x_depthmap ); +} + +static void pdp_spiral_allocate(t_pdp_spiral *x) +{ + int i; + + the_wave_table = (char*) malloc (WAVE_TABLE_SIZE); + x->x_focus_radius = x->x_vwidth / 2; + + // allocate space for the frame buffers. A lot of memory is required - + // with the default settings, it totals nearly 5 megs. + x->x_buffer = (short int *) malloc ( ( ( x->x_vsize + x->x_vsize>>1 ) << 1 ) * 2 * PLANES); + + // set up the array of pointers to the frame buffers + for(i=0;i<PLANES;i++) + { + x->x_planetable[i] = &x->x_buffer[ ( ( x->x_vsize + x->x_vsize>>1 ) << 1 ) * i]; + } + + x->x_depthmap = (t_int*) malloc ( x->x_vsize * sizeof ( t_int ) ); + + if ( !the_wave_table || !x->x_buffer || !x->x_depthmap ) + { + post( "pdp_spiral : severe error : cannot allocate buffers !!! "); + return; + } +} + +static void pdp_spiral_move_focus(t_pdp_spiral *x) +{ + x->x_focus_counter++; + // We'll only switch maps every X frames. + if (x->x_focus_interval <= x->x_focus_counter) + { + x->x_focus_counter = 0; + x->x_focus_x = (x->x_focus_radius * cos(x->x_focus_degree)) + (x->x_vwidth/2); + x->x_focus_y = (x->x_focus_radius * sin(x->x_focus_degree*2.0)) + (x->x_vheight/2); + pdp_spiral_create_map(x); + x->x_focus_degree += x->x_focus_increment; + if ((2.0*M_PI) <= x->x_focus_degree) + { + x->x_focus_degree -= (2.0*M_PI); + } + } +} + +static void pdp_spiral_process_yv12(t_pdp_spiral *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); + int i, iu; + + unsigned int totalnbpixels; + unsigned int u_offset; + unsigned int v_offset; + unsigned int totnbpixels; + + int px, py; + int cf; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_spiral_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; + x->x_focus_x = (x->x_vwidth/2); + x->x_focus_y = (x->x_vheight/2); + x->x_plane = PLANE_MAX; + pdp_spiral_allocate(x); + post( "pdp_spiral : reallocated buffers" ); + pdp_spiral_define_waves( x ); + pdp_spiral_create_map( x ); + post( "pdp_spiral : set wave table" ); + } + + totalnbpixels = x->x_vsize; + u_offset = x->x_vsize; + v_offset = x->x_vsize + (x->x_vsize>>2); + totnbpixels = x->x_vsize + (x->x_vsize>>1); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + // post( "pdp_spiral : buffer=%x limit=%x dest=%x size=%d data=%x plane=%d", + // x->x_buffer, x->x_buffer + ( ( x->x_vsize + x->x_vsize>>1 ) << 1 ) * PLANES - 1, + // x->x_planetable[x->x_plane], ( ( x->x_vsize + x->x_vsize>>1) << 1 ), data, x->x_plane ); + memcpy( x->x_planetable[x->x_plane], data, ( ( x->x_vsize + x->x_vsize>>1 ) << 1 ) ); + + if (x->x_animate_focus) + { + pdp_spiral_move_focus(x); + } + + i = 0; + iu = 0; + for(py = 0; py < x->x_vheight; py++) + { + for(px = 0; px < x->x_vwidth; px++) + { + cf = (x->x_plane + x->x_depthmap[i]) & PLANE_MASK; + newdata[i] = (x->x_planetable[cf])[i]; + // u & v are untouched + newdata[x->x_vsize+iu] = data[x->x_vsize+iu]; + newdata[x->x_vsize+(x->x_vsize>>2)+iu] = data[x->x_vsize+(x->x_vsize>>2)+iu]; + i++; + if ( (px%2==0) && (py%2==0) ) + { + iu++; + } + } + } + + x->x_plane--; + x->x_plane &= PLANE_MASK; + + return; +} + +static void pdp_spiral_sendpacket(t_pdp_spiral *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_spiral_process(t_pdp_spiral *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_spiral_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_spiral_process_yv12, pdp_spiral_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + break; + + default: + /* don't know the type, so dont pdp_spiral_process */ + break; + + } + } +} + +static void pdp_spiral_input_0(t_pdp_spiral *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_spiral_process(x); + } +} + +static void pdp_spiral_free(t_pdp_spiral *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_spiral_free_ressources(x); +} + +t_class *pdp_spiral_class; + +void *pdp_spiral_new(void) +{ + int i; + + t_pdp_spiral *x = (t_pdp_spiral *)pd_new(pdp_spiral_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("mode")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("focus_x")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("focus_y")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("depth_shift")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("focus_interval")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("focus_increment")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("toggle_xor")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("animate_focus")); + + 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_mode = 0; + x->x_cursor_state = 0; + x->x_cursor_local = 0; + x->x_toggle_xor = 0; + x->x_animate_focus = 0; + x->x_focus_interval = 6; + x->x_focus_counter = 0; + x->x_depth_shift = 0; // Cheesy way to adjust intensity + x->x_focus_radius = 100; + x->x_focus_degree = 1.0; + x->x_focus_increment = FOCUS_INCREMENT_PRESET; + x->x_buffer = NULL; + x->x_depthmap = NULL; + the_wave_table = NULL; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_spiral_setup(void) +{ +// post( pdp_spiral_version ); + pdp_spiral_class = class_new(gensym("pdp_spiral"), (t_newmethod)pdp_spiral_new, + (t_method)pdp_spiral_free, sizeof(t_pdp_spiral), 0, A_NULL); + + class_addmethod(pdp_spiral_class, (t_method)pdp_spiral_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_spiral_class, (t_method)pdp_spiral_mode, gensym("mode"), A_FLOAT, A_NULL); + class_addmethod(pdp_spiral_class, (t_method)pdp_spiral_focus_x, gensym("focus_x"), A_FLOAT, A_NULL); + class_addmethod(pdp_spiral_class, (t_method)pdp_spiral_focus_y, gensym("focus_y"), A_FLOAT, A_NULL); + class_addmethod(pdp_spiral_class, (t_method)pdp_spiral_depth_shift, gensym("depth_shift"), A_FLOAT, A_NULL); + class_addmethod(pdp_spiral_class, (t_method)pdp_spiral_depth_shift, gensym("focus_interval"), A_FLOAT, A_NULL); + class_addmethod(pdp_spiral_class, (t_method)pdp_spiral_depth_shift, gensym("focus_increment"), A_FLOAT, A_NULL); + class_addmethod(pdp_spiral_class, (t_method)pdp_spiral_depth_shift, gensym("toggle_xor"), A_FLOAT, A_NULL); + class_addmethod(pdp_spiral_class, (t_method)pdp_spiral_depth_shift, gensym("animate_focus"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_text.c b/modules/pdp_text.c new file mode 100644 index 0000000..81bd83f --- /dev/null +++ b/modules/pdp_text.c @@ -0,0 +1,630 @@ +/* + * 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 text rendering object for PDP + * It uses imlib2 for all graphical operations + */ + +/* Listening to : + * Deviants - Nothing Man + * Monte Cazzaza - Kick That Habit Man + */ + +#include "pdp.h" +#include "yuv.h" +#include <math.h> +#include <ctype.h> +#include <Imlib2.h> // imlib2 is required + +#define DEFAULT_CAPACITY 10 +#define DEFAULT_FONT "helmetr/16" + +static char *pdp_text_version = "pdp_text: version 0.2 : text rendering object written by ydegoyon@free.fr"; + +typedef struct pdp_text_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + char **x_text_array; + t_int *x_xoffsets; + t_int *x_yoffsets; + t_int *x_r; + t_int *x_g; + t_int *x_b; + t_float *x_angle; + t_int *x_scroll; + + t_int x_nbtexts; + t_int x_current; + t_int x_capacity; + + /* imlib data */ + Imlib_Image x_image; + Imlib_Font x_font; + +} t_pdp_text; + + /* add a new text : syntax : text <my%20text> x y */ +static void pdp_text_add(t_pdp_text *x, t_symbol *s, int argc, t_atom *argv) +{ + char *pname; + char *pdname; + + if ( x->x_nbtexts >= x->x_capacity ) + { + post( "pdp_text : sorry, maximum capacity has been reached... try resize" ); + return; + } + + if ( argc < 3 ) + { + post( "pdp_text : error in the number of arguments ( minimum is 3 )", argc ); + return; + } + if ( argv[0].a_type != A_SYMBOL || argv[1].a_type != A_FLOAT || argv[2].a_type != A_FLOAT ) { + post( "pdp_text : add : wrong arguments" ); + return; + } + + // allocate new text area + pdname = x->x_text_array[x->x_nbtexts] = (char *) malloc( strlen( argv[0].a_w.w_symbol->s_name ) ); + pname=argv[0].a_w.w_symbol->s_name; + while (*(pname)) + { + if ( (*pname=='%') && ( isdigit(*(pname+1)) || (*(pname+1)=='%') ) ) + { + t_int ivalue; + t_int ndigits; + char *piname; + + ndigits=0; + piname=pname+1; + while ( isdigit( *(piname++) ) ) ndigits++; + + ivalue=atoi(pname+1); + + // special case %% + if ( ( pname != argv[0].a_w.w_symbol->s_name ) && ( *(pname+1) == '%' ) ) + { + *(pdname++)=*(pname++); + pname++; + continue; + } + *(pdname++)=(char)ivalue; + pname+=ndigits+1; + } + else if ( !strncmp( pname, "\"", 1 ) ) // quotes are ignored unless %34 + { + pname++; + } + else + { + *(pdname++)=*(pname++); + } + } + *(pdname)='\0'; + x->x_xoffsets[x->x_nbtexts] = (int)argv[1].a_w.w_float; + x->x_yoffsets[x->x_nbtexts] = (int)argv[2].a_w.w_float; + + if ( (argc>=4) && (argv[3].a_type == A_FLOAT) ) + { + x->x_r[x->x_nbtexts] = (int)argv[3].a_w.w_float; + } + if ( (argc>=5) && (argv[4].a_type == A_FLOAT) ) + { + x->x_g[x->x_nbtexts] = (int)argv[4].a_w.w_float; + } + if ( (argc>=6) && (argv[5].a_type == A_FLOAT) ) + { + x->x_b[x->x_nbtexts] = (int)argv[5].a_w.w_float; + } + if ( (argc>=7) && (argv[6].a_type == A_FLOAT) ) + { + x->x_angle[x->x_nbtexts] = argv[6].a_w.w_float; + } + if ( (argc>=8) && (argv[7].a_type == A_FLOAT) ) + { + x->x_scroll[x->x_nbtexts] = (int)argv[7].a_w.w_float; + } + + + post( "pdp_text : added text >%s< @ %d (r=%d g=%d b=%d)", + x->x_text_array[x->x_nbtexts], x->x_nbtexts, + x->x_r[x->x_nbtexts], x->x_g[x->x_nbtexts], x->x_b[x->x_nbtexts] ); + + if ( x->x_current == -1 ) x->x_current = x->x_nbtexts; + x->x_nbtexts++; + +} + +static void pdp_text_current(t_pdp_text *x, t_floatarg fcurrent ) +{ + if ( ( fcurrent >= 0 ) && ( fcurrent < x->x_nbtexts ) ) + { + x->x_current = fcurrent; + } +} + +static void pdp_text_textx(t_pdp_text *x, t_floatarg fx ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbtexts ) ) + { + x->x_xoffsets[ x->x_current ] = fx; + } +} + +static void pdp_text_texty(t_pdp_text *x, t_floatarg fy ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbtexts ) ) + { + x->x_yoffsets[ x->x_current ] = fy; + } +} + +static void pdp_text_textr(t_pdp_text *x, t_floatarg fr ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbtexts ) ) + { + x->x_r[ x->x_current ] = fr; + } +} + +static void pdp_text_textg(t_pdp_text *x, t_floatarg fg ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbtexts ) ) + { + x->x_g[ x->x_current ] = fg; + } +} + +static void pdp_text_textb(t_pdp_text *x, t_floatarg fb ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbtexts ) ) + { + x->x_b[ x->x_current ] = fb; + } +} + +static void pdp_text_angle(t_pdp_text *x, t_floatarg fangle ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbtexts ) ) + { + x->x_angle[ x->x_current ] = fangle; + } +} + +static void pdp_text_scroll(t_pdp_text *x, t_floatarg fscroll ) +{ + if ( ( x->x_current >= 0 ) && ( x->x_current < x->x_nbtexts ) ) + { + x->x_scroll[ x->x_current ] = fscroll; + } +} + +static void pdp_text_dither(t_pdp_text *x, t_floatarg fdither ) +{ + imlib_context_set_dither( (char)fdither ); +} + +static void pdp_text_blend(t_pdp_text *x, t_floatarg fblend ) +{ + imlib_context_set_blend( (char)fblend ); +} + +static void pdp_text_antialias(t_pdp_text *x, t_floatarg fantialias ) +{ + imlib_context_set_anti_alias( (char)fantialias ); +} + +static void pdp_text_clear(t_pdp_text *x ) +{ + x->x_nbtexts = 0; +} + +static void pdp_text_delete(t_pdp_text *x, t_floatarg fnum ) +{ + t_int i; + char *lostword; + + if ( ( fnum>0 ) && ( fnum<=x->x_nbtexts ) ) + { + lostword = x->x_text_array[ (int)fnum-1 ]; + for ( i=(int)fnum; i<x->x_nbtexts; i++ ) + { + x->x_text_array[ i-1 ] = x->x_text_array[ i ]; + x->x_xoffsets[ i-1 ] = x->x_xoffsets[ i ]; + x->x_yoffsets[ i-1 ] = x->x_yoffsets[ i ]; + x->x_r[ i-1 ] = x->x_r[ i ]; + x->x_g[ i-1 ] = x->x_g[ i ]; + x->x_b[ i-1 ] = x->x_b[ i ]; + x->x_angle[ i-1 ] = x->x_angle[ i ]; + x->x_scroll[ i-1 ] = x->x_scroll[ i ]; + } + x->x_nbtexts--, + free( lostword ); + } +} + +static void pdp_text_resize(t_pdp_text *x, t_floatarg fnewsize ) +{ + char **text_array; + t_int *xoffsets; + t_int *yoffsets; + t_int *r; + t_int *g; + t_int *b; + t_float *angle; + t_int *scroll; + + t_int i, csize; + + if ( (int) fnewsize<=0 ) return; + + // allocate new structures + text_array = (char**) getbytes( fnewsize*sizeof(char*) ); + xoffsets = (t_int*) getbytes( fnewsize*sizeof(t_int) ); + yoffsets = (t_int*) getbytes( fnewsize*sizeof(t_int) ); + r = (t_int*) getbytes( fnewsize*sizeof(t_int) ); + g = (t_int*) getbytes( fnewsize*sizeof(t_int) ); + b = (t_int*) getbytes( fnewsize*sizeof(t_int) ); + angle = (t_float*) getbytes( fnewsize*sizeof(t_float) ); + scroll = (t_int*) getbytes( fnewsize*sizeof(t_int) ); + + + for ( i=0; i<fnewsize; i++ ) + { + r[i] = g[i] = b[i] = 255; + } + + if ( fnewsize < x->x_nbtexts ) + { + post( "pdp_text : new size is too small : texts lost !!" ); + csize = fnewsize; + } + else + { + csize = x->x_nbtexts; + } + + // copy all values + for ( i=0; i<csize; i++ ) + { + text_array[i] = (char*) malloc( strlen( x->x_text_array[i] ) + 1 ); + strcpy( text_array[i], x->x_text_array[i] ); + free( x->x_text_array[i] ); + xoffsets[i] = x->x_xoffsets[i]; + yoffsets[i] = x->x_yoffsets[i]; + r[i] = x->x_r[i]; + g[i] = x->x_g[i]; + b[i] = x->x_b[i]; + angle[i] = x->x_angle[i]; + scroll[i] = x->x_scroll[i]; + } + + // free old structures + if ( x->x_text_array ) freebytes( x->x_text_array, x->x_capacity*sizeof(char*) ); + if ( x->x_xoffsets ) freebytes( x->x_xoffsets, x->x_capacity*sizeof(t_int) ); + if ( x->x_yoffsets ) freebytes( x->x_yoffsets, x->x_capacity*sizeof(t_int) ); + if ( x->x_r ) freebytes( x->x_r, x->x_capacity*sizeof(t_int) ); + if ( x->x_g ) freebytes( x->x_g, x->x_capacity*sizeof(t_int) ); + if ( x->x_b ) freebytes( x->x_b, x->x_capacity*sizeof(t_int) ); + if ( x->x_angle) freebytes( x->x_angle, x->x_capacity*sizeof(t_float) ); + if ( x->x_scroll) freebytes( x->x_scroll, x->x_capacity*sizeof(t_int) ); + + // set new structures + x->x_text_array = text_array; + x->x_xoffsets = xoffsets; + x->x_yoffsets = yoffsets; + x->x_r = r; + x->x_g = g; + x->x_b = b; + x->x_angle = angle; + x->x_scroll = scroll; + x->x_nbtexts = csize; + x->x_capacity = csize; + if ( x->x_nbtexts > 0 ) + { + x->x_current = 0; + } + else + { + x->x_current = -1; + } +} + +static void pdp_text_font(t_pdp_text *x, t_symbol *sfont ) +{ + Imlib_Font font; + + font = imlib_load_font(sfont->s_name); + if ( !font ) + { + post( "pdp_text : could not load font : >%s<", sfont->s_name ); + return; + } + imlib_context_set_font( font ); + x->x_font = font; +} + +static void pdp_text_allocate(t_pdp_text *x) +{ + x->x_image = imlib_create_image( x->x_vwidth, x->x_vheight ); + if ( x->x_image == NULL ) + { + post( "pdp_text : severe error : could not allocate image !!" ); + return; + } + imlib_context_set_image(x->x_image); +} + +static void pdp_text_free_ressources(t_pdp_text *x) +{ + if ( x->x_image != NULL ) imlib_free_image(); +} + +static void pdp_text_process_yv12(t_pdp_text *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 ti; + t_int px, py; + unsigned char y, u, v; + DATA32 *imdata; + DATA32 bgcolor; + short int *pY, *pU, *pV; + int text_width, text_height; + + if ( ( (int)(header->info.image.width) != x->x_vwidth ) || + ( (int)(header->info.image.height) != x->x_vheight ) ) + { + pdp_text_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_text_allocate(x); + } + + 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 ); + + // draw all texts + imlib_image_clear(); + imlib_context_set_direction(IMLIB_TEXT_TO_ANGLE); + imdata = imlib_image_get_data(); + bgcolor = imdata[0]; + + for (ti=0; ti<x->x_nbtexts; ti++) + { + imlib_context_set_angle( x->x_angle[ti] ); + imlib_context_set_color( x->x_r[ti], x->x_g[ti], x->x_b[ti], 255 ); + + imlib_get_text_size( x->x_text_array[ti], &text_width, &text_height); + + imlib_text_draw( x->x_xoffsets[ti] - (0.5*text_width) + (cos(x->x_angle[ti]) * x->x_scroll[ti]), x->x_yoffsets[ti] - (0.5*text_height) + (sin(x->x_angle[ti]) * x->x_scroll[ti]), x->x_text_array[ti] ); + } + + pY = newdata; + pV = newdata+x->x_vsize; + pU = newdata+x->x_vsize+(x->x_vsize>>2); + for ( py=0; py<x->x_vheight; py++ ) + { + for ( px=0; px<x->x_vwidth; px++ ) + { + if ( imdata[py*x->x_vwidth+px] != bgcolor ) + { + y = yuv_RGBtoY(imdata[py*x->x_vwidth+px]); + u = yuv_RGBtoU(imdata[py*x->x_vwidth+px]); + v = yuv_RGBtoV(imdata[py*x->x_vwidth+px]); + + *(pY) = y<<7; + if ( (px%2==0) && (py%2==0) ) + { + *(pV) = (v-128)<<8; + *(pU) = (u-128)<<8; + } + } + pY++; + if ( (px%2==0) && (py%2==0) ) + { + pV++;pU++; + } + } + } + + return; +} + +static void pdp_text_sendpacket(t_pdp_text *x) +{ + /* delete source 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_text_process(t_pdp_text *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_text_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_text_process_yv12, pdp_text_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_text_process */ + break; + + } + } + +} + +static void pdp_text_input_0(t_pdp_text *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_text_process(x); + + } + +} + +static void pdp_text_free(t_pdp_text *x) +{ + int i; + + pdp_text_free_ressources(x); + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_text_class; + +void *pdp_text_new(void) +{ + int i; + + t_pdp_text *x = (t_pdp_text *)pd_new(pdp_text_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("current")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("textx")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("texty")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("textr")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("textg")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("textb")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("angle")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("scroll")); + + 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_image = NULL; + + x->x_font = imlib_context_get_font(); + + x->x_capacity = DEFAULT_CAPACITY; + + + x->x_text_array = (char**) getbytes( x->x_capacity*sizeof(char*) ); + x->x_xoffsets = (t_int*) getbytes( x->x_capacity*sizeof(t_int) ); + x->x_yoffsets = (t_int*) getbytes( x->x_capacity*sizeof(t_int) ); + x->x_r = (t_int*) getbytes( x->x_capacity*sizeof(t_int) ); + x->x_g = (t_int*) getbytes( x->x_capacity*sizeof(t_int) ); + x->x_b = (t_int*) getbytes( x->x_capacity*sizeof(t_int) ); + x->x_angle = (t_float*) getbytes( x->x_capacity*sizeof(t_float) ); + x->x_scroll = (t_int*) getbytes( x->x_capacity*sizeof(t_int) ); + + for ( i=0; i<x->x_capacity; i++ ) + { + x->x_r[i] = x->x_g[i] = x->x_b[i] = 255; + } + + x->x_nbtexts = 0; + x->x_current = -1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_text_setup(void) +{ + Imlib_Font font; + + post( pdp_text_version ); + pdp_text_class = class_new(gensym("pdp_text"), (t_newmethod)pdp_text_new, + (t_method)pdp_text_free, sizeof(t_pdp_text), 0, A_NULL); + + class_addmethod(pdp_text_class, (t_method)pdp_text_input_0, gensym("pdp"), + A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_add, gensym("text"), A_GIMME, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_current, gensym("current"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_textx, gensym("textx"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_texty, gensym("texty"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_textr, gensym("textr"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_textg, gensym("textg"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_textb, gensym("textb"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_clear, gensym("clear"), A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_delete, gensym("delete"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_resize, gensym("resize"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_font, gensym("font"), A_SYMBOL, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_angle, gensym("angle"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_scroll, gensym("scroll"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_dither, gensym("dither"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_blend, gensym("blend"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_text_class, (t_method)pdp_text_antialias, gensym("antialias"), A_DEFFLOAT, A_NULL); + + imlib_add_path_to_font_path("/usr/X11R6/lib/X11/fonts/TTF"); + font = imlib_load_font(DEFAULT_FONT); + if ( !font ) + { + post( "pdp_text : severe error : could not load default font : no rendering !!!" ); + } + imlib_context_set_font( font ); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_transform.c b/modules/pdp_transform.c new file mode 100644 index 0000000..b69f778 --- /dev/null +++ b/modules/pdp_transform.c @@ -0,0 +1,357 @@ +/* + * 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 port of transform effect from EffecTV + * Originally written by clifford smith <nullset@dookie.net> + * Pd-fication by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include <math.h> + +#define MAX_TABLES 6 +static unsigned int fastrand_val; +#define inline_fastrand() (fastrand_val=fastrand_val*1103515245+12345) + +static char *pdp_transform_version = "pdp_transform: version 0.1, port of transform from EffecTV by clifford smith, adapted by ydegoyon@free.fr "; + +typedef struct pdp_transform_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_outlet *x_outlet0; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_int **x_table_list; // mapping tables + t_int **x_table_list_u; // mapping tables + t_int x_table; // current table + t_int x_t; + + +} t_pdp_transform; + +static void pdp_transform_table(t_pdp_transform *x, t_floatarg ftable ) +{ + if ( ( ftable >= 0 ) && ( ftable < MAX_TABLES ) ) + { + x->x_table = ftable; + } +} + +static int pdp_transform_map_from_table(t_pdp_transform *x, t_int px, t_int py, t_int t) +{ + int xd,yd; + + yd = py + (inline_fastrand() >> 30)-2; + xd = px + (inline_fastrand() >> 30)-2; + if (xd > x->x_vwidth) { + xd-=1; + } + return (xd+yd*x->x_vwidth); +} + +static int pdp_transform_map_from_table_u(t_pdp_transform *x, t_int px, t_int py, t_int t) +{ + int xd,yd; + + yd = py + (inline_fastrand() >> 30)-2; + xd = px + (inline_fastrand() >> 30)-2; + if (xd > x->x_vwidth) { + xd-=1; + } + return ((xd>>1)+(yd>>1)*(x->x_vwidth>>1)); +} + +static void pdp_transform_square_table_init(t_pdp_transform *x) +{ + const int size = 16; + t_int px, py, tx, ty; + + for(py=0; py<x->x_vheight; py++) + { + ty = py % size - size / 2; + if((py/size)%2) + { + ty = py - ty; + } + else + { + ty = py + ty; + } + if(ty<0) ty = 0; + if(ty>=x->x_vheight) + { + ty = x->x_vheight - 1; + } + for(px=0; px<x->x_vwidth; px++) + { + tx = px % size - size / 2; + if((px/size)%2) + { + tx = px - tx; + } + else + { + tx = px + tx; + } + if(tx<0) tx = 0; + if(tx>=x->x_vwidth) tx = x->x_vwidth - 1; + x->x_table_list[5][px+py*x->x_vwidth] = ty*x->x_vwidth+tx; + x->x_table_list_u[5][px+py*x->x_vwidth] = (ty>>1)*(x->x_vwidth>>1)+(tx>>1); + } + } +} + +static void pdp_transform_init_tables(t_pdp_transform *x) +{ + t_int px, py; + + for (py=0;py<x->x_vheight;py++) + { + for (px=0;px<x->x_vwidth;px++) + { + x->x_table_list[0][px+py*x->x_vwidth] = px+py*x->x_vwidth; + x->x_table_list[1][px+py*x->x_vwidth] = (x->x_vwidth-1-px)+py*x->x_vwidth; + x->x_table_list[2][px+py*x->x_vwidth] = px+(x->x_vheight-1-py)*x->x_vwidth; + x->x_table_list[3][px+py*x->x_vwidth] = (x->x_vwidth-1-px)+(x->x_vheight-1-py)*x->x_vwidth; + x->x_table_list_u[0][px+py*x->x_vwidth]= (px>>1)+((py*x->x_vwidth)>>2); + x->x_table_list_u[1][px+py*x->x_vwidth] = (x->x_vwidth>>1)-1-(px>>1)+(py>>1)*(x->x_vwidth>>1); + x->x_table_list_u[2][px+py*x->x_vwidth] = (px>>1)+((x->x_vheight>>1)-1-(py>>1))*(x->x_vwidth>>1); + x->x_table_list_u[3][px+py*x->x_vwidth] = ((x->x_vwidth-1-px)>>1)+((x->x_vheight-1-py)>>1)*(x->x_vwidth>>1); + x->x_table_list[4][px+py*x->x_vwidth] = -2; /* Function */ + x->x_table_list_u[4][px+py*x->x_vwidth] = -2; /* Function */ + } + } + pdp_transform_square_table_init(x); +} + +static void pdp_transform_free_ressources(t_pdp_transform *x) +{ + int i; + + // free tables + for(i=0;i<MAX_TABLES;i++) + { + if ( x->x_table_list[i] ) freebytes( x->x_table_list[i], x->x_vsize*sizeof(int) ); + if ( x->x_table_list_u[i] ) freebytes( x->x_table_list_u[i], x->x_vsize*sizeof(int) ); + } +} + +static void pdp_transform_allocate(t_pdp_transform *x) +{ + int i; + + // allocate tables + for(i=0;i<MAX_TABLES;i++) + { + x->x_table_list[i] = (t_int *) getbytes( x->x_vsize*sizeof(int) ); + x->x_table_list_u[i] = (t_int *) getbytes( x->x_vsize*sizeof(int) ); + } +} + +static void pdp_transform_process_yv12(t_pdp_transform *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, iu; + t_int px, py; + t_int d, o, du=0, ou; + short int *pY, *pU, *pV, *pnY, *pnU, *pnV; + + /* allocate all ressources */ + if ( ((int)header->info.image.width != x->x_vwidth) || + ((int)header->info.image.height != x->x_vheight) ) + { + pdp_transform_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_transform_allocate(x); + post( "pdp_transform : reallocated buffers" ); + pdp_transform_init_tables(x); + post( "pdp_transform : initialized tables" ); + } + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + x->x_t++; + + pnY = newdata; + pnV = newdata+x->x_vsize; + pnU = newdata+x->x_vsize+(x->x_vsize>>2); + iu = 0; + for(py=0; py<x->x_vheight; py++) + { + for(px=0; px<x->x_vwidth; px++) + { + d = x->x_table_list[x->x_table][py*x->x_vwidth+px]; + if ( (px%2==0) && (py%2==0) ) + { + du = x->x_table_list_u[x->x_table][py*x->x_vwidth+px]; + iu++; + } + if ( d==-2 ) + { + d = pdp_transform_map_from_table( x, px, py, x->x_t ); + du = pdp_transform_map_from_table_u( x, px, py, x->x_t ); + } + if ( d < 0) { + o = 0; + ou = 0; + } else { + o = d; + ou = du; + } + *pnY++ = *(data+o); + if ( (px%2==0) && (py%2==0) ) + { + *pnV++ = *(data+x->x_vsize+ou); + *pnU++ = *(data+x->x_vsize+(x->x_vsize>>2)+ou); + } + } + } + + return; +} + +static void pdp_transform_sendpacket(t_pdp_transform *x) +{ + /* delete source 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_transform_process(t_pdp_transform *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_transform_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_transform_process_yv12, pdp_transform_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_transform_process */ + break; + + } + } + +} + +static void pdp_transform_input_0(t_pdp_transform *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_transform_process(x); + + } + +} + +static void pdp_transform_free(t_pdp_transform *x) +{ + int i; + + pdp_transform_free_ressources(x); + if ( x->x_table_list ) freebytes(x->x_table_list, MAX_TABLES * sizeof(int *)); + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_transform_class; + +void *pdp_transform_new(void) +{ + int i; + + t_pdp_transform *x = (t_pdp_transform *)pd_new(pdp_transform_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("table")); + + 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_table_list = (t_int **) getbytes(MAX_TABLES * sizeof(int *)); + x->x_table_list_u = (t_int **) getbytes(MAX_TABLES * sizeof(int *)); + x->x_t = 0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_transform_setup(void) +{ +// post( pdp_transform_version ); + pdp_transform_class = class_new(gensym("pdp_transform"), (t_newmethod)pdp_transform_new, + (t_method)pdp_transform_free, sizeof(t_pdp_transform), 0, A_NULL); + + class_addmethod(pdp_transform_class, (t_method)pdp_transform_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transform_class, (t_method)pdp_transform_table, gensym("table"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_transition.c b/modules/pdp_transition.c new file mode 100644 index 0000000..d186aa0 --- /dev/null +++ b/modules/pdp_transition.c @@ -0,0 +1,787 @@ +/* + * 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 object allowing transitions between two video sources + * "circle", "wipe", "random", "melt" and "blend" + * Written by Yves Degoyon + */ + +#include "pdp.h" +#include <math.h> + +#define BLEND_MAX 200 + +static char *pdp_transition_version = "pdp_transition: version 0.1, two sources transition, written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_transition_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + t_int x_packet0; + t_int x_packet1; + t_int x_packet; + t_int x_dropped; + t_int x_queue_id; + + t_int x_vwidth0; + t_int x_vheight0; + t_int x_vsize0; + + t_int x_vwidth1; + t_int x_vheight1; + t_int x_vsize1; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_int x_transition_mode; // 1 : "circle" + t_int x_transition_pending; + + t_int x_current_source; + t_int x_target_source; + + t_int x_pos; // current position for transition + t_int x_inc; // increment for various mode + t_int x_rand;// randomizing argument + +} t_pdp_transition; + +static t_int pdp_transition_min( t_int a, t_int b ) +{ + if ( a == 0 ) return b; + if ( b == 0 ) return a; + if ( a < b ) return a; + else return b; +} + +static void pdp_transition_circle(t_pdp_transition *x, t_floatarg finc ) +{ + if ( x->x_transition_pending ) + { + post ( "pdp_transition : a transition is already pending, retry later...." ); + return; + } + if ( (int) finc > 0 ) + { + x->x_inc = (int)finc; + } + x->x_transition_mode = 1; + x->x_transition_pending = 1; +} + +static void pdp_transition_wipelr(t_pdp_transition *x, t_floatarg finc, t_floatarg frand ) +{ + if ( x->x_transition_pending ) + { + post ( "pdp_transition : a transition is already pending, retry later...." ); + return; + } + if ( (int) finc > 0 ) + { + x->x_inc = (int)finc; + } + if ( (int) frand >= 0 ) + { + x->x_rand = (int)frand; + } + x->x_transition_mode = 2; + x->x_transition_pending = 1; +} + +static void pdp_transition_wiperl(t_pdp_transition *x, t_floatarg finc, t_floatarg frand ) +{ + if ( x->x_transition_pending ) + { + post ( "pdp_transition : a transition is already pending, retry later...." ); + return; + } + if ( (int) finc > 0 ) + { + x->x_inc = (int)finc; + } + if ( (int) frand >= 0 ) + { + x->x_rand = (int)frand; + } + x->x_transition_mode = 3; + x->x_transition_pending = 1; + x->x_pos = x->x_vwidth; +} + +static void pdp_transition_mwipe(t_pdp_transition *x, t_floatarg finc, t_floatarg frand ) +{ + if ( x->x_transition_pending ) + { + post ( "pdp_transition : a transition is already pending, retry later...." ); + return; + } + if ( (int) finc > 0 ) + { + x->x_inc = (int)finc; + } + if ( (int) frand >= 0 ) + { + x->x_rand = (int)frand; + } + x->x_transition_mode = 4; + x->x_transition_pending = 1; + x->x_pos = 0; +} + +static void pdp_transition_wipetd(t_pdp_transition *x, t_floatarg finc, t_floatarg frand ) +{ + if ( x->x_transition_pending ) + { + post ( "pdp_transition : a transition is already pending, retry later...." ); + return; + } + if ( (int) finc > 0 ) + { + x->x_inc = (int)finc; + } + if ( (int) frand >= 0 ) + { + x->x_rand = (int)frand; + } + x->x_transition_mode = 5; + x->x_transition_pending = 1; + x->x_pos = 0; +} + +static void pdp_transition_wipebu(t_pdp_transition *x, t_floatarg finc, t_floatarg frand ) +{ + if ( x->x_transition_pending ) + { + post ( "pdp_transition : a transition is already pending, retry later...." ); + return; + } + if ( (int) finc > 0 ) + { + x->x_inc = (int)finc; + } + if ( (int) frand >= 0 ) + { + x->x_rand = (int)frand; + } + x->x_transition_mode = 6; + x->x_transition_pending = 1; + x->x_pos = x->x_vheight; +} + +static void pdp_transition_random(t_pdp_transition *x, t_floatarg finc ) +{ + if ( x->x_transition_pending ) + { + post ( "pdp_transition : a transition is already pending, retry later...." ); + return; + } + if ( (int) finc > 0 ) + { + x->x_inc = (int)finc; + } + x->x_transition_mode = 7; + x->x_transition_pending = 1; + x->x_rand = x->x_vsize/(x->x_inc*(pdp_transition_min( x->x_vwidth, 100 ))); +} + +static void pdp_transition_melt(t_pdp_transition *x, t_floatarg finc ) +{ + if ( x->x_transition_pending ) + { + post ( "pdp_transition : a transition is already pending, retry later...." ); + return; + } + if ( (int) finc > 0 ) + { + x->x_inc = (int)finc; + } + x->x_transition_mode = 8; + x->x_transition_pending = 1; + x->x_rand = 20; +} + +static void pdp_transition_blend(t_pdp_transition *x, t_floatarg finc, t_floatarg frand ) +{ + if ( x->x_transition_pending ) + { + post ( "pdp_transition : a transition is already pending, retry later...." ); + return; + } + if ( (int) finc > 0 ) + { + x->x_inc = (int)finc; + } + if ( (int) frand >= 0 ) + { + x->x_rand = (int)frand; + } + x->x_transition_mode = 9; + x->x_transition_pending = 1; + x->x_pos = 0; +} + +static void pdp_transition_process_yv12(t_pdp_transition *x) +{ + t_pdp *header0 = pdp_packet_header(x->x_packet0); + short int *data0 = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *header1 = pdp_packet_header(x->x_packet1); + short int *data1 = (short int *)pdp_packet_data(x->x_packet1); + t_pdp *header; + short int *data; + t_int tsource, cx=0, cy=0; + t_int px, py, rvalue=0; + t_float factor; + int i; + short int *poY, *poV, *poU, *p0Y, *p0V, *p0U, *p1Y, *p1V, *p1U; + + if ( header0 ) + { + x->x_vwidth0 = header0->info.image.width; + x->x_vheight0 = header0->info.image.height; + x->x_vsize0 = x->x_vwidth0*x->x_vheight0; + } + else + { + x->x_vwidth0 = x->x_vheight0 = x->x_vsize0 = 0; + } + + if ( header1 ) + { + x->x_vwidth1 = header1->info.image.width; + x->x_vheight1 = header1->info.image.height; + x->x_vsize1 = x->x_vwidth1*x->x_vheight1; + } + else + { + x->x_vwidth1 = x->x_vheight1 = x->x_vsize1 = 0; + } + + x->x_vwidth = pdp_transition_min( x->x_vwidth0 , x->x_vwidth1); + x->x_vheight = pdp_transition_min( x->x_vheight0 , x->x_vheight1); + x->x_vsize = x->x_vwidth*x->x_vheight; + // post( "pdp_transition : resulting frame : %dx%d", x->x_vwidth, x->x_vheight ); + + x->x_packet = pdp_packet_new_image_YCrCb( x->x_vwidth, x->x_vheight ); + + header = pdp_packet_header(x->x_packet); + data = (short int *)pdp_packet_data(x->x_packet); + + header->info.image.encoding = PDP_IMAGE_YV12; + header->info.image.width = x->x_vwidth; + header->info.image.height = x->x_vheight; + + poY = data; + poV = data+x->x_vsize; + poU = data+x->x_vsize+(x->x_vsize>>2); + if ( x->x_current_source == 0 ) + { + if ( x->x_vsize0 > 0 ) memcpy( data, data0, (x->x_vsize+(x->x_vsize>>1))<<1 ); + p0Y = data0; + p0V = data0+x->x_vsize0; + p0U = data0+x->x_vsize0+(x->x_vsize0>>2); + p1Y = data1; + p1V = data1+x->x_vsize1; + p1U = data1+x->x_vsize1+(x->x_vsize1>>2); + } + else + { + if ( x->x_vsize1 > 0 ) memcpy( data, data1, (x->x_vsize+(x->x_vsize>>1))<<1 ); + p0Y = data1; + p0V = data1+x->x_vsize0; + p0U = data1+x->x_vsize0+(x->x_vsize0>>2); + p1Y = data0; + p1V = data0+x->x_vsize1; + p1U = data0+x->x_vsize1+(x->x_vsize1>>2); + } + if ( ( x->x_transition_pending ) && ( x->x_vsize0 > 0 ) && ( x->x_vsize1 > 0 ) ) + { + switch ( x->x_transition_mode ) + { + case 1: // circle + for ( py=0; py<x->x_vheight; py++ ) + { + for ( px=0; px<x->x_vwidth; px++ ) + { + cx = px-(x->x_vwidth/2); + cy = py-(x->x_vheight/2); + if ( cx*cx + cy*cy < x->x_pos*x->x_pos ) + { + *(poY) = *(p1Y); + *(poU) = *(p1U); + *(poV) = *(p1V); + } + poY++; p1Y++; + if ( (px%2==0) && (py%2==0) ) + { + poU++; poV++; + p1U++; p1V++; + } + } + } + if ( ( x->x_pos > (x->x_vwidth/2) ) && ( x->x_pos > (x->x_vheight/2) ) ) + { + post( "pdp_transition : circle transition finished" ); + x->x_transition_pending = 0; + x->x_transition_mode = 0; + tsource = x->x_current_source; + x->x_current_source = x->x_target_source; + x->x_target_source = tsource; + x->x_pos = 0; + } + x->x_pos += x->x_inc; + break; + + case 2: // wipelr + for ( py=0; py<x->x_vheight; py++ ) + { + rvalue = (int)(((float) x->x_rand )*( (float)random() ) / RAND_MAX); + for ( px=0; px<x->x_vwidth; px++ ) + { + if ( px <= x->x_pos + rvalue ) + { + *(poY) = *(p1Y); + *(poU) = *(p1U); + *(poV) = *(p1V); + } + poY++; p1Y++; + if ( (px%2==0) && (py%2==0) ) + { + poU++; poV++; + p1U++; p1V++; + } + } + } + if ( x->x_pos > x->x_vwidth ) + { + post( "pdp_transition : wipelr transition finished" ); + x->x_transition_pending = 0; + x->x_transition_mode = 0; + tsource = x->x_current_source; + x->x_current_source = x->x_target_source; + x->x_target_source = tsource; + x->x_pos = 0; + } + x->x_pos += x->x_inc; + break; + + case 3: // wiperl + for ( py=0; py<x->x_vheight; py++ ) + { + rvalue = (int)(((float) x->x_rand )*( (float)random() ) / RAND_MAX); + for ( px=0; px<x->x_vwidth; px++ ) + { + if ( px >= x->x_pos + rvalue ) + { + *(poY) = *(p1Y); + *(poU) = *(p1U); + *(poV) = *(p1V); + } + poY++; p1Y++; + if ( (px%2==0) && (py%2==0) ) + { + poU++; poV++; + p1U++; p1V++; + } + } + } + if ( x->x_pos <= 0 ) + { + post( "pdp_transition : wiperl transition finished" ); + x->x_transition_pending = 0; + x->x_transition_mode = 0; + tsource = x->x_current_source; + x->x_current_source = x->x_target_source; + x->x_target_source = tsource; + x->x_pos = 0; + } + x->x_pos -= x->x_inc; + break; + + case 4: // mwipe + for ( px=0; px<x->x_vwidth; px++ ) + { + rvalue = (int)(((float) x->x_rand )*( (float)random() ) / RAND_MAX); + for ( py=0; py<x->x_vheight; py++ ) + { + if ( py <= x->x_pos + rvalue ) + { + *(poY) = *(p1Y); + *(poU) = *(p1U); + *(poV) = *(p1V); + } + poY++; p1Y++; + if ( (px%2==0) && (py%2==0) ) + { + poU++; poV++; + p1U++; p1V++; + } + } + } + if ( x->x_pos >= x->x_vheight ) + { + post( "pdp_transition : mwipe transition finished" ); + x->x_transition_pending = 0; + x->x_transition_mode = 0; + tsource = x->x_current_source; + x->x_current_source = x->x_target_source; + x->x_target_source = tsource; + x->x_pos = 0; + } + x->x_pos += x->x_inc; + break; + + case 5: // wipetd + for ( px=0; px<x->x_vwidth; px++ ) + { + rvalue = (int)(((float) x->x_rand )*( (float)random() ) / RAND_MAX); + for ( py=0; py<x->x_vheight; py++ ) + { + if ( py <= x->x_pos + rvalue ) + { + *(poY+py*x->x_vwidth+px) = *(p1Y+py*x->x_vwidth1+px); + *(poU+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = + *(p1U+(py>>1)*(x->x_vwidth1>>1)+(px>>1)); + *(poV+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = + *(p1V+(py>>1)*(x->x_vwidth1>>1)+(px>>1)); + } + } + } + if ( x->x_pos >= x->x_vheight ) + { + post( "pdp_transition : wipetd transition finished" ); + x->x_transition_pending = 0; + x->x_transition_mode = 0; + tsource = x->x_current_source; + x->x_current_source = x->x_target_source; + x->x_target_source = tsource; + x->x_pos = 0; + } + x->x_pos += x->x_inc; + break; + + case 6: // wipebu + for ( px=0; px<x->x_vwidth; px++ ) + { + rvalue = (int)(((float) x->x_rand )*( (float)random() ) / RAND_MAX); + for ( py=0; py<x->x_vheight; py++ ) + { + if ( py >= x->x_pos + rvalue ) + { + *(poY+py*x->x_vwidth+px) = *(p1Y+py*x->x_vwidth1+px); + *(poU+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = + *(p1U+(py>>1)*(x->x_vwidth1>>1)+(px>>1)); + *(poV+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = + *(p1V+(py>>1)*(x->x_vwidth1>>1)+(px>>1)); + } + } + } + if ( x->x_pos <= 0 ) + { + post( "pdp_transition : wipebu transition finished" ); + x->x_transition_pending = 0; + x->x_transition_mode = 0; + tsource = x->x_current_source; + x->x_current_source = x->x_target_source; + x->x_target_source = tsource; + x->x_pos = 0; + } + x->x_pos -= x->x_inc; + break; + + case 7: // random + for ( px=0; px<x->x_vwidth; px++ ) + { + for ( py=0; py<x->x_vheight; py++ ) + { + rvalue = (int)(((float) x->x_rand )*( (float)random() ) / RAND_MAX); + if ( rvalue <= x->x_pos ) + { + *(poY+py*x->x_vwidth+px) = *(p1Y+py*x->x_vwidth1+px); + *(poU+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = + *(p1U+(py>>1)*(x->x_vwidth1>>1)+(px>>1)); + *(poV+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = + *(p1V+(py>>1)*(x->x_vwidth1>>1)+(px>>1)); + } + } + } + if ( x->x_pos >= x->x_rand ) + { + post( "pdp_transition : wipebu transition finished" ); + x->x_transition_pending = 0; + x->x_transition_mode = 0; + tsource = x->x_current_source; + x->x_current_source = x->x_target_source; + x->x_target_source = tsource; + x->x_pos = 0; + } + x->x_pos += x->x_inc; + break; + + case 8: // melt + for ( px=0; px<x->x_vwidth; px++ ) + { + rvalue = (int)(((float) x->x_rand )*( (float)random() ) / RAND_MAX)+(abs(px-x->x_vwidth/2)/5); + for ( py=0; py<x->x_vheight; py++ ) + { + if ( py <= x->x_pos + rvalue ) + { + *(poY+py*x->x_vwidth+px) = 0; + *(poU+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = 0; + *(poV+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = 0; + } + } + } + if ( x->x_pos >= x->x_vheight ) + { + post( "pdp_transition : melt transition finished" ); + x->x_transition_pending = 0; + x->x_transition_mode = 0; + tsource = x->x_current_source; + x->x_current_source = x->x_target_source; + x->x_target_source = tsource; + x->x_pos = 0; + } + x->x_pos += x->x_inc; + break; + + case 9: // blend + for ( px=0; px<x->x_vwidth; px++ ) + { + for ( py=0; py<x->x_vheight; py++ ) + { + rvalue = (((float) x->x_rand )*( (float)random() ) / RAND_MAX); + factor = ( (float) x->x_pos + rvalue ) / BLEND_MAX; + *(poY+py*x->x_vwidth+px) = + (int)((1.0-factor)*(*(poY+py*x->x_vwidth+px)) + + factor*(*(p1Y+py*x->x_vwidth1+px))); + *(poU+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = + (int)((1.0-factor)*(*(poU+(py>>1)*(x->x_vwidth>>1)+(px>>1))) + + factor*(*(p1U+(py>>1)*(x->x_vwidth1>>1)+(px>>1)))); + *(poV+(py>>1)*(x->x_vwidth>>1)+(px>>1)) = + (int)((1.0-factor)*(*(poV+(py>>1)*(x->x_vwidth>>1)+(px>>1))) + + factor*(*(p1V+(py>>1)*(x->x_vwidth1>>1)+(px>>1)))); + } + } + if ( x->x_pos >= BLEND_MAX ) + { + post( "pdp_transition : blend transition finished" ); + x->x_transition_pending = 0; + x->x_transition_mode = 0; + tsource = x->x_current_source; + x->x_current_source = x->x_target_source; + x->x_target_source = tsource; + x->x_pos = 0; + } + x->x_pos += x->x_inc; + break; + + default: + break; + } + } + + return; +} + +static void pdp_transition_sendpacket0(t_pdp_transition *x) +{ + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet); +} + +static void pdp_transition_sendpacket1(t_pdp_transition *x) +{ + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet); +} + +static void pdp_transition_process0(t_pdp_transition *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_transition_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_transition_process_yv12, pdp_transition_sendpacket0, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + break; + + default: + /* don't know the type, so dont pdp_transition_process */ + break; + + } + } +} + +static void pdp_transition_process1(t_pdp_transition *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet1)) + && (PDP_IMAGE == header->type)){ + + /* pdp_transition_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet1)->info.image.encoding){ + + case PDP_IMAGE_YV12: + pdp_queue_add(x, pdp_transition_process_yv12, pdp_transition_sendpacket1, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + break; + + default: + /* don't know the type, so dont pdp_transition_process */ + break; + + } + } +} + +static void pdp_transition_input_0(t_pdp_transition *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")) + { + /* release the packet */ + if ( x->x_packet0 != -1 ) + { + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + } + 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_transition_process0(x); + + } +} + +static void pdp_transition_input_1(t_pdp_transition *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")) + { + /* release the packet */ + if ( x->x_packet1 != -1 ) + { + pdp_packet_mark_unused(x->x_packet1); + x->x_packet1 = -1; + } + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet1, (int)f, pdp_gensym("image/YCrCb/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet1) && (!x->x_dropped)){ + + /* add the process method and callback to the process queue */ + pdp_transition_process1(x); + + } +} + +static void pdp_transition_free(t_pdp_transition *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_packet_mark_unused(x->x_packet1); +} + +t_class *pdp_transition_class; + +void *pdp_transition_new(void) +{ + int i; + + t_pdp_transition *x = (t_pdp_transition *)pd_new(pdp_transition_class); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("pdp"), gensym("pdp1")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("pdp"), gensym("pdp2")); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_packet = -1; + x->x_queue_id = -1; + + x->x_transition_mode = 0; + x->x_transition_pending = 0; + + x->x_current_source = 0; + x->x_target_source = 1; + + x->x_pos = 0; + x->x_inc = 1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_transition_setup(void) +{ + post( pdp_transition_version ); + pdp_transition_class = class_new(gensym("pdp_transition"), (t_newmethod)pdp_transition_new, + (t_method)pdp_transition_free, sizeof(t_pdp_transition), 0, A_NULL); + + class_addmethod(pdp_transition_class, (t_method)pdp_transition_input_0, gensym("pdp1"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transition_class, (t_method)pdp_transition_input_1, gensym("pdp2"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transition_class, (t_method)pdp_transition_circle, gensym("circle"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transition_class, (t_method)pdp_transition_wipelr, gensym("wipelr"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transition_class, (t_method)pdp_transition_wiperl, gensym("wiperl"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transition_class, (t_method)pdp_transition_mwipe, gensym("mwipe"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transition_class, (t_method)pdp_transition_wipetd, gensym("wipetd"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transition_class, (t_method)pdp_transition_wipebu, gensym("wipebu"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transition_class, (t_method)pdp_transition_random, gensym("random"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transition_class, (t_method)pdp_transition_melt, gensym("melt"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_transition_class, (t_method)pdp_transition_blend, gensym("blend"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_underwatch.c b/modules/pdp_underwatch.c new file mode 100644 index 0000000..887be29 --- /dev/null +++ b/modules/pdp_underwatch.c @@ -0,0 +1,244 @@ +/* + * 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 adaptation of 1d effect from effectv + * but i found it funnier to rename it as underwatch + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +static char *pdp_underwatch_version = "pdp_underwatch: version 0.1, inspired by 1d from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_underwatch_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_line; + t_int x_sline; + t_int x_sheight; + t_int x_prevline; + t_int x_prevsline; + t_int x_prevsheight; + t_int x_stripsize; + +} t_pdp_underwatch; + +static void pdp_underwatch_setparams(t_pdp_underwatch *x) +{ + int snext; + + x->x_sline = x->x_line; + snext = (x->x_line + 1); + x->x_sheight = snext - x->x_sline; +} + +static void pdp_underwatch_process_yv12(t_pdp_underwatch *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); + short int *p=0, *po=0, *pu=0, *pv=0, *pou=0, *pov=0; + int i; + + unsigned int u_offset; + unsigned int v_offset; + unsigned int totnbpixels; + int px, py, pd, t; + + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + u_offset = x->x_vsize; + v_offset = x->x_vsize + (x->x_vsize>>2); + totnbpixels = x->x_vsize + (x->x_vsize>>1); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + /* copy region */ + for (pd=0; pd<x->x_stripsize; pd++ ) + { + p = newdata+x->x_vwidth*x->x_sline; + pu = newdata+((x->x_vwidth*x->x_sline)>>2)+x->x_vsize; + pv = newdata+((x->x_vwidth*x->x_sline)>>2)+x->x_vsize+(x->x_vsize>>2); + po = data+x->x_vwidth*x->x_line; + pou = data+((x->x_vwidth*x->x_line)>>2)+x->x_vsize; + pov = data+((x->x_vwidth*x->x_line)>>2)+x->x_vsize+(x->x_vsize>>2); + // post("INIT : pov=%x limit=%x", pov, data+x->x_vsize+(x->x_vsize>>1) ); + for(py=0; py<=x->x_sheight; py++) + { + for(px=0; px<x->x_vwidth; px++) + { + if( po < data+x->x_vsize+(x->x_vsize>>1) ) *p = *po; + if( pou < data+x->x_vsize+(x->x_vsize>>1) ) *(pu) = *(pou); + if( pov < data+x->x_vsize+(x->x_vsize>>1) ) *(pv) = *(pov); + p++; + po++; + if ( ((px+1)%2==0) && ((py+1)%2==0) ) { pu++; pv++; pou++; pov++; }; + } + } + x->x_prevline = x->x_line; + x->x_prevsline = x->x_sline; + x->x_prevsheight = x->x_sheight; + x->x_line=(x->x_line+1)%(x->x_vheight); + pdp_underwatch_setparams(x); + //p = newdata + x->x_vwidth * x->x_sline+1; + //for(px=0; px<x->x_vwidth; px++) + //{ + // p[px] = 0xff00; + //} + } + + return; +} + +static void pdp_underwatch_sendpacket(t_pdp_underwatch *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_underwatch_process(t_pdp_underwatch *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_underwatch_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_underwatch_process_yv12, pdp_underwatch_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_underwatch_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_underwatch_process */ + break; + + } + } +} + +static void pdp_underwatch_stripsize(t_pdp_underwatch *x, t_floatarg fstripsize ) +{ + if ( fstripsize>0 && fstripsize<x->x_vheight ) + { + x->x_stripsize = (int)fstripsize; + } +} + +static void pdp_underwatch_input_0(t_pdp_underwatch *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_underwatch_process(x); + } +} + +static void pdp_underwatch_free(t_pdp_underwatch *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_underwatch_class; + +void *pdp_underwatch_new(void) +{ + int i; + + t_pdp_underwatch *x = (t_pdp_underwatch *)pd_new(pdp_underwatch_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("stripsize")); + + 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_line = 0; + pdp_underwatch_setparams(x); + x->x_prevline = 0; + x->x_prevsline = 0; + x->x_prevsheight = 0; + x->x_stripsize = 10; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_underwatch_setup(void) +{ +// post( pdp_underwatch_version ); + pdp_underwatch_class = class_new(gensym("pdp_underwatch"), (t_newmethod)pdp_underwatch_new, + (t_method)pdp_underwatch_free, sizeof(t_pdp_underwatch), 0, A_NULL); + + class_addmethod(pdp_underwatch_class, (t_method)pdp_underwatch_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_underwatch_class, (t_method)pdp_underwatch_stripsize, gensym("stripsize"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_vertigo.c b/modules/pdp_vertigo.c new file mode 100644 index 0000000..b6f4e37 --- /dev/null +++ b/modules/pdp_vertigo.c @@ -0,0 +1,349 @@ +/* + * 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 adaptation of lens effect from effectv + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +static char *pdp_vertigo_version = "pdp_vertigo: version 0.1, port of vertigo from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_vertigo_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; + short int *x_buffer; + short int *x_current_buffer; + short int *x_alt_buffer; + t_int x_dx; + t_int x_dy; + t_int x_sx; + t_int x_sy; + double x_phase; + double x_phase_increment; + double x_zoomrate; + + +} t_pdp_vertigo; + +static void pdp_vertigo_increment(t_pdp_vertigo *x, t_floatarg fincrement ) +{ + x->x_phase_increment = fincrement; +} + +static void pdp_vertigo_zoomrate(t_pdp_vertigo *x, t_floatarg fzoomrate ) +{ + x->x_zoomrate = (int)fzoomrate; +} + +static void pdp_vertigo_allocate(t_pdp_vertigo *x, t_floatarg fnewsize ) +{ + t_int nsize = (int) fnewsize; + + if ( x->x_buffer ) freebytes( x->x_buffer, 2*((x->x_vsize + (x->x_vsize>>1))<<1) ); + x->x_buffer = (short int *) getbytes( 2*(( nsize + (nsize>>1))<<1) ); + if ( x->x_buffer ) + { + bzero( x->x_buffer, 2*((nsize + (nsize>>1))<<1) ); + x->x_current_buffer = x->x_buffer; + x->x_alt_buffer = x->x_buffer + (nsize + (nsize>>1)); + } + x->x_phase = 0; +} + +static void pdp_vertigo_set_params(t_pdp_vertigo *x) +{ + double vx, vy; + double t; + double X, Y; + double dizz; + + dizz = sin(x->x_phase) * 10 + sin(x->x_phase*1.9+5) * 5; + + X = x->x_vwidth / 2; + Y = x->x_vheight / 2; + t = (X*X + Y*Y) * x->x_zoomrate; + if( x->x_vwidth > x->x_vheight ) + { + if(dizz >= 0) + { + if(dizz > X) dizz = X; + vx = (X*(X-dizz) + Y*Y) / t; + } + else + { + if(dizz < -X) dizz = -X; + vx = (X*(X+dizz) + Y*Y) / t; + } + vy = (dizz*Y) / t; + } + else + { + if(dizz >= 0) + { + if(dizz > Y) dizz = Y; + vx = (X*X + Y*(Y-dizz)) / t; + } + else + { + if(dizz < -Y) dizz = -Y; + vx = (X*X + Y*(Y+dizz)) / t; + } + vy = (dizz*X) / t; + } + x->x_dx = vx * 65536; + x->x_dy = vy * 65536; + x->x_sx = (-vx * X + vy * Y + X + cos(x->x_phase*5) * 2) * 65536; + x->x_sy = (-vx * Y - vy * X + Y + sin(x->x_phase*6) * 2) * 65536; + + x->x_phase += x->x_phase_increment; + if(x->x_phase > 5700000) x->x_phase = 0; +} + + +static void pdp_vertigo_process_yv12(t_pdp_vertigo *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); + + unsigned int totalnbpixels; + unsigned int u_offset; + unsigned int v_offset; + unsigned int totnbpixels; + short int *poy, *pou, *pov, *pny, *pnu, *pnv, *pcy, *pcu, *pcv; + int px, py; + short int v; + int ox, oy; + int i, ninc; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_vertigo_allocate(x, header->info.image.width*header->info.image.height ); + post( "pdp_vertigo : reallocated buffers" ); + } + + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + totalnbpixels = x->x_vsize; + u_offset = x->x_vsize; + v_offset = x->x_vsize + (x->x_vsize>>2); + totnbpixels = x->x_vsize + (x->x_vsize>>1); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + pdp_vertigo_set_params(x); + + poy = data; + pou = data + x->x_vsize; + pov = data + x->x_vsize + (x->x_vsize>>2); + pcy = x->x_current_buffer; + pcu = x->x_current_buffer + x->x_vsize; + pcv = x->x_current_buffer + x->x_vsize + (x->x_vsize>>2); + pny = x->x_alt_buffer; + pnu = x->x_alt_buffer + x->x_vsize; + pnv = x->x_alt_buffer + x->x_vsize + (x->x_vsize>>2); + ninc=0; + for(py=x->x_vheight; py>0; py--) + { + ox = x->x_sx; + oy = x->x_sy; + for(px=x->x_vwidth; px>0; px--) + { + if ( pny >= ( x->x_alt_buffer + x->x_vsize ) ) + { + post( "pdp_vertigo : abnormal pointer position : pny=%x, start=%x, size=%x ninc=%d px=%d py=%d", + pny, x->x_alt_buffer , x->x_vsize-1, ninc, px, py ); + break; + } + if ( pnu >= ( x->x_alt_buffer + x->x_vsize + (x->x_vsize>>2) ) ) + { + post( "pdp_vertigo : abnormal pointer position : pnu=%x, start=%x, size=%x ninc=%d px=%d py=%d", + pnu, x->x_alt_buffer + x->x_vsize, (x->x_vsize>>2)-1, ninc, px, py ); + break; + } + if ( pnv >= ( x->x_alt_buffer + x->x_vsize + (x->x_vsize>>1) ) ) + { + post( "pdp_vertigo : abnormal pointer position : pnv=%x, start=%x, size=%x ninc=%d px=%d py=%d", + pnv, x->x_alt_buffer + x->x_vsize + (x->x_vsize>>2), (x->x_vsize>>2)-1, ninc, px, py ); + break; + } + i = (oy>>16)*x->x_vwidth + (ox>>16); + if (i<0) i = 0; + if ( i >= (x->x_vsize + (x->x_vsize>>1)) ) i = (x->x_vsize + (x->x_vsize>>1))-1; + v = pcy[i] & 0xffff; + v = (v * 3) + ((*poy++) & 0xffff); + *pny++ = (v>>2); + if ( (((px+1)%2)==0) && (((py+1)%2)==0) ) + { + ninc++; + v = pcu[(i/4)] & 0xffff; + v = (v * 3) + ((*pou++) & 0xffff); + *pnu++ = (v>>2); + v = pcv[(i/4)] & 0xffff; + v = (v * 3) + ((*pov++) & 0xffff); + *pnv++ = (v>>2); + } + ox += x->x_dx; + oy += x->x_dy; + } + x->x_sx -= x->x_dy; + x->x_sy += x->x_dx; + } + + memcpy(newdata, x->x_alt_buffer, (x->x_vsize + (x->x_vsize>>1))<<1); + + poy = x->x_current_buffer; + x->x_current_buffer = x->x_alt_buffer; + x->x_alt_buffer = poy; + + return; +} + +static void pdp_vertigo_sendpacket(t_pdp_vertigo *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_vertigo_process(t_pdp_vertigo *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_vertigo_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_vertigo_process_yv12, pdp_vertigo_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_vertigo_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_vertigo_process */ + break; + + } + } +} + +static void pdp_vertigo_input_0(t_pdp_vertigo *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_vertigo_process(x); + } +} + +static void pdp_vertigo_free(t_pdp_vertigo *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + + if ( x->x_buffer ) freebytes( x->x_buffer, 2*((x->x_vsize + (x->x_vsize>>1))<<1) ); +} + +t_class *pdp_vertigo_class; + +void *pdp_vertigo_new(void) +{ + int i; + + t_pdp_vertigo *x = (t_pdp_vertigo *)pd_new(pdp_vertigo_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("increment")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("zoomrate")); + + 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_phase = 0; + x->x_buffer = NULL; + x->x_phase_increment = 0.02; + x->x_zoomrate = 1.01; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_vertigo_setup(void) +{ +// post( pdp_vertigo_version ); + pdp_vertigo_class = class_new(gensym("pdp_vertigo"), (t_newmethod)pdp_vertigo_new, + (t_method)pdp_vertigo_free, sizeof(t_pdp_vertigo), 0, A_NULL); + + class_addmethod(pdp_vertigo_class, (t_method)pdp_vertigo_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_vertigo_class, (t_method)pdp_vertigo_increment, gensym("increment"), A_FLOAT, A_NULL); + class_addmethod(pdp_vertigo_class, (t_method)pdp_vertigo_zoomrate, gensym("zoomrate"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_warhol.c b/modules/pdp_warhol.c new file mode 100644 index 0000000..8d12f2b --- /dev/null +++ b/modules/pdp_warhol.c @@ -0,0 +1,285 @@ +/* + * 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 adaptation of warhol effect from effectv + * Copyright (C) 2002 Jun IIO + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define NBCOLORS 9 +static t_int colortable[NBCOLORS] = { + 0x000080, 0x008045, 0x07f0e7, + 0x0000f0, 0x00f07f, 0x037a10, + 0x0023d9, 0x0080f0, 0x083df0 +}; + +static char *pdp_warhol_version = "pdp_warhol: version 0.1, port of warhol from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_warhol_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_dividerx; + t_int x_dividery; + t_int x_colorindex; + +} t_pdp_warhol; + +static void pdp_warhol_dividerx(t_pdp_warhol *x, t_floatarg fdivider ) +{ + if ( ( fdivider > 1 ) && ( fdivider < x->x_vwidth ) ) + { + x->x_dividerx = (t_int) fdivider; + } +} + +static void pdp_warhol_dividery(t_pdp_warhol *x, t_floatarg fdivider ) +{ + if ( ( fdivider > 1 ) && ( fdivider < x->x_vwidth ) ) + { + x->x_dividery = (t_int) fdivider; + } +} + +static void pdp_warhol_colorindex(t_pdp_warhol *x, t_floatarg findex ) +{ + if ( ( findex >= 0 ) && ( findex < NBCOLORS ) ) + { + x->x_colorindex = (t_int) findex; + } +} + +static void pdp_warhol_v(t_pdp_warhol *x, t_floatarg fv ) +{ + t_int tc; + + if ( ( fv >= 0 ) && ( fv < 255 ) ) + { + tc = colortable[x->x_colorindex] & 0xffff00; + tc = tc | (int) fv; + colortable[x->x_colorindex] = tc; + } +} + +static void pdp_warhol_u(t_pdp_warhol *x, t_floatarg fu ) +{ + t_int tc; + + if ( ( fu >= 0 ) && ( fu < 255 ) ) + { + tc = colortable[x->x_colorindex] & 0xff00ff; + tc = tc | (((int)fu)<<8); + colortable[x->x_colorindex] = tc; + } +} + +static void pdp_warhol_y(t_pdp_warhol *x, t_floatarg fy ) +{ + t_int tc; + + if ( ( fy >= 0 ) && ( fy < 255 ) ) + { + tc = colortable[x->x_colorindex] & 0x00ffff; + tc = tc | (((int)fy)<<16); + colortable[x->x_colorindex] = tc; + } +} + +static void pdp_warhol_process_yv12(t_pdp_warhol *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); + short int *pny, *pnu, *pnv; + short int *poy, *pou, *pov; + + short int Y, U, V; + int p, q, px, py, i; + + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + poy = data; + pou = data + x->x_vsize; + pov = data + x->x_vsize + (x->x_vsize>>2); + pny = newdata; + pnu = newdata + x->x_vsize; + pnv = newdata + x->x_vsize + (x->x_vsize>>2); + for (py = 0; py < x->x_vheight; py++) + { + for (px = 0; px < x->x_vwidth; px++) + { + p = (px * x->x_dividerx) % x->x_vwidth; + q = (py * x->x_dividery) % x->x_vheight; + i = ( ((py * x->x_dividery) / x->x_vheight) * x->x_dividery + + ((px * x->x_dividerx) / x->x_vwidth) ) % NBCOLORS; + Y = (colortable[i] >> 16); + U = ( (colortable[i] >> 8) & 0xff ); + V = ( colortable[i] & 0xff); + *pny = ( *(poy + (q*x->x_vwidth+p) ) ) ^ ( Y<<8 ); + pny++; + if ( ( px%2==0 ) && ( py%2==0 ) ) + { + *pnu = ( ( U - 128 << 7 ) ); + *pnv = ( ( V - 128 << 7 ) ); + // *pnu = ( *(pou + ((q>>1)*(x->x_vwidth>>1)+(p>>1)) ) ) ^ ( ( U - 128 << 7 ) ); + // *pnv = ( *(pov + ((q>>1)*(x->x_vwidth>>1)+(p>>1)) ) ) ^ ( ( V - 128 << 7 ) ); + pnu++; pnv++; + } + } + } + + return; +} + +static void pdp_warhol_sendpacket(t_pdp_warhol *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_warhol_process(t_pdp_warhol *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_warhol_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_warhol_process_yv12, pdp_warhol_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + break; + + default: + /* don't know the type, so dont pdp_warhol_process */ + break; + + } + } +} + +static void pdp_warhol_input_0(t_pdp_warhol *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_warhol_process(x); + } +} + +static void pdp_warhol_free(t_pdp_warhol *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_warhol_class; + +void *pdp_warhol_new(void) +{ + int i; + + t_pdp_warhol *x = (t_pdp_warhol *)pd_new(pdp_warhol_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("dividerx")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("dividery")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("colorindex")); + 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")); + + 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_dividerx = 3; + x->x_dividery = 3; + x->x_colorindex = 0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_warhol_setup(void) +{ +// post( pdp_warhol_version ); + pdp_warhol_class = class_new(gensym("pdp_warhol"), (t_newmethod)pdp_warhol_new, + (t_method)pdp_warhol_free, sizeof(t_pdp_warhol), 0, A_NULL); + + class_addmethod(pdp_warhol_class, (t_method)pdp_warhol_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_warhol_class, (t_method)pdp_warhol_dividerx, gensym("dividerx"), A_FLOAT, A_NULL); + class_addmethod(pdp_warhol_class, (t_method)pdp_warhol_dividery, gensym("dividery"), A_FLOAT, A_NULL); + class_addmethod(pdp_warhol_class, (t_method)pdp_warhol_colorindex, gensym("colorindex"), A_FLOAT, A_NULL); + class_addmethod(pdp_warhol_class, (t_method)pdp_warhol_y, gensym("Y"), A_FLOAT, A_NULL); + class_addmethod(pdp_warhol_class, (t_method)pdp_warhol_u, gensym("U"), A_FLOAT, A_NULL); + class_addmethod(pdp_warhol_class, (t_method)pdp_warhol_v, gensym("V"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_warp.c b/modules/pdp_warp.c new file mode 100644 index 0000000..e3b4311 --- /dev/null +++ b/modules/pdp_warp.c @@ -0,0 +1,352 @@ +/* + * 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 adaptation of warp effect from effectv + * Originally written by Fukuchi Kentaro & others + * Pd-fication by Yves Degoyon + */ + + + +#include "pdp.h" +#include <math.h> + +#define CTABLE_SIZE 1024 + +static t_int sintable[CTABLE_SIZE+256]; + +static char *pdp_warp_version = "pdp_warp: version 0.1, port of warp from effectv( Fukuchi Kentaro ) adapted by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_warp_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_tval; + t_int x_mode; + t_int x_ctable[CTABLE_SIZE]; + t_int *x_disttable; + t_int *x_offstable; + +} t_pdp_warp; + +static void pdp_warp_mode(t_pdp_warp *x, t_floatarg fmode ) +{ + if ( ( fmode == 0 ) || ( fmode == 1 ) ) + { + x->x_mode = (int)fmode; + } +} + +static void pdp_warp_tval(t_pdp_warp *x, t_floatarg ftval ) +{ + x->x_tval = (int)ftval; +} + +static void pdp_warp_init_sin_table(void) +{ + t_int *tptr, *tsinptr; + double i; + + tsinptr = tptr = sintable; + + for (i = 0; i < 1024; i++) + { + *tptr++ = (int) (sin (i*M_PI/512) * 32767); + } + + for (i = 0; i < 256; i++) + { + *tptr++ = *tsinptr++; + } +} + +static void pdp_warp_init_offs_table(t_pdp_warp* x) +{ + int y; + + for (y = 0; y < x->x_vheight; y++) { + x->x_offstable[y] = y * x->x_vwidth; + } +} + +static void pdp_warp_init_dist_table(t_pdp_warp *x) +{ + t_int halfw, halfh, *distptr; + double px,py,m; + + halfw = x->x_vwidth>> 1; + halfh = x->x_vheight >> 1; + + distptr = x->x_disttable; + + m = sqrt ((double)(halfw*halfw + halfh*halfh)); + + for (py = -halfh; py < halfh; py++) + { + for (px= -halfw; px < halfw; px++) + { + *distptr++ = ((int) ( (sqrt (px*px+py*py) * 511.9999) / m)) << 1; + } + } +} + + +static void pdp_warp_free_ressources(t_pdp_warp *x) +{ + if ( x->x_offstable ) freebytes( x->x_offstable, x->x_vheight * sizeof (t_int) ); + if ( x->x_disttable ) freebytes( x->x_disttable, x->x_vwidth * x->x_vheight * sizeof (t_int) ); +} + +static void pdp_warp_allocate(t_pdp_warp *x) +{ + int i; + + x->x_offstable = (t_int*) getbytes ( x->x_vheight * sizeof (t_int) ); + x->x_disttable = (t_int*) getbytes ( x->x_vwidth * x->x_vheight * sizeof (t_int) ); + pdp_warp_init_offs_table(x); + pdp_warp_init_dist_table(x); + +} + +void pdp_warp_do_warp(t_pdp_warp *x, short int* src, short int *dest, int xw, int yw, int cw) +{ + t_int c, i, px, py, dx, dy, dxu, dyu, maxx, maxy; + t_int width, height, skip, *ctptr, *distptr; + short int *destptr, *destptru, *destptrv; + + ctptr = x->x_ctable; + distptr = x->x_disttable; + width = x->x_vwidth; + height = x->x_vheight; + destptr = dest; + destptrv = dest+x->x_vsize; + destptru = dest+x->x_vsize+(x->x_vsize>>2); + skip = 0 ; /* x->x_vwidth*sizeof(short int)/4 - x->x_vwidth; */ + c = 0; + for (px = 0; px < 512; px++) + { + i = (c >> 3) & 0x3FE; + *ctptr++ = ((sintable[i] * yw) >> 15); + *ctptr++ = ((sintable[i+256] * xw) >> 15); + c += cw; + } + maxx = width - 2; maxy = height - 2; + for (py = 0; py < height-1; py++) + { + for (px = 0; px < width; px++) + { + i = *distptr++; + dx = x->x_ctable [i+1] + px; + dxu = x->x_ctable [i+1] + (px>>1); + dy = x->x_ctable [i] + py; + dyu = x->x_ctable [i] + (py>>1); + + if (dx < 0) dx = 0; + else if (dx > maxx) dx = maxx; + if (dy < 0) dy = 0; + else if (dy > maxy) dy = maxy; + if (dxu < 0) dxu = 0; + else if (dxu > (maxx>>1)) dxu = (maxx>>1); + if (dyu < 0) dyu = 0; + else if (dyu > (maxy>>1)) dyu = (maxy>>1); + + *destptr++ = src[dy*x->x_vwidth+dx]; + if ( (py%2==0) && (px%2==0) ) + { + *destptrv++ = src[x->x_vsize+((dyu*x->x_vwidth)>>1)+dxu]; + *destptru++ = src[x->x_vsize+(x->x_vsize>>2)+((dyu*x->x_vwidth)>>1)+dxu]; + } + } + } + +} + +static void pdp_warp_process_yv12(t_pdp_warp *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); + int i; + + unsigned int totalnbpixels; + unsigned int u_offset; + unsigned int v_offset; + unsigned int totnbpixels; + + int px, py; + int dx, dy; + int h, v; + int width, height; + int *p, *q, *r; + signed char *vp; + t_int xw, yw, cw; + + /* allocate all ressources */ + if ( (int)(header->info.image.width*header->info.image.height) != x->x_vsize ) + { + pdp_warp_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_warp_allocate(x); + post( "pdp_warp : reallocated buffers" ); + } + + totalnbpixels = x->x_vsize; + u_offset = x->x_vsize; + v_offset = x->x_vsize + (x->x_vsize>>2); + totnbpixels = x->x_vsize + (x->x_vsize>>1); + + 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 ); + + xw = (int) (sin((x->x_tval+100)*M_PI/128) * 30); + yw = (int) (sin((x->x_tval)*M_PI/256) * -35); + cw = (int) (sin((x->x_tval-70)*M_PI/64) * 50); + xw += (int) (sin((x->x_tval-10)*M_PI/512) * 40); + yw += (int) (sin((x->x_tval+30)*M_PI/512) * 40); + + pdp_warp_do_warp( x, data, newdata, xw, yw, cw); + if ( x->x_mode ) x->x_tval = (x->x_tval+1) &511; + + return; +} + +static void pdp_warp_sendpacket(t_pdp_warp *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_warp_process(t_pdp_warp *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_warp_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_warp_process_yv12, pdp_warp_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + break; + + default: + /* don't know the type, so dont pdp_warp_process */ + break; + + } + } +} + +static void pdp_warp_input_0(t_pdp_warp *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_warp_process(x); + } +} + +static void pdp_warp_free(t_pdp_warp *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_warp_free_ressources(x); +} + +t_class *pdp_warp_class; + +void *pdp_warp_new(void) +{ + int i; + + t_pdp_warp *x = (t_pdp_warp *)pd_new(pdp_warp_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("mode")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("tval")); + + 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_mode = 0; + x->x_tval = 0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_warp_setup(void) +{ +// post( pdp_warp_version ); + pdp_warp_class = class_new(gensym("pdp_warp"), (t_newmethod)pdp_warp_new, + (t_method)pdp_warp_free, sizeof(t_pdp_warp), 0, A_NULL); + + pdp_warp_init_sin_table(); + + class_addmethod(pdp_warp_class, (t_method)pdp_warp_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_warp_class, (t_method)pdp_warp_mode, gensym("mode"), A_FLOAT, A_NULL); + class_addmethod(pdp_warp_class, (t_method)pdp_warp_tval, gensym("tval"), A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_yqt.c b/modules/pdp_yqt.c new file mode 100644 index 0000000..5f0ebbe --- /dev/null +++ b/modules/pdp_yqt.c @@ -0,0 +1,444 @@ +/* + * Pure Data Packet module. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "pdp.h" +#include "pdp_llconv.h" +#include <quicktime/lqt.h> +#include <quicktime/colormodels.h> + +#define MIN_AUDIO_INPUT 1024 /* we must have at least n chunks to play a steady sound */ +#define OUTPUT_BUFFER_SIZE 128*1024 /* audio output buffer : 128k */ +#define DECODE_PACKET_SIZE 16*1024 /* size of audio data decoded in one call */ + +typedef struct pdp_yqt_data +{ + short int gain[4]; +} t_pdp_yqt_data; + +typedef struct pdp_yqt_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + t_outlet *x_outlet1; + t_outlet *x_outlet2; + t_outlet *x_outlet3; /* audio left channel */ + t_outlet *x_outlet4; /* audio right channel */ + + int packet0; + bool initialized; + + unsigned int x_vwidth; + unsigned int x_vheight; + + bool loop; + + unsigned char * qt_rows[3]; + + unsigned char * qt_frame; + quicktime_t *qt; + int qt_cmodel; + + t_int x_audio; /* indicates the existence of an audio track */ + t_int x_audio_channels; /* number of audio channels of first track */ + t_int x_mono; /* indicates a mono audio track */ + t_int x_audio_rate; /* audio rate */ + t_int x_resampling_factor; /* resampling factor */ + + t_float *x_outbuffer; /* buffer to store audio decoded data */ + t_int x_outwriteposition; + t_int x_outreadposition; + t_int x_outunread; + t_int x_outbuffersize; + t_float *x_outl; + t_float *x_outr; + + t_pdp_yqt_data *state_data; + +} t_pdp_yqt; + + + +static void pdp_yqt_close(t_pdp_yqt *x) +{ + if (x->initialized){ + quicktime_close(x->qt); + free(x->qt_frame); + x->initialized = false; + } + +} + +static void pdp_yqt_open(t_pdp_yqt *x, t_symbol *name) +{ + unsigned int size; + + post("pdp_yqt: opening %s", name->s_name); + + pdp_yqt_close(x); + + x->qt = quicktime_open(name->s_name, 1, 0); + + if (!(x->qt)){ + post("pdp_yqt: error opening qt file"); + x->initialized = false; + return; + } + + if (!quicktime_has_video(x->qt)) { + post("pdp_yqt: no video stream"); + quicktime_close(x->qt); + x->initialized = false; + return; + + } + else if (!quicktime_supported_video(x->qt,0)) { + post("pdp_yqt: unsupported video codec\n"); + quicktime_close(x->qt); + x->initialized = false; + return; + } + else + { + x->qt_cmodel = BC_YUV420P; + x->x_vwidth = quicktime_video_width(x->qt,0); + x->x_vheight = quicktime_video_height(x->qt,0); + x->qt_frame = (unsigned char*)malloc(x->x_vwidth*x->x_vheight*4); + size = x->x_vwidth * x->x_vheight; + x->qt_rows[0] = &x->qt_frame[0]; + x->qt_rows[2] = &x->qt_frame[size]; + x->qt_rows[1] = &x->qt_frame[size + (size>>2)]; + + quicktime_set_cmodel(x->qt, x->qt_cmodel); + x->initialized = true; + outlet_float(x->x_outlet2, (float)quicktime_video_length(x->qt,0)); + + } + + if (!quicktime_has_audio(x->qt)) { + post("pdp_yqt: warning : no audio stream"); + x->x_audio = 0; + return; + } + + if ( quicktime_audio_tracks(x->qt) > 1 ) + { + post("pdp_yqt: warning : more that one audio track, using first one"); + } + + if ( ( x->x_audio_channels = quicktime_track_channels(x->qt, 0) ) != 2 ) { + x->x_mono=0; + post("pdp_yqt: track 0 has %d channels", x->x_audio_channels ); + post("pdp_yqt: warning : not a stereo audio track ( audio channels : %d )", x->x_audio_channels ); + if ( x->x_audio_channels == 1 ) x->x_mono = 1; + else x->x_audio_channels = 2; + } else { + post("pdp_yqt: track 0 has %d channels", x->x_audio_channels ); + } + + if (!quicktime_supported_audio(x->qt,0)) { + post("pdp_yqt: warning : audio not supported" ); + x->x_audio = 0; + } else { + x->x_audio = 1; + } + + if ( x->x_audio ) + { + post("pdp_yqt: using audio track 0 with %d channels", x->x_audio_channels ); + post("pdp_yqt: audio data is %d bytes, %d kHz compressed with %s", + quicktime_audio_bits(x->qt, 0), + x->x_audio_rate = quicktime_sample_rate(x->qt, 0), + quicktime_audio_compressor(x->qt, 0) ); + x->x_resampling_factor = ( sys_getsr() / x->x_audio_rate ); + } + +} + + +static void pdp_yqt_bang(t_pdp_yqt *x) +{ + unsigned int w, h, nbpixels, packet_size; + int object, length, pos, i, j; + short int* data; + t_pdp* header; + + static short int gain[4] = {0x7fff, 0x7fff, 0x7fff, 0x7fff}; + + if (!(x->initialized)){ + //post("pdp_yqt: no qt file opened"); + return; + } + + w = x->x_vwidth; + h = x->x_vheight; + nbpixels = w * h; + packet_size = (nbpixels + (nbpixels >> 1)) << 1; + + object = pdp_packet_new_image_YCrCb( x->x_vwidth, x->x_vheight ); + header = pdp_packet_header(object); + data = (short int *) pdp_packet_data(object); + + header->info.image.encoding = PDP_IMAGE_YV12; + header->info.image.width = w; + header->info.image.height = h; + + length = quicktime_video_length(x->qt,0); + pos = quicktime_video_position(x->qt,0); + // post("pdp_yqt : video position : %d length =%d", pos, length ); + + if (pos >= length){ + pos = (x->loop) ? 0 : length - 1; + // post("pdp_yqt : setting video position to %d", pos); + quicktime_set_video_position(x->qt, pos, 0); + if (x->loop) + { + // post("pdp_yqt : resetting audio position"); + if ( x->x_audio ) quicktime_set_audio_position(x->qt, 0, 0); + } + } + + lqt_decode_video(x->qt, x->qt_rows, 0); + + switch(x->qt_cmodel){ + case BC_YUV420P: + pdp_llconv(x->qt_frame, RIF_YVU__P411_U8, data, RIF_YVU__P411_S16, x->x_vwidth, x->x_vheight); + break; + + case BC_YUV422: + pdp_llconv(x->qt_frame, RIF_YUYV_P____U8, data, RIF_YVU__P411_S16, x->x_vwidth, x->x_vheight); + break; + + case BC_RGB888: + pdp_llconv(x->qt_frame, RIF_RGB__P____U8, data, RIF_YVU__P411_S16, x->x_vwidth, x->x_vheight); + break; + + default: + post("pdp_yqt : error on decode: unkown colour model"); + break; + } + + outlet_float(x->x_outlet1, (float)pos); + pdp_packet_pass_if_valid(x->x_outlet0, &object); + + // fills in the audio buffer with a chunk if necessary + if ( x->x_audio && x->x_outunread < MIN_AUDIO_INPUT ) + { + int csize, rsize; + + // watch remaining size + rsize = (int ) ( quicktime_audio_length(x->qt, 0) - quicktime_audio_position(x->qt, 0) ); + csize = ( rsize < DECODE_PACKET_SIZE ) ? rsize : DECODE_PACKET_SIZE; + + // post("pdp_yqt : decode one chunk (size=%d)", csize ); + if ( ( quicktime_decode_audio(x->qt, NULL, x->x_outl, csize, 1) <0 ) || + ( !x->x_mono && ( quicktime_decode_audio(x->qt, NULL, x->x_outl, csize, 2) <0 ) ) ) + { + post("pdp_yqt : could not decode audio data" ); + } else { + for ( i=0; i<csize; i++ ) + { + if ( x->x_outunread >= x->x_outbuffersize-2 ) + { + post( "pdp_yqt: decode audio : too much input ... ignored" ); + continue; + } + for ( j=0; j<x->x_resampling_factor; j++ ) + { + *(x->x_outbuffer+x->x_outwriteposition) = x->x_outl[i]; + x->x_outwriteposition = (x->x_outwriteposition + 1)%x->x_outbuffersize; + *(x->x_outbuffer+x->x_outwriteposition) = + ((x->x_mono)? x->x_outl[i] : x->x_outr[i] ); + x->x_outwriteposition = (x->x_outwriteposition + 1)%x->x_outbuffersize; + x->x_outunread+=2; + } + } + } + } + +} + +static void pdp_yqt_loop(t_pdp_yqt *x, t_floatarg loop) +{ + int loopi = (int)loop; + x->loop = !(loopi == 0); +} + +static void pdp_yqt_frame_cold(t_pdp_yqt *x, t_floatarg frameindex) +{ + int frame = (int)frameindex; + int length; + + + if (!(x->initialized)) return; + + length = quicktime_video_length(x->qt,0); + + frame = (frame >= length) ? length-1 : frame; + frame = (frame < 0) ? 0 : frame; + + // post("pdp_yqt : frame cold : setting video position to : %d", frame ); + quicktime_set_video_position(x->qt, frame, 0); +} + +static void pdp_yqt_frame(t_pdp_yqt *x, t_floatarg frameindex) +{ + // pdp_yqt_frame_cold(x, frameindex); + pdp_yqt_bang(x); +} + +static void pdp_yqt_gain(t_pdp_yqt *x, t_floatarg f) +{ + int i; + short int g; + float bound = (float)0x7fff; + + f *= (float)0x7fff; + + f = (f>bound) ? bound : f; + f = (f<-bound) ? -bound : f; + + g = (short int)f; + + for (i=0; i<4; i++) x->state_data->gain[i] = g; +} + +static void pdp_yqt_free(t_pdp_yqt *x) +{ + + free (x->state_data); + pdp_yqt_close(x); + + freebytes(x->x_outbuffer, OUTPUT_BUFFER_SIZE*sizeof(t_float)); + freebytes(x->x_outl, DECODE_PACKET_SIZE*sizeof(t_float)); + freebytes(x->x_outr, DECODE_PACKET_SIZE*sizeof(t_float)); +} + +t_class *pdp_yqt_class; + +void *pdp_yqt_new(void) +{ + t_pdp_yqt *x = (t_pdp_yqt *)pd_new(pdp_yqt_class); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("frame_cold")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("gain")); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_outlet1 = outlet_new(&x->x_obj, &s_float); + x->x_outlet2 = outlet_new(&x->x_obj, &s_float); + + x->x_outlet3 = outlet_new(&x->x_obj, &s_signal); /* audio left channel */ + x->x_outlet4 = outlet_new(&x->x_obj, &s_signal); /* audio right channel */ + + x->packet0 = -1; + + x->initialized = false; + + x->loop = false; + + x->state_data = (t_pdp_yqt_data *)malloc(sizeof(t_pdp_yqt_data)); + pdp_yqt_gain(x, 1.0f); + + // allocate audio buffers + x->x_outbuffersize = OUTPUT_BUFFER_SIZE; + x->x_outl = (t_float*) getbytes(DECODE_PACKET_SIZE*sizeof(t_float)); + x->x_outr = (t_float*) getbytes(DECODE_PACKET_SIZE*sizeof(t_float)); + x->x_outbuffer = (t_float*) getbytes(OUTPUT_BUFFER_SIZE*sizeof(t_float)); + + if ( !x->x_outl || !x->x_outr || !x->x_outbuffer ) + { + post( "mp3amp~: could not allocate buffers" ); + return NULL; + } + memset( x->x_outl, 0x0, DECODE_PACKET_SIZE*sizeof(t_float) ); + memset( x->x_outr, 0x0, DECODE_PACKET_SIZE*sizeof(t_float) ); + memset( x->x_outbuffer, 0x0, OUTPUT_BUFFER_SIZE*sizeof(t_float) ); + + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + + return (void *)x; +} + +static t_int *pdp_yqt_perform(t_int *w) +{ + t_pdp_yqt *x = (t_pdp_yqt*) (w[1]); + t_float *out1 = (t_float *)(w[2]); + t_float *out2 = (t_float *)(w[3]); + int n = (int)(w[4]); + int ret; + int i = 0; + + while( n-- ) + { + if ( x->x_audio && x->x_outunread > 0 ) + { + *out1++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + *out2++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + x->x_outunread-=2; + } + else + { + *out1++=0.; + *out2++=0.; + } + } + + return (w+5); +} + +static void pdp_yqt_dsp(t_pdp_yqt *x, t_signal **sp) +{ + dsp_add(pdp_yqt_perform, 4, x, sp[1]->s_vec, sp[2]->s_vec, sp[1]->s_n); +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_yqt_setup(void) +{ + pdp_yqt_class = class_new(gensym("pdp_yqt"), (t_newmethod)pdp_yqt_new, + (t_method)pdp_yqt_free, sizeof(t_pdp_yqt), 0, A_NULL); + + class_addmethod(pdp_yqt_class, (t_method)pdp_yqt_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_yqt_class, (t_method)pdp_yqt_close, gensym("close"), A_NULL); + class_addmethod(pdp_yqt_class, (t_method)pdp_yqt_open, gensym("open"), A_SYMBOL, A_NULL); + class_addmethod(pdp_yqt_class, (t_method)pdp_yqt_loop, gensym("loop"), A_DEFFLOAT, A_NULL); + class_addfloat (pdp_yqt_class, (t_method)pdp_yqt_frame); + class_addmethod(pdp_yqt_class, (t_method)pdp_yqt_frame_cold, gensym("frame_cold"), A_FLOAT, A_NULL); + class_addmethod(pdp_yqt_class, (t_method)pdp_yqt_gain, gensym("gain"), A_FLOAT, A_NULL); + class_addmethod(pdp_yqt_class, nullfn, gensym("signal"), 0); + class_addmethod(pdp_yqt_class, (t_method)pdp_yqt_dsp, gensym("dsp"), 0); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_yvu2rgb.c b/modules/pdp_yvu2rgb.c new file mode 100644 index 0000000..ba22d8b --- /dev/null +++ b/modules/pdp_yvu2rgb.c @@ -0,0 +1,185 @@ +/* + * Pure Data Packet module. + * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "pdp.h" +#include "yuv.h" +#include <math.h> + + +typedef struct pdp_yvu2rgb_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; + + unsigned int *x_RGBFrame; + t_int x_RGBFrameSize; + + +} t_pdp_yvu2rgb; + +static void pdp_yvu2rgb_process_yv12(t_pdp_yvu2rgb *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); + int i; + + unsigned int w = header->info.image.width; + unsigned int h = header->info.image.height; + + unsigned int size = w*h; + unsigned int totalnbpixels = size; + unsigned int u_offset = size; + unsigned int v_offset = size + (size>>2); + unsigned int totnbpixels = size + (size>>1); + + unsigned int row, col; + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = w; + newheader->info.image.height = h; + + if ( !x->x_RGBFrame ) + { + x->x_RGBFrame = ( unsigned int* ) getbytes( size*sizeof( unsigned int ) ); + x->x_RGBFrameSize = size*sizeof( unsigned int ); + post( "pdp_yvu2rgb : allocated frame size=%d", x->x_RGBFrameSize ); + } + if ( !x->x_RGBFrame ) + { + post( "pdp_yvu2rgb : cannot allocate frame" ); + return; + } + + yuv_Y122RGB( data, x->x_RGBFrame, w, h ); + // post( "pdp_yvu2rgb : converted to RGB" ); + yuv_RGB2Y12( x->x_RGBFrame, newdata, w, h ); + // post( "pdp_yvu2rgb : converted to Y12" ); + + return; +} + +static void pdp_yvu2rgb_sendpacket(t_pdp_yvu2rgb *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_yvu2rgb_process(t_pdp_yvu2rgb *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_yvu2rgb_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_yvu2rgb_process_yv12, pdp_yvu2rgb_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_yvu2rgb_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_yvu2rgb_process */ + break; + + } + } +} + +static void pdp_yvu2rgb_input_0(t_pdp_yvu2rgb *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_yvu2rgb_process(x); + } +} + +static void pdp_yvu2rgb_free(t_pdp_yvu2rgb *x) +{ + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + if (x->x_RGBFrame ) freebytes( x->x_RGBFrame, x->x_RGBFrameSize ); +} + +t_class *pdp_yvu2rgb_class; + +void *pdp_yvu2rgb_new(void) +{ + int i; + + t_pdp_yvu2rgb *x = (t_pdp_yvu2rgb *)pd_new(pdp_yvu2rgb_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_queue_id = -1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_yvu2rgb_setup(void) +{ + + + pdp_yvu2rgb_class = class_new(gensym("pdp_yvu2rgb"), (t_newmethod)pdp_yvu2rgb_new, + (t_method)pdp_yvu2rgb_free, sizeof(t_pdp_yvu2rgb), 0, A_NULL); + + class_addmethod(pdp_yvu2rgb_class, (t_method)pdp_yvu2rgb_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif |