1 /* uade123 - a simple command line frontend for uadecore.
2 
3    Copyright (C) 2005-2006 Heikki Orsila <heikki.orsila@iki.fi>
4 
5    This source code module is dual licensed under GPL and Public Domain.
6    Hence you may use _this_ module (not another code module) in any way you
7    want in your projects.
8 */
9 
10 #define _GNU_SOURCE
11 
12 #include <assert.h>
13 #include <errno.h>
14 #include <dirent.h>
15 #include <limits.h>
16 #include <signal.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <sys/types.h>
23 #include <sys/wait.h>
24 #include <time.h>
25 #include <unistd.h>
26 #include <getopt.h>
27 
28 #include "uadecontrol.h"
29 #include "uadeipc.h"
30 #include "ossupport.h"
31 #include "uadeconfig.h"
32 #include "eagleplayer.h"
33 #include "uadeconf.h"
34 #include "sysincludes.h"
35 #include "songdb.h"
36 #include "support.h"
37 #include "uadestate.h"
38 #include "uade123.h"
39 #include "playlist.h"
40 #include "playloop.h"
41 #include "audio.h"
42 #include "terminal.h"
43 #include "amigafilter.h"
44 
45 int uade_debug_trigger;
46 int uade_info_mode;
47 double uade_jump_pos = 0.0;
48 
49 int uade_no_audio_output;
50 int uade_no_text_output;
51 
52 char uade_output_file_format[16];
53 char uade_output_file_name[PATH_MAX];
54 struct playlist uade_playlist;
55 FILE *uade_terminal_file;
56 int uade_terminated;
57 int uade_song_end_trigger;
58 
59 static int debug_mode;
60 static char md5name[PATH_MAX];
61 static time_t md5_load_time;
62 static pid_t uadepid = -1;
63 static char uadename[PATH_MAX];
64 
65 
66 static void print_help(void);
67 static void setup_sighandlers(void);
68 ssize_t stat_file_size(const char *name);
69 static void trivial_sigchld(int sig);
70 static void trivial_sigint(int sig);
71 static void cleanup(void);
72 
73 
load_content_db(struct uade_config * uc)74 static void load_content_db(struct uade_config *uc)
75 {
76   struct stat st;
77   time_t curtime = time(NULL);
78   char name[PATH_MAX];
79 
80   if (curtime)
81     md5_load_time = curtime;
82 
83   if (md5name[0] == 0) {
84     char *home = uade_open_create_home();
85     if (home)
86       snprintf(md5name, sizeof md5name, "%s/.uade2/contentdb", home);
87   }
88 
89   /* First try to read users database */
90   if (md5name[0]) {
91     /* Try home directory first */
92     if (stat(md5name, &st) == 0) {
93       if (uade_read_content_db(md5name))
94 	return;
95     } else {
96       FILE *f = fopen(md5name, "w");
97       if (f)
98 	fclose(f);
99       uade_read_content_db(md5name);
100     }
101   }
102 
103   /* Second try to read global database, this does not override any data
104      from user database */
105   snprintf(name, sizeof name, "%s/contentdb", uc->basedir.name);
106   if (stat(name, &st) == 0)
107     uade_read_content_db(name);
108 }
109 
110 
save_content_db(void)111 static void save_content_db(void)
112 {
113   struct stat st;
114   if (md5name[0] && stat(md5name, &st) == 0) {
115 
116     if (md5_load_time < st.st_mtime)
117       uade_read_content_db(md5name);
118 
119     uade_save_content_db(md5name);
120   }
121 }
122 
123 
scan_playlist(struct uade_config * uc)124 static void scan_playlist(struct uade_config *uc)
125 {
126   struct playlist_iterator pli;
127   char *songfile;
128   struct uade_state state = {.config = *uc};
129 
130   playlist_iterator(&pli, &uade_playlist);
131 
132   while (1) {
133     songfile = playlist_iterator_get(&pli);
134     if (songfile == NULL)
135       break;
136 
137     if (!uade_is_our_file(songfile, 1, &state))
138       continue;
139 
140     songfile = canonicalize_file_name(songfile);
141 
142     if (songfile)
143       printf("%s\n", songfile);
144 
145     free(songfile);
146     songfile = NULL;
147   }
148 }
149 
150 
set_song_options(int * songconf_loaded,char * songoptions,char * songconfname,size_t maxname)151 static void set_song_options(int *songconf_loaded, char *songoptions,
152 			     char *songconfname, size_t maxname)
153 {
154   char homesongconfname[PATH_MAX];
155   struct playlist_iterator pli;
156   char *songfile;
157   char *home;
158 
159   home = uade_open_create_home();
160   if (home == NULL)
161     die("No $HOME for song.conf :(\n");
162 
163   snprintf(homesongconfname, sizeof homesongconfname, "%s/.uade2/song.conf", home);
164 
165   if (*songconf_loaded == 0)
166     strlcpy(songconfname, homesongconfname, maxname);
167 
168   playlist_iterator(&pli, &uade_playlist);
169 
170   while (1) {
171     songfile = playlist_iterator_get(&pli);
172     if (songfile == NULL)
173       break;
174 
175     if (!uade_update_song_conf(songconfname, homesongconfname, songfile, songoptions))
176       fprintf(stderr, "Could not update song.conf entry for %s\n", songfile);
177   }
178 }
179 
180 
main(int argc,char * argv[])181 int main(int argc, char *argv[])
182 {
183   int i;
184   char configname[PATH_MAX] = "";
185   char playername[PATH_MAX] = "";
186   char scorename[PATH_MAX] = "";
187   int playernamegiven = 0;
188   char tmpstr[PATH_MAX + 256];
189   long subsong = -1;
190   FILE *listfile = NULL;
191   int have_modules = 0;
192   int ret;
193   char *endptr;
194   int uadeconf_loaded, songconf_loaded;
195   char songconfname[PATH_MAX] = "";
196   char uadeconfname[PATH_MAX];
197   struct uade_config uc_eff, uc_cmdline, uc_main;
198   char songoptions[256] = "";
199   int have_song_options = 0;
200   int plistdir;
201   int scanmode = 0;
202 
203   struct uade_state state;
204 
205   enum {
206     OPT_FIRST = 0x1FFF,
207     OPT_BASEDIR,
208     OPT_REPEAT,
209     OPT_SCAN,
210     OPT_SCOPE,
211     OPT_SET,
212     OPT_STDERR,
213     OPT_VERSION
214   };
215 
216   struct option long_options[] = {
217     {"ao-option",        1, NULL, UC_AO_OPTION},
218     {"basedir",          1, NULL, OPT_BASEDIR},
219     {"buffer-time",      1, NULL, UC_BUFFER_TIME},
220     {"cygwin",           0, NULL, UC_CYGWIN_DRIVE_WORKAROUND},
221     {"debug",            0, NULL, 'd'},
222     {"detect-format-by-content", 0, NULL, UC_CONTENT_DETECTION},
223     {"disable-timeouts", 0, NULL, UC_DISABLE_TIMEOUTS},
224     {"enable-timeouts",  0, NULL, UC_ENABLE_TIMEOUTS},
225     {"ep-option",        1, NULL, 'x'},
226     {"filter",           2, NULL, UC_FILTER_TYPE},
227     {"force-led",        1, NULL, UC_FORCE_LED},
228     {"frequency",        1, NULL, UC_FREQUENCY},
229     {"gain",             1, NULL, 'G'},
230     {"get-info",         0, NULL, 'g'},
231     {"headphones",       0, NULL, UC_HEADPHONES},
232     {"headphones2",      0, NULL, UC_HEADPHONES2},
233     {"help",             0, NULL, 'h'},
234     {"ignore",           0, NULL, 'i'},
235     {"interpolator",     1, NULL, UC_RESAMPLER},
236     {"jump",             1, NULL, 'j'},
237     {"keys",             1, NULL, 'k'},
238     {"list",             1, NULL, '@'},
239     {"magic",            0, NULL, UC_CONTENT_DETECTION},
240     {"no-ep-end-detect", 0, NULL, 'n'},
241     {"no-song-end",      0, NULL, 'n'},
242     {"normalise",        2, NULL, UC_NORMALISE},
243     {"ntsc",             0, NULL, UC_NTSC},
244     {"one",              0, NULL, '1'},
245     {"pal",              0, NULL, UC_PAL},
246     {"panning",          1, NULL, 'p'},
247     {"recursive",        0, NULL, 'r'},
248     {"repeat",           0, NULL, OPT_REPEAT},
249     {"resampler",        1, NULL, UC_RESAMPLER},
250     {"scan",             0, NULL, OPT_SCAN},
251     {"scope",            0, NULL, OPT_SCOPE},
252     {"shuffle",          0, NULL, 'z'},
253     {"set",              1, NULL, OPT_SET},
254     {"silence-timeout",  1, NULL, 'y'},
255     {"speed-hack",       0, NULL, UC_SPEED_HACK},
256     {"stderr",           0, NULL, OPT_STDERR},
257     {"stdout",           0, NULL, 'c'},
258     {"subsong",          1, NULL, 's'},
259     {"subsong-timeout",  1, NULL, 'w'},
260     {"timeout",          1, NULL, 't'},
261     {"verbose",          0, NULL, 'v'},
262     {"version",          0, NULL, OPT_VERSION},
263     {NULL,               0, NULL, 0}
264   };
265 
266   uade_config_set_defaults(&uc_cmdline);
267 
268   if (!playlist_init(&uade_playlist))
269     die("Can not initialize playlist.\n");
270 
271 #define GET_OPT_STRING(x) if (strlcpy((x), optarg, sizeof(x)) >= sizeof(x)) { die("Too long a string for option %c.\n", ret); }
272 
273   while ((ret = getopt_long(argc, argv, "@:1cde:f:gG:hij:k:np:P:rs:S:t:u:vw:x:y:z", long_options, 0)) != -1) {
274     switch (ret) {
275 
276     case '@':
277       listfile = fopen(optarg, "r");
278       if (listfile == NULL)
279 	die("Can not open list file: %s\n", optarg);
280       break;
281 
282     case '1':
283       uade_set_config_option(&uc_cmdline, UC_ONE_SUBSONG, NULL);
284       break;
285 
286     case 'c':
287       strlcpy(uade_output_file_name, "/dev/stdout", sizeof uade_output_file_name);
288       /* Output sample data to stdout so do not print anything on stdout */
289       uade_terminal_file = stderr;
290       break;
291 
292     case 'd':
293       debug_mode = 1;
294       uade_debug_trigger = 1;
295       break;
296     case 'e':
297       GET_OPT_STRING(uade_output_file_format);
298       break;
299 
300     case 'f':
301       GET_OPT_STRING(uade_output_file_name);
302       break;
303 
304     case 'g':
305       uade_info_mode = 1;
306       uade_no_audio_output = 1;
307       uade_no_text_output = 1;
308       uade_set_config_option(&uc_cmdline, UC_ACTION_KEYS, "off");
309       break;
310 
311     case 'G':
312       uade_set_config_option(&uc_cmdline, UC_GAIN, optarg);
313       break;
314 
315     case 'h':
316       print_help();
317       exit(0);
318 
319     case 'i':
320       uade_set_config_option(&uc_cmdline, UC_IGNORE_PLAYER_CHECK, NULL);
321       break;
322 
323     case 'j':
324       uade_jump_pos = strtod(optarg, &endptr);
325       if (*endptr != 0 || uade_jump_pos < 0.0)
326 	die("Invalid jump position: %s\n", optarg);
327       break;
328 
329     case 'k':
330       uade_set_config_option(&uc_cmdline, UC_ACTION_KEYS, optarg);
331       break;
332 
333     case 'n':
334       uade_set_config_option(&uc_cmdline, UC_NO_EP_END, NULL);
335       break;
336 
337     case 'p':
338       uade_set_config_option(&uc_cmdline, UC_PANNING_VALUE, optarg);
339       break;
340 
341     case 'P':
342       GET_OPT_STRING(playername);
343       playernamegiven = 1;
344       have_modules = 1;
345       break;
346 
347     case 'r':
348       uade_set_config_option(&uc_cmdline, UC_RECURSIVE_MODE, NULL);
349       break;
350 
351     case 's':
352       subsong = strtol(optarg, &endptr, 10);
353       if (*endptr != 0 || subsong < 0 || subsong > 255)
354 	die("Invalid subsong string: %s\n", optarg);
355       break;
356 
357     case 'S':
358       GET_OPT_STRING(scorename);
359       break;
360 
361     case 't':
362       uade_set_config_option(&uc_cmdline, UC_TIMEOUT_VALUE, optarg);
363       break;
364 
365     case 'u':
366       GET_OPT_STRING(uadename);
367       break;
368 
369     case 'v':
370       uade_set_config_option(&uc_cmdline, UC_VERBOSE, NULL);
371       break;
372 
373     case 'w':
374       uade_set_config_option(&uc_cmdline, UC_SUBSONG_TIMEOUT_VALUE, optarg);
375       break;
376 
377     case 'x':
378       uade_set_config_option(&uc_cmdline, UC_EAGLEPLAYER_OPTION, optarg);
379       break;
380 
381     case 'y':
382       uade_set_config_option(&uc_cmdline, UC_SILENCE_TIMEOUT_VALUE, optarg);
383       break;
384 
385     case 'z':
386       uade_set_config_option(&uc_cmdline, UC_RANDOM_PLAY, NULL);
387       break;
388 
389     case '?':
390     case ':':
391       exit(1);
392 
393     case OPT_BASEDIR:
394       uade_set_config_option(&uc_cmdline, UC_BASE_DIR, optarg);
395       break;
396 
397     case OPT_REPEAT:
398       playlist_repeat(&uade_playlist);
399       break;
400 
401     case OPT_SCAN:
402       scanmode = 1;
403       /* Set recursive mode in scan mode */
404       uade_set_config_option(&uc_cmdline, UC_RECURSIVE_MODE, NULL);
405       break;
406 
407     case OPT_SCOPE:
408       uade_no_text_output = 1;
409       uade_set_config_option(&uc_cmdline, UC_USE_TEXT_SCOPE, NULL);
410       break;
411 
412     case OPT_SET:
413       have_song_options = 1;
414       strlcpy(songoptions, optarg, sizeof songoptions);
415       break;
416 
417     case OPT_STDERR:
418       uade_terminal_file = stderr;
419       break;
420 
421     case OPT_VERSION:
422       printf("uade123 %s\n", UADE_VERSION);
423       exit(0);
424       break;
425 
426     case UC_AO_OPTION:
427     case UC_BUFFER_TIME:
428     case UC_FILTER_TYPE:
429     case UC_FORCE_LED:
430     case UC_FREQUENCY:
431     case UC_NORMALISE:
432     case UC_RESAMPLER:
433       uade_set_config_option(&uc_cmdline, ret, optarg);
434       break;
435 
436     case UC_CONTENT_DETECTION:
437     case UC_CYGWIN_DRIVE_WORKAROUND:
438     case UC_DISABLE_TIMEOUTS:
439     case UC_ENABLE_TIMEOUTS:
440     case UC_HEADPHONES:
441     case UC_HEADPHONES2:
442     case UC_NTSC:
443     case UC_PAL:
444     case UC_SPEED_HACK:
445       uade_set_config_option(&uc_cmdline, ret, NULL);
446       break;
447 
448     default:
449       die("Impossible option.\n");
450     }
451   }
452 
453   uadeconf_loaded = uade_load_initial_config(uadeconfname, sizeof uadeconfname,
454 					     &uc_main, &uc_cmdline);
455 
456   /* Merge loaded configurations and command line options */
457   uc_eff = uc_main;
458   uade_merge_configs(&uc_eff, &uc_cmdline);
459 
460   if (uadeconf_loaded == 0) {
461     debug(uc_eff.verbose, "Not able to load uade.conf from ~/.uade2/ or %s/.\n", uc_eff.basedir.name);
462   } else {
463     debug(uc_eff.verbose, "Loaded configuration: %s\n", uadeconfname);
464   }
465 
466   songconf_loaded = uade_load_initial_song_conf(songconfname,
467 						sizeof songconfname,
468 						&uc_main, &uc_cmdline);
469 
470   if (songconf_loaded == 0) {
471     debug(uc_eff.verbose, "Not able to load song.conf from ~/.uade2/ or %s/.\n", uc_eff.basedir.name);
472   } else {
473     debug(uc_eff.verbose, "Loaded song.conf: %s\n", songconfname);
474   }
475 
476   /* Read play list from file */
477   if (listfile != NULL) {
478     while (xfgets(tmpstr, sizeof(tmpstr), listfile) != NULL) {
479       if (tmpstr[0] == '#')
480 	continue;
481       if (tmpstr[strlen(tmpstr) - 1] == '\n')
482 	tmpstr[strlen(tmpstr) - 1] = 0;
483       playlist_add(&uade_playlist, tmpstr, uc_eff.recursive_mode, uc_eff.cygwin_drive_workaround);
484     }
485     fclose(listfile);
486     listfile = NULL;
487     have_modules = 1;
488   }
489 
490   /* Read play list from command line parameters */
491   for (i = optind; i < argc; i++) {
492     /* Play files */
493     playlist_add(&uade_playlist, argv[i], uc_eff.recursive_mode, uc_eff.cygwin_drive_workaround);
494     have_modules = 1;
495   }
496 
497   if (scanmode) {
498     scan_playlist(&uc_eff);
499     exit(0);
500   }
501 
502   if (have_song_options) {
503     set_song_options(&songconf_loaded, songoptions, songconfname, sizeof songconfname);
504     exit(0);
505   }
506 
507   load_content_db(&uc_eff);
508 
509   if (uc_eff.random_play)
510     playlist_randomize(&uade_playlist);
511 
512   if (have_modules == 0) {
513     print_help();
514     exit(0);
515   }
516 
517   /* we want to control terminal differently in debug mode */
518   if (debug_mode)
519     uc_eff.action_keys = 0;
520 
521   if (uc_eff.action_keys)
522     setup_terminal();
523 
524   do {
525     DIR *bd = opendir(uc_eff.basedir.name);
526     if (bd == NULL)
527       dieerror("Could not access dir %s", uc_eff.basedir.name);
528 
529     closedir(bd);
530 
531     snprintf(configname, sizeof configname, "%s/uaerc", uc_eff.basedir.name);
532 
533     if (scorename[0] == 0)
534       snprintf(scorename, sizeof scorename, "%s/score", uc_eff.basedir.name);
535 
536     if (uadename[0] == 0)
537       strlcpy(uadename, UADE_CONFIG_UADE_CORE, sizeof uadename);
538 
539     if (access(configname, R_OK))
540       dieerror("Could not read %s", configname);
541 
542     if (access(scorename, R_OK))
543       dieerror("Could not read %s", scorename);
544 
545     if (access(uadename, X_OK))
546       dieerror("Could not execute %s", uadename);
547 
548   } while (0);
549 
550   setup_sighandlers();
551 
552   memset(&state, 0, sizeof state);
553 
554   uade_spawn(&state, uadename, configname);
555 
556   if (!audio_init(&uc_eff))
557     goto cleanup;
558 
559   plistdir = UADE_PLAY_CURRENT;
560 
561   while (1) {
562 
563     ssize_t filesize;
564 
565     /* modulename and songname are a bit different. modulename is the name
566        of the song from uadecore's point of view and songname is the
567        name of the song from user point of view. Sound core considers all
568        custom songs to be players (instead of modules) and therefore modulename
569        will become a zero-string with custom songs. */
570     char modulename[PATH_MAX];
571     char songname[PATH_MAX];
572 
573     if (!playlist_get(modulename, sizeof modulename, &uade_playlist, plistdir))
574       break;
575 
576     plistdir = UADE_PLAY_NEXT;
577 
578     state.config = uc_main;
579     state.song = NULL;
580     state.ep = NULL;
581 
582     if (uc_cmdline.verbose)
583       state.config.verbose = 1;
584 
585     if (playernamegiven == 0) {
586       debug(state.config.verbose, "\n");
587 
588       if (!uade_is_our_file(modulename, 0, &state)) {
589 	fprintf(stderr, "Unknown format: %s\n", modulename);
590 	continue;
591       }
592 
593       debug(state.config.verbose, "Player candidate: %s\n", state.ep->playername);
594 
595       if (strcmp(state.ep->playername, "custom") == 0) {
596 	strlcpy(playername, modulename, sizeof playername);
597 	modulename[0] = 0;
598       } else {
599 	snprintf(playername, sizeof playername, "%s/players/%s", uc_cmdline.basedir.name, state.ep->playername);
600       }
601     }
602 
603     if (strlen(playername) == 0) {
604       fprintf(stderr, "Error: an empty player name given\n");
605       goto cleanup;
606     }
607 
608     /* If no modulename given, try the playername as it can be a custom song */
609     strlcpy(songname, modulename[0] ? modulename : playername, sizeof songname);
610 
611     if (!uade_alloc_song(&state, songname)) {
612       fprintf(stderr, "Can not read %s: %s\n", songname, strerror(errno));
613       continue;
614     }
615 
616     /* The order of parameter processing is important:
617      * 0. set uade.conf options (done before this)
618      * 1. set eagleplayer attributes
619      * 2. set song attributes
620      * 3. set command line options
621      */
622 
623     if (state.ep != NULL)
624       uade_set_ep_attributes(&state);
625 
626     if (uade_set_song_attributes(&state, playername, sizeof playername)) {
627       debug(state.config.verbose, "Song rejected based on attributes: %s\n",
628 	    state.song->module_filename);
629       uade_unalloc_song(&state);
630       continue;
631     }
632 
633     uade_merge_configs(&state.config, &uc_cmdline);
634 
635     /* Now we have the final configuration in "uc". */
636 
637     uade_set_effects(&state);
638 
639     if ((filesize = stat_file_size(playername)) < 0) {
640       fprintf(stderr, "Can not find player: %s (%s)\n", playername, strerror(errno));
641       uade_unalloc_song(&state);
642       continue;
643     }
644 
645     debug(state.config.verbose, "Player: %s (%zd bytes)\n", playername, filesize);
646 
647     fprintf(stderr, "Song: %s (%zd bytes)\n", state.song->module_filename, state.song->bufsize);
648 
649     ret = uade_song_initialization(scorename, playername, modulename, &state);
650     switch (ret) {
651     case UADECORE_INIT_OK:
652       break;
653 
654     case UADECORE_INIT_ERROR:
655       uade_unalloc_song(&state);
656       goto cleanup;
657 
658     case UADECORE_CANT_PLAY:
659 	debug(state.config.verbose, "Uadecore refuses to play the song.\n");
660 	uade_unalloc_song(&state);
661 	continue; /* jump to the beginning of playlist loop */
662 
663     default:
664       die("Unknown error from uade_song_initialization()\n");
665     }
666 
667     if (subsong >= 0)
668       uade_set_subsong(subsong, &state.ipc);
669 
670     plistdir = play_loop(&state);
671 
672     uade_unalloc_song(&state);
673 
674     if (plistdir == UADE_PLAY_FAILURE)
675       goto cleanup;
676     else if (plistdir == UADE_PLAY_EXIT)
677       break;
678   }
679 
680   debug(uc_cmdline.verbose || uc_main.verbose, "Killing child (%d).\n", uadepid);
681   cleanup();
682   return 0;
683 
684  cleanup:
685   cleanup();
686   return 1;
687 }
688 
689 
print_help(void)690 static void print_help(void)
691 {
692   printf("uade123 %s\n", UADE_VERSION);
693   printf(
694 "\n"
695 "Usage: uade123 [<options>] <input file> ...\n"
696 "\n"
697 "OPTIONS:\n"
698 "\n"
699 " -1, --one,          Play at most one subsong per file\n"
700 " -@ filename, --list=filename,  Read playlist of files from 'filename'\n"
701 " --ao-option=x:y,    Set option for libao, where 'x' is an audio driver specific\n"
702 "                     option and 'y' is a value. Note 'x' may not contain\n"
703 "                     ':' character, but 'y' may. This option can be used\n"
704 "                     multiple times to specify multiple options.\n"
705 "                     Example for alsa09 plugin: --ao-option=dev:default\n"
706 " --buffer-time=x,    Set audio buffer length to x milliseconds. The default\n"
707 "                     value is determined by the libao.\n"
708 " -c, --stdout        Write sample data to stdout\n"
709 " --cygwin,           Cygwin path name workaround: X:\\ -> /cygdrive/X\n"
710 " --detect-format-by-content, Detect modules strictly by file content.\n"
711 "                     Detection will ignore file name prefixes.\n"
712 " --disable-timeouts, Disable timeouts. This can be used for songs that are\n"
713 "                     known to end. Useful for recording fixed time pieces.\n"
714 "                     Some formats, such as protracker, disable timeouts\n"
715 "                     automatically, because it is known they will always end.\n"
716 " -e format,          Set output file format. Use with -f. wav is the default\n"
717 "                     format.\n"
718 " --enable-timeouts,  Enable timeouts. See --disable-timeouts.\n"
719 " -f filename,        Write audio output into 'filename' (see -e also)\n"
720 " --filter=model      Set filter model to A500, A1200 or NONE. The default is\n"
721 "                     A500. NONE means disabling the filter.\n"
722 " --filter,           Enable filter emulation. It is enabled by default.\n"
723 " --force-led=0/1,    Force LED state to 0 or 1. That is, filter is OFF or ON.\n"
724 " --frequency=x,      Set output frequency to x Hz. The default is 44,1 kHz.\n"
725 " -G x, --gain=x,     Set volume gain to x in range [0, 128]. Default is 1,0.\n"
726 " -g, --get-info,     Just print playername and subsong info on stdout.\n"
727 "                     Do not play.\n"
728 " -h, --help,         Print help\n"
729 " --headphones,       Enable headphones postprocessing effect.\n"
730 " --headphones2       Enable headphones 2 postprocessing effect.\n"
731 " -i, --ignore,       Ignore eagleplayer fileformat check result. Play always.\n"
732 " -j x, --jump=x,     Jump to time position 'x' seconds from the beginning.\n"
733 "                     fractions of a second are allowed too.\n"
734 " -k 0/1, --keys=0/1, Turn action keys on (1) or off (0) for playback control\n"
735 "                     on terminal. \n"
736 " -n, --no-ep-end-detect, Ignore song end reported by the eagleplayer. Just\n"
737 "                     keep playing. This does not affect timeouts. Check -w.\n"
738 " --ntsc,             Set NTSC mode for playing (can be buggy).\n"
739 " --pal,              Set PAL mode (default)\n"
740 " --normalise,        Enable normalise postprocessing effect.\n"
741 " -p x, --panning=x,  Set panning value in range [0, 2]. 0 is full stereo,\n"
742 "                     1 is mono, and 2 is inverse stereo. The default is 0,7.\n"
743 " -P filename,        Set player name\n"
744 " -r, --recursive,    Recursive directory scan\n"
745 " --repeat,           Play playlist over and over again\n"
746 " --resampler=x       Set resampling method to x, where x = default, sinc\n"
747 "                     or none.\n"
748 " -s x, --subsong=x,  Set subsong 'x'\n"
749 " --scan              Scan given files and directories for playable songs and\n"
750 "                     print their names, one per line on stdout.\n"
751 "                     Example: uade123 --scan /song/directory\n"
752 " --set=\"options\"     Set song.conf options for each given song.\n"
753 " --speed-hack,       Set speed hack on. This gives more virtual CPU power.\n"
754 " --stderr,           Print messages on stderr rather than stdout\n"
755 " -t x, --timeout=x,  Set song timeout in seconds. -1 is infinite.\n"
756 "                     The default is infinite.\n"
757 " -v,  --verbose,     Turn on verbose mode\n"
758 " -w x, --subsong-timeout=x,  Set subsong timeout in seconds. -1 is infinite.\n"
759 "                             Default is 512s\n"
760 " -x y, --ep-option=y, Use eagleplayer option y. Option can be used many times.\n"
761 "                      Example: uade123 -x type:nt10 mod.foobar, will play\n"
762 "                      mod.foobar as a Noisetracker 1.0 module. See eagleplayer\n"
763 "                      options from the man page.\n"
764 " -y x, --silence-timeout=x,  Set silence timeout in seconds. -1 is infinite.\n"
765 "                         Default is 20s\n"
766 " -z, --shuffle,      Randomize playlist order before playing.\n"
767 "\n"
768 "EXPERT OPTIONS:\n"
769 "\n"
770 " --basedir=dirname,  Set uade base directory (contains data files)\n"
771 " -d, --debug,        Enable debug mode (expert only)\n"
772 " -S filename,        Set sound core name\n"
773 " --scope,            Turn on Paula hardware register debug mode\n"
774 " -u uadename,        Set uadecore executable name\n"
775 "\n");
776 
777   print_action_keys();
778 
779   printf(
780 "\n"
781 "Example: Play all songs under /chip/fc directory in shuffling mode:\n"
782 "  uade -z /chip/fc/*\n");
783 }
784 
785 
print_action_keys(void)786 void print_action_keys(void)
787 {
788   tprintf("ACTION KEYS FOR INTERACTIVE MODE:\n");
789   tprintf(" [0-9]         Change subsong.\n");
790   tprintf(" '<'           Previous song.\n");
791   tprintf(" '.'           Skip 10 seconds forward.\n");
792   tprintf(" SPACE, 'b'    Next subsong.\n");
793   tprintf(" 'c'           Pause.\n");
794   tprintf(" 'f'           Toggle filter (takes filter control away from eagleplayer).\n");
795   tprintf(" 'g'           Toggle gain effect.\n");
796   tprintf(" 'h'           Print this list.\n");
797   tprintf(" 'H'           Toggle headphones effect.\n");
798   tprintf(" 'i'           Print module info.\n");
799   tprintf(" 'I'           Print hex dump of head of module.\n");
800   tprintf(" 'N'           Toggle normalise effect.\n");
801   tprintf(" RETURN, 'n'   Next song.\n");
802   tprintf(" 'p'           Toggle postprocessing effects.\n");
803   tprintf(" 'P'           Toggle panning effect. Default value is 0.7.\n");
804   tprintf(" 'q'           Quit.\n");
805   tprintf(" 's'           Toggle between random and normal play.\n");
806   tprintf(" 'v'           Toggle verbose mode.\n");
807   tprintf(" 'x'           Restart current subsong.\n");
808   tprintf(" 'z'           Previous subsong.\n");
809 }
810 
811 
setup_sighandlers(void)812 static void setup_sighandlers(void)
813 {
814   struct sigaction act;
815 
816   memset(&act, 0, sizeof act);
817   act.sa_handler = trivial_sigint;
818 
819   if ((sigaction(SIGINT, &act, NULL)) < 0)
820     dieerror("can not install signal handler SIGINT");
821 
822   memset(&act, 0, sizeof act);
823   act.sa_handler = trivial_sigchld;
824   act.sa_flags = SA_NOCLDSTOP;
825 
826   if ((sigaction(SIGCHLD, &act, NULL)) < 0)
827     dieerror("can not install signal handler SIGCHLD");
828 }
829 
830 
stat_file_size(const char * name)831 ssize_t stat_file_size(const char *name)
832 {
833   struct stat st;
834 
835   if (stat(name, &st))
836     return -1;
837 
838   return st.st_size;
839 }
840 
841 
842 /* test song_end_trigger by taking care of mutual exclusion with signal
843    handlers */
test_song_end_trigger(void)844 int test_song_end_trigger(void)
845 {
846   int ret;
847   sigset_t set;
848 
849   /* Block SIGINT while handling uade_song_end_trigger */
850   if (sigemptyset(&set))
851     goto sigerr;
852   if (sigaddset(&set, SIGINT))
853     goto sigerr;
854   if (sigprocmask(SIG_BLOCK, &set, NULL))
855     goto sigerr;
856 
857   ret = uade_song_end_trigger;
858   uade_song_end_trigger = 0;
859 
860   /* Unblock SIGINT */
861   if (sigprocmask(SIG_UNBLOCK, &set, NULL))
862     goto sigerr;
863 
864   return ret;
865 
866  sigerr:
867   die("signal hell\n");
868 }
869 
870 
cleanup(void)871 static void cleanup(void)
872 {
873   save_content_db();
874 
875   if (uadepid != -1) {
876     kill(uadepid, SIGTERM);
877     uadepid = -1;
878   }
879 
880   audio_close();
881 }
882 
883 
trivial_sigchld(int sig)884 static void trivial_sigchld(int sig)
885 {
886   int status;
887 
888   if (waitpid(-1, &status, WNOHANG) == uadepid) {
889     uadepid = -1;
890     uade_terminated = 1;
891   }
892 }
893 
894 
trivial_sigint(int sig)895 static void trivial_sigint(int sig)
896 {
897   static struct timeval otv = {.tv_sec = 0, .tv_usec = 0};
898   struct timeval tv;
899   int msecs;
900 
901   if (debug_mode == 1) {
902     uade_debug_trigger = 1;
903     return;
904   }
905 
906   uade_song_end_trigger = 1;
907 
908   /* counts number of milliseconds between ctrl-c pushes, and terminates the
909      prog if they are less than 100 msecs apart. */
910   if (gettimeofday(&tv, NULL)) {
911     fprintf(stderr, "Gettimeofday() does not work.\n");
912     return;
913   }
914 
915   msecs = 0;
916   if (otv.tv_sec) {
917     msecs = (tv.tv_sec - otv.tv_sec) * 1000 + (tv.tv_usec - otv.tv_usec) / 1000;
918     if (msecs < 100)
919       exit(1);
920   }
921 
922   otv = tv;
923 }
924