diff options
author | jdl <x75@users.sourceforge.net> | 2004-01-04 12:16:08 +0000 |
---|---|---|
committer | jdl <x75@users.sourceforge.net> | 2004-01-04 12:16:08 +0000 |
commit | ced726412c09f7fd3ef2eab1ff59305a720c03ec (patch) | |
tree | 156dc8ce8fe0c818617ba13fc4664f7415999673 /oggamp~ | |
parent | ea8af3dcd4921987a6e63eb7b6e5c4601f20bbaa (diff) |
recommitting olaf's sources from 0.25
svn path=/trunk/externals/pdogg/; revision=1249
Diffstat (limited to 'oggamp~')
-rw-r--r-- | oggamp~/HISTORY | 84 | ||||
-rw-r--r-- | oggamp~/codec.h | 466 | ||||
-rw-r--r-- | oggamp~/help-oggamp~.pd | 108 | ||||
-rw-r--r-- | oggamp~/oggamp~.c | 2917 | ||||
-rw-r--r-- | oggamp~/readme | 172 |
5 files changed, 1844 insertions, 1903 deletions
diff --git a/oggamp~/HISTORY b/oggamp~/HISTORY index f21a233..f800e80 100644 --- a/oggamp~/HISTORY +++ b/oggamp~/HISTORY @@ -1,40 +1,44 @@ -Version history of oggamp~ external for pure-data - -v 0.2f (20st july 2002): -- recompiled with the final 1.0 release of Ogg Vorbis - -v 0.2e (21st june 2002 - stable release): -- added downsamling -- cleaned up code a bit -- added some more info-printout -- fixed some bugs to make it work correctly on Linux - thanks to Oliver Thuns at radiostudio.org -- now disconnects correctly at end-of-stream (when no - chained stream follows) -- KNOWN BUG: graphic buffer status display might cause ugly - printout of Tcl/Tk commands to console window on Linux - under some circumstances (e.g. in case server dies) - -v 0.2d (12th june 2002): -- added upsamling -- finally fixed the End-Of-Stream bug: it's now - possible to listen to a playlist with correct - update of stream information - -v 0.2c (10th june 2002): -- fixed some bugs, introduced some new ones... - -v 0.2a (11th mar. 2002): -- introduced child thread for connect: now pd - does no longer 'stop' audio; as a side effect it - is now possible to connect to an oggcast~ stream - from the same instance of pd -- threads now use pthreads libraray on Win to have - things compatible with UNIX -- fixed a small bug that made 'old' audio appear on - the beginning of 'new' one after reconnecting - -v 0.1c (19th feb. 2002): -- first (sort of) stable release - - +Version history of oggamp~ external for pure-data
+
+v 0.3 (2nd january 2004):
+- removed the crappy resampling to get rid of audio clicks
+- multichannel support
+
+v 0.2f (20st july 2002):
+- recompiled with the final 1.0 release of Ogg Vorbis
+
+v 0.2e (21st june 2002 - stable release):
+- added downsamling
+- cleaned up code a bit
+- added some more info-printout
+- fixed some bugs to make it work correctly on Linux
+ thanks to Oliver Thuns at radiostudio.org
+- now disconnects correctly at end-of-stream (when no
+ chained stream follows)
+- KNOWN BUG: graphic buffer status display might cause ugly
+ printout of Tcl/Tk commands to console window on Linux
+ under some circumstances (e.g. in case server dies)
+
+v 0.2d (12th june 2002):
+- added upsamling
+- finally fixed the End-Of-Stream bug: it's now
+ possible to listen to a playlist with correct
+ update of stream information
+
+v 0.2c (10th june 2002):
+- fixed some bugs, introduced some new ones...
+
+v 0.2a (11th mar. 2002):
+- introduced child thread for connect: now pd
+ does no longer 'stop' audio; as a side effect it
+ is now possible to connect to an oggcast~ stream
+ from the same instance of pd
+- threads now use pthreads libraray on Win to have
+ things compatible with UNIX
+- fixed a small bug that made 'old' audio appear on
+ the beginning of 'new' one after reconnecting
+
+v 0.1c (19th feb. 2002):
+- first (sort of) stable release
+
+
diff --git a/oggamp~/codec.h b/oggamp~/codec.h index d986a9b..964a067 100644 --- a/oggamp~/codec.h +++ b/oggamp~/codec.h @@ -1,233 +1,233 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * - * * - * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * - * by the XIPHOPHORUS Company http://www.xiph.org/ * - - ******************************************************************** - - function: libvorbis codec headers - last mod: $Id: codec.h,v 1.2 2003-04-23 10:36:25 xovo Exp $ - - ********************************************************************/ - -#ifndef _vorbis_codec_h_ -#define _vorbis_codec_h_ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -#include <ogg/ogg.h> - -typedef struct vorbis_info{ - int version; - int channels; - long rate; - - /* The below bitrate declarations are *hints*. - Combinations of the three values carry the following implications: - - all three set to the same value: - implies a fixed rate bitstream - only nominal set: - implies a VBR stream that averages the nominal bitrate. No hard - upper/lower limit - upper and or lower set: - implies a VBR bitstream that obeys the bitrate limits. nominal - may also be set to give a nominal rate. - none set: - the coder does not care to speculate. - */ - - long bitrate_upper; - long bitrate_nominal; - long bitrate_lower; - long bitrate_window; - - void *codec_setup; -} vorbis_info; - -/* vorbis_dsp_state buffers the current vorbis audio - analysis/synthesis state. The DSP state belongs to a specific - logical bitstream ****************************************************/ -typedef struct vorbis_dsp_state{ - int analysisp; - vorbis_info *vi; - - float **pcm; - float **pcmret; - int pcm_storage; - int pcm_current; - int pcm_returned; - - int preextrapolate; - int eofflag; - - long lW; - long W; - long nW; - long centerW; - - ogg_int64_t granulepos; - ogg_int64_t sequence; - - ogg_int64_t glue_bits; - ogg_int64_t time_bits; - ogg_int64_t floor_bits; - ogg_int64_t res_bits; - - void *backend_state; -} vorbis_dsp_state; - -typedef struct vorbis_block{ - /* necessary stream state for linking to the framing abstraction */ - float **pcm; /* this is a pointer into local storage */ - oggpack_buffer opb; - - long lW; - long W; - long nW; - int pcmend; - int mode; - - int eofflag; - ogg_int64_t granulepos; - ogg_int64_t sequence; - vorbis_dsp_state *vd; /* For read-only access of configuration */ - - /* local storage to avoid remallocing; it's up to the mapping to - structure it */ - void *localstore; - long localtop; - long localalloc; - long totaluse; - struct alloc_chain *reap; - - /* bitmetrics for the frame */ - long glue_bits; - long time_bits; - long floor_bits; - long res_bits; - - void *internal; - -} vorbis_block; - -/* vorbis_block is a single block of data to be processed as part of -the analysis/synthesis stream; it belongs to a specific logical -bitstream, but is independant from other vorbis_blocks belonging to -that logical bitstream. *************************************************/ - -struct alloc_chain{ - void *ptr; - struct alloc_chain *next; -}; - -/* vorbis_info contains all the setup information specific to the - specific compression/decompression mode in progress (eg, - psychoacoustic settings, channel setup, options, codebook - etc). vorbis_info and substructures are in backends.h. -*********************************************************************/ - -/* the comments are not part of vorbis_info so that vorbis_info can be - static storage */ -typedef struct vorbis_comment{ - /* unlimited user comment fields. libvorbis writes 'libvorbis' - whatever vendor is set to in encode */ - char **user_comments; - int *comment_lengths; - int comments; - char *vendor; - -} vorbis_comment; - - -/* libvorbis encodes in two abstraction layers; first we perform DSP - and produce a packet (see docs/analysis.txt). The packet is then - coded into a framed OggSquish bitstream by the second layer (see - docs/framing.txt). Decode is the reverse process; we sync/frame - the bitstream and extract individual packets, then decode the - packet back into PCM audio. - - The extra framing/packetizing is used in streaming formats, such as - files. Over the net (such as with UDP), the framing and - packetization aren't necessary as they're provided by the transport - and the streaming layer is not used */ - -/* Vorbis PRIMITIVES: general ***************************************/ - -extern void vorbis_info_init(vorbis_info *vi); -extern void vorbis_info_clear(vorbis_info *vi); -extern int vorbis_info_blocksize(vorbis_info *vi,int zo); -extern void vorbis_comment_init(vorbis_comment *vc); -extern void vorbis_comment_add(vorbis_comment *vc, char *comment); -extern void vorbis_comment_add_tag(vorbis_comment *vc, - char *tag, char *contents); -extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count); -extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag); -extern void vorbis_comment_clear(vorbis_comment *vc); - -extern int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb); -extern int vorbis_block_clear(vorbis_block *vb); -extern void vorbis_dsp_clear(vorbis_dsp_state *v); - -/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/ - -extern int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi); -extern int vorbis_commentheader_out(vorbis_comment *vc, ogg_packet *op); -extern int vorbis_analysis_headerout(vorbis_dsp_state *v, - vorbis_comment *vc, - ogg_packet *op, - ogg_packet *op_comm, - ogg_packet *op_code); -extern float **vorbis_analysis_buffer(vorbis_dsp_state *v,int vals); -extern int vorbis_analysis_wrote(vorbis_dsp_state *v,int vals); -extern int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb); -extern int vorbis_analysis(vorbis_block *vb,ogg_packet *op); - -extern int vorbis_bitrate_addblock(vorbis_block *vb); -extern int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd, - ogg_packet *op); - -/* Vorbis PRIMITIVES: synthesis layer *******************************/ -extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc, - ogg_packet *op); - -extern int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi); -extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op); -extern int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op); -extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb); -extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm); -extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples); -extern long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op); - -/* Vorbis ERRORS and return codes ***********************************/ - -#define OV_FALSE -1 -#define OV_EOF -2 -#define OV_HOLE -3 - -#define OV_EREAD -128 -#define OV_EFAULT -129 -#define OV_EIMPL -130 -#define OV_EINVAL -131 -#define OV_ENOTVORBIS -132 -#define OV_EBADHEADER -133 -#define OV_EVERSION -134 -#define OV_ENOTAUDIO -135 -#define OV_EBADPACKET -136 -#define OV_EBADLINK -137 -#define OV_ENOSEEK -138 - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif - +/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
+ * by the XIPHOPHORUS Company http://www.xiph.org/ *
+
+ ********************************************************************
+
+ function: libvorbis codec headers
+ last mod: $Id: codec.h,v 1.3 2004-01-04 12:16:07 x75 Exp $
+
+ ********************************************************************/
+
+#ifndef _vorbis_codec_h_
+#define _vorbis_codec_h_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#include <ogg/ogg.h>
+
+typedef struct vorbis_info{
+ int version;
+ int channels;
+ long rate;
+
+ /* The below bitrate declarations are *hints*.
+ Combinations of the three values carry the following implications:
+
+ all three set to the same value:
+ implies a fixed rate bitstream
+ only nominal set:
+ implies a VBR stream that averages the nominal bitrate. No hard
+ upper/lower limit
+ upper and or lower set:
+ implies a VBR bitstream that obeys the bitrate limits. nominal
+ may also be set to give a nominal rate.
+ none set:
+ the coder does not care to speculate.
+ */
+
+ long bitrate_upper;
+ long bitrate_nominal;
+ long bitrate_lower;
+ long bitrate_window;
+
+ void *codec_setup;
+} vorbis_info;
+
+/* vorbis_dsp_state buffers the current vorbis audio
+ analysis/synthesis state. The DSP state belongs to a specific
+ logical bitstream ****************************************************/
+typedef struct vorbis_dsp_state{
+ int analysisp;
+ vorbis_info *vi;
+
+ float **pcm;
+ float **pcmret;
+ int pcm_storage;
+ int pcm_current;
+ int pcm_returned;
+
+ int preextrapolate;
+ int eofflag;
+
+ long lW;
+ long W;
+ long nW;
+ long centerW;
+
+ ogg_int64_t granulepos;
+ ogg_int64_t sequence;
+
+ ogg_int64_t glue_bits;
+ ogg_int64_t time_bits;
+ ogg_int64_t floor_bits;
+ ogg_int64_t res_bits;
+
+ void *backend_state;
+} vorbis_dsp_state;
+
+typedef struct vorbis_block{
+ /* necessary stream state for linking to the framing abstraction */
+ float **pcm; /* this is a pointer into local storage */
+ oggpack_buffer opb;
+
+ long lW;
+ long W;
+ long nW;
+ int pcmend;
+ int mode;
+
+ int eofflag;
+ ogg_int64_t granulepos;
+ ogg_int64_t sequence;
+ vorbis_dsp_state *vd; /* For read-only access of configuration */
+
+ /* local storage to avoid remallocing; it's up to the mapping to
+ structure it */
+ void *localstore;
+ long localtop;
+ long localalloc;
+ long totaluse;
+ struct alloc_chain *reap;
+
+ /* bitmetrics for the frame */
+ long glue_bits;
+ long time_bits;
+ long floor_bits;
+ long res_bits;
+
+ void *internal;
+
+} vorbis_block;
+
+/* vorbis_block is a single block of data to be processed as part of
+the analysis/synthesis stream; it belongs to a specific logical
+bitstream, but is independant from other vorbis_blocks belonging to
+that logical bitstream. *************************************************/
+
+struct alloc_chain{
+ void *ptr;
+ struct alloc_chain *next;
+};
+
+/* vorbis_info contains all the setup information specific to the
+ specific compression/decompression mode in progress (eg,
+ psychoacoustic settings, channel setup, options, codebook
+ etc). vorbis_info and substructures are in backends.h.
+*********************************************************************/
+
+/* the comments are not part of vorbis_info so that vorbis_info can be
+ static storage */
+typedef struct vorbis_comment{
+ /* unlimited user comment fields. libvorbis writes 'libvorbis'
+ whatever vendor is set to in encode */
+ char **user_comments;
+ int *comment_lengths;
+ int comments;
+ char *vendor;
+
+} vorbis_comment;
+
+
+/* libvorbis encodes in two abstraction layers; first we perform DSP
+ and produce a packet (see docs/analysis.txt). The packet is then
+ coded into a framed OggSquish bitstream by the second layer (see
+ docs/framing.txt). Decode is the reverse process; we sync/frame
+ the bitstream and extract individual packets, then decode the
+ packet back into PCM audio.
+
+ The extra framing/packetizing is used in streaming formats, such as
+ files. Over the net (such as with UDP), the framing and
+ packetization aren't necessary as they're provided by the transport
+ and the streaming layer is not used */
+
+/* Vorbis PRIMITIVES: general ***************************************/
+
+extern void vorbis_info_init(vorbis_info *vi);
+extern void vorbis_info_clear(vorbis_info *vi);
+extern int vorbis_info_blocksize(vorbis_info *vi,int zo);
+extern void vorbis_comment_init(vorbis_comment *vc);
+extern void vorbis_comment_add(vorbis_comment *vc, char *comment);
+extern void vorbis_comment_add_tag(vorbis_comment *vc,
+ char *tag, char *contents);
+extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count);
+extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag);
+extern void vorbis_comment_clear(vorbis_comment *vc);
+
+extern int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb);
+extern int vorbis_block_clear(vorbis_block *vb);
+extern void vorbis_dsp_clear(vorbis_dsp_state *v);
+
+/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/
+
+extern int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi);
+extern int vorbis_commentheader_out(vorbis_comment *vc, ogg_packet *op);
+extern int vorbis_analysis_headerout(vorbis_dsp_state *v,
+ vorbis_comment *vc,
+ ogg_packet *op,
+ ogg_packet *op_comm,
+ ogg_packet *op_code);
+extern float **vorbis_analysis_buffer(vorbis_dsp_state *v,int vals);
+extern int vorbis_analysis_wrote(vorbis_dsp_state *v,int vals);
+extern int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb);
+extern int vorbis_analysis(vorbis_block *vb,ogg_packet *op);
+
+extern int vorbis_bitrate_addblock(vorbis_block *vb);
+extern int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd,
+ ogg_packet *op);
+
+/* Vorbis PRIMITIVES: synthesis layer *******************************/
+extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,
+ ogg_packet *op);
+
+extern int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi);
+extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op);
+extern int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op);
+extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb);
+extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm);
+extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples);
+extern long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op);
+
+/* Vorbis ERRORS and return codes ***********************************/
+
+#define OV_FALSE -1
+#define OV_EOF -2
+#define OV_HOLE -3
+
+#define OV_EREAD -128
+#define OV_EFAULT -129
+#define OV_EIMPL -130
+#define OV_EINVAL -131
+#define OV_ENOTVORBIS -132
+#define OV_EBADHEADER -133
+#define OV_EVERSION -134
+#define OV_ENOTAUDIO -135
+#define OV_EBADPACKET -136
+#define OV_EBADLINK -137
+#define OV_ENOSEEK -138
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
diff --git a/oggamp~/help-oggamp~.pd b/oggamp~/help-oggamp~.pd index b0bb504..9f30cb8 100644 --- a/oggamp~/help-oggamp~.pd +++ b/oggamp~/help-oggamp~.pd @@ -1,54 +1,54 @@ -#N canvas 176 105 678 455 10; -#X obj 38 420 dac~; -#X floatatom 100 419 5 0 0; -#X text 145 419 connection state; -#X msg 80 83 connect localhost puredata.ogg 8000; -#X msg 109 131 disconnect; -#X msg 35 60 connect ogg.bbc.co.uk radio1_low.ogg 8001; -#X msg 93 106 connect 141.53.196.149 puredata.ogg 8000; -#X msg 147 224 recover 0; -#X msg 163 251 recover 1; -#X msg 125 153 print; -#X obj 15 389 *~ 0; -#X obj 57 391 *~ 0; -#X floatatom 20 332 5 0 0; -#X obj 20 354 / 100; -#X text 57 7 oggamp~ version 0.2 - Ogg Vorbis sreaming client; -#X msg 139 191 recover -1; -#X text 218 191 resume (default): mute audio and refill buffer; -#X text 230 249 reconnect (disconnect and connect again); -#X text 138 303 CREATION ARGUMENTS:; -#X text 260 303 oggamp~ <graphic> <outlets> <buffersize>; -#X text 167 324 <graphic> - turn graphical buffer status display on -(1) or off (0 \, default); -#X text 167 351 <outlets> - number of outlets (default = 2) \, mono -to stereo and stereo to mono conversion supported; -#X text 333 50 written by Olaf Matthes <olaf.matthes@gmx.de>; -#X text 333 63 get source at http://www.akustische-kunst.de/; -#X msg 530 406 \; pd dsp 1; -#X msg 596 406 \; pd dsp 0; -#X obj 530 380 loadbang; -#X msg 16 34 connect radio.jcraft.com test.ogg 8000; -#X obj 15 300 oggamp~ 1 2 256; -#X text 154 171 BEHAVIOUR ON BUFFER UNDERRUNS:; -#X text 166 378 <buffersize> - size of circular buffer in kbytes (default -= 256k); -#X text 219 222 disconnect on buffer underrun; -#X connect 3 0 28 0; -#X connect 4 0 28 0; -#X connect 5 0 28 0; -#X connect 6 0 28 0; -#X connect 7 0 28 0; -#X connect 8 0 28 0; -#X connect 9 0 28 0; -#X connect 10 0 0 0; -#X connect 11 0 0 1; -#X connect 12 0 13 0; -#X connect 13 0 10 1; -#X connect 13 0 11 1; -#X connect 15 0 28 0; -#X connect 26 0 24 0; -#X connect 27 0 28 0; -#X connect 28 0 10 0; -#X connect 28 1 11 0; -#X connect 28 2 1 0; +#N canvas 176 105 678 455 10;
+#X obj 38 420 dac~;
+#X floatatom 100 419 5 0 0;
+#X text 145 419 connection state;
+#X msg 80 83 connect localhost puredata.ogg 8000;
+#X msg 109 131 disconnect;
+#X msg 35 60 connect ogg.bbc.co.uk radio1_low.ogg 8001;
+#X msg 93 106 connect 141.53.196.149 puredata.ogg 8000;
+#X msg 147 224 recover 0;
+#X msg 163 251 recover 1;
+#X msg 125 153 print;
+#X obj 15 389 *~ 0;
+#X obj 57 391 *~ 0;
+#X floatatom 20 332 5 0 0;
+#X obj 20 354 / 100;
+#X text 57 7 oggamp~ version 0.2 - Ogg Vorbis sreaming client;
+#X msg 139 191 recover -1;
+#X text 218 191 resume (default): mute audio and refill buffer;
+#X text 230 249 reconnect (disconnect and connect again);
+#X text 138 303 CREATION ARGUMENTS:;
+#X text 260 303 oggamp~ <graphic> <outlets> <buffersize>;
+#X text 167 324 <graphic> - turn graphical buffer status display on
+(1) or off (0 \, default);
+#X text 167 351 <outlets> - number of outlets (default = 2) \, mono
+to stereo and stereo to mono conversion supported;
+#X text 333 50 written by Olaf Matthes <olaf.matthes@gmx.de>;
+#X text 333 63 get source at http://www.akustische-kunst.de/;
+#X msg 530 406 \; pd dsp 1;
+#X msg 596 406 \; pd dsp 0;
+#X obj 530 380 loadbang;
+#X msg 16 34 connect radio.jcraft.com test.ogg 8000;
+#X obj 15 300 oggamp~ 1 2 256;
+#X text 154 171 BEHAVIOUR ON BUFFER UNDERRUNS:;
+#X text 166 378 <buffersize> - size of circular buffer in kbytes (default
+= 256k);
+#X text 219 222 disconnect on buffer underrun;
+#X connect 3 0 28 0;
+#X connect 4 0 28 0;
+#X connect 5 0 28 0;
+#X connect 6 0 28 0;
+#X connect 7 0 28 0;
+#X connect 8 0 28 0;
+#X connect 9 0 28 0;
+#X connect 10 0 0 0;
+#X connect 11 0 0 1;
+#X connect 12 0 13 0;
+#X connect 13 0 10 1;
+#X connect 13 0 11 1;
+#X connect 15 0 28 0;
+#X connect 26 0 24 0;
+#X connect 27 0 28 0;
+#X connect 28 0 10 0;
+#X connect 28 1 11 0;
+#X connect 28 2 1 0;
diff --git a/oggamp~/oggamp~.c b/oggamp~/oggamp~.c index d421e18..662c58b 100644 --- a/oggamp~/oggamp~.c +++ b/oggamp~/oggamp~.c @@ -1,1491 +1,1428 @@ -/* ------------------------- oggamp~ ------------------------------------------ */ -/* */ -/* Tilde object to receive an Ogg Vorbis stream from an IceCast2 server. */ -/* Written by Olaf Matthes <olaf.matthes@gmx.de> */ -/* Get source at http://www.akustische-kunst.de/puredata/ */ -/* */ -/* Graphical buffer status display written by Yves Degoyon. */ -/* */ -/* Thanks for hours (maybe days?) of beta testing to Oliver Thuns. */ -/* */ -/* 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/ */ -/* */ -/* ---------------------------------------------------------------------------- */ - - /* Pd includes */ -#include "m_pd.h" -#include "g_canvas.h" - /* Vorbis includes */ -#include "codec.h" - -#include <sys/types.h> -#include <ctype.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <fcntl.h> -#include <pthread.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> /* for 'write' in pute-function only */ -#include <winsock.h> -#include <winbase.h> -#endif - -#ifdef NT -#pragma warning( disable : 4244 ) -#pragma warning( disable : 4305 ) -#endif - -#ifdef UNIX -#define sys_closesocket close -#endif -#ifdef NT -#define sys_closesocket closesocket -#endif - - -/************************* oggamp~ object ******************************/ - -/* - Each instance of oggamp~ owns a "child" thread for doing the data - transfer. The parent thread signals the child each time: - (1) a connection wants opening or closing; - (2) we've eaten another 1/16 of the shared buffer (so that the - child thread should check if it's time to receive some more.) - The child signals the parent whenever a receive has completed. Signalling - is done by setting "conditions" and putting data in mutex-controlled common - areas. -*/ - - -#define REQUEST_NOTHING 0 -#define REQUEST_CONNECT 1 -#define REQUEST_CLOSE 2 -#define REQUEST_QUIT 3 -#define REQUEST_BUSY 4 -#define REQUEST_DATA 5 -#define REQUEST_RECONNECT 6 - -#define STATE_IDLE 0 -#define STATE_STARTUP 1 /* connecting and filling the buffer */ -#define STATE_STREAM 2 /* streaming aund audio output */ - -#define READSIZE 65536 /* _encoded_ data we request from buffer */ -#define READ 4096 /* amount of data we pass on to decoder */ -#define DEFBUFPERCHAN 262144 /* audio output buffer default: 256k */ -#define MINBUFSIZE (4 * READSIZE) -#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */ -#define STRBUF_SIZE 1024 /* char received from server on startup */ -#define OBJWIDTH 68 /* width of buffer statis display */ -#define OBJHEIGHT 10 /* height of buffer statis display */ -#define MAXSTREAMCHANS 2 /* maximum number of channels: restricted to 2 by Ogg specs */ - -static char *oggamp_version = "oggamp~: ogg/vorbis streaming client version 0.2f, written by Olaf Matthes"; - -static t_class *oggamp_class; - -typedef struct _oggamp -{ - t_object x_obj; - t_canvas *x_canvas; /* remember canvas */ - t_outlet *x_connection; - t_clock *x_clock; - - t_float *x_buf; /* audio data buffer */ - t_int x_bufsize; /* buffer size in bytes */ - t_int x_noutlets; /* number of audio outlets */ - t_sample *(x_outvec[MAXSTREAMCHANS]); /* audio vectors */ - t_int x_vecsize; /* vector size for transfers */ - t_int x_state; /* opened, running, or idle */ - - /* parameters to communicate with subthread */ - t_int x_requestcode; /* pending request from parent to I/O thread */ - t_int x_connecterror; /* slot for "errno" return */ - t_int x_streamchannels; /* number of channels in Ogg Vorbis bitstream */ - t_int x_streamrate; /* sample rate of stream */ - - /* buffer stuff */ - t_int x_fifosize; /* buffer size appropriately rounded down */ - t_int x_fifohead; /* index of next byte to get from file */ - t_int x_fifotail; /* index of next byte the ugen will read */ - t_int x_fifobytes; /* number of bytes available in buffer */ - t_int x_eof; /* true if ogg stream has ended */ - t_int x_sigcountdown; /* counter for signalling child for more data */ - t_int x_sigperiod; /* number of ticks per signal */ - t_int x_siginterval; /* number of times per buffer (depends on data rate) */ - - /* ogg/vorbis related stuff */ - 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 to ogg/vorbis */ - - t_int x_vorbis; /* info about encoder status */ - t_int x_sync; /* indicates whether the decoder has been synced */ - t_float x_pages; /* number of pages that have been output to server */ - t_outlet *x_outpages; /* output to send them to */ - - - t_int x_connectstate; /* indicates the state of socket connection */ - t_int x_fd; /* the socket number */ - t_int x_graphic; /* indicates if we show a graphic bar */ - t_float x_resample; /* indicates if we need to resample signal (1 = no resampling) */ - t_int x_recover; /* indicate how to behave on buffer underruns */ - t_int x_disconnect; /* indicates that user want's to disconnect */ - t_int x_samplerate; /* Pd's sample rate */ - - /* server stuff */ - char *x_hostname; /* name or IP of host to connect to */ - char *x_mountpoint; /* mountpoint of ogg-bitstream */ - t_int x_port; /* port number on which the connection is made */ - - /* tread stuff */ - pthread_mutex_t x_mutex; - pthread_cond_t x_requestcondition; - pthread_cond_t x_answercondition; - pthread_t x_childthread; -} t_oggamp; - - /* check if we can read from socket */ -static int oggamp_check_for_data(t_int sock) -{ - - fd_set set; - struct timeval tv; - t_int ret; - - tv.tv_sec = 0; - tv.tv_usec = 20000; - FD_ZERO(&set); - FD_SET(sock, &set); - ret = select(sock + 1, &set, NULL, NULL, &tv); - if (ret > 0) - return 1; - return 0; -} - - /* receive 'size' bytes from server */ -static int oggamp_child_receive(int fd, char *buffer, int size) -{ - int ret = -1; - int i; - - ret = recv(fd, buffer, size, 0); - if(ret < 0) - { - post("oggamp~: receive error" ); - } - return ret; -} - - /* ogg/vorbis decoder sync init */ -static void oggamp_vorbis_sync_init(t_oggamp *x) -{ - ogg_sync_init(&(x->x_oy)); /* Now we can read pages */ - x->x_sync = 1; -} - - /* ogg/vorbis decoder setup */ -static int oggamp_vorbis_init(t_oggamp *x, int fd) -{ - int i; - int result; /* error return code */ - int bytes; /* number of bytes receive returned */ - char *buffer; /* buffer for undecoded ogg vorbis data */ - - if(!x->x_sync)oggamp_vorbis_sync_init(x); /* init the sync */ - - x->x_eos = 0; /* indicate beginning of new stream */ - - /* submit a 4k block to libvorbis' ogg layer */ - buffer = ogg_sync_buffer(&(x->x_oy),READ); - post("oggamp~: prebuffering..."); - bytes = oggamp_child_receive(fd, buffer, READ); - ogg_sync_wrote(&(x->x_oy),bytes); - result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); - - /* we might need more data */ - if(result == -1) - { - post("reading more..."); - buffer = ogg_sync_buffer(&(x->x_oy),READ); - bytes = oggamp_child_receive(fd, buffer, READ); - ogg_sync_wrote(&(x->x_oy),bytes); - result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); - } - /* Get the first page. */ - if(result != 1) - { - /* error case. Must not be Vorbis data */ - error("oggamp~: input does not appear to be an ogg bitstream (error %d)", result); - return -1; - } - - ogg_stream_init(&(x->x_os),ogg_page_serialno(&(x->x_og))); - - vorbis_info_init(&(x->x_vi)); - vorbis_comment_init(&(x->x_vc)); - if(ogg_stream_pagein(&(x->x_os),&(x->x_og))<0){ - /* error; stream version mismatch perhaps */ - error("oggamp~: error reading first page of ogg bitstream data"); - return -1; - } - - if(ogg_stream_packetout(&(x->x_os),&(x->x_op))!=1){ - /* no page? must not be vorbis */ - error("oggamp~: error reading initial header packet"); - return -1; - } - - if(vorbis_synthesis_headerin(&(x->x_vi),&(x->x_vc),&(x->x_op))<0){ - /* error case; not a vorbis header */ - error("oggamp~: this ogg bitstream does not contain Vorbis audio data"); - return -1; - } - - i=0; - while(i<2){ - while(i<2){ - result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); - if(result == 0)break; /* Need more data */ - /* Don't complain about missing or corrupt data yet. We'll - catch it at the packet output phase */ - if(result == 1) - { - ogg_stream_pagein(&(x->x_os),&(x->x_og)); /* we can ignore any errors here - as they'll also become apparent - at packetout */ - while(i<2){ - result = ogg_stream_packetout(&(x->x_os),&(x->x_op)); - if(result==0)break; - if(result<0){ - /* Uh oh; data at some point was corrupted or missing! - We can't tolerate that in a header. Die. */ - error("oggamp~: corrupt secondary header, exiting"); - return -1; - } - vorbis_synthesis_headerin(&(x->x_vi),&(x->x_vc),&(x->x_op)); - i++; - } - } - } - /* no harm in not checking before adding more */ - buffer = ogg_sync_buffer(&(x->x_oy),READ); - /* read in next 4k of data */ - bytes = oggamp_child_receive(fd, buffer, READ); - if(bytes==0 && i<2){ - error("oggamp~: end of stream before finding all Vorbis headers"); - return -1; - } - ogg_sync_wrote(&(x->x_oy),bytes); - } - - /* Throw the comments plus a few lines about the bitstream we're decoding */ - post("oggamp~: reading Ogg Vorbis header..."); - { - char **ptr = x->x_vc.user_comments; - while(*ptr){ - post(" %s",*ptr); - ++ptr; - } - post("oggamp~: bitstream is %d channels @ %ld Hz with %ldkbps", x->x_vi.channels, x->x_vi.rate, x->x_vi.bitrate_nominal / 1000); - x->x_streamchannels = x->x_vi.channels; - x->x_streamrate = x->x_vi.rate; - // x->x_siginterval = 64 / x->x_vi.channels; /* estimate interval in which we need to decode */ - if(x->x_samplerate > x->x_streamrate) /* upsampling */ - { /* we need to use upsampling */ - if(x->x_samplerate % x->x_streamrate) - { - post("oggamp~: upsampling from %ld Hz to %ld Hz not supported !", x->x_vi.rate, x->x_samplerate); - } - else - { - post("oggamp~: upsampling from %ld Hz to %ld Hz", x->x_vi.rate, x->x_samplerate); - x->x_resample = x->x_samplerate / x->x_streamrate; - } - } - else if(x->x_samplerate < x->x_streamrate) - { /* we need to use downsampling */ - if(x->x_streamrate % x->x_samplerate) - { - post("oggamp~: downsampling from %ld Hz to %ld Hz not supported !", x->x_vi.rate, x->x_samplerate); - } - else - { - post("oggamp~: downsampling from %ld Hz to %ld Hz", x->x_vi.rate, x->x_samplerate); - x->x_resample = (t_float)x->x_samplerate / x->x_vi.rate; - } - } - else /* no resampling */ - { - x->x_resample = 1; - } - post("oggamp~: encoded by: %s", x->x_vc.vendor); - } - - /* OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder. */ - vorbis_synthesis_init(&(x->x_vd),&(x->x_vi)); /* central decode state */ - vorbis_block_init(&(x->x_vd),&(x->x_vb)); /* local state */ - x->x_vorbis = 1; - return (1); -} - - /* clear the ogg/vorbis decoder */ -static void oggamp_vorbis_sync_clear(t_oggamp *x) -{ - /* OK, clean up the framer */ - ogg_sync_clear(&(x->x_oy)); - x->x_sync = 0; - post("oggamp~: decoder cleared"); -} - /* deinit the ogg/vorbis decoder */ -static void oggamp_vorbis_deinit(t_oggamp *x) -{ - if(x->x_vorbis) - { - x->x_vorbis = 0; - - /* clean up this logical bitstream; before exit we see if we're - followed by another [chained] */ - ogg_stream_clear(&(x->x_os)); - - /* ogg_page and ogg_packet structs always point to storage in - libvorbis. They're never freed or manipulated directly */ - - vorbis_block_clear(&(x->x_vb)); - vorbis_dsp_clear(&(x->x_vd)); - vorbis_comment_clear(&(x->x_vc)); - vorbis_info_clear(&(x->x_vi)); /* must be called last */ - - post("oggamp~: decoder deinitialised"); - /* only clear completely in case we're going to disconnect */ - /* !! must not be called when receiving chained streams !! */ - if(x->x_disconnect)oggamp_vorbis_sync_clear(x); - } -} - - /* decode ogg/vorbis and receive new data */ -static int oggamp_decode_input(t_oggamp *x, float *buf, int fifohead, int fifosize, int fd) -{ - int i, result; - - float **pcm; /* pointer to decoded float samples */ - char *buffer; /* buffer for ogg vorbis */ - int samples; /* number of samples returned by decoder at each block! */ - int n = 0; /* total number of samples returned by decoder at this call */ - int bytes; /* number of bytes submitted to decoder */ - int position = fifohead; - - - /* the rest is just a straight decode loop until end of stream */ - while(!x->x_eos) - { - result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); - if(result == 0)break; /* need more data */ - if(result < 0) - { /* missing or corrupt data at this page position */ - error("oggamp~: corrupt or missing data in bitstream, continuing..."); - } - else{ - ogg_stream_pagein(&(x->x_os),&(x->x_og)); /* can safely ignore errors at this point */ - while(1) - { - result = ogg_stream_packetout(&(x->x_os),&(x->x_op)); - - if(result==0)break; /* need more data */ - if(result<0) - { /* missing or corrupt data at this page position */ - /* no reason to complain; already complained above */ - }else - { - /* we have a packet. Decode it */ - if(vorbis_synthesis(&(x->x_vb),&(x->x_op))==0) /* test for success! */ - vorbis_synthesis_blockin(&(x->x_vd),&(x->x_vb)); - /* - - **pcm is a multichannel float vector. In stereo, for - example, pcm[0] is left, and pcm[1] is right. samples is - the size of each channel. Convert the float values - (-1.<=range<=1.) to whatever PCM format and write it out */ - - while((samples = vorbis_synthesis_pcmout(&(x->x_vd),&pcm))>0) - { - int j; - int clipflag = 0; - - /* copy into our output buffer */ - for(j = 0; j < samples; j++) - { - for(i = 0; i < x->x_vi.channels; i++) - { - buf[position] = pcm[i][j]; - position = (position + 1) % fifosize; - } - } - - if(clipflag)post("oggamp~: warning: clipping in frame %ld",(long)(x->x_vd.sequence)); - - vorbis_synthesis_read(&(x->x_vd),samples); /* tell libvorbis how - many samples we - actually consumed */ - n += samples; /* sum up the samples we got from decoder */ - } - } - } - if(ogg_page_eos(&(x->x_og)))x->x_eos=1; - } - } - - /* read data from socket */ - if(!x->x_eos) /* read from input until end of stream */ - { - buffer = ogg_sync_buffer(&(x->x_oy), READ); - /* read next 4k of data out of buffer */ - bytes = oggamp_child_receive(fd, buffer, READ); - if(bytes < 0) - { - x->x_eos = 1; /* indicate end of stream */ - return (bytes); - } - ogg_sync_wrote(&(x->x_oy),bytes); - } - else /* we read through all the file... */ - { /* will have to reinit decoder for new file */ - post("oggamp~: end of stream detected"); - return (0); - } - return (n*x->x_vi.channels); -} - - /* connect to shoutcast server */ -static int oggamp_child_connect(char *hostname, char *mountpoint, t_int portno) -{ - struct sockaddr_in server; - struct hostent *hp; - - /* variables used for communication with server */ - char *sptr = NULL; - char request[STRBUF_SIZE]; /* string to be send to server */ - char *url; /* used for relocation */ - fd_set fdset; - struct timeval tv; - t_int sockfd; /* socket to server */ - t_int relocate, numrelocs = 0; - t_int i, ret, rest, nanswers=0; - char *cpoint = NULL; - t_int eof = 0; - - sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sockfd < 0) - { - error("oggamp~: internal error while attempting to open socket"); - return (-1); - } - - /* connect socket using hostname provided in command line */ - server.sin_family = AF_INET; - hp = gethostbyname(hostname); - if (hp == 0) - { - post("oggamp~: bad host?"); - sys_closesocket(sockfd); - return (-1); - } - memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); - - /* assign client port number */ - server.sin_port = htons((unsigned short)portno); - - /* try to connect. */ - post("oggamp~: connecting to http://%s:%d/%s", hostname, portno, mountpoint); - if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) - { - error("oggamp~: connection failed!\n"); - sys_closesocket(sockfd); - return (-1); - } - - /* sheck if we can read/write from/to the socket */ - FD_ZERO( &fdset); - FD_SET( sockfd, &fdset); - tv.tv_sec = 0; /* seconds */ - tv.tv_usec = 500; /* microseconds */ - - ret = select(sockfd + 1, &fdset, NULL, NULL, &tv); - if(ret != 0) - { - error("oggamp~: can not read from socket"); - sys_closesocket(sockfd); - return (-1); - } - - /* build up stuff we need to send to server */ - sprintf(request, "GET /%s HTTP/1.0 \r\nHost: %s\r\nUser-Agent: oggamp~ 0.2\r\nAccept: audio/x-ogg\r\n\r\n", - mountpoint, hostname); - - if ( send(sockfd, request, strlen(request), 0) < 0 ) /* say hello to server */ - { - post("oggamp~: could not contact server..."); - return (-1); - } - - /* read first line of response */ - i = 0; - while(i < STRBUF_SIZE - 1) - { - if(oggamp_check_for_data(sockfd)) - { - if (recv(sockfd, request + i, 1, 0) <= 0) - { - error("oggamp~: could not read from socket, quitting"); - sys_closesocket(sockfd); - return (-1); - } - if (request[i] == '\n') - break; - if (request[i] != '\r') - i++; - } - } - request[i] = '\0'; - - /* time to parse content of the response... */ - if(strstr(request, "HTTP/1.0 200 OK")) /* server is ready */ - { - post("oggamp~: IceCast2 server detected"); - while(!eof) /* read everything we can get */ - { - i = 0; - while(i < STRBUF_SIZE - 1) - { - if(oggamp_check_for_data(sockfd)) - { - if (recv(sockfd, request + i, 1, 0) <= 0) - { - error("oggamp~: could not read from socket, quitting"); - sys_closesocket(sockfd); - return (-1); - } - if(request[i] == '\n') /* leave at end of line */ - break; - if(request[i] == 0x0A) /* leave at end of line */ - break; - if(request[i] != '\r') /* go on until 'return' */ - i++; - } - } - request[i] = '\0'; /* make it a null terminated string */ - - if(sptr = strstr(request, "application/x-ogg")) - { /* check for content type */ - post("oggamp~: Ogg Vorbis stream found"); - } - if(sptr = strstr(request, "ice-name:")) - { /* display ice-name */ - post("oggamp~: \"%s\"", sptr + 10); - } - if(i == 0)eof = 1; /* we got last '\r\n' from server */ - } - } - else /* wrong server or wrong answer */ - { - post("oggamp~: unknown response from server"); - sys_closesocket(sockfd); - return (-1); - } - post("oggamp~: connected to http://%s:%d/%s", hp->h_name, portno, mountpoint); - - return (sockfd); -} - - -static void oggamp_child_dographics(t_oggamp *x) -{ - /* do graphics stuff :: create rectangle */ - if ( x->x_graphic && glist_isvisible( x->x_canvas ) ) - { - sys_vgui(".x%x.c create rectangle %d %d %d %d -fill lightblue -tags %xPBAR\n", - x->x_canvas, x->x_obj.te_xpix, x->x_obj.te_ypix-OBJHEIGHT-1, - x->x_obj.te_xpix + OBJWIDTH, x->x_obj.te_ypix - 1, x ); - } -} - -static void oggamp_child_updategraphics(t_oggamp *x) -{ - /* update buffer status display */ - if(x->x_graphic && glist_isvisible(x->x_canvas)) - { - /* update graphical read status */ - char color[32]; - - sys_vgui(".x%x.c delete rectangle %xSTATUS\n", x->x_canvas, x); - if(x->x_fifobytes < (x->x_fifosize / 8)) - { - strcpy(color, "red"); - } - else - { - strcpy(color, "lightgreen"); - } - sys_vgui(".x%x.c create rectangle %d %d %d %d -fill %s -tags %xSTATUS\n", - x->x_canvas, x->x_obj.te_xpix, x->x_obj.te_ypix-OBJHEIGHT-1, - x->x_obj.te_xpix+((x->x_fifobytes*OBJWIDTH)/x->x_fifosize), - x->x_obj.te_ypix - 1, color, x); - } -} -static void oggamp_child_delgraphics(t_oggamp *x) -{ - if(x->x_graphic) /* delete graphics */ - { - sys_vgui(".x%x.c delete rectangle %xPBAR\n", x->x_canvas, x ); - sys_vgui(".x%x.c delete rectangle %xSTATUS\n", x->x_canvas, x ); - } -} - -static void oggamp_child_disconnect(t_int fd) -{ - sys_closesocket(fd); - post("oggamp~: connection closed"); -} -/************** the child thread which performs data I/O ***********/ - -#if 0 /* set this to get debugging output */ -static void pute(char *s) /* debug routine */ -{ - write(2, s, strlen(s)); -} -#else -#define pute(x) -#endif - -#if 1 -#define oggamp_cond_wait pthread_cond_wait -#define oggamp_cond_signal pthread_cond_signal -#else -#include <sys/time.h> /* debugging version... */ -#include <sys/types.h> -static void oggamp_fakewait(pthread_mutex_t *b) -{ - struct timeval timout; - timout.tv_sec = 0; - timout.tv_usec = 1000000; - pthread_mutex_unlock(b); - select(0, 0, 0, 0, &timout); - pthread_mutex_lock(b); -} - -void oggamp_banana( void) -{ - struct timeval timout; - timout.tv_sec = 0; - timout.tv_usec = 200000; - pute("banana1\n"); - select(0, 0, 0, 0, &timout); - pute("banana2\n"); -} - - -#define oggamp_cond_wait(a,b) oggamp_fakewait(b) -#define oggamp_cond_signal(a) -#endif - -static void *oggamp_child_main(void *zz) -{ - t_oggamp *x = zz; - pute("1\n"); - pthread_mutex_lock(&x->x_mutex); - while (1) - { - int fd, fifohead; - char *buffer; /* Ogg Vorbis data */ - float *buf; /* encoded PCM floats */ - pute("0\n"); - if (x->x_requestcode == REQUEST_NOTHING) - { - pute("wait 2\n"); - oggamp_cond_signal(&x->x_answercondition); - oggamp_cond_wait(&x->x_requestcondition, &x->x_mutex); - pute("3\n"); - } - // connect to Icecast2 server - else if (x->x_requestcode == REQUEST_CONNECT) - { - char boo[80]; - int sysrtn, wantbytes; - - /* copy connect stuff out of the data structure so we can - relinquish the mutex while we're in oggcast_child_connect(). */ - char *hostname = x->x_hostname; - char *mountpoint = x->x_mountpoint; - t_int portno = x->x_port; - x->x_disconnect = 0; - /* alter the request code so that an ensuing "open" will get - noticed. */ - pute("4\n"); - x->x_requestcode = REQUEST_BUSY; - x->x_connecterror = 0; - - /* if there's already a connection open, close it */ - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggamp_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - x->x_fd = -1; - if (x->x_requestcode != REQUEST_BUSY) - goto lost; - } - /* open the socket with the mutex unlocked */ - pthread_mutex_unlock(&x->x_mutex); - fd = oggamp_child_connect(hostname, mountpoint, portno); - pthread_mutex_lock(&x->x_mutex); - pute("5\n"); - /* copy back into the instance structure. */ - x->x_connectstate = 1; - clock_delay(x->x_clock, 0); - x->x_fd = fd; - if (fd < 0) - { - x->x_connecterror = fd; - x->x_eof = 1; - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - pute("connect failed\n"); - goto lost; - } - else - { - /* initialise the decoder */ - if(oggamp_vorbis_init(x, fd) != -1) - { - post("oggamp~: decoder initialised"); - oggamp_child_dographics(x); - } - else - { - post("oggamp~: could not init decoder"); - oggamp_child_disconnect(fd); - post("oggamp~: connection closed due to bitstream error"); - x->x_disconnect = 1; - x->x_fd = -1; - x->x_eof = 1; - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - pute("initialisation failed\n"); - goto lost; - } - } - /* check if another request has been made; if so, field it */ - if (x->x_requestcode != REQUEST_BUSY) - goto lost; - pute("6\n"); - x->x_fifohead = fifohead = 0; - /* set fifosize from bufsize. fifosize must be a - multiple of the number of bytes eaten for each DSP - tick. We pessimistically assume MAXVECSIZE samples - per tick since that could change. There could be a - problem here if the vector size increases while a - stream is being played... */ - x->x_fifosize = x->x_bufsize - (x->x_bufsize % - (x->x_streamchannels * 2)); - /* arrange for the "request" condition to be signalled x->x_siginterval - times per buffer */ - sprintf(boo, "fifosize %d\n", - x->x_fifosize); - pute(boo); - x->x_sigcountdown = x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * x->x_streamchannels * x->x_vecsize)); - - /* in a loop, wait for the fifo to get hungry and feed it */ - while (x->x_requestcode == REQUEST_BUSY) - { - int fifosize = x->x_fifosize; - buf = x->x_buf; - pute("77\n"); - if (x->x_eof) - break; - /* try to get new data from decoder whenever - there is some space at end of buffer */ - if(x->x_fifobytes < fifosize - READSIZE) - { - sprintf(boo, "head %d, tail %d\n", x->x_fifohead, x->x_fifotail); - pute(boo); - - /* we pass x on to the routine since we need the ogg vorbis stuff - to be presend. all other values should not be changed because - mutex is unlocked ! */ - pute("decode... "); - pthread_mutex_unlock(&x->x_mutex); - sysrtn = oggamp_decode_input(x, buf, fifohead, fifosize, fd); - oggamp_child_updategraphics(x); - pthread_mutex_lock(&x->x_mutex); - if (x->x_requestcode != REQUEST_BUSY) - break; - if (sysrtn == 0) - { - if (x->x_eos && !x->x_disconnect) /* got end of stream */ - { - pute("end of stream\n"); - oggamp_vorbis_deinit(x); - if(oggamp_vorbis_init(x, fd) == -1) /* reinit stream */ - { - x->x_state = STATE_IDLE; - x->x_disconnect = 1; - goto quit; - } - } - else if (x->x_eos && x->x_disconnect) /* we're disconnecting */ - { - pute("end of stream: disconnecting\n"); - break; /* go to disconnect */ - } - } - else if (sysrtn < 0) /* got any other error from decoder */ - { - pute("connecterror\n"); - x->x_connecterror = sysrtn; - break; - } - x->x_fifohead = (fifohead + sysrtn) % fifosize; - x->x_fifobytes += sysrtn; - sprintf(boo, "after: head %d, tail %d\n", - x->x_fifohead, x->x_fifotail); - pute(boo); - /* check wether the buffer is filled enough to start streaming */ - } - else /* there is enough data in the buffer :: do nothing */ - { - x->x_state = STATE_STREAM; - pute("wait 7...\n"); - oggamp_cond_signal(&x->x_answercondition); - oggamp_cond_wait(&x->x_requestcondition, &x->x_mutex); - pute("7 done\n"); - continue; - } - pute("8\n"); - fd = x->x_fd; - buf = x->x_buf; - fifohead = x->x_fifohead; - pthread_mutex_unlock(&x->x_mutex); - - /* signal parent in case it's waiting for data */ - oggamp_cond_signal(&x->x_answercondition); - } - -lost: - - if (x->x_requestcode == REQUEST_BUSY) - x->x_requestcode = REQUEST_NOTHING; - /* fell out of read loop: close connection if necessary, - set EOF and signal once more */ - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggamp_vorbis_deinit(x); - oggamp_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - x->x_fd = -1; - oggamp_child_delgraphics(x); - } - oggamp_cond_signal(&x->x_answercondition); - - } - /* reconnect to server */ - else if (x->x_requestcode == REQUEST_RECONNECT) - { - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggamp_vorbis_deinit(x); - oggamp_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - oggamp_child_delgraphics(x); - x->x_fd = -1; - } - /* connect again */ - x->x_requestcode = REQUEST_CONNECT; - - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - oggamp_cond_signal(&x->x_answercondition); - } - /* close connection to server (disconnect) */ - else if (x->x_requestcode == REQUEST_CLOSE) - { -quit: - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggamp_vorbis_deinit(x); - oggamp_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - oggamp_child_delgraphics(x); - x->x_fd = -1; - } - if (x->x_requestcode == REQUEST_CLOSE) - x->x_requestcode = REQUEST_NOTHING; - else if (x->x_requestcode == REQUEST_BUSY) - x->x_requestcode = REQUEST_NOTHING; - else if (x->x_requestcode == REQUEST_CONNECT) - x->x_requestcode = REQUEST_NOTHING; - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - oggamp_cond_signal(&x->x_answercondition); - } - // quit everything - else if (x->x_requestcode == REQUEST_QUIT) - { - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggamp_vorbis_deinit(x); - oggamp_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - x->x_fd = -1; - } - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - x->x_requestcode = REQUEST_NOTHING; - oggamp_cond_signal(&x->x_answercondition); - break; - } - else - { - pute("13\n"); - } - } - pute("thread exit\n"); - pthread_mutex_unlock(&x->x_mutex); - return (0); -} - -/******** the object proper runs in the calling (parent) thread ****/ - -static void oggamp_tick(t_oggamp *x) -{ - outlet_float(x->x_connection, x->x_connectstate); -} - -static void *oggamp_new(t_floatarg fdographics, t_floatarg fnchannels, t_floatarg fbufsize) -{ - t_oggamp *x; - int nchannels = fnchannels, bufsize = fbufsize * 1024, i; - float *buf; - - if (nchannels < 1) - nchannels = 2; /* two channels as default */ - else if (nchannels > MAXSTREAMCHANS) - nchannels = MAXSTREAMCHANS; - /* check / set buffer size */ - if (!bufsize) bufsize = DEFBUFPERCHAN * nchannels; - else if (bufsize < MINBUFSIZE) - bufsize = MINBUFSIZE; - else if (bufsize > MAXBUFSIZE) - bufsize = MAXBUFSIZE; - buf = getbytes(bufsize*sizeof(t_float)); - if (!buf) return (0); - - x = (t_oggamp *)pd_new(oggamp_class); - - for (i = 0; i < nchannels; i++) - outlet_new(&x->x_obj, gensym("signal")); - x->x_noutlets = nchannels; - x->x_connection = outlet_new(&x->x_obj, gensym("float")); - x->x_clock = clock_new(x, (t_method)oggamp_tick); - - pthread_mutex_init(&x->x_mutex, 0); - pthread_cond_init(&x->x_requestcondition, 0); - pthread_cond_init(&x->x_answercondition, 0); - - x->x_vecsize = 2; - x->x_disconnect = 0; - x->x_state = STATE_IDLE; - x->x_canvas = canvas_getcurrent(); - x->x_streamchannels = 2; - x->x_fd = -1; - x->x_buf = buf; - x->x_bufsize = bufsize; - x->x_siginterval = 16; /* signal 16 times per buffer */ - x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_fifobytes = x->x_requestcode = 0; - - x->x_connectstate = 0; /* indicating state of connection */ - - x->x_samplerate = x->x_streamrate = sys_getsr(); - x->x_resample = 0; - x->x_vorbis = 0; - x->x_sync = 0; - x->x_recover = -1; /* just ignore buffer underruns */ - - /* graphical buffer status display */ - x->x_graphic = (int)fdographics; - x->x_canvas = canvas_getcurrent(); - - post(oggamp_version); - post("oggamp~: set buffer to %dk bytes", bufsize/1024); - - /* start child thread */ - pthread_create(&x->x_childthread, 0, oggamp_child_main, x); - return (x); -} - -static t_int *oggamp_perform(t_int *w) -{ - t_oggamp *x = (t_oggamp *)(w[1]); - t_int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i, j, r; - t_float *fp; - t_float *sp = x->x_buf; - t_float *buffer = x->x_buf; - t_float resample = x->x_resample; - t_int skip = (t_int)(1.0 / resample); - - if (x->x_state == STATE_STREAM) - { - t_int wantbytes, getbytes, havebytes, nchannels, streamchannels = x->x_streamchannels; - - pthread_mutex_lock(&x->x_mutex); - - /* get 'getbytes' bytes from input buffer, convert them to - 'wantbytes' which is the number of bytes after resampling */ - getbytes = streamchannels * vecsize; /* number of bytes we get after resampling */ - wantbytes = (t_float)getbytes / resample; /* we need vecsize bytes per channel */ - havebytes = x->x_fifobytes; - - /* check for error */ - if(havebytes < wantbytes) - { - if(x->x_connecterror) - { /* report error and close connection */ - pd_error(x, "dsp: error %d", x->x_connecterror); - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_CLOSE; - x->x_disconnect = 1; - oggamp_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); - } - if(!x->x_disconnect) /* it's not due to disconnect */ - { - if(x->x_recover == 0) /* disconnect */ - { - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_CLOSE; - x->x_disconnect = 1; - oggamp_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); - } - else if(x->x_recover == 1) /* reconnect */ - { - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_RECONNECT; - x->x_disconnect = 1; - oggamp_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); - } - else /* resume */ - { - x->x_state = STATE_IDLE; - x->x_disconnect = 0; - oggamp_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); - } - } - goto idle; - } - - /* output audio */ - sp += x->x_fifotail; /* go to actual audio position */ - - /* resample if necessary */ - if (resample > 1.0) /* upsampling */ - { - int parent = vecsize / resample; /* how many samples to read from buffer */ - for(j = 0; j < parent; j++) - { - for(i = 0; i < streamchannels; i++) - { /* copy same sample several times */ - for (r = 0; r < resample; r++) - *buffer++ = *sp; - sp++; /* get next sample from stream */ - } - } - } - else if (resample < 1.0) /* downsampling */ - { - int parent = vecsize * skip;/* how many samples to read from buffer */ - for(j = 0; j < parent; j++) - { - for(i = 0; i < streamchannels; i++) - { - *buffer++ = *sp; /* get one sample */ - sp += skip; /* skip next few samples in stream */ - } - } - } - else if (resample == 1.0) /* no resampling */ - { /* copy without any changes */ - for(i = 0; i < getbytes; i++)*buffer++ = *sp++; - } - buffer -= getbytes; /* reset to beginning of buffer */ - - - if(noutlets == streamchannels) - { /* normal output */ - for(j = 0; j < vecsize; j++) - { - for(i = 0; i < noutlets; i++) - { - x->x_outvec[i][j] = *buffer++; - } - } - } - else if((noutlets / 2) == streamchannels) - { /* mono to stereo conversion */ - for(j = 0; j < vecsize; j++) - { - for(i = 0; i < noutlets; i++) - { - x->x_outvec[i][j] = *buffer; - } - buffer++; - } - } - else if((noutlets * 2) == streamchannels) - { /* stereo to mono conversion */ - for(j = 0; j < vecsize; j++) - { - for(i = 0; i < streamchannels; i++) - { - x->x_outvec[i/2][j] += (float) (*buffer++ * 0.5); - } - } - } - else goto idle; - - - x->x_fifotail += wantbytes; - x->x_fifobytes -= wantbytes; - if (x->x_fifotail >= x->x_fifosize) - x->x_fifotail = 0; - /* signal the child thread */ - if ((--x->x_sigcountdown) <= 0) - { - oggamp_cond_signal(&x->x_requestcondition); - x->x_sigcountdown = x->x_sigperiod; - } - pthread_mutex_unlock(&x->x_mutex); - } - else - { - idle: - for (i = 0; i < noutlets; i++) - for (j = vecsize, fp = x->x_outvec[i]; j--; ) - *fp++ = 0; - } - - return (w+2); -} - - -static void oggamp_disconnect(t_oggamp *x) -{ - /* LATER rethink whether you need the mutex just to set a variable? */ - pthread_mutex_lock(&x->x_mutex); - x->x_disconnect = 1; - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_CLOSE; - oggamp_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); -} - - - /* connect method. Called as: - connect <hostname or IP> <mountpoint> <portnumber> - */ - -static void oggamp_connect(t_oggamp *x, t_symbol *s, int argc, t_atom *argv) -{ - t_symbol *hostsym = atom_getsymbolarg(0, argc, argv); - t_symbol *mountsym = atom_getsymbolarg(1, argc, argv); - t_float portno = atom_getfloatarg(2, argc, argv); - if (!*hostsym->s_name) /* check for hostname */ - return; - if (!portno) /* check wether the portnumber is specified */ - portno = 8000; /* ...assume port 8000 as standard */ - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd == -1) - { - x->x_hostname = hostsym->s_name; - x->x_mountpoint = mountsym->s_name; - x->x_port = portno; - x->x_requestcode = REQUEST_CONNECT; - /* empty buffer */ - x->x_fifotail = 0; - x->x_fifohead = 0; - x->x_fifobytes = 0; - x->x_streamchannels = 2; - x->x_eof = 0; - x->x_connecterror = 0; - x->x_state = STATE_STARTUP; - x->x_disconnect = 0; - oggamp_cond_signal(&x->x_requestcondition); - } - else post("oggamp~: already connected"); - pthread_mutex_unlock(&x->x_mutex); -} - - /* connect using url like "http://localhost:8000/mountpoint" */ -static void oggamp_connect_url(t_oggamp *x, t_symbol *url) -{ - char *hname, *port; - char *h, *p; - char *hostptr; - char *r_hostptr; - char *pathptr; - char *portptr; - char *p0; - char *defaultportstr = "8000"; - t_int stringlength; - t_int portno; - - /* strip http:// or ftp:// */ - p = url->s_name; - if (strncmp(p, "http://", 7) == 0) - p += 7; - - if (strncmp(p, "ftp://", 6) == 0) - p += 6; - - hostptr = p; - while (*p && *p != '/') /* look for end of hostname:port */ - p++; - p++; /* also skip '/' */ - pathptr = p; - - r_hostptr = --p; - while (*p && hostptr < p && *p != ':' && *p != ']') /* split at ':' */ - p--; - - if (!*p || p < hostptr || *p != ':') { - portptr = NULL; - } - else{ - portptr = p + 1; - r_hostptr = p - 1; - } - if (*hostptr == '[' && *r_hostptr == ']') { - hostptr++; - r_hostptr--; - } - - stringlength = r_hostptr - hostptr + 1; - h = getbytes(stringlength + 1); - if (h == NULL) { - hname = NULL; - port = NULL; - pathptr = NULL; - } - strncpy(h, hostptr, stringlength); - *(h+stringlength) = '\0'; - hname = h; /* the hostname */ - - if (portptr) { - stringlength = (pathptr - portptr); - if(!stringlength) portptr = NULL; - } - if (portptr == NULL) { - portptr = defaultportstr; - stringlength = strlen(defaultportstr); - } - p0 = getbytes(stringlength + 1); - if (p0 == NULL) { - freebytes(h, stringlength + 1); - hname = NULL; - port = NULL; - pathptr = NULL; - } - strncpy(p0, portptr, stringlength); - *(p0 + stringlength) = '\0'; - - for (p = p0; *p && isdigit((unsigned char) *p); p++) ; - - *p = '\0'; - port = (unsigned char *) p0; - /* convert port from string to int */ - portno = (int)strtol(port, NULL, 10); - freebytes(p0, stringlength + 1); - /* set values and signal child to connect */ - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd == -1) - { - x->x_hostname = hname; - x->x_mountpoint = pathptr; - x->x_port = portno; - x->x_requestcode = REQUEST_CONNECT; - x->x_fifotail = 0; - x->x_fifohead = 0; - x->x_fifobytes = 0; - x->x_streamchannels = 2; - x->x_eof = 0; - x->x_connecterror = 0; - x->x_state = STATE_STARTUP; - oggamp_cond_signal(&x->x_requestcondition); - } - else post("oggamp~: already connected"); - pthread_mutex_unlock(&x->x_mutex); -} - -static void oggamp_float(t_oggamp *x, t_floatarg f) -{ - if (f != 0) - { - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd == -1) - { - x->x_requestcode = REQUEST_CONNECT; - - x->x_fifotail = 0; - x->x_fifohead = 0; - x->x_fifobytes = 0; - x->x_streamchannels = 2; - x->x_eof = 0; - x->x_connecterror = 0; - x->x_state = STATE_STARTUP; - oggamp_cond_signal(&x->x_requestcondition); - } - else post("oggamp~: already connected"); - pthread_mutex_unlock(&x->x_mutex); - } - else oggamp_disconnect(x); -} - -static void oggamp_dsp(t_oggamp *x, t_signal **sp) -{ - int i, noutlets = x->x_noutlets; - pthread_mutex_lock(&x->x_mutex); - x->x_vecsize = sp[0]->s_n; - - x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * x->x_streamchannels * x->x_vecsize)); - for (i = 0; i < noutlets; i++) - x->x_outvec[i] = sp[i]->s_vec; - pthread_mutex_unlock(&x->x_mutex); - dsp_add(oggamp_perform, 1, x); -} - -static void oggamp_print(t_oggamp *x) -{ - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd >= 0) - { - post("oggamp~: connected to http://%s:%d/%s", x->x_hostname, x->x_port, x->x_mountpoint); - post("oggamp~: bitstream is %d channels @ %ld Hz with %ldkbps nominal bitrate", - x->x_streamchannels, x->x_streamrate, x->x_vi.bitrate_nominal / 1000); - } else post("oggamp~: not connected"); - if(x->x_recover == 0) - post("oggamp~: recover mode set to \"disconnect\" (0)"); - else if(x->x_recover == 1) - post("oggamp~: recover mode set to \"reconnect\" (1)"); - else if(x->x_recover == -1) - post("oggamp~: recover mode set to \"resume\" (-1)"); - pthread_mutex_unlock(&x->x_mutex); -} - - /* set behavior for buffer underruns */ -static void oggamp_recover(t_oggamp *x, t_floatarg f) -{ - pthread_mutex_lock(&x->x_mutex); - if(f <= -1) - { /* mute audio and try to fill buffer again: the default */ - post("oggamp~: set recover mode to \"resume\" (-1)"); - f = -1; - } - else if(f >= 1) - { /* reconnect to server */ - post("oggamp~: set recover mode to \"reconnect\" (1)"); - f = 1; - } - else - { /* disconnect from server */ - post("oggamp~: set recover mode to \"disconnect\" (0)"); - f = 0; - } - x->x_recover = f; - pthread_mutex_unlock(&x->x_mutex); -} - -static void oggamp_free(t_oggamp *x) -{ - /* request QUIT and wait for acknowledge */ - void *threadrtn; - pthread_mutex_lock(&x->x_mutex); - x->x_requestcode = REQUEST_QUIT; - x->x_disconnect = 1; - post("stopping oggamp thread..."); - oggamp_cond_signal(&x->x_requestcondition); - while (x->x_requestcode != REQUEST_NOTHING) - { - post("signalling..."); - oggamp_cond_signal(&x->x_requestcondition); - oggamp_cond_wait(&x->x_answercondition, &x->x_mutex); - } - pthread_mutex_unlock(&x->x_mutex); - if (pthread_join(x->x_childthread, &threadrtn)) - error("oggamp_free: join failed"); - post("... done."); - - pthread_cond_destroy(&x->x_requestcondition); - pthread_cond_destroy(&x->x_answercondition); - pthread_mutex_destroy(&x->x_mutex); - freebytes(x->x_buf, x->x_bufsize*sizeof(t_float)); - clock_free(x->x_clock); -} - -void oggamp_tilde_setup(void) -{ - oggamp_class = class_new(gensym("oggamp~"), (t_newmethod)oggamp_new, - (t_method)oggamp_free, sizeof(t_oggamp), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); - class_addfloat(oggamp_class, (t_method)oggamp_float); - class_addmethod(oggamp_class, (t_method)oggamp_disconnect, gensym("disconnect"), 0); - class_addmethod(oggamp_class, (t_method)oggamp_dsp, gensym("dsp"), 0); - class_addmethod(oggamp_class, (t_method)oggamp_connect, gensym("connect"), A_GIMME, 0); - class_addmethod(oggamp_class, (t_method)oggamp_connect_url, gensym("connecturl"), A_SYMBOL, 0); - class_addmethod(oggamp_class, (t_method)oggamp_recover, gensym("recover"), A_FLOAT, 0); - class_addmethod(oggamp_class, (t_method)oggamp_print, gensym("print"), 0); - class_sethelpsymbol(oggamp_class, gensym("help-oggamp~.pd")); +/* ------------------------- oggamp~ ------------------------------------------ */
+/* */
+/* Tilde object to receive an Ogg Vorbis stream from an IceCast2 server. */
+/* Written by Olaf Matthes <olaf.matthes@gmx.de> */
+/* Get source at http://www.akustische-kunst.de/puredata/ */
+/* */
+/* Graphical buffer status display written by Yves Degoyon. */
+/* */
+/* Thanks for hours (maybe days?) of beta testing to Oliver Thuns. */
+/* */
+/* 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/ */
+/* */
+/* ---------------------------------------------------------------------------- */
+
+ /* Pd includes */
+#include "m_pd.h"
+#include "g_canvas.h"
+ /* Vorbis includes */
+#include "codec.h"
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.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> /* for 'write' in pute-function only */
+#include <winsock.h>
+#include <winbase.h>
+#endif
+
+#ifdef NT
+#pragma warning( disable : 4244 )
+#pragma warning( disable : 4305 )
+#endif
+
+#ifdef UNIX
+#define sys_closesocket close
+#endif
+#ifdef NT
+#define sys_closesocket closesocket
+#endif
+
+
+/************************* oggamp~ object ******************************/
+
+/*
+ Each instance of oggamp~ owns a "child" thread for doing the data
+ transfer. The parent thread signals the child each time:
+ (1) a connection wants opening or closing;
+ (2) we've eaten another 1/16 of the shared buffer (so that the
+ child thread should check if it's time to receive some more.)
+ The child signals the parent whenever a receive has completed. Signalling
+ is done by setting "conditions" and putting data in mutex-controlled common
+ areas.
+*/
+
+
+#define REQUEST_NOTHING 0
+#define REQUEST_CONNECT 1
+#define REQUEST_CLOSE 2
+#define REQUEST_QUIT 3
+#define REQUEST_BUSY 4
+#define REQUEST_DATA 5
+#define REQUEST_RECONNECT 6
+
+#define STATE_IDLE 0
+#define STATE_STARTUP 1 /* connecting and filling the buffer */
+#define STATE_STREAM 2 /* streaming aund audio output */
+
+#define READSIZE 65536 /* _encoded_ data we request from buffer */
+#define READ 1024 /* amount of data we pass on to decoder */
+#define DEFBUFPERCHAN 262144 /* audio output buffer default: 256k */
+#define MINBUFSIZE (4 * READSIZE)
+#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */
+#define STRBUF_SIZE 1024 /* char received from server on startup */
+#define OBJWIDTH 68 /* width of buffer statis display */
+#define OBJHEIGHT 10 /* height of buffer statis display */
+#define MAXSTREAMCHANS 250 /* maximum number of channels */
+
+static char *oggamp_version = "oggamp~: ogg/vorbis streaming client version 0.3, written by Olaf Matthes";
+
+static t_class *oggamp_class;
+
+typedef struct _oggamp
+{
+ t_object x_obj;
+ t_canvas *x_canvas; /* remember canvas */
+ t_outlet *x_connection;
+ t_clock *x_clock;
+
+ t_float *x_buf; /* audio data buffer */
+ t_int x_bufsize; /* buffer size in bytes */
+ t_int x_noutlets; /* number of audio outlets */
+ t_sample **x_outvec; /* audio vectors */
+ t_int x_vecsize; /* vector size for transfers */
+ t_int x_state; /* opened, running, or idle */
+
+ /* parameters to communicate with subthread */
+ t_int x_requestcode; /* pending request from parent to I/O thread */
+ t_int x_connecterror; /* slot for "errno" return */
+ t_int x_streamchannels; /* number of channels in Ogg Vorbis bitstream */
+ t_int x_streamrate; /* sample rate of stream */
+
+ /* buffer stuff */
+ t_int x_fifosize; /* buffer size appropriately rounded down */
+ t_int x_fifohead; /* index of next byte to get from file */
+ t_int x_fifotail; /* index of next byte the ugen will read */
+ t_int x_fifobytes; /* number of bytes available in buffer */
+ t_int x_eof; /* true if ogg stream has ended */
+ t_int x_sigcountdown; /* counter for signalling child for more data */
+ t_int x_sigperiod; /* number of ticks per signal */
+ t_int x_siginterval; /* number of times per buffer (depends on data rate) */
+
+ /* ogg/vorbis related stuff */
+ 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 to ogg/vorbis */
+
+ t_int x_vorbis; /* info about encoder status */
+ t_int x_sync; /* indicates whether the decoder has been synced */
+ t_float x_pages; /* number of pages that have been output to server */
+ t_outlet *x_outpages; /* output to send them to */
+
+
+ t_int x_connectstate; /* indicates the state of socket connection */
+ t_int x_fd; /* the socket number */
+ t_int x_graphic; /* indicates if we show a graphic bar */
+ t_float x_resample; /* indicates if we need to resample signal (1 = no resampling) */
+ t_int x_recover; /* indicate how to behave on buffer underruns */
+ t_int x_disconnect; /* indicates that user want's to disconnect */
+ t_int x_samplerate; /* Pd's sample rate */
+
+ /* server stuff */
+ char *x_hostname; /* name or IP of host to connect to */
+ char *x_mountpoint; /* mountpoint of ogg-bitstream */
+ t_int x_port; /* port number on which the connection is made */
+
+ /* tread stuff */
+ pthread_mutex_t x_mutex;
+ pthread_cond_t x_requestcondition;
+ pthread_cond_t x_answercondition;
+ pthread_t x_childthread;
+} t_oggamp;
+
+ /* check if we can read from socket */
+static int oggamp_check_for_data(t_int sock)
+{
+
+ fd_set set;
+ struct timeval tv;
+ t_int ret;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 20000;
+ FD_ZERO(&set);
+ FD_SET(sock, &set);
+ ret = select(sock + 1, &set, NULL, NULL, &tv);
+ if (ret > 0)
+ return 1;
+ return 0;
+}
+
+ /* receive 'size' bytes from server */
+static int oggamp_child_receive(int fd, char *buffer, int size)
+{
+ int ret = -1;
+ int i;
+
+ ret = recv(fd, buffer, size, 0);
+ if(ret < 0)
+ {
+ post("oggamp~: receive error" );
+ }
+ return ret;
+}
+
+ /* ogg/vorbis decoder sync init */
+static void oggamp_vorbis_sync_init(t_oggamp *x)
+{
+ ogg_sync_init(&(x->x_oy)); /* Now we can read pages */
+ x->x_sync = 1;
+}
+
+ /* ogg/vorbis decoder setup */
+static int oggamp_vorbis_init(t_oggamp *x, int fd)
+{
+ int i;
+ int result; /* error return code */
+ int bytes; /* number of bytes receive returned */
+ char *buffer; /* buffer for undecoded ogg vorbis data */
+
+ if(!x->x_sync)oggamp_vorbis_sync_init(x); /* init the sync */
+
+ x->x_eos = 0; /* indicate beginning of new stream */
+
+ /* submit a 4k block to libvorbis' ogg layer */
+ buffer = ogg_sync_buffer(&(x->x_oy),4096);
+ post("oggamp~: prebuffering...");
+ bytes = oggamp_child_receive(fd, buffer, 4096);
+ ogg_sync_wrote(&(x->x_oy),bytes);
+ result = ogg_sync_pageout(&(x->x_oy),&(x->x_og));
+
+ /* we might need more data */
+ if(result == -1)
+ {
+ post("reading more...");
+ buffer = ogg_sync_buffer(&(x->x_oy),4096);
+ bytes = oggamp_child_receive(fd, buffer, 4096);
+ ogg_sync_wrote(&(x->x_oy),bytes);
+ result = ogg_sync_pageout(&(x->x_oy),&(x->x_og));
+ }
+ /* Get the first page. */
+ if(result != 1)
+ {
+ /* error case. Must not be Vorbis data */
+ error("oggamp~: input does not appear to be an ogg bitstream (error %d)", result);
+ return -1;
+ }
+
+ ogg_stream_init(&(x->x_os),ogg_page_serialno(&(x->x_og)));
+
+ vorbis_info_init(&(x->x_vi));
+ vorbis_comment_init(&(x->x_vc));
+ if(ogg_stream_pagein(&(x->x_os),&(x->x_og))<0){
+ /* error; stream version mismatch perhaps */
+ error("oggamp~: error reading first page of ogg bitstream data");
+ return -1;
+ }
+
+ if(ogg_stream_packetout(&(x->x_os),&(x->x_op))!=1){
+ /* no page? must not be vorbis */
+ error("oggamp~: error reading initial header packet");
+ return -1;
+ }
+
+ if(vorbis_synthesis_headerin(&(x->x_vi),&(x->x_vc),&(x->x_op))<0){
+ /* error case; not a vorbis header */
+ error("oggamp~: this ogg bitstream does not contain Vorbis audio data");
+ return -1;
+ }
+
+ i=0;
+ while(i<2){
+ while(i<2){
+ result = ogg_sync_pageout(&(x->x_oy),&(x->x_og));
+ if(result == 0)break; /* Need more data */
+ /* Don't complain about missing or corrupt data yet. We'll
+ catch it at the packet output phase */
+ if(result == 1)
+ {
+ ogg_stream_pagein(&(x->x_os),&(x->x_og)); /* we can ignore any errors here
+ as they'll also become apparent
+ at packetout */
+ while(i<2){
+ result = ogg_stream_packetout(&(x->x_os),&(x->x_op));
+ if(result==0)break;
+ if(result<0){
+ /* Uh oh; data at some point was corrupted or missing!
+ We can't tolerate that in a header. Die. */
+ error("oggamp~: corrupt secondary header, exiting");
+ return -1;
+ }
+ vorbis_synthesis_headerin(&(x->x_vi),&(x->x_vc),&(x->x_op));
+ i++;
+ }
+ }
+ }
+ /* no harm in not checking before adding more */
+ buffer = ogg_sync_buffer(&(x->x_oy),READ);
+ /* read in next 4k of data */
+ bytes = oggamp_child_receive(fd, buffer, READ);
+ if(bytes==0 && i<2){
+ error("oggamp~: end of stream before finding all Vorbis headers");
+ return -1;
+ }
+ ogg_sync_wrote(&(x->x_oy),bytes);
+ }
+
+ /* Throw the comments plus a few lines about the bitstream we're decoding */
+ post("oggamp~: reading Ogg Vorbis header...");
+ {
+ char **ptr = x->x_vc.user_comments;
+ while(*ptr){
+ post(" %s",*ptr);
+ ++ptr;
+ }
+ post("oggamp~: bitstream is %d channels @ %ld Hz with %ldkbps", x->x_vi.channels, x->x_vi.rate, x->x_vi.bitrate_nominal / 1000);
+ x->x_streamchannels = x->x_vi.channels;
+ x->x_streamrate = x->x_vi.rate;
+ if(x->x_samplerate != x->x_streamrate) /* upsampling */
+ { /* we would need to use upsampling */
+ post("oggamp~: resampling from %ld Hz to %ld Hz not supported !", x->x_vi.rate, x->x_samplerate);
+ return (-1);
+ }
+ post("oggamp~: encoded by: %s", x->x_vc.vendor);
+ }
+
+ /* OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder. */
+ vorbis_synthesis_init(&(x->x_vd),&(x->x_vi)); /* central decode state */
+ vorbis_block_init(&(x->x_vd),&(x->x_vb)); /* local state */
+ x->x_vorbis = 1;
+ return (1);
+}
+
+ /* clear the ogg/vorbis decoder */
+static void oggamp_vorbis_sync_clear(t_oggamp *x)
+{
+ /* OK, clean up the framer */
+ ogg_sync_clear(&(x->x_oy));
+ x->x_sync = 0;
+ post("oggamp~: decoder cleared");
+}
+ /* deinit the ogg/vorbis decoder */
+static void oggamp_vorbis_deinit(t_oggamp *x)
+{
+ if(x->x_vorbis)
+ {
+ x->x_vorbis = 0;
+
+ /* clean up this logical bitstream; before exit we see if we're
+ followed by another [chained] */
+ ogg_stream_clear(&(x->x_os));
+
+ /* ogg_page and ogg_packet structs always point to storage in
+ libvorbis. They're never freed or manipulated directly */
+
+ vorbis_block_clear(&(x->x_vb));
+ vorbis_dsp_clear(&(x->x_vd));
+ vorbis_comment_clear(&(x->x_vc));
+ vorbis_info_clear(&(x->x_vi)); /* must be called last */
+
+ post("oggamp~: decoder deinitialised");
+ /* only clear completely in case we're going to disconnect */
+ /* !! must not be called when receiving chained streams !! */
+ if(x->x_disconnect)oggamp_vorbis_sync_clear(x);
+ }
+}
+
+ /* decode ogg/vorbis and receive new data */
+static int oggamp_decode_input(t_oggamp *x, float *buf, int fifohead, int fifosize, int fd)
+{
+ int i, result;
+
+ float **pcm; /* pointer to decoded float samples */
+ char *buffer; /* buffer for ogg vorbis */
+ int samples; /* number of samples returned by decoder at each block! */
+ int n = 0; /* total number of samples returned by decoder at this call */
+ int bytes; /* number of bytes submitted to decoder */
+ int position = fifohead;
+ int streamchannels = x->x_streamchannels;
+ int channels = x->x_noutlets;
+
+
+ /* the rest is just a straight decode loop until end of stream */
+ while(!x->x_eos)
+ {
+ result = ogg_sync_pageout(&(x->x_oy),&(x->x_og));
+ if(result == 0)break; /* need more data */
+ if(result < 0)
+ { /* missing or corrupt data at this page position */
+ error("oggamp~: corrupt or missing data in bitstream, continuing...");
+ }
+ else{
+ ogg_stream_pagein(&(x->x_os),&(x->x_og)); /* can safely ignore errors at this point */
+ while(1)
+ {
+ result = ogg_stream_packetout(&(x->x_os),&(x->x_op));
+
+ if(result==0)break; /* need more data */
+ if(result<0)
+ { /* missing or corrupt data at this page position */
+ /* no reason to complain; already complained above */
+ }else
+ {
+ /* we have a packet. Decode it */
+ if(vorbis_synthesis(&(x->x_vb),&(x->x_op))==0) /* test for success! */
+ vorbis_synthesis_blockin(&(x->x_vd),&(x->x_vb));
+ /*
+
+ **pcm is a multichannel float vector. In stereo, for
+ example, pcm[0] is left, and pcm[1] is right. samples is
+ the size of each channel. Convert the float values
+ (-1.<=range<=1.) to whatever PCM format and write it out */
+
+ while((samples = vorbis_synthesis_pcmout(&(x->x_vd),&pcm))>0)
+ {
+ int j;
+
+ /* copy into our output buffer */
+ if(streamchannels >= channels)
+ {
+ for(j = 0; j < samples; j++)
+ {
+ for(i = 0; i < channels; i++)
+ {
+ buf[position + i] = pcm[i][j];
+ }
+ position = (position + channels) % fifosize;
+ }
+ vorbis_synthesis_read(&(x->x_vd),samples); /* tell libvorbis how
+ many samples we
+ actually consumed */
+ n += samples; /* sum up the samples we got from decoder */
+ }
+ else
+ {
+ for(j = 0; j < samples; j++)
+ {
+ /* copy the channels we have */
+ for(i = 0; i < streamchannels; i++)
+ {
+ buf[position + i] = pcm[i][j];
+ }
+ /* fill rest of buffer with silence */
+ for(i = streamchannels; i < channels; i++)
+ {
+ buf[position + i] = 0.;
+ }
+ position = (position + channels) % fifosize;
+ }
+ vorbis_synthesis_read(&(x->x_vd),samples); /* tell libvorbis how
+ many samples we
+ actually consumed */
+ n += samples; /* sum up the samples we got from decoder */
+ }
+ }
+ }
+ }
+ if(ogg_page_eos(&(x->x_og)))x->x_eos=1;
+ }
+ }
+
+ /* read data from socket */
+ if(!x->x_eos) /* read from input until end of stream */
+ {
+ buffer = ogg_sync_buffer(&(x->x_oy), READ);
+ /* read next 4k of data out of buffer */
+ bytes = oggamp_child_receive(fd, buffer, READ);
+ if(bytes < 0)
+ {
+ x->x_eos = 1; /* indicate end of stream */
+ return (bytes);
+ }
+ ogg_sync_wrote(&(x->x_oy),bytes);
+ }
+ else /* we read through all the file... */
+ { /* will have to reinit decoder for new file */
+ post("oggamp~: end of stream detected");
+ return (0);
+ }
+ return (n*x->x_vi.channels);
+}
+
+ /* connect to shoutcast server */
+static int oggamp_child_connect(char *hostname, char *mountpoint, t_int portno)
+{
+ struct sockaddr_in server;
+ struct hostent *hp;
+
+ /* variables used for communication with server */
+ char *sptr = NULL;
+ char request[STRBUF_SIZE]; /* string to be send to server */
+ char *url; /* used for relocation */
+ fd_set fdset;
+ struct timeval tv;
+ t_int sockfd; /* socket to server */
+ t_int relocate, numrelocs = 0;
+ t_int i, ret, rest, nanswers=0;
+ char *cpoint = NULL;
+ t_int eof = 0;
+
+ sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sockfd < 0)
+ {
+ error("oggamp~: internal error while attempting to open socket");
+ return (-1);
+ }
+
+ /* connect socket using hostname provided in command line */
+ server.sin_family = AF_INET;
+ hp = gethostbyname(hostname);
+ if (hp == 0)
+ {
+ post("oggamp~: bad host?");
+ sys_closesocket(sockfd);
+ return (-1);
+ }
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+
+ /* assign client port number */
+ server.sin_port = htons((unsigned short)portno);
+
+ /* try to connect. */
+ post("oggamp~: connecting to http://%s:%d/%s", hostname, portno, mountpoint);
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
+ {
+ error("oggamp~: connection failed!\n");
+ sys_closesocket(sockfd);
+ return (-1);
+ }
+
+ /* sheck if we can read/write from/to the socket */
+ FD_ZERO( &fdset);
+ FD_SET( sockfd, &fdset);
+ tv.tv_sec = 0; /* seconds */
+ tv.tv_usec = 500; /* microseconds */
+
+ ret = select(sockfd + 1, &fdset, NULL, NULL, &tv);
+ if(ret != 0)
+ {
+ error("oggamp~: can not read from socket");
+ sys_closesocket(sockfd);
+ return (-1);
+ }
+
+ /* build up stuff we need to send to server */
+ sprintf(request, "GET /%s HTTP/1.0 \r\nHost: %s\r\nUser-Agent: oggamp~ 0.2\r\nAccept: audio/x-ogg\r\n\r\n",
+ mountpoint, hostname);
+
+ if ( send(sockfd, request, strlen(request), 0) < 0 ) /* say hello to server */
+ {
+ post("oggamp~: could not contact server...");
+ return (-1);
+ }
+
+ /* read first line of response */
+ i = 0;
+ while(i < STRBUF_SIZE - 1)
+ {
+ if(oggamp_check_for_data(sockfd))
+ {
+ if (recv(sockfd, request + i, 1, 0) <= 0)
+ {
+ error("oggamp~: could not read from socket, quitting");
+ sys_closesocket(sockfd);
+ return (-1);
+ }
+ if (request[i] == '\n')
+ break;
+ if (request[i] != '\r')
+ i++;
+ }
+ }
+ request[i] = '\0';
+
+ /* time to parse content of the response... */
+ if(strstr(request, "HTTP/1.0 200 OK")) /* server is ready */
+ {
+ post("oggamp~: IceCast2 server detected");
+ while(!eof) /* read everything we can get */
+ {
+ i = 0;
+ while(i < STRBUF_SIZE - 1)
+ {
+ if(oggamp_check_for_data(sockfd))
+ {
+ if (recv(sockfd, request + i, 1, 0) <= 0)
+ {
+ error("oggamp~: could not read from socket, quitting");
+ sys_closesocket(sockfd);
+ return (-1);
+ }
+ if(request[i] == '\n') /* leave at end of line */
+ break;
+ if(request[i] == 0x0A) /* leave at end of line */
+ break;
+ if(request[i] != '\r') /* go on until 'return' */
+ i++;
+ }
+ }
+ request[i] = '\0'; /* make it a null terminated string */
+
+ if(sptr = strstr(request, "application/x-ogg"))
+ { /* check for content type */
+ post("oggamp~: Ogg Vorbis stream found");
+ }
+ if(sptr = strstr(request, "ice-name:"))
+ { /* display ice-name */
+ post("oggamp~: \"%s\"", sptr + 10);
+ }
+ if(i == 0)eof = 1; /* we got last '\r\n' from server */
+ }
+ }
+ else /* wrong server or wrong answer */
+ {
+ post("oggamp~: unknown response from server");
+ sys_closesocket(sockfd);
+ return (-1);
+ }
+ post("oggamp~: connected to http://%s:%d/%s", hp->h_name, portno, mountpoint);
+
+ return (sockfd);
+}
+
+
+static void oggamp_child_dographics(t_oggamp *x)
+{
+ /* do graphics stuff :: create rectangle */
+ if ( x->x_graphic && glist_isvisible( x->x_canvas ) )
+ {
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill lightblue -tags %xPBAR\n",
+ x->x_canvas, x->x_obj.te_xpix, x->x_obj.te_ypix-OBJHEIGHT-1,
+ x->x_obj.te_xpix + OBJWIDTH, x->x_obj.te_ypix - 1, x );
+ }
+}
+
+static void oggamp_child_updategraphics(t_oggamp *x)
+{
+ /* update buffer status display */
+ if(x->x_graphic && glist_isvisible(x->x_canvas))
+ {
+ /* update graphical read status */
+ char color[32];
+
+ sys_vgui(".x%x.c delete rectangle %xSTATUS\n", x->x_canvas, x);
+ if(x->x_fifobytes < (x->x_fifosize / 8))
+ {
+ strcpy(color, "red");
+ }
+ else
+ {
+ strcpy(color, "lightgreen");
+ }
+ sys_vgui(".x%x.c create rectangle %d %d %d %d -fill %s -tags %xSTATUS\n",
+ x->x_canvas, x->x_obj.te_xpix, x->x_obj.te_ypix-OBJHEIGHT-1,
+ x->x_obj.te_xpix+((x->x_fifobytes*OBJWIDTH)/x->x_fifosize),
+ x->x_obj.te_ypix - 1, color, x);
+ }
+}
+static void oggamp_child_delgraphics(t_oggamp *x)
+{
+ if(x->x_graphic) /* delete graphics */
+ {
+ sys_vgui(".x%x.c delete rectangle %xPBAR\n", x->x_canvas, x );
+ sys_vgui(".x%x.c delete rectangle %xSTATUS\n", x->x_canvas, x );
+ }
+}
+
+static void oggamp_child_disconnect(t_int fd)
+{
+ sys_closesocket(fd);
+ post("oggamp~: connection closed");
+}
+/************** the child thread which performs data I/O ***********/
+
+#if 0 /* set this to get debugging output */
+static void pute(char *s) /* debug routine */
+{
+ write(2, s, strlen(s));
+}
+#else
+#define pute(x)
+#endif
+
+#if 1
+#define oggamp_cond_wait pthread_cond_wait
+#define oggamp_cond_signal pthread_cond_signal
+#else
+#include <sys/time.h> /* debugging version... */
+#include <sys/types.h>
+static void oggamp_fakewait(pthread_mutex_t *b)
+{
+ struct timeval timout;
+ timout.tv_sec = 0;
+ timout.tv_usec = 1000000;
+ pthread_mutex_unlock(b);
+ select(0, 0, 0, 0, &timout);
+ pthread_mutex_lock(b);
+}
+
+void oggamp_banana( void)
+{
+ struct timeval timout;
+ timout.tv_sec = 0;
+ timout.tv_usec = 200000;
+ pute("banana1\n");
+ select(0, 0, 0, 0, &timout);
+ pute("banana2\n");
+}
+
+
+#define oggamp_cond_wait(a,b) oggamp_fakewait(b)
+#define oggamp_cond_signal(a)
+#endif
+
+static void *oggamp_child_main(void *zz)
+{
+ t_oggamp *x = zz;
+ pute("1\n");
+ pthread_mutex_lock(&x->x_mutex);
+ while (1)
+ {
+ int fd, fifohead;
+ char *buffer; /* Ogg Vorbis data */
+ float *buf; /* encoded PCM floats */
+ pute("0\n");
+ if (x->x_requestcode == REQUEST_NOTHING)
+ {
+ pute("wait 2\n");
+ oggamp_cond_signal(&x->x_answercondition);
+ oggamp_cond_wait(&x->x_requestcondition, &x->x_mutex);
+ pute("3\n");
+ }
+ // connect to Icecast2 server
+ else if (x->x_requestcode == REQUEST_CONNECT)
+ {
+ char boo[80];
+ int sysrtn, wantbytes;
+
+ /* copy connect stuff out of the data structure so we can
+ relinquish the mutex while we're in oggcast_child_connect(). */
+ char *hostname = x->x_hostname;
+ char *mountpoint = x->x_mountpoint;
+ t_int portno = x->x_port;
+ x->x_disconnect = 0;
+ /* alter the request code so that an ensuing "open" will get
+ noticed. */
+ pute("4\n");
+ x->x_requestcode = REQUEST_BUSY;
+ x->x_connecterror = 0;
+
+ /* if there's already a connection open, close it */
+ if (x->x_fd >= 0)
+ {
+ fd = x->x_fd;
+ pthread_mutex_unlock(&x->x_mutex);
+ oggamp_child_disconnect(fd);
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_connectstate = 0;
+ clock_delay(x->x_clock, 0);
+ x->x_fd = -1;
+ if (x->x_requestcode != REQUEST_BUSY)
+ goto lost;
+ }
+ /* open the socket with the mutex unlocked */
+ pthread_mutex_unlock(&x->x_mutex);
+ fd = oggamp_child_connect(hostname, mountpoint, portno);
+ pthread_mutex_lock(&x->x_mutex);
+ pute("5\n");
+ /* copy back into the instance structure. */
+ x->x_connectstate = 1;
+ clock_delay(x->x_clock, 0);
+ x->x_fd = fd;
+ if (fd < 0)
+ {
+ x->x_connecterror = fd;
+ x->x_eof = 1;
+ x->x_connectstate = 0;
+ clock_delay(x->x_clock, 0);
+ pute("connect failed\n");
+ goto lost;
+ }
+ else
+ {
+ /* initialise the decoder */
+ if(oggamp_vorbis_init(x, fd) != -1)
+ {
+ post("oggamp~: decoder initialised");
+ oggamp_child_dographics(x);
+ }
+ else
+ {
+ post("oggamp~: could not init decoder");
+ oggamp_child_disconnect(fd);
+ post("oggamp~: connection closed due to bitstream error");
+ x->x_disconnect = 1;
+ x->x_fd = -1;
+ x->x_eof = 1;
+ x->x_connectstate = 0;
+ clock_delay(x->x_clock, 0);
+ pute("initialisation failed\n");
+ goto lost;
+ }
+ }
+ /* check if another request has been made; if so, field it */
+ if (x->x_requestcode != REQUEST_BUSY)
+ goto lost;
+ pute("6\n");
+ x->x_fifohead = fifohead = 0;
+ /* set fifosize from bufsize. fifosize must be a
+ multiple of the number of bytes eaten for each DSP
+ tick. We pessimistically assume MAXVECSIZE samples
+ per tick since that could change. There could be a
+ problem here if the vector size increases while a
+ stream is being played... */
+ x->x_fifosize = x->x_bufsize - (x->x_bufsize %
+ (x->x_streamchannels * 2));
+ /* arrange for the "request" condition to be signalled x->x_siginterval
+ times per buffer */
+ sprintf(boo, "fifosize %d\n",
+ x->x_fifosize);
+ pute(boo);
+ x->x_sigcountdown = x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * x->x_noutlets * x->x_vecsize));
+
+ /* in a loop, wait for the fifo to get hungry and feed it */
+ while (x->x_requestcode == REQUEST_BUSY)
+ {
+ int fifosize = x->x_fifosize;
+ buf = x->x_buf;
+ pute("77\n");
+ if (x->x_eof)
+ break;
+ /* try to get new data from decoder whenever
+ there is some space at end of buffer */
+ if(x->x_fifobytes < fifosize - READSIZE)
+ {
+ sprintf(boo, "head %d, tail %d\n", x->x_fifohead, x->x_fifotail);
+ pute(boo);
+
+ /* we pass x on to the routine since we need the ogg vorbis stuff
+ to be presend. all other values should not be changed because
+ mutex is unlocked ! */
+ pute("decode... ");
+ pthread_mutex_unlock(&x->x_mutex);
+ sysrtn = oggamp_decode_input(x, buf, fifohead, fifosize, fd);
+ oggamp_child_updategraphics(x);
+ pthread_mutex_lock(&x->x_mutex);
+ if (x->x_requestcode != REQUEST_BUSY)
+ break;
+ if (sysrtn == 0)
+ {
+ if (x->x_eos && !x->x_disconnect) /* got end of stream */
+ {
+ pute("end of stream\n");
+ oggamp_vorbis_deinit(x);
+ if(oggamp_vorbis_init(x, fd) == -1) /* reinit stream */
+ {
+ x->x_state = STATE_IDLE;
+ x->x_disconnect = 1;
+ goto quit;
+ }
+ }
+ else if (x->x_eos && x->x_disconnect) /* we're disconnecting */
+ {
+ pute("end of stream: disconnecting\n");
+ break; /* go to disconnect */
+ }
+ }
+ else if (sysrtn < 0) /* got any other error from decoder */
+ {
+ pute("connecterror\n");
+ x->x_connecterror = sysrtn;
+ break;
+ }
+ x->x_fifohead = (fifohead + sysrtn) % fifosize;
+ x->x_fifobytes += sysrtn;
+ sprintf(boo, "after: head %d, tail %d\n",
+ x->x_fifohead, x->x_fifotail);
+ pute(boo);
+ /* check wether the buffer is filled enough to start streaming */
+ }
+ else /* there is enough data in the buffer :: do nothing */
+ {
+ x->x_state = STATE_STREAM;
+ pute("wait 7...\n");
+ oggamp_cond_signal(&x->x_answercondition);
+ oggamp_cond_wait(&x->x_requestcondition, &x->x_mutex);
+ pute("7 done\n");
+ continue;
+ }
+ pute("8\n");
+ fd = x->x_fd;
+ buf = x->x_buf;
+ fifohead = x->x_fifohead;
+
+ /* signal parent in case it's waiting for data */
+ oggamp_cond_signal(&x->x_answercondition);
+ }
+
+lost:
+
+ if (x->x_requestcode == REQUEST_BUSY)
+ x->x_requestcode = REQUEST_NOTHING;
+ /* fell out of read loop: close connection if necessary,
+ set EOF and signal once more */
+ if (x->x_fd >= 0)
+ {
+ fd = x->x_fd;
+ pthread_mutex_unlock(&x->x_mutex);
+ oggamp_vorbis_deinit(x);
+ oggamp_child_disconnect(fd);
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_fd = -1;
+ oggamp_child_delgraphics(x);
+ }
+ oggamp_cond_signal(&x->x_answercondition);
+
+ }
+ /* reconnect to server */
+ else if (x->x_requestcode == REQUEST_RECONNECT)
+ {
+ if (x->x_fd >= 0)
+ {
+ fd = x->x_fd;
+ pthread_mutex_unlock(&x->x_mutex);
+ oggamp_vorbis_deinit(x);
+ oggamp_child_disconnect(fd);
+ pthread_mutex_lock(&x->x_mutex);
+ oggamp_child_delgraphics(x);
+ x->x_fd = -1;
+ }
+ /* connect again */
+ x->x_requestcode = REQUEST_CONNECT;
+
+ x->x_connectstate = 0;
+ clock_delay(x->x_clock, 0);
+ oggamp_cond_signal(&x->x_answercondition);
+ }
+ /* close connection to server (disconnect) */
+ else if (x->x_requestcode == REQUEST_CLOSE)
+ {
+quit:
+ if (x->x_fd >= 0)
+ {
+ fd = x->x_fd;
+ pthread_mutex_unlock(&x->x_mutex);
+ oggamp_vorbis_deinit(x);
+ oggamp_child_disconnect(fd);
+ pthread_mutex_lock(&x->x_mutex);
+ oggamp_child_delgraphics(x);
+ x->x_fd = -1;
+ }
+ if (x->x_requestcode == REQUEST_CLOSE)
+ x->x_requestcode = REQUEST_NOTHING;
+ else if (x->x_requestcode == REQUEST_BUSY)
+ x->x_requestcode = REQUEST_NOTHING;
+ else if (x->x_requestcode == REQUEST_CONNECT)
+ x->x_requestcode = REQUEST_NOTHING;
+ x->x_connectstate = 0;
+ clock_delay(x->x_clock, 0);
+ oggamp_cond_signal(&x->x_answercondition);
+ }
+ // quit everything
+ else if (x->x_requestcode == REQUEST_QUIT)
+ {
+ if (x->x_fd >= 0)
+ {
+ fd = x->x_fd;
+ pthread_mutex_unlock(&x->x_mutex);
+ oggamp_vorbis_deinit(x);
+ oggamp_child_disconnect(fd);
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_fd = -1;
+ }
+ x->x_connectstate = 0;
+ clock_delay(x->x_clock, 0);
+ x->x_requestcode = REQUEST_NOTHING;
+ oggamp_cond_signal(&x->x_answercondition);
+ break;
+ }
+ else
+ {
+ pute("13\n");
+ }
+ }
+ pute("thread exit\n");
+ pthread_mutex_unlock(&x->x_mutex);
+ return (0);
+}
+
+/******** the object proper runs in the calling (parent) thread ****/
+
+static void oggamp_tick(t_oggamp *x)
+{
+ outlet_float(x->x_connection, x->x_connectstate);
+}
+
+static void *oggamp_new(t_floatarg fdographics, t_floatarg fnchannels, t_floatarg fbufsize)
+{
+ t_oggamp *x;
+ int nchannels = fnchannels, bufsize = fbufsize * 1024, i;
+ float *buf;
+
+ if (nchannels < 1)
+ nchannels = 2; /* two channels as default */
+ else if (nchannels > MAXSTREAMCHANS)
+ nchannels = MAXSTREAMCHANS;
+ /* check / set buffer size */
+ if (!bufsize) bufsize = DEFBUFPERCHAN * nchannels;
+ else if (bufsize < MINBUFSIZE)
+ bufsize = MINBUFSIZE;
+ else if (bufsize > MAXBUFSIZE)
+ bufsize = MAXBUFSIZE;
+ buf = getbytes(bufsize*sizeof(t_float));
+ if (!buf) return (0);
+
+ x = (t_oggamp *)pd_new(oggamp_class);
+
+ for (i = 0; i < nchannels; i++)
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_noutlets = nchannels;
+ x->x_connection = outlet_new(&x->x_obj, gensym("float"));
+ x->x_clock = clock_new(x, (t_method)oggamp_tick);
+ x->x_outvec = (t_sample **)getbytes(nchannels * sizeof(t_sample *));
+
+ pthread_mutex_init(&x->x_mutex, 0);
+ pthread_cond_init(&x->x_requestcondition, 0);
+ pthread_cond_init(&x->x_answercondition, 0);
+
+ x->x_vecsize = 2;
+ x->x_disconnect = 0;
+ x->x_state = STATE_IDLE;
+ x->x_canvas = canvas_getcurrent();
+ x->x_streamchannels = 2;
+ x->x_fd = -1;
+ x->x_buf = buf;
+ x->x_bufsize = bufsize;
+ x->x_siginterval = 16; /* signal 16 times per buffer */
+ x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_fifobytes = x->x_requestcode = 0;
+
+ x->x_connectstate = 0; /* indicating state of connection */
+
+ x->x_samplerate = x->x_streamrate = sys_getsr();
+ x->x_resample = 0;
+ x->x_vorbis = 0;
+ x->x_sync = 0;
+ x->x_recover = -1; /* just ignore buffer underruns */
+
+ /* graphical buffer status display */
+ x->x_graphic = (int)fdographics;
+ x->x_canvas = canvas_getcurrent();
+
+ post(oggamp_version);
+ post("oggamp~: set buffer to %dk bytes", bufsize/1024);
+
+ /* start child thread */
+ pthread_create(&x->x_childthread, 0, oggamp_child_main, x);
+ return (x);
+}
+
+static t_int *oggamp_perform(t_int *w)
+{
+ t_oggamp *x = (t_oggamp *)(w[1]);
+ t_int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i, j, r;
+ t_float *fp;
+ t_float *buffer = x->x_buf;
+
+ if (x->x_state == STATE_STREAM)
+ {
+ t_int wantbytes, getbytes, havebytes, nchannels, streamchannels = x->x_streamchannels;
+
+ pthread_mutex_lock(&x->x_mutex);
+
+ /* get 'getbytes' bytes from input buffer, convert them to
+ 'wantbytes' which is the number of bytes after resampling */
+ wantbytes = noutlets * vecsize; /* number of bytes we get after resampling */
+ havebytes = x->x_fifobytes;
+
+ /* check for error */
+ if(havebytes < wantbytes)
+ {
+ if(x->x_connecterror)
+ { /* report error and close connection */
+ pd_error(x, "dsp: error %d", x->x_connecterror);
+ x->x_state = STATE_IDLE;
+ x->x_requestcode = REQUEST_CLOSE;
+ x->x_disconnect = 1;
+ oggamp_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+ }
+ if(!x->x_disconnect) /* it's not due to disconnect */
+ {
+ if(x->x_recover == 0) /* disconnect */
+ {
+ x->x_state = STATE_IDLE;
+ x->x_requestcode = REQUEST_CLOSE;
+ x->x_disconnect = 1;
+ oggamp_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+ }
+ else if(x->x_recover == 1) /* reconnect */
+ {
+ x->x_state = STATE_IDLE;
+ x->x_requestcode = REQUEST_RECONNECT;
+ x->x_disconnect = 1;
+ oggamp_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+ }
+ else /* resume */
+ {
+ x->x_state = STATE_IDLE;
+ x->x_disconnect = 0;
+ oggamp_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+ }
+ }
+ goto idle;
+ }
+
+ /* output audio */
+ buffer += x->x_fifotail; /* go to actual audio position */
+
+ for(j = 0; j < vecsize; j++)
+ {
+ for(i = 0; i < noutlets; i++)
+ {
+ x->x_outvec[i][j] = *buffer++;
+ }
+ }
+
+
+ x->x_fifotail += wantbytes;
+ x->x_fifobytes -= wantbytes;
+ if (x->x_fifotail >= x->x_fifosize)
+ x->x_fifotail = 0;
+ /* signal the child thread */
+ if ((--x->x_sigcountdown) <= 0)
+ {
+ oggamp_cond_signal(&x->x_requestcondition);
+ x->x_sigcountdown = x->x_sigperiod;
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ }
+ else
+ {
+ idle:
+ for (i = 0; i < noutlets; i++)
+ for (j = vecsize, fp = x->x_outvec[i]; j--; )
+ *fp++ = 0;
+ }
+
+ return (w+2);
+}
+
+
+static void oggamp_disconnect(t_oggamp *x)
+{
+ /* LATER rethink whether you need the mutex just to set a variable? */
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_disconnect = 1;
+ x->x_state = STATE_IDLE;
+ x->x_requestcode = REQUEST_CLOSE;
+ oggamp_cond_signal(&x->x_requestcondition);
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+
+ /* connect method. Called as:
+ connect <hostname or IP> <mountpoint> <portnumber>
+ */
+
+static void oggamp_connect(t_oggamp *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *hostsym = atom_getsymbolarg(0, argc, argv);
+ t_symbol *mountsym = atom_getsymbolarg(1, argc, argv);
+ t_float portno = atom_getfloatarg(2, argc, argv);
+ if (!*hostsym->s_name) /* check for hostname */
+ return;
+ if (!portno) /* check wether the portnumber is specified */
+ portno = 8000; /* ...assume port 8000 as standard */
+ pthread_mutex_lock(&x->x_mutex);
+ if(x->x_fd == -1)
+ {
+ x->x_hostname = hostsym->s_name;
+ x->x_mountpoint = mountsym->s_name;
+ x->x_port = portno;
+ x->x_requestcode = REQUEST_CONNECT;
+ /* empty buffer */
+ x->x_fifotail = 0;
+ x->x_fifohead = 0;
+ x->x_fifobytes = 0;
+ x->x_streamchannels = 2;
+ x->x_eof = 0;
+ x->x_connecterror = 0;
+ x->x_state = STATE_STARTUP;
+ x->x_disconnect = 0;
+ oggamp_cond_signal(&x->x_requestcondition);
+ }
+ else post("oggamp~: already connected");
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+ /* connect using url like "http://localhost:8000/mountpoint" */
+static void oggamp_connect_url(t_oggamp *x, t_symbol *url)
+{
+ char *hname, *port;
+ char *h, *p;
+ char *hostptr;
+ char *r_hostptr;
+ char *pathptr;
+ char *portptr;
+ char *p0;
+ char *defaultportstr = "8000";
+ t_int stringlength;
+ t_int portno;
+
+ /* strip http:// or ftp:// */
+ p = url->s_name;
+ if (strncmp(p, "http://", 7) == 0)
+ p += 7;
+
+ if (strncmp(p, "ftp://", 6) == 0)
+ p += 6;
+
+ hostptr = p;
+ while (*p && *p != '/') /* look for end of hostname:port */
+ p++;
+ p++; /* also skip '/' */
+ pathptr = p;
+
+ r_hostptr = --p;
+ while (*p && hostptr < p && *p != ':' && *p != ']') /* split at ':' */
+ p--;
+
+ if (!*p || p < hostptr || *p != ':') {
+ portptr = NULL;
+ }
+ else{
+ portptr = p + 1;
+ r_hostptr = p - 1;
+ }
+ if (*hostptr == '[' && *r_hostptr == ']') {
+ hostptr++;
+ r_hostptr--;
+ }
+
+ stringlength = r_hostptr - hostptr + 1;
+ h = getbytes(stringlength + 1);
+ if (h == NULL) {
+ hname = NULL;
+ port = NULL;
+ pathptr = NULL;
+ }
+ strncpy(h, hostptr, stringlength);
+ *(h+stringlength) = '\0';
+ hname = h; /* the hostname */
+
+ if (portptr) {
+ stringlength = (pathptr - portptr);
+ if(!stringlength) portptr = NULL;
+ }
+ if (portptr == NULL) {
+ portptr = defaultportstr;
+ stringlength = strlen(defaultportstr);
+ }
+ p0 = getbytes(stringlength + 1);
+ if (p0 == NULL) {
+ freebytes(h, stringlength + 1);
+ hname = NULL;
+ port = NULL;
+ pathptr = NULL;
+ }
+ strncpy(p0, portptr, stringlength);
+ *(p0 + stringlength) = '\0';
+
+ for (p = p0; *p && isdigit((unsigned char) *p); p++) ;
+
+ *p = '\0';
+ port = (unsigned char *) p0;
+ /* convert port from string to int */
+ portno = (int)strtol(port, NULL, 10);
+ freebytes(p0, stringlength + 1);
+ /* set values and signal child to connect */
+ pthread_mutex_lock(&x->x_mutex);
+ if(x->x_fd == -1)
+ {
+ x->x_hostname = hname;
+ x->x_mountpoint = pathptr;
+ x->x_port = portno;
+ x->x_requestcode = REQUEST_CONNECT;
+ x->x_fifotail = 0;
+ x->x_fifohead = 0;
+ x->x_fifobytes = 0;
+ x->x_streamchannels = 2;
+ x->x_eof = 0;
+ x->x_connecterror = 0;
+ x->x_state = STATE_STARTUP;
+ oggamp_cond_signal(&x->x_requestcondition);
+ }
+ else post("oggamp~: already connected");
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void oggamp_float(t_oggamp *x, t_floatarg f)
+{
+ if (f != 0)
+ {
+ pthread_mutex_lock(&x->x_mutex);
+ if(x->x_fd == -1)
+ {
+ x->x_requestcode = REQUEST_CONNECT;
+
+ x->x_fifotail = 0;
+ x->x_fifohead = 0;
+ x->x_fifobytes = 0;
+ x->x_streamchannels = 2;
+ x->x_eof = 0;
+ x->x_connecterror = 0;
+ x->x_state = STATE_STARTUP;
+ oggamp_cond_signal(&x->x_requestcondition);
+ }
+ else post("oggamp~: already connected");
+ pthread_mutex_unlock(&x->x_mutex);
+ }
+ else oggamp_disconnect(x);
+}
+
+static void oggamp_dsp(t_oggamp *x, t_signal **sp)
+{
+ int i, noutlets = x->x_noutlets;
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_vecsize = sp[0]->s_n;
+
+ x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * x->x_streamchannels * x->x_vecsize));
+ for (i = 0; i < noutlets; i++)
+ x->x_outvec[i] = sp[i]->s_vec;
+ pthread_mutex_unlock(&x->x_mutex);
+ dsp_add(oggamp_perform, 1, x);
+}
+
+static void oggamp_print(t_oggamp *x)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ if(x->x_fd >= 0)
+ {
+ post("oggamp~: connected to http://%s:%d/%s", x->x_hostname, x->x_port, x->x_mountpoint);
+ post("oggamp~: bitstream is %d channels @ %ld Hz with %ldkbps nominal bitrate",
+ x->x_streamchannels, x->x_streamrate, x->x_vi.bitrate_nominal / 1000);
+ } else post("oggamp~: not connected");
+ if(x->x_recover == 0)
+ post("oggamp~: recover mode set to \"disconnect\" (0)");
+ else if(x->x_recover == 1)
+ post("oggamp~: recover mode set to \"reconnect\" (1)");
+ else if(x->x_recover == -1)
+ post("oggamp~: recover mode set to \"resume\" (-1)");
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+ /* set behavior for buffer underruns */
+static void oggamp_recover(t_oggamp *x, t_floatarg f)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ if(f <= -1)
+ { /* mute audio and try to fill buffer again: the default */
+ post("oggamp~: set recover mode to \"resume\" (-1)");
+ f = -1;
+ }
+ else if(f >= 1)
+ { /* reconnect to server */
+ post("oggamp~: set recover mode to \"reconnect\" (1)");
+ f = 1;
+ }
+ else
+ { /* disconnect from server */
+ post("oggamp~: set recover mode to \"disconnect\" (0)");
+ f = 0;
+ }
+ x->x_recover = f;
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void oggamp_free(t_oggamp *x)
+{
+ /* request QUIT and wait for acknowledge */
+ void *threadrtn;
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_requestcode = REQUEST_QUIT;
+ x->x_disconnect = 1;
+ post("stopping oggamp thread...");
+ oggamp_cond_signal(&x->x_requestcondition);
+ while (x->x_requestcode != REQUEST_NOTHING)
+ {
+ post("signalling...");
+ oggamp_cond_signal(&x->x_requestcondition);
+ oggamp_cond_wait(&x->x_answercondition, &x->x_mutex);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ if (pthread_join(x->x_childthread, &threadrtn))
+ error("oggamp_free: join failed");
+ post("... done.");
+
+ pthread_cond_destroy(&x->x_requestcondition);
+ pthread_cond_destroy(&x->x_answercondition);
+ pthread_mutex_destroy(&x->x_mutex);
+ freebytes(x->x_buf, x->x_bufsize*sizeof(t_float));
+ freebytes(x->x_outvec, x->x_noutlets * sizeof(t_sample *));
+ clock_free(x->x_clock);
+}
+
+void oggamp_tilde_setup(void)
+{
+ oggamp_class = class_new(gensym("oggamp~"), (t_newmethod)oggamp_new,
+ (t_method)oggamp_free, sizeof(t_oggamp), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
+ class_addfloat(oggamp_class, (t_method)oggamp_float);
+ class_addmethod(oggamp_class, (t_method)oggamp_disconnect, gensym("disconnect"), 0);
+ class_addmethod(oggamp_class, (t_method)oggamp_dsp, gensym("dsp"), 0);
+ class_addmethod(oggamp_class, (t_method)oggamp_connect, gensym("connect"), A_GIMME, 0);
+ class_addmethod(oggamp_class, (t_method)oggamp_connect_url, gensym("connecturl"), A_SYMBOL, 0);
+ class_addmethod(oggamp_class, (t_method)oggamp_recover, gensym("recover"), A_FLOAT, 0);
+ class_addmethod(oggamp_class, (t_method)oggamp_print, gensym("print"), 0);
+ class_sethelpsymbol(oggamp_class, gensym("help-oggamp~.pd"));
}
\ No newline at end of file diff --git a/oggamp~/readme b/oggamp~/readme index 26dd10d..4e7d8b7 100644 --- a/oggamp~/readme +++ b/oggamp~/readme @@ -1,87 +1,87 @@ -Version 0.2 -copyright (c) 2002 by Olaf Matthes - -oggamp~ is an ogg/vorbis streaming client external for pd (by Miller -Puckette) that connects to an IceCast2 server. -Graphical buffer status display written by Yves Degoyon (ydegoyon@free.fr). - - -To run oggamp~ place the file oggamp~.dll for win or oggamp~.pd_linux -in the directory of our patch or start pd with '-lib oggamp~' flag. - -To compile oggamp~ on Linux get the ogg/vorbice library from -http://www.vorbis.com/. -You have to modify the makefile to make it point to the place where the -ogg/vorbis library is. - - -This software is published under LGPL terms. - -This is software with ABSOLUTELY NO WARRANTY. -Use it at your OWN RISK. It's possible to damage e.g. hardware or your hearing -due to a bug or for other reasons. - -***************************************************************************** - -oggamp~ uses the ogg/vorbice library to encode audio data. -The latest version of ogg/vorbis can be found at http://www.vorbice.com/ - -Below is the original copyright information taken from the ogg/vorbis library: - - -Copyright (c) 2001, Xiphophorus - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiphophorus nor the names of its contributors -may be used to endorse or promote products derived from this software -without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -***************************************************************************** - -Usage: - -To run oggamp~ innormal mode, just use [oggamp~] or, to get the buffer status -displayed, use [oggamp~ 1]. - -Message "connect <host> <mountpoint> <port>" connects to an IceCast2 server. -Note that no response about succesfull connection is send by the server. All -messages in the pd console window about connection status depend on the ability -to receive data from the server. -Use "connecturl <url>" to use url-like server adresses (like http://host:post/ -stream.ogg). - -Known bugs and other things: -- pd halts for a moment when oggamp~ connects to the server. This results in a - short audio drop out of sound currently played back. -- resampling not jet supported -- playback does not stop on a buffer underrun -- oggamp~ disconnects at end of stream, i.e. it is not possible to play back - files streamed one after another without manual reconnect - - -Latest version can be found at: -http://www.akustische-kunst.de/puredata/ - +Version 0.2
+copyright (c) 2002 by Olaf Matthes
+
+oggamp~ is an ogg/vorbis streaming client external for pd (by Miller
+Puckette) that connects to an IceCast2 server.
+Graphical buffer status display written by Yves Degoyon (ydegoyon@free.fr).
+
+
+To run oggamp~ place the file oggamp~.dll for win or oggamp~.pd_linux
+in the directory of our patch or start pd with '-lib oggamp~' flag.
+
+To compile oggamp~ on Linux get the ogg/vorbice library from
+http://www.vorbis.com/.
+You have to modify the makefile to make it point to the place where the
+ogg/vorbis library is.
+
+
+This software is published under LGPL terms.
+
+This is software with ABSOLUTELY NO WARRANTY.
+Use it at your OWN RISK. It's possible to damage e.g. hardware or your hearing
+due to a bug or for other reasons.
+
+*****************************************************************************
+
+oggamp~ uses the ogg/vorbice library to encode audio data.
+The latest version of ogg/vorbis can be found at http://www.vorbice.com/
+
+Below is the original copyright information taken from the ogg/vorbis library:
+
+
+Copyright (c) 2001, Xiphophorus
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiphophorus nor the names of its contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*****************************************************************************
+
+Usage:
+
+To run oggamp~ innormal mode, just use [oggamp~] or, to get the buffer status
+displayed, use [oggamp~ 1].
+
+Message "connect <host> <mountpoint> <port>" connects to an IceCast2 server.
+Note that no response about succesfull connection is send by the server. All
+messages in the pd console window about connection status depend on the ability
+to receive data from the server.
+Use "connecturl <url>" to use url-like server adresses (like http://host:post/
+stream.ogg).
+
+Known bugs and other things:
+- pd halts for a moment when oggamp~ connects to the server. This results in a
+ short audio drop out of sound currently played back.
+- resampling not jet supported
+- playback does not stop on a buffer underrun
+- oggamp~ disconnects at end of stream, i.e. it is not possible to play back
+ files streamed one after another without manual reconnect
+
+
+Latest version can be found at:
+http://www.akustische-kunst.de/puredata/
+
Please report any bugs to olaf.matthes@gmx.de!
\ No newline at end of file |