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
38 enum producer_status
{
45 enum consumer_status
{
51 struct player_info player_info
= {
52 .mutex
= CMUS_MUTEX_INITIALIZER
,
55 .status
= PLAYER_STATUS_STOPPED
,
61 .metadata_changed
= 0,
63 .position_changed
= 0,
64 .buffer_fill_changed
= 0,
67 /* continue playing after track is finished? */
70 enum replaygain replaygain
;
71 int replaygain_limit
= 1;
72 double replaygain_preamp
= 6.0;
78 static const struct player_callbacks
*player_cbs
= NULL
;
80 static sample_format_t buffer_sf
;
82 static pthread_t producer_thread
;
83 static pthread_mutex_t producer_mutex
= CMUS_MUTEX_INITIALIZER
;
84 static int producer_running
= 1;
85 static enum producer_status producer_status
= PS_UNLOADED
;
86 static struct input_plugin
*ip
= NULL
;
88 static pthread_t consumer_thread
;
89 static pthread_mutex_t consumer_mutex
= CMUS_MUTEX_INITIALIZER
;
90 static int consumer_running
= 1;
91 static enum consumer_status consumer_status
= CS_STOPPED
;
92 static unsigned int consumer_pos
= 0;
94 /* for replay gain and soft vol
95 * usually same as consumer_pos, sometimes less than consumer_pos
97 static unsigned int scale_pos
;
98 static double replaygain_scale
= 1.0;
102 #define producer_lock() cmus_mutex_lock(&producer_mutex)
103 #define producer_unlock() cmus_mutex_unlock(&producer_mutex)
105 #define consumer_lock() cmus_mutex_lock(&consumer_mutex)
106 #define consumer_unlock() cmus_mutex_unlock(&consumer_mutex)
108 #define player_lock() \
114 #define player_unlock() \
122 static void reset_buffer(void)
129 static void set_buffer_sf(sample_format_t sf
)
133 /* ip_read converts samples to this format */
134 if (sf_get_channels(buffer_sf
) <= 2 && sf_get_bits(buffer_sf
) <= 16) {
135 buffer_sf
&= SF_RATE_MASK
;
136 buffer_sf
|= sf_channels(2) | sf_bits(16) | sf_signed(1);
140 #define SOFT_VOL_SCALE 65536
142 /* coefficients for volumes 0..99, for 100 65536 is used
143 * data copied from alsa-lib src/pcm/pcm_softvol.c
145 static const unsigned short soft_vol_db
[100] = {
146 0x0000, 0x0110, 0x011c, 0x012f, 0x013d, 0x0152, 0x0161, 0x0179,
147 0x018a, 0x01a5, 0x01c1, 0x01d5, 0x01f5, 0x020b, 0x022e, 0x0247,
148 0x026e, 0x028a, 0x02b6, 0x02d5, 0x0306, 0x033a, 0x035f, 0x0399,
149 0x03c2, 0x0403, 0x0431, 0x0479, 0x04ac, 0x04fd, 0x0553, 0x058f,
150 0x05ef, 0x0633, 0x069e, 0x06ea, 0x0761, 0x07b5, 0x083a, 0x0898,
151 0x092c, 0x09cb, 0x0a3a, 0x0aeb, 0x0b67, 0x0c2c, 0x0cb6, 0x0d92,
152 0x0e2d, 0x0f21, 0x1027, 0x10de, 0x1202, 0x12cf, 0x1414, 0x14f8,
153 0x1662, 0x1761, 0x18f5, 0x1a11, 0x1bd3, 0x1db4, 0x1f06, 0x211d,
154 0x2297, 0x24ec, 0x2690, 0x292a, 0x2aff, 0x2de5, 0x30fe, 0x332b,
155 0x369f, 0x390d, 0x3ce6, 0x3f9b, 0x43e6, 0x46eb, 0x4bb3, 0x4f11,
156 0x5466, 0x5a18, 0x5e19, 0x6472, 0x68ea, 0x6ffd, 0x74f8, 0x7cdc,
157 0x826a, 0x8b35, 0x9499, 0x9b35, 0xa5ad, 0xad0b, 0xb8b7, 0xc0ee,
158 0xcdf1, 0xd71a, 0xe59c, 0xefd3
161 static inline unsigned short swap16(unsigned short u
)
163 return (u
<< 8) | (u
>> 8);
166 static inline void scale_sample(signed short *buf
, int i
, int vol
)
168 #ifdef WORDS_BIGENDIAN
169 int sample
= (short)swap16(buf
[i
]);
175 sample
= (sample
* vol
- SOFT_VOL_SCALE
/ 2) / SOFT_VOL_SCALE
;
179 sample
= (sample
* vol
+ SOFT_VOL_SCALE
/ 2) / SOFT_VOL_SCALE
;
183 #ifdef WORDS_BIGENDIAN
184 buf
[i
] = swap16(sample
);
190 static void scale_samples(char *buffer
, unsigned int *countp
)
193 unsigned int count
= *countp
;
194 int ch
, bits
, l
, r
, i
;
196 BUG_ON(scale_pos
< consumer_pos
);
198 if (consumer_pos
!= scale_pos
) {
199 unsigned int offs
= scale_pos
- consumer_pos
;
207 buf
= (signed short *)buffer
;
209 if (replaygain_scale
== 1.0 && soft_vol_l
== 100 && soft_vol_r
== 100)
212 ch
= sf_get_channels(buffer_sf
);
213 bits
= sf_get_bits(buffer_sf
);
214 if (ch
!= 2 || bits
!= 16)
219 if (soft_vol_l
!= 100)
220 l
= soft_vol_db
[soft_vol_l
];
221 if (soft_vol_r
!= 100)
222 r
= soft_vol_db
[soft_vol_r
];
224 l
*= replaygain_scale
;
225 r
*= replaygain_scale
;
227 /* avoid underflowing -32768 to 32767 when scale is 65536 */
228 if (l
!= SOFT_VOL_SCALE
&& r
!= SOFT_VOL_SCALE
) {
229 for (i
= 0; i
< count
/ 4; i
++) {
230 scale_sample(buf
, i
* 2, l
);
231 scale_sample(buf
, i
* 2 + 1, r
);
233 } else if (l
!= SOFT_VOL_SCALE
) {
234 for (i
= 0; i
< count
/ 4; i
++)
235 scale_sample(buf
, i
* 2, l
);
236 } else if (r
!= SOFT_VOL_SCALE
) {
237 for (i
= 0; i
< count
/ 4; i
++)
238 scale_sample(buf
, i
* 2 + 1, r
);
242 static int parse_double(const char *str
, double *val
)
246 *val
= strtod(str
, &end
);
250 static void update_rg_scale(void)
253 double gain
, peak
, db
, scale
, limit
;
255 replaygain_scale
= 1.0;
256 if (!player_info
.ti
|| !replaygain
)
259 if (replaygain
== RG_TRACK
) {
260 g
= comments_get_val(player_info
.ti
->comments
, "replaygain_track_gain");
261 p
= comments_get_val(player_info
.ti
->comments
, "replaygain_track_peak");
263 g
= comments_get_val(player_info
.ti
->comments
, "replaygain_album_gain");
264 p
= comments_get_val(player_info
.ti
->comments
, "replaygain_album_peak");
268 d_print("gain or peak not available\n");
271 if (parse_double(g
, &gain
) || parse_double(p
, &peak
)) {
272 d_print("could not parse gain (%s) or peak (%s)\n", g
, p
);
276 d_print("peak (%g) is too small\n", peak
);
280 db
= replaygain_preamp
+ gain
;
282 scale
= pow(10.0, db
/ 20.0);
283 replaygain_scale
= scale
;
285 if (replaygain_limit
&& replaygain_scale
> limit
)
286 replaygain_scale
= limit
;
288 d_print("gain = %f, peak = %f, db = %f, scale = %f, limit = %f, replaygain_scale = %f\n",
289 gain
, peak
, db
, scale
, limit
, replaygain_scale
);
292 static inline unsigned int buffer_second_size(void)
294 return sf_get_second_size(buffer_sf
);
297 static inline int get_next(struct track_info
**ti
)
299 return player_cbs
->get_next(ti
);
302 /* updating player status {{{ */
304 static inline void file_changed(struct track_info
*ti
)
308 track_info_unref(player_info
.ti
);
312 d_print("file: %s\n", ti
->filename
);
314 d_print("unloaded\n");
317 player_info
.metadata
[0] = 0;
318 player_info
.file_changed
= 1;
319 player_info_unlock();
322 static inline void metadata_changed(void)
325 d_print("metadata changed: %s\n", ip_get_metadata(ip
));
326 memcpy(player_info
.metadata
, ip_get_metadata(ip
), 255 * 16 + 1);
327 player_info
.metadata_changed
= 1;
328 player_info_unlock();
331 static void player_error(const char *msg
)
334 player_info
.status
= consumer_status
;
336 player_info
.buffer_fill
= buffer_get_filled_chunks();
337 player_info
.buffer_size
= buffer_nr_chunks
;
338 player_info
.status_changed
= 1;
340 free(player_info
.error_msg
);
341 player_info
.error_msg
= xstrdup(msg
);
342 player_info_unlock();
344 d_print("ERROR: '%s'\n", msg
);
347 static void __FORMAT(2, 3) player_ip_error(int rc
, const char *format
, ...)
354 va_start(ap
, format
);
355 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
359 msg
= ip_get_error_msg(ip
, rc
, buffer
);
364 static void __FORMAT(2, 3) player_op_error(int rc
, const char *format
, ...)
371 va_start(ap
, format
);
372 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
376 msg
= op_get_error_msg(rc
, buffer
);
382 * buffer-fill changed
384 static void __producer_buffer_fill_update(void)
389 fill
= buffer_get_filled_chunks();
390 if (fill
!= player_info
.buffer_fill
) {
392 player_info
.buffer_fill
= fill
;
393 player_info
.buffer_fill_changed
= 1;
395 player_info_unlock();
399 * playing position changed
401 static void __consumer_position_update(void)
403 static unsigned int old_pos
= -1;
404 unsigned int pos
= 0;
406 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
407 pos
= consumer_pos
/ buffer_second_size();
408 if (pos
!= old_pos
) {
413 player_info
.pos
= pos
;
414 player_info
.position_changed
= 1;
415 player_info_unlock();
420 * something big happened (stopped/paused/unpaused...)
422 static void __player_status_changed(void)
424 unsigned int pos
= 0;
427 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
428 pos
= consumer_pos
/ buffer_second_size();
431 player_info
.status
= consumer_status
;
432 player_info
.pos
= pos
;
433 player_info
.buffer_fill
= buffer_get_filled_chunks();
434 player_info
.buffer_size
= buffer_nr_chunks
;
435 player_info
.status_changed
= 1;
436 player_info_unlock();
439 /* updating player status }}} */
441 static void __prebuffer(void)
445 BUG_ON(producer_status
!= PS_PLAYING
);
446 if (ip_is_remote(ip
)) {
447 limit_chunks
= buffer_nr_chunks
;
449 int limit_ms
, limit_size
;
452 limit_size
= limit_ms
* buffer_second_size() / 1000;
453 limit_chunks
= limit_size
/ CHUNK_SIZE
;
454 if (limit_chunks
< 1)
458 int nr_read
, size
, filled
;
461 filled
= buffer_get_filled_chunks();
462 /* d_print("PREBUF: %2d / %2d\n", filled, limit_chunks); */
465 //BUG_ON(filled > limit_chunks);
467 if (filled
>= limit_chunks
)
470 size
= buffer_get_wpos(&wpos
);
471 nr_read
= ip_read(ip
, wpos
, size
);
473 if (nr_read
== -1 && errno
== EAGAIN
)
475 player_ip_error(nr_read
, "reading file %s", ip_get_filename(ip
));
476 /* ip_read sets eof */
479 if (ip_metadata_changed(ip
))
482 /* buffer_fill with 0 count marks current chunk filled */
483 buffer_fill(nr_read
);
485 __producer_buffer_fill_update();
493 /* setting producer status {{{ */
495 static void __producer_play(void)
497 if (producer_status
== PS_UNLOADED
) {
498 struct track_info
*ti
;
500 if (get_next(&ti
) == 0) {
503 ip
= ip_new(ti
->filename
);
506 player_ip_error(rc
, "opening file `%s'", ti
->filename
);
508 track_info_unref(ti
);
512 producer_status
= PS_PLAYING
;
516 } else if (producer_status
== PS_PLAYING
) {
517 if (ip_seek(ip
, 0.0) == 0) {
520 } else if (producer_status
== PS_STOPPED
) {
525 player_ip_error(rc
, "opening file `%s'", ip_get_filename(ip
));
527 producer_status
= PS_UNLOADED
;
530 producer_status
= PS_PLAYING
;
532 } else if (producer_status
== PS_PAUSED
) {
533 producer_status
= PS_PLAYING
;
537 static void __producer_stop(void)
539 if (producer_status
== PS_PLAYING
|| producer_status
== PS_PAUSED
) {
541 producer_status
= PS_STOPPED
;
546 static void __producer_unload(void)
549 if (producer_status
== PS_STOPPED
) {
551 producer_status
= PS_UNLOADED
;
555 static void __producer_pause(void)
557 if (producer_status
== PS_PLAYING
) {
558 producer_status
= PS_PAUSED
;
559 } else if (producer_status
== PS_PAUSED
) {
560 producer_status
= PS_PLAYING
;
564 static void __producer_set_file(struct track_info
*ti
)
567 ip
= ip_new(ti
->filename
);
568 producer_status
= PS_STOPPED
;
572 /* setting producer status }}} */
574 /* setting consumer status {{{ */
576 static void __consumer_play(void)
578 if (consumer_status
== CS_PLAYING
) {
580 } else if (consumer_status
== CS_STOPPED
) {
583 set_buffer_sf(ip_get_sf(ip
));
584 rc
= op_open(buffer_sf
);
586 player_op_error(rc
, "opening audio device");
588 consumer_status
= CS_PLAYING
;
590 } else if (consumer_status
== CS_PAUSED
) {
592 consumer_status
= CS_PLAYING
;
596 static void __consumer_drain_and_stop(void)
598 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
600 consumer_status
= CS_STOPPED
;
604 static void __consumer_stop(void)
606 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
609 consumer_status
= CS_STOPPED
;
613 static void __consumer_pause(void)
615 if (consumer_status
== CS_PLAYING
) {
617 consumer_status
= CS_PAUSED
;
618 } else if (consumer_status
== CS_PAUSED
) {
620 consumer_status
= CS_PLAYING
;
624 /* setting consumer status }}} */
626 static int change_sf(sample_format_t sf
, int drop
)
628 int old_sf
= buffer_sf
;
631 if (buffer_sf
!= old_sf
) {
638 rc
= op_open(buffer_sf
);
640 player_op_error(rc
, "opening audio device");
641 consumer_status
= CS_STOPPED
;
645 } else if (consumer_status
== CS_PAUSED
) {
649 consumer_status
= CS_PLAYING
;
653 static void __consumer_handle_eof(void)
655 struct track_info
*ti
;
657 if (ip_is_remote(ip
)) {
659 __consumer_drain_and_stop();
660 player_error("lost connection");
664 if (get_next(&ti
) == 0) {
666 ip
= ip_new(ti
->filename
);
667 producer_status
= PS_STOPPED
;
668 /* PS_STOPPED, CS_PLAYING */
671 if (producer_status
== PS_UNLOADED
) {
673 track_info_unref(ti
);
678 if (!change_sf(ip_get_sf(ip
), 0))
682 __consumer_drain_and_stop();
687 __consumer_drain_and_stop();
690 __player_status_changed();
693 static void *consumer_loop(void *arg
)
701 if (!consumer_running
)
704 if (consumer_status
== CS_PAUSED
|| consumer_status
== CS_STOPPED
) {
709 space
= op_buffer_space();
712 __consumer_position_update();
717 /* d_print("BS: %6d %3d\n", space, space * 1000 / (44100 * 2 * 2)); */
720 /* 25 ms is 4410 B */
722 __consumer_position_update();
727 size
= buffer_get_rpos(&rpos
);
730 if (producer_status
!= PS_PLAYING
) {
735 /* must recheck rpos */
736 size
= buffer_get_rpos(&rpos
);
738 /* OK. now it's safe to check if we are at EOF */
741 __consumer_handle_eof();
746 /* possible underrun */
748 __consumer_position_update();
750 /* d_print("possible underrun\n"); */
756 /* player_buffer and ip.eof were inconsistent */
761 if (soft_vol
|| replaygain
)
762 scale_samples(rpos
, &size
);
763 rc
= op_write(rpos
, size
);
765 d_print("op_write returned %d %s\n", rc
,
766 rc
== -1 ? strerror(errno
) : "");
770 consumer_status
= CS_STOPPED
;
786 static void *producer_loop(void *arg
)
789 /* number of chunks to fill
790 * too big => seeking is slow
791 * too small => underruns?
793 const int chunks
= 1;
794 int size
, nr_read
, i
;
798 if (!producer_running
)
801 if (producer_status
== PS_UNLOADED
||
802 producer_status
== PS_PAUSED
||
803 producer_status
== PS_STOPPED
|| ip_eof(ip
)) {
809 size
= buffer_get_wpos(&wpos
);
816 nr_read
= ip_read(ip
, wpos
, size
);
818 if (nr_read
!= -1 || errno
!= EAGAIN
) {
819 player_ip_error(nr_read
, "reading file %s",
820 ip_get_filename(ip
));
821 /* ip_read sets eof */
829 if (ip_metadata_changed(ip
))
832 /* buffer_fill with 0 count marks current chunk filled */
833 buffer_fill(nr_read
);
835 /* consumer handles EOF */
846 __producer_buffer_fill_update();
853 void player_load_plugins(void)
859 void player_init(const struct player_callbacks
*callbacks
)
862 #if defined(__linux__) || defined(__FreeBSD__)
865 pthread_attr_t
*attrp
= NULL
;
867 /* 1 s is 176400 B (0.168 MB)
870 buffer_nr_chunks
= 10 * 44100 * 16 / 8 * 2 / CHUNK_SIZE
;
873 player_cbs
= callbacks
;
875 #if defined(__linux__) || defined(__FreeBSD__)
876 rc
= pthread_attr_init(&attr
);
878 rc
= pthread_attr_setschedpolicy(&attr
, SCHED_RR
);
880 d_print("could not set real-time scheduling priority: %s\n", strerror(rc
));
882 struct sched_param param
;
884 d_print("using real-time scheduling\n");
885 param
.sched_priority
= sched_get_priority_max(SCHED_RR
);
886 d_print("setting priority to %d\n", param
.sched_priority
);
887 rc
= pthread_attr_setschedparam(&attr
, ¶m
);
893 rc
= pthread_create(&producer_thread
, NULL
, producer_loop
, NULL
);
896 rc
= pthread_create(&consumer_thread
, attrp
, consumer_loop
, NULL
);
898 d_print("could not create thread using real-time scheduling: %s\n", strerror(rc
));
899 rc
= pthread_create(&consumer_thread
, NULL
, consumer_loop
, NULL
);
903 /* update player_info.cont etc. */
905 __player_status_changed();
909 void player_exit(void)
914 consumer_running
= 0;
915 producer_running
= 0;
918 rc
= pthread_join(consumer_thread
, NULL
);
920 rc
= pthread_join(producer_thread
, NULL
);
926 void player_stop(void)
931 __player_status_changed();
935 void player_play(void)
940 if (producer_status
== PS_PLAYING
&& ip_is_remote(ip
)) {
941 /* seeking not allowed */
945 prebuffer
= consumer_status
== CS_STOPPED
;
947 if (producer_status
== PS_PLAYING
) {
949 if (consumer_status
!= CS_PLAYING
)
954 __player_status_changed();
955 if (consumer_status
== CS_PLAYING
&& prebuffer
)
960 void player_pause(void)
964 if (consumer_status
== CS_STOPPED
) {
966 if (producer_status
== PS_PLAYING
) {
968 if (consumer_status
!= CS_PLAYING
)
971 __player_status_changed();
972 if (consumer_status
== CS_PLAYING
)
978 if (ip
&& ip_is_remote(ip
)) {
979 /* pausing not allowed */
985 __player_status_changed();
989 void player_set_file(struct track_info
*ti
)
992 __producer_set_file(ti
);
993 if (producer_status
== PS_UNLOADED
) {
999 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1001 if (producer_status
== PS_UNLOADED
) {
1005 change_sf(ip_get_sf(ip
), 1);
1008 __player_status_changed();
1009 if (producer_status
== PS_PLAYING
)
1014 void player_play_file(struct track_info
*ti
)
1017 __producer_set_file(ti
);
1018 if (producer_status
== PS_UNLOADED
) {
1026 /* PS_UNLOADED,PS_PLAYING */
1027 if (producer_status
== PS_UNLOADED
) {
1033 if (consumer_status
== CS_STOPPED
) {
1035 if (consumer_status
== CS_STOPPED
)
1038 change_sf(ip_get_sf(ip
), 1);
1041 __player_status_changed();
1042 if (producer_status
== PS_PLAYING
)
1047 void player_seek(double offset
, int relative
)
1050 if (consumer_status
== CS_STOPPED
) {
1052 if (producer_status
== PS_PLAYING
) {
1054 if (consumer_status
!= CS_PLAYING
) {
1059 __player_status_changed();
1062 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1063 double pos
, duration
, new_pos
;
1066 pos
= (double)consumer_pos
/ (double)buffer_second_size();
1067 duration
= ip_duration(ip
);
1070 d_print("can't seek\n");
1075 new_pos
= pos
+ offset
;
1079 /* seeking forward */
1080 if (new_pos
> duration
- 5.0)
1081 new_pos
= duration
- 5.0;
1084 if (new_pos
< pos
- 0.5) {
1085 /* must seek at least 0.5s */
1086 d_print("must seek at least 0.5s\n");
1093 if (new_pos
< 0.0) {
1094 d_print("seek offset negative\n");
1098 if (new_pos
> duration
- 5.0) {
1099 new_pos
= duration
- 5.0;
1104 /* d_print("seeking %g/%g (%g from eof)\n", new_pos, duration, duration - new_pos); */
1105 rc
= ip_seek(ip
, new_pos
);
1107 /* d_print("doing op_drop after seek\n"); */
1110 consumer_pos
= new_pos
* buffer_second_size();
1111 scale_pos
= consumer_pos
;
1112 __consumer_position_update();
1114 d_print("error: ip_seek returned %d\n", rc
);
1121 * change output plugin without stopping playback
1123 void player_set_op(const char *name
)
1129 /* drop needed because close drains the buffer */
1130 if (consumer_status
== CS_PAUSED
)
1133 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
1137 d_print("setting op to '%s'\n", name
);
1138 rc
= op_select(name
);
1140 /* first initialized plugin */
1141 d_print("selecting first initialized op\n");
1142 rc
= op_select_any();
1145 consumer_status
= CS_STOPPED
;
1148 player_op_error(rc
, "selecting output plugin '%s'", name
);
1153 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1154 set_buffer_sf(ip_get_sf(ip
));
1155 rc
= op_open(buffer_sf
);
1157 consumer_status
= CS_STOPPED
;
1159 player_op_error(rc
, "opening audio device");
1163 if (consumer_status
== CS_PAUSED
)
1170 char *player_get_op(void)
1172 return op_get_current();
1175 void player_set_buffer_chunks(unsigned int nr_chunks
)
1186 buffer_nr_chunks
= nr_chunks
;
1189 __player_status_changed();
1193 int player_get_buffer_chunks(void)
1195 return buffer_nr_chunks
;
1198 int player_get_fileinfo(const char *filename
, int *duration
,
1199 struct keyval
**comments
)
1201 struct input_plugin
*plug
;
1206 plug
= ip_new(filename
);
1207 if (ip_is_remote(plug
)) {
1208 *comments
= xnew0(struct keyval
, 1);
1219 rc
= -PLAYER_ERROR_NOT_SUPPORTED
;
1222 *duration
= ip_duration(plug
);
1223 rc
= ip_read_comments(plug
, comments
);
1228 void player_set_soft_volume(int l
, int r
)
1236 void player_set_soft_vol(int soft
)
1239 /* don't mess with scale_pos if soft_vol or replaygain is already enabled */
1240 if (!soft_vol
&& !replaygain
)
1241 scale_pos
= consumer_pos
;
1246 void player_set_rg(enum replaygain rg
)
1249 /* don't mess with scale_pos if soft_vol or replaygain is already enabled */
1250 if (!soft_vol
&& !replaygain
)
1251 scale_pos
= consumer_pos
;
1256 player_info_unlock();
1261 void player_set_rg_limit(int limit
)
1264 replaygain_limit
= limit
;
1268 player_info_unlock();
1273 void player_set_rg_preamp(double db
)
1276 replaygain_preamp
= db
;
1280 player_info_unlock();
1285 int player_set_op_option(unsigned int id
, const char *val
)
1292 rc
= op_set_option(id
, val
);
1293 __player_status_changed();
1298 int player_get_op_option(unsigned int id
, char **val
)
1303 rc
= op_get_option(id
, val
);
1308 int player_for_each_op_option(void (*callback
)(unsigned int id
, const char *key
))
1313 op_for_each_option(callback
);
1314 __player_status_changed();
1319 void player_dump_plugins(void)