help_view: one in all patch
[cmus.git] / player.c
blob2336521adc5a92a817dc6ea978b66ef7640118e9
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_delete(ip);
420 producer_status = PS_UNLOADED;
421 return;
423 if (ip_metadata_changed(ip))
424 metadata_changed();
426 /* buffer_fill with 0 count marks current chunk filled */
427 buffer_fill(nr_read);
429 __producer_buffer_fill_update();
430 if (nr_read == 0) {
431 /* EOF */
432 break;
437 /* setting producer status {{{ */
439 static void __producer_play(void)
441 if (producer_status == PS_UNLOADED) {
442 char *filename;
444 if (get_next(&filename) == 0) {
445 int rc;
447 ip = ip_new(filename);
448 rc = ip_open(ip);
449 if (rc) {
450 player_ip_error(rc, "opening file `%s'", filename);
451 ip_delete(ip);
452 } else {
453 ip_setup(ip);
454 producer_status = PS_PLAYING;
456 free(filename);
457 file_changed();
459 } else if (producer_status == PS_PLAYING) {
460 if (ip_seek(ip, 0.0) == 0) {
461 reset_buffer();
463 } else if (producer_status == PS_STOPPED) {
464 int rc;
466 rc = ip_open(ip);
467 if (rc) {
468 player_ip_error(rc, "opening file `%s'", ip_get_filename(ip));
469 ip_delete(ip);
470 producer_status = PS_UNLOADED;
471 } else {
472 ip_setup(ip);
473 producer_status = PS_PLAYING;
475 } else if (producer_status == PS_PAUSED) {
476 producer_status = PS_PLAYING;
480 static void __producer_stop(void)
482 if (producer_status == PS_PLAYING || producer_status == PS_PAUSED) {
483 ip_close(ip);
484 producer_status = PS_STOPPED;
485 reset_buffer();
489 static void __producer_unload(void)
491 __producer_stop();
492 if (producer_status == PS_STOPPED) {
493 ip_delete(ip);
494 producer_status = PS_UNLOADED;
498 static void __producer_pause(void)
500 if (producer_status == PS_PLAYING) {
501 producer_status = PS_PAUSED;
502 } else if (producer_status == PS_PAUSED) {
503 producer_status = PS_PLAYING;
507 static void __producer_set_file(const char *filename)
509 __producer_unload();
510 ip = ip_new(filename);
511 producer_status = PS_STOPPED;
512 file_changed();
515 /* setting producer status }}} */
517 /* setting consumer status {{{ */
519 static void __consumer_play(void)
521 if (consumer_status == CS_PLAYING) {
522 op_drop();
523 } else if (consumer_status == CS_STOPPED) {
524 int rc;
526 set_buffer_sf(ip_get_sf(ip));
527 rc = op_open(buffer_sf);
528 if (rc) {
529 player_op_error(rc, "opening audio device");
530 } else {
531 consumer_status = CS_PLAYING;
533 } else if (consumer_status == CS_PAUSED) {
534 op_unpause();
535 consumer_status = CS_PLAYING;
539 static void __consumer_drain_and_stop(void)
541 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
542 op_close();
543 consumer_status = CS_STOPPED;
547 static void __consumer_stop(void)
549 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
550 op_drop();
551 op_close();
552 consumer_status = CS_STOPPED;
556 static void __consumer_pause(void)
558 if (consumer_status == CS_PLAYING) {
559 op_pause();
560 consumer_status = CS_PAUSED;
561 } else if (consumer_status == CS_PAUSED) {
562 op_unpause();
563 consumer_status = CS_PLAYING;
567 /* setting consumer status }}} */
569 static int change_sf(sample_format_t sf, int drop)
571 int old_sf = buffer_sf;
573 set_buffer_sf(sf);
574 if (buffer_sf != old_sf) {
575 /* reopen */
576 int rc;
578 if (drop)
579 op_drop();
580 op_close();
581 rc = op_open(buffer_sf);
582 if (rc) {
583 player_op_error(rc, "opening audio device");
584 consumer_status = CS_STOPPED;
585 __producer_stop();
586 return rc;
588 } else if (consumer_status == CS_PAUSED) {
589 op_drop();
590 op_unpause();
592 consumer_status = CS_PLAYING;
593 return 0;
596 static void __consumer_handle_eof(void)
598 char *filename;
600 if (ip_is_remote(ip)) {
601 __producer_stop();
602 __consumer_drain_and_stop();
603 player_error("lost connection");
604 return;
607 if (get_next(&filename) == 0) {
608 __producer_unload();
609 ip = ip_new(filename);
610 producer_status = PS_STOPPED;
611 /* PS_STOPPED, CS_PLAYING */
612 if (player_cont) {
613 __producer_play();
614 if (producer_status == PS_UNLOADED) {
615 __consumer_stop();
616 file_changed();
617 } else {
618 /* PS_PLAYING */
619 file_changed();
620 if (!change_sf(ip_get_sf(ip), 0))
621 __prebuffer();
623 } else {
624 __consumer_drain_and_stop();
625 file_changed();
627 free(filename);
628 } else {
629 __producer_unload();
630 __consumer_drain_and_stop();
631 file_changed();
633 __player_status_changed();
636 static void *consumer_loop(void *arg)
638 while (1) {
639 int rc, space;
640 int size;
641 char *rpos;
643 consumer_lock();
644 if (!consumer_running)
645 break;
647 if (consumer_status == CS_PAUSED || consumer_status == CS_STOPPED) {
648 mixer_check();
649 consumer_unlock();
650 ms_sleep(50);
651 continue;
653 space = op_buffer_space();
654 if (space == -1) {
655 /* busy */
656 __consumer_position_update();
657 consumer_unlock();
658 ms_sleep(50);
659 continue;
661 /* d_print("BS: %6d %3d\n", space, space * 1000 / (44100 * 2 * 2)); */
663 while (1) {
664 /* 25 ms is 4410 B */
665 if (space < 4096) {
666 __consumer_position_update();
667 mixer_check();
668 consumer_unlock();
669 ms_sleep(25);
670 break;
672 size = buffer_get_rpos(&rpos);
673 if (size == 0) {
674 producer_lock();
675 if (producer_status != PS_PLAYING) {
676 producer_unlock();
677 consumer_unlock();
678 break;
680 /* must recheck rpos */
681 size = buffer_get_rpos(&rpos);
682 if (size == 0) {
683 /* OK. now it's safe to check if we are at EOF */
684 if (ip_eof(ip)) {
685 /* EOF */
686 __consumer_handle_eof();
687 producer_unlock();
688 consumer_unlock();
689 break;
690 } else {
691 /* possible underrun */
692 producer_unlock();
693 __consumer_position_update();
694 consumer_unlock();
695 /* d_print("possible underrun\n"); */
696 ms_sleep(10);
697 break;
701 /* player_buffer and ip.eof were inconsistent */
702 producer_unlock();
704 if (size > space)
705 size = space;
706 if (soft_vol)
707 soft_vol_scale(rpos, &size);
708 rc = op_write(rpos, size);
709 if (rc < 0) {
710 d_print("op_write returned %d %s\n", rc,
711 rc == -1 ? strerror(errno) : "");
713 /* try to reopen */
714 op_close();
715 consumer_status = CS_STOPPED;
716 __consumer_play();
718 consumer_unlock();
719 break;
721 buffer_consume(rc);
722 consumer_pos += rc;
723 space -= rc;
726 __consumer_stop();
727 consumer_unlock();
728 return NULL;
731 static void *producer_loop(void *arg)
733 while (1) {
734 /* number of chunks to fill
735 * too big => seeking is slow
736 * too small => underruns?
738 const int chunks = 1;
739 int size, nr_read, i;
740 char *wpos;
742 producer_lock();
743 if (!producer_running)
744 break;
746 if (producer_status == PS_UNLOADED ||
747 producer_status == PS_PAUSED ||
748 producer_status == PS_STOPPED || ip_eof(ip)) {
749 producer_unlock();
750 ms_sleep(50);
751 continue;
753 for (i = 0; ; i++) {
754 size = buffer_get_wpos(&wpos);
755 if (size == 0) {
756 /* buffer is full */
757 producer_unlock();
758 ms_sleep(50);
759 break;
761 nr_read = ip_read(ip, wpos, size);
762 if (nr_read < 0) {
763 if (nr_read != -1 || errno != EAGAIN) {
764 player_ip_error(nr_read, "reading file %s",
765 ip_get_filename(ip));
766 ip_delete(ip);
767 producer_status = PS_UNLOADED;
769 producer_unlock();
770 ms_sleep(50);
771 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();
907 if (ip && ip_is_remote(ip)) {
908 /* pausing not allowed */
909 player_unlock();
910 return;
912 __producer_pause();
913 __consumer_pause();
914 __player_status_changed();
915 player_unlock();
918 void player_set_file(const char *filename)
920 player_lock();
921 __producer_set_file(filename);
922 if (producer_status == PS_UNLOADED) {
923 __consumer_stop();
924 goto out;
927 /* PS_STOPPED */
928 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
929 __producer_play();
930 if (producer_status == PS_UNLOADED) {
931 __consumer_stop();
932 goto out;
934 change_sf(ip_get_sf(ip), 1);
936 out:
937 __player_status_changed();
938 if (producer_status == PS_PLAYING)
939 __prebuffer();
940 player_unlock();
943 void player_play_file(const char *filename)
945 player_lock();
946 __producer_set_file(filename);
947 if (producer_status == PS_UNLOADED) {
948 __consumer_stop();
949 goto out;
952 /* PS_STOPPED */
953 __producer_play();
955 /* PS_UNLOADED,PS_PLAYING */
956 if (producer_status == PS_UNLOADED) {
957 __consumer_stop();
958 goto out;
961 /* PS_PLAYING */
962 if (consumer_status == CS_STOPPED) {
963 __consumer_play();
964 if (consumer_status == CS_STOPPED)
965 __producer_stop();
966 } else {
967 change_sf(ip_get_sf(ip), 1);
969 out:
970 __player_status_changed();
971 if (producer_status == PS_PLAYING)
972 __prebuffer();
973 player_unlock();
976 void player_seek(double offset, int relative)
978 player_lock();
979 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
980 double pos, duration, new_pos;
981 int rc;
983 pos = (double)consumer_pos / (double)buffer_second_size();
984 duration = ip_duration(ip);
985 if (duration < 0) {
986 /* can't seek */
987 d_print("can't seek\n");
988 player_unlock();
989 return;
991 if (relative) {
992 new_pos = pos + offset;
993 if (new_pos < 0.0)
994 new_pos = 0.0;
995 if (offset > 0.0) {
996 /* seeking forward */
997 if (new_pos > duration - 5.0)
998 new_pos = duration - 5.0;
999 if (new_pos < 0.0)
1000 new_pos = 0.0;
1001 if (new_pos < pos - 0.5) {
1002 /* must seek at least 0.5s */
1003 d_print("must seek at least 0.5s\n");
1004 player_unlock();
1005 return;
1008 } else {
1009 new_pos = offset;
1010 if (new_pos < 0.0) {
1011 d_print("seek offset negative\n");
1012 player_unlock();
1013 return;
1015 if (new_pos > duration) {
1016 d_print("seek offset too large\n");
1017 player_unlock();
1018 return;
1021 /* d_print("seeking %g/%g (%g from eof)\n", new_pos, duration, duration - new_pos); */
1022 rc = ip_seek(ip, new_pos);
1023 if (rc == 0) {
1024 /* d_print("doing op_drop after seek\n"); */
1025 op_drop();
1026 reset_buffer();
1027 consumer_pos = new_pos * buffer_second_size();
1028 soft_vol_pos = consumer_pos;
1029 __consumer_position_update();
1030 } else {
1031 d_print("error: ip_seek returned %d\n", rc);
1034 player_unlock();
1038 * change output plugin without stopping playback
1040 void player_set_op(const char *name)
1042 int rc, l, r;
1044 player_lock();
1046 /* drop needed because close drains the buffer */
1047 if (consumer_status == CS_PAUSED)
1048 op_drop();
1050 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED)
1051 op_close();
1053 if (name) {
1054 d_print("setting op to '%s'\n", name);
1055 rc = op_select(name);
1056 } else {
1057 /* first initialized plugin */
1058 d_print("selecting first initialized op\n");
1059 rc = op_select_any();
1061 if (rc) {
1062 consumer_status = CS_STOPPED;
1064 __producer_stop();
1065 player_op_error(rc, "selecting output plugin '%s'", name);
1066 player_unlock();
1067 return;
1070 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
1071 set_buffer_sf(ip_get_sf(ip));
1072 rc = op_open(buffer_sf);
1073 if (rc) {
1074 consumer_status = CS_STOPPED;
1075 __producer_stop();
1076 player_op_error(rc, "opening audio device");
1077 player_unlock();
1078 return;
1080 if (consumer_status == CS_PAUSED)
1081 op_pause();
1084 if (!op_get_volume(&l, &r))
1085 volume_update(l, r);
1087 player_unlock();
1090 char *player_get_op(void)
1092 return op_get_current();
1095 void player_set_buffer_chunks(unsigned int nr_chunks)
1097 if (nr_chunks < 3)
1098 nr_chunks = 3;
1099 if (nr_chunks > 30)
1100 nr_chunks = 30;
1102 player_lock();
1103 __producer_stop();
1104 __consumer_stop();
1106 buffer_nr_chunks = nr_chunks;
1107 buffer_init();
1109 __player_status_changed();
1110 player_unlock();
1113 int player_get_buffer_chunks(void)
1115 return buffer_nr_chunks;
1118 int player_get_fileinfo(const char *filename, int *duration,
1119 struct keyval **comments)
1121 struct input_plugin *plug;
1122 int rc;
1124 *comments = NULL;
1125 *duration = -1;
1126 plug = ip_new(filename);
1127 if (ip_is_remote(plug)) {
1128 *comments = xnew0(struct keyval, 1);
1129 ip_delete(plug);
1130 return 0;
1132 rc = ip_open(plug);
1133 if (rc) {
1134 int save = errno;
1136 ip_delete(plug);
1137 errno = save;
1138 if (rc != -1)
1139 rc = -PLAYER_ERROR_NOT_SUPPORTED;
1140 return rc;
1142 *duration = ip_duration(plug);
1143 rc = ip_read_comments(plug, comments);
1144 ip_delete(plug);
1145 return rc;
1148 int player_get_volume(int *left, int *right)
1150 int rc;
1152 consumer_lock();
1153 rc = op_get_volume(left, right);
1154 consumer_unlock();
1155 return rc;
1158 int player_set_volume(int left, int right)
1160 int rc;
1162 consumer_lock();
1163 rc = op_set_volume(left, right);
1164 if (!rc)
1165 volume_update(left, right);
1166 consumer_unlock();
1167 return rc;
1170 void player_set_soft_vol(int soft)
1172 int l, r;
1174 consumer_lock();
1175 /* don't mess with soft_vol_pos if soft_vol is already true */
1176 if (!soft_vol)
1177 soft_vol_pos = consumer_pos;
1178 op_set_soft_vol(soft);
1179 if (!op_get_volume(&l, &r))
1180 volume_update(l, r);
1181 consumer_unlock();
1184 int player_set_op_option(unsigned int id, const char *val)
1186 int rc;
1188 player_lock();
1189 __consumer_stop();
1190 __producer_stop();
1191 rc = op_set_option(id, val);
1192 __player_status_changed();
1193 player_unlock();
1194 return rc;
1197 int player_get_op_option(unsigned int id, char **val)
1199 int rc;
1201 player_lock();
1202 rc = op_get_option(id, val);
1203 player_unlock();
1204 return rc;
1207 int player_for_each_op_option(void (*callback)(unsigned int id, const char *key))
1209 player_lock();
1210 __consumer_stop();
1211 __producer_stop();
1212 op_for_each_option(callback);
1213 __player_status_changed();
1214 player_unlock();
1215 return 0;
1218 void player_dump_plugins(void)
1220 ip_dump_plugins();
1221 op_dump_plugins();