00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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 guint 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 *, UINT32, NONE, NONE);
00085 XMMS_CMD_DEFINE (seekms, xmms_output_seekms, xmms_output_t *, NONE, UINT32, 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, UINT32, 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 *, UINT32, NONE, NONE);
00090 XMMS_CMD_DEFINE (currentid, xmms_output_current_id, xmms_output_t *, UINT32, NONE, NONE);
00091 XMMS_CMD_DEFINE (volume_set, xmms_output_volume_set, xmms_output_t *, NONE, STRING, UINT32);
00092 XMMS_CMD_DEFINE (volume_get, xmms_output_volume_get, xmms_output_t *, DICT, NONE, NONE);
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
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
00137
00138 GMutex *status_mutex;
00139 guint status;
00140
00141 xmms_playlist_t *playlist;
00142
00143
00144 GList *format_list;
00145
00146 xmms_stream_type_t *format;
00147
00148
00149
00150
00151
00152 guint64 bytes_written;
00153
00154
00155
00156
00157 gint32 buffer_underruns;
00158
00159 GThread *monitor_volume_thread;
00160 gboolean monitor_volume_running;
00161 };
00162
00163
00164
00165
00166
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 ret)
00204 {
00205 guint buffersize = 0;
00206
00207 g_mutex_lock (output->playtime_mutex);
00208 output->played += ret;
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 XMMS_OBJECT_CMD_ARG_UINT32,
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
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 XMMS_OBJECT_CMD_ARG_UINT32,
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);
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
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
00546
00547
00548
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
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 guint
00630 xmms_output_status (xmms_output_t *output, xmms_error_t *error)
00631 {
00632 guint 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 guint
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
00699 if (!xmms_output_plugin_method_volume_get (output->plugin, output,
00700 NULL, NULL, &map.num_channels)) {
00701 return NULL;
00702 }
00703
00704
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;
00717 }
00718
00719 ret = xmms_volume_map_to_dict (&map);
00720
00721
00722 xmms_error_reset (error);
00723
00724 return ret;
00725 }
00726
00727
00728
00729
00730 guint32
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
00744
00745
00746
00747
00748 static gboolean
00749 xmms_output_status_set (xmms_output_t *output, gint status)
00750 {
00751 gboolean ret = TRUE;
00752
00753 if (!output->plugin) {
00754 XMMS_DBG ("No plugin to set status on..");
00755 return FALSE;
00756 }
00757
00758 g_mutex_lock (output->status_mutex);
00759
00760 if (output->status != status) {
00761 if (status == XMMS_PLAYBACK_STATUS_PAUSE &&
00762 output->status != XMMS_PLAYBACK_STATUS_PLAY) {
00763 XMMS_DBG ("Can only pause from play.");
00764 ret = FALSE;
00765 } else {
00766 output->status = status;
00767
00768 if (status == XMMS_PLAYBACK_STATUS_STOP) {
00769 xmms_object_unref (output->format);
00770 output->format = NULL;
00771 }
00772 if (!xmms_output_plugin_method_status (output->plugin, output, status)) {
00773 xmms_log_error ("Status method returned an error!");
00774 output->status = XMMS_PLAYBACK_STATUS_STOP;
00775 ret = FALSE;
00776 }
00777
00778 xmms_object_emit_f (XMMS_OBJECT (output),
00779 XMMS_IPC_SIGNAL_PLAYBACK_STATUS,
00780 XMMS_OBJECT_CMD_ARG_UINT32,
00781 output->status);
00782 }
00783 }
00784
00785 g_mutex_unlock (output->status_mutex);
00786
00787 return ret;
00788 }
00789
00790 static void
00791 xmms_output_destroy (xmms_object_t *object)
00792 {
00793 xmms_output_t *output = (xmms_output_t *)object;
00794
00795 output->monitor_volume_running = FALSE;
00796 if (output->monitor_volume_thread) {
00797 g_thread_join (output->monitor_volume_thread);
00798 output->monitor_volume_thread = NULL;
00799 }
00800
00801 xmms_output_filler_state (output, FILLER_QUIT);
00802 g_thread_join (output->filler_thread);
00803
00804 if (output->plugin) {
00805 xmms_output_plugin_method_destroy (output->plugin, output);
00806 xmms_object_unref (output->plugin);
00807 }
00808
00809 xmms_object_unref (output->playlist);
00810
00811 g_mutex_free (output->status_mutex);
00812 g_mutex_free (output->playtime_mutex);
00813 g_mutex_free (output->filler_mutex);
00814 g_cond_free (output->filler_state_cond);
00815 xmms_ringbuf_destroy (output->filler_buffer);
00816
00817 xmms_ipc_broadcast_unregister ( XMMS_IPC_SIGNAL_OUTPUT_VOLUME_CHANGED);
00818 xmms_ipc_broadcast_unregister ( XMMS_IPC_SIGNAL_PLAYBACK_STATUS);
00819 xmms_ipc_broadcast_unregister ( XMMS_IPC_SIGNAL_OUTPUT_CURRENTID);
00820 xmms_ipc_signal_unregister (XMMS_IPC_SIGNAL_OUTPUT_PLAYTIME);
00821 xmms_ipc_object_unregister (XMMS_IPC_OBJECT_OUTPUT);
00822 }
00823
00824
00825
00826
00827
00828
00829
00830 gboolean
00831 xmms_output_plugin_switch (xmms_output_t *output, xmms_output_plugin_t *new_plugin)
00832 {
00833 xmms_output_plugin_t *old_plugin;
00834 gboolean ret;
00835
00836 g_return_val_if_fail (output, FALSE);
00837 g_return_val_if_fail (new_plugin, FALSE);
00838
00839 xmms_output_stop (output, NULL);
00840
00841 g_mutex_lock (output->status_mutex);
00842
00843 old_plugin = output->plugin;
00844
00845 ret = set_plugin (output, new_plugin);
00846
00847
00848
00849
00850
00851
00852 if (ret) {
00853 xmms_object_unref (old_plugin);
00854 } else if (old_plugin) {
00855 XMMS_DBG ("cannot switch plugin, going back to old one");
00856 set_plugin (output, old_plugin);
00857 }
00858
00859 g_mutex_unlock (output->status_mutex);
00860
00861 return ret;
00862 }
00863
00864
00865
00866
00867 xmms_output_t *
00868 xmms_output_new (xmms_output_plugin_t *plugin, xmms_playlist_t *playlist)
00869 {
00870 xmms_output_t *output;
00871 xmms_config_property_t *prop;
00872 gint size;
00873
00874 g_return_val_if_fail (playlist, NULL);
00875
00876 XMMS_DBG ("Trying to open output");
00877
00878 output = xmms_object_new (xmms_output_t, xmms_output_destroy);
00879
00880 output->playlist = playlist;
00881
00882 output->status_mutex = g_mutex_new ();
00883 output->playtime_mutex = g_mutex_new ();
00884
00885 prop = xmms_config_property_register ("output.buffersize", "32768", NULL, NULL);
00886 size = xmms_config_property_get_int (prop);
00887 XMMS_DBG ("Using buffersize %d", size);
00888
00889 output->filler_mutex = g_mutex_new ();
00890 output->filler_state = FILLER_STOP;
00891 output->filler_state_cond = g_cond_new ();
00892 output->filler_buffer = xmms_ringbuf_new (size);
00893 output->filler_thread = g_thread_create (xmms_output_filler, output, TRUE, NULL);
00894
00895 xmms_config_property_register ("output.flush_on_pause", "1", NULL, NULL);
00896 xmms_ipc_object_register (XMMS_IPC_OBJECT_OUTPUT, XMMS_OBJECT (output));
00897
00898
00899
00900 xmms_ipc_broadcast_register (XMMS_OBJECT (output),
00901 XMMS_IPC_SIGNAL_OUTPUT_VOLUME_CHANGED);
00902 xmms_ipc_broadcast_register (XMMS_OBJECT (output),
00903 XMMS_IPC_SIGNAL_PLAYBACK_STATUS);
00904 xmms_ipc_broadcast_register (XMMS_OBJECT (output),
00905 XMMS_IPC_SIGNAL_OUTPUT_CURRENTID);
00906
00907
00908
00909 xmms_ipc_signal_register (XMMS_OBJECT (output),
00910 XMMS_IPC_SIGNAL_OUTPUT_PLAYTIME);
00911
00912
00913 xmms_object_cmd_add (XMMS_OBJECT (output),
00914 XMMS_IPC_CMD_START,
00915 XMMS_CMD_FUNC (start));
00916 xmms_object_cmd_add (XMMS_OBJECT (output),
00917 XMMS_IPC_CMD_STOP,
00918 XMMS_CMD_FUNC (stop));
00919 xmms_object_cmd_add (XMMS_OBJECT (output),
00920 XMMS_IPC_CMD_PAUSE,
00921 XMMS_CMD_FUNC (pause));
00922 xmms_object_cmd_add (XMMS_OBJECT (output),
00923 XMMS_IPC_CMD_DECODER_KILL,
00924 XMMS_CMD_FUNC (xform_kill));
00925 xmms_object_cmd_add (XMMS_OBJECT (output),
00926 XMMS_IPC_CMD_CPLAYTIME,
00927 XMMS_CMD_FUNC (playtime));
00928 xmms_object_cmd_add (XMMS_OBJECT (output),
00929 XMMS_IPC_CMD_SEEKMS,
00930 XMMS_CMD_FUNC (seekms));
00931 xmms_object_cmd_add (XMMS_OBJECT (output),
00932 XMMS_IPC_CMD_SEEKMS_REL,
00933 XMMS_CMD_FUNC (seekms_rel));
00934 xmms_object_cmd_add (XMMS_OBJECT (output),
00935 XMMS_IPC_CMD_SEEKSAMPLES,
00936 XMMS_CMD_FUNC (seeksamples));
00937 xmms_object_cmd_add (XMMS_OBJECT (output),
00938 XMMS_IPC_CMD_SEEKSAMPLES_REL,
00939 XMMS_CMD_FUNC (seeksamples_rel));
00940 xmms_object_cmd_add (XMMS_OBJECT (output),
00941 XMMS_IPC_CMD_OUTPUT_STATUS,
00942 XMMS_CMD_FUNC (output_status));
00943 xmms_object_cmd_add (XMMS_OBJECT (output),
00944 XMMS_IPC_CMD_CURRENTID,
00945 XMMS_CMD_FUNC (currentid));
00946 xmms_object_cmd_add (XMMS_OBJECT (output),
00947 XMMS_IPC_CMD_VOLUME_SET,
00948 XMMS_CMD_FUNC (volume_set));
00949 xmms_object_cmd_add (XMMS_OBJECT (output),
00950 XMMS_IPC_CMD_VOLUME_GET,
00951 XMMS_CMD_FUNC (volume_get));
00952
00953 output->status = XMMS_PLAYBACK_STATUS_STOP;
00954
00955 if (plugin) {
00956 if (!set_plugin (output, plugin)) {
00957 xmms_log_error ("Could not initialize output plugin");
00958 }
00959 } else {
00960 xmms_log_error ("initalized output without a plugin, please fix!");
00961 }
00962
00963
00964
00965 return output;
00966 }
00967
00968
00969
00970
00971 void
00972 xmms_output_flush (xmms_output_t *output)
00973 {
00974 g_return_if_fail (output);
00975
00976 xmms_output_plugin_method_flush (output->plugin, output);
00977 }
00978
00979
00980
00981
00982 static gboolean
00983 xmms_output_format_set (xmms_output_t *output, xmms_stream_type_t *fmt)
00984 {
00985 g_return_val_if_fail (output, FALSE);
00986 g_return_val_if_fail (fmt, FALSE);
00987
00988 XMMS_DBG ("Setting format!");
00989
00990 if (!xmms_output_plugin_format_set_always (output->plugin)) {
00991 if (output->format && xmms_stream_type_match (output->format, fmt)) {
00992 XMMS_DBG ("audio formats are equal, not updating");
00993 return TRUE;
00994 }
00995
00996 xmms_object_unref (output->format);
00997 xmms_object_ref (fmt);
00998 output->format = fmt;
00999 return xmms_output_plugin_method_format_set (output->plugin, output, output->format);
01000 } else {
01001 if (output->format && !xmms_stream_type_match (output->format, fmt)) {
01002 xmms_object_unref (output->format);
01003 xmms_object_ref (fmt);
01004 output->format = fmt;
01005 }
01006 if (!output->format) {
01007 xmms_object_unref (output->format);
01008 xmms_object_ref (fmt);
01009 output->format = fmt;
01010 }
01011 return xmms_output_plugin_method_format_set (output->plugin, output, output->format);
01012 }
01013 }
01014
01015
01016 static gboolean
01017 set_plugin (xmms_output_t *output, xmms_output_plugin_t *plugin)
01018 {
01019 gboolean ret;
01020
01021 g_assert (output);
01022 g_assert (plugin);
01023
01024 output->monitor_volume_running = FALSE;
01025 if (output->monitor_volume_thread) {
01026 g_thread_join (output->monitor_volume_thread);
01027 output->monitor_volume_thread = NULL;
01028 }
01029
01030 if (output->plugin) {
01031 xmms_output_plugin_method_destroy (output->plugin, output);
01032 output->plugin = NULL;
01033 }
01034
01035
01036
01037
01038 output->plugin = plugin;
01039 ret = xmms_output_plugin_method_new (output->plugin, output);
01040
01041 if (!ret) {
01042 output->plugin = NULL;
01043 } else if (!output->monitor_volume_thread) {
01044 output->monitor_volume_running = TRUE;
01045 output->monitor_volume_thread = g_thread_create (xmms_output_monitor_volume_thread,
01046 output, TRUE, NULL);
01047 }
01048
01049 return ret;
01050 }
01051
01052 static gint
01053 xmms_volume_map_lookup (xmms_volume_map_t *vl, const gchar *name)
01054 {
01055 gint i;
01056
01057 for (i = 0; i < vl->num_channels; i++) {
01058 if (!strcmp (vl->names[i], name)) {
01059 return i;
01060 }
01061 }
01062
01063 return -1;
01064 }
01065
01066
01067 static gboolean
01068 xmms_volume_map_equal (xmms_volume_map_t *a, xmms_volume_map_t *b)
01069 {
01070 guint i;
01071
01072 g_assert (a);
01073 g_assert (b);
01074
01075 if (a->num_channels != b->num_channels) {
01076 return FALSE;
01077 }
01078
01079 for (i = 0; i < a->num_channels; i++) {
01080 gint j;
01081
01082 j = xmms_volume_map_lookup (b, a->names[i]);
01083 if (j == -1 || b->values[j] != a->values[i]) {
01084 return FALSE;
01085 }
01086 }
01087
01088 return TRUE;
01089 }
01090
01091 static void
01092 xmms_volume_map_init (xmms_volume_map_t *vl)
01093 {
01094 vl->status = FALSE;
01095 vl->num_channels = 0;
01096 vl->names = NULL;
01097 vl->values = NULL;
01098 }
01099
01100 static void
01101 xmms_volume_map_free (xmms_volume_map_t *vl)
01102 {
01103 g_free (vl->names);
01104 g_free (vl->values);
01105
01106
01107 }
01108
01109 static void
01110 xmms_volume_map_copy (xmms_volume_map_t *src, xmms_volume_map_t *dst)
01111 {
01112 dst->num_channels = src->num_channels;
01113 dst->status = src->status;
01114
01115 if (!src->status) {
01116 g_free (dst->names);
01117 dst->names = NULL;
01118
01119 g_free (dst->values);
01120 dst->values = NULL;
01121
01122 return;
01123 }
01124
01125 dst->names = g_renew (const gchar *, dst->names, src->num_channels);
01126 dst->values = g_renew (guint, dst->values, src->num_channels);
01127
01128 memcpy (dst->names, src->names, src->num_channels * sizeof (gchar *));
01129 memcpy (dst->values, src->values, src->num_channels * sizeof (guint));
01130 }
01131
01132 static GTree *
01133 xmms_volume_map_to_dict (xmms_volume_map_t *vl)
01134 {
01135 GTree *ret;
01136 gint i;
01137
01138 ret = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
01139 NULL, (GDestroyNotify)xmms_object_cmd_value_unref);
01140 if (!ret) {
01141 return NULL;
01142 }
01143
01144 for (i = 0; i < vl->num_channels; i++) {
01145 xmms_object_cmd_value_t *val;
01146
01147 val = xmms_object_cmd_value_uint_new (vl->values[i]);
01148 g_tree_replace (ret, (gpointer) vl->names[i], val);
01149 }
01150
01151 return ret;
01152 }
01153
01154 static gpointer
01155 xmms_output_monitor_volume_thread (gpointer data)
01156 {
01157 GTree *dict;
01158 xmms_output_t *output = data;
01159 xmms_volume_map_t old, cur;
01160
01161 if (!xmms_output_plugin_method_volume_get_available (output->plugin)) {
01162 return NULL;
01163 }
01164
01165 xmms_volume_map_init (&old);
01166 xmms_volume_map_init (&cur);
01167
01168 while (output->monitor_volume_running) {
01169 cur.num_channels = 0;
01170 cur.status = xmms_output_plugin_method_volume_get (output->plugin,
01171 output, NULL, NULL,
01172 &cur.num_channels);
01173
01174 if (cur.status) {
01175
01176 if (cur.num_channels < 1 ||
01177 cur.num_channels > VOLUME_MAX_CHANNELS) {
01178 cur.status = FALSE;
01179 } else {
01180 cur.names = g_renew (const gchar *, cur.names,
01181 cur.num_channels);
01182 cur.values = g_renew (guint, cur.values, cur.num_channels);
01183 }
01184 }
01185
01186 if (cur.status) {
01187 cur.status =
01188 xmms_output_plugin_method_volume_get (output->plugin,
01189 output, cur.names,
01190 cur.values,
01191 &cur.num_channels);
01192 }
01193
01194
01195
01196
01197 if ((cur.status ^ old.status) ||
01198 (cur.status && old.status &&
01199 !xmms_volume_map_equal (&old, &cur))) {
01200
01201 if (cur.status) {
01202 dict = xmms_volume_map_to_dict (&cur);
01203 xmms_object_emit_f (XMMS_OBJECT (output),
01204 XMMS_IPC_SIGNAL_OUTPUT_VOLUME_CHANGED,
01205 XMMS_OBJECT_CMD_ARG_DICT, dict);
01206 g_tree_destroy (dict);
01207 } else {
01208
01209 xmms_object_emit_f (XMMS_OBJECT (output),
01210 XMMS_IPC_SIGNAL_OUTPUT_VOLUME_CHANGED,
01211 XMMS_OBJECT_CMD_ARG_NONE);
01212 }
01213 }
01214
01215 xmms_volume_map_copy (&cur, &old);
01216
01217 g_usleep (G_USEC_PER_SEC);
01218 }
01219
01220 xmms_volume_map_free (&old);
01221 xmms_volume_map_free (&cur);
01222
01223 return NULL;
01224 }
01225
01226