aboutsummaryrefslogtreecommitdiff
path: root/oggread~/oggread~.c
diff options
context:
space:
mode:
Diffstat (limited to 'oggread~/oggread~.c')
-rw-r--r--oggread~/oggread~.c413
1 files changed, 413 insertions, 0 deletions
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 <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef UNIX
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <unistd.h>
+#define SOCKET_ERROR -1
+#else
+#include <io.h>
+#include <stdlib.h>
+#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"));
+}