cmus_play_file: Treat URLs specially
[cmus.git] / player.c
blob57fd2cddfbf0540ba7b40e5c53a1c7823e0c24a0
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 .ti = NULL,
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 unsigned int consumer_pos = 0;
88 /* usually same as consumer_pos, sometimes less than consumer_pos */
89 static unsigned 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, unsigned int *countp)
165 signed short *buf;
166 unsigned int count = *countp;
167 int ch, bits, l, r, i;
169 BUG_ON(soft_vol_pos < consumer_pos);
171 if (consumer_pos != soft_vol_pos) {
172 unsigned int offs = soft_vol_pos - consumer_pos;
174 if (offs >= count)
175 return;
176 buffer += offs;
177 count -= offs;
179 soft_vol_pos += count;
180 buf = (signed short *)buffer;
182 if (soft_vol_l == 100 && soft_vol_r == 100)
183 return;
185 ch = sf_get_channels(buffer_sf);
186 bits = sf_get_bits(buffer_sf);
187 if (ch != 2 || bits != 16)
188 return;
190 l = SOFT_VOL_SCALE;
191 r = SOFT_VOL_SCALE;
192 if (soft_vol_l != 100)
193 l = soft_vol_db[soft_vol_l];
194 if (soft_vol_r != 100)
195 r = soft_vol_db[soft_vol_r];
197 for (i = 0; i < count / 4; i++) {
198 scale_sample(buf, i * 2, l);
199 scale_sample(buf, i * 2 + 1, r);
203 static inline unsigned int buffer_second_size(void)
205 return sf_get_second_size(buffer_sf);
208 static inline int get_next(struct track_info **ti)
210 return player_cbs->get_next(ti);
213 /* updating player status {{{ */
215 static inline void file_changed(struct track_info *ti)
217 player_info_lock();
218 if (player_info.ti)
219 track_info_unref(player_info.ti);
221 player_info.ti = ti;
222 if (ti) {
223 d_print("file: %s\n", ti->filename);
224 } else {
225 d_print("unloaded\n");
227 player_info.metadata[0] = 0;
228 player_info.file_changed = 1;
229 player_info_unlock();
232 static inline void metadata_changed(void)
234 player_info_lock();
235 d_print("metadata changed: %s\n", ip_get_metadata(ip));
236 memcpy(player_info.metadata, ip_get_metadata(ip), 255 * 16 + 1);
237 player_info.metadata_changed = 1;
238 player_info_unlock();
241 static inline void volume_update(int left, int right)
243 if (player_info.vol_left == left && player_info.vol_right == right)
244 return;
246 player_info_lock();
247 player_info.vol_left = left;
248 player_info.vol_right = right;
249 player_info.vol_changed = 1;
250 player_info_unlock();
253 static void player_error(const char *msg)
255 player_info_lock();
256 player_info.status = consumer_status;
257 player_info.pos = 0;
258 player_info.buffer_fill = buffer_get_filled_chunks();
259 player_info.buffer_size = buffer_nr_chunks;
260 player_info.status_changed = 1;
262 free(player_info.error_msg);
263 player_info.error_msg = xstrdup(msg);
264 player_info_unlock();
266 d_print("ERROR: '%s'\n", msg);
269 static void __FORMAT(2, 3) player_ip_error(int rc, const char *format, ...)
271 char buffer[1024];
272 va_list ap;
273 char *msg;
274 int save = errno;
276 va_start(ap, format);
277 vsnprintf(buffer, sizeof(buffer), format, ap);
278 va_end(ap);
280 errno = save;
281 msg = ip_get_error_msg(ip, rc, buffer);
282 player_error(msg);
283 free(msg);
286 static void __FORMAT(2, 3) player_op_error(int rc, const char *format, ...)
288 char buffer[1024];
289 va_list ap;
290 char *msg;
291 int save = errno;
293 va_start(ap, format);
294 vsnprintf(buffer, sizeof(buffer), format, ap);
295 va_end(ap);
297 errno = save;
298 msg = op_get_error_msg(rc, buffer);
299 player_error(msg);
300 free(msg);
303 /* FIXME: don't poll */
304 static void mixer_check(void)
306 static struct timeval old_t = { 0L, 0L };
307 struct timeval t;
308 long usec, sec;
309 int l, r;
311 gettimeofday(&t, NULL);
312 usec = t.tv_usec - old_t.tv_usec;
313 sec = t.tv_sec - old_t.tv_sec;
314 if (sec) {
315 /* multiplying sec with 1e6 can overflow */
316 usec += 1e6L;
318 if (usec < 300e3)
319 return;
321 old_t = t;
322 if (!op_get_volume(&l, &r))
323 volume_update(l, r);
327 * buffer-fill changed
329 static void __producer_buffer_fill_update(void)
331 int fill;
333 player_info_lock();
334 fill = buffer_get_filled_chunks();
335 if (fill != player_info.buffer_fill) {
336 /* d_print("\n"); */
337 player_info.buffer_fill = fill;
338 player_info.buffer_fill_changed = 1;
340 player_info_unlock();
344 * playing position changed
346 static void __consumer_position_update(void)
348 static unsigned int old_pos = -1;
349 unsigned int pos = 0;
351 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED)
352 pos = consumer_pos / buffer_second_size();
353 if (pos != old_pos) {
354 /* d_print("\n"); */
355 old_pos = pos;
357 player_info_lock();
358 player_info.pos = pos;
359 player_info.position_changed = 1;
360 player_info_unlock();
365 * something big happened (stopped/paused/unpaused...)
367 static void __player_status_changed(void)
369 unsigned int pos = 0;
371 /* d_print("\n"); */
372 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED)
373 pos = consumer_pos / buffer_second_size();
375 player_info_lock();
376 player_info.status = consumer_status;
377 player_info.pos = pos;
378 player_info.buffer_fill = buffer_get_filled_chunks();
379 player_info.buffer_size = buffer_nr_chunks;
380 player_info.status_changed = 1;
381 player_info_unlock();
384 /* updating player status }}} */
386 static void __prebuffer(void)
388 int limit_chunks;
390 BUG_ON(producer_status != PS_PLAYING);
391 if (ip_is_remote(ip)) {
392 limit_chunks = buffer_nr_chunks;
393 } else {
394 int limit_ms, limit_size;
396 limit_ms = 250;
397 limit_size = limit_ms * buffer_second_size() / 1000;
398 limit_chunks = limit_size / CHUNK_SIZE;
399 if (limit_chunks < 1)
400 limit_chunks = 1;
402 while (1) {
403 int nr_read, size, filled;
404 char *wpos;
406 filled = buffer_get_filled_chunks();
407 /* d_print("PREBUF: %2d / %2d\n", filled, limit_chunks); */
409 /* not fatal */
410 //BUG_ON(filled > limit_chunks);
412 if (filled >= limit_chunks)
413 break;
415 size = buffer_get_wpos(&wpos);
416 nr_read = ip_read(ip, wpos, size);
417 if (nr_read < 0) {
418 if (nr_read == -1 && errno == EAGAIN)
419 continue;
420 player_ip_error(nr_read, "reading file %s", ip_get_filename(ip));
421 /* ip_read sets eof */
422 nr_read = 0;
424 if (ip_metadata_changed(ip))
425 metadata_changed();
427 /* buffer_fill with 0 count marks current chunk filled */
428 buffer_fill(nr_read);
430 __producer_buffer_fill_update();
431 if (nr_read == 0) {
432 /* EOF */
433 break;
438 /* setting producer status {{{ */
440 static void __producer_play(void)
442 if (producer_status == PS_UNLOADED) {
443 struct track_info *ti;
445 if (get_next(&ti) == 0) {
446 int rc;
448 ip = ip_new(ti->filename);
449 rc = ip_open(ip);
450 if (rc) {
451 player_ip_error(rc, "opening file `%s'", ti->filename);
452 ip_delete(ip);
453 track_info_unref(ti);
454 file_changed(NULL);
455 } else {
456 ip_setup(ip);
457 producer_status = PS_PLAYING;
458 file_changed(ti);
461 } else if (producer_status == PS_PLAYING) {
462 if (ip_seek(ip, 0.0) == 0) {
463 reset_buffer();
465 } else if (producer_status == PS_STOPPED) {
466 int rc;
468 rc = ip_open(ip);
469 if (rc) {
470 player_ip_error(rc, "opening file `%s'", ip_get_filename(ip));
471 ip_delete(ip);
472 producer_status = PS_UNLOADED;
473 } else {
474 ip_setup(ip);
475 producer_status = PS_PLAYING;
477 } else if (producer_status == PS_PAUSED) {
478 producer_status = PS_PLAYING;
482 static void __producer_stop(void)
484 if (producer_status == PS_PLAYING || producer_status == PS_PAUSED) {
485 ip_close(ip);
486 producer_status = PS_STOPPED;
487 reset_buffer();
491 static void __producer_unload(void)
493 __producer_stop();
494 if (producer_status == PS_STOPPED) {
495 ip_delete(ip);
496 producer_status = PS_UNLOADED;
500 static void __producer_pause(void)
502 if (producer_status == PS_PLAYING) {
503 producer_status = PS_PAUSED;
504 } else if (producer_status == PS_PAUSED) {
505 producer_status = PS_PLAYING;
509 static void __producer_set_file(struct track_info *ti)
511 __producer_unload();
512 ip = ip_new(ti->filename);
513 producer_status = PS_STOPPED;
514 file_changed(ti);
517 /* setting producer status }}} */
519 /* setting consumer status {{{ */
521 static void __consumer_play(void)
523 if (consumer_status == CS_PLAYING) {
524 op_drop();
525 } else if (consumer_status == CS_STOPPED) {
526 int rc;
528 set_buffer_sf(ip_get_sf(ip));
529 rc = op_open(buffer_sf);
530 if (rc) {
531 player_op_error(rc, "opening audio device");
532 } else {
533 consumer_status = CS_PLAYING;
535 } else if (consumer_status == CS_PAUSED) {
536 op_unpause();
537 consumer_status = CS_PLAYING;
541 static void __consumer_drain_and_stop(void)
543 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
544 op_close();
545 consumer_status = CS_STOPPED;
549 static void __consumer_stop(void)
551 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
552 op_drop();
553 op_close();
554 consumer_status = CS_STOPPED;
558 static void __consumer_pause(void)
560 if (consumer_status == CS_PLAYING) {
561 op_pause();
562 consumer_status = CS_PAUSED;
563 } else if (consumer_status == CS_PAUSED) {
564 op_unpause();
565 consumer_status = CS_PLAYING;
569 /* setting consumer status }}} */
571 static int change_sf(sample_format_t sf, int drop)
573 int old_sf = buffer_sf;
575 set_buffer_sf(sf);
576 if (buffer_sf != old_sf) {
577 /* reopen */
578 int rc;
580 if (drop)
581 op_drop();
582 op_close();
583 rc = op_open(buffer_sf);
584 if (rc) {
585 player_op_error(rc, "opening audio device");
586 consumer_status = CS_STOPPED;
587 __producer_stop();
588 return rc;
590 } else if (consumer_status == CS_PAUSED) {
591 op_drop();
592 op_unpause();
594 consumer_status = CS_PLAYING;
595 return 0;
598 static void __consumer_handle_eof(void)
600 struct track_info *ti;
602 if (ip_is_remote(ip)) {
603 __producer_stop();
604 __consumer_drain_and_stop();
605 player_error("lost connection");
606 return;
609 if (get_next(&ti) == 0) {
610 __producer_unload();
611 ip = ip_new(ti->filename);
612 producer_status = PS_STOPPED;
613 /* PS_STOPPED, CS_PLAYING */
614 if (player_cont) {
615 __producer_play();
616 if (producer_status == PS_UNLOADED) {
617 __consumer_stop();
618 track_info_unref(ti);
619 file_changed(NULL);
620 } else {
621 /* PS_PLAYING */
622 file_changed(ti);
623 if (!change_sf(ip_get_sf(ip), 0))
624 __prebuffer();
626 } else {
627 __consumer_drain_and_stop();
628 file_changed(ti);
630 } else {
631 __producer_unload();
632 __consumer_drain_and_stop();
633 file_changed(NULL);
635 __player_status_changed();
638 static void *consumer_loop(void *arg)
640 while (1) {
641 int rc, space;
642 int size;
643 char *rpos;
645 consumer_lock();
646 if (!consumer_running)
647 break;
649 if (consumer_status == CS_PAUSED || consumer_status == CS_STOPPED) {
650 mixer_check();
651 consumer_unlock();
652 ms_sleep(50);
653 continue;
655 space = op_buffer_space();
656 if (space == -1) {
657 /* busy */
658 __consumer_position_update();
659 consumer_unlock();
660 ms_sleep(50);
661 continue;
663 /* d_print("BS: %6d %3d\n", space, space * 1000 / (44100 * 2 * 2)); */
665 while (1) {
666 /* 25 ms is 4410 B */
667 if (space < 4096) {
668 __consumer_position_update();
669 mixer_check();
670 consumer_unlock();
671 ms_sleep(25);
672 break;
674 size = buffer_get_rpos(&rpos);
675 if (size == 0) {
676 producer_lock();
677 if (producer_status != PS_PLAYING) {
678 producer_unlock();
679 consumer_unlock();
680 break;
682 /* must recheck rpos */
683 size = buffer_get_rpos(&rpos);
684 if (size == 0) {
685 /* OK. now it's safe to check if we are at EOF */
686 if (ip_eof(ip)) {
687 /* EOF */
688 __consumer_handle_eof();
689 producer_unlock();
690 consumer_unlock();
691 break;
692 } else {
693 /* possible underrun */
694 producer_unlock();
695 __consumer_position_update();
696 consumer_unlock();
697 /* d_print("possible underrun\n"); */
698 ms_sleep(10);
699 break;
703 /* player_buffer and ip.eof were inconsistent */
704 producer_unlock();
706 if (size > space)
707 size = space;
708 if (soft_vol)
709 soft_vol_scale(rpos, &size);
710 rc = op_write(rpos, size);
711 if (rc < 0) {
712 d_print("op_write returned %d %s\n", rc,
713 rc == -1 ? strerror(errno) : "");
715 /* try to reopen */
716 op_close();
717 consumer_status = CS_STOPPED;
718 __consumer_play();
720 consumer_unlock();
721 break;
723 buffer_consume(rc);
724 consumer_pos += rc;
725 space -= rc;
728 __consumer_stop();
729 consumer_unlock();
730 return NULL;
733 static void *producer_loop(void *arg)
735 while (1) {
736 /* number of chunks to fill
737 * too big => seeking is slow
738 * too small => underruns?
740 const int chunks = 1;
741 int size, nr_read, i;
742 char *wpos;
744 producer_lock();
745 if (!producer_running)
746 break;
748 if (producer_status == PS_UNLOADED ||
749 producer_status == PS_PAUSED ||
750 producer_status == PS_STOPPED || ip_eof(ip)) {
751 producer_unlock();
752 ms_sleep(50);
753 continue;
755 for (i = 0; ; i++) {
756 size = buffer_get_wpos(&wpos);
757 if (size == 0) {
758 /* buffer is full */
759 producer_unlock();
760 ms_sleep(50);
761 break;
763 nr_read = ip_read(ip, wpos, size);
764 if (nr_read < 0) {
765 if (nr_read != -1 || errno != EAGAIN) {
766 player_ip_error(nr_read, "reading file %s",
767 ip_get_filename(ip));
768 /* ip_read sets eof */
769 nr_read = 0;
770 } else {
771 producer_unlock();
772 ms_sleep(50);
773 break;
776 if (ip_metadata_changed(ip))
777 metadata_changed();
779 /* buffer_fill with 0 count marks current chunk filled */
780 buffer_fill(nr_read);
781 if (nr_read == 0) {
782 /* consumer handles EOF */
783 producer_unlock();
784 ms_sleep(50);
785 break;
787 if (i == chunks) {
788 producer_unlock();
789 /* don't sleep! */
790 break;
793 __producer_buffer_fill_update();
795 __producer_unload();
796 producer_unlock();
797 return NULL;
800 void player_load_plugins(void)
802 ip_load_plugins();
803 op_load_plugins();
806 void player_init(const struct player_callbacks *callbacks)
808 int rc;
809 #if defined(__linux__) || defined(__FreeBSD__)
810 pthread_attr_t attr;
811 #endif
812 pthread_attr_t *attrp = NULL;
814 /* 1 s is 176400 B (0.168 MB)
815 * 10 s is 1.68 MB
817 buffer_nr_chunks = 10 * 44100 * 16 / 8 * 2 / CHUNK_SIZE;
818 buffer_init();
820 player_cbs = callbacks;
822 #if defined(__linux__) || defined(__FreeBSD__)
823 rc = pthread_attr_init(&attr);
824 BUG_ON(rc);
825 rc = pthread_attr_setschedpolicy(&attr, SCHED_RR);
826 if (rc) {
827 d_print("could not set real-time scheduling priority: %s\n", strerror(rc));
828 } else {
829 struct sched_param param;
831 d_print("using real-time scheduling\n");
832 param.sched_priority = sched_get_priority_max(SCHED_RR);
833 d_print("setting priority to %d\n", param.sched_priority);
834 rc = pthread_attr_setschedparam(&attr, &param);
835 BUG_ON(rc);
836 attrp = &attr;
838 #endif
840 rc = pthread_create(&producer_thread, NULL, producer_loop, NULL);
841 BUG_ON(rc);
843 rc = pthread_create(&consumer_thread, attrp, consumer_loop, NULL);
844 if (rc && attrp) {
845 d_print("could not create thread using real-time scheduling: %s\n", strerror(rc));
846 rc = pthread_create(&consumer_thread, NULL, consumer_loop, NULL);
848 BUG_ON(rc);
850 /* update player_info.cont etc. */
851 player_lock();
852 __player_status_changed();
853 player_unlock();
856 void player_exit(void)
858 int rc;
860 player_lock();
861 consumer_running = 0;
862 producer_running = 0;
863 player_unlock();
865 rc = pthread_join(consumer_thread, NULL);
866 BUG_ON(rc);
867 rc = pthread_join(producer_thread, NULL);
868 BUG_ON(rc);
870 op_exit_plugins();
873 void player_stop(void)
875 player_lock();
876 __consumer_stop();
877 __producer_stop();
878 __player_status_changed();
879 player_unlock();
882 void player_play(void)
884 int prebuffer;
886 player_lock();
887 if (producer_status == PS_PLAYING && ip_is_remote(ip)) {
888 /* seeking not allowed */
889 player_unlock();
890 return;
892 prebuffer = consumer_status == CS_STOPPED;
893 __producer_play();
894 if (producer_status == PS_PLAYING) {
895 __consumer_play();
896 if (consumer_status != CS_PLAYING)
897 __producer_stop();
898 } else {
899 __consumer_stop();
901 __player_status_changed();
902 if (consumer_status == CS_PLAYING && prebuffer)
903 __prebuffer();
904 player_unlock();
907 void player_pause(void)
909 player_lock();
911 if (consumer_status == CS_STOPPED) {
912 __producer_play();
913 if (producer_status == PS_PLAYING) {
914 __consumer_play();
915 if (consumer_status != CS_PLAYING)
916 __producer_stop();
918 __player_status_changed();
919 if (consumer_status == CS_PLAYING)
920 __prebuffer();
921 player_unlock();
922 return;
925 if (ip && ip_is_remote(ip)) {
926 /* pausing not allowed */
927 player_unlock();
928 return;
930 __producer_pause();
931 __consumer_pause();
932 __player_status_changed();
933 player_unlock();
936 void player_set_file(struct track_info *ti)
938 player_lock();
939 __producer_set_file(ti);
940 if (producer_status == PS_UNLOADED) {
941 __consumer_stop();
942 goto out;
945 /* PS_STOPPED */
946 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
947 __producer_play();
948 if (producer_status == PS_UNLOADED) {
949 __consumer_stop();
950 goto out;
952 change_sf(ip_get_sf(ip), 1);
954 out:
955 __player_status_changed();
956 if (producer_status == PS_PLAYING)
957 __prebuffer();
958 player_unlock();
961 void player_play_file(struct track_info *ti)
963 player_lock();
964 __producer_set_file(ti);
965 if (producer_status == PS_UNLOADED) {
966 __consumer_stop();
967 goto out;
970 /* PS_STOPPED */
971 __producer_play();
973 /* PS_UNLOADED,PS_PLAYING */
974 if (producer_status == PS_UNLOADED) {
975 __consumer_stop();
976 goto out;
979 /* PS_PLAYING */
980 if (consumer_status == CS_STOPPED) {
981 __consumer_play();
982 if (consumer_status == CS_STOPPED)
983 __producer_stop();
984 } else {
985 change_sf(ip_get_sf(ip), 1);
987 out:
988 __player_status_changed();
989 if (producer_status == PS_PLAYING)
990 __prebuffer();
991 player_unlock();
994 void player_seek(double offset, int relative)
996 player_lock();
997 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
998 double pos, duration, new_pos;
999 int rc;
1001 pos = (double)consumer_pos / (double)buffer_second_size();
1002 duration = ip_duration(ip);
1003 if (duration < 0) {
1004 /* can't seek */
1005 d_print("can't seek\n");
1006 player_unlock();
1007 return;
1009 if (relative) {
1010 new_pos = pos + offset;
1011 if (new_pos < 0.0)
1012 new_pos = 0.0;
1013 if (offset > 0.0) {
1014 /* seeking forward */
1015 if (new_pos > duration - 5.0)
1016 new_pos = duration - 5.0;
1017 if (new_pos < 0.0)
1018 new_pos = 0.0;
1019 if (new_pos < pos - 0.5) {
1020 /* must seek at least 0.5s */
1021 d_print("must seek at least 0.5s\n");
1022 player_unlock();
1023 return;
1026 } else {
1027 new_pos = offset;
1028 if (new_pos < 0.0) {
1029 d_print("seek offset negative\n");
1030 player_unlock();
1031 return;
1033 if (new_pos > duration) {
1034 d_print("seek offset too large\n");
1035 player_unlock();
1036 return;
1039 /* d_print("seeking %g/%g (%g from eof)\n", new_pos, duration, duration - new_pos); */
1040 rc = ip_seek(ip, new_pos);
1041 if (rc == 0) {
1042 /* d_print("doing op_drop after seek\n"); */
1043 op_drop();
1044 reset_buffer();
1045 consumer_pos = new_pos * buffer_second_size();
1046 soft_vol_pos = consumer_pos;
1047 __consumer_position_update();
1048 } else {
1049 d_print("error: ip_seek returned %d\n", rc);
1052 player_unlock();
1056 * change output plugin without stopping playback
1058 void player_set_op(const char *name)
1060 int rc, l, r;
1062 player_lock();
1064 /* drop needed because close drains the buffer */
1065 if (consumer_status == CS_PAUSED)
1066 op_drop();
1068 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED)
1069 op_close();
1071 if (name) {
1072 d_print("setting op to '%s'\n", name);
1073 rc = op_select(name);
1074 } else {
1075 /* first initialized plugin */
1076 d_print("selecting first initialized op\n");
1077 rc = op_select_any();
1079 if (rc) {
1080 consumer_status = CS_STOPPED;
1082 __producer_stop();
1083 player_op_error(rc, "selecting output plugin '%s'", name);
1084 player_unlock();
1085 return;
1088 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
1089 set_buffer_sf(ip_get_sf(ip));
1090 rc = op_open(buffer_sf);
1091 if (rc) {
1092 consumer_status = CS_STOPPED;
1093 __producer_stop();
1094 player_op_error(rc, "opening audio device");
1095 player_unlock();
1096 return;
1098 if (consumer_status == CS_PAUSED)
1099 op_pause();
1102 if (!op_get_volume(&l, &r))
1103 volume_update(l, r);
1105 player_unlock();
1108 char *player_get_op(void)
1110 return op_get_current();
1113 void player_set_buffer_chunks(unsigned int nr_chunks)
1115 if (nr_chunks < 3)
1116 nr_chunks = 3;
1117 if (nr_chunks > 30)
1118 nr_chunks = 30;
1120 player_lock();
1121 __producer_stop();
1122 __consumer_stop();
1124 buffer_nr_chunks = nr_chunks;
1125 buffer_init();
1127 __player_status_changed();
1128 player_unlock();
1131 int player_get_buffer_chunks(void)
1133 return buffer_nr_chunks;
1136 int player_get_fileinfo(const char *filename, int *duration,
1137 struct keyval **comments)
1139 struct input_plugin *plug;
1140 int rc;
1142 *comments = NULL;
1143 *duration = -1;
1144 plug = ip_new(filename);
1145 if (ip_is_remote(plug)) {
1146 *comments = xnew0(struct keyval, 1);
1147 ip_delete(plug);
1148 return 0;
1150 rc = ip_open(plug);
1151 if (rc) {
1152 int save = errno;
1154 ip_delete(plug);
1155 errno = save;
1156 if (rc != -1)
1157 rc = -PLAYER_ERROR_NOT_SUPPORTED;
1158 return rc;
1160 *duration = ip_duration(plug);
1161 rc = ip_read_comments(plug, comments);
1162 ip_delete(plug);
1163 return rc;
1166 int player_get_volume(int *left, int *right)
1168 int rc;
1170 consumer_lock();
1171 rc = op_get_volume(left, right);
1172 consumer_unlock();
1173 return rc;
1176 int player_set_volume(int left, int right)
1178 int rc;
1180 consumer_lock();
1181 rc = op_set_volume(left, right);
1182 if (!rc)
1183 volume_update(left, right);
1184 consumer_unlock();
1185 return rc;
1188 void player_set_soft_vol(int soft)
1190 int l, r;
1192 consumer_lock();
1193 /* don't mess with soft_vol_pos if soft_vol is already true */
1194 if (!soft_vol)
1195 soft_vol_pos = consumer_pos;
1196 op_set_soft_vol(soft);
1197 if (!op_get_volume(&l, &r))
1198 volume_update(l, r);
1199 consumer_unlock();
1202 int player_set_op_option(unsigned int id, const char *val)
1204 int rc;
1206 player_lock();
1207 __consumer_stop();
1208 __producer_stop();
1209 rc = op_set_option(id, val);
1210 __player_status_changed();
1211 player_unlock();
1212 return rc;
1215 int player_get_op_option(unsigned int id, char **val)
1217 int rc;
1219 player_lock();
1220 rc = op_get_option(id, val);
1221 player_unlock();
1222 return rc;
1225 int player_for_each_op_option(void (*callback)(unsigned int id, const char *key))
1227 player_lock();
1228 __consumer_stop();
1229 __producer_stop();
1230 op_for_each_option(callback);
1231 __player_status_changed();
1232 player_unlock();
1233 return 0;
1236 void player_dump_plugins(void)
1238 ip_dump_plugins();
1239 op_dump_plugins();