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