diff options
author | Thomas Grill <xovo@users.sourceforge.net> | 2003-04-23 10:36:25 +0000 |
---|---|---|
committer | Thomas Grill <xovo@users.sourceforge.net> | 2003-04-23 10:36:25 +0000 |
commit | 6d4b0233efc1a8b1860bf275a2d42b282021a57a (patch) | |
tree | cc68998343273bb2b775ce14756e1e19a097d540 /oggamp~ | |
parent | 2979b062ddcbdc9f2c78fe410a322b470e4a29db (diff) |
check-in of packaged version 0.23 for Olaf....
svn path=/trunk/externals/pdogg/; revision=590
Diffstat (limited to 'oggamp~')
-rw-r--r-- | oggamp~/HISTORY | 80 | ||||
-rw-r--r-- | oggamp~/codec.h | 466 | ||||
-rw-r--r-- | oggamp~/help-oggamp~.pd | 108 | ||||
-rw-r--r-- | oggamp~/oggamp~.c | 2982 | ||||
-rw-r--r-- | oggamp~/readme | 172 |
5 files changed, 1904 insertions, 1904 deletions
diff --git a/oggamp~/HISTORY b/oggamp~/HISTORY index e0cceda..f21a233 100644 --- a/oggamp~/HISTORY +++ b/oggamp~/HISTORY @@ -1,40 +1,40 @@ -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.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 9365ea3..d986a9b 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.1 2002-11-26 10:51:49 ggeiger 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.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 + diff --git a/oggamp~/help-oggamp~.pd b/oggamp~/help-oggamp~.pd index 9f30cb8..b0bb504 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 452532e..d421e18 100644 --- a/oggamp~/oggamp~.c +++ b/oggamp~/oggamp~.c @@ -1,1491 +1,1491 @@ -/* ------------------------- 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 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")); +}
\ No newline at end of file diff --git a/oggamp~/readme b/oggamp~/readme index 4e7d8b7..26dd10d 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 |