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