aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuenter Geiger <ggeiger@users.sourceforge.net>2002-11-26 10:52:38 +0000
committerGuenter Geiger <ggeiger@users.sourceforge.net>2002-11-26 10:52:38 +0000
commit39bfab603950686c2b007be44fc75643daacc932 (patch)
treefab9e4f4c7fc2e38682eb8b9c0a42d774d4b7f96
svn path=/trunk/externals/pdogg/; revision=248
-rw-r--r--makefile100
-rw-r--r--oggamp~/HISTORY40
-rw-r--r--oggamp~/codec.h233
-rw-r--r--oggamp~/help-oggamp~.pd54
-rw-r--r--oggamp~/oggamp~.c1491
-rw-r--r--oggamp~/readme87
-rw-r--r--oggcast~/HISTORY65
-rw-r--r--oggcast~/codec.h233
-rw-r--r--oggcast~/help-oggcast~.pd57
-rw-r--r--oggcast~/oggcast~.c1302
-rw-r--r--oggcast~/readme116
-rw-r--r--oggcast~/vorbisenc.h93
-rw-r--r--oggread~/codec.h233
-rw-r--r--oggread~/help-oggread~.pd34
-rw-r--r--oggread~/oggread~.c413
-rw-r--r--oggread~/readme75
-rw-r--r--oggwrite~/codec.h233
-rw-r--r--oggwrite~/help-oggwrite~.pd59
-rw-r--r--oggwrite~/oggwrite~.c751
-rw-r--r--oggwrite~/readme110
-rw-r--r--oggwrite~/vorbisenc.h93
-rw-r--r--pdogg.c46
-rw-r--r--readme79
23 files changed, 5997 insertions, 0 deletions
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..45755a3
--- /dev/null
+++ b/makefile
@@ -0,0 +1,100 @@
+NAME=pdogg
+CSYM=pdogg
+
+current: pd_nt pd_darwin pd_linux
+
+# ----------------------- NT -----------------------
+
+pd_nt: $(NAME).dll
+
+.SUFFIXES: .dll
+
+PDNTCFLAGS = /W3 /WX /MD /O2 /G6 /DNT /DPD /nologo
+VC="C:\Programme\Microsoft Visual Studio\VC98"
+
+PDNTINCLUDE = /I. /Ic:\pd\tcl\include /Ic:\pd\src /I$(VC)\include /Iinclude
+
+PDNTLDIR = $(VC)\Lib
+PDNTLIB = $(PDNTLDIR)\msvcrt.lib \
+ $(PDNTLDIR)\oldnames.lib \
+ $(PDNTLDIR)\kernel32.lib \
+ $(PDNTLDIR)\user32.lib \
+ $(PDNTLDIR)\uuid.lib \
+ $(PDNTLDIR)\ws2_32.lib \
+ $(PDNTLDIR)\pthreadVC.lib \
+ lib\ogg_static.lib \
+ lib\vorbis_static.lib \
+ lib\vorbisenc_static.lib \
+ c:\pd\bin\pd.lib
+
+
+EXTERNALS = oggamp~.obj oggcast~.obj oggread~.obj oggwrite~.obj
+
+.c.dll:
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c oggamp~\oggamp~.c
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c oggcast~\oggcast~.c
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c oggread~\oggread~.c
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c oggwrite~\oggwrite~.c
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c
+ link /dll /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libc.lib /export:$(CSYM)_setup $*.obj \
+ $(EXTERNALS) $(PDNTLIB)
+
+# ----------------------- Mac OS X -----------------------
+
+pd_darwin: $(NAME).pd_darwin
+
+.SUFFIXES: .pd_darwin
+
+DARWINCFLAGS = -DPD -DUNIX -DMACOSX -O3 \
+ -Wall -W -Wshadow -Wstrict-prototypes \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+# where is your m_pd.h ???
+DARWININCLUDE = -I../../src -I. -Iinclude
+
+DARWINEXTERNALS = oggamp~.o oggcast~.o oggread~.o oggwrite~.o
+
+.c.pd_darwin:
+ cc $(DARWINCFLAGS) $(DARWININCLUDE) -c oggamp~/oggamp~.c
+ cc $(DARWINCFLAGS) $(DARWININCLUDE) -c oggcast~/oggcast~.c
+ cc $(DARWINCFLAGS) $(DARWININCLUDE) -c oggread~/oggread~.c
+ cc $(DARWINCFLAGS) $(DARWININCLUDE) -c oggwrite~/oggwrite~.c
+ cc $(DARWINCFLAGS) $(DARWININCLUDE) -c $*.c
+ cc -bundle -undefined suppress -flat_namespace -o $*.pd_darwin $*.o $(DARWINEXTERNALS) \
+ -Llib -lvorbisfile -lvorbisenc -lvorbis -logg
+ rm -f $*.o ../$*.pd_darwin
+ ln -s $*/$*.pd_darwin ..
+
+# ----------------------- LINUX i386 -----------------------
+
+pd_linux: $(NAME).pd_linux
+
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -DPD -DUNIX -DICECAST -O2 -funroll-loops -fomit-frame-pointer \
+ -Wall -W -Wshadow -Wno-unused -Wno-parentheses -Wno-switch
+
+LINUXINCLUDE = -I../../src
+
+LINUXEXTERNALS = oggamp~.o oggcast~.o oggread~.o oggwrite~.o
+
+.c.pd_linux:
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -c oggamp~/oggamp~.c
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -c oggcast~/oggcast~.c
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -c oggread~/oggread~.c
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -c oggwrite~/oggwrite~.c
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -c $*.c
+ ld -export_dynamic -shared -o $*.pd_linux $*.o $(LINUXEXTERNALS) -lc \
+ -lm -L/usr/local/lib -lvorbisfile -lvorbisenc -lvorbis -logg
+ strip --strip-unneeded $*.pd_linux
+
+# ----------------------------------------------------------
+
+PDDIR=/usr/lib/pd
+
+install:
+ install -d $(PDDIR)/doc/5.reference/pdogg
+ cp help-*.pd ../../doc/5.reference/pdogg
+
+clean:
+ rm -f *.o *.pd_* so_locations
diff --git a/oggamp~/HISTORY b/oggamp~/HISTORY
new file mode 100644
index 0000000..e0cceda
--- /dev/null
+++ b/oggamp~/HISTORY
@@ -0,0 +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
+
+
diff --git a/oggamp~/codec.h b/oggamp~/codec.h
new file mode 100644
index 0000000..9365ea3
--- /dev/null
+++ b/oggamp~/codec.h
@@ -0,0 +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
+
diff --git a/oggamp~/help-oggamp~.pd b/oggamp~/help-oggamp~.pd
new file mode 100644
index 0000000..9f30cb8
--- /dev/null
+++ b/oggamp~/help-oggamp~.pd
@@ -0,0 +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;
diff --git a/oggamp~/oggamp~.c b/oggamp~/oggamp~.c
new file mode 100644
index 0000000..807a415
--- /dev/null
+++ b/oggamp~/oggamp~.c
@@ -0,0 +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"));
+} \ No newline at end of file
diff --git a/oggamp~/readme b/oggamp~/readme
new file mode 100644
index 0000000..4e7d8b7
--- /dev/null
+++ b/oggamp~/readme
@@ -0,0 +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/
+
+Please report any bugs to olaf.matthes@gmx.de! \ No newline at end of file
diff --git a/oggcast~/HISTORY b/oggcast~/HISTORY
new file mode 100644
index 0000000..ef27e13
--- /dev/null
+++ b/oggcast~/HISTORY
@@ -0,0 +1,65 @@
+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)
+
+v 0.1: first public release \ No newline at end of file
diff --git a/oggcast~/codec.h b/oggcast~/codec.h
new file mode 100644
index 0000000..9365ea3
--- /dev/null
+++ b/oggcast~/codec.h
@@ -0,0 +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
+
diff --git a/oggcast~/help-oggcast~.pd b/oggcast~/help-oggcast~.pd
new file mode 100644
index 0000000..6b45d35
--- /dev/null
+++ b/oggcast~/help-oggcast~.pd
@@ -0,0 +1,57 @@
+#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;
diff --git a/oggcast~/oggcast~.c b/oggcast~/oggcast~.c
new file mode 100644
index 0000000..67ea43a
--- /dev/null
+++ b/oggcast~/oggcast~.c
@@ -0,0 +1,1302 @@
+/* ------------------------- 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"));
+} \ No newline at end of file
diff --git a/oggcast~/readme b/oggcast~/readme
new file mode 100644
index 0000000..b3090ec
--- /dev/null
+++ b/oggcast~/readme
@@ -0,0 +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/
+
+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
new file mode 100644
index 0000000..901c47a
--- /dev/null
+++ b/oggcast~/vorbisenc.h
@@ -0,0 +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
+
+
diff --git a/oggread~/codec.h b/oggread~/codec.h
new file mode 100644
index 0000000..9365ea3
--- /dev/null
+++ b/oggread~/codec.h
@@ -0,0 +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
+
diff --git a/oggread~/help-oggread~.pd b/oggread~/help-oggread~.pd
new file mode 100644
index 0000000..a37e225
--- /dev/null
+++ b/oggread~/help-oggread~.pd
@@ -0,0 +1,34 @@
+#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;
diff --git a/oggread~/oggread~.c b/oggread~/oggread~.c
new file mode 100644
index 0000000..af07fb0
--- /dev/null
+++ b/oggread~/oggread~.c
@@ -0,0 +1,413 @@
+/* ------------------------- oggread~ ------------------------------------------ */
+/* */
+/* Tilde object to read and play back Ogg Vorbis files. */
+/* Written by Olaf Matthes (olaf.matthes@gmx.de) */
+/* Get source at http://www.akustische-kunst.de/puredata/ */
+/* */
+/* This library is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU Lesser General Public */
+/* License as published by the Free Software Foundation; either */
+/* version 2 of the License, or (at your option) any later version. */
+/* */
+/* This library is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* Lesser General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU Lesser General Public */
+/* License along with this library; if not, write to the */
+/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */
+/* Boston, MA 02111-1307, USA. */
+/* */
+/* Based on PureData by Miller Puckette and others. */
+/* Uses the Ogg Vorbis decoding library which can be found at */
+/* http://www.vorbis.com/ */
+/* */
+/* ---------------------------------------------------------------------------- */
+
+
+#include "m_imp.h"
+#include "g_canvas.h"
+#include "codec.h"
+#include "vorbis/vorbisfile.h"
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef UNIX
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <unistd.h>
+#define SOCKET_ERROR -1
+#else
+#include <io.h>
+#include <stdlib.h>
+#endif
+
+#ifdef NT
+#pragma warning( disable : 4244 )
+#pragma warning( disable : 4305 )
+#endif
+
+#define READ 4096 /* amount of data we pass on to decoder */
+#define MIN_AUDIO_INPUT READ*8 /* this is completely guessed! we just fill half the buffer */
+#define OUTPUT_BUFFER_SIZE 65536 /* audio output buffer: 64k */
+
+static char *oggread_version = "oggread~: ogg/vorbis file reader version 0.2b, written by Olaf Matthes";
+
+/* ------------------------ oggread~ ----------------------------- */
+
+static t_class *oggread_class;
+
+typedef struct _oggread
+{
+ t_object x_obj;
+ t_clock *x_clock;
+ /* ogg/vorbis related stuff */
+ OggVorbis_File x_ov;
+ ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */
+ ogg_sync_state x_oy; /* sync and verify incoming physical bitstream */
+ ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */
+ ogg_packet x_op; /* one raw packet of data for decode */
+ vorbis_info *x_vi; /* struct that stores all the static vorbis bitstream settings */
+ vorbis_comment x_vc; /* struct that stores all the user comments */
+ vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */
+ vorbis_block x_vb; /* local working space for packet->PCM decode */
+ t_int x_eos; /* end of stream */
+ char *x_buffer;/* buffer used to pass on data to ogg/vorbis */
+
+ t_float x_position; /* current playing position */
+ t_outlet *x_out_position; /* output to send them to */
+
+
+ t_outlet *x_connection;
+ t_int x_fd; /* the file handle */
+ FILE *x_file;
+ int x_current_section;
+ t_int x_blocksize; /* size of a dsp block */
+ t_int x_decoded; /* number of samples we got from decoder on last call */
+
+ t_float *x_outbuffer; /* buffer to store audio decoded data */
+ t_int x_outwriteposition;
+ t_int x_outreadposition;
+ t_int x_outunread;
+ t_int x_outbuffersize;
+
+ t_int x_samplerate; /* pd's samplerate, might differ from stream */
+ t_int x_stream; /* indicates if a stream gets output */
+} t_oggread;
+
+ /* output playing position */
+static void oggread_tick(t_oggread *x)
+{
+ outlet_float(x->x_out_position, x->x_position);
+ clock_delay(x->x_clock, 250);
+}
+
+static int oggread_decode_input(t_oggread *x)
+{
+ long ret; /* bytes per channel returned by decoder */
+ int i;
+ float **pcm;
+
+ x->x_vi = ov_info(&x->x_ov, x->x_current_section);
+
+ while(!x->x_eos)
+ {
+ ret = ov_read_float(&x->x_ov, &pcm, READ, &x->x_current_section);
+ if (ret == 0)
+ {
+ /* EOF */
+ x->x_eos = 1;
+ x->x_stream = 0;
+ clock_unset(x->x_clock);
+ post("oggread~: end of file detected, stopping");
+ }
+ else if (ret < 0)
+ {
+ /* error in the stream. Not a problem, just reporting it in
+ case we (the app) cares. In this case, we don't. */
+ }
+ else
+ {
+ /* we don't bother dealing with sample rate changes, etc, but
+ you'll have to */
+ long j;
+ for(j = 0; j < ret; j++)
+ {
+ for(i = 0; i < x->x_vi->channels; i++)
+ {
+ x->x_outbuffer[x->x_outwriteposition] = pcm[i][j];
+ x->x_outwriteposition = (x->x_outwriteposition + 1)%x->x_outbuffersize;
+ }
+ }
+ x->x_outunread += (t_int)ret * x->x_vi->channels;
+
+ }
+ break;
+ }
+ x->x_decoded = (t_int)ret * x->x_vi->channels; /* num. of samples we got from decoder */
+
+ x->x_position = (t_float)ov_time_tell(&x->x_ov);
+
+ /* exit decoding 'loop' here, we'll get called again by perform() */
+ return 1;
+}
+
+
+static t_int *oggread_perform(t_int *w)
+{
+ t_oggread *x = (t_oggread*) (w[1]);
+ t_float *out1 = (t_float *)(w[2]);
+ t_float *out2 = (t_float *)(w[3]);
+ int n = (int)(w[4]);
+ int ret;
+ int i = 0;
+
+ x->x_blocksize = n;
+
+ while( n-- )
+ { /* check that the stream provides enough data */
+ if((x->x_stream == 1) && (x->x_outunread > (x->x_blocksize * x->x_vi->channels)))
+ {
+ if(x->x_vi->channels != 1) /* play stereo */
+ {
+ *out1++=*(x->x_outbuffer+x->x_outreadposition);
+ x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize;
+ *out2++=*(x->x_outbuffer+x->x_outreadposition);
+ x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize;
+ x->x_outunread-=2;
+ }
+ else /* play mono on both sides */
+ {
+ *out1++=*(x->x_outbuffer+x->x_outreadposition);
+ *out2++=*(x->x_outbuffer+x->x_outreadposition);
+ x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize;
+ x->x_outunread--;
+ }
+ }
+ else /* silence in case of buffer underrun */
+ {
+ *out1++=0.0;
+ *out2++=0.0;
+ }
+ }
+
+ /* decode data whenever we used up some samples from outbuffer */
+ if((x->x_fd > 0) && (x->x_stream) /* only go when file is open and ready */
+ && (x->x_outunread < (MIN_AUDIO_INPUT - x->x_decoded))) /* we used up data from last decode */
+ {
+ // post("oggread~: decoding...");
+ if(oggread_decode_input(x) != 1)
+ {
+ post("oggread~: decoder error");
+ }
+ // else post("oggread~: decoder returned %d samples", x->x_decoded);
+ }
+ return (w+5);
+}
+
+static void oggread_dsp(t_oggread *x, t_signal **sp)
+{
+ dsp_add(oggread_perform, 4, x, sp[1]->s_vec, sp[2]->s_vec, sp[1]->s_n);
+}
+
+
+ /* start playing */
+static void oggread_start(t_oggread *x)
+{
+ if(x->x_fd > 0)
+ {
+ if(ov_time_seek(&x->x_ov, 0) < 0)
+ {
+ post("oggread~: could not rewind file to beginning");
+ }
+ post("oggread~: START");
+ x->x_eos = 0;
+ x->x_outreadposition = 0;
+ x->x_outwriteposition = 0;
+ x->x_outunread = 0;
+ x->x_position = 0;
+ clock_delay(x->x_clock, 0);
+ x->x_stream = 1;
+ }
+ else post("oggread~: no file open (ignored)");
+}
+
+ /* resume file reading */
+static void oggread_resume(t_oggread *x)
+{
+ if(x->x_fd > 0)
+ {
+ x->x_stream = 1;
+ clock_delay(x->x_clock, 0);
+ post("oggread~: RESUME");
+ }
+ else post("oggread~: encoder not initialised");
+}
+
+ /* seek in file */
+static void oggread_seek(t_oggread *x, t_floatarg f)
+{
+ if(x->x_fd > 0)
+ if(ov_time_seek(&x->x_ov, f) < 0)
+ {
+ post("oggread~: could not set playing position to %g seconds", f);
+ }
+ else post("oggread~: playing position set to %g seconds", f);
+}
+
+ /* stop playing */
+static void oggread_stop(t_oggread *x)
+{
+ if(x->x_stream)post("oggread~: STOP");
+ x->x_stream = 0;
+ clock_unset(x->x_clock);
+}
+
+static void oggread_float(t_oggread *x, t_floatarg f)
+{
+ if(f == 0) oggread_stop(x);
+ else oggread_start(x);
+}
+
+ /* open ogg/vorbis file */
+static void oggread_open(t_oggread *x, t_symbol *filename)
+{
+ int i;
+
+ x->x_stream = 0;
+ /* first close previous file */
+ if(x->x_fd > 0)
+ {
+ ov_clear(&x->x_ov);
+ post("oggread~: previous file closed");
+ }
+ /* open file for reading */
+#ifdef UNIX
+ if((x->x_file = fopen(filename->s_name, "r")) < 0)
+#else
+ if((x->x_file = fopen(filename->s_name, "rb")) < 0)
+#endif
+ {
+ post("oggread~: could not open file \"%s\"", filename->s_name);
+ x->x_eos = 1;
+ x->x_fd = -1;
+ }
+ else
+ {
+ x->x_stream = 0;
+ x->x_eos = 0;
+ x->x_fd = 1;
+ x->x_outreadposition = 0;
+ x->x_outwriteposition = 0;
+ x->x_outunread = 0;
+ post("oggread~: file \"%s\" opened", filename->s_name);
+ outlet_float( x->x_out_position, 0);
+
+ /* try to open as ogg vorbis file */
+ if(ov_open(x->x_file, &x->x_ov, NULL, -1) < 0)
+ { /* an error occured (no ogg vorbis file ?) */
+ post("oggread~: error: could not open \"%s\" as an OggVorbis file", filename->s_name);
+ ov_clear(&x->x_ov);
+ post("oggread~: file closed due to error");
+ }
+
+ /* print details about each logical bitstream in the input */
+ if(ov_seekable(&x->x_ov))
+ {
+ post("oggread~: input bitstream contained %ld logical bitstream section(s)", ov_streams(&x->x_ov));
+ post("oggread~: total bitstream playing time: %ld seconds", (long)ov_time_total(&x->x_ov,-1));
+ post("oggread~: encoded by: %s\n",ov_comment(&x->x_ov,-1)->vendor);
+ }
+ else
+ {
+ post("oggread~: file \"%s\" was not seekable"
+ "oggread~: first logical bitstream information:", filename->s_name);
+ }
+
+ for(i = 0; i < ov_streams(&x->x_ov); i++)
+ {
+ x->x_vi = ov_info(&x->x_ov,i);
+ post("\tlogical bitstream section %d information:",i+1);
+ post("\t\t%ldHz %d channels bitrate %ldkbps serial number=%ld",
+ x->x_vi->rate,x->x_vi->channels,ov_bitrate(&x->x_ov,i)/1000, ov_serialnumber(&x->x_ov,i));
+ post("\t\theader length: %ld bytes",(long)
+ (x->x_ov.dataoffsets[i] - x->x_ov.offsets[i]));
+ post("\t\tcompressed length: %ld bytes",(long)(ov_raw_total(&x->x_ov,i)));
+ post("\t\tplay time: %ld seconds\n",(long)ov_time_total(&x->x_ov,i));
+ }
+
+ }
+}
+
+
+static void oggread_free(t_oggread *x)
+{
+ if (x->x_fd > 0) {
+ post( "oggread~: closing file" );
+ ov_clear(&x->x_ov);
+ x->x_fd = -1;
+ }
+ freebytes(x->x_outbuffer, OUTPUT_BUFFER_SIZE*sizeof(t_float));
+ clock_free(x->x_clock);
+}
+
+static void *oggread_new(t_floatarg fdographics)
+{
+ t_oggread *x = NULL;
+
+ x = (t_oggread *)pd_new(oggread_class);
+ outlet_new(&x->x_obj, gensym("signal"));
+ outlet_new(&x->x_obj, gensym("signal"));
+ x->x_out_position = outlet_new(&x->x_obj, gensym("float"));
+ x->x_clock = clock_new(x, (t_method)oggread_tick);
+
+ x->x_fd = -1;
+ x->x_eos = 1;
+ x->x_stream = 0;
+ x->x_position = 0;
+ x->x_samplerate = sys_getsr();
+
+ x->x_outbuffersize = OUTPUT_BUFFER_SIZE;
+ x->x_outbuffer = (t_float*) getbytes(OUTPUT_BUFFER_SIZE*sizeof(t_float));
+
+ if(!x->x_outbuffer)
+ {
+ post( "oggread~: could not allocate buffer" );
+ return NULL;
+ }
+ memset(x->x_outbuffer, 0x0, OUTPUT_BUFFER_SIZE);
+
+ x->x_outreadposition = 0;
+ x->x_outwriteposition = 0;
+ x->x_outunread = 0;
+ x->x_decoded = 0;
+
+ post(oggread_version);
+
+ return (x);
+}
+
+
+void oggread_tilde_setup(void)
+{
+ oggread_class = class_new(gensym("oggread~"),
+ (t_newmethod) oggread_new, (t_method) oggread_free,
+ sizeof(t_oggread), 0, A_DEFFLOAT, A_NULL);
+ class_addfloat(oggread_class, (t_method)oggread_float);
+ class_addmethod(oggread_class, nullfn, gensym("signal"), 0);
+ class_addmethod(oggread_class, (t_method)oggread_dsp, gensym("dsp"), 0);
+ class_addmethod(oggread_class, (t_method)oggread_open, gensym("open"), A_SYMBOL, 0);
+ class_addmethod(oggread_class, (t_method)oggread_start, gensym("start"), 0);
+ class_addmethod(oggread_class, (t_method)oggread_resume, gensym("resume"), 0);
+ class_addmethod(oggread_class, (t_method)oggread_seek, gensym("seek"), A_DEFFLOAT, 0);
+ class_addmethod(oggread_class, (t_method)oggread_stop, gensym("stop"), 0);
+ class_sethelpsymbol(oggread_class, gensym("help-oggread~.pd"));
+}
diff --git a/oggread~/readme b/oggread~/readme
new file mode 100644
index 0000000..0f61fc8
--- /dev/null
+++ b/oggread~/readme
@@ -0,0 +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/
+
+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
new file mode 100644
index 0000000..9365ea3
--- /dev/null
+++ b/oggwrite~/codec.h
@@ -0,0 +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
+
diff --git a/oggwrite~/help-oggwrite~.pd b/oggwrite~/help-oggwrite~.pd
new file mode 100644
index 0000000..86bbd91
--- /dev/null
+++ b/oggwrite~/help-oggwrite~.pd
@@ -0,0 +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;
diff --git a/oggwrite~/oggwrite~.c b/oggwrite~/oggwrite~.c
new file mode 100644
index 0000000..1648168
--- /dev/null
+++ b/oggwrite~/oggwrite~.c
@@ -0,0 +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);
+}
diff --git a/oggwrite~/readme b/oggwrite~/readme
new file mode 100644
index 0000000..4945d0d
--- /dev/null
+++ b/oggwrite~/readme
@@ -0,0 +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/
+
+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
new file mode 100644
index 0000000..901c47a
--- /dev/null
+++ b/oggwrite~/vorbisenc.h
@@ -0,0 +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
+
+
diff --git a/pdogg.c b/pdogg.c
new file mode 100644
index 0000000..47421b1
--- /dev/null
+++ b/pdogg.c
@@ -0,0 +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");
+}
diff --git a/readme b/readme
new file mode 100644
index 0000000..03a6f59
--- /dev/null
+++ b/readme
@@ -0,0 +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/
+
+Please report any bugs to olaf.matthes@gmx.de! \ No newline at end of file