00001
00002
00003
00075 #include <iostream>
00076 #include <stdio.h>
00077 #include <stdlib.h>
00078 #include <getopt.h>
00079 #include <string.h>
00080 #include <unistd.h>
00081 #include <fcntl.h>
00082 #include <sys/stat.h>
00083 #include <sys/types.h>
00084 #include <signal.h>
00085 #include <assert.h>
00086
00087 #include <jutils.h>
00088 #include <generic.h>
00089 #include <jmixer.h>
00090 #include <gui.h>
00091 #include <out_lame.h>
00092 #include <out_vorbis.h>
00093
00094 #include <config.h>
00095
00096 #ifdef GUI_NIGHTOLO
00097 #include <gtkgui/gtk_gui.h>
00098 #endif
00099
00100 #ifdef GUI_NIGHTOLO2
00101 #include <gtkgui2/gtk2_gui.h>
00102 #endif
00103
00104 #ifdef GUI_RUBIK
00105 #include <ncursesgui/ncurses_gui.h>
00106 #endif
00107
00108 #ifdef UI_XMLRPC
00109 #include <xmlrpc/xmlrpc_ui.h>
00110 #endif
00111
00112
00113
00114
00115 char *version =
00116 "%s version %s [ http://muse.dyne.org ]";
00117
00118 char *help =
00119 "Usage: muse [generic options] [-e [encoder options] [stream options] ] [files]\n"
00120 ":: generic options:\n"
00121 " -h --help this help\n"
00122 " -v --version version information\n"
00123 " -D --debug [1-3] debug verbosity level - default 1\n"
00124 " -o --dspout disable souncard output - default on\n"
00125 " -C --cli command line input (no GUI)\n"
00126 " -g --gui specify GUI to use (-g list)\n"
00127 ":: input channels options\n"
00128 " -i --live mix soundcard live input - default off\n"
00129 " -N --number channel number - default 1\n"
00130 " -V --volume channel volume - default 1.0\n"
00131 " -S --position channel starting position - default 0.0\n"
00132 " -P --playmode playmode: play, cont, loop - default cont\n"
00133 ":: output encoders options:\n"
00134 " -e --encoder codec to use [ogg|mp3] - default ogg\n"
00135 " -b --bitrate codec bitrate in Kbit/s - default 24\n"
00136 " -r --frequency encoding frequency - default auto\n"
00137 " -q --quality encoding quality (0.1-9.0) - default 4.0\n"
00138 " -c --channels number of audio channels - default 1\n"
00139 " -f --filedump dump stream to file\n"
00140 ":: broadcast stream options:\n"
00141 " -s --server stream to server[:port] - default port 8000\n"
00142 " -m --mount mounpoint on server - default live\n"
00143 " -l --login login type [ice1|ice2|icy] - default ice1\n"
00144 " -p --pass encoder password on server\n"
00145 " -n --name name of the stream\n"
00146 " -u --url descriptive url of the stream\n"
00147 " -d --desc description of the stream\n"
00148 "\n";
00149
00150 static const struct option long_options[] = {
00151 { "help", no_argument, NULL, 'h' },
00152 { "version", no_argument, NULL, 'v' },
00153 { "debug", required_argument, NULL, 'D' },
00154 { "live", no_argument, NULL, 'i' },
00155 { "dspout", no_argument, NULL, 'o' },
00156 { "cli", no_argument, NULL, 'C' },
00157 { "number", required_argument, NULL, 'N' },
00158 { "volume", required_argument, NULL, 'V' },
00159 { "position", required_argument, NULL, 'S' },
00160 { "playmode", required_argument, NULL, 'P' },
00161 { "encoder", required_argument, NULL, 'e' },
00162 { "bitrate", required_argument, NULL, 'b' },
00163 { "frequency", required_argument, NULL, 'r' },
00164 { "quality", required_argument, NULL, 'q' },
00165 { "channels", required_argument, NULL, 'c' },
00166 { "filedump", required_argument, NULL, 'f' },
00167 { "gui", required_argument, NULL, 'g' },
00168 { "server", required_argument, NULL, 's' },
00169 { "port", required_argument, NULL, 'p' },
00170 { "mount", required_argument, NULL, 'm' },
00171 { "login", required_argument, NULL, 'l' },
00172 { "pass",required_argument, NULL, 'p' },
00173 { "name",required_argument, NULL, 'n' },
00174 { "url",required_argument, NULL, 'u' },
00175 { "desc", required_argument, NULL, 'd' },
00176 { 0, 0, 0, 0 }
00177 };
00178
00179 char *short_options = "-hvD:ioCN:V:S:P:e:b:r:q:c:f:g:s:m:l:p:n:u:d:";
00180
00181
00182 #define MAX_CLI_CHARS 9182
00183 int debug = 1;
00184 bool daemon_mode = false;
00185 char *queue_file = NULL;
00186
00187 int lfreq = 0;
00188 float quality = 1.0f;
00189 int channels = 1;
00190
00191 int thegui = -1;
00192 enum interface { CLI, GTK1, GTK2, NCURSES };
00193
00194
00195 int number = 0;
00196 int playmode = PLAYMODE_CONT;
00197
00198 bool has_playlist = false;
00199 bool dspout = true;
00200 bool micrec = false;
00201 bool snddev = false;
00202
00203 Stream_mixer *mix = NULL;
00204 GUI *gui = NULL;
00205
00206 OutChannel *outch = NULL;
00207 int encid = 0;
00208
00209 Shouter *ice = NULL;
00210 int iceid = 0;
00211
00212
00213 void quitproc (int Sig);
00214 void fsigpipe (int Sig);
00215 bool got_sigpipe;
00216
00217
00218 bool take_args(int argc, char **argv) {
00219 int res;
00220
00221 MuseSetDebug(1);
00222
00223 do {
00224 res = getopt_long(argc, argv, short_options, long_options, NULL);
00225
00226 switch(res) {
00227
00228 case 'h':
00229 fprintf(stderr,"%s",help);
00230 exit(0);
00231
00232 case 'v':
00233 act("MuSE is copyright (c) 2000-2003 by jaromil");
00234 act("MuSE's GTK+ GUI is copyright (c) 2001, 2002 by nightolo");
00235 act("MuSE's NCURSES GUI is copyright (c) 2002 by rubik");
00236 act(" ");
00237 act("part of the redistributed code is copyright by the respective authors,");
00238 act("please refer to the AUTHORS file and to the sourcecode for complete");
00239 act("information.");
00240 act(" ");
00241 act("This source code is free software; you can redistribute it and/or");
00242 act("modify it under the terms of the GNU Public License as published");
00243 act("by the Free Software Foundation; either version 2 of the License,");
00244 act("or (at your option) any later version.");
00245 act(" ");
00246 act("This source code is distributed in the hope that it will be useful,");
00247 act("but WITHOUT ANY WARRANTY; without even the implied warranty of");
00248 act("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
00249 act("Please refer to the GNU Public License for more details.");
00250 act(" ");
00251 act("You should have received a copy of the GNU Public License along with");
00252 act("this source code; if not, write to:");
00253 act("Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.");
00254 exit(0);
00255
00256 case 'D':
00257 MuseSetDebug( atoi(optarg) );
00258 break;
00259
00260 case 'o':
00261
00262
00263
00264
00265
00266
00267 dspout = false;
00268 break;
00269
00270 case 'i':
00271
00272
00273
00274
00275
00276
00277 micrec = true;
00278 break;
00279
00280 case 'e':
00281 encid = 0;
00282
00283 #ifdef HAVE_VORBIS
00284 if (strncasecmp("ogg",optarg,3) == 0) {
00285 encid = mix->create_enc(OGG);
00286 notice("CLI: created Ogg encoder");
00287 }
00288 #endif
00289
00290 #ifdef HAVE_LAME
00291 if (strncasecmp("mp3",optarg,3) == 0) {
00292 encid = mix->create_enc(MP3);
00293 notice("CLI: created Mp3 encoder");
00294 }
00295 #endif
00296
00297
00298
00299 if(encid>0) outch = mix->get_enc(encid);
00300 if(outch) break;
00301
00302 error("you can specify an encoder with the -e option");
00303 act("supported encoders are:");
00304 #ifdef HAVE_VORBIS
00305 act(" OGG - Ogg/Vorbis codec");
00306 #endif
00307 #ifdef HAVE_LAME
00308 act(" MP3 - Lame MP3 codec");
00309 #endif
00310 exit(0);
00311
00312 case 'b':
00313 if(!outch) {
00314 error("invalid command line argument: bps");
00315 error("you must specify a codec first with the -e option");
00316 break;
00317 }
00318 outch->bps( atoi(optarg) );
00319 act("CLI: bitrate set to %iKbit/s",outch->bps());
00320 break;
00321
00322 case 'r':
00323 if(!outch) {
00324 error("invalid command line argument: frequency");
00325 error("you must specify a codec first with the -e option");
00326 break;
00327 }
00328 lfreq = atoi(optarg);
00329 if(lfreq != 0 &&
00330 lfreq != 11000 &&
00331 lfreq != 16000 &&
00332 lfreq != 22050 &&
00333 lfreq != 32000 &&
00334 lfreq != 44100) {
00335 error("invalid frequency %i",lfreq);
00336 error("must be 0, 11000, 16000, 22050, 32000 or 44100 Hz!");
00337 act("CLI: falling back to auto");
00338 lfreq = 0;
00339 }
00340 outch->freq(lfreq);
00341 act("CLI: frequency set to %iKhz",outch->freq());
00342 break;
00343
00344 case 'q':
00345 if(!outch) {
00346 error("invalid command line argument: quality");
00347 error("you must specify a codec first with the -e option");
00348 break;
00349 }
00350 sscanf(optarg,"%f",&quality);
00351
00352
00353
00354
00355 outch->quality(quality);
00356 act("CLI: quality set to %.1f",outch->quality());
00357 break;
00358
00359 case 'c':
00360 if(!outch) {
00361 error("invalid command line argument: channels");
00362 error("you must specify a codec first with the -e option");
00363 break;
00364 }
00365
00366 channels = atoi(optarg);
00367 if(channels>2 | channels<1) {
00368 error("audio channels can be only 1 (mono) or 2 (stereo)");
00369 act("falling back to default: 1 (mono)");
00370 channels = 1;
00371 }
00372 outch->channels(channels);
00373 act("CLI: encoding %i channel(s)",channels);
00374 break;
00375
00376 case 'f':
00377 if(!outch) {
00378 error("invalid command line argument: file dump");
00379 error("you must specify a codec first with the -e option");
00380 break;
00381 }
00382 outch->dump_start( optarg );
00383 act("CLI: file saving to %s",optarg);
00384 break;
00385
00386 case 'C':
00387 thegui = 0;
00388 break;
00389
00390 case 'N':
00391 number = atoi(optarg);
00392 number = (number<0) ? 0 : (number>=MAX_CHANNELS) ? MAX_CHANNELS-1 : number;
00393
00394 if(!mix->chan[number]) {
00395 if(!mix->create_channel(number)) {
00396 error("got problems creating channel %i",number);
00397 } else {
00398 notice("CLI: created channel %i",number);
00399 mix->set_playmode(number,playmode);
00400 }
00401 }
00402 break;
00403
00404 case 'V':
00405 float vol;
00406 if(sscanf(optarg,"%f",&vol)==EOF)
00407 error("CLI: invalid volume for channel %i",number);
00408 else {
00409 vol = (vol>1.0) ? 1.0 : (vol < 0.0) ? 0.0 : vol;
00410 mix->set_volume(number,vol);
00411 act("CLI: volume set to %.2f",vol);
00412 }
00413 break;
00414
00415 case 'S':
00416 float pos;
00417 if(sscanf(optarg,"%f",&pos)==EOF)
00418 error("CLI: invalid position for channel %i",number);
00419 else {
00420 pos = (pos>1.0) ? 1.0 : (pos < 0.0) ? 0.0 : pos;
00421 mix->set_position(number,pos);
00422 act("CLI: starting from position %.2f",pos);
00423 }
00424 break;
00425
00426 case 'P':
00427 playmode = 0;
00428 if(strncasecmp(optarg,"play",4)==0) playmode=PLAYMODE_PLAY;
00429 if(strncasecmp(optarg,"cont",4)==0) playmode=PLAYMODE_CONT;
00430 if(strncasecmp(optarg,"loop",4)==0) playmode=PLAYMODE_LOOP;
00431 if(!playmode)
00432 error("invalid playmode %s",optarg);
00433 else
00434
00435 act("CLI: set playmode \"%s\" for following channels",optarg);
00436 break;
00437
00438 case 'g':
00439 #ifdef GUI_NIGHTOLO
00440 if(strcasecmp(optarg,"gtk")==0) thegui=GTK1;
00441 #endif
00442 #ifdef GUI_NIGHTOLO2
00443 if(strcasecmp(optarg,"gtk2")==0) thegui=GTK2;
00444 #endif
00445 #ifdef GUI_RUBIK
00446 if(strcasecmp(optarg,"ncurses")==0) thegui=NCURSES;
00447 #endif
00448 if(strcasecmp(optarg,"cli")==0) thegui=CLI;
00449
00450 if(thegui>=0) break;
00451
00452 notice("listing available user interfaces:");
00453 #ifdef GUI_NIGHTOLO
00454 act("[gtk] - Xwin graphical interactive clicky clicky");
00455 #endif
00456 #ifdef GUI_NIGHTOLO2
00457 act("[gtk2] - new graphical interactive, experimental with languages");
00458 #endif
00459 #ifdef GUI_RUBIK
00460 act("[ncurses] - 0ld sch00l l33t console display");
00461 #endif
00462 act("[cli] - command line interface, not interactive");
00463 exit(0);
00464
00465 case 's':
00466 if(!outch) {
00467 error("invalid command line argument: server");
00468 error("you must specify a codec first with the -e option");
00469 break;
00470 }
00471
00472 iceid = outch->create_ice();
00473 if(iceid<0) {
00474 error("could'nt create icecast shouter");
00475 break;
00476 } else func("created icecast shouter ID %i",iceid);
00477 ice = outch->get_ice(iceid);
00478
00479 char *p;
00480 p = strstr(optarg,":");
00481 if(p) {
00482 ice->port( atoi(p+1) );
00483 *p = '\0';
00484 } else ice->port(8000);
00485 ice->host(optarg);
00486 notice("CLI: created streamer to %s %i",
00487 ice->host(), ice->port());
00488
00489 break;
00490
00491 case 'l':
00492 if(!outch) {
00493 error("invalid command line argument: server login type");
00494 error("you must specify a codec first with the -e option");
00495 break;
00496 }
00497 if(!iceid) {
00498 error("invalid command line argument: server login type");
00499 error("you must specify a server first with the -s option");
00500 break;
00501 }
00502 int ltype;
00503 if(strncasecmp(optarg,"ice1",4)==0)
00504 ltype = SHOUT_PROTOCOL_XAUDIOCAST;
00505 else if(strncasecmp(optarg,"ice2",4)==0)
00506 ltype = SHOUT_PROTOCOL_HTTP;
00507 else if(strncasecmp(optarg,"icy",4)==0)
00508 ltype = SHOUT_PROTOCOL_ICY;
00509 else {
00510 error("unrecognized login type: %s",optarg);
00511 error("please use one of the following:");
00512 error("ice1 = icecast 1, darwin and litestream");
00513 error("ice2 = icecast 2 server");
00514 error("icy = shoutcast server");
00515 break;
00516 }
00517 ice = outch->get_ice(iceid);
00518 ice->login(ltype);
00519 act("CLI: login type set to %s",optarg);
00520 break;
00521
00522 case 'p':
00523 if(!outch) {
00524 error("invalid command line argument: server password");
00525 error("you must specify a codec first with the -e option");
00526 break;
00527 }
00528 if(!iceid) {
00529 error("invalid command line argument: server password");
00530 error("you must specify a server first with the -s option");
00531 break;
00532 }
00533 ice = outch->get_ice(iceid);
00534 ice->pass(optarg);
00535 act("CLI: stream password set");
00536 break;
00537
00538 case 'm':
00539 if(!outch) {
00540 error("invalid command line argument: server mountpoint");
00541 error("you must specify a codec first with the -e option");
00542 break;
00543 }
00544 if(!iceid) {
00545 error("invalid command line argument: server mountpoint");
00546 error("you must specify a server first with the -s option");
00547 break;
00548 }
00549 ice = outch->get_ice(iceid);
00550 ice->mount(optarg);
00551 act("CLI: stream mountpoint %s",ice->mount());
00552 break;
00553
00554 case 'n':
00555 if(!outch) {
00556 error("invalid command line argument: stream name");
00557 error("you must specify a codec first with the -e option");
00558 break;
00559 }
00560 if(!iceid) {
00561 error("invalid command line argument: stream name");
00562 error("you must specify a server first with the -s option");
00563 break;
00564 }
00565 ice = outch->get_ice(iceid);
00566 ice->name(optarg);
00567 act("CLI: stream descriptive name: %s",ice->name());
00568 break;
00569
00570 case 'u':
00571 if(!outch) {
00572 error("CLI: invalid command line argument: stream url");
00573 error("you must specify a codec first with the -e option");
00574 break;
00575 }
00576 if(!iceid) {
00577 error("CLI: invalid command line argument: stream url");
00578 error("you must specify a server first with the -s option");
00579 break;
00580 }
00581 ice = outch->get_ice(iceid);
00582 ice->url(optarg);
00583 act("CLI: stream descriptive: url %s",ice->url());
00584 break;
00585
00586 case 'd':
00587 if(!outch) {
00588 error("CLI: invalid command line argument: stream description");
00589 error("you must specify a codec first with the -e option");
00590 break;
00591 }
00592 if(!iceid) {
00593 error("CLI: invalid command line argument: stream description");
00594 error("you must specify a server first with the -s option");
00595 break;
00596 }
00597 ice = outch->get_ice(iceid);
00598 ice->desc(optarg);
00599 act("CLI: stream description: %s",ice->desc());
00600 break;
00601
00602 case 1:
00603 act("CLI: queue %s on channel %i",optarg,number);
00604 if(!mix->chan[number])
00605 if(!mix->create_channel(number)) {
00606 error("CLI: can't create channel %i",number);
00607 break;
00608 } else {
00609 notice("CLI: created channel %i",number);
00610 mix->set_playmode(number,playmode);
00611 }
00612 if(!mix->add_to_playlist(number,optarg))
00613 error("CLI: can't add %s to channel %1",optarg,number);
00614 break;
00615
00616 default:
00617 break;
00618 }
00619 } while(res > 0);
00620 return true;
00621 }
00622
00623 bool check_config() {
00624
00625
00626 char temp[MAX_PATH_SIZE];
00627 char *home = getenv("HOME");
00628 sprintf(temp,"%s/.muse",home);
00629 mkdir(temp,0744);
00630 return(true);
00631 }
00632
00633 int main(int argc, char **argv) {
00634
00635 notice(version,PACKAGE,VERSION);
00636 act("by Denis Rojo aka jaromil http://rastasoft.org");
00637 act("--");
00638
00639
00640 if (signal (SIGINT, quitproc) == SIG_ERR) {
00641 error ("Couldn't install SIGINT handler"); exit (0);
00642 }
00643
00644 if (signal (SIGQUIT, quitproc) == SIG_ERR) {
00645 error ("Couldn't install SIGQUIT handler"); exit (0);
00646 }
00647
00648 if (signal (SIGTERM, quitproc) == SIG_ERR) {
00649 error ("Couldn't install SIGTERM handler"); exit (0);
00650 }
00651 got_sigpipe = false;
00652 if (signal (SIGPIPE, fsigpipe) == SIG_ERR) {
00653 error ("Couldn't install SIGPIPE handler"); exit (0);
00654 }
00655
00656 mix = new Stream_mixer();
00657
00658 if( !take_args(argc, argv) ) goto QUIT;
00659
00660 check_config();
00661
00662 if(dspout||micrec) {
00663 snddev = mix->open_soundcard(micrec,dspout);
00664 }
00665
00666 if(!snddev) {
00667 warning("no soundcard found");
00668 act("line-in and speaker out deactivated");
00669 }
00670
00671 if(thegui==GTK1 || thegui==GTK2)
00672 if(!getenv("DISPLAY")) {
00673 error("DISPLAY not found, falling back to console");
00674 thegui=-1;
00675 }
00676
00677 if(thegui<0) {
00678 if(getenv("DISPLAY")) {
00679 #ifdef GUI_NIGHTOLO
00680 thegui=GTK1;
00681 #elif GUI_NIGHTOLO2
00682 thegui=GTK2;
00683 #endif
00684 }
00685 if(thegui<0) {
00686 #ifdef GUI_RUBIK
00687 thegui = NCURSES;
00688 #else
00689 thegui=CLI;
00690 #endif
00691 }
00692 }
00693
00694 switch(thegui) {
00695 case GTK1:
00696 #ifdef GUI_NIGHTOLO
00697 notice("spawning the GTK-1.2 GUI");
00698 act("by nightolo <night@dyne.org>");
00699 gui = new GTK_GUI(argc,argv,mix);
00700 #else
00701 error("the Gtk-1.2 interface is not compiled in");
00702 #endif
00703 break;
00704 case GTK2:
00705 #ifdef GUI_NIGHTOLO2
00706 notice("spawning the GTK-2 GUI");
00707 act("by nightolo <night@dyne.org>");
00708 gui = new GTK2_GUI(argc,argv,mix);
00709 #else
00710 error("the Gtk2 interface is not compiled in");
00711 #endif
00712 break;
00713 case NCURSES:
00714 #ifdef GUI_RUBIK
00715 notice("spawning the NCURSES console user interface");
00716 act("by Luca Profico aka rubik <rubik@olografix.org>");
00717 gui = new NCURSES_GUI(argc,argv,mix);
00718 MuseSetLog("muse.log");
00719 #else
00720 error("the ncurses console interface is not compiled in");
00721 #endif
00722 break;
00723 default:
00724 notice("using commandline interface (non interactive)");
00725 thegui=CLI;
00726 }
00727
00728 if(thegui<0) error("no interface selected, this should never happen");
00729
00730 if(thegui!=CLI) {
00731 gui->start();
00732 mix->register_gui(gui);
00733 set_guimsg(gui);
00734 notice("%s version %s",PACKAGE,VERSION);
00735 }
00736
00737
00738 outch = (OutChannel*)mix->outchans.begin();
00739 while(outch) {
00740
00741 if( (outch->icelist.len() == 0)
00742 && !outch->fd) {
00743 error("codec %s [%u] has no server neither filedump configured",
00744 outch->name,outch->id);
00745 mix->delete_enc(outch->id);
00746 error("encoder removed");
00747
00748 } else {
00749 func("configuring %s Q%i freq%i chan%i",
00750 outch->name,
00751 (int)fabs(outch->quality()),
00752 outch->freq(),
00753 outch->channels());
00754
00755 if(mix->apply_enc( outch->id )) outch->start();
00756 }
00757 if(!outch) break;
00758 outch = (OutChannel*) outch->next;
00759 }
00760
00761 if(thegui==CLI) {
00762
00763 int c;
00764 for(c=0;c<MAX_CHANNELS;c++)
00765 if(mix->chan[c])
00766 if(!mix->set_channel(c,1))
00767 error("CLI: error in set_channel(%i,1)",c);
00768 else
00769 if(!mix->play_channel(c))
00770 error("CLI: error in play_channel(%i)",c);
00771 else
00772 has_playlist = true;
00773 }
00774
00775 if((!has_playlist && !micrec) && (thegui==CLI)) {
00776 warning("nothing to play, you must specify at least songs or live input");
00777 act("see --help switch for more information");
00778 goto QUIT;
00779 }
00780
00781
00782
00783 while(!mix->quit)
00784 mix->cafudda();
00785
00786
00787
00788 QUIT:
00789 notice("quitting MuSE");
00790
00791 if(thegui!=CLI) gui->quit = true;
00792
00793 set_guimsg(NULL);
00794
00795 if(mix) {
00796 act("stopping mixer...");
00797
00798
00799 jsleep(0,50);
00800 delete mix;
00801 }
00802
00803 if(thegui!=CLI) {
00804 act("quitting graphic interface");
00805 delete gui;
00806 }
00807
00808 act("cya on http://muse.dyne.org");
00809 MuseCloseLog();
00810 exit(0);
00811 }
00812
00813
00814 void quitproc (int Sig) {
00815 func("received signal %u on process %u",Sig,getpid());
00816 if(thegui!=CLI) gui->quit = true;
00817 mix->quit = true;
00818 }
00819
00820 void fsigpipe (int Sig) {
00821 warning("received signal SIGPIPE (%u) on process %u",Sig,getpid());
00822 got_sigpipe = true;
00823 }