From 93678cb6667a9ccd8e992ddbdf16c492cdaf33be Mon Sep 17 00:00:00 2001 From: "N.N." Date: Sat, 25 Feb 2006 19:58:40 +0000 Subject: updated to 0.5 svn path=/trunk/externals/unauthorized/; revision=4617 --- mp3cast~/CHANGES.LOG | 6 + mp3cast~/Makefile | 2 +- mp3cast~/mp3cast~-help.pd | 101 +++--- mp3cast~/mp3cast~.c | 781 ++++++++++++++++++++++++++++++++-------------- 4 files changed, 613 insertions(+), 277 deletions(-) diff --git a/mp3cast~/CHANGES.LOG b/mp3cast~/CHANGES.LOG index 7418ad7..3d088ed 100644 --- a/mp3cast~/CHANGES.LOG +++ b/mp3cast~/CHANGES.LOG @@ -1,3 +1,9 @@ +0.5 (by rama) + changed "ispublic" option to true as default + added messages handling for setting stream info: url, genre, description, isPublic + (all was tested using icecast2) +0.4 + added icecast 2 compatibility 0.3 renamed it to mp3cast 0.2 diff --git a/mp3cast~/Makefile b/mp3cast~/Makefile index 799788e..610423e 100644 --- a/mp3cast~/Makefile +++ b/mp3cast~/Makefile @@ -61,7 +61,7 @@ pd_linux: $(NAME).pd_linux .SUFFIXES: .pd_linux LINUXCFLAGS = -DPD -DUNIX -DICECAST -O2 -funroll-loops -fomit-frame-pointer \ - -Wall -W -Wshadow -Wstrict-prototypes -Werror \ + -Wall -W -Wshadow -Wstrict-prototypes \ -Wno-unused -Wno-parentheses -Wno-switch LINUXINCLUDE = -I../../src diff --git a/mp3cast~/mp3cast~-help.pd b/mp3cast~/mp3cast~-help.pd index c7d8e72..243570a 100644 --- a/mp3cast~/mp3cast~-help.pd +++ b/mp3cast~/mp3cast~-help.pd @@ -1,50 +1,65 @@ -#N canvas 52 75 741 425 10; -#X floatatom 53 24 0 40 16000; -#X msg 30 378 \; pd dsp 1; -#X obj 61 242 mp3cast~; -#X floatatom 61 280 0 0 0; +#N canvas 1180 63 687 606 10; +#X floatatom 53 24 0 40 16000 0 - - -; +#X msg 351 547 \; pd dsp 1; +#X floatatom 48 537 0 0 0 0 - - -; #X msg 181 88 disconnect; -#X msg 151 44 connect localhost 8000; #X text 266 63 <-- your password for the server; #X text 317 75 (default passwd is "pd"); #X text 257 87 <-- close connection; -#X text 411 21 <-- host and port; -#X text 292 215 <-- settings for mp3 stream; -#X text 315 245 bitrate: bitrate of stream \, def. 224kbit/s; -#X text 352 269 1 = joint stereo (default); -#X text 312 307 quality: 1 = high \, 9 = low; -#X text 313 227 (samplerate \, bitrate \, mode \, quality); -#X text 316 257 mode: 0 = stereo; -#X text 352 281 2 = dual channel; -#X text 352 294 3 = mono; -#X msg 13 145 print; -#X msg 96 378 \; pd dsp 0; -#X msg 203 141 icecast; -#X text 259 116 <-- choose type of server; +#X text 313 45 <-- host and port; +#X text 349 218 <-- settings for mp3 stream; +#X text 372 248 bitrate: bitrate of stream \, def. 224kbit/s; +#X text 409 272 1 = joint stereo (default); +#X text 369 310 quality: 1 = high \, 9 = low; +#X text 370 230 (samplerate \, bitrate \, mode \, quality); +#X text 373 260 mode: 0 = stereo; +#X text 409 284 2 = dual channel; +#X text 409 297 3 = mono; +#X msg 6 439 print; +#X msg 417 547 \; pd dsp 0; +#X msg 204 129 icecast; #X obj 53 51 osc~ 440; -#X msg 163 66 passwd letmein; -#X msg 154 239 mpeg 8000 8 3 5; -#X msg 155 215 mpeg 44100 128 1 4; -#X msg 209 170 mountpoint pd; -#X obj 29 349 loadbang; -#X text 293 374 Author : Yves Degoyon ( ydegoyon@free.fr ); -#X msg 190 118 shoutcast; -#X msg 116 23 connect college-invisible.dyndns.org 8000; +#X msg 211 242 mpeg 8000 8 3 5; +#X msg 212 218 mpeg 44100 128 1 4; +#X obj 350 518 loadbang; +#X text 313 477 Author : Yves Degoyon ( ydegoyon@free.fr ); +#X msg 191 109 shoutcast; #X msg 212 194 name mystream; #X text 322 195 name of the stream; -#X connect 0 0 22 0; -#X connect 2 0 3 0; -#X connect 4 0 2 0; -#X connect 5 0 2 0; -#X connect 18 0 2 0; -#X connect 20 0 2 0; -#X connect 22 0 2 0; -#X connect 22 0 2 1; -#X connect 23 0 2 0; -#X connect 24 0 2 0; -#X connect 25 0 2 0; -#X connect 26 0 2 0; -#X connect 27 0 1 0; -#X connect 29 0 2 0; -#X connect 30 0 2 0; -#X connect 31 0 2 0; +#X text 260 107 <-- choose type of server ( default = icecast ); +#X msg 209 148 icecast2; +#X msg 180 365 isPublic 0; +#X msg 181 387 isPublic 1; +#X text 385 421 <-- stream description; +#X text 265 380 <-- publish stream info in the yellow pages; +#X text 269 438 <-- stream genre; +#X text 338 344 <-- stream url; +#X msg 164 66 passwd pd; +#X msg 151 45 connect localhost 8000; +#X msg 212 172 mountpoint live.mp3; +#X msg 179 343 url http://localhost; +#X msg 180 415 description hardcore; +#X msg 180 439 genre other; +#X text 313 490 Contributor : Ramiro Consentino; +#X obj 48 499 mp3cast~; +#X connect 0 0 19 0; +#X connect 3 0 42 0; +#X connect 16 0 42 0; +#X connect 18 0 42 0; +#X connect 19 0 42 0; +#X connect 19 0 42 1; +#X connect 20 0 42 0; +#X connect 21 0 42 0; +#X connect 22 0 1 0; +#X connect 24 0 42 0; +#X connect 25 0 42 0; +#X connect 28 0 42 0; +#X connect 29 0 42 0; +#X connect 30 0 42 0; +#X connect 35 0 42 0; +#X connect 36 0 42 0; +#X connect 37 0 42 0; +#X connect 38 0 42 0; +#X connect 39 0 42 0; +#X connect 40 0 42 0; +#X connect 42 0 2 0; diff --git a/mp3cast~/mp3cast~.c b/mp3cast~/mp3cast~.c index 11c3d19..1d6d4be 100644 --- a/mp3cast~/mp3cast~.c +++ b/mp3cast~/mp3cast~.c @@ -26,6 +26,13 @@ /* */ /* ---------------------------------------------------------------------------- */ + + +#ifdef NT +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + #include "m_pd.h" /* standard pd stuff */ #include @@ -35,7 +42,7 @@ #include #include #include -#ifndef __APPLE__ +#ifndef MACOSX #include #endif #include @@ -46,15 +53,17 @@ #include #include #include +#include /* lame encoder stuff */ +#include "mpg123.h" #define SOCKET_ERROR -1 #else #include #include #include #include +#include "lame_enc.h" /* lame encoder stuff */ #endif -#include -#include "mpg123.h" + #define MY_MP3_MALLOC_IN_SIZE 65536 /* max size taken from lame readme */ @@ -63,7 +72,24 @@ #define MAXDATARATE 320 /* maximum mp3 data rate is 320kbit/s */ #define STRBUF_SIZE 32 -static char *mp3cast_version = "mp3cast~: mp3 streamer version 0.3, written by Yves Degoyon"; +static char *mp3cast_version = "mp3cast~: mp3 streamer version 0.5, written by Yves Degoyon"; + +#ifndef UNIX +static HINSTANCE dll = NULL; +static BEINITSTREAM initStream = NULL; +static BEENCODECHUNK encodeChunk = NULL; +static BEDEINITSTREAM deinitStream = NULL; +static BECLOSESTREAM closeStream = NULL; +static BEVERSION dllVersion = NULL; +static BEWRITEVBRHEADER writeVBRHeader = NULL; +#endif + +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; @@ -95,13 +121,19 @@ typedef struct _mp3cast 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 */ + /* 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 */ +#ifdef UNIX lame_global_flags *lgfp; /* lame encoder configuration */ +#endif } t_mp3cast; @@ -142,41 +174,52 @@ static void mp3cast_encode(t_mp3cast *x) 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; - } + /* 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 */ +#ifndef UNIX + err = encodeChunk(x->x_lame, x->x_lamechunk, x->x_mp3inbuf, x->x_mp3outbuf, &x->x_mp3size); +#else 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 ); +#endif /* check result */ +#ifndef UNIX + if(err != BE_ERR_SUCCESSFUL) + { + closeStream(x->x_lame); + error("mp3cast~: lameEncodeChunk() failed (%lu)", err); +#else if(x->x_mp3size<0) { lame_close( x->lgfp ); error("mp3cast~: lame_encode_buffer_interleaved failed (%d)", x->x_mp3size); +#endif x->x_lame = -1; } } @@ -192,7 +235,11 @@ static void mp3cast_stream(t_mp3cast *x) if(err < 0) { error("mp3cast~: could not send encoded data to server (%d)", err); +#ifndef UNIX + closeStream(x->x_lame); +#else lame_close( x->lgfp ); +#endif x->x_lame = -1; #ifndef UNIX closesocket(x->x_fd); @@ -219,65 +266,65 @@ static t_int *mp3cast_perform(t_int *w) /* 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 */ + 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 */ + /* 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 */ + /* 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)) @@ -308,14 +355,21 @@ static void mp3cast_dsp(t_mp3cast *x, t_signal **sp) /* initialize the lame library */ static void mp3cast_tilde_lame_init(t_mp3cast *x) { +#ifndef UNIX + /* encoder related stuff (calculating buffer size) */ + BE_VERSION lameVersion = {0,}; /* version number of LAME */ + BE_CONFIG lameConfig = {0,}; /* config structure of LAME */ + unsigned int ret; +#else int ret; x->lgfp = lame_init(); /* set default parameters for now */ +#endif #ifndef UNIX /* load lame_enc.dll library */ - HINSTANCE dll; - dll = LoadLibrary("lame_enc.dll"); - if(!dll) + + dll=LoadLibrary("lame_enc.dll"); + if(dll==NULL) { error("mp3cast~: error loading lame_enc.dll"); closesocket(x->x_fd); @@ -324,12 +378,75 @@ static void mp3cast_tilde_lame_init(t_mp3cast *x) post("mp3cast~: connection closed"); return; } -#endif + /* get Interface functions */ + initStream = (BEINITSTREAM) GetProcAddress(dll, TEXT_BEINITSTREAM); + encodeChunk = (BEENCODECHUNK) GetProcAddress(dll, TEXT_BEENCODECHUNK); + deinitStream = (BEDEINITSTREAM) GetProcAddress(dll, TEXT_BEDEINITSTREAM); + closeStream = (BECLOSESTREAM) GetProcAddress(dll, TEXT_BECLOSESTREAM); + dllVersion = (BEVERSION) GetProcAddress(dll, TEXT_BEVERSION); + writeVBRHeader = (BEWRITEVBRHEADER) GetProcAddress(dll,TEXT_BEWRITEVBRHEADER); + + /* check if all interfaces are present */ + if(!initStream || !encodeChunk || !deinitStream || !closeStream || !dllVersion || !writeVBRHeader) + { + + error("mp3cast~: unable to get LAME interfaces"); + closesocket(x->x_fd); + x->x_fd = -1; + outlet_float(x->x_obj.ob_outlet, 0); + post("mp3cast~: connection closed"); + return; + } + + /* get LAME version number */ + dllVersion(&lameVersion); + + post( "mp3cast~: lame_enc.dll version %u.%02u (%u/%u/%u)\n" + " lame_enc engine %u.%02u", + lameVersion.byDLLMajorVersion, lameVersion.byDLLMinorVersion, + lameVersion.byDay, lameVersion.byMonth, lameVersion.wYear, + lameVersion.byMajorVersion, lameVersion.byMinorVersion); + + memset(&lameConfig,0,sizeof(lameConfig)); /* clear all fields */ +#else { const char *lameVersion = get_lame_version(); post( "mp3cast~ : using lame version : %s", lameVersion ); } +#endif + +#ifndef UNIX + + /* use the LAME config structure */ + lameConfig.dwConfig = BE_CONFIG_LAME; + + /* set the mpeg format flags */ + lameConfig.format.LHV1.dwStructVersion = 1; + lameConfig.format.LHV1.dwStructSize = sizeof(lameConfig); + lameConfig.format.LHV1.dwSampleRate = (int)sys_getsr(); /* input frequency - pd's sample rate */ + lameConfig.format.LHV1.dwReSampleRate = x->x_samplerate; /* output s/r - resample if necessary */ + lameConfig.format.LHV1.nMode = x->x_mp3mode; /* output mode */ + lameConfig.format.LHV1.dwBitrate = x->x_bitrate; /* mp3 bitrate */ + lameConfig.format.LHV1.nPreset = x->x_mp3quality; /* mp3 encoding quality */ + lameConfig.format.LHV1.dwMpegVersion = MPEG1; /* use MPEG1 */ + lameConfig.format.LHV1.dwPsyModel = 0; /* USE DEFAULT PSYCHOACOUSTIC MODEL */ + lameConfig.format.LHV1.dwEmphasis = 0; /* NO EMPHASIS TURNED ON */ + lameConfig.format.LHV1.bOriginal = TRUE; /* SET ORIGINAL FLAG */ + lameConfig.format.LHV1.bCopyright = TRUE; /* SET COPYRIGHT FLAG */ + lameConfig.format.LHV1.bNoRes = TRUE; /* no bit resorvoir */ + + /* init the MP3 stream */ + ret = initStream(&lameConfig, &x->x_lamechunk, &x->x_mp3size, &x->x_lame); + + /* check result */ + if(ret != BE_ERR_SUCCESSFUL) + { + post("mp3cast~: error opening encoding stream (%lu)", ret); + return; + } + +#else /* setting lame parameters */ lame_set_num_channels( x->lgfp, 2); lame_set_in_samplerate( x->lgfp, sys_getsr() ); @@ -353,9 +470,46 @@ static void mp3cast_tilde_lame_init(t_mp3cast *x) post( "mp3cast~ : lame initialization done. (%d)", x->x_lame ); } lame_init_bitstream( x->lgfp ); +#endif + + +} + +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 SHOUTcast server */ + /* connect to server */ static void mp3cast_connect(t_mp3cast *x, t_symbol *hostname, t_floatarg fportno) { struct sockaddr_in server; @@ -364,13 +518,14 @@ static void mp3cast_connect(t_mp3cast *x, t_symbol *hostname, t_floatarg fportno /* information about this broadcast to be send to the server */ const char *name = x->x_name; /* name of broadcast */ - const char *url = "http://guess.where.i.am"; /* url of broadcast */ - const char *genre = "abstract break"; /* genre 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 = 0; /* don't publish broadcast on www.shoutcast.com */ + int isPublic = x->x_isPublic; /* don't publish broadcast on www.shoutcast.com */ /* variables used for communication with server */ const char * buf = 0; @@ -379,9 +534,14 @@ static void mp3cast_connect(t_mp3cast *x, t_symbol *hostname, t_floatarg fportno fd_set fdset; struct timeval tv; int sockfd; + +#ifndef UNIX + unsigned int ret; +#else int ret; +#endif - if(x->x_icecast == 0)portno++; /* use SHOUTcast, portno is one higher */ + if(x->x_icecast == 0)portno++; /* use SHOUTcast, portno is one higher */ if (x->x_fd >= 0) { @@ -434,7 +594,6 @@ static void mp3cast_connect(t_mp3cast *x, t_symbol *hostname, t_floatarg fportno tv.tv_usec = 500; /* microseconds */ ret = select(sockfd + 1, &fdset, NULL, NULL, &tv); - if(ret < 0) { error("mp3cast~: can not read from socket"); @@ -445,134 +604,216 @@ static void mp3cast_connect(t_mp3cast *x, t_symbol *hostname, t_floatarg fportno #endif return; } -#ifndef NT ret = select(sockfd + 1, NULL, &fdset, NULL, &tv); if(ret < 0) { error("mp3cast~: can not write to socket"); +#ifndef UNIX + closesocket(sockfd); +#else close(sockfd); +#endif return; } -#endif - 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-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 /* 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 = "\n\n"; - send(sockfd, buf, strlen(buf), 0); - /* end login for IceCast */ - } + + 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 ( len < 2 || resp[0] != 'O' || resp[1] != 'K' ) + if ( strstr( resp, "OK" ) == NULL ) { post("mp3cast~: login failed!"); + if ( len>0 ) post("mp3cast~: server answered : %s", resp); #ifndef UNIX closesocket(sockfd); #else @@ -580,11 +821,11 @@ static void mp3cast_connect(t_mp3cast *x, t_symbol *hostname, t_floatarg fportno #endif return; } - + /* suck anything that the other side has to say */ // while (len = recv(sockfd, resp, STRBUF_SIZE,0)) // { - ; /* do nothing, just wait ! */ + // post("mp3cast~: server answered : %s", resp); // } x->x_fd = sockfd; @@ -601,12 +842,23 @@ static void mp3cast_disconnect(t_mp3cast *x) int err = -1; if(x->x_lame >= 0) { +#ifndef UNIX + /* deinit the stream */ + err = deinitStream(x->x_lame, x->x_mp3outbuf, &x->x_mp3size); + + /* check result */ + if(err != BE_ERR_SUCCESSFUL) + { + error("exiting mp3 stream failed (%lu)", err); + } + closeStream(x->x_lame); /* close mp3 encoder stream */ +#else /* 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 ); - +#endif x->x_lame = -1; post("mp3cast~: encoder stream closed"); } @@ -673,54 +925,108 @@ static void mp3cast_print(t_mp3cast *x) } post(" mode: %s\n" " quality: %d", buf, x->x_mp3quality); +#ifndef UNIX + if(x->x_lamechunk!=0)post(" calculated mp3 chunk size: %d", x->x_lamechunk); +#else post(" mp3 chunk size: %d", x->x_lamechunk); +#endif 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 - { - post(" server type is IceCast"); - } + + 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"); + } } - /* we use iceCast server */ static void mp3cast_icecast(t_mp3cast *x) { - x->x_icecast = 1; - post("mp3cast~: set server type to IceCast"); + 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"); } - /* we use SHOUTcast server (default) */ static void mp3cast_shoutcast(t_mp3cast *x) { - x->x_icecast = 0; - post("mp3cast~: set server type to SHOUTcast"); + x->x_icecast = 0; + post("mp3cast~: set server type to SHOUTcast"); } - /* set mountpoint for IceCast server */ + /* 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); + x->x_mountpoint = mount->s_name; + post("mp3cast~: mountpoint set to %s", x->x_mountpoint); } - /* set namle for IceCast server */ + /* 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); + x->x_name = name->s_name; + post("mp3cast~: name set to %s", x->x_name); } - /* clean up */ + /* 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 ); +#ifndef UNIX + closeStream(x->x_lame); +#else + lame_close( x->lgfp ); +#endif if(x->x_fd >= 0) #ifndef UNIX closesocket(x->x_fd); @@ -756,9 +1062,13 @@ static void *mp3cast_new(void) x->x_outp = 0; x->lgfp = NULL; x->x_start = -1; - x->x_icecast = 0; + 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); } @@ -775,8 +1085,13 @@ void mp3cast_tilde_setup(void) 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); } -- cgit v1.2.1