cmus-remote: Read command results
[cmus.git] / player.c
blob284645d323dab02999ad977d1bfba9781e5100a6
1 /*
2 * Copyright 2004-2006 Timo Hirvonen
3 *
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
17 * 02111-1307, USA.
20 #include "player.h"
21 #include "buffer.h"
22 #include "input.h"
23 #include "output.h"
24 #include "sf.h"
25 #include "utils.h"
26 #include "xmalloc.h"
27 #include "debug.h"
28 #include "compiler.h"
30 #include <stdlib.h>
31 #include <pthread.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/time.h>
35 #include <stdarg.h>
36 #include <math.h>
38 enum producer_status {
39 PS_UNLOADED,
40 PS_STOPPED,
41 PS_PLAYING,
42 PS_PAUSED
45 enum consumer_status {
46 CS_STOPPED,
47 CS_PLAYING,
48 CS_PAUSED
51 struct player_info player_info = {
52 .mutex = CMUS_MUTEX_INITIALIZER,
53 .ti = NULL,
54 .metadata = { 0, },
55 .status = PLAYER_STATUS_STOPPED,
56 .pos = 0,
57 .buffer_fill = 0,
58 .buffer_size = 0,
59 .error_msg = NULL,
60 .file_changed = 0,
61 .metadata_changed = 0,
62 .status_changed = 0,
63 .position_changed = 0,
64 .buffer_fill_changed = 0,
67 /* continue playing after track is finished? */
68 int player_cont = 1;
70 enum replaygain replaygain;
71 int replaygain_limit = 1;
72 double replaygain_preamp = 6.0;
74 int soft_vol;
75 int soft_vol_l;
76 int soft_vol_r;
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;
100 /* locking {{{ */
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() \
109 do { \
110 consumer_lock(); \
111 producer_lock(); \
112 } while (0)
114 #define player_unlock() \
115 do { \
116 producer_unlock(); \
117 consumer_unlock(); \
118 } while (0)
120 /* locking }}} */
122 static void reset_buffer(void)
124 buffer_reset();
125 consumer_pos = 0;
126 scale_pos = 0;
129 static void set_buffer_sf(sample_format_t sf)
131 buffer_sf = 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]);
170 #else
171 int sample = buf[i];
172 #endif
174 if (sample < 0) {
175 sample = (sample * vol - SOFT_VOL_SCALE / 2) / SOFT_VOL_SCALE;
176 if (sample < -32768)
177 sample = -32768;
178 } else {
179 sample = (sample * vol + SOFT_VOL_SCALE / 2) / SOFT_VOL_SCALE;
180 if (sample > 32767)
181 sample = 32767;
183 #ifdef WORDS_BIGENDIAN
184 buf[i] = swap16(sample);
185 #else
186 buf[i] = sample;
187 #endif
190 static void scale_samples(char *buffer, unsigned int *countp)
192 signed short *buf;
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;
201 if (offs >= count)
202 return;
203 buffer += offs;
204 count -= offs;
206 scale_pos += count;
207 buf = (signed short *)buffer;
209 if (replaygain_scale == 1.0 && soft_vol_l == 100 && soft_vol_r == 100)
210 return;
212 ch = sf_get_channels(buffer_sf);
213 bits = sf_get_bits(buffer_sf);
214 if (ch != 2 || bits != 16)
215 return;
217 l = SOFT_VOL_SCALE;
218 r = SOFT_VOL_SCALE;
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)
244 char *end;
246 *val = strtod(str, &end);
247 return str == end;
250 static void update_rg_scale(void)
252 const char *g, *p;
253 double gain, peak, db, scale, limit;
255 replaygain_scale = 1.0;
256 if (!player_info.ti || !replaygain)
257 return;
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");
262 } else {
263 g = comments_get_val(player_info.ti->comments, "replaygain_album_gain");
264 p = comments_get_val(player_info.ti->comments, "replaygain_album_peak");
267 if (!g || !p) {
268 d_print("gain or peak not available\n");
269 return;
271 if (parse_double(g, &gain) || parse_double(p, &peak)) {
272 d_print("could not parse gain (%s) or peak (%s)\n", g, p);
273 return;
275 if (peak < 0.05) {
276 d_print("peak (%g) is too small\n", peak);
277 return;
280 db = replaygain_preamp + gain;
282 scale = pow(10.0, db / 20.0);
283 replaygain_scale = scale;
284 limit = 1.0 / peak;
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)
306 player_info_lock();
307 if (player_info.ti)
308 track_info_unref(player_info.ti);
310 player_info.ti = ti;
311 if (ti) {
312 d_print("file: %s\n", ti->filename);
313 } else {
314 d_print("unloaded\n");
316 update_rg_scale();
317 player_info.metadata[0] = 0;
318 player_info.file_changed = 1;
319 player_info_unlock();
322 static inline void metadata_changed(void)
324 player_info_lock();
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)
333 player_info_lock();
334 player_info.status = consumer_status;
335 player_info.pos = 0;
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, ...)
349 char buffer[1024];
350 va_list ap;
351 char *msg;
352 int save = errno;
354 va_start(ap, format);
355 vsnprintf(buffer, sizeof(buffer), format, ap);
356 va_end(ap);
358 errno = save;
359 msg = ip_get_error_msg(ip, rc, buffer);
360 player_error(msg);
361 free(msg);
364 static void __FORMAT(2, 3) player_op_error(int rc, const char *format, ...)
366 char buffer[1024];
367 va_list ap;
368 char *msg;
369 int save = errno;
371 va_start(ap, format);
372 vsnprintf(buffer, sizeof(buffer), format, ap);
373 va_end(ap);
375 errno = save;
376 msg = op_get_error_msg(rc, buffer);
377 player_error(msg);
378 free(msg);
382 * buffer-fill changed
384 static void __producer_buffer_fill_update(void)
386 int fill;
388 player_info_lock();
389 fill = buffer_get_filled_chunks();
390 if (fill != player_info.buffer_fill) {
391 /* d_print("\n"); */
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) {
409 /* d_print("\n"); */
410 old_pos = pos;
412 player_info_lock();
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;
426 /* d_print("\n"); */
427 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED)
428 pos = consumer_pos / buffer_second_size();
430 player_info_lock();
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)
443 int limit_chunks;
445 BUG_ON(producer_status != PS_PLAYING);
446 if (ip_is_remote(ip)) {
447 limit_chunks = buffer_nr_chunks;
448 } else {
449 int limit_ms, limit_size;
451 limit_ms = 250;
452 limit_size = limit_ms * buffer_second_size() / 1000;
453 limit_chunks = limit_size / CHUNK_SIZE;
454 if (limit_chunks < 1)
455 limit_chunks = 1;
457 while (1) {
458 int nr_read, size, filled;
459 char *wpos;
461 filled = buffer_get_filled_chunks();
462 /* d_print("PREBUF: %2d / %2d\n", filled, limit_chunks); */
464 /* not fatal */
465 //BUG_ON(filled > limit_chunks);
467 if (filled >= limit_chunks)
468 break;
470 size = buffer_get_wpos(&wpos);
471 nr_read = ip_read(ip, wpos, size);
472 if (nr_read < 0) {
473 if (nr_read == -1 && errno == EAGAIN)
474 continue;
475 player_ip_error(nr_read, "reading file %s", ip_get_filename(ip));
476 /* ip_read sets eof */
477 nr_read = 0;
479 if (ip_metadata_changed(ip))
480 metadata_changed();
482 /* buffer_fill with 0 count marks current chunk filled */
483 buffer_fill(nr_read);
485 __producer_buffer_fill_update();
486 if (nr_read == 0) {
487 /* EOF */
488 break;
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) {
501 int rc;
503 ip = ip_new(ti->filename);
504 rc = ip_open(ip);
505 if (rc) {
506 player_ip_error(rc, "opening file `%s'", ti->filename);
507 ip_delete(ip);
508 track_info_unref(ti);
509 file_changed(NULL);
510 } else {
511 ip_setup(ip);
512 producer_status = PS_PLAYING;
513 file_changed(ti);
516 } else if (producer_status == PS_PLAYING) {
517 if (ip_seek(ip, 0.0) == 0) {
518 reset_buffer();
520 } else if (producer_status == PS_STOPPED) {
521 int rc;
523 rc = ip_open(ip);
524 if (rc) {
525 player_ip_error(rc, "opening file `%s'", ip_get_filename(ip));
526 ip_delete(ip);
527 producer_status = PS_UNLOADED;
528 } else {
529 ip_setup(ip);
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) {
540 ip_close(ip);
541 producer_status = PS_STOPPED;
542 reset_buffer();
546 static void __producer_unload(void)
548 __producer_stop();
549 if (producer_status == PS_STOPPED) {
550 ip_delete(ip);
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)
566 __producer_unload();
567 ip = ip_new(ti->filename);
568 producer_status = PS_STOPPED;
569 file_changed(ti);
572 /* setting producer status }}} */
574 /* setting consumer status {{{ */
576 static void __consumer_play(void)
578 if (consumer_status == CS_PLAYING) {
579 op_drop();
580 } else if (consumer_status == CS_STOPPED) {
581 int rc;
583 set_buffer_sf(ip_get_sf(ip));
584 rc = op_open(buffer_sf);
585 if (rc) {
586 player_op_error(rc, "opening audio device");
587 } else {
588 consumer_status = CS_PLAYING;
590 } else if (consumer_status == CS_PAUSED) {
591 op_unpause();
592 consumer_status = CS_PLAYING;
596 static void __consumer_drain_and_stop(void)
598 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
599 op_close();
600 consumer_status = CS_STOPPED;
604 static void __consumer_stop(void)
606 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
607 op_drop();
608 op_close();
609 consumer_status = CS_STOPPED;
613 static void __consumer_pause(void)
615 if (consumer_status == CS_PLAYING) {
616 op_pause();
617 consumer_status = CS_PAUSED;
618 } else if (consumer_status == CS_PAUSED) {
619 op_unpause();
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;
630 set_buffer_sf(sf);
631 if (buffer_sf != old_sf) {
632 /* reopen */
633 int rc;
635 if (drop)
636 op_drop();
637 op_close();
638 rc = op_open(buffer_sf);
639 if (rc) {
640 player_op_error(rc, "opening audio device");
641 consumer_status = CS_STOPPED;
642 __producer_stop();
643 return rc;
645 } else if (consumer_status == CS_PAUSED) {
646 op_drop();
647 op_unpause();
649 consumer_status = CS_PLAYING;
650 return 0;
653 static void __consumer_handle_eof(void)
655 struct track_info *ti;
657 if (ip_is_remote(ip)) {
658 __producer_stop();
659 __consumer_drain_and_stop();
660 player_error("lost connection");
661 return;
664 if (get_next(&ti) == 0) {
665 __producer_unload();
666 ip = ip_new(ti->filename);
667 producer_status = PS_STOPPED;
668 /* PS_STOPPED, CS_PLAYING */
669 if (player_cont) {
670 __producer_play();
671 if (producer_status == PS_UNLOADED) {
672 __consumer_stop();
673 track_info_unref(ti);
674 file_changed(NULL);
675 } else {
676 /* PS_PLAYING */
677 file_changed(ti);
678 if (!change_sf(ip_get_sf(ip), 0))
679 __prebuffer();
681 } else {
682 __consumer_drain_and_stop();
683 file_changed(ti);
685 } else {
686 __producer_unload();
687 __consumer_drain_and_stop();
688 file_changed(NULL);
690 __player_status_changed();
693 static void *consumer_loop(void *arg)
695 while (1) {
696 int rc, space;
697 int size;
698 char *rpos;
700 consumer_lock();
701 if (!consumer_running)
702 break;
704 if (consumer_status == CS_PAUSED || consumer_status == CS_STOPPED) {
705 consumer_unlock();
706 ms_sleep(50);
707 continue;
709 space = op_buffer_space();
710 if (space == -1) {
711 /* busy */
712 __consumer_position_update();
713 consumer_unlock();
714 ms_sleep(50);
715 continue;
717 /* d_print("BS: %6d %3d\n", space, space * 1000 / (44100 * 2 * 2)); */
719 while (1) {
720 /* 25 ms is 4410 B */
721 if (space < 4096) {
722 __consumer_position_update();
723 consumer_unlock();
724 ms_sleep(25);
725 break;
727 size = buffer_get_rpos(&rpos);
728 if (size == 0) {
729 producer_lock();
730 if (producer_status != PS_PLAYING) {
731 producer_unlock();
732 consumer_unlock();
733 break;
735 /* must recheck rpos */
736 size = buffer_get_rpos(&rpos);
737 if (size == 0) {
738 /* OK. now it's safe to check if we are at EOF */
739 if (ip_eof(ip)) {
740 /* EOF */
741 __consumer_handle_eof();
742 producer_unlock();
743 consumer_unlock();
744 break;
745 } else {
746 /* possible underrun */
747 producer_unlock();
748 __consumer_position_update();
749 consumer_unlock();
750 /* d_print("possible underrun\n"); */
751 ms_sleep(10);
752 break;
756 /* player_buffer and ip.eof were inconsistent */
757 producer_unlock();
759 if (size > space)
760 size = space;
761 if (soft_vol || replaygain)
762 scale_samples(rpos, &size);
763 rc = op_write(rpos, size);
764 if (rc < 0) {
765 d_print("op_write returned %d %s\n", rc,
766 rc == -1 ? strerror(errno) : "");
768 /* try to reopen */
769 op_close();
770 consumer_status = CS_STOPPED;
771 __consumer_play();
773 consumer_unlock();
774 break;
776 buffer_consume(rc);
777 consumer_pos += rc;
778 space -= rc;
781 __consumer_stop();
782 consumer_unlock();
783 return NULL;
786 static void *producer_loop(void *arg)
788 while (1) {
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;
795 char *wpos;
797 producer_lock();
798 if (!producer_running)
799 break;
801 if (producer_status == PS_UNLOADED ||
802 producer_status == PS_PAUSED ||
803 producer_status == PS_STOPPED || ip_eof(ip)) {
804 producer_unlock();
805 ms_sleep(50);
806 continue;
808 for (i = 0; ; i++) {
809 size = buffer_get_wpos(&wpos);
810 if (size == 0) {
811 /* buffer is full */
812 producer_unlock();
813 ms_sleep(50);
814 break;
816 nr_read = ip_read(ip, wpos, size);
817 if (nr_read < 0) {
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 */
822 nr_read = 0;
823 } else {
824 producer_unlock();
825 ms_sleep(50);
826 break;
829 if (ip_metadata_changed(ip))
830 metadata_changed();
832 /* buffer_fill with 0 count marks current chunk filled */
833 buffer_fill(nr_read);
834 if (nr_read == 0) {
835 /* consumer handles EOF */
836 producer_unlock();
837 ms_sleep(50);
838 break;
840 if (i == chunks) {
841 producer_unlock();
842 /* don't sleep! */
843 break;
846 __producer_buffer_fill_update();
848 __producer_unload();
849 producer_unlock();
850 return NULL;
853 void player_load_plugins(void)
855 ip_load_plugins();
856 op_load_plugins();
859 void player_init(const struct player_callbacks *callbacks)
861 int rc;
862 #if defined(__linux__) || defined(__FreeBSD__)
863 pthread_attr_t attr;
864 #endif
865 pthread_attr_t *attrp = NULL;
867 /* 1 s is 176400 B (0.168 MB)
868 * 10 s is 1.68 MB
870 buffer_nr_chunks = 10 * 44100 * 16 / 8 * 2 / CHUNK_SIZE;
871 buffer_init();
873 player_cbs = callbacks;
875 #if defined(__linux__) || defined(__FreeBSD__)
876 rc = pthread_attr_init(&attr);
877 BUG_ON(rc);
878 rc = pthread_attr_setschedpolicy(&attr, SCHED_RR);
879 if (rc) {
880 d_print("could not set real-time scheduling priority: %s\n", strerror(rc));
881 } else {
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, &param);
888 BUG_ON(rc);
889 attrp = &attr;
891 #endif
893 rc = pthread_create(&producer_thread, NULL, producer_loop, NULL);
894 BUG_ON(rc);
896 rc = pthread_create(&consumer_thread, attrp, consumer_loop, NULL);
897 if (rc && attrp) {
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);
901 BUG_ON(rc);
903 /* update player_info.cont etc. */
904 player_lock();
905 __player_status_changed();
906 player_unlock();
909 void player_exit(void)
911 int rc;
913 player_lock();
914 consumer_running = 0;
915 producer_running = 0;
916 player_unlock();
918 rc = pthread_join(consumer_thread, NULL);
919 BUG_ON(rc);
920 rc = pthread_join(producer_thread, NULL);
921 BUG_ON(rc);
923 op_exit_plugins();
926 void player_stop(void)
928 player_lock();
929 __consumer_stop();
930 __producer_stop();
931 __player_status_changed();
932 player_unlock();
935 void player_play(void)
937 int prebuffer;
939 player_lock();
940 if (producer_status == PS_PLAYING && ip_is_remote(ip)) {
941 /* seeking not allowed */
942 player_unlock();
943 return;
945 prebuffer = consumer_status == CS_STOPPED;
946 __producer_play();
947 if (producer_status == PS_PLAYING) {
948 __consumer_play();
949 if (consumer_status != CS_PLAYING)
950 __producer_stop();
951 } else {
952 __consumer_stop();
954 __player_status_changed();
955 if (consumer_status == CS_PLAYING && prebuffer)
956 __prebuffer();
957 player_unlock();
960 void player_pause(void)
962 player_lock();
964 if (consumer_status == CS_STOPPED) {
965 __producer_play();
966 if (producer_status == PS_PLAYING) {
967 __consumer_play();
968 if (consumer_status != CS_PLAYING)
969 __producer_stop();
971 __player_status_changed();
972 if (consumer_status == CS_PLAYING)
973 __prebuffer();
974 player_unlock();
975 return;
978 if (ip && ip_is_remote(ip)) {
979 /* pausing not allowed */
980 player_unlock();
981 return;
983 __producer_pause();
984 __consumer_pause();
985 __player_status_changed();
986 player_unlock();
989 void player_set_file(struct track_info *ti)
991 player_lock();
992 __producer_set_file(ti);
993 if (producer_status == PS_UNLOADED) {
994 __consumer_stop();
995 goto out;
998 /* PS_STOPPED */
999 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
1000 __producer_play();
1001 if (producer_status == PS_UNLOADED) {
1002 __consumer_stop();
1003 goto out;
1005 change_sf(ip_get_sf(ip), 1);
1007 out:
1008 __player_status_changed();
1009 if (producer_status == PS_PLAYING)
1010 __prebuffer();
1011 player_unlock();
1014 void player_play_file(struct track_info *ti)
1016 player_lock();
1017 __producer_set_file(ti);
1018 if (producer_status == PS_UNLOADED) {
1019 __consumer_stop();
1020 goto out;
1023 /* PS_STOPPED */
1024 __producer_play();
1026 /* PS_UNLOADED,PS_PLAYING */
1027 if (producer_status == PS_UNLOADED) {
1028 __consumer_stop();
1029 goto out;
1032 /* PS_PLAYING */
1033 if (consumer_status == CS_STOPPED) {
1034 __consumer_play();
1035 if (consumer_status == CS_STOPPED)
1036 __producer_stop();
1037 } else {
1038 change_sf(ip_get_sf(ip), 1);
1040 out:
1041 __player_status_changed();
1042 if (producer_status == PS_PLAYING)
1043 __prebuffer();
1044 player_unlock();
1047 void player_seek(double offset, int relative)
1049 player_lock();
1050 if (consumer_status == CS_STOPPED) {
1051 __producer_play();
1052 if (producer_status == PS_PLAYING) {
1053 __consumer_play();
1054 if (consumer_status != CS_PLAYING) {
1055 __producer_stop();
1056 player_unlock();
1057 return;
1058 } else
1059 __player_status_changed();
1062 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
1063 double pos, duration, new_pos;
1064 int rc;
1066 pos = (double)consumer_pos / (double)buffer_second_size();
1067 duration = ip_duration(ip);
1068 if (duration < 0) {
1069 /* can't seek */
1070 d_print("can't seek\n");
1071 player_unlock();
1072 return;
1074 if (relative) {
1075 new_pos = pos + offset;
1076 if (new_pos < 0.0)
1077 new_pos = 0.0;
1078 if (offset > 0.0) {
1079 /* seeking forward */
1080 if (new_pos > duration - 5.0)
1081 new_pos = duration - 5.0;
1082 if (new_pos < 0.0)
1083 new_pos = 0.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");
1087 player_unlock();
1088 return;
1091 } else {
1092 new_pos = offset;
1093 if (new_pos < 0.0) {
1094 d_print("seek offset negative\n");
1095 player_unlock();
1096 return;
1098 if (new_pos > duration - 5.0) {
1099 new_pos = duration - 5.0;
1100 if (new_pos < 0.0)
1101 new_pos = 0.0;
1104 /* d_print("seeking %g/%g (%g from eof)\n", new_pos, duration, duration - new_pos); */
1105 rc = ip_seek(ip, new_pos);
1106 if (rc == 0) {
1107 /* d_print("doing op_drop after seek\n"); */
1108 op_drop();
1109 reset_buffer();
1110 consumer_pos = new_pos * buffer_second_size();
1111 scale_pos = consumer_pos;
1112 __consumer_position_update();
1113 } else {
1114 d_print("error: ip_seek returned %d\n", rc);
1117 player_unlock();
1121 * change output plugin without stopping playback
1123 void player_set_op(const char *name)
1125 int rc;
1127 player_lock();
1129 /* drop needed because close drains the buffer */
1130 if (consumer_status == CS_PAUSED)
1131 op_drop();
1133 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED)
1134 op_close();
1136 if (name) {
1137 d_print("setting op to '%s'\n", name);
1138 rc = op_select(name);
1139 } else {
1140 /* first initialized plugin */
1141 d_print("selecting first initialized op\n");
1142 rc = op_select_any();
1144 if (rc) {
1145 consumer_status = CS_STOPPED;
1147 __producer_stop();
1148 player_op_error(rc, "selecting output plugin '%s'", name);
1149 player_unlock();
1150 return;
1153 if (consumer_status == CS_PLAYING || consumer_status == CS_PAUSED) {
1154 set_buffer_sf(ip_get_sf(ip));
1155 rc = op_open(buffer_sf);
1156 if (rc) {
1157 consumer_status = CS_STOPPED;
1158 __producer_stop();
1159 player_op_error(rc, "opening audio device");
1160 player_unlock();
1161 return;
1163 if (consumer_status == CS_PAUSED)
1164 op_pause();
1167 player_unlock();
1170 char *player_get_op(void)
1172 return op_get_current();
1175 void player_set_buffer_chunks(unsigned int nr_chunks)
1177 if (nr_chunks < 3)
1178 nr_chunks = 3;
1179 if (nr_chunks > 30)
1180 nr_chunks = 30;
1182 player_lock();
1183 __producer_stop();
1184 __consumer_stop();
1186 buffer_nr_chunks = nr_chunks;
1187 buffer_init();
1189 __player_status_changed();
1190 player_unlock();
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;
1202 int rc;
1204 *comments = NULL;
1205 *duration = -1;
1206 plug = ip_new(filename);
1207 if (ip_is_remote(plug)) {
1208 *comments = xnew0(struct keyval, 1);
1209 ip_delete(plug);
1210 return 0;
1212 rc = ip_open(plug);
1213 if (rc) {
1214 int save = errno;
1216 ip_delete(plug);
1217 errno = save;
1218 if (rc != -1)
1219 rc = -PLAYER_ERROR_NOT_SUPPORTED;
1220 return rc;
1222 *duration = ip_duration(plug);
1223 rc = ip_read_comments(plug, comments);
1224 ip_delete(plug);
1225 return rc;
1228 void player_set_soft_volume(int l, int r)
1230 consumer_lock();
1231 soft_vol_l = l;
1232 soft_vol_r = r;
1233 consumer_unlock();
1236 void player_set_soft_vol(int soft)
1238 consumer_lock();
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;
1242 soft_vol = soft;
1243 consumer_unlock();
1246 void player_set_rg(enum replaygain rg)
1248 player_lock();
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;
1252 replaygain = rg;
1254 player_info_lock();
1255 update_rg_scale();
1256 player_info_unlock();
1258 player_unlock();
1261 void player_set_rg_limit(int limit)
1263 player_lock();
1264 replaygain_limit = limit;
1266 player_info_lock();
1267 update_rg_scale();
1268 player_info_unlock();
1270 player_unlock();
1273 void player_set_rg_preamp(double db)
1275 player_lock();
1276 replaygain_preamp = db;
1278 player_info_lock();
1279 update_rg_scale();
1280 player_info_unlock();
1282 player_unlock();
1285 int player_set_op_option(unsigned int id, const char *val)
1287 int rc;
1289 player_lock();
1290 __consumer_stop();
1291 __producer_stop();
1292 rc = op_set_option(id, val);
1293 __player_status_changed();
1294 player_unlock();
1295 return rc;
1298 int player_get_op_option(unsigned int id, char **val)
1300 int rc;
1302 player_lock();
1303 rc = op_get_option(id, val);
1304 player_unlock();
1305 return rc;
1308 int player_for_each_op_option(void (*callback)(unsigned int id, const char *key))
1310 player_lock();
1311 __consumer_stop();
1312 __producer_stop();
1313 op_for_each_option(callback);
1314 __player_status_changed();
1315 player_unlock();
1316 return 0;
1319 void player_dump_plugins(void)
1321 ip_dump_plugins();
1322 op_dump_plugins();