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