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