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 
25 #include "xmmspriv/xmms_output.h"
26 #include "xmmspriv/xmms_ringbuf.h"
27 #include "xmmspriv/xmms_plugin.h"
28 #include "xmmspriv/xmms_xform.h"
29 #include "xmmspriv/xmms_sample.h"
30 #include "xmmspriv/xmms_medialib.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 
40 typedef struct xmms_volume_map_St {
41  const gchar **names;
42  guint *values;
43  guint num_channels;
44  gboolean status;
46 
47 static gboolean xmms_output_format_set (xmms_output_t *output, xmms_stream_type_t *fmt);
48 static gpointer xmms_output_monitor_volume_thread (gpointer data);
49 
50 static void xmms_playback_client_start (xmms_output_t *output, xmms_error_t *err);
51 static void xmms_playback_client_stop (xmms_output_t *output, xmms_error_t *err);
52 static void xmms_playback_client_pause (xmms_output_t *output, xmms_error_t *err);
53 static void xmms_playback_client_tickle (xmms_output_t *output, xmms_error_t *err);
54 static void xmms_playback_client_seek_ms (xmms_output_t *output, gint32 ms, gint32 whence, xmms_error_t *error);
55 static void xmms_playback_client_seek_samples (xmms_output_t *output, gint32 samples, gint32 whence, xmms_error_t *error);
56 static gint32 xmms_playback_client_status (xmms_output_t *output, xmms_error_t *error);
57 static gint xmms_playback_client_current_id (xmms_output_t *output, xmms_error_t *error);
58 static gint32 xmms_playback_client_playtime (xmms_output_t *output, xmms_error_t *err);
59 
67 
68 static void xmms_playback_client_volume_set (xmms_output_t *output, const gchar *channel, gint32 volume, xmms_error_t *error);
69 static GTree *xmms_playback_client_volume_get (xmms_output_t *output, xmms_error_t *error);
70 static void xmms_output_filler_state (xmms_output_t *output, xmms_output_filler_state_t state);
71 static void xmms_output_filler_state_nolock (xmms_output_t *output, xmms_output_filler_state_t state);
72 
73 static void xmms_volume_map_init (xmms_volume_map_t *vl);
74 static void xmms_volume_map_free (xmms_volume_map_t *vl);
75 static void xmms_volume_map_copy (xmms_volume_map_t *src, xmms_volume_map_t *dst);
76 static GTree *xmms_volume_map_to_dict (xmms_volume_map_t *vl);
77 static gboolean xmms_output_status_set (xmms_output_t *output, gint status);
78 static gboolean set_plugin (xmms_output_t *output, xmms_output_plugin_t *plugin);
79 
80 static void xmms_output_format_list_free_elem (gpointer data, gpointer user_data);
81 static 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 
104 struct 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 
161 gpointer
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 
170 void
172 {
173  g_return_if_fail (output);
174  g_return_if_fail (output->plugin);
175 
176  output->plugin_data = data;
177 }
178 
179 void
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 
194 static void
195 xmms_output_format_list_free_elem (gpointer data, gpointer user_data)
196 {
198 
199  g_return_if_fail (data);
200 
201  f = data;
202 
203  xmms_object_unref (f);
204 }
205 
206 static void
207 xmms_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 
220 static void
221 update_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 
254 void
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 
268 typedef struct {
269  xmms_output_t *output;
270  xmms_xform_t *chain;
271  gboolean flush;
272 } xmms_output_song_changed_arg_t;
273 
274 static void
275 song_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 
282 static gboolean
283 song_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;
287  xmms_medialib_entry_t entry;
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 
325 static gboolean
326 seek_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 
339 static void
340 xmms_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 
352 static void
353 xmms_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 }
359 static void
360 xmms_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 
369 static void *
370 xmms_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) {
435  xmms_medialib_entry_t entry;
436  xmms_output_song_changed_arg_t *hsarg;
437  xmms_medialib_session_t *session;
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 
521 gint
522 xmms_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 
561 gint
563 {
564  return xmms_ringbuf_bytes_used (output->filler_buffer);
565 }
566 
568 xmms_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 
575 xmms_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 */
593 static void
594 xmms_playback_client_tickle (xmms_output_t *output, xmms_error_t *error)
595 {
596  xmms_output_filler_state (output, FILLER_KILL);
597 }
598 
599 static void
600 xmms_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,
619  XMMS_PLAYBACK_SEEK_SET, error);
620  }
621 }
622 
623 static void
624 xmms_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 
639 static void
640 xmms_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 
652 static void
653 xmms_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 
662 static void
663 xmms_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 
671 static gint32
672 xmms_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 
683 static gint
684 xmms_playback_client_current_id (xmms_output_t *output, xmms_error_t *error)
685 {
686  return output->current_entry;
687 }
688 
689 static void
690 xmms_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 
700  if (!xmms_output_plugin_method_volume_set_available (output->plugin)) {
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 
717 static GTree *
718 xmms_playback_client_volume_get (xmms_output_t *output, xmms_error_t *error)
719 {
720  GTree *ret;
721  xmms_volume_map_t map;
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 
729  if (!xmms_output_plugin_method_volume_get_available (output->plugin)) {
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  */
772 static gint32
773 xmms_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  */
788 guint32
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 
811 static gboolean
812 xmms_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 
853 static void
854 xmms_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  */
890 gboolean
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  */
977 void
979 {
980  g_return_if_fail (output);
981 
982  xmms_output_plugin_method_flush (output->plugin, output);
983 }
984 
985 /**
986  * @internal
987  */
988 static gboolean
989 xmms_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 
1027 static gboolean
1028 set_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 
1064 static gint
1065 xmms_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 */
1079 static gboolean
1080 xmms_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 
1103 static void
1104 xmms_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 
1112 static void
1113 xmms_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 
1121 static void
1122 xmms_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 
1144 static GTree *
1145 xmms_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 
1166 static gpointer
1167 xmms_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 =
1202  xmms_output_plugin_method_volume_get (output->plugin,
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);
1217  xmms_object_emit_f (XMMS_OBJECT (output),
1219  XMMSV_TYPE_DICT, dict);
1220  g_tree_destroy (dict);
1221  } else {
1222  /** @todo When bug 691 is solved, emit an error here */
1223  xmms_object_emit_f (XMMS_OBJECT (output),
1225  XMMSV_TYPE_NONE);
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 /** @} */
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
struct xmms_ringbuf_St xmms_ringbuf_t
Definition: xmms_ringbuf.h:25
G_BEGIN_DECLS typedef gint32 xmms_medialib_entry_t
Definition: xmms_medialib.h:86
struct xmms_volume_map_St xmms_volume_map_t
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
#define VOLUME_MAX_CHANNELS
Definition: output.c:38
void xmms_set_thread_name(const gchar *name)
#define XMMS_OBJECT(p)
Definition: xmms_object.h:77
gint xmms_stream_type_get_int(const xmms_stream_type_t *st, xmms_stream_type_key_t key)
Definition: streamtype.c:171
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
gboolean xmms_ringbuf_iseos(const xmms_ringbuf_t *ringbuf)
Tell if the ringbuffer is EOS.
Definition: ringbuf.c:416
#define xmms_object_unref(obj)
Definition: xmms_object.h:109
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
xmms_xform_t * xmms_xform_chain_setup(xmms_medialib_entry_t entry, GList *goal_formats, gboolean rehash)
Definition: xform.c:1388
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
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_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
struct xmms_stream_type_St xmms_stream_type_t
struct xmmsv_St xmmsv_t
Definition: xmmsv_general.h:48
gboolean xmms_output_plugin_method_status(xmms_output_plugin_t *plugin, xmms_output_t *output, gint st)
Definition: outputplugin.c:262
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_xform_St xmms_xform_t
guint xmms_ringbuf_bytes_used(const xmms_ringbuf_t *ringbuf)
Number of bytes used in the buffer.
Definition: ringbuf.c:154
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
guint32 xmms_output_latency(xmms_output_t *output)
Definition: output.c:789
const gchar * xmms_plugin_shortname_get(const xmms_plugin_t *plugin)
Definition: plugin.c:158
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_new(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:160
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
gboolean xmms_playlist_advance(xmms_playlist_t *playlist)
Go to next song in playlist according to current playlist mode.
Definition: playlist.c:376
void xmms_output_flush(xmms_output_t *output)
Flush the buffers in soundcard.
Definition: output.c:978
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
void xmms_output_plugin_method_flush(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:211
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
guint xmms_ringbuf_read(xmms_ringbuf_t *ringbuf, gpointer data, guint len)
Reads data from the ringbuffer.
Definition: ringbuf.c:222
const gchar * xmms_error_message_get(xmms_error_t *err)
Get the human readable error.
Definition: error.c:38
gboolean xmms_stream_type_match(const xmms_stream_type_t *in_type, const xmms_stream_type_t *out_type)
Definition: streamtype.c:210
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
gboolean xmms_output_plugin_method_volume_set_available(xmms_output_plugin_t *plugin)
Definition: outputplugin.c:298
#define xmms_log_error(fmt,...)
Definition: xmms_log.h:35
gboolean xmms_output_plugin_switch(xmms_output_t *output, xmms_output_plugin_t *new_plugin)
Switch to another output plugin.
Definition: output.c:891
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
xmms_ringbuf_t * xmms_ringbuf_new(guint size)
Allocate a new ringbuffer.
Definition: ringbuf.c:74
struct xmms_playlist_St xmms_playlist_t
Definition: xmms_playlist.h:41
struct xmms_output_St xmms_output_t
#define XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS
Definition: xmms_medialib.h:67
struct xmms_output_plugin_St xmms_output_plugin_t
xmmsv_t * xmmsv_new_int(int32_t i)
Allocates a new integer xmmsv_t.
Definition: value.c:161
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_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
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_medialib_session_St xmms_medialib_session_t
Definition: xmms_medialib.h:87
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
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
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
gint xmms_output_bytes_available(xmms_output_t *output)
Gets Number of available bytes in the output buffer.
Definition: output.c:562
void xmms_ringbuf_clear(xmms_ringbuf_t *ringbuf)
Clear the ringbuffers data.
Definition: ringbuf.c:121
void xmms_output_stream_type_add(xmms_output_t *output,...)
Add format to list of supported formats.
Definition: output.c:180
void xmms_medialib_entry_send_update(xmms_medialib_entry_t entry)
Trigger a update signal to the client.
Definition: medialib.c:674
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
#define xmms_object_ref(obj)
Definition: xmms_object.h:103
#define MIN(a, b)
Definition: xmmsc_util.h:36
gboolean xmms_output_plugin_method_volume_get_available(xmms_output_plugin_t *plugin)
Definition: outputplugin.c:325
void xmms_ringbuf_set_eos(xmms_ringbuf_t *ringbuf, gboolean eos)
Set EOS flag on ringbuffer.
Definition: ringbuf.c:427
enum xmms_output_filler_state_E xmms_output_filler_state_t
xmms_output_filler_state_E
Definition: output.c:60
gint xmms_sample_frame_size_get(const xmms_stream_type_t *st)
Definition: sample.head.c:206
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
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
#define xmms_medialib_begin_write()
void xmms_ringbuf_destroy(xmms_ringbuf_t *ringbuf)
Free all memory used by the ringbuffer.
Definition: ringbuf.c:104
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:115
#define xmms_medialib_entry_status_set(session, e, st)
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
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
xmms_config_property_t * xmms_plugin_config_lookup(xmms_plugin_t *plugin, const gchar *key)
Definition: plugin.c:76
struct xmms_config_property_St xmms_config_property_t
Definition: xmms_config.h:26
xmms_stream_type_t * xmms_xform_outtype_get(xmms_xform_t *xform)
Definition: xform.c:484
gint xmms_xform_this_read(xmms_xform_t *xform, gpointer buf, gint siz, xmms_error_t *err)
Definition: xform.c:943
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_medialib_entry_remove(xmms_medialib_entry_t entry)
Remove a medialib entry from the database.
Definition: medialib.c:710
xmms_stream_type_t * xmms_stream_type_parse(va_list ap)
Definition: streamtype.c:71
xmms_medialib_entry_t xmms_playlist_current_entry(xmms_playlist_t *playlist)
Retrieve the currently active xmms_medialib_entry_t.
Definition: playlist.c:394
void xmms_medialib_end(xmms_medialib_session_t *session)
Definition: medialib.c:425
void xmms_output_plugin_method_destroy(xmms_output_plugin_t *plugin, xmms_output_t *output)
Definition: outputplugin.c:185
void xmms_ringbuf_hotspot_set(xmms_ringbuf_t *ringbuf, gboolean(*cb)(void *), void(*destroy)(void *), void *arg)
Definition: ringbuf.c:462
void(* xmms_object_handler_t)(xmms_object_t *object, xmmsv_t *data, gpointer userdata)
Definition: xmms_object.h:66
void xmms_output_set_error(xmms_output_t *output, xmms_error_t *error)
Set an error.
Definition: output.c:255