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 gint32 xmms_output_status (xmms_output_t *output, xmms_error_t *error);
00058
00059 typedef enum xmms_output_filler_state_E {
00060 FILLER_STOP,
00061 FILLER_RUN,
00062 FILLER_QUIT,
00063 FILLER_KILL,
00064 FILLER_SEEK,
00065 } xmms_output_filler_state_t;
00066
00067 static void xmms_output_volume_set (xmms_output_t *output, const gchar *channel, guint volume, xmms_error_t *error);
00068 static GTree *xmms_output_volume_get (xmms_output_t *output, xmms_error_t *error);
00069 static void xmms_output_filler_state (xmms_output_t *output, xmms_output_filler_state_t state);
00070 static void xmms_output_filler_state_nolock (xmms_output_t *output, xmms_output_filler_state_t state);
00071
00072 static void xmms_volume_map_init (xmms_volume_map_t *vl);
00073 static void xmms_volume_map_free (xmms_volume_map_t *vl);
00074 static void xmms_volume_map_copy (xmms_volume_map_t *src, xmms_volume_map_t *dst);
00075 static GTree *xmms_volume_map_to_dict (xmms_volume_map_t *vl);
00076
00077 static gboolean xmms_output_status_set (xmms_output_t *output, gint status);
00078 static gboolean set_plugin (xmms_output_t *output, xmms_output_plugin_t *plugin);
00079
00080 XMMS_CMD_DEFINE (start, xmms_output_start, xmms_output_t *, NONE, NONE, NONE);
00081 XMMS_CMD_DEFINE (stop, xmms_output_stop, xmms_output_t *, NONE, NONE, NONE);
00082 XMMS_CMD_DEFINE (pause, xmms_output_pause, xmms_output_t *, NONE, NONE, NONE);
00083 XMMS_CMD_DEFINE (xform_kill, xmms_output_xform_kill, xmms_output_t *, NONE, NONE, NONE);
00084 XMMS_CMD_DEFINE (playtime, xmms_output_playtime, xmms_output_t *, INT32, NONE, NONE);
00085 XMMS_CMD_DEFINE (seekms, xmms_output_seekms, xmms_output_t *, NONE, INT32, NONE);
00086 XMMS_CMD_DEFINE (seekms_rel, xmms_output_seekms_rel, xmms_output_t *, NONE, INT32, NONE);
00087 XMMS_CMD_DEFINE (seeksamples, xmms_output_seeksamples, xmms_output_t *, NONE, INT32, NONE);
00088 XMMS_CMD_DEFINE (seeksamples_rel, xmms_output_seeksamples_rel, xmms_output_t *, NONE, INT32, NONE);
00089 XMMS_CMD_DEFINE (output_status, xmms_output_status, xmms_output_t *, INT32, NONE, NONE);
00090 XMMS_CMD_DEFINE (currentid, xmms_output_current_id, xmms_output_t *, INT32, NONE, NONE);
00091 XMMS_CMD_DEFINE (volume_set, xmms_output_volume_set, xmms_output_t *, NONE, STRING, INT32);
00092 XMMS_CMD_DEFINE (volume_get, xmms_output_volume_get, xmms_output_t *, DICT, NONE, NONE);
00093
00094
00095
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 advance)
00204 {
00205 guint buffersize = 0;
00206
00207 g_mutex_lock (output->playtime_mutex);
00208 output->played += advance;
00209 g_mutex_unlock (output->playtime_mutex);
00210
00211 buffersize = xmms_output_plugin_method_latency_get (output->plugin, output);
00212
00213 if (output->played < buffersize) {
00214 buffersize = output->played;
00215 }
00216
00217 g_mutex_lock (output->playtime_mutex);
00218
00219 if (output->format) {
00220 guint ms = xmms_sample_bytes_to_ms (output->format,
00221 output->played - buffersize);
00222 if ((ms / 100) != (output->played_time / 100)) {
00223 xmms_object_emit_f (XMMS_OBJECT (output),
00224 XMMS_IPC_SIGNAL_OUTPUT_PLAYTIME,
00225 XMMSV_TYPE_INT32,
00226 ms);
00227 }
00228 output->played_time = ms;
00229
00230 }
00231
00232 g_mutex_unlock (output->playtime_mutex);
00233
00234 }
00235
00236 void
00237 xmms_output_set_error (xmms_output_t *output, xmms_error_t *error)
00238 {
00239 g_return_if_fail (output);
00240
00241 xmms_output_status_set (output, XMMS_PLAYBACK_STATUS_STOP);
00242
00243 if (error) {
00244 xmms_log_error ("Output plugin %s reported error, '%s'",
00245 xmms_plugin_shortname_get ((xmms_plugin_t *)output->plugin),
00246 xmms_error_message_get (error));
00247 }
00248 }
00249
00250 typedef struct {
00251 xmms_output_t *output;
00252 xmms_xform_t *chain;
00253 gboolean flush;
00254 } xmms_output_song_changed_arg_t;
00255
00256 static void
00257 song_changed_arg_free (void *data)
00258 {
00259 xmms_output_song_changed_arg_t *arg = (xmms_output_song_changed_arg_t *)data;
00260 xmms_object_unref (arg->chain);
00261 g_free (arg);
00262 }
00263
00264 static gboolean
00265 song_changed (void *data)
00266 {
00267
00268 xmms_output_song_changed_arg_t *arg = (xmms_output_song_changed_arg_t *)data;
00269 xmms_medialib_entry_t entry;
00270
00271 entry = xmms_xform_entry_get (arg->chain);
00272
00273 XMMS_DBG ("Running hotspot! Song changed!! %d", entry);
00274
00275 arg->output->played = 0;
00276 arg->output->current_entry = entry;
00277
00278 if (!xmms_output_format_set (arg->output, xmms_xform_outtype_get (arg->chain))) {
00279 XMMS_DBG ("Couldn't set format, stopping filler..");
00280 xmms_output_filler_state_nolock (arg->output, FILLER_STOP);
00281 xmms_ringbuf_set_eos (arg->output->filler_buffer, TRUE);
00282 return FALSE;
00283 }
00284
00285 if (arg->flush)
00286 xmms_output_flush (arg->output);
00287
00288 xmms_object_emit_f (XMMS_OBJECT (arg->output),
00289 XMMS_IPC_SIGNAL_OUTPUT_CURRENTID,
00290 XMMSV_TYPE_INT32,
00291 entry);
00292
00293 return TRUE;
00294 }
00295
00296 static gboolean
00297 seek_done (void *data)
00298 {
00299 xmms_output_t *output = (xmms_output_t *)data;
00300
00301 g_mutex_lock (output->playtime_mutex);
00302 output->played = output->filler_seek * xmms_sample_frame_size_get (output->format);
00303 output->toskip = output->filler_skip * xmms_sample_frame_size_get (output->format);
00304 g_mutex_unlock (output->playtime_mutex);
00305
00306 xmms_output_flush (output);
00307 return TRUE;
00308 }
00309
00310 static void
00311 xmms_output_filler_state_nolock (xmms_output_t *output, xmms_output_filler_state_t state)
00312 {
00313 output->filler_state = state;
00314 g_cond_signal (output->filler_state_cond);
00315 if (state == FILLER_QUIT || state == FILLER_STOP || state == FILLER_KILL) {
00316 xmms_ringbuf_clear (output->filler_buffer);
00317 }
00318 if (state != FILLER_STOP) {
00319 xmms_ringbuf_set_eos (output->filler_buffer, FALSE);
00320 }
00321 }
00322
00323 static void
00324 xmms_output_filler_state (xmms_output_t *output, xmms_output_filler_state_t state)
00325 {
00326 g_mutex_lock (output->filler_mutex);
00327 xmms_output_filler_state_nolock (output, state);
00328 g_mutex_unlock (output->filler_mutex);
00329 }
00330 static void
00331 xmms_output_filler_seek_state (xmms_output_t *output, guint32 samples)
00332 {
00333 g_mutex_lock (output->filler_mutex);
00334 output->filler_state = FILLER_SEEK;
00335 output->filler_seek = samples;
00336 g_cond_signal (output->filler_state_cond);
00337 g_mutex_unlock (output->filler_mutex);
00338 }
00339
00340 static void *
00341 xmms_output_filler (void *arg)
00342 {
00343 xmms_output_t *output = (xmms_output_t *)arg;
00344 xmms_xform_t *chain = NULL;
00345 gboolean last_was_kill = FALSE;
00346 char buf[4096];
00347 xmms_error_t err;
00348 gint ret;
00349
00350 xmms_error_reset (&err);
00351
00352 g_mutex_lock (output->filler_mutex);
00353 while (output->filler_state != FILLER_QUIT) {
00354 if (output->filler_state == FILLER_STOP) {
00355 if (chain) {
00356 xmms_object_unref (chain);
00357 chain = NULL;
00358 }
00359 xmms_ringbuf_set_eos (output->filler_buffer, TRUE);
00360 g_cond_wait (output->filler_state_cond, output->filler_mutex);
00361 last_was_kill = FALSE;
00362 continue;
00363 }
00364 if (output->filler_state == FILLER_KILL) {
00365 if (chain) {
00366 xmms_object_unref (chain);
00367 chain = NULL;
00368 output->filler_state = FILLER_RUN;
00369 last_was_kill = TRUE;
00370 } else {
00371 output->filler_state = FILLER_STOP;
00372 }
00373 continue;
00374 }
00375 if (output->filler_state == FILLER_SEEK) {
00376 if (!chain) {
00377 XMMS_DBG ("Seek without chain, ignoring..");
00378 output->filler_state = FILLER_STOP;
00379 continue;
00380 }
00381
00382 ret = xmms_xform_this_seek (chain, output->filler_seek, XMMS_XFORM_SEEK_SET, &err);
00383 if (ret == -1) {
00384 XMMS_DBG ("Seeking failed: %s", xmms_error_message_get (&err));
00385 } else {
00386 XMMS_DBG ("Seek ok! %d", ret);
00387
00388 output->filler_skip = output->filler_seek - ret;
00389 if (output->filler_skip < 0) {
00390 XMMS_DBG ("Seeked %d samples too far! Updating position...",
00391 -output->filler_skip);
00392
00393 output->filler_skip = 0;
00394 output->filler_seek = ret;
00395 }
00396
00397 xmms_ringbuf_clear (output->filler_buffer);
00398 xmms_ringbuf_hotspot_set (output->filler_buffer, seek_done, NULL, output);
00399 }
00400 output->filler_state = FILLER_RUN;
00401 }
00402
00403 if (!chain) {
00404 xmms_medialib_entry_t entry;
00405 xmms_output_song_changed_arg_t *arg;
00406 xmms_medialib_session_t *session;
00407
00408 g_mutex_unlock (output->filler_mutex);
00409
00410 entry = xmms_playlist_current_entry (output->playlist);
00411 if (!entry) {
00412 XMMS_DBG ("No entry from playlist!");
00413 output->filler_state = FILLER_STOP;
00414 g_mutex_lock (output->filler_mutex);
00415 continue;
00416 }
00417
00418 chain = xmms_xform_chain_setup (entry, output->format_list, FALSE);
00419 if (!chain) {
00420 session = xmms_medialib_begin_write ();
00421 if (xmms_medialib_entry_property_get_int (session, entry, XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS) == XMMS_MEDIALIB_ENTRY_STATUS_NEW) {
00422 xmms_medialib_end (session);
00423 xmms_medialib_entry_remove (entry);
00424 } else {
00425 xmms_medialib_entry_status_set (session, entry, XMMS_MEDIALIB_ENTRY_STATUS_NOT_AVAILABLE);
00426 xmms_medialib_entry_send_update (entry);
00427 xmms_medialib_end (session);
00428 }
00429
00430 if (!xmms_playlist_advance (output->playlist)) {
00431 XMMS_DBG ("End of playlist");
00432 output->filler_state = FILLER_STOP;
00433 }
00434 g_mutex_lock (output->filler_mutex);
00435 continue;
00436 }
00437
00438 arg = g_new0 (xmms_output_song_changed_arg_t, 1);
00439 arg->output = output;
00440 arg->chain = chain;
00441 arg->flush = last_was_kill;
00442 xmms_object_ref (chain);
00443
00444 last_was_kill = FALSE;
00445
00446 g_mutex_lock (output->filler_mutex);
00447 xmms_ringbuf_hotspot_set (output->filler_buffer, song_changed, song_changed_arg_free, arg);
00448 }
00449
00450 xmms_ringbuf_wait_free (output->filler_buffer, sizeof (buf), output->filler_mutex);
00451
00452 if (output->filler_state != FILLER_RUN) {
00453 XMMS_DBG ("State changed while waiting...");
00454 continue;
00455 }
00456 g_mutex_unlock (output->filler_mutex);
00457
00458 ret = xmms_xform_this_read (chain, buf, sizeof (buf), &err);
00459
00460 g_mutex_lock (output->filler_mutex);
00461
00462 if (ret > 0) {
00463 gint skip = MIN (ret, output->toskip);
00464
00465 output->toskip -= skip;
00466 if (ret > skip) {
00467 xmms_ringbuf_write_wait (output->filler_buffer,
00468 buf + skip,
00469 ret - skip,
00470 output->filler_mutex);
00471 }
00472 } else {
00473 if (ret == -1) {
00474
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 gint32
00630 xmms_output_status (xmms_output_t *output, xmms_error_t *error)
00631 {
00632 gint32 ret;
00633 g_return_val_if_fail (output, XMMS_PLAYBACK_STATUS_STOP);
00634
00635 g_mutex_lock (output->status_mutex);
00636 ret = output->status;
00637 g_mutex_unlock (output->status_mutex);
00638 return ret;
00639 }
00640
00641 gint
00642 xmms_output_current_id (xmms_output_t *output, xmms_error_t *error)
00643 {
00644 return output->current_entry;
00645 }
00646
00647 static void
00648 xmms_output_volume_set (xmms_output_t *output, const gchar *channel,
00649 guint volume, xmms_error_t *error)
00650 {
00651
00652 if (!output->plugin) {
00653 xmms_error_set (error, XMMS_ERROR_GENERIC,
00654 "couldn't set volume, output plugin not loaded");
00655 return;
00656 }
00657
00658 if (!xmms_output_plugin_method_volume_set_available (output->plugin)) {
00659 xmms_error_set (error, XMMS_ERROR_GENERIC,
00660 "operation not supported");
00661 return;
00662 }
00663
00664 if (volume > 100) {
00665 xmms_error_set (error, XMMS_ERROR_INVAL, "volume out of range");
00666 return;
00667 }
00668
00669 if (!xmms_output_plugin_methods_volume_set (output->plugin, output, channel, volume)) {
00670 xmms_error_set (error, XMMS_ERROR_GENERIC,
00671 "couldn't set volume");
00672 }
00673 }
00674
00675 static GTree *
00676 xmms_output_volume_get (xmms_output_t *output, xmms_error_t *error)
00677 {
00678 GTree *ret;
00679 xmms_volume_map_t map;
00680
00681 if (!output->plugin) {
00682 xmms_error_set (error, XMMS_ERROR_GENERIC,
00683 "couldn't get volume, output plugin not loaded");
00684 return NULL;
00685 }
00686
00687 if (!xmms_output_plugin_method_volume_get_available (output->plugin)) {
00688 xmms_error_set (error, XMMS_ERROR_GENERIC,
00689 "operation not supported");
00690 return NULL;
00691 }
00692
00693 xmms_error_set (error, XMMS_ERROR_GENERIC,
00694 "couldn't get volume");
00695
00696 xmms_volume_map_init (&map);
00697
00698
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 gint32
00731 xmms_output_playtime (xmms_output_t *output, xmms_error_t *error)
00732 {
00733 guint32 ret;
00734 g_return_val_if_fail (output, 0);
00735
00736 g_mutex_lock (output->playtime_mutex);
00737 ret = output->played_time;
00738 g_mutex_unlock (output->playtime_mutex);
00739
00740 return ret;
00741 }
00742
00743
00744
00745
00746 guint32
00747 xmms_output_latency (xmms_output_t *output)
00748 {
00749 guint ret = 0;
00750 guint buffersize = 0;
00751
00752 if (output->format) {
00753
00754 buffersize += xmms_ringbuf_bytes_used (output->filler_buffer);
00755
00756
00757 buffersize += xmms_output_plugin_method_latency_get (output->plugin, output);
00758
00759 ret = xmms_sample_bytes_to_ms (output->format, buffersize);
00760 }
00761
00762 return ret;
00763 }
00764
00765
00766
00767
00768
00769 static gboolean
00770 xmms_output_status_set (xmms_output_t *output, gint status)
00771 {
00772 gboolean ret = TRUE;
00773
00774 if (!output->plugin) {
00775 XMMS_DBG ("No plugin to set status on..");
00776 return FALSE;
00777 }
00778
00779 g_mutex_lock (output->status_mutex);
00780
00781 if (output->status != status) {
00782 if (status == XMMS_PLAYBACK_STATUS_PAUSE &&
00783 output->status != XMMS_PLAYBACK_STATUS_PLAY) {
00784 XMMS_DBG ("Can only pause from play.");
00785 ret = FALSE;
00786 } else {
00787 output->status = status;
00788
00789 if (status == XMMS_PLAYBACK_STATUS_STOP) {
00790 xmms_object_unref (output->format);
00791 output->format = NULL;
00792 }
00793 if (!xmms_output_plugin_method_status (output->plugin, output, status)) {
00794 xmms_log_error ("Status method returned an error!");
00795 output->status = XMMS_PLAYBACK_STATUS_STOP;
00796 ret = FALSE;
00797 }
00798
00799 xmms_object_emit_f (XMMS_OBJECT (output),
00800 XMMS_IPC_SIGNAL_PLAYBACK_STATUS,
00801 XMMSV_TYPE_INT32,
00802 output->status);
00803 }
00804 }
00805
00806 g_mutex_unlock (output->status_mutex);
00807
00808 return ret;
00809 }
00810
00811 static void
00812 xmms_output_destroy (xmms_object_t *object)
00813 {
00814 xmms_output_t *output = (xmms_output_t *)object;
00815
00816 output->monitor_volume_running = FALSE;
00817 if (output->monitor_volume_thread) {
00818 g_thread_join (output->monitor_volume_thread);
00819 output->monitor_volume_thread = NULL;
00820 }
00821
00822 xmms_output_filler_state (output, FILLER_QUIT);
00823 g_thread_join (output->filler_thread);
00824
00825 if (output->plugin) {
00826 xmms_output_plugin_method_destroy (output->plugin, output);
00827 xmms_object_unref (output->plugin);
00828 }
00829
00830 xmms_object_unref (output->playlist);
00831
00832 g_mutex_free (output->status_mutex);
00833 g_mutex_free (output->playtime_mutex);
00834 g_mutex_free (output->filler_mutex);
00835 g_cond_free (output->filler_state_cond);
00836 xmms_ringbuf_destroy (output->filler_buffer);
00837
00838 xmms_ipc_broadcast_unregister ( XMMS_IPC_SIGNAL_OUTPUT_VOLUME_CHANGED);
00839 xmms_ipc_broadcast_unregister ( XMMS_IPC_SIGNAL_PLAYBACK_STATUS);
00840 xmms_ipc_broadcast_unregister ( XMMS_IPC_SIGNAL_OUTPUT_CURRENTID);
00841 xmms_ipc_signal_unregister (XMMS_IPC_SIGNAL_OUTPUT_PLAYTIME);
00842 xmms_ipc_object_unregister (XMMS_IPC_OBJECT_OUTPUT);
00843 }
00844
00845
00846
00847
00848
00849
00850
00851 gboolean
00852 xmms_output_plugin_switch (xmms_output_t *output, xmms_output_plugin_t *new_plugin)
00853 {
00854 xmms_output_plugin_t *old_plugin;
00855 gboolean ret;
00856
00857 g_return_val_if_fail (output, FALSE);
00858 g_return_val_if_fail (new_plugin, FALSE);
00859
00860 xmms_output_stop (output, NULL);
00861
00862 g_mutex_lock (output->status_mutex);
00863
00864 old_plugin = output->plugin;
00865
00866 ret = set_plugin (output, new_plugin);
00867
00868
00869
00870
00871
00872
00873 if (ret) {
00874 xmms_object_unref (old_plugin);
00875 } else if (old_plugin) {
00876 XMMS_DBG ("cannot switch plugin, going back to old one");
00877 set_plugin (output, old_plugin);
00878 }
00879
00880 g_mutex_unlock (output->status_mutex);
00881
00882 return ret;
00883 }
00884
00885
00886
00887
00888 xmms_output_t *
00889 xmms_output_new (xmms_output_plugin_t *plugin, xmms_playlist_t *playlist)
00890 {
00891 xmms_output_t *output;
00892 xmms_config_property_t *prop;
00893 gint size;
00894
00895 g_return_val_if_fail (playlist, NULL);
00896
00897 XMMS_DBG ("Trying to open output");
00898
00899 output = xmms_object_new (xmms_output_t, xmms_output_destroy);
00900
00901 output->playlist = playlist;
00902
00903 output->status_mutex = g_mutex_new ();
00904 output->playtime_mutex = g_mutex_new ();
00905
00906 prop = xmms_config_property_register ("output.buffersize", "32768", NULL, NULL);
00907 size = xmms_config_property_get_int (prop);
00908 XMMS_DBG ("Using buffersize %d", size);
00909
00910 output->filler_mutex = g_mutex_new ();
00911 output->filler_state = FILLER_STOP;
00912 output->filler_state_cond = g_cond_new ();
00913 output->filler_buffer = xmms_ringbuf_new (size);
00914 output->filler_thread = g_thread_create (xmms_output_filler, output, TRUE, NULL);
00915
00916 xmms_config_property_register ("output.flush_on_pause", "1", NULL, NULL);
00917 xmms_ipc_object_register (XMMS_IPC_OBJECT_OUTPUT, XMMS_OBJECT (output));
00918
00919
00920
00921 xmms_ipc_broadcast_register (XMMS_OBJECT (output),
00922 XMMS_IPC_SIGNAL_OUTPUT_VOLUME_CHANGED);
00923 xmms_ipc_broadcast_register (XMMS_OBJECT (output),
00924 XMMS_IPC_SIGNAL_PLAYBACK_STATUS);
00925 xmms_ipc_broadcast_register (XMMS_OBJECT (output),
00926 XMMS_IPC_SIGNAL_OUTPUT_CURRENTID);
00927
00928
00929
00930 xmms_ipc_signal_register (XMMS_OBJECT (output),
00931 XMMS_IPC_SIGNAL_OUTPUT_PLAYTIME);
00932
00933
00934 xmms_object_cmd_add (XMMS_OBJECT (output),
00935 XMMS_IPC_CMD_START,
00936 XMMS_CMD_FUNC (start));
00937 xmms_object_cmd_add (XMMS_OBJECT (output),
00938 XMMS_IPC_CMD_STOP,
00939 XMMS_CMD_FUNC (stop));
00940 xmms_object_cmd_add (XMMS_OBJECT (output),
00941 XMMS_IPC_CMD_PAUSE,
00942 XMMS_CMD_FUNC (pause));
00943 xmms_object_cmd_add (XMMS_OBJECT (output),
00944 XMMS_IPC_CMD_DECODER_KILL,
00945 XMMS_CMD_FUNC (xform_kill));
00946 xmms_object_cmd_add (XMMS_OBJECT (output),
00947 XMMS_IPC_CMD_CPLAYTIME,
00948 XMMS_CMD_FUNC (playtime));
00949 xmms_object_cmd_add (XMMS_OBJECT (output),
00950 XMMS_IPC_CMD_SEEKMS,
00951 XMMS_CMD_FUNC (seekms));
00952 xmms_object_cmd_add (XMMS_OBJECT (output),
00953 XMMS_IPC_CMD_SEEKMS_REL,
00954 XMMS_CMD_FUNC (seekms_rel));
00955 xmms_object_cmd_add (XMMS_OBJECT (output),
00956 XMMS_IPC_CMD_SEEKSAMPLES,
00957 XMMS_CMD_FUNC (seeksamples));
00958 xmms_object_cmd_add (XMMS_OBJECT (output),
00959 XMMS_IPC_CMD_SEEKSAMPLES_REL,
00960 XMMS_CMD_FUNC (seeksamples_rel));
00961 xmms_object_cmd_add (XMMS_OBJECT (output),
00962 XMMS_IPC_CMD_OUTPUT_STATUS,
00963 XMMS_CMD_FUNC (output_status));
00964 xmms_object_cmd_add (XMMS_OBJECT (output),
00965 XMMS_IPC_CMD_CURRENTID,
00966 XMMS_CMD_FUNC (currentid));
00967 xmms_object_cmd_add (XMMS_OBJECT (output),
00968 XMMS_IPC_CMD_VOLUME_SET,
00969 XMMS_CMD_FUNC (volume_set));
00970 xmms_object_cmd_add (XMMS_OBJECT (output),
00971 XMMS_IPC_CMD_VOLUME_GET,
00972 XMMS_CMD_FUNC (volume_get));
00973
00974 output->status = XMMS_PLAYBACK_STATUS_STOP;
00975
00976 if (plugin) {
00977 if (!set_plugin (output, plugin)) {
00978 xmms_log_error ("Could not initialize output plugin");
00979 }
00980 } else {
00981 xmms_log_error ("initalized output without a plugin, please fix!");
00982 }
00983
00984
00985
00986 return output;
00987 }
00988
00989
00990
00991
00992 void
00993 xmms_output_flush (xmms_output_t *output)
00994 {
00995 g_return_if_fail (output);
00996
00997 xmms_output_plugin_method_flush (output->plugin, output);
00998 }
00999
01000
01001
01002
01003 static gboolean
01004 xmms_output_format_set (xmms_output_t *output, xmms_stream_type_t *fmt)
01005 {
01006 g_return_val_if_fail (output, FALSE);
01007 g_return_val_if_fail (fmt, FALSE);
01008
01009 XMMS_DBG ("Setting format!");
01010
01011 if (!xmms_output_plugin_format_set_always (output->plugin)) {
01012 gboolean ret;
01013
01014 if (output->format && xmms_stream_type_match (output->format, fmt)) {
01015 XMMS_DBG ("audio formats are equal, not updating");
01016 return TRUE;
01017 }
01018
01019 ret = xmms_output_plugin_method_format_set (output->plugin, output, fmt);
01020 if (ret) {
01021 xmms_object_unref (output->format);
01022 xmms_object_ref (fmt);
01023 output->format = fmt;
01024 }
01025 return ret;
01026 } else {
01027 if (output->format && !xmms_stream_type_match (output->format, fmt)) {
01028 xmms_object_unref (output->format);
01029 xmms_object_ref (fmt);
01030 output->format = fmt;
01031 }
01032 if (!output->format) {
01033 xmms_object_unref (output->format);
01034 xmms_object_ref (fmt);
01035 output->format = fmt;
01036 }
01037 return xmms_output_plugin_method_format_set (output->plugin, output, output->format);
01038 }
01039 }
01040
01041
01042 static gboolean
01043 set_plugin (xmms_output_t *output, xmms_output_plugin_t *plugin)
01044 {
01045 gboolean ret;
01046
01047 g_assert (output);
01048 g_assert (plugin);
01049
01050 output->monitor_volume_running = FALSE;
01051 if (output->monitor_volume_thread) {
01052 g_thread_join (output->monitor_volume_thread);
01053 output->monitor_volume_thread = NULL;
01054 }
01055
01056 if (output->plugin) {
01057 xmms_output_plugin_method_destroy (output->plugin, output);
01058 output->plugin = NULL;
01059 }
01060
01061
01062
01063
01064 output->plugin = plugin;
01065 ret = xmms_output_plugin_method_new (output->plugin, output);
01066
01067 if (!ret) {
01068 output->plugin = NULL;
01069 } else if (!output->monitor_volume_thread) {
01070 output->monitor_volume_running = TRUE;
01071 output->monitor_volume_thread = g_thread_create (xmms_output_monitor_volume_thread,
01072 output, TRUE, NULL);
01073 }
01074
01075 return ret;
01076 }
01077
01078 static gint
01079 xmms_volume_map_lookup (xmms_volume_map_t *vl, const gchar *name)
01080 {
01081 gint i;
01082
01083 for (i = 0; i < vl->num_channels; i++) {
01084 if (!strcmp (vl->names[i], name)) {
01085 return i;
01086 }
01087 }
01088
01089 return -1;
01090 }
01091
01092
01093 static gboolean
01094 xmms_volume_map_equal (xmms_volume_map_t *a, xmms_volume_map_t *b)
01095 {
01096 guint i;
01097
01098 g_assert (a);
01099 g_assert (b);
01100
01101 if (a->num_channels != b->num_channels) {
01102 return FALSE;
01103 }
01104
01105 for (i = 0; i < a->num_channels; i++) {
01106 gint j;
01107
01108 j = xmms_volume_map_lookup (b, a->names[i]);
01109 if (j == -1 || b->values[j] != a->values[i]) {
01110 return FALSE;
01111 }
01112 }
01113
01114 return TRUE;
01115 }
01116
01117 static void
01118 xmms_volume_map_init (xmms_volume_map_t *vl)
01119 {
01120 vl->status = FALSE;
01121 vl->num_channels = 0;
01122 vl->names = NULL;
01123 vl->values = NULL;
01124 }
01125
01126 static void
01127 xmms_volume_map_free (xmms_volume_map_t *vl)
01128 {
01129 g_free (vl->names);
01130 g_free (vl->values);
01131
01132
01133 }
01134
01135 static void
01136 xmms_volume_map_copy (xmms_volume_map_t *src, xmms_volume_map_t *dst)
01137 {
01138 dst->num_channels = src->num_channels;
01139 dst->status = src->status;
01140
01141 if (!src->status) {
01142 g_free (dst->names);
01143 dst->names = NULL;
01144
01145 g_free (dst->values);
01146 dst->values = NULL;
01147
01148 return;
01149 }
01150
01151 dst->names = g_renew (const gchar *, dst->names, src->num_channels);
01152 dst->values = g_renew (guint, dst->values, src->num_channels);
01153
01154 memcpy (dst->names, src->names, src->num_channels * sizeof (gchar *));
01155 memcpy (dst->values, src->values, src->num_channels * sizeof (guint));
01156 }
01157
01158 static GTree *
01159 xmms_volume_map_to_dict (xmms_volume_map_t *vl)
01160 {
01161 GTree *ret;
01162 gint i;
01163
01164 ret = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
01165 NULL, (GDestroyNotify) xmmsv_unref);
01166 if (!ret) {
01167 return NULL;
01168 }
01169
01170 for (i = 0; i < vl->num_channels; i++) {
01171 xmmsv_t *val;
01172
01173 val = xmmsv_new_int (vl->values[i]);
01174 g_tree_replace (ret, (gpointer) vl->names[i], val);
01175 }
01176
01177 return ret;
01178 }
01179
01180 static gpointer
01181 xmms_output_monitor_volume_thread (gpointer data)
01182 {
01183 GTree *dict;
01184 xmms_output_t *output = data;
01185 xmms_volume_map_t old, cur;
01186
01187 if (!xmms_output_plugin_method_volume_get_available (output->plugin)) {
01188 return NULL;
01189 }
01190
01191 xmms_volume_map_init (&old);
01192 xmms_volume_map_init (&cur);
01193
01194 while (output->monitor_volume_running) {
01195 cur.num_channels = 0;
01196 cur.status = xmms_output_plugin_method_volume_get (output->plugin,
01197 output, NULL, NULL,
01198 &cur.num_channels);
01199
01200 if (cur.status) {
01201
01202 if (cur.num_channels < 1 ||
01203 cur.num_channels > VOLUME_MAX_CHANNELS) {
01204 cur.status = FALSE;
01205 } else {
01206 cur.names = g_renew (const gchar *, cur.names,
01207 cur.num_channels);
01208 cur.values = g_renew (guint, cur.values, cur.num_channels);
01209 }
01210 }
01211
01212 if (cur.status) {
01213 cur.status =
01214 xmms_output_plugin_method_volume_get (output->plugin,
01215 output, cur.names,
01216 cur.values,
01217 &cur.num_channels);
01218 }
01219
01220
01221
01222
01223 if ((cur.status ^ old.status) ||
01224 (cur.status && old.status &&
01225 !xmms_volume_map_equal (&old, &cur))) {
01226
01227 if (cur.status) {
01228 dict = xmms_volume_map_to_dict (&cur);
01229 xmms_object_emit_f (XMMS_OBJECT (output),
01230 XMMS_IPC_SIGNAL_OUTPUT_VOLUME_CHANGED,
01231 XMMSV_TYPE_DICT, dict);
01232 g_tree_destroy (dict);
01233 } else {
01234
01235 xmms_object_emit_f (XMMS_OBJECT (output),
01236 XMMS_IPC_SIGNAL_OUTPUT_VOLUME_CHANGED,
01237 XMMSV_TYPE_NONE);
01238 }
01239 }
01240
01241 xmms_volume_map_copy (&cur, &old);
01242
01243 g_usleep (G_USEC_PER_SEC);
01244 }
01245
01246 xmms_volume_map_free (&old);
01247 xmms_volume_map_free (&cur);
01248
01249 return NULL;
01250 }
01251
01252