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