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