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 | |
parent | 2979b062ddcbdc9f2c78fe410a322b470e4a29db (diff) |
check-in of packaged version 0.23 for Olaf....
svn path=/trunk/externals/pdogg/; revision=590
-rw-r--r-- | makefile | 3 | ||||
-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 | ||||
-rw-r--r-- | oggcast~/HISTORY | 133 | ||||
-rw-r--r-- | oggcast~/codec.h | 466 | ||||
-rw-r--r-- | oggcast~/help-oggcast~.pd | 122 | ||||
-rw-r--r-- | oggcast~/oggcast~.c | 2695 | ||||
-rw-r--r-- | oggcast~/readme | 230 | ||||
-rw-r--r-- | oggcast~/vorbisenc.h | 186 | ||||
-rw-r--r-- | oggread~/codec.h | 466 | ||||
-rw-r--r-- | oggread~/help-oggread~.pd | 78 | ||||
-rw-r--r-- | oggread~/oggread~.c | 828 | ||||
-rw-r--r-- | oggread~/readme | 148 | ||||
-rw-r--r-- | oggwrite~/codec.h | 466 | ||||
-rw-r--r-- | oggwrite~/help-oggwrite~.pd | 118 | ||||
-rw-r--r-- | oggwrite~/oggwrite~.c | 1502 | ||||
-rw-r--r-- | oggwrite~/readme | 218 | ||||
-rw-r--r-- | oggwrite~/vorbisenc.h | 186 | ||||
-rw-r--r-- | pdogg.c | 92 | ||||
-rw-r--r-- | readme | 156 |
23 files changed, 6009 insertions, 5892 deletions
@@ -10,7 +10,7 @@ pd_nt: $(NAME).dll .SUFFIXES: .dll PDNTCFLAGS = /W3 /WX /MD /O2 /G6 /DNT /DPD /nologo -VC="C:\Programme\Microsoft Visual Studio\VC98" +VC = "C:\Programme\Microsoft Visual Studio\VC98" PDNTINCLUDE = /I. /Ic:\pd\tcl\include /Ic:\pd\src /I$(VC)\include /Iinclude @@ -25,6 +25,7 @@ PDNTLIB = $(PDNTLDIR)\msvcrt.lib \ lib\ogg_static.lib \ lib\vorbis_static.lib \ lib\vorbisenc_static.lib \ + lib\vorbisfile_static.lib \ c:\pd\bin\pd.lib 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 diff --git a/oggcast~/HISTORY b/oggcast~/HISTORY index ef27e13..cc7c6f5 100644 --- a/oggcast~/HISTORY +++ b/oggcast~/HISTORY @@ -1,65 +1,70 @@ -Version history of oggcast~ external for pure-data
-
-v 0.2g (3rd August 2002):
-- finally fixed the bug that made oggcast~ crash after a
- while. seems to be realted with output from child thread
- using t_clocks but couldn't proove that
-
-v 0.2f (20st july 2002):
-- recompiled with the final 1.0 release of Ogg Vorbis
-- changed the URL to the new akustische-kunst.org domain
-
-v 0.2e (5th july 2002):
-- added simple downsampling to stream at lower sample rates
-
-v 0.2d (21st june 2002 - stable release!):
-- cleaned up code a bit
-- now clean disconnect in case server dies or closes
- socket
-
-v 0.2c (13th june 2002):
-- fixed some small bugs
-- buffer size now in kbytes per channel
-- some more info-printout
-
-v 0.2b (12th june 2002):
-- completetly rewritten multithreaded version, first
- sort of stable release
-- KNOWN BUG: eats too much CPU power
-
-v 0.1g (23rd feb. 2002, not for public use!):
-- added multithreading functionality: now sending data
- to server in a second thread
-- now included the static ogg vorbis libraries - no dll's
- needed under win any longer
-- fixed a bug that sometimes made pd crash
-
-v 0.1f (11th feb. 2002):
-- converted ringbuffer to simple buffer of fixed size
-
-v 0.1e (10th feb. 2002):
-- now really fixed the bug that made pd die when server
- disconnected
-
-v 0.1d (9th feb. 2002):
-- fixed a bug in the "vorbis" setting that made on-the-run
- changes impossible
-- introduced a function to check writeability of the socket
-- fixed the bug that crashed pd due to an access violation
- in vorbis.dll when send() returned an error (more of a
- fast workaround)
-- corrected bitrate setting, now it really is _k_bps
-
-
-v 0.1c (9th feb. 2002):
-- added support for setting / changing the comment tags in
- the ogg/vorbis header, spaces have to be replaced by '='
-- fixed a bug in oggcast_stream() that made Pd crash - now
- it's vorbis.dll that makes Pd crash... ;-(
-
-v 0.1b (not released):
-- added support for changing encoder settings when stream
- is running (no need to disconnect), seems to be unstable
- on linux (with JRoar)
-
+Version history of oggcast~ external for pure-data + +v 0.2h (27th March 2003): +- added HTTP base auth login for (alpha) Icecast2 server: + message 'server 1' switches to Icecast2, 'server 0' back + top JRoar and PeerCast compatibility (which is the default) + +v 0.2g (3rd August 2002): +- finally fixed the bug that made oggcast~ crash after a + while. seems to be realted with output from child thread + using t_clocks but couldn't proove that + +v 0.2f (20st july 2002): +- recompiled with the final 1.0 release of Ogg Vorbis +- changed the URL to the new akustische-kunst.org domain + +v 0.2e (5th july 2002): +- added simple downsampling to stream at lower sample rates + +v 0.2d (21st june 2002 - stable release!): +- cleaned up code a bit +- now clean disconnect in case server dies or closes + socket + +v 0.2c (13th june 2002): +- fixed some small bugs +- buffer size now in kbytes per channel +- some more info-printout + +v 0.2b (12th june 2002): +- completetly rewritten multithreaded version, first + sort-of-stable release +- KNOWN BUG: eats too much CPU power + +v 0.1g (23rd feb. 2002, not for public use!): +- added multithreading functionality: now sending data + to server in a second thread +- now included the static ogg vorbis libraries - no dll's + needed under win any longer +- fixed a bug that sometimes made pd crash + +v 0.1f (11th feb. 2002): +- converted ringbuffer to simple buffer of fixed size + +v 0.1e (10th feb. 2002): +- now really fixed the bug that made pd die when server + disconnected + +v 0.1d (9th feb. 2002): +- fixed a bug in the "vorbis" setting that made on-the-run + changes impossible +- introduced a function to check writeability of the socket +- fixed the bug that crashed pd due to an access violation + in vorbis.dll when send() returned an error (more of a + fast workaround) +- corrected bitrate setting, now it really is _k_bps + + +v 0.1c (9th feb. 2002): +- added support for setting / changing the comment tags in + the ogg/vorbis header, spaces have to be replaced by '=' +- fixed a bug in oggcast_stream() that made Pd crash - now + it's vorbis.dll that makes Pd crash... ;-( + +v 0.1b (not released): +- added support for changing encoder settings when stream + is running (no need to disconnect), seems to be unstable + on linux (with JRoar) + v 0.1: first public release
\ No newline at end of file diff --git a/oggcast~/codec.h b/oggcast~/codec.h index 9365ea3..d986a9b 100644 --- a/oggcast~/codec.h +++ b/oggcast~/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/oggcast~/help-oggcast~.pd b/oggcast~/help-oggcast~.pd index 6b45d35..c61a94a 100644 --- a/oggcast~/help-oggcast~.pd +++ b/oggcast~/help-oggcast~.pd @@ -1,57 +1,65 @@ -#N canvas 256 31 609 501 10;
-#X floatatom 18 36 0 40 16000;
-#X msg 467 454 \; pd dsp 1;
-#X floatatom 18 470 0 0 0;
-#X msg 171 152 disconnect;
-#X msg 207 283 print;
-#X msg 533 454 \; pd dsp 0;
-#X obj 467 428 loadbang;
-#X floatatom 96 448 10 0 0;
-#X text 120 80 connect <host> <mountpoint> <port>;
-#X text 272 45 get latest version at;
-#X text 274 59 http://www.akustische-kunst.de/puredata/;
-#X text 188 178 vorbis <samplerate> <channles> <max.br> <nom.br> <min.br>
-;
-#X text 167 448 ogg pages;
-#X text 47 473 connection state;
-#X text 372 249 channels: 1 or 2 (default);
-#X text 203 214 vbr <samplerate> <channels> <quality>;
-#X msg 100 57 passwd letmein;
-#X obj 18 63 osc~ 440;
-#X text 323 236 quality settings: 0 - 1 (low - hi);
-#X text 324 265 resampling currently not supported!;
-#X text 216 345 possible tags: TITLE \, ARTIST \, PERFORMER \, DESCRIPTION
-\,;
-#X text 305 358 GENRE \, LOCATION \, COPYRIGHT \, CONTACT \, DATE;
-#X msg 188 195 vorbis 44100 2 144 128 96;
-#X msg 152 120 connect 141.53.196.149 puredata.ogg 8000;
-#X msg 122 98 connect localhost puredata.ogg 8000;
-#X msg 202 230 vbr 44100 2 0.4;
-#X obj 17 421 oggcast~ 2 512;
-#X text 19 6 oggcast~ 0.2b - send Ogg Vorbis stream to IceCast2 or
-JRoar;
-#X text 273 34 written by Olaf Matthes <olaf.matthes@gmx.de>;
-#X text 246 284 print current settings to console window;
-#X text 214 306 comment: <TAG> <content>;
-#X msg 214 323 ARTIST your_name;
-#X text 118 421 creation arguments: oggcast~ <channles> <buffer size>
-;
-#X text 239 439 default is 2 channels and 512k;
-#X text 240 451 bytes buffer size;
-#X text 217 376 '_' (underline) or '=' (equal sign) will get replaced
-with spaces! Note that changing any settings while streaming might
-result in audible dropouts!;
-#X connect 0 0 17 0;
-#X connect 3 0 26 0;
-#X connect 4 0 26 0;
-#X connect 6 0 1 0;
-#X connect 16 0 26 0;
-#X connect 17 0 26 0;
-#X connect 17 0 26 1;
-#X connect 22 0 26 0;
-#X connect 23 0 26 0;
-#X connect 24 0 26 0;
-#X connect 25 0 26 0;
-#X connect 26 0 2 0;
-#X connect 26 1 7 0;
-#X connect 31 0 26 0;
+#N canvas 298 121 629 593 10; +#X floatatom 18 36 0 40 16000 0 - - -; +#X msg 468 508 \; pd dsp 1; +#X floatatom 18 524 0 0 0 0 - - -; +#X msg 185 250 disconnect; +#X msg 213 352 print; +#X msg 534 508 \; pd dsp 0; +#X obj 468 482 loadbang; +#X floatatom 97 502 10 0 0 0 - - -; +#X text 131 154 connect <host> <mountpoint> <port>; +#X text 272 45 get latest version at; +#X text 168 502 ogg pages; +#X text 48 527 connection state; +#X text 378 337 channels: 1 or 2 (default); +#X text 210 304 vbr <samplerate> <channels> <quality>; +#X msg 100 57 passwd letmein; +#X obj 18 63 osc~ 440; +#X text 330 326 quality settings: 0 - 1 (low - hi); +#X text 227 412 possible tags: TITLE \, ARTIST \, PERFORMER \, DESCRIPTION +\,; +#X text 316 425 GENRE \, LOCATION \, COPYRIGHT \, CONTACT \, DATE; +#X msg 163 194 connect 141.53.196.149 puredata.ogg 8000; +#X msg 133 172 connect localhost puredata.ogg 8000; +#X msg 170 220 connect 141.53.3.18 musicforfilms.ogg 8100; +#X obj 18 475 oggcast~ 2 512; +#X text 273 34 written by Olaf Matthes <olaf.matthes@gmx.de>; +#X text 252 353 print current settings to console window; +#X text 225 373 comment: <TAG> <content>; +#X msg 225 390 ARTIST your_name; +#X text 228 444 '_' (underline) or '=' (equal sign) will get replaced +with spaces!; +#X text 240 493 default is 2 channels and 512k; +#X text 241 505 bytes buffer size; +#X text 119 475 creation arguments: oggcast~ <channels> <buffer size> +; +#X msg 195 285 vorbis 44100 2 144 128 96; +#X obj 33 110 noise~; +#X text 19 6 oggcast~ 0.2f - send Ogg Vorbis stream to IceCast2 or +JRoar; +#X text 274 59 http://www.akustische-kunst.org/puredata/; +#X text 195 268 vorbis <samplerate> <channels> <max.br> <nom.br> <min.br> +; +#X msg 210 320 vbr 44100 2 0.6; +#X msg 117 95 server 0; +#X msg 129 120 server 1; +#X text 175 94 choose JRoar or old Icecast2 as server type (default) +; +#X text 188 121 choose new Icecast2 (final) as server type; +#X connect 0 0 15 0; +#X connect 3 0 22 0; +#X connect 4 0 22 0; +#X connect 6 0 1 0; +#X connect 14 0 22 0; +#X connect 15 0 22 0; +#X connect 19 0 22 0; +#X connect 20 0 22 0; +#X connect 21 0 22 0; +#X connect 22 0 2 0; +#X connect 22 1 7 0; +#X connect 26 0 22 0; +#X connect 31 0 22 0; +#X connect 32 0 22 1; +#X connect 36 0 22 0; +#X connect 37 0 22 0; +#X connect 38 0 22 0; diff --git a/oggcast~/oggcast~.c b/oggcast~/oggcast~.c index 4210fa6..70b269b 100644 --- a/oggcast~/oggcast~.c +++ b/oggcast~/oggcast~.c @@ -1,1302 +1,1393 @@ -/* ------------------------- oggcast~ ----------------------------------------- */
-/* */
-/* Tilde object to send an Ogg Vorbis stream from to IceCast2 server. */
-/* Written by Olaf Matthes (olaf.matthes@gmx.de) */
-/* Get source at http://www.akustische-kunst.org/puredata/pdogg/ */
-/* */
-/* 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 "vorbisenc.h"
-
-#include <sys/types.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
-
-/************************* oggcast~ object ******************************/
-
-/* Each instance of oggcast~ 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_REINIT 6
-
-#define STATE_IDLE 0
-#define STATE_STARTUP 1 /* connecting and filling the buffer */
-#define STATE_STREAM 2 /* streaming aund audio output */
-
-#define READ 4096 /* amount of data we pass on to encoder */
-#define DEFBUFPERCHAN 262144 /* audio output buffer by default: 256k */
-#define MINBUFSIZE 65536
-#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */
-#define STRBUF_SIZE 1024 /* char received from server on startup */
-#define MAXSTREAMCHANS 2 /* maximum number of channels: restricted to 2 by Ogg specs */
-#define UPDATE_INTERVAL 250 /* time in milliseconds between updates of output values */
-
-static char *oggcast_version = "oggcast~: ogg/vorbis streaming client version 0.2g, written by Olaf Matthes";
-
-static t_class *oggcast_class;
-
-typedef struct _oggcast
-{
- t_object x_obj;
- t_float *x_f;
- t_clock *x_clock_connect;
- t_clock *x_clock_pages;
- t_outlet *x_connection; /* outlet for connection state */
- t_outlet *x_outpages; /* outlet for no. of ogg pages */
-
- t_float *x_buf; /* audio data buffer */
- t_int x_bufsize; /* buffer size in bytes */
- t_int x_ninlets; /* 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 */
-
- /* 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_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_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 */
- t_float x_pages; /* number of pages that have been output to server */
- t_float x_lastpages;
-
- /* ringbuffer stuff */
- t_float *x_buffer; /* data to be buffered (ringbuffer)*/
- t_int x_bytesbuffered; /* number of unprocessed bytes in buffer */
-
- /* ogg/vorbis format stuff */
- t_int x_samplerate; /* samplerate of stream (default = getsr() ) */
- t_int x_skip; /* samples from input to skip (for resampling) */
- t_float x_quality; /* desired quality level from 0.0 to 1.0 (lo to hi) */
- t_int x_br_max; /* max. bitrate of ogg/vorbis stream */
- t_int x_br_nom; /* nom. bitrate of ogg/vorbis stream */
- t_int x_br_min; /* min. bitrate of ogg/vorbis stream */
- t_int x_channels; /* number of channels (1 or 2) */
- t_int x_vbr;
-
- /* IceCast server stuff */
- char* x_passwd; /* password for server */
- char* x_bcname; /* name of broadcast */
- char* x_bcurl; /* url of broadcast */
- char* x_bcgenre; /* genre of broadcast */
- char* x_bcdescription; /* description */
- char* x_bcartist; /* artist */
- char* x_bclocation;
- char* x_bccopyright;
- char* x_bcperformer;
- char* x_bccontact;
- char* x_bcdate; /* system date when broadcast started */
- char* x_hostname; /* name or IP of host to connect to */
- char* x_mountpoint; /* mountpoint for IceCast server */
- t_float x_port; /* port number on which the connection is made */
- t_int x_bcpublic; /* do(n't) publish broadcast on www.oggcast.com */
-
-
-
-
- t_int x_connectstate; /* indicates to state of socket connection */
- t_int x_outvalue; /* value that has last been output via outlet */
- t_int x_fd; /* the socket number */
- t_resample x_resample; /* resampling unit */
- t_int x_recover; /* indicate how to behave on buffer underruns */
-
- /* thread stuff */
- pthread_mutex_t x_mutex;
- pthread_cond_t x_requestcondition;
- pthread_cond_t x_answercondition;
- pthread_t x_childthread;
-} t_oggcast;
-
- /* check server for writeability */
-static int oggcast_checkserver(t_int sock)
-{
- fd_set fdset;
- struct timeval ztout;
- fd_set writeset;
- fd_set exceptset;
-
- FD_ZERO(&writeset);
- FD_ZERO(&exceptset);
- FD_SET(sock, &writeset );
- FD_SET(sock, &exceptset );
-
- if(select(sock+1, NULL, &writeset, &exceptset, &ztout) > 0)
- {
- if(!FD_ISSET(sock, &writeset))
- {
- post("oggcast~: can not write data to the server, quitting");
- return -1;
- }
- if(FD_ISSET(sock, &exceptset))
- {
- post("oggcast~: socket returned an error, quitting");
- return -1;
- }
- }
- return 0;
-}
-
- /* stream ogg/vorbis to IceCast2 server */
-static int oggcast_stream(t_oggcast *x, t_int fd)
-{
- int err = -1; /* error return code */
- int pages = 0;
-
- /* write out pages (if any) */
- while(!x->x_eos)
- {
- int result=ogg_stream_pageout(&(x->x_os),&(x->x_og));
- if(result==0)break;
- err = send(fd, x->x_og.header, x->x_og.header_len, 0);
- if(err < 0)
- {
- error("oggcast~: could not send ogg header to server (%d)", err);
- x->x_eos = 1; /* indicate (artificial) end of stream */
- return err;
- }
- err = send(fd, x->x_og.body, x->x_og.body_len, 0);
- if(err < 0)
- {
- error("oggcast~: could not send ogg body to server (%d)", err);
- x->x_eos = 1; /* indicate (artificial) end of stream */
- return err;
- }
- pages++; /* count number of pages */
- /* there might be more than one pages we have to send */
- if(ogg_page_eos(&(x->x_og)))x->x_eos=1;
- }
- return (pages);
-}
-
- /* ogg/vorbis decoder setup */
-static int oggcast_vorbis_init(t_oggcast *x)
-{
- int err = -1;
-
- x->x_eos = 0;
- x->x_skip = 1; /* assume no resampling */
- /* choose an encoding mode */
- vorbis_info_init(&(x->x_vi));
-
- if(x->x_samplerate != sys_getsr()) /* downsampling for Oliver (http://radiostudio.org) */
- {
- if(sys_getsr() / x->x_samplerate == 2.0)
- {
- post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate);
- x->x_skip = 2;
- }
- else if(sys_getsr() / x->x_samplerate == 4.0)
- {
- post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate);
- x->x_skip = 4;
- }
- else if(sys_getsr() / x->x_samplerate == 3.0)
- {
- post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate);
- x->x_skip = 3;
- } else post("oggcast~: warning: resampling from %.0f to %d not supported", sys_getsr(), x->x_samplerate);
- }
- if(x->x_vbr == 1)
- { /* quality based setting */
- if(vorbis_encode_init_vbr(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_quality))
- {
- post("oggcast~: ogg/vorbis mode initialisation failed: invalid parameters for quality");
- vorbis_info_clear(&(x->x_vi));
- return (-1);
- }
- }
- else
- { /* bitrate based setting */
- if(vorbis_encode_init(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_br_max*1024, x->x_br_nom*1024, x->x_br_min*1024))
- {
- post("oggcast~: ogg/vorbis mode initialisation failed: invalid parameters for quality");
- vorbis_info_clear(&(x->x_vi));
- return (-1);
- }
- }
-
- /* add a comment */
- vorbis_comment_init(&(x->x_vc));
- vorbis_comment_add_tag(&(x->x_vc),"TITLE", x->x_bcname);
- vorbis_comment_add_tag(&(x->x_vc),"ARTIST", x->x_bcartist);
- vorbis_comment_add_tag(&(x->x_vc),"GENRE",x->x_bcgenre);
- vorbis_comment_add_tag(&(x->x_vc),"DESCRIPTION", x->x_bcdescription);
- vorbis_comment_add_tag(&(x->x_vc),"LOCATION",x->x_bclocation);
- vorbis_comment_add_tag(&(x->x_vc),"PERFORMER",x->x_bcperformer);
- vorbis_comment_add_tag(&(x->x_vc),"COPYRIGHT",x->x_bccopyright);
- vorbis_comment_add_tag(&(x->x_vc),"CONTACT",x->x_bccontact);
- vorbis_comment_add_tag(&(x->x_vc),"DATE",x->x_bcdate);
- vorbis_comment_add_tag(&(x->x_vc),"ENCODER","oggcast~ v0.2 for pure-data");
-
- /* set up the analysis state and auxiliary encoding storage */
- vorbis_analysis_init(&(x->x_vd),&(x->x_vi));
- vorbis_block_init(&(x->x_vd),&(x->x_vb));
-
- /* set up our packet->stream encoder */
- /* pick a random serial number; that way we can more likely build
- chained streams just by concatenation */
- srand(time(NULL));
- ogg_stream_init(&(x->x_os),rand());
-
- /* Vorbis streams begin with three headers; the initial header (with
- most of the codec setup parameters) which is mandated by the Ogg
- bitstream spec. The second header holds any comment fields. The
- third header holds the bitstream codebook. We merely need to
- make the headers, then pass them to libvorbis one at a time;
- libvorbis handles the additional Ogg bitstream constraints */
-
- {
- ogg_packet header;
- ogg_packet header_comm;
- ogg_packet header_code;
-
- vorbis_analysis_headerout(&(x->x_vd),&(x->x_vc),&header,&header_comm,&header_code);
- ogg_stream_packetin(&(x->x_os),&header); /* automatically placed in its own page */
- ogg_stream_packetin(&(x->x_os),&header_comm);
- ogg_stream_packetin(&(x->x_os),&header_code);
-
- /* We don't have to write out here, but doing so makes streaming
- * much easier, so we do, flushing ALL pages. This ensures the actual
- * audio data will start on a new page
- *
- * IceCast2 server will take this as a first info about our stream
- */
- while(!x->x_eos)
- {
- int result=ogg_stream_flush(&(x->x_os),&(x->x_og));
- if(result==0)break;
- err = send(x->x_fd, x->x_og.header, x->x_og.header_len, 0);
- if(err < 0)
- {
- error("oggcast~: could not send ogg header to server (%d)", err);
- x->x_eos = 1; /* indicate end of stream */
- return (-1);
- }
- err = send(x->x_fd, x->x_og.body, x->x_og.body_len, 0);
- if(err < 0)
- {
- error("oggcast~: could not send ogg body to server (%d)", err);
- x->x_eos = 1; /* indicate end of stream */
- return (-1);
- }
- }
- }
- return (0);
-}
-
- /* deinit the ogg/vorbis decoder */
-static void oggcast_vorbis_deinit(t_oggcast *x)
-{
- vorbis_analysis_wrote(&(x->x_vd),0);
- /* get rid of remaining data in encoder, if any */
- while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1)
- {
- vorbis_analysis(&(x->x_vb),NULL);
- vorbis_bitrate_addblock(&(x->x_vb));
-
- while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op)))
- {
- ogg_stream_packetin(&(x->x_os),&(x->x_op));
- oggcast_stream(x, x->x_fd);
- }
- }
- /* clean up and exit. vorbis_info_clear() must be called last */
- ogg_stream_clear(&(x->x_os));
- vorbis_block_clear(&(x->x_vb));
- vorbis_dsp_clear(&(x->x_vd));
- vorbis_comment_clear(&(x->x_vc));
- vorbis_info_clear(&(x->x_vi));
-}
-
- /* encode ogg/vorbis and stream new data */
-static int oggcast_encode(t_oggcast *x, float *buf, int channels, int fifosize, int fd)
-{
- unsigned short i, ch;
- int err = 0;
- int n, pages = 0;
-
- /* expose the buffer to submit data */
- float **inbuffer=vorbis_analysis_buffer(&(x->x_vd),READ);
-
- /* read from buffer */
- for(n = 0; n < READ / channels; n++) /* fill encode buffer */
- {
- for(ch = 0; ch < channels; ch++)
- {
- inbuffer[ch][n] = *buf++;
- }
- }
- /* tell the library how much we actually submitted */
- vorbis_analysis_wrote(&(x->x_vd),n);
-
- /* vorbis does some data preanalysis, then divvies up blocks for
- more involved (potentially parallel) processing. Get a single
- block for encoding now */
- while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1)
- {
- /* analysis, assume we want to use bitrate management */
- vorbis_analysis(&(x->x_vb),NULL);
- vorbis_bitrate_addblock(&(x->x_vb));
-
- while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op)))
- {
- /* weld the packet into the bitstream */
- ogg_stream_packetin(&(x->x_os),&(x->x_op));
- err = oggcast_stream(x, fd); /* stream packet to server */
- if(err >= 0)
- {
- pages += err; /* count pages */
- }
- else return (err);
- }
- }
- return (pages);
-}
-
- /* connect to icecast2 server */
-static int oggcast_child_connect(char *hostname, char *mountpoint, t_int portno,
- char *passwd, char *bcname, char *bcurl,
- char *bcgenre, t_int bcpublic, t_int br_nom)
-{
- struct sockaddr_in server;
- struct hostent *hp;
-
- /* variables used for communication with server */
- const char * buf = 0;
- char resp[STRBUF_SIZE];
- unsigned int len;
- fd_set fdset;
- struct timeval tv;
- t_int sockfd; /* our internal handle for the socket */
- t_int ret;
-
- sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (sockfd < 0)
- {
- error("oggcast~: 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("oggcast~: 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("oggcast~: connecting to port %d", portno);
- if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
- {
- error("oggcast~: 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("oggcast~: can not read from socket");
- sys_closesocket(sockfd);
- return (-1);
- }
-
- /* now try to log in at IceCast2 server using ICE/1.0 scheme */
- post("oggcast~: logging in to IceCast2 server...");
- /* send the request, a string like: "SOURCE /<mountpoint> ICE/1.0\n" */
- buf = "SOURCE ";
- send(sockfd, buf, strlen(buf), 0);
- buf = "/";
- send(sockfd, buf, strlen(buf), 0);
- buf = mountpoint;
- send(sockfd, buf, strlen(buf), 0);
- buf = " ICE/1.0";
- send(sockfd, buf, strlen(buf), 0);
- /* send the ice headers */
- /* password */
- buf = "\nice-password: ";
- send(sockfd, buf, strlen(buf), 0);
- buf = passwd;
- send(sockfd, buf, strlen(buf), 0);
- /* name */
- buf = "\r\nice-name: ";
- send(sockfd, buf, strlen(buf), 0);
- buf = bcname;
- send(sockfd, buf, strlen(buf), 0);
- /* url */
- buf = "\r\nice-url: ";
- send(sockfd, buf, strlen(buf), 0);
- buf = bcurl;
- send(sockfd, buf, strlen(buf), 0);
- /* genre */
- buf = "\r\nice-genre: ";
- send(sockfd, buf, strlen(buf), 0);
- buf = bcgenre;
- send(sockfd, buf, strlen(buf), 0);
- /* public */
- buf = "\r\nice-public: ";
- send(sockfd, buf, strlen(buf), 0);
- if(bcpublic==0) /* set the public flag for broadcast */
- {
- buf = "no";
- }
- else
- {
- buf ="yes";
- }
- send(sockfd, buf, strlen(buf), 0);
- /* bitrate */
- buf = "\r\nice-bitrate: ";
- send(sockfd, buf, strlen(buf), 0);
- if(sprintf(resp, "%d", br_nom) == -1) /* convert int to a string */
- {
- error("oggcast~: wrong bitrate");
- }
- send(sockfd, resp, strlen(resp), 0);
- /* description */
- buf = "\r\nice-description: ";
- send(sockfd, buf, strlen(buf), 0);
- buf = "ogg/vorbis streamed from pure-data with oggcast~";
- send(sockfd, buf, strlen(buf), 0);
- /* end of header */
- buf = "\r\n\r\n";
- send(sockfd, buf, strlen(buf), 0);
- /* end login for IceCast */
-
- /* check if we can write to server */
- if(oggcast_checkserver(sockfd)!= 0)
- {
- post("oggcast~: error: server refused to receive data");
- return (-1);
- }
-
- post("oggcast~: logged in to http://%s:%d/%s", hp->h_name, portno, mountpoint);
-
- return (sockfd);
-}
-
-
-
-static void oggcast_child_disconnect(t_int fd)
-{
- sys_closesocket(fd);
- post("oggcast~: connection closed");
-}
-/************** the child thread which performs data I/O ***********/
-
-#if 0 /* set this to 1 to get debugging output */
-static void pute(char *s) /* debug routine */
-{
- write(2, s, strlen(s));
-}
-#else
-#define pute(x)
-#endif
-
-#if 1
-#define oggcast_cond_wait pthread_cond_wait
-#define oggcast_cond_signal pthread_cond_signal
-#else
-#include <sys/time.h> /* debugging version... */
-#include <sys/types.h>
-static void oggcast_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 oggcast_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 oggcast_cond_wait(a,b) oggcast_fakewait(b)
-#define oggcast_cond_signal(a)
-#endif
-
-static void *oggcast_child_main(void *zz)
-{
- t_oggcast *x = zz;
- time_t now; /* to get the time */
- pute("1\n");
- pthread_mutex_lock(&x->x_mutex);
- while (1)
- {
- int fd, fifotail;
- pute("0\n");
- if (x->x_requestcode == REQUEST_NOTHING)
- {
- pute("wait 2\n");
- oggcast_cond_signal(&x->x_answercondition);
- oggcast_cond_wait(&x->x_requestcondition, &x->x_mutex);
- pute("3\n");
- }
- // connect to Icecast2 server
- else if (x->x_requestcode == REQUEST_CONNECT)
- {
- char boo[100];
- int sysrtn, wantbytes;
-
- /* copy connect stuff out of the data structure so we can
- relinquish the mutex while we're connecting to server. */
- char *hostname = x->x_hostname;
- char *mountpoint = x->x_mountpoint;
- t_int portno = x->x_port;
- char *passwd = x->x_passwd;
- char *bcname = x->x_bcname;
- char *bcgenre = x->x_bcgenre;
- char *bcurl = x->x_bcurl;
- t_int bcpublic = x->x_bcpublic;
- t_int br_nom = x->x_br_nom;
- /* alter the request code so that an ensuing "open" will get
- noticed. */
- pute("4\n");
- x->x_requestcode = REQUEST_BUSY;
- x->x_connecterror = 0;
-
- /* open the socket with the mutex unlocked in case
- we're not already connected */
- if(x->x_fd < 0)
- {
- pthread_mutex_unlock(&x->x_mutex);
- fd = oggcast_child_connect(hostname, mountpoint, portno, passwd, bcname,
- bcurl, bcgenre, bcpublic, br_nom);
- pthread_mutex_lock(&x->x_mutex);
- pute("5\n");
- /* copy back into the instance structure. */
- x->x_connectstate = 1;
- clock_delay(x->x_clock_connect, 0);
- x->x_fd = fd;
- if (fd < 0)
- {
- x->x_connecterror = fd;
- x->x_connectstate = 0;
- clock_delay(x->x_clock_connect, 0);
- pute("connect failed\n");
- goto lost;
- }
- else
- {
- /* get the time for the DATE comment */
- now=time(NULL);
- x->x_bcdate = ctime(&now);
- x->x_pages = 0;
- clock_delay(x->x_clock_pages, 0);
- /* initialise the encoder */
- if(oggcast_vorbis_init(x) == 0)
- {
- post("oggcast~: ogg/vorbis encoder initialised");
- x->x_eos = 0;
- x->x_state = STATE_STREAM;
- }
- else
- {
- post("oggcast~: could not init encoder");
- oggcast_child_disconnect(fd);
- post("oggcast~: connection closed due to initialisation error");
- x->x_fd = -1;
- x->x_connectstate = 0;
- clock_delay(x->x_clock_connect, 0);
- pute("oggcast~: initialisation failed\n");
- goto lost;
- }
- }
- x->x_fifotail = fifotail = 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_channels * READ));
- /* 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_channels * x->x_vecsize));
- }
- /* check if another request has been made; if so, field it */
- if (x->x_requestcode != REQUEST_BUSY)
- goto lost;
- pute("6\n");
-
- while (x->x_requestcode == REQUEST_BUSY)
- {
- int fifosize = x->x_fifosize, fifotail, channels;
- float *buf = x->x_buf;
- pute("77\n");
-
- /* if the head is < the tail, we can immediately write
- from tail to end of fifo to disk; otherwise we hold off
- writing until there are at least WRITESIZE bytes in the
- buffer */
- if (x->x_fifohead < x->x_fifotail ||
- x->x_fifohead >= x->x_fifotail + READ
- || (x->x_requestcode == REQUEST_CLOSE &&
- x->x_fifohead != x->x_fifotail))
- { /* encode audio and send to server */
- pute("8\n");
- fifotail = x->x_fifotail;
- channels = x->x_channels;
- fd = x->x_fd;
- pthread_mutex_unlock(&x->x_mutex);
- sysrtn = oggcast_encode(x, buf + fifotail, channels, fifosize, fd);
- pthread_mutex_lock(&x->x_mutex);
- if (x->x_requestcode != REQUEST_BUSY &&
- x->x_requestcode != REQUEST_CLOSE)
- break;
- if (sysrtn < 0)
- {
- post("oggcast~: closing due to error...");
- goto lost;
- }
- else
- {
- x->x_fifotail += READ;
- x->x_pages += sysrtn;
- if (x->x_fifotail >= fifosize)
- x->x_fifotail = 0;
- }
- sprintf(boo, "after: head %d, tail %d, pages %d\n", x->x_fifohead, x->x_fifotail, sysrtn);
- pute(boo);
- }
- else /* just wait... */
- {
- pute("wait 7a ...\n");
- oggcast_cond_signal(&x->x_answercondition);
- pute("signalled\n");
- oggcast_cond_wait(&x->x_requestcondition,
- &x->x_mutex);
- pute("7a done\n");
- continue;
- }
- /* signal parent in case it's waiting for data */
- oggcast_cond_signal(&x->x_answercondition);
- }
- }
-
- /* reinit encoder (settings have changed) */
- else if (x->x_requestcode == REQUEST_REINIT)
- {
- pthread_mutex_unlock(&x->x_mutex);
- oggcast_vorbis_deinit(x);
- oggcast_vorbis_init(x);
- pthread_mutex_lock(&x->x_mutex);
- post("oggcast~: ogg/vorbis encoder reinitialised");
- x->x_state = STATE_STREAM;
- if (x->x_requestcode == REQUEST_REINIT)
- x->x_requestcode = REQUEST_CONNECT;
- oggcast_cond_signal(&x->x_answercondition);
- }
- /* close connection to server (disconnect) */
- else if (x->x_requestcode == REQUEST_CLOSE)
- {
-lost:
- x->x_state = STATE_IDLE;
- if (x->x_fd >= 0)
- {
- fd = x->x_fd;
- pthread_mutex_unlock(&x->x_mutex);
- oggcast_vorbis_deinit(x);
- oggcast_child_disconnect(fd);
- pthread_mutex_lock(&x->x_mutex);
- x->x_fd = -1;
- }
- if (x->x_requestcode == REQUEST_CLOSE)
- x->x_requestcode = REQUEST_NOTHING;
- if (x->x_requestcode == REQUEST_BUSY) /* disconnect due to error */
- x->x_requestcode = REQUEST_NOTHING;
- x->x_connectstate = 0;
- clock_delay(x->x_clock_connect, 0);
- x->x_eos = 1;
- oggcast_cond_signal(&x->x_answercondition);
- }
- // quit everything
- else if (x->x_requestcode == REQUEST_QUIT)
- {
- x->x_state = STATE_IDLE;
- if (x->x_fd >= 0)
- {
- fd = x->x_fd;
- pthread_mutex_unlock(&x->x_mutex);
- oggcast_vorbis_deinit(x);
- oggcast_child_disconnect(fd);
- pthread_mutex_lock(&x->x_mutex);
- x->x_fd = -1;
- }
- x->x_connectstate = 0;
- clock_delay(x->x_clock_connect, 0);
- x->x_requestcode = REQUEST_NOTHING;
- oggcast_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 oggcast_tick_connect(t_oggcast *x)
-{
- pthread_mutex_lock(&x->x_mutex);
- outlet_float(x->x_connection, x->x_connectstate);
- pthread_mutex_unlock(&x->x_mutex);
-}
-
-static void oggcast_tick_pages(t_oggcast *x)
-{
- /* output new no. of pages if anything changed */
- t_float pages;
- pthread_mutex_lock(&x->x_mutex);
- pages = x->x_pages; /* get current value with mutex locked */
- pthread_mutex_unlock(&x->x_mutex);
- if(pages != x->x_lastpages)
- {
- outlet_float(x->x_outpages, pages);
- x->x_lastpages = pages;
- }
- clock_delay(x->x_clock_pages, UPDATE_INTERVAL); /* come back again... */
-}
-
-static void *oggcast_new(t_floatarg fnchannels, t_floatarg fbufsize)
-{
- t_oggcast *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 <= 0) 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_oggcast *)pd_new(oggcast_class);
-
- for (i = 1; i < nchannels; i++)
- inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal"));
- x->x_connection = outlet_new(&x->x_obj, gensym("float"));
- x->x_outpages = outlet_new(&x->x_obj, gensym("float"));
- x->x_ninlets = nchannels;
-
- x->x_clock_connect = clock_new(x, (t_method)oggcast_tick_connect);
- x->x_clock_pages = clock_new(x, (t_method)oggcast_tick_pages);
-
- 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_state = STATE_IDLE;
- x->x_buf = buf;
- x->x_bufsize = bufsize;
- x->x_siginterval = 32; /* signal 32 times per buffer */
- /* I found this to be most efficient on my machine */
- x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0;
-
- x->x_connectstate = 0; /* indicating state of connection */
- x->x_outvalue = 0; /* value at output currently is 0 */
-
- x->x_samplerate = sys_getsr();
- x->x_resample.upsample = x->x_resample.downsample = 1; /* don't resample */
-
-
- x->x_fd = -1;
- x->x_eos = 0;
- x->x_vbr = 1; /* use the vbr setting by default */
- x->x_passwd = "letmein";
- x->x_samplerate = sys_getsr(); /* default to Pd's sampling rate */
- x->x_skip = 1; /* no resampling supported */
- x->x_quality = 0.4; /* quality 0.4 gives roughly 128kbps VBR stream */
- x->x_channels = nchannels; /* stereo */
- x->x_br_max = 144;
- x->x_br_nom = 128;
- x->x_br_min = 96;
- x->x_pages = x->x_lastpages = 0;
- x->x_bcname = "ogg/vorbis stream";
- x->x_bcurl = "http://www.akustische-kunst.org/puredata/";
- x->x_bcgenre = "experimental";
- x->x_bcdescription = "ogg/vorbis stream emitted from pure-data with oggcast~";
- x->x_bcartist = "Pd and oggcast~ v0.2";
- x->x_bclocation = x->x_bcurl;
- x->x_bccopyright = "";
- x->x_bcperformer = "";
- x->x_bccontact = "";
- x->x_bcdate = "";
- x->x_bcpublic = 1;
- x->x_mountpoint = "puredata.ogg";
-
- post(oggcast_version);
- post("oggcast~: set buffer to %dk bytes", bufsize / 1024);
- post("oggcast~: encoding %d channels @ %d Hz", x->x_channels, x->x_samplerate);
-
- clock_delay(x->x_clock_pages, 0);
- pthread_create(&x->x_childthread, 0, oggcast_child_main, x);
- return (x);
-}
-
-static t_int *oggcast_perform(t_int *w)
-{
- t_oggcast *x = (t_oggcast *)(w[1]);
- int vecsize = x->x_vecsize, ninlets = x->x_ninlets, channels = x->x_channels, i, j, skip = x->x_skip;
- float *sp = x->x_buf;
- pthread_mutex_lock(&x->x_mutex);
- if (x->x_state != STATE_IDLE)
- {
- int wantbytes;
- /* get 'wantbytes' bytes from inlet */
- wantbytes = channels * vecsize / skip; /* we'll get vecsize bytes per channel */
- /* check if there is enough space in buffer to write all samples */
- while (x->x_fifotail > x->x_fifohead &&
- x->x_fifotail < x->x_fifohead + wantbytes + 1)
- {
- pute("wait...\n");
- oggcast_cond_signal(&x->x_requestcondition);
- oggcast_cond_wait(&x->x_answercondition, &x->x_mutex);
- pute("done\n");
- }
-
- /* output audio */
- sp += x->x_fifohead;
-
- if(ninlets >= channels)
- {
- for(j = 0; j < vecsize; j += skip)
- {
- for(i = 0; i < channels; i++)
- {
- *sp++ = x->x_outvec[i][j];
- }
- }
- }
- else if(channels == ninlets * 2) /* convert mono -> stereo */
- {
- for(j = 0; j < vecsize; j += skip)
- {
- for(i = 0; i < ninlets; i++)
- {
- *sp++ = x->x_outvec[i][j];
- *sp++ = x->x_outvec[i][j];
- }
- }
- }
-
-
- x->x_fifohead += wantbytes;
- if (x->x_fifohead >= x->x_fifosize)
- x->x_fifohead = 0;
- /* signal the child thread */
- if ((--x->x_sigcountdown) <= 0)
- {
- pute("signal 1\n");
- oggcast_cond_signal(&x->x_requestcondition);
- x->x_sigcountdown = x->x_sigperiod;
- }
- }
-
- pthread_mutex_unlock(&x->x_mutex);
-
- return (w+2);
-}
-
-
-static void oggcast_disconnect(t_oggcast *x)
-{
- /* LATER rethink whether you need the mutex just to set a variable? */
- pthread_mutex_lock(&x->x_mutex);
- if(x->x_fd >= 0)
- {
- x->x_state = STATE_IDLE;
- x->x_requestcode = REQUEST_CLOSE;
- oggcast_cond_signal(&x->x_requestcondition);
- }
- else post("oggcast~: not connected");
- pthread_mutex_unlock(&x->x_mutex);
-}
-
-
- /* connect method. Called as:
- connect <hostname or IP> <mountpoint> <portnumber>
- */
-
-static void oggcast_connect(t_oggcast *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 >= 0)
- {
- post("oggcast~: already connected");
- }
- else
- {
- x->x_requestcode = REQUEST_CONNECT;
- x->x_hostname = hostsym->s_name;
- x->x_mountpoint = mountsym->s_name;
- x->x_port = portno;
-
- x->x_fifotail = 0;
- x->x_fifohead = 0;
- // if(x->x_recover != 1)x->x_fifobytes = 0;
-
- x->x_connecterror = 0;
- x->x_state = STATE_STARTUP;
- oggcast_cond_signal(&x->x_requestcondition);
- }
- pthread_mutex_unlock(&x->x_mutex);
-}
-
-static void oggcast_float(t_oggcast *x, t_floatarg f)
-{
- if (f != 0)
- {
- pthread_mutex_lock(&x->x_mutex);
- if(x->x_fd >= 0)
- {
- post("oggcast~: already connected");
- }
- else
- {
- if(x->x_recover != 1)
- {
- x->x_fifotail = 0;
- x->x_fifohead = 0;
- }
- x->x_requestcode = REQUEST_CONNECT;
-
- x->x_fifotail = 0;
- x->x_fifohead = 0;
-
- x->x_connecterror = 0;
- x->x_state = STATE_STARTUP;
- oggcast_cond_signal(&x->x_requestcondition);
- }
- pthread_mutex_unlock(&x->x_mutex);
- }
- else oggcast_disconnect(x);
-}
-
-static void oggcast_dsp(t_oggcast *x, t_signal **sp)
-{
- int i, ninlets = x->x_ninlets;
- pthread_mutex_lock(&x->x_mutex);
- x->x_vecsize = sp[0]->s_n;
-
- x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * ninlets * x->x_vecsize));
- for (i = 0; i < ninlets; i++)
- x->x_outvec[i] = sp[i]->s_vec;
- pthread_mutex_unlock(&x->x_mutex);
- dsp_add(oggcast_perform, 1, x);
-}
-
- /* set password for oggcast server */
-static void oggcast_password(t_oggcast *x, t_symbol *password)
-{
- pthread_mutex_lock(&x->x_mutex);
- x->x_passwd = password->s_name;
- pthread_mutex_unlock(&x->x_mutex);
-}
- /* set comment fields for header (reads in just anything) */
-static void oggcast_comment(t_oggcast *x, t_symbol *s, t_int argc, t_atom* argv)
-{
- t_int i = argc;
- char *comment = NULL;
- int len = strlen(atom_gensym(argv)->s_name);
-
- comment = atom_gensym(argv)->s_name;
-
- while (len--)
- {
- if(*(comment + len) == '=')*(comment + len) = ' ';
- if(*(comment + len) == '_')*(comment + len) = ' ';
- }
-
- pthread_mutex_lock(&x->x_mutex);
- if(strstr(s->s_name, "ARTIST"))
- {
- x->x_bcartist = comment;
- post("oggcast~: ARTIST=%s", x->x_bcartist);
- }
- else if(strstr(s->s_name, "GENRE"))
- {
- x->x_bcgenre = comment;
- post("oggcast~: GENRE=%s", x->x_bcgenre);
- }
- else if(strstr(s->s_name, "TITLE"))
- {
- x->x_bcname = comment;
- post("oggcast~: TITLE=%s", x->x_bcname);
- }
- else if(strstr(s->s_name, "PERFORMER"))
- {
- x->x_bcperformer = comment;
- post("oggcast~: PERFORMER=%s", x->x_bcperformer);
- }
- else if(strstr(s->s_name, "LOCATION"))
- {
- x->x_bclocation = comment;
- post("oggcast~: LOCATION=%s", x->x_bclocation);
- }
- else if(strstr(s->s_name, "COPYRIGHT"))
- {
- x->x_bccopyright = comment;
- post("oggcast~: COPYRIGHT=%s", x->x_bccopyright);
- }
- else if(strstr(s->s_name, "CONTACT"))
- {
- x->x_bccontact = comment;
- post("oggcast~: CONTACT=%s", x->x_bccontact);
- }
- else if(strstr(s->s_name, "DESCRIPTION"))
- {
- x->x_bcdescription = comment;
- post("oggcast~: DESCRIPTION=%s", x->x_bcdescription);
- }
- else if(strstr(s->s_name, "DATE"))
- {
- x->x_bcdate = comment;
- post("oggcast~: DATE=%s", x->x_bcdate);
- }
- else post("oggcast~: no method for %s", s->s_name);
- if(x->x_state == STATE_STREAM)
- {
- x->x_state = STATE_IDLE;
- x->x_requestcode = REQUEST_REINIT;
- oggcast_cond_signal(&x->x_requestcondition);
- }
- pthread_mutex_unlock(&x->x_mutex);
-}
- /* settings for variable bitrate encoding */
-static void oggcast_vbr(t_oggcast *x, t_floatarg fsr, t_floatarg fchannels,
- t_floatarg fquality)
-{
- pthread_mutex_lock(&x->x_mutex);
- x->x_vbr = 1;
- x->x_samplerate = (t_int)fsr;
- x->x_quality = fquality;
- x->x_channels = (t_int)fchannels;
- post("oggcast~: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality);
- if(x->x_state == STATE_STREAM)
- {
- x->x_state = STATE_IDLE;
- x->x_requestcode = REQUEST_REINIT;
- oggcast_cond_signal(&x->x_requestcondition);
- }
- pthread_mutex_unlock(&x->x_mutex);
-}
-
- /* settings for bitrate-based vbr encoding */
-static void oggcast_vorbis(t_oggcast *x, t_floatarg fsr, t_floatarg fchannels,
- t_floatarg fmax, t_floatarg fnom, t_floatarg fmin)
-{
- pthread_mutex_lock(&x->x_mutex);
- x->x_vbr = 0;
- x->x_samplerate = (t_int)fsr;
- x->x_channels = (t_int)fchannels;
- x->x_br_max = (t_int)fmax;
- x->x_br_nom = (t_int)fnom;
- x->x_br_min = (t_int)fmin;
- post("oggcast~: %d channels @ %d Hz, bitrates: max. %d / nom. %d / min. %d",
- x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min);
- if(x->x_state == STATE_STREAM)
- {
- x->x_state = STATE_IDLE;
- x->x_requestcode = REQUEST_REINIT;
- oggcast_cond_signal(&x->x_requestcondition);
- }
- pthread_mutex_unlock(&x->x_mutex);
-}
- /* print settings to pd's console window */
-static void oggcast_print(t_oggcast *x)
-{
- pthread_mutex_lock(&x->x_mutex);
- post("oggcast~: mountpoint at IceCast2: %s", x->x_mountpoint);
- if(x->x_vbr == 1)
- {
- post("oggcast~: Ogg Vorbis encoder: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality);
- }
- else
- {
- post("oggcast~: Ogg Vorbis encoder: %d channels @ %d Hz, bitrates: max. %d, nom. %d, min. %d",
- x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min);
- }
- post("oggcast~: Ogg Vorbis comments:");
- post(" TITLE = %s", x->x_bcname);
- post(" ARTIST = %s", x->x_bcartist);
- post(" PERFORMER = %s", x->x_bcperformer);
- post(" GENRE = %s", x->x_bcgenre);
- post(" LOCATION = %s", x->x_bclocation);
- post(" COPYRIGHT = %s", x->x_bccopyright);
- post(" CONTACT = %s", x->x_bccontact);
- post(" DESCRIPTION = %s", x->x_bcdescription);
- post(" DATE = %s", x->x_bcdate);
- pthread_mutex_unlock(&x->x_mutex);
-}
-
-
-static void oggcast_free(t_oggcast *x)
-{
- /* request QUIT and wait for acknowledge */
- void *threadrtn;
- pthread_mutex_lock(&x->x_mutex);
- x->x_requestcode = REQUEST_QUIT;
- post("stopping oggcast thread...");
- oggcast_cond_signal(&x->x_requestcondition);
- while (x->x_requestcode != REQUEST_NOTHING)
- {
- post("signalling...");
- oggcast_cond_signal(&x->x_requestcondition);
- oggcast_cond_wait(&x->x_answercondition, &x->x_mutex);
- }
- pthread_mutex_unlock(&x->x_mutex);
- if (pthread_join(x->x_childthread, &threadrtn))
- error("oggcast_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_connect);
- clock_free(x->x_clock_pages);
-}
-
-void oggcast_tilde_setup(void)
-{
- oggcast_class = class_new(gensym("oggcast~"), (t_newmethod)oggcast_new,
- (t_method)oggcast_free, sizeof(t_oggcast), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
- CLASS_MAINSIGNALIN(oggcast_class, t_oggcast, x_f );
- class_addfloat(oggcast_class, (t_method)oggcast_float);
- class_addmethod(oggcast_class, (t_method)oggcast_disconnect, gensym("disconnect"), 0);
- class_addmethod(oggcast_class, (t_method)oggcast_dsp, gensym("dsp"), 0);
- class_addmethod(oggcast_class, (t_method)oggcast_connect, gensym("connect"), A_GIMME, 0);
- class_addmethod(oggcast_class, (t_method)oggcast_print, gensym("print"), 0);
- class_addmethod(oggcast_class, (t_method)oggcast_password, gensym("passwd"), A_SYMBOL, 0);
- class_addmethod(oggcast_class, (t_method)oggcast_vorbis, gensym("vorbis"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
- class_addmethod(oggcast_class, (t_method)oggcast_vbr, gensym("vbr"), A_FLOAT, A_FLOAT, A_FLOAT, 0);
- class_addanything(oggcast_class, oggcast_comment);
- class_sethelpsymbol(oggcast_class, gensym("help-oggcast~.pd"));
-}
+/* ------------------------- oggcast~ ----------------------------------------- */ +/* */ +/* Tilde object to send an Ogg Vorbis stream from to IceCast2 server. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/pdogg/ */ +/* */ +/* 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 "vorbisenc.h" + +#include <sys/types.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 + +/************************* oggcast~ object ******************************/ + +/* Each instance of oggcast~ 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_REINIT 6 + +#define STATE_IDLE 0 +#define STATE_STARTUP 1 /* connecting and filling the buffer */ +#define STATE_STREAM 2 /* streaming aund audio output */ + +#define READ 4096 /* amount of data we pass on to encoder */ +#define DEFBUFPERCHAN 262144 /* audio output buffer by default: 256k */ +#define MINBUFSIZE 65536 +#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */ +#define STRBUF_SIZE 1024 /* char received from server on startup */ +#define MAXSTREAMCHANS 2 /* maximum number of channels: restricted to 2 by Ogg specs */ +#define UPDATE_INTERVAL 250 /* time in milliseconds between updates of output values */ + +static char *oggcast_version = "oggcast~: ogg/vorbis streaming client version 0.2h, written by Olaf Matthes"; + +static t_class *oggcast_class; + +typedef struct _oggcast +{ + t_object x_obj; + t_float *x_f; + t_clock *x_clock_connect; + t_clock *x_clock_pages; + t_outlet *x_connection; /* outlet for connection state */ + t_outlet *x_outpages; /* outlet for no. of ogg pages */ + + t_float *x_buf; /* audio data buffer */ + t_int x_bufsize; /* buffer size in bytes */ + t_int x_ninlets; /* 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 */ + + /* 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_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_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 */ + t_float x_pages; /* number of pages that have been output to server */ + t_float x_lastpages; + + /* ringbuffer stuff */ + t_float *x_buffer; /* data to be buffered (ringbuffer)*/ + t_int x_bytesbuffered; /* number of unprocessed bytes in buffer */ + + /* ogg/vorbis format stuff */ + t_int x_samplerate; /* samplerate of stream (default = getsr() ) */ + t_int x_skip; /* samples from input to skip (for resampling) */ + t_float x_quality; /* desired quality level from 0.0 to 1.0 (lo to hi) */ + t_int x_br_max; /* max. bitrate of ogg/vorbis stream */ + t_int x_br_nom; /* nom. bitrate of ogg/vorbis stream */ + t_int x_br_min; /* min. bitrate of ogg/vorbis stream */ + t_int x_channels; /* number of channels (1 or 2) */ + t_int x_vbr; + + /* IceCast server stuff */ + char* x_passwd; /* password for server */ + char* x_bcname; /* name of broadcast */ + char* x_bcurl; /* url of broadcast */ + char* x_bcgenre; /* genre of broadcast */ + char* x_bcdescription; /* description */ + char* x_bcartist; /* artist */ + char* x_bclocation; + char* x_bccopyright; + char* x_bcperformer; + char* x_bccontact; + char* x_bcdate; /* system date when broadcast started */ + char* x_hostname; /* name or IP of host to connect to */ + char* x_mountpoint; /* mountpoint for IceCast server */ + t_float x_port; /* port number on which the connection is made */ + t_int x_bcpublic; /* do(n't) publish broadcast on www.oggcast.com */ + t_int x_servertype; /* type of server: 0 = JRoar or old Icecast2; 1 = new Icecast2 */ + + + + + t_int x_connectstate; /* indicates to state of socket connection */ + t_int x_outvalue; /* value that has last been output via outlet */ + t_int x_fd; /* the socket number */ + t_resample x_resample; /* resampling unit */ + t_int x_recover; /* indicate how to behave on buffer underruns */ + + /* thread stuff */ + pthread_mutex_t x_mutex; + pthread_cond_t x_requestcondition; + pthread_cond_t x_answercondition; + pthread_t x_childthread; +} t_oggcast; + + /* check server for writeability */ +static int oggcast_checkserver(t_int sock) +{ + fd_set fdset; + struct timeval ztout; + fd_set writeset; + fd_set exceptset; + + FD_ZERO(&writeset); + FD_ZERO(&exceptset); + FD_SET(sock, &writeset ); + FD_SET(sock, &exceptset ); + + if(select(sock+1, NULL, &writeset, &exceptset, &ztout) > 0) + { + if(!FD_ISSET(sock, &writeset)) + { + post("oggcast~: can not write data to the server, quitting"); + return -1; + } + if(FD_ISSET(sock, &exceptset)) + { + post("oggcast~: socket returned an error, quitting"); + return -1; + } + } + return 0; +} + + /* stream ogg/vorbis to IceCast2 server */ +static int oggcast_stream(t_oggcast *x, t_int fd) +{ + int err = -1; /* error return code */ + int pages = 0; + + /* write out pages (if any) */ + while(!x->x_eos) + { + int result=ogg_stream_pageout(&(x->x_os),&(x->x_og)); + if(result==0)break; + err = send(fd, x->x_og.header, x->x_og.header_len, 0); + if(err < 0) + { + error("oggcast~: could not send ogg header to server (%d)", err); + x->x_eos = 1; /* indicate (artificial) end of stream */ + return err; + } + err = send(fd, x->x_og.body, x->x_og.body_len, 0); + if(err < 0) + { + error("oggcast~: could not send ogg body to server (%d)", err); + x->x_eos = 1; /* indicate (artificial) end of stream */ + return err; + } + pages++; /* count number of pages */ + /* there might be more than one pages we have to send */ + if(ogg_page_eos(&(x->x_og)))x->x_eos=1; + } + return (pages); +} + + /* ogg/vorbis decoder setup */ +static int oggcast_vorbis_init(t_oggcast *x) +{ + int err = -1; + + x->x_eos = 0; + x->x_skip = 1; /* assume no resampling */ + /* choose an encoding mode */ + vorbis_info_init(&(x->x_vi)); + + if(x->x_samplerate != sys_getsr()) /* downsampling for Oliver (http://radiostudio.org) */ + { + if(sys_getsr() / x->x_samplerate == 2.0) + { + post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate); + x->x_skip = 2; + } + else if(sys_getsr() / x->x_samplerate == 4.0) + { + post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate); + x->x_skip = 4; + } + else if(sys_getsr() / x->x_samplerate == 3.0) + { + post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate); + x->x_skip = 3; + } else post("oggcast~: warning: resampling from %.0f to %d not supported", sys_getsr(), x->x_samplerate); + } + if(x->x_vbr == 1) + { /* quality based setting */ + if(vorbis_encode_init_vbr(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_quality)) + { + post("oggcast~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); + vorbis_info_clear(&(x->x_vi)); + return (-1); + } + } + else + { /* bitrate based setting */ + if(vorbis_encode_init(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_br_max*1024, x->x_br_nom*1024, x->x_br_min*1024)) + { + post("oggcast~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); + vorbis_info_clear(&(x->x_vi)); + return (-1); + } + } + + /* add a comment */ + vorbis_comment_init(&(x->x_vc)); + vorbis_comment_add_tag(&(x->x_vc),"TITLE", x->x_bcname); + vorbis_comment_add_tag(&(x->x_vc),"ARTIST", x->x_bcartist); + vorbis_comment_add_tag(&(x->x_vc),"GENRE",x->x_bcgenre); + vorbis_comment_add_tag(&(x->x_vc),"DESCRIPTION", x->x_bcdescription); + vorbis_comment_add_tag(&(x->x_vc),"LOCATION",x->x_bclocation); + vorbis_comment_add_tag(&(x->x_vc),"PERFORMER",x->x_bcperformer); + vorbis_comment_add_tag(&(x->x_vc),"COPYRIGHT",x->x_bccopyright); + vorbis_comment_add_tag(&(x->x_vc),"CONTACT",x->x_bccontact); + vorbis_comment_add_tag(&(x->x_vc),"DATE",x->x_bcdate); + vorbis_comment_add_tag(&(x->x_vc),"ENCODER","oggcast~ v0.2 for pure-data"); + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init(&(x->x_vd),&(x->x_vi)); + vorbis_block_init(&(x->x_vd),&(x->x_vb)); + + /* set up our packet->stream encoder */ + /* pick a random serial number; that way we can more likely build + chained streams just by concatenation */ + srand(time(NULL)); + ogg_stream_init(&(x->x_os),rand()); + + /* Vorbis streams begin with three headers; the initial header (with + most of the codec setup parameters) which is mandated by the Ogg + bitstream spec. The second header holds any comment fields. The + third header holds the bitstream codebook. We merely need to + make the headers, then pass them to libvorbis one at a time; + libvorbis handles the additional Ogg bitstream constraints */ + + { + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout(&(x->x_vd),&(x->x_vc),&header,&header_comm,&header_code); + ogg_stream_packetin(&(x->x_os),&header); /* automatically placed in its own page */ + ogg_stream_packetin(&(x->x_os),&header_comm); + ogg_stream_packetin(&(x->x_os),&header_code); + + /* We don't have to write out here, but doing so makes streaming + * much easier, so we do, flushing ALL pages. This ensures the actual + * audio data will start on a new page + * + * IceCast2 server will take this as a first info about our stream + */ + while(!x->x_eos) + { + int result=ogg_stream_flush(&(x->x_os),&(x->x_og)); + if(result==0)break; + err = send(x->x_fd, x->x_og.header, x->x_og.header_len, 0); + if(err < 0) + { + error("oggcast~: could not send ogg header to server (%d)", err); + x->x_eos = 1; /* indicate end of stream */ + return (-1); + } + err = send(x->x_fd, x->x_og.body, x->x_og.body_len, 0); + if(err < 0) + { + error("oggcast~: could not send ogg body to server (%d)", err); + x->x_eos = 1; /* indicate end of stream */ + return (-1); + } + } + } + return (0); +} + + /* deinit the ogg/vorbis decoder */ +static void oggcast_vorbis_deinit(t_oggcast *x) +{ + vorbis_analysis_wrote(&(x->x_vd),0); + /* get rid of remaining data in encoder, if any */ + while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1) + { + vorbis_analysis(&(x->x_vb),NULL); + vorbis_bitrate_addblock(&(x->x_vb)); + + while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op))) + { + ogg_stream_packetin(&(x->x_os),&(x->x_op)); + oggcast_stream(x, x->x_fd); + } + } + /* clean up and exit. vorbis_info_clear() must be called last */ + ogg_stream_clear(&(x->x_os)); + vorbis_block_clear(&(x->x_vb)); + vorbis_dsp_clear(&(x->x_vd)); + vorbis_comment_clear(&(x->x_vc)); + vorbis_info_clear(&(x->x_vi)); +} + + /* encode ogg/vorbis and stream new data */ +static int oggcast_encode(t_oggcast *x, float *buf, int channels, int fifosize, int fd) +{ + unsigned short i, ch; + int err = 0; + int n, pages = 0; + + /* expose the buffer to submit data */ + float **inbuffer=vorbis_analysis_buffer(&(x->x_vd),READ); + + /* read from buffer */ + for(n = 0; n < READ / channels; n++) /* fill encode buffer */ + { + for(ch = 0; ch < channels; ch++) + { + inbuffer[ch][n] = *buf++; + } + } + /* tell the library how much we actually submitted */ + vorbis_analysis_wrote(&(x->x_vd),n); + + /* vorbis does some data preanalysis, then divvies up blocks for + more involved (potentially parallel) processing. Get a single + block for encoding now */ + while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1) + { + /* analysis, assume we want to use bitrate management */ + vorbis_analysis(&(x->x_vb),NULL); + vorbis_bitrate_addblock(&(x->x_vb)); + + while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op))) + { + /* weld the packet into the bitstream */ + ogg_stream_packetin(&(x->x_os),&(x->x_op)); + err = oggcast_stream(x, fd); /* stream packet to server */ + if(err >= 0) + { + pages += err; /* count pages */ + } + else return (err); + } + } + return (pages); +} + + /* connect to icecast2 server */ +static int oggcast_child_connect(char *hostname, char *mountpoint, t_int portno, + char *passwd, char *bcname, char *bcurl, + char *bcgenre, t_int bcpublic, t_int br_nom, t_int servertype) +{ + struct sockaddr_in server; + struct hostent *hp; + + /* variables used for communication with server */ + const char * buf = 0; + char resp[STRBUF_SIZE]; + unsigned int len; + fd_set fdset; + struct timeval tv; + t_int sockfd; /* our internal handle for the socket */ + t_int ret; + + sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) + { + error("oggcast~: 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("oggcast~: 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("oggcast~: connecting to port %d", portno); + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) + { + error("oggcast~: 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("oggcast~: can not read from socket"); + sys_closesocket(sockfd); + return (-1); + } + + post("oggcast~: logging in to IceCast2 server..."); + /* now try to log in at IceCast2 server using ICE/1.0 scheme */ + if(servertype == 0) + { + /* send the request, a string like: "SOURCE /<mountpoint> ICE/1.0\n" */ + buf = "SOURCE "; + send(sockfd, buf, strlen(buf), 0); + buf = "/"; + send(sockfd, buf, strlen(buf), 0); + buf = mountpoint; + send(sockfd, buf, strlen(buf), 0); + buf = " ICE/1.0"; + send(sockfd, buf, strlen(buf), 0); + /* send the ice headers */ + /* password */ + buf = "\nice-password: "; + send(sockfd, buf, strlen(buf), 0); + buf = passwd; + send(sockfd, buf, strlen(buf), 0); + /* name */ + buf = "\r\nice-name: "; + send(sockfd, buf, strlen(buf), 0); + buf = bcname; + send(sockfd, buf, strlen(buf), 0); + /* url */ + buf = "\r\nice-url: "; + send(sockfd, buf, strlen(buf), 0); + buf = bcurl; + send(sockfd, buf, strlen(buf), 0); + /* genre */ + buf = "\r\nice-genre: "; + send(sockfd, buf, strlen(buf), 0); + buf = bcgenre; + send(sockfd, buf, strlen(buf), 0); + /* public */ + buf = "\r\nice-public: "; + send(sockfd, buf, strlen(buf), 0); + if(bcpublic==0) /* set the public flag for broadcast */ + { + buf = "no"; + } + else + { + buf ="yes"; + } + send(sockfd, buf, strlen(buf), 0); + /* bitrate */ + buf = "\r\nice-bitrate: "; + send(sockfd, buf, strlen(buf), 0); + if(sprintf(resp, "%d", br_nom) == -1) /* convert int to a string */ + { + error("oggcast~: wrong bitrate"); + } + send(sockfd, resp, strlen(resp), 0); + /* description */ + buf = "\r\nice-description: "; + send(sockfd, buf, strlen(buf), 0); + buf = "ogg/vorbis streamed from pure-data with oggcast~"; + send(sockfd, buf, strlen(buf), 0); + /* end of header */ + buf = "\r\n\r\n"; + send(sockfd, buf, strlen(buf), 0); + /* end login for IceCast using ICE/1.0 scheme */ + } + else /* or try to log in at IceCast2 server using HTTP/1.0 base auth scheme */ + { + /* send the request, a string like: "SOURCE /<mountpoint> HTTP/1.0\nContent-Type: application/x-ogg" */ + buf = "SOURCE "; + send(sockfd, buf, strlen(buf), 0); + buf = "/"; + send(sockfd, buf, strlen(buf), 0); + buf = mountpoint; + send(sockfd, buf, strlen(buf), 0); + buf = " HTTP/1.0"; + send(sockfd, buf, strlen(buf), 0); + buf = "\nContent-Type: application/x-ogg"; + send(sockfd, buf, strlen(buf), 0); + /* send the ice headers */ + /* password */ + buf = "\nice-password: "; + send(sockfd, buf, strlen(buf), 0); + buf = passwd; + send(sockfd, buf, strlen(buf), 0); + /* name */ + buf = "\r\nice-name: "; + send(sockfd, buf, strlen(buf), 0); + buf = bcname; + send(sockfd, buf, strlen(buf), 0); + /* url */ + buf = "\r\nice-url: "; + send(sockfd, buf, strlen(buf), 0); + buf = bcurl; + send(sockfd, buf, strlen(buf), 0); + /* genre */ + buf = "\r\nice-genre: "; + send(sockfd, buf, strlen(buf), 0); + buf = bcgenre; + send(sockfd, buf, strlen(buf), 0); + /* public */ + buf = "\r\nice-public: "; + send(sockfd, buf, strlen(buf), 0); + if(bcpublic==0) /* set the public flag for broadcast */ + { + buf = "no"; + } + else + { + buf ="yes"; + } + send(sockfd, buf, strlen(buf), 0); + /* bitrate */ + buf = "\r\nice-bitrate: "; + send(sockfd, buf, strlen(buf), 0); + if(sprintf(resp, "%d", br_nom) == -1) /* convert int to a string */ + { + error("oggcast~: wrong bitrate"); + } + send(sockfd, resp, strlen(resp), 0); + /* description */ + buf = "\r\nice-description: "; + send(sockfd, buf, strlen(buf), 0); + buf = "ogg/vorbis streamed from pure-data with oggcast~"; + send(sockfd, buf, strlen(buf), 0); + /* end of header */ + buf = "\r\n\r\n"; + send(sockfd, buf, strlen(buf), 0); + /* end login for IceCast2 using ICE/1.0 scheme */ + } + + /* check if we can write to server */ + if(oggcast_checkserver(sockfd)!= 0) + { + post("oggcast~: error: server refused to receive data"); + return (-1); + } + + post("oggcast~: logged in to http://%s:%d/%s", hp->h_name, portno, mountpoint); + + return (sockfd); +} + + + +static void oggcast_child_disconnect(t_int fd) +{ + sys_closesocket(fd); + post("oggcast~: connection closed"); +} +/************** the child thread which performs data I/O ***********/ + +#if 0 /* set this to 1 to get debugging output */ +static void pute(char *s) /* debug routine */ +{ + write(2, s, strlen(s)); +} +#else +#define pute(x) +#endif + +#if 1 +#define oggcast_cond_wait pthread_cond_wait +#define oggcast_cond_signal pthread_cond_signal +#else +#include <sys/time.h> /* debugging version... */ +#include <sys/types.h> +static void oggcast_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 oggcast_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 oggcast_cond_wait(a,b) oggcast_fakewait(b) +#define oggcast_cond_signal(a) +#endif + +static void *oggcast_child_main(void *zz) +{ + t_oggcast *x = zz; + time_t now; /* to get the time */ + pute("1\n"); + pthread_mutex_lock(&x->x_mutex); + while (1) + { + int fd, fifotail; + pute("0\n"); + if (x->x_requestcode == REQUEST_NOTHING) + { + pute("wait 2\n"); + oggcast_cond_signal(&x->x_answercondition); + oggcast_cond_wait(&x->x_requestcondition, &x->x_mutex); + pute("3\n"); + } + // connect to Icecast2 server + else if (x->x_requestcode == REQUEST_CONNECT) + { + char boo[100]; + int sysrtn, wantbytes; + + /* copy connect stuff out of the data structure so we can + relinquish the mutex while we're connecting to server. */ + char *hostname = x->x_hostname; + char *mountpoint = x->x_mountpoint; + t_int portno = x->x_port; + char *passwd = x->x_passwd; + char *bcname = x->x_bcname; + char *bcgenre = x->x_bcgenre; + char *bcurl = x->x_bcurl; + t_int bcpublic = x->x_bcpublic; + t_int br_nom = x->x_br_nom; + t_int servertype = x->x_servertype; + /* alter the request code so that an ensuing "open" will get + noticed. */ + pute("4\n"); + x->x_requestcode = REQUEST_BUSY; + x->x_connecterror = 0; + + /* open the socket with the mutex unlocked in case + we're not already connected */ + if(x->x_fd < 0) + { + pthread_mutex_unlock(&x->x_mutex); + fd = oggcast_child_connect(hostname, mountpoint, portno, passwd, bcname, + bcurl, bcgenre, bcpublic, br_nom, servertype); + pthread_mutex_lock(&x->x_mutex); + pute("5\n"); + /* copy back into the instance structure. */ + x->x_connectstate = 1; + clock_delay(x->x_clock_connect, 0); + x->x_fd = fd; + if (fd < 0) + { + x->x_connecterror = fd; + x->x_connectstate = 0; + clock_delay(x->x_clock_connect, 0); + pute("connect failed\n"); + goto lost; + } + else + { + /* get the time for the DATE comment */ + now=time(NULL); + x->x_bcdate = ctime(&now); + x->x_pages = 0; + clock_delay(x->x_clock_pages, 0); + /* initialise the encoder */ + if(oggcast_vorbis_init(x) == 0) + { + post("oggcast~: ogg/vorbis encoder initialised"); + x->x_eos = 0; + x->x_state = STATE_STREAM; + } + else + { + post("oggcast~: could not init encoder"); + oggcast_child_disconnect(fd); + post("oggcast~: connection closed due to initialisation error"); + x->x_fd = -1; + x->x_connectstate = 0; + clock_delay(x->x_clock_connect, 0); + pute("oggcast~: initialisation failed\n"); + goto lost; + } + } + x->x_fifotail = fifotail = 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_channels * READ)); + /* 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_channels * x->x_vecsize)); + } + /* check if another request has been made; if so, field it */ + if (x->x_requestcode != REQUEST_BUSY) + goto lost; + pute("6\n"); + + while (x->x_requestcode == REQUEST_BUSY) + { + int fifosize = x->x_fifosize, fifotail, channels; + float *buf = x->x_buf; + pute("77\n"); + + /* if the head is < the tail, we can immediately write + from tail to end of fifo to disk; otherwise we hold off + writing until there are at least WRITESIZE bytes in the + buffer */ + if (x->x_fifohead < x->x_fifotail || + x->x_fifohead >= x->x_fifotail + READ + || (x->x_requestcode == REQUEST_CLOSE && + x->x_fifohead != x->x_fifotail)) + { /* encode audio and send to server */ + pute("8\n"); + fifotail = x->x_fifotail; + channels = x->x_channels; + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + sysrtn = oggcast_encode(x, buf + fifotail, channels, fifosize, fd); + pthread_mutex_lock(&x->x_mutex); + if (x->x_requestcode != REQUEST_BUSY && + x->x_requestcode != REQUEST_CLOSE) + break; + if (sysrtn < 0) + { + post("oggcast~: closing due to error..."); + goto lost; + } + else + { + x->x_fifotail += READ; + x->x_pages += sysrtn; + if (x->x_fifotail >= fifosize) + x->x_fifotail = 0; + } + sprintf(boo, "after: head %d, tail %d, pages %d\n", x->x_fifohead, x->x_fifotail, sysrtn); + pute(boo); + } + else /* just wait... */ + { + pute("wait 7a ...\n"); + oggcast_cond_signal(&x->x_answercondition); + pute("signalled\n"); + oggcast_cond_wait(&x->x_requestcondition, + &x->x_mutex); + pute("7a done\n"); + continue; + } + /* signal parent in case it's waiting for data */ + oggcast_cond_signal(&x->x_answercondition); + } + } + + /* reinit encoder (settings have changed) */ + else if (x->x_requestcode == REQUEST_REINIT) + { + pthread_mutex_unlock(&x->x_mutex); + oggcast_vorbis_deinit(x); + oggcast_vorbis_init(x); + pthread_mutex_lock(&x->x_mutex); + post("oggcast~: ogg/vorbis encoder reinitialised"); + x->x_state = STATE_STREAM; + if (x->x_requestcode == REQUEST_REINIT) + x->x_requestcode = REQUEST_CONNECT; + oggcast_cond_signal(&x->x_answercondition); + } + /* close connection to server (disconnect) */ + else if (x->x_requestcode == REQUEST_CLOSE) + { +lost: + x->x_state = STATE_IDLE; + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + oggcast_vorbis_deinit(x); + oggcast_child_disconnect(fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + if (x->x_requestcode == REQUEST_CLOSE) + x->x_requestcode = REQUEST_NOTHING; + if (x->x_requestcode == REQUEST_BUSY) /* disconnect due to error */ + x->x_requestcode = REQUEST_NOTHING; + x->x_connectstate = 0; + clock_delay(x->x_clock_connect, 0); + x->x_eos = 1; + oggcast_cond_signal(&x->x_answercondition); + } + // quit everything + else if (x->x_requestcode == REQUEST_QUIT) + { + x->x_state = STATE_IDLE; + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + oggcast_vorbis_deinit(x); + oggcast_child_disconnect(fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + x->x_connectstate = 0; + clock_delay(x->x_clock_connect, 0); + x->x_requestcode = REQUEST_NOTHING; + oggcast_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 oggcast_tick_connect(t_oggcast *x) +{ + pthread_mutex_lock(&x->x_mutex); + outlet_float(x->x_connection, x->x_connectstate); + pthread_mutex_unlock(&x->x_mutex); +} + +static void oggcast_tick_pages(t_oggcast *x) +{ + /* output new no. of pages if anything changed */ + t_float pages; + pthread_mutex_lock(&x->x_mutex); + pages = x->x_pages; /* get current value with mutex locked */ + pthread_mutex_unlock(&x->x_mutex); + if(pages != x->x_lastpages) + { + outlet_float(x->x_outpages, pages); + x->x_lastpages = pages; + } + clock_delay(x->x_clock_pages, UPDATE_INTERVAL); /* come back again... */ +} + +static void *oggcast_new(t_floatarg fnchannels, t_floatarg fbufsize) +{ + t_oggcast *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 <= 0) 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_oggcast *)pd_new(oggcast_class); + + for (i = 1; i < nchannels; i++) + inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); + x->x_connection = outlet_new(&x->x_obj, gensym("float")); + x->x_outpages = outlet_new(&x->x_obj, gensym("float")); + x->x_ninlets = nchannels; + + x->x_clock_connect = clock_new(x, (t_method)oggcast_tick_connect); + x->x_clock_pages = clock_new(x, (t_method)oggcast_tick_pages); + + 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_state = STATE_IDLE; + x->x_buf = buf; + x->x_bufsize = bufsize; + x->x_siginterval = 32; /* signal 32 times per buffer */ + /* I found this to be most efficient on my machine */ + x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0; + + x->x_connectstate = 0; /* indicating state of connection */ + x->x_outvalue = 0; /* value at output currently is 0 */ + + x->x_samplerate = sys_getsr(); + x->x_resample.upsample = x->x_resample.downsample = 1; /* don't resample */ + + + x->x_fd = -1; + x->x_eos = 0; + x->x_vbr = 1; /* use the vbr setting by default */ + x->x_passwd = "letmein"; + x->x_samplerate = sys_getsr(); /* default to Pd's sampling rate */ + x->x_skip = 1; /* no resampling supported */ + x->x_quality = 0.4; /* quality 0.4 gives roughly 128kbps VBR stream */ + x->x_channels = nchannels; /* stereo */ + x->x_br_max = 144; + x->x_br_nom = 128; + x->x_br_min = 96; + x->x_pages = x->x_lastpages = 0; + x->x_bcname = "ogg/vorbis stream"; + x->x_bcurl = "http://www.akustische-kunst.org/puredata/"; + x->x_bcgenre = "experimental"; + x->x_bcdescription = "ogg/vorbis stream emitted from pure-data with oggcast~"; + x->x_bcartist = "Pd and oggcast~ v0.2"; + x->x_bclocation = x->x_bcurl; + x->x_bccopyright = ""; + x->x_bcperformer = ""; + x->x_bccontact = ""; + x->x_bcdate = ""; + x->x_bcpublic = 1; + x->x_mountpoint = "puredata.ogg"; + x->x_servertype = 0; /* ICE/1.0 protocol for JRoar and old Icecast2 */ + + post(oggcast_version); + post("oggcast~: set buffer to %dk bytes", bufsize / 1024); + post("oggcast~: encoding %d channels @ %d Hz", x->x_channels, x->x_samplerate); + + clock_delay(x->x_clock_pages, 0); + pthread_create(&x->x_childthread, 0, oggcast_child_main, x); + return (x); +} + +static t_int *oggcast_perform(t_int *w) +{ + t_oggcast *x = (t_oggcast *)(w[1]); + int vecsize = x->x_vecsize, ninlets = x->x_ninlets, channels = x->x_channels, i, j, skip = x->x_skip; + float *sp = x->x_buf; + pthread_mutex_lock(&x->x_mutex); + if (x->x_state != STATE_IDLE) + { + int wantbytes; + /* get 'wantbytes' bytes from inlet */ + wantbytes = channels * vecsize / skip; /* we'll get vecsize bytes per channel */ + /* check if there is enough space in buffer to write all samples */ + while (x->x_fifotail > x->x_fifohead && + x->x_fifotail < x->x_fifohead + wantbytes + 1) + { + pute("wait...\n"); + oggcast_cond_signal(&x->x_requestcondition); + oggcast_cond_wait(&x->x_answercondition, &x->x_mutex); + pute("done\n"); + } + + /* output audio */ + sp += x->x_fifohead; + + if(ninlets >= channels) + { + for(j = 0; j < vecsize; j += skip) + { + for(i = 0; i < channels; i++) + { + *sp++ = x->x_outvec[i][j]; + } + } + } + else if(channels == ninlets * 2) /* convert mono -> stereo */ + { + for(j = 0; j < vecsize; j += skip) + { + for(i = 0; i < ninlets; i++) + { + *sp++ = x->x_outvec[i][j]; + *sp++ = x->x_outvec[i][j]; + } + } + } + + + x->x_fifohead += wantbytes; + if (x->x_fifohead >= x->x_fifosize) + x->x_fifohead = 0; + /* signal the child thread */ + if ((--x->x_sigcountdown) <= 0) + { + pute("signal 1\n"); + oggcast_cond_signal(&x->x_requestcondition); + x->x_sigcountdown = x->x_sigperiod; + } + } + + pthread_mutex_unlock(&x->x_mutex); + + return (w+2); +} + + +static void oggcast_disconnect(t_oggcast *x) +{ + /* LATER rethink whether you need the mutex just to set a variable? */ + pthread_mutex_lock(&x->x_mutex); + if(x->x_fd >= 0) + { + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_CLOSE; + oggcast_cond_signal(&x->x_requestcondition); + } + else post("oggcast~: not connected"); + pthread_mutex_unlock(&x->x_mutex); +} + + + /* connect method. Called as: + connect <hostname or IP> <mountpoint> <portnumber> + */ + +static void oggcast_connect(t_oggcast *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 >= 0) + { + post("oggcast~: already connected"); + } + else + { + x->x_requestcode = REQUEST_CONNECT; + x->x_hostname = hostsym->s_name; + x->x_mountpoint = mountsym->s_name; + x->x_port = portno; + + x->x_fifotail = 0; + x->x_fifohead = 0; + // if(x->x_recover != 1)x->x_fifobytes = 0; + + x->x_connecterror = 0; + x->x_state = STATE_STARTUP; + oggcast_cond_signal(&x->x_requestcondition); + } + pthread_mutex_unlock(&x->x_mutex); +} + +static void oggcast_float(t_oggcast *x, t_floatarg f) +{ + if (f != 0) + { + pthread_mutex_lock(&x->x_mutex); + if(x->x_fd >= 0) + { + post("oggcast~: already connected"); + } + else + { + if(x->x_recover != 1) + { + x->x_fifotail = 0; + x->x_fifohead = 0; + } + x->x_requestcode = REQUEST_CONNECT; + + x->x_fifotail = 0; + x->x_fifohead = 0; + + x->x_connecterror = 0; + x->x_state = STATE_STARTUP; + oggcast_cond_signal(&x->x_requestcondition); + } + pthread_mutex_unlock(&x->x_mutex); + } + else oggcast_disconnect(x); +} + +static void oggcast_dsp(t_oggcast *x, t_signal **sp) +{ + int i, ninlets = x->x_ninlets; + pthread_mutex_lock(&x->x_mutex); + x->x_vecsize = sp[0]->s_n; + + x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * ninlets * x->x_vecsize)); + for (i = 0; i < ninlets; i++) + x->x_outvec[i] = sp[i]->s_vec; + pthread_mutex_unlock(&x->x_mutex); + dsp_add(oggcast_perform, 1, x); +} + + /* set password for oggcast server */ +static void oggcast_password(t_oggcast *x, t_symbol *password) +{ + pthread_mutex_lock(&x->x_mutex); + x->x_passwd = password->s_name; + pthread_mutex_unlock(&x->x_mutex); +} + /* set comment fields for header (reads in just anything) */ +static void oggcast_comment(t_oggcast *x, t_symbol *s, t_int argc, t_atom* argv) +{ + t_int i = argc; + char *comment = NULL; + int len = strlen(atom_gensym(argv)->s_name); + + comment = atom_gensym(argv)->s_name; + + while (len--) + { + if(*(comment + len) == '=')*(comment + len) = ' '; + if(*(comment + len) == '_')*(comment + len) = ' '; + } + + pthread_mutex_lock(&x->x_mutex); + if(strstr(s->s_name, "ARTIST")) + { + x->x_bcartist = comment; + post("oggcast~: ARTIST=%s", x->x_bcartist); + } + else if(strstr(s->s_name, "GENRE")) + { + x->x_bcgenre = comment; + post("oggcast~: GENRE=%s", x->x_bcgenre); + } + else if(strstr(s->s_name, "TITLE")) + { + x->x_bcname = comment; + post("oggcast~: TITLE=%s", x->x_bcname); + } + else if(strstr(s->s_name, "PERFORMER")) + { + x->x_bcperformer = comment; + post("oggcast~: PERFORMER=%s", x->x_bcperformer); + } + else if(strstr(s->s_name, "LOCATION")) + { + x->x_bclocation = comment; + post("oggcast~: LOCATION=%s", x->x_bclocation); + } + else if(strstr(s->s_name, "COPYRIGHT")) + { + x->x_bccopyright = comment; + post("oggcast~: COPYRIGHT=%s", x->x_bccopyright); + } + else if(strstr(s->s_name, "CONTACT")) + { + x->x_bccontact = comment; + post("oggcast~: CONTACT=%s", x->x_bccontact); + } + else if(strstr(s->s_name, "DESCRIPTION")) + { + x->x_bcdescription = comment; + post("oggcast~: DESCRIPTION=%s", x->x_bcdescription); + } + else if(strstr(s->s_name, "DATE")) + { + x->x_bcdate = comment; + post("oggcast~: DATE=%s", x->x_bcdate); + } + else post("oggcast~: no method for %s", s->s_name); + if(x->x_state == STATE_STREAM) + { + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_REINIT; + oggcast_cond_signal(&x->x_requestcondition); + } + pthread_mutex_unlock(&x->x_mutex); +} + /* settings for variable bitrate encoding */ +static void oggcast_vbr(t_oggcast *x, t_floatarg fsr, t_floatarg fchannels, + t_floatarg fquality) +{ + pthread_mutex_lock(&x->x_mutex); + x->x_vbr = 1; + x->x_samplerate = (t_int)fsr; + x->x_quality = fquality; + x->x_channels = (t_int)fchannels; + post("oggcast~: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); + if(x->x_state == STATE_STREAM) + { + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_REINIT; + oggcast_cond_signal(&x->x_requestcondition); + } + pthread_mutex_unlock(&x->x_mutex); +} + + /* settings for bitrate-based vbr encoding */ +static void oggcast_vorbis(t_oggcast *x, t_floatarg fsr, t_floatarg fchannels, + t_floatarg fmax, t_floatarg fnom, t_floatarg fmin) +{ + pthread_mutex_lock(&x->x_mutex); + x->x_vbr = 0; + x->x_samplerate = (t_int)fsr; + x->x_channels = (t_int)fchannels; + x->x_br_max = (t_int)fmax; + x->x_br_nom = (t_int)fnom; + x->x_br_min = (t_int)fmin; + post("oggcast~: %d channels @ %d Hz, bitrates: max. %d / nom. %d / min. %d", + x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); + if(x->x_state == STATE_STREAM) + { + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_REINIT; + oggcast_cond_signal(&x->x_requestcondition); + } + pthread_mutex_unlock(&x->x_mutex); +} + /* select server type */ +static void oggcast_server(t_oggcast *x, t_floatarg f) +{ + pthread_mutex_lock(&x->x_mutex); + if(f) + { + x->x_servertype = 1; + post("oggcast~: set server type to new Icecast2 (HTTP/1.0 scheme)"); + } + else + { + x->x_servertype = 0; + post("oggcast~: set server type to JRoar (ICE/1.0 scheme)"); + } + pthread_mutex_unlock(&x->x_mutex); +} + /* print settings to pd's console window */ +static void oggcast_print(t_oggcast *x) +{ + pthread_mutex_lock(&x->x_mutex); + if(x->x_servertype) + post("oggcast~: server type is Icecast2"); + else + post("oggcast~: server type is JRoar"); + post("oggcast~: mountpoint at Icecast2: %s", x->x_mountpoint); + if(x->x_vbr == 1) + { + post("oggcast~: Ogg Vorbis encoder: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); + } + else + { + post("oggcast~: Ogg Vorbis encoder: %d channels @ %d Hz, bitrates: max. %d, nom. %d, min. %d", + x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); + } + post("oggcast~: Ogg Vorbis comments:"); + post(" TITLE = %s", x->x_bcname); + post(" ARTIST = %s", x->x_bcartist); + post(" PERFORMER = %s", x->x_bcperformer); + post(" GENRE = %s", x->x_bcgenre); + post(" LOCATION = %s", x->x_bclocation); + post(" COPYRIGHT = %s", x->x_bccopyright); + post(" CONTACT = %s", x->x_bccontact); + post(" DESCRIPTION = %s", x->x_bcdescription); + post(" DATE = %s", x->x_bcdate); + pthread_mutex_unlock(&x->x_mutex); +} + + +static void oggcast_free(t_oggcast *x) +{ + /* request QUIT and wait for acknowledge */ + void *threadrtn; + pthread_mutex_lock(&x->x_mutex); + x->x_requestcode = REQUEST_QUIT; + post("stopping oggcast thread..."); + oggcast_cond_signal(&x->x_requestcondition); + while (x->x_requestcode != REQUEST_NOTHING) + { + post("signalling..."); + oggcast_cond_signal(&x->x_requestcondition); + oggcast_cond_wait(&x->x_answercondition, &x->x_mutex); + } + pthread_mutex_unlock(&x->x_mutex); + if (pthread_join(x->x_childthread, &threadrtn)) + error("oggcast_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_connect); + clock_free(x->x_clock_pages); +} + +void oggcast_tilde_setup(void) +{ + oggcast_class = class_new(gensym("oggcast~"), (t_newmethod)oggcast_new, + (t_method)oggcast_free, sizeof(t_oggcast), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(oggcast_class, t_oggcast, x_f ); + class_addfloat(oggcast_class, (t_method)oggcast_float); + class_addmethod(oggcast_class, (t_method)oggcast_disconnect, gensym("disconnect"), 0); + class_addmethod(oggcast_class, (t_method)oggcast_dsp, gensym("dsp"), 0); + class_addmethod(oggcast_class, (t_method)oggcast_connect, gensym("connect"), A_GIMME, 0); + class_addmethod(oggcast_class, (t_method)oggcast_print, gensym("print"), 0); + class_addmethod(oggcast_class, (t_method)oggcast_password, gensym("passwd"), A_SYMBOL, 0); + class_addmethod(oggcast_class, (t_method)oggcast_vorbis, gensym("vorbis"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(oggcast_class, (t_method)oggcast_vbr, gensym("vbr"), A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(oggcast_class, (t_method)oggcast_server, gensym("server"), A_FLOAT, 0); + class_addanything(oggcast_class, oggcast_comment); + class_sethelpsymbol(oggcast_class, gensym("help-oggcast~.pd")); +}
\ No newline at end of file diff --git a/oggcast~/readme b/oggcast~/readme index b3090ec..0f6b84b 100644 --- a/oggcast~/readme +++ b/oggcast~/readme @@ -1,116 +1,116 @@ -Version 0.2
-copyright (c) 2002 by Olaf Matthes
-
-oggcast~ is a ogg/vorbis streaming external for pd (by Miller
-Puckette) that connects to an IceCast2 server.
-
-
-To run oggcast~ place the file oggcast~.dll for win or oggcast~.pd_linux
-in the directory of our patch or start pd with '-lib oggcast~' flag.
-
-To compile oggcast~ 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.
-
-*****************************************************************************
-
-oggcast~ 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:
-
-Use message "vbr <samplerate> <channels> <quality>" to set the vorbis
-encoding parameters. Resampling is currently not supported, so 'samplerate'
-should be the one pd is running at. 'channels' specyfies the number of channels
-to stream. This can be set to 2 (default) or 1 which means mono stream taking
-the leftmost audio input only. 'quality' can be a value between 0.0 and 1.0
-giving the quality of the stream. 0.4 (default) results in a stream that's
-about 128kbps.
-
-Since Vorbis uses VBR encoding, bitrates vary depending on the type of audio
-signal to be encoded. A pure sine [osc~] results in the smalest stream, com-
-plex audio signals will increase this value significantly. To test the maximum
-bitrate that might occur for a quality setting use noise~ as signal input.
-
-Use message "vorbis <samplerate> <channels> <maximum bitrate> <nominal bit-
-rate> <minimal bitrate>" to set encoding quality on the basis of bitrates.
-When setting all three bitrate parameters to the same value one gets a
-constant bitrate stream. Values are in kbps!
-
-Message "connect <host> <mountpoint> <port>" connects to the 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 send data to the server.
-The mountpoint should end with '.ogg' to indiocate to the player/client that
-it is an ogg/vorbis encoded stream.
-
-Use "passwd <passwort>" to set your password (default is 'letmein') and
-"disconnect" to disconnect from the server. "print" prints out the current
-vorbis encoder settings.
-
-To set the comment tags in the ogg/vorbis header (which can be displayed by
-the receiving client) use message "<NAMEOFTAG> <comment>". Supported tags are:
-TITLE, ARTIST, GENRE, PERFORMER, LOCATION, COPYRIGHT, CONTACT, DESCRIPTION and
-DATE (which is automatically set to the date/time the broadcast started). To
-get spaces use '=' or '_' instead. Note that under Win2k '=' sometimes get lost
-from the patch after saving!!!
-
-
-Listening to it:
-
-To listen to ogg/vorbis encoded livestreams many player need an extra plug-in.
-Have a look at http://www.vorbis.com/ to find the appropiate plug-in for your
-player.
-To play back the stream just open lacation http://<server>:<port>/<mountpoint>.
-
-Note that changing encoding parameters or header comments while oggcast~ is
-streaming to the server might result in audible dropouts.
-
-
-
-Latest version can be found at:
-http://www.akustische-kunst.de/puredata/
-
+Version 0.2 +copyright (c) 2002 by Olaf Matthes + +oggcast~ is a ogg/vorbis streaming external for pd (by Miller +Puckette) that connects to an IceCast2 server. + + +To run oggcast~ place the file oggcast~.dll for win or oggcast~.pd_linux +in the directory of our patch or start pd with '-lib oggcast~' flag. + +To compile oggcast~ 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. + +***************************************************************************** + +oggcast~ 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: + +Use message "vbr <samplerate> <channels> <quality>" to set the vorbis +encoding parameters. Resampling is currently not supported, so 'samplerate' +should be the one pd is running at. 'channels' specyfies the number of channels +to stream. This can be set to 2 (default) or 1 which means mono stream taking +the leftmost audio input only. 'quality' can be a value between 0.0 and 1.0 +giving the quality of the stream. 0.4 (default) results in a stream that's +about 128kbps. + +Since Vorbis uses VBR encoding, bitrates vary depending on the type of audio +signal to be encoded. A pure sine [osc~] results in the smalest stream, com- +plex audio signals will increase this value significantly. To test the maximum +bitrate that might occur for a quality setting use noise~ as signal input. + +Use message "vorbis <samplerate> <channels> <maximum bitrate> <nominal bit- +rate> <minimal bitrate>" to set encoding quality on the basis of bitrates. +When setting all three bitrate parameters to the same value one gets a +constant bitrate stream. Values are in kbps! + +Message "connect <host> <mountpoint> <port>" connects to the 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 send data to the server. +The mountpoint should end with '.ogg' to indiocate to the player/client that +it is an ogg/vorbis encoded stream. + +Use "passwd <passwort>" to set your password (default is 'letmein') and +"disconnect" to disconnect from the server. "print" prints out the current +vorbis encoder settings. + +To set the comment tags in the ogg/vorbis header (which can be displayed by +the receiving client) use message "<NAMEOFTAG> <comment>". Supported tags are: +TITLE, ARTIST, GENRE, PERFORMER, LOCATION, COPYRIGHT, CONTACT, DESCRIPTION and +DATE (which is automatically set to the date/time the broadcast started). To +get spaces use '=' or '_' instead. Note that under Win2k '=' sometimes get lost +from the patch after saving!!! + + +Listening to it: + +To listen to ogg/vorbis encoded livestreams many player need an extra plug-in. +Have a look at http://www.vorbis.com/ to find the appropiate plug-in for your +player. +To play back the stream just open lacation http://<server>:<port>/<mountpoint>. + +Note that changing encoding parameters or header comments while oggcast~ is +streaming to the server might result in audible dropouts. + + + +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 diff --git a/oggcast~/vorbisenc.h b/oggcast~/vorbisenc.h index 901c47a..1fc8913 100644 --- a/oggcast~/vorbisenc.h +++ b/oggcast~/vorbisenc.h @@ -1,93 +1,93 @@ -/********************************************************************
- * *
- * 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: vorbis encode-engine setup
- last mod: $Id: vorbisenc.h,v 1.1 2002-11-26 10:51:49 ggeiger Exp $
-
- ********************************************************************/
-
-#ifndef _OV_ENC_H_
-#define _OV_ENC_H_
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-#include "codec.h"
-
-extern int vorbis_encode_init(vorbis_info *vi,
- long channels,
- long rate,
-
- long max_bitrate,
- long nominal_bitrate,
- long min_bitrate);
-
-extern int vorbis_encode_setup_managed(vorbis_info *vi,
- long channels,
- long rate,
-
- long max_bitrate,
- long nominal_bitrate,
- long min_bitrate);
-
-extern int vorbis_encode_setup_vbr(vorbis_info *vi,
- long channels,
- long rate,
-
- float /* quality level from 0. (lo) to 1. (hi) */
- );
-
-extern int vorbis_encode_init_vbr(vorbis_info *vi,
- long channels,
- long rate,
-
- float base_quality /* quality level from 0. (lo) to 1. (hi) */
- );
-
-extern int vorbis_encode_setup_init(vorbis_info *vi);
-
-extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg);
-
-#define OV_ECTL_RATEMANAGE_GET 0x10
-
-#define OV_ECTL_RATEMANAGE_SET 0x11
-#define OV_ECTL_RATEMANAGE_AVG 0x12
-#define OV_ECTL_RATEMANAGE_HARD 0x13
-
-#define OV_ECTL_LOWPASS_GET 0x20
-#define OV_ECTL_LOWPASS_SET 0x21
-
-#define OV_ECTL_IBLOCK_GET 0x30
-#define OV_ECTL_IBLOCK_SET 0x31
-
-struct ovectl_ratemanage_arg {
- int management_active;
-
- long bitrate_hard_min;
- long bitrate_hard_max;
- double bitrate_hard_window;
-
- long bitrate_av_lo;
- long bitrate_av_hi;
- double bitrate_av_window;
- double bitrate_av_window_center;
-};
-
-#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: vorbis encode-engine setup + last mod: $Id: vorbisenc.h,v 1.2 2003-04-23 10:36:25 xovo Exp $ + + ********************************************************************/ + +#ifndef _OV_ENC_H_ +#define _OV_ENC_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include "codec.h" + +extern int vorbis_encode_init(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_managed(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_vbr(vorbis_info *vi, + long channels, + long rate, + + float /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_init_vbr(vorbis_info *vi, + long channels, + long rate, + + float base_quality /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_setup_init(vorbis_info *vi); + +extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg); + +#define OV_ECTL_RATEMANAGE_GET 0x10 + +#define OV_ECTL_RATEMANAGE_SET 0x11 +#define OV_ECTL_RATEMANAGE_AVG 0x12 +#define OV_ECTL_RATEMANAGE_HARD 0x13 + +#define OV_ECTL_LOWPASS_GET 0x20 +#define OV_ECTL_LOWPASS_SET 0x21 + +#define OV_ECTL_IBLOCK_GET 0x30 +#define OV_ECTL_IBLOCK_SET 0x31 + +struct ovectl_ratemanage_arg { + int management_active; + + long bitrate_hard_min; + long bitrate_hard_max; + double bitrate_hard_window; + + long bitrate_av_lo; + long bitrate_av_hi; + double bitrate_av_window; + double bitrate_av_window_center; +}; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + + diff --git a/oggread~/codec.h b/oggread~/codec.h index 9365ea3..d986a9b 100644 --- a/oggread~/codec.h +++ b/oggread~/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/oggread~/help-oggread~.pd b/oggread~/help-oggread~.pd index a37e225..51e5972 100644 --- a/oggread~/help-oggread~.pd +++ b/oggread~/help-oggread~.pd @@ -1,34 +1,44 @@ -#N canvas 415 163 496 344 10;
-#X obj 35 298 dac~;
-#X floatatom 78 268 5 0 0;
-#X obj 35 236 oggread~;
-#X msg 55 83 start;
-#X msg 94 128 stop;
-#X msg 117 180 resume;
-#X msg 35 45 open myfile.ogg;
-#X text 99 81 play file from beginning;
-#X text 132 126 stop (pause) playing;
-#X text 165 179 resume playing at current position;
-#X msg 317 275 \; pd dsp 1;
-#X msg 383 275 \; pd dsp 0;
-#X obj 317 249 loadbang;
-#X text 185 50 get latest version at;
-#X text 186 63 http://www.akustische-kunst.de/puredata/;
-#X text 121 269 position in file (seconds);
-#X text 186 38 written by Olaf Matthes <olaf.matthes@gmx.de>;
-#X text 17 16 oggread~ version 0.2 - Ogg Vorbis file player;
-#X msg 110 152 0;
-#X msg 72 105 1;
-#X msg 148 210 seek 60;
-#X text 206 210 set new playing position (in seconds);
-#X connect 2 0 0 0;
-#X connect 2 1 0 1;
-#X connect 2 2 1 0;
-#X connect 3 0 2 0;
-#X connect 4 0 2 0;
-#X connect 5 0 2 0;
-#X connect 6 0 2 0;
-#X connect 12 0 10 0;
-#X connect 18 0 2 0;
-#X connect 19 0 2 0;
-#X connect 20 0 2 0;
+#N canvas 280 107 572 464 12; +#X obj 27 411 dac~; +#X floatatom 75 412 8 0 0 0 - - -; +#X obj 36 343 oggread~; +#X msg 71 170 start; +#X msg 110 226 stop; +#X msg 118 287 resume; +#X msg 36 45 open myfile.ogg; +#X text 119 168 play file from beginning; +#X text 152 226 stop (pause) playing; +#X text 179 288 resume playing at current position; +#X msg 396 394 \; pd dsp 1; +#X msg 472 394 \; pd dsp 0; +#X obj 396 368 loadbang; +#X text 185 52 get latest version at; +#X text 152 411 position in file (seconds); +#X text 186 38 written by Olaf Matthes <olaf.matthes@gmx.de>; +#X text 17 16 oggread~ version 0.2 - Ogg Vorbis file player; +#X msg 126 250 0; +#X msg 95 195 1; +#X text 230 318 set new playing position (in seconds); +#X obj 95 375 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X text 121 375 bang at end of file; +#X text 186 67 http://www.akustische-kunst.org/puredata/; +#X obj 55 102 openpanel; +#X msg 55 74 bang; +#X msg 55 129 open \$1; +#X msg 149 317 seek 60; +#X connect 2 0 0 0; +#X connect 2 1 0 1; +#X connect 2 2 1 0; +#X connect 2 3 20 0; +#X connect 3 0 2 0; +#X connect 4 0 2 0; +#X connect 5 0 2 0; +#X connect 6 0 2 0; +#X connect 12 0 10 0; +#X connect 17 0 2 0; +#X connect 18 0 2 0; +#X connect 23 0 25 0; +#X connect 24 0 23 0; +#X connect 25 0 2 0; +#X connect 26 0 2 0; diff --git a/oggread~/oggread~.c b/oggread~/oggread~.c index 8cc879a..a2a3bd6 100644 --- a/oggread~/oggread~.c +++ b/oggread~/oggread~.c @@ -1,413 +1,415 @@ -/* ------------------------- oggread~ ------------------------------------------ */
-/* */
-/* Tilde object to read and play back Ogg Vorbis files. */
-/* Written by Olaf Matthes (olaf.matthes@gmx.de) */
-/* Get source at http://www.akustische-kunst.de/puredata/ */
-/* */
-/* This library is free software; you can redistribute it and/or */
-/* modify it under the terms of the GNU Lesser General Public */
-/* License as published by the Free Software Foundation; either */
-/* version 2 of the License, or (at your option) any later version. */
-/* */
-/* This library is distributed in the hope that it will be useful, */
-/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
-/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
-/* Lesser General Public License for more details. */
-/* */
-/* You should have received a copy of the GNU Lesser General Public */
-/* License along with this library; if not, write to the */
-/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */
-/* Boston, MA 02111-1307, USA. */
-/* */
-/* Based on PureData by Miller Puckette and others. */
-/* Uses the Ogg Vorbis decoding library which can be found at */
-/* http://www.vorbis.com/ */
-/* */
-/* ---------------------------------------------------------------------------- */
-
-
-#include "m_imp.h"
-#include "g_canvas.h"
-#include "codec.h"
-#include "vorbis/vorbisfile.h"
-
-#include <sys/types.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#ifdef unix
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <sys/time.h>
-#include <unistd.h>
-#define SOCKET_ERROR -1
-#else
-#include <io.h>
-#include <stdlib.h>
-#endif
-
-#ifdef NT
-#pragma warning( disable : 4244 )
-#pragma warning( disable : 4305 )
-#endif
-
-#define READ 4096 /* amount of data we pass on to decoder */
-#define MIN_AUDIO_INPUT READ*8 /* this is completely guessed! we just fill half the buffer */
-#define OUTPUT_BUFFER_SIZE 65536 /* audio output buffer: 64k */
-
-static char *oggread_version = "oggread~: ogg/vorbis file reader version 0.2b, written by Olaf Matthes";
-
-/* ------------------------ oggread~ ----------------------------- */
-
-static t_class *oggread_class;
-
-typedef struct _oggread
-{
- t_object x_obj;
- t_clock *x_clock;
- /* ogg/vorbis related stuff */
- OggVorbis_File x_ov;
- ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */
- ogg_sync_state x_oy; /* sync and verify incoming physical bitstream */
- ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */
- ogg_packet x_op; /* one raw packet of data for decode */
- vorbis_info *x_vi; /* struct that stores all the static vorbis bitstream settings */
- vorbis_comment x_vc; /* struct that stores all the user comments */
- vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */
- vorbis_block x_vb; /* local working space for packet->PCM decode */
- t_int x_eos; /* end of stream */
- char *x_buffer;/* buffer used to pass on data to ogg/vorbis */
-
- t_float x_position; /* current playing position */
- t_outlet *x_out_position; /* output to send them to */
-
-
- t_outlet *x_connection;
- t_int x_fd; /* the file handle */
- FILE *x_file;
- int x_current_section;
- t_int x_blocksize; /* size of a dsp block */
- t_int x_decoded; /* number of samples we got from decoder on last call */
-
- t_float *x_outbuffer; /* buffer to store audio decoded data */
- t_int x_outwriteposition;
- t_int x_outreadposition;
- t_int x_outunread;
- t_int x_outbuffersize;
-
- t_int x_samplerate; /* pd's samplerate, might differ from stream */
- t_int x_stream; /* indicates if a stream gets output */
-} t_oggread;
-
- /* output playing position */
-static void oggread_tick(t_oggread *x)
-{
- outlet_float(x->x_out_position, x->x_position);
- clock_delay(x->x_clock, 250);
-}
-
-static int oggread_decode_input(t_oggread *x)
-{
- long ret; /* bytes per channel returned by decoder */
- int i;
- float **pcm;
-
- x->x_vi = ov_info(&x->x_ov, x->x_current_section);
-
- while(!x->x_eos)
- {
- ret = ov_read_float(&x->x_ov, &pcm, READ, &x->x_current_section);
- if (ret == 0)
- {
- /* EOF */
- x->x_eos = 1;
- x->x_stream = 0;
- clock_unset(x->x_clock);
- post("oggread~: end of file detected, stopping");
- }
- else if (ret < 0)
- {
- /* error in the stream. Not a problem, just reporting it in
- case we (the app) cares. In this case, we don't. */
- }
- else
- {
- /* we don't bother dealing with sample rate changes, etc, but
- you'll have to */
- long j;
- for(j = 0; j < ret; j++)
- {
- for(i = 0; i < x->x_vi->channels; i++)
- {
- x->x_outbuffer[x->x_outwriteposition] = pcm[i][j];
- x->x_outwriteposition = (x->x_outwriteposition + 1)%x->x_outbuffersize;
- }
- }
- x->x_outunread += (t_int)ret * x->x_vi->channels;
-
- }
- break;
- }
- x->x_decoded = (t_int)ret * x->x_vi->channels; /* num. of samples we got from decoder */
-
- x->x_position = (t_float)ov_time_tell(&x->x_ov);
-
- /* exit decoding 'loop' here, we'll get called again by perform() */
- return 1;
-}
-
-
-static t_int *oggread_perform(t_int *w)
-{
- t_oggread *x = (t_oggread*) (w[1]);
- t_float *out1 = (t_float *)(w[2]);
- t_float *out2 = (t_float *)(w[3]);
- int n = (int)(w[4]);
- int ret;
- int i = 0;
-
- x->x_blocksize = n;
-
- while( n-- )
- { /* check that the stream provides enough data */
- if((x->x_stream == 1) && (x->x_outunread > (x->x_blocksize * x->x_vi->channels)))
- {
- if(x->x_vi->channels != 1) /* play stereo */
- {
- *out1++=*(x->x_outbuffer+x->x_outreadposition);
- x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize;
- *out2++=*(x->x_outbuffer+x->x_outreadposition);
- x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize;
- x->x_outunread-=2;
- }
- else /* play mono on both sides */
- {
- *out1++=*(x->x_outbuffer+x->x_outreadposition);
- *out2++=*(x->x_outbuffer+x->x_outreadposition);
- x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize;
- x->x_outunread--;
- }
- }
- else /* silence in case of buffer underrun */
- {
- *out1++=0.0;
- *out2++=0.0;
- }
- }
-
- /* decode data whenever we used up some samples from outbuffer */
- if((x->x_fd > 0) && (x->x_stream) /* only go when file is open and ready */
- && (x->x_outunread < (MIN_AUDIO_INPUT - x->x_decoded))) /* we used up data from last decode */
- {
- // post("oggread~: decoding...");
- if(oggread_decode_input(x) != 1)
- {
- post("oggread~: decoder error");
- }
- // else post("oggread~: decoder returned %d samples", x->x_decoded);
- }
- return (w+5);
-}
-
-static void oggread_dsp(t_oggread *x, t_signal **sp)
-{
- dsp_add(oggread_perform, 4, x, sp[1]->s_vec, sp[2]->s_vec, sp[1]->s_n);
-}
-
-
- /* start playing */
-static void oggread_start(t_oggread *x)
-{
- if(x->x_fd > 0)
- {
- if(ov_time_seek(&x->x_ov, 0) < 0)
- {
- post("oggread~: could not rewind file to beginning");
- }
- post("oggread~: START");
- x->x_eos = 0;
- x->x_outreadposition = 0;
- x->x_outwriteposition = 0;
- x->x_outunread = 0;
- x->x_position = 0;
- clock_delay(x->x_clock, 0);
- x->x_stream = 1;
- }
- else post("oggread~: no file open (ignored)");
-}
-
- /* resume file reading */
-static void oggread_resume(t_oggread *x)
-{
- if(x->x_fd > 0)
- {
- x->x_stream = 1;
- clock_delay(x->x_clock, 0);
- post("oggread~: RESUME");
- }
- else post("oggread~: encoder not initialised");
-}
-
- /* seek in file */
-static void oggread_seek(t_oggread *x, t_floatarg f)
-{
- if(x->x_fd > 0)
- if(ov_time_seek(&x->x_ov, f) < 0)
- {
- post("oggread~: could not set playing position to %g seconds", f);
- }
- else post("oggread~: playing position set to %g seconds", f);
-}
-
- /* stop playing */
-static void oggread_stop(t_oggread *x)
-{
- if(x->x_stream)post("oggread~: STOP");
- x->x_stream = 0;
- clock_unset(x->x_clock);
-}
-
-static void oggread_float(t_oggread *x, t_floatarg f)
-{
- if(f == 0) oggread_stop(x);
- else oggread_start(x);
-}
-
- /* open ogg/vorbis file */
-static void oggread_open(t_oggread *x, t_symbol *filename)
-{
- int i;
-
- x->x_stream = 0;
- /* first close previous file */
- if(x->x_fd > 0)
- {
- ov_clear(&x->x_ov);
- post("oggread~: previous file closed");
- }
- /* open file for reading */
-#ifdef unix
- if((x->x_file = fopen(filename->s_name, "r")) < 0)
-#else
- if((x->x_file = fopen(filename->s_name, "rb")) < 0)
-#endif
- {
- post("oggread~: could not open file \"%s\"", filename->s_name);
- x->x_eos = 1;
- x->x_fd = -1;
- }
- else
- {
- x->x_stream = 0;
- x->x_eos = 0;
- x->x_fd = 1;
- x->x_outreadposition = 0;
- x->x_outwriteposition = 0;
- x->x_outunread = 0;
- post("oggread~: file \"%s\" opened", filename->s_name);
- outlet_float( x->x_out_position, 0);
-
- /* try to open as ogg vorbis file */
- if(ov_open(x->x_file, &x->x_ov, NULL, -1) < 0)
- { /* an error occured (no ogg vorbis file ?) */
- post("oggread~: error: could not open \"%s\" as an OggVorbis file", filename->s_name);
- ov_clear(&x->x_ov);
- post("oggread~: file closed due to error");
- }
-
- /* print details about each logical bitstream in the input */
- if(ov_seekable(&x->x_ov))
- {
- post("oggread~: input bitstream contained %ld logical bitstream section(s)", ov_streams(&x->x_ov));
- post("oggread~: total bitstream playing time: %ld seconds", (long)ov_time_total(&x->x_ov,-1));
- post("oggread~: encoded by: %s\n",ov_comment(&x->x_ov,-1)->vendor);
- }
- else
- {
- post("oggread~: file \"%s\" was not seekable"
- "oggread~: first logical bitstream information:", filename->s_name);
- }
-
- for(i = 0; i < ov_streams(&x->x_ov); i++)
- {
- x->x_vi = ov_info(&x->x_ov,i);
- post("\tlogical bitstream section %d information:",i+1);
- post("\t\t%ldHz %d channels bitrate %ldkbps serial number=%ld",
- x->x_vi->rate,x->x_vi->channels,ov_bitrate(&x->x_ov,i)/1000, ov_serialnumber(&x->x_ov,i));
- post("\t\theader length: %ld bytes",(long)
- (x->x_ov.dataoffsets[i] - x->x_ov.offsets[i]));
- post("\t\tcompressed length: %ld bytes",(long)(ov_raw_total(&x->x_ov,i)));
- post("\t\tplay time: %ld seconds\n",(long)ov_time_total(&x->x_ov,i));
- }
-
- }
-}
-
-
-static void oggread_free(t_oggread *x)
-{
- if (x->x_fd > 0) {
- post( "oggread~: closing file" );
- ov_clear(&x->x_ov);
- x->x_fd = -1;
- }
- freebytes(x->x_outbuffer, OUTPUT_BUFFER_SIZE*sizeof(t_float));
- clock_free(x->x_clock);
-}
-
-static void *oggread_new(t_floatarg fdographics)
-{
- t_oggread *x = NULL;
-
- x = (t_oggread *)pd_new(oggread_class);
- outlet_new(&x->x_obj, gensym("signal"));
- outlet_new(&x->x_obj, gensym("signal"));
- x->x_out_position = outlet_new(&x->x_obj, gensym("float"));
- x->x_clock = clock_new(x, (t_method)oggread_tick);
-
- x->x_fd = -1;
- x->x_eos = 1;
- x->x_stream = 0;
- x->x_position = 0;
- x->x_samplerate = sys_getsr();
-
- x->x_outbuffersize = OUTPUT_BUFFER_SIZE;
- x->x_outbuffer = (t_float*) getbytes(OUTPUT_BUFFER_SIZE*sizeof(t_float));
-
- if(!x->x_outbuffer)
- {
- post( "oggread~: could not allocate buffer" );
- return NULL;
- }
- memset(x->x_outbuffer, 0x0, OUTPUT_BUFFER_SIZE);
-
- x->x_outreadposition = 0;
- x->x_outwriteposition = 0;
- x->x_outunread = 0;
- x->x_decoded = 0;
-
- post(oggread_version);
-
- return (x);
-}
-
-
-void oggread_tilde_setup(void)
-{
- oggread_class = class_new(gensym("oggread~"),
- (t_newmethod) oggread_new, (t_method) oggread_free,
- sizeof(t_oggread), 0, A_DEFFLOAT, A_NULL);
- class_addfloat(oggread_class, (t_method)oggread_float);
- class_addmethod(oggread_class, nullfn, gensym("signal"), 0);
- class_addmethod(oggread_class, (t_method)oggread_dsp, gensym("dsp"), 0);
- class_addmethod(oggread_class, (t_method)oggread_open, gensym("open"), A_SYMBOL, 0);
- class_addmethod(oggread_class, (t_method)oggread_start, gensym("start"), 0);
- class_addmethod(oggread_class, (t_method)oggread_resume, gensym("resume"), 0);
- class_addmethod(oggread_class, (t_method)oggread_seek, gensym("seek"), A_DEFFLOAT, 0);
- class_addmethod(oggread_class, (t_method)oggread_stop, gensym("stop"), 0);
- class_sethelpsymbol(oggread_class, gensym("help-oggread~.pd"));
-}
+/* ------------------------- oggread~ ------------------------------------------ */ +/* */ +/* Tilde object to read and play back Ogg Vorbis files. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.de/puredata/ */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Uses the Ogg Vorbis decoding library which can be found at */ +/* http://www.vorbis.com/ */ +/* */ +/* ---------------------------------------------------------------------------- */ + + +#include "m_imp.h" +#include "g_canvas.h" +#include "codec.h" +#include "vorbis/vorbisfile.h" + +#include <sys/types.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#ifdef UNIX +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/time.h> +#include <unistd.h> +#define SOCKET_ERROR -1 +#else +#include <io.h> +#include <stdlib.h> +#endif + +#ifdef NT +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#define READ 4096 /* amount of data we pass on to decoder */ +#define MIN_AUDIO_INPUT READ*8 /* this is completely guessed! we just fill half the buffer */ +#define OUTPUT_BUFFER_SIZE 65536 /* audio output buffer: 64k */ + +static char *oggread_version = "oggread~: ogg/vorbis file reader version 0.2c, written by Olaf Matthes"; + +/* ------------------------ oggread~ ----------------------------- */ + +static t_class *oggread_class; + +typedef struct _oggread +{ + t_object x_obj; + t_clock *x_clock; + /* ogg/vorbis related stuff */ + OggVorbis_File x_ov; + ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ + ogg_sync_state x_oy; /* sync and verify incoming physical bitstream */ + ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet x_op; /* one raw packet of data for decode */ + vorbis_info *x_vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment x_vc; /* struct that stores all the user comments */ + vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */ + vorbis_block x_vb; /* local working space for packet->PCM decode */ + t_int x_eos; /* end of stream */ + char *x_buffer;/* buffer used to pass on data to ogg/vorbis */ + + t_float x_position; /* current playing position */ + t_outlet *x_out_position; /* output to send them to */ + t_outlet *x_out_end; /* signal end of file */ + + t_outlet *x_connection; + t_int x_fd; /* the file handle */ + FILE *x_file; + int x_current_section; + t_int x_blocksize; /* size of a dsp block */ + t_int x_decoded; /* number of samples we got from decoder on last call */ + + t_float *x_outbuffer; /* buffer to store audio decoded data */ + t_int x_outwriteposition; + t_int x_outreadposition; + t_int x_outunread; + t_int x_outbuffersize; + + t_int x_samplerate; /* pd's samplerate, might differ from stream */ + t_int x_stream; /* indicates if a stream gets output */ +} t_oggread; + + /* output playing position */ +static void oggread_tick(t_oggread *x) +{ + outlet_float(x->x_out_position, x->x_position); + clock_delay(x->x_clock, 250); +} + +static int oggread_decode_input(t_oggread *x) +{ + long ret; /* bytes per channel returned by decoder */ + int i; + float **pcm; + + x->x_vi = ov_info(&x->x_ov, x->x_current_section); + + while(!x->x_eos) + { + ret = ov_read_float(&x->x_ov, &pcm, READ, &x->x_current_section); + if (ret == 0) + { + /* EOF */ + x->x_eos = 1; + x->x_stream = 0; + clock_unset(x->x_clock); + // post("oggread~: end of file detected, stopping"); + outlet_bang(x->x_out_end); + } + else if (ret < 0) + { + /* error in the stream. Not a problem, just reporting it in + case we (the app) cares. In this case, we don't. */ + } + else + { + /* we don't bother dealing with sample rate changes, etc, but + you'll have to */ + long j; + for(j = 0; j < ret; j++) + { + for(i = 0; i < x->x_vi->channels; i++) + { + x->x_outbuffer[x->x_outwriteposition] = pcm[i][j]; + x->x_outwriteposition = (x->x_outwriteposition + 1)%x->x_outbuffersize; + } + } + x->x_outunread += (t_int)ret * x->x_vi->channels; + + } + break; + } + x->x_decoded = (t_int)ret * x->x_vi->channels; /* num. of samples we got from decoder */ + + x->x_position = (t_float)ov_time_tell(&x->x_ov); + + /* exit decoding 'loop' here, we'll get called again by perform() */ + return 1; +} + + +static t_int *oggread_perform(t_int *w) +{ + t_oggread *x = (t_oggread*) (w[1]); + t_float *out1 = (t_float *)(w[2]); + t_float *out2 = (t_float *)(w[3]); + int n = (int)(w[4]); + int ret; + int i = 0; + + x->x_blocksize = n; + + while( n-- ) + { /* check that the stream provides enough data */ + if((x->x_stream == 1) && (x->x_outunread > (x->x_blocksize * x->x_vi->channels))) + { + if(x->x_vi->channels != 1) /* play stereo */ + { + *out1++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + *out2++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + x->x_outunread-=2; + } + else /* play mono on both sides */ + { + *out1++=*(x->x_outbuffer+x->x_outreadposition); + *out2++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + x->x_outunread--; + } + } + else /* silence in case of buffer underrun */ + { + *out1++=0.0; + *out2++=0.0; + } + } + + /* decode data whenever we used up some samples from outbuffer */ + if((x->x_fd > 0) && (x->x_stream) /* only go when file is open and ready */ + && (x->x_outunread < (MIN_AUDIO_INPUT - x->x_decoded))) /* we used up data from last decode */ + { + // post("oggread~: decoding..."); + if(oggread_decode_input(x) != 1) + { + post("oggread~: decoder error"); + } + // else post("oggread~: decoder returned %d samples", x->x_decoded); + } + return (w+5); +} + +static void oggread_dsp(t_oggread *x, t_signal **sp) +{ + dsp_add(oggread_perform, 4, x, sp[1]->s_vec, sp[2]->s_vec, sp[1]->s_n); +} + + + /* start playing */ +static void oggread_start(t_oggread *x) +{ + if(x->x_fd > 0) + { + if(ov_time_seek(&x->x_ov, 0) < 0) + { + post("oggread~: could not rewind file to beginning"); + } + post("oggread~: START"); + x->x_eos = 0; + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + x->x_position = 0; + clock_delay(x->x_clock, 0); + x->x_stream = 1; + } + else post("oggread~: no file open (ignored)"); +} + + /* resume file reading */ +static void oggread_resume(t_oggread *x) +{ + if(x->x_fd > 0) + { + x->x_stream = 1; + clock_delay(x->x_clock, 0); + post("oggread~: RESUME"); + } + else post("oggread~: encoder not initialised"); +} + + /* seek in file */ +static void oggread_seek(t_oggread *x, t_floatarg f) +{ + if(x->x_fd > 0) + if(ov_time_seek(&x->x_ov, f) < 0) + { + post("oggread~: could not set playing position to %g seconds", f); + } + else post("oggread~: playing position set to %g seconds", f); +} + + /* stop playing */ +static void oggread_stop(t_oggread *x) +{ + if(x->x_stream)post("oggread~: STOP"); + x->x_stream = 0; + clock_unset(x->x_clock); +} + +static void oggread_float(t_oggread *x, t_floatarg f) +{ + if(f == 0) oggread_stop(x); + else oggread_start(x); +} + + /* open ogg/vorbis file */ +static void oggread_open(t_oggread *x, t_symbol *filename) +{ + int i; + + x->x_stream = 0; + /* first close previous file */ + if(x->x_fd > 0) + { + ov_clear(&x->x_ov); + post("oggread~: previous file closed"); + } + /* open file for reading */ +#ifdef UNIX + if((x->x_file = fopen(filename->s_name, "r")) < 0) +#else + if((x->x_file = fopen(filename->s_name, "rb")) < 0) +#endif + { + post("oggread~: could not open file \"%s\"", filename->s_name); + x->x_eos = 1; + x->x_fd = -1; + } + else + { + x->x_stream = 0; + x->x_eos = 0; + x->x_fd = 1; + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + post("oggread~: file \"%s\" opened", filename->s_name); + outlet_float( x->x_out_position, 0); + + /* try to open as ogg vorbis file */ + if(ov_open(x->x_file, &x->x_ov, NULL, -1) < 0) + { /* an error occured (no ogg vorbis file ?) */ + post("oggread~: error: could not open \"%s\" as an OggVorbis file", filename->s_name); + ov_clear(&x->x_ov); + post("oggread~: file closed due to error"); + } + + /* print details about each logical bitstream in the input */ + if(ov_seekable(&x->x_ov)) + { + post("oggread~: input bitstream contained %ld logical bitstream section(s)", ov_streams(&x->x_ov)); + post("oggread~: total bitstream playing time: %ld seconds", (long)ov_time_total(&x->x_ov,-1)); + post("oggread~: encoded by: %s\n",ov_comment(&x->x_ov,-1)->vendor); + } + else + { + post("oggread~: file \"%s\" was not seekable" + "oggread~: first logical bitstream information:", filename->s_name); + } + + for(i = 0; i < ov_streams(&x->x_ov); i++) + { + x->x_vi = ov_info(&x->x_ov,i); + post("\tlogical bitstream section %d information:",i+1); + post("\t\t%ldHz %d channels bitrate %ldkbps serial number=%ld", + x->x_vi->rate,x->x_vi->channels,ov_bitrate(&x->x_ov,i)/1000, ov_serialnumber(&x->x_ov,i)); + post("\t\theader length: %ld bytes",(long) + (x->x_ov.dataoffsets[i] - x->x_ov.offsets[i])); + post("\t\tcompressed length: %ld bytes",(long)(ov_raw_total(&x->x_ov,i))); + post("\t\tplay time: %ld seconds\n",(long)ov_time_total(&x->x_ov,i)); + } + + } +} + + +static void oggread_free(t_oggread *x) +{ + if (x->x_fd > 0) { + post( "oggread~: closing file" ); + ov_clear(&x->x_ov); + x->x_fd = -1; + } + freebytes(x->x_outbuffer, OUTPUT_BUFFER_SIZE*sizeof(t_float)); + clock_free(x->x_clock); +} + +static void *oggread_new(t_floatarg fdographics) +{ + t_oggread *x = NULL; + + x = (t_oggread *)pd_new(oggread_class); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_out_position = outlet_new(&x->x_obj, gensym("float")); + x->x_out_end = outlet_new(&x->x_obj, gensym("bang")); + x->x_clock = clock_new(x, (t_method)oggread_tick); + + x->x_fd = -1; + x->x_eos = 1; + x->x_stream = 0; + x->x_position = 0; + x->x_samplerate = sys_getsr(); + + x->x_outbuffersize = OUTPUT_BUFFER_SIZE; + x->x_outbuffer = (t_float*) getbytes(OUTPUT_BUFFER_SIZE*sizeof(t_float)); + + if(!x->x_outbuffer) + { + post( "oggread~: could not allocate buffer" ); + return NULL; + } + memset(x->x_outbuffer, 0x0, OUTPUT_BUFFER_SIZE); + + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + x->x_decoded = 0; + + post(oggread_version); + + return (x); +} + + +void oggread_tilde_setup(void) +{ + oggread_class = class_new(gensym("oggread~"), + (t_newmethod) oggread_new, (t_method) oggread_free, + sizeof(t_oggread), 0, A_DEFFLOAT, A_NULL); + class_addfloat(oggread_class, (t_method)oggread_float); + class_addmethod(oggread_class, nullfn, gensym("signal"), 0); + class_addmethod(oggread_class, (t_method)oggread_dsp, gensym("dsp"), 0); + class_addmethod(oggread_class, (t_method)oggread_open, gensym("open"), A_SYMBOL, 0); + class_addmethod(oggread_class, (t_method)oggread_start, gensym("start"), 0); + class_addmethod(oggread_class, (t_method)oggread_resume, gensym("resume"), 0); + class_addmethod(oggread_class, (t_method)oggread_seek, gensym("seek"), A_DEFFLOAT, 0); + class_addmethod(oggread_class, (t_method)oggread_stop, gensym("stop"), 0); + class_sethelpsymbol(oggread_class, gensym("help-oggread~.pd")); +} diff --git a/oggread~/readme b/oggread~/readme index 0f61fc8..7f8058e 100644 --- a/oggread~/readme +++ b/oggread~/readme @@ -1,75 +1,75 @@ -oggread~ version 0.2a
-copyright (c) 2002 by Olaf Matthes
-
-oggread~ is an ogg/vorbis file player external for pd (by Miller
-Puckette).
-
-
-To run oggread~ place the file oggread~.dll for win or oggread~.pd_linux
-in the directory of our patch or start pd with '-lib oggread~' flag.
-
-To compile oggread~ 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.
-
-*****************************************************************************
-
-oggread~ 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.
-
-*****************************************************************************
-
-Listening to it:
-
-To listen to ogg/vorbis encoded audio files many player need an extra plug-in.
-Have a look at http://www.vorbis.com/ to find the appropiate plug-in for your
-player.
-
-
-
-
-
-Latest version can be found at:
-http://www.akustische-kunst.de/puredata/
-
+oggread~ version 0.2c +copyright (c) 2002 by Olaf Matthes + +oggread~ is an ogg/vorbis file player external for pd (by Miller +Puckette). + + +To run oggread~ place the file oggread~.dll for win or oggread~.pd_linux +in the directory of our patch or start pd with '-lib oggread~' flag. + +To compile oggread~ 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. + +***************************************************************************** + +oggread~ 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. + +***************************************************************************** + +Listening to it: + +To listen to ogg/vorbis encoded audio files many player need an extra plug-in. +Have a look at http://www.vorbis.com/ to find the appropiate plug-in for your +player. + + + + + +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 diff --git a/oggwrite~/codec.h b/oggwrite~/codec.h index 9365ea3..d986a9b 100644 --- a/oggwrite~/codec.h +++ b/oggwrite~/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/oggwrite~/help-oggwrite~.pd b/oggwrite~/help-oggwrite~.pd index 86bbd91..8e9ca4b 100644 --- a/oggwrite~/help-oggwrite~.pd +++ b/oggwrite~/help-oggwrite~.pd @@ -1,59 +1,59 @@ -#N canvas 286 47 631 513 10;
-#X obj 40 415 oggwrite~;
-#X obj 40 64 osc~ 440;
-#X floatatom 40 33 5 0 0;
-#X msg 120 52 open myfile.ogg;
-#X msg 175 125 start;
-#X msg 185 148 stop;
-#X msg 155 78 append;
-#X msg 166 99 truncate;
-#X floatatom 89 441 5 0 0;
-#X floatatom 40 470 5 0 0;
-#X msg 204 259 print;
-#X text 189 180 vorbis <samplerate> <channles> <max.br> <nom.br> <min.br>
-;
-#X text 373 251 channels: 1 or 2 (default);
-#X text 204 216 vbr <samplerate> <channels> <quality>;
-#X msg 203 232 vbr 44100 2 0.4;
-#X text 324 238 quality settings: 0 - 1 (low - hi);
-#X text 325 267 resampling currently not supported!;
-#X text 202 290 comment <tag> <content>;
-#X text 204 329 possible tags: TITLE \, ARTIST \, PERFORMER \, DESCRIPTION
-\,;
-#X text 293 342 GENRE \, LOCATION \, COPYRIGHT \, CONTACT \, DATE;
-#X msg 202 307 ARTIST your=name;
-#X msg 189 197 vorbis 44100 2 144 128 96;
-#X text 136 441 ogg pages written to file;
-#X msg 481 455 \; pd dsp 1;
-#X msg 547 455 \; pd dsp 0;
-#X obj 481 429 loadbang;
-#X text 354 9 written by Olaf Matthes (olaf.matthes@gmx.de);
-#X text 353 21 get latest version at;
-#X text 354 34 http://www.akustische-kunst.de/puredata/;
-#X text 209 76 append data at end of file;
-#X text 226 97 overwrite previously recorded data;
-#X text 221 145 stop recording;
-#X text 225 50 open a file first!;
-#X text 240 407 might result in audible dropouts!;
-#X text 202 395 note: changing settings while recording;
-#X text 85 470 file state (1 = open \; 0 = closed);
-#X text 5 9 oggwrite~ version 0.1 - write Ogg Vorbis stream to file
-;
-#X msg 204 361 COPYRIGHT (c)=2002=Olaf=Matthes;
-#X text 220 123 start recording;
-#X connect 0 0 9 0;
-#X connect 0 1 8 0;
-#X connect 1 0 0 0;
-#X connect 1 0 0 1;
-#X connect 2 0 1 0;
-#X connect 3 0 0 0;
-#X connect 4 0 0 0;
-#X connect 5 0 0 0;
-#X connect 6 0 0 0;
-#X connect 7 0 0 0;
-#X connect 10 0 0 0;
-#X connect 14 0 0 0;
-#X connect 20 0 0 0;
-#X connect 21 0 0 0;
-#X connect 25 0 23 0;
-#X connect 37 0 0 0;
+#N canvas 286 47 631 513 10; +#X obj 40 415 oggwrite~; +#X obj 40 64 osc~ 440; +#X floatatom 40 33 5 0 0; +#X msg 120 52 open myfile.ogg; +#X msg 175 125 start; +#X msg 185 148 stop; +#X msg 155 78 append; +#X msg 166 99 truncate; +#X floatatom 89 441 5 0 0; +#X floatatom 40 470 5 0 0; +#X msg 204 259 print; +#X text 189 180 vorbis <samplerate> <channles> <max.br> <nom.br> <min.br> +; +#X text 373 251 channels: 1 or 2 (default); +#X text 204 216 vbr <samplerate> <channels> <quality>; +#X msg 203 232 vbr 44100 2 0.4; +#X text 324 238 quality settings: 0 - 1 (low - hi); +#X text 325 267 resampling currently not supported!; +#X text 202 290 comment <tag> <content>; +#X text 204 329 possible tags: TITLE \, ARTIST \, PERFORMER \, DESCRIPTION +\,; +#X text 293 342 GENRE \, LOCATION \, COPYRIGHT \, CONTACT \, DATE; +#X msg 202 307 ARTIST your=name; +#X msg 189 197 vorbis 44100 2 144 128 96; +#X text 136 441 ogg pages written to file; +#X msg 481 455 \; pd dsp 1; +#X msg 547 455 \; pd dsp 0; +#X obj 481 429 loadbang; +#X text 354 9 written by Olaf Matthes (olaf.matthes@gmx.de); +#X text 353 21 get latest version at; +#X text 354 34 http://www.akustische-kunst.de/puredata/; +#X text 209 76 append data at end of file; +#X text 226 97 overwrite previously recorded data; +#X text 221 145 stop recording; +#X text 225 50 open a file first!; +#X text 240 407 might result in audible dropouts!; +#X text 202 395 note: changing settings while recording; +#X text 85 470 file state (1 = open \; 0 = closed); +#X text 5 9 oggwrite~ version 0.1 - write Ogg Vorbis stream to file +; +#X msg 204 361 COPYRIGHT (c)=2002=Olaf=Matthes; +#X text 220 123 start recording; +#X connect 0 0 9 0; +#X connect 0 1 8 0; +#X connect 1 0 0 0; +#X connect 1 0 0 1; +#X connect 2 0 1 0; +#X connect 3 0 0 0; +#X connect 4 0 0 0; +#X connect 5 0 0 0; +#X connect 6 0 0 0; +#X connect 7 0 0 0; +#X connect 10 0 0 0; +#X connect 14 0 0 0; +#X connect 20 0 0 0; +#X connect 21 0 0 0; +#X connect 25 0 23 0; +#X connect 37 0 0 0; diff --git a/oggwrite~/oggwrite~.c b/oggwrite~/oggwrite~.c index f9619de..552973c 100644 --- a/oggwrite~/oggwrite~.c +++ b/oggwrite~/oggwrite~.c @@ -1,751 +1,751 @@ -/* -------------------------- oggwrite~ ---------------------------------------- */
-/* */
-/* Tilde object to send ogg/vorbis encoded stream to icecast2 server. */
-/* Written by Olaf Matthes (olaf.matthes@gmx.de). */
-/* Get source at http://www.akustische-kunst.de/puredata/ */
-/* */
-/* This library is free software; you can redistribute it and/or */
-/* modify it under the terms of the GNU Lesser General Public */
-/* License as published by the Free Software Foundation; either */
-/* version 2 of the License, or (at your option) any later version. */
-/* */
-/* This library is distributed in the hope that it will be useful, */
-/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
-/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
-/* Lesser General Public License for more details. */
-/* */
-/* You should have received a copy of the GNU Lesser General Public */
-/* License along with this library; if not, write to the */
-/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */
-/* Boston, MA 02111-1307, USA. */
-/* */
-/* Based on PureData by Miller Puckette and others. */
-/* Uses the Ogg/Vorbis encoding library which can be found at */
-/* http://www.vorbis.org */
-/* */
-/* ---------------------------------------------------------------------------- */
-
-
-
-#ifdef NT
-#pragma warning( disable : 4244 )
-#pragma warning( disable : 4305 )
-#endif
-
-#include <sys/types.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <malloc.h>
-#include <ctype.h>
-#include <time.h>
-#ifdef unix
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <sys/time.h>
-#define SOCKET_ERROR -1
-#else
-#include <io.h>
-#include <windows.h>
-#include <winsock.h>
-#include <windef.h>
-#endif
-
-#include "m_pd.h" /* standard pd stuff */
-#include "vorbisenc.h" /* vorbis encoder stuff */
-
-#define READ 1024 /* number of samples send to encoder at each call */
- /* has to be even multiple of 64 */
-
-static char *oggwrite_version = "oggwrite~: ogg/vorbis recorder version 0.1c, written by Olaf Matthes";
-
-static t_class *oggwrite_class;
-
-typedef struct _oggwrite
-{
- t_object x_obj;
- /* ogg/vorbis related stuff */
- ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */
- 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 */
- t_int x_vorbis; /* info about encoder status */
- t_float x_pages; /* number of pages that have been output to server */
- t_outlet *x_outpages; /* output to send them to */
-
- /* ringbuffer stuff */
- t_float *x_buffer; /* data to be buffered (ringbuffer)*/
- t_int x_bytesbuffered; /* number of unprocessed bytes in buffer */
-
- /* ogg/vorbis format stuff */
- t_int x_samplerate; /* samplerate of stream (default = getsr() ) */
- t_float x_quality; /* desired quality level from 0.0 to 1.0 (lo to hi) */
- t_int x_br_max; /* max. bitrate of ogg/vorbis stream */
- t_int x_br_nom; /* nom. bitrate of ogg/vorbis stream */
- t_int x_br_min; /* min. bitrate of ogg/vorbis stream */
- t_int x_channels; /* number of channels (1 or 2) */
- t_int x_vbr;
-
- /* IceCast server stuff */
- char* x_passwd; /* password for server */
- char* x_bcname; /* name of broadcast */
- char* x_bcurl; /* url of broadcast */
- char* x_bcgenre; /* genre of broadcast */
- char* x_bcdescription; /* description */
- char* x_bcartist; /* artist */
- char* x_bclocation;
- char* x_bccopyright;
- char* x_bcperformer;
- char* x_bccontact;
- char* x_bcdate; /* system date when broadcast started */
- char* x_mountpoint; /* mountpoint for IceCast server */
- t_int x_bcpublic; /* do(n't) publish broadcast on www.oggwrite.com */
-
- /* recording stuff */
- t_int x_fd; /* file descriptor of the mp3 output */
- t_int x_file_open_mode;/* file opening mode */
- t_int x_byteswritten; /* number of bytes written */
- t_int x_recflag; /* recording flag toggled by messages "start" and "stop" */
-
- t_float x_f; /* float needed for signal input */
-} t_oggwrite;
-
-/* Utility functions */
-
-static void sys_closesocket(int fd)
-{
-#ifdef unix
- close(fd);
-#endif
-#ifdef NT
- closesocket(fd);
-#endif
-}
- /* prototypes */
-static void oggwrite_vorbis_deinit(t_oggwrite *x);
-
-/* ----------------------------------------- oggwrite~ ---------------------------------- */
- /* write ogg/vorbis to file */
-static int oggwrite_write(t_oggwrite *x)
-{
- int err = -1; /* error return code */
-
- /* write out pages (if any) */
- while(!x->x_eos)
- {
- int result=ogg_stream_pageout(&(x->x_os),&(x->x_og));
- if(result==0)break;
-#ifndef unix
- err = _write(x->x_fd, x->x_og.header, x->x_og.header_len);
-#else
- err = write(x->x_fd, x->x_og.header, x->x_og.header_len);
-#endif
- if(err < 0)
- {
- error("oggwrite~: could not send ogg header to server (%d)", err);
- x->x_eos = 1; /* indicate (artificial) end of stream */
- return err;
- }
-#ifndef unix
- err = _write(x->x_fd, x->x_og.body, x->x_og.body_len);
-#else
- err = write(x->x_fd, x->x_og.body, x->x_og.body_len);
-#endif
- if(err < 0)
- {
- error("oggwrite~: could not send ogg body to server (%d)", err);
- x->x_eos = 1; /* indicate (artificial) end of stream */
- return err;
- }
- x->x_pages++; /* count number of pages */
- /* there might be more than one pages we have to send */
- if(ogg_page_eos(&(x->x_og)))x->x_eos=1;
- }
- outlet_float(x->x_outpages, x->x_pages); /* update info */
- return 1;
-}
-
-
- /* encode data to ogg/vorbis stream */
-static void oggwrite_encode(t_oggwrite *x)
-{
- unsigned short i, ch;
- int err = 1;
- int n;
- int channel = x->x_channels; /* make lokal copy of num. of channels */
-
- /* expose the buffer to submit data */
- float **inbuffer=vorbis_analysis_buffer(&(x->x_vd),READ);
-
- /* read from buffer */
- for(n = 0; n < READ / channel; n++) /* fill encode buffer */
- {
- for(ch = 0; ch < channel; ch++)
- {
- inbuffer[ch][n] = (float)x->x_buffer[n * channel + ch];
- }
- }
- /* tell the library how much we actually submitted */
- vorbis_analysis_wrote(&(x->x_vd),n);
-
- /* vorbis does some data preanalysis, then divvies up blocks for
- more involved (potentially parallel) processing. Get a single
- block for encoding now */
- while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1)
- {
- /* analysis, assume we want to use bitrate management */
- vorbis_analysis(&(x->x_vb),NULL);
- vorbis_bitrate_addblock(&(x->x_vb));
-
- while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op)))
- {
- /* weld the packet into the bitstream */
- ogg_stream_packetin(&(x->x_os),&(x->x_op));
- err = oggwrite_write(x); /* stream packet to server */
- }
- }
- /* check for errors */
- if(err < 0)
- {
- if(x->x_fd > 0)
- {
-#ifndef unix
- if(_close(x->x_fd) < 0)
-#else
- if(close(x->x_fd) < 0)
-#endif
- {
- post( "oggwrite~: file closed due to an error" );
- outlet_float(x->x_obj.ob_outlet, 0);
- }
- }
- }
-}
-
- /* buffer data as channel interleaved floats */
-static t_int *oggwrite_perform(t_int *w)
-{
- t_float *in1 = (t_float *)(w[1]); /* left audio inlet */
- t_float *in2 = (t_float *)(w[2]); /* right audio inlet */
- t_oggwrite *x = (t_oggwrite *)(w[3]);
- int n = (int)(w[4]); /* number of samples */
- int i;
- t_float in;
-
- /* copy the data into the buffer */
- if(x->x_channels != 1) /* everything but mono */
- {
- n *= 2; /* two channels go into one buffer */
-
- for(i = 0; i < n; i++)
- {
- if(i%2)
- {
- in = *(in2++); /* right inlet */
- }
- else
- {
- in = *(in1++); /* left inlet */
- }
- if (in > 1.0) { in = 1.0; }
- if (in < -1.0) { in = -1.0; }
- x->x_buffer[i + x->x_bytesbuffered] = in;
- }
- }
- else /* mono encoding -> just take left signal inlet 'in1' */
- {
- for(i = 0; i < n; i++)
- {
- in = *(in1++);
- if (in > 1.0) { in = 1.0; }
- if (in < -1.0) { in = -1.0; }
- x->x_buffer[i + x->x_bytesbuffered] = in;
- }
- }
- /* count, encode and send ogg/vorbis */
- if((x->x_fd >= 0)&&(x->x_recflag))
- {
- /* count buffered samples when connected */
- x->x_bytesbuffered += n;
-
- /* encode and send to server */
- if((x->x_bytesbuffered >= READ)&&(x->x_vorbis >= 0))
- {
- oggwrite_encode(x); /* encode data */
- x->x_bytesbuffered = 0; /* assume we got rid of all of them */
- }
- }
- return (w+5);
-}
-
-static void oggwrite_dsp(t_oggwrite *x, t_signal **sp)
-{
- dsp_add(oggwrite_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
-}
-
- /* initialize the vorbisenc library */
-static void oggwrite_vorbis_init(t_oggwrite *x)
-{
- int err = -1;
-
- x->x_vorbis = -1; /* indicate that encoder is not available right now */
-
- /* choose an encoding mode */
- vorbis_info_init(&(x->x_vi));
-
- if(x->x_samplerate != sys_getsr())post("oggwrite~: warning: resampling from %d to %.0f not supported", x->x_samplerate, sys_getsr());
- if(x->x_vbr == 1)
- { /* quality based setting */
- if(vorbis_encode_init_vbr(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_quality))
- {
- post("oggwrite~: ogg/vorbis mode initialisation failed: invalid parameters for quality");
- vorbis_info_clear(&(x->x_vi));
- return;
- }
- }
- else
- { /* bitrate based setting */
- if(vorbis_encode_init(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_br_max*1024, x->x_br_nom*1024, x->x_br_min*1024))
- {
- post("oggwrite~: ogg/vorbis mode initialisation failed: invalid parameters for quality");
- vorbis_info_clear(&(x->x_vi));
- return;
- }
- }
-
- /* add a comment */
- vorbis_comment_init(&(x->x_vc));
- vorbis_comment_add_tag(&(x->x_vc),"TITLE", x->x_bcname);
- vorbis_comment_add_tag(&(x->x_vc),"ARTIST", x->x_bcartist);
- vorbis_comment_add_tag(&(x->x_vc),"GENRE",x->x_bcgenre);
- vorbis_comment_add_tag(&(x->x_vc),"DESCRIPTION", x->x_bcdescription);
- vorbis_comment_add_tag(&(x->x_vc),"LOCATION",x->x_bclocation);
- vorbis_comment_add_tag(&(x->x_vc),"PERFORMER",x->x_bcperformer);
- vorbis_comment_add_tag(&(x->x_vc),"COPYRIGHT",x->x_bccopyright);
- vorbis_comment_add_tag(&(x->x_vc),"CONTACT",x->x_bccontact);
- vorbis_comment_add_tag(&(x->x_vc),"DATE",x->x_bcdate);
- vorbis_comment_add_tag(&(x->x_vc),"ENCODER","oggwrite~ v0.1b for pure-data");
-
- /* set up the analysis state and auxiliary encoding storage */
- vorbis_analysis_init(&(x->x_vd),&(x->x_vi));
- vorbis_block_init(&(x->x_vd),&(x->x_vb));
-
- /* set up our packet->stream encoder */
- /* pick a random serial number; that way we can more likely build
- chained streams just by concatenation */
- srand(time(NULL));
- ogg_stream_init(&(x->x_os),rand());
-
- /* Vorbis streams begin with three headers; the initial header (with
- most of the codec setup parameters) which is mandated by the Ogg
- bitstream spec. The second header holds any comment fields. The
- third header holds the bitstream codebook. We merely need to
- make the headers, then pass them to libvorbis one at a time;
- libvorbis handles the additional Ogg bitstream constraints */
-
- {
- ogg_packet header;
- ogg_packet header_comm;
- ogg_packet header_code;
-
- vorbis_analysis_headerout(&(x->x_vd),&(x->x_vc),&header,&header_comm,&header_code);
- ogg_stream_packetin(&(x->x_os),&header); /* automatically placed in its own page */
- ogg_stream_packetin(&(x->x_os),&header_comm);
- ogg_stream_packetin(&(x->x_os),&header_code);
-
- /* We don't have to write out here, but doing so makes streaming
- * much easier, so we do, flushing ALL pages. This ensures the actual
- * audio data will start on a new page
- *
- * IceCast2 server will take this as a first info about our stream
- */
- while(!x->x_eos)
- {
- int result=ogg_stream_flush(&(x->x_os),&(x->x_og));
- if(result==0)break;
-#ifndef unix
- err = _write(x->x_fd, x->x_og.header, x->x_og.header_len);
-#else
- err = write(x->x_fd, x->x_og.header, x->x_og.header_len);
-#endif
- if(err < 0)
- {
- error("oggwrite~: could not write ogg header to file (%d)", err);
- x->x_eos = 1; /* indicate end of stream */
- x->x_vorbis = -1; /* stop encoding instantly */
- if(x->x_fd > 0)
- {
-#ifndef unix
- if(_close(x->x_fd) < 0)
-#else
- if(close(x->x_fd) < 0)
-#endif
- {
- post( "oggwrite~: file closed due to an error" );
- outlet_float(x->x_obj.ob_outlet, 0);
- }
- }
- return;
- }
-#ifndef unix
- err = _write(x->x_fd, x->x_og.body, x->x_og.body_len);
-#else
- err = write(x->x_fd, x->x_og.body, x->x_og.body_len);
-#endif
- if(err < 0)
- {
- error("oggwrite~: could not write ogg body to file (%d)", err);
- x->x_eos = 1; /* indicate end of stream */
- x->x_vorbis = -1; /* stop encoding instantly */
- if(x->x_fd > 0)
- {
-#ifndef unix
- if(_close(x->x_fd) < 0)
-#else
- if(close(x->x_fd) < 0)
-#endif
- {
- post( "oggwrite~: file closed due to an error" );
- outlet_float(x->x_obj.ob_outlet, 0);
- }
- }
- return;
- }
- }
- }
- x->x_vorbis = 1; /* vorbis encoder initialised */
- post("oggwrite~: ogg/vorbis encoder (re)initialised");
-}
-
- /* initialize the vorbisenc library */
-static void oggwrite_vorbis_deinit(t_oggwrite *x)
-{
- x->x_vorbis = -1;
- vorbis_analysis_wrote(&(x->x_vd),0);
- /* clean up and exit. vorbis_info_clear() must be called last */
- ogg_stream_clear(&(x->x_os));
- vorbis_block_clear(&(x->x_vb));
- vorbis_dsp_clear(&(x->x_vd));
- vorbis_comment_clear(&(x->x_vc));
- vorbis_info_clear(&(x->x_vi));
- post("oggwrite~: ogg/vorbis encoder closed");
-}
-
- /* connect to oggwrite server */
-static void oggwrite_open(t_oggwrite *x, t_symbol *sfile)
-{
- time_t now; /* to get the time */
-
- /* closing previous file descriptor */
- if(x->x_fd > 0)
- {
-#ifndef unix
- if(_close(x->x_fd) < 0)
-#else
- if(close(x->x_fd) < 0)
-#endif
- {
- error( "oggwrite~: file closed" );
- outlet_float(x->x_obj.ob_outlet, 0);
- }
- }
-
- if(x->x_recflag)
- {
- x->x_recflag = 0;
- }
-
-#ifndef unix
- if((x->x_fd = _open( sfile->s_name, x->x_file_open_mode, _S_IREAD|_S_IWRITE)) < 0)
-#else
- if((x->x_fd = open( sfile->s_name, x->x_file_open_mode, S_IRWXU|S_IRWXG|S_IRWXO )) < 0)
-#endif
- {
- error( "oggwrite~: can not open \"%s\"", sfile->s_name);
- x->x_fd=-1;
- return;
- }
- x->x_byteswritten = 0;
- post( "oggwrite~: \"%s \" opened", sfile->s_name);
- outlet_float(x->x_obj.ob_outlet, 1);
-
- /* get the time for the DATE comment, then init encoder */
- now=time(NULL);
- x->x_bcdate = ctime(&now);
-
- x->x_eos = 0;
- oggwrite_vorbis_init(x);
-}
-
- /* setting file write mode to append */
-static void oggwrite_append(t_oggwrite *x)
-{
-#ifndef unix
- x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_APPEND|_O_BINARY;
-#else
- x->x_file_open_mode = O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK;
-#endif
- if(x->x_fd>=0)post("oggwrite~: mode set to append: open a new file to make changes take effect");
-}
-
- /* setting file write mode to truncate */
-static void oggwrite_truncate(t_oggwrite *x)
-{
-#ifndef unix
- x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_TRUNC|_O_BINARY;
-#else
- x->x_file_open_mode = O_CREAT|O_WRONLY|O_TRUNC|O_NONBLOCK;
-#endif
- if(x->x_fd>=0)post("oggwrite~: mode set to truncate: open a new file to make changes take effect");
-}
-
- /* start recording */
-static void oggwrite_start(t_oggwrite *x)
-{
- if ( x->x_fd < 0 ) {
- post("oggwrite~: no file selected");
- return;
- }
-
- if ( x->x_recflag == 1 ) {
- post("oggwrite~: already recording");
- return;
- }
- if(x->x_vorbis < 0)
- {
- oggwrite_vorbis_init(x);
- }
-
- x->x_recflag = 1;
- post("oggwrite~: start recording");
-}
-
- /* stop recording */
-static void oggwrite_stop(t_oggwrite *x)
-{
- int err = -1;
-
- /* first stop recording / buffering and so on, than do the rest */
- x->x_recflag = 0;
- post("oggwrite~: recording stoped");
- if(x->x_vorbis >= 0)
- {
- oggwrite_vorbis_deinit(x);
- }
-}
-
- /* set comment fields for header (reads in just anything) */
-static void oggwrite_comment(t_oggwrite *x, t_symbol *s, t_int argc, t_atom* argv)
-{
- int i = argc;
- char *comment = NULL;
- int len = strlen(atom_gensym(argv)->s_name);
-
- comment = atom_gensym(argv)->s_name;
-
- while (len--)
- {
- if(*(comment + len) == '=')*(comment + len) = ' ';
- }
-
- if(strstr(s->s_name, "ARTIST"))
- {
- x->x_bcartist = comment;
- post("oggwrite~: ARTIST = %s", x->x_bcartist);
- }
- else if(strstr(s->s_name, "GENRE"))
- {
- x->x_bcgenre = comment;
- post("oggwrite~: GENRE = %s", x->x_bcgenre);
- }
- else if(strstr(s->s_name, "TITLE"))
- {
- x->x_bcname = comment;
- post("oggwrite~: TITLE = %s", x->x_bcname);
- }
- else if(strstr(s->s_name, "PERFORMER"))
- {
- x->x_bcperformer = comment;
- post("oggwrite~: PERFORMER = %s", x->x_bcperformer);
- }
- else if(strstr(s->s_name, "LOCATION"))
- {
- x->x_bclocation = comment;
- post("oggwrite~: LOCATION = %s", x->x_bclocation);
- }
- else if(strstr(s->s_name, "COPYRIGHT"))
- {
- x->x_bccopyright = comment;
- post("oggwrite~: COPYRIGHT = %s", x->x_bccopyright);
- }
- else if(strstr(s->s_name, "CONTACT"))
- {
- x->x_bccontact = comment;
- post("oggwrite~: CONTACT = %s", x->x_bccontact);
- }
- else if(strstr(s->s_name, "DESCRIPTION"))
- {
- x->x_bcdescription = comment;
- post("oggwrite~: DESCRIPTION = %s", x->x_bcdescription);
- }
- else if(strstr(s->s_name, "DATE"))
- {
- x->x_bcdate = comment;
- post("oggwrite~: DATE=%s", x->x_bcdate);
- }
- else post("oggwrite~: no method for %s", s->s_name);
- if(x->x_vorbis >=0)
- {
- oggwrite_vorbis_deinit(x);
- oggwrite_vorbis_init(x);
- }
-}
-
- /* settings for variable bitrate encoding */
-static void oggwrite_vbr(t_oggwrite *x, t_floatarg fsr, t_floatarg fchannels,
- t_floatarg fquality)
-{
- x->x_vbr = 1;
- x->x_samplerate = (t_int)fsr;
- x->x_quality = fquality;
- x->x_channels = (t_int)fchannels;
- post("oggwrite~: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality);
- if(x->x_vorbis >=0)
- {
- oggwrite_vorbis_deinit(x);
- oggwrite_vorbis_init(x);
- }
-}
-
- /* settings for bitrate-based vbr encoding */
-static void oggwrite_vorbis(t_oggwrite *x, t_floatarg fsr, t_floatarg fchannels,
- t_floatarg fmax, t_floatarg fnom, t_floatarg fmin)
-{
- x->x_vbr = 0;
- x->x_samplerate = (t_int)fsr;
- x->x_channels = (t_int)fchannels;
- x->x_br_max = (t_int)fmax;
- x->x_br_nom = (t_int)fnom;
- x->x_br_min = (t_int)fmin;
- post("oggwrite~: %d channels @ %d Hz, bitrates: max. %d / nom. %d / min. %d",
- x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min);
- if(x->x_vorbis >=0)
- {
- oggwrite_vorbis_deinit(x);
- oggwrite_vorbis_init(x);
- }
-}
-
- /* print settings to pd's console window */
-static void oggwrite_print(t_oggwrite *x)
-{
- if(x->x_vbr == 1)
- {
- post("oggwrite~: Ogg Vorbis encoder: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality);
- }
- else
- {
- post("oggwrite~: Ogg Vorbis encoder: %d channels @ %d Hz, bitrates: max. %d, nom. %d, min. %d",
- x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min);
- }
- post("oggwrite~: Ogg Vorbis comments:");
- post(" TITLE = %s", x->x_bcname);
- post(" ARTIST = %s", x->x_bcartist);
- post(" PERFORMER = %s", x->x_bcperformer);
- post(" GENRE = %s", x->x_bcgenre);
- post(" LOCATION = %s", x->x_bclocation);
- post(" COPYRIGHT = %s", x->x_bccopyright);
- post(" CONTACT = %s", x->x_bccontact);
- post(" DESCRIPTION = %s", x->x_bcdescription);
- post(" DATE = %s", x->x_bcdate);
-}
-
-
- /* clean up */
-static void oggwrite_free(t_oggwrite *x)
-{
- if(x->x_vorbis >= 0) /* close encoder */
- {
- oggwrite_vorbis_deinit(x);
- }
- if(x->x_fd >= 0)
- { /* close file */
-#ifndef unix
- _close(x->x_fd);
-#else
- close(x->x_fd);
-#endif
- outlet_float(x->x_obj.ob_outlet, 0);
- }
- freebytes(x->x_buffer, READ*sizeof(t_float));
-}
-
-static void *oggwrite_new(void)
-{
- t_oggwrite *x = (t_oggwrite *)pd_new(oggwrite_class);
- inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal"));
- outlet_new(&x->x_obj, gensym("float"));
- x->x_outpages = outlet_new(&x->x_obj, gensym("float"));
- x->x_fd = -1;
-#ifndef unix
- x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_APPEND|_O_BINARY;
-#else
- x->x_file_open_mode = O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK;
-#endif
- x->x_vorbis = -1;
- x->x_eos = 0;
- x->x_vbr = 1; /* use the vbr setting by default */
- x->x_samplerate = sys_getsr(); /* no resampling supported, sorry */
- x->x_quality = 0.4; /* quality 0.4 gives roughly 128kbps VBR stream */
- x->x_channels = 2; /* stereo */
- x->x_br_max = 144;
- x->x_br_nom = 128;
- x->x_br_min = 96;
- x->x_buffer = getbytes(READ*sizeof(t_float));
- if (!x->x_buffer) /* check buffer */
- {
- error("out of memory!");
- }
- x->x_bytesbuffered = 0;
- x->x_pages = 0;
- x->x_bcname = "ogg/vorbis stream";
- x->x_bcurl = "http://www.pure-data.org/";
- x->x_bcgenre = "experimental";
- x->x_bcdescription = "ogg/vorbis stream recorded with pure-data using oggwrite~";
- x->x_bcartist = "pure-data";
- x->x_bclocation = x->x_bcurl;
- x->x_bccopyright = "";
- x->x_bcperformer = "";
- x->x_bccontact = "";
- x->x_bcdate = "";
- post(oggwrite_version);
- return(x);
-}
-
-void oggwrite_tilde_setup(void)
-{
- oggwrite_class = class_new(gensym("oggwrite~"), (t_newmethod)oggwrite_new, (t_method)oggwrite_free,
- sizeof(t_oggwrite), 0, 0);
- CLASS_MAINSIGNALIN(oggwrite_class, t_oggwrite, x_f );
- class_sethelpsymbol(oggwrite_class, gensym("help-oggwrite~.pd"));
- class_addmethod(oggwrite_class, (t_method)oggwrite_dsp, gensym("dsp"), 0);
- class_addmethod(oggwrite_class, (t_method)oggwrite_open, gensym("open"), A_SYMBOL, 0);
- class_addmethod(oggwrite_class, (t_method)oggwrite_start, gensym("start"), 0);
- class_addmethod(oggwrite_class, (t_method)oggwrite_stop, gensym("stop"), 0);
- class_addmethod(oggwrite_class, (t_method)oggwrite_append, gensym("append"), 0);
- class_addmethod(oggwrite_class, (t_method)oggwrite_truncate, gensym("truncate"), 0);
- class_addmethod(oggwrite_class, (t_method)oggwrite_vorbis, gensym("vorbis"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
- class_addmethod(oggwrite_class, (t_method)oggwrite_vbr, gensym("vbr"), A_FLOAT, A_FLOAT, A_FLOAT, 0);
- class_addmethod(oggwrite_class, (t_method)oggwrite_print, gensym("print"), 0);
- class_addanything(oggwrite_class, oggwrite_comment);
-}
+/* -------------------------- oggwrite~ ---------------------------------------- */ +/* */ +/* Tilde object to send ogg/vorbis encoded stream to icecast2 server. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de). */ +/* Get source at http://www.akustische-kunst.de/puredata/ */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Uses the Ogg/Vorbis encoding library which can be found at */ +/* http://www.vorbis.org */ +/* */ +/* ---------------------------------------------------------------------------- */ + + + +#ifdef NT +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#include <sys/types.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <malloc.h> +#include <ctype.h> +#include <time.h> +#ifdef UNIX +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/time.h> +#define SOCKET_ERROR -1 +#else +#include <io.h> +#include <windows.h> +#include <winsock.h> +#include <windef.h> +#endif + +#include "m_pd.h" /* standard pd stuff */ +#include "vorbisenc.h" /* vorbis encoder stuff */ + +#define READ 1024 /* number of samples send to encoder at each call */ + /* has to be even multiple of 64 */ + +static char *oggwrite_version = "oggwrite~: ogg/vorbis recorder version 0.1c, written by Olaf Matthes"; + +static t_class *oggwrite_class; + +typedef struct _oggwrite +{ + t_object x_obj; + /* ogg/vorbis related stuff */ + ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ + 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 */ + t_int x_vorbis; /* info about encoder status */ + t_float x_pages; /* number of pages that have been output to server */ + t_outlet *x_outpages; /* output to send them to */ + + /* ringbuffer stuff */ + t_float *x_buffer; /* data to be buffered (ringbuffer)*/ + t_int x_bytesbuffered; /* number of unprocessed bytes in buffer */ + + /* ogg/vorbis format stuff */ + t_int x_samplerate; /* samplerate of stream (default = getsr() ) */ + t_float x_quality; /* desired quality level from 0.0 to 1.0 (lo to hi) */ + t_int x_br_max; /* max. bitrate of ogg/vorbis stream */ + t_int x_br_nom; /* nom. bitrate of ogg/vorbis stream */ + t_int x_br_min; /* min. bitrate of ogg/vorbis stream */ + t_int x_channels; /* number of channels (1 or 2) */ + t_int x_vbr; + + /* IceCast server stuff */ + char* x_passwd; /* password for server */ + char* x_bcname; /* name of broadcast */ + char* x_bcurl; /* url of broadcast */ + char* x_bcgenre; /* genre of broadcast */ + char* x_bcdescription; /* description */ + char* x_bcartist; /* artist */ + char* x_bclocation; + char* x_bccopyright; + char* x_bcperformer; + char* x_bccontact; + char* x_bcdate; /* system date when broadcast started */ + char* x_mountpoint; /* mountpoint for IceCast server */ + t_int x_bcpublic; /* do(n't) publish broadcast on www.oggwrite.com */ + + /* recording stuff */ + t_int x_fd; /* file descriptor of the mp3 output */ + t_int x_file_open_mode;/* file opening mode */ + t_int x_byteswritten; /* number of bytes written */ + t_int x_recflag; /* recording flag toggled by messages "start" and "stop" */ + + t_float x_f; /* float needed for signal input */ +} t_oggwrite; + +/* Utility functions */ + +static void sys_closesocket(int fd) +{ +#ifdef UNIX + close(fd); +#endif +#ifdef NT + closesocket(fd); +#endif +} + /* prototypes */ +static void oggwrite_vorbis_deinit(t_oggwrite *x); + +/* ----------------------------------------- oggwrite~ ---------------------------------- */ + /* write ogg/vorbis to file */ +static int oggwrite_write(t_oggwrite *x) +{ + int err = -1; /* error return code */ + + /* write out pages (if any) */ + while(!x->x_eos) + { + int result=ogg_stream_pageout(&(x->x_os),&(x->x_og)); + if(result==0)break; +#ifndef UNIX + err = _write(x->x_fd, x->x_og.header, x->x_og.header_len); +#else + err = write(x->x_fd, x->x_og.header, x->x_og.header_len); +#endif + if(err < 0) + { + error("oggwrite~: could not send ogg header to server (%d)", err); + x->x_eos = 1; /* indicate (artificial) end of stream */ + return err; + } +#ifndef UNIX + err = _write(x->x_fd, x->x_og.body, x->x_og.body_len); +#else + err = write(x->x_fd, x->x_og.body, x->x_og.body_len); +#endif + if(err < 0) + { + error("oggwrite~: could not send ogg body to server (%d)", err); + x->x_eos = 1; /* indicate (artificial) end of stream */ + return err; + } + x->x_pages++; /* count number of pages */ + /* there might be more than one pages we have to send */ + if(ogg_page_eos(&(x->x_og)))x->x_eos=1; + } + outlet_float(x->x_outpages, x->x_pages); /* update info */ + return 1; +} + + + /* encode data to ogg/vorbis stream */ +static void oggwrite_encode(t_oggwrite *x) +{ + unsigned short i, ch; + int err = 1; + int n; + int channel = x->x_channels; /* make lokal copy of num. of channels */ + + /* expose the buffer to submit data */ + float **inbuffer=vorbis_analysis_buffer(&(x->x_vd),READ); + + /* read from buffer */ + for(n = 0; n < READ / channel; n++) /* fill encode buffer */ + { + for(ch = 0; ch < channel; ch++) + { + inbuffer[ch][n] = (float)x->x_buffer[n * channel + ch]; + } + } + /* tell the library how much we actually submitted */ + vorbis_analysis_wrote(&(x->x_vd),n); + + /* vorbis does some data preanalysis, then divvies up blocks for + more involved (potentially parallel) processing. Get a single + block for encoding now */ + while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1) + { + /* analysis, assume we want to use bitrate management */ + vorbis_analysis(&(x->x_vb),NULL); + vorbis_bitrate_addblock(&(x->x_vb)); + + while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op))) + { + /* weld the packet into the bitstream */ + ogg_stream_packetin(&(x->x_os),&(x->x_op)); + err = oggwrite_write(x); /* stream packet to server */ + } + } + /* check for errors */ + if(err < 0) + { + if(x->x_fd > 0) + { +#ifndef UNIX + if(_close(x->x_fd) < 0) +#else + if(close(x->x_fd) < 0) +#endif + { + post( "oggwrite~: file closed due to an error" ); + outlet_float(x->x_obj.ob_outlet, 0); + } + } + } +} + + /* buffer data as channel interleaved floats */ +static t_int *oggwrite_perform(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); /* left audio inlet */ + t_float *in2 = (t_float *)(w[2]); /* right audio inlet */ + t_oggwrite *x = (t_oggwrite *)(w[3]); + int n = (int)(w[4]); /* number of samples */ + int i; + t_float in; + + /* copy the data into the buffer */ + if(x->x_channels != 1) /* everything but mono */ + { + n *= 2; /* two channels go into one buffer */ + + for(i = 0; i < n; i++) + { + if(i%2) + { + in = *(in2++); /* right inlet */ + } + else + { + in = *(in1++); /* left inlet */ + } + if (in > 1.0) { in = 1.0; } + if (in < -1.0) { in = -1.0; } + x->x_buffer[i + x->x_bytesbuffered] = in; + } + } + else /* mono encoding -> just take left signal inlet 'in1' */ + { + for(i = 0; i < n; i++) + { + in = *(in1++); + if (in > 1.0) { in = 1.0; } + if (in < -1.0) { in = -1.0; } + x->x_buffer[i + x->x_bytesbuffered] = in; + } + } + /* count, encode and send ogg/vorbis */ + if((x->x_fd >= 0)&&(x->x_recflag)) + { + /* count buffered samples when connected */ + x->x_bytesbuffered += n; + + /* encode and send to server */ + if((x->x_bytesbuffered >= READ)&&(x->x_vorbis >= 0)) + { + oggwrite_encode(x); /* encode data */ + x->x_bytesbuffered = 0; /* assume we got rid of all of them */ + } + } + return (w+5); +} + +static void oggwrite_dsp(t_oggwrite *x, t_signal **sp) +{ + dsp_add(oggwrite_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + + /* initialize the vorbisenc library */ +static void oggwrite_vorbis_init(t_oggwrite *x) +{ + int err = -1; + + x->x_vorbis = -1; /* indicate that encoder is not available right now */ + + /* choose an encoding mode */ + vorbis_info_init(&(x->x_vi)); + + if(x->x_samplerate != sys_getsr())post("oggwrite~: warning: resampling from %d to %.0f not supported", x->x_samplerate, sys_getsr()); + if(x->x_vbr == 1) + { /* quality based setting */ + if(vorbis_encode_init_vbr(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_quality)) + { + post("oggwrite~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); + vorbis_info_clear(&(x->x_vi)); + return; + } + } + else + { /* bitrate based setting */ + if(vorbis_encode_init(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_br_max*1024, x->x_br_nom*1024, x->x_br_min*1024)) + { + post("oggwrite~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); + vorbis_info_clear(&(x->x_vi)); + return; + } + } + + /* add a comment */ + vorbis_comment_init(&(x->x_vc)); + vorbis_comment_add_tag(&(x->x_vc),"TITLE", x->x_bcname); + vorbis_comment_add_tag(&(x->x_vc),"ARTIST", x->x_bcartist); + vorbis_comment_add_tag(&(x->x_vc),"GENRE",x->x_bcgenre); + vorbis_comment_add_tag(&(x->x_vc),"DESCRIPTION", x->x_bcdescription); + vorbis_comment_add_tag(&(x->x_vc),"LOCATION",x->x_bclocation); + vorbis_comment_add_tag(&(x->x_vc),"PERFORMER",x->x_bcperformer); + vorbis_comment_add_tag(&(x->x_vc),"COPYRIGHT",x->x_bccopyright); + vorbis_comment_add_tag(&(x->x_vc),"CONTACT",x->x_bccontact); + vorbis_comment_add_tag(&(x->x_vc),"DATE",x->x_bcdate); + vorbis_comment_add_tag(&(x->x_vc),"ENCODER","oggwrite~ v0.1b for pure-data"); + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init(&(x->x_vd),&(x->x_vi)); + vorbis_block_init(&(x->x_vd),&(x->x_vb)); + + /* set up our packet->stream encoder */ + /* pick a random serial number; that way we can more likely build + chained streams just by concatenation */ + srand(time(NULL)); + ogg_stream_init(&(x->x_os),rand()); + + /* Vorbis streams begin with three headers; the initial header (with + most of the codec setup parameters) which is mandated by the Ogg + bitstream spec. The second header holds any comment fields. The + third header holds the bitstream codebook. We merely need to + make the headers, then pass them to libvorbis one at a time; + libvorbis handles the additional Ogg bitstream constraints */ + + { + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout(&(x->x_vd),&(x->x_vc),&header,&header_comm,&header_code); + ogg_stream_packetin(&(x->x_os),&header); /* automatically placed in its own page */ + ogg_stream_packetin(&(x->x_os),&header_comm); + ogg_stream_packetin(&(x->x_os),&header_code); + + /* We don't have to write out here, but doing so makes streaming + * much easier, so we do, flushing ALL pages. This ensures the actual + * audio data will start on a new page + * + * IceCast2 server will take this as a first info about our stream + */ + while(!x->x_eos) + { + int result=ogg_stream_flush(&(x->x_os),&(x->x_og)); + if(result==0)break; +#ifndef UNIX + err = _write(x->x_fd, x->x_og.header, x->x_og.header_len); +#else + err = write(x->x_fd, x->x_og.header, x->x_og.header_len); +#endif + if(err < 0) + { + error("oggwrite~: could not write ogg header to file (%d)", err); + x->x_eos = 1; /* indicate end of stream */ + x->x_vorbis = -1; /* stop encoding instantly */ + if(x->x_fd > 0) + { +#ifndef UNIX + if(_close(x->x_fd) < 0) +#else + if(close(x->x_fd) < 0) +#endif + { + post( "oggwrite~: file closed due to an error" ); + outlet_float(x->x_obj.ob_outlet, 0); + } + } + return; + } +#ifndef UNIX + err = _write(x->x_fd, x->x_og.body, x->x_og.body_len); +#else + err = write(x->x_fd, x->x_og.body, x->x_og.body_len); +#endif + if(err < 0) + { + error("oggwrite~: could not write ogg body to file (%d)", err); + x->x_eos = 1; /* indicate end of stream */ + x->x_vorbis = -1; /* stop encoding instantly */ + if(x->x_fd > 0) + { +#ifndef UNIX + if(_close(x->x_fd) < 0) +#else + if(close(x->x_fd) < 0) +#endif + { + post( "oggwrite~: file closed due to an error" ); + outlet_float(x->x_obj.ob_outlet, 0); + } + } + return; + } + } + } + x->x_vorbis = 1; /* vorbis encoder initialised */ + post("oggwrite~: ogg/vorbis encoder (re)initialised"); +} + + /* initialize the vorbisenc library */ +static void oggwrite_vorbis_deinit(t_oggwrite *x) +{ + x->x_vorbis = -1; + vorbis_analysis_wrote(&(x->x_vd),0); + /* clean up and exit. vorbis_info_clear() must be called last */ + ogg_stream_clear(&(x->x_os)); + vorbis_block_clear(&(x->x_vb)); + vorbis_dsp_clear(&(x->x_vd)); + vorbis_comment_clear(&(x->x_vc)); + vorbis_info_clear(&(x->x_vi)); + post("oggwrite~: ogg/vorbis encoder closed"); +} + + /* connect to oggwrite server */ +static void oggwrite_open(t_oggwrite *x, t_symbol *sfile) +{ + time_t now; /* to get the time */ + + /* closing previous file descriptor */ + if(x->x_fd > 0) + { +#ifndef UNIX + if(_close(x->x_fd) < 0) +#else + if(close(x->x_fd) < 0) +#endif + { + error( "oggwrite~: file closed" ); + outlet_float(x->x_obj.ob_outlet, 0); + } + } + + if(x->x_recflag) + { + x->x_recflag = 0; + } + +#ifndef UNIX + if((x->x_fd = _open( sfile->s_name, x->x_file_open_mode, _S_IREAD|_S_IWRITE)) < 0) +#else + if((x->x_fd = open( sfile->s_name, x->x_file_open_mode, S_IRWXU|S_IRWXG|S_IRWXO )) < 0) +#endif + { + error( "oggwrite~: can not open \"%s\"", sfile->s_name); + x->x_fd=-1; + return; + } + x->x_byteswritten = 0; + post( "oggwrite~: \"%s \" opened", sfile->s_name); + outlet_float(x->x_obj.ob_outlet, 1); + + /* get the time for the DATE comment, then init encoder */ + now=time(NULL); + x->x_bcdate = ctime(&now); + + x->x_eos = 0; + oggwrite_vorbis_init(x); +} + + /* setting file write mode to append */ +static void oggwrite_append(t_oggwrite *x) +{ +#ifndef UNIX + x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_APPEND|_O_BINARY; +#else + x->x_file_open_mode = O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK; +#endif + if(x->x_fd>=0)post("oggwrite~: mode set to append: open a new file to make changes take effect"); +} + + /* setting file write mode to truncate */ +static void oggwrite_truncate(t_oggwrite *x) +{ +#ifndef UNIX + x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_TRUNC|_O_BINARY; +#else + x->x_file_open_mode = O_CREAT|O_WRONLY|O_TRUNC|O_NONBLOCK; +#endif + if(x->x_fd>=0)post("oggwrite~: mode set to truncate: open a new file to make changes take effect"); +} + + /* start recording */ +static void oggwrite_start(t_oggwrite *x) +{ + if ( x->x_fd < 0 ) { + post("oggwrite~: no file selected"); + return; + } + + if ( x->x_recflag == 1 ) { + post("oggwrite~: already recording"); + return; + } + if(x->x_vorbis < 0) + { + oggwrite_vorbis_init(x); + } + + x->x_recflag = 1; + post("oggwrite~: start recording"); +} + + /* stop recording */ +static void oggwrite_stop(t_oggwrite *x) +{ + int err = -1; + + /* first stop recording / buffering and so on, than do the rest */ + x->x_recflag = 0; + post("oggwrite~: recording stoped"); + if(x->x_vorbis >= 0) + { + oggwrite_vorbis_deinit(x); + } +} + + /* set comment fields for header (reads in just anything) */ +static void oggwrite_comment(t_oggwrite *x, t_symbol *s, t_int argc, t_atom* argv) +{ + int i = argc; + char *comment = NULL; + int len = strlen(atom_gensym(argv)->s_name); + + comment = atom_gensym(argv)->s_name; + + while (len--) + { + if(*(comment + len) == '=')*(comment + len) = ' '; + } + + if(strstr(s->s_name, "ARTIST")) + { + x->x_bcartist = comment; + post("oggwrite~: ARTIST = %s", x->x_bcartist); + } + else if(strstr(s->s_name, "GENRE")) + { + x->x_bcgenre = comment; + post("oggwrite~: GENRE = %s", x->x_bcgenre); + } + else if(strstr(s->s_name, "TITLE")) + { + x->x_bcname = comment; + post("oggwrite~: TITLE = %s", x->x_bcname); + } + else if(strstr(s->s_name, "PERFORMER")) + { + x->x_bcperformer = comment; + post("oggwrite~: PERFORMER = %s", x->x_bcperformer); + } + else if(strstr(s->s_name, "LOCATION")) + { + x->x_bclocation = comment; + post("oggwrite~: LOCATION = %s", x->x_bclocation); + } + else if(strstr(s->s_name, "COPYRIGHT")) + { + x->x_bccopyright = comment; + post("oggwrite~: COPYRIGHT = %s", x->x_bccopyright); + } + else if(strstr(s->s_name, "CONTACT")) + { + x->x_bccontact = comment; + post("oggwrite~: CONTACT = %s", x->x_bccontact); + } + else if(strstr(s->s_name, "DESCRIPTION")) + { + x->x_bcdescription = comment; + post("oggwrite~: DESCRIPTION = %s", x->x_bcdescription); + } + else if(strstr(s->s_name, "DATE")) + { + x->x_bcdate = comment; + post("oggwrite~: DATE=%s", x->x_bcdate); + } + else post("oggwrite~: no method for %s", s->s_name); + if(x->x_vorbis >=0) + { + oggwrite_vorbis_deinit(x); + oggwrite_vorbis_init(x); + } +} + + /* settings for variable bitrate encoding */ +static void oggwrite_vbr(t_oggwrite *x, t_floatarg fsr, t_floatarg fchannels, + t_floatarg fquality) +{ + x->x_vbr = 1; + x->x_samplerate = (t_int)fsr; + x->x_quality = fquality; + x->x_channels = (t_int)fchannels; + post("oggwrite~: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); + if(x->x_vorbis >=0) + { + oggwrite_vorbis_deinit(x); + oggwrite_vorbis_init(x); + } +} + + /* settings for bitrate-based vbr encoding */ +static void oggwrite_vorbis(t_oggwrite *x, t_floatarg fsr, t_floatarg fchannels, + t_floatarg fmax, t_floatarg fnom, t_floatarg fmin) +{ + x->x_vbr = 0; + x->x_samplerate = (t_int)fsr; + x->x_channels = (t_int)fchannels; + x->x_br_max = (t_int)fmax; + x->x_br_nom = (t_int)fnom; + x->x_br_min = (t_int)fmin; + post("oggwrite~: %d channels @ %d Hz, bitrates: max. %d / nom. %d / min. %d", + x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); + if(x->x_vorbis >=0) + { + oggwrite_vorbis_deinit(x); + oggwrite_vorbis_init(x); + } +} + + /* print settings to pd's console window */ +static void oggwrite_print(t_oggwrite *x) +{ + if(x->x_vbr == 1) + { + post("oggwrite~: Ogg Vorbis encoder: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); + } + else + { + post("oggwrite~: Ogg Vorbis encoder: %d channels @ %d Hz, bitrates: max. %d, nom. %d, min. %d", + x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); + } + post("oggwrite~: Ogg Vorbis comments:"); + post(" TITLE = %s", x->x_bcname); + post(" ARTIST = %s", x->x_bcartist); + post(" PERFORMER = %s", x->x_bcperformer); + post(" GENRE = %s", x->x_bcgenre); + post(" LOCATION = %s", x->x_bclocation); + post(" COPYRIGHT = %s", x->x_bccopyright); + post(" CONTACT = %s", x->x_bccontact); + post(" DESCRIPTION = %s", x->x_bcdescription); + post(" DATE = %s", x->x_bcdate); +} + + + /* clean up */ +static void oggwrite_free(t_oggwrite *x) +{ + if(x->x_vorbis >= 0) /* close encoder */ + { + oggwrite_vorbis_deinit(x); + } + if(x->x_fd >= 0) + { /* close file */ +#ifndef UNIX + _close(x->x_fd); +#else + close(x->x_fd); +#endif + outlet_float(x->x_obj.ob_outlet, 0); + } + freebytes(x->x_buffer, READ*sizeof(t_float)); +} + +static void *oggwrite_new(void) +{ + t_oggwrite *x = (t_oggwrite *)pd_new(oggwrite_class); + inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); + outlet_new(&x->x_obj, gensym("float")); + x->x_outpages = outlet_new(&x->x_obj, gensym("float")); + x->x_fd = -1; +#ifndef UNIX + x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_APPEND|_O_BINARY; +#else + x->x_file_open_mode = O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK; +#endif + x->x_vorbis = -1; + x->x_eos = 0; + x->x_vbr = 1; /* use the vbr setting by default */ + x->x_samplerate = sys_getsr(); /* no resampling supported, sorry */ + x->x_quality = 0.4; /* quality 0.4 gives roughly 128kbps VBR stream */ + x->x_channels = 2; /* stereo */ + x->x_br_max = 144; + x->x_br_nom = 128; + x->x_br_min = 96; + x->x_buffer = getbytes(READ*sizeof(t_float)); + if (!x->x_buffer) /* check buffer */ + { + error("out of memory!"); + } + x->x_bytesbuffered = 0; + x->x_pages = 0; + x->x_bcname = "ogg/vorbis stream"; + x->x_bcurl = "http://www.pure-data.org/"; + x->x_bcgenre = "experimental"; + x->x_bcdescription = "ogg/vorbis stream recorded with pure-data using oggwrite~"; + x->x_bcartist = "pure-data"; + x->x_bclocation = x->x_bcurl; + x->x_bccopyright = ""; + x->x_bcperformer = ""; + x->x_bccontact = ""; + x->x_bcdate = ""; + post(oggwrite_version); + return(x); +} + +void oggwrite_tilde_setup(void) +{ + oggwrite_class = class_new(gensym("oggwrite~"), (t_newmethod)oggwrite_new, (t_method)oggwrite_free, + sizeof(t_oggwrite), 0, 0); + CLASS_MAINSIGNALIN(oggwrite_class, t_oggwrite, x_f ); + class_sethelpsymbol(oggwrite_class, gensym("help-oggwrite~.pd")); + class_addmethod(oggwrite_class, (t_method)oggwrite_dsp, gensym("dsp"), 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_open, gensym("open"), A_SYMBOL, 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_start, gensym("start"), 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_stop, gensym("stop"), 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_append, gensym("append"), 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_truncate, gensym("truncate"), 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_vorbis, gensym("vorbis"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_vbr, gensym("vbr"), A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_print, gensym("print"), 0); + class_addanything(oggwrite_class, oggwrite_comment); +} diff --git a/oggwrite~/readme b/oggwrite~/readme index 4945d0d..b79840c 100644 --- a/oggwrite~/readme +++ b/oggwrite~/readme @@ -1,110 +1,110 @@ -oggwrite~ version 0.1b
-copyright (c) 2002 by Olaf Matthes
-
-oggwrite~ is an ogg/vorbis file writing external for pd (by Miller
-Puckette).
-
-
-To run oggwrite~ place the file oggwrite~.dll for win or oggwrite~.pd_linux
-in the directory of our patch or start pd with '-lib oggwrite~' flag.
-
-To compile oggwrite~ 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.
-
-*****************************************************************************
-
-oggwrite~ 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:
-
-Use message "vbr <samplerate> <channels> <quality>" to set the vorbis
-encoding parameters. Resampling is currently not supported, so 'samplerate'
-should be the one pd is running at. 'channels' specyfies the number of channels
-to stream. This can be set to 2 (default) or 1 which means mono stream taking
-the leftmost audio input only. 'quality' can be a value between 0.0 and 1.0
-giving the quality of the stream. 0.4 (default) results in a stream that's
-about 128kbps.
-
-Since Vorbis uses VBR encoding, bitrates vary depending on the type of audio
-signal to be encoded. A pure sine [osc~] results in the smalest stream, com-
-plex audio signals will increase this value significantly. To test the maximum
-bitrate that might occur for a quality setting use noise~ as signal input.
-
-Use message "vorbis <samplerate> <channels> <maximum bitrate> <nominal bit-
-rate> <minimal bitrate>" to set encoding quality on the basis of bitrates.
-When setting all three bitrate parameters to the same value one gets a
-constant bitrate stream. Values are in kbps!
-
-Message "open <filename>" opens an existing file. In case the file does not
-exist oggwrite~ will create it. Previously opened files will be closed. To
-determine how data should be written to the file choose "append" (default)
-or "truncate" (in case you want to overwrite data in your file).
-
-To set the comment tags in the ogg/vorbis header (which can be displayed by
-the receiving client) use message "<NAMEOFTAG> <comment>". Supported tags are:
-TITLE, ARTIST, GENRE, PERFORMER, LOCATION, COPYRIGHT, CONTACT, DESCRIPTION and
-DATE (which is automatically set to the date/time the broadcast started). To
-get spaces use '=' instead.
-
-Note that changing encoding parameters or header comments while oggwrite~ is
-recording to file might result in audible dropouts.
-
-
-Listening to it:
-
-To listen to ogg/vorbis encoded audio files many player need an extra plug-in.
-Have a look at http://www.vorbis.com/ to find the appropiate plug-in for your
-player.
-
-
-
-
-
-Latest version can be found at:
-http://www.akustische-kunst.de/puredata/
-
+oggwrite~ version 0.1b +copyright (c) 2002 by Olaf Matthes + +oggwrite~ is an ogg/vorbis file writing external for pd (by Miller +Puckette). + + +To run oggwrite~ place the file oggwrite~.dll for win or oggwrite~.pd_linux +in the directory of our patch or start pd with '-lib oggwrite~' flag. + +To compile oggwrite~ 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. + +***************************************************************************** + +oggwrite~ 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: + +Use message "vbr <samplerate> <channels> <quality>" to set the vorbis +encoding parameters. Resampling is currently not supported, so 'samplerate' +should be the one pd is running at. 'channels' specyfies the number of channels +to stream. This can be set to 2 (default) or 1 which means mono stream taking +the leftmost audio input only. 'quality' can be a value between 0.0 and 1.0 +giving the quality of the stream. 0.4 (default) results in a stream that's +about 128kbps. + +Since Vorbis uses VBR encoding, bitrates vary depending on the type of audio +signal to be encoded. A pure sine [osc~] results in the smalest stream, com- +plex audio signals will increase this value significantly. To test the maximum +bitrate that might occur for a quality setting use noise~ as signal input. + +Use message "vorbis <samplerate> <channels> <maximum bitrate> <nominal bit- +rate> <minimal bitrate>" to set encoding quality on the basis of bitrates. +When setting all three bitrate parameters to the same value one gets a +constant bitrate stream. Values are in kbps! + +Message "open <filename>" opens an existing file. In case the file does not +exist oggwrite~ will create it. Previously opened files will be closed. To +determine how data should be written to the file choose "append" (default) +or "truncate" (in case you want to overwrite data in your file). + +To set the comment tags in the ogg/vorbis header (which can be displayed by +the receiving client) use message "<NAMEOFTAG> <comment>". Supported tags are: +TITLE, ARTIST, GENRE, PERFORMER, LOCATION, COPYRIGHT, CONTACT, DESCRIPTION and +DATE (which is automatically set to the date/time the broadcast started). To +get spaces use '=' instead. + +Note that changing encoding parameters or header comments while oggwrite~ is +recording to file might result in audible dropouts. + + +Listening to it: + +To listen to ogg/vorbis encoded audio files many player need an extra plug-in. +Have a look at http://www.vorbis.com/ to find the appropiate plug-in for your +player. + + + + + +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 diff --git a/oggwrite~/vorbisenc.h b/oggwrite~/vorbisenc.h index 901c47a..1fc8913 100644 --- a/oggwrite~/vorbisenc.h +++ b/oggwrite~/vorbisenc.h @@ -1,93 +1,93 @@ -/********************************************************************
- * *
- * 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: vorbis encode-engine setup
- last mod: $Id: vorbisenc.h,v 1.1 2002-11-26 10:51:49 ggeiger Exp $
-
- ********************************************************************/
-
-#ifndef _OV_ENC_H_
-#define _OV_ENC_H_
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-#include "codec.h"
-
-extern int vorbis_encode_init(vorbis_info *vi,
- long channels,
- long rate,
-
- long max_bitrate,
- long nominal_bitrate,
- long min_bitrate);
-
-extern int vorbis_encode_setup_managed(vorbis_info *vi,
- long channels,
- long rate,
-
- long max_bitrate,
- long nominal_bitrate,
- long min_bitrate);
-
-extern int vorbis_encode_setup_vbr(vorbis_info *vi,
- long channels,
- long rate,
-
- float /* quality level from 0. (lo) to 1. (hi) */
- );
-
-extern int vorbis_encode_init_vbr(vorbis_info *vi,
- long channels,
- long rate,
-
- float base_quality /* quality level from 0. (lo) to 1. (hi) */
- );
-
-extern int vorbis_encode_setup_init(vorbis_info *vi);
-
-extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg);
-
-#define OV_ECTL_RATEMANAGE_GET 0x10
-
-#define OV_ECTL_RATEMANAGE_SET 0x11
-#define OV_ECTL_RATEMANAGE_AVG 0x12
-#define OV_ECTL_RATEMANAGE_HARD 0x13
-
-#define OV_ECTL_LOWPASS_GET 0x20
-#define OV_ECTL_LOWPASS_SET 0x21
-
-#define OV_ECTL_IBLOCK_GET 0x30
-#define OV_ECTL_IBLOCK_SET 0x31
-
-struct ovectl_ratemanage_arg {
- int management_active;
-
- long bitrate_hard_min;
- long bitrate_hard_max;
- double bitrate_hard_window;
-
- long bitrate_av_lo;
- long bitrate_av_hi;
- double bitrate_av_window;
- double bitrate_av_window_center;
-};
-
-#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: vorbis encode-engine setup + last mod: $Id: vorbisenc.h,v 1.2 2003-04-23 10:36:25 xovo Exp $ + + ********************************************************************/ + +#ifndef _OV_ENC_H_ +#define _OV_ENC_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include "codec.h" + +extern int vorbis_encode_init(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_managed(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_vbr(vorbis_info *vi, + long channels, + long rate, + + float /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_init_vbr(vorbis_info *vi, + long channels, + long rate, + + float base_quality /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_setup_init(vorbis_info *vi); + +extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg); + +#define OV_ECTL_RATEMANAGE_GET 0x10 + +#define OV_ECTL_RATEMANAGE_SET 0x11 +#define OV_ECTL_RATEMANAGE_AVG 0x12 +#define OV_ECTL_RATEMANAGE_HARD 0x13 + +#define OV_ECTL_LOWPASS_GET 0x20 +#define OV_ECTL_LOWPASS_SET 0x21 + +#define OV_ECTL_IBLOCK_GET 0x30 +#define OV_ECTL_IBLOCK_SET 0x31 + +struct ovectl_ratemanage_arg { + int management_active; + + long bitrate_hard_min; + long bitrate_hard_max; + double bitrate_hard_window; + + long bitrate_av_lo; + long bitrate_av_hi; + double bitrate_av_window; + double bitrate_av_window_center; +}; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + + @@ -1,46 +1,46 @@ -#ifndef VERSION
-#define VERSION "0.22"
-#endif
-
-#include <m_pd.h>
-
-
-#ifndef __DATE__
-#define __DATE__ "without using a gnu compiler"
-#endif
-
-typedef struct _pdogg
-{
- t_object x_obj;
-} t_pdogg;
-
-static t_class* pdogg_class;
-
-// tilde objects
-void oggamp_tilde_setup();
-void oggcast_tilde_setup();
-void oggread_tilde_setup();
-void oggwrite_tilde_setup();
-
-static void* pdogg_new(t_symbol* s) {
- t_pdogg *x = (t_pdogg *)pd_new(pdogg_class);
- return (x);
-}
-
-void pdogg_setup(void)
-{
- pdogg_class = class_new(gensym("pdogg"), (t_newmethod)pdogg_new, 0,
- sizeof(t_pdogg), 0,0);
-
- oggamp_tilde_setup();
- oggcast_tilde_setup();
- oggread_tilde_setup();
- oggwrite_tilde_setup();
-
- post("\n pdogg :: Ogg Vorbis library for pure-data");
- post(" written by Olaf Matthes <olaf.matthes@gmx.de>");
- post(" version: "VERSION);
- post(" compiled: "__DATE__", using Ogg Vorbis library 1.0");
- post(" home: http://www.akustische-kunst.org/puredata/pdogg/");
- post(" including: oggamp~0.2f, oggcast~0.2g, oggread~0.2b, oggwrite~0.1c\n");
-}
+#ifndef VERSION +#define VERSION "0.23" +#endif + +#include <m_pd.h> + + +#ifndef __DATE__ +#define __DATE__ "without using a gnu compiler" +#endif + +typedef struct _pdogg +{ + t_object x_obj; +} t_pdogg; + +static t_class* pdogg_class; + +// tilde objects +void oggamp_tilde_setup(); +void oggcast_tilde_setup(); +void oggread_tilde_setup(); +void oggwrite_tilde_setup(); + +static void* pdogg_new(t_symbol* s) { + t_pdogg *x = (t_pdogg *)pd_new(pdogg_class); + return (x); +} + +void pdogg_setup(void) +{ + pdogg_class = class_new(gensym("pdogg"), (t_newmethod)pdogg_new, 0, + sizeof(t_pdogg), 0,0); + + oggamp_tilde_setup(); + oggcast_tilde_setup(); + oggread_tilde_setup(); + oggwrite_tilde_setup(); + + post("\n pdogg :: Ogg Vorbis library for pure-data"); + post(" written by Olaf Matthes <olaf.matthes@gmx.de>"); + post(" version: "VERSION); + post(" compiled: "__DATE__", using Ogg Vorbis library 1.0"); + post(" home: http://www.akustische-kunst.org/puredata/pdogg/"); + post(" including: oggamp~0.2f, oggcast~0.2h, oggread~0.2c, oggwrite~0.1c\n"); +} @@ -1,79 +1,79 @@ -Version 0.2
-copyright (c) 2002 by Olaf Matthes
-
-pdogg~ is a collection of ogg/vorbis externals for pd (by Miller
-Puckette).
-
-It includes:
-- oggamp~ : streaming client
-- oggcast~ : streamer (for Icecast2)
-- oggread~ : reads files from disk
-- oggwrite~ : writes files to disk
-
-
-To use pdogg~ start pd with '-lib path\to\pdogg' flag.
-On Win32 systems Pd 0.35 test 17 or later is necessary to get it working!
-
-To compile pdogg~ you need the ogg/vorbice library from
-http://www.vorbis.com/ and under win additionally Pthreads-win32 from
-http://sources.redhat.com/pthreads-win32/.
-You have to modify the makefile to make it point to the place where the
-libraries can be found on your system.
-
-
-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.
-
-*****************************************************************************
-
-pdogg~ 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.
-
-
-Below the copyright notice for Pthreads-win32:
-
- Pthreads-win32 is open 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 version 2.1 of the
- License.
- Pthreads-win32 is several binary link libraries, several modules,
- associated interface definition files and scripts used to control
- its compilation and installation.
- Pthreads-win32 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.
-
-*****************************************************************************
-
-For information about the included externals see the README files provided
-in the subdirectories.
-
-
-Latest version can be found at:
-http://www.akustische-kunst.de/puredata/
-
+Version 0.2 +copyright (c) 2002 by Olaf Matthes + +pdogg~ is a collection of ogg/vorbis externals for pd (by Miller +Puckette). + +It includes: +- oggamp~ : streaming client +- oggcast~ : streamer (for Icecast2) +- oggread~ : reads files from disk +- oggwrite~ : writes files to disk + + +To use pdogg~ start pd with '-lib path\to\pdogg' flag. +On Win32 systems Pd 0.35 test 17 or later is necessary to get it working! + +To compile pdogg~ you need the ogg/vorbice library from +http://www.vorbis.com/ and under win additionally Pthreads-win32 from +http://sources.redhat.com/pthreads-win32/. +You have to modify the makefile to make it point to the place where the +libraries can be found on your system. + + +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. + +***************************************************************************** + +pdogg~ 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. + + +Below the copyright notice for Pthreads-win32: + + Pthreads-win32 is open 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 version 2.1 of the + License. + Pthreads-win32 is several binary link libraries, several modules, + associated interface definition files and scripts used to control + its compilation and installation. + Pthreads-win32 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. + +***************************************************************************** + +For information about the included externals see the README files provided +in the subdirectories. + + +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 |