1 /*
2 * madplay - MPEG audio decoder and player
3 * Copyright (C) 2000-2004 Robert Leslie
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 02111-1307 USA
18 *
19 * $Id: madplay.c,v 1.86 2004/02/23 21:34:53 rob Exp $
20 */
21
22 # ifdef HAVE_CONFIG_H
23 # include "config.h"
24 # endif
25
26 # include "global.h"
27
28 /* include this first to avoid conflicts with MinGW __argc et al. */
29 # include "getopt.h"
30
31 # include <locale.h>
32 # include <stdio.h>
33 # include <stdarg.h>
34 # include <stdlib.h>
35 # include <string.h>
36
37 # ifdef HAVE_ASSERT_H
38 # include <assert.h>
39 # endif
40
41 # ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 # endif
44
45 # ifdef HAVE_FCNTL_H
46 # include <fcntl.h>
47 # endif
48
49 # include <ctype.h>
50 # include <mad.h>
51
52 # include "gettext.h"
53
54 # include "version.h"
55 # include "audio.h"
56 # include "player.h"
57
58 # define FADE_DEFAULT "0:05"
59
60 # if defined(EXPERIMENTAL)
61 static int external_mix;
62 static int experimental;
63 # endif
64
65 static
66 struct option const options[] = {
67 { "adjust-volume", required_argument, 0, 'A' },
68 { "amplify", required_argument, 0, 'a' },
69 { "ancillary-output", required_argument, 0, -'a' },
70 { "attenuate", required_argument, 0, 'a' },
71 { "bit-depth", required_argument, 0, 'b' },
72 { "display-time", required_argument, 0, -'t' },
73 { "downsample", no_argument, 0, -'d' },
74 { "fade-in", optional_argument, 0, -'i' },
75 { "help", no_argument, 0, 'h' },
76 { "ignore-crc", no_argument, 0, 'i' },
77 { "left", no_argument, 0, '1' },
78 { "license", no_argument, 0, -'l' },
79 { "mono", no_argument, 0, 'm' },
80 { "no-dither", no_argument, 0, 'd' },
81 { "output", required_argument, 0, 'o' },
82 { "pre-amp", required_argument, 0, 'a' },
83 { "quiet", no_argument, 0, 'q' },
84 { "repeat", optional_argument, 0, 'r' },
85 { "replay-gain", optional_argument, 0, 'G' },
86 { "right", no_argument, 0, '2' },
87 { "sample-rate", required_argument, 0, 'R' },
88 { "show-tags-only", no_argument, 0, 'T' },
89 { "shuffle", no_argument, 0, 'z' },
90 { "start", required_argument, 0, 's' },
91 { "stereo", no_argument, 0, 'S' },
92 { "time", required_argument, 0, 't' },
93 { "verbose", no_argument, 0, 'v' },
94 { "version", no_argument, 0, 'V' },
95 { "very-quiet", no_argument, 0, 'Q' },
96 # if defined(USE_TTY)
97 { "tty-control", no_argument, 0, -'c' },
98 { "no-tty-control", no_argument, 0, -'C' },
99 # endif
100 # if 0
101 { "cross-fade", no_argument, 0, 'x' },
102 { "fade-out", optional_argument, 0, -'o' },
103 { "gap", required_argument, 0, 'g' },
104 # endif
105 # if defined(EXPERIMENTAL)
106 { "external-mix", no_argument, &external_mix, 1 },
107 { "experimental", no_argument, &experimental, 1 },
108 # endif
109 { 0 }
110 };
111
112 char const *argv0;
113
114 # define EPUTS(str) fputs(str, stream)
115
116 /*
117 * NAME: show_usage()
118 * DESCRIPTION: display usage message
119 */
120 static
show_usage(int verbose)121 void show_usage(int verbose)
122 {
123 FILE *stream = verbose ? stdout : stderr;
124
125 fprintf(stream, _("Usage: %s [OPTIONS] FILE [...]\n"), argv0);
126
127 if (!verbose) {
128 fprintf(stream, _("Try `%s --help' for more information.\n"), argv0);
129 return;
130 }
131
132 EPUTS(_("Decode and play MPEG audio FILE(s).\n"));
133
134 /* the following usage text should agree with the option names */
135
136 EPUTS(_("\nVerbosity:\n"));
137 EPUTS(_(" -v, --verbose show status while decoding\n"));
138 EPUTS(_(" -q, --quiet be quiet but show warnings\n"));
139 EPUTS(_(" -Q, --very-quiet "
140 "be quiet and do not show warnings\n"));
141 EPUTS(_(" --display-time=MODE "
142 "use default verbose time display MODE\n"
143 " (remaining, current, overall)\n"));
144
145 EPUTS(_("\nDecoding:\n"));
146 EPUTS(_(" --downsample reduce sample rate 2:1\n"));
147 EPUTS(_(" -i, --ignore-crc ignore CRC errors\n"));
148 EPUTS(_(" --ancillary-output=PATH write ancillary data to PATH\n"));
149
150 EPUTS(_("\nAudio output:\n"));
151 EPUTS(_(" -o, --output=[TYPE:]PATH "
152 "write output to PATH with format TYPE (below)\n"));
153 EPUTS(_(" -b, --bit-depth=DEPTH request DEPTH bits per sample\n"));
154 EPUTS(_(" -R, --sample-rate=HERTZ "
155 "request HERTZ samples per second\n"));
156 EPUTS(_(" -d, --no-dither "
157 "do not dither output PCM samples\n"));
158 fprintf(stream,
159 _(" --fade-in[=DURATION] "
160 "fade-in songs over DURATION (default %s)\n"), FADE_DEFAULT);
161 # if 0
162 fprintf(stream,
163 _(" --fade-out[=DURATION] "
164 "fade-out songs over DURATION (default %s)\n"), FADE_DEFAULT);
165 EPUTS(_(" -g, --gap=DURATION set inter-song gap to DURATION\n"));
166 EPUTS(_(" -x, --cross-fade "
167 "cross-fade songs (use with negative gap)\n"));
168 # endif
169 EPUTS(_(" -a, --attenuate=DECIBELS "
170 "attenuate signal by DECIBELS (-)\n"));
171 EPUTS(_(" -a, --amplify=DECIBELS amplify signal by DECIBELS (+)\n"));
172 EPUTS(_(" -A, --adjust-volume=DECIBELS "
173 "override per-file volume adjustments\n"));
174 EPUTS(_(" -G, --replay-gain[=PROFILE] "
175 "enable Replay Gain volume adjustments using\n"
176 " PROFILE (radio, audiophile)\n"));
177
178 EPUTS(_("\nChannel selection:\n"));
179 EPUTS(_(" -1, --left "
180 "output first (left) channel only\n"));
181 EPUTS(_(" -2, --right "
182 "output second (right) channel only\n"));
183 EPUTS(_(" -m, --mono "
184 "mix left and right channels for monaural output\n"));
185 EPUTS(_(" -S, --stereo force stereo output\n"));
186
187 # if defined(EXPERIMENTAL)
188 EPUTS(_("\nExperimental:\n"));
189 EPUTS(_(" --external-mix "
190 "output pre-synthesis samples for external mixer\n"));
191 EPUTS(_(" --experimental enable experimental filter\n"));
192 # endif
193
194 EPUTS(_("\nPlayback:\n"));
195 EPUTS(_(" -s, --start=TIME "
196 "skip to begin at TIME (HH:MM:SS.DDD)\n"));
197 EPUTS(_(" -t, --time=DURATION "
198 "play only for DURATION (HH:MM:SS.DDD)\n"));
199 EPUTS(_(" -z, --shuffle randomize file list\n"));
200 EPUTS(_(" -r, --repeat[=MAX] "
201 "play files MAX times, or indefinitely\n"));
202 # if defined(USE_TTY)
203 EPUTS(_(" --tty-control enable keyboard controls\n"));
204 EPUTS(_(" --no-tty-control disable keyboard controls\n"));
205 # endif
206
207 EPUTS(_("\nMiscellaneous:\n"));
208 EPUTS(_(" -T, --show-tags-only "
209 "show ID3/encoder tags only (do not decode)\n"));
210 EPUTS(_(" -V, --version display version number and exit\n"));
211 EPUTS(_(" --license "
212 "show copyright/license message and exit\n"));
213 EPUTS(_(" -h, --help display this help and exit\n"));
214
215 EPUTS(_("\nSupported output formats:\n"));
216 EPUTS(_(" cdda "
217 "CD audio, 16-bit big-endian 44100 Hz stereo PCM (*.cdr, *.cda)\n"));
218 EPUTS(_(" aiff Audio IFF, [16-bit] PCM (*.aif, *.aiff)\n"));
219 EPUTS(_(" wave Microsoft RIFF/WAVE, [16-bit] PCM (*.wav)\n"));
220 fprintf(stream,
221 _(" snd Sun/NeXT audio, 8-bit ISDN %s (*.au, *.snd)\n"),
222 _("mu-law"));
223 EPUTS(_(" raw binary [16-bit] host-endian linear PCM\n"));
224 EPUTS(_(" hex ASCII hexadecimal [24-bit] linear PCM\n"));
225 # if defined(HAVE_LIBESD)
226 EPUTS(_(" esd "
227 "Enlightened Sound Daemon [16-bit] (give speaker host as PATH)\n"));
228 # endif
229 # if defined(HAVE_LIBAUDIO)
230 EPUTS(_(" nas "
231 "Network Audio System [16-bit] (give server name as PATH)\n"));
232 # endif
233 EPUTS(_(" null no output (decode only)\n"));
234 }
235
236 # undef EPUTS
237
238 /*
239 * NAME: verror()
240 * DESCRIPTION: print error message with program title prefix
241 */
242 static
verror(char const * message,va_list args)243 void verror(char const *message, va_list args)
244 {
245 fprintf(stderr, "%s: ", argv0);
246 vfprintf(stderr, message, args);
247 fputc('\n', stderr);
248 }
249
250 /*
251 * NAME: warn()
252 * DESCRIPTION: print warning message
253 */
254 static
warn(char const * message,...)255 void warn(char const *message, ...)
256 {
257 va_list args;
258
259 va_start(args, message);
260 verror(message, args);
261 va_end(args);
262 }
263
264 /*
265 * NAME: die()
266 * DESCRIPTION: exit with failure status after printing message
267 */
268 static
die(char const * message,...)269 void die(char const *message, ...)
270 {
271 va_list args;
272
273 va_start(args, message);
274 verror(message, args);
275 va_end(args);
276
277 exit(1);
278 }
279
280 /*
281 * NAME: parse_time()
282 * DESCRIPTION: parse a time specification string
283 */
284 static
parse_time(mad_timer_t * timer,char const * str)285 int parse_time(mad_timer_t *timer, char const *str)
286 {
287 mad_timer_t time, accum = mad_timer_zero;
288 signed long decimal;
289 unsigned long seconds, fraction, fracpart;
290 int minus;
291
292 while (isspace((unsigned char) *str))
293 ++str;
294
295 do {
296 seconds = fraction = fracpart = 0;
297
298 switch (*str) {
299 case '-':
300 ++str;
301 minus = 1;
302 break;
303
304 case '+':
305 ++str;
306 default:
307 minus = 0;
308 }
309
310 do {
311 decimal = strtol(str, (char **) &str, 10);
312 if (decimal < 0)
313 return -1;
314
315 seconds += decimal;
316
317 if (*str == ':') {
318 seconds *= 60;
319 ++str;
320 }
321 }
322 while (*str >= '0' && *str <= '9');
323
324 if (*str == '.'
325 # if defined(HAVE_LOCALECONV)
326 || *str == *localeconv()->decimal_point
327 # endif
328 ) {
329 char const *ptr;
330
331 decimal = strtol(++str, (char **) &ptr, 10);
332 if (decimal < 0)
333 return -1;
334
335 fraction = decimal;
336
337 for (fracpart = 1; str != ptr; ++str)
338 fracpart *= 10;
339 }
340 else if (*str == '/') {
341 ++str;
342
343 decimal = strtol(str, (char **) &str, 10);
344 if (decimal < 0)
345 return -1;
346
347 fraction = seconds;
348 fracpart = decimal;
349
350 seconds = 0;
351 }
352
353 mad_timer_set(&time, seconds, fraction, fracpart);
354 if (minus)
355 mad_timer_negate(&time);
356
357 mad_timer_add(&accum, time);
358 }
359 while (*str == '-' || *str == '+');
360
361 while (isspace((unsigned char) *str))
362 ++str;
363
364 if (*str != 0)
365 return -1;
366
367 *timer = accum;
368
369 return 0;
370 }
371
372 /*
373 * NAME: get_time()
374 * DESCRIPTION: parse a time value or die
375 */
376 static
get_time(char const * str,int positive,char const * name)377 mad_timer_t get_time(char const *str, int positive, char const *name)
378 {
379 mad_timer_t time;
380
381 if (parse_time(&time, str) == -1)
382 die(_("invalid %s specification \"%s\""), name, str);
383
384 if (positive && mad_timer_sign(time) <= 0)
385 die(_("%s must be positive"), name);
386
387 return time;
388 }
389
390 /*
391 * NAME: parse_decibels()
392 * DESCRIPTION: parse a decibel value specification string
393 */
394 static
parse_decibels(double * db,char const * str)395 int parse_decibels(double *db, char const *str)
396 {
397 *db = strtod(str, (char **) &str);
398
399 while (isspace((unsigned char) *str))
400 ++str;
401
402 if (strncasecmp(str, "dB", 2) == 0) {
403 str += 2;
404
405 while (isspace((unsigned char) *str))
406 ++str;
407 }
408
409 if (*str != 0)
410 return -1;
411
412 return 0;
413 }
414
415 /*
416 * NAME: get_decibels()
417 * DESCRIPTION: parse a decibel value into a linear ratio or die
418 */
419 static
get_decibels(char const * str)420 double get_decibels(char const *str)
421 {
422 double db;
423
424 if (parse_decibels(&db, str) == -1)
425 die(_("invalid decibel specification \"%s\""), str);
426
427 if (db < DB_MIN || db > DB_MAX)
428 die(_("decibel value must be in the range %+d to %+d dB"), DB_MIN, DB_MAX);
429
430 return db;
431 }
432
433 /*
434 * NAME: parse_hertz()
435 * DESCRIPTION: parse a hertz specification string
436 */
437 static
parse_hertz(double * hz,char const * str)438 int parse_hertz(double *hz, char const *str)
439 {
440 *hz = strtod(str, (char **) &str);
441
442 while (isspace((unsigned char) *str))
443 ++str;
444
445 if (*str == 'k' || *str == 'K') {
446 *hz *= 1000;
447
448 do
449 ++str;
450 while (isspace((unsigned char) *str));
451 }
452
453 if (strncasecmp(str, "Hz", 2) == 0) {
454 str += 2;
455
456 while (isspace((unsigned char) *str))
457 ++str;
458 }
459
460 if (*str != 0)
461 return -1;
462
463 return 0;
464 }
465
466 /*
467 * NAME: get_hertz()
468 * DESCRIPTION: parse a hertz value or die
469 */
470 static
get_hertz(char const * str)471 unsigned int get_hertz(char const *str)
472 {
473 double hz;
474
475 enum {
476 HZ_MIN = 1000,
477 HZ_MAX = 0xffff
478 };
479
480 if (parse_hertz(&hz, str) == -1)
481 die(_("invalid hertz specification \"%s\""), str);
482
483 if (hz < HZ_MIN || hz > HZ_MAX)
484 die(_("hertz value must be in the range %u to %u Hz"), HZ_MIN, HZ_MAX);
485
486 return (unsigned int) (hz + 0.5);
487 }
488
489 /*
490 * NAME: get_options()
491 * DESCRIPTION: parse command-line options or die
492 */
493 static
get_options(int argc,char * argv[],struct player * player)494 void get_options(int argc, char *argv[], struct player *player)
495 {
496 int opt, index;
497 int ttyset = 0, preamp = 0;
498
499 while ((opt = getopt_long(argc, argv,
500 "vqQ" /* verbosity options */
501 "i" /* decoding options */
502 "o:b:R:da:A:G::" /* audio output options */
503 # if 0
504 "g:x"
505 # endif
506 "12mS" /* channel selection options */
507 "s:t:zr::" /* playback options */
508 "TVh", /* miscellaneous options */
509 options, &index)) != -1) {
510 switch (opt) {
511 case 0:
512 break;
513
514 case '1':
515 case '2':
516 player->output.select = PLAYER_CHANNEL_LEFT + (opt - '1');
517 break;
518
519 case 'a':
520 player->output.attamp_db = get_decibels(optarg);
521 preamp = 1;
522 break;
523
524 case 'A':
525 player->output.voladj_db = get_decibels(optarg);
526 player->options |= PLAYER_OPTION_IGNOREVOLADJ;
527 break;
528
529 case -'a':
530 if (player->ancillary.path)
531 die(_("multiple output destinations not supported"));
532
533 player->ancillary.path = optarg;
534 break;
535
536 case 'b':
537 opt = atoi(optarg);
538 if (opt <= 0)
539 die(_("invalid bit depth \"%s\""), optarg);
540
541 player->output.precision_in = opt;
542 break;
543
544 # if defined(USE_TTY)
545 case -'c':
546 player->options |= PLAYER_OPTION_TTYCONTROL;
547 ttyset = 1;
548 break;
549
550 case -'C':
551 player->options &= ~PLAYER_OPTION_TTYCONTROL;
552 ttyset = 1;
553 break;
554 # endif
555
556 case 'd':
557 player->output.mode = AUDIO_MODE_ROUND;
558 break;
559
560 case -'d':
561 player->options |= PLAYER_OPTION_DOWNSAMPLE;
562 break;
563
564 # if 0
565 case 'g':
566 player->gap = get_time(optarg, 0, _("gap time"));
567 player->options |= PLAYER_OPTION_GAP;
568 break;
569 # endif
570
571 case 'G':
572 player->output.replay_gain |= PLAYER_RGAIN_ENABLED;
573
574 if (optarg) {
575 size_t len;
576
577 len = strlen(optarg);
578
579 if (len && strncmp(optarg, "audiophile", len) == 0)
580 player->output.replay_gain |= PLAYER_RGAIN_AUDIOPHILE;
581 else if (len && strncmp(optarg, "radio", len) == 0)
582 ;
583 else
584 die(_("invalid Replay Gain argument \"%s\""), optarg);
585 }
586
587 if (!preamp)
588 player->output.attamp_db = +6; /* default pre-amp */
589 break;
590
591 case 'h':
592 show_usage(1);
593 exit(0);
594
595 case 'i':
596 player->options |= PLAYER_OPTION_IGNORECRC;
597 break;
598
599 case -'i':
600 player->fade_in = get_time(optarg ? optarg : FADE_DEFAULT, 1,
601 _("fade-in time"));
602 player->options |= PLAYER_OPTION_FADEIN;
603 break;
604
605 case -'l':
606 ver_license(stdout);
607 exit(0);
608
609 case 'm':
610 player->output.select = PLAYER_CHANNEL_MONO;
611 break;
612
613 case 'o':
614 if (player->output.path)
615 die(_("multiple output destinations not supported"));
616
617 player->output.path = optarg;
618
619 player->output.command = audio_output(&player->output.path);
620 if (player->output.command == 0)
621 die(_("unknown output format type for \"%s\""), optarg);
622
623 if (!ttyset)
624 player->options &= ~PLAYER_OPTION_TTYCONTROL;
625 break;
626
627 # if 0
628 case -'o':
629 player->fade_out = get_time(optarg ? optarg : FADE_DEFAULT, 1,
630 _("fade-out time"));
631 player->options |= PLAYER_OPTION_FADEOUT;
632 break;
633 # endif
634
635 case 'q':
636 player->verbosity = -1;
637
638 if (!ttyset)
639 player->options &= ~PLAYER_OPTION_TTYCONTROL;
640 break;
641
642 case 'Q':
643 player->verbosity = -2;
644
645 if (!ttyset)
646 player->options &= ~PLAYER_OPTION_TTYCONTROL;
647 break;
648
649 case 'r':
650 if (optarg == 0)
651 player->repeat = -1;
652 else {
653 player->repeat = atoi(optarg);
654 if (player->repeat <= 0)
655 die(_("invalid repeat count \"%s\""), optarg);
656 }
657 break;
658
659 case 'R':
660 player->output.speed_request = get_hertz(optarg);
661 break;
662
663 case 's':
664 player->global_start = get_time(optarg, 0, _("start time"));
665 player->options |= PLAYER_OPTION_SKIP;
666 break;
667
668 case 'S':
669 player->output.select = PLAYER_CHANNEL_STEREO;
670 break;
671
672 case 't':
673 player->global_stop = get_time(optarg, 1, _("playing time"));
674 player->options |= PLAYER_OPTION_TIMED;
675 break;
676
677 case -'t':
678 {
679 size_t len;
680
681 len = strlen(optarg);
682
683 if (len && strncmp(optarg, "remaining", len) == 0)
684 player->stats.show = STATS_SHOW_REMAINING;
685 else if (len && strncmp(optarg, "current", len) == 0)
686 player->stats.show = STATS_SHOW_CURRENT;
687 else if (len && strncmp(optarg, "overall", len) == 0)
688 player->stats.show = STATS_SHOW_OVERALL;
689 else
690 die(_("invalid display time argument \"%s\""), optarg);
691 }
692 break;
693
694 case 'T':
695 player->options |= PLAYER_OPTION_SHOWTAGSONLY;
696 break;
697
698 case 'v':
699 player->verbosity = +1;
700 break;
701
702 case 'V':
703 ver_version(stdout);
704 fprintf(stderr, "`%s --license' %s.\n", argv0,
705 _("for license and warranty information"));
706 exit(0);
707
708 # if 0
709 case 'x':
710 player->options |= PLAYER_OPTION_CROSSFADE;
711 break;
712 # endif
713
714 case 'z':
715 player->options |= PLAYER_OPTION_SHUFFLE;
716 break;
717
718 case '?':
719 show_usage(0);
720 exit(1);
721
722 default:
723 assert(!"option handler");
724 }
725 }
726
727 if (optind == argc) {
728 show_usage(0);
729 exit(2);
730 }
731 }
732
733 /*
734 * NAME: main()
735 * DESCRIPTION: program entry point
736 */
main(int argc,char * argv[])737 int main(int argc, char *argv[])
738 {
739 struct player player;
740 int result = 0;
741
742 argv0 = argv[0];
743
744 /* ensure binary standard I/O */
745
746 # if defined(_WIN32)
747 _setmode(_fileno(stdin), _O_BINARY);
748 _setmode(_fileno(stdout), _O_BINARY);
749 # endif
750
751 /* internationalization support */
752
753 # if defined(ENABLE_NLS)
754 setlocale(LC_ALL, "");
755 bindtextdomain(PACKAGE, LOCALEDIR);
756 textdomain(PACKAGE);
757 # endif
758
759 /* initialize and get options */
760
761 player_init(&player);
762
763 # if !defined(__CYGWIN__) /* Cygwin support for this is currently buggy */
764 /* check for default tty control */
765 if (isatty(STDIN_FILENO))
766 player.options |= PLAYER_OPTION_TTYCONTROL;
767 # endif
768
769 get_options(argc, argv, &player);
770
771 /* main processing */
772
773 if (player.verbosity >= 0)
774 ver_banner(stderr);
775
776 if (player.options & PLAYER_OPTION_CROSSFADE) {
777 if (!(player.options & PLAYER_OPTION_GAP))
778 warn(_("cross-fade ignored without gap"));
779 else if (mad_timer_sign(player.gap) >= 0)
780 warn(_("cross-fade ignored without negative gap"));
781 }
782
783 if (player.output.replay_gain & PLAYER_RGAIN_ENABLED) {
784 if (player.options & PLAYER_OPTION_IGNOREVOLADJ)
785 warn(_("volume adjustment ignored with Replay Gain enabled"));
786 else
787 player.options |= PLAYER_OPTION_IGNOREVOLADJ;
788 }
789
790 if ((player.options & PLAYER_OPTION_SHOWTAGSONLY) &&
791 player.repeat != 1) {
792 warn(_("ignoring repeat"));
793 player.repeat = 1;
794 }
795
796 /* make stop time absolute */
797 if (player.options & PLAYER_OPTION_TIMED)
798 mad_timer_add(&player.global_stop, player.global_start);
799
800 /* get default audio output module */
801 if (player.output.command == 0 &&
802 !(player.options & PLAYER_OPTION_SHOWTAGSONLY))
803 player.output.command = audio_output(0);
804
805 # if defined(EXPERIMENTAL)
806 if (external_mix) {
807 player.options |= PLAYER_OPTION_EXTERNALMIX;
808 player.output.command = 0;
809 }
810 if (experimental)
811 player.options |= PLAYER_OPTION_EXPERIMENTAL;
812 # endif
813
814 /* run the player */
815
816 if (player_run(&player, argc - optind, (char const **) &argv[optind]) == -1)
817 result = 4;
818
819 /* finish up */
820
821 player_finish(&player);
822
823 return result;
824 }
825