1 /*
2 * Copyright (c) 2011 Tim van der Molen <tim@kariliq.nl>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <pthread.h>
18 #include <signal.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21
22 #include "siren.h"
23
24 #ifdef HAVE_FREEBSD_BSWAP16
25 #include <sys/endian.h>
26 #endif
27
28 #ifdef HAVE_NETBSD_BSWAP16
29 #include <sys/types.h>
30 #include <machine/bswap.h>
31 #endif
32
33 #ifdef HAVE_OPENBSD_SWAP16
34 #include <sys/types.h>
35 #endif
36
37 #if defined(HAVE_FREEBSD_BSWAP16) || defined(HAVE_NETBSD_BSWAP16)
38 #define PLAYER_SWAP16(i) bswap16(i)
39 #define PLAYER_SWAP32(i) bswap32(i)
40 #elif defined(HAVE_OPENBSD_SWAP16)
41 #define PLAYER_SWAP16(i) swap16(i)
42 #define PLAYER_SWAP32(i) swap32(i)
43 #elif defined(HAVE___BUILTIN_BSWAP16)
44 #define PLAYER_SWAP16(i) __builtin_bswap16(i)
45 #define PLAYER_SWAP32(i) __builtin_bswap32(i)
46 #else
47 #define PLAYER_SWAP16(i) ((uint16_t)(i) >> 8 | (uint16_t)(i) << 8)
48 #define PLAYER_SWAP32(i) \
49 ((i) >> 24 | ((i) & 0xff0000) >> 16 | ((i) & 0xff00) << 8 | (i) << 24)
50 #endif
51
52 #define PLAYER_FMT_CONTINUE 0
53 #define PLAYER_FMT_DURATION 1
54 #define PLAYER_FMT_POSITION 2
55 #define PLAYER_FMT_REPEAT_ALL 3
56 #define PLAYER_FMT_REPEAT_TRACK 4
57 #define PLAYER_FMT_SOURCE 5
58 #define PLAYER_FMT_STATE 6
59 #define PLAYER_FMT_VOLUME 7
60 #define PLAYER_FMT_NVARS 8
61
62 enum player_command {
63 PLAYER_COMMAND_PAUSE,
64 PLAYER_COMMAND_PLAY,
65 PLAYER_COMMAND_QUIT,
66 PLAYER_COMMAND_STOP
67 };
68
69 enum player_state {
70 PLAYER_STATE_PAUSED,
71 PLAYER_STATE_PLAYING,
72 PLAYER_STATE_STOPPED
73 };
74
75 static void player_close_op(void);
76 static int player_open_op(void);
77 static void *player_playback_handler(void *);
78 static void player_print_status(void);
79 static void player_print_track(void);
80 static void player_quit(void);
81 static void player_set_signal_mask(void);
82
83 static pthread_t player_playback_thd;
84
85 static enum player_state player_state = PLAYER_STATE_STOPPED;
86 static pthread_mutex_t player_state_mtx = PTHREAD_MUTEX_INITIALIZER;
87 static enum player_command player_command = PLAYER_COMMAND_STOP;
88 static pthread_cond_t player_command_cond =
89 PTHREAD_COND_INITIALIZER;
90
91 static pthread_mutex_t player_source_mtx = PTHREAD_MUTEX_INITIALIZER;
92 static enum player_source player_source = PLAYER_SOURCE_LIBRARY;
93
94 static const struct op *player_op = NULL;
95 static int player_op_opened;
96 static pthread_mutex_t player_op_mtx = PTHREAD_MUTEX_INITIALIZER;
97
98 static struct track *player_track = NULL;
99 static pthread_mutex_t player_track_mtx = PTHREAD_MUTEX_INITIALIZER;
100
101 static enum byte_order player_byte_order;
102
103 /*
104 * The player_state_mtx mutex must be locked before calling this function.
105 */
106 static int
player_begin_playback(struct sample_buffer * sb)107 player_begin_playback(struct sample_buffer *sb)
108 {
109 XPTHREAD_MUTEX_LOCK(&player_track_mtx);
110 XPTHREAD_MUTEX_LOCK(&player_op_mtx);
111
112 if (player_track == NULL)
113 goto error1;
114
115 if (player_track->ip == NULL) {
116 msg_errx("%s: Unsupported file format", player_track->path);
117 goto error1;
118 }
119
120 if (player_track->ip->open(player_track))
121 goto error1;
122
123 LOG_DEBUG("rate=%u, nchannels=%u, nbits=%u", player_track->format.rate,
124 player_track->format.nchannels, player_track->format.nbits);
125
126 if (player_open_op() == -1)
127 goto error2;
128
129 if (player_op->start(&player_track->format) == -1)
130 goto error2;
131
132 if (player_track->format.nbits <= 8)
133 sb->nbytes = 1;
134 else if (player_track->format.nbits <= 16)
135 sb->nbytes = 2;
136 else
137 sb->nbytes = 4;
138
139 sb->size_b = player_op->get_buffer_size();
140 sb->size_s = sb->size_b / sb->nbytes;
141
142 if (sb->size_s == 0) {
143 msg_errx("Output buffer too small");
144 goto error2;
145 }
146
147 sb->data = xmalloc(sb->size_b);
148 sb->data1 = sb->data;
149 sb->data2 = sb->data;
150 sb->data4 = sb->data;
151
152 if (player_track->format.byte_order == player_byte_order ||
153 sb->nbytes == 1)
154 sb->swap = 0;
155 else
156 sb->swap = 1;
157
158 LOG_DEBUG("size_b=%zu, size_s=%zu, nbytes=%u, swap=%d", sb->size_b,
159 sb->size_s, sb->nbytes, sb->swap);
160
161 XPTHREAD_MUTEX_UNLOCK(&player_op_mtx);
162 XPTHREAD_MUTEX_UNLOCK(&player_track_mtx);
163 return 0;
164
165 error2:
166 player_track->ip->close(player_track);
167 error1:
168 XPTHREAD_MUTEX_UNLOCK(&player_op_mtx);
169 XPTHREAD_MUTEX_UNLOCK(&player_track_mtx);
170 return -1;
171 }
172
173 void
player_change_op(void)174 player_change_op(void)
175 {
176 player_stop();
177
178 XPTHREAD_MUTEX_LOCK(&player_op_mtx);
179 player_close_op();
180 player_op = NULL;
181 player_open_op();
182 XPTHREAD_MUTEX_UNLOCK(&player_op_mtx);
183
184 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
185 player_print_status();
186 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
187 }
188
189 /*
190 * The player_op_mtx mutex must be locked before calling this function.
191 */
192 static void
player_close_op(void)193 player_close_op(void)
194 {
195 if (player_op_opened) {
196 player_op->close();
197 player_op_opened = 0;
198 }
199 }
200
201 static void
player_determine_byte_order(void)202 player_determine_byte_order(void)
203 {
204 int i;
205
206 i = 1;
207 if (*(char *)&i == 1)
208 player_byte_order = BYTE_ORDER_LITTLE;
209 else
210 player_byte_order = BYTE_ORDER_BIG;
211 }
212
213 void
player_end(void)214 player_end(void)
215 {
216 player_quit();
217 XPTHREAD_JOIN(player_playback_thd, NULL);
218 player_close_op();
219 }
220
221 static void
player_end_playback(struct sample_buffer * sb)222 player_end_playback(struct sample_buffer *sb)
223 {
224 XPTHREAD_MUTEX_LOCK(&player_track_mtx);
225 player_track->ip->close(player_track);
226 XPTHREAD_MUTEX_UNLOCK(&player_track_mtx);
227
228 XPTHREAD_MUTEX_LOCK(&player_op_mtx);
229 if (player_op->stop() == -1)
230 player_close_op();
231 XPTHREAD_MUTEX_UNLOCK(&player_op_mtx);
232
233 free(sb->data);
234 }
235
236 void
player_forcibly_close_op(void)237 player_forcibly_close_op(void)
238 {
239 player_stop();
240
241 XPTHREAD_MUTEX_LOCK(&player_op_mtx);
242 if (player_op_opened) {
243 LOG_INFO("forcibly closing %s", player_op->name);
244 player_op->close();
245 player_op_opened = 0;
246 }
247 XPTHREAD_MUTEX_UNLOCK(&player_op_mtx);
248 }
249
250 enum byte_order
player_get_byte_order(void)251 player_get_byte_order(void)
252 {
253 return player_byte_order;
254 }
255
256 static int
player_get_track(void)257 player_get_track(void)
258 {
259 struct track *t;
260
261 if (!option_get_boolean("repeat-track")) {
262 if ((t = queue_get_next_track()) == NULL) {
263 XPTHREAD_MUTEX_LOCK(&player_source_mtx);
264 switch (player_source) {
265 case PLAYER_SOURCE_BROWSER:
266 t = browser_get_next_track();
267 break;
268 case PLAYER_SOURCE_LIBRARY:
269 t = library_get_next_track();
270 break;
271 case PLAYER_SOURCE_PLAYLIST:
272 t = playlist_get_next_track();
273 break;
274 }
275 XPTHREAD_MUTEX_UNLOCK(&player_source_mtx);
276
277 if (t == NULL)
278 return -1;
279 }
280
281 XPTHREAD_MUTEX_LOCK(&player_track_mtx);
282 player_track = t;
283 XPTHREAD_MUTEX_UNLOCK(&player_track_mtx);
284 }
285
286 return option_get_boolean("continue") ? 0 : -1;
287 }
288
289 void
player_init(void)290 player_init(void)
291 {
292 player_determine_byte_order();
293 XPTHREAD_CREATE(&player_playback_thd, NULL, player_playback_handler,
294 NULL);
295 }
296
297 /*
298 * The player_op_mtx mutex must be locked before calling this function.
299 */
300 static int
player_open_op(void)301 player_open_op(void)
302 {
303 char *name;
304
305 if (player_op_opened)
306 /* Output plug-in already opened. */
307 return 0;
308
309 if (player_op == NULL) {
310 name = option_get_string("output-plugin");
311 if ((player_op = plugin_find_op(name)) == NULL) {
312 msg_errx("Output plug-in not found: %s", name);
313 free(name);
314 return -1;
315 }
316 free(name);
317 }
318
319 LOG_INFO("opening %s", player_op->name);
320 if (player_op->open() == -1)
321 return -1;
322
323 player_op_opened = 1;
324 return 0;
325 }
326
327 void
player_pause(void)328 player_pause(void)
329 {
330 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
331 if (player_state == PLAYER_STATE_PLAYING)
332 player_command = PLAYER_COMMAND_PAUSE;
333 else if (player_state == PLAYER_STATE_PAUSED) {
334 player_command = PLAYER_COMMAND_PLAY;
335 XPTHREAD_COND_BROADCAST(&player_command_cond);
336 }
337 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
338 }
339
340 void
player_play(void)341 player_play(void)
342 {
343 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
344 if (player_state == PLAYER_STATE_PLAYING) {
345 player_command = PLAYER_COMMAND_STOP;
346 XPTHREAD_COND_WAIT(&player_command_cond, &player_state_mtx);
347 }
348
349 player_command = PLAYER_COMMAND_PLAY;
350 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
351 XPTHREAD_COND_BROADCAST(&player_command_cond);
352 }
353
354 void
player_play_next(void)355 player_play_next(void)
356 {
357 struct track *t;
358
359 XPTHREAD_MUTEX_LOCK(&player_source_mtx);
360 switch (player_source) {
361 case PLAYER_SOURCE_BROWSER:
362 t = browser_get_next_track();
363 break;
364 case PLAYER_SOURCE_LIBRARY:
365 t = library_get_next_track();
366 break;
367 case PLAYER_SOURCE_PLAYLIST:
368 t = playlist_get_next_track();
369 break;
370 default:
371 t = NULL;
372 break;
373 }
374 XPTHREAD_MUTEX_UNLOCK(&player_source_mtx);
375
376 if (t != NULL)
377 player_play_track(t);
378 }
379
380 void
player_play_prev(void)381 player_play_prev(void)
382 {
383 struct track *t;
384
385 XPTHREAD_MUTEX_LOCK(&player_source_mtx);
386 switch (player_source) {
387 case PLAYER_SOURCE_BROWSER:
388 t = browser_get_prev_track();
389 break;
390 case PLAYER_SOURCE_LIBRARY:
391 t = library_get_prev_track();
392 break;
393 case PLAYER_SOURCE_PLAYLIST:
394 t = playlist_get_prev_track();
395 break;
396 default:
397 t = NULL;
398 break;
399 }
400 XPTHREAD_MUTEX_UNLOCK(&player_source_mtx);
401
402 if (t != NULL)
403 player_play_track(t);
404 }
405
406 static int
player_play_sample_buffer(struct sample_buffer * sb)407 player_play_sample_buffer(struct sample_buffer *sb)
408 {
409 size_t i;
410 int ret;
411
412 XPTHREAD_MUTEX_LOCK(&player_track_mtx);
413 ret = player_track->ip->read(player_track, sb);
414 XPTHREAD_MUTEX_UNLOCK(&player_track_mtx);
415
416 if (ret == 0)
417 /* EOF reached. */
418 return -1;
419
420 if (ret < 0)
421 /* Error encountered. */
422 goto error;
423
424 if (sb->swap) {
425 if (sb->nbytes == 2)
426 for (i = 0; i < sb->len_s; i++)
427 sb->data2[i] = PLAYER_SWAP16(sb->data2[i]);
428 else
429 for (i = 0; i < sb->len_s; i++)
430 sb->data4[i] = PLAYER_SWAP32(sb->data4[i]);
431 }
432
433 XPTHREAD_MUTEX_LOCK(&player_op_mtx);
434 ret = player_op->write(sb);
435 XPTHREAD_MUTEX_UNLOCK(&player_op_mtx);
436
437 if (ret == -1)
438 goto error;
439
440 return 0;
441
442 error:
443 if (!option_get_boolean("continue-after-error")) {
444 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
445 player_command = PLAYER_COMMAND_STOP;
446 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
447 }
448 return -1;
449 }
450
451 void
player_play_track(struct track * t)452 player_play_track(struct track *t)
453 {
454 player_stop();
455 XPTHREAD_MUTEX_LOCK(&player_track_mtx);
456 player_track = t;
457 XPTHREAD_MUTEX_UNLOCK(&player_track_mtx);
458 player_play();
459 }
460
461 static void *
player_playback_handler(UNUSED void * p)462 player_playback_handler(UNUSED void *p)
463 {
464 struct sample_buffer sb;
465
466 /*
467 * Block all signals in this thread so that they can be handled in the
468 * main thread by input_handle_signal().
469 */
470 player_set_signal_mask();
471
472 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
473 for (;;) {
474 if (player_command == PLAYER_COMMAND_PLAY) {
475 if (player_get_track() == -1)
476 player_command = PLAYER_COMMAND_STOP;
477 player_print_track();
478 }
479
480 if (player_command == PLAYER_COMMAND_STOP) {
481 XPTHREAD_COND_WAIT(&player_command_cond,
482 &player_state_mtx);
483 if (player_command == PLAYER_COMMAND_QUIT)
484 break;
485 player_print_track();
486 }
487
488 if (player_begin_playback(&sb) == -1) {
489 player_command = PLAYER_COMMAND_STOP;
490 continue;
491 }
492
493 player_state = PLAYER_STATE_PLAYING;
494 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
495
496 for (;;) {
497 if (player_play_sample_buffer(&sb) == -1) {
498 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
499 break;
500 }
501
502 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
503 if (player_command == PLAYER_COMMAND_PAUSE) {
504 player_state = PLAYER_STATE_PAUSED;
505 player_print_status();
506
507 XPTHREAD_COND_WAIT(&player_command_cond,
508 &player_state_mtx);
509
510 if (player_command == PLAYER_COMMAND_PLAY)
511 player_state = PLAYER_STATE_PLAYING;
512 }
513
514 if (player_command == PLAYER_COMMAND_STOP)
515 break;
516
517 player_print_status();
518 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
519 }
520
521 player_end_playback(&sb);
522 player_state = PLAYER_STATE_STOPPED;
523 player_print_status();
524
525 if (player_command == PLAYER_COMMAND_STOP)
526 XPTHREAD_COND_BROADCAST(&player_command_cond);
527 }
528 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
529
530 return NULL;
531 }
532
533 void
player_print(void)534 player_print(void)
535 {
536 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
537 player_print_track();
538 player_print_status();
539 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
540 }
541
542 /*
543 * The player_state_mtx mutex must be locked before calling this function.
544 */
545 static void
player_print_status(void)546 player_print_status(void)
547 {
548 struct format *format;
549 struct format_variable vars[PLAYER_FMT_NVARS];
550 unsigned int pos;
551 int vol;
552
553 vars[PLAYER_FMT_CONTINUE].lname = "continue";
554 vars[PLAYER_FMT_CONTINUE].sname = 'c';
555 vars[PLAYER_FMT_CONTINUE].type = FORMAT_VARIABLE_STRING;
556 vars[PLAYER_FMT_DURATION].lname = "duration";
557 vars[PLAYER_FMT_DURATION].sname = 'd';
558 vars[PLAYER_FMT_DURATION].type = FORMAT_VARIABLE_TIME;
559 vars[PLAYER_FMT_POSITION].lname = "position";
560 vars[PLAYER_FMT_POSITION].sname = 'p';
561 vars[PLAYER_FMT_POSITION].type = FORMAT_VARIABLE_TIME;
562 vars[PLAYER_FMT_REPEAT_ALL].lname = "repeat-all";
563 vars[PLAYER_FMT_REPEAT_ALL].sname = 'r';
564 vars[PLAYER_FMT_REPEAT_ALL].type = FORMAT_VARIABLE_STRING;
565 vars[PLAYER_FMT_REPEAT_TRACK].lname = "repeat-track";
566 vars[PLAYER_FMT_REPEAT_TRACK].sname = 't';
567 vars[PLAYER_FMT_REPEAT_TRACK].type = FORMAT_VARIABLE_STRING;
568 vars[PLAYER_FMT_SOURCE].lname = "source";
569 vars[PLAYER_FMT_SOURCE].sname = 'u';
570 vars[PLAYER_FMT_SOURCE].type = FORMAT_VARIABLE_STRING;
571 vars[PLAYER_FMT_STATE].lname = "state";
572 vars[PLAYER_FMT_STATE].sname = 's';
573 vars[PLAYER_FMT_STATE].type = FORMAT_VARIABLE_STRING;
574 vars[PLAYER_FMT_VOLUME].lname = "volume";
575 vars[PLAYER_FMT_VOLUME].sname = 'v';
576 vars[PLAYER_FMT_VOLUME].type = FORMAT_VARIABLE_NUMBER;
577
578 /* Set the state variable. */
579 switch (player_state) {
580 case PLAYER_STATE_PAUSED:
581 vars[PLAYER_FMT_STATE].value.string = "Paused";
582 break;
583 case PLAYER_STATE_PLAYING:
584 vars[PLAYER_FMT_STATE].value.string = "Playing";
585 break;
586 case PLAYER_STATE_STOPPED:
587 vars[PLAYER_FMT_STATE].value.string = "Stopped";
588 break;
589 }
590
591 XPTHREAD_MUTEX_LOCK(&player_track_mtx);
592
593 /* Set the position variable. */
594 if (player_state == PLAYER_STATE_STOPPED || player_track->ip == NULL ||
595 player_track->ip->get_position(player_track, &pos) == -1)
596 vars[PLAYER_FMT_POSITION].value.time = 0;
597 else
598 vars[PLAYER_FMT_POSITION].value.time = pos;
599
600 /* Set the duration variable. */
601 if (player_track == NULL)
602 vars[PLAYER_FMT_DURATION].value.time = 0;
603 else {
604 track_lock_metadata();
605 vars[PLAYER_FMT_DURATION].value.time =
606 player_track->duration;
607 track_unlock_metadata();
608 }
609
610 XPTHREAD_MUTEX_UNLOCK(&player_track_mtx);
611
612 /* Set the volume variable. */
613 XPTHREAD_MUTEX_LOCK(&player_op_mtx);
614 if (player_open_op() == -1 || !player_op->get_volume_support() ||
615 (vol = player_op->get_volume()) == -1)
616 vars[PLAYER_FMT_VOLUME].value.number = 0;
617 else
618 vars[PLAYER_FMT_VOLUME].value.number = vol;
619 XPTHREAD_MUTEX_UNLOCK(&player_op_mtx);
620
621 /* Set the continue variable. */
622 if (option_get_boolean("continue"))
623 vars[PLAYER_FMT_CONTINUE].value.string = "continue";
624 else
625 vars[PLAYER_FMT_CONTINUE].value.string = "";
626
627 /* Set the repeat-all variable. */
628 if (option_get_boolean("repeat-all"))
629 vars[PLAYER_FMT_REPEAT_ALL].value.string = "repeat-all";
630 else
631 vars[PLAYER_FMT_REPEAT_ALL].value.string = "";
632
633 /* Set the repeat-track variable. */
634 if (option_get_boolean("repeat-track"))
635 vars[PLAYER_FMT_REPEAT_TRACK].value.string = "repeat-track";
636 else
637 vars[PLAYER_FMT_REPEAT_TRACK].value.string = "";
638
639 /* Set the player-source variable. */
640 XPTHREAD_MUTEX_LOCK(&player_source_mtx);
641 switch (player_source) {
642 case PLAYER_SOURCE_BROWSER:
643 vars[PLAYER_FMT_SOURCE].value.string = "browser";
644 break;
645 case PLAYER_SOURCE_LIBRARY:
646 vars[PLAYER_FMT_SOURCE].value.string = "library";
647 break;
648 case PLAYER_SOURCE_PLAYLIST:
649 vars[PLAYER_FMT_SOURCE].value.string = "playlist";
650 break;
651 }
652 XPTHREAD_MUTEX_UNLOCK(&player_source_mtx);
653
654 /* Print the status. */
655 option_lock();
656 format = option_get_format("player-status-format");
657 screen_player_status_printf(format, vars, PLAYER_FMT_NVARS);
658 option_unlock();
659 }
660
661 /*
662 * The player_state_mtx mutex must be locked before calling this function.
663 */
664 static void
player_print_track(void)665 player_print_track(void)
666 {
667 struct format *altfmt, *fmt;
668
669 option_lock();
670 track_lock_metadata();
671 fmt = option_get_format("player-track-format");
672 altfmt = option_get_format("player-track-format-alt");
673 screen_player_track_printf(fmt, altfmt, player_track);
674 track_unlock_metadata();
675 option_unlock();
676 }
677
678 static void
player_quit(void)679 player_quit(void)
680 {
681 player_stop();
682 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
683 player_command = PLAYER_COMMAND_QUIT;
684 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
685 XPTHREAD_COND_BROADCAST(&player_command_cond);
686 }
687
688 void
player_reopen_op(void)689 player_reopen_op(void)
690 {
691 player_stop();
692
693 XPTHREAD_MUTEX_LOCK(&player_op_mtx);
694 if (player_op_opened) {
695 LOG_INFO("reopening %s", player_op->name);
696 player_op->close();
697 if (player_op->open() != 0)
698 player_op_opened = 0;
699 }
700 XPTHREAD_MUTEX_UNLOCK(&player_op_mtx);
701 }
702
703 void
player_seek(int pos,int relative)704 player_seek(int pos, int relative)
705 {
706 unsigned int curpos;
707
708 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
709 XPTHREAD_MUTEX_LOCK(&player_track_mtx);
710
711 if (player_state == PLAYER_STATE_STOPPED)
712 goto out;
713
714 if (relative) {
715 if (player_track->ip->get_position(player_track, &curpos))
716 goto out;
717 pos += curpos;
718 }
719
720 if (pos < 0)
721 pos = 0;
722 else if ((unsigned int)pos > player_track->duration)
723 pos = player_track->duration;
724
725 player_track->ip->seek(player_track, pos);
726
727 out:
728 XPTHREAD_MUTEX_UNLOCK(&player_track_mtx);
729 player_print_status();
730 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
731 }
732
733 static void
player_set_signal_mask(void)734 player_set_signal_mask(void)
735 {
736 sigset_t ss;
737
738 sigfillset(&ss);
739 pthread_sigmask(SIG_BLOCK, &ss, NULL);
740 }
741
742 void
player_set_source(enum player_source source)743 player_set_source(enum player_source source)
744 {
745 XPTHREAD_MUTEX_LOCK(&player_source_mtx);
746 player_source = source;
747 XPTHREAD_MUTEX_UNLOCK(&player_source_mtx);
748 }
749
750 void
player_set_volume(int volume,int relative)751 player_set_volume(int volume, int relative)
752 {
753 int ret;
754
755 XPTHREAD_MUTEX_LOCK(&player_op_mtx);
756
757 if (player_open_op() == -1)
758 goto out;
759
760 if (!player_op->get_volume_support()) {
761 msg_errx("Output plug-in does not have volume support");
762 goto out;
763 }
764
765 if (relative) {
766 if ((ret = player_op->get_volume()) == -1)
767 goto out;
768
769 volume += ret;
770 if (volume < 0)
771 volume = 0;
772 else if (volume > 100)
773 volume = 100;
774 }
775
776 player_op->set_volume(volume);
777
778 out:
779 XPTHREAD_MUTEX_UNLOCK(&player_op_mtx);
780 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
781 player_print_status();
782 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
783 }
784
785 void
player_stop(void)786 player_stop(void)
787 {
788 XPTHREAD_MUTEX_LOCK(&player_state_mtx);
789 if (player_state != PLAYER_STATE_STOPPED) {
790 player_command = PLAYER_COMMAND_STOP;
791 if (player_state == PLAYER_STATE_PAUSED)
792 XPTHREAD_COND_BROADCAST(&player_command_cond);
793 XPTHREAD_COND_WAIT(&player_command_cond, &player_state_mtx);
794 }
795 XPTHREAD_MUTEX_UNLOCK(&player_state_mtx);
796 }
797