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