From a9cfc5852d75ca1c62e4c97e3334c99ee2f2f9a8 Mon Sep 17 00:00:00 2001 From: "N.N." Date: Sun, 14 Nov 2004 23:05:44 +0000 Subject: PiDiP 0.12.17 svn path=/trunk/externals/pidip/; revision=2273 --- modules/pdp_theorin~.c | 970 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 970 insertions(+) create mode 100644 modules/pdp_theorin~.c (limited to 'modules/pdp_theorin~.c') diff --git a/modules/pdp_theorin~.c b/modules/pdp_theorin~.c new file mode 100644 index 0000000..2a7c72b --- /dev/null +++ b/modules/pdp_theorin~.c @@ -0,0 +1,970 @@ +/* + * PiDiP module. + * Copyright (c) by Yves Degoyon (ydegoyon@free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* This object is a theora file decoder object + * It uses libtheora and some of it code samples ( copyright xiph.org ) + * Copyleft by Yves Degoyon ( ydegoyon@free.fr ) + */ + + +#include "pdp.h" +#include "yuv.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* theora stuff */ +#include /* vorbis stuff */ + +#define VIDEO_BUFFER_SIZE (1024*1024) +#define MAX_AUDIO_PACKET_SIZE (64 * 1024) +#define MIN_AUDIO_SIZE (64*1024) + +#define DEFAULT_CHANNELS 1 +#define DEFAULT_WIDTH 320 +#define DEFAULT_HEIGHT 240 +#define DEFAULT_FRAME_RATE 25 +#define END_OF_STREAM 20 +#define MIN_PRIORITY 0 +#define DEFAULT_PRIORITY 1 +#define MAX_PRIORITY 20 + +#define THEORA_NUM_HEADER_PACKETS 3 + +static char *pdp_theorin_version = "pdp_theorin~: version 0.1, a theora file reader ( ydegoyon@free.fr)."; + +typedef struct pdp_theorin_struct +{ + t_object x_obj; + t_float x_f; + + t_int x_packet0; + t_int x_dropped; + + t_pdp *x_header; + unsigned char *x_data; + t_int x_vwidth; + t_int x_vheight; + t_int x_vsize; + + t_outlet *x_pdp_out; // output decoded pdp packets + t_outlet *x_outlet_left; // left audio output + t_outlet *x_outlet_right; // right audio output + t_outlet *x_outlet_nbframes; // number of frames emitted + t_outlet *x_outlet_framerate; // real framerate + t_outlet *x_outlet_endoffile; // for signaling the end of the file + t_outlet *x_outlet_filesize; // for informing of the file size + + pthread_t x_decodechild; // file decoding thread + t_int x_usethread; // flag to activate decoding in a thread + t_int x_autoplay; // flag to autoplay the file ( default = true ) + t_int x_nextimage; // flag to play next image in manual mode + t_int x_priority; // priority of decoding thread + + char *x_filename; + FILE *x_infile; // file descriptor + t_int x_decoding; // decoding flag + t_int x_theorainit; // flag for indicating that theora is initialized + t_int x_videoready; // video ready flag + t_int x_newpicture; // new picture flag + t_int x_newpictureready;// new picture ready flag + t_int x_loop; // looping flag ( default = on ) + t_int x_notpackets; // number of theora packets decoded + t_int x_novpackets; // number of vorbis packets decoded + t_int x_endoffile; // end of the file reached + t_int x_nbframes; // number of frames emitted + t_int x_framerate; // framerate + t_int x_samplerate; // audio sample rate + t_int x_audiochannels; // audio channels + t_int x_blocksize; // audio block size + t_int x_audioon; // audio buffer filling flag + t_int x_reading; // file reading flag + t_int x_cursec; // current second + t_int x_secondcount; // number of frames received in the current second + struct timeval x_starttime; // reading starting time + + /* vorbis/theora structures */ + ogg_sync_state x_sync_state; // ogg sync state + ogg_page x_ogg_page; // ogg page + ogg_packet x_ogg_packet; // ogg packet + ogg_stream_state x_statev; // vorbis stream state + ogg_stream_state x_statet; // theora stream state + theora_info x_theora_info; // theora info + theora_comment x_theora_comment; // theora comment + theora_state x_theora_state; // theora state + vorbis_info x_vorbis_info; // vorbis info + vorbis_dsp_state x_dsp_state; // vorbis dsp state + vorbis_block x_vorbis_block; // vorbis block + vorbis_comment x_vorbis_comment; // vorbis comment + yuv_buffer x_yuvbuffer; // yuv buffer + + /* audio structures */ + t_int x_audio; // flag to activate the decoding of audio + t_float x_audio_inl[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for float audio decoded from ogg */ + t_float x_audio_inr[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for float audio decoded from ogg */ + t_int x_audioin_position; // writing position for incoming audio + +} t_pdp_theorin; + +static void pdp_theorin_priority(t_pdp_theorin *x, t_floatarg fpriority ) +{ + if ( ( x->x_priority >= MIN_PRIORITY ) && ( x->x_priority <= MAX_PRIORITY ) ) + { + x->x_priority = (int)fpriority; + } +} + +static void pdp_theorin_threadify(t_pdp_theorin *x, t_floatarg fusethread ) +{ + if ( ( fusethread == 0 ) || ( fusethread == 1 ) ) + { + x->x_usethread = (int)fusethread; + } +} + +static void pdp_theorin_audio(t_pdp_theorin *x, t_floatarg faudio ) +{ + if ( ( faudio == 0. ) || ( faudio == 1. ) ) + { + x->x_audio = (int)faudio; + } +} + +static void pdp_theorin_autoplay(t_pdp_theorin *x, t_floatarg fautoplay ) +{ + if ( ( fautoplay == 0. ) || ( fautoplay == 1. ) ) + { + x->x_autoplay = (int)fautoplay; + } +} + +static void pdp_theorin_loop(t_pdp_theorin *x, t_floatarg floop ) +{ + if ( ( floop == 0. ) || ( floop == 1. ) ) + { + x->x_loop = (int)floop; + } +} + +static void pdp_theorin_bang(t_pdp_theorin *x) +{ + if ( x->x_nextimage == 1 ) + { + // post( "pdp_theorin~ : banging too fast, previous image is not decoded yet... ignored" ); + return; + } + x->x_nextimage = 1; +} + +static t_int pdp_theorin_get_buffer_from_file(FILE *in, ogg_sync_state *oy) +{ + char *buffer; + t_int bytes; + + buffer=ogg_sync_buffer(oy,4096); + bytes=fread(buffer,1,4096,in); + ogg_sync_wrote(oy,bytes); + return(bytes); +} + +static t_int pdp_theorin_queue_page(t_pdp_theorin *x) +{ + if(x->x_notpackets) ogg_stream_pagein(&x->x_statet, &x->x_ogg_page); + if(x->x_novpackets) ogg_stream_pagein(&x->x_statev, &x->x_ogg_page); + return 0; +} + +static t_int pdp_theorin_decode_packet(t_pdp_theorin *x) +{ + int ret, count, maxsamples, samples, si=0, sj=0; + float **pcm; + struct timespec mwait; + struct timeval ctime; + long long tplaying; + long long ttheoretical; + unsigned char *pY, *pU, *pV; + unsigned char *psY, *psU, *psV; + t_int px, py; + + // post( "pdp_theorin~ : decode packet" ); + + if ( !x->x_reading ) return -1; + + while ( x->x_novpackets && !x->x_audioon ) + { + /* if there's pending, decoded audio, grab it */ + if((ret=vorbis_synthesis_pcmout(&x->x_dsp_state, &pcm))>0) + { + if (x->x_audio) + { + maxsamples=(3*MAX_AUDIO_PACKET_SIZE-x->x_audioin_position); + samples=(retx_audioin_position + samples*x->x_audiochannels < 3*MAX_AUDIO_PACKET_SIZE ) + { + memcpy( (void*)&x->x_audio_inl[x->x_audioin_position], pcm[0], samples*sizeof(t_float) ); + memcpy( (void*)&x->x_audio_inr[x->x_audioin_position], pcm[1], samples*sizeof(t_float) ); + x->x_audioin_position = ( x->x_audioin_position + samples ) % (4*MAX_AUDIO_PACKET_SIZE); + } + else + { + post( "pdp_theorin~ : audio overflow : packet ignored..."); + x->x_audioin_position = 0; + } + if ( ( x->x_audioin_position > MIN_AUDIO_SIZE ) && (!x->x_audioon) ) + { + x->x_audioon = 1; + // post( "pdp_theorin~ : audio on (audioin=%d)", x->x_audioin_position ); + } + // tell vorbis how many samples were read + // post( "pdp_theorin~ : got %d audio samples (audioin=%d)", samples, x->x_audioin_position ); + vorbis_synthesis_read(&x->x_dsp_state, samples); + } + else + { + break; + } + } + else + { + // no pending audio: is there a pending packet to decode? + if( ogg_stream_packetout(&x->x_statev, &x->x_ogg_packet)>0 ) + { + if(vorbis_synthesis(&x->x_vorbis_block, &x->x_ogg_packet)==0) + { + vorbis_synthesis_blockin(&x->x_dsp_state, &x->x_vorbis_block); + } + } + else /* we need more data; suck in another page */ + { + break; + } + } + } + + if ( !x->x_newpictureready && !x->x_newpicture ) + { + while(x->x_notpackets && !x->x_videoready) + { + // theora is one in, one out... + if(ogg_stream_packetout(&x->x_statet, &x->x_ogg_packet)>0) + { + theora_decode_packetin(&x->x_theora_state, &x->x_ogg_packet); + // post( "pdp_theorin~ : got one video frame" ); + x->x_videoready=1; + } + else + { + // post( "pdp_theorin~ : no more video frame (frames=%d)", x->x_nbframes ); + if ( x->x_nbframes > 0 ) + { + if ( !x->x_loop ) + { + x->x_endoffile = 1; + } + else + { + // restart a new loop + if ( gettimeofday(&x->x_starttime, NULL) == -1) + { + post("pdp_theorin~ : could not set start time" ); + } + } + x->x_nbframes = 0; + x->x_audioin_position = 0; // reset audio + x->x_audioon = 0; + } + break; + } + } + + if ( x->x_videoready ) + { + theora_decode_YUVout(&x->x_theora_state, &x->x_yuvbuffer); + + // create a new pdp packet from PIX_FMT_YUV420P image format + x->x_vwidth = x->x_yuvbuffer.y_width; + x->x_vheight = x->x_yuvbuffer.y_height; + x->x_vsize = x->x_vwidth*x->x_vheight; + x->x_packet0 = pdp_packet_new_bitmap_yv12( x->x_vwidth, x->x_vheight ); + // post( "pdp_theorin~ : allocated packet %d", x->x_packet0 ); + x->x_header = pdp_packet_header(x->x_packet0); + x->x_data = (unsigned char*) pdp_packet_data(x->x_packet0); + + x->x_header->info.image.encoding = PDP_BITMAP_YV12; + x->x_header->info.image.width = x->x_vwidth; + x->x_header->info.image.height = x->x_vheight; + + pY = x->x_data; + pV = x->x_data+x->x_vsize; + pU = x->x_data+x->x_vsize+(x->x_vsize>>2); + + psY = x->x_yuvbuffer.y; + psU = x->x_yuvbuffer.u; + psV = x->x_yuvbuffer.v; + + for ( py=0; pyx_vheight; py++) + { + memcpy( (void*)pY, (void*)psY, x->x_vwidth ); + pY += x->x_vwidth; + psY += x->x_yuvbuffer.y_stride; + if ( py%2==0 ) + { + memcpy( (void*)pU, (void*)psU, (x->x_vwidth>>1) ); + memcpy( (void*)pV, (void*)psV, (x->x_vwidth>>1) ); + pU += (x->x_vwidth>>1); + pV += (x->x_vwidth>>1); + psU += x->x_yuvbuffer.uv_stride; + psV += x->x_yuvbuffer.uv_stride; + } + } + if ( !x->x_autoplay ) + { + x->x_newpicture = 1; + } + else + { + x->x_newpictureready = 1; + } + } + } + + if ( x->x_newpictureready ) + { + if ( gettimeofday(&ctime, NULL) == -1) + { + post("pdp_theorin~ : could not read time" ); + } + + tplaying = ( ctime.tv_sec-x->x_starttime.tv_sec )*1000 + + ( ctime.tv_usec-x->x_starttime.tv_usec )/1000; + ttheoretical = ((x->x_nbframes)*1000 )/x->x_framerate; + // post( "pdp-theorin~ : %d playing since : %lldms ( theory : %lldms )", + // x->x_nbframes, tplaying, ttheoretical ); + + if ( ttheoretical <= tplaying ) + { + x->x_newpicture = 1; + x->x_newpictureready = 0; + } + + } + + // check end of file + if(!x->x_videoready && feof(x->x_infile)) + { + if ( x->x_loop ) + { + if ( fseek( x->x_infile, 0x0, SEEK_SET ) < 0 ) + { + post( "pdp_theorin~ : could not reset file." ); + perror( "fseek" ); + } + } + } + + // read more data in + if( ( x->x_audioin_position < MIN_AUDIO_SIZE ) || ( !x->x_newpicture && !x->x_newpictureready ) ) + { + ret=pdp_theorin_get_buffer_from_file(x->x_infile, &x->x_sync_state); + // post( "pdp_theorin~ : read %d bytes from file", ret ); + while( ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page)>0 ) + { + pdp_theorin_queue_page(x); + } + } + + x->x_videoready = 0; + + return 0; + +} + +static void *pdp_decode_file(void *tdata) +{ + t_pdp_theorin *x = (t_pdp_theorin*)tdata; + struct sched_param schedprio; + t_int pmin, pmax, p1; + struct timespec twait; + + twait.tv_sec = 0; + twait.tv_nsec = 5000000; // 5 ms + + schedprio.sched_priority = sched_get_priority_min(SCHED_FIFO) + x->x_priority; + if ( sched_setscheduler(0, SCHED_FIFO, &schedprio) == -1) + { + post("pdp_theorin~ : couldn't set priority for decoding thread."); + } + + while ( x->x_decodechild ) + { + if ( ( x->x_reading ) && ( ( x->x_autoplay ) || ( x->x_nextimage == 1 ) ) ) + { + x->x_decoding = 1; + + // decode incoming packets + if ( x->x_reading ) pdp_theorin_decode_packet( x ); + nanosleep( &twait, NULL ); + x->x_nextimage = -1; + } + else + { + x->x_decoding = 0; + nanosleep( &twait, NULL ); // nothing to do, just wait + } + } + + x->x_decoding = 0; + post("pdp_theorin~ : decoding child exiting." ); + return NULL; +} + +static void pdp_theorin_close(t_pdp_theorin *x) +{ + t_int ret, i, count=0; + struct timespec twait; + + twait.tv_sec = 0; + twait.tv_nsec = 10000000; // 10 ms + + if ( x->x_infile == NULL ) + { + post("pdp_theorin~ : close request but no file is played ... ignored" ); + return; + } + + if ( x->x_reading ) + { + x->x_newpicture = 0; + x->x_reading = 0; + post("pdp_theorin~ : waiting end of decoding..." ); + while ( x->x_decoding ) nanosleep( &twait, NULL ); + + if ( fclose( x->x_infile ) < 0 ) + { + post( "pdp_theorin~ : could not close input file" ); + perror( "fclose" ); + } + x->x_infile = NULL; + + if ( x->x_notpackets > 0 ) + { + ogg_stream_clear(&x->x_statet); + theora_clear(&x->x_theora_state); + theora_comment_clear(&x->x_theora_comment); + theora_info_clear(&x->x_theora_info); + } + + if ( x->x_novpackets > 0 ) + { + ogg_stream_clear(&x->x_statev); + vorbis_block_clear(&x->x_vorbis_block); + vorbis_dsp_clear(&x->x_dsp_state); + vorbis_comment_clear(&x->x_vorbis_comment); + vorbis_info_clear(&x->x_vorbis_info); + } + + } + + x->x_notpackets = 0; + x->x_novpackets = 0; + x->x_endoffile = 0; + x->x_nbframes = 0; + x->x_decoding = 0; + x->x_theorainit = 0; + + x->x_videoready = 0; + x->x_newpicture = 0; + + x->x_nbframes = 0; + outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + x->x_framerate = 0; + outlet_float( x->x_outlet_framerate, x->x_framerate ); +} + +static void pdp_theorin_open(t_pdp_theorin *x, t_symbol *s) +{ + t_int ret, i; + pthread_attr_t decode_child_attr; + ogg_stream_state o_tempstate; + struct stat fileinfos; + + if ( x->x_infile != NULL ) + { + post("pdp_theorin~ : open request but a file is open ... closing" ); + pdp_theorin_close(x); + } + + if ( x->x_filename ) free( x->x_filename ); + x->x_filename = (char*) malloc( strlen( s->s_name ) + 1 ); + strcpy( x->x_filename, s->s_name ); + post( "pdp_theorin~ : opening file : %s", x->x_filename ); + + if ( ( x->x_infile = fopen(x->x_filename,"r") ) == NULL ) + { + post( "pdp_theorin~ : unable to open file >%s<", x->x_filename ); + return; + } + + ogg_sync_init(&x->x_sync_state); + + // init supporting Vorbis structures needed in header parsing + vorbis_info_init(&x->x_vorbis_info); + vorbis_comment_init(&x->x_vorbis_comment); + + // init supporting Theora structures needed in header parsing + theora_comment_init(&x->x_theora_comment); + theora_info_init(&x->x_theora_info); + + // parse headers + while( !x->x_theorainit ) + { + if ( ( ret = pdp_theorin_get_buffer_from_file(x->x_infile, &x->x_sync_state) )==0) break; + + while( ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page) > 0 ) + { + /* is this a mandated initial header? If not, stop parsing */ + if(!ogg_page_bos(&x->x_ogg_page)) + { + pdp_theorin_queue_page(x); + x->x_theorainit = 1; + break; + } + + ogg_stream_init(&o_tempstate, ogg_page_serialno(&x->x_ogg_page)); + ogg_stream_pagein(&o_tempstate, &x->x_ogg_page); + ogg_stream_packetout(&o_tempstate, &x->x_ogg_packet); + + /* identify the codec: try theora */ + if(!x->x_notpackets && + theora_decode_header(&x->x_theora_info, &x->x_theora_comment, &x->x_ogg_packet)>=0) + { + /* it is theora */ + memcpy(&x->x_statet, &o_tempstate, sizeof(o_tempstate)); + x->x_notpackets=1; + }else + if(!x->x_novpackets && + vorbis_synthesis_headerin(&x->x_vorbis_info, &x->x_vorbis_comment, &x->x_ogg_packet)>=0){ + memcpy(&x->x_statev, &o_tempstate, sizeof(o_tempstate)); + x->x_novpackets=1; + }else{ + /* whatever it is, we don't care about it */ + ogg_stream_clear(&o_tempstate); + } + } + } + + // we're expecting more header packets. + while( (x->x_notpackets && x->x_notpackets<3) || (x->x_novpackets && x->x_novpackets<3) ) + { + // look for further theora headers + while(x->x_notpackets && (x->x_notpackets<3) && + (ret=ogg_stream_packetout(&x->x_statet, &x->x_ogg_packet))) + { + if( ret<0 ) + { + post("pdp_theorin~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + return; + } + if( theora_decode_header(&x->x_theora_info, &x->x_theora_comment, &x->x_ogg_packet) ) + { + post("pdp_theorin~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + return; + } + x->x_notpackets++; + if(x->x_notpackets==3) break; + } + + /* look for more vorbis header packets */ + while(x->x_novpackets && (x->x_novpackets<3) && + (ret=ogg_stream_packetout(&x->x_statev, &x->x_ogg_packet))) + { + if(ret<0) + { + post("pdp_theorin~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + return; + } + if( vorbis_synthesis_headerin(&x->x_vorbis_info, &x->x_vorbis_comment, &x->x_ogg_packet) ) + { + post("pdp_theorin~ : error parsing theora stream headers\n"); + x->x_theorainit = 0; + return; + } + x->x_novpackets++; + if(x->x_novpackets==3) break; + } + + if(ogg_sync_pageout(&x->x_sync_state, &x->x_ogg_page)>0) + { + pdp_theorin_queue_page(x); + } + else + { + if( (ret=pdp_theorin_get_buffer_from_file(x->x_infile, &x->x_sync_state))==0 ) + { + post("pdp_theorin~ : end of file while parsing headers\n"); + x->x_theorainit = 0; + return; + } + } + } + post( "pdp_theorin~ : parsed headers ok." ); + + // initialize decoders + if( x->x_notpackets ) + { + theora_decode_init(&x->x_theora_state, &x->x_theora_info); + x->x_framerate = (t_int)x->x_theora_info.fps_numerator/x->x_theora_info.fps_denominator; + post("pdp_theorin~ : stream %x is theora %dx%d %d fps video.", + x->x_statet.serialno, + x->x_theora_info.width,x->x_theora_info.height, + x->x_framerate); + if(x->x_theora_info.width!=x->x_theora_info.frame_width || + x->x_theora_info.height!=x->x_theora_info.frame_height) + { + post("pdp_theorin~ : frame content is %dx%d with offset (%d,%d).", + x->x_theora_info.frame_width, x->x_theora_info.frame_height, + x->x_theora_info.offset_x, x->x_theora_info.offset_y); + } + x->x_vwidth = x->x_theora_info.width; + x->x_vheight = x->x_theora_info.height; + x->x_vsize = x->x_vwidth*x->x_vheight; + + switch(x->x_theora_info.colorspace) + { + case OC_CS_UNSPECIFIED: + /* nothing to report */ + break;; + case OC_CS_ITU_REC_470M: + post("pdp_theorin~ : encoder specified ITU Rec 470M (NTSC) color."); + break;; + case OC_CS_ITU_REC_470BG: + post("pdp_theorin~ : encoder specified ITU Rec 470BG (PAL) color."); + break;; + default: + post("pdp_theorin~ : warning: encoder specified unknown colorspace (%d).", + x->x_theora_info.colorspace); + break;; + } + } + else + { + // tear down the partial theora setup + theora_info_clear(&x->x_theora_info); + theora_comment_clear(&x->x_theora_comment); + post("pdp_theorin~ : could not initialize theora decoder."); + x->x_theorainit = 0; + return; + } + + if( x->x_novpackets ) + { + vorbis_synthesis_init(&x->x_dsp_state, &x->x_vorbis_info); + vorbis_block_init(&x->x_dsp_state, &x->x_vorbis_block); + x->x_audiochannels = x->x_vorbis_info.channels; + x->x_samplerate = x->x_vorbis_info.rate; + post("pdp_theorin~ : ogg logical stream %x is vorbis %d channel %d Hz audio.", + x->x_statev.serialno, + x->x_audiochannels, x->x_samplerate); + } + else + { + /* tear down the partial vorbis setup */ + vorbis_info_clear(&x->x_vorbis_info); + vorbis_comment_clear(&x->x_vorbis_comment); + post("pdp_theorin~ : could not initialize vorbis decoder."); + // x->x_theorainit = 0; + // return; + x->x_audio = 0; + } + // everything seems to be ready + x->x_reading = 1; + + if ( x->x_usethread && ( x->x_decodechild == 0 ) ) + { + x->x_decodechild = 1; // trick & treets + // launch decoding thread + if ( pthread_attr_init( &decode_child_attr ) < 0 ) + { + post( "pdp_theorin~ : could not launch decoding thread" ); + perror( "pthread_attr_init" ); + pthread_exit(NULL); + } + if ( pthread_create( &x->x_decodechild, &decode_child_attr, pdp_decode_file, x ) < 0 ) + { + post( "pdp_theorin~ : could not launch decoding thread" ); + perror( "pthread_create" ); + pthread_exit(NULL); + } + else + { + // post( "pdp_theorin~ : decoding thread %d launched", (int)x->x_decodechild ); + } + } + + if ( stat( x->x_filename, &fileinfos ) < 0 ) + { + post("pdp_theorin~ : couldn't get file informations" ); + perror( "stat" ); + } + else + { + outlet_float( x->x_outlet_filesize, (fileinfos.st_size)/1024 ); + } + + if ( gettimeofday(&x->x_starttime, NULL) == -1) + { + post("pdp_theorin~ : could not set start time" ); + } + + x->x_nbframes = 0; + x->x_endoffile = -1; + + return; +} + +static void pdp_theorin_frame_cold(t_pdp_theorin *x, t_floatarg kbytes) +{ + int pos = (int)kbytes; + int ret; + + if (x->x_infile==NULL) return; + + pdp_theorin_open(x, gensym(x->x_filename)); + + // it's very approximative, we're are positioning the file on the number of requested kilobytes + if ( fseek( x->x_infile, pos*1024, SEEK_SET ) < 0 ) + { + post( "pdp_theorin~ : could not set file at that position (%d kilobytes)", pos ); + perror( "fseek" ); + return; + } + // post( "pdp_theorin~ : file seeked at %d kilobytes", pos ); +} + + /* decode the audio buffer */ +static t_int *pdp_theorin_perform(t_int *w) +{ + t_float *out1 = (t_float *)(w[1]); // left audio inlet + t_float *out2 = (t_float *)(w[2]); // right audio inlet + t_pdp_theorin *x = (t_pdp_theorin *)(w[3]); + int n = (int)(w[4]); // number of samples + struct timeval etime; + t_int sn; + + // decode a packet if not in thread mode + if ( !x->x_usethread && x->x_reading ) + { + pdp_theorin_decode_packet( x ); + } + + x->x_blocksize = n; + + // just read the buffer + if ( x->x_audioon ) + { + sn=0; + while (n--) + { + *(out1)=x->x_audio_inl[ sn ]; + if ( x->x_audiochannels == 1 ) + { + *(out2) = *(out1); + sn++; + } + if ( x->x_audiochannels == 2 ) + { + *(out2)=x->x_audio_inr[ sn++ ]; + } + out1++; + out2++; + } + memcpy( &x->x_audio_inl[0], &x->x_audio_inl[sn], (x->x_audioin_position-sn)*sizeof(t_float) ); + memcpy( &x->x_audio_inr[0], &x->x_audio_inr[sn], (x->x_audioin_position-sn)*sizeof(t_float) ); + x->x_audioin_position-=sn; + // post( "pdp_theorin~ : audio in position : %d", x->x_audioin_position ); + if ( x->x_audioin_position <= sn ) + { + x->x_audioon = 0; + // post( "pdp_theorin~ : audio off ( audioin : %d, channels=%d )", + // x->x_audioin_position, x->x_audiochannels ); + } + } + else + { + // post("pdp_theorin~ : no available audio" ); + while (n--) + { + *(out1++) = 0.0; + *(out2++) = 0.0; + } + } + + // check if the framerate has been exceeded + if ( gettimeofday(&etime, NULL) == -1) + { + post("pdp_theorin~ : could not read time" ); + } + if ( etime.tv_sec != x->x_cursec ) + { + x->x_cursec = etime.tv_sec; + if (x->x_reading) outlet_float( x->x_outlet_framerate, x->x_secondcount ); + x->x_secondcount = 0; + } + + // output image if there's a new one decoded + if ( x->x_newpicture ) + { + pdp_packet_pass_if_valid(x->x_pdp_out, &x->x_packet0); + x->x_newpicture = 0; + + // update streaming status + x->x_nbframes++; + x->x_secondcount++; + outlet_float( x->x_outlet_nbframes, x->x_nbframes ); + + } + if ( x->x_endoffile == 1 ) // only once + { + outlet_float( x->x_outlet_endoffile, x->x_endoffile ); + x->x_endoffile = 0; + } + if ( x->x_endoffile == -1 ) // reset + { + x->x_endoffile = 0; + outlet_float( x->x_outlet_endoffile, x->x_endoffile ); + } + + return (w+5); +} + +static void pdp_theorin_dsp(t_pdp_theorin *x, t_signal **sp) +{ + dsp_add(pdp_theorin_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + +static void pdp_theorin_free(t_pdp_theorin *x) +{ + int i; + + if ( x->x_decodechild ) + { + x->x_decodechild = 0; + } + + if ( x->x_reading ) + { + pdp_theorin_close(x); + } + + post( "pdp_theorin~ : freeing object" ); +} + +t_class *pdp_theorin_class; + +void *pdp_theorin_new(void) +{ + int i; + + t_pdp_theorin *x = (t_pdp_theorin *)pd_new(pdp_theorin_class); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("frame_cold")); + + x->x_pdp_out = outlet_new(&x->x_obj, &s_anything); + + x->x_outlet_left = outlet_new(&x->x_obj, &s_signal); + x->x_outlet_right = outlet_new(&x->x_obj, &s_signal); + + x->x_outlet_nbframes = outlet_new(&x->x_obj, &s_float); + x->x_outlet_framerate = outlet_new(&x->x_obj, &s_float); + x->x_outlet_endoffile = outlet_new(&x->x_obj, &s_float); + x->x_outlet_filesize = outlet_new(&x->x_obj, &s_float); + + x->x_packet0 = -1; + x->x_decodechild = 0; + x->x_decoding = 0; + x->x_theorainit = 0; + x->x_usethread = 1; + x->x_priority = DEFAULT_PRIORITY; + x->x_framerate = DEFAULT_FRAME_RATE; + x->x_nbframes = 0; + x->x_samplerate = 0; + x->x_audio = 1; + x->x_audiochannels = 0; + x->x_audioin_position = 0; + x->x_videoready = 0; + x->x_newpicture = 0; + x->x_newpictureready = 0; + x->x_endoffile = 0; + x->x_notpackets = 0; + x->x_novpackets = 0; + x->x_blocksize = MIN_AUDIO_SIZE; + x->x_autoplay = 1; + x->x_loop = 1; + x->x_nextimage = 0; + x->x_infile = NULL; + x->x_reading = 0; + + memset( &x->x_audio_inl[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(t_float) ); + memset( &x->x_audio_inr[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(t_float) ); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_theorin_tilde_setup(void) +{ + // post( pdp_theorin_version ); + pdp_theorin_class = class_new(gensym("pdp_theorin~"), (t_newmethod)pdp_theorin_new, + (t_method)pdp_theorin_free, sizeof(t_pdp_theorin), 0, A_NULL); + + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_dsp, gensym("dsp"), A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_open, gensym("open"), A_SYMBOL, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_close, gensym("close"), A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_priority, gensym("priority"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_audio, gensym("audio"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_autoplay, gensym("autoplay"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_loop, gensym("loop"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_threadify, gensym("thread"), A_FLOAT, A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_theorin_class, (t_method)pdp_theorin_frame_cold, gensym("frame_cold"), A_FLOAT, A_NULL); + class_sethelpsymbol( pdp_theorin_class, gensym("pdp_theorin~.pd") ); + +} + +#ifdef __cplusplus +} +#endif -- cgit v1.2.1