• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

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

Generated on Wed Feb 9 2011 for XMMS2 by  doxygen 1.7.1