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