aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans-Christoph Steiner <eighthave@users.sourceforge.net>2003-05-29 23:57:38 +0000
committerHans-Christoph Steiner <eighthave@users.sourceforge.net>2003-05-29 23:57:38 +0000
commitff6a28aa57fc980c333d45a137f5926db436723b (patch)
tree659e237a315eefe5378331ce63407ff7a16608a7
parent153e8d376f5c69efb9e5c00de0047cc1eba588aa (diff)
This commit was generated by cvs2svn to compensate for changes in r663,
which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/olafmatt/; revision=664
-rw-r--r--shoutcast~/HISTORY40
-rw-r--r--shoutcast~/shoutcast~.c1299
2 files changed, 1339 insertions, 0 deletions
diff --git a/shoutcast~/HISTORY b/shoutcast~/HISTORY
new file mode 100644
index 0000000..f5262cf
--- /dev/null
+++ b/shoutcast~/HISTORY
@@ -0,0 +1,40 @@
+version history of shoutcast~
+
+v0.3h (2003/05/29)
+- added 'lame_encoding_flush()' on disconnect
+- changed send() options to MSG_DONTWAIT|MSG_NOSIGNAL on Linux to avoid
+ calling Pd's signal handler on errors
+
+v0.3g (2003/02/09)
+- changed the way comment tags can be entered, now accepts spaces
+- added printout for lame settings
+- fixed a bug that made it impossible to connect to a port different
+ than 8000
+
+v0.3f (2003/01/17)
+- fixed a bug that made shoutcast~ or Pd crash when server crashed
+ or network connection was interrupted
+- corrected the 'public flag' in the server login routine
+
+v0.3e (2002/11/04)
+- added method to set stream name and the like using messages
+
+v0.3d (2002/09/07)
+- BUGFIX: first creation argument (inchannels) now works correctly
+- updated Lame library to version 3.92
+
+v0.3c (2002/08/30)
+- BUGFIX: resampling works now
+- BUGFIX: ringbuffer gets only filled when we're streaming
+- binary release for OS X (10.1.5)
+
+v0.3b (2002/08/03)
+- first stable release with POSIX multithreading
+- archive function no longer supported (for savety reasons)
+
+v0.2 (2002/02/02)
+- added archive functionallity to write mp3 to disk
+- win only: use of libmp3lame.lib instead of lame.dll
+
+v0.1 (2001/11/??)
+- first release \ No newline at end of file
diff --git a/shoutcast~/shoutcast~.c b/shoutcast~/shoutcast~.c
new file mode 100644
index 0000000..957edd6
--- /dev/null
+++ b/shoutcast~/shoutcast~.c
@@ -0,0 +1,1299 @@
+/* ------------------------ shoutcast~ ---------------------------------------- */
+/* */
+/* Tilde object to send mp3-stream to shoutcast/icecast server. */
+/* Written by Olaf Matthes <olaf.matthes@gmx.de>. */
+/* Original version ported to Linux by Yves Degoyon */
+/* Get source at http://www.akustische-kunst.org/puredata/shout/ */
+/* */
+/* 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 LAME MPEG 1 Layer 3 encoding library which can be found at */
+/* http://www.mp3dev.org */
+/* */
+/* ---------------------------------------------------------------------------- */
+
+ /* Pd includes */
+#include "m_pd.h"
+#include "g_canvas.h"
+#include "lame/lame.h" /* lame encoder stuff */
+
+#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
+
+#ifdef __linux__ // 'real' linux only, not for OS X !
+#define SEND_OPT MSG_DONTWAIT|MSG_NOSIGNAL
+#else
+#define SEND_OPT 0
+#endif
+
+/************************* shoutcast~ object ******************************/
+
+/* Each instance of shoutcast~ 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 and audio output */
+
+#define DEFBUFPERCHAN 262144 /* audio output buffer by default: 256k */
+#define ENCODER_CHANNELS 2 /* we allways use 2 channels internally */
+#define MINBUFSIZE 65536 /* minimum size of ring buffer */
+#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 MAXDATARATE 320 /* maximum mp3 data rate is 320kbit/s */
+
+#define MY_MP3_MALLOC_IN_SIZE 65536 /* max size taken from lame readme */
+#define MY_MP3_MALLOC_OUT_SIZE 1.25*MY_MP3_MALLOC_IN_SIZE+7200 /* max size of encoded output */
+
+#define UPDATE_INTERVAL 250 /* time in milliseconds between updates of output values */
+
+
+static char *shoutcast_version = "shoutcast~: mp3 streamer version 0.3h, written by Olaf Matthes";
+
+static t_class *shoutcast_class;
+
+typedef struct _shoutcast
+{
+ 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) */
+
+ /* LAME stuff */
+ t_int x_lame; /* info about encoder status */
+ lame_global_flags *x_lgfp;
+ t_int x_lamechunk; /* chunk size for LAME encoder */
+ short *x_mp3inbuf; /* data to be sent to LAME */
+ char *x_mp3outbuf; /* data returned by LAME -> our mp3 stream */
+ t_int x_mp3mode; /* mode (mono, joint stereo, stereo, dual mono) */
+ t_int x_mp3quality; /* quality of encoding */
+ t_int x_vbr; /* quality of VBR encoding if not -1 */
+
+ t_int x_eos; /* end of stream */
+ t_float x_streamedbytes; /* number of bytes that have been output to server */
+ t_float x_oldstreamedbytes;
+
+ /* ringbuffer stuff */
+ t_float *x_buffer; /* data to be buffered (ringbuffer)*/
+ t_int x_bytesbuffered; /* number of unprocessed bytes in buffer */
+
+ /* mp3 stream format stuff */
+ t_int x_samplerate; /* samplerate of stream (default = getsr() ) */
+ t_int x_bitrate; /* bitrate of mp3 stream (CBR only) */
+ t_int x_channels; /* number of channels sent to the encoder */
+ /* this is allways 2 because of lame's software design */
+
+ /* 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_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.shoutcast.com */
+ t_int x_icecast; /* tells if we use a IceCast server (1) or SHOUTcast (0) */
+
+ 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 */
+
+ /* tread stuff */
+ pthread_mutex_t x_mutex;
+ pthread_cond_t x_requestcondition;
+ pthread_cond_t x_answercondition;
+ pthread_t x_childthread;
+} t_shoutcast;
+
+ /* check server for writeability */
+static int shoutcast_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("shoutcast~: can not write data to the server, quitting");
+ return -1;
+ }
+ if(FD_ISSET(sock, &exceptset))
+ {
+ post("shoutcast~: socket returned an error, quitting");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+ /* lame decoder setup */
+static int shoutcast_lame_init(t_shoutcast *x)
+{
+ int ret;
+ char *title = NULL;
+ time_t now;
+ int title_length;
+
+ x->x_lgfp = lame_init(); /* set default parameters for now */
+ post( "shoutcast~: using lame version %s", get_lame_version() );
+
+ /* setting lame parameters */
+ if(x->x_mp3mode!=3)
+ {
+ lame_set_num_channels( x->x_lgfp, 2);
+ }
+ else
+ {
+ lame_set_num_channels( x->x_lgfp, 1);
+ } /* maybe we're mono */
+ lame_set_in_samplerate( x->x_lgfp, sys_getsr() );
+ lame_set_out_samplerate( x->x_lgfp, x->x_samplerate );
+ if(x->x_vbr == -1) /* constand bitrate encoding (default) */
+ lame_set_brate( x->x_lgfp, x->x_bitrate );
+ else /* VBR encoding */
+ {
+ lame_set_VBR( x->x_lgfp, vbr_rh );
+ lame_set_VBR_q( x->x_lgfp, x->x_vbr);
+ }
+ lame_set_mode( x->x_lgfp, x->x_mp3mode );
+ lame_set_quality( x->x_lgfp, x->x_mp3quality );
+ lame_set_emphasis( x->x_lgfp, 0 );
+ lame_set_original( x->x_lgfp, 1 );
+ lame_set_copyright( x->x_lgfp, 0 ); /* viva free music societies !!! */
+ lame_set_disable_reservoir( x->x_lgfp, 1 );
+ lame_set_padding_type( x->x_lgfp, PAD_NO );
+ ret = lame_init_params( x->x_lgfp );
+ if( ret<0 )
+ {
+ error("shoutcast~: lame params initialization returned %d", ret);
+ }
+ else
+ {
+ x->x_lame = 1;
+ /* magic formula copied from windows dll for MPEG-I */
+ if(x->x_mp3mode!=3)
+ {
+ x->x_lamechunk = 1152*2;
+ }
+ else
+ {
+ x->x_lamechunk = 1152;
+ }
+ post( "shoutcast~: lame initialization done");
+ }
+ lame_init_bitstream(x->x_lgfp );
+
+ /* setting tag information */
+ id3tag_init(x->x_lgfp);
+ id3tag_v1_only(x->x_lgfp);
+ id3tag_space_v1(x->x_lgfp);
+ id3tag_set_artist(x->x_lgfp, "Pd Session");
+ now=time(NULL);
+ title_length = 256;
+ if ( title != NULL )
+ {
+ freebytes(title, title_length );
+ }
+ title=(char*)getbytes(title_length);
+ sprintf(title, "Started at %s", ctime(&now));
+ id3tag_set_title(x->x_lgfp, title);
+ x->x_streamedbytes = x->x_oldstreamedbytes = 0;
+ return (0);
+}
+
+ /* deinit the lame decoder */
+static void shoutcast_lame_deinit(t_shoutcast *x)
+{
+ int err, ret;
+ if(x->x_lame >= 0)
+ {
+ ret = lame_encode_flush( x->x_lgfp, x->x_mp3outbuf, MY_MP3_MALLOC_OUT_SIZE ); /* flush lame buffers */
+ err = send(x->x_fd, x->x_mp3outbuf, ret, SEND_OPT);
+ if(err < 0)
+ {
+ error("shoutcast~: could not send flushed data to server (%d)", err);
+ }
+ lame_close( x->x_lgfp ); /* close lame */
+ }
+ x->x_lame = 0;
+}
+
+ /* encode lame and stream new data */
+static int shoutcast_encode(t_shoutcast *x, float *buf, int channels, int fifosize, int fd)
+{
+ unsigned short i, ch;
+ int err = 0;
+ int n;
+ int mp3size; /* number of mp3 bytes returned from encoder */
+
+ /* read from buffer */
+ for(n = 0; n < x->x_lamechunk; n++) /* fill encode buffer */
+ {
+ x->x_mp3inbuf[n] = (short) (32767.0 * (*buf++)); /* convert to PCM */
+ }
+
+ /* encode mp3 data */
+#ifndef UNIX /* the clear WinNT solution: */
+ mp3size = lame_encode_buffer_interleaved(x->x_lgfp, x->x_mp3inbuf,
+ x->x_lamechunk / ENCODER_CHANNELS, x->x_mp3outbuf, MY_MP3_MALLOC_OUT_SIZE);
+#else /* is this really different under Linux ???? */
+ mp3size = lame_encode_buffer_interleaved(x->x_lgfp, x->x_mp3inbuf,
+ x->x_lamechunk / lame_get_num_channels(x->x_lgfp),
+ x->x_mp3outbuf, MY_MP3_MALLOC_OUT_SIZE);
+#endif
+ // post( "shoutcast~: encoding returned %d frames", mp3size );
+
+ /* check result */
+ if(mp3size < 0)
+ {
+ lame_close( x->x_lgfp );
+ error("shoutcast~: lame_encode_buffer_interleaved failed (%d)",mp3size);
+ x->x_lame = -1;
+ return(-1);
+ }
+ /* stream to server */
+ err = send(fd, x->x_mp3outbuf, mp3size, SEND_OPT);
+ if(err < 0)
+ {
+ error("shoutcast~: could not send encoded data to server (%d)", err);
+ lame_close( x->x_lgfp );
+ x->x_lame = -1;
+ return(-1);
+ }
+ if((err > 0)&&(err != mp3size))error("shoutcast~: %d bytes skipped", mp3size - err);
+
+ return (mp3size); /* return number of bytes we encoded */
+}
+
+ /* connect to icecast2 server */
+static int shoutcast_child_connect(char *hostname, char *mountpoint, t_int portno,
+ char *passwd, char *bcname, char *bcurl,
+ char *bcgenre, t_int bcpublic, t_int bitrate, t_int icecast)
+{
+ 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;
+ int sockfd;
+ int ret;
+
+ if(icecast == 0)portno++; /* use SHOUTcast, portno is one higher */
+
+ sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sockfd < 0)
+ {
+ error("shoutcast~: 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("shoutcast~: 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("shoutcast~: connecting to port %d", portno);
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
+ {
+ error("shoutcast~: 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("shoutcast~: can not read from socket");
+ sys_closesocket(sockfd);
+ return(-1);
+ }
+ /* ret = select(sockfd + 1, NULL, &fdset, NULL, &tv);
+ if(ret < 0)
+ {
+ error("shoutcast~: can not write to socket");
+ sys_closesocket(sockfd);
+ return;
+ } */
+
+ if(icecast == 0) /* SHOUTCAST */
+ {
+ /* now try to log in at SHOUTcast server */
+ post("shoutcast~: logging in to SHOUTcast server...");
+ /* first line is the passwd */
+ buf = passwd;
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = "\n";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ /* header for SHOUTcast server */
+ buf = "icy-name:"; /* name of broadcast */
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = bcname;
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = "\nicy-url:"; /* URL of broadcast */
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = bcurl;
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = "\nicy-genre:"; /* genre of broadcast */
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = bcgenre;
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = "\nicy-br:";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ if(sprintf(resp, "%d", bitrate) == -1) /* convert int to a string */
+ {
+ error("shoutcast~: wrong bitrate");
+ }
+ send(sockfd, resp, strlen(resp), SEND_OPT);
+ buf = "\nicy-pub:";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ if(bcpublic==0) /* set the public flag for broadcast */
+ {
+ buf = "0";
+ }
+ else
+ {
+ buf ="1";
+ }
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = "\n\n";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ }
+ else /* IceCast */
+ {
+ /* now try to log in at IceCast server */
+ post("shoutcast~: logging in to IceCast server...");
+ /* send the request, a string like: "SOURCE <password> /<mountpoint>\n" */
+ buf = "SOURCE ";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = passwd;
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = " /";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = mountpoint;
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ /* send the x-audiocast headers */
+ buf = "\nx-audiocast-bitrate: ";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ if(sprintf(resp, "%d", bitrate) == -1) /* convert int to a string */
+ {
+ error("shoutcast~: wrong bitrate");
+ }
+ send(sockfd, resp, strlen(resp), SEND_OPT);
+ buf = "\nx-audiocast-public: ";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ if(bcpublic==0) /* set the public flag for broadcast */
+ {
+ buf = "0";
+ }
+ else
+ {
+ buf ="1";
+ }
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = "\nx-audiocast-name: ";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = bcname;
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = "\nx-audiocast-url: ";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = bcurl;
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = "\nx-audiocast-genre: ";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = bcgenre;
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ buf = "\n\n";
+ send(sockfd, buf, strlen(buf), SEND_OPT);
+ /* end login for IceCast */
+ }
+
+ /* read the anticipated response: "OK" */
+ len = recv(sockfd, resp, STRBUF_SIZE, 0);
+ if ( len < 2 || resp[0] != 'O' || resp[1] != 'K' )
+ {
+ post("shoutcast~: login failed!");
+ sys_closesocket(sockfd);
+ return(-1);
+ }
+
+ /* check if we can write to server */
+ if(shoutcast_checkserver(sockfd)!= 0)
+ {
+ post("shoutcast~: error: server refused to receive data");
+ sys_closesocket(sockfd);
+ return (-1);
+ }
+
+ post("shoutcast~: logged in to http://%s:%d/%s", hp->h_name, portno, mountpoint);
+
+ return (sockfd);
+}
+
+
+
+static void shoutcast_child_disconnect(t_int fd)
+{
+ sys_closesocket(fd);
+ post("shoutcast~: 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 shoutcast_cond_wait pthread_cond_wait
+#define shoutcast_cond_signal pthread_cond_signal
+#else
+#include <sys/time.h> /* debugging version... */
+#include <sys/types.h>
+static void shoutcast_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 shoutcast_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 shoutcast_cond_wait(a,b) shoutcast_fakewait(b)
+#define shoutcast_cond_signal(a)
+#endif
+
+static void *shoutcast_child_main(void *zz)
+{
+ t_shoutcast *x = zz;
+ 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");
+ shoutcast_cond_signal(&x->x_answercondition);
+ shoutcast_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 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_bitrate;
+ t_int servertype = x->x_icecast;
+ /* 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 = shoutcast_child_connect(hostname, mountpoint, portno, passwd, bcname,
+ bcurl, bcgenre, bcpublic, br_nom, servertype);
+ pthread_mutex_lock(&x->x_mutex);
+ pute("5\n");
+ /* copy back into the instance structure. */
+ x->x_connectstate = 1;
+ clock_delay(x->x_clock_connect, 0);
+ x->x_fd = fd;
+ if (fd < 0)
+ {
+ x->x_connecterror = fd;
+ x->x_connectstate = 0;
+ clock_delay(x->x_clock_connect, 0);
+ pute("connect failed\n");
+ goto lost;
+ }
+ else
+ {
+ x->x_streamedbytes = 0;
+ clock_delay(x->x_clock_pages, 0);
+ /* initialise the encoder */
+ if(shoutcast_lame_init(x) == 0)
+ {
+ post("shoutcast~: lame encoder initialised");
+ x->x_state = STATE_STREAM;
+ }
+ else
+ {
+ post("shoutcast~: could not init encoder");
+ shoutcast_child_disconnect(fd);
+ post("shoutcast~: connection closed due to initialisation error");
+ x->x_fd = -1;
+ x->x_connectstate = 0;
+ clock_delay(x->x_clock_connect, 0);
+ pute("shoutcast~: 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 * x->x_lamechunk));
+ /* 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 x->x_lamechunk bytes in the
+ buffer */
+ if (x->x_fifohead < x->x_fifotail ||
+ x->x_fifohead >= x->x_fifotail + x->x_lamechunk
+ || (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 = shoutcast_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("shoutcast~: closing due to error...");
+ goto lost;
+ }
+ else
+ {
+ x->x_fifotail += x->x_lamechunk;
+ x->x_streamedbytes += 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");
+ shoutcast_cond_signal(&x->x_answercondition);
+ pute("signalled\n");
+ shoutcast_cond_wait(&x->x_requestcondition,
+ &x->x_mutex);
+ pute("7a done\n");
+ continue;
+ }
+ /* signal parent in case it's waiting for data */
+ shoutcast_cond_signal(&x->x_answercondition);
+ }
+ }
+
+ /* reinit encoder (settings have changed) */
+ else if (x->x_requestcode == REQUEST_REINIT)
+ {
+ pthread_mutex_unlock(&x->x_mutex);
+ shoutcast_lame_deinit(x);
+ shoutcast_lame_init(x);
+ pthread_mutex_lock(&x->x_mutex);
+ post("shoutcast~: lame encoder reinitialised");
+ x->x_state = STATE_STREAM;
+ if (x->x_requestcode == REQUEST_REINIT)
+ x->x_requestcode = REQUEST_CONNECT;
+ shoutcast_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);
+ shoutcast_lame_deinit(x);
+ shoutcast_child_disconnect(fd);
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_fd = -1;
+ }
+ x->x_requestcode = REQUEST_NOTHING;
+ x->x_connectstate = 0;
+ clock_delay(x->x_clock_connect, 0);
+ shoutcast_cond_signal(&x->x_answercondition);
+ // shoutcast_cond_signal(&x->x_requestcondition);
+ // break;
+ }
+ // 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);
+ shoutcast_lame_deinit(x);
+ shoutcast_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;
+ shoutcast_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 shoutcast_tick_connect(t_shoutcast *x)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ outlet_float(x->x_connection, x->x_connectstate);
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void shoutcast_tick_pages(t_shoutcast *x)
+{
+ /* output kB we sent to server */
+ t_float bytes;
+ pthread_mutex_lock(&x->x_mutex);
+ bytes = x->x_streamedbytes;
+ pthread_mutex_unlock(&x->x_mutex);
+ outlet_float(x->x_outpages, (t_int)(bytes / 1024));
+ x->x_oldstreamedbytes = bytes;
+ clock_delay(x->x_clock_pages, UPDATE_INTERVAL);
+}
+
+static void *shoutcast_new(t_floatarg fnchannels, t_floatarg fbufsize)
+{
+ t_shoutcast *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_shoutcast *)pd_new(shoutcast_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)shoutcast_tick_connect);
+ x->x_clock_pages = clock_new(x, (t_method)shoutcast_tick_pages);
+
+ x->x_mp3inbuf = getbytes(MY_MP3_MALLOC_IN_SIZE*sizeof(short)); /* buffer for encoder input */
+ x->x_mp3outbuf = getbytes(MY_MP3_MALLOC_OUT_SIZE*sizeof(char)); /* our mp3 stream */
+
+ 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 */
+ 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_fd = -1;
+ x->x_vbr = -1; /* don't use the vbr setting by default */
+ x->x_passwd = "letmein";
+ x->x_samplerate = sys_getsr(); /* default to Pd's sampling rate */
+ x->x_mp3quality = 5; /* quality 5 - medium CPU usage */
+ x->x_channels = ENCODER_CHANNELS;
+ x->x_lamechunk = 2304; /* the mp3 chunk size for stereo */
+ x->x_bitrate = 128;
+ x->x_streamedbytes = x->x_oldstreamedbytes = 0;
+ x->x_bcname = "mp3 stream emitted from Pure Data";
+ x->x_bcurl = "http://www.akustische-kunst.org/";
+ x->x_bcgenre = "experimental";
+ x->x_bcpublic = 1;
+ x->x_mountpoint = "puredata.mp3";
+ x->x_hostname = "localhost";
+ x->x_port = 8000;
+
+ post(shoutcast_version);
+ post("shoutcast~: set buffer to %dk bytes", bufsize / 1024);
+ post("shoutcast~: encoding %d channels @ %d Hz", x->x_channels, x->x_samplerate);
+ if(x->x_ninlets > ENCODER_CHANNELS)
+ {
+ post("shoutcast~: a maximum of 2 channels is supported");
+ x->x_ninlets = 2;
+ }
+
+ clock_delay(x->x_clock_pages, 0);
+ pthread_create(&x->x_childthread, 0, shoutcast_child_main, x);
+ return (x);
+}
+
+static t_int *shoutcast_perform(t_int *w)
+{
+ t_shoutcast *x = (t_shoutcast *)(w[1]);
+ int vecsize = x->x_vecsize, ninlets = x->x_ninlets, channels = x->x_channels, i, j, k;
+ float *sp = x->x_buf;
+ pthread_mutex_lock(&x->x_mutex);
+ if (x->x_state != STATE_IDLE) /* don't fill buffers in case everything's turned off */
+ {
+ int wantbytes;
+
+
+ /* get 'wantbytes' bytes from input */
+ wantbytes = channels * vecsize; /* we need vecsize bytes per channel */
+
+ /* check if there is enough space in buffer to write samples */
+ while((x->x_fifotail > x->x_fifohead &&
+ x->x_fifotail < x->x_fifohead + wantbytes + 1) && x->x_fd > 0)
+ {
+ pute("wait...\n");
+ shoutcast_cond_signal(&x->x_requestcondition);
+ shoutcast_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++)
+ {
+ for(i = 0; i < channels; i++)
+ {
+ *sp++ = x->x_outvec[i][j];
+ }
+ }
+ }
+ else if(channels > ninlets) /* convert mono -> stereo */
+ {
+ for(j = 0; j < vecsize; j++)
+ {
+ for(i = 0; i < ninlets; i++)
+ {
+ for(k = 0; k < channels; k++)
+ *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)
+ {
+ shoutcast_cond_signal(&x->x_requestcondition);
+ x->x_sigcountdown = x->x_sigperiod;
+ }
+ }
+
+ pthread_mutex_unlock(&x->x_mutex);
+
+ return (w+2);
+}
+
+
+static void shoutcast_disconnect(t_shoutcast *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;
+ shoutcast_cond_signal(&x->x_requestcondition);
+ }
+ else post("shoutcast~: not connected");
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+
+ /* connect method. Called as:
+ connect <hostname or IP> <mountpoint> <portnumber>
+ */
+
+static void shoutcast_connect(t_shoutcast *x, t_symbol *s, int argc, t_atom *argv)
+{
+ t_symbol *hostsym, *mountsym;
+ t_float portno;
+ if(argc > 0)hostsym = atom_getsymbolarg(0, argc, argv);
+ if(argc > 1)mountsym = atom_getsymbolarg(1, argc, argv);
+ if(argc > 2)portno = atom_getfloatarg(2, argc, argv);
+ pthread_mutex_lock(&x->x_mutex);
+ if(x->x_fd >= 0)
+ {
+ post("shoutcast~: already connected");
+ }
+ else
+ {
+ x->x_requestcode = REQUEST_CONNECT;
+ if(argc >= 1)x->x_hostname = hostsym->s_name;
+ if(argc >= 2)x->x_mountpoint = mountsym->s_name;
+ if(argc >= 3)x->x_port = portno;
+
+ x->x_fifotail = 0;
+ x->x_fifohead = 0;
+
+ x->x_connecterror = 0;
+ x->x_streamedbytes = x->x_oldstreamedbytes = 0;
+ x->x_state = STATE_STARTUP;
+ shoutcast_cond_signal(&x->x_requestcondition);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void shoutcast_float(t_shoutcast *x, t_floatarg f)
+{
+ if (f != 0)
+ {
+ pthread_mutex_lock(&x->x_mutex);
+ if(x->x_fd >= 0)
+ {
+ post("shoutcast~: already connected");
+ }
+ else
+ {
+ x->x_requestcode = REQUEST_CONNECT;
+
+ x->x_fifotail = 0;
+ x->x_fifohead = 0;
+
+ x->x_connecterror = 0;
+ x->x_state = STATE_STARTUP;
+ shoutcast_cond_signal(&x->x_requestcondition);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ }
+ else shoutcast_disconnect(x);
+}
+
+static void shoutcast_dsp(t_shoutcast *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(shoutcast_perform, 1, x);
+}
+
+ /* set password for shoutcast server */
+static void shoutcast_password(t_shoutcast *x, t_symbol *password)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_passwd = password->s_name;
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+ /* set broadcast name */
+static void shoutcast_bcname(t_shoutcast *x, t_symbol *s, int ac, t_atom* av)
+{
+ t_binbuf *b = binbuf_new();
+ char* text;
+ int length;
+ binbuf_add(b, ac, av);
+ binbuf_gettext(b, &text, &length);
+ // text[length] = '\0';
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_bcname = text;
+ post("shoutcast~: set broadcast name to:\n %s", x->x_bcname);
+ if(x->x_state == STATE_STREAM)
+ post("shoutcast~: reconnect to make changes take effect!");
+ pthread_mutex_unlock(&x->x_mutex);
+ binbuf_free(b);
+}
+
+ /* set broadcast genre */
+static void shoutcast_bcgenre(t_shoutcast *x, t_symbol *s, int ac, t_atom* av)
+{
+ t_binbuf *b = binbuf_new();
+ char* text;
+ int length;
+ binbuf_add(b, ac, av);
+ binbuf_gettext(b, &text, &length);
+ // text[length] = '\0';
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_bcgenre = text;
+ post("shoutcast~: set broadcast genre to:\n %s", x->x_bcgenre);
+ if(x->x_state == STATE_STREAM)
+ post("shoutcast~: reconnect to make changes take effect!");
+ pthread_mutex_unlock(&x->x_mutex);
+ binbuf_free(b);
+}
+
+ /* set broadcast URL */
+static void shoutcast_bcurl(t_shoutcast *x, t_symbol *s, int ac, t_atom* av)
+{
+ t_binbuf *b = binbuf_new();
+ char* text;
+ int length;
+ binbuf_add(b, ac, av);
+ binbuf_gettext(b, &text, &length);
+ // text[length] = '\0';
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_bcurl = text;
+ post("shoutcast~: set broadcast URL to:\n %s", x->x_bcurl);
+ if(x->x_state == STATE_STREAM)
+ post("shoutcast~: reconnect to make changes take effect!");
+ pthread_mutex_unlock(&x->x_mutex);
+ binbuf_free(b);
+}
+
+ /* set broadcast public */
+static void shoutcast_bcpublic(t_shoutcast *x, t_floatarg fp)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_bcpublic = (t_int)fp;
+ if(x->x_bcpublic == 0)
+ post("shoutcast~: set broadcast to not public");
+ else
+ post("shoutcast~: set broadcast to public");
+ if(x->x_state == STATE_STREAM)
+ post("shoutcast~: reconnect to make changes take effect!");
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void shoutcast_icecast(t_shoutcast *x)
+{
+ x->x_icecast = 1;
+ post("shoutcast~: set server type to IceCast");
+}
+
+ /* we use SHOUTcast server (default) */
+static void shoutcast_shoutcast(t_shoutcast *x)
+{
+ x->x_icecast = 0;
+ post("shoutcast~: set server type to SHOUTcast");
+}
+
+ /* settings for mp3 encoding */
+static void shoutcast_mpeg(t_shoutcast *x, t_floatarg fsamplerate, t_floatarg fbitrate,
+ t_floatarg fmode, t_floatarg fquality)
+{
+ char *mode[4] = { "stereo", "joint stereo", "dual channel", "mono"};
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_samplerate = fsamplerate;
+ if(fbitrate > MAXDATARATE)
+ {
+ fbitrate = MAXDATARATE;
+ }
+ x->x_bitrate = (t_int)fbitrate;
+ x->x_mp3mode = (t_int)fmode;
+ x->x_vbr = -1;
+ x->x_channels = ENCODER_CHANNELS;
+ x->x_mp3quality = (t_int)fquality;
+ post("shoutcast~: mp3 stream: CBR, %dHz, %dkbit/s, %s (mode %d), quality %d",
+ x->x_samplerate, x->x_bitrate, mode[x->x_mp3mode], x->x_mp3mode, x->x_mp3quality);
+ if(x->x_fd>=0)post("shoutcast~: reconnect to make changes take effect! ");
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+ /* settings for vbr mp3 encoding */
+static void shoutcast_vbr(t_shoutcast *x, t_floatarg fsamplerate, t_floatarg fmode, t_floatarg fquality)
+{
+ char *mode[4] = { "stereo", "joint stereo", "dual channel", "mono"};
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_samplerate = fsamplerate;
+ x->x_bitrate = 0;
+ x->x_vbr = (t_int)fquality;
+ x->x_mp3mode = (t_int)fmode;
+ x->x_channels = 2;
+ post("shoutcast~: mp3 stream: VBR, %dHz, %s (mode %d), quality %d",
+ x->x_samplerate, mode[x->x_mp3mode], x->x_mp3mode, x->x_vbr);
+ if(x->x_fd>=0)post("shoutcast~: reconnect to make changes take effect! ");
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+ /* print settings to pd's console window */
+static void shoutcast_print(t_shoutcast *x)
+{
+ const char *mode[4] = { "stereo", "joint stereo", "dual channel", "mono" };
+ pthread_mutex_lock(&x->x_mutex);
+ post(shoutcast_version);
+ post(" LAME mp3 settings:\n"
+ " output sample rate: %d Hz", x->x_samplerate);
+ post(" mode: %s", mode[x->x_mp3mode]);
+ if(x->x_vbr == -1)
+ {
+ post(" bitrate: %d kB/s", x->x_bitrate);
+ post(" quality: %d", x->x_mp3quality);
+ }
+ else
+ {
+ post(" using variable bitrate encoding");
+ post(" quality: %d", x->x_vbr);
+ }
+ post(" mp3 chunk size: %d", x->x_lamechunk);
+ if(x->x_samplerate!=sys_getsr())
+ {
+ post(" resampling from %d to %d Hz!", (t_int)sys_getsr(), x->x_samplerate);
+ }
+ if(x->x_icecast == 0)
+ {
+ post(" server type: SHOUTcast");
+ if(x->x_fd > 0)
+ post(" server state: connected");
+ else
+ post(" server state: not connected");
+ post(" URL: http://%s:%d/", x->x_hostname, (t_int)x->x_port);
+ }
+ else
+ {
+ post(" server type: IceCast");
+ if(x->x_fd > 0)
+ post(" server state: connected");
+ else
+ post(" server state: not connected");
+ post(" stream URL: http://%s:%d/%s", x->x_hostname, (t_int)x->x_port, x->x_mountpoint);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+ /* print lame settings to pd's console window */
+static void shoutcast_printlame(t_shoutcast *x)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ if(x->x_state == STATE_STREAM)
+ lame_print_config(x->x_lgfp);
+ else
+ post("shoutcast~: can't print, lame is not running");
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void shoutcast_free(t_shoutcast *x)
+{
+ /* request QUIT and wait for acknowledge */
+ void *threadrtn;
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_requestcode = REQUEST_QUIT;
+ post("stopping shoutcast thread...");
+ shoutcast_cond_signal(&x->x_requestcondition);
+ while (x->x_requestcode != REQUEST_NOTHING)
+ {
+ post("signalling...");
+ shoutcast_cond_signal(&x->x_requestcondition);
+ shoutcast_cond_wait(&x->x_answercondition, &x->x_mutex);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ if (pthread_join(x->x_childthread, &threadrtn))
+ error("shoutcast_free: join failed");
+ post("... done.");
+
+ pthread_cond_destroy(&x->x_requestcondition);
+ pthread_cond_destroy(&x->x_answercondition);
+ pthread_mutex_destroy(&x->x_mutex);
+ freebytes(x->x_buf, x->x_bufsize*sizeof(t_float));
+ freebytes(x->x_mp3inbuf, MY_MP3_MALLOC_IN_SIZE*sizeof(short));
+ freebytes(x->x_mp3outbuf, MY_MP3_MALLOC_OUT_SIZE);
+ clock_free(x->x_clock_connect);
+ clock_free(x->x_clock_pages);
+}
+
+void shoutcast_tilde_setup(void)
+{
+ shoutcast_class = class_new(gensym("shoutcast~"), (t_newmethod)shoutcast_new,
+ (t_method)shoutcast_free, sizeof(t_shoutcast), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(shoutcast_class, t_shoutcast, x_f );
+ class_addfloat(shoutcast_class, (t_method)shoutcast_float);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_disconnect, gensym("disconnect"), 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_dsp, gensym("dsp"), 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_connect, gensym("connect"), A_GIMME, 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_print, gensym("print"), 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_printlame, gensym("printlame"), 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_icecast, gensym("icecast"), 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_shoutcast, gensym("shoutcast"), 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_password, gensym("passwd"), A_SYMBOL, 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_mpeg, gensym("mpeg"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_mpeg, gensym("cbr"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_vbr, gensym("vbr"), A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_bcname, gensym("name"), A_GIMME, 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_bcgenre, gensym("genre"), A_GIMME, 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_bcurl, gensym("url"), A_GIMME, 0);
+ class_addmethod(shoutcast_class, (t_method)shoutcast_bcpublic, gensym("public"), A_FLOAT, 0);
+ class_sethelpsymbol(shoutcast_class, gensym("help-shoutcast~.pd"));
+} \ No newline at end of file