2 * Copyright 2004-2006 Timo Hirvonen
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
37 enum producer_status
{
44 enum consumer_status
{
50 struct player_info player_info
= {
51 .mutex
= CMUS_MUTEX_INITIALIZER
,
54 .status
= PLAYER_STATUS_STOPPED
,
62 .metadata_changed
= 0,
64 .position_changed
= 0,
65 .buffer_fill_changed
= 0,
69 /* continue playing after track is finished? */
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;
90 #define producer_lock() cmus_mutex_lock(&producer_mutex)
91 #define producer_unlock() cmus_mutex_unlock(&producer_mutex)
93 #define consumer_lock() cmus_mutex_lock(&consumer_mutex)
94 #define consumer_unlock() cmus_mutex_unlock(&consumer_mutex)
96 #define player_lock() \
102 #define player_unlock() \
110 static void reset_buffer(void)
116 static void set_buffer_sf(sample_format_t sf
)
120 /* ip_read converts samples to this format */
121 if (sf_get_channels(buffer_sf
) <= 2 && sf_get_bits(buffer_sf
) <= 16) {
122 buffer_sf
&= SF_RATE_MASK
;
123 buffer_sf
|= sf_channels(2) | sf_bits(16) | sf_signed(1);
127 static inline int buffer_second_size(void)
129 return sf_get_second_size(buffer_sf
);
132 static inline int get_next(char **filename
)
134 return player_cbs
->get_next(filename
);
137 /* updating player status {{{ */
139 static inline void file_changed(void)
142 if (producer_status
== PS_UNLOADED
) {
143 player_info
.filename
[0] = 0;
145 strncpy(player_info
.filename
, ip_get_filename(ip
), sizeof(player_info
.filename
));
146 player_info
.filename
[sizeof(player_info
.filename
) - 1] = 0;
148 d_print("%s\n", player_info
.filename
);
149 player_info
.metadata
[0] = 0;
150 player_info
.file_changed
= 1;
151 player_info_unlock();
154 static inline void metadata_changed(void)
157 d_print("metadata changed: %s\n", ip_get_metadata(ip
));
158 memcpy(player_info
.metadata
, ip_get_metadata(ip
), 255 * 16 + 1);
159 player_info
.metadata_changed
= 1;
160 player_info_unlock();
163 static inline void volume_update(int left
, int right
)
165 if (player_info
.vol_left
== left
&& player_info
.vol_right
== right
)
169 player_info
.vol_left
= left
;
170 player_info
.vol_right
= right
;
171 player_info
.vol_changed
= 1;
172 player_info_unlock();
175 static void player_error(const char *msg
)
178 player_info
.status
= consumer_status
;
180 player_info
.buffer_fill
= buffer_get_filled_chunks();
181 player_info
.buffer_size
= buffer_nr_chunks
;
182 player_info
.status_changed
= 1;
184 free(player_info
.error_msg
);
185 player_info
.error_msg
= xstrdup(msg
);
186 player_info_unlock();
188 d_print("ERROR: '%s'\n", msg
);
191 static void __FORMAT(2, 3) player_ip_error(int rc
, const char *format
, ...)
198 va_start(ap
, format
);
199 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
203 msg
= ip_get_error_msg(ip
, rc
, buffer
);
208 static void __FORMAT(2, 3) player_op_error(int rc
, const char *format
, ...)
215 va_start(ap
, format
);
216 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
220 msg
= op_get_error_msg(rc
, buffer
);
225 /* FIXME: don't poll */
226 static void mixer_check(void)
228 static struct timeval old_st
= { 0L, 0L };
232 gettimeofday(&st
, NULL
);
233 if (st
.tv_sec
== old_st
.tv_sec
) {
234 unsigned long usecs
= st
.tv_sec
- old_st
.tv_sec
;
240 if (op_get_volume(&l
, &r
) == 0)
245 * buffer-fill changed
247 static void __producer_buffer_fill_update(void)
252 fill
= buffer_get_filled_chunks();
253 if (fill
!= player_info
.buffer_fill
) {
255 player_info
.buffer_fill
= fill
;
256 player_info
.buffer_fill_changed
= 1;
258 player_info_unlock();
262 * playing position changed
264 static void __consumer_position_update(void)
266 static int old_pos
= -1;
270 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
271 pos
= consumer_pos
/ buffer_second_size();
272 if (pos
!= old_pos
) {
277 player_info
.pos
= pos
;
278 player_info
.position_changed
= 1;
279 player_info_unlock();
284 * something big happened (stopped/paused/unpaused...)
286 static void __player_status_changed(void)
291 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
292 pos
= consumer_pos
/ buffer_second_size();
295 player_info
.status
= consumer_status
;
296 player_info
.pos
= pos
;
297 player_info
.buffer_fill
= buffer_get_filled_chunks();
298 player_info
.buffer_size
= buffer_nr_chunks
;
299 player_info
.status_changed
= 1;
300 player_info_unlock();
303 /* updating player status }}} */
305 static void __prebuffer(void)
309 BUG_ON(producer_status
!= PS_PLAYING
);
310 if (ip_is_remote(ip
)) {
311 limit_chunks
= buffer_nr_chunks
;
313 int limit_ms
, limit_size
;
316 limit_size
= limit_ms
* buffer_second_size() / 1000;
317 limit_chunks
= limit_size
/ CHUNK_SIZE
;
318 if (limit_chunks
< 1)
322 int nr_read
, size
, filled
;
325 filled
= buffer_get_filled_chunks();
326 /* d_print("PREBUF: %2d / %2d\n", filled, limit_chunks); */
329 //BUG_ON(filled > limit_chunks);
331 if (filled
>= limit_chunks
)
334 size
= buffer_get_wpos(&wpos
);
335 nr_read
= ip_read(ip
, wpos
, size
);
337 if (nr_read
== -1 && errno
== EAGAIN
)
339 player_ip_error(nr_read
, "reading file %s", ip_get_filename(ip
));
341 producer_status
= PS_UNLOADED
;
344 if (ip_metadata_changed(ip
))
347 /* buffer_fill with 0 count marks current chunk filled */
348 buffer_fill(nr_read
);
350 __producer_buffer_fill_update();
358 /* setting producer status {{{ */
360 static void __producer_play(void)
362 if (producer_status
== PS_UNLOADED
) {
365 if (get_next(&filename
) == 0) {
368 ip
= ip_new(filename
);
371 player_ip_error(rc
, "opening file `%s'", filename
);
374 producer_status
= PS_PLAYING
;
379 } else if (producer_status
== PS_PLAYING
) {
380 if (ip_seek(ip
, 0.0) == 0) {
383 } else if (producer_status
== PS_STOPPED
) {
388 player_ip_error(rc
, "opening file `%s'", ip_get_filename(ip
));
390 producer_status
= PS_UNLOADED
;
392 producer_status
= PS_PLAYING
;
394 } else if (producer_status
== PS_PAUSED
) {
395 producer_status
= PS_PLAYING
;
399 static void __producer_stop(void)
401 if (producer_status
== PS_PLAYING
|| producer_status
== PS_PAUSED
) {
403 producer_status
= PS_STOPPED
;
408 static void __producer_unload(void)
411 if (producer_status
== PS_STOPPED
) {
413 producer_status
= PS_UNLOADED
;
417 static void __producer_pause(void)
419 if (producer_status
== PS_PLAYING
) {
420 producer_status
= PS_PAUSED
;
421 } else if (producer_status
== PS_PAUSED
) {
422 producer_status
= PS_PLAYING
;
426 static void __producer_set_file(const char *filename
)
429 ip
= ip_new(filename
);
430 producer_status
= PS_STOPPED
;
434 /* setting producer status }}} */
436 /* setting consumer status {{{ */
438 static void __consumer_play(void)
440 if (consumer_status
== CS_PLAYING
) {
442 } else if (consumer_status
== CS_STOPPED
) {
445 set_buffer_sf(ip_get_sf(ip
));
446 rc
= op_open(buffer_sf
);
448 player_op_error(rc
, "opening audio device");
450 consumer_status
= CS_PLAYING
;
452 } else if (consumer_status
== CS_PAUSED
) {
454 consumer_status
= CS_PLAYING
;
458 static void __consumer_drain_and_stop(void)
460 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
462 consumer_status
= CS_STOPPED
;
466 static void __consumer_stop(void)
468 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
471 consumer_status
= CS_STOPPED
;
475 static void __consumer_pause(void)
477 if (consumer_status
== CS_PLAYING
) {
479 consumer_status
= CS_PAUSED
;
480 } else if (consumer_status
== CS_PAUSED
) {
482 consumer_status
= CS_PLAYING
;
486 /* setting consumer status }}} */
488 static void __consumer_handle_eof(void)
492 if (ip_is_remote(ip
)) {
494 __consumer_drain_and_stop();
495 player_error("lost connection");
499 if (get_next(&filename
) == 0) {
503 ip
= ip_new(filename
);
504 producer_status
= PS_STOPPED
;
505 /* PS_STOPPED, CS_PLAYING */
508 if (producer_status
== PS_UNLOADED
) {
513 set_buffer_sf(ip_get_sf(ip
));
514 rc
= op_set_sf(buffer_sf
);
517 consumer_status
= CS_STOPPED
;
518 player_op_error(rc
, "setting sample format");
526 __consumer_drain_and_stop();
532 __consumer_drain_and_stop();
535 __player_status_changed();
538 static void *consumer_loop(void *arg
)
546 if (!consumer_running
)
549 if (consumer_status
== CS_PAUSED
|| consumer_status
== CS_STOPPED
) {
555 space
= op_buffer_space();
558 __consumer_position_update();
563 /* d_print("BS: %6d %3d\n", space, space * 1000 / (44100 * 2 * 2)); */
566 /* 25 ms is 4410 B */
568 __consumer_position_update();
574 size
= buffer_get_rpos(&rpos
);
577 if (producer_status
!= PS_PLAYING
) {
582 /* must recheck rpos */
583 size
= buffer_get_rpos(&rpos
);
585 /* OK. now it's safe to check if we are at EOF */
588 __consumer_handle_eof();
593 /* possible underrun */
595 __consumer_position_update();
597 /* d_print("possible underrun\n"); */
603 /* player_buffer and ip.eof were inconsistent */
608 rc
= op_write(rpos
, size
);
610 d_print("op_write returned %d %s\n", rc
,
611 rc
== -1 ? strerror(errno
) : "");
615 consumer_status
= CS_STOPPED
;
631 static void *producer_loop(void *arg
)
634 /* number of chunks to fill
635 * too big => seeking is slow
636 * too small => underruns?
638 const int chunks
= 1;
639 int size
, nr_read
, i
;
643 if (!producer_running
)
646 if (producer_status
== PS_UNLOADED
||
647 producer_status
== PS_PAUSED
||
648 producer_status
== PS_STOPPED
|| ip_eof(ip
)) {
654 size
= buffer_get_wpos(&wpos
);
661 nr_read
= ip_read(ip
, wpos
, size
);
663 if (nr_read
!= -1 || errno
!= EAGAIN
) {
664 player_ip_error(nr_read
, "reading file %s",
665 ip_get_filename(ip
));
667 producer_status
= PS_UNLOADED
;
673 if (ip_metadata_changed(ip
))
676 /* buffer_fill with 0 count marks current chunk filled */
677 buffer_fill(nr_read
);
679 /* consumer handles EOF */
690 __producer_buffer_fill_update();
697 void player_load_plugins(void)
703 void player_init(const struct player_callbacks
*callbacks
)
706 #if defined(__linux__) || defined(__FreeBSD__)
709 pthread_attr_t
*attrp
= NULL
;
711 /* 1 s is 176400 B (0.168 MB)
714 buffer_nr_chunks
= 10 * 44100 * 16 / 8 * 2 / CHUNK_SIZE
;
717 player_cbs
= callbacks
;
719 #if defined(__linux__) || defined(__FreeBSD__)
720 rc
= pthread_attr_init(&attr
);
722 rc
= pthread_attr_setschedpolicy(&attr
, SCHED_RR
);
724 d_print("could not set real-time scheduling priority: %s\n", strerror(rc
));
726 struct sched_param param
;
728 d_print("using real-time scheduling\n");
729 param
.sched_priority
= sched_get_priority_max(SCHED_RR
);
730 d_print("setting priority to %d\n", param
.sched_priority
);
731 rc
= pthread_attr_setschedparam(&attr
, ¶m
);
737 rc
= pthread_create(&producer_thread
, NULL
, producer_loop
, NULL
);
740 rc
= pthread_create(&consumer_thread
, attrp
, consumer_loop
, NULL
);
742 d_print("could not create thread using real-time scheduling: %s\n", strerror(rc
));
743 rc
= pthread_create(&consumer_thread
, NULL
, consumer_loop
, NULL
);
747 /* update player_info.cont etc. */
749 __player_status_changed();
753 void player_exit(void)
758 consumer_running
= 0;
759 producer_running
= 0;
762 rc
= pthread_join(consumer_thread
, NULL
);
764 rc
= pthread_join(producer_thread
, NULL
);
770 void player_stop(void)
775 __player_status_changed();
779 void player_play(void)
784 if (producer_status
== PS_PLAYING
&& ip_is_remote(ip
)) {
785 /* seeking not allowed */
789 prebuffer
= consumer_status
== CS_STOPPED
;
791 if (producer_status
== PS_PLAYING
) {
793 if (consumer_status
!= CS_PLAYING
)
798 __player_status_changed();
799 if (consumer_status
== CS_PLAYING
&& prebuffer
)
804 void player_pause(void)
807 if (ip
&& ip_is_remote(ip
)) {
808 /* pausing not allowed */
814 __player_status_changed();
818 void player_set_file(const char *filename
)
823 __producer_set_file(filename
);
824 if (producer_status
== PS_UNLOADED
) {
830 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
832 if (producer_status
== PS_UNLOADED
) {
837 /* must do op_drop() here because op_set_sf()
838 * might call op_close() which calls drain()
842 set_buffer_sf(ip_get_sf(ip
));
843 rc
= op_set_sf(buffer_sf
);
845 /* device wasn't reopened */
846 if (consumer_status
== CS_PAUSED
) {
847 /* status was paused => need to unpause */
849 d_print("op_unpause: %d\n", rc
);
851 consumer_status
= CS_PLAYING
;
852 } else if (rc
== 1) {
853 /* device was reopened */
854 consumer_status
= CS_PLAYING
;
857 player_op_error(rc
, "setting sample format");
858 consumer_status
= CS_STOPPED
;
862 __player_status_changed();
863 if (producer_status
== PS_PLAYING
)
868 void player_play_file(const char *filename
)
873 __producer_set_file(filename
);
874 if (producer_status
== PS_UNLOADED
) {
882 /* PS_UNLOADED,PS_PLAYING */
883 if (producer_status
== PS_UNLOADED
) {
889 if (consumer_status
== CS_STOPPED
) {
891 if (consumer_status
== CS_STOPPED
)
894 /* PS_PLAYING, CS_PLAYING,CS_PAUSED */
895 /* must do op_drop() here because op_set_sf()
896 * might call op_close() which calls drain()
900 set_buffer_sf(ip_get_sf(ip
));
901 rc
= op_set_sf(buffer_sf
);
903 /* device wasn't reopened */
904 if (consumer_status
== CS_PAUSED
) {
905 /* status was paused => need to unpause */
907 d_print("op_unpause: %d\n", rc
);
909 consumer_status
= CS_PLAYING
;
910 } else if (rc
== 1) {
911 /* device was reopened */
912 consumer_status
= CS_PLAYING
;
915 player_op_error(rc
, "setting sample format");
916 consumer_status
= CS_STOPPED
;
920 __player_status_changed();
921 if (producer_status
== PS_PLAYING
)
926 void player_seek(double offset
, int whence
)
929 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
930 double pos
, duration
, new_pos
;
933 pos
= (double)consumer_pos
/ (double)buffer_second_size();
934 duration
= ip_duration(ip
);
937 d_print("can't seek\n");
941 if (whence
== SEEK_CUR
) {
942 /* relative to current position */
943 new_pos
= pos
+ offset
;
947 /* seeking forward */
948 if (new_pos
> duration
- 5.0)
949 new_pos
= duration
- 5.0;
952 if (new_pos
< pos
- 0.5) {
953 /* must seek at least 0.5s */
954 d_print("must seek at least 0.5s\n");
960 /* absolute position */
963 d_print("seek offset negative\n");
967 if (new_pos
> duration
) {
968 d_print("seek offset too large\n");
973 /* d_print("seeking %g/%g (%g from eof)\n", new_pos, duration, duration - new_pos); */
974 rc
= ip_seek(ip
, new_pos
);
976 /* d_print("doing op_drop after seek\n"); */
979 consumer_pos
= new_pos
* buffer_second_size();
980 __consumer_position_update();
982 d_print("error: ip_seek returned %d\n", rc
);
989 * change output plugin without stopping playback
991 int player_set_op(const char *name
)
997 /* drop needed because close drains the buffer */
998 if (consumer_status
== CS_PAUSED
)
1001 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
1005 d_print("setting op to '%s'\n", name
);
1006 rc
= op_select(name
);
1008 /* first initialized plugin */
1009 d_print("selecting first initialized op\n");
1010 rc
= op_select_any();
1013 consumer_status
= CS_STOPPED
;
1016 player_op_error(rc
, "selecting output plugin '%s'", name
);
1021 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1022 set_buffer_sf(ip_get_sf(ip
));
1023 rc
= op_open(buffer_sf
);
1025 consumer_status
= CS_STOPPED
;
1027 player_op_error(rc
, "opening audio device");
1031 if (consumer_status
== CS_PAUSED
)
1035 if (!op_get_volume(&l
, &r
))
1036 volume_update(l
, r
);
1039 d_print("rc = %d\n", rc
);
1043 char *player_get_op(void)
1045 return op_get_current();
1048 void player_set_buffer_chunks(unsigned int nr_chunks
)
1059 buffer_nr_chunks
= nr_chunks
;
1062 __player_status_changed();
1066 int player_get_buffer_chunks(void)
1068 return buffer_nr_chunks
;
1071 int player_get_fileinfo(const char *filename
, int *duration
,
1072 struct keyval
**comments
)
1074 struct input_plugin
*plug
;
1079 plug
= ip_new(filename
);
1080 if (ip_is_remote(plug
)) {
1081 *comments
= xnew0(struct keyval
, 1);
1092 rc
= -PLAYER_ERROR_NOT_SUPPORTED
;
1095 *duration
= ip_duration(plug
);
1096 rc
= ip_read_comments(plug
, comments
);
1101 int player_get_volume(int *left
, int *right
)
1106 rc
= op_get_volume(left
, right
);
1111 int player_set_volume(int left
, int right
)
1116 rc
= op_set_volume(left
, right
);
1119 volume_update(left
, right
);
1123 int player_set_op_option(unsigned int id
, const char *val
)
1130 rc
= op_set_option(id
, val
);
1131 __player_status_changed();
1136 int player_get_op_option(unsigned int id
, char **val
)
1141 rc
= op_get_option(id
, val
);
1146 int player_for_each_op_option(void (*callback
)(unsigned int id
, const char *key
))
1151 op_for_each_option(callback
);
1152 __player_status_changed();
1157 void player_dump_plugins(void)