/* -------------------------- mp3cast~ ---------------------------------------- */ /* */ /* Tilde object to send mp3-stream to shoutcast/icecast server. */ /* Written by Olaf Matthes (olaf.matthes@gmx.de). */ /* Get source at http://www.akustische-kunst.de/puredata/ */ /* */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* */ /* See file LICENSE for further informations on licensing terms. */ /* */ /* This program 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 General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; 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 (lame_enc.dll) which can */ /* be found at http://www.cdex.n3.net. */ /* */ /* ---------------------------------------------------------------------------- */ #ifdef _MSC_VER #pragma warning( disable : 4244 ) #pragma warning( disable : 4305 ) #endif #include "m_pd.h" /* standard pd stuff */ #include #include #include #include #include #include #include #include #ifndef __APPLE__ #include #endif #ifdef _WIN32 #include #include #include #include #else #include #include #include #include #include #include #define SOCKET_ERROR -1 #endif #include /* lame encoder stuff */ #include "mpg123.h" #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 #define MAXDATARATE 320 /* maximum mp3 data rate is 320kbit/s */ #define STRBUF_SIZE 1024 static char *mp3cast_version = "mp3cast~: mp3 streamer version 0.5, written by Yves Degoyon"; static char base64table[65] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/', }; static t_class *mp3cast_class; typedef struct _mp3cast { t_object x_obj; /* LAME stuff */ int x_lame; /* info about encoder status */ int x_lamechunk; /* chunk size for LAME encoder */ int x_mp3size; /* number of returned mp3 samples */ /* buffer stuff */ unsigned short x_inp; /* in position for buffer */ unsigned short x_outp; /* out position for buffer*/ short *x_mp3inbuf; /* data to be sent to LAME */ char *x_mp3outbuf; /* data returned by LAME -> our mp3 stream */ short *x_buffer; /* data to be buffered */ int x_bytesbuffered; /* number of unprocessed bytes in buffer */ int x_start; /* mp3 format stuff */ int x_samplerate; int x_bitrate; /* bitrate of mp3 stream */ int x_mp3mode; /* mode (mono, joint stereo, stereo, dual mono) */ int x_mp3quality; /* quality of encoding */ /* SHOUTcast server stuff */ int x_fd; /* info about connection status */ char* x_passwd; /* password for server */ int x_icecast; /* tells if we use a IceCast server or SHOUTcast */ /* special IceCast server stuff */ char* x_mountpoint; char* x_name; char* x_url; char* x_genre; char* x_description; int x_isPublic; t_float x_f; /* float needed for signal input */ lame_global_flags *lgfp; /* lame encoder configuration */ } t_mp3cast; /* encode PCM data to mp3 stream */ static void mp3cast_encode(t_mp3cast *x) { unsigned short i, wp; int err = -1; int n = x->x_lamechunk; #ifdef _WIN32 if(x->x_lamechunk < sizeof(x->x_mp3inbuf)) #else if(x->x_lamechunk < (int)sizeof(x->x_mp3inbuf)) #endif { error("not enough memory!"); return; } /* on start/reconnect set outpoint that it not interferes with inpoint */ if(x->x_start == -1) { post("mp3cast~: initialising buffers"); /* we try to keep 2.5 times the data the encoder needs in the buffer */ if(x->x_inp > (2 * x->x_lamechunk)) { x->x_outp = (short) x->x_inp - (2.5 * x->x_lamechunk); } else if(x->x_inp < (2 * x->x_lamechunk)) { x->x_outp = (short) MY_MP3_MALLOC_IN_SIZE - (2.5 * x->x_lamechunk); } x->x_start = 1; } if((unsigned short)(x->x_outp - x->x_inp) < x->x_lamechunk)error("mp3cast~: buffers overlap!"); i = MY_MP3_MALLOC_IN_SIZE - x->x_outp; /* read from buffer */ if(x->x_lamechunk <= i) { /* enough data until end of buffer */ for(n = 0; n < x->x_lamechunk; n++) /* fill encode buffer */ { x->x_mp3inbuf[n] = x->x_buffer[n + x->x_outp]; } x->x_outp += x->x_lamechunk; } else /* split data */ { for(wp = 0; wp < i; wp++) /* data at end of buffer */ { x->x_mp3inbuf[wp] = x->x_buffer[wp + x->x_outp]; } for(wp = i; wp < x->x_lamechunk; wp++) /* write rest of data at beginning of buffer */ { x->x_mp3inbuf[wp] = x->x_buffer[wp - i]; } x->x_outp = x->x_lamechunk - i; } /* encode mp3 data */ x->x_mp3size = lame_encode_buffer_interleaved(x->lgfp, x->x_mp3inbuf, x->x_lamechunk/lame_get_num_channels(x->lgfp), x->x_mp3outbuf, MY_MP3_MALLOC_OUT_SIZE); // post( "mp3cast~ : encoding returned %d frames", x->x_mp3size ); /* check result */ if(x->x_mp3size<0) { lame_close( x->lgfp ); error("mp3cast~: lame_encode_buffer_interleaved failed (%d)", x->x_mp3size); x->x_lame = -1; } } /* stream mp3 to SHOUTcast server */ static void mp3cast_stream(t_mp3cast *x) { int err = -1, i; /* error return code */ struct frame hframe; err = send(x->x_fd, x->x_mp3outbuf, x->x_mp3size, 0); if(err < 0) { error("mp3cast~: could not send encoded data to server (%d)", err); lame_close( x->lgfp ); x->x_lame = -1; #ifdef _WIN32 closesocket(x->x_fd); #else close(x->x_fd); #endif /* _WIN32 */ x->x_fd = -1; outlet_float(x->x_obj.ob_outlet, 0); } if((err > 0)&&(err != x->x_mp3size))error("mp3cast~: %d bytes skipped", x->x_mp3size - err); } /* buffer data as channel interleaved PCM */ static t_int *mp3cast_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_mp3cast *x = (t_mp3cast *)(w[3]); int n = (int)(w[4]); /* number of samples */ unsigned short i,wp; float in; /* copy the data into the buffer */ i = MY_MP3_MALLOC_IN_SIZE - x->x_inp; /* space left at the end of buffer */ n *= 2; /* two channels go into one buffer */ if( n <= i ) { /* the place between inp and MY_MP3_MALLOC_IN_SIZE */ /* is big enough to hold the data */ for(wp = 0; wp < n; wp++) { if(wp%2) { in = *(in2++); /* right channel / inlet */ } else { in = *(in1++); /* left channel / inlet */ } if (in > 1.0) { in = 1.0; } if (in < -1.0) { in = -1.0; } x->x_buffer[wp + x->x_inp] = (short) (32767.0 * in); } x->x_inp += n; /* n more samples written to buffer */ } else { /* the place between inp and MY_MP3_MALLOC_IN_SIZE is not */ /* big enough to hold the data */ /* writing will take place in two turns, one from */ /* x->x_inp -> MY_MP3_MALLOC_IN_SIZE, then from 0 on */ for(wp = 0; wp < i; wp++) /* fill up to end of buffer */ { if(wp%2) { in = *(in2++); } else { in = *(in1++); } if (in > 1.0) { in = 1.0; } if (in < -1.0) { in = -1.0; } x->x_buffer[wp + x->x_inp] = (short) (32767.0 * in); } for(wp = i; wp < n; wp++) /* write rest at start of buffer */ { if(wp%2) { in = *(in2++); } else { in = *(in1++); } if (in > 1.0) { in = 1.0; } if (in < -1.0) { in = -1.0; } x->x_buffer[wp - i] = (short) (32767.0 * in); } x->x_inp = n - i; /* new writeposition in buffer */ } if((x->x_fd >= 0)&&(x->x_lame >= 0)) { /* count buffered samples when things are running */ x->x_bytesbuffered += n; /* encode and send to server */ if(x->x_bytesbuffered > x->x_lamechunk) { mp3cast_encode(x); /* encode to mp3 */ mp3cast_stream(x); /* stream mp3 to server */ x->x_bytesbuffered -= x->x_lamechunk; } } else { x->x_start = -1; } return (w+5); } static void mp3cast_dsp(t_mp3cast *x, t_signal **sp) { dsp_add(mp3cast_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); } /* initialize the lame library */ static void mp3cast_tilde_lame_init(t_mp3cast *x) { int ret; x->lgfp = lame_init(); /* set default parameters for now */ #ifdef _WIN32 /* load lame_enc.dll library */ HINSTANCE dll; dll=LoadLibrary("lame_enc.dll"); if(!dll) { error("mp3cast~: error loading lame_enc.dll"); closesocket(x->x_fd); x->x_fd = -1; outlet_float(x->x_obj.ob_outlet, 0); post("mp3cast~: connection closed"); return; } #endif /* _WIN32 */ { const char *lameVersion = get_lame_version(); logpost(NULL, 4, "%s", "mp3cast~ : using lame version : %s", lameVersion ); } /* setting lame parameters */ lame_set_num_channels( x->lgfp, 2); lame_set_in_samplerate( x->lgfp, sys_getsr() ); lame_set_out_samplerate( x->lgfp, x->x_samplerate ); lame_set_brate( x->lgfp, x->x_bitrate ); lame_set_mode( x->lgfp, x->x_mp3mode ); lame_set_quality( x->lgfp, x->x_mp3quality ); lame_set_emphasis( x->lgfp, 1 ); lame_set_original( x->lgfp, 1 ); lame_set_copyright( x->lgfp, 1 ); /* viva free music societies !!! */ lame_set_disable_reservoir( x->lgfp, 0 ); //lame_set_padding_type( x->lgfp, PAD_NO ); /* deprecated function */ ret = lame_init_params( x->lgfp ); if ( ret<0 ) { post( "mp3cast~ : error : lame params initialization returned : %d", ret ); } else { x->x_lame=1; /* magic formula copied from windows dll for MPEG-I */ x->x_lamechunk = 2*1152; post( "mp3cast~ : lame initialization done. (%d)", x->x_lame ); } lame_init_bitstream( x->lgfp ); } char *mp3cast_base64_encode(char *data) { int len = strlen(data); char *out = t_getbytes(len*4/3 + 4); char *result = out; int chunk; while(len > 0) { chunk = (len >3)?3:len; *out++ = base64table[(*data & 0xFC)>>2]; *out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)]; switch(chunk) { case 3: *out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)]; *out++ = base64table[(*(data+2)) & 0x3F]; break; case 2: *out++ = base64table[((*(data+1) & 0x0F)<<2)]; *out++ = '='; break; case 1: *out++ = '='; *out++ = '='; break; } data += chunk; len -= chunk; } *out = 0; return result; } /* connect to server */ static void mp3cast_connect(t_mp3cast *x, t_symbol *hostname, t_floatarg fportno) { struct sockaddr_in server; struct hostent *hp; int portno = fportno; /* get port from message box */ /* information about this broadcast to be send to the server */ const char *name = x->x_name; /* name of broadcast */ const char *url = x->x_url; /* url of broadcast */ const char *genre = x->x_genre; /* genre of broadcast */ const char *description = x->x_description; /* description of broadcast */ const char *aim = "N/A"; /* aim of broadcast */ const char *irc = "#mp3cast"; /* ??? what's this ??? */ const char *icq = ""; /* icq id of broadcaster */ const char *mountpoint = x->x_mountpoint; /* mountpoint for IceCast server */ int isPublic = x->x_isPublic; /* don't publish broadcast on www.shoutcast.com */ /* 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(x->x_icecast == 0)portno++; /* use SHOUTcast, portno is one higher */ if (x->x_fd >= 0) { error("mp3cast~: already connected"); return; } sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sockfd < 0) { error("mp3cast~: internal error while attempting to open socket"); return; } /* connect socket using hostname provided in command line */ server.sin_family = AF_INET; hp = gethostbyname(hostname->s_name); if (hp == 0) { post("mp3cast~: bad host?"); close(sockfd); return; } 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("mp3cast~: connecting to port %d", portno); if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) { error("mp3cast~: connection failed!\n"); #ifdef _WIN32 closesocket(sockfd); #else close(sockfd); #endif /* _WIN32 */ return; } /* 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("mp3cast~: can not read from socket"); #ifdef _WIN32 closesocket(sockfd); #else close(sockfd); #endif /* _WIN32 */ return; } #ifndef _WIN32 ret = select(sockfd + 1, NULL, &fdset, NULL, &tv); if(ret < 0) { error("mp3cast~: can not write to socket"); close(sockfd); return; } #endif /* not _WIN32 */ if(x->x_icecast == 0) /* SHOUTCAST */ { /* now try to log in at SHOUTcast server */ post("mp3cast~: logging in to SHOUTcast server..."); /* first line is the passwd */ buf = x->x_passwd; send(sockfd, buf, strlen(buf), 0); buf = "\n"; send(sockfd, buf, strlen(buf), 0); /* header for SHOUTcast server */ buf = "icy-name:"; /* name of broadcast */ send(sockfd, buf, strlen(buf), 0); buf = name; send(sockfd, buf, strlen(buf), 0); buf = "\nicy-url:"; /* URL of broadcast */ send(sockfd, buf, strlen(buf), 0); buf = url; send(sockfd, buf, strlen(buf), 0); buf = "\nicy-genre:"; /* genre of broadcast */ send(sockfd, buf, strlen(buf), 0); buf = genre; send(sockfd, buf, strlen(buf), 0); buf = "\nicy-description:"; /* description of broadcast */ send(sockfd, buf, strlen(buf), 0); buf = description; send(sockfd, buf, strlen(buf), 0); buf = "\nicy-irc:"; send(sockfd, buf, strlen(buf), 0); buf = irc; send(sockfd, buf, strlen(buf), 0); buf = "\nicy-aim:"; send(sockfd, buf, strlen(buf), 0); buf = aim; send(sockfd, buf, strlen(buf), 0); buf = "\nicy-icq:"; send(sockfd, buf, strlen(buf), 0); buf = icq; send(sockfd, buf, strlen(buf), 0); buf = "\nicy-br:"; send(sockfd, buf, strlen(buf), 0); if(sprintf(resp, "%d", x->x_bitrate) == -1) /* convert int to a string */ { error("mp3cast~: wrong bitrate"); } send(sockfd, resp, strlen(resp), 0); buf = "\nicy-pub:"; send(sockfd, buf, strlen(buf), 0); if(isPublic==0) /* set the public flag for broadcast */ { buf = "no"; } else { buf ="yes"; } send(sockfd, buf, strlen(buf), 0); buf = "\n\n"; send(sockfd, buf, strlen(buf), 0); } else if ( x->x_icecast == 1 ) /* IceCast */ { /* now try to log in at IceCast server */ post("mp3cast~: logging in to IceCast server..."); /* send the request, a string like: * "SOURCE /\n" */ buf = "SOURCE "; send(sockfd, buf, strlen(buf), 0); buf = x->x_passwd; send(sockfd, buf, strlen(buf), 0); buf = " /"; send(sockfd, buf, strlen(buf), 0); buf = mountpoint; send(sockfd, buf, strlen(buf), 0); /* send the x-audiocast headers */ buf = "\nx-audiocast-bitrate: "; send(sockfd, buf, strlen(buf), 0); if(sprintf(resp, "%d", x->x_bitrate) == -1) /* convert int to a string */ { error("mp3cast~: wrong bitrate"); } send(sockfd, resp, strlen(resp), 0); buf = "\nx-audiocast-public: "; send(sockfd, buf, strlen(buf), 0); if(isPublic==0) /* set the public flag for broadcast */ { buf = "no"; } else { buf ="yes"; } send(sockfd, buf, strlen(buf), 0); buf = "\nx-audiocast-name: "; send(sockfd, buf, strlen(buf), 0); buf = name; send(sockfd, buf, strlen(buf), 0); buf = "\nx-audiocast-url: "; send(sockfd, buf, strlen(buf), 0); buf = url; send(sockfd, buf, strlen(buf), 0); buf = "\nx-audiocast-genre: "; send(sockfd, buf, strlen(buf), 0); buf = genre; send(sockfd, buf, strlen(buf), 0); buf = "\nx-audiocast-description: "; send(sockfd, buf, strlen(buf), 0); buf = description; send(sockfd, buf, strlen(buf), 0); buf = "\n\n"; send(sockfd, buf, strlen(buf), 0); /* end login for IceCast */ } else if ( x->x_icecast == 2 ) /* Icecast 2 */ { char *base64; /* buffer to hold 64bit encoded strings */ /* send the request, a string like: "SOURCE / HTTP/1.0\r\n" */ buf = "SOURCE /"; send(sockfd, buf, strlen(buf), 0); buf = x->x_mountpoint; send(sockfd, buf, strlen(buf), 0); buf = " HTTP/1.0\r\n"; send(sockfd, buf, strlen(buf), 0); /* send basic authorization as base64 encoded string */ sprintf(resp, "source:%s", x->x_passwd); len = strlen(resp); base64 = mp3cast_base64_encode(resp); sprintf(resp, "Authorization: Basic %s\r\n", base64); send(sockfd, resp, strlen(resp), 0); t_freebytes(base64, len*4/3 + 4); /* send application name */ buf = "User-Agent: mp3cast~"; send(sockfd, buf, strlen(buf), 0); /* send content type: mpeg */ buf = "\r\nContent-Type: audio/mpeg"; send(sockfd, buf, strlen(buf), 0); /* send the ice headers */ /* name */ buf = "\r\nice-name: "; send(sockfd, buf, strlen(buf), 0); buf = x->x_name; send(sockfd, buf, strlen(buf), 0); /* url */ buf = "\r\nice-url: "; send(sockfd, buf, strlen(buf), 0); buf = x->x_url; send(sockfd, buf, strlen(buf), 0); /* genre */ buf = "\r\nice-genre: "; send(sockfd, buf, strlen(buf), 0); buf = genre; send(sockfd, buf, strlen(buf), 0); /* public */ buf = "\r\nice-public: "; send(sockfd, buf, strlen(buf), 0); if(isPublic==0) { buf = "0"; } else { buf = "1"; } send(sockfd, buf, strlen(buf), 0); /* bitrate */ if(sprintf(resp, "\r\nice-audio-info: bitrate=%d", x->x_bitrate) == -1) { error("shoutcast~: could not create audio-info"); } send(sockfd, resp, strlen(resp), 0); /* description */ buf = "\r\nice-description: "; send(sockfd, buf, strlen(buf), 0); buf = description; send(sockfd, buf, strlen(buf), 0); /* end of header: write an empty line */ buf = "\r\n\r\n"; send(sockfd, buf, strlen(buf), 0); } /* read the anticipated response: "OK" */ len = recv(sockfd, resp, STRBUF_SIZE, 0); if ( strstr( resp, "OK" ) == NULL ) { post("mp3cast~: login failed!"); if ( len>0 ) post("mp3cast~: server answered : %s", resp); #ifdef _WIN32 closesocket(sockfd); #else close(sockfd); #endif /* _WIN32 */ return; } /* suck anything that the other side has to say */ // while (len = recv(sockfd, resp, STRBUF_SIZE,0)) // { // post("mp3cast~: server answered : %s", resp); // } x->x_fd = sockfd; outlet_float(x->x_obj.ob_outlet, 1); post("mp3cast~: logged in to %s", hp->h_name); mp3cast_tilde_lame_init(x); } /* close connection to SHOUTcast server */ static void mp3cast_disconnect(t_mp3cast *x) { int err = -1; if(x->x_lame >= 0) { /* ignore remaining bytes */ if ( x->x_mp3size = lame_encode_flush( x->lgfp, x->x_mp3outbuf, 0) < 0 ) { post( "mp3cast~ : warning : remaining encoded bytes" ); } lame_close( x->lgfp ); x->x_lame = -1; post("mp3cast~: encoder stream closed"); } if(x->x_fd >= 0) /* close socket */ { #ifdef _WIN32 closesocket(x->x_fd); #else close(x->x_fd); #endif /* _WIN32 */ x->x_fd = -1; outlet_float(x->x_obj.ob_outlet, 0); post("mp3cast~: connection closed"); } } /* set password for SHOUTcast server */ static void mp3cast_password(t_mp3cast *x, t_symbol *password) { post("mp3cast~ : setting password to %s", password->s_name ); x->x_passwd = password->s_name; } /* settings for mp3 encoding */ static void mp3cast_mpeg(t_mp3cast *x, t_floatarg fsamplerate, t_floatarg fbitrate, t_floatarg fmode, t_floatarg fquality) { x->x_samplerate = fsamplerate; if(fbitrate > MAXDATARATE) { fbitrate = MAXDATARATE; } x->x_bitrate = fbitrate; x->x_mp3mode = fmode; x->x_mp3quality = fquality; post("mp3cast~: setting mp3 stream to %dHz, %dkbit/s, mode %d, quality %d", x->x_samplerate, x->x_bitrate, x->x_mp3mode, x->x_mp3quality); if(x->x_fd>=0)post("mp3cast~ : reconnect to make changes take effect! "); } /* print settings */ static void mp3cast_print(t_mp3cast *x) { const char * buf = 0; logpost(NULL, 4, "%s", mp3cast_version); post(" LAME mp3 settings:\n" " output sample rate: %d Hz\n" " bitrate: %d kbit/s", x->x_samplerate, x->x_bitrate); switch(x->x_mp3mode) { case 0 : buf = "stereo"; break; case 1 : buf = "joint stereo"; break; case 2 : buf = "dual channel"; break; case 3 : buf = "mono"; break; } post(" mode: %s\n" " quality: %d", buf, x->x_mp3quality); post(" mp3 chunk size: %d", x->x_lamechunk); if(x->x_samplerate!=sys_getsr()) { post(" resampling from %d to %d Hz!", (int)sys_getsr(), x->x_samplerate); } if ( x->x_icecast == 0) { post(" server type is SHOUTcast"); } else if ( x->x_icecast == 1 ) { post(" server type is IceCast"); } else if ( x->x_icecast == 2 ) { post(" server type is IceCast 2"); } } static void mp3cast_icecast(t_mp3cast *x) { x->x_icecast = 1; post("mp3cast~: set server type to IceCast"); } static void mp3cast_icecast2(t_mp3cast *x) { x->x_icecast = 2; post("mp3cast~: set server type to IceCast 2"); } static void mp3cast_shoutcast(t_mp3cast *x) { x->x_icecast = 0; post("mp3cast~: set server type to SHOUTcast"); } /* set mountpoint for IceCast server */ static void mp3cast_mountpoint(t_mp3cast *x, t_symbol *mount) { x->x_mountpoint = mount->s_name; post("mp3cast~: mountpoint set to %s", x->x_mountpoint); } /* set namle for IceCast server */ static void mp3cast_name(t_mp3cast *x, t_symbol *name) { x->x_name = name->s_name; post("mp3cast~: name set to %s", x->x_name); } /* set url for IceCast server */ static void mp3cast_url(t_mp3cast *x, t_symbol *url) { x->x_url = url->s_name; post("mp3cast~: url set to %s", x->x_url); } /* set genre for IceCast server */ static void mp3cast_genre(t_mp3cast *x, t_symbol *genre) { x->x_genre = genre->s_name; post("mp3cast~: genre set to %s", x->x_genre); } /* set isPublic for IceCast server */ static void mp3cast_isPublic(t_mp3cast *x, t_floatarg isPublic) { x->x_isPublic = isPublic; char* isPublicStr; if(isPublic==0) { isPublicStr = "no"; } else { isPublicStr = "yes"; } post("mp3cast~: isPublic set to %s", isPublicStr); } /* set description for IceCast server */ static void mp3cast_description(t_mp3cast *x, t_symbol *description) { x->x_description = description->s_name; post("mp3cast~: description set to %s", x->x_description); } /* clean up */ static void mp3cast_free(t_mp3cast *x) { if(x->x_lame >= 0) lame_close( x->lgfp ); if(x->x_fd >= 0) #ifdef _WIN32 closesocket(x->x_fd); #else close(x->x_fd); #endif /* _WIN32 */ freebytes(x->x_mp3inbuf, MY_MP3_MALLOC_IN_SIZE*sizeof(short)); freebytes(x->x_mp3outbuf, MY_MP3_MALLOC_OUT_SIZE); freebytes(x->x_buffer, MY_MP3_MALLOC_IN_SIZE*sizeof(short)); } static void *mp3cast_new(void) { t_mp3cast *x = (t_mp3cast *)pd_new(mp3cast_class); inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); outlet_new(&x->x_obj, gensym("float")); x->x_fd = -1; x->x_lame = -1; x->x_passwd = "pd"; x->x_samplerate = sys_getsr(); x->x_bitrate = 224; x->x_mp3mode = 1; x->x_mp3quality = 5; 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 */ x->x_buffer = getbytes(MY_MP3_MALLOC_IN_SIZE*sizeof(short)); /* what we get from pd, converted to PCM */ if ((!x->x_buffer)||(!x->x_mp3inbuf)||(!x->x_mp3outbuf)) /* check buffers... */ { error("out of memory!"); } x->x_bytesbuffered = 0; x->x_inp = 0; x->x_outp = 0; x->lgfp = NULL; x->x_start = -1; x->x_icecast = 1; x->x_mountpoint = "puredata"; x->x_name = "puredata"; x->x_url = "http://puredata.info"; x->x_genre = "experimental sound"; x->x_isPublic = 1; x->x_description = "playing with my patches"; return(x); } void mp3cast_tilde_setup(void) { logpost(NULL, 4, "%s", mp3cast_version); mp3cast_class = class_new(gensym("mp3cast~"), (t_newmethod)mp3cast_new, (t_method)mp3cast_free, sizeof(t_mp3cast), 0, 0); CLASS_MAINSIGNALIN(mp3cast_class, t_mp3cast, x_f ); class_addmethod(mp3cast_class, (t_method)mp3cast_dsp, gensym("dsp"), 0); class_addmethod(mp3cast_class, (t_method)mp3cast_connect, gensym("connect"), A_SYMBOL, A_FLOAT, 0); class_addmethod(mp3cast_class, (t_method)mp3cast_disconnect, gensym("disconnect"), 0); class_addmethod(mp3cast_class, (t_method)mp3cast_password, gensym("passwd"), A_SYMBOL, 0); class_addmethod(mp3cast_class, (t_method)mp3cast_mpeg, gensym("mpeg"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(mp3cast_class, (t_method)mp3cast_print, gensym("print"), 0); class_addmethod(mp3cast_class, (t_method)mp3cast_icecast, gensym("icecast"), 0); class_addmethod(mp3cast_class, (t_method)mp3cast_icecast2, gensym("icecast2"), 0); class_addmethod(mp3cast_class, (t_method)mp3cast_shoutcast, gensym("shoutcast"), 0); class_addmethod(mp3cast_class, (t_method)mp3cast_mountpoint, gensym("mountpoint"), A_SYMBOL, 0); class_addmethod(mp3cast_class, (t_method)mp3cast_name, gensym("name"), A_SYMBOL, 0); class_addmethod(mp3cast_class, (t_method)mp3cast_url, gensym("url"), A_SYMBOL, 0); class_addmethod(mp3cast_class, (t_method)mp3cast_genre, gensym("genre"), A_SYMBOL, 0); class_addmethod(mp3cast_class, (t_method)mp3cast_isPublic, gensym("isPublic"), A_FLOAT, 0); class_addmethod(mp3cast_class, (t_method)mp3cast_description, gensym("description"), A_SYMBOL, 0); }