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 "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
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
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
00129
00130 GMutex *status_mutex;
00131 guint status;
00132
00133 xmms_playlist_t *playlist;
00134
00135
00136 GList *format_list;
00137
00138 xmms_stream_type_t *format;
00139
00140
00141
00142
00143
00144 guint64 bytes_written;
00145
00146
00147
00148
00149 gint32 buffer_underruns;
00150
00151 GThread *monitor_volume_thread;
00152 gboolean monitor_volume_running;
00153 };
00154
00155
00156
00157
00158
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
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
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
00590
00591
00592
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
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
00741 if (!xmms_output_plugin_method_volume_get (output->plugin, output,
00742 NULL, NULL, &map.num_channels)) {
00743 return NULL;
00744 }
00745
00746
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;
00759 }
00760
00761 ret = xmms_volume_map_to_dict (&map);
00762
00763
00764 xmms_error_reset (error);
00765
00766 return ret;
00767 }
00768
00769
00770
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
00786
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
00796 buffersize += xmms_ringbuf_bytes_used (output->filler_buffer);
00797
00798
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
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
00886
00887
00888
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
00908
00909
00910
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
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
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
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
01048
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
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
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
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
01209
01210
01211 if ((cur.status ^ old.status) ||
01212 (cur.status && old.status &&
01213 !xmms_volume_map_equal (&old, &cur))) {
01214
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
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