From 39bfab603950686c2b007be44fc75643daacc932 Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Tue, 26 Nov 2002 10:52:38 +0000 Subject: added svn path=/trunk/externals/pdogg/; revision=248 --- oggread~/oggread~.c | 413 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 oggread~/oggread~.c (limited to 'oggread~/oggread~.c') diff --git a/oggread~/oggread~.c b/oggread~/oggread~.c new file mode 100644 index 0000000..af07fb0 --- /dev/null +++ b/oggread~/oggread~.c @@ -0,0 +1,413 @@ +/* ------------------------- oggread~ ------------------------------------------ */ +/* */ +/* Tilde object to read and play back Ogg Vorbis files. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.de/puredata/ */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library 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 */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Uses the Ogg Vorbis decoding library which can be found at */ +/* http://www.vorbis.com/ */ +/* */ +/* ---------------------------------------------------------------------------- */ + + +#include "m_imp.h" +#include "g_canvas.h" +#include "codec.h" +#include "vorbis/vorbisfile.h" + +#include +#include +#include +#include +#include +#ifdef UNIX +#include +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#else +#include +#include +#endif + +#ifdef NT +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#define READ 4096 /* amount of data we pass on to decoder */ +#define MIN_AUDIO_INPUT READ*8 /* this is completely guessed! we just fill half the buffer */ +#define OUTPUT_BUFFER_SIZE 65536 /* audio output buffer: 64k */ + +static char *oggread_version = "oggread~: ogg/vorbis file reader version 0.2b, written by Olaf Matthes"; + +/* ------------------------ oggread~ ----------------------------- */ + +static t_class *oggread_class; + +typedef struct _oggread +{ + t_object x_obj; + t_clock *x_clock; + /* ogg/vorbis related stuff */ + OggVorbis_File x_ov; + ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ + ogg_sync_state x_oy; /* sync and verify incoming physical bitstream */ + ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet x_op; /* one raw packet of data for decode */ + vorbis_info *x_vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment x_vc; /* struct that stores all the user comments */ + vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */ + vorbis_block x_vb; /* local working space for packet->PCM decode */ + t_int x_eos; /* end of stream */ + char *x_buffer;/* buffer used to pass on data to ogg/vorbis */ + + t_float x_position; /* current playing position */ + t_outlet *x_out_position; /* output to send them to */ + + + t_outlet *x_connection; + t_int x_fd; /* the file handle */ + FILE *x_file; + int x_current_section; + t_int x_blocksize; /* size of a dsp block */ + t_int x_decoded; /* number of samples we got from decoder on last call */ + + t_float *x_outbuffer; /* buffer to store audio decoded data */ + t_int x_outwriteposition; + t_int x_outreadposition; + t_int x_outunread; + t_int x_outbuffersize; + + t_int x_samplerate; /* pd's samplerate, might differ from stream */ + t_int x_stream; /* indicates if a stream gets output */ +} t_oggread; + + /* output playing position */ +static void oggread_tick(t_oggread *x) +{ + outlet_float(x->x_out_position, x->x_position); + clock_delay(x->x_clock, 250); +} + +static int oggread_decode_input(t_oggread *x) +{ + long ret; /* bytes per channel returned by decoder */ + int i; + float **pcm; + + x->x_vi = ov_info(&x->x_ov, x->x_current_section); + + while(!x->x_eos) + { + ret = ov_read_float(&x->x_ov, &pcm, READ, &x->x_current_section); + if (ret == 0) + { + /* EOF */ + x->x_eos = 1; + x->x_stream = 0; + clock_unset(x->x_clock); + post("oggread~: end of file detected, stopping"); + } + else if (ret < 0) + { + /* error in the stream. Not a problem, just reporting it in + case we (the app) cares. In this case, we don't. */ + } + else + { + /* we don't bother dealing with sample rate changes, etc, but + you'll have to */ + long j; + for(j = 0; j < ret; j++) + { + for(i = 0; i < x->x_vi->channels; i++) + { + x->x_outbuffer[x->x_outwriteposition] = pcm[i][j]; + x->x_outwriteposition = (x->x_outwriteposition + 1)%x->x_outbuffersize; + } + } + x->x_outunread += (t_int)ret * x->x_vi->channels; + + } + break; + } + x->x_decoded = (t_int)ret * x->x_vi->channels; /* num. of samples we got from decoder */ + + x->x_position = (t_float)ov_time_tell(&x->x_ov); + + /* exit decoding 'loop' here, we'll get called again by perform() */ + return 1; +} + + +static t_int *oggread_perform(t_int *w) +{ + t_oggread *x = (t_oggread*) (w[1]); + t_float *out1 = (t_float *)(w[2]); + t_float *out2 = (t_float *)(w[3]); + int n = (int)(w[4]); + int ret; + int i = 0; + + x->x_blocksize = n; + + while( n-- ) + { /* check that the stream provides enough data */ + if((x->x_stream == 1) && (x->x_outunread > (x->x_blocksize * x->x_vi->channels))) + { + if(x->x_vi->channels != 1) /* play stereo */ + { + *out1++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + *out2++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + x->x_outunread-=2; + } + else /* play mono on both sides */ + { + *out1++=*(x->x_outbuffer+x->x_outreadposition); + *out2++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + x->x_outunread--; + } + } + else /* silence in case of buffer underrun */ + { + *out1++=0.0; + *out2++=0.0; + } + } + + /* decode data whenever we used up some samples from outbuffer */ + if((x->x_fd > 0) && (x->x_stream) /* only go when file is open and ready */ + && (x->x_outunread < (MIN_AUDIO_INPUT - x->x_decoded))) /* we used up data from last decode */ + { + // post("oggread~: decoding..."); + if(oggread_decode_input(x) != 1) + { + post("oggread~: decoder error"); + } + // else post("oggread~: decoder returned %d samples", x->x_decoded); + } + return (w+5); +} + +static void oggread_dsp(t_oggread *x, t_signal **sp) +{ + dsp_add(oggread_perform, 4, x, sp[1]->s_vec, sp[2]->s_vec, sp[1]->s_n); +} + + + /* start playing */ +static void oggread_start(t_oggread *x) +{ + if(x->x_fd > 0) + { + if(ov_time_seek(&x->x_ov, 0) < 0) + { + post("oggread~: could not rewind file to beginning"); + } + post("oggread~: START"); + x->x_eos = 0; + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + x->x_position = 0; + clock_delay(x->x_clock, 0); + x->x_stream = 1; + } + else post("oggread~: no file open (ignored)"); +} + + /* resume file reading */ +static void oggread_resume(t_oggread *x) +{ + if(x->x_fd > 0) + { + x->x_stream = 1; + clock_delay(x->x_clock, 0); + post("oggread~: RESUME"); + } + else post("oggread~: encoder not initialised"); +} + + /* seek in file */ +static void oggread_seek(t_oggread *x, t_floatarg f) +{ + if(x->x_fd > 0) + if(ov_time_seek(&x->x_ov, f) < 0) + { + post("oggread~: could not set playing position to %g seconds", f); + } + else post("oggread~: playing position set to %g seconds", f); +} + + /* stop playing */ +static void oggread_stop(t_oggread *x) +{ + if(x->x_stream)post("oggread~: STOP"); + x->x_stream = 0; + clock_unset(x->x_clock); +} + +static void oggread_float(t_oggread *x, t_floatarg f) +{ + if(f == 0) oggread_stop(x); + else oggread_start(x); +} + + /* open ogg/vorbis file */ +static void oggread_open(t_oggread *x, t_symbol *filename) +{ + int i; + + x->x_stream = 0; + /* first close previous file */ + if(x->x_fd > 0) + { + ov_clear(&x->x_ov); + post("oggread~: previous file closed"); + } + /* open file for reading */ +#ifdef UNIX + if((x->x_file = fopen(filename->s_name, "r")) < 0) +#else + if((x->x_file = fopen(filename->s_name, "rb")) < 0) +#endif + { + post("oggread~: could not open file \"%s\"", filename->s_name); + x->x_eos = 1; + x->x_fd = -1; + } + else + { + x->x_stream = 0; + x->x_eos = 0; + x->x_fd = 1; + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + post("oggread~: file \"%s\" opened", filename->s_name); + outlet_float( x->x_out_position, 0); + + /* try to open as ogg vorbis file */ + if(ov_open(x->x_file, &x->x_ov, NULL, -1) < 0) + { /* an error occured (no ogg vorbis file ?) */ + post("oggread~: error: could not open \"%s\" as an OggVorbis file", filename->s_name); + ov_clear(&x->x_ov); + post("oggread~: file closed due to error"); + } + + /* print details about each logical bitstream in the input */ + if(ov_seekable(&x->x_ov)) + { + post("oggread~: input bitstream contained %ld logical bitstream section(s)", ov_streams(&x->x_ov)); + post("oggread~: total bitstream playing time: %ld seconds", (long)ov_time_total(&x->x_ov,-1)); + post("oggread~: encoded by: %s\n",ov_comment(&x->x_ov,-1)->vendor); + } + else + { + post("oggread~: file \"%s\" was not seekable" + "oggread~: first logical bitstream information:", filename->s_name); + } + + for(i = 0; i < ov_streams(&x->x_ov); i++) + { + x->x_vi = ov_info(&x->x_ov,i); + post("\tlogical bitstream section %d information:",i+1); + post("\t\t%ldHz %d channels bitrate %ldkbps serial number=%ld", + x->x_vi->rate,x->x_vi->channels,ov_bitrate(&x->x_ov,i)/1000, ov_serialnumber(&x->x_ov,i)); + post("\t\theader length: %ld bytes",(long) + (x->x_ov.dataoffsets[i] - x->x_ov.offsets[i])); + post("\t\tcompressed length: %ld bytes",(long)(ov_raw_total(&x->x_ov,i))); + post("\t\tplay time: %ld seconds\n",(long)ov_time_total(&x->x_ov,i)); + } + + } +} + + +static void oggread_free(t_oggread *x) +{ + if (x->x_fd > 0) { + post( "oggread~: closing file" ); + ov_clear(&x->x_ov); + x->x_fd = -1; + } + freebytes(x->x_outbuffer, OUTPUT_BUFFER_SIZE*sizeof(t_float)); + clock_free(x->x_clock); +} + +static void *oggread_new(t_floatarg fdographics) +{ + t_oggread *x = NULL; + + x = (t_oggread *)pd_new(oggread_class); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_out_position = outlet_new(&x->x_obj, gensym("float")); + x->x_clock = clock_new(x, (t_method)oggread_tick); + + x->x_fd = -1; + x->x_eos = 1; + x->x_stream = 0; + x->x_position = 0; + x->x_samplerate = sys_getsr(); + + x->x_outbuffersize = OUTPUT_BUFFER_SIZE; + x->x_outbuffer = (t_float*) getbytes(OUTPUT_BUFFER_SIZE*sizeof(t_float)); + + if(!x->x_outbuffer) + { + post( "oggread~: could not allocate buffer" ); + return NULL; + } + memset(x->x_outbuffer, 0x0, OUTPUT_BUFFER_SIZE); + + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + x->x_decoded = 0; + + post(oggread_version); + + return (x); +} + + +void oggread_tilde_setup(void) +{ + oggread_class = class_new(gensym("oggread~"), + (t_newmethod) oggread_new, (t_method) oggread_free, + sizeof(t_oggread), 0, A_DEFFLOAT, A_NULL); + class_addfloat(oggread_class, (t_method)oggread_float); + class_addmethod(oggread_class, nullfn, gensym("signal"), 0); + class_addmethod(oggread_class, (t_method)oggread_dsp, gensym("dsp"), 0); + class_addmethod(oggread_class, (t_method)oggread_open, gensym("open"), A_SYMBOL, 0); + class_addmethod(oggread_class, (t_method)oggread_start, gensym("start"), 0); + class_addmethod(oggread_class, (t_method)oggread_resume, gensym("resume"), 0); + class_addmethod(oggread_class, (t_method)oggread_seek, gensym("seek"), A_DEFFLOAT, 0); + class_addmethod(oggread_class, (t_method)oggread_stop, gensym("stop"), 0); + class_sethelpsymbol(oggread_class, gensym("help-oggread~.pd")); +} -- cgit v1.2.1