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 /** @} */