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 /* repeat current track forever? */
71 int player_repeat_current
;
73 enum replaygain replaygain
;
74 int replaygain_limit
= 1;
75 double replaygain_preamp
= 6.0;
81 static const struct player_callbacks
*player_cbs
= NULL
;
83 static sample_format_t buffer_sf
;
85 static pthread_t producer_thread
;
86 static pthread_mutex_t producer_mutex
= CMUS_MUTEX_INITIALIZER
;
87 static int producer_running
= 1;
88 static enum producer_status producer_status
= PS_UNLOADED
;
89 static struct input_plugin
*ip
= NULL
;
91 static pthread_t consumer_thread
;
92 static pthread_mutex_t consumer_mutex
= CMUS_MUTEX_INITIALIZER
;
93 static int consumer_running
= 1;
94 static enum consumer_status consumer_status
= CS_STOPPED
;
95 static unsigned int consumer_pos
= 0;
97 /* for replay gain and soft vol
98 * usually same as consumer_pos, sometimes less than consumer_pos
100 static unsigned int scale_pos
;
101 static double replaygain_scale
= 1.0;
105 #define producer_lock() cmus_mutex_lock(&producer_mutex)
106 #define producer_unlock() cmus_mutex_unlock(&producer_mutex)
108 #define consumer_lock() cmus_mutex_lock(&consumer_mutex)
109 #define consumer_unlock() cmus_mutex_unlock(&consumer_mutex)
111 #define player_lock() \
117 #define player_unlock() \
125 static void reset_buffer(void)
132 static void set_buffer_sf(sample_format_t sf
)
136 /* ip_read converts samples to this format */
137 if (sf_get_channels(buffer_sf
) <= 2 && sf_get_bits(buffer_sf
) <= 16) {
138 buffer_sf
&= SF_RATE_MASK
;
139 buffer_sf
|= sf_channels(2) | sf_bits(16) | sf_signed(1);
143 #define SOFT_VOL_SCALE 65536
145 /* coefficients for volumes 0..99, for 100 65536 is used
146 * data copied from alsa-lib src/pcm/pcm_softvol.c
148 static const unsigned short soft_vol_db
[100] = {
149 0x0000, 0x0110, 0x011c, 0x012f, 0x013d, 0x0152, 0x0161, 0x0179,
150 0x018a, 0x01a5, 0x01c1, 0x01d5, 0x01f5, 0x020b, 0x022e, 0x0247,
151 0x026e, 0x028a, 0x02b6, 0x02d5, 0x0306, 0x033a, 0x035f, 0x0399,
152 0x03c2, 0x0403, 0x0431, 0x0479, 0x04ac, 0x04fd, 0x0553, 0x058f,
153 0x05ef, 0x0633, 0x069e, 0x06ea, 0x0761, 0x07b5, 0x083a, 0x0898,
154 0x092c, 0x09cb, 0x0a3a, 0x0aeb, 0x0b67, 0x0c2c, 0x0cb6, 0x0d92,
155 0x0e2d, 0x0f21, 0x1027, 0x10de, 0x1202, 0x12cf, 0x1414, 0x14f8,
156 0x1662, 0x1761, 0x18f5, 0x1a11, 0x1bd3, 0x1db4, 0x1f06, 0x211d,
157 0x2297, 0x24ec, 0x2690, 0x292a, 0x2aff, 0x2de5, 0x30fe, 0x332b,
158 0x369f, 0x390d, 0x3ce6, 0x3f9b, 0x43e6, 0x46eb, 0x4bb3, 0x4f11,
159 0x5466, 0x5a18, 0x5e19, 0x6472, 0x68ea, 0x6ffd, 0x74f8, 0x7cdc,
160 0x826a, 0x8b35, 0x9499, 0x9b35, 0xa5ad, 0xad0b, 0xb8b7, 0xc0ee,
161 0xcdf1, 0xd71a, 0xe59c, 0xefd3
164 static inline unsigned short swap16(unsigned short u
)
166 return (u
<< 8) | (u
>> 8);
169 static inline void scale_sample(signed short *buf
, int i
, int vol
)
171 #ifdef WORDS_BIGENDIAN
172 int sample
= (short)swap16(buf
[i
]);
178 sample
= (sample
* vol
- SOFT_VOL_SCALE
/ 2) / SOFT_VOL_SCALE
;
182 sample
= (sample
* vol
+ SOFT_VOL_SCALE
/ 2) / SOFT_VOL_SCALE
;
186 #ifdef WORDS_BIGENDIAN
187 buf
[i
] = swap16(sample
);
193 static void scale_samples(char *buffer
, unsigned int *countp
)
196 unsigned int count
= *countp
;
197 int ch
, bits
, l
, r
, i
;
199 BUG_ON(scale_pos
< consumer_pos
);
201 if (consumer_pos
!= scale_pos
) {
202 unsigned int offs
= scale_pos
- consumer_pos
;
210 buf
= (signed short *)buffer
;
212 if (replaygain_scale
== 1.0 && soft_vol_l
== 100 && soft_vol_r
== 100)
215 ch
= sf_get_channels(buffer_sf
);
216 bits
= sf_get_bits(buffer_sf
);
217 if (ch
!= 2 || bits
!= 16)
222 if (soft_vol_l
!= 100)
223 l
= soft_vol_db
[soft_vol_l
];
224 if (soft_vol_r
!= 100)
225 r
= soft_vol_db
[soft_vol_r
];
227 l
*= replaygain_scale
;
228 r
*= replaygain_scale
;
230 /* avoid underflowing -32768 to 32767 when scale is 65536 */
231 if (l
!= SOFT_VOL_SCALE
&& r
!= SOFT_VOL_SCALE
) {
232 for (i
= 0; i
< count
/ 4; i
++) {
233 scale_sample(buf
, i
* 2, l
);
234 scale_sample(buf
, i
* 2 + 1, r
);
236 } else if (l
!= SOFT_VOL_SCALE
) {
237 for (i
= 0; i
< count
/ 4; i
++)
238 scale_sample(buf
, i
* 2, l
);
239 } else if (r
!= SOFT_VOL_SCALE
) {
240 for (i
= 0; i
< count
/ 4; i
++)
241 scale_sample(buf
, i
* 2 + 1, r
);
245 static int parse_double(const char *str
, double *val
)
249 *val
= strtod(str
, &end
);
253 static void update_rg_scale(void)
256 double gain
, peak
, db
, scale
, limit
;
258 replaygain_scale
= 1.0;
259 if (!player_info
.ti
|| !replaygain
)
262 if (replaygain
== RG_TRACK
) {
263 g
= keyvals_get_val(player_info
.ti
->comments
, "replaygain_track_gain");
264 p
= keyvals_get_val(player_info
.ti
->comments
, "replaygain_track_peak");
266 g
= keyvals_get_val(player_info
.ti
->comments
, "replaygain_album_gain");
267 p
= keyvals_get_val(player_info
.ti
->comments
, "replaygain_album_peak");
271 d_print("gain or peak not available\n");
274 if (parse_double(g
, &gain
) || parse_double(p
, &peak
)) {
275 d_print("could not parse gain (%s) or peak (%s)\n", g
, p
);
279 d_print("peak (%g) is too small\n", peak
);
283 db
= replaygain_preamp
+ gain
;
285 scale
= pow(10.0, db
/ 20.0);
286 replaygain_scale
= scale
;
288 if (replaygain_limit
&& replaygain_scale
> limit
)
289 replaygain_scale
= limit
;
291 d_print("gain = %f, peak = %f, db = %f, scale = %f, limit = %f, replaygain_scale = %f\n",
292 gain
, peak
, db
, scale
, limit
, replaygain_scale
);
295 static inline unsigned int buffer_second_size(void)
297 return sf_get_second_size(buffer_sf
);
300 static inline int get_next(struct track_info
**ti
)
302 return player_cbs
->get_next(ti
);
305 /* updating player status {{{ */
307 static inline void file_changed(struct track_info
*ti
)
311 track_info_unref(player_info
.ti
);
315 d_print("file: %s\n", ti
->filename
);
317 d_print("unloaded\n");
320 player_info
.metadata
[0] = 0;
321 player_info
.file_changed
= 1;
322 player_info_unlock();
325 static inline void metadata_changed(void)
328 d_print("metadata changed: %s\n", ip_get_metadata(ip
));
329 memcpy(player_info
.metadata
, ip_get_metadata(ip
), 255 * 16 + 1);
330 player_info
.metadata_changed
= 1;
331 player_info_unlock();
334 static void player_error(const char *msg
)
337 player_info
.status
= consumer_status
;
339 player_info
.buffer_fill
= buffer_get_filled_chunks();
340 player_info
.buffer_size
= buffer_nr_chunks
;
341 player_info
.status_changed
= 1;
343 free(player_info
.error_msg
);
344 player_info
.error_msg
= xstrdup(msg
);
345 player_info_unlock();
347 d_print("ERROR: '%s'\n", msg
);
350 static void __FORMAT(2, 3) player_ip_error(int rc
, const char *format
, ...)
357 va_start(ap
, format
);
358 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
362 msg
= ip_get_error_msg(ip
, rc
, buffer
);
367 static void __FORMAT(2, 3) player_op_error(int rc
, const char *format
, ...)
374 va_start(ap
, format
);
375 vsnprintf(buffer
, sizeof(buffer
), format
, ap
);
379 msg
= op_get_error_msg(rc
, buffer
);
385 * buffer-fill changed
387 static void __producer_buffer_fill_update(void)
392 fill
= buffer_get_filled_chunks();
393 if (fill
!= player_info
.buffer_fill
) {
395 player_info
.buffer_fill
= fill
;
396 player_info
.buffer_fill_changed
= 1;
398 player_info_unlock();
402 * playing position changed
404 static void __consumer_position_update(void)
406 static unsigned int old_pos
= -1;
407 unsigned int pos
= 0;
409 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
410 pos
= consumer_pos
/ buffer_second_size();
411 if (pos
!= old_pos
) {
416 player_info
.pos
= pos
;
417 player_info
.position_changed
= 1;
418 player_info_unlock();
423 * something big happened (stopped/paused/unpaused...)
425 static void __player_status_changed(void)
427 unsigned int pos
= 0;
430 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
431 pos
= consumer_pos
/ buffer_second_size();
434 player_info
.status
= consumer_status
;
435 player_info
.pos
= pos
;
436 player_info
.buffer_fill
= buffer_get_filled_chunks();
437 player_info
.buffer_size
= buffer_nr_chunks
;
438 player_info
.status_changed
= 1;
439 player_info_unlock();
442 /* updating player status }}} */
444 static void __prebuffer(void)
448 BUG_ON(producer_status
!= PS_PLAYING
);
449 if (ip_is_remote(ip
)) {
450 limit_chunks
= buffer_nr_chunks
;
452 int limit_ms
, limit_size
;
455 limit_size
= limit_ms
* buffer_second_size() / 1000;
456 limit_chunks
= limit_size
/ CHUNK_SIZE
;
457 if (limit_chunks
< 1)
461 int nr_read
, size
, filled
;
464 filled
= buffer_get_filled_chunks();
465 /* d_print("PREBUF: %2d / %2d\n", filled, limit_chunks); */
468 //BUG_ON(filled > limit_chunks);
470 if (filled
>= limit_chunks
)
473 size
= buffer_get_wpos(&wpos
);
474 nr_read
= ip_read(ip
, wpos
, size
);
476 if (nr_read
== -1 && errno
== EAGAIN
)
478 player_ip_error(nr_read
, "reading file %s", ip_get_filename(ip
));
479 /* ip_read sets eof */
482 if (ip_metadata_changed(ip
))
485 /* buffer_fill with 0 count marks current chunk filled */
486 buffer_fill(nr_read
);
488 __producer_buffer_fill_update();
496 /* setting producer status {{{ */
498 static void __producer_play(void)
500 if (producer_status
== PS_UNLOADED
) {
501 struct track_info
*ti
;
503 if (get_next(&ti
) == 0) {
506 ip
= ip_new(ti
->filename
);
509 player_ip_error(rc
, "opening file `%s'", ti
->filename
);
511 track_info_unref(ti
);
515 producer_status
= PS_PLAYING
;
519 } else if (producer_status
== PS_PLAYING
) {
520 if (ip_seek(ip
, 0.0) == 0) {
523 } else if (producer_status
== PS_STOPPED
) {
528 player_ip_error(rc
, "opening file `%s'", ip_get_filename(ip
));
530 producer_status
= PS_UNLOADED
;
533 producer_status
= PS_PLAYING
;
535 } else if (producer_status
== PS_PAUSED
) {
536 producer_status
= PS_PLAYING
;
540 static void __producer_stop(void)
542 if (producer_status
== PS_PLAYING
|| producer_status
== PS_PAUSED
) {
544 producer_status
= PS_STOPPED
;
549 static void __producer_unload(void)
552 if (producer_status
== PS_STOPPED
) {
554 producer_status
= PS_UNLOADED
;
558 static void __producer_pause(void)
560 if (producer_status
== PS_PLAYING
) {
561 producer_status
= PS_PAUSED
;
562 } else if (producer_status
== PS_PAUSED
) {
563 producer_status
= PS_PLAYING
;
567 static void __producer_set_file(struct track_info
*ti
)
570 ip
= ip_new(ti
->filename
);
571 producer_status
= PS_STOPPED
;
575 /* setting producer status }}} */
577 /* setting consumer status {{{ */
579 static void __consumer_play(void)
581 if (consumer_status
== CS_PLAYING
) {
583 } else if (consumer_status
== CS_STOPPED
) {
586 set_buffer_sf(ip_get_sf(ip
));
587 rc
= op_open(buffer_sf
);
589 player_op_error(rc
, "opening audio device");
591 consumer_status
= CS_PLAYING
;
593 } else if (consumer_status
== CS_PAUSED
) {
595 consumer_status
= CS_PLAYING
;
599 static void __consumer_drain_and_stop(void)
601 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
603 consumer_status
= CS_STOPPED
;
607 static void __consumer_stop(void)
609 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
612 consumer_status
= CS_STOPPED
;
616 static void __consumer_pause(void)
618 if (consumer_status
== CS_PLAYING
) {
620 consumer_status
= CS_PAUSED
;
621 } else if (consumer_status
== CS_PAUSED
) {
623 consumer_status
= CS_PLAYING
;
627 /* setting consumer status }}} */
629 static int change_sf(sample_format_t sf
, int drop
)
631 int old_sf
= buffer_sf
;
634 if (buffer_sf
!= old_sf
) {
641 rc
= op_open(buffer_sf
);
643 player_op_error(rc
, "opening audio device");
644 consumer_status
= CS_STOPPED
;
648 } else if (consumer_status
== CS_PAUSED
) {
652 consumer_status
= CS_PLAYING
;
656 static void __consumer_handle_eof(void)
658 struct track_info
*ti
;
660 if (ip_is_remote(ip
)) {
662 __consumer_drain_and_stop();
663 player_error("lost connection");
667 if (player_repeat_current
) {
673 __consumer_drain_and_stop();
674 __player_status_changed();
679 if (get_next(&ti
) == 0) {
681 ip
= ip_new(ti
->filename
);
682 producer_status
= PS_STOPPED
;
683 /* PS_STOPPED, CS_PLAYING */
686 if (producer_status
== PS_UNLOADED
) {
688 track_info_unref(ti
);
693 if (!change_sf(ip_get_sf(ip
), 0))
697 __consumer_drain_and_stop();
702 __consumer_drain_and_stop();
705 __player_status_changed();
708 static void *consumer_loop(void *arg
)
716 if (!consumer_running
)
719 if (consumer_status
== CS_PAUSED
|| consumer_status
== CS_STOPPED
) {
724 space
= op_buffer_space();
727 __consumer_position_update();
732 /* d_print("BS: %6d %3d\n", space, space * 1000 / (44100 * 2 * 2)); */
735 /* 25 ms is 4410 B */
737 __consumer_position_update();
742 size
= buffer_get_rpos(&rpos
);
745 if (producer_status
!= PS_PLAYING
) {
750 /* must recheck rpos */
751 size
= buffer_get_rpos(&rpos
);
753 /* OK. now it's safe to check if we are at EOF */
756 __consumer_handle_eof();
761 /* possible underrun */
763 __consumer_position_update();
765 /* d_print("possible underrun\n"); */
771 /* player_buffer and ip.eof were inconsistent */
776 if (soft_vol
|| replaygain
)
777 scale_samples(rpos
, &size
);
778 rc
= op_write(rpos
, size
);
780 d_print("op_write returned %d %s\n", rc
,
781 rc
== -1 ? strerror(errno
) : "");
785 consumer_status
= CS_STOPPED
;
801 static void *producer_loop(void *arg
)
804 /* number of chunks to fill
805 * too big => seeking is slow
806 * too small => underruns?
808 const int chunks
= 1;
809 int size
, nr_read
, i
;
813 if (!producer_running
)
816 if (producer_status
== PS_UNLOADED
||
817 producer_status
== PS_PAUSED
||
818 producer_status
== PS_STOPPED
|| ip_eof(ip
)) {
824 size
= buffer_get_wpos(&wpos
);
831 nr_read
= ip_read(ip
, wpos
, size
);
833 if (nr_read
!= -1 || errno
!= EAGAIN
) {
834 player_ip_error(nr_read
, "reading file %s",
835 ip_get_filename(ip
));
836 /* ip_read sets eof */
844 if (ip_metadata_changed(ip
))
847 /* buffer_fill with 0 count marks current chunk filled */
848 buffer_fill(nr_read
);
850 /* consumer handles EOF */
861 __producer_buffer_fill_update();
868 void player_init(const struct player_callbacks
*callbacks
)
871 #ifdef REALTIME_SCHEDULING
874 pthread_attr_t
*attrp
= NULL
;
876 /* 1 s is 176400 B (0.168 MB)
879 buffer_nr_chunks
= 10 * 44100 * 16 / 8 * 2 / CHUNK_SIZE
;
882 player_cbs
= callbacks
;
884 #ifdef REALTIME_SCHEDULING
885 rc
= pthread_attr_init(&attr
);
887 rc
= pthread_attr_setschedpolicy(&attr
, SCHED_RR
);
889 d_print("could not set real-time scheduling priority: %s\n", strerror(rc
));
891 struct sched_param param
;
893 d_print("using real-time scheduling\n");
894 param
.sched_priority
= sched_get_priority_max(SCHED_RR
);
895 d_print("setting priority to %d\n", param
.sched_priority
);
896 rc
= pthread_attr_setschedparam(&attr
, ¶m
);
902 rc
= pthread_create(&producer_thread
, NULL
, producer_loop
, NULL
);
905 rc
= pthread_create(&consumer_thread
, attrp
, consumer_loop
, NULL
);
907 d_print("could not create thread using real-time scheduling: %s\n", strerror(rc
));
908 rc
= pthread_create(&consumer_thread
, NULL
, consumer_loop
, NULL
);
912 /* update player_info.cont etc. */
914 __player_status_changed();
918 void player_exit(void)
923 consumer_running
= 0;
924 producer_running
= 0;
927 rc
= pthread_join(consumer_thread
, NULL
);
929 rc
= pthread_join(producer_thread
, NULL
);
933 void player_stop(void)
938 __player_status_changed();
942 void player_play(void)
947 if (producer_status
== PS_PLAYING
&& ip_is_remote(ip
)) {
948 /* seeking not allowed */
952 prebuffer
= consumer_status
== CS_STOPPED
;
954 if (producer_status
== PS_PLAYING
) {
956 if (consumer_status
!= CS_PLAYING
)
961 __player_status_changed();
962 if (consumer_status
== CS_PLAYING
&& prebuffer
)
967 void player_pause(void)
971 if (consumer_status
== CS_STOPPED
) {
973 if (producer_status
== PS_PLAYING
) {
975 if (consumer_status
!= CS_PLAYING
)
978 __player_status_changed();
979 if (consumer_status
== CS_PLAYING
)
985 if (ip
&& ip_is_remote(ip
)) {
986 /* pausing not allowed */
992 __player_status_changed();
996 void player_set_file(struct track_info
*ti
)
999 __producer_set_file(ti
);
1000 if (producer_status
== PS_UNLOADED
) {
1006 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1008 if (producer_status
== PS_UNLOADED
) {
1012 change_sf(ip_get_sf(ip
), 1);
1015 __player_status_changed();
1016 if (producer_status
== PS_PLAYING
)
1021 void player_play_file(struct track_info
*ti
)
1024 __producer_set_file(ti
);
1025 if (producer_status
== PS_UNLOADED
) {
1033 /* PS_UNLOADED,PS_PLAYING */
1034 if (producer_status
== PS_UNLOADED
) {
1040 if (consumer_status
== CS_STOPPED
) {
1042 if (consumer_status
== CS_STOPPED
)
1045 change_sf(ip_get_sf(ip
), 1);
1048 __player_status_changed();
1049 if (producer_status
== PS_PLAYING
)
1054 void player_seek(double offset
, int relative
)
1057 if (consumer_status
== CS_STOPPED
) {
1059 if (producer_status
== PS_PLAYING
) {
1061 if (consumer_status
!= CS_PLAYING
) {
1066 __player_status_changed();
1069 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1070 double pos
, duration
, new_pos
;
1073 pos
= (double)consumer_pos
/ (double)buffer_second_size();
1074 duration
= ip_duration(ip
);
1077 d_print("can't seek\n");
1082 new_pos
= pos
+ offset
;
1086 /* seeking forward */
1087 if (new_pos
> duration
- 5.0)
1088 new_pos
= duration
- 5.0;
1091 if (new_pos
< pos
- 0.5) {
1092 /* must seek at least 0.5s */
1093 d_print("must seek at least 0.5s\n");
1100 if (new_pos
< 0.0) {
1101 d_print("seek offset negative\n");
1105 if (new_pos
> duration
- 5.0) {
1106 new_pos
= duration
- 5.0;
1111 /* d_print("seeking %g/%g (%g from eof)\n", new_pos, duration, duration - new_pos); */
1112 rc
= ip_seek(ip
, new_pos
);
1114 /* d_print("doing op_drop after seek\n"); */
1117 consumer_pos
= new_pos
* buffer_second_size();
1118 scale_pos
= consumer_pos
;
1119 __consumer_position_update();
1121 d_print("error: ip_seek returned %d\n", rc
);
1128 * change output plugin without stopping playback
1130 void player_set_op(const char *name
)
1136 /* drop needed because close drains the buffer */
1137 if (consumer_status
== CS_PAUSED
)
1140 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
)
1144 d_print("setting op to '%s'\n", name
);
1145 rc
= op_select(name
);
1147 /* first initialized plugin */
1148 d_print("selecting first initialized op\n");
1149 rc
= op_select_any();
1152 consumer_status
= CS_STOPPED
;
1155 player_op_error(rc
, "selecting output plugin '%s'", name
);
1160 if (consumer_status
== CS_PLAYING
|| consumer_status
== CS_PAUSED
) {
1161 set_buffer_sf(ip_get_sf(ip
));
1162 rc
= op_open(buffer_sf
);
1164 consumer_status
= CS_STOPPED
;
1166 player_op_error(rc
, "opening audio device");
1170 if (consumer_status
== CS_PAUSED
)
1177 void player_set_buffer_chunks(unsigned int nr_chunks
)
1188 buffer_nr_chunks
= nr_chunks
;
1191 __player_status_changed();
1195 int player_get_buffer_chunks(void)
1197 return buffer_nr_chunks
;
1200 void player_set_soft_volume(int l
, int r
)
1208 void player_set_soft_vol(int soft
)
1211 /* don't mess with scale_pos if soft_vol or replaygain is already enabled */
1212 if (!soft_vol
&& !replaygain
)
1213 scale_pos
= consumer_pos
;
1218 void player_set_rg(enum replaygain rg
)
1221 /* don't mess with scale_pos if soft_vol or replaygain is already enabled */
1222 if (!soft_vol
&& !replaygain
)
1223 scale_pos
= consumer_pos
;
1228 player_info_unlock();
1233 void player_set_rg_limit(int limit
)
1236 replaygain_limit
= limit
;
1240 player_info_unlock();
1245 void player_set_rg_preamp(double db
)
1248 replaygain_preamp
= db
;
1252 player_info_unlock();