build system: Mark clean and distclean .NOTPARALLEL
[cmus.git] / player.c
blob456c3f8fb76864135ed456afd2682095ce6ecaba
1 /*
2 * Copyright 2004-2006 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include "player.h"
21 #include "buffer.h"
22 #include "input.h"
23 #include "output.h"
24 #include "sf.h"
25 #include "utils.h"
26 #include "xmalloc.h"
27 #include "debug.h"
28 #include "compiler.h"
30 #include <stdlib.h>
31 #include <pthread.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/time.h>
35 #include <stdarg.h>
37 enum producer_status {
38 PS_UNLOADED,
39 PS_STOPPED,
40 PS_PLAYING,
41 PS_PAUSED
44 enum consumer_status {
45 CS_STOPPED,
46 CS_PLAYING,
47 CS_PAUSED
50 struct player_info player_info = {
51 .mutex = CMUS_MUTEX_INITIALIZER,
52 .filename = { 0, },
53 .metadata = { 0, },
54 .status = PLAYER_STATUS_STOPPED,
55 .pos = 0,
56 .vol_left = 0,
57 .vol_right = 0,
58 .buffer_fill = 0,
59 .buffer_size = 0,
60 .error_msg = NULL,
61 .file_changed = 0,
62 .metadata_changed = 0,
63 .status_changed = 0,
64 .position_changed = 0,
65 .buffer_fill_changed = 0,
66 .vol_changed = 0,
69 /* continue playing after track is finished? */
70 int player_cont = 1;
72 static const struct player_callbacks *player_cbs = NULL;
74 static sample_format_t buffer_sf;
76 static pthread_t producer_thread;
77 static pthread_mutex_t producer_mutex = CMUS_MUTEX_INITIALIZER;
78 static int producer_running = 1;
79 static enum producer_status producer_status = PS_UNLOADED;
80 static struct input_plugin *ip = NULL;
82 static pthread_t consumer_thread;
83 static pthread_mutex_t consumer_mutex = CMUS_MUTEX_INITIALIZER;
84 static int consumer_running = 1;
85 static enum consumer_status consumer_status = CS_STOPPED;
86 static int consumer_pos = 0;
88 /* usually same as consumer_pos, sometimes less than consumer_pos */
89 static int soft_vol_pos;
91 /* locking {{{ */
93 #define producer_lock() cmus_mutex_lock(&producer_mutex)
94 #define producer_unlock() cmus_mutex_unlock(&producer_mutex)
96 #define consumer_lock() cmus_mutex_lock(&consumer_mutex)
97 #define consumer_unlock() cmus_mutex_unlock(&consumer_mutex)
99 #define player_lock() \
100 do { \
101 consumer_lock(); \
102 producer_lock(); \
103 } while (0)
105 #define player_unlock() \
106 do { \
107 producer_unlock(); \
108 consumer_unlock(); \
109 } while (0)
111 /* locking }}} */
113 static void reset_buffer(void)
115 buffer_reset();
116 consumer_pos = 0;
117 soft_vol_pos = 0;
120 static void set_buffer_sf(sample_format_t sf)
122 buffer_sf = sf;
124 /* ip_read converts samples to this format */
125 if (sf_get_channels(buffer_sf) <= 2 && sf_get_bits(buffer_sf) <= 16) {
126 buffer_sf &= SF_RATE_MASK;
127 buffer_sf |= sf_channels(2) | sf_bits(16) | sf_signed(1);
131 #define SOFT_VOL_SCALE 65536
133 /* coefficients for volumes 0..99, for 100 65536 is used
134 * data copied from alsa-lib src/pcm/pcm_softvol.c
136 static const unsigned short soft_vol_db[100] = {
137 0x0000, 0x0110, 0x011c, 0x012f, 0x013d, 0x0152, 0x0161, 0x0179,
138 0x018a, 0x01a5, 0x01c1, 0x01d5, 0x01f5, 0x020b, 0x022e, 0x0247,
139 0x026e, 0x028a, 0x02b6, 0x02d5, 0x0306, 0x033a, 0x035f, 0x0399,
140 0x03c2, 0x0403, 0x0431, 0x0479, 0x04ac, 0x04fd, 0x0553, 0x058f,
141 0x05ef, 0x0633, 0x069e, 0x06ea, 0x0761, 0x07b5, 0x083a, 0x0898,
142 0x092c, 0x09cb, 0x0a3a, 0x0aeb, 0x0b67, 0x0c2c, 0x0cb6, 0x0d92,
143 0x0e2d, 0x0f21, 0x1027, 0x10de, 0x1202, 0x12cf, 0x1414, 0x14f8,
144 0x1662, 0x1761, 0x18f5, 0x1a11, 0x1bd3, 0x1db4, 0x1f06, 0x211d,
145 0x2297, 0x24ec, 0x2690, 0x292a, 0x2aff, 0x2de5, 0x30fe, 0x332b,
146 0x369f, 0x390d, 0x3ce6, 0x3f9b, 0x43e6, 0x46eb, 0x4bb3, 0x4f11,
147 0x5466, 0x5a18, 0x5e19, 0x6472, 0x68ea, 0x6ffd, 0x74f8, 0x7cdc,
148 0x826a, 0x8b35, 0x9499, 0x9b35, 0xa5ad, 0xad0b, 0xb8b7, 0xc0ee,
149 0xcdf1, 0xd71a, 0xe59c, 0xefd3
152 static inline void scale_sample(signed short *buf, int i, int vol)
154 int sample = buf[i];
156 if (sample < 0) {
157 buf[i] = (sample * vol - SOFT_VOL_SCALE / 2) / SOFT_VOL_SCALE;
158 } else {
159 buf[i] = (sample * vol + SOFT_VOL_SCALE / 2) / SOFT_VOL_SCALE;
163 static void soft_vol_scale(char *buffer, int *countp)
165 signed short *buf;
166 int count = *countp;
167 int ch, bits, l, r, i;
169 if (consumer_pos != soft_vol_pos) {
170 int offs = soft_vol_pos - consumer_pos;
172 BUG_ON(offs < 0);
173 if (offs >= count)
174 return;
175 buffer += offs;
176 count -= offs;
178 soft_vol_pos += count;
179 buf = (signed short *)buffer;
181 if (soft_vol_l == 100 && soft_vol_r == 100)
182 return;
184 ch = sf_get_channels(buffer_sf);
185 bits = sf_get_bits(buffer_sf);
186 if (ch != 2 || bits != 16)
187 return;
189 l = SOFT_VOL_SCALE;
190 r = SOFT_VOL_SCALE;
191 if (soft_vol_l != 100)
192 l = soft_vol_db[soft_vol_l];
193 if (soft_vol_r != 100)
194 r = soft_vol_db[soft_vol_r];
196 for (i = 0; i < count / 4; i++) {
197 scale_sample(buf, i * 2, l);
198 scale_sample(buf, i * 2 + 1, r);
202 static inline int buffer_second_size(void)
204 return sf_get_second_size(buffer_sf);
207 static inline int get_next(char **filename)
209 return player_cbs->get_next(filename);
212 /* updating player status {{{ */
214 static inline void file_changed(void)
216 player_info_lock();
217 if (producer_status == PS_UNLOADED) {
218 player_info.filename[0] = 0;
219 } else {
220 strncpy(player_info.filename, ip_get_filename(ip), sizeof(player_info.filename));
221 player_info.filename[sizeof(player_info.filename) - 1] = 0;
223 d_print("%s\n", player_info.filename);
224 player_info.metadata[0] = 0;
225 player_info.file_changed = 1;
226 player_info_unlock();
229 static inline void metadata_changed(void)
231 player_info_lock();
232 d_print("metadata changed: %s\n", ip_get_metadata(ip));
233 memcpy(player_info.metadata, ip_get_metadata(ip), 255 * 16 + 1);
234 player_info.metadata_changed = 1;
235 player_info_unlock();
238 static inline void volume_update(int left, int right)
240 if (player_info.vol_left == left && player_info.vol_right == right)
241 return;
243 player_info_lock();
244 player_info.vol_left = left;
245 player_info.vol_right = right;
246 player_info.vol_changed = 1;
247 player_info_unlock();
250 static void player_error(const char *msg)
252 player_info_lock();
253 player_info.status = consumer_status;
254 player_info.pos = 0;
255 player_info.buffer_fill = buffer_get_filled_chunks();
256 player_info.buffer_size = buffer_nr_chunks;
257 player_info.status_changed = 1;
259 free(player_info.error_msg);
260 player_info.error_msg = xstrdup(msg);
261 player_info_unlock();
263 d_print("ERROR: '%s'\n", msg);
266 static void __FORMAT(2, 3) player_ip_error(int rc, const char *format, ...)
268 char buffer[1024];
269 va_list ap;
270 char *msg;
271 int save = errno;
273 va_start(ap, format);
274 vsnprintf(buffer, sizeof(buffer), format, ap);
275 va_end(ap);
277 errno = save;
278 msg = ip_get_error_msg(ip, rc, buffer);
279 player_error(msg);
280 free(msg);
283 static void __FORMAT(2, 3) player_op_error(int rc, const char *format, ...)
285 char buffer[1024];
286 va_list ap;
287 char *msg;
288 int save = errno;
290 va_start(ap, format);
291 vsnprintf(buffer, sizeof(buffer), format, ap);
292 va_end(ap);
294 errno = save;
295 msg = op_get_error_msg(rc, buffer);
296 player_error(msg);
297 free(msg);
300 /* FIXME: don't poll */
301 static void mixer_check(void)
303 static struct timeval old_t = { 0L, 0L };
304 struct timeval t;
305 long usec, sec;
306 int l, r;
308 gettimeofday(&t, NULL);
309 usec = t.tv_usec - old_t.tv_usec;
310 sec = t.tv_sec - old_t.tv_sec;
311 if (sec) {
312 /* multiplying sec with 1e6 can overflow */
313 usec += 1e6L;
315 if (usec < 300e3)
316 return;
318 old_t = t;
319 if (!op_get_volume(&l, &r))
320 volume_update(l, r);
324 * buffer-fill changed
326 static void __producer_buffer_fill_update(void)
328 int fill;
330 player_info_lock();
331 fill = buffer_get_filled_chunks();
332 if (fill != player_info.buffer_fill) {
333 /* d_print("\n"); */
334 player_info.buffer_fill = fill;
335 player_info.buffer_fill_changed = 1;
337 player_info_unlock();
341 * playing position changed
343 static void __consumer_position_update(void)
345 static int old_pos = -1;
346 int pos;
348 pos = 0;
349 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED)
350 pos = consumer_pos / buffer_second_size();
351 if (pos != old_pos) {
352 /* d_print("\n"); */
353 old_pos = pos;
355 player_info_lock();
356 player_info.pos = pos;
357 player_info.position_changed = 1;
358 player_info_unlock();
363 * something big happened (stopped/paused/unpaused...)
365 static void __player_status_changed(void)
367 int pos = 0;
369 /* d_print("\n"); */
370 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED)
371 pos = consumer_pos / buffer_second_size();
373 player_info_lock();
374 player_info.status = consumer_status;
375 player_info.pos = pos;
376 player_info.buffer_fill = buffer_get_filled_chunks();
377 player_info.buffer_size = buffer_nr_chunks;
378 player_info.status_changed = 1;
379 player_info_unlock();
382 /* updating player status }}} */
384 static void __prebuffer(void)
386 int limit_chunks;
388 BUG_ON(producer_status != PS_PLAYING);
389 if (ip_is_remote(ip)) {
390 limit_chunks = buffer_nr_chunks;
391 } else {
392 int limit_ms, limit_size;
394 limit_ms = 250;
395 limit_size = limit_ms * buffer_second_size() / 1000;
396 limit_chunks = limit_size / CHUNK_SIZE;
397 if (limit_chunks < 1)
398 limit_chunks = 1;
400 while (1) {
401 int nr_read, size, filled;
402 char *wpos;
404 filled = buffer_get_filled_chunks();
405 /* d_print("PREBUF: %2d / %2d\n", filled, limit_chunks); */
407 /* not fatal */
408 //BUG_ON(filled > limit_chunks);
410 if (filled >= limit_chunks)
411 break;
413 size = buffer_get_wpos(&wpos);
414 nr_read = ip_read(ip, wpos, size);
415 if (nr_read < 0) {
416 if (nr_read == -1 && errno == EAGAIN)
417 continue;
418 player_ip_error(nr_read, "reading file %s", ip_get_filename(ip));
419 /* ip_read sets eof */
420 nr_read = 0;
422 if (ip_metadata_changed(ip))
423 metadata_changed();
425 /* buffer_fill with 0 count marks current chunk filled */
426 buffer_fill(nr_read);
428 __producer_buffer_fill_update();
429 if (nr_read == 0) {
430 /* EOF */
431 break;
436 /* setting producer status {{{ */
438 static void __producer_play(void)
440 if (producer_status == PS_UNLOADED) {
441 char *filename;
443 if (get_next(&filename) == 0) {
444 int rc;
446 ip = ip_new(filename);
447 rc = ip_open(ip);
448 if (rc) {
449 player_ip_error(rc, "opening file `%s'", filename);
450 ip_delete(ip);
451 } else {
452 ip_setup(ip);
453 producer_status = PS_PLAYING;
455 free(filename);
456 file_changed();
458 } else if (producer_status == PS_PLAYING) {
459 if (ip_seek(ip, 0.0) == 0) {
460 reset_buffer();
462 } else if (producer_status == PS_STOPPED) {
463 int rc;
465 rc = ip_open(ip);
466 if (rc) {
467 player_ip_error(rc, "opening file `%s'", ip_get_filename(ip));
468 ip_delete(ip);
469 producer_status = PS_UNLOADED;
470 } else {
471 ip_setup(ip);
472 producer_status = PS_PLAYING;
474 } else if (producer_status == PS_PAUSED) {
475 producer_status = PS_PLAYING;
479 static void __producer_stop(void)
481 if (producer_status == PS_PLAYING || producer_status == PS_PAUSED) {
482 ip_close(ip);
483 producer_status = PS_STOPPED;
484 reset_buffer();
488 static void __producer_unload(void)
490 __producer_stop();
491 if (producer_status == PS_STOPPED) {
492 ip_delete(ip);
493 producer_status = PS_UNLOADED;
497 static void __producer_pause(void)
499 if (producer_status == PS_PLAYING) {
500 producer_status = PS_PAUSED;
501 } else if (producer_status == PS_PAUSED) {
502 producer_status = PS_PLAYING;
506 static void __producer_set_file(const char *filename)
508 __producer_unload();
509 ip = ip_new(filename);
510 producer_status = PS_STOPPED;
511 file_changed();
514 /* setting producer status }}} */
516 /* setting consumer status {{{ */
518 static void __consumer_play(void)
520 if (consumer_status == CS_PLAYING) {
521 op_drop();
522 } else if (consumer_status == CS_STOPPED) {
523 int rc;
525 set_buffer_sf(ip_get_sf(ip));
526 rc = op_open(buffer_sf);
527 if (rc) {
528 player_op_error(rc, "opening audio device");
529 } else {
530 consumer_status = CS_PLAYING;
532 } else if (consumer_status == CS_PAUSED) {
533 op_unpause();
534 consumer_status = CS_PLAYING;
538 static void __consumer_drain_and_stop(void)
540 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
541 op_close();
542 consumer_status = CS_STOPPED;
546 static void __consumer_stop(void)
548 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
549 op_drop();
550 op_close();
551 consumer_status = CS_STOPPED;
555 static void __consumer_pause(void)
557 if (consumer_status == CS_PLAYING) {
558 op_pause();
559 consumer_status = CS_PAUSED;
560 } else if (consumer_status == CS_PAUSED) {
561 op_unpause();
562 consumer_status = CS_PLAYING;
566 /* setting consumer status }}} */
568 static int change_sf(sample_format_t sf, int drop)
570 int old_sf = buffer_sf;
572 set_buffer_sf(sf);
573 if (buffer_sf != old_sf) {
574 /* reopen */
575 int rc;
577 if (drop)
578 op_drop();
579 op_close();
580 rc = op_open(buffer_sf);
581 if (rc) {
582 player_op_error(rc, "opening audio device");
583 consumer_status = CS_STOPPED;
584 __producer_stop();
585 return rc;
587 } else if (consumer_status == CS_PAUSED) {
588 op_drop();
589 op_unpause();
591 consumer_status = CS_PLAYING;
592 return 0;
595 static void __consumer_handle_eof(void)
597 char *filename;
599 if (ip_is_remote(ip)) {
600 __producer_stop();
601 __consumer_drain_and_stop();
602 player_error("lost connection");
603 return;
606 if (get_next(&filename) == 0) {
607 __producer_unload();
608 ip = ip_new(filename);
609 producer_status = PS_STOPPED;
610 /* PS_STOPPED, CS_PLAYING */
611 if (player_cont) {
612 __producer_play();
613 if (producer_status == PS_UNLOADED) {
614 __consumer_stop();
615 file_changed();
616 } else {
617 /* PS_PLAYING */
618 file_changed();
619 if (!change_sf(ip_get_sf(ip), 0))
620 __prebuffer();
622 } else {
623 __consumer_drain_and_stop();
624 file_changed();
626 free(filename);
627 } else {
628 __producer_unload();
629 __consumer_drain_and_stop();
630 file_changed();
632 __player_status_changed();
635 static void *consumer_loop(void *arg)
637 while (1) {
638 int rc, space;
639 int size;
640 char *rpos;
642 consumer_lock();
643 if (!consumer_running)
644 break;
646 if (consumer_status == CS_PAUSED || consumer_status == CS_STOPPED) {
647 mixer_check();
648 consumer_unlock();
649 ms_sleep(50);
650 continue;
652 space = op_buffer_space();
653 if (space == -1) {
654 /* busy */
655 __consumer_position_update();
656 consumer_unlock();
657 ms_sleep(50);
658 continue;
660 /* d_print("BS: %6d %3d\n", space, space * 1000 / (44100 * 2 * 2)); */
662 while (1) {
663 /* 25 ms is 4410 B */
664 if (space < 4096) {
665 __consumer_position_update();
666 mixer_check();
667 consumer_unlock();
668 ms_sleep(25);
669 break;
671 size = buffer_get_rpos(&rpos);
672 if (size == 0) {
673 producer_lock();
674 if (producer_status != PS_PLAYING) {
675 producer_unlock();
676 consumer_unlock();
677 break;
679 /* must recheck rpos */
680 size = buffer_get_rpos(&rpos);
681 if (size == 0) {
682 /* OK. now it's safe to check if we are at EOF */
683 if (ip_eof(ip)) {
684 /* EOF */
685 __consumer_handle_eof();
686 producer_unlock();
687 consumer_unlock();
688 break;
689 } else {
690 /* possible underrun */
691 producer_unlock();
692 __consumer_position_update();
693 consumer_unlock();
694 /* d_print("possible underrun\n"); */
695 ms_sleep(10);
696 break;
700 /* player_buffer and ip.eof were inconsistent */
701 producer_unlock();
703 if (size > space)
704 size = space;
705 if (soft_vol)
706 soft_vol_scale(rpos, &size);
707 rc = op_write(rpos, size);
708 if (rc < 0) {
709 d_print("op_write returned %d %s\n", rc,
710 rc == -1 ? strerror(errno) : "");
712 /* try to reopen */
713 op_close();
714 consumer_status = CS_STOPPED;
715 __consumer_play();
717 consumer_unlock();
718 break;
720 buffer_consume(rc);
721 consumer_pos += rc;
722 space -= rc;
725 __consumer_stop();
726 consumer_unlock();
727 return NULL;
730 static void *producer_loop(void *arg)
732 while (1) {
733 /* number of chunks to fill
734 * too big => seeking is slow
735 * too small => underruns?
737 const int chunks = 1;
738 int size, nr_read, i;
739 char *wpos;
741 producer_lock();
742 if (!producer_running)
743 break;
745 if (producer_status == PS_UNLOADED ||
746 producer_status == PS_PAUSED ||
747 producer_status == PS_STOPPED || ip_eof(ip)) {
748 producer_unlock();
749 ms_sleep(50);
750 continue;
752 for (i = 0; ; i++) {
753 size = buffer_get_wpos(&wpos);
754 if (size == 0) {
755 /* buffer is full */
756 producer_unlock();
757 ms_sleep(50);
758 break;
760 nr_read = ip_read(ip, wpos, size);
761 if (nr_read < 0) {
762 if (nr_read != -1 || errno != EAGAIN) {
763 player_ip_error(nr_read, "reading file %s",
764 ip_get_filename(ip));
765 /* ip_read sets eof */
766 nr_read = 0;
767 } else {
768 producer_unlock();
769 ms_sleep(50);
770 break;
773 if (ip_metadata_changed(ip))
774 metadata_changed();
776 /* buffer_fill with 0 count marks current chunk filled */
777 buffer_fill(nr_read);
778 if (nr_read == 0) {
779 /* consumer handles EOF */
780 producer_unlock();
781 ms_sleep(50);
782 break;
784 if (i == chunks) {
785 producer_unlock();
786 /* don't sleep! */
787 break;
790 __producer_buffer_fill_update();
792 __producer_unload();
793 producer_unlock();
794 return NULL;
797 void player_load_plugins(void)
799 ip_load_plugins();
800 op_load_plugins();
803 void player_init(const struct player_callbacks *callbacks)
805 int rc;
806 #if defined(__linux__) || defined(__FreeBSD__)
807 pthread_attr_t attr;
808 #endif
809 pthread_attr_t *attrp = NULL;
811 /* 1 s is 176400 B (0.168 MB)
812 * 10 s is 1.68 MB
814 buffer_nr_chunks = 10 * 44100 * 16 / 8 * 2 / CHUNK_SIZE;
815 buffer_init();
817 player_cbs = callbacks;
819 #if defined(__linux__) || defined(__FreeBSD__)
820 rc = pthread_attr_init(&attr);
821 BUG_ON(rc);
822 rc = pthread_attr_setschedpolicy(&attr, SCHED_RR);
823 if (rc) {
824 d_print("could not set real-time scheduling priority: %s\n", strerror(rc));
825 } else {
826 struct sched_param param;
828 d_print("using real-time scheduling\n");
829 param.sched_priority = sched_get_priority_max(SCHED_RR);
830 d_print("setting priority to %d\n", param.sched_priority);
831 rc = pthread_attr_setschedparam(&attr, &param);
832 BUG_ON(rc);
833 attrp = &attr;
835 #endif
837 rc = pthread_create(&producer_thread, NULL, producer_loop, NULL);
838 BUG_ON(rc);
840 rc = pthread_create(&consumer_thread, attrp, consumer_loop, NULL);
841 if (rc && attrp) {
842 d_print("could not create thread using real-time scheduling: %s\n", strerror(rc));
843 rc = pthread_create(&consumer_thread, NULL, consumer_loop, NULL);
845 BUG_ON(rc);
847 /* update player_info.cont etc. */
848 player_lock();
849 __player_status_changed();
850 player_unlock();
853 void player_exit(void)
855 int rc;
857 player_lock();
858 consumer_running = 0;
859 producer_running = 0;
860 player_unlock();
862 rc = pthread_join(consumer_thread, NULL);
863 BUG_ON(rc);
864 rc = pthread_join(producer_thread, NULL);
865 BUG_ON(rc);
867 op_exit_plugins();
870 void player_stop(void)
872 player_lock();
873 __consumer_stop();
874 __producer_stop();
875 __player_status_changed();
876 player_unlock();
879 void player_play(void)
881 int prebuffer;
883 player_lock();
884 if (producer_status == PS_PLAYING && ip_is_remote(ip)) {
885 /* seeking not allowed */
886 player_unlock();
887 return;
889 prebuffer = consumer_status == CS_STOPPED;
890 __producer_play();
891 if (producer_status == PS_PLAYING) {
892 __consumer_play();
893 if (consumer_status != CS_PLAYING)
894 __producer_stop();
895 } else {
896 __consumer_stop();
898 __player_status_changed();
899 if (consumer_status == CS_PLAYING && prebuffer)
900 __prebuffer();
901 player_unlock();
904 void player_pause(void)
906 player_lock();
908 if (consumer_status == CS_STOPPED) {
909 __producer_play();
910 if (producer_status == PS_PLAYING) {
911 __consumer_play();
912 if (consumer_status != CS_PLAYING)
913 __producer_stop();
915 __player_status_changed();
916 if (consumer_status == CS_PLAYING)
917 __prebuffer();
918 player_unlock();
919 return;
922 if (ip && ip_is_remote(ip)) {
923 /* pausing not allowed */
924 player_unlock();
925 return;
927 __producer_pause();
928 __consumer_pause();
929 __player_status_changed();
930 player_unlock();
933 void player_set_file(const char *filename)
935 player_lock();
936 __producer_set_file(filename);
937 if (producer_status == PS_UNLOADED) {
938 __consumer_stop();
939 goto out;
942 /* PS_STOPPED */
943 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
944 __producer_play();
945 if (producer_status == PS_UNLOADED) {
946 __consumer_stop();
947 goto out;
949 change_sf(ip_get_sf(ip), 1);
951 out:
952 __player_status_changed();
953 if (producer_status == PS_PLAYING)
954 __prebuffer();
955 player_unlock();
958 void player_play_file(const char *filename)
960 player_lock();
961 __producer_set_file(filename);
962 if (producer_status == PS_UNLOADED) {
963 __consumer_stop();
964 goto out;
967 /* PS_STOPPED */
968 __producer_play();
970 /* PS_UNLOADED,PS_PLAYING */
971 if (producer_status == PS_UNLOADED) {
972 __consumer_stop();
973 goto out;
976 /* PS_PLAYING */
977 if (consumer_status == CS_STOPPED) {
978 __consumer_play();
979 if (consumer_status == CS_STOPPED)
980 __producer_stop();
981 } else {
982 change_sf(ip_get_sf(ip), 1);
984 out:
985 __player_status_changed();
986 if (producer_status == PS_PLAYING)
987 __prebuffer();
988 player_unlock();
991 void player_seek(double offset, int relative)
993 player_lock();
994 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
995 double pos, duration, new_pos;
996 int rc;
998 pos = (double)consumer_pos / (double)buffer_second_size();
999 duration = ip_duration(ip);
1000 if (duration < 0) {
1001 /* can't seek */
1002 d_print("can't seek\n");
1003 player_unlock();
1004 return;
1006 if (relative) {
1007 new_pos = pos + offset;
1008 if (new_pos < 0.0)
1009 new_pos = 0.0;
1010 if (offset > 0.0) {
1011 /* seeking forward */
1012 if (new_pos > duration - 5.0)
1013 new_pos = duration - 5.0;
1014 if (new_pos < 0.0)
1015 new_pos = 0.0;
1016 if (new_pos < pos - 0.5) {
1017 /* must seek at least 0.5s */
1018 d_print("must seek at least 0.5s\n");
1019 player_unlock();
1020 return;
1023 } else {
1024 new_pos = offset;
1025 if (new_pos < 0.0) {
1026 d_print("seek offset negative\n");
1027 player_unlock();
1028 return;
1030 if (new_pos > duration) {
1031 d_print("seek offset too large\n");
1032 player_unlock();
1033 return;
1036 /* d_print("seeking %g/%g (%g from eof)\n", new_pos, duration, duration - new_pos); */
1037 rc = ip_seek(ip, new_pos);
1038 if (rc == 0) {
1039 /* d_print("doing op_drop after seek\n"); */
1040 op_drop();
1041 reset_buffer();
1042 consumer_pos = new_pos * buffer_second_size();
1043 soft_vol_pos = consumer_pos;
1044 __consumer_position_update();
1045 } else {
1046 d_print("error: ip_seek returned %d\n", rc);
1049 player_unlock();
1053 * change output plugin without stopping playback
1055 void player_set_op(const char *name)
1057 int rc, l, r;
1059 player_lock();
1061 /* drop needed because close drains the buffer */
1062 if (consumer_status == CS_PAUSED)
1063 op_drop();
1065 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED)
1066 op_close();
1068 if (name) {
1069 d_print("setting op to '%s'\n", name);
1070 rc = op_select(name);
1071 } else {
1072 /* first initialized plugin */
1073 d_print("selecting first initialized op\n");
1074 rc = op_select_any();
1076 if (rc) {
1077 consumer_status = CS_STOPPED;
1079 __producer_stop();
1080 player_op_error(rc, "selecting output plugin '%s'", name);
1081 player_unlock();
1082 return;
1085 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
1086 set_buffer_sf(ip_get_sf(ip));
1087 rc = op_open(buffer_sf);
1088 if (rc) {
1089 consumer_status = CS_STOPPED;
1090 __producer_stop();
1091 player_op_error(rc, "opening audio device");
1092 player_unlock();
1093 return;
1095 if (consumer_status == CS_PAUSED)
1096 op_pause();
1099 if (!op_get_volume(&l, &r))
1100 volume_update(l, r);
1102 player_unlock();
1105 char *player_get_op(void)
1107 return op_get_current();
1110 void player_set_buffer_chunks(unsigned int nr_chunks)
1112 if (nr_chunks < 3)
1113 nr_chunks = 3;
1114 if (nr_chunks > 30)
1115 nr_chunks = 30;
1117 player_lock();
1118 __producer_stop();
1119 __consumer_stop();
1121 buffer_nr_chunks = nr_chunks;
1122 buffer_init();
1124 __player_status_changed();
1125 player_unlock();
1128 int player_get_buffer_chunks(void)
1130 return buffer_nr_chunks;
1133 int player_get_fileinfo(const char *filename, int *duration,
1134 struct keyval **comments)
1136 struct input_plugin *plug;
1137 int rc;
1139 *comments = NULL;
1140 *duration = -1;
1141 plug = ip_new(filename);
1142 if (ip_is_remote(plug)) {
1143 *comments = xnew0(struct keyval, 1);
1144 ip_delete(plug);
1145 return 0;
1147 rc = ip_open(plug);
1148 if (rc) {
1149 int save = errno;
1151 ip_delete(plug);
1152 errno = save;
1153 if (rc != -1)
1154 rc = -PLAYER_ERROR_NOT_SUPPORTED;
1155 return rc;
1157 *duration = ip_duration(plug);
1158 rc = ip_read_comments(plug, comments);
1159 ip_delete(plug);
1160 return rc;
1163 int player_get_volume(int *left, int *right)
1165 int rc;
1167 consumer_lock();
1168 rc = op_get_volume(left, right);
1169 consumer_unlock();
1170 return rc;
1173 int player_set_volume(int left, int right)
1175 int rc;
1177 consumer_lock();
1178 rc = op_set_volume(left, right);
1179 if (!rc)
1180 volume_update(left, right);
1181 consumer_unlock();
1182 return rc;
1185 void player_set_soft_vol(int soft)
1187 int l, r;
1189 consumer_lock();
1190 /* don't mess with soft_vol_pos if soft_vol is already true */
1191 if (!soft_vol)
1192 soft_vol_pos = consumer_pos;
1193 op_set_soft_vol(soft);
1194 if (!op_get_volume(&l, &r))
1195 volume_update(l, r);
1196 consumer_unlock();
1199 int player_set_op_option(unsigned int id, const char *val)
1201 int rc;
1203 player_lock();
1204 __consumer_stop();
1205 __producer_stop();
1206 rc = op_set_option(id, val);
1207 __player_status_changed();
1208 player_unlock();
1209 return rc;
1212 int player_get_op_option(unsigned int id, char **val)
1214 int rc;
1216 player_lock();
1217 rc = op_get_option(id, val);
1218 player_unlock();
1219 return rc;
1222 int player_for_each_op_option(void (*callback)(unsigned int id, const char *key))
1224 player_lock();
1225 __consumer_stop();
1226 __producer_stop();
1227 op_for_each_option(callback);
1228 __player_status_changed();
1229 player_unlock();
1230 return 0;
1233 void player_dump_plugins(void)
1235 ip_dump_plugins();
1236 op_dump_plugins();