/* MikMod example player (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for complete list. 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. 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. */ /*============================================================================== $Id: mikmod.c,v 1.11 2003/10/08 02:08:48 raph Exp $ Module player example of MikMod ==============================================================================*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_GETOPT_LONG_ONLY #include #else #include "getopt.h" #endif #include #include #include #include #include #include #ifdef NEED_USLEEP void usleep(unsigned long); #endif #if defined(__OS2__)||defined(__EMX__) #define INCL_DOS #define INCL_KBD #define INCL_DOSPROCESS #include #endif #if !defined(__OS2__)&&!defined(__EMX__) #ifdef HAVE_NCURSES_H #include #else #include #endif #else #include #endif #include #include "player.h" #include "mwindow.h" #include "mdialog.h" #include "attr.h" #if defined(__FreeBSD__)||defined(__NetBSD__)||defined(__OpenBSD__) #ifdef HAVE_SYS_TIME_H #include #endif #include #include #ifdef __FreeBSD__ #include #endif #endif #if defined(__linux) #ifdef BROKEN_SCHED #define _P __P #endif #include #endif /* Long options definition */ static struct option options[]={ /* Output options */ {"driver", required_argument, NULL,'d'}, {"output", required_argument, NULL,'o'}, {"frequency", required_argument, NULL,'f'}, {"interpolate", no_argument, NULL,'i'}, {"nointerpolate", no_argument, NULL, 1}, {"hqmixer", no_argument, NULL, 2}, {"nohqmixer", no_argument, NULL, 3}, {"surround", no_argument, NULL, 4}, {"nosurround", no_argument, NULL, 5}, {"reverb", required_argument, NULL,'r'}, /* Playback options */ {"volume", required_argument, NULL,'v'}, {"fadeout", no_argument, NULL,'F'}, {"nofadeout", no_argument, NULL, 6}, {"loops", no_argument, NULL,'l'}, {"noloops", no_argument, NULL, 7}, {"panning", no_argument, NULL,'a'}, {"nopanning", no_argument, NULL, 8}, {"protracker", no_argument, NULL,'x'}, {"noprotracker", no_argument, NULL, 9}, {"exitafter", no_argument, NULL,'X'}, {"noexitafter", no_argument, NULL, 15}, /* Loading options */ {"curious", no_argument, NULL,'c'}, {"nocurious", no_argument, NULL, 10}, {"playmode", required_argument, NULL,'p'}, {"tolerant", no_argument, NULL,'t'}, {"notolerant", no_argument, NULL, 11}, /* Scheduling options */ {"renice", no_argument, NULL,'s'}, {"norenice", no_argument, NULL, 12}, {"realtime", no_argument, NULL,'S'}, {"norealtime", no_argument, NULL, 12}, /* Display options */ {"quiet", no_argument, NULL,'q'}, {"terse", no_argument, NULL, 14}, #ifdef ENABLE_COLOR_INTERFACE {"color", no_argument, NULL,'C'}, {"nocolor", no_argument, NULL, 16}, #endif /* Information options */ {"information", no_argument, NULL,'n'}, {"version", no_argument, NULL,'V'}, {"help", no_argument, NULL,'h'}, {NULL, 0, NULL, 0} }; static CHAR *PRG_NAME; PLAYLIST playlist; CONFIG config; /* current module */ MODULE *mf=NULL; /* set if quiet mode is enabled */ BOOL quiet=0; BOOL semiquiet=0; static BOOL player_on=0; static BOOL library_on=0; static BOOL quit=0; /* archive handling these variables need to be accessible from signal handlers */ static BOOL clean=1; static char *playfile=NULL; static char *archive=NULL; /* playlist handling */ static int next=0; /* 0 or a PL_CONT_xxx code */ static int next_pl_pos=0; /* for PL_CONT_POS, next pos in playlist */ static int next_sng_pos=0; /* next pos in module */ /* help text */ #define S_B(b) ((b)?"Yes":"No") static void help(CONFIG *c) { char output[4]; char *conf_name=CF_GetFilename(); puts(mikcopyr); #ifdef HAVE_SNPRINTF snprintf(output,4,"%s%c",c->mode_16bit?"16":"8",c->stereo?'s':'m'); #else sprintf(output,"%s%c",c->mode_16bit?"16":"8",c->stereo?'s':'m'); #endif printf( "\n" "Usage: %s [option]... [module|playlist]...\n" "\n" "Output options:\n" " -d, --driver n,options Use nth driver for output (0: autodetect),\n" " default: %d\n" " -o, --output 8m|8s|16m|16s 8/16 bit output in stereo/mono, default: %s\n" " -f, --frequency nnnnn Set mixing frequency, default: %d\n" "* -i, --interpolate Use interpolate mixing, default: %s\n" "* --hqmixer Use high-quality (but slower) software mixer,\n" " default: %s\n" "* --surround Use surround mixing, default: %s\n" " -r, --reverb nn Set reverb amount (0-15), default: %d\n" "Playback options:\n" " -v, --volume nn Set volume from 0%% (silence) to 100%%, default: %d%%\n" "* -F, --fadeout Force volume fade at the end of module,\n" " default: %s\n" "* -l, --loops Enable in-module loops, default: %s\n" "* -a, --panning Process panning effects, default: %s\n" "* -x, --protracker Disable extended protracker effects, default: %s\n" "* -X, --exitafter Exit MikMod when the playlist is finished,\n" " default: %s\n" "Loading options:\n" "* -c, --curious Look for hidden patterns in module, default: %s\n" " -p, --playmode n Playlist mode (1: loop module, 2: list multi,\n" " 4: shuffle list, 8: list random), default: %d\n" "* -t, --tolerant Don't halt on file access errors, default: %s\n", PRG_NAME,c->driver,output,c->frequency,S_B(c->interpolate), S_B(c->hqmixer),S_B(c->surround),c->reverb,c->volume,S_B(c->fade), S_B(c->loop),S_B(c->panning),S_B(!c->extspd),S_B(c->exit_after), S_B(c->curious),c->playmode,S_B(c->tolerant) ); #if defined(__OS2__)||defined(__EMX__)||defined(__linux)||defined(__FreeBSD__)||defined(__NetBSD__)||defined(__OpenBSD__) #if defined(__OS2__)||defined(__EMX__) puts("Scheduling options:"); #else puts("Scheduling options (need root privileges or a setuid root binary):"); #endif printf( "* -s, --renice Renice to -20 (more scheduling priority),\n" " default: %s\n", (c->renice==RENICE_PRI?"Yes":"No")); #if !defined(__NetBSD__)&&!defined(__OpenBSD__) printf( "* -S, --realtime Get realtime priority (will hog CPU power),\n" " default: %s\n", (c->renice==RENICE_REAL?"Yes":"No")); #endif #endif printf( "Display options:\n" " -q, --quiet Quiet mode, no interface, displays only errors.\n" " --terse Terse output : status line only.\n" #ifdef ENABLE_COLOR_INTERFACE " -C, --color Enable color interface. default: %s\n" #endif "Information options:\n" " -n, --information List all available drivers and module loaders.\n" " -V, --version Display MikMod version.\n" " -h, --help Display this help screen.\n" "Configuration option:\n" " --norc Don't parse the file '%s'\n" " on startup\n" "\n" "Options marked with '*' also exist in negative form (eg --nointerpolate)\n" "F1 or H while playing: Display help panel.\n", #ifdef ENABLE_COLOR_INTERFACE S_B(c->color), #endif conf_name); if (conf_name) free(conf_name); } #if defined(__EMX__)||defined(__OS2__) #ifdef __EMX__ static int kbhit(void) { KBDKEYINFO k; if (KbdPeek(&k,0)) return 0; else return(k.fbStatus&KBDTRF_FINAL_CHAR_IN); } #endif static int mikmod_getch(void) { int c=0; if (kbhit()) { c=getch(); if ((!c)||(c==0xe0)) c=0x100|getch(); } return c; } #define RETSIGTYPE void #else static int mikmod_getch(void) { int c=getch(); return c==ERR?0:c; } #endif /* nice exit function */ static void exit_player(int status,char *message,...) { va_list args; win_exit(); if (library_on) { MikMod_Exit(); library_on=0; } va_start(args,message); if (status>0) vfprintf(stderr,message,args); else vprintf(message,args); va_end(args); set_term_title(NULL); // reset the title exit(status); } /* signal handlers */ static RETSIGTYPE GotoNext(int signum) { next=PL_CONT_NEXT; signal(SIGUSR1,GotoNext); } static RETSIGTYPE GotoPrev(int signum) { next=PL_CONT_PREV; signal(SIGUSR2,GotoPrev); } static RETSIGTYPE ExitGracefully(int signum) { /* can't exit now if playing */ if (player_on) { quit=1; signal(signum,ExitGracefully); } else { win_exit(); /* erase temporary files if necessary */ if ((clean)&&(playfile)&&(archive)&&(archive[0])) unlink(playfile); if (!quiet) fputs((signum==SIGTERM)?"Halted by SIGTERM\n":"Halted by SIGINT\n",stderr); signal(SIGINT,SIG_DFL); signal(SIGTERM,SIG_DFL); set_term_title(NULL); // reset the title exit(0); } } static void Player_SetNextModPos(int pos,int sng_pos) { if (pos<0 || pos>=PL_GetLength(&playlist)) { next=PL_CONT_NEXT; next_sng_pos=sng_pos; } else { next_pl_pos=pos; next_sng_pos=sng_pos; next=PL_CONT_POS; } } void Player_SetNextMod(int pos) { Player_SetNextModPos(pos,0); } static void Player_InitLib(void) { long engineversion=MikMod_GetVersion(); if (engineversion>16)&255,(engineversion>>8)&255,(engineversion)&255, LIBMIKMOD_VERSION_MAJOR,LIBMIKMOD_VERSION_MINOR,LIBMIKMOD_REVISION); /* Register the loaders we want to use: */ MikMod_RegisterAllLoaders(); /* Register the drivers we want to use: */ MikMod_RegisterAllDrivers(); } static void set_priority(CONFIG *cfg) { if (cfg->renice==RENICE_PRI) { #if defined(__FreeBSD__)||defined(__NetBSD__)||defined(__OpenBSD__) setpriority(PRIO_PROCESS,0,-20); #endif #ifdef __linux nice(-20); #endif #if defined(__OS2__)||defined(__EMX__) DosSetPriority(PRTYS_PROCESSTREE,PRTYC_NOCHANGE,20,0); #endif } else if (cfg->renice==RENICE_REAL) { #ifdef __FreeBSD__ struct rtprio rtp; rtp.type=RTP_PRIO_REALTIME; rtp.prio=0; rtprio(RTP_SET,0,&rtp); #endif #ifdef __linux struct sched_param sp; memset(&sp,0,sizeof(struct sched_param)); sp.sched_priority=sched_get_priority_min(SCHED_RR); sched_setscheduler(0,SCHED_RR,&sp); #endif #if defined(__OS2__)||defined(__EMX__) DosSetPriority(PRTYS_PROCESSTREE,PRTYC_TIMECRITICAL,20,0); #endif } } static BOOL cmp_bit(int value,int mask,BOOL cmp) { return (BTST(value,mask))?cmp:!cmp; } void Player_SetConfig(CONFIG *cfg) { static char *driveroptions=NULL; BOOL restart=MikMod_Active() && ((cfg->frequency!=md_mixfreq)|| ((cfg->driver)&&(cfg->driver!=md_device))|| (!cmp_bit(md_mode,DMODE_16BITS,cfg->mode_16bit))|| (!cmp_bit(md_mode,DMODE_STEREO,cfg->stereo))|| (!cmp_bit(md_mode,DMODE_HQMIXER,cfg->hqmixer)) #if LIBMIKMOD_VERSION >= 0x030107 ||((!driveroptions && cfg->driveroptions) || (driveroptions && strcmp(driveroptions,cfg->driveroptions))) #endif ); #if LIBMIKMOD_VERSION >= 0x030107 if (driveroptions) free(driveroptions); driveroptions=strdup(cfg->driveroptions); #endif md_pansep=128; /* panning separation (0=mono 128=full stereo) */ md_volume=(cfg->volume*128)/100; md_reverb=cfg->reverb; md_device=cfg->driver; md_mixfreq=cfg->frequency; md_mode|=DMODE_SOFT_MUSIC; if (cfg->interpolate) md_mode|=DMODE_INTERP; else md_mode&=~DMODE_INTERP; if (cfg->hqmixer) md_mode|=DMODE_HQMIXER; else md_mode&=~DMODE_HQMIXER; if (cfg->surround) md_mode|=DMODE_SURROUND; else md_mode&=~DMODE_SURROUND; if (cfg->mode_16bit) md_mode|=DMODE_16BITS; else md_mode&=~DMODE_16BITS; if (cfg->stereo) md_mode|=DMODE_STEREO; else md_mode&=~DMODE_STEREO; if (restart) { int cur=PL_GetCurrentPos(&playlist),pos=0; if (cur>=0) { if (mf) pos=mf->sngpos; Player_SetNextModPos(cur,pos); } #if LIBMIKMOD_VERSION >= 0x030107 if (MikMod_Reset(cfg->driveroptions)) #else if (MikMod_Reset()) #endif exit_player(1,"MikMod reset error : %s\n", MikMod_strerror(MikMod_errno)); } else win_panel_repaint(); if (!semiquiet) win_init_status(cfg->statusbar); if (mf) mf->wrap=(BTST(config.playmode,PM_MODULE)?1:0); } /* Display the error when loading a file, and take the appropriate resume action */ static void handle_ListError(BOOL tolerant,CHAR *filename,CHAR *archive,BOOL mm_error) { char buf[PATH_MAX+40]=""; if (!tolerant) { if (mm_error) #ifdef HAVE_SNPRINTF snprintf(buf,PATH_MAX+40,"(reason: %s)\n",MikMod_strerror(MikMod_errno)); #else sprintf(buf,"(reason: %s)\n",MikMod_strerror(MikMod_errno)); #endif if (!filename) exit_player(1,"Corrupted playlist, filename is NULL.\n%s",buf); else if (archive) exit_player(1,"MikMod error: can't load \"%s\" from archive \"%s\".\n%s", filename,archive,buf); else exit_player(1,"MikMod error: can't load %s\n%s",filename,buf); } else { if (filename) #ifdef HAVE_SNPRINTF snprintf(buf,PATH_MAX+40,"Error loading list entry \"%s\" !",filename); #else sprintf(buf,"Error loading list entry \"%s\" !",filename); #endif else #ifdef HAVE_SNPRINTF snprintf(buf,PATH_MAX+40,"Error loading list entry !"); #else sprintf(buf,"Error loading list entry !"); #endif display_message(buf); PL_DelEntry(&playlist,PL_GetCurrentPos(&playlist)); } } /* parse an integer argument */ static void get_int(char *arg,int *value,int min,int max) { char *end=NULL; int t=min-1; if (arg) t=strtol(arg,&end,10); if ((end)&&(!*end)&&(t>=min)&&(t<=max)) *value=t; else exit_player(1,mikcopyr "\n\n" "Argument '%s' out of bounds, must be between %d and %d.\n" "Use '%s --help' for more information.\n", arg?arg:"(not given)",min,max,PRG_NAME); } int main(int argc,char *argv[]) { int t,c=0; BOOL finished=0,norc=0,settime=1; char *filename,*pos=NULL; int cfg_maxchn=128; int uservolume=128; long engineversion=MikMod_GetVersion(); #ifdef __EMX__ _wildcard(&argc,&argv); #endif /* Find program name without path component */ PRG_NAME=argv[0]; while (*PRG_NAME) PRG_NAME++; while ((PRG_NAME>=argv[0])&&(*PRG_NAME!=PATH_SEP)) PRG_NAME--; PRG_NAME++; /* Check if configuration file should be loaded */ for (t=0;t=0 #else !=ERR #endif ) { switch (t) { case 'd': /* -d --driver */ #if LIBMIKMOD_VERSION >= 0x030107 if (strlen(optarg)>2) { char *opts=strchr(optarg,','); if (opts) { *opts=0; /* numeric driver specification ? */ if (opts-optarg<=2) get_int(optarg,&config.driver,0,999); else config.driver=MikMod_DriverFromAlias(optarg); CF_set_string(&config.driveroptions,++opts,99); } else config.driver=MikMod_DriverFromAlias(optarg); } else #endif get_int(optarg,&config.driver,0,999); break; case 'o': /* -o --output */ for (pos=optarg;pos&&*pos;pos++) switch (toupper((int)*pos)) { case '1': case '6': config.mode_16bit=1; break; case '8': config.mode_16bit=0; break; case 'S': config.stereo=1; break; case 'M': config.stereo=0; break; } break; case 'f': /* -f --frequency */ get_int(optarg,&config.frequency,4000,60000); break; case 'i': /* -i --interpolate */ config.interpolate=1; break; case 1: /* --nointerpolate */ config.interpolate=0; break; case 2: /* --hqmixer */ config.hqmixer=1; break; case 3: /* --nohqmixer */ config.hqmixer=0; break; case 4: /* --surround */ config.surround=1; break; case 5: /* --nosurround */ config.surround=0; break; case 'r': /* -r --reverb */ get_int(optarg,&config.reverb,0,15); break; case 'v': /* -v --volume */ get_int(optarg,&config.volume,0,100); break; case 'F': /* -F --fadeout */ config.fade=1; break; case 6: /* --nofadeout */ config.fade=0; break; case 'l': /* -l --loops */ config.loop=1; break; case 7: /* --noloops */ config.loop=0; break; case 'a': /* -a --panning */ config.panning=1; break; case 8: /* --nopanning */ config.panning=0; break; case 'x': /* -x --protracker */ config.extspd=0; break; case 9: /* --noprotracker */ config.extspd=1; break; case 'X': /* -X --exitafter */ config.exit_after=1; break; case 15: /* --noexitafter */ config.exit_after=0; break; case 'c': /* -c --curious */ config.curious=1; break; case 10: /* --nocurious */ config.curious=0; break; case 'p': /* -p --playmode */ get_int(optarg,&config.playmode,0, PM_MODULE|PM_MULTI|PM_SHUFFLE|PM_RANDOM); break; case 't': /* -t --tolerant */ config.tolerant=1; break; case 11: /* --notolerant */ config.tolerant=0; break; case 's': /* -s --renice */ config.renice=RENICE_PRI; break; case 'S': /* -S --realtime */ config.renice=RENICE_REAL; break; case 12: /* --norenice --norealtime */ config.renice=RENICE_NONE; break; case 'q': /* -q --quiet */ quiet=1; break; case 14: /* --terse */ semiquiet=1; break; #ifdef ENABLE_COLOR_INTERFACE case 'C': /* --color */ config.color=1; break; case 16: /* --nocolor */ config.color=0; break; #endif case 'n': /* -n --information */ puts(mikcopyr); printf("Sound engine version %ld.%ld.%ld\n", (engineversion>>16)&255,(engineversion>>8)&255, (engineversion)&255); printf("\nAvailable drivers are :\n%s\n" "\nRecognized module formats are :\n%s\n", MikMod_InfoDriver(),MikMod_InfoLoader()); exit(0); case 'V': /* -V --version */ puts(mikcopyr); printf("Sound engine version %ld.%ld.%ld\n", (engineversion>>16)&255,(engineversion>>8)&255, (engineversion)&255); exit(0); case 'h': /* -h --help */ help(&config); exit(0); default: /* ignore errors */ break; } } set_priority(&config); /* Add remaining parameters to the playlist */ for (t=optind;t= 0x030107 if (MikMod_Init(config.driveroptions)) #else if (MikMod_Init()) #endif exit_player(1,"MikMod initialisation error : %s\n", MikMod_strerror(MikMod_errno)); library_on=1; /* initialize interface */ win_init(quiet); attr_init_colors(quiet); display_init(); signal(SIGTERM,ExitGracefully); signal(SIGINT,ExitGracefully); signal(SIGUSR1,GotoNext); signal(SIGUSR2,GotoPrev); while (!quit) { mf=NULL; filename=archive=NULL; switch (next) { case 0: case PL_CONT_NEXT: finished=!PL_ContNext(&playlist,&filename,&archive,config.playmode); break; case PL_CONT_PREV: finished=!PL_ContPrev(&playlist,&filename,&archive); break; case PL_CONT_POS: finished=!PL_ContPos(&playlist,&filename,&archive,next_pl_pos); break; } next=0; settime=1; if (finished && ((PL_GetLength(&playlist)>0) || quiet || semiquiet)) break; if (!finished) { if (!filename) { handle_ListError(config.tolerant,filename,archive,0); continue; } /* load the module */ playfile=MA_dearchive(archive,filename); if (!playfile) { handle_ListError(config.tolerant,filename,archive,0); continue; } display_loadbanner(); clean=1; mf=Player_Load(playfile,cfg_maxchn,config.curious); if ((archive)&&(archive[0])) unlink(playfile); clean=0; free(playfile); if (!mf) { handle_ListError(config.tolerant,filename,archive,1); continue; } mf->extspd=config.extspd; mf->panflag=config.panning; mf->wrap=(BTST(config.playmode,PM_MODULE)?1:0); mf->loop=config.loop; mf->fadeout=config.fade; player_on=1; Player_Start(mf); if (mf->volume>uservolume) Player_SetVolume(uservolume); if (next_sng_pos>0) { Player_SetPosition(next_sng_pos); settime=0; next_sng_pos=0; } } display_start(); /* if we have a quit signal, exit loop */ while (!quit && ((Player_Active() && !next) || (finished && (PL_GetLength(&playlist)<=0))) ) { MikMod_Update(); if (config.volrestrict && mf) if (mf->volume>uservolume) Player_SetVolume(uservolume); #if defined(__OS2__)||defined(__EMX__) DosSleep(5); #else usleep(5000); #endif /* update the status display... */ display_status(); if (!quiet && (c=mikmod_getch())) { if (win_handle_key(c)) c=0; if ((c<256) && (isalpha(c))) c=toupper(c); /* always enabled commands */ switch (c) { case ' ': /* toggle pause */ Player_TogglePause(); win_panel_repaint(); break; case 'N': next=PL_CONT_NEXT; break; case 'P': next=PL_CONT_PREV; break; case 'Q': quit=1; break; case CTRL_L: #if !defined(__OS2__)&&!defined(__EMX__) case KEY_CLEAR: #endif win_panel_repaint(); break; case 'H': win_change_panel(1); break; case 'S': win_change_panel(2); break; case 'I': win_change_panel(3); break; case 'M': win_change_panel(4); break; case 'L': win_change_panel(5); break; case 'C': win_change_panel(6); break; } /* commands which only work when module is not paused */ if (!Player_Paused()) switch(c) { case '+': case KEY_RIGHT: Player_NextPosition(); settime=0; break; case '-': case KEY_LEFT: Player_PrevPosition(); settime=0; break; case 'R': Player_SetPosition(0); settime=1; break; case '(': if (mf) Player_SetSpeed(mf->sngspd-1); settime=0; break; case ')': if (mf) Player_SetSpeed(mf->sngspd+1); settime=0; break; case '{': if (mf) Player_SetTempo(mf->bpm-1); settime=0; break; case '}': if (mf) Player_SetTempo(mf->bpm+1); settime=0; break; case ';': case ':': md_mode^=DMODE_INTERP; display_header(); break; case 'U': md_mode^=DMODE_SURROUND; display_header(); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': Player_SetVolume(uservolume=(((c-'0')<<7)+5)/10); break; case '0': Player_SetVolume(uservolume=128); break; case '<': if (mf && mf->volume) Player_SetVolume(uservolume=mf->volume-1); break; case '>': if (mf && mf->volume<128) Player_SetVolume(uservolume=mf->volume+1); break; } #if !defined(__OS2__)&&!defined(__EMX__) flushinp(); #endif } win_refresh(); } if (!finished) { if ((!quit)&&!BTST(config.playmode,PM_MODULE)&&(!next)&&(settime)) PL_SetTimeCurrent(&playlist,mf->sngtime); PL_SetPlayedCurrent(&playlist); Player_Stop(); /* stop playing */ Player_Free(mf); /* and free the module */ } player_on=0; } MikMod_Exit(); library_on=0; win_exit(); if ((!quit)&&(!quiet)) { if (!finished) exit_player(1,"MikMod error : %s\n",MikMod_strerror(MikMod_errno)); else puts("Finished playlist..."); } if (!norc) { if (config.save_config) CF_Save(&config); if (config.save_playlist) PL_SaveDefault(&playlist); } set_term_title(NULL); // reset the title exit(0); } /* ex:set ts=4: */