XMMS2

src/xmms/output.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2009 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 /**
00018  * @file
00019  * Output plugin helper
00020  */
00021 
00022 #include <string.h>
00023 #include <unistd.h>
00024 
00025 #include "xmmspriv/xmms_output.h"
00026 #include "xmmspriv/xmms_ringbuf.h"
00027 #include "xmmspriv/xmms_plugin.h"
00028 #include "xmmspriv/xmms_xform.h"
00029 #include "xmmspriv/xmms_sample.h"
00030 #include "xmmspriv/xmms_medialib.h"
00031 #include "xmmspriv/xmms_outputplugin.h"
00032 #include "xmms/xmms_log.h"
00033 #include "xmms/xmms_ipc.h"
00034 #include "xmms/xmms_object.h"
00035 #include "xmms/xmms_config.h"
00036 
00037 #define VOLUME_MAX_CHANNELS 128
00038 
00039 typedef struct xmms_volume_map_St {
00040     const gchar **names;
00041     guint *values;
00042     guint num_channels;
00043     gboolean status;
00044 } xmms_volume_map_t;
00045 
00046 static gboolean xmms_output_format_set (xmms_output_t *output, xmms_stream_type_t *fmt);
00047 static gpointer xmms_output_monitor_volume_thread (gpointer data);
00048 
00049 static void xmms_playback_client_start (xmms_output_t *output, xmms_error_t *err);
00050 static void xmms_playback_client_stop (xmms_output_t *output, xmms_error_t *err);
00051 static void xmms_playback_client_pause (xmms_output_t *output, xmms_error_t *err);
00052 static void xmms_playback_client_xform_kill (xmms_output_t *output, xmms_error_t *err);
00053 static void xmms_playback_client_seekms (xmms_output_t *output, gint32 ms, gint32 whence, xmms_error_t *error);
00054 static void xmms_playback_client_seeksamples (xmms_output_t *output, gint32 samples, gint32 whence, xmms_error_t *error);
00055 static gint32 xmms_playback_client_status (xmms_output_t *output, xmms_error_t *error);
00056 static gint xmms_playback_client_current_id (xmms_output_t *output, xmms_error_t *error);
00057 static gint32 xmms_playback_client_playtime (xmms_output_t *output, xmms_error_t *err);
00058 
00059 typedef enum xmms_output_filler_state_E {
00060     FILLER_STOP,
00061     FILLER_RUN,
00062     FILLER_QUIT,
00063     FILLER_KILL,
00064     FILLER_SEEK,
00065 } xmms_output_filler_state_t;
00066 
00067 static void xmms_playback_client_volume_set (xmms_output_t *output, const gchar *channel, gint32 volume, xmms_error_t *error);
00068 static GTree *xmms_playback_client_volume_get (xmms_output_t *output, xmms_error_t *error);
00069 static void xmms_output_filler_state (xmms_output_t *output, xmms_output_filler_state_t state);
00070 static void xmms_output_filler_state_nolock (xmms_output_t *output, xmms_output_filler_state_t state);
00071 
00072 static void xmms_volume_map_init (xmms_volume_map_t *vl);
00073 static void xmms_volume_map_free (xmms_volume_map_t *vl);
00074 static void xmms_volume_map_copy (xmms_volume_map_t *src, xmms_volume_map_t *dst);
00075 static GTree *xmms_volume_map_to_dict (xmms_volume_map_t *vl);
00076 static gboolean xmms_output_status_set (xmms_output_t *output, gint status);
00077 static gboolean set_plugin (xmms_output_t *output, xmms_output_plugin_t *plugin);
00078 
00079 static void xmms_output_format_list_free_elem (gpointer data, gpointer user_data);
00080 static void xmms_output_format_list_clear (xmms_output_t *output);
00081 xmms_medialib_entry_t xmms_output_current_id (xmms_output_t *output);
00082 
00083 XMMS_CMD_DEFINE (start, xmms_playback_client_start, xmms_output_t *, NONE, NONE, NONE);
00084 XMMS_CMD_DEFINE (stop, xmms_playback_client_stop, xmms_output_t *, NONE, NONE, NONE);
00085 XMMS_CMD_DEFINE (pause, xmms_playback_client_pause, xmms_output_t *, NONE, NONE, NONE);
00086 XMMS_CMD_DEFINE (xform_kill, xmms_playback_client_xform_kill, xmms_output_t *, NONE, NONE, NONE);
00087 XMMS_CMD_DEFINE (playtime, xmms_playback_client_playtime, xmms_output_t *, INT32, NONE, NONE);
00088 XMMS_CMD_DEFINE (seekms, xmms_playback_client_seekms, xmms_output_t *, NONE, INT32, INT32);
00089 XMMS_CMD_DEFINE (seeksamples, xmms_playback_client_seeksamples, xmms_output_t *, NONE, INT32, INT32);
00090 XMMS_CMD_DEFINE (output_status, xmms_playback_client_status, xmms_output_t *, INT32, NONE, NONE);
00091 XMMS_CMD_DEFINE (currentid, xmms_playback_client_current_id, xmms_output_t *, INT32, NONE, NONE);
00092 XMMS_CMD_DEFINE (volume_set, xmms_playback_client_volume_set, xmms_output_t *, NONE, STRING, INT32);
00093 XMMS_CMD_DEFINE (volume_get, xmms_playback_client_volume_get, xmms_output_t *, DICT, NONE, NONE);
00094 
00095 /*
00096  * Type definitions
00097  */
00098 
00099 /** @defgroup Output Output
00100   * @ingroup XMMSServer
00101   * @brief Output is responsible to put the decoded data on
00102   * the soundcard.
00103   * @{
00104   */
00105 
00106 /*
00107  *
00108  * locking order: status_mutex > write_mutex
00109  *                filler_mutex
00110  *                playtime_mutex is leaflock.
00111  */
00112 
00113 struct xmms_output_St {
00114     xmms_object_t object;
00115 
00116     xmms_output_plugin_t *plugin;
00117     gpointer plugin_data;
00118 
00119     /* */
00120     GMutex *playtime_mutex;
00121     guint played;
00122     guint played_time;
00123     xmms_medialib_entry_t current_entry;
00124     guint toskip;
00125 
00126     /* */
00127     GThread *filler_thread;
00128     GMutex *filler_mutex;
00129 
00130     GCond *filler_state_cond;
00131     xmms_output_filler_state_t filler_state;
00132 
00133     xmms_ringbuf_t *filler_buffer;
00134     guint32 filler_seek;
00135     gint filler_skip;
00136 
00137     /** Internal status, tells which state the
00138         output really is in */
00139     GMutex *status_mutex;
00140     guint status;
00141 
00142     xmms_playlist_t *playlist;
00143 
00144     /** Supported formats */
00145     GList *format_list;
00146     /** Active format */
00147     xmms_stream_type_t *format;
00148 
00149     /**
00150      * Number of bytes totaly written to output driver,
00151      * this is only for statistics...
00152      */
00153     guint64 bytes_written;
00154 
00155     /**
00156      * How many times didn't we have enough data in the buffer?
00157      */
00158     gint32 buffer_underruns;
00159 
00160     GThread *monitor_volume_thread;
00161     gboolean monitor_volume_running;
00162 };
00163 
00164 /** @} */
00165 
00166 /*
00167  * Public functions
00168  */
00169 
00170 gpointer
00171 xmms_output_private_data_get (xmms_output_t *output)
00172 {
00173     g_return_val_if_fail (output, NULL);
00174     g_return_val_if_fail (output->plugin, NULL);
00175 
00176     return output->plugin_data;
00177 }
00178 
00179 void
00180 xmms_output_private_data_set (xmms_output_t *output, gpointer data)
00181 {
00182     g_return_if_fail (output);
00183     g_return_if_fail (output->plugin);
00184 
00185     output->plugin_data = data;
00186 }
00187 
00188 void
00189 xmms_output_stream_type_add (xmms_output_t *output, ...)
00190 {
00191     xmms_stream_type_t *f;
00192     va_list ap;
00193 
00194     va_start (ap, output);
00195     f = xmms_stream_type_parse (ap);
00196     va_end (ap);
00197 
00198     g_return_if_fail (f);
00199 
00200     output->format_list = g_list_append (output->format_list, f);
00201 }
00202 
00203 static void
00204 xmms_output_format_list_free_elem (gpointer data, gpointer user_data)
00205 {
00206     xmms_stream_type_t *f;
00207 
00208     g_return_if_fail (data);
00209 
00210     f = data;
00211 
00212     xmms_object_unref (f);
00213 }
00214 
00215 static void
00216 xmms_output_format_list_clear(xmms_output_t *output)
00217 {
00218     if (output->format_list == NULL)
00219         return;
00220 
00221     g_list_foreach (output->format_list,
00222                     xmms_output_format_list_free_elem,
00223                     NULL);
00224 
00225     g_list_free (output->format_list);
00226     output->format_list = NULL;
00227 }
00228 
00229 static void
00230 update_playtime (xmms_output_t *output, int advance)
00231 {
00232     guint buffersize = 0;
00233 
00234     g_mutex_lock (output->playtime_mutex);
00235     output->played += advance;
00236     g_mutex_unlock (output->playtime_mutex);
00237 
00238     buffersize = xmms_output_plugin_method_latency_get (output->plugin, output);
00239 
00240     if (output->played < buffersize) {
00241         buffersize = output->played;
00242     }
00243 
00244     g_mutex_lock (output->playtime_mutex);
00245 
00246     if (output->format) {
00247         guint ms = xmms_sample_bytes_to_ms (output->format,
00248                                             output->played - buffersize);
00249         if ((ms / 100) != (output->played_time / 100)) {
00250             xmms_object_emit_f (XMMS_OBJECT (output),
00251                                 XMMS_IPC_SIGNAL_PLAYBACK_PLAYTIME,
00252                                 XMMSV_TYPE_INT32,
00253                                 ms);
00254         }
00255         output->played_time = ms;
00256 
00257     }
00258 
00259     g_mutex_unlock (output->playtime_mutex);
00260 
00261 }
00262 
00263 void
00264 xmms_output_set_error (xmms_output_t *output, xmms_error_t *error)
00265 {
00266     g_return_if_fail (output);
00267 
00268     xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_STOP);
00269 
00270     if (error) {
00271         xmms_log_error ("Output plugin %s reported error, '%s'",
00272                         xmms_plugin_shortname_get ((xmms_plugin_t *)output->plugin),
00273                         xmms_error_message_get (error));
00274     }
00275 }
00276 
00277 typedef struct {
00278     xmms_output_t *output;
00279     xmms_xform_t *chain;
00280     gboolean flush;
00281 } xmms_output_song_changed_arg_t;
00282 
00283 static void
00284 song_changed_arg_free (void *data)
00285 {
00286     xmms_output_song_changed_arg_t *arg = (xmms_output_song_changed_arg_t *)data;
00287     xmms_object_unref (arg->chain);
00288     g_free (arg);
00289 }
00290 
00291 static gboolean
00292 song_changed (void *data)
00293 {
00294     /* executes in the output thread; NOT the filler thread */
00295     xmms_output_song_changed_arg_t *arg = (xmms_output_song_changed_arg_t *)data;
00296     xmms_medialib_entry_t entry;
00297     xmms_stream_type_t *type;
00298 
00299     entry = xmms_xform_entry_get (arg->chain);
00300 
00301     XMMS_DBG ("Running hotspot! Song changed!! %d", entry);
00302 
00303     arg->output->played = 0;
00304     arg->output->current_entry = entry;
00305 
00306     type = xmms_xform_outtype_get (arg->chain);
00307 
00308     if (!xmms_output_format_set (arg->output, type)) {
00309         gint fmt, rate, chn;
00310 
00311         fmt = xmms_stream_type_get_int (type, XMMS_STREAM_TYPE_FMT_FORMAT);
00312         rate = xmms_stream_type_get_int (type, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
00313         chn = xmms_stream_type_get_int (type, XMMS_STREAM_TYPE_FMT_CHANNELS);
00314 
00315         XMMS_DBG ("Couldn't set format %s/%d/%d, stopping filler..",
00316                   xmms_sample_name_get (fmt), rate, chn);
00317 
00318         xmms_output_filler_state_nolock (arg->output, FILLER_STOP);
00319         xmms_ringbuf_set_eos (arg->output->filler_buffer, TRUE);
00320         return FALSE;
00321     }
00322 
00323     if (arg->flush)
00324         xmms_output_flush (arg->output);
00325 
00326     xmms_object_emit_f (XMMS_OBJECT (arg->output),
00327                         XMMS_IPC_SIGNAL_PLAYBACK_CURRENTID,
00328                         XMMSV_TYPE_INT32,
00329                         entry);
00330 
00331     return TRUE;
00332 }
00333 
00334 static gboolean
00335 seek_done (void *data)
00336 {
00337     xmms_output_t *output = (xmms_output_t *)data;
00338 
00339     g_mutex_lock (output->playtime_mutex);
00340     output->played = output->filler_seek * xmms_sample_frame_size_get (output->format);
00341     output->toskip = output->filler_skip * xmms_sample_frame_size_get (output->format);
00342     g_mutex_unlock (output->playtime_mutex);
00343 
00344     xmms_output_flush (output);
00345     return TRUE;
00346 }
00347 
00348 static void
00349 xmms_output_filler_state_nolock (xmms_output_t *output, xmms_output_filler_state_t state)
00350 {
00351     output->filler_state = state;
00352     g_cond_signal (output->filler_state_cond);
00353     if (state == FILLER_QUIT || state == FILLER_STOP || state == FILLER_KILL) {
00354         xmms_ringbuf_clear (output->filler_buffer);
00355     }
00356     if (state != FILLER_STOP) {
00357         xmms_ringbuf_set_eos (output->filler_buffer, FALSE);
00358     }
00359 }
00360 
00361 static void
00362 xmms_output_filler_state (xmms_output_t *output, xmms_output_filler_state_t state)
00363 {
00364     g_mutex_lock (output->filler_mutex);
00365     xmms_output_filler_state_nolock (output, state);
00366     g_mutex_unlock (output->filler_mutex);
00367 }
00368 static void
00369 xmms_output_filler_seek_state (xmms_output_t *output, guint32 samples)
00370 {
00371     g_mutex_lock (output->filler_mutex);
00372     output->filler_state = FILLER_SEEK;
00373     output->filler_seek = samples;
00374     g_cond_signal (output->filler_state_cond);
00375     g_mutex_unlock (output->filler_mutex);
00376 }
00377 
00378 static void *
00379 xmms_output_filler (void *arg)
00380 {
00381     xmms_output_t *output = (xmms_output_t *)arg;
00382     xmms_xform_t *chain = NULL;
00383     gboolean last_was_kill = FALSE;
00384     char buf[4096];
00385     xmms_error_t err;
00386     gint ret;
00387 
00388     xmms_error_reset (&err);
00389 
00390     g_mutex_lock (output->filler_mutex);
00391     while (output->filler_state != FILLER_QUIT) {
00392         if (output->filler_state == FILLER_STOP) {
00393             if (chain) {
00394                 xmms_object_unref (chain);
00395                 chain = NULL;
00396             }
00397             xmms_ringbuf_set_eos (output->filler_buffer, TRUE);
00398             g_cond_wait (output->filler_state_cond, output->filler_mutex);
00399             last_was_kill = FALSE;
00400             continue;
00401         }
00402         if (output->filler_state == FILLER_KILL) {
00403             if (chain) {
00404                 xmms_object_unref (chain);
00405                 chain = NULL;
00406                 output->filler_state = FILLER_RUN;
00407                 last_was_kill = TRUE;
00408             } else {
00409                 output->filler_state = FILLER_STOP;
00410             }
00411             continue;
00412         }
00413         if (output->filler_state == FILLER_SEEK) {
00414             if (!chain) {
00415                 XMMS_DBG ("Seek without chain, ignoring..");
00416                 output->filler_state = FILLER_STOP;
00417                 continue;
00418             }
00419 
00420             ret = xmms_xform_this_seek (chain, output->filler_seek, XMMS_XFORM_SEEK_SET, &err);
00421             if (ret == -1) {
00422                 XMMS_DBG ("Seeking failed: %s", xmms_error_message_get (&err));
00423             } else {
00424                 XMMS_DBG ("Seek ok! %d", ret);
00425 
00426                 output->filler_skip = output->filler_seek - ret;
00427                 if (output->filler_skip < 0) {
00428                     XMMS_DBG ("Seeked %d samples too far! Updating position...",
00429                               -output->filler_skip);
00430 
00431                     output->filler_skip = 0;
00432                     output->filler_seek = ret;
00433                 }
00434 
00435                 xmms_ringbuf_clear (output->filler_buffer);
00436                 xmms_ringbuf_hotspot_set (output->filler_buffer, seek_done, NULL, output);
00437             }
00438             output->filler_state = FILLER_RUN;
00439         }
00440 
00441         if (!chain) {
00442             xmms_medialib_entry_t entry;
00443             xmms_output_song_changed_arg_t *hsarg;
00444             xmms_medialib_session_t *session;
00445 
00446             g_mutex_unlock (output->filler_mutex);
00447 
00448             entry = xmms_playlist_current_entry (output->playlist);
00449             if (!entry) {
00450                 XMMS_DBG ("No entry from playlist!");
00451                 output->filler_state = FILLER_STOP;
00452                 g_mutex_lock (output->filler_mutex);
00453                 continue;
00454             }
00455 
00456             chain = xmms_xform_chain_setup (entry, output->format_list, FALSE);
00457             if (!chain) {
00458                 session = xmms_medialib_begin_write ();
00459                 if (xmms_medialib_entry_property_get_int (session, entry, XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS) == XMMS_MEDIALIB_ENTRY_STATUS_NEW) {
00460                     xmms_medialib_end (session);
00461                     xmms_medialib_entry_remove (entry);
00462                 } else {
00463                     xmms_medialib_entry_status_set (session, entry, XMMS_MEDIALIB_ENTRY_STATUS_NOT_AVAILABLE);
00464                     xmms_medialib_entry_send_update (entry);
00465                     xmms_medialib_end (session);
00466                 }
00467 
00468                 if (!xmms_playlist_advance (output->playlist)) {
00469                     XMMS_DBG ("End of playlist");
00470                     output->filler_state = FILLER_STOP;
00471                 }
00472                 g_mutex_lock (output->filler_mutex);
00473                 continue;
00474             }
00475 
00476             hsarg = g_new0 (xmms_output_song_changed_arg_t, 1);
00477             hsarg->output = output;
00478             hsarg->chain = chain;
00479             hsarg->flush = last_was_kill;
00480             xmms_object_ref (chain);
00481 
00482             last_was_kill = FALSE;
00483 
00484             g_mutex_lock (output->filler_mutex);
00485             xmms_ringbuf_hotspot_set (output->filler_buffer, song_changed, song_changed_arg_free, hsarg);
00486         }
00487 
00488         xmms_ringbuf_wait_free (output->filler_buffer, sizeof (buf), output->filler_mutex);
00489 
00490         if (output->filler_state != FILLER_RUN) {
00491             XMMS_DBG ("State changed while waiting...");
00492             continue;
00493         }
00494         g_mutex_unlock (output->filler_mutex);
00495 
00496         ret = xmms_xform_this_read (chain, buf, sizeof (buf), &err);
00497 
00498         g_mutex_lock (output->filler_mutex);
00499 
00500         if (ret > 0) {
00501             gint skip = MIN (ret, output->toskip);
00502 
00503             output->toskip -= skip;
00504             if (ret > skip) {
00505                 xmms_ringbuf_write_wait (output->filler_buffer,
00506                                          buf + skip,
00507                                          ret - skip,
00508                                          output->filler_mutex);
00509             }
00510         } else {
00511             if (ret == -1) {
00512                 /* print error */
00513                 xmms_error_reset (&err);
00514             }
00515             xmms_object_unref (chain);
00516             chain = NULL;
00517             if (!xmms_playlist_advance (output->playlist)) {
00518                 XMMS_DBG ("End of playlist");
00519                 output->filler_state = FILLER_STOP;
00520             }
00521         }
00522 
00523     }
00524     g_mutex_unlock (output->filler_mutex);
00525     return NULL;
00526 }
00527 
00528 gint
00529 xmms_output_read (xmms_output_t *output, char *buffer, gint len)
00530 {
00531     gint ret;
00532     xmms_error_t err;
00533 
00534     xmms_error_reset (&err);
00535 
00536     g_return_val_if_fail (output, -1);
00537     g_return_val_if_fail (buffer, -1);
00538 
00539     g_mutex_lock (output->filler_mutex);
00540     xmms_ringbuf_wait_used (output->filler_buffer, len, output->filler_mutex);
00541     ret = xmms_ringbuf_read (output->filler_buffer, buffer, len);
00542     if (ret == 0 && xmms_ringbuf_iseos (output->filler_buffer)) {
00543         xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_STOP);
00544         g_mutex_unlock (output->filler_mutex);
00545         return -1;
00546     }
00547     g_mutex_unlock (output->filler_mutex);
00548 
00549     update_playtime (output, ret);
00550 
00551     if (ret < len) {
00552         XMMS_DBG ("Underrun %d of %d (%d)", ret, len, xmms_sample_frame_size_get (output->format));
00553 
00554         if ((ret % xmms_sample_frame_size_get (output->format)) != 0) {
00555             xmms_log_error ("***********************************");
00556             xmms_log_error ("* Read non-multiple of sample size,");
00557             xmms_log_error ("*  you probably hear noise now :)");
00558             xmms_log_error ("***********************************");
00559         }
00560         output->buffer_underruns++;
00561     }
00562 
00563     output->bytes_written += ret;
00564 
00565     return ret;
00566 }
00567 
00568 xmms_config_property_t *
00569 xmms_output_config_property_register (xmms_output_t *output, const gchar *name, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
00570 {
00571     g_return_val_if_fail (output->plugin, NULL);
00572     return xmms_plugin_config_property_register ((xmms_plugin_t *)output->plugin, name, default_value, cb, userdata);
00573 }
00574 
00575 xmms_config_property_t *
00576 xmms_output_config_lookup (xmms_output_t *output, const gchar *path)
00577 {
00578     g_return_val_if_fail (output->plugin, NULL);
00579     return xmms_plugin_config_lookup ((xmms_plugin_t *)output->plugin, path);
00580 }
00581 
00582 xmms_medialib_entry_t
00583 xmms_output_current_id (xmms_output_t *output)
00584 {
00585     g_return_val_if_fail (output, 0);
00586     return output->current_entry;
00587 }
00588 
00589 
00590 /** @addtogroup Output
00591  * @{
00592  */
00593 /** Methods */
00594 static void
00595 xmms_playback_client_xform_kill (xmms_output_t *output, xmms_error_t *error)
00596 {
00597     xmms_output_filler_state (output, FILLER_KILL);
00598 }
00599 
00600 static void
00601 xmms_playback_client_seekms (xmms_output_t *output, gint32 ms, gint32 whence, xmms_error_t *error)
00602 {
00603     guint samples;
00604 
00605     g_return_if_fail (output);
00606 
00607     if (whence == XMMS_PLAYBACK_SEEK_CUR) {
00608         g_mutex_lock (output->playtime_mutex);
00609         ms += output->played_time;
00610         if (ms < 0) {
00611             ms = 0;
00612         }
00613         g_mutex_unlock (output->playtime_mutex);
00614     }
00615 
00616     if (output->format) {
00617         samples = xmms_sample_ms_to_samples (output->format, ms);
00618 
00619         xmms_playback_client_seeksamples (output, samples,
00620                                           XMMS_PLAYBACK_SEEK_SET, error);
00621     }
00622 }
00623 
00624 static void
00625 xmms_playback_client_seeksamples (xmms_output_t *output, gint32 samples, gint32 whence, xmms_error_t *error)
00626 {
00627     if (whence == XMMS_PLAYBACK_SEEK_CUR) {
00628         g_mutex_lock (output->playtime_mutex);
00629         samples += output->played / xmms_sample_frame_size_get (output->format);
00630         if (samples < 0) {
00631             samples = 0;
00632         }
00633         g_mutex_unlock (output->playtime_mutex);
00634     }
00635 
00636     /* "just" tell filler */
00637     xmms_output_filler_seek_state (output, samples);
00638 }
00639 
00640 static void
00641 xmms_playback_client_start (xmms_output_t *output, xmms_error_t *err)
00642 {
00643     g_return_if_fail (output);
00644 
00645     xmms_output_filler_state (output, FILLER_RUN);
00646     if (!xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_PLAY)) {
00647         xmms_output_filler_state (output, FILLER_STOP);
00648         xmms_error_set (err, XMMS_ERROR_GENERIC, "Could not start playback");
00649     }
00650 
00651 }
00652 
00653 static void
00654 xmms_playback_client_stop (xmms_output_t *output, xmms_error_t *err)
00655 {
00656     g_return_if_fail (output);
00657 
00658     xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_STOP);
00659 
00660     xmms_output_filler_state (output, FILLER_STOP);
00661 }
00662 
00663 static void
00664 xmms_playback_client_pause (xmms_output_t *output, xmms_error_t *err)
00665 {
00666     g_return_if_fail (output);
00667 
00668     xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_PAUSE);
00669 }
00670 
00671 
00672 static gint32
00673 xmms_playback_client_status (xmms_output_t *output, xmms_error_t *error)
00674 {
00675     gint32 ret;
00676     g_return_val_if_fail (output, XMMS_PLAYBACK_STATUS_STOP);
00677 
00678     g_mutex_lock (output->status_mutex);
00679     ret = output->status;
00680     g_mutex_unlock (output->status_mutex);
00681     return ret;
00682 }
00683 
00684 static gint
00685 xmms_playback_client_current_id (xmms_output_t *output, xmms_error_t *error)
00686 {
00687     return output->current_entry;
00688 }
00689 
00690 static void
00691 xmms_playback_client_volume_set (xmms_output_t *output, const gchar *channel,
00692                                gint32 volume, xmms_error_t *error)
00693 {
00694 
00695     if (!output->plugin) {
00696         xmms_error_set (error, XMMS_ERROR_GENERIC,
00697                         "couldn't set volume, output plugin not loaded");
00698         return;
00699     }
00700 
00701     if (!xmms_output_plugin_method_volume_set_available (output->plugin)) {
00702         xmms_error_set (error, XMMS_ERROR_GENERIC,
00703                         "operation not supported");
00704         return;
00705     }
00706 
00707     if (volume > 100) {
00708         xmms_error_set (error, XMMS_ERROR_INVAL, "volume out of range");
00709         return;
00710     }
00711 
00712     if (!xmms_output_plugin_methods_volume_set (output->plugin, output, channel, volume)) {
00713         xmms_error_set (error, XMMS_ERROR_GENERIC,
00714                         "couldn't set volume");
00715     }
00716 }
00717 
00718 static GTree *
00719 xmms_playback_client_volume_get (xmms_output_t *output, xmms_error_t *error)
00720 {
00721     GTree *ret;
00722     xmms_volume_map_t map;
00723 
00724     if (!output->plugin) {
00725         xmms_error_set (error, XMMS_ERROR_GENERIC,
00726                         "couldn't get volume, output plugin not loaded");
00727         return NULL;
00728     }
00729 
00730     if (!xmms_output_plugin_method_volume_get_available (output->plugin)) {
00731         xmms_error_set (error, XMMS_ERROR_GENERIC,
00732                         "operation not supported");
00733         return NULL;
00734     }
00735 
00736     xmms_error_set (error, XMMS_ERROR_GENERIC,
00737                     "couldn't get volume");
00738 
00739     xmms_volume_map_init (&map);
00740 
00741     /* ask the plugin how much channels it would like to set */
00742     if (!xmms_output_plugin_method_volume_get (output->plugin, output,
00743                                                NULL, NULL, &map.num_channels)) {
00744         return NULL;
00745     }
00746 
00747     /* check for sane values */
00748     g_return_val_if_fail (map.num_channels > 0, NULL);
00749     g_return_val_if_fail (map.num_channels <= VOLUME_MAX_CHANNELS, NULL);
00750 
00751     map.names = g_new (const gchar *, map.num_channels);
00752     map.values = g_new (guint, map.num_channels);
00753 
00754     map.status = xmms_output_plugin_method_volume_get (output->plugin, output,
00755                                                        map.names, map.values,
00756                                                        &map.num_channels);
00757 
00758     if (!map.status || !map.num_channels) {
00759         return NULL; /* error is set (-> no leak) */
00760     }
00761 
00762     ret = xmms_volume_map_to_dict (&map);
00763 
00764     /* success! */
00765     xmms_error_reset (error);
00766 
00767     return ret;
00768 }
00769 
00770 /**
00771  * Get the current playtime in milliseconds.
00772  */
00773 static gint32
00774 xmms_playback_client_playtime (xmms_output_t *output, xmms_error_t *error)
00775 {
00776     guint32 ret;
00777     g_return_val_if_fail (output, 0);
00778 
00779     g_mutex_lock (output->playtime_mutex);
00780     ret = output->played_time;
00781     g_mutex_unlock (output->playtime_mutex);
00782 
00783     return ret;
00784 }
00785 
00786 /* returns the current latency: time left in ms until the data currently read
00787  *                              from the latest xform in the chain will actually be played
00788  */
00789 guint32
00790 xmms_output_latency (xmms_output_t *output)
00791 {
00792     guint ret = 0;
00793     guint buffersize = 0;
00794 
00795     if (output->format) {
00796         /* data already waiting in the ringbuffer */
00797         buffersize += xmms_ringbuf_bytes_used (output->filler_buffer);
00798 
00799         /* latency of the soundcard */
00800         buffersize += xmms_output_plugin_method_latency_get (output->plugin, output);
00801 
00802         ret = xmms_sample_bytes_to_ms (output->format, buffersize);
00803     }
00804 
00805     return ret;
00806 }
00807 
00808 /**
00809  * @internal
00810  */
00811 
00812 static gboolean
00813 xmms_output_status_set (xmms_output_t *output, gint status)
00814 {
00815     gboolean ret = TRUE;
00816 
00817     if (!output->plugin) {
00818         XMMS_DBG ("No plugin to set status on..");
00819         return FALSE;
00820     }
00821 
00822     g_mutex_lock (output->status_mutex);
00823 
00824     if (output->status != status) {
00825         if (status == XMMS_PLAYBACK_STATUS_PAUSE &&
00826             output->status != XMMS_PLAYBACK_STATUS_PLAY) {
00827             XMMS_DBG ("Can only pause from play.");
00828             ret = FALSE;
00829         } else {
00830             output->status = status;
00831 
00832             if (status == XMMS_PLAYBACK_STATUS_STOP) {
00833                 xmms_object_unref (output->format);
00834                 output->format = NULL;
00835             }
00836             if (!xmms_output_plugin_method_status (output->plugin, output, status)) {
00837                 xmms_log_error ("Status method returned an error!");
00838                 output->status = XMMS_PLAYBACK_STATUS_STOP;
00839                 ret = FALSE;
00840             }
00841 
00842             xmms_object_emit_f (XMMS_OBJECT (output),
00843                                 XMMS_IPC_SIGNAL_PLAYBACK_STATUS,
00844                                 XMMSV_TYPE_INT32,
00845                                 output->status);
00846         }
00847     }
00848 
00849     g_mutex_unlock (output->status_mutex);
00850 
00851     return ret;
00852 }
00853 
00854 static void
00855 xmms_output_destroy (xmms_object_t *object)
00856 {
00857     xmms_output_t *output = (xmms_output_t *)object;
00858 
00859     output->monitor_volume_running = FALSE;
00860     if (output->monitor_volume_thread) {
00861         g_thread_join (output->monitor_volume_thread);
00862         output->monitor_volume_thread = NULL;
00863     }
00864 
00865     xmms_output_filler_state (output, FILLER_QUIT);
00866     g_thread_join (output->filler_thread);
00867 
00868     if (output->plugin) {
00869         xmms_output_plugin_method_destroy (output->plugin, output);
00870         xmms_object_unref (output->plugin);
00871     }
00872     xmms_output_format_list_clear (output);
00873 
00874     xmms_object_unref (output->playlist);
00875 
00876     g_mutex_free (output->status_mutex);
00877     g_mutex_free (output->playtime_mutex);
00878     g_mutex_free (output->filler_mutex);
00879     g_cond_free (output->filler_state_cond);
00880     xmms_ringbuf_destroy (output->filler_buffer);
00881 
00882     xmms_ipc_broadcast_unregister ( XMMS_IPC_SIGNAL_PLAYBACK_VOLUME_CHANGED);
00883     xmms_ipc_broadcast_unregister ( XMMS_IPC_SIGNAL_PLAYBACK_STATUS);
00884     xmms_ipc_broadcast_unregister ( XMMS_IPC_SIGNAL_PLAYBACK_CURRENTID);
00885     xmms_ipc_signal_unregister (XMMS_IPC_SIGNAL_PLAYBACK_PLAYTIME);
00886     xmms_ipc_object_unregister (XMMS_IPC_OBJECT_PLAYBACK);
00887 }
00888 
00889 /**
00890  * Switch to another output plugin.
00891  * @param output output pointer
00892  * @param new_plugin the new #xmms_plugin_t to use as output.
00893  * @returns TRUE on success and FALSE on failure
00894  */
00895 gboolean
00896 xmms_output_plugin_switch (xmms_output_t *output, xmms_output_plugin_t *new_plugin)
00897 {
00898     xmms_output_plugin_t *old_plugin;
00899     gboolean ret;
00900 
00901     g_return_val_if_fail (output, FALSE);
00902     g_return_val_if_fail (new_plugin, FALSE);
00903 
00904     xmms_playback_client_stop (output, NULL);
00905 
00906     g_mutex_lock (output->status_mutex);
00907 
00908     old_plugin = output->plugin;
00909 
00910     ret = set_plugin (output, new_plugin);
00911 
00912     /* if the switch succeeded, release the reference to the old plugin
00913      * now.
00914      * if we couldn't switch to the new plugin, but we had a working
00915      * plugin before, switch back to the old plugin.
00916      */
00917     if (ret) {
00918         xmms_object_unref (old_plugin);
00919     } else if (old_plugin) {
00920         XMMS_DBG ("cannot switch plugin, going back to old one");
00921         set_plugin (output, old_plugin);
00922     }
00923 
00924     g_mutex_unlock (output->status_mutex);
00925 
00926     return ret;
00927 }
00928 
00929 /**
00930  * Allocate a new #xmms_output_t
00931  */
00932 xmms_output_t *
00933 xmms_output_new (xmms_output_plugin_t *plugin, xmms_playlist_t *playlist)
00934 {
00935     xmms_output_t *output;
00936     xmms_config_property_t *prop;
00937     gint size;
00938 
00939     g_return_val_if_fail (playlist, NULL);
00940 
00941     XMMS_DBG ("Trying to open output");
00942 
00943     output = xmms_object_new (xmms_output_t, xmms_output_destroy);
00944 
00945     output->playlist = playlist;
00946 
00947     output->status_mutex = g_mutex_new ();
00948     output->playtime_mutex = g_mutex_new ();
00949 
00950     prop = xmms_config_property_register ("output.buffersize", "32768", NULL, NULL);
00951     size = xmms_config_property_get_int (prop);
00952     XMMS_DBG ("Using buffersize %d", size);
00953 
00954     output->filler_mutex = g_mutex_new ();
00955     output->filler_state = FILLER_STOP;
00956     output->filler_state_cond = g_cond_new ();
00957     output->filler_buffer = xmms_ringbuf_new (size);
00958     output->filler_thread = g_thread_create (xmms_output_filler, output, TRUE, NULL);
00959 
00960     xmms_config_property_register ("output.flush_on_pause", "1", NULL, NULL);
00961     xmms_ipc_object_register (XMMS_IPC_OBJECT_PLAYBACK, XMMS_OBJECT (output));
00962 
00963     /* Broadcasts are always transmitted to the client if he
00964      * listens to them. */
00965     xmms_ipc_broadcast_register (XMMS_OBJECT (output),
00966                                  XMMS_IPC_SIGNAL_PLAYBACK_VOLUME_CHANGED);
00967     xmms_ipc_broadcast_register (XMMS_OBJECT (output),
00968                                  XMMS_IPC_SIGNAL_PLAYBACK_STATUS);
00969     xmms_ipc_broadcast_register (XMMS_OBJECT (output),
00970                                  XMMS_IPC_SIGNAL_PLAYBACK_CURRENTID);
00971 
00972     /* Signals are only emitted if the client has a pending question to it
00973      * after the client recivies a signal, he must ask for it again */
00974     xmms_ipc_signal_register (XMMS_OBJECT (output),
00975                               XMMS_IPC_SIGNAL_PLAYBACK_PLAYTIME);
00976 
00977 
00978     xmms_object_cmd_add (XMMS_OBJECT (output),
00979                          XMMS_IPC_CMD_START,
00980                          XMMS_CMD_FUNC (start));
00981     xmms_object_cmd_add (XMMS_OBJECT (output),
00982                          XMMS_IPC_CMD_STOP,
00983                          XMMS_CMD_FUNC (stop));
00984     xmms_object_cmd_add (XMMS_OBJECT (output),
00985                          XMMS_IPC_CMD_PAUSE,
00986                          XMMS_CMD_FUNC (pause));
00987     xmms_object_cmd_add (XMMS_OBJECT (output),
00988                          XMMS_IPC_CMD_DECODER_KILL,
00989                          XMMS_CMD_FUNC (xform_kill));
00990     xmms_object_cmd_add (XMMS_OBJECT (output),
00991                          XMMS_IPC_CMD_CPLAYTIME,
00992                          XMMS_CMD_FUNC (playtime));
00993     xmms_object_cmd_add (XMMS_OBJECT (output),
00994                          XMMS_IPC_CMD_SEEKMS,
00995                          XMMS_CMD_FUNC (seekms));
00996     xmms_object_cmd_add (XMMS_OBJECT (output),
00997                          XMMS_IPC_CMD_SEEKSAMPLES,
00998                          XMMS_CMD_FUNC (seeksamples));
00999     xmms_object_cmd_add (XMMS_OBJECT (output),
01000                          XMMS_IPC_CMD_PLAYBACK_STATUS,
01001                          XMMS_CMD_FUNC (output_status));
01002     xmms_object_cmd_add (XMMS_OBJECT (output),
01003                          XMMS_IPC_CMD_CURRENTID,
01004                          XMMS_CMD_FUNC (currentid));
01005     xmms_object_cmd_add (XMMS_OBJECT (output),
01006                          XMMS_IPC_CMD_VOLUME_SET,
01007                          XMMS_CMD_FUNC (volume_set));
01008     xmms_object_cmd_add (XMMS_OBJECT (output),
01009                          XMMS_IPC_CMD_VOLUME_GET,
01010                          XMMS_CMD_FUNC (volume_get));
01011 
01012     output->status = XMMS_PLAYBACK_STATUS_STOP;
01013 
01014     if (plugin) {
01015         if (!set_plugin (output, plugin)) {
01016             xmms_log_error ("Could not initialize output plugin");
01017         }
01018     } else {
01019         xmms_log_error ("initalized output without a plugin, please fix!");
01020     }
01021 
01022 
01023 
01024     return output;
01025 }
01026 
01027 /**
01028  * Flush the buffers in soundcard.
01029  */
01030 void
01031 xmms_output_flush (xmms_output_t *output)
01032 {
01033     g_return_if_fail (output);
01034 
01035     xmms_output_plugin_method_flush (output->plugin, output);
01036 }
01037 
01038 /**
01039  * @internal
01040  */
01041 static gboolean
01042 xmms_output_format_set (xmms_output_t *output, xmms_stream_type_t *fmt)
01043 {
01044     g_return_val_if_fail (output, FALSE);
01045     g_return_val_if_fail (fmt, FALSE);
01046 
01047     XMMS_DBG ("Setting format!");
01048 
01049     if (!xmms_output_plugin_format_set_always (output->plugin)) {
01050         gboolean ret;
01051 
01052         if (output->format && xmms_stream_type_match (output->format, fmt)) {
01053             XMMS_DBG ("audio formats are equal, not updating");
01054             return TRUE;
01055         }
01056 
01057         ret = xmms_output_plugin_method_format_set (output->plugin, output, fmt);
01058         if (ret) {
01059             xmms_object_unref (output->format);
01060             xmms_object_ref (fmt);
01061             output->format = fmt;
01062         }
01063         return ret;
01064     } else {
01065         if (output->format && !xmms_stream_type_match (output->format, fmt)) {
01066             xmms_object_unref (output->format);
01067             xmms_object_ref (fmt);
01068             output->format = fmt;
01069         }
01070         if (!output->format) {
01071             xmms_object_unref (output->format);
01072             xmms_object_ref (fmt);
01073             output->format = fmt;
01074         }
01075         return xmms_output_plugin_method_format_set (output->plugin, output, output->format);
01076     }
01077 }
01078 
01079 
01080 static gboolean
01081 set_plugin (xmms_output_t *output, xmms_output_plugin_t *plugin)
01082 {
01083     gboolean ret;
01084 
01085     g_assert (output);
01086     g_assert (plugin);
01087 
01088     output->monitor_volume_running = FALSE;
01089     if (output->monitor_volume_thread) {
01090         g_thread_join (output->monitor_volume_thread);
01091         output->monitor_volume_thread = NULL;
01092     }
01093 
01094     if (output->plugin) {
01095         xmms_output_plugin_method_destroy (output->plugin, output);
01096         output->plugin = NULL;
01097     }
01098     xmms_output_format_list_clear (output);
01099 
01100     /* output->plugin needs to be set before we can call the
01101      * NEW method
01102      */
01103     output->plugin = plugin;
01104     ret = xmms_output_plugin_method_new (output->plugin, output);
01105 
01106     if (!ret) {
01107         output->plugin = NULL;
01108     } else if (!output->monitor_volume_thread) {
01109         output->monitor_volume_running = TRUE;
01110         output->monitor_volume_thread = g_thread_create (xmms_output_monitor_volume_thread,
01111                                                          output, TRUE, NULL);
01112     }
01113 
01114     return ret;
01115 }
01116 
01117 static gint
01118 xmms_volume_map_lookup (xmms_volume_map_t *vl, const gchar *name)
01119 {
01120     gint i;
01121 
01122     for (i = 0; i < vl->num_channels; i++) {
01123         if (!strcmp (vl->names[i], name)) {
01124             return i;
01125         }
01126     }
01127 
01128     return -1;
01129 }
01130 
01131 /* returns TRUE when both hashes are equal, else FALSE */
01132 static gboolean
01133 xmms_volume_map_equal (xmms_volume_map_t *a, xmms_volume_map_t *b)
01134 {
01135     guint i;
01136 
01137     g_assert (a);
01138     g_assert (b);
01139 
01140     if (a->num_channels != b->num_channels) {
01141         return FALSE;
01142     }
01143 
01144     for (i = 0; i < a->num_channels; i++) {
01145         gint j;
01146 
01147         j = xmms_volume_map_lookup (b, a->names[i]);
01148         if (j == -1 || b->values[j] != a->values[i]) {
01149             return FALSE;
01150         }
01151     }
01152 
01153     return TRUE;
01154 }
01155 
01156 static void
01157 xmms_volume_map_init (xmms_volume_map_t *vl)
01158 {
01159     vl->status = FALSE;
01160     vl->num_channels = 0;
01161     vl->names = NULL;
01162     vl->values = NULL;
01163 }
01164 
01165 static void
01166 xmms_volume_map_free (xmms_volume_map_t *vl)
01167 {
01168     g_free (vl->names);
01169     g_free (vl->values);
01170 
01171     /* don't free vl here, its always allocated on the stack */
01172 }
01173 
01174 static void
01175 xmms_volume_map_copy (xmms_volume_map_t *src, xmms_volume_map_t *dst)
01176 {
01177     dst->num_channels = src->num_channels;
01178     dst->status = src->status;
01179 
01180     if (!src->status) {
01181         g_free (dst->names);
01182         dst->names = NULL;
01183 
01184         g_free (dst->values);
01185         dst->values = NULL;
01186 
01187         return;
01188     }
01189 
01190     dst->names = g_renew (const gchar *, dst->names, src->num_channels);
01191     dst->values = g_renew (guint, dst->values, src->num_channels);
01192 
01193     memcpy (dst->names, src->names, src->num_channels * sizeof (gchar *));
01194     memcpy (dst->values, src->values, src->num_channels * sizeof (guint));
01195 }
01196 
01197 static GTree *
01198 xmms_volume_map_to_dict (xmms_volume_map_t *vl)
01199 {
01200     GTree *ret;
01201     gint i;
01202 
01203     ret = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
01204                            NULL, (GDestroyNotify) xmmsv_unref);
01205     if (!ret) {
01206         return NULL;
01207     }
01208 
01209     for (i = 0; i < vl->num_channels; i++) {
01210         xmmsv_t *val;
01211 
01212         val = xmmsv_new_int (vl->values[i]);
01213         g_tree_replace (ret, (gpointer) vl->names[i], val);
01214     }
01215 
01216     return ret;
01217 }
01218 
01219 static gpointer
01220 xmms_output_monitor_volume_thread (gpointer data)
01221 {
01222     GTree *dict;
01223     xmms_output_t *output = data;
01224     xmms_volume_map_t old, cur;
01225 
01226     if (!xmms_output_plugin_method_volume_get_available (output->plugin)) {
01227         return NULL;
01228     }
01229 
01230     xmms_volume_map_init (&old);
01231     xmms_volume_map_init (&cur);
01232 
01233     while (output->monitor_volume_running) {
01234         cur.num_channels = 0;
01235         cur.status = xmms_output_plugin_method_volume_get (output->plugin,
01236                                                            output, NULL, NULL,
01237                                                            &cur.num_channels);
01238 
01239         if (cur.status) {
01240             /* check for sane values */
01241             if (cur.num_channels < 1 ||
01242                 cur.num_channels > VOLUME_MAX_CHANNELS) {
01243                 cur.status = FALSE;
01244             } else {
01245                 cur.names = g_renew (const gchar *, cur.names,
01246                                      cur.num_channels);
01247                 cur.values = g_renew (guint, cur.values, cur.num_channels);
01248             }
01249         }
01250 
01251         if (cur.status) {
01252             cur.status =
01253                 xmms_output_plugin_method_volume_get (output->plugin,
01254                                                       output, cur.names,
01255                                                       cur.values,
01256                                                       &cur.num_channels);
01257         }
01258 
01259         /* we failed at getting volume for one of the two maps or
01260          * we succeeded both times and they differ -> changed
01261          */
01262         if ((cur.status ^ old.status) ||
01263             (cur.status && old.status &&
01264              !xmms_volume_map_equal (&old, &cur))) {
01265             /* emit the broadcast */
01266             if (cur.status) {
01267                 dict = xmms_volume_map_to_dict (&cur);
01268                 xmms_object_emit_f (XMMS_OBJECT (output),
01269                                     XMMS_IPC_SIGNAL_PLAYBACK_VOLUME_CHANGED,
01270                                     XMMSV_TYPE_DICT, dict);
01271                 g_tree_destroy (dict);
01272             } else {
01273                 /** @todo When bug 691 is solved, emit an error here */
01274                 xmms_object_emit_f (XMMS_OBJECT (output),
01275                                     XMMS_IPC_SIGNAL_PLAYBACK_VOLUME_CHANGED,
01276                                     XMMSV_TYPE_NONE);
01277             }
01278         }
01279 
01280         xmms_volume_map_copy (&cur, &old);
01281 
01282         g_usleep (G_USEC_PER_SEC);
01283     }
01284 
01285     xmms_volume_map_free (&old);
01286     xmms_volume_map_free (&cur);
01287 
01288     return NULL;
01289 }
01290 
01291 /** @} */