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
,
63 .metadata_changed
= 0,
65 .position_changed
= 0,
66 .buffer_fill_changed
= 0,
70 /* continue playing after track is finished? */
73 enum replaygain replaygain
;
74 int replaygain_limit
= 1;
75 double replaygain_preamp
= 6.0;
77 static const struct player_callbacks
*player_cbs
= NULL
;
79 static sample_format_t buffer_sf
;
81 static pthread_t producer_thread
;
82 static pthread_mutex_t producer_mutex
= CMUS_MUTEX_INITIALIZER
;
83 static int producer_running
= 1;
84 static enum producer_status producer_status
= PS_UNLOADED
;
85 static struct input_plugin
*ip
= NULL
;
87 static pthread_t consumer_thread
;
88 static pthread_mutex_t consumer_mutex
= CMUS_MUTEX_INITIALIZER
;
89 static int consumer_running
= 1;
90 static enum consumer_status consumer_status
= CS_STOPPED
;
91 static unsigned int consumer_pos
= 0;
93 /* for replay gain and soft vol
94 * usually same as consumer_pos, sometimes less than consumer_pos
96 static unsigned int scale_pos
;
97 static double replaygain_scale
= 1.0;
101 #define producer_lock() cmus_mutex_lock(&producer_mutex)
102 #define producer_unlock() cmus_mutex_unlock(&producer_mutex)
104 #define consumer_lock() cmus_mutex_lock(&consumer_mutex)
105 #define consumer_unlock() cmus_mutex_unlock(&consumer_mutex)
107 #define player_lock() \
113 #define player_unlock() \
121 static void reset_buffer(void)
128 static void set_buffer_sf(sample_format_t sf
)
132 /* ip_read converts samples to this format */
133 if (sf_get_channels(buffer_sf
) <= 2 && sf_get_bits(buffer_sf
) <= 16) {
134 buffer_sf
&= SF_RATE_MASK
;
135 buffer_sf
|= sf_channels(2) | sf_bits(16) | sf_signed(1);
139 #define SOFT_VOL_SCALE 65536
141 /* coefficients for volumes 0..99, for 100 65536 is used
142 * data copied from alsa-lib src/pcm/pcm_softvol.c
144 static const unsigned short soft_vol_db
[100] = {
145 0x0000, 0x0110, 0x011c, 0x012f, 0x013d, 0x0152, 0x0161, 0x0179,
146 0x018a, 0x01a5, 0x01c1, 0x01d5, 0x01f5, 0x020b, 0x022e, 0x0247,
147 0x026e, 0x028a, 0x02b6, 0x02d5, 0x0306, 0x033a, 0x035f, 0x0399,
148 0x03c2, 0x0403, 0x0431, 0x0479, 0x04ac, 0x04fd, 0x0553, 0x058f,
149 0x05ef, 0x0633, 0x069e, 0x06ea, 0x0761, 0x07b5, 0x083a, 0x0898,
150 0x092c, 0x09cb, 0x0a3a, 0x0aeb, 0x0b67, 0x0c2c, 0x0cb6, 0x0d92,
151 0x0e2d, 0x0f21, 0x1027, 0x10de, 0x1202, 0x12cf, 0x1414, 0x14f8,
152 0x1662, 0x1761, 0x18f5, 0x1a11, 0x1bd3, 0x1db4, 0x1f06, 0x211d,
153 0x2297, 0x24ec, 0x2690, 0x292a, 0x2aff, 0x2de5, 0x30fe, 0x332b,
154 0x369f, 0x390d, 0x3ce6, 0x3f9b, 0x43e6, 0x46eb, 0x4bb3, 0x4f11,
155 0x5466, 0x5a18, 0x5e19, 0x6472, 0x68ea, 0x6ffd, 0x74f8, 0x7cdc,
156 0x826a, 0x8b35, 0x9499, 0x9b35, 0xa5ad, 0xad0b, 0xb8b7, 0xc0ee,
157 0xcdf1, 0xd71a, 0xe59c, 0xefd3
160 static inline void scale_sample(signed short *buf
, int i
, int vol
)
165 sample
= (sample
* vol
- SOFT_VOL_SCALE
/ 2) / SOFT_VOL_SCALE
;
169 sample
= (sample
* vol
+ SOFT_VOL_SCALE
/ 2) / SOFT_VOL_SCALE
;
176 static void scale_samples(char *buffer
, unsigned int *countp
)
179 unsigned int count
= *countp
;
180 int ch
, bits
, l
, r
, i
;
182 BUG_ON(scale_pos
< consumer_pos
);
184 if (consumer_pos
!= scale_pos
) {
185 unsigned int offs
= scale_pos
- consumer_pos
;
193 buf
= (signed short *)buffer
;
195 if (replaygain_scale
== 1.0 && soft_vol_l
== 100 && soft_vol_r
== 100)
198 ch
= sf_get_channels(buffer_sf
);
199 bits
= sf_get_bits(buffer_sf
);
200 if (ch
!= 2 || bits
!= 16)
205 if (soft_vol_l
!= 100)
206 l
= soft_vol_db
[soft_vol_l
];
207 if (soft_vol_r
!= 100)
208 r
= soft_vol_db
[soft_vol_r
];
210 l
*= replaygain_scale
;
211 r
*= replaygain_scale
;
213 for (i
= 0; i
< count
/ 4; i
++) {
214 scale_sample(buf
, i
* 2, l
);
215 scale_sample(buf
, i
* 2 + 1, r
);
219 static int parse_double(const char *str
, double *val
)
223 *val
= strtod(str
, &end
);
227 static void update_rg_scale(void)
230 double gain
, peak
, db
, scale
, limit
;
232 replaygain_scale
= 1.0;
233 if (!player_info
.ti
|| !replaygain
)
236 if (replaygain
== RG_TRACK
) {
237 g
= comments_get_val(player_info
.ti
->comments
, "replaygain_track_gain");
238 p
= comments_get_val(player_info
.ti
->comments
, "replaygain_track_peak");
240 g
= comments_get_val(player_info
.ti
->comments
, "replaygain_album_gain");
241 p
= comments_get_val(player_info
.ti
->comments
, "replaygain_album_peak");
245 d_print("gain or peak not available\n");
248 if (parse_double(g
, &gain
) || parse_double(p
, &peak
)) {
249 d_print("could not parse gain (%s) or peak (%s)\n", g
, p
);
253 d_print("peak (%g) is too small\n", peak
);
257 db
= replaygain_preamp
+ gain
;
259 scale
= pow(10.0, db
/ 20.0);
260 replaygain_scale
= scale
;
262 if (replaygain_limit
&& replaygain_scale
> limit
)
263 replaygain_scale
= limit
;
265 d_print("gain = %f, peak = %f, db = %f, scale = %f, limit = %f, replaygain_scale = %f\n",
266 gain
, peak
, db
, scale
, limit
, replaygain_scale
);
269 static inline unsigned int buffer_second_size(void)
271 return sf_get_second_size(buffer_sf
);
274 static inline int get_next(struct track_info
**ti
)
276 return player_cbs
->get_next(ti
);
279 /* updating player status {{{ */
281 static inline void file_changed(struct track_info
*ti
)
285 track_info_unref(player_info
.ti
);
289 d_print("file: %s\n", ti
->filename
);
291 d_print("unloaded\n");
294 player_info
.metadata
[0] = 0;
295 player_info
.file_changed
= 1;
296 player_info_unlock();
299 static inline void metadata_changed(void)
302 d_print("metadata changed: %s\n", ip_get_metadata(ip
));
303 memcpy(player_info
.metadata
, ip_get_metadata(ip
), 255 * 16 + 1);
304 player_info
.metadata_changed
= 1;
305 player_info_unlock();
308 static inline void volume_update(int left
, int right
)
310 if (player_info
.vol_left
== left
&& player_info
.vol_right
== right
)
314 player_info
.vol_left
= left
;
315 player_info
.vol_right
= right
;
316 player_info
.vol_changed
= 1;
317 player_info_unlock();
320 static void player_error(const char *msg
)
323 player_info
.status
= consumer_status
;
325 player_info
.buffer_fill
= buffer_get_filled_chunks();
326 player_info
.buffer_size
= buffer_nr_chunks
;
327 player_info
.status_changed
= 1;
329 free(player_info
.error_msg
);
330 player_info
.error_msg
= xstrdup(msg
);
331 player_info_unlock();
333 d_print("ERROR: '%s'\n", msg
);
336 static void __FORMAT(2, 3) player_ip_error(int rc
, const char *format
, ...)
343 va_start(ap
, format
);
344 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
348 msg
= ip_get_error_msg(ip
, rc
, buffer
);
353 static void __FORMAT(2, 3) player_op_error(int rc
, const char *format
, ...)
360 va_start(ap
, format
);
361 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
365 msg
= op_get_error_msg(rc
, buffer
);
370 /* FIXME: don't poll */
371 static void mixer_check(void)
373 static struct timeval old_t
= { 0L, 0L };
378 gettimeofday(&t
, NULL
);
379 usec
= t
.tv_usec
- old_t
.tv_usec
;
380 sec
= t
.tv_sec
- old_t
.tv_sec
;
382 /* multiplying sec with 1e6 can overflow */
389 if (!op_get_volume(&l
, &r
))
394 * buffer-fill changed
396 static void __producer_buffer_fill_update(void)
401 fill
= buffer_get_filled_chunks();
402 if (fill
!= player_info
.buffer_fill
) {
404 player_info
.buffer_fill
= fill
;
405 player_info
.buffer_fill_changed
= 1;
407 player_info_unlock();
411 * playing position changed
413 static void __consumer_position_update(void)
415 static unsigned int old_pos
= -1;
416 unsigned int pos
= 0;
418 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
419 pos
= consumer_pos
/ buffer_second_size();
420 if (pos
!= old_pos
) {
425 player_info
.pos
= pos
;
426 player_info
.position_changed
= 1;
427 player_info_unlock();
432 * something big happened (stopped/paused/unpaused...)
434 static void __player_status_changed(void)
436 unsigned int pos
= 0;
439 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
440 pos
= consumer_pos
/ buffer_second_size();
443 player_info
.status
= consumer_status
;
444 player_info
.pos
= pos
;
445 player_info
.buffer_fill
= buffer_get_filled_chunks();
446 player_info
.buffer_size
= buffer_nr_chunks
;
447 player_info
.status_changed
= 1;
448 player_info_unlock();
451 /* updating player status }}} */
453 static void __prebuffer(void)
457 BUG_ON(producer_status
!= PS_PLAYING
);
458 if (ip_is_remote(ip
)) {
459 limit_chunks
= buffer_nr_chunks
;
461 int limit_ms
, limit_size
;
464 limit_size
= limit_ms
* buffer_second_size() / 1000;
465 limit_chunks
= limit_size
/ CHUNK_SIZE
;
466 if (limit_chunks
< 1)
470 int nr_read
, size
, filled
;
473 filled
= buffer_get_filled_chunks();
474 /* d_print("PREBUF: %2d / %2d\n", filled, limit_chunks); */
477 //BUG_ON(filled > limit_chunks);
479 if (filled
>= limit_chunks
)
482 size
= buffer_get_wpos(&wpos
);
483 nr_read
= ip_read(ip
, wpos
, size
);
485 if (nr_read
== -1 && errno
== EAGAIN
)
487 player_ip_error(nr_read
, "reading file %s", ip_get_filename(ip
));
488 /* ip_read sets eof */
491 if (ip_metadata_changed(ip
))
494 /* buffer_fill with 0 count marks current chunk filled */
495 buffer_fill(nr_read
);
497 __producer_buffer_fill_update();
505 /* setting producer status {{{ */
507 static void __producer_play(void)
509 if (producer_status
== PS_UNLOADED
) {
510 struct track_info
*ti
;
512 if (get_next(&ti
) == 0) {
515 ip
= ip_new(ti
->filename
);
518 player_ip_error(rc
, "opening file `%s'", ti
->filename
);
520 track_info_unref(ti
);
524 producer_status
= PS_PLAYING
;
528 } else if (producer_status
== PS_PLAYING
) {
529 if (ip_seek(ip
, 0.0) == 0) {
532 } else if (producer_status
== PS_STOPPED
) {
537 player_ip_error(rc
, "opening file `%s'", ip_get_filename(ip
));
539 producer_status
= PS_UNLOADED
;
542 producer_status
= PS_PLAYING
;
544 } else if (producer_status
== PS_PAUSED
) {
545 producer_status
= PS_PLAYING
;
549 static void __producer_stop(void)
551 if (producer_status
== PS_PLAYING
|| producer_status
== PS_PAUSED
) {
553 producer_status
= PS_STOPPED
;
558 static void __producer_unload(void)
561 if (producer_status
== PS_STOPPED
) {
563 producer_status
= PS_UNLOADED
;
567 static void __producer_pause(void)
569 if (producer_status
== PS_PLAYING
) {
570 producer_status
= PS_PAUSED
;
571 } else if (producer_status
== PS_PAUSED
) {
572 producer_status
= PS_PLAYING
;
576 static void __producer_set_file(struct track_info
*ti
)
579 ip
= ip_new(ti
->filename
);
580 producer_status
= PS_STOPPED
;
584 /* setting producer status }}} */
586 /* setting consumer status {{{ */
588 static void __consumer_play(void)
590 if (consumer_status
== CS_PLAYING
) {
592 } else if (consumer_status
== CS_STOPPED
) {
595 set_buffer_sf(ip_get_sf(ip
));
596 rc
= op_open(buffer_sf
);
598 player_op_error(rc
, "opening audio device");
600 consumer_status
= CS_PLAYING
;
602 } else if (consumer_status
== CS_PAUSED
) {
604 consumer_status
= CS_PLAYING
;
608 static void __consumer_drain_and_stop(void)
610 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
612 consumer_status
= CS_STOPPED
;
616 static void __consumer_stop(void)
618 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
621 consumer_status
= CS_STOPPED
;
625 static void __consumer_pause(void)
627 if (consumer_status
== CS_PLAYING
) {
629 consumer_status
= CS_PAUSED
;
630 } else if (consumer_status
== CS_PAUSED
) {
632 consumer_status
= CS_PLAYING
;
636 /* setting consumer status }}} */
638 static int change_sf(sample_format_t sf
, int drop
)
640 int old_sf
= buffer_sf
;
643 if (buffer_sf
!= old_sf
) {
650 rc
= op_open(buffer_sf
);
652 player_op_error(rc
, "opening audio device");
653 consumer_status
= CS_STOPPED
;
657 } else if (consumer_status
== CS_PAUSED
) {
661 consumer_status
= CS_PLAYING
;
665 static void __consumer_handle_eof(void)
667 struct track_info
*ti
;
669 if (ip_is_remote(ip
)) {
671 __consumer_drain_and_stop();
672 player_error("lost connection");
676 if (get_next(&ti
) == 0) {
678 ip
= ip_new(ti
->filename
);
679 producer_status
= PS_STOPPED
;
680 /* PS_STOPPED, CS_PLAYING */
683 if (producer_status
== PS_UNLOADED
) {
685 track_info_unref(ti
);
690 if (!change_sf(ip_get_sf(ip
), 0))
694 __consumer_drain_and_stop();
699 __consumer_drain_and_stop();
702 __player_status_changed();
705 static void *consumer_loop(void *arg
)
713 if (!consumer_running
)
716 if (consumer_status
== CS_PAUSED
|| consumer_status
== CS_STOPPED
) {
722 space
= op_buffer_space();
725 __consumer_position_update();
730 /* d_print("BS: %6d %3d\n", space, space * 1000 / (44100 * 2 * 2)); */
733 /* 25 ms is 4410 B */
735 __consumer_position_update();
741 size
= buffer_get_rpos(&rpos
);
744 if (producer_status
!= PS_PLAYING
) {
749 /* must recheck rpos */
750 size
= buffer_get_rpos(&rpos
);
752 /* OK. now it's safe to check if we are at EOF */
755 __consumer_handle_eof();
760 /* possible underrun */
762 __consumer_position_update();
764 /* d_print("possible underrun\n"); */
770 /* player_buffer and ip.eof were inconsistent */
775 if (soft_vol
|| replaygain
)
776 scale_samples(rpos
, &size
);
777 rc
= op_write(rpos
, size
);
779 d_print("op_write returned %d %s\n", rc
,
780 rc
== -1 ? strerror(errno
) : "");
784 consumer_status
= CS_STOPPED
;
800 static void *producer_loop(void *arg
)
803 /* number of chunks to fill
804 * too big => seeking is slow
805 * too small => underruns?
807 const int chunks
= 1;
808 int size
, nr_read
, i
;
812 if (!producer_running
)
815 if (producer_status
== PS_UNLOADED
||
816 producer_status
== PS_PAUSED
||
817 producer_status
== PS_STOPPED
|| ip_eof(ip
)) {
823 size
= buffer_get_wpos(&wpos
);
830 nr_read
= ip_read(ip
, wpos
, size
);
832 if (nr_read
!= -1 || errno
!= EAGAIN
) {
833 player_ip_error(nr_read
, "reading file %s",
834 ip_get_filename(ip
));
835 /* ip_read sets eof */
843 if (ip_metadata_changed(ip
))
846 /* buffer_fill with 0 count marks current chunk filled */
847 buffer_fill(nr_read
);
849 /* consumer handles EOF */
860 __producer_buffer_fill_update();
867 void player_load_plugins(void)
873 void player_init(const struct player_callbacks
*callbacks
)
876 #if defined(__linux__) || defined(__FreeBSD__)
879 pthread_attr_t
*attrp
= NULL
;
881 /* 1 s is 176400 B (0.168 MB)
884 buffer_nr_chunks
= 10 * 44100 * 16 / 8 * 2 / CHUNK_SIZE
;
887 player_cbs
= callbacks
;
889 #if defined(__linux__) || defined(__FreeBSD__)
890 rc
= pthread_attr_init(&attr
);
892 rc
= pthread_attr_setschedpolicy(&attr
, SCHED_RR
);
894 d_print("could not set real-time scheduling priority: %s\n", strerror(rc
));
896 struct sched_param param
;
898 d_print("using real-time scheduling\n");
899 param
.sched_priority
= sched_get_priority_max(SCHED_RR
);
900 d_print("setting priority to %d\n", param
.sched_priority
);
901 rc
= pthread_attr_setschedparam(&attr
, ¶m
);
907 rc
= pthread_create(&producer_thread
, NULL
, producer_loop
, NULL
);
910 rc
= pthread_create(&consumer_thread
, attrp
, consumer_loop
, NULL
);
912 d_print("could not create thread using real-time scheduling: %s\n", strerror(rc
));
913 rc
= pthread_create(&consumer_thread
, NULL
, consumer_loop
, NULL
);
917 /* update player_info.cont etc. */
919 __player_status_changed();
923 void player_exit(void)
928 consumer_running
= 0;
929 producer_running
= 0;
932 rc
= pthread_join(consumer_thread
, NULL
);
934 rc
= pthread_join(producer_thread
, NULL
);
940 void player_stop(void)
945 __player_status_changed();
949 void player_play(void)
954 if (producer_status
== PS_PLAYING
&& ip_is_remote(ip
)) {
955 /* seeking not allowed */
959 prebuffer
= consumer_status
== CS_STOPPED
;
961 if (producer_status
== PS_PLAYING
) {
963 if (consumer_status
!= CS_PLAYING
)
968 __player_status_changed();
969 if (consumer_status
== CS_PLAYING
&& prebuffer
)
974 void player_pause(void)
978 if (consumer_status
== CS_STOPPED
) {
980 if (producer_status
== PS_PLAYING
) {
982 if (consumer_status
!= CS_PLAYING
)
985 __player_status_changed();
986 if (consumer_status
== CS_PLAYING
)
992 if (ip
&& ip_is_remote(ip
)) {
993 /* pausing not allowed */
999 __player_status_changed();
1003 void player_set_file(struct track_info
*ti
)
1006 __producer_set_file(ti
);
1007 if (producer_status
== PS_UNLOADED
) {
1013 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1015 if (producer_status
== PS_UNLOADED
) {
1019 change_sf(ip_get_sf(ip
), 1);
1022 __player_status_changed();
1023 if (producer_status
== PS_PLAYING
)
1028 void player_play_file(struct track_info
*ti
)
1031 __producer_set_file(ti
);
1032 if (producer_status
== PS_UNLOADED
) {
1040 /* PS_UNLOADED,PS_PLAYING */
1041 if (producer_status
== PS_UNLOADED
) {
1047 if (consumer_status
== CS_STOPPED
) {
1049 if (consumer_status
== CS_STOPPED
)
1052 change_sf(ip_get_sf(ip
), 1);
1055 __player_status_changed();
1056 if (producer_status
== PS_PLAYING
)
1061 void player_seek(double offset
, int relative
)
1064 if (consumer_status
== CS_STOPPED
) {
1066 if (producer_status
== PS_PLAYING
) {
1068 if (consumer_status
!= CS_PLAYING
) {
1075 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1076 double pos
, duration
, new_pos
;
1079 pos
= (double)consumer_pos
/ (double)buffer_second_size();
1080 duration
= ip_duration(ip
);
1083 d_print("can't seek\n");
1088 new_pos
= pos
+ offset
;
1092 /* seeking forward */
1093 if (new_pos
> duration
- 5.0)
1094 new_pos
= duration
- 5.0;
1097 if (new_pos
< pos
- 0.5) {
1098 /* must seek at least 0.5s */
1099 d_print("must seek at least 0.5s\n");
1106 if (new_pos
< 0.0) {
1107 d_print("seek offset negative\n");
1111 if (new_pos
> duration
- 5.0) {
1112 new_pos
= duration
- 5.0;
1117 /* d_print("seeking %g/%g (%g from eof)\n", new_pos, duration, duration - new_pos); */
1118 rc
= ip_seek(ip
, new_pos
);
1120 /* d_print("doing op_drop after seek\n"); */
1123 consumer_pos
= new_pos
* buffer_second_size();
1124 scale_pos
= consumer_pos
;
1125 __consumer_position_update();
1127 d_print("error: ip_seek returned %d\n", rc
);
1134 * change output plugin without stopping playback
1136 void player_set_op(const char *name
)
1142 /* drop needed because close drains the buffer */
1143 if (consumer_status
== CS_PAUSED
)
1146 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
1150 d_print("setting op to '%s'\n", name
);
1151 rc
= op_select(name
);
1153 /* first initialized plugin */
1154 d_print("selecting first initialized op\n");
1155 rc
= op_select_any();
1158 consumer_status
= CS_STOPPED
;
1161 player_op_error(rc
, "selecting output plugin '%s'", name
);
1166 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1167 set_buffer_sf(ip_get_sf(ip
));
1168 rc
= op_open(buffer_sf
);
1170 consumer_status
= CS_STOPPED
;
1172 player_op_error(rc
, "opening audio device");
1176 if (consumer_status
== CS_PAUSED
)
1180 if (!op_get_volume(&l
, &r
))
1181 volume_update(l
, r
);
1186 char *player_get_op(void)
1188 return op_get_current();
1191 void player_set_buffer_chunks(unsigned int nr_chunks
)
1202 buffer_nr_chunks
= nr_chunks
;
1205 __player_status_changed();
1209 int player_get_buffer_chunks(void)
1211 return buffer_nr_chunks
;
1214 int player_get_fileinfo(const char *filename
, int *duration
,
1215 struct keyval
**comments
)
1217 struct input_plugin
*plug
;
1222 plug
= ip_new(filename
);
1223 if (ip_is_remote(plug
)) {
1224 *comments
= xnew0(struct keyval
, 1);
1235 rc
= -PLAYER_ERROR_NOT_SUPPORTED
;
1238 *duration
= ip_duration(plug
);
1239 rc
= ip_read_comments(plug
, comments
);
1244 int player_get_volume(int *left
, int *right
)
1249 rc
= op_get_volume(left
, right
);
1254 int player_set_volume(int left
, int right
)
1259 rc
= op_set_volume(left
, right
);
1261 volume_update(left
, right
);
1266 void player_set_soft_vol(int soft
)
1271 /* don't mess with scale_pos if soft_vol or replaygain is already enabled */
1272 if (!soft_vol
&& !replaygain
)
1273 scale_pos
= consumer_pos
;
1274 op_set_soft_vol(soft
);
1275 if (!op_get_volume(&l
, &r
))
1276 volume_update(l
, r
);
1280 void player_set_rg(enum replaygain rg
)
1283 /* don't mess with scale_pos if soft_vol or replaygain is already enabled */
1284 if (!soft_vol
&& !replaygain
)
1285 scale_pos
= consumer_pos
;
1290 player_info_unlock();
1295 void player_set_rg_limit(int limit
)
1298 replaygain_limit
= limit
;
1302 player_info_unlock();
1307 void player_set_rg_preamp(double db
)
1310 replaygain_preamp
= db
;
1314 player_info_unlock();
1319 int player_set_op_option(unsigned int id
, const char *val
)
1326 rc
= op_set_option(id
, val
);
1327 __player_status_changed();
1332 int player_get_op_option(unsigned int id
, char **val
)
1337 rc
= op_get_option(id
, val
);
1342 int player_for_each_op_option(void (*callback
)(unsigned int id
, const char *key
))
1347 op_for_each_option(callback
);
1348 __player_status_changed();
1353 void player_dump_plugins(void)