1 /* UADE2 plugin for XMMS
2 *
3 * Copyright (C) 2005-2006 Heikki Orsila
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 #include <libgen.h>
11 #include <assert.h>
12 #include <stdint.h>
13 #include <signal.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <time.h>
17 #include <unistd.h>
18
19 #include "uadeipc.h"
20 #include "eagleplayer.h"
21 #include "uadeconfig.h"
22 #include "uadecontrol.h"
23 #include "uadeconstants.h"
24 #include "ossupport.h"
25 #include "uadeconf.h"
26 #include "effects.h"
27 #include "sysincludes.h"
28 #include "songdb.h"
29 #include "plugin.h"
30 #include "subsongseek.h"
31 #include "fileinfo.h"
32 #include "songinfo.h"
33 #include "uadestate.h"
34
35
36 #define PLUGIN_DEBUG 0
37
38 #if PLUGIN_DEBUG
39 #define plugindebug(fmt, args...) do { fprintf(stderr, "%s:%d: %s: " fmt, __FILE__, __LINE__, __func__, ## args); } while(0)
40 #else
41 #define plugindebug(fmt, args...)
42 #endif
43
44
45 static int initialize_song(char *filename);
46 static void uade_cleanup(void);
47 static void uade_file_info(char *filename);
48 static void uade_get_song_info(char *filename, char **title, int *length);
49 static int uade_get_time(void);
50 static void uade_init(void);
51 static int uadexmms_is_our_file(char *filename);
52 static void uade_pause(short paused);
53 static void uade_play_file(char *filename);
54 static void uade_seek(int time);
55 static void uade_stop(void);
56 static void uade_info_string(void);
57
58 /* GLOBAL VARIABLE DECLARATIONS */
59
60 static InputPlugin uade_ip = {
61 .description = "UADE " UADE_VERSION,
62 .init = uade_init,
63 .is_our_file = uadexmms_is_our_file,
64 .play_file = uade_play_file,
65 .stop = uade_stop,
66 .pause = uade_pause,
67 .seek = uade_seek,
68 .get_time = uade_get_time,
69 .cleanup = uade_cleanup,
70 .get_song_info = uade_get_song_info,
71 .file_info_box = uade_file_info,
72 };
73
74 static const AFormat sample_format = FMT_S16_NE;
75
76 /* Definition of trigger type:
77 abort_playing variable does not need locking, because it is initialized to
78 false synchronously in play_file, and it is triggered to be true in stop().
79 Other places in the plugin only read the value. We call this type of
80 variable a trigger type.
81
82 The locking strategy in the plugin is that all lockable variables are
83 initialized in play_file() for each song. Using variables after that
84 requires locking. When locking is needed, call uade_lock() and
85 uade_unlock(). */
86
87 static int abort_playing; /* Trigger type */
88
89 static struct uade_state state;
90
91 static char configname[PATH_MAX];
92 static pthread_t decode_thread;
93 static struct uade_config config_backup;
94 static char gui_filename[PATH_MAX];
95 static char gui_formatname[256];
96 static int gui_info_set;
97 static char gui_modulename[256];
98 static char gui_module_filename[PATH_MAX];
99 static char gui_playername[256];
100 static char gui_player_filename[PATH_MAX];
101 static int last_beat_played; /* Lock before use */
102
103 static char contentname[PATH_MAX];
104 static time_t content_mtime;
105
106 static int record_playtime; /* Lock before use */
107 static int plugin_disabled;
108 static char songconfname[PATH_MAX];
109
110 static time_t config_load_time;
111
112
113 int uade_is_paused; /* Lock before use */
114 int uade_thread_running; /* Trigger type */
115 int uade_seek_forward; /* Lock before use */
116 int uade_select_sub; /* Lock before use */
117
118
119 static int uade_config_optimization;
120
121
122 static pthread_mutex_t vlock = PTHREAD_MUTEX_INITIALIZER;
123
124
test_uade_conf(void)125 static void test_uade_conf(void)
126 {
127 struct stat st;
128
129 if (stat(configname, &st))
130 return;
131
132 /* Read only newer files */
133 if (st.st_mtime <= config_load_time)
134 return;
135
136 config_load_time = st.st_mtime;
137
138 uade_load_config(&config_backup, configname);
139 }
140
141
load_config(void)142 static void load_config(void)
143 {
144 test_uade_conf();
145 }
146
147
load_content_db(void)148 static void load_content_db(void)
149 {
150 struct stat st;
151 char name[PATH_MAX];
152 int ret;
153
154 if (contentname[0] == 0) {
155 char *home = uade_open_create_home();
156 if (home)
157 snprintf(contentname, sizeof contentname, "%s/.uade2/contentdb", home);
158 }
159
160 /* User database has priority over global database, so we read it
161 * first */
162 if (contentname[0]) {
163 if (stat(contentname, &st) == 0) {
164 if (content_mtime < st.st_mtime) {
165 ret = uade_read_content_db(contentname);
166 if (stat(contentname, &st) == 0)
167 content_mtime = st.st_mtime;
168 if (ret)
169 return;
170 }
171 } else {
172 FILE *f = fopen(contentname, "w");
173 if (f)
174 fclose(f);
175 uade_read_content_db(contentname);
176 }
177 }
178
179 snprintf(name, sizeof name, "%s/contentdb.conf", config_backup.basedir.name);
180 if (stat(name, &st) == 0 && content_mtime < st.st_mtime) {
181 uade_read_content_db(name);
182 if (stat(name, &st) == 0)
183 content_mtime = st.st_mtime;
184 }
185 }
186
187
uade_cleanup(void)188 static void uade_cleanup(void)
189 {
190 if (state.pid)
191 kill(state.pid, SIGTERM);
192
193 if (contentname[0]) {
194 struct stat st;
195 if (stat(contentname, &st) == 0 && content_mtime >= st.st_mtime)
196 uade_save_content_db(contentname);
197 }
198 }
199
200
uade_file_info(char * filename)201 static void uade_file_info(char *filename)
202 {
203 int adder = 0;
204
205 if (strncmp(filename, "uade://", 7) == 0)
206 adder = 7;
207 uade_gui_file_info(filename + adder, gui_player_filename, gui_modulename, gui_playername, gui_formatname);
208
209 }
210
211
uade_get_cur_subsong(int def)212 int uade_get_cur_subsong(int def)
213 {
214 int subsong;
215 uade_lock();
216 subsong = -1;
217 if (state.song != NULL)
218 subsong = state.song->cur_subsong;
219 uade_unlock();
220 if (subsong == -1)
221 subsong = def;
222 return subsong;
223 }
224
225
uade_get_max_subsong(int def)226 int uade_get_max_subsong(int def)
227 {
228 int subsong;
229 uade_lock();
230 subsong = -1;
231 if (state.song != NULL)
232 subsong = state.song->max_subsong;
233 uade_unlock();
234 if (subsong == -1)
235 subsong = def;
236 return subsong;
237 }
238
239
uade_get_min_subsong(int def)240 int uade_get_min_subsong(int def)
241 {
242 int subsong;
243 uade_lock();
244 subsong = -1;
245 if (state.song != NULL)
246 subsong = state.song->min_subsong;
247 uade_unlock();
248 if (subsong == -1)
249 subsong = def;
250 return subsong;
251 }
252
253
uade_lock(void)254 void uade_lock(void)
255 {
256 if (pthread_mutex_lock(&vlock)) {
257 fprintf(stderr, "UADE2 locking error.\n");
258 exit(-1);
259 }
260 }
261
262
uade_unlock(void)263 void uade_unlock(void)
264 {
265 if (pthread_mutex_unlock(&vlock)) {
266 fprintf(stderr, "UADE2 unlocking error.\n");
267 exit(-1);
268 }
269 }
270
271
272 /* this function is first called by xmms. returns pointer to plugin table */
get_iplugin_info(void)273 InputPlugin *get_iplugin_info(void)
274 {
275 return &uade_ip;
276 }
277
278
279 /* xmms initializes uade by calling this function */
uade_init(void)280 static void uade_init(void)
281 {
282 char *home;
283 int config_loaded;
284
285 config_load_time = time(NULL);
286
287 config_loaded = uade_load_initial_config(configname, sizeof configname,
288 &config_backup, NULL);
289
290 load_content_db();
291
292 uade_load_initial_song_conf(songconfname, sizeof songconfname,
293 &config_backup, NULL);
294
295 home = uade_open_create_home();
296
297 if (home != NULL) {
298 /* If config exists in home, ignore global uade.conf. */
299 snprintf(configname, sizeof configname, "%s/.uade2/uade.conf", home);
300 }
301
302 if (config_loaded == 0) {
303 fprintf(stderr, "No config file found for UADE XMMS plugin. Will try to load config from\n");
304 fprintf(stderr, "$HOME/.uade2/uade.conf in the future.\n");
305 }
306 }
307
308
309 /* XMMS calls this function to check if filename belongs to this plugin. */
uadexmms_is_our_file(char * filename)310 static int uadexmms_is_our_file(char *filename)
311 {
312 int ret;
313
314 if (strncmp(filename, "uade://", 7) == 0)
315 return TRUE;
316
317 uade_lock();
318
319
320 /* This is a performance optimization to avoid re-reading uade.conf
321 * when state.config hasn't yet been read. uade_is_our_file() needs the
322 * config. */
323 if (!state.validconfig) {
324 state.config = config_backup;
325 state.validconfig = 1;
326
327 /* Verify that this condition is true at most once */
328 assert(!uade_config_optimization);
329 uade_config_optimization = 1;
330 }
331
332 ret = uade_is_our_file(filename, 1, &state) ? TRUE : FALSE;
333
334 uade_unlock();
335
336 return ret;
337 }
338
339
340 /* Analyze file format, and handshake with uadecore. */
initialize_song(char * filename)341 static int initialize_song(char *filename)
342 {
343 int ret;
344 char modulename[PATH_MAX];
345 char playername[PATH_MAX];
346 char scorename[PATH_MAX];
347
348 uade_lock();
349
350 state.config = config_backup;
351 state.validconfig = 1;
352
353 state.ep = NULL;
354 state.song = NULL;
355
356 ret = uade_is_our_file(filename, 0, &state);
357
358 if (!ret)
359 goto error;
360
361 strlcpy(modulename, filename, sizeof modulename);
362 strlcpy(gui_module_filename, filename, sizeof gui_module_filename);
363
364 snprintf(scorename, sizeof scorename, "%s/score", state.config.basedir.name);
365
366 if (strcmp(state.ep->playername, "custom") == 0) {
367 strlcpy(playername, modulename, sizeof playername);
368 modulename[0] = 0;
369 gui_module_filename[0] = 0;
370 } else {
371 snprintf(playername, sizeof playername, "%s/players/%s", state.config.basedir.name, state.ep->playername);
372 }
373
374 if (!uade_alloc_song(&state, filename))
375 goto error;
376
377 uade_set_ep_attributes(&state);
378
379 uade_set_song_attributes(&state, playername, sizeof playername);
380
381 uade_set_effects(&state);
382
383 strlcpy(gui_player_filename, playername, sizeof gui_player_filename);
384
385 ret = uade_song_initialization(scorename, playername, modulename, &state);
386 if (ret) {
387 if (ret != UADECORE_CANT_PLAY && ret != UADECORE_INIT_ERROR) {
388 fprintf(stderr, "Can not initialize song. Unknown error.\n");
389 plugin_disabled = 1;
390 }
391 uade_unalloc_song(&state);
392
393 goto error;
394 }
395
396 uade_unlock();
397 return TRUE;
398
399 error:
400 uade_unlock();
401 return FALSE;
402 }
403
play_loop(void * arg)404 static void *play_loop(void *arg)
405 {
406 enum uade_control_state controlstate = UADE_S_STATE;
407 int ret;
408 int left = 0;
409 uint8_t space[UADE_MAX_MESSAGE_SIZE];
410 struct uade_msg *um = (struct uade_msg *) space;
411 int subsong_end = 0;
412 uint16_t *sm;
413 int i;
414 unsigned int play_bytes, tailbytes = 0;
415 uint64_t subsong_bytes = 0;
416 char *reason;
417 uint32_t *u32ptr;
418 int writeoffs;
419 int framesize = UADE_CHANNELS * UADE_BYTES_PER_SAMPLE;
420 int song_end_trigger = 0;
421 int64_t skip_bytes = 0;
422
423 uade_lock();
424 record_playtime = 1;
425 uade_unlock();
426
427 while (1) {
428 if (controlstate == UADE_S_STATE) {
429
430 assert(left == 0);
431
432 if (abort_playing) {
433 uade_lock();
434 record_playtime = 0;
435 uade_unlock();
436 break;
437 }
438
439 uade_lock();
440 if (uade_seek_forward) {
441 skip_bytes += uade_seek_forward * UADE_BYTES_PER_FRAME * state.config.frequency;
442 uade_ip.output->flush(uade_ip.output->written_time() + uade_seek_forward * 1000);
443 uade_seek_forward = 0;
444 }
445 if (uade_select_sub != -1) {
446 state.song->cur_subsong = uade_select_sub;
447
448 uade_change_subsong(&state);
449
450 uade_ip.output->flush(0);
451 uade_select_sub = -1;
452 subsong_end = 0;
453 subsong_bytes = 0;
454
455 /* we do this to avoid timeout, and to not record playtime */
456 state.song->out_bytes = 0;
457 record_playtime = 0;
458
459 uade_info_string();
460 }
461
462 if (subsong_end && song_end_trigger == 0) {
463
464 if (state.song->cur_subsong == -1 || state.song->max_subsong == -1) {
465 song_end_trigger = 1;
466
467 } else {
468
469 state.song->cur_subsong++;
470
471 if (state.song->cur_subsong > state.song->max_subsong) {
472 song_end_trigger = 1;
473 } else {
474 int x = 0;
475
476 uade_change_subsong(&state);
477
478 while (uade_ip.output->buffer_playing()) {
479 /* Sleep at most 5 secs */
480 if (x >= 500) {
481 fprintf(stderr, "UADE: blocking work-around activated.\n");
482 break;
483 }
484 x++;
485 xmms_usleep(10000);
486 }
487 uade_ip.output->flush(0);
488 subsong_end = 0;
489 subsong_bytes = 0;
490
491 uade_gui_subsong_changed(state.song->cur_subsong);
492
493 uade_info_string();
494 }
495 }
496 }
497 uade_unlock();
498
499 if (song_end_trigger) {
500 /* We must drain the audio fast if abort_playing happens (e.g.
501 the user changes song when we are here waiting the sound device) */
502 while (uade_ip.output->buffer_playing() && abort_playing == 0)
503
504 xmms_usleep(10000);
505 break;
506 }
507
508 left = uade_read_request(&state.ipc);
509
510 if (uade_send_short_message(UADE_COMMAND_TOKEN, &state.ipc)) {
511 fprintf(stderr, "Can not send token.\n");
512 return NULL;
513 }
514 controlstate = UADE_R_STATE;
515
516 } else {
517
518 if (uade_receive_message(um, sizeof(space), &state.ipc) <= 0) {
519 fprintf(stderr, "Can not receive events from uade\n");
520 exit(-1);
521 }
522
523 switch (um->msgtype) {
524
525 case UADE_COMMAND_TOKEN:
526 controlstate = UADE_S_STATE;
527 break;
528
529 case UADE_REPLY_DATA:
530 sm = (uint16_t *) um->data;
531 for (i = 0; i < um->size; i += 2) {
532 *sm = ntohs(*sm);
533 sm++;
534 }
535
536 if (subsong_end) {
537 play_bytes = tailbytes;
538 tailbytes = 0;
539 } else {
540 play_bytes = um->size;
541 }
542
543 if (subsong_end == 0 && song_end_trigger == 0 &&
544 uade_test_silence(um->data, play_bytes, &state)) {
545 subsong_end = 1;
546 }
547
548 subsong_bytes += play_bytes;
549 uade_lock();
550 state.song->out_bytes += play_bytes;
551 uade_unlock();
552
553 if (skip_bytes > 0) {
554 if (play_bytes <= skip_bytes) {
555 skip_bytes -= play_bytes;
556 play_bytes = 0;
557 } else {
558 play_bytes -= skip_bytes;
559 skip_bytes = 0;
560 }
561 }
562
563 uade_effect_run(&state.effects, (int16_t *) um->data, play_bytes / framesize);
564 uade_ip.add_vis_pcm(uade_ip.output->written_time(), sample_format, UADE_CHANNELS, play_bytes, um->data);
565
566 writeoffs = 0;
567 while (writeoffs < play_bytes) {
568 int writable;
569 while ((writable = uade_ip.output->buffer_free()) <= 0) {
570 if (abort_playing)
571 goto nowrite;
572 xmms_usleep(10000);
573 }
574
575 if (writable > (play_bytes - writeoffs))
576 writable = play_bytes - writeoffs;
577
578 uade_ip.output->write_audio(&um->data[writeoffs], writable);
579
580 writeoffs += writable;
581 }
582
583
584 nowrite:
585
586 if (state.config.timeout != -1 && state.config.use_timeouts) {
587 if (song_end_trigger == 0) {
588 uade_lock();
589 if (state.song->out_bytes / (UADE_BYTES_PER_FRAME * state.config.frequency) >= state.config.timeout) {
590 song_end_trigger = 1;
591 record_playtime = 0;
592 }
593 uade_unlock();
594 }
595 }
596
597 if (state.config.subsong_timeout != -1 && state.config.use_timeouts) {
598 if (subsong_end == 0 && song_end_trigger == 0) {
599 if (subsong_bytes / (UADE_BYTES_PER_FRAME * state.config.frequency) >= state.config.subsong_timeout) {
600 subsong_end = 1;
601 record_playtime = 0;
602 }
603 }
604 }
605
606 assert (left >= um->size);
607 left -= um->size;
608 break;
609
610 case UADE_REPLY_FORMATNAME:
611 uade_check_fix_string(um, 128);
612 strlcpy(gui_formatname, (char *) um->data, sizeof gui_formatname);
613 strlcpy(state.song->formatname, (char *) um->data, sizeof state.song->formatname);
614 break;
615
616 case UADE_REPLY_MODULENAME:
617 uade_check_fix_string(um, 128);
618 strlcpy(gui_modulename, (char *) um->data, sizeof gui_modulename);
619 strlcpy(state.song->modulename, (char *) um->data, sizeof state.song->modulename);
620 break;
621
622 case UADE_REPLY_MSG:
623 uade_check_fix_string(um, 128);
624 plugindebug("Message: %s\n", (char *) um->data);
625 break;
626
627 case UADE_REPLY_PLAYERNAME:
628 uade_check_fix_string(um, 128);
629 strlcpy(gui_playername, (char *) um->data, sizeof gui_playername);
630 strlcpy(state.song->playername, (char *) um->data, sizeof state.song->playername);
631 break;
632
633 case UADE_REPLY_SONG_END:
634 if (um->size < 9) {
635 fprintf(stderr, "Invalid song end reply\n");
636 exit(-1);
637 }
638 tailbytes = ntohl(((uint32_t *) um->data)[0]);
639 /* next ntohl() is only there for a principle. it is not useful */
640 if (ntohl(((uint32_t *) um->data)[1]) == 0) {
641 /* normal happy song end. go to next subsong if any */
642 subsong_end = 1;
643 } else {
644 /* unhappy song end (error in the 68k side). skip to next song
645 ignoring possible subsongs */
646 song_end_trigger = 1;
647 }
648 i = 0;
649 reason = (char *) &um->data[8];
650 while (reason[i] && i < (um->size - 8))
651 i++;
652 if (reason[i] != 0 || (i != (um->size - 9))) {
653 fprintf(stderr, "Broken reason string with song end notice\n");
654 exit(-1);
655 }
656 /* fprintf(stderr, "Song end (%s)\n", reason); */
657 break;
658
659 case UADE_REPLY_SUBSONG_INFO:
660 if (um->size != 12) {
661 fprintf(stderr, "subsong info: too short a message\n");
662 exit(-1);
663 }
664 u32ptr = (uint32_t *) um->data;
665 uade_lock();
666 state.song->min_subsong = ntohl(u32ptr[0]);
667 state.song->max_subsong = ntohl(u32ptr[1]);
668 state.song->cur_subsong = ntohl(u32ptr[2]);
669
670 if (!(-1 <= state.song->min_subsong && state.song->min_subsong <= state.song->cur_subsong && state.song->cur_subsong <= state.song->max_subsong)) {
671 int tempmin = state.song->min_subsong, tempmax = state.song->max_subsong;
672 fprintf(stderr, "uade: The player is broken. Subsong info does not match with %s.\n", gui_filename);
673 state.song->min_subsong = tempmin <= tempmax ? tempmin : tempmax;
674 state.song->max_subsong = tempmax >= tempmin ? tempmax : tempmin;
675 if (state.song->cur_subsong > state.song->max_subsong)
676 state.song->max_subsong = state.song->cur_subsong;
677 else if (state.song->cur_subsong < state.song->min_subsong)
678 state.song->min_subsong = state.song->cur_subsong;
679 }
680 uade_unlock();
681 break;
682
683 default:
684 fprintf(stderr, "Expected sound data. got %d.\n", um->msgtype);
685 plugin_disabled = 1;
686 return NULL;
687 }
688 }
689 }
690
691 last_beat_played = 1;
692
693 if (uade_send_short_message(UADE_COMMAND_REBOOT, &state.ipc)) {
694 fprintf(stderr, "Can not send reboot.\n");
695 return NULL;
696 }
697
698 if (uade_send_short_message(UADE_COMMAND_TOKEN, &state.ipc)) {
699 fprintf(stderr, "Can not send token.\n");
700 return NULL;
701 }
702
703 do {
704 ret = uade_receive_message(um, sizeof(space), &state.ipc);
705 if (ret < 0) {
706 fprintf(stderr, "Can not receive events from uade.\n");
707 return NULL;
708 }
709 if (ret == 0) {
710 fprintf(stderr, "End of input after reboot.\n");
711 return NULL;
712 }
713 } while (um->msgtype != UADE_COMMAND_TOKEN);
714
715 return NULL;
716 }
717
718
uade_play_file(char * filename)719 static void uade_play_file(char *filename)
720 {
721 char tempname[PATH_MAX];
722 char *t;
723
724 load_config();
725
726 uade_lock();
727
728 abort_playing = 0;
729 last_beat_played = 0;
730 record_playtime = 0;
731
732 uade_is_paused = 0;
733 uade_select_sub = -1;
734 uade_seek_forward = 0;
735
736 assert(state.song == NULL);
737
738 uade_unlock();
739
740 if (strncmp(filename, "uade://", 7) == 0)
741 filename += 7;
742
743 strlcpy(tempname, filename, sizeof tempname);
744 t = basename(tempname);
745 if (t == NULL)
746 t = filename;
747 strlcpy(gui_filename, t, sizeof gui_filename);
748 gui_info_set = 0;
749
750 gui_formatname[0] = 0;
751 gui_modulename[0] = 0;
752 gui_playername[0] = 0;
753 gui_module_filename[0] = 0;
754 gui_player_filename[0] = 0;
755
756 if (!state.pid) {
757 char configname[PATH_MAX];
758 snprintf(configname, sizeof configname, "%s/uaerc", config_backup.basedir.name);
759 uade_spawn(&state, UADE_CONFIG_UADE_CORE, configname);
760 }
761
762 if (!uade_ip.output->open_audio(sample_format, config_backup.frequency, UADE_CHANNELS)) {
763 abort_playing = 1;
764 return;
765 }
766
767 if (plugin_disabled) {
768 fprintf(stderr, "An error has occured. uade plugin is internally disabled.\n");
769 goto err;
770 }
771
772 /* If content db has changed (newer mtime chan previously read) then force
773 a reload */
774 load_content_db();
775
776 /* Save current db if an hour has passed */
777 if (contentname[0]) {
778 time_t curtime = time(NULL);
779 if (curtime >= (content_mtime + 3600)) {
780 struct stat st;
781 uade_save_content_db(contentname);
782 if (stat(contentname, &st) == 0)
783 content_mtime = st.st_mtime;
784 }
785 }
786
787 if (initialize_song(filename) == FALSE)
788 goto err;
789
790 if (pthread_create(&decode_thread, NULL, play_loop, NULL)) {
791 fprintf(stderr, "uade: can't create play_loop() thread\n");
792 goto err;
793 }
794
795 uade_thread_running = 1;
796 return;
797
798 err:
799 /* close audio that was opened */
800 uade_ip.output->close_audio();
801 abort_playing = 1;
802
803 uade_lock();
804
805 if (state.song)
806 uade_unalloc_song(&state);
807
808 uade_unlock();
809 }
810
uade_stop(void)811 static void uade_stop(void)
812 {
813 /* Signal other subsystems to proceed to finished state as soon as possible
814 */
815 abort_playing = 1;
816
817 /* Wait for playing thread to finish */
818 if (uade_thread_running) {
819 pthread_join(decode_thread, NULL);
820 uade_thread_running = 0;
821 }
822
823 uade_gui_close_subsong_win();
824
825 if (state.song != NULL) {
826 /* If song ended volutarily, tell the play time for XMMS. */
827 uade_lock();
828 if (record_playtime) {
829 int play_time = (state.song->out_bytes * 1000) / (UADE_BYTES_PER_FRAME * state.config.frequency);
830 if (state.song->md5[0] != 0)
831 uade_add_playtime(state.song->md5, play_time);
832
833 state.song->playtime = play_time;
834 state.song->cur_subsong = state.song->max_subsong;
835 uade_info_string();
836 }
837
838 /* We must free uadesong after playthread has finished and additional
839 GUI windows have been closed. */
840 uade_unalloc_song(&state);
841
842 uade_unlock();
843 }
844
845 uade_ip.output->close_audio();
846 }
847
848
849 /* XMMS calls this function when pausing or unpausing */
uade_pause(short paused)850 static void uade_pause(short paused)
851 {
852 uade_lock();
853 uade_is_paused = paused;
854 uade_unlock();
855 uade_ip.output->pause(paused);
856 }
857
858
859 /* XMMS calls this function when song is seeked */
uade_seek(int time)860 static void uade_seek(int time)
861 {
862 uade_gui_seek_subsong(time);
863 }
864
865
866 /* XMMS calls this function periodically to determine current playing time.
867 We use this function to report song name and title after play_file(),
868 and to tell XMMS to end playing if song ends for any reason. */
uade_get_time(void)869 static int uade_get_time(void)
870 {
871 if (abort_playing || last_beat_played)
872 return -1;
873
874 if (gui_info_set == 0 && state.song->max_subsong != -1) {
875
876 uade_lock();
877
878 if (state.song->max_subsong != -1)
879 uade_info_string();
880
881 gui_info_set = 1;
882 uade_unlock();
883
884 file_info_update(gui_module_filename, gui_player_filename, gui_modulename, gui_playername, gui_formatname);
885 }
886
887 return uade_ip.output->output_time();
888 }
889
890
uade_get_song_info(char * filename,char ** title,int * length)891 static void uade_get_song_info(char *filename, char **title, int *length)
892 {
893 char tempname[PATH_MAX];
894 char *t;
895
896 if (strncmp(filename, "uade://", 7) == 0)
897 filename += 7;
898
899 strlcpy(tempname, filename, sizeof tempname);
900 t = basename(tempname);
901 if (t == NULL)
902 t = filename;
903 if ((*title = strdup(t)) == NULL)
904 plugindebug("Not enough memory for song info.\n");
905 *length = -1;
906 }
907
908
uade_info_string(void)909 static void uade_info_string(void)
910 {
911 char info[256];
912 int playtime = state.song->playtime;
913
914 /* Hack. Set info text and song length late because we didn't know
915 subsong amounts before this. Pass zero as a length so that the
916 graphical play time counter will run but seek is still enabled.
917 Passing -1 as playtime would disable seeking. */
918 if (playtime <= 0)
919 playtime = 0;
920
921 if (uade_generate_song_title(info, sizeof info, &state))
922 strlcpy(info, gui_filename, sizeof info);
923
924 uade_ip.set_info(info, playtime, UADE_BYTES_PER_FRAME * state.config.frequency,
925 state.config.frequency, UADE_CHANNELS);
926 }
927