From a9cfc5852d75ca1c62e4c97e3334c99ee2f2f9a8 Mon Sep 17 00:00:00 2001 From: "N.N." Date: Sun, 14 Nov 2004 23:05:44 +0000 Subject: PiDiP 0.12.17 svn path=/trunk/externals/pidip/; revision=2273 --- modules/pdp_background.c | 188 +++++++++ modules/pdp_binary.c | 364 ++++++++++++++++ modules/pdp_cropper.c | 291 +++++++++++++ modules/pdp_dilate.c | 281 +++++++++++++ modules/pdp_disintegration.c | 280 +++++++++++++ modules/pdp_distance.c | 337 +++++++++++++++ modules/pdp_erode.c | 281 +++++++++++++ modules/pdp_hitandmiss.c | 386 +++++++++++++++++ modules/pdp_theorin~.c | 970 +++++++++++++++++++++++++++++++++++++++++++ modules/pdp_theorout~.c | 884 +++++++++++++++++++++++++++++++++++++++ 10 files changed, 4262 insertions(+) create mode 100644 modules/pdp_background.c create mode 100644 modules/pdp_binary.c create mode 100644 modules/pdp_cropper.c create mode 100644 modules/pdp_dilate.c create mode 100644 modules/pdp_disintegration.c create mode 100644 modules/pdp_distance.c create mode 100644 modules/pdp_erode.c create mode 100644 modules/pdp_hitandmiss.c create mode 100644 modules/pdp_theorin~.c create mode 100644 modules/pdp_theorout~.c (limited to 'modules') diff --git a/modules/pdp_background.c b/modules/pdp_background.c new file mode 100644 index 0000000..130253d --- /dev/null +++ b/modules/pdp_background.c @@ -0,0 +1,188 @@ +/* + * PiDiP module. + * Copyright (c) by Yves Degoyon (ydegoyon@free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#include "pdp.h" +#include "yuv.h" +#include "time.h" +#include "sys/time.h" + +#define DEFAULT_RED_VALUE 255 +#define DEFAULT_GREEN_VALUE 255 +#define DEFAULT_BLUE_VALUE 255 +#define DEFAULT_WIDTH 320 +#define DEFAULT_HEIGHT 240 + +typedef struct pdp_background_struct +{ + t_object x_obj; + + t_outlet *x_outlet0; + + t_int x_packet0; + + t_int x_colorR; + t_int x_colorG; + t_int x_colorB; + t_int x_colorY; + t_int x_colorU; + t_int x_colorV; + + t_int x_width; + t_int x_height; + +} t_pdp_background; + +static void pdp_background_bang(t_pdp_background *x) +{ + + t_pdp *header; + unsigned char *data; + unsigned char *pY, *pU, *pV; + t_int px, py; + + x->x_packet0 = pdp_packet_new_bitmap_yv12( x->x_width, x->x_height ); + + data = (char *)pdp_packet_data(x->x_packet0); + pY = data; + pV = data+(x->x_width*x->x_height); + pU = data+(x->x_width*x->x_height)+((x->x_width*x->x_height)>>2); + + memset( pY, (unsigned char)x->x_colorY, x->x_width*x->x_height ); + memset( pV, (unsigned char)x->x_colorV, (x->x_width*x->x_height>>2) ); + memset( pU, (unsigned char)x->x_colorU, (x->x_width*x->x_height>>2) ); + + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); + +} + +static void pdp_background_dim(t_pdp_background *x, t_floatarg fwidth, t_floatarg fheight) +{ + if ( ( (t_int)fwidth>0 ) && ( (t_int) fheight>0 ) ) + { + if ( ((t_int)fwidth)%8 == 0 ) + { + x->x_width = (t_int)fwidth; + } + else + { + x->x_width = (t_int)fwidth + (8-((t_int)fwidth)%8); // align on 8 + } + if ( ((t_int)fheight)%8 == 0 ) + { + x->x_height = (t_int)fheight; + } + else + { + x->x_height = (t_int)fheight + (8-((t_int)fheight)%8); // align on 8 + } + } +} + +static void pdp_background_red(t_pdp_background *x, t_floatarg fred) +{ + if ( ( (t_int)fred>=0 ) && ( (t_int) fred <= 255 ) ) + { + x->x_colorR = (t_int) fred; + x->x_colorY = yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + x->x_colorU = yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + x->x_colorV = yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + } +} + +static void pdp_background_green(t_pdp_background *x, t_floatarg fgreen) +{ + if ( ( (t_int)fgreen>=0 ) && ( (t_int) fgreen <= 255 ) ) + { + x->x_colorG = (t_int) fgreen; + x->x_colorY = yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + x->x_colorU = yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + x->x_colorV = yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + } +} + +static void pdp_background_blue(t_pdp_background *x, t_floatarg fblue) +{ + if ( ( (t_int)fblue>=0 ) && ( (t_int) fblue <= 255 ) ) + { + x->x_colorB = (t_int) fblue; + x->x_colorY = yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + x->x_colorU = yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + x->x_colorV = yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + } +} + +static void pdp_background_free(t_pdp_background *x) +{ + // nothing to do +} + +t_class *pdp_background_class; + +void *pdp_background_new(void) +{ + t_pdp_background *x = (t_pdp_background *)pd_new(pdp_background_class); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("red")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("green")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("blue")); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_colorR = DEFAULT_RED_VALUE; + x->x_colorG = DEFAULT_GREEN_VALUE; + x->x_colorB = DEFAULT_BLUE_VALUE; + + x->x_colorY = yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + x->x_colorU = yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + x->x_colorV = yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ); + + x->x_width = DEFAULT_WIDTH; + x->x_height = DEFAULT_HEIGHT; + + x->x_packet0 = -1; + + return (void *)x; +} + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_background_setup(void) +{ + pdp_background_class = class_new(gensym("pdp_background"), (t_newmethod)pdp_background_new, + (t_method)pdp_background_free, sizeof(t_pdp_background), 0, A_NULL); + + class_addmethod(pdp_background_class, (t_method)pdp_background_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_background_class, (t_method)pdp_background_red, gensym("red"), A_FLOAT, A_NULL); + class_addmethod(pdp_background_class, (t_method)pdp_background_green, gensym("green"), A_FLOAT, A_NULL); + class_addmethod(pdp_background_class, (t_method)pdp_background_blue, gensym("blue"), A_FLOAT, A_NULL); + class_addmethod(pdp_background_class, (t_method)pdp_background_dim, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_sethelpsymbol( pdp_background_class, gensym("pdp_background.pd") ); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_binary.c b/modules/pdp_binary.c new file mode 100644 index 0000000..5ce943c --- /dev/null +++ b/modules/pdp_binary.c @@ -0,0 +1,364 @@ +/* + * PiDiP module + * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is an image binarizer, based on some (y,u,v) setting + */ + +#include "pdp.h" +#include "yuv.h" +#include +#include + +static char *pdp_binary_version = "pdp_binary: a image binarizer version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_binary_struct +{ + t_object x_obj; + + t_int x_packet0; + t_int x_packet1; + t_int x_queue_id; + t_int x_dropped; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + t_int x_colorY; // YUV components of binary mask + t_int x_colorU; + t_int x_colorV; + t_int x_cursX; // X position of the cursor + t_int x_cursY; // Y position of the cursor + t_int x_tolerance; // tolerance + short int *x_frame; // keep a copy of current frame for picking color + + t_outlet *x_pdp_output; // output packets + t_outlet *x_Y; // output Y component of selected color + t_outlet *x_U; // output U component of selected color + t_outlet *x_V; // output V component of selected color + +} t_pdp_binary; + +static void pdp_binary_setcur(t_pdp_binary *x, t_floatarg fpx, t_floatarg fpy ) +{ + if ( (fpx>=0.0) && (fpx<=1.0) && (fpy>=0.0) && (fpy<=1.0) ) + { + x->x_cursX = fpx*(t_float)x->x_vwidth; + x->x_cursY = fpy*(t_float)x->x_vheight; + } +} + +static void pdp_binary_y(t_pdp_binary *x, t_floatarg fy ) +{ + if ( fy <= 255. ) + { + x->x_colorY = (t_int)fy; + outlet_float( x->x_Y, x->x_colorY ); + } +} + +static void pdp_binary_u(t_pdp_binary *x, t_floatarg fu ) +{ + if ( fu <= 255. ) + { + x->x_colorU = (t_int)fu; + outlet_float( x->x_U, x->x_colorU ); + } +} + +static void pdp_binary_v(t_pdp_binary *x, t_floatarg fv ) +{ + if ( fv < 255 ) + { + x->x_colorV = (t_int)fv; + outlet_float( x->x_V, x->x_colorV ); + } +} + +static void pdp_binary_cursx(t_pdp_binary *x, t_floatarg fx ) +{ + if ( ( fx >= 0 ) && ( fx < x->x_vwidth) ) + { + x->x_cursX = (int)fx; + } +} + +static void pdp_binary_cursy(t_pdp_binary *x, t_floatarg fy ) +{ + if ( ( fy >= 0 ) && ( fy < x->x_vheight) ) + { + x->x_cursY = (int)fy; + } +} + +static void pdp_binary_tolerance(t_pdp_binary *x, t_floatarg ftolerance ) +{ + if ( ftolerance >= 0 ) + { + x->x_tolerance = (int)ftolerance; + } +} + +static void pdp_binary_pick(t_pdp_binary *x) +{ + if ( x->x_frame && ( x->x_cursX > 0 ) && ( x->x_cursX < x->x_vwidth ) + && ( x->x_cursY > 0 ) && ( x->x_cursY < x->x_vheight ) ) + { + // post( "pdp_binary : picking up color : x=%d y=%d", x->x_cursX, x->x_cursY ); + x->x_colorY = x->x_frame[ x->x_cursY*x->x_vwidth+x->x_cursX ]; + x->x_colorV = x->x_frame[ x->x_vsize + (x->x_cursY>>1)*(x->x_vwidth>>1)+(x->x_cursX>>1) ]; + x->x_colorU = x->x_frame[ x->x_vsize + (x->x_vsize>>2) + (x->x_cursY>>1)*(x->x_vwidth>>1)+(x->x_cursX>>1) ]; + x->x_colorY = (x->x_colorY)>>7; + x->x_colorV = (x->x_colorV>>8)+128; + x->x_colorU = (x->x_colorU>>8)+128; + outlet_float( x->x_Y, x->x_colorY ); + outlet_float( x->x_V, x->x_colorV ); + outlet_float( x->x_U, x->x_colorU ); + } +} + +static void pdp_binary_allocate(t_pdp_binary *x) +{ + x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + + if ( !x->x_frame ) + { + post( "pdp_binary : severe error : cannot allocate buffer !!! "); + return; + } +} + +static void pdp_binary_free_ressources(t_pdp_binary *x) +{ + if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); +} + +static void pdp_binary_process_yv12(t_pdp_binary *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *newheader = pdp_packet_header(x->x_packet1); + short int *newdata = (short int *)pdp_packet_data(x->x_packet1); + t_int i; + t_int px=0, py=0; + t_int y=0, u=0, v=0; + short int *pfY, *pfU, *pfV; + t_int diff; + + /* allocate all ressources */ + if ( ( (int)header->info.image.width != x->x_vwidth ) || + ( (int)header->info.image.height != x->x_vheight ) ) + { + pdp_binary_free_ressources( x ); + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + pdp_binary_allocate( x ); + post( "pdp_binary : reallocated buffers" ); + } + + memcpy(x->x_frame, data, (x->x_vsize + (x->x_vsize>>1))<<1 ); + + // post( "pdp_binary : newheader:%x", newheader ); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + // binarize + pfY = data; + pfV = data+x->x_vsize; + pfU = data+x->x_vsize+(x->x_vsize>>2); + for ( py=0; pyx_vheight; py++ ) + { + for ( px=0; pxx_vwidth; px++ ) + { + y = (*pfY)>>7; + v = ((*pfV)>>8)+128; + u = ((*pfU)>>8)+128; + + // post( "pdp_binary : y=%d, u=%d, v=%d", y, u, v ); + + diff = 0; + if ( x->x_colorY >= 0 ) + { + diff += abs(y-x->x_colorY ); + } + if ( x->x_colorV >= 0 ) + { + diff += abs(v-x->x_colorV ); + } + if ( x->x_colorU >=0 ) + { + diff += abs(u-x->x_colorU ); + } + + if ( diff <= x->x_tolerance ) + { + *(newdata+(py*x->x_vwidth+px)) = 0xff<<7; + } + else + { + *(newdata+(py*x->x_vwidth+px)) = 0; + } + + pfY++; + if ( (px%2==0) && (py%2==0) ) + { + pfU++;pfV++; + } + } + } + + memset( newdata+x->x_vsize, 0x0, (x->x_vsize>>1)<<1 ); + + return; +} + +static void pdp_binary_sendpacket(t_pdp_binary *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1); +} + +static void pdp_binary_process(t_pdp_binary *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_IMAGE == header->type)){ + + /* pdp_binary_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding) + { + + case PDP_IMAGE_YV12: + x->x_packet1 = pdp_packet_clone_rw(x->x_packet0); + pdp_queue_add(x, pdp_binary_process_yv12, pdp_binary_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // should write something to handle these one day + // but i don't use this mode + break; + + default: + /* don't know the type, so dont pdp_binary_process */ + break; + + } + } + +} + +static void pdp_binary_input_0(t_pdp_binary *x, t_symbol *s, t_floatarg f) +{ + /* if this is a register_ro message or register_rw message, register with packet factory */ + + if (s== gensym("register_rw")) + { + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + pdp_binary_process(x); + } +} + +static void pdp_binary_free(t_pdp_binary *x) +{ + int i; + + pdp_packet_mark_unused(x->x_packet0); + pdp_binary_free_ressources( x ); +} + +t_class *pdp_binary_class; + +void *pdp_binary_new(void) +{ + int i; + + t_pdp_binary *x = (t_pdp_binary *)pd_new(pdp_binary_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("Y")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("U")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("V")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cursx")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cursy")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("tolerance")); + + x->x_pdp_output = outlet_new(&x->x_obj, &s_anything); + x->x_Y = outlet_new(&x->x_obj, &s_float); + x->x_U = outlet_new(&x->x_obj, &s_float); + x->x_V = outlet_new(&x->x_obj, &s_float); + + x->x_colorY = 200; + x->x_colorU = -1; + x->x_colorV = -1; + + x->x_packet0 = -1; + x->x_packet1 = -1; + + x->x_cursX = -1; + x->x_cursY = -1; + x->x_tolerance = 55; + + x->x_vwidth = -1; + x->x_vheight = -1; + x->x_vsize = -1; + x->x_frame = NULL; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_binary_setup(void) +{ + // post( pdp_binary_version ); + pdp_binary_class = class_new(gensym("pdp_binary"), (t_newmethod)pdp_binary_new, + (t_method)pdp_binary_free, sizeof(t_pdp_binary), 0, A_NULL); + + class_addmethod(pdp_binary_class, (t_method)pdp_binary_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_binary_class, (t_method)pdp_binary_y, gensym("Y"), A_FLOAT, A_NULL); + class_addmethod(pdp_binary_class, (t_method)pdp_binary_u, gensym("U"), A_FLOAT, A_NULL); + class_addmethod(pdp_binary_class, (t_method)pdp_binary_v, gensym("V"), A_FLOAT, A_NULL); + class_addmethod(pdp_binary_class, (t_method)pdp_binary_cursx, gensym("cursx"), A_FLOAT, A_NULL); + class_addmethod(pdp_binary_class, (t_method)pdp_binary_cursy, gensym("cursy"), A_FLOAT, A_NULL); + class_addmethod(pdp_binary_class, (t_method)pdp_binary_pick, gensym("pick"), A_NULL); + class_addmethod(pdp_binary_class, (t_method)pdp_binary_tolerance, gensym("tolerance"), A_FLOAT, A_NULL); + class_addmethod(pdp_binary_class, (t_method)pdp_binary_setcur, gensym("setcur"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_sethelpsymbol( pdp_binary_class, gensym("pdp_binary.pd") ); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_cropper.c b/modules/pdp_cropper.c new file mode 100644 index 0000000..f1c3004 --- /dev/null +++ b/modules/pdp_cropper.c @@ -0,0 +1,291 @@ +/* + * PiDiP module. + * Copyright (c) by Yves Degoyon (ydegoyon@free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a video cropper + * Written by Yves Degoyon + */ + + + +#include "pdp.h" +#include + +static char *pdp_cropper_version = "pdp_cropper: a video cropper, version 0.1, written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_cropper_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + t_int x_csizex; + t_int x_csizey; + t_int x_csizev; + unsigned int x_encoding; + + int x_cropx1; + int x_cropx2; + int x_cropy1; + int x_cropy2; + +} t_pdp_cropper; + +static void pdp_cropper_cropx1(t_pdp_cropper *x, t_floatarg fcropx1 ) +{ + if ( ( fcropx1>=0 ) && ( fcropx1x_vwidth ) ) + { + x->x_cropx1 = (t_int)fcropx1; + } +} + +static void pdp_cropper_cropx2(t_pdp_cropper *x, t_floatarg fcropx2 ) +{ + if ( ( fcropx2>=0 ) && ( fcropx2x_vwidth ) ) + { + x->x_cropx2 = (t_int)fcropx2; + } +} + +static void pdp_cropper_cropy1(t_pdp_cropper *x, t_floatarg fcropy1 ) +{ + if ( ( fcropy1>=0 ) && ( fcropy1x_vheight ) ) + { + x->x_cropy1 = (t_int)fcropy1; + } +} + +static void pdp_cropper_cropy2(t_pdp_cropper *x, t_floatarg fcropy2 ) +{ + if ( ( fcropy2>=0 ) && ( fcropy2x_vheight ) ) + { + x->x_cropy2 = (t_int)fcropy2; + } +} + +static void pdp_cropper_process_yv12(t_pdp_cropper *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *newheader = NULL; + short int *newdata = NULL; + int i; + + int px, py; + short int *pY, *pU, *pV; + short int *pnY, *pnU, *pnV; + int minx, maxx; + int miny, maxy; + + /* allocate all ressources */ + if ( ( (t_int)header->info.image.width != x->x_vwidth ) || + ( (t_int)header->info.image.height != x->x_vheight ) ) + { + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + if ( ( x->x_cropx1 <0 ) || ( x->x_cropx1 >= x->x_vwidth ) ) x->x_cropx1 = 0; + if ( ( x->x_cropx2 <0 ) || ( x->x_cropx2 >= x->x_vwidth ) ) x->x_cropx2 = x->x_vwidth-1; + if ( ( x->x_cropy1 <0 ) || ( x->x_cropy1 >= x->x_vheight ) ) x->x_cropy1 = 0; + if ( ( x->x_cropy2 <0 ) || ( x->x_cropy2 >= x->x_vheight ) ) x->x_cropy2 = x->x_vheight-1; + } + + x->x_csizex = abs ( x->x_cropx2 - x->x_cropx1 ); + if ( x->x_csizex%8 != 0 ) x->x_csizex = x->x_csizex + (8-(x->x_csizex%8)); // align on 8 + x->x_csizey = abs ( x->x_cropy2 - x->x_cropy1 ); + if ( x->x_csizey%8 != 0 ) x->x_csizey = x->x_csizey + (8-(x->x_csizey%8)); // align on 8 + if ( x->x_csizex == 0 ) x->x_csizex = 8; + if ( x->x_csizey == 0 ) x->x_csizey = 8; + // post( "pdp_cropper : new image %dx%d", x->x_csizex, x->x_csizey ); + + x->x_csizev = x->x_csizex*x->x_csizey; + x->x_packet1 = pdp_packet_new_image_YCrCb( x->x_csizex, x->x_csizey ); + newheader = pdp_packet_header(x->x_packet1); + newdata = (short int *)pdp_packet_data(x->x_packet1); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_csizex; + newheader->info.image.height = x->x_csizey; + + pY = data; + pU = (data+x->x_vsize); + pV = (data+x->x_vsize+(x->x_vsize>>2)); + pnY = newdata; + pnU = (newdata+x->x_csizev); + pnV = (newdata+x->x_csizev+(x->x_csizev>>2)); + + if ( x->x_cropx1x_cropx2 ) + { + minx = x->x_cropx1; + maxx = x->x_cropx2; + } + else + { + minx = x->x_cropx2; + maxx = x->x_cropx1; + } + + if ( x->x_cropy1x_cropy2 ) + { + miny = x->x_cropy1; + maxy = x->x_cropy2; + } + else + { + miny = x->x_cropy2; + maxy = x->x_cropy1; + } + + for(py=miny; pyx_csizex+(px-minx)) = *(pY+py*x->x_vwidth+px); + if ( (py%2==0) && (px%2==0) ) + { + *(pnU+((py-miny)>>1)*(x->x_csizex>>1)+((px-minx)>>1)) = *(pU+(py>>1)*(x->x_vwidth>>1)+(px>>1)); + *(pnV+((py-miny)>>1)*(x->x_csizex>>1)+((px-minx)>>1)) = *(pV+(py>>1)*(x->x_vwidth>>1)+(px>>1)); + } + } + } + + return; +} + +static void pdp_cropper_sendpacket(t_pdp_cropper *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet1); +} + +static void pdp_cropper_process(t_pdp_cropper *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_IMAGE == header->type)){ + + /* pdp_cropper_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding){ + + case PDP_IMAGE_YV12: + pdp_queue_add(x, pdp_cropper_process_yv12, pdp_cropper_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // pdp_cropper_process_packet(x); + break; + + default: + /* don't know the type, so dont pdp_cropper_process */ + break; + + } + } +} + +static void pdp_cropper_input_0(t_pdp_cropper *x, t_symbol *s, t_floatarg f) +{ + /* if this is a register_ro message or register_rw message, register with packet factory */ + + if (s== gensym("register_rw")) + { + x->x_dropped = + pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + /* add the process method and callback to the process queue */ + pdp_cropper_process(x); + } +} + +static void pdp_cropper_free(t_pdp_cropper *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_cropper_class; + +void *pdp_cropper_new(void) +{ + int i; + + t_pdp_cropper *x = (t_pdp_cropper *)pd_new(pdp_cropper_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("x1")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("x2")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("y1")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("y2")); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_queue_id = -1; + + x->x_cropx1=-1; + x->x_cropx2=-1; + x->x_cropy1=-1; + x->x_cropy2=-1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_cropper_setup(void) +{ +// post( pdp_cropper_version ); + pdp_cropper_class = class_new(gensym("pdp_cropper"), (t_newmethod)pdp_cropper_new, + (t_method)pdp_cropper_free, sizeof(t_pdp_cropper), 0, A_NULL); + + class_addmethod(pdp_cropper_class, (t_method)pdp_cropper_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_cropper_class, (t_method)pdp_cropper_cropx1, gensym("x1"), A_FLOAT, A_NULL); + class_addmethod(pdp_cropper_class, (t_method)pdp_cropper_cropx2, gensym("x2"), A_FLOAT, A_NULL); + class_addmethod(pdp_cropper_class, (t_method)pdp_cropper_cropy1, gensym("y1"), A_FLOAT, A_NULL); + class_addmethod(pdp_cropper_class, (t_method)pdp_cropper_cropy2, gensym("y2"), A_FLOAT, A_NULL); + class_sethelpsymbol( pdp_cropper_class, gensym("pdp_cropper.pd") ); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_dilate.c b/modules/pdp_dilate.c new file mode 100644 index 0000000..062f452 --- /dev/null +++ b/modules/pdp_dilate.c @@ -0,0 +1,281 @@ +/* + * PiDiP module + * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a morphological operator for dilation using as a structuring element a WxH square + */ + +#include "pdp.h" +#include "yuv.h" +#include +#include + +static char *pdp_dilate_version = "pdp_dilate: morphology : dilation version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_dilate_struct +{ + t_object x_obj; + + t_int x_packet0; + t_int x_packet1; + t_int x_queue_id; + t_int x_dropped; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + t_int x_kernelw; // width of the (square) kernel + t_int x_kernelh; // height of the square kernel + t_int x_nbpasses; // number of passes + short int *x_frame; // keep a copy of current frame for transformations + + t_outlet *x_pdp_output; // output packets + +} t_pdp_dilate; + +static void pdp_dilate_nbpasses(t_pdp_dilate *x, t_floatarg fpasses ) +{ + if ( fpasses>=1.) + { + x->x_nbpasses = (t_int)fpasses; + } +} + +static void pdp_dilate_kernelw(t_pdp_dilate *x, t_floatarg fkernelw ) +{ + if ( fkernelw>=0.) + { + x->x_kernelw = (t_int)fkernelw; + } +} + +static void pdp_dilate_kernelh(t_pdp_dilate *x, t_floatarg fkernelh ) +{ + if ( fkernelh>=0.) + { + x->x_kernelh = (t_int)fkernelh; + } +} + +static void pdp_dilate_allocate(t_pdp_dilate *x) +{ + x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + + if ( !x->x_frame ) + { + post( "pdp_dilate : severe error : cannot allocate buffer !!! "); + return; + } +} + +static void pdp_dilate_free_ressources(t_pdp_dilate *x) +{ + if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); +} + +static void pdp_dilate_process_yv12(t_pdp_dilate *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *newheader = pdp_packet_header(x->x_packet1); + short int *newdata = (short int *)pdp_packet_data(x->x_packet1); + t_int i; + t_int px=0, py=0; + short int *pfY, *pfU, *pfV; + t_int ppx, ppy, ix, iy, pn, kx, ky; + + // allocate all ressources + if ( ( (int)header->info.image.width != x->x_vwidth ) || + ( (int)header->info.image.height != x->x_vheight ) ) + { + pdp_dilate_free_ressources( x ); + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + pdp_dilate_allocate( x ); + post( "pdp_dilate : reallocated buffers" ); + } + + // post( "pdp_dilate : newheader:%x", newheader ); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + memcpy( newdata, data, x->x_vsize+(x->x_vsize>>1)<<1 ); + memcpy( x->x_frame, data, x->x_vsize+(x->x_vsize>>1)<<1 ); + + // dilate (supposedly) binary image by using a 3x3 square as a structuring element + pfY = x->x_frame; + pfV = x->x_frame+x->x_vsize; + pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2); + kx = (x->x_kernelw/2); + ky = (x->x_kernelh/2); + for ( pn=0; pnx_nbpasses; pn++ ) + { + memcpy( x->x_frame, newdata, x->x_vsize+(x->x_vsize>>1)<<1 ); + for ( py=0; pyx_vheight; py++ ) + { + for ( px=0; pxx_vwidth; px++ ) + { + if ( *(pfY+py*x->x_vwidth+px) == 0 ) + { + for (ix=-kx; ix<=kx; ix++) + { + ppx=px+ix; + if ( (ppx>=0) && (ppxx_vwidth) ) + { + for (iy=-ky; iy<=ky; iy++) + { + ppy=py+iy; + if ( (ppy>=0) && (ppyx_vheight) ) + { + if( *(pfY+ppy*x->x_vwidth+ppx) == ((255)<<7) ) + { + *(newdata+py*x->x_vwidth+px) = ((255)<<7); + break; + } + } + } + if ( *(newdata+py*x->x_vwidth+px) == ((255)<<7) ) break; + } + } + } + } + } + } + + return; +} + +static void pdp_dilate_sendpacket(t_pdp_dilate *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1); +} + +static void pdp_dilate_process(t_pdp_dilate *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_IMAGE == header->type)){ + + /* pdp_dilate_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding) + { + + case PDP_IMAGE_YV12: + x->x_packet1 = pdp_packet_clone_rw(x->x_packet0); + pdp_queue_add(x, pdp_dilate_process_yv12, pdp_dilate_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // should write something to handle these one day + // but i don't use this mode + break; + + default: + /* don't know the type, so dont pdp_dilate_process */ + break; + + } + } + +} + +static void pdp_dilate_input_0(t_pdp_dilate *x, t_symbol *s, t_floatarg f) +{ + /* if this is a register_ro message or register_rw message, register with packet factory */ + + if (s== gensym("register_rw")) + { + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + pdp_dilate_process(x); + } +} + +static void pdp_dilate_free(t_pdp_dilate *x) +{ + int i; + + pdp_packet_mark_unused(x->x_packet0); + pdp_dilate_free_ressources( x ); +} + +t_class *pdp_dilate_class; + +void *pdp_dilate_new(void) +{ + int i; + + t_pdp_dilate *x = (t_pdp_dilate *)pd_new(pdp_dilate_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("nbpasses")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelw")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelh")); + + x->x_pdp_output = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_vwidth = -1; + x->x_vheight = -1; + x->x_vsize = -1; + x->x_frame = NULL; + x->x_kernelw = 3; + x->x_kernelh = 3; + + x->x_nbpasses = 1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_dilate_setup(void) +{ + // post( pdp_dilate_version ); + pdp_dilate_class = class_new(gensym("pdp_dilate"), (t_newmethod)pdp_dilate_new, + (t_method)pdp_dilate_free, sizeof(t_pdp_dilate), 0, A_NULL); + + class_addmethod(pdp_dilate_class, (t_method)pdp_dilate_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_dilate_class, (t_method)pdp_dilate_nbpasses, gensym("nbpasses"), A_FLOAT, A_NULL); + class_addmethod(pdp_dilate_class, (t_method)pdp_dilate_kernelw, gensym("kernelw"), A_FLOAT, A_NULL); + class_addmethod(pdp_dilate_class, (t_method)pdp_dilate_kernelh, gensym("kernelh"), A_FLOAT, A_NULL); + class_sethelpsymbol( pdp_dilate_class, gensym("pdp_dilate.pd") ); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_disintegration.c b/modules/pdp_disintegration.c new file mode 100644 index 0000000..93eeec7 --- /dev/null +++ b/modules/pdp_disintegration.c @@ -0,0 +1,280 @@ +/* + * PiDiP module + * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a piksels sum-up operator + */ + +#include "pdp.h" +#include "yuv.h" +#include +#include + +static char *pdp_disintegration_version = "pdp_disintegration: piksels sum-up version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_disintegration_struct +{ + t_object x_obj; + + t_int x_packet0; + t_int x_packet1; + t_int x_queue_id; + t_int x_dropped; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + t_int x_nbpasses; // number of passes + t_int x_reductor; // fraction reductor + short int *x_frame; // keep a copy of current frame for transformations + + t_outlet *x_pdp_output; // output packets + +} t_pdp_disintegration; + +static void pdp_disintegration_nbpasses(t_pdp_disintegration *x, t_floatarg fpasses ) +{ + if ( fpasses>=1.) + { + x->x_nbpasses = (t_int)fpasses; + } +} + +static void pdp_disintegration_reductor(t_pdp_disintegration *x, t_floatarg freductor ) +{ + if ( freductor>=1.) + { + x->x_reductor = (t_int)freductor; + } +} + +static void pdp_disintegration_allocate(t_pdp_disintegration *x) +{ + x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + + if ( !x->x_frame ) + { + post( "pdp_disintegration : severe error : cannot allocate buffer !!! "); + return; + } +} + +static void pdp_disintegration_free_ressources(t_pdp_disintegration *x) +{ + if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); +} + +static void pdp_disintegration_process_yv12(t_pdp_disintegration *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *newheader = pdp_packet_header(x->x_packet1); + short int *newdata = (short int *)pdp_packet_data(x->x_packet1); + t_int i; + t_int px=0, py=0; + short int *pfY, *pfU, *pfV; + t_int ppx, ppy, ix, iy, pn; + t_int nvalue; + + // allocate all ressources + if ( ( (int)header->info.image.width != x->x_vwidth ) || + ( (int)header->info.image.height != x->x_vheight ) ) + { + pdp_disintegration_free_ressources( x ); + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + pdp_disintegration_allocate( x ); + post( "pdp_disintegration : reallocated buffers" ); + } + + // post( "pdp_disintegration : newheader:%x", newheader ); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + memcpy( newdata, data, x->x_vsize+(x->x_vsize>>1)<<1 ); + memcpy( x->x_frame, data, x->x_vsize+(x->x_vsize>>1)<<1 ); + + pfY = x->x_frame; + pfV = x->x_frame+x->x_vsize; + pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2); + + // foreground piksels are now 1 + for ( py=0; pyx_vheight; py++ ) + { + for ( px=0; pxx_vwidth; px++ ) + { + if ( *(pfY+py*x->x_vwidth+px) == ((255)<<7) ) + { + *(pfY+py*x->x_vwidth+px) = ((1)<<7); + } + } + } + + for ( pn=0; pnx_nbpasses; pn++ ) + { + memcpy( x->x_frame, newdata, x->x_vsize+(x->x_vsize>>1)<<1 ); + for ( py=0; pyx_vheight; py++ ) + { + for ( px=0; pxx_vwidth; px++ ) + { + nvalue = 0; + for (ix=-1; ix<=1; ix++) + { + ppx=px+ix; + if ( (ppx>=0) && (ppxx_vwidth) ) + { + for (iy=-1; iy<=1; iy++) + { + ppy=py+iy; + if ( (ppy>=0) && (ppyx_vheight) ) + { + nvalue += *(pfY+ppy*x->x_vwidth+ppx); + } + } + } + } + if ( nvalue > ((255)<<7)*9 ) + { + *(newdata+py*x->x_vwidth+px) = ((255)<<7); + } + else + { + *(newdata+py*x->x_vwidth+px) = nvalue/x->x_reductor; + } + } + } + } + + return; +} + +static void pdp_disintegration_sendpacket(t_pdp_disintegration *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1); +} + +static void pdp_disintegration_process(t_pdp_disintegration *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_IMAGE == header->type)){ + + /* pdp_disintegration_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding) + { + + case PDP_IMAGE_YV12: + x->x_packet1 = pdp_packet_clone_rw(x->x_packet0); + pdp_queue_add(x, pdp_disintegration_process_yv12, pdp_disintegration_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // should write something to handle these one day + // but i don't use this mode + break; + + default: + /* don't know the type, so dont pdp_disintegration_process */ + break; + + } + } + +} + +static void pdp_disintegration_input_0(t_pdp_disintegration *x, t_symbol *s, t_floatarg f) +{ + /* if this is a register_ro message or register_rw message, register with packet factory */ + + if (s== gensym("register_rw")) + { + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + pdp_disintegration_process(x); + } +} + +static void pdp_disintegration_free(t_pdp_disintegration *x) +{ + int i; + + pdp_packet_mark_unused(x->x_packet0); + pdp_disintegration_free_ressources( x ); +} + +t_class *pdp_disintegration_class; + +void *pdp_disintegration_new(void) +{ + int i; + + t_pdp_disintegration *x = (t_pdp_disintegration *)pd_new(pdp_disintegration_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("nbpasses")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("reductor")); + + x->x_pdp_output = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_vwidth = -1; + x->x_vheight = -1; + x->x_vsize = -1; + x->x_frame = NULL; + x->x_nbpasses = 3; + x->x_reductor = 5; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_disintegration_setup(void) +{ + // post( pdp_disintegration_version ); + pdp_disintegration_class = class_new(gensym("pdp_disintegration"), (t_newmethod)pdp_disintegration_new, + (t_method)pdp_disintegration_free, sizeof(t_pdp_disintegration), 0, A_NULL); + + class_addmethod(pdp_disintegration_class, (t_method)pdp_disintegration_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_disintegration_class, (t_method)pdp_disintegration_nbpasses, gensym("nbpasses"), A_FLOAT, A_NULL); + class_addmethod(pdp_disintegration_class, (t_method)pdp_disintegration_reductor, gensym("reductor"), A_FLOAT, A_NULL); + class_sethelpsymbol( pdp_disintegration_class, gensym("pdp_disintegration.pd") ); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_distance.c b/modules/pdp_distance.c new file mode 100644 index 0000000..34b3ddf --- /dev/null +++ b/modules/pdp_distance.c @@ -0,0 +1,337 @@ +/* + * PiDiP module + * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a salience distance operator + * ( inspired by Paul Rosin, 91, http://www.cs.cf.ac.uk/User/Paul.Rosin/resources/sdt/ ) + */ + +#include "pdp.h" +#include "yuv.h" +#include +#include + +static char *pdp_distance_version = "pdp_distance: morphology : distance operator version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_distance_struct +{ + t_object x_obj; + + t_int x_packet0; + t_int x_packet1; + t_int x_queue_id; + t_int x_dropped; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + short int *x_frame; // keep a copy of current frame for transformations + + t_int x_coeff1; + t_int x_coeff2; + t_int x_coeff3; + t_int x_coeff4; + + t_outlet *x_pdp_output; // output packets + +} t_pdp_distance; + +static void pdp_distance_allocate(t_pdp_distance *x) +{ + x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + + if ( !x->x_frame ) + { + post( "pdp_distance : severe error : cannot allocate buffer !!! "); + return; + } +} + +static void pdp_distance_coeff1(t_pdp_distance *x, t_floatarg fcoeff1 ) +{ + x->x_coeff1 = (int) fcoeff1; +} + +static void pdp_distance_coeff2(t_pdp_distance *x, t_floatarg fcoeff2 ) +{ + x->x_coeff2 = (int) fcoeff2; +} + +static void pdp_distance_coeff3(t_pdp_distance *x, t_floatarg fcoeff3 ) +{ + x->x_coeff3 = (int) fcoeff3; +} + +static void pdp_distance_coeff4(t_pdp_distance *x, t_floatarg fcoeff4 ) +{ + x->x_coeff4 = (int) fcoeff4; +} + +static void pdp_distance_free_ressources(t_pdp_distance *x) +{ + if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); +} + +static void pdp_distance_process_yv12(t_pdp_distance *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *newheader = pdp_packet_header(x->x_packet1); + short int *newdata = (short int *)pdp_packet_data(x->x_packet1); + t_int i; + t_int px=0, py=0; + short int *pfY, *pfU, *pfV; + t_int nvalues, values[5], ival, mval; + + // allocate all ressources + if ( ( (int)header->info.image.width != x->x_vwidth ) || + ( (int)header->info.image.height != x->x_vheight ) ) + { + pdp_distance_free_ressources( x ); + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + pdp_distance_allocate( x ); + post( "pdp_distance : reallocated buffers" ); + } + + // post( "pdp_distance : newheader:%x", newheader ); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + memcpy( newdata, data, x->x_vsize+(x->x_vsize>>1)<<1 ); + memcpy( x->x_frame, data, x->x_vsize+(x->x_vsize>>1)<<1 ); + + pfY = x->x_frame; + pfV = x->x_frame+x->x_vsize; + pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2); + + // thresholding + for ( py=0; pyx_vheight; py++ ) + { + for ( px=0; pxx_vwidth; px++ ) + { + if ( *(pfY+py*x->x_vwidth+px) > ((128)<<7) ) + { + *(pfY+py*x->x_vwidth+px) = ((128)<<7); + } + } + } + + // forward pass + for ( py=0; pyx_vheight; py++ ) + { + for ( px=0; pxx_vwidth; px++ ) + { + nvalues = 0; + if ( ((px-1)>=0) && ((py-1)>=0) ) + { + values[nvalues] = *(pfY+(py-1)*x->x_vwidth+(px-1)) + (x->x_coeff1<<7); + nvalues++; + } + if ( (py-1)>=0 ) + { + values[nvalues] = *(pfY+(py-1)*x->x_vwidth+px) + (x->x_coeff2<<7); + nvalues++; + } + if ( ((px+1)x_vwidth) && ((py-1)>=0) ) + { + values[nvalues] = *(pfY+(py-1)*x->x_vwidth+(px+1)) + (x->x_coeff3<<7); + nvalues++; + } + if ( (px-1)>=0 ) + { + values[nvalues] = *(pfY+py*x->x_vwidth+(px-1)) + (x->x_coeff4<<7); + nvalues++; + } + values[nvalues] = *(pfY+py*x->x_vwidth+px); + nvalues++; + + mval = values[0]; + for (ival=0; ivalx_frame+py*x->x_vwidth+px)=mval; + } + } + + // backward pass + for ( py=x->x_vheight-1; py>=0; py-- ) + { + for ( px=x->x_vwidth-1; px>=0; px-- ) + { + nvalues = 0; + if ( ((px-1)>=0) && ((py+1)x_vheight) ) + { + values[nvalues] = *(pfY+(py+1)*x->x_vwidth+(px-1)) + (x->x_coeff1<<7); + nvalues++; + } + if ( (py+1)x_vheight ) + { + values[nvalues] = *(pfY+(py+1)*x->x_vwidth+px) + (x->x_coeff2<<7); + nvalues++; + } + if ( ((px+1)x_vwidth) && ((py+1)x_vheight) ) + { + values[nvalues] = *(pfY+(py+1)*x->x_vwidth+(px+1)) + (x->x_coeff3<<7); + nvalues++; + } + if ( (px+1)x_vwidth ) + { + values[nvalues] = *(pfY+py*x->x_vwidth+(px+1)) + (x->x_coeff4<<7); + nvalues++; + } + values[nvalues] = *(pfY+py*x->x_vwidth+px); + nvalues++; + + mval = values[0]; + for (ival=0; ivalx_frame+py*x->x_vwidth+px)=mval; + } + } + + memcpy( newdata, x->x_frame, x->x_vsize+(x->x_vsize>>1)<<1 ); + + return; +} + +static void pdp_distance_sendpacket(t_pdp_distance *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1); +} + +static void pdp_distance_process(t_pdp_distance *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_IMAGE == header->type)){ + + /* pdp_distance_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding) + { + + case PDP_IMAGE_YV12: + x->x_packet1 = pdp_packet_clone_rw(x->x_packet0); + pdp_queue_add(x, pdp_distance_process_yv12, pdp_distance_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // should write something to handle these one day + // but i don't use this mode + break; + + default: + /* don't know the type, so dont pdp_distance_process */ + break; + + } + } + +} + +static void pdp_distance_input_0(t_pdp_distance *x, t_symbol *s, t_floatarg f) +{ + /* if this is a register_ro message or register_rw message, register with packet factory */ + + if (s== gensym("register_rw")) + { + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + pdp_distance_process(x); + } +} + +static void pdp_distance_free(t_pdp_distance *x) +{ + int i; + + pdp_packet_mark_unused(x->x_packet0); + pdp_distance_free_ressources( x ); +} + +t_class *pdp_distance_class; + +void *pdp_distance_new(void) +{ + int i; + + t_pdp_distance *x = (t_pdp_distance *)pd_new(pdp_distance_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("coeff1")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("coeff2")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("coeff3")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("coeff4")); + + x->x_pdp_output = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_vwidth = -1; + x->x_vheight = -1; + x->x_vsize = -1; + x->x_frame = NULL; + + x->x_coeff1 = 4; + x->x_coeff2 = 3; + x->x_coeff3 = 4; + x->x_coeff4 = 3; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_distance_setup(void) +{ + // post( pdp_distance_version ); + pdp_distance_class = class_new(gensym("pdp_distance"), (t_newmethod)pdp_distance_new, + (t_method)pdp_distance_free, sizeof(t_pdp_distance), 0, A_NULL); + + class_addmethod(pdp_distance_class, (t_method)pdp_distance_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_distance_class, (t_method)pdp_distance_coeff1, gensym("coeff1"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_distance_class, (t_method)pdp_distance_coeff2, gensym("coeff2"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_distance_class, (t_method)pdp_distance_coeff3, gensym("coeff3"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_distance_class, (t_method)pdp_distance_coeff4, gensym("coeff4"), A_DEFFLOAT, A_NULL); + class_sethelpsymbol( pdp_distance_class, gensym("pdp_distance.pd") ); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_erode.c b/modules/pdp_erode.c new file mode 100644 index 0000000..a843308 --- /dev/null +++ b/modules/pdp_erode.c @@ -0,0 +1,281 @@ +/* + * PiDiP module + * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a morphological operator for erosion using as a structuring element a WxH square + */ + +#include "pdp.h" +#include "yuv.h" +#include +#include + +static char *pdp_erode_version = "pdp_erode: morphology : erosion version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_erode_struct +{ + t_object x_obj; + + t_int x_packet0; + t_int x_packet1; + t_int x_queue_id; + t_int x_dropped; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + t_int x_kernelw; // width of the (square) kernel + t_int x_kernelh; // height of the square kernel + t_int x_nbpasses; // number of passes + short int *x_frame; // keep a copy of current frame for transformations + + t_outlet *x_pdp_output; // output packets + +} t_pdp_erode; + +static void pdp_erode_nbpasses(t_pdp_erode *x, t_floatarg fpasses ) +{ + if ( fpasses>=1.) + { + x->x_nbpasses = (t_int)fpasses; + } +} + +static void pdp_erode_kernelw(t_pdp_erode *x, t_floatarg fkernelw ) +{ + if ( fkernelw>=0.) + { + x->x_kernelw = (t_int)fkernelw; + } +} + +static void pdp_erode_kernelh(t_pdp_erode *x, t_floatarg fkernelh ) +{ + if ( fkernelh>=0.) + { + x->x_kernelh = (t_int)fkernelh; + } +} + +static void pdp_erode_allocate(t_pdp_erode *x) +{ + x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + + if ( !x->x_frame ) + { + post( "pdp_erode : severe error : cannot allocate buffer !!! "); + return; + } +} + +static void pdp_erode_free_ressources(t_pdp_erode *x) +{ + if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); +} + +static void pdp_erode_process_yv12(t_pdp_erode *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *newheader = pdp_packet_header(x->x_packet1); + short int *newdata = (short int *)pdp_packet_data(x->x_packet1); + t_int i; + t_int px=0, py=0; + short int *pfY, *pfU, *pfV; + t_int ppx, ppy, ix, iy, pn, kx, ky; + + // allocate all ressources + if ( ( (int)header->info.image.width != x->x_vwidth ) || + ( (int)header->info.image.height != x->x_vheight ) ) + { + pdp_erode_free_ressources( x ); + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + pdp_erode_allocate( x ); + post( "pdp_erode : reallocated buffers" ); + } + + // post( "pdp_erode : newheader:%x", newheader ); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + memcpy( newdata, data, x->x_vsize+(x->x_vsize>>1)<<1 ); + memcpy( x->x_frame, data, x->x_vsize+(x->x_vsize>>1)<<1 ); + + // erode (supposedly) binary image by using a 3x3 square as a structuring element + pfY = x->x_frame; + pfV = x->x_frame+x->x_vsize; + pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2); + kx = (x->x_kernelw/2); + ky = (x->x_kernelh/2); + for ( pn=0; pnx_nbpasses; pn++ ) + { + memcpy( x->x_frame, newdata, x->x_vsize+(x->x_vsize>>1)<<1 ); + for ( py=0; pyx_vheight; py++ ) + { + for ( px=0; pxx_vwidth; px++ ) + { + if ( *(pfY+py*x->x_vwidth+px) != 0 ) + { + for (ix=-kx; ix<=kx; ix++) + { + ppx=px+ix; + if ( (ppx>=0) && (ppxx_vwidth) ) + { + for (iy=-ky; iy<=ky; iy++) + { + ppy=py+iy; + if ( (ppy>=0) && (ppyx_vheight) ) + { + if( *(pfY+ppy*x->x_vwidth+ppx) != ((255)<<7) ) + { + *(newdata+py*x->x_vwidth+px) = 0; + break; + } + } + } + if ( *(newdata+py*x->x_vwidth+px) == 0 ) break; + } + } + } + } + } + } + + return; +} + +static void pdp_erode_sendpacket(t_pdp_erode *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1); +} + +static void pdp_erode_process(t_pdp_erode *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_IMAGE == header->type)){ + + /* pdp_erode_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding) + { + + case PDP_IMAGE_YV12: + x->x_packet1 = pdp_packet_clone_rw(x->x_packet0); + pdp_queue_add(x, pdp_erode_process_yv12, pdp_erode_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // should write something to handle these one day + // but i don't use this mode + break; + + default: + /* don't know the type, so dont pdp_erode_process */ + break; + + } + } + +} + +static void pdp_erode_input_0(t_pdp_erode *x, t_symbol *s, t_floatarg f) +{ + /* if this is a register_ro message or register_rw message, register with packet factory */ + + if (s== gensym("register_rw")) + { + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + pdp_erode_process(x); + } +} + +static void pdp_erode_free(t_pdp_erode *x) +{ + int i; + + pdp_packet_mark_unused(x->x_packet0); + pdp_erode_free_ressources( x ); +} + +t_class *pdp_erode_class; + +void *pdp_erode_new(void) +{ + int i; + + t_pdp_erode *x = (t_pdp_erode *)pd_new(pdp_erode_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("nbpasses")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelw")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelh")); + + x->x_pdp_output = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_vwidth = -1; + x->x_vheight = -1; + x->x_vsize = -1; + x->x_frame = NULL; + x->x_kernelw = 3; + x->x_kernelh = 3; + + x->x_nbpasses = 1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_erode_setup(void) +{ + // post( pdp_erode_version ); + pdp_erode_class = class_new(gensym("pdp_erode"), (t_newmethod)pdp_erode_new, + (t_method)pdp_erode_free, sizeof(t_pdp_erode), 0, A_NULL); + + class_addmethod(pdp_erode_class, (t_method)pdp_erode_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_erode_class, (t_method)pdp_erode_nbpasses, gensym("nbpasses"), A_FLOAT, A_NULL); + class_addmethod(pdp_erode_class, (t_method)pdp_erode_kernelw, gensym("kernelw"), A_FLOAT, A_NULL); + class_addmethod(pdp_erode_class, (t_method)pdp_erode_kernelh, gensym("kernelh"), A_FLOAT, A_NULL); + class_sethelpsymbol( pdp_erode_class, gensym("pdp_erode.pd") ); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_hitandmiss.c b/modules/pdp_hitandmiss.c new file mode 100644 index 0000000..0664605 --- /dev/null +++ b/modules/pdp_hitandmiss.c @@ -0,0 +1,386 @@ +/* + * PiDiP module + * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a morphological operator for hit and miss agorithm + * using as a structuring element a WxH square + */ + +#include "pdp.h" +#include "yuv.h" +#include +#include + +static char *pdp_hitandmiss_version = "pdp_hitandmiss: morphology : hit&miss version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; + +typedef struct pdp_hitandmiss_struct +{ + t_object x_obj; + + t_int x_packet0; + t_int x_packet1; + t_int x_queue_id; + t_int x_dropped; + + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + t_int x_kernelw; // width of the (square) kernel + t_int x_kernelh; // height of the square kernel + char *x_kdata; // kernel data + t_int x_nbpasses; // number of passes + short int *x_frame; // keep a copy of current frame for transformations + + t_outlet *x_pdp_output; // output packets + +} t_pdp_hitandmiss; + +static void pdp_hitandmiss_nbpasses(t_pdp_hitandmiss *x, t_floatarg fpasses ) +{ + if ( fpasses>=1.) + { + x->x_nbpasses = (t_int)fpasses; + } +} + +static void pdp_hitandmiss_kernelw(t_pdp_hitandmiss *x, t_floatarg fkernelw ) +{ + t_int oldw, minw; + char *nkdata; + + if ( fkernelw>=0.) + { + oldw = x->x_kernelw; + x->x_kernelw = (t_int)fkernelw; + + nkdata= (char*) malloc( x->x_kernelw*x->x_kernelh ); + + minw = (x->x_kernelw>oldw?oldw:x->x_kernelw ); + memcpy( nkdata, x->x_kdata, minw*x->x_kernelh ); + + if ( x->x_kdata ) + { + free( x->x_kdata ); + } + x->x_kdata = nkdata; + } + +} + +static void pdp_hitandmiss_kernelh(t_pdp_hitandmiss *x, t_floatarg fkernelh ) +{ + t_int oldh, minh; + char *nkdata; + + if ( fkernelh>=0.) + { + oldh = x->x_kernelh; + x->x_kernelh = (t_int)fkernelh; + + nkdata= (char*) malloc( x->x_kernelw*x->x_kernelh ); + + minh = (x->x_kernelh>oldh?oldh:x->x_kernelh ); + memcpy( nkdata, x->x_kdata, x->x_kernelw*minh ); + + if ( x->x_kdata ) + { + free( x->x_kdata ); + } + x->x_kdata = nkdata; + } +} + +static void pdp_hitandmiss_kdata(t_pdp_hitandmiss *x, t_symbol *s, int argc, t_atom *argv) +{ + t_int nbvalues, ivalue, iv; + + if ( argc > x->x_kernelw*x->x_kernelh ) + { + nbvalues = x->x_kernelh*x->x_kernelw; + post( "pdp_hitandmiss : too many data for the kernel, it should be %dx%d", x->x_kernelw, x->x_kernelh ); + post( "pdp_hitandmiss : truncated to %d", nbvalues ); + } + else if ( argc < x->x_kernelw*x->x_kernelh ) + { + nbvalues = argc; + post( "pdp_hitandmiss : too few data for the kernel, it should be %dx%d", x->x_kernelw, x->x_kernelh ); + post( "pdp_hitandmiss : padding with -1 (as if not used)" ); + for ( iv=argc; ivx_kernelw*x->x_kernelh; iv++ ) + { + x->x_kdata[iv] = -1; + } + } + else + { + nbvalues = argc; + } + + for ( iv=0; ivx_kdata[iv] = ivalue; + } + } + } +} + +static void pdp_hitandmiss_allocate(t_pdp_hitandmiss *x) +{ + x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); + + if ( !x->x_frame ) + { + post( "pdp_hitandmiss : severe error : cannot allocate buffer !!! "); + return; + } +} + +static void pdp_hitandmiss_free_ressources(t_pdp_hitandmiss *x) +{ + if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); +} + +static void pdp_hitandmiss_process_yv12(t_pdp_hitandmiss *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data(x->x_packet0); + t_pdp *newheader = pdp_packet_header(x->x_packet1); + short int *newdata = (short int *)pdp_packet_data(x->x_packet1); + t_int i; + t_int px=0, py=0; + short int *pfY, *pfU, *pfV; + t_int ppx, ppy, ix, iy, pn, kx, ky; + short int pvalue; + + // allocate all ressources + if ( ( (int)header->info.image.width != x->x_vwidth ) || + ( (int)header->info.image.height != x->x_vheight ) ) + { + pdp_hitandmiss_free_ressources( x ); + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + pdp_hitandmiss_allocate( x ); + post( "pdp_hitandmiss : reallocated buffers" ); + } + + // post( "pdp_hitandmiss : newheader:%x", newheader ); + + newheader->info.image.encoding = header->info.image.encoding; + newheader->info.image.width = x->x_vwidth; + newheader->info.image.height = x->x_vheight; + + memcpy( newdata, data, x->x_vsize+(x->x_vsize>>1)<<1 ); + memcpy( x->x_frame, data, x->x_vsize+(x->x_vsize>>1)<<1 ); + + // hit and miss (supposedly) binary image by using a 3x3 square as a structuring element + pfY = x->x_frame; + pfV = x->x_frame+x->x_vsize; + pfU = x->x_frame+x->x_vsize+(x->x_vsize>>2); + kx = (x->x_kernelw/2); + ky = (x->x_kernelh/2); + for ( pn=0; pnx_nbpasses; pn++ ) + { + memcpy( x->x_frame, newdata, x->x_vsize+(x->x_vsize>>1)<<1 ); + for ( py=0; pyx_vheight; py++ ) + { + for ( px=0; pxx_vwidth; px++ ) + { + *(newdata+py*x->x_vwidth+px) = ((255)<<7); + for (ix=-kx; ix<=kx; ix++) + { + ppx=px+ix; + if ( (ppx>=0) && (ppxx_vwidth) ) + { + for (iy=-ky; iy<=ky; iy++) + { + ppy=py+iy; + if ( (ppy>=0) && (ppyx_vheight) ) + { + if ( x->x_kdata[(ky+iy)*x->x_kernelw+(kx+ix)] == 1 ) + { + pvalue = ((255)<<7); + } + else if ( x->x_kdata[(ky+iy)*x->x_kernelw+(kx+ix)] == 0 ) + { + pvalue = 0; + } + else // unused bit in the kernel, usually -1 + { + continue; + } + if( *(pfY+ppy*x->x_vwidth+ppx) != pvalue ) + { + *(newdata+py*x->x_vwidth+px) = 0; + break; + } + } + } + if ( *(newdata+py*x->x_vwidth+px) == 0 ) break; + } + } + } + } + } + + return; +} + +static void pdp_hitandmiss_sendpacket(t_pdp_hitandmiss *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet1); +} + +static void pdp_hitandmiss_process(t_pdp_hitandmiss *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_IMAGE == header->type)){ + + /* pdp_hitandmiss_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding) + { + + case PDP_IMAGE_YV12: + x->x_packet1 = pdp_packet_clone_rw(x->x_packet0); + pdp_queue_add(x, pdp_hitandmiss_process_yv12, pdp_hitandmiss_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + // should write something to handle these one day + // but i don't use this mode + break; + + default: + /* don't know the type, so dont pdp_hitandmiss_process */ + break; + + } + } + +} + +static void pdp_hitandmiss_input_0(t_pdp_hitandmiss *x, t_symbol *s, t_floatarg f) +{ + /* if this is a register_ro message or register_rw message, register with packet factory */ + + if (s== gensym("register_rw")) + { + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + pdp_hitandmiss_process(x); + } +} + +static void pdp_hitandmiss_free(t_pdp_hitandmiss *x) +{ + int i; + + pdp_packet_mark_unused(x->x_packet0); + pdp_hitandmiss_free_ressources( x ); +} + +t_class *pdp_hitandmiss_class; + +void *pdp_hitandmiss_new(void) +{ + int i; + + t_pdp_hitandmiss *x = (t_pdp_hitandmiss *)pd_new(pdp_hitandmiss_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("nbpasses")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelw")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("kernelh")); + + x->x_pdp_output = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_vwidth = -1; + x->x_vheight = -1; + x->x_vsize = -1; + x->x_frame = NULL; + x->x_kernelw = 3; + x->x_kernelh = 3; + x->x_kdata = (char *)malloc( x->x_kernelw*x->x_kernelh ); + x->x_kdata[0] = -1; + x->x_kdata[1] = 1; + x->x_kdata[2] = -1; + x->x_kdata[3] = 0; + x->x_kdata[4] = 1; + x->x_kdata[5] = 1; + x->x_kdata[6] = 0; + x->x_kdata[7] = 0; + x->x_kdata[8] = -1; + // that is : + // | -1 1 -1 | + // | 0 1 1 | + // | 0 0 -1 | + + x->x_nbpasses = 1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_hitandmiss_setup(void) +{ + // post( pdp_hitandmiss_version ); + pdp_hitandmiss_class = class_new(gensym("pdp_hitandmiss"), (t_newmethod)pdp_hitandmiss_new, + (t_method)pdp_hitandmiss_free, sizeof(t_pdp_hitandmiss), 0, A_NULL); + + class_addmethod(pdp_hitandmiss_class, (t_method)pdp_hitandmiss_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_hitandmiss_class, (t_method)pdp_hitandmiss_nbpasses, gensym("nbpasses"), A_FLOAT, A_NULL); + class_addmethod(pdp_hitandmiss_class, (t_method)pdp_hitandmiss_kernelw, gensym("kernelw"), A_FLOAT, A_NULL); + class_addmethod(pdp_hitandmiss_class, (t_method)pdp_hitandmiss_kernelh, gensym("kernelh"), A_FLOAT, A_NULL); + class_addmethod(pdp_hitandmiss_class, (t_method)pdp_hitandmiss_kdata, gensym("kernel"), A_GIMME, A_NULL); + class_sethelpsymbol( pdp_hitandmiss_class, gensym("pdp_hitandmiss.pd") ); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_theorin~.c b/modules/pdp_theorin~.c new file mode 100644 index 0000000..2a7c72b --- /dev/null +++ b/modules/pdp_theorin~.c @@ -0,0 +1,970 @@ +/* + * PiDiP module. + * Copyright (c) by Yves Degoyon (ydegoyon@free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a theora file decoder object + * It uses libtheora and some of it code samples ( copyright xiph.org ) + * Copyleft by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include "yuv.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* theora stuff */ +#include /* vorbis stuff */ + +#define VIDEO_BUFFER_SIZE (1024*1024) +#define MAX_AUDIO_PACKET_SIZE (64 * 1024) +#define MIN_AUDIO_SIZE (64*1024) + +#define DEFAULT_CHANNELS 1 +#define DEFAULT_WIDTH 320 +#define DEFAULT_HEIGHT 240 +#define DEFAULT_FRAME_RATE 25 +#define END_OF_STREAM 20 +#define MIN_PRIORITY 0 +#define DEFAULT_PRIORITY 1 +#define MAX_PRIORITY 20 + +#define THEORA_NUM_HEADER_PACKETS 3 + +static char *pdp_theorin_version = "pdp_theorin~: version 0.1, a theora file reader ( ydegoyon@free.fr)."; + +typedef struct pdp_theorin_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_dropped; + + t_pdp *x_header; + unsigned char *x_data; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_outlet *x_pdp_out; // output decoded pdp packets + t_outlet *x_outlet_left; // left audio output + t_outlet *x_outlet_right; // right audio output + t_outlet *x_outlet_nbframes; // number of frames emitted + t_outlet *x_outlet_framerate; // real framerate + t_outlet *x_outlet_endoffile; // for signaling the end of the file + t_outlet *x_outlet_filesize; // for informing of the file size + + pthread_t x_decodechild; // file decoding thread + t_int x_usethread; // flag to activate decoding in a thread + t_int x_autoplay; // flag to autoplay the file ( default = true ) + t_int x_nextimage; // flag to play next image in manual mode + t_int x_priority; // priority of decoding thread + + char *x_filename; + FILE *x_infile; // file descriptor + t_int x_decoding; // decoding flag + t_int x_theorainit; // flag for indicating that theora is initialized + t_int x_videoready; // video ready flag + t_int x_newpicture; // new picture flag + t_int x_newpictureready;// new picture ready flag + t_int x_loop; // looping flag ( default = on ) + t_int x_notpackets; // number of theora packets decoded + t_int x_novpackets; // number of vorbis packets decoded + t_int x_endoffile; // end of the file reached + t_int x_nbframes; // number of frames emitted + t_int x_framerate; // framerate + t_int x_samplerate; // audio sample rate + t_int x_audiochannels; // audio channels + t_int x_blocksize; // audio block size + t_int x_audioon; // audio buffer filling flag + t_int x_reading; // file reading flag + t_int x_cursec; // current second + t_int x_secondcount; // number of frames received in the current second + struct timeval x_starttime; // reading starting time + + /* vorbis/theora structures */ + ogg_sync_state x_sync_state; // ogg sync state + ogg_page x_ogg_page; // ogg page + ogg_packet x_ogg_packet; // ogg packet + ogg_stream_state x_statev; // vorbis stream state + ogg_stream_state x_statet; // theora stream state + theora_info x_theora_info; // theora info + theora_comment x_theora_comment; // theora comment + theora_state x_theora_state; // theora state + vorbis_info x_vorbis_info; // vorbis info + vorbis_dsp_state x_dsp_state; // vorbis dsp state + vorbis_block x_vorbis_block; // vorbis block + vorbis_comment x_vorbis_comment; // vorbis comment + yuv_buffer x_yuvbuffer; // yuv buffer + + /* audio structures */ + t_int x_audio; // flag to activate the decoding of audio + t_float x_audio_inl[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for float audio decoded from ogg */ + t_float x_audio_inr[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for float audio decoded from ogg */ + t_int x_audioin_position; // writing position for incoming audio + +} t_pdp_theorin; + +static void pdp_theorin_priority(t_pdp_theorin *x, t_floatarg fpriority ) +{ + if ( ( x->x_priority >= MIN_PRIORITY ) && ( x->x_priority <= MAX_PRIORITY ) ) + { + x->x_priority = (int)fpriority; + } +} + +static void pdp_theorin_threadify(t_pdp_theorin *x, t_floatarg fusethread ) +{ + if ( ( fusethread == 0 ) || ( fusethread == 1 ) ) + { + x->x_usethread = (int)fusethread; + } +} + +static void pdp_theorin_audio(t_pdp_theorin *x, t_floatarg faudio ) +{ + if ( ( faudio == 0. ) || ( faudio == 1. ) ) + { + x->x_audio = (int)faudio; + } +} + +static void pdp_theorin_autoplay(t_pdp_theorin *x, t_floatarg fautoplay ) +{ + if ( ( fautoplay == 0. ) || ( fautoplay == 1. ) ) + { + x->x_autoplay = (int)fautoplay; + } +} + +static void pdp_theorin_loop(t_pdp_theorin *x, t_floatarg floop ) +{ + if ( ( floop == 0. ) || ( floop == 1. ) ) + { + x->x_loop = (int)floop; + } +} + +static void pdp_theorin_bang(t_pdp_theorin *x) +{ + if ( x->x_nextimage == 1 ) + { + // post( "pdp_theorin~ : banging too fast, previous image is not decoded yet... ignored" ); + return; + } + x->x_nextimage = 1; +} + +static t_int pdp_theorin_get_buffer_from_file(FILE *in, ogg_sync_state *oy) +{ + char *buffer; + t_int bytes; + + buffer=ogg_sync_buffer(oy,4096); + bytes=fread(buffer,1,4096,in); + ogg_sync_wrote(oy,bytes); + return(bytes); +} + +static t_int pdp_theorin_queue_page(t_pdp_theorin *x) +{ + if(x->x_notpackets) ogg_stream_pagein(&x->x_statet, &x->x_ogg_page); + if(x->x_novpackets) ogg_stream_pagein(&x->x_statev, &x->x_ogg_page); + return 0; +} + +static t_int pdp_theorin_decode_packet(t_pdp_theorin *x) +{ + int ret, count, maxsamples, samples, si=0, sj=0; + float **pcm; + struct timespec mwait; + struct timeval ctime; + long long tplaying; + long long ttheoretical; + unsigned char *pY, *pU, *pV; + unsigned char *psY, *psU, *psV; + t_int px, py; + + // post( "pdp_theorin~ : decode packet" ); + + if ( !x->x_reading ) return -1; + + while ( x->x_novpackets && !x->x_audioon ) + { + /* if there's pending, decoded audio, grab it */ + if((ret=vorbis_synthesis_pcmout(&x->x_dsp_state, &pcm))>0) + { + if (x->x_audio) + { + maxsamples=(3*MAX_AUDIO_PACKET_SIZE-x->x_audioin_position); + samples=(retx_audioin_position + samples*x->x_audiochannels < 3*MAX_AUDIO_PACKET_SIZE ) + { + memcpy( (void*)&x->x_audio_inl[x->x_audioin_position], pcm[0], samples*sizeof(t_float) ); + memcpy( (void*)&x->x_audio_inr[x->x_audioin_position], pcm[1], samples*sizeof(t_float) ); + x->x_audioin_position = ( x->x_audioin_position + samples ) % (4*MAX_AUDIO_PACKET_SIZE); + } + else + { + post( "pdp_theorin~ : audio overflow : packet ignored..."); + x->x_audioin_position = 0; + } + if ( ( x->x_audioin_position > MIN_AUDIO_SIZE ) && (!x->x_audioon) ) + { + x->x_audioon = 1; + // post( "pdp_theorin~ : audio on (audioin=%d)", x->x_audioin_position ); + } + // tell vorbis how many samples were read + // post( "pdp_theorin~ : got %d audio samples (audioin=%d)", samples, x->x_audioin_position ); + vorbis_synthesis_read(&x->x_dsp_state, samples); + } + else + { + break; + } + } + else + { + // no pending audio: is there a pending packet to decode? + if( ogg_stream_packetout(&x->x_statev, &x->x_ogg_packet)>0 ) + { + if(vorbis_synthesis(&x->x_vorbis_block, &x->x_ogg_packet)==0) + { + vorbis_synthesis_blockin(&x->x_dsp_state, &x->x_vorbis_block); + } + } + else /* we need more data; suck in another page */ + { + break; + } + } + } + + if ( !x->x_newpictureready && !x->x_newpicture ) + { + while(x->x_notpackets && !x->x_videoready) + { + // theora is one in, one out... + if(ogg_stream_packetout(&x->x_statet, &x->x_ogg_packet)>0) + { + theora_decode_packetin(&x->x_theora_state, &x->x_ogg_packet); + // post( "pdp_theorin~ : got one video frame" ); + x->x_videoready=1; + } + else + { + // post( "pdp_theorin~ : no more video frame (frames=%d)", x->x_nbframes ); + if ( x->x_nbframes > 0 ) + { + if ( !x->x_loop ) + { + x->x_endoffile = 1; + } + else + { + // restart a new loop + if ( gettimeofday(&x->x_starttime, NULL) == -1) + { + post("pdp_theorin~ : could not set start time" ); + } + } + x->x_nbframes = 0; + x->x_audioin_position = 0; // reset audio + x->x_audioon = 0; + } + break; + } + } + + if ( x->x_videoready ) + { + theora_decode_YUVout(&x->x_theora_state, &x->x_yuvbuffer); + + // create a new pdp packet from PIX_FMT_YUV420P image format + x->x_vwidth = x->x_yuvbuffer.y_width; + x->x_vheight = x->x_yuvbuffer.y_height; + x->x_vsize = x->x_vwidth*x->x_vheight; + x->x_packet0 = pdp_packet_new_bitmap_yv12( x->x_vwidth, x->x_vheight ); + // post( "pdp_theorin~ : allocated packet %d", x->x_packet0 ); + x->x_header = pdp_packet_header(x->x_packet0); + x->x_data = (unsigned char*) pdp_packet_data(x->x_packet0); + + x->x_header->info.image.encoding = PDP_BITMAP_YV12; + x->x_header->info.image.width = x->x_vwidth; + x->x_header->info.image.height = x->x_vheight; + + pY = x->x_data; + pV = x->x_data+x->x_vsize; + pU = x->x_data+x->x_vsize+(x->x_vsize>>2); + + psY = x->x_yuvbuffer.y; + psU = x->x_yuvbuffer.u; + psV = x->x_yuvbuffer.v; + + for ( py=0; pyx_vheight; py++) + { + memcpy( (void*)pY, (void*)psY, x->x_vwidth ); + pY += x->x_vwidth; + psY += x->x_yuvbuffer.y_stride; + if ( py%2==0 ) + { + memcpy( (void*)pU, (void*)psU, (x->x_vwidth>>1) ); + memcpy( (void*)pV, (void*)psV, (x->x_vwidth>>1) ); + pU += (x->x_vwidth>>1); + pV += (x->x_vwidth>>1); + psU += x->x_yuvbuffer.uv_stride; + psV += x->x_yuvbuffer.uv_stride; + } + } + if ( !x->x_autoplay ) + { + x->x_newpicture = 1; + } + else + { + x->x_newpictureready = 1; + } + } + } + + if ( x->x_newpictureready ) + { + if ( gettimeofday(&ctime, NULL) == -1) + { + post("pdp_theorin~ : could not read time" ); + } + + tplaying = ( ctime.tv_sec-x->x_starttime.tv_sec )*1000 + + ( ctime.tv_usec-x->x_starttime.tv_usec )/1000; + ttheoretical = ((x->x_nbframes)*1000 )/x->x_framerate; + // post( "pdp-theorin~ : %d playing since : %lldms ( theory : %lldms )", + // x->x_nbframes, tplaying, ttheoretical ); + + if ( ttheoretical <= tplaying ) + { + x->x_newpicture = 1; + x->x_newpictureready = 0; + } + + } + + // check end of file + if(!x->x_videoready && feof(x->x_infile)) + { + if ( x->x_loop ) + { + if ( fseek( x->x_infile, 0x0, SEEK_SET ) < 0 ) + { + post( "pdp_theorin~ : could not reset file." ); + perror( "fseek" ); + } + } + } + + // read more data in + if( ( x->x_audioin_position < MIN_AUDIO_SIZE ) || ( !x->x_newpicture && !x->x_newpictureready ) ) + { + ret=pdp_theorin_get_buffer_from_file(x->x_infile, &x->x_sync_state); + // post( "pdp_theorin~ : read %d bytes from file", ret ); + while( ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page)>0 ) + { + pdp_theorin_queue_page(x); + } + } + + x->x_videoready = 0; + + return 0; + +} + +static void *pdp_decode_file(void *tdata) +{ + t_pdp_theorin *x = (t_pdp_theorin*)tdata; + struct sched_param schedprio; + t_int pmin, pmax, p1; + struct timespec twait; + + twait.tv_sec = 0; + twait.tv_nsec = 5000000; // 5 ms + + schedprio.sched_priority = sched_get_priority_min(SCHED_FIFO) + x->x_priority; + if ( sched_setscheduler(0, SCHED_FIFO, &schedprio) == -1) + { + post("pdp_theorin~ : couldn't set priority for decoding thread."); + } + + while ( x->x_decodechild ) + { + if ( ( x->x_reading ) && ( ( x->x_autoplay ) || ( x->x_nextimage == 1 ) ) ) + { + x->x_decoding = 1; + + // decode incoming packets + if ( x->x_reading ) pdp_theorin_decode_packet( x ); + nanosleep( &twait, NULL ); + x->x_nextimage = -1; + } + else + { + x->x_decoding = 0; + nanosleep( &twait, NULL ); // nothing to do, just wait + } + } + + x->x_decoding = 0; + post("pdp_theorin~ : decoding child exiting." ); + return NULL; +} + +static void pdp_theorin_close(t_pdp_theorin *x) +{ + t_int ret, i, count=0; + struct timespec twait; + + twait.tv_sec = 0; + twait.tv_nsec = 10000000; // 10 ms + + if ( x->x_infile == NULL ) + { + post("pdp_theorin~ : close request but no file is played ... ignored" ); + return; + } + + if ( x->x_reading ) + { + x->x_newpicture = 0; + x->x_reading = 0; + post("pdp_theorin~ : waiting end of decoding..." ); + while ( x->x_decoding ) nanosleep( &twait, NULL ); + + if ( fclose( x->x_infile ) < 0 ) + { + post( "pdp_theorin~ : could not close input file" ); + perror( "fclose" ); + } + x->x_infile = NULL; + + if ( x->x_notpackets > 0 ) + { + ogg_stream_clear(&x->x_statet); + theora_clear(&x->x_theora_state); + theora_comment_clear(&x->x_theora_comment); + theora_info_clear(&x->x_theora_info); + } + + if ( x->x_novpackets > 0 ) + { + ogg_stream_clear(&x->x_statev); + vorbis_block_clear(&x->x_vorbis_block); + vorbis_dsp_clear(&x->x_dsp_state); + vorbis_comment_clear(&x->x_vorbis_comment); + vorbis_info_clear(&x->x_vorbis_info); + } + + } + + x->x_notpackets = 0; + x->x_novpackets = 0; + x->x_endoffile = 0; + x->x_nbframes = 0; + x->x_decoding = 0; + x->x_theorainit = 0; + + x->x_videoready = 0; + x->x_newpicture = 0; + + x->x_nbframes = 0; + outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + x->x_framerate = 0; + outlet_float( x->x_outlet_framerate, x->x_framerate ); +} + +static void pdp_theorin_open(t_pdp_theorin *x, t_symbol *s) +{ + t_int ret, i; + pthread_attr_t decode_child_attr; + ogg_stream_state o_tempstate; + struct stat fileinfos; + + if ( x->x_infile != NULL ) + { + post("pdp_theorin~ : open request but a file is open ... closing" ); + pdp_theorin_close(x); + } + + if ( x->x_filename ) free( x->x_filename ); + x->x_filename = (char*) malloc( strlen( s->s_name ) + 1 ); + strcpy( x->x_filename, s->s_name ); + post( "pdp_theorin~ : opening file : %s", x->x_filename ); + + if ( ( x->x_infile = fopen(x->x_filename,"r") ) == NULL ) + { + post( "pdp_theorin~ : unable to open file >%s<", x->x_filename ); + return; + } + + ogg_sync_init(&x->x_sync_state); + + // init supporting Vorbis structures needed in header parsing + vorbis_info_init(&x->x_vorbis_info); + vorbis_comment_init(&x->x_vorbis_comment); + + // init supporting Theora structures needed in header parsing + theora_comment_init(&x->x_theora_comment); + theora_info_init(&x->x_theora_info); + + // parse headers + while( !x->x_theorainit ) + { + if ( ( ret = pdp_theorin_get_buffer_from_file(x->x_infile, &x->x_sync_state) )==0) break; + + while( ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page) > 0 ) + { + /* is this a mandated initial header? If not, stop parsing */ + if(!ogg_page_bos(&x->x_ogg_page)) + { + pdp_theorin_queue_page(x); + x->x_theorainit = 1; + break; + } + + ogg_stream_init(&o_tempstate, ogg_page_serialno(&x->x_ogg_page)); + ogg_stream_pagein(&o_tempstate, &x->x_ogg_page); + ogg_stream_packetout(&o_tempstate, &x->x_ogg_packet); + + /* identify the codec: try theora */ + if(!x->x_notpackets && + theora_decode_header(&x->x_theora_info, &x->x_theora_comment, &x->x_ogg_packet)>=0) + { + /* it is theora */ + memcpy(&x->x_statet, &o_tempstate, sizeof(o_tempstate)); + x->x_notpackets=1; + }else + if(!x->x_novpackets && + vorbis_synthesis_headerin(&x->x_vorbis_info, &x->x_vorbis_comment, &x->x_ogg_packet)>=0){ + memcpy(&x->x_statev, &o_tempstate, sizeof(o_tempstate)); + x->x_novpackets=1; + }else{ + /* whatever it is, we don't care about it */ + ogg_stream_clear(&o_tempstate); + } + } + } + + // we're expecting more header packets. + while( (x->x_notpackets && x->x_notpackets<3) || (x->x_novpackets && x->x_novpackets<3) ) + { + // look for further theora headers + while(x->x_notpackets && (x->x_notpackets<3) && + (ret=ogg_stream_packetout(&x->x_statet, &x->x_ogg_packet))) + { + if( ret<0 ) + { + post("pdp_theorin~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + return; + } + if( theora_decode_header(&x->x_theora_info, &x->x_theora_comment, &x->x_ogg_packet) ) + { + post("pdp_theorin~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + return; + } + x->x_notpackets++; + if(x->x_notpackets==3) break; + } + + /* look for more vorbis header packets */ + while(x->x_novpackets && (x->x_novpackets<3) && + (ret=ogg_stream_packetout(&x->x_statev, &x->x_ogg_packet))) + { + if(ret<0) + { + post("pdp_theorin~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + return; + } + if( vorbis_synthesis_headerin(&x->x_vorbis_info, &x->x_vorbis_comment, &x->x_ogg_packet) ) + { + post("pdp_theorin~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + return; + } + x->x_novpackets++; + if(x->x_novpackets==3) break; + } + + if(ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page)>0) + { + pdp_theorin_queue_page(x); + } + else + { + if( (ret=pdp_theorin_get_buffer_from_file(x->x_infile, &x->x_sync_state))==0 ) + { + post("pdp_theorin~ : end of file while parsing headers\n"); + x->x_theorainit = 0; + return; + } + } + } + post( "pdp_theorin~ : parsed headers ok." ); + + // initialize decoders + if( x->x_notpackets ) + { + theora_decode_init(&x->x_theora_state, &x->x_theora_info); + x->x_framerate = (t_int)x->x_theora_info.fps_numerator/x->x_theora_info.fps_denominator; + post("pdp_theorin~ : stream %x is theora %dx%d %d fps video.", + x->x_statet.serialno, + x->x_theora_info.width,x->x_theora_info.height, + x->x_framerate); + if(x->x_theora_info.width!=x->x_theora_info.frame_width || + x->x_theora_info.height!=x->x_theora_info.frame_height) + { + post("pdp_theorin~ : frame content is %dx%d with offset (%d,%d).", + x->x_theora_info.frame_width, x->x_theora_info.frame_height, + x->x_theora_info.offset_x, x->x_theora_info.offset_y); + } + x->x_vwidth = x->x_theora_info.width; + x->x_vheight = x->x_theora_info.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + switch(x->x_theora_info.colorspace) + { + case OC_CS_UNSPECIFIED: + /* nothing to report */ + break;; + case OC_CS_ITU_REC_470M: + post("pdp_theorin~ : encoder specified ITU Rec 470M (NTSC) color."); + break;; + case OC_CS_ITU_REC_470BG: + post("pdp_theorin~ : encoder specified ITU Rec 470BG (PAL) color."); + break;; + default: + post("pdp_theorin~ : warning: encoder specified unknown colorspace (%d).", + x->x_theora_info.colorspace); + break;; + } + } + else + { + // tear down the partial theora setup + theora_info_clear(&x->x_theora_info); + theora_comment_clear(&x->x_theora_comment); + post("pdp_theorin~ : could not initialize theora decoder."); + x->x_theorainit = 0; + return; + } + + if( x->x_novpackets ) + { + vorbis_synthesis_init(&x->x_dsp_state, &x->x_vorbis_info); + vorbis_block_init(&x->x_dsp_state, &x->x_vorbis_block); + x->x_audiochannels = x->x_vorbis_info.channels; + x->x_samplerate = x->x_vorbis_info.rate; + post("pdp_theorin~ : ogg logical stream %x is vorbis %d channel %d Hz audio.", + x->x_statev.serialno, + x->x_audiochannels, x->x_samplerate); + } + else + { + /* tear down the partial vorbis setup */ + vorbis_info_clear(&x->x_vorbis_info); + vorbis_comment_clear(&x->x_vorbis_comment); + post("pdp_theorin~ : could not initialize vorbis decoder."); + // x->x_theorainit = 0; + // return; + x->x_audio = 0; + } + // everything seems to be ready + x->x_reading = 1; + + if ( x->x_usethread && ( x->x_decodechild == 0 ) ) + { + x->x_decodechild = 1; // trick & treets + // launch decoding thread + if ( pthread_attr_init( &decode_child_attr ) < 0 ) + { + post( "pdp_theorin~ : could not launch decoding thread" ); + perror( "pthread_attr_init" ); + pthread_exit(NULL); + } + if ( pthread_create( &x->x_decodechild, &decode_child_attr, pdp_decode_file, x ) < 0 ) + { + post( "pdp_theorin~ : could not launch decoding thread" ); + perror( "pthread_create" ); + pthread_exit(NULL); + } + else + { + // post( "pdp_theorin~ : decoding thread %d launched", (int)x->x_decodechild ); + } + } + + if ( stat( x->x_filename, &fileinfos ) < 0 ) + { + post("pdp_theorin~ : couldn't get file informations" ); + perror( "stat" ); + } + else + { + outlet_float( x->x_outlet_filesize, (fileinfos.st_size)/1024 ); + } + + if ( gettimeofday(&x->x_starttime, NULL) == -1) + { + post("pdp_theorin~ : could not set start time" ); + } + + x->x_nbframes = 0; + x->x_endoffile = -1; + + return; +} + +static void pdp_theorin_frame_cold(t_pdp_theorin *x, t_floatarg kbytes) +{ + int pos = (int)kbytes; + int ret; + + if (x->x_infile==NULL) return; + + pdp_theorin_open(x, gensym(x->x_filename)); + + // it's very approximative, we're are positioning the file on the number of requested kilobytes + if ( fseek( x->x_infile, pos*1024, SEEK_SET ) < 0 ) + { + post( "pdp_theorin~ : could not set file at that position (%d kilobytes)", pos ); + perror( "fseek" ); + return; + } + // post( "pdp_theorin~ : file seeked at %d kilobytes", pos ); +} + + /* decode the audio buffer */ +static t_int *pdp_theorin_perform(t_int *w) +{ + t_float *out1 = (t_float *)(w[1]); // left audio inlet + t_float *out2 = (t_float *)(w[2]); // right audio inlet + t_pdp_theorin *x = (t_pdp_theorin *)(w[3]); + int n = (int)(w[4]); // number of samples + struct timeval etime; + t_int sn; + + // decode a packet if not in thread mode + if ( !x->x_usethread && x->x_reading ) + { + pdp_theorin_decode_packet( x ); + } + + x->x_blocksize = n; + + // just read the buffer + if ( x->x_audioon ) + { + sn=0; + while (n--) + { + *(out1)=x->x_audio_inl[ sn ]; + if ( x->x_audiochannels == 1 ) + { + *(out2) = *(out1); + sn++; + } + if ( x->x_audiochannels == 2 ) + { + *(out2)=x->x_audio_inr[ sn++ ]; + } + out1++; + out2++; + } + memcpy( &x->x_audio_inl[0], &x->x_audio_inl[sn], (x->x_audioin_position-sn)*sizeof(t_float) ); + memcpy( &x->x_audio_inr[0], &x->x_audio_inr[sn], (x->x_audioin_position-sn)*sizeof(t_float) ); + x->x_audioin_position-=sn; + // post( "pdp_theorin~ : audio in position : %d", x->x_audioin_position ); + if ( x->x_audioin_position <= sn ) + { + x->x_audioon = 0; + // post( "pdp_theorin~ : audio off ( audioin : %d, channels=%d )", + // x->x_audioin_position, x->x_audiochannels ); + } + } + else + { + // post("pdp_theorin~ : no available audio" ); + while (n--) + { + *(out1++) = 0.0; + *(out2++) = 0.0; + } + } + + // check if the framerate has been exceeded + if ( gettimeofday(&etime, NULL) == -1) + { + post("pdp_theorin~ : could not read time" ); + } + if ( etime.tv_sec != x->x_cursec ) + { + x->x_cursec = etime.tv_sec; + if (x->x_reading) outlet_float( x->x_outlet_framerate, x->x_secondcount ); + x->x_secondcount = 0; + } + + // output image if there's a new one decoded + if ( x->x_newpicture ) + { + pdp_packet_pass_if_valid(x->x_pdp_out, &x->x_packet0); + x->x_newpicture = 0; + + // update streaming status + x->x_nbframes++; + x->x_secondcount++; + outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + + } + if ( x->x_endoffile == 1 ) // only once + { + outlet_float( x->x_outlet_endoffile, x->x_endoffile ); + x->x_endoffile = 0; + } + if ( x->x_endoffile == -1 ) // reset + { + x->x_endoffile = 0; + outlet_float( x->x_outlet_endoffile, x->x_endoffile ); + } + + return (w+5); +} + +static void pdp_theorin_dsp(t_pdp_theorin *x, t_signal **sp) +{ + dsp_add(pdp_theorin_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + +static void pdp_theorin_free(t_pdp_theorin *x) +{ + int i; + + if ( x->x_decodechild ) + { + x->x_decodechild = 0; + } + + if ( x->x_reading ) + { + pdp_theorin_close(x); + } + + post( "pdp_theorin~ : freeing object" ); +} + +t_class *pdp_theorin_class; + +void *pdp_theorin_new(void) +{ + int i; + + t_pdp_theorin *x = (t_pdp_theorin *)pd_new(pdp_theorin_class); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("frame_cold")); + + x->x_pdp_out = outlet_new(&x->x_obj, &s_anything); + + x->x_outlet_left = outlet_new(&x->x_obj, &s_signal); + x->x_outlet_right = outlet_new(&x->x_obj, &s_signal); + + x->x_outlet_nbframes = outlet_new(&x->x_obj, &s_float); + x->x_outlet_framerate = outlet_new(&x->x_obj, &s_float); + x->x_outlet_endoffile = outlet_new(&x->x_obj, &s_float); + x->x_outlet_filesize = outlet_new(&x->x_obj, &s_float); + + x->x_packet0 = -1; + x->x_decodechild = 0; + x->x_decoding = 0; + x->x_theorainit = 0; + x->x_usethread = 1; + x->x_priority = DEFAULT_PRIORITY; + x->x_framerate = DEFAULT_FRAME_RATE; + x->x_nbframes = 0; + x->x_samplerate = 0; + x->x_audio = 1; + x->x_audiochannels = 0; + x->x_audioin_position = 0; + x->x_videoready = 0; + x->x_newpicture = 0; + x->x_newpictureready = 0; + x->x_endoffile = 0; + x->x_notpackets = 0; + x->x_novpackets = 0; + x->x_blocksize = MIN_AUDIO_SIZE; + x->x_autoplay = 1; + x->x_loop = 1; + x->x_nextimage = 0; + x->x_infile = NULL; + x->x_reading = 0; + + memset( &x->x_audio_inl[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(t_float) ); + memset( &x->x_audio_inr[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(t_float) ); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_theorin_tilde_setup(void) +{ + // post( pdp_theorin_version ); + pdp_theorin_class = class_new(gensym("pdp_theorin~"), (t_newmethod)pdp_theorin_new, + (t_method)pdp_theorin_free, sizeof(t_pdp_theorin), 0, A_NULL); + + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_dsp, gensym("dsp"), A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_open, gensym("open"), A_SYMBOL, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_close, gensym("close"), A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_priority, gensym("priority"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_audio, gensym("audio"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_autoplay, gensym("autoplay"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_loop, gensym("loop"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_threadify, gensym("thread"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_frame_cold, gensym("frame_cold"), A_FLOAT, A_NULL); + class_sethelpsymbol( pdp_theorin_class, gensym("pdp_theorin~.pd") ); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/pdp_theorout~.c b/modules/pdp_theorout~.c new file mode 100644 index 0000000..96d610a --- /dev/null +++ b/modules/pdp_theorout~.c @@ -0,0 +1,884 @@ +/* + * PiDiP module. + * Copyright (c) by Yves Degoyon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a theora file encoder object + * It uses libtheora and some of it code samples ( copyright xiph.org ) + * Copyleft by Yves Degoyon ( ydegoyon@free.fr ) + * + */ + + +#include "pdp.h" +#include +#include +#include +#include +#include +#include + +#define DEFAULT_FRAME_RATE 25 +#define MIN_VIDEO_QUALITY 0 +#define MAX_VIDEO_QUALITY 63 +#define DEFAULT_VIDEO_QUALITY 16 +#define MIN_VIDEO_BITRATE 45 +#define MAX_VIDEO_BITRATE 2000 +#define DEFAULT_VIDEO_BITRATE 96 +#define MIN_AUDIO_QUALITY -0.1 +#define MAX_AUDIO_QUALITY 1.0 +#define DEFAULT_AUDIO_QUALITY 0.5 +#define MIN_AUDIO_BITRATE 8 +#define MAX_AUDIO_BITRATE 2000 +#define DEFAULT_AUDIO_BITRATE 32 + +#define DEFAULT_CHANNELS 2 +#define DEFAULT_BITS 8 +#define MAX_AUDIO_PACKET_SIZE (128 * 1024) +// streams hard-coded serial numbers +#define STREAMV_SNO 0x987654 +#define STREAMA_SNO 0x456789 + +#ifndef _REENTRANT +# define _REENTRANT +#endif + +static char *pdp_theorout_version = "pdp_theorout~: version 0.1, a theora video/audio recording object, written by ydegoyon@free.fr"; + +typedef struct pdp_theorout_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + t_int x_packet0; + t_int x_packet1; + t_int x_dropped; + t_int x_queue_id; + + t_int x_vwidth; + t_int x_tvwidth; /* theora 16 pixels aligned width value */ + t_int x_vheight; + t_int x_tvheight; /* theora 16 pixels aligned height value */ + t_int x_vsize; + + FILE *x_tfile; + t_int x_framerate; + t_int x_newfile; + t_int x_einit; + t_int x_recflag; + t_int x_enduprec;; + t_int x_frameswritten; + t_int x_frames; + struct timeval x_tstart; + struct timeval x_tzero; + struct timeval x_tcurrent; + struct timeval x_tlastrec; + + /* vorbis/theora structures */ + ogg_page x_ogg_page; // ogg page + ogg_packet x_ogg_packet; // ogg packet + ogg_stream_state x_statev; // vorbis stream state + ogg_stream_state x_statet; // theora stream state + theora_info x_theora_info; // theora info + theora_comment x_theora_comment; // theora comment + theora_state x_theora_state; // theora state + vorbis_info x_vorbis_info; // vorbis info + vorbis_dsp_state x_dsp_state; // vorbis dsp state + vorbis_block x_vorbis_block; // vorbis block + vorbis_comment x_vorbis_comment; // vorbis comment + yuv_buffer x_yuvbuffer; // yuv buffer + + t_int x_akbps; // audio bit rate + t_int x_vkbps; // video bit rate + t_float x_aquality; // audio quality + t_int x_vquality; // video quality + t_int x_abytesout; // audio bytes written + t_int x_vbytesout; // video bytes written + + /* audio structures */ + t_float **x_audio_buf; /* buffer for incoming audio */ + t_int x_audioin_position; // writing position for incoming audio + t_int x_channels; // audio channels + t_int x_samplerate; // audio sample rate + t_int x_bits; // audio bits + +} t_pdp_theorout; + + /* allocate internal ressources */ +static void pdp_theorout_allocate(t_pdp_theorout *x) +{ + int ret; + + x->x_yuvbuffer.y_width=x->x_vwidth; + x->x_yuvbuffer.y_height=x->x_vheight; + x->x_yuvbuffer.y_stride=x->x_vwidth; + + x->x_yuvbuffer.uv_width=x->x_vwidth>>1; + x->x_yuvbuffer.uv_height=x->x_vheight>>1; + x->x_yuvbuffer.uv_stride=x->x_vwidth>>1; + + x->x_yuvbuffer.y = (char *)malloc( x->x_yuvbuffer.y_width * x->x_yuvbuffer.y_height ); + x->x_yuvbuffer.u = (char *)malloc( x->x_yuvbuffer.uv_width * x->x_yuvbuffer.uv_height ); + x->x_yuvbuffer.v = (char *)malloc( x->x_yuvbuffer.uv_width * x->x_yuvbuffer.uv_height ); +} + + /* free internal ressources */ +static void pdp_theorout_free_ressources(t_pdp_theorout *x) +{ + if ( x->x_yuvbuffer.y ) free( x->x_yuvbuffer.y ); + if ( x->x_yuvbuffer.u ) free( x->x_yuvbuffer.u ); + if ( x->x_yuvbuffer.v ) free( x->x_yuvbuffer.v ); +} + + /* initialize the encoder */ +static void pdp_theorout_init_encoder(t_pdp_theorout *x) +{ + t_int ret; + + x->x_einit=0; + + // init streams + ogg_stream_init(&x->x_statet, STREAMA_SNO); + ogg_stream_init(&x->x_statev, STREAMV_SNO); + + theora_info_init(&x->x_theora_info); + x->x_theora_info.width=x->x_tvwidth; + x->x_theora_info.height=x->x_tvheight; + x->x_theora_info.frame_width=x->x_vwidth; + x->x_theora_info.frame_height=x->x_vheight; + x->x_theora_info.offset_x=(x->x_tvwidth-x->x_vwidth)>>1; + x->x_theora_info.offset_y=(x->x_tvheight-x->x_vheight)>>1; + x->x_theora_info.fps_numerator=x->x_framerate; + x->x_theora_info.fps_denominator=1; + x->x_theora_info.aspect_numerator=x->x_vwidth; + x->x_theora_info.aspect_denominator=x->x_vheight; + x->x_theora_info.colorspace=OC_CS_UNSPECIFIED; + x->x_theora_info.target_bitrate=x->x_vkbps; + x->x_theora_info.quality=x->x_vquality; + + x->x_theora_info.dropframes_p=0; + x->x_theora_info.quick_p=1; + x->x_theora_info.keyframe_auto_p=1; + x->x_theora_info.keyframe_frequency=64; + x->x_theora_info.keyframe_frequency_force=64; + x->x_theora_info.keyframe_data_target_bitrate=x->x_vkbps*1.5; + x->x_theora_info.keyframe_auto_threshold=80; + x->x_theora_info.keyframe_mindistance=8; + x->x_theora_info.noise_sensitivity=1; + + theora_encode_init(&x->x_theora_state,&x->x_theora_info); + + vorbis_info_init(&x->x_vorbis_info); + + if(x->x_aquality > -0.1) + { + ret = vorbis_encode_init_vbr(&x->x_vorbis_info, x->x_channels, x->x_samplerate, x->x_aquality); + } + else + { + ret = vorbis_encode_init(&x->x_vorbis_info, x->x_channels, x->x_samplerate, -1, x->x_akbps, -1); + } + + if (ret) + { + post( "pdp_theorout~ : could not initialize vorbis encoder" ); + x->x_einit=0; + return; + } + + vorbis_comment_init(&x->x_vorbis_comment); + vorbis_analysis_init(&x->x_dsp_state,&x->x_vorbis_info); + vorbis_block_init(&x->x_dsp_state,&x->x_vorbis_block); + + post( "pdp_theorout~ : encoder initialized." ); + x->x_einit=1; + +} + +static void pdp_theorout_write_headers(t_pdp_theorout *x) +{ + t_int ret; + ogg_packet aheader, aheadercomm, aheadercode; + + if ( !x->x_einit ) + { + post( "pdp_theorout~ : trying to write headers but encoder is not initialized." ); + return; + } + + if ( x->x_tfile == NULL ) + { + post( "pdp_theorout~ : trying to write headers but no file is opened." ); + return; + } + + theora_encode_header(&x->x_theora_state, &x->x_ogg_packet); + ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet); + if(ogg_stream_pageout(&x->x_statet, &x->x_ogg_page)!=1) + { + post( "pdp_theorout~ : ogg encoding error." ); + return; + } + if ( ( ret = fwrite(x->x_ogg_page.header, 1, x->x_ogg_page.header_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + return; + } + if ( ( ret = fwrite(x->x_ogg_page.body, 1, x->x_ogg_page.body_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + return; + } + + theora_comment_init(&x->x_theora_comment); + theora_encode_comment(&x->x_theora_comment, &x->x_ogg_packet); + ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet); + theora_encode_tables(&x->x_theora_state, &x->x_ogg_packet); + ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet); + + vorbis_analysis_headerout(&x->x_dsp_state, &x->x_vorbis_comment, + &aheader,&aheadercomm,&aheadercode); + ogg_stream_packetin(&x->x_statev,&aheader); + + if(ogg_stream_pageout(&x->x_statev, &x->x_ogg_page)!=1) + { + post( "pdp_theorout~ : ogg encoding error." ); + return; + } + if ( ( ret = fwrite(x->x_ogg_page.header, 1, x->x_ogg_page.header_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + return; + } + if ( ( ret = fwrite(x->x_ogg_page.body, 1, x->x_ogg_page.body_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + return; + } + + // remaining vorbis header packets + ogg_stream_packetin(&x->x_statev, &aheadercomm); + ogg_stream_packetin(&x->x_statev, &aheadercode); + + // flush all the headers + while(1) + { + ret = ogg_stream_flush(&x->x_statet, &x->x_ogg_page); + if(ret<0){ + post( "pdp_theorout~ : ogg encoding error." ); + return; + } + if(ret==0)break; + if ( ( ret = fwrite(x->x_ogg_page.header, 1, x->x_ogg_page.header_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + return; + } + if ( ( ret = fwrite(x->x_ogg_page.body, 1, x->x_ogg_page.body_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + return; + } + } + + while(1) + { + ret = ogg_stream_flush(&x->x_statev, &x->x_ogg_page); + if(ret<0){ + post( "pdp_theorout~ : ogg encoding error." ); + return; + } + if(ret==0)break; + if ( ( ret = fwrite(x->x_ogg_page.header, 1, x->x_ogg_page.header_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + return; + } + if ( ( ret = fwrite(x->x_ogg_page.body, 1, x->x_ogg_page.body_len, x->x_tfile) ) <= 0 ) + { + post( "pdp_theorout~ : could not write headers (ret=%d).", ret ); + perror( "fwrite" ); + return; + } + } +} + + /* terminate the encoding process */ +static void pdp_theorout_shutdown_encoder(t_pdp_theorout *x) +{ + ogg_stream_clear(&x->x_statev); + vorbis_block_clear(&x->x_vorbis_block); + vorbis_dsp_clear(&x->x_dsp_state); + vorbis_comment_clear(&x->x_vorbis_comment); + vorbis_info_clear(&x->x_vorbis_info); + ogg_stream_clear(&x->x_statet); + theora_clear(&x->x_theora_state); +} + + + /* close a video file */ +static void pdp_theorout_close(t_pdp_theorout *x) +{ + int ret; + + if ( x->x_tfile ) + { + if ( fclose( x->x_tfile ) < 0 ) + { + post( "pdp_theorout~ : could not close output file" ); + perror( "fclose" ); + } + x->x_tfile = NULL; + } +} + + /* open a new video file */ +static void pdp_theorout_open(t_pdp_theorout *x, t_symbol *sfile) +{ + t_int ret=0; + + // close previous video file if existing + pdp_theorout_close(x); + + if ( x->x_recflag ) { + x->x_recflag = 0; + } + x->x_frameswritten = 0; + + if ( ( x->x_tfile = fopen( sfile->s_name, "w+" ) ) == NULL ) + { + post( "pdp_theorout~ : could not open output file" ); + perror( "fopen" ); + return; + } + else + { + post( "pdp_theorout~ : opened >%s<", sfile->s_name); + } + x->x_newfile = 1; + +} + + /* start recording */ +static void pdp_theorout_start(t_pdp_theorout *x) +{ + if ( !x->x_tfile ) + { + post("pdp_theorout~ : start received but no file has been opened ... ignored."); + return; + } + + if ( x->x_recflag == 1 ) + { + post("pdp_theorout~ : start received but recording is started ... ignored."); + return; + } + + if ( gettimeofday(&x->x_tstart, NULL) == -1) + { + post("pdp_theorout~ : could not set start time" ); + } + + x->x_recflag = 1; + pdp_theorout_init_encoder( x ); + pdp_theorout_write_headers( x ); + post("pdp_theorout~ : start recording at %d frames/second", x->x_framerate); +} + + /* stop recording */ +static void pdp_theorout_stop(t_pdp_theorout *x) +{ + if ( !x->x_tfile ) + { + post("pdp_theorout~ : stop received but no file has been opened ... ignored."); + return; + } + + if ( x->x_recflag == 0 ) + { + post("pdp_theorout~ : stop received but recording is stopped ... ignored."); + return; + } + + x->x_recflag = 0; + + // record last packet + x->x_enduprec = 1; + +} + + /* set video bitrate */ +static void pdp_theorout_vbitrate(t_pdp_theorout *x, t_floatarg vbitrate ) +{ + if ( ( (t_int) vbitrate < MIN_VIDEO_BITRATE ) || ( (t_int) vbitrate > MAX_VIDEO_BITRATE ) ) + { + post( "pdp_theorout~ : wrong video bitrate %d : should be in [%d,%d] kbps", + (t_int) vbitrate, MIN_VIDEO_BITRATE, MAX_VIDEO_BITRATE ); + return; + } + x->x_vkbps = (t_int) vbitrate; +} + + /* set audio bitrate */ +static void pdp_theorout_abitrate(t_pdp_theorout *x, t_floatarg abitrate ) +{ + if ( ( (t_int) abitrate < MIN_AUDIO_BITRATE ) || ( (t_int) abitrate > MAX_AUDIO_BITRATE ) ) + { + post( "pdp_theorout~ : wrong audio bitrate %d : should be in [%d,%d] kbps", + (t_int) abitrate, MIN_AUDIO_BITRATE, MAX_AUDIO_BITRATE ); + return; + } + x->x_akbps = (t_int) abitrate; +} + + /* set video quality */ +static void pdp_theorout_vquality(t_pdp_theorout *x, t_floatarg vquality ) +{ + if ( ( (t_int) vquality < MIN_VIDEO_QUALITY ) || ( (t_int) vquality > MAX_VIDEO_QUALITY ) ) + { + post( "pdp_theorout~ : wrong video quality %d : should be in [%d,%d]", + (t_int) vquality, MIN_VIDEO_QUALITY, MAX_VIDEO_QUALITY ); + return; + } + x->x_vquality = (t_int) vquality; +} + + /* set audio quality */ +static void pdp_theorout_aquality(t_pdp_theorout *x, t_floatarg aquality ) +{ + if ( ( (t_int) aquality < MIN_AUDIO_QUALITY ) || ( (t_int) aquality > MAX_AUDIO_QUALITY ) ) + { + post( "pdp_theorout~ : wrong audio quality %d : should be in [%d,%d]", + (t_int) aquality, MIN_AUDIO_QUALITY, MAX_AUDIO_QUALITY ); + return; + } + x->x_aquality = (t_int) aquality; +} + + /* store audio data in PCM format in a buffer for now */ +static t_int *pdp_theorout_perform(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); // left audio inlet + t_float *in2 = (t_float *)(w[2]); // right audio inlet + t_pdp_theorout *x = (t_pdp_theorout *)(w[3]); + int n = (int)(w[4]); // number of samples + t_float fsample; + t_int isample, i; + + if ( x->x_recflag ) + { + // just fills the buffer + while (n--) + { + fsample=*(in1++); + if (fsample > 1.0) { fsample = 1.0; } + if (fsample < -1.0) { fsample = -1.0; } + x->x_audio_buf[0][x->x_audioin_position]=fsample; + fsample=*(in2++); + if (fsample > 1.0) { fsample = 1.0; } + if (fsample < -1.0) { fsample = -1.0; } + x->x_audio_buf[1][x->x_audioin_position]=fsample; + x->x_audioin_position=(x->x_audioin_position+1)%(MAX_AUDIO_PACKET_SIZE); + if ( x->x_audioin_position == MAX_AUDIO_PACKET_SIZE-1 ) + { + post( "pdp_theorout~ : reaching end of audio buffer" ); + } + } + } + + return (w+5); +} + +static void pdp_theorout_dsp(t_pdp_theorout *x, t_signal **sp) +{ + dsp_add(pdp_theorout_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + +static void pdp_theorout_process_yv12(t_pdp_theorout *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + unsigned char *data = (unsigned char *)pdp_packet_data(x->x_packet0); + t_int i, ret; + t_int px, py; + char *pY, *pU, *pV; + struct timeval trec; + t_int nbaudiosamples, nbusecs, nbrecorded; + t_float fframerate=0.0; + t_int precflag; + ogg_page apage; + ogg_page vpage; + t_float **vbuffer; + double videotime, audiotime; + + if ( ( (int)(header->info.image.width) != x->x_vwidth ) || + ( (int)(header->info.image.height) != x->x_vheight ) || + ( x->x_newfile ) ) + { + precflag = x->x_recflag; + x->x_recflag = 0; + pdp_theorout_free_ressources( x ); + pdp_theorout_shutdown_encoder( x ); + x->x_vwidth = header->info.image.width; + x->x_vheight = header->info.image.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + x->x_tvwidth=((x->x_vwidth + 15) >>4)<<4; + x->x_tvheight=((x->x_vheight + 15) >>4)<<4; + pdp_theorout_allocate( x ); + if ( x->x_tzero.tv_sec != 0 ) + { + pdp_theorout_init_encoder( x ); + pdp_theorout_write_headers( x ); + } + x->x_recflag = precflag; + x->x_newfile = 0; + } + + if ( x->x_tzero.tv_sec == 0 ) + { + if ( gettimeofday(&x->x_tzero, NULL) == -1) + { + post("pdp_theorout~ : could get initial time" ); + } + } + + x->x_frames++; + + // calculate current framerate + if ( gettimeofday(&x->x_tcurrent, NULL) == -1) + { + post("pdp_theorout~ : could get current time" ); + } + + // calculate frame rate if it hasn't been set + if ( ( x->x_tcurrent.tv_sec - x->x_tzero.tv_sec ) > 0 ) + { + x->x_framerate = x->x_frames / ( x->x_tcurrent.tv_sec - x->x_tzero.tv_sec ); + } + else + { + x->x_framerate = DEFAULT_FRAME_RATE; + } + + if ( x->x_frameswritten == 0 ) + { + if ( gettimeofday(&x->x_tlastrec, NULL) == -1) + { + post("pdp_theorout~ : could set start time" ); + } + } + + pY = x->x_yuvbuffer.y; + memcpy( (void*)pY, (void*)&data[0], x->x_vsize ); + pV = x->x_yuvbuffer.v; + memcpy( (void*)pV, (void*)&data[x->x_vsize], (x->x_vsize>>2) ); + pU = x->x_yuvbuffer.u; + memcpy( (void*)pU, (void*)&data[x->x_vsize+(x->x_vsize>>2)], (x->x_vsize>>2) ); + + if ( x->x_tfile && x->x_recflag && !x->x_enduprec) + { + + if ( ( ret = theora_encode_YUVin( &x->x_theora_state, &x->x_yuvbuffer ) ) != 0 ) + { + post( "pdp_theorout~ : could not encode yuv image (ret=%d).", ret ); + } + else + { + // stream one packet + theora_encode_packetout(&x->x_theora_state, 0, &x->x_ogg_packet); + ogg_stream_packetin(&x->x_statet, &x->x_ogg_packet); + // post( "pdp_theorout~ : new (theora) ogg packet : bytes:%ld, bos:%ld, eos:%ld, no:%lld", + // x->x_ogg_packet.bytes, x->x_ogg_packet.b_o_s, + // x->x_ogg_packet.e_o_s, x->x_ogg_packet.packetno ); + + while( ( ret = ogg_stream_pageout(&x->x_statet, &vpage) ) >0 ) + { + videotime = theora_granule_time(&x->x_theora_state, ogg_page_granulepos(&vpage)); + x->x_vbytesout+=fwrite(vpage.header, 1, vpage.header_len, x->x_tfile ); + x->x_vbytesout+=fwrite(vpage.body, 1, vpage.body_len, x->x_tfile ); + } + } + + // calculate the number of audio samples to output + if ( gettimeofday(&trec, NULL) == -1) + { + post("pdp_theorout~ : could set stop time" ); + } + // calculate time diff in micro seconds + nbusecs = ( trec.tv_usec - x->x_tlastrec.tv_usec ) + + ( trec.tv_sec - x->x_tlastrec.tv_sec )*1000000; + nbaudiosamples = (sys_getsr()*1000000)/nbusecs; + memcpy( &x->x_tlastrec, &trec, sizeof( struct timeval) ); + + if ( x->x_audioin_position > nbaudiosamples ) + { + nbrecorded = nbaudiosamples; + } + else + { + nbrecorded = x->x_audioin_position; + } + + vbuffer=vorbis_analysis_buffer( &x->x_dsp_state, nbrecorded ); + memcpy( (void*)&vbuffer[0][0], (void*)&x->x_audio_buf[0][0], nbrecorded*sizeof( t_float ) ); + memcpy( (void*)&vbuffer[1][0], (void*)&x->x_audio_buf[1][0], nbrecorded*sizeof( t_float ) ); + + vorbis_analysis_wrote( &x->x_dsp_state, nbrecorded); + + while(vorbis_analysis_blockout( &x->x_dsp_state, &x->x_vorbis_block)==1) + { + + // analysis, assume we want to use bitrate management + vorbis_analysis( &x->x_vorbis_block, NULL); + vorbis_bitrate_addblock( &x->x_vorbis_block ); + + // weld packets into the bitstream + while(vorbis_bitrate_flushpacket( &x->x_dsp_state, &x->x_ogg_packet)) + { + ogg_stream_packetin( &x->x_statev, &x->x_ogg_packet); + } + + } + + while( ogg_stream_pageout( &x->x_statev, &apage) >0 ) + { + audiotime = vorbis_granule_time(&x->x_dsp_state, ogg_page_granulepos(&apage)); + x->x_abytesout+=fwrite(apage.header, 1, apage.header_len, x->x_tfile ); + x->x_abytesout+=fwrite(apage.body, 1, apage.body_len, x->x_tfile ); + } + + memcpy( &x->x_audio_buf[0][0], &x->x_audio_buf[0][nbrecorded], + ( x->x_audioin_position-nbrecorded ) * sizeof( t_float ) ); + memcpy( &x->x_audio_buf[1][0], &x->x_audio_buf[1][nbrecorded], + ( x->x_audioin_position-nbrecorded ) * sizeof( t_float ) ); + x->x_audioin_position -= nbrecorded; + // post ( "pdp_theorout~ : recorded %d samples.", nbrecorded ); + + x->x_frameswritten++; + + } + + if ( x->x_tfile && x->x_enduprec ) + { + x->x_enduprec = 0; + post( "pdp_theorout~ : ending up recording." ); + x->x_frameswritten++; + + if ( ( ret = theora_encode_YUVin( &x->x_theora_state, &x->x_yuvbuffer ) ) != 0 ) + { + post( "pdp_theorout~ : could not encode yuv image (ret=%d).", ret ); + } + else + { + // stream one packet + theora_encode_packetout(&x->x_theora_state, 1, &x->x_ogg_packet); + ogg_stream_packetin( &x->x_statet, &x->x_ogg_packet); + + while( ( ret = ogg_stream_pageout( &x->x_statet, &vpage) ) > 0 ) + { + videotime = theora_granule_time(&x->x_theora_state, ogg_page_granulepos(&vpage)); + x->x_vbytesout+=fwrite(vpage.header, 1, vpage.header_len, x->x_tfile ); + x->x_vbytesout+=fwrite(vpage.body, 1, vpage.body_len, x->x_tfile ); + } + } + + // end up audio stream + vorbis_analysis_wrote( &x->x_dsp_state, 0); + + while(vorbis_analysis_blockout( &x->x_dsp_state, &x->x_vorbis_block)==1) + { + // analysis, assume we want to use bitrate management + vorbis_analysis( &x->x_vorbis_block, NULL); + vorbis_bitrate_addblock( &x->x_vorbis_block); + + // weld packets into the bitstream + while(vorbis_bitrate_flushpacket( &x->x_dsp_state, &x->x_ogg_packet)) + { + ogg_stream_packetin( &x->x_statev, &x->x_ogg_packet); + } + } + + while( ogg_stream_pageout( &x->x_statev, &apage) >0 ) + { + audiotime = vorbis_granule_time(&x->x_dsp_state, ogg_page_granulepos(&apage)); + x->x_abytesout+=fwrite(apage.header, 1, apage.header_len, x->x_tfile ); + x->x_abytesout+=fwrite(apage.body, 1, apage.body_len, x->x_tfile ); + } + + post("pdp_theorout~ : stop recording"); + + pdp_theorout_shutdown_encoder( x ); + pdp_theorout_close(x); + } + + return; +} + +static void pdp_theorout_killpacket(t_pdp_theorout *x) +{ + /* release the packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; +} + +static void pdp_theorout_process(t_pdp_theorout *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_BITMAP == header->type)){ + + /* pdp_theorout_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding) + { + + case PDP_BITMAP_YV12: + if ( x->x_tfile && x->x_recflag ) + { + outlet_float( x->x_obj.ob_outlet, x->x_frameswritten ); + } + pdp_queue_add(x, pdp_theorout_process_yv12, pdp_theorout_killpacket, &x->x_queue_id); + break; + + default: + /* don't know the type, so dont pdp_theorout_process */ + break; + + } + } + +} + +static void pdp_theorout_input_0(t_pdp_theorout *x, t_symbol *s, t_floatarg f) +{ + /* if this is a register_ro message or register_rw message, register with packet factory */ + + if (s== gensym("register_rw")) + { + x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("bitmap/yv12/*") ); + } + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)) + { + /* add the process method and callback to the process queue */ + pdp_theorout_process(x); + } + +} + +static void pdp_theorout_free(t_pdp_theorout *x) +{ + int i; + + pdp_queue_finish(x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + // close video file if existing + pdp_theorout_close(x); + for ( i=0; ix_channels; i++) + { + if ( x->x_audio_buf[i] ) freebytes( x->x_audio_buf[i], MAX_AUDIO_PACKET_SIZE*sizeof(t_float) ); + } + if ( x->x_audio_buf ) freebytes( x->x_audio_buf, x->x_channels*sizeof(t_float*) ); + +} + +t_class *pdp_theorout_class; + +void *pdp_theorout_new(void) +{ + t_int i; + + t_pdp_theorout *x = (t_pdp_theorout *)pd_new(pdp_theorout_class); + inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); + outlet_new (&x->x_obj, &s_float); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_queue_id = -1; + + x->x_tfile = NULL; + x->x_yuvbuffer.y = NULL; + x->x_yuvbuffer.u = NULL; + x->x_yuvbuffer.v = NULL; + + /* audio defaults */ + x->x_samplerate = sys_getsr(); + x->x_channels = DEFAULT_CHANNELS; + x->x_bits = DEFAULT_BITS; + + x->x_framerate = DEFAULT_FRAME_RATE; + x->x_vkbps = DEFAULT_VIDEO_BITRATE; + x->x_vquality = DEFAULT_VIDEO_QUALITY; + x->x_akbps = DEFAULT_AUDIO_BITRATE; + x->x_aquality = DEFAULT_AUDIO_QUALITY; + + x->x_audio_buf = (t_float**) getbytes( x->x_channels*sizeof(t_float*) ); + for ( i=0; ix_channels; i++) + { + x->x_audio_buf[i] = (t_float*) getbytes( MAX_AUDIO_PACKET_SIZE*sizeof(t_float) ); + } + + x->x_newfile = 0; + x->x_frames = 0; + x->x_frameswritten = 0; + + x->x_tzero.tv_sec = 0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_theorout_tilde_setup(void) +{ + // post( pdp_theorout_version ); + pdp_theorout_class = class_new(gensym("pdp_theorout~"), (t_newmethod)pdp_theorout_new, + (t_method)pdp_theorout_free, sizeof(t_pdp_theorout), 0, A_NULL); + + CLASS_MAINSIGNALIN(pdp_theorout_class, t_pdp_theorout, x_f ); + class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_dsp, gensym("dsp"), 0); + class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_open, gensym("open"), A_SYMBOL, A_NULL); + class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_close, gensym("close"), A_NULL); + class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_start, gensym("start"), A_NULL); + class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_stop, gensym("stop"), A_NULL); + class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_abitrate, gensym("audiobitrate"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_vbitrate, gensym("videobitrate"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_aquality, gensym("audioquality"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorout_class, (t_method)pdp_theorout_vquality, gensym("videoquality"), A_FLOAT, A_NULL); + class_sethelpsymbol( pdp_theorout_class, gensym("pdp_theorout~.pd") ); + +} + +#ifdef __cplusplus +} +#endif -- cgit v1.2.1