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