XMMS2
output.c
Go to the documentation of this file.
1/* XMMS2 - X Music Multiplexer System
2 * Copyright (C) 2003-2011 XMMS2 Team
3 *
4 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 */
16
17/**
18 * @file
19 * Output plugin helper
20 */
21
22#include <string.h>
23#include <unistd.h>
24
28#include "xmmspriv/xmms_xform.h"
33#include "xmms/xmms_log.h"
34#include "xmms/xmms_ipc.h"
35#include "xmms/xmms_object.h"
36#include "xmms/xmms_config.h"
37
38#define VOLUME_MAX_CHANNELS 128
39
40typedef struct xmms_volume_map_St {
41 const gchar **names;
42 guint *values;
43 guint num_channels;
44 gboolean status;
46
47static gboolean xmms_output_format_set (xmms_output_t *output, xmms_stream_type_t *fmt);
48static gpointer xmms_output_monitor_volume_thread (gpointer data);
49
50static void xmms_playback_client_start (xmms_output_t *output, xmms_error_t *err);
51static void xmms_playback_client_stop (xmms_output_t *output, xmms_error_t *err);
52static void xmms_playback_client_pause (xmms_output_t *output, xmms_error_t *err);
53static void xmms_playback_client_tickle (xmms_output_t *output, xmms_error_t *err);
54static void xmms_playback_client_seek_ms (xmms_output_t *output, gint32 ms, gint32 whence, xmms_error_t *error);
55static void xmms_playback_client_seek_samples (xmms_output_t *output, gint32 samples, gint32 whence, xmms_error_t *error);
56static gint32 xmms_playback_client_status (xmms_output_t *output, xmms_error_t *error);
57static gint xmms_playback_client_current_id (xmms_output_t *output, xmms_error_t *error);
58static gint32 xmms_playback_client_playtime (xmms_output_t *output, xmms_error_t *err);
59
67
68static void xmms_playback_client_volume_set (xmms_output_t *output, const gchar *channel, gint32 volume, xmms_error_t *error);
69static GTree *xmms_playback_client_volume_get (xmms_output_t *output, xmms_error_t *error);
70static void xmms_output_filler_state (xmms_output_t *output, xmms_output_filler_state_t state);
71static void xmms_output_filler_state_nolock (xmms_output_t *output, xmms_output_filler_state_t state);
72
73static void xmms_volume_map_init (xmms_volume_map_t *vl);
74static void xmms_volume_map_free (xmms_volume_map_t *vl);
75static void xmms_volume_map_copy (xmms_volume_map_t *src, xmms_volume_map_t *dst);
76static GTree *xmms_volume_map_to_dict (xmms_volume_map_t *vl);
77static gboolean xmms_output_status_set (xmms_output_t *output, gint status);
78static gboolean set_plugin (xmms_output_t *output, xmms_output_plugin_t *plugin);
79
80static void xmms_output_format_list_free_elem (gpointer data, gpointer user_data);
81static void xmms_output_format_list_clear (xmms_output_t *output);
83
84#include "output_ipc.c"
85
86/*
87 * Type definitions
88 */
89
90/** @defgroup Output Output
91 * @ingroup XMMSServer
92 * @brief Output is responsible to put the decoded data on
93 * the soundcard.
94 * @{
95 */
96
97/*
98 *
99 * locking order: status_mutex > write_mutex
100 * filler_mutex
101 * playtime_mutex is leaflock.
102 */
103
104struct xmms_output_St {
105 xmms_object_t object;
106
107 xmms_output_plugin_t *plugin;
108 gpointer plugin_data;
109
110 /* */
111 GMutex *playtime_mutex;
112 guint played;
113 guint played_time;
114 xmms_medialib_entry_t current_entry;
115 guint toskip;
116
117 /* */
118 GThread *filler_thread;
119 GMutex *filler_mutex;
120
121 GCond *filler_state_cond;
122 xmms_output_filler_state_t filler_state;
123
124 xmms_ringbuf_t *filler_buffer;
125 guint32 filler_seek;
126 gint filler_skip;
127
128 /** Internal status, tells which state the
129 output really is in */
130 GMutex *status_mutex;
131 guint status;
132
133 xmms_playlist_t *playlist;
134
135 /** Supported formats */
136 GList *format_list;
137 /** Active format */
138 xmms_stream_type_t *format;
139
140 /**
141 * Number of bytes totaly written to output driver,
142 * this is only for statistics...
143 */
144 guint64 bytes_written;
145
146 /**
147 * How many times didn't we have enough data in the buffer?
148 */
149 gint32 buffer_underruns;
150
151 GThread *monitor_volume_thread;
152 gboolean monitor_volume_running;
153};
154
155/** @} */
156
157/*
158 * Public functions
159 */
160
161gpointer
163{
164 g_return_val_if_fail (output, NULL);
165 g_return_val_if_fail (output->plugin, NULL);
166
167 return output->plugin_data;
168}
169
170void
172{
173 g_return_if_fail (output);
174 g_return_if_fail (output->plugin);
175
176 output->plugin_data = data;
177}
178
179void
181{
183 va_list ap;
184
185 va_start (ap, output);
186 f = xmms_stream_type_parse (ap);
187 va_end (ap);
188
189 g_return_if_fail (f);
190
191 output->format_list = g_list_append (output->format_list, f);
192}
193
194static void
195xmms_output_format_list_free_elem (gpointer data, gpointer user_data)
196{
198
199 g_return_if_fail (data);
200
201 f = data;
202
204}
205
206static void
207xmms_output_format_list_clear(xmms_output_t *output)
208{
209 if (output->format_list == NULL)
210 return;
211
212 g_list_foreach (output->format_list,
213 xmms_output_format_list_free_elem,
214 NULL);
215
216 g_list_free (output->format_list);
217 output->format_list = NULL;
218}
219
220static void
221update_playtime (xmms_output_t *output, int advance)
222{
223 guint buffersize = 0;
224
225 g_mutex_lock (output->playtime_mutex);
226 output->played += advance;
227 g_mutex_unlock (output->playtime_mutex);
228
229 buffersize = xmms_output_plugin_method_latency_get (output->plugin, output);
230
231 if (output->played < buffersize) {
232 buffersize = output->played;
233 }
234
235 g_mutex_lock (output->playtime_mutex);
236
237 if (output->format) {
238 guint ms = xmms_sample_bytes_to_ms (output->format,
239 output->played - buffersize);
240 if ((ms / 100) != (output->played_time / 100)) {
244 ms);
245 }
246 output->played_time = ms;
247
248 }
249
250 g_mutex_unlock (output->playtime_mutex);
251
252}
253
254void
256{
257 g_return_if_fail (output);
258
259 xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_STOP);
260
261 if (error) {
262 xmms_log_error ("Output plugin %s reported error, '%s'",
263 xmms_plugin_shortname_get ((xmms_plugin_t *)output->plugin),
264 xmms_error_message_get (error));
265 }
266}
267
268typedef struct {
269 xmms_output_t *output;
270 xmms_xform_t *chain;
271 gboolean flush;
272} xmms_output_song_changed_arg_t;
273
274static void
275song_changed_arg_free (void *data)
276{
277 xmms_output_song_changed_arg_t *arg = (xmms_output_song_changed_arg_t *)data;
278 xmms_object_unref (arg->chain);
279 g_free (arg);
280}
281
282static gboolean
283song_changed (void *data)
284{
285 /* executes in the output thread; NOT the filler thread */
286 xmms_output_song_changed_arg_t *arg = (xmms_output_song_changed_arg_t *)data;
288 xmms_stream_type_t *type;
289
290 entry = xmms_xform_entry_get (arg->chain);
291
292 XMMS_DBG ("Running hotspot! Song changed!! %d", entry);
293
294 arg->output->played = 0;
295 arg->output->current_entry = entry;
296
297 type = xmms_xform_outtype_get (arg->chain);
298
299 if (!xmms_output_format_set (arg->output, type)) {
300 gint fmt, rate, chn;
301
305
306 XMMS_DBG ("Couldn't set format %s/%d/%d, stopping filler..",
307 xmms_sample_name_get (fmt), rate, chn);
308
309 xmms_output_filler_state_nolock (arg->output, FILLER_STOP);
310 xmms_ringbuf_set_eos (arg->output->filler_buffer, TRUE);
311 return FALSE;
312 }
313
314 if (arg->flush)
315 xmms_output_flush (arg->output);
316
317 xmms_object_emit_f (XMMS_OBJECT (arg->output),
320 entry);
321
322 return TRUE;
323}
324
325static gboolean
326seek_done (void *data)
327{
328 xmms_output_t *output = (xmms_output_t *)data;
329
330 g_mutex_lock (output->playtime_mutex);
331 output->played = output->filler_seek * xmms_sample_frame_size_get (output->format);
332 output->toskip = output->filler_skip * xmms_sample_frame_size_get (output->format);
333 g_mutex_unlock (output->playtime_mutex);
334
335 xmms_output_flush (output);
336 return TRUE;
337}
338
339static void
340xmms_output_filler_state_nolock (xmms_output_t *output, xmms_output_filler_state_t state)
341{
342 output->filler_state = state;
343 g_cond_signal (output->filler_state_cond);
344 if (state == FILLER_QUIT || state == FILLER_STOP || state == FILLER_KILL) {
345 xmms_ringbuf_clear (output->filler_buffer);
346 }
347 if (state != FILLER_STOP) {
348 xmms_ringbuf_set_eos (output->filler_buffer, FALSE);
349 }
350}
351
352static void
353xmms_output_filler_state (xmms_output_t *output, xmms_output_filler_state_t state)
354{
355 g_mutex_lock (output->filler_mutex);
356 xmms_output_filler_state_nolock (output, state);
357 g_mutex_unlock (output->filler_mutex);
358}
359static void
360xmms_output_filler_seek_state (xmms_output_t *output, guint32 samples)
361{
362 g_mutex_lock (output->filler_mutex);
363 output->filler_state = FILLER_SEEK;
364 output->filler_seek = samples;
365 g_cond_signal (output->filler_state_cond);
366 g_mutex_unlock (output->filler_mutex);
367}
368
369static void *
370xmms_output_filler (void *arg)
371{
372 xmms_output_t *output = (xmms_output_t *)arg;
373 xmms_xform_t *chain = NULL;
374 gboolean last_was_kill = FALSE;
375 char buf[4096];
376 xmms_error_t err;
377 gint ret;
378
379 xmms_error_reset (&err);
380
381 xmms_set_thread_name ("x2 out filler");
382
383 g_mutex_lock (output->filler_mutex);
384 while (output->filler_state != FILLER_QUIT) {
385 if (output->filler_state == FILLER_STOP) {
386 if (chain) {
387 xmms_object_unref (chain);
388 chain = NULL;
389 }
390 xmms_ringbuf_set_eos (output->filler_buffer, TRUE);
391 g_cond_wait (output->filler_state_cond, output->filler_mutex);
392 last_was_kill = FALSE;
393 continue;
394 }
395 if (output->filler_state == FILLER_KILL) {
396 if (chain) {
397 xmms_object_unref (chain);
398 chain = NULL;
399 output->filler_state = FILLER_RUN;
400 last_was_kill = TRUE;
401 } else {
402 output->filler_state = FILLER_STOP;
403 }
404 continue;
405 }
406 if (output->filler_state == FILLER_SEEK) {
407 if (!chain) {
408 XMMS_DBG ("Seek without chain, ignoring..");
409 output->filler_state = FILLER_STOP;
410 continue;
411 }
412
413 ret = xmms_xform_this_seek (chain, output->filler_seek, XMMS_XFORM_SEEK_SET, &err);
414 if (ret == -1) {
415 XMMS_DBG ("Seeking failed: %s", xmms_error_message_get (&err));
416 } else {
417 XMMS_DBG ("Seek ok! %d", ret);
418
419 output->filler_skip = output->filler_seek - ret;
420 if (output->filler_skip < 0) {
421 XMMS_DBG ("Seeked %d samples too far! Updating position...",
422 -output->filler_skip);
423
424 output->filler_skip = 0;
425 output->filler_seek = ret;
426 }
427
428 xmms_ringbuf_clear (output->filler_buffer);
429 xmms_ringbuf_hotspot_set (output->filler_buffer, seek_done, NULL, output);
430 }
431 output->filler_state = FILLER_RUN;
432 }
433
434 if (!chain) {
436 xmms_output_song_changed_arg_t *hsarg;
438
439 g_mutex_unlock (output->filler_mutex);
440
441 entry = xmms_playlist_current_entry (output->playlist);
442 if (!entry) {
443 XMMS_DBG ("No entry from playlist!");
444 output->filler_state = FILLER_STOP;
445 g_mutex_lock (output->filler_mutex);
446 continue;
447 }
448
449 chain = xmms_xform_chain_setup (entry, output->format_list, FALSE);
450 if (!chain) {
451 session = xmms_medialib_begin_write ();
453 xmms_medialib_end (session);
455 } else {
458 xmms_medialib_end (session);
459 }
460
461 if (!xmms_playlist_advance (output->playlist)) {
462 XMMS_DBG ("End of playlist");
463 output->filler_state = FILLER_STOP;
464 }
465 g_mutex_lock (output->filler_mutex);
466 continue;
467 }
468
469 hsarg = g_new0 (xmms_output_song_changed_arg_t, 1);
470 hsarg->output = output;
471 hsarg->chain = chain;
472 hsarg->flush = last_was_kill;
473 xmms_object_ref (chain);
474
475 last_was_kill = FALSE;
476
477 g_mutex_lock (output->filler_mutex);
478 xmms_ringbuf_hotspot_set (output->filler_buffer, song_changed, song_changed_arg_free, hsarg);
479 }
480
481 xmms_ringbuf_wait_free (output->filler_buffer, sizeof (buf), output->filler_mutex);
482
483 if (output->filler_state != FILLER_RUN) {
484 XMMS_DBG ("State changed while waiting...");
485 continue;
486 }
487 g_mutex_unlock (output->filler_mutex);
488
489 ret = xmms_xform_this_read (chain, buf, sizeof (buf), &err);
490
491 g_mutex_lock (output->filler_mutex);
492
493 if (ret > 0) {
494 gint skip = MIN (ret, output->toskip);
495
496 output->toskip -= skip;
497 if (ret > skip) {
498 xmms_ringbuf_write_wait (output->filler_buffer,
499 buf + skip,
500 ret - skip,
501 output->filler_mutex);
502 }
503 } else {
504 if (ret == -1) {
505 /* print error */
506 xmms_error_reset (&err);
507 }
508 xmms_object_unref (chain);
509 chain = NULL;
510 if (!xmms_playlist_advance (output->playlist)) {
511 XMMS_DBG ("End of playlist");
512 output->filler_state = FILLER_STOP;
513 }
514 }
515
516 }
517 g_mutex_unlock (output->filler_mutex);
518 return NULL;
519}
520
521gint
522xmms_output_read (xmms_output_t *output, char *buffer, gint len)
523{
524 gint ret;
525 xmms_error_t err;
526
527 xmms_error_reset (&err);
528
529 g_return_val_if_fail (output, -1);
530 g_return_val_if_fail (buffer, -1);
531
532 g_mutex_lock (output->filler_mutex);
533 xmms_ringbuf_wait_used (output->filler_buffer, len, output->filler_mutex);
534 ret = xmms_ringbuf_read (output->filler_buffer, buffer, len);
535 if (ret == 0 && xmms_ringbuf_iseos (output->filler_buffer)) {
536 xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_STOP);
537 g_mutex_unlock (output->filler_mutex);
538 return -1;
539 }
540 g_mutex_unlock (output->filler_mutex);
541
542 update_playtime (output, ret);
543
544 if (ret < len) {
545 XMMS_DBG ("Underrun %d of %d (%d)", ret, len, xmms_sample_frame_size_get (output->format));
546
547 if ((ret % xmms_sample_frame_size_get (output->format)) != 0) {
548 xmms_log_error ("***********************************");
549 xmms_log_error ("* Read non-multiple of sample size,");
550 xmms_log_error ("* you probably hear noise now :)");
551 xmms_log_error ("***********************************");
552 }
553 output->buffer_underruns++;
554 }
555
556 output->bytes_written += ret;
557
558 return ret;
559}
560
561gint
563{
564 return xmms_ringbuf_bytes_used (output->filler_buffer);
565}
566
568xmms_output_config_property_register (xmms_output_t *output, const gchar *name, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
569{
570 g_return_val_if_fail (output->plugin, NULL);
571 return xmms_plugin_config_property_register ((xmms_plugin_t *)output->plugin, name, default_value, cb, userdata);
572}
573
575xmms_output_config_lookup (xmms_output_t *output, const gchar *path)
576{
577 g_return_val_if_fail (output->plugin, NULL);
578 return xmms_plugin_config_lookup ((xmms_plugin_t *)output->plugin, path);
579}
580
583{
584 g_return_val_if_fail (output, 0);
585 return output->current_entry;
586}
587
588
589/** @addtogroup Output
590 * @{
591 */
592/** Methods */
593static void
594xmms_playback_client_tickle (xmms_output_t *output, xmms_error_t *error)
595{
596 xmms_output_filler_state (output, FILLER_KILL);
597}
598
599static void
600xmms_playback_client_seek_ms (xmms_output_t *output, gint32 ms, gint32 whence, xmms_error_t *error)
601{
602 guint samples;
603
604 g_return_if_fail (output);
605
606 if (whence == XMMS_PLAYBACK_SEEK_CUR) {
607 g_mutex_lock (output->playtime_mutex);
608 ms += output->played_time;
609 if (ms < 0) {
610 ms = 0;
611 }
612 g_mutex_unlock (output->playtime_mutex);
613 }
614
615 if (output->format) {
616 samples = xmms_sample_ms_to_samples (output->format, ms);
617
618 xmms_playback_client_seek_samples (output, samples,
620 }
621}
622
623static void
624xmms_playback_client_seek_samples (xmms_output_t *output, gint32 samples, gint32 whence, xmms_error_t *error)
625{
626 if (whence == XMMS_PLAYBACK_SEEK_CUR) {
627 g_mutex_lock (output->playtime_mutex);
628 samples += output->played / xmms_sample_frame_size_get (output->format);
629 if (samples < 0) {
630 samples = 0;
631 }
632 g_mutex_unlock (output->playtime_mutex);
633 }
634
635 /* "just" tell filler */
636 xmms_output_filler_seek_state (output, samples);
637}
638
639static void
640xmms_playback_client_start (xmms_output_t *output, xmms_error_t *err)
641{
642 g_return_if_fail (output);
643
644 xmms_output_filler_state (output, FILLER_RUN);
645 if (!xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_PLAY)) {
646 xmms_output_filler_state (output, FILLER_STOP);
647 xmms_error_set (err, XMMS_ERROR_GENERIC, "Could not start playback");
648 }
649
650}
651
652static void
653xmms_playback_client_stop (xmms_output_t *output, xmms_error_t *err)
654{
655 g_return_if_fail (output);
656
657 xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_STOP);
658
659 xmms_output_filler_state (output, FILLER_STOP);
660}
661
662static void
663xmms_playback_client_pause (xmms_output_t *output, xmms_error_t *err)
664{
665 g_return_if_fail (output);
666
667 xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_PAUSE);
668}
669
670
671static gint32
672xmms_playback_client_status (xmms_output_t *output, xmms_error_t *error)
673{
674 gint32 ret;
675 g_return_val_if_fail (output, XMMS_PLAYBACK_STATUS_STOP);
676
677 g_mutex_lock (output->status_mutex);
678 ret = output->status;
679 g_mutex_unlock (output->status_mutex);
680 return ret;
681}
682
683static gint
684xmms_playback_client_current_id (xmms_output_t *output, xmms_error_t *error)
685{
686 return output->current_entry;
687}
688
689static void
690xmms_playback_client_volume_set (xmms_output_t *output, const gchar *channel,
691 gint32 volume, xmms_error_t *error)
692{
693
694 if (!output->plugin) {
695 xmms_error_set (error, XMMS_ERROR_GENERIC,
696 "couldn't set volume, output plugin not loaded");
697 return;
698 }
699
701 xmms_error_set (error, XMMS_ERROR_GENERIC,
702 "operation not supported");
703 return;
704 }
705
706 if (volume > 100 || volume < 0) {
707 xmms_error_set (error, XMMS_ERROR_INVAL, "volume out of range");
708 return;
709 }
710
711 if (!xmms_output_plugin_methods_volume_set (output->plugin, output, channel, volume)) {
712 xmms_error_set (error, XMMS_ERROR_GENERIC,
713 "couldn't set volume");
714 }
715}
716
717static GTree *
718xmms_playback_client_volume_get (xmms_output_t *output, xmms_error_t *error)
719{
720 GTree *ret;
722
723 if (!output->plugin) {
724 xmms_error_set (error, XMMS_ERROR_GENERIC,
725 "couldn't get volume, output plugin not loaded");
726 return NULL;
727 }
728
730 xmms_error_set (error, XMMS_ERROR_GENERIC,
731 "operation not supported");
732 return NULL;
733 }
734
735 xmms_error_set (error, XMMS_ERROR_GENERIC,
736 "couldn't get volume");
737
738 xmms_volume_map_init (&map);
739
740 /* ask the plugin how much channels it would like to set */
741 if (!xmms_output_plugin_method_volume_get (output->plugin, output,
742 NULL, NULL, &map.num_channels)) {
743 return NULL;
744 }
745
746 /* check for sane values */
747 g_return_val_if_fail (map.num_channels > 0, NULL);
748 g_return_val_if_fail (map.num_channels <= VOLUME_MAX_CHANNELS, NULL);
749
750 map.names = g_new (const gchar *, map.num_channels);
751 map.values = g_new (guint, map.num_channels);
752
753 map.status = xmms_output_plugin_method_volume_get (output->plugin, output,
754 map.names, map.values,
755 &map.num_channels);
756
757 if (!map.status || !map.num_channels) {
758 return NULL; /* error is set (-> no leak) */
759 }
760
761 ret = xmms_volume_map_to_dict (&map);
762
763 /* success! */
764 xmms_error_reset (error);
765
766 return ret;
767}
768
769/**
770 * Get the current playtime in milliseconds.
771 */
772static gint32
773xmms_playback_client_playtime (xmms_output_t *output, xmms_error_t *error)
774{
775 guint32 ret;
776 g_return_val_if_fail (output, 0);
777
778 g_mutex_lock (output->playtime_mutex);
779 ret = output->played_time;
780 g_mutex_unlock (output->playtime_mutex);
781
782 return ret;
783}
784
785/* returns the current latency: time left in ms until the data currently read
786 * from the latest xform in the chain will actually be played
787 */
788guint32
790{
791 guint ret = 0;
792 guint buffersize = 0;
793
794 if (output->format) {
795 /* data already waiting in the ringbuffer */
796 buffersize += xmms_ringbuf_bytes_used (output->filler_buffer);
797
798 /* latency of the soundcard */
799 buffersize += xmms_output_plugin_method_latency_get (output->plugin, output);
800
801 ret = xmms_sample_bytes_to_ms (output->format, buffersize);
802 }
803
804 return ret;
805}
806
807/**
808 * @internal
809 */
810
811static gboolean
812xmms_output_status_set (xmms_output_t *output, gint status)
813{
814 gboolean ret = TRUE;
815
816 if (!output->plugin) {
817 XMMS_DBG ("No plugin to set status on..");
818 return FALSE;
819 }
820
821 g_mutex_lock (output->status_mutex);
822
823 if (output->status != status) {
824 if (status == XMMS_PLAYBACK_STATUS_PAUSE &&
825 output->status != XMMS_PLAYBACK_STATUS_PLAY) {
826 XMMS_DBG ("Can only pause from play.");
827 ret = FALSE;
828 } else {
829 output->status = status;
830
831 if (status == XMMS_PLAYBACK_STATUS_STOP) {
832 xmms_object_unref (output->format);
833 output->format = NULL;
834 }
835 if (!xmms_output_plugin_method_status (output->plugin, output, status)) {
836 xmms_log_error ("Status method returned an error!");
837 output->status = XMMS_PLAYBACK_STATUS_STOP;
838 ret = FALSE;
839 }
840
844 output->status);
845 }
846 }
847
848 g_mutex_unlock (output->status_mutex);
849
850 return ret;
851}
852
853static void
854xmms_output_destroy (xmms_object_t *object)
855{
856 xmms_output_t *output = (xmms_output_t *)object;
857
858 output->monitor_volume_running = FALSE;
859 if (output->monitor_volume_thread) {
860 g_thread_join (output->monitor_volume_thread);
861 output->monitor_volume_thread = NULL;
862 }
863
864 xmms_output_filler_state (output, FILLER_QUIT);
865 g_thread_join (output->filler_thread);
866
867 if (output->plugin) {
868 xmms_output_plugin_method_destroy (output->plugin, output);
869 xmms_object_unref (output->plugin);
870 }
871 xmms_output_format_list_clear (output);
872
873 xmms_object_unref (output->playlist);
874
875 g_mutex_free (output->status_mutex);
876 g_mutex_free (output->playtime_mutex);
877 g_mutex_free (output->filler_mutex);
878 g_cond_free (output->filler_state_cond);
879 xmms_ringbuf_destroy (output->filler_buffer);
880
881 xmms_playback_unregister_ipc_commands ();
882}
883
884/**
885 * Switch to another output plugin.
886 * @param output output pointer
887 * @param new_plugin the new #xmms_plugin_t to use as output.
888 * @returns TRUE on success and FALSE on failure
889 */
890gboolean
892{
893 xmms_output_plugin_t *old_plugin;
894 gboolean ret;
895
896 g_return_val_if_fail (output, FALSE);
897 g_return_val_if_fail (new_plugin, FALSE);
898
899 xmms_playback_client_stop (output, NULL);
900
901 g_mutex_lock (output->status_mutex);
902
903 old_plugin = output->plugin;
904
905 ret = set_plugin (output, new_plugin);
906
907 /* if the switch succeeded, release the reference to the old plugin
908 * now.
909 * if we couldn't switch to the new plugin, but we had a working
910 * plugin before, switch back to the old plugin.
911 */
912 if (ret) {
913 xmms_object_unref (old_plugin);
914 } else if (old_plugin) {
915 XMMS_DBG ("cannot switch plugin, going back to old one");
916 set_plugin (output, old_plugin);
917 }
918
919 g_mutex_unlock (output->status_mutex);
920
921 return ret;
922}
923
924/**
925 * Allocate a new #xmms_output_t
926 */
929{
930 xmms_output_t *output;
932 gint size;
933
934 g_return_val_if_fail (playlist, NULL);
935
936 XMMS_DBG ("Trying to open output");
937
938 output = xmms_object_new (xmms_output_t, xmms_output_destroy);
939
940 output->playlist = playlist;
941
942 output->status_mutex = g_mutex_new ();
943 output->playtime_mutex = g_mutex_new ();
944
945 prop = xmms_config_property_register ("output.buffersize", "32768", NULL, NULL);
946 size = xmms_config_property_get_int (prop);
947 XMMS_DBG ("Using buffersize %d", size);
948
949 output->filler_mutex = g_mutex_new ();
950 output->filler_state = FILLER_STOP;
951 output->filler_state_cond = g_cond_new ();
952 output->filler_buffer = xmms_ringbuf_new (size);
953 output->filler_thread = g_thread_create (xmms_output_filler, output, TRUE, NULL);
954
955 xmms_config_property_register ("output.flush_on_pause", "1", NULL, NULL);
956
957 xmms_playback_register_ipc_commands (XMMS_OBJECT (output));
958
959 output->status = XMMS_PLAYBACK_STATUS_STOP;
960
961 if (plugin) {
962 if (!set_plugin (output, plugin)) {
963 xmms_log_error ("Could not initialize output plugin");
964 }
965 } else {
966 xmms_log_error ("initalized output without a plugin, please fix!");
967 }
968
969
970
971 return output;
972}
973
974/**
975 * Flush the buffers in soundcard.
976 */
977void
979{
980 g_return_if_fail (output);
981
982 xmms_output_plugin_method_flush (output->plugin, output);
983}
984
985/**
986 * @internal
987 */
988static gboolean
989xmms_output_format_set (xmms_output_t *output, xmms_stream_type_t *fmt)
990{
991 g_return_val_if_fail (output, FALSE);
992 g_return_val_if_fail (fmt, FALSE);
993
994 XMMS_DBG ("Setting format!");
995
996 if (!xmms_output_plugin_format_set_always (output->plugin)) {
997 gboolean ret;
998
999 if (output->format && xmms_stream_type_match (output->format, fmt)) {
1000 XMMS_DBG ("audio formats are equal, not updating");
1001 return TRUE;
1002 }
1003
1004 ret = xmms_output_plugin_method_format_set (output->plugin, output, fmt);
1005 if (ret) {
1006 xmms_object_unref (output->format);
1007 xmms_object_ref (fmt);
1008 output->format = fmt;
1009 }
1010 return ret;
1011 } else {
1012 if (output->format && !xmms_stream_type_match (output->format, fmt)) {
1013 xmms_object_unref (output->format);
1014 xmms_object_ref (fmt);
1015 output->format = fmt;
1016 }
1017 if (!output->format) {
1018 xmms_object_unref (output->format);
1019 xmms_object_ref (fmt);
1020 output->format = fmt;
1021 }
1022 return xmms_output_plugin_method_format_set (output->plugin, output, output->format);
1023 }
1024}
1025
1026
1027static gboolean
1028set_plugin (xmms_output_t *output, xmms_output_plugin_t *plugin)
1029{
1030 gboolean ret;
1031
1032 g_assert (output);
1033 g_assert (plugin);
1034
1035 output->monitor_volume_running = FALSE;
1036 if (output->monitor_volume_thread) {
1037 g_thread_join (output->monitor_volume_thread);
1038 output->monitor_volume_thread = NULL;
1039 }
1040
1041 if (output->plugin) {
1042 xmms_output_plugin_method_destroy (output->plugin, output);
1043 output->plugin = NULL;
1044 }
1045 xmms_output_format_list_clear (output);
1046
1047 /* output->plugin needs to be set before we can call the
1048 * NEW method
1049 */
1050 output->plugin = plugin;
1051 ret = xmms_output_plugin_method_new (output->plugin, output);
1052
1053 if (!ret) {
1054 output->plugin = NULL;
1055 } else if (!output->monitor_volume_thread) {
1056 output->monitor_volume_running = TRUE;
1057 output->monitor_volume_thread = g_thread_create (xmms_output_monitor_volume_thread,
1058 output, TRUE, NULL);
1059 }
1060
1061 return ret;
1062}
1063
1064static gint
1065xmms_volume_map_lookup (xmms_volume_map_t *vl, const gchar *name)
1066{
1067 gint i;
1068
1069 for (i = 0; i < vl->num_channels; i++) {
1070 if (!strcmp (vl->names[i], name)) {
1071 return i;
1072 }
1073 }
1074
1075 return -1;
1076}
1077
1078/* returns TRUE when both hashes are equal, else FALSE */
1079static gboolean
1080xmms_volume_map_equal (xmms_volume_map_t *a, xmms_volume_map_t *b)
1081{
1082 guint i;
1083
1084 g_assert (a);
1085 g_assert (b);
1086
1087 if (a->num_channels != b->num_channels) {
1088 return FALSE;
1089 }
1090
1091 for (i = 0; i < a->num_channels; i++) {
1092 gint j;
1093
1094 j = xmms_volume_map_lookup (b, a->names[i]);
1095 if (j == -1 || b->values[j] != a->values[i]) {
1096 return FALSE;
1097 }
1098 }
1099
1100 return TRUE;
1101}
1102
1103static void
1104xmms_volume_map_init (xmms_volume_map_t *vl)
1105{
1106 vl->status = FALSE;
1107 vl->num_channels = 0;
1108 vl->names = NULL;
1109 vl->values = NULL;
1110}
1111
1112static void
1113xmms_volume_map_free (xmms_volume_map_t *vl)
1114{
1115 g_free (vl->names);
1116 g_free (vl->values);
1117
1118 /* don't free vl here, its always allocated on the stack */
1119}
1120
1121static void
1122xmms_volume_map_copy (xmms_volume_map_t *src, xmms_volume_map_t *dst)
1123{
1124 dst->num_channels = src->num_channels;
1125 dst->status = src->status;
1126
1127 if (!src->status) {
1128 g_free (dst->names);
1129 dst->names = NULL;
1130
1131 g_free (dst->values);
1132 dst->values = NULL;
1133
1134 return;
1135 }
1136
1137 dst->names = g_renew (const gchar *, dst->names, src->num_channels);
1138 dst->values = g_renew (guint, dst->values, src->num_channels);
1139
1140 memcpy (dst->names, src->names, src->num_channels * sizeof (gchar *));
1141 memcpy (dst->values, src->values, src->num_channels * sizeof (guint));
1142}
1143
1144static GTree *
1145xmms_volume_map_to_dict (xmms_volume_map_t *vl)
1146{
1147 GTree *ret;
1148 gint i;
1149
1150 ret = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
1151 NULL, (GDestroyNotify) xmmsv_unref);
1152 if (!ret) {
1153 return NULL;
1154 }
1155
1156 for (i = 0; i < vl->num_channels; i++) {
1157 xmmsv_t *val;
1158
1159 val = xmmsv_new_int (vl->values[i]);
1160 g_tree_replace (ret, (gpointer) vl->names[i], val);
1161 }
1162
1163 return ret;
1164}
1165
1166static gpointer
1167xmms_output_monitor_volume_thread (gpointer data)
1168{
1169 GTree *dict;
1170 xmms_output_t *output = data;
1171 xmms_volume_map_t old, cur;
1172
1173 if (!xmms_output_plugin_method_volume_get_available (output->plugin)) {
1174 return NULL;
1175 }
1176
1177 xmms_set_thread_name ("x2 volume mon");
1178
1179 xmms_volume_map_init (&old);
1180 xmms_volume_map_init (&cur);
1181
1182 while (output->monitor_volume_running) {
1183 cur.num_channels = 0;
1184 cur.status = xmms_output_plugin_method_volume_get (output->plugin,
1185 output, NULL, NULL,
1186 &cur.num_channels);
1187
1188 if (cur.status) {
1189 /* check for sane values */
1190 if (cur.num_channels < 1 ||
1191 cur.num_channels > VOLUME_MAX_CHANNELS) {
1192 cur.status = FALSE;
1193 } else {
1194 cur.names = g_renew (const gchar *, cur.names,
1195 cur.num_channels);
1196 cur.values = g_renew (guint, cur.values, cur.num_channels);
1197 }
1198 }
1199
1200 if (cur.status) {
1201 cur.status =
1203 output, cur.names,
1204 cur.values,
1205 &cur.num_channels);
1206 }
1207
1208 /* we failed at getting volume for one of the two maps or
1209 * we succeeded both times and they differ -> changed
1210 */
1211 if ((cur.status ^ old.status) ||
1212 (cur.status && old.status &&
1213 !xmms_volume_map_equal (&old, &cur))) {
1214 /* emit the broadcast */
1215 if (cur.status) {
1216 dict = xmms_volume_map_to_dict (&cur);
1219 XMMSV_TYPE_DICT, dict);
1220 g_tree_destroy (dict);
1221 } else {
1222 /** @todo When bug 691 is solved, emit an error here */
1226 }
1227 }
1228
1229 xmms_volume_map_copy (&cur, &old);
1230
1231 g_usleep (G_USEC_PER_SEC);
1232 }
1233
1234 xmms_volume_map_free (&old);
1235 xmms_volume_map_free (&cur);
1236
1237 return NULL;
1238}
1239
1240/** @} */
xmms_config_property_t * xmms_config_property_register(const gchar *path, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Register a new config property.
Definition: config.c:334
gint xmms_config_property_get_int(const xmms_config_property_t *prop)
Return the value of a config property as an int.
Definition: config.c:255
const gchar * xmms_error_message_get(xmms_error_t *err)
Get the human readable error.
Definition: error.c:38
void xmms_medialib_entry_remove(xmms_medialib_entry_t entry)
Remove a medialib entry from the database.
Definition: medialib.c:710
gint xmms_medialib_entry_property_get_int(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property)
Retrieve a property as a int from a entry.
Definition: medialib.c:543
void xmms_medialib_entry_send_update(xmms_medialib_entry_t entry)
Trigger a update signal to the client.
Definition: medialib.c:674
void xmms_medialib_end(xmms_medialib_session_t *session)
Definition: medialib.c:425
void xmms_object_emit_f(xmms_object_t *object, guint32 signalid, xmmsv_type_t type,...)
Emits a signal on the current object.
Definition: object.c:256
gint xmms_output_bytes_available(xmms_output_t *output)
Gets Number of available bytes in the output buffer.
Definition: output.c:562
xmms_medialib_entry_t xmms_output_current_id(xmms_output_t *output)
Get the currently medialib id of the currently played entry.
Definition: output.c:582
xmms_config_property_t * xmms_output_config_property_register(xmms_output_t *output, const gchar *name, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Register a configuration directive.
Definition: output.c:568
xmms_config_property_t * xmms_output_config_lookup(xmms_output_t *output, const gchar *path)
Lookup a configuration directive for the output plugin.
Definition: output.c:575
void xmms_output_private_data_set(xmms_output_t *output, gpointer data)
Set the private data for the plugin that can be retrived with xmms_output_private_data_get later.
Definition: output.c:171
void xmms_output_stream_type_add(xmms_output_t *output,...)
Add format to list of supported formats.
Definition: output.c:180
void xmms_output_set_error(xmms_output_t *output, xmms_error_t *error)
Set an error.
Definition: output.c:255
gboolean xmms_output_plugin_format_set_always(xmms_output_plugin_t *plugin)
Check if an output plugin needs format updates on each track change.
Definition: outputplugin.c:226
gint xmms_output_read(xmms_output_t *output, char *buffer, gint len)
Read a number of bytes of data from the output buffer into a buffer.
Definition: output.c:522
struct xmms_output_St xmms_output_t
gpointer xmms_output_private_data_get(xmms_output_t *output)
Retrieve the private data for the plugin that was set with xmms_output_private_data_set.
Definition: output.c:162
struct xmms_output_plugin_St xmms_output_plugin_t
guint32 xmms_output_latency(xmms_output_t *output)
Definition: output.c:789
xmms_output_t * xmms_output_new(xmms_output_plugin_t *plugin, xmms_playlist_t *playlist)
Allocate a new xmms_output_t.
Definition: output.c:928
void xmms_output_flush(xmms_output_t *output)
Flush the buffers in soundcard.
Definition: output.c:978
gboolean xmms_output_plugin_switch(xmms_output_t *output, xmms_output_plugin_t *new_plugin)
Switch to another output plugin.
Definition: output.c:891
gboolean xmms_playlist_advance(xmms_playlist_t *playlist)
Go to next song in playlist according to current playlist mode.
Definition: playlist.c:376
xmms_medialib_entry_t xmms_playlist_current_entry(xmms_playlist_t *playlist)
Retrieve the currently active xmms_medialib_entry_t.
Definition: playlist.c:394
guint xmms_ringbuf_write_wait(xmms_ringbuf_t *ringbuf, gconstpointer data, guint len, GMutex *mtx)
Same as xmms_ringbuf_write but blocks until there is enough free space.
Definition: ringbuf.c:353
guint xmms_ringbuf_read(xmms_ringbuf_t *ringbuf, gpointer data, guint len)
Reads data from the ringbuffer.
Definition: ringbuf.c:222
void xmms_ringbuf_set_eos(xmms_ringbuf_t *ringbuf, gboolean eos)
Set EOS flag on ringbuffer.
Definition: ringbuf.c:427
void xmms_ringbuf_clear(xmms_ringbuf_t *ringbuf)
Clear the ringbuffers data.
Definition: ringbuf.c:121
gboolean xmms_ringbuf_iseos(const xmms_ringbuf_t *ringbuf)
Tell if the ringbuffer is EOS.
Definition: ringbuf.c:416
void xmms_ringbuf_destroy(xmms_ringbuf_t *ringbuf)
Free all memory used by the ringbuffer.
Definition: ringbuf.c:104
void xmms_ringbuf_wait_free(const xmms_ringbuf_t *ringbuf, guint len, GMutex *mtx)
Block until we have free space in the ringbuffer.
Definition: ringbuf.c:380
void xmms_ringbuf_wait_used(const xmms_ringbuf_t *ringbuf, guint len, GMutex *mtx)
Block until we have used space in the buffer.
Definition: ringbuf.c:397
xmms_ringbuf_t * xmms_ringbuf_new(guint size)
Allocate a new ringbuffer.
Definition: ringbuf.c:74
guint xmms_ringbuf_bytes_used(const xmms_ringbuf_t *ringbuf)
Number of bytes used in the buffer.
Definition: ringbuf.c:154
gint xmms_sample_frame_size_get(const xmms_stream_type_t *st)
Definition: sample.head.c:206
guint xmms_sample_bytes_to_ms(const xmms_stream_type_t *st, guint bytes)
Convert from bytes to milliseconds for this format.
Definition: sample.head.c:199
guint xmms_sample_ms_to_samples(const xmms_stream_type_t *st, guint milliseconds)
convert from milliseconds to samples for this format.
Definition: sample.head.c:177
void xmmsv_unref(xmmsv_t *val)
Decreases the references for the xmmsv_t When the number of references reaches 0 it will be freed.
Definition: value.c:303
struct xmmsv_St xmmsv_t
Definition: xmmsv_general.h:48
xmmsv_t * xmmsv_new_int(int32_t i)
Allocates a new integer xmmsv_t.
Definition: value.c:161
@ XMMSV_TYPE_NONE
Definition: xmmsv_general.h:36
@ XMMSV_TYPE_DICT
Definition: xmmsv_general.h:43
@ XMMSV_TYPE_INT32
Definition: xmmsv_general.h:38
struct xmms_xform_St xmms_xform_t
xmms_medialib_entry_t xmms_xform_entry_get(xmms_xform_t *xform)
Get the medialib entry played by this xform.
Definition: xform.c:418
@ XMMS_XFORM_SEEK_SET
xmms_output_filler_state_E
Definition: output.c:60
@ FILLER_QUIT
Definition: output.c:63
@ FILLER_SEEK
Definition: output.c:65
@ FILLER_STOP
Definition: output.c:61
@ FILLER_KILL
Definition: output.c:64
@ FILLER_RUN
Definition: output.c:62
struct xmms_volume_map_St xmms_volume_map_t
#define VOLUME_MAX_CHANNELS
Definition: output.c:38
enum xmms_output_filler_state_E xmms_output_filler_state_t
gboolean xmms_output_plugin_methods_volume_set(xmms_output_plugin_t *plugin, xmms_output_t *output, const gchar *chan, guint val)
Definition: outputplugin.c:307
gboolean xmms_output_plugin_method_format_set(xmms_output_plugin_t *plugin, xmms_output_t *output, xmms_stream_type_t *st)
Definition: outputplugin.c:238
gboolean xmms_output_plugin_method_status(xmms_output_plugin_t *plugin, xmms_output_t *output, gint st)
Definition: outputplugin.c:262
void xmms_output_plugin_method_flush(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:211
gboolean xmms_output_plugin_method_volume_get(xmms_output_plugin_t *plugin, xmms_output_t *output, const gchar **n, guint *x, guint *y)
Definition: outputplugin.c:334
void xmms_output_plugin_method_destroy(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:185
gboolean xmms_output_plugin_method_new(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:160
gboolean xmms_output_plugin_method_volume_get_available(xmms_output_plugin_t *plugin)
Definition: outputplugin.c:325
guint xmms_output_plugin_method_latency_get(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:281
gboolean xmms_output_plugin_method_volume_set_available(xmms_output_plugin_t *plugin)
Definition: outputplugin.c:298
const gchar * xmms_plugin_shortname_get(const xmms_plugin_t *plugin)
Definition: plugin.c:158
xmms_config_property_t * xmms_plugin_config_lookup(xmms_plugin_t *plugin, const gchar *key)
Definition: plugin.c:76
xmms_config_property_t * xmms_plugin_config_property_register(xmms_plugin_t *plugin, const gchar *name, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Definition: plugin.c:104
void xmms_ringbuf_hotspot_set(xmms_ringbuf_t *ringbuf, gboolean(*cb)(void *), void(*destroy)(void *), void *arg)
Definition: ringbuf.c:462
gboolean xmms_stream_type_match(const xmms_stream_type_t *in_type, const xmms_stream_type_t *out_type)
Definition: streamtype.c:210
gint xmms_stream_type_get_int(const xmms_stream_type_t *st, xmms_stream_type_key_t key)
Definition: streamtype.c:171
xmms_stream_type_t * xmms_stream_type_parse(va_list ap)
Definition: streamtype.c:71
void xmms_set_thread_name(const gchar *name)
gint xmms_xform_this_read(xmms_xform_t *xform, gpointer buf, gint siz, xmms_error_t *err)
Definition: xform.c:943
xmms_xform_t * xmms_xform_chain_setup(xmms_medialib_entry_t entry, GList *goal_formats, gboolean rehash)
Definition: xform.c:1388
xmms_stream_type_t * xmms_xform_outtype_get(xmms_xform_t *xform)
Definition: xform.c:484
gint64 xmms_xform_this_seek(xmms_xform_t *xform, gint64 offset, xmms_xform_seek_mode_t whence, xmms_error_t *err)
Definition: xform.c:1021
struct xmms_config_property_St xmms_config_property_t
Definition: xmms_config.h:26
#define xmms_log_error(fmt,...)
Definition: xmms_log.h:35
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
#define xmms_medialib_begin_write()
struct xmms_medialib_session_St xmms_medialib_session_t
Definition: xmms_medialib.h:87
#define xmms_medialib_entry_status_set(session, e, st)
G_BEGIN_DECLS typedef gint32 xmms_medialib_entry_t
Definition: xmms_medialib.h:86
#define XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS
Definition: xmms_medialib.h:67
@ XMMS_STREAM_TYPE_FMT_FORMAT
@ XMMS_STREAM_TYPE_FMT_SAMPLERATE
@ XMMS_STREAM_TYPE_FMT_CHANNELS
struct xmms_stream_type_St xmms_stream_type_t
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
void(* xmms_object_handler_t)(xmms_object_t *object, xmmsv_t *data, gpointer userdata)
Definition: xmms_object.h:66
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:115
#define XMMS_OBJECT(p)
Definition: xmms_object.h:77
#define xmms_object_ref(obj)
Definition: xmms_object.h:103
#define xmms_object_unref(obj)
Definition: xmms_object.h:109
struct xmms_playlist_St xmms_playlist_t
Definition: xmms_playlist.h:41
struct xmms_ringbuf_St xmms_ringbuf_t
Definition: xmms_ringbuf.h:25
@ XMMS_ERROR_GENERIC
@ XMMS_ERROR_INVAL
@ XMMS_MEDIALIB_ENTRY_STATUS_NOT_AVAILABLE
@ XMMS_MEDIALIB_ENTRY_STATUS_NEW
@ XMMS_PLAYBACK_SEEK_CUR
@ XMMS_PLAYBACK_SEEK_SET
@ XMMS_IPC_SIGNAL_PLAYBACK_VOLUME_CHANGED
@ XMMS_IPC_SIGNAL_PLAYBACK_PLAYTIME
@ XMMS_IPC_SIGNAL_PLAYBACK_CURRENTID
@ XMMS_IPC_SIGNAL_PLAYBACK_STATUS
@ XMMS_PLAYBACK_STATUS_PLAY
@ XMMS_PLAYBACK_STATUS_STOP
@ XMMS_PLAYBACK_STATUS_PAUSE
#define MIN(a, b)
Definition: xmmsc_util.h:36