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 void scale_sample(signed short *buf
, int i
, int vol
)
166 sample
= (sample
* vol
- SOFT_VOL_SCALE
/ 2) / SOFT_VOL_SCALE
;
170 sample
= (sample
* vol
+ SOFT_VOL_SCALE
/ 2) / SOFT_VOL_SCALE
;
177 static void scale_samples(char *buffer
, unsigned int *countp
)
180 unsigned int count
= *countp
;
181 int ch
, bits
, l
, r
, i
;
183 BUG_ON(scale_pos
< consumer_pos
);
185 if (consumer_pos
!= scale_pos
) {
186 unsigned int offs
= scale_pos
- consumer_pos
;
194 buf
= (signed short *)buffer
;
196 if (replaygain_scale
== 1.0 && soft_vol_l
== 100 && soft_vol_r
== 100)
199 ch
= sf_get_channels(buffer_sf
);
200 bits
= sf_get_bits(buffer_sf
);
201 if (ch
!= 2 || bits
!= 16)
206 if (soft_vol_l
!= 100)
207 l
= soft_vol_db
[soft_vol_l
];
208 if (soft_vol_r
!= 100)
209 r
= soft_vol_db
[soft_vol_r
];
211 l
*= replaygain_scale
;
212 r
*= replaygain_scale
;
214 for (i
= 0; i
< count
/ 4; i
++) {
215 scale_sample(buf
, i
* 2, l
);
216 scale_sample(buf
, i
* 2 + 1, r
);
220 static int parse_double(const char *str
, double *val
)
224 *val
= strtod(str
, &end
);
228 static void update_rg_scale(void)
231 double gain
, peak
, db
, scale
, limit
;
233 replaygain_scale
= 1.0;
234 if (!player_info
.ti
|| !replaygain
)
237 if (replaygain
== RG_TRACK
) {
238 g
= comments_get_val(player_info
.ti
->comments
, "replaygain_track_gain");
239 p
= comments_get_val(player_info
.ti
->comments
, "replaygain_track_peak");
241 g
= comments_get_val(player_info
.ti
->comments
, "replaygain_album_gain");
242 p
= comments_get_val(player_info
.ti
->comments
, "replaygain_album_peak");
246 d_print("gain or peak not available\n");
249 if (parse_double(g
, &gain
) || parse_double(p
, &peak
)) {
250 d_print("could not parse gain (%s) or peak (%s)\n", g
, p
);
254 d_print("peak (%g) is too small\n", peak
);
258 db
= replaygain_preamp
+ gain
;
260 scale
= pow(10.0, db
/ 20.0);
261 replaygain_scale
= scale
;
263 if (replaygain_limit
&& replaygain_scale
> limit
)
264 replaygain_scale
= limit
;
266 d_print("gain = %f, peak = %f, db = %f, scale = %f, limit = %f, replaygain_scale = %f\n",
267 gain
, peak
, db
, scale
, limit
, replaygain_scale
);
270 static inline unsigned int buffer_second_size(void)
272 return sf_get_second_size(buffer_sf
);
275 static inline int get_next(struct track_info
**ti
)
277 return player_cbs
->get_next(ti
);
280 /* updating player status {{{ */
282 static inline void file_changed(struct track_info
*ti
)
286 track_info_unref(player_info
.ti
);
290 d_print("file: %s\n", ti
->filename
);
292 d_print("unloaded\n");
295 player_info
.metadata
[0] = 0;
296 player_info
.file_changed
= 1;
297 player_info_unlock();
300 static inline void metadata_changed(void)
303 d_print("metadata changed: %s\n", ip_get_metadata(ip
));
304 memcpy(player_info
.metadata
, ip_get_metadata(ip
), 255 * 16 + 1);
305 player_info
.metadata_changed
= 1;
306 player_info_unlock();
309 static void player_error(const char *msg
)
312 player_info
.status
= consumer_status
;
314 player_info
.buffer_fill
= buffer_get_filled_chunks();
315 player_info
.buffer_size
= buffer_nr_chunks
;
316 player_info
.status_changed
= 1;
318 free(player_info
.error_msg
);
319 player_info
.error_msg
= xstrdup(msg
);
320 player_info_unlock();
322 d_print("ERROR: '%s'\n", msg
);
325 static void __FORMAT(2, 3) player_ip_error(int rc
, const char *format
, ...)
332 va_start(ap
, format
);
333 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
337 msg
= ip_get_error_msg(ip
, rc
, buffer
);
342 static void __FORMAT(2, 3) player_op_error(int rc
, const char *format
, ...)
349 va_start(ap
, format
);
350 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
354 msg
= op_get_error_msg(rc
, buffer
);
360 * buffer-fill changed
362 static void __producer_buffer_fill_update(void)
367 fill
= buffer_get_filled_chunks();
368 if (fill
!= player_info
.buffer_fill
) {
370 player_info
.buffer_fill
= fill
;
371 player_info
.buffer_fill_changed
= 1;
373 player_info_unlock();
377 * playing position changed
379 static void __consumer_position_update(void)
381 static unsigned int old_pos
= -1;
382 unsigned int pos
= 0;
384 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
385 pos
= consumer_pos
/ buffer_second_size();
386 if (pos
!= old_pos
) {
391 player_info
.pos
= pos
;
392 player_info
.position_changed
= 1;
393 player_info_unlock();
398 * something big happened (stopped/paused/unpaused...)
400 static void __player_status_changed(void)
402 unsigned int pos
= 0;
405 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
406 pos
= consumer_pos
/ buffer_second_size();
409 player_info
.status
= consumer_status
;
410 player_info
.pos
= pos
;
411 player_info
.buffer_fill
= buffer_get_filled_chunks();
412 player_info
.buffer_size
= buffer_nr_chunks
;
413 player_info
.status_changed
= 1;
414 player_info_unlock();
417 /* updating player status }}} */
419 static void __prebuffer(void)
423 BUG_ON(producer_status
!= PS_PLAYING
);
424 if (ip_is_remote(ip
)) {
425 limit_chunks
= buffer_nr_chunks
;
427 int limit_ms
, limit_size
;
430 limit_size
= limit_ms
* buffer_second_size() / 1000;
431 limit_chunks
= limit_size
/ CHUNK_SIZE
;
432 if (limit_chunks
< 1)
436 int nr_read
, size
, filled
;
439 filled
= buffer_get_filled_chunks();
440 /* d_print("PREBUF: %2d / %2d\n", filled, limit_chunks); */
443 //BUG_ON(filled > limit_chunks);
445 if (filled
>= limit_chunks
)
448 size
= buffer_get_wpos(&wpos
);
449 nr_read
= ip_read(ip
, wpos
, size
);
451 if (nr_read
== -1 && errno
== EAGAIN
)
453 player_ip_error(nr_read
, "reading file %s", ip_get_filename(ip
));
454 /* ip_read sets eof */
457 if (ip_metadata_changed(ip
))
460 /* buffer_fill with 0 count marks current chunk filled */
461 buffer_fill(nr_read
);
463 __producer_buffer_fill_update();
471 /* setting producer status {{{ */
473 static void __producer_play(void)
475 if (producer_status
== PS_UNLOADED
) {
476 struct track_info
*ti
;
478 if (get_next(&ti
) == 0) {
481 ip
= ip_new(ti
->filename
);
484 player_ip_error(rc
, "opening file `%s'", ti
->filename
);
486 track_info_unref(ti
);
490 producer_status
= PS_PLAYING
;
494 } else if (producer_status
== PS_PLAYING
) {
495 if (ip_seek(ip
, 0.0) == 0) {
498 } else if (producer_status
== PS_STOPPED
) {
503 player_ip_error(rc
, "opening file `%s'", ip_get_filename(ip
));
505 producer_status
= PS_UNLOADED
;
508 producer_status
= PS_PLAYING
;
510 } else if (producer_status
== PS_PAUSED
) {
511 producer_status
= PS_PLAYING
;
515 static void __producer_stop(void)
517 if (producer_status
== PS_PLAYING
|| producer_status
== PS_PAUSED
) {
519 producer_status
= PS_STOPPED
;
524 static void __producer_unload(void)
527 if (producer_status
== PS_STOPPED
) {
529 producer_status
= PS_UNLOADED
;
533 static void __producer_pause(void)
535 if (producer_status
== PS_PLAYING
) {
536 producer_status
= PS_PAUSED
;
537 } else if (producer_status
== PS_PAUSED
) {
538 producer_status
= PS_PLAYING
;
542 static void __producer_set_file(struct track_info
*ti
)
545 ip
= ip_new(ti
->filename
);
546 producer_status
= PS_STOPPED
;
550 /* setting producer status }}} */
552 /* setting consumer status {{{ */
554 static void __consumer_play(void)
556 if (consumer_status
== CS_PLAYING
) {
558 } else if (consumer_status
== CS_STOPPED
) {
561 set_buffer_sf(ip_get_sf(ip
));
562 rc
= op_open(buffer_sf
);
564 player_op_error(rc
, "opening audio device");
566 consumer_status
= CS_PLAYING
;
568 } else if (consumer_status
== CS_PAUSED
) {
570 consumer_status
= CS_PLAYING
;
574 static void __consumer_drain_and_stop(void)
576 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
578 consumer_status
= CS_STOPPED
;
582 static void __consumer_stop(void)
584 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
587 consumer_status
= CS_STOPPED
;
591 static void __consumer_pause(void)
593 if (consumer_status
== CS_PLAYING
) {
595 consumer_status
= CS_PAUSED
;
596 } else if (consumer_status
== CS_PAUSED
) {
598 consumer_status
= CS_PLAYING
;
602 /* setting consumer status }}} */
604 static int change_sf(sample_format_t sf
, int drop
)
606 int old_sf
= buffer_sf
;
609 if (buffer_sf
!= old_sf
) {
616 rc
= op_open(buffer_sf
);
618 player_op_error(rc
, "opening audio device");
619 consumer_status
= CS_STOPPED
;
623 } else if (consumer_status
== CS_PAUSED
) {
627 consumer_status
= CS_PLAYING
;
631 static void __consumer_handle_eof(void)
633 struct track_info
*ti
;
635 if (ip_is_remote(ip
)) {
637 __consumer_drain_and_stop();
638 player_error("lost connection");
642 if (get_next(&ti
) == 0) {
644 ip
= ip_new(ti
->filename
);
645 producer_status
= PS_STOPPED
;
646 /* PS_STOPPED, CS_PLAYING */
649 if (producer_status
== PS_UNLOADED
) {
651 track_info_unref(ti
);
656 if (!change_sf(ip_get_sf(ip
), 0))
660 __consumer_drain_and_stop();
665 __consumer_drain_and_stop();
668 __player_status_changed();
671 static void *consumer_loop(void *arg
)
679 if (!consumer_running
)
682 if (consumer_status
== CS_PAUSED
|| consumer_status
== CS_STOPPED
) {
687 space
= op_buffer_space();
690 __consumer_position_update();
695 /* d_print("BS: %6d %3d\n", space, space * 1000 / (44100 * 2 * 2)); */
698 /* 25 ms is 4410 B */
700 __consumer_position_update();
705 size
= buffer_get_rpos(&rpos
);
708 if (producer_status
!= PS_PLAYING
) {
713 /* must recheck rpos */
714 size
= buffer_get_rpos(&rpos
);
716 /* OK. now it's safe to check if we are at EOF */
719 __consumer_handle_eof();
724 /* possible underrun */
726 __consumer_position_update();
728 /* d_print("possible underrun\n"); */
734 /* player_buffer and ip.eof were inconsistent */
739 if (soft_vol
|| replaygain
)
740 scale_samples(rpos
, &size
);
741 rc
= op_write(rpos
, size
);
743 d_print("op_write returned %d %s\n", rc
,
744 rc
== -1 ? strerror(errno
) : "");
748 consumer_status
= CS_STOPPED
;
764 static void *producer_loop(void *arg
)
767 /* number of chunks to fill
768 * too big => seeking is slow
769 * too small => underruns?
771 const int chunks
= 1;
772 int size
, nr_read
, i
;
776 if (!producer_running
)
779 if (producer_status
== PS_UNLOADED
||
780 producer_status
== PS_PAUSED
||
781 producer_status
== PS_STOPPED
|| ip_eof(ip
)) {
787 size
= buffer_get_wpos(&wpos
);
794 nr_read
= ip_read(ip
, wpos
, size
);
796 if (nr_read
!= -1 || errno
!= EAGAIN
) {
797 player_ip_error(nr_read
, "reading file %s",
798 ip_get_filename(ip
));
799 /* ip_read sets eof */
807 if (ip_metadata_changed(ip
))
810 /* buffer_fill with 0 count marks current chunk filled */
811 buffer_fill(nr_read
);
813 /* consumer handles EOF */
824 __producer_buffer_fill_update();
831 void player_load_plugins(void)
837 void player_init(const struct player_callbacks
*callbacks
)
840 #if defined(__linux__) || defined(__FreeBSD__)
843 pthread_attr_t
*attrp
= NULL
;
845 /* 1 s is 176400 B (0.168 MB)
848 buffer_nr_chunks
= 10 * 44100 * 16 / 8 * 2 / CHUNK_SIZE
;
851 player_cbs
= callbacks
;
853 #if defined(__linux__) || defined(__FreeBSD__)
854 rc
= pthread_attr_init(&attr
);
856 rc
= pthread_attr_setschedpolicy(&attr
, SCHED_RR
);
858 d_print("could not set real-time scheduling priority: %s\n", strerror(rc
));
860 struct sched_param param
;
862 d_print("using real-time scheduling\n");
863 param
.sched_priority
= sched_get_priority_max(SCHED_RR
);
864 d_print("setting priority to %d\n", param
.sched_priority
);
865 rc
= pthread_attr_setschedparam(&attr
, ¶m
);
871 rc
= pthread_create(&producer_thread
, NULL
, producer_loop
, NULL
);
874 rc
= pthread_create(&consumer_thread
, attrp
, consumer_loop
, NULL
);
876 d_print("could not create thread using real-time scheduling: %s\n", strerror(rc
));
877 rc
= pthread_create(&consumer_thread
, NULL
, consumer_loop
, NULL
);
881 /* update player_info.cont etc. */
883 __player_status_changed();
887 void player_exit(void)
892 consumer_running
= 0;
893 producer_running
= 0;
896 rc
= pthread_join(consumer_thread
, NULL
);
898 rc
= pthread_join(producer_thread
, NULL
);
904 void player_stop(void)
909 __player_status_changed();
913 void player_play(void)
918 if (producer_status
== PS_PLAYING
&& ip_is_remote(ip
)) {
919 /* seeking not allowed */
923 prebuffer
= consumer_status
== CS_STOPPED
;
925 if (producer_status
== PS_PLAYING
) {
927 if (consumer_status
!= CS_PLAYING
)
932 __player_status_changed();
933 if (consumer_status
== CS_PLAYING
&& prebuffer
)
938 void player_pause(void)
942 if (consumer_status
== CS_STOPPED
) {
944 if (producer_status
== PS_PLAYING
) {
946 if (consumer_status
!= CS_PLAYING
)
949 __player_status_changed();
950 if (consumer_status
== CS_PLAYING
)
956 if (ip
&& ip_is_remote(ip
)) {
957 /* pausing not allowed */
963 __player_status_changed();
967 void player_set_file(struct track_info
*ti
)
970 __producer_set_file(ti
);
971 if (producer_status
== PS_UNLOADED
) {
977 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
979 if (producer_status
== PS_UNLOADED
) {
983 change_sf(ip_get_sf(ip
), 1);
986 __player_status_changed();
987 if (producer_status
== PS_PLAYING
)
992 void player_play_file(struct track_info
*ti
)
995 __producer_set_file(ti
);
996 if (producer_status
== PS_UNLOADED
) {
1004 /* PS_UNLOADED,PS_PLAYING */
1005 if (producer_status
== PS_UNLOADED
) {
1011 if (consumer_status
== CS_STOPPED
) {
1013 if (consumer_status
== CS_STOPPED
)
1016 change_sf(ip_get_sf(ip
), 1);
1019 __player_status_changed();
1020 if (producer_status
== PS_PLAYING
)
1025 void player_seek(double offset
, int relative
)
1028 if (consumer_status
== CS_STOPPED
) {
1030 if (producer_status
== PS_PLAYING
) {
1032 if (consumer_status
!= CS_PLAYING
) {
1037 __player_status_changed();
1040 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1041 double pos
, duration
, new_pos
;
1044 pos
= (double)consumer_pos
/ (double)buffer_second_size();
1045 duration
= ip_duration(ip
);
1048 d_print("can't seek\n");
1053 new_pos
= pos
+ offset
;
1057 /* seeking forward */
1058 if (new_pos
> duration
- 5.0)
1059 new_pos
= duration
- 5.0;
1062 if (new_pos
< pos
- 0.5) {
1063 /* must seek at least 0.5s */
1064 d_print("must seek at least 0.5s\n");
1071 if (new_pos
< 0.0) {
1072 d_print("seek offset negative\n");
1076 if (new_pos
> duration
- 5.0) {
1077 new_pos
= duration
- 5.0;
1082 /* d_print("seeking %g/%g (%g from eof)\n", new_pos, duration, duration - new_pos); */
1083 rc
= ip_seek(ip
, new_pos
);
1085 /* d_print("doing op_drop after seek\n"); */
1088 consumer_pos
= new_pos
* buffer_second_size();
1089 scale_pos
= consumer_pos
;
1090 __consumer_position_update();
1092 d_print("error: ip_seek returned %d\n", rc
);
1099 * change output plugin without stopping playback
1101 void player_set_op(const char *name
)
1107 /* drop needed because close drains the buffer */
1108 if (consumer_status
== CS_PAUSED
)
1111 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
1115 d_print("setting op to '%s'\n", name
);
1116 rc
= op_select(name
);
1118 /* first initialized plugin */
1119 d_print("selecting first initialized op\n");
1120 rc
= op_select_any();
1123 consumer_status
= CS_STOPPED
;
1126 player_op_error(rc
, "selecting output plugin '%s'", name
);
1131 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1132 set_buffer_sf(ip_get_sf(ip
));
1133 rc
= op_open(buffer_sf
);
1135 consumer_status
= CS_STOPPED
;
1137 player_op_error(rc
, "opening audio device");
1141 if (consumer_status
== CS_PAUSED
)
1148 char *player_get_op(void)
1150 return op_get_current();
1153 void player_set_buffer_chunks(unsigned int nr_chunks
)
1164 buffer_nr_chunks
= nr_chunks
;
1167 __player_status_changed();
1171 int player_get_buffer_chunks(void)
1173 return buffer_nr_chunks
;
1176 int player_get_fileinfo(const char *filename
, int *duration
,
1177 struct keyval
**comments
)
1179 struct input_plugin
*plug
;
1184 plug
= ip_new(filename
);
1185 if (ip_is_remote(plug
)) {
1186 *comments
= xnew0(struct keyval
, 1);
1197 rc
= -PLAYER_ERROR_NOT_SUPPORTED
;
1200 *duration
= ip_duration(plug
);
1201 rc
= ip_read_comments(plug
, comments
);
1206 void player_set_soft_volume(int l
, int r
)
1214 void player_set_soft_vol(int soft
)
1217 /* don't mess with scale_pos if soft_vol or replaygain is already enabled */
1218 if (!soft_vol
&& !replaygain
)
1219 scale_pos
= consumer_pos
;
1224 void player_set_rg(enum replaygain rg
)
1227 /* don't mess with scale_pos if soft_vol or replaygain is already enabled */
1228 if (!soft_vol
&& !replaygain
)
1229 scale_pos
= consumer_pos
;
1234 player_info_unlock();
1239 void player_set_rg_limit(int limit
)
1242 replaygain_limit
= limit
;
1246 player_info_unlock();
1251 void player_set_rg_preamp(double db
)
1254 replaygain_preamp
= db
;
1258 player_info_unlock();
1263 int player_set_op_option(unsigned int id
, const char *val
)
1270 rc
= op_set_option(id
, val
);
1271 __player_status_changed();
1276 int player_get_op_option(unsigned int id
, char **val
)
1281 rc
= op_get_option(id
, val
);
1286 int player_for_each_op_option(void (*callback
)(unsigned int id
, const char *key
))
1291 op_for_each_option(callback
);
1292 __player_status_changed();
1297 void player_dump_plugins(void)