1 /*  MikMod module player
2 	(c) 1998 - 2000 Miodrag Vallat and others - see file AUTHORS for
3 	complete list.
4 
5 	This program is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation; either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	This program is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with this program; if not, write to the Free Software
17 	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 	02111-1307, USA.
19 */
20 
21 /*==============================================================================
22 
23   $Id: mikmod.c,v 1.3 2004/01/30 18:01:40 raph Exp $
24 
25   Module player which uses the MikMod library as the player engine.
26 
27 ==============================================================================*/
28 
29 #ifdef HAVE_CONFIG_H
30 #  include "config.h"
31 #endif
32 
33 #ifdef HAVE_UNISTD_H
34 #  include <unistd.h>
35 #endif
36 #ifdef HAVE_GETOPT_LONG_ONLY
37 #  include <getopt.h>
38 #else
39 #  include "mgetopt.h"
40 #endif
41 #include <ctype.h>
42 #ifndef _WIN32
43 #  include <signal.h>
44 #endif
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #if defined(__OS2__)||defined(__EMX__)
50 #  define INCL_DOS
51 #  define INCL_KBD
52 #  define INCL_DOSPROCESS
53 #  include <os2.h>
54 #endif
55 
56 #if defined(__FreeBSD__)||defined(__NetBSD__)||defined(__OpenBSD__)
57 #  ifdef HAVE_SYS_TIME_H
58 #    include <sys/time.h>
59 #  endif
60 #  include <sys/resource.h>
61 #  include <sys/types.h>
62 #  ifdef __FreeBSD__
63 #    include <sys/rtprio.h>
64 #  endif
65 #endif
66 #if defined(__linux)
67 #  ifdef BROKEN_SCHED
68 #    define _P __P
69 #  endif
70 #  include <sched.h>
71 #endif
72 
73 #include <mikmod.h>
74 
75 #include "player.h"
76 #include "mutilities.h"
77 #include "display.h"
78 #include "rcfile.h"
79 #include "mconfig.h"
80 #include "mlist.h"
81 #include "mlistedit.h"
82 #include "marchive.h"
83 #include "mwindow.h"
84 #include "mdialog.h"
85 #include "mplayer.h"
86 #include "keys.h"
87 
88 #define CFG_MAXCHN		128
89 
90 /* Long options definition */
91 static struct option options[] = {
92 	/* Output options */
93 	{"driver", required_argument, NULL, 'd'},
94 	{"output", required_argument, NULL, 'o'},
95 	{"frequency", required_argument, NULL, 'f'},
96 	{"interpolate", no_argument, NULL, 'i'},
97 	{"nointerpolate", no_argument, NULL, 1},
98 	{"hqmixer", no_argument, NULL, 2},
99 	{"nohqmixer", no_argument, NULL, 3},
100 	{"surround", no_argument, NULL, 4},
101 	{"nosurround", no_argument, NULL, 5},
102 	{"reverb", required_argument, NULL, 'r'},
103 	/* Playback options */
104 	{"volume", required_argument, NULL, 'v'},
105 	{"fadeout", no_argument, NULL, 'F'},
106 	{"nofadeout", no_argument, NULL, 6},
107 	{"loops", no_argument, NULL, 'l'},
108 	{"noloops", no_argument, NULL, 7},
109 	{"panning", no_argument, NULL, 'a'},
110 	{"nopanning", no_argument, NULL, 8},
111 	{"protracker", no_argument, NULL, 'x'},
112 	{"noprotracker", no_argument, NULL, 9},
113 	/* Loading options */
114 	{"directory", required_argument, NULL, 'y'},
115 	{"curious", no_argument, NULL, 'c'},
116 	{"nocurious", no_argument, NULL, 10},
117 	{"playmode", required_argument, NULL, 'p'},
118 	{"tolerant", no_argument, NULL, 't'},
119 	{"notolerant", no_argument, NULL, 11},
120 	/* Scheduling options */
121 	{"renice", no_argument, NULL, 's'},
122 	{"norenice", no_argument, NULL, 12},
123 	{"realtime", no_argument, NULL, 'S'},
124 	{"norealtime", no_argument, NULL, 12},
125 	/* Display options */
126 	{"quiet", no_argument, NULL, 'q'},
127 	/* Information options */
128 	{"information", optional_argument, NULL, 'n'},
129 	{"drvinfo", required_argument, NULL, 'N'},
130 	{"version", no_argument, NULL, 'V'},
131 	{"help", no_argument, NULL, 'h'},
132 	{NULL, 0, NULL, 0}
133 };
134 
135 static const CHAR *PRG_NAME;
136 PLAYLIST playlist;
137 CONFIG config;
138 MODULE *mf = NULL;				/* current module */
139 BOOL quiet = 0;					/* set if quiet mode is enabled */
140 
141 typedef enum {
142 	STATE_INIT,					/* Library not initialised */
143 	STATE_INIT_ERROR,			/* Error during MikMod_Init() */
144 	STATE_ERROR,				/* Error during MikMod_Reset() */
145 	STATE_READY,				/* Player is ready for playing */
146 	STATE_PLAY					/* Playing in progess */
147 } PL_STATE;
148 
149 static struct {
150 	PL_STATE state;
151 	BOOL quit;					/* quit was scheduled */
152 	BOOL listend;				/* end of playlist was reached */
153 	BOOL norc;					/* don't load default config file */
154 } status = {STATE_INIT,0,0,0};
155 
156 /* playlist handling */
157 static int next = 0;			/* 0 or a PL_CONT_xxx code */
158 static int next_pl_pos = 0;		/* for PL_CONT_POS, next pos in playlist */
159 static int next_sng_pos = 0;	/* next pos in module */
160 
161 static BOOL settime = 1;
162 static int uservolume = 128;
163 
164 /* help text */
165 #define S_B(b) ((b)?"Yes":"No")
help(CONFIG * c)166 static void help(CONFIG * c)
167 {
168 	char output[4];
169 	char *conf_name = CF_GetFilename();
170 
171 	puts(mikcopyr);
172 
173 	SNPRINTF(output, 4, "%s%c", c->mode_16bit ? "16" : "8",
174 			 c->stereo ? 's' : 'm');
175 	printf("\n" "Usage: %s [option|-y dir]... [module|playlist]...\n" "\n"
176 		   "Output options:\n"
177 		   "  -d[river] n,options     Use nth driver for output (0: autodetect), default: %d\n"
178 		   "  -o[utput] 8m|8s|16m|16s 8/16 bit output in stereo/mono, default: %s\n"
179 		   "  -f[requency] nnnnn      Set mixing frequency, default: %d\n"
180 		   "* -i[nterpolate]          Use interpolate mixing, default: %s\n"
181 		   "* -hq[mixer]              Use high-quality (but slower) software mixer,\n"
182 		   "                          default: %s\n"
183 		   "* -su[rround]             Use surround mixing, default: %s\n"
184 		   "  -r[everb] nn            Set reverb amount (0-15), default: %d\n"
185 		   "Playback options:\n"
186 		   "  -v[olume] nn            Set volume from 0%% (silence) to 100%%, default: %d%%\n"
187 		   "* -F, -fa[deout]          Force volume fade at the end of module, default: %s\n"
188 		   "* -l[oops]                Enable in-module loops, default: %s\n"
189 		   "* -a, -pa[nning]          Process panning effects, default: %s\n"
190 		   "* -x, -pr[otracker]       Disable extended protracker effects, default: %s\n"
191 		   "Loading options:\n"
192 		   "  -y, -di[rectory] dir    Scan directory recursively for modules\n"
193 		   "* -c[urious]              Look for hidden patterns in module, default: %s\n"
194 		   "  -p[laymode] n           Playlist mode (1: loop module, 2: list multi\n"
195 		   "                             4: shuffle list, 8: list random), default: %d\n"
196 		   "* -t[olerant]             Don't halt on file access errors, default: %s\n",
197 			PRG_NAME, c->driver, output, c->frequency,
198 			S_B(c->interpolate), S_B(c->hqmixer), S_B(c->surround), c->reverb,
199 			c->volume, S_B(c->fade), S_B(c->loop), S_B(c->panning),
200 			S_B(!c->extspd), S_B(c->curious), c->playmode, S_B(c->tolerant));
201 #if defined(__OS2__)||defined(__EMX__)||defined(__linux)||defined(__FreeBSD__)||defined(__NetBSD__)||defined(__OpenBSD__)
202 #if defined(__OS2__)||defined(__EMX__)
203 	printf("Scheduling options:\n");
204 #else
205 	printf("Scheduling options (need root privileges or a setuid root binary):\n");
206 #endif
207 	printf("* -s, -ren[ice]           Renice to -20 (more scheduling priority), default: %s\n",
208 			(c->renice == RENICE_PRI ? "Yes" : "No" ));
209 #if !defined(__NetBSD__)&&!defined(__OpenBSD__)
210 	printf("* -S, -rea[ltime]         Get realtime priority (will hog CPU power), default: %s\n",
211 			(c->renice == RENICE_REAL ? "Yes" : "No" ));
212 
213 #endif
214 #endif
215 	printf("Display options:\n"
216 		   "  -q[uiet]                Quiet mode, no interface, displays only errors.\n"
217 		   "Information options:\n"
218 		   "  -n, -in[formation]      List all available drivers and module loaders.\n"
219 		   "  -N n, -drvinfo          Print information on a specific driver.\n"
220 		   "  -V -ve[rsion]           Display MikMod version.\n"
221 		   "  -h[elp]                 Display this help screen.\n"
222 		   "Configuration option:\n"
223 		   "  -norc                   Don't parse the file '%s' on startup\n"
224 		   "\n"
225 		   "Options marked with '*' also exist in negative form (eg -nointerpolate)\n"
226 		   "F1 or H while playing: Display help panel.\n",
227 		   conf_name);
228 
229 	if (conf_name)
230 		free(conf_name);
231 }
232 
233 /* nice exit function */
exit_player(int exitcode,const char * message,...)234 static void exit_player(int exitcode, const char *message, ...)
235 {
236 	va_list args;
237 
238 	win_exit();
239 	if (status.state > STATE_INIT) {
240 		MikMod_Exit();
241 		status.state = STATE_INIT;
242 	}
243 	if (message) {
244 		va_start(args, message);
245 		if (exitcode > 0)
246 			vfprintf(stderr, message, args);
247 		else if (!quiet)
248 			vprintf(message, args);
249 		va_end(args);
250 	}
251 	if (!exitcode && !status.norc) {
252 		if (config.save_config)
253 			CF_Save(&config);
254 		if (config.save_playlist)
255 			PL_SaveDefault(&playlist);
256 	}
257 
258 	printf("\n");
259 	exit(exitcode);
260 }
261 
262 #ifndef _WIN32
263 /* signal handlers */
GotoNext(int signum)264 static RETSIGTYPE GotoNext(int signum)
265 {
266 	next = PL_CONT_NEXT;
267 
268 	signal(SIGUSR1, GotoNext);
269 }
270 
GotoPrev(int signum)271 static RETSIGTYPE GotoPrev(int signum)
272 {
273 	next = PL_CONT_PREV;
274 
275 	signal(SIGUSR2, GotoPrev);
276 }
277 
ExitGracefully(int signum)278 static RETSIGTYPE ExitGracefully(int signum)
279 {
280 	/* can't exit now if playing */
281 	if (status.state == STATE_PLAY) {
282 		status.quit = 1;
283 		signal(signum, ExitGracefully);
284 	} else {
285 		win_exit();
286 
287 		if (!quiet)
288 			fputs((signum == SIGTERM) ? "Halted by SIGTERM\n" :
289 				  "Halted by SIGINT\n", stderr);
290 
291 		signal(SIGINT, SIG_DFL);
292 		signal(SIGTERM, SIG_DFL);
293 		exit(0);
294 	}
295 }
296 #endif
297 
Player_SetNextModPos(int pos,int sng_pos)298 static void Player_SetNextModPos(int pos, int sng_pos)
299 {
300 	next_pl_pos = pos;
301 	next_sng_pos = sng_pos;
302 	next = PL_CONT_POS;
303 }
304 
Player_SetNextMod(int pos)305 void Player_SetNextMod(int pos)
306 {
307 	Player_SetNextModPos(pos, 0);
308 }
309 
Player_InitLib(void)310 static void Player_InitLib(void)
311 {
312 	long engineversion = MikMod_GetVersion();
313 
314 	if (engineversion < LIBMIKMOD_VERSION)
315 		exit_player(2,
316 					"The current engine version (%ld.%ld.%ld) is too old.\n"
317 					"This programs requires at least version %ld.%ld.%ld\n",
318 					(engineversion >> 16) & 255, (engineversion >> 8) & 255,
319 					(engineversion) & 255, LIBMIKMOD_VERSION_MAJOR,
320 					LIBMIKMOD_VERSION_MINOR, LIBMIKMOD_REVISION);
321 
322 	/* Register the loaders we want to use:  */
323 	MikMod_RegisterAllLoaders();
324 
325 	/* Register the drivers we want to use: */
326 	MikMod_RegisterAllDrivers();
327 }
328 
set_priority(CONFIG * cfg)329 static void set_priority(CONFIG *cfg)
330 {
331 	if (cfg->renice == RENICE_PRI) {
332 #if defined(__FreeBSD__)||defined(__NetBSD__)||defined(__OpenBSD__)
333 		setpriority(PRIO_PROCESS, 0, -20);
334 #endif
335 #ifdef __linux
336 		nice(-20);
337 #endif
338 #if defined(__OS2__)||defined(__EMX__)
339 		DosSetPriority(PRTYS_PROCESSTREE, PRTYC_NOCHANGE, 20, 0);
340 #endif
341 	} else if (cfg->renice == RENICE_REAL) {
342 #ifdef __FreeBSD__
343 		struct rtprio rtp;
344 
345 		rtp.type = RTP_PRIO_REALTIME;
346 		rtp.prio = 0;
347 		rtprio(RTP_SET, 0, &rtp);
348 #endif
349 #ifdef __linux
350 		struct sched_param sp;
351 
352 		memset(&sp, 0, sizeof(struct sched_param));
353 		sp.sched_priority = sched_get_priority_min(SCHED_RR);
354 		sched_setscheduler(0, SCHED_RR, &sp);
355 #endif
356 #if defined(__OS2__)||defined(__EMX__)
357 		DosSetPriority(PRTYS_PROCESSTREE, PRTYC_TIMECRITICAL, 20, 0);
358 #endif
359 	}
360 }
361 
cmp_bit(int value,int mask,BOOL cmp)362 static BOOL cmp_bit (int value, int mask, BOOL cmp)
363 {
364 	return (BTST(value, mask)) ? cmp : !cmp;
365 }
set_bit(UWORD * value,int mask,BOOL boolv)366 static void set_bit (UWORD *value, int mask, BOOL boolv)
367 {
368 	if (boolv)
369 		*value |= mask;
370 	else
371 		*value &= ~mask;
372 }
config_error(const char * err,PL_STATE state)373 static void config_error (const char *err, PL_STATE state)
374 {
375 	if (quiet) {
376 		exit_player (1, "%s: %s.\n", err, MikMod_strerror(MikMod_errno));
377 	} else {
378 		if (win_get_panel() != DISPLAY_CONFIG)
379 			win_change_panel (DISPLAY_CONFIG);
380 		sprintf (storage, "%s:\n  %s.\nTry changing the configuration.",
381 				 err, MikMod_strerror(MikMod_errno));
382 		dlg_message_open (storage, "&Ok", 0, 1, NULL, NULL);
383 		status.state = state;
384 	}
385 }
386 
Player_SetConfig(CONFIG * cfg)387 void Player_SetConfig (CONFIG * cfg)
388 {
389 #if LIBMIKMOD_VERSION >= 0x030107
390 	static char *driveroptions = NULL;
391 #endif
392 	BOOL restart =
393 		MP_Active() &&
394 		( (cfg->frequency != md_mixfreq) ||
395 		  ((cfg->driver) && (cfg->driver != md_device)) ||
396 		  (!cmp_bit(md_mode, DMODE_16BITS, cfg->mode_16bit)) ||
397 		  (!cmp_bit(md_mode, DMODE_STEREO, cfg->stereo)) ||
398 		  (!cmp_bit(md_mode, DMODE_HQMIXER, cfg->hqmixer))
399 #if LIBMIKMOD_VERSION >= 0x030107
400 		  || ( (!driveroptions && cfg->driveroptions) ||
401 			   (driveroptions &&
402 				strcmp(driveroptions, cfg->driveroptions)))
403 #endif
404 	   );
405 	PL_STATE oldstate = status.state;
406 
407 	if (status.state <= STATE_ERROR)
408 		status.state = STATE_READY;
409 
410 #if LIBMIKMOD_VERSION >= 0x030107
411 	if (driveroptions)
412 		free(driveroptions);
413 	driveroptions = strdup(cfg->driveroptions);
414 #endif
415 
416 	md_pansep = 128;			/* panning separation (0=mono 128=full stereo) */
417 	md_volume = (cfg->volume * 128) / 100;
418 	md_reverb = cfg->reverb;
419 
420 	md_device = cfg->driver;
421 	md_mixfreq = cfg->frequency;
422 	md_mode |= DMODE_SOFT_MUSIC;
423 
424 	set_bit (&md_mode, DMODE_INTERP, cfg->interpolate);
425 	set_bit (&md_mode, DMODE_HQMIXER, cfg->hqmixer);
426 	set_bit (&md_mode, DMODE_SURROUND, cfg->surround);
427 	set_bit (&md_mode, DMODE_16BITS, cfg->mode_16bit);
428 	set_bit (&md_mode, DMODE_STEREO, cfg->stereo);
429 
430 	if (!win_has_colors() && cfg->themes[cfg->theme].color)
431 		cfg->theme = THEME_MONO;
432 	win_set_theme (&cfg->themes[cfg->theme]);
433 
434 	if (restart || oldstate == STATE_ERROR) {
435 		int cur = PL_GetCurrentPos(&playlist), pos = 0;
436 
437 		if (cur >= 0) {
438 			if (mf) pos = mf->sngpos;
439 			Player_SetNextModPos(cur, pos);
440 		}
441 		if (mf) MP_End();
442 #if LIBMIKMOD_VERSION >= 0x030107
443 		if (MikMod_Reset(cfg->driveroptions))
444 #else
445 		if (MikMod_Reset())
446 #endif
447 			config_error ("MikMod reset error", STATE_ERROR);
448 		cfg->frequency = md_mixfreq;
449 	} else
450 		win_panel_repaint();
451 	win_init_status(cfg->statusbar);
452 
453 	if (mf)
454 		mf->wrap = (BTST(config.playmode, PM_MODULE) ? 1 : 0);
455 	if (oldstate == STATE_INIT || oldstate == STATE_INIT_ERROR)
456 #if LIBMIKMOD_VERSION >= 0x030107
457 		if (MikMod_Init(config.driveroptions))
458 #else
459 		if (MikMod_Init())
460 #endif
461 			config_error ("MikMod initialisation error", STATE_INIT_ERROR);
462 }
463 
464 /* Display the error when loading a file, and take the appropriate resume
465    action */
handle_ListError(BOOL tolerant,const CHAR * filename,const CHAR * archive,BOOL mm_error)466 static void handle_ListError(BOOL tolerant, const CHAR *filename, const CHAR *archive,
467 							 BOOL mm_error)
468 {
469 	char buf[PATH_MAX + 40] = "";
470 
471 	if (!tolerant) {
472 		if (mm_error)
473 			SNPRINTF(buf, PATH_MAX + 40, "(reason: %s)\n",
474 					 MikMod_strerror(MikMod_errno));
475 		if (!filename)
476 			exit_player(1, "Corrupted playlist, filename is NULL.\n%s", buf);
477 		else if (archive)
478 			exit_player(1,
479 						"MikMod error: can't load \"%s\" from archive \"%s\".\n%s",
480 						filename, archive, buf);
481 		else
482 			exit_player(1, "MikMod error: can't load %s\n%s", filename, buf);
483 	} else {
484 		if (filename)
485 			SNPRINTF(buf, PATH_MAX + 40, "Error loading list entry \"%s\" !",
486 					 filename);
487 		else
488 			SNPRINTF(buf, PATH_MAX + 40, "Error loading list entry !");
489 		display_message(buf);
490 		PL_DelEntry(&playlist, PL_GetCurrentPos(&playlist));
491 	}
492 }
493 
494 /* parse an integer argument */
get_int(const char * arg,int * value,int min,int max)495 static void get_int(const char *arg, int *value, int min, int max)
496 {
497 	char *end = NULL;
498 	int t = min - 1;
499 
500 	if (arg)
501 		t = strtol(arg, &end, 10);
502 	if (end && (!*end) && (t >= min) && (t <= max))
503 		*value = t;
504 	else
505 		exit_player(1, mikcopyr "\n\n"
506 					"Argument '%s' out of bounds, must be between %d and %d.\n"
507 					"Use '%s --help' for more information.\n",
508 					arg ? arg : "(not given)", min, max, PRG_NAME);
509 }
510 
display_driver_help(int drvno)511 static void display_driver_help (int drvno)
512 {
513 #define MAX_VALUES	64
514 	char *version, *cmdline, *cmdend, *cur;
515 
516 	driver_get_info (drvno, &version, &cmdline);
517 	if (!drvno || !version)
518 		exit_player (1, "Bad driver ordinal number: %d\n", drvno);
519 
520 	printf ("Parameter list for %s:\n", version);
521 	free (version);
522 	if (!cmdline) {
523 		printf ("    No arguments with this driver\n");
524 		return;
525 	}
526 
527 	cmdend = cmdline + strlen (cmdline);
528 	cur = cmdline;
529 	while (cur < cmdend) {
530 		char *tmp, *tmp2, *lineend;
531 		char *values [MAX_VALUES];
532 		int nvalues = 0;
533 		char valuetype;
534 		int i;
535 
536 		lineend = strchr (cur, '\n');
537 		if (!lineend)
538 			lineend = cur + strlen (cur);
539 		*lineend = 0;
540 		if (!(tmp = strchr (cur, ':'))) break;
541 		*tmp++ = 0;
542 		valuetype = *tmp;
543 		if (!(tmp = strchr (tmp, ':'))) break;
544 		tmp++;
545 		if (!(tmp2 = strchr (tmp, ':'))) break;
546 		if (valuetype != 't') {
547 			while (tmp < tmp2 && nvalues < MAX_VALUES) {
548 				values [nvalues++] = tmp;
549 				tmp = strchr (tmp, ',');
550 				if (tmp && tmp < tmp2)
551 					*tmp++ = 0;
552 				else
553 					break;
554 			}
555 		} else
556 			values [nvalues++] = tmp;
557 		tmp = tmp2;
558 		*tmp++ = 0;
559 		printf ("    %s (%s): %s\n", cur,
560 				(valuetype == 'c') ? "choice" :
561 				(valuetype == 't') ? "text" :
562 				(valuetype == 'r') ? "range" :
563 				(valuetype == 'b') ? "yes/no" : "unknown",
564 				tmp);
565 		if (valuetype == 'c' || valuetype == 'r') {
566 			printf ("        %s:", valuetype == 'c' ? "values" : "range");
567 			for (i = 0; i < nvalues - 1; i++)
568 				printf (" %s%c", values [i],
569 						i < nvalues - 2 ? ',' : '\n');
570 		}
571 		printf ("        default value: %s\n", values [nvalues - 1]);
572 		cur = lineend + 1;
573 	}
574 	free (cmdline);
575 }
576 
577 /* handle global keys */
player_handle_key(MWINDOW * win,int ch)578 static BOOL player_handle_key(MWINDOW *win, int ch)
579 {
580 	BOOL handled = 1;
581 
582 	if (ch < 256 && isalpha(ch))
583 		ch = toupper(ch);
584 
585 	/* always enabled commands */
586 	switch (ch) {
587 		case ' ':			/* toggle pause */
588 			MP_TogglePause();
589 			win_panel_repaint();
590 			break;
591 		case 'N':
592 			next = PL_CONT_NEXT;
593 			break;
594 		case 'P':
595 			next = PL_CONT_PREV;
596 			break;
597 		case 'Q':
598 			status.quit = 1;
599 			break;
600 		case CTRL_L:
601 #if !defined(__OS2__)&&!defined(__EMX__)&&!defined(__DJGPP__)&&!defined(_WIN32)
602 		case KEY_CLEAR:
603 #endif
604 			win_panel_repaint_force();
605 			break;
606 		case 'H':
607 			win_change_panel(DISPLAY_HELP);
608 			break;
609 		case 'S':
610 			win_change_panel(DISPLAY_SAMPLE);
611 			break;
612 		case 'I':
613 			win_change_panel(DISPLAY_INST);
614 			break;
615 		case 'M':
616 			win_change_panel(DISPLAY_MESSAGE);
617 			break;
618 		case 'L':
619 			win_change_panel(DISPLAY_LIST);
620 			break;
621 		case 'C':
622 			win_change_panel(DISPLAY_CONFIG);
623 			break;
624 #if LIBMIKMOD_VERSION >= 0x030200
625 		case 'V':
626 			win_change_panel(DISPLAY_VOLBARS);
627 			break;
628 		case 'F':
629 			config.fakevolbars = 1 - config.fakevolbars;
630 			break;
631 #endif
632 		default:
633 			handled = 0;
634 	}
635 	/* commands which only work when module is not paused */
636 	if (!MP_Paused()) {
637 		handled = 1;
638 		switch (ch) {
639 			case '+':
640 			case KEY_RIGHT:
641 				Player_NextPosition();
642 				settime = 0;
643 				break;
644 			case '-':
645 			case KEY_LEFT:
646 				Player_PrevPosition();
647 				settime = 0;
648 				break;
649 			case 'R':
650 				Player_SetPosition(0);
651 				settime = 1;
652 				break;
653 			case '(':
654 				if (mf)
655 					Player_SetSpeed(mf->sngspd - 1);
656 				settime = 0;
657 				break;
658 			case ')':
659 				if (mf)
660 					Player_SetSpeed(mf->sngspd + 1);
661 				settime = 0;
662 				break;
663 			case '{':
664 				if (mf)
665 					Player_SetTempo(mf->bpm - 1);
666 				settime = 0;
667 				break;
668 			case '}':
669 				if (mf)
670 					Player_SetTempo(mf->bpm + 1);
671 				settime = 0;
672 				break;
673 			case ';':
674 			case ':':
675 				md_mode ^= DMODE_INTERP;
676 				display_header();
677 				break;
678 			case 'U':
679 				md_mode ^= DMODE_SURROUND;
680 				display_header();
681 				break;
682 			case '1':
683 			case '2':
684 			case '3':
685 			case '4':
686 			case '5':
687 			case '6':
688 			case '7':
689 			case '8':
690 			case '9':
691 				Player_SetVolume(uservolume =
692 								 ((ch - '0') << 7) / 10);
693 				break;
694 			case '0':
695 				Player_SetVolume(uservolume = 128);
696 				break;
697 			case '<':
698 				if (mf && mf->volume)
699 					Player_SetVolume(uservolume = mf->volume - 1);
700 				break;
701 			case '>':
702 				if (mf && mf->volume < 128)
703 					Player_SetVolume(uservolume = mf->volume + 1);
704 				break;
705 			default:
706 				handled = 0;
707 		}
708 	}
709 	return handled;
710 }
711 
player_quit(void)712 static void player_quit(void)
713 {
714 	if (status.quit)
715 		exit_player(0,NULL);
716 	else if (!status.listend)
717 		exit_player(1, "MikMod error: %s\n",
718 					MikMod_strerror(MikMod_errno));
719 	else
720 		exit_player(0,"Finished playlist...");
721 }
722 
player_timeout(MWINDOW * win,void * data)723 static BOOL player_timeout (MWINDOW *win, void *data)
724 {
725 	char *filename, *archive;
726 
727 	/* exit if quit was scheduled */
728 	if (status.quit) {
729 		if (status.state == STATE_PLAY) {
730 			MP_End();
731 			Player_Stop();
732 			Player_Free(mf);
733 			status.state = STATE_READY;
734 		}
735 		mf = NULL;
736 		player_quit();
737 	}
738 
739 	if (status.state >= STATE_READY &&
740 		(!MP_Active() || next || PL_CurrentDeleted(&playlist)) &&
741 		(!status.listend || (PL_GetLength(&playlist) > 0))) {
742 
743 		/* stop playing */
744 		if (status.state == STATE_PLAY) {
745 			MP_End();
746 			if (!BTST(config.playmode, PM_MODULE) && !next && settime)
747 				PL_SetTimeCurrent(&playlist, mf->sngtime);
748 			PL_SetPlayedCurrent(&playlist);
749 
750 			Player_Stop();
751 			Player_Free(mf);
752 			status.state = STATE_READY;
753 		}
754 		mf = NULL;
755 
756 		filename = archive = NULL;
757 		switch (next) {
758 			case 0:
759 			case PL_CONT_NEXT:
760 				status.listend = !PL_ContNext(&playlist, &filename, &archive,
761 											  config.playmode);
762 				break;
763 			case PL_CONT_PREV:
764 				status.listend = !PL_ContPrev(&playlist, &filename, &archive);
765 				break;
766 			case PL_CONT_POS:
767 				status.listend = !PL_ContPos(&playlist, &filename, &archive,
768 											 next_pl_pos);
769 				break;
770 		}
771 		next = 0;
772 		settime = 1;
773 		if (status.listend && (PL_GetLength(&playlist) > 0 || quiet))
774 			player_quit();
775 
776 		if (!status.listend) {
777 			int playfd;
778 			FILE *playfile = NULL;
779 			char *playname;
780 
781 			if (!filename) {
782 				handle_ListError(config.tolerant, filename, archive, 0);
783 				return 1;
784 			}
785 
786 			/* load the module */
787 			playfd = MA_dearchive(archive, filename, &playname);
788 			if (playfd >= 0) playfile = fdopen (playfd, "rb");
789 			if (playfd < 0 || !playfile) {
790 				handle_ListError(config.tolerant, filename, archive, 0);
791 				return 1;
792 			}
793 			display_loadbanner();
794 			mf = Player_LoadFP(playfile, CFG_MAXCHN, config.curious);
795 			fclose (playfile);
796 			if (playname) {
797 				unlink (path_conv_sys(playname));
798 				free (playname);
799 			}
800 			if (!mf) {
801 				handle_ListError(config.tolerant, filename, archive, 1);
802 				return 1;
803 			}
804 
805 			/* start playing */
806 			mf->extspd = config.extspd;
807 			mf->panflag = config.panning;
808 			mf->wrap = (BTST(config.playmode, PM_MODULE) ? 1 : 0);
809 			mf->loop = config.loop;
810 			mf->fadeout = config.fade;
811 			Player_Start(mf);
812 			if (mf->volume > uservolume)
813 				Player_SetVolume(uservolume);
814 			if (next_sng_pos > 0) {
815 				Player_SetPosition(next_sng_pos);
816 				settime = 0;
817 				next_sng_pos = 0;
818 			}
819 			MP_Start();
820 			status.state = STATE_PLAY;
821 		}
822 		display_start();
823 	}
824 
825 	MP_Update();
826 	if (config.volrestrict && mf)
827 		if (mf->volume > uservolume)
828 			MP_Volume(uservolume);
829 
830 	/* update the status display... */
831 	display_status();
832 	win_refresh();
833 	return 1;
834 }
835 
main(int argc,char * argv[])836 int main(int argc, char *argv[])
837 {
838 	int t;
839 	BOOL use_threads = 0;
840 	char *pos = NULL;
841 	long engineversion = MikMod_GetVersion();
842 
843 #ifdef __EMX__
844 	_wildcard(&argc, &argv);
845 #endif
846 
847 	/* Find program name without path component */
848 	pos = FIND_LAST_DIRSEP(argv[0]);
849 	PRG_NAME = (pos)? pos + 1 : argv[0];
850 
851 	for (t = 0; t < argc; t++)
852 		if ((!strcmp(argv[t], "-norc")) || (!strcmp(argv[t], "--norc"))) {
853 			status.norc = 1;
854 			argv[t][0] = 0;
855 			break;
856 		}
857 
858 	/* Read configuration */
859 	CF_Init(&config);
860 	if (!status.norc)
861 		CF_Load(&config);
862 
863 	/* Initialize libmikmod */
864 	Player_InitLib();
865 
866 	/* Setup playlist */
867 	PL_InitList(&playlist);
868 
869 	/* Parse commandline */
870 	opterr = 0;
871 	while ((t = getopt_long_only(argc, argv,
872 								 "d:o:f:r:v:y:p:iFlaxctsSqn::N:Vh",
873 								 options, NULL)) != -1) {
874 		switch (t) {
875 		  case 'd':				/* -d --driver */
876 #if LIBMIKMOD_VERSION >= 0x030107
877 			if (strlen(optarg) > 2) {
878 				char *opts = strchr(optarg, ',');
879 
880 				if (opts) {
881 					*opts = 0;
882 
883 					/* numeric driver specification ? */
884 					if (opts - optarg <= 2)
885 						get_int(optarg, &config.driver, 0, 999);
886 					else
887 						config.driver = MikMod_DriverFromAlias(optarg);
888 
889 					rc_set_string(&config.driveroptions, ++opts, 99);
890 				} else
891 					config.driver = MikMod_DriverFromAlias(optarg);
892 			} else
893 #endif
894 				get_int(optarg, &config.driver, 0, 999);
895 			break;
896 		  case 'o':				/* -o --output */
897 			for (pos = optarg; pos && *pos; pos++)
898 				switch (toupper((int)*pos)) {
899 				  case '1':
900 				  case '6':
901 					config.mode_16bit = 1;
902 					break;
903 				  case '8':
904 					config.mode_16bit = 0;
905 					break;
906 				  case 'S':
907 					config.stereo = 1;
908 					break;
909 				  case 'M':
910 					config.stereo = 0;
911 					break;
912 				}
913 			break;
914 		  case 'f':				/* -f --frequency */
915 			get_int(optarg, &config.frequency, 4000, 60000);
916 			break;
917 		  case 'i':				/* -i --interpolate */
918 			config.interpolate = 1;
919 			break;
920 		  case 1:				/* --nointerpolate */
921 			config.interpolate = 0;
922 			break;
923 		  case 2:				/* --hqmixer */
924 			config.hqmixer = 1;
925 			break;
926 		  case 3:				/* --nohqmixer */
927 			config.hqmixer = 0;
928 			break;
929 		  case 4:				/* --surround */
930 			config.surround = 1;
931 			break;
932 		  case 5:				/* --nosurround */
933 			config.surround = 0;
934 			break;
935 		  case 'r':				/* -r --reverb */
936 			get_int(optarg, &config.reverb, 0, 15);
937 			break;
938 		  case 'v':				/* -v --volume */
939 			get_int(optarg, &config.volume, 0, 100);
940 			break;
941 		  case 'F':				/* -F --fadeout */
942 			config.fade = 1;
943 			break;
944 		  case 6:				/* --nofadeout */
945 			config.fade = 0;
946 			break;
947 		  case 'l':				/* -l --loops */
948 			config.loop = 1;
949 			break;
950 		  case 7:				/* --noloops */
951 			config.loop = 0;
952 			break;
953 		  case 'a':				/* -a --panning */
954 			config.panning = 1;
955 			break;
956 		  case 8:				/* --nopanning */
957 			config.panning = 0;
958 			break;
959 		  case 'x':				/* -x --protracker */
960 			config.extspd = 0;
961 			break;
962 		  case 9:				/* --noprotracker */
963 			config.extspd = 1;
964 			break;
965 		  case 'y':				/* -y --directory */
966 			path_conv(optarg);
967 			list_scan_dir (optarg,quiet);
968 			break;
969 		  case 'c':				/* -c --curious */
970 			config.curious = 1;
971 			break;
972 		  case 10:				/* --nocurious */
973 			config.curious = 0;
974 			break;
975 		  case 'p':				/* -p --playmode */
976 			get_int(optarg, &config.playmode, 0,
977 					PM_MODULE | PM_MULTI | PM_SHUFFLE | PM_RANDOM);
978 			break;
979 		  case 't':				/* -t --tolerant */
980 			config.tolerant = 1;
981 			break;
982 		  case 11:				/* --notolerant */
983 			config.tolerant = 0;
984 			break;
985 		  case 's':				/* -s --renice */
986 			config.renice = RENICE_PRI;
987 			break;
988 		  case 'S':				/* -S --realtime */
989 			config.renice = RENICE_REAL;
990 			break;
991 		  case 12:				/* --norenice --norealtime */
992 			config.renice = RENICE_NONE;
993 			break;
994 		  case 'q':				/* -q --quiet */
995 			quiet = 1;
996 			break;
997 		  case 'n':				/* -n --information */
998 			if (optarg) {
999 				int drvno;
1000 				get_int(optarg, &drvno, 1, 99);
1001 				puts(mikcopyr);
1002 				display_driver_help(drvno);
1003 			} else {
1004 				puts(mikcopyr);
1005 				printf("Sound engine version %ld.%ld.%ld\n",
1006 					   (engineversion >> 16) & 255, (engineversion >> 8) & 255,
1007 					   (engineversion) & 255);
1008 				printf("\nAvailable drivers are :\n%s\n"
1009 					   "\nRecognized module formats are :\n%s\n",
1010 					   MikMod_InfoDriver(), MikMod_InfoLoader());
1011 			}
1012 			exit(0);
1013  		  case 'N':
1014 			{
1015 				int drvno;
1016 				get_int(optarg, &drvno, 1, 99);
1017 				puts(mikcopyr);
1018 				display_driver_help(drvno);
1019 				exit(0);
1020 			}
1021 		  case 'V':				/* --version */
1022 			puts(mikcopyr);
1023 			printf("Sound engine version %ld.%ld.%ld\n",
1024 				   (engineversion >> 16) & 255, (engineversion >> 8) & 255,
1025 				   (engineversion) & 255);
1026 			exit(0);
1027 		  case 'h':				/* -h --help */
1028 			help(&config);
1029 			exit(0);
1030 		  default:
1031 			/* ignore errors */
1032 			break;
1033 		}
1034 	}
1035 	set_priority(&config);
1036 
1037 	/* Add remaining parameters to the playlist */
1038 	for (t = optind; t < argc; t++) {
1039 		if (!quiet) {
1040 			printf("\rScanning files... %c (%d left) ", ("/-\\|")[t & 3],
1041 				   argc - t);
1042 			fflush(stdout);
1043 		}
1044 		path_conv(argv[t]);
1045 		MA_FindFiles(&playlist, argv[t]);
1046 	}
1047 
1048 	if (!PL_GetLength(&playlist) && !status.norc)
1049 		PL_LoadDefault(&playlist);
1050 
1051 	PL_DelDouble(&playlist);
1052 	if (BTST(config.playmode, PM_SHUFFLE))
1053 		PL_Randomize(&playlist);
1054 	PL_InitCurrent(&playlist);
1055 
1056 	if (!quiet)
1057 		puts(mikbanner);
1058 
1059 	/* initialize interface */
1060 	win_init(quiet);
1061 	display_init();
1062 
1063 	Player_SetConfig(&config);
1064 	use_threads = MP_Init();
1065 
1066 #ifndef _WIN32
1067 	signal(SIGTERM, ExitGracefully);
1068 	signal(SIGINT, ExitGracefully);
1069 #if defined(__linux)
1070 	if (!use_threads)
1071 #endif
1072 	{
1073 		signal(SIGUSR1, GotoNext);
1074 		signal(SIGUSR2, GotoPrev);
1075 	}
1076 #endif
1077 
1078 	if (!quiet)
1079 		win_panel_set_handle_key(DISPLAY_ROOT, player_handle_key);
1080 	win_timeout_add (5, player_timeout, NULL);
1081 
1082 	win_run();
1083 	return 0;	/* never reached */
1084 }
1085 
1086 /* ex:set ts=4: */
1087