37 #define VOLUME_MAX_CHANNELS 128
39 typedef struct xmms_volume_map_St {
47 static gpointer xmms_output_monitor_volume_thread (gpointer data);
67 static void xmms_playback_client_volume_set (
xmms_output_t *output,
const gchar *channel, gint32 volume,
xmms_error_t *error);
76 static gboolean xmms_output_status_set (
xmms_output_t *output, gint status);
79 static void xmms_output_format_list_free_elem (gpointer data, gpointer user_data);
80 static void xmms_output_format_list_clear (
xmms_output_t *output);
113 struct xmms_output_St {
117 gpointer plugin_data;
120 GMutex *playtime_mutex;
127 GThread *filler_thread;
128 GMutex *filler_mutex;
130 GCond *filler_state_cond;
139 GMutex *status_mutex;
153 guint64 bytes_written;
158 gint32 buffer_underruns;
160 GThread *monitor_volume_thread;
161 gboolean monitor_volume_running;
173 g_return_val_if_fail (output, NULL);
174 g_return_val_if_fail (output->plugin, NULL);
176 return output->plugin_data;
182 g_return_if_fail (output);
183 g_return_if_fail (output->plugin);
185 output->plugin_data = data;
194 va_start (ap, output);
198 g_return_if_fail (f);
200 output->format_list = g_list_append (output->format_list, f);
204 xmms_output_format_list_free_elem (gpointer data, gpointer user_data)
208 g_return_if_fail (data);
218 if (output->format_list == NULL)
221 g_list_foreach (output->format_list,
222 xmms_output_format_list_free_elem,
225 g_list_free (output->format_list);
226 output->format_list = NULL;
232 guint buffersize = 0;
234 g_mutex_lock (output->playtime_mutex);
235 output->played += advance;
236 g_mutex_unlock (output->playtime_mutex);
240 if (output->played < buffersize) {
241 buffersize = output->played;
244 g_mutex_lock (output->playtime_mutex);
246 if (output->format) {
248 output->played - buffersize);
249 if ((ms / 100) != (output->played_time / 100)) {
255 output->played_time = ms;
259 g_mutex_unlock (output->playtime_mutex);
266 g_return_if_fail (output);
281 } xmms_output_song_changed_arg_t;
284 song_changed_arg_free (
void *data)
286 xmms_output_song_changed_arg_t *arg = (xmms_output_song_changed_arg_t *)data;
292 song_changed (
void *data)
295 xmms_output_song_changed_arg_t *arg = (xmms_output_song_changed_arg_t *)data;
301 XMMS_DBG (
"Running hotspot! Song changed!! %d", entry);
303 arg->output->played = 0;
304 arg->output->current_entry = entry;
308 if (!xmms_output_format_set (arg->output, type)) {
315 XMMS_DBG (
"Couldn't set format %s/%d/%d, stopping filler..",
316 xmms_sample_name_get (fmt), rate, chn);
318 xmms_output_filler_state_nolock (arg->output,
FILLER_STOP);
335 seek_done (
void *data)
339 g_mutex_lock (output->playtime_mutex);
342 g_mutex_unlock (output->playtime_mutex);
351 output->filler_state = state;
352 g_cond_signal (output->filler_state_cond);
364 g_mutex_lock (output->filler_mutex);
365 xmms_output_filler_state_nolock (output, state);
366 g_mutex_unlock (output->filler_mutex);
369 xmms_output_filler_seek_state (
xmms_output_t *output, guint32 samples)
371 g_mutex_lock (output->filler_mutex);
373 output->filler_seek = samples;
374 g_cond_signal (output->filler_state_cond);
375 g_mutex_unlock (output->filler_mutex);
379 xmms_output_filler (
void *arg)
383 gboolean last_was_kill = FALSE;
388 xmms_error_reset (&err);
390 g_mutex_lock (output->filler_mutex);
398 g_cond_wait (output->filler_state_cond, output->filler_mutex);
399 last_was_kill = FALSE;
407 last_was_kill = TRUE;
415 XMMS_DBG (
"Seek without chain, ignoring..");
426 output->filler_skip = output->filler_seek - ret;
427 if (output->filler_skip < 0) {
428 XMMS_DBG (
"Seeked %d samples too far! Updating position...",
429 -output->filler_skip);
431 output->filler_skip = 0;
432 output->filler_seek = ret;
443 xmms_output_song_changed_arg_t *hsarg;
446 g_mutex_unlock (output->filler_mutex);
450 XMMS_DBG (
"No entry from playlist!");
452 g_mutex_lock (output->filler_mutex);
472 g_mutex_lock (output->filler_mutex);
476 hsarg = g_new0 (xmms_output_song_changed_arg_t, 1);
477 hsarg->output = output;
478 hsarg->chain = chain;
479 hsarg->flush = last_was_kill;
482 last_was_kill = FALSE;
484 g_mutex_lock (output->filler_mutex);
491 XMMS_DBG (
"State changed while waiting...");
494 g_mutex_unlock (output->filler_mutex);
498 g_mutex_lock (output->filler_mutex);
501 gint skip =
MIN (ret, output->toskip);
503 output->toskip -= skip;
508 output->filler_mutex);
513 xmms_error_reset (&err);
524 g_mutex_unlock (output->filler_mutex);
534 xmms_error_reset (&err);
536 g_return_val_if_fail (output, -1);
537 g_return_val_if_fail (buffer, -1);
539 g_mutex_lock (output->filler_mutex);
544 g_mutex_unlock (output->filler_mutex);
547 g_mutex_unlock (output->filler_mutex);
549 update_playtime (output, ret);
560 output->buffer_underruns++;
563 output->bytes_written += ret;
571 g_return_val_if_fail (output->plugin, NULL);
578 g_return_val_if_fail (output->plugin, NULL);
585 g_return_val_if_fail (output, 0);
586 return output->current_entry;
605 g_return_if_fail (output);
608 g_mutex_lock (output->playtime_mutex);
609 ms += output->played_time;
613 g_mutex_unlock (output->playtime_mutex);
616 if (output->format) {
619 xmms_playback_client_seeksamples (output, samples,
628 g_mutex_lock (output->playtime_mutex);
633 g_mutex_unlock (output->playtime_mutex);
637 xmms_output_filler_seek_state (output, samples);
643 g_return_if_fail (output);
645 xmms_output_filler_state (output,
FILLER_RUN);
656 g_return_if_fail (output);
666 g_return_if_fail (output);
678 g_mutex_lock (output->status_mutex);
679 ret = output->status;
680 g_mutex_unlock (output->status_mutex);
687 return output->current_entry;
691 xmms_playback_client_volume_set (
xmms_output_t *output,
const gchar *channel,
695 if (!output->plugin) {
697 "couldn't set volume, output plugin not loaded");
703 "operation not supported");
714 "couldn't set volume");
724 if (!output->plugin) {
726 "couldn't get volume, output plugin not loaded");
732 "operation not supported");
737 "couldn't get volume");
739 xmms_volume_map_init (&map);
743 NULL, NULL, &map.num_channels)) {
748 g_return_val_if_fail (map.num_channels > 0, NULL);
751 map.names = g_new (
const gchar *, map.num_channels);
752 map.values = g_new (guint, map.num_channels);
755 map.names, map.values,
758 if (!map.status || !map.num_channels) {
762 ret = xmms_volume_map_to_dict (&map);
765 xmms_error_reset (error);
777 g_return_val_if_fail (output, 0);
779 g_mutex_lock (output->playtime_mutex);
780 ret = output->played_time;
781 g_mutex_unlock (output->playtime_mutex);
793 guint buffersize = 0;
795 if (output->format) {
817 if (!output->plugin) {
818 XMMS_DBG (
"No plugin to set status on..");
822 g_mutex_lock (output->status_mutex);
824 if (output->status != status) {
827 XMMS_DBG (
"Can only pause from play.");
830 output->status = status;
834 output->format = NULL;
849 g_mutex_unlock (output->status_mutex);
859 output->monitor_volume_running = FALSE;
860 if (output->monitor_volume_thread) {
861 g_thread_join (output->monitor_volume_thread);
862 output->monitor_volume_thread = NULL;
866 g_thread_join (output->filler_thread);
868 if (output->plugin) {
872 xmms_output_format_list_clear (output);
876 g_mutex_free (output->status_mutex);
877 g_mutex_free (output->playtime_mutex);
878 g_mutex_free (output->filler_mutex);
879 g_cond_free (output->filler_state_cond);
901 g_return_val_if_fail (output, FALSE);
902 g_return_val_if_fail (new_plugin, FALSE);
904 xmms_playback_client_stop (output, NULL);
906 g_mutex_lock (output->status_mutex);
908 old_plugin = output->plugin;
910 ret = set_plugin (output, new_plugin);
919 }
else if (old_plugin) {
920 XMMS_DBG (
"cannot switch plugin, going back to old one");
921 set_plugin (output, old_plugin);
924 g_mutex_unlock (output->status_mutex);
939 g_return_val_if_fail (playlist, NULL);
945 output->playlist = playlist;
947 output->status_mutex = g_mutex_new ();
948 output->playtime_mutex = g_mutex_new ();
952 XMMS_DBG (
"Using buffersize %d", size);
954 output->filler_mutex = g_mutex_new ();
956 output->filler_state_cond = g_cond_new ();
958 output->filler_thread = g_thread_create (xmms_output_filler, output, TRUE, NULL);
1015 if (!set_plugin (output, plugin)) {
1019 xmms_log_error (
"initalized output without a plugin, please fix!");
1033 g_return_if_fail (output);
1044 g_return_val_if_fail (output, FALSE);
1045 g_return_val_if_fail (fmt, FALSE);
1053 XMMS_DBG (
"audio formats are equal, not updating");
1061 output->format = fmt;
1068 output->format = fmt;
1070 if (!output->format) {
1073 output->format = fmt;
1088 output->monitor_volume_running = FALSE;
1089 if (output->monitor_volume_thread) {
1090 g_thread_join (output->monitor_volume_thread);
1091 output->monitor_volume_thread = NULL;
1094 if (output->plugin) {
1096 output->plugin = NULL;
1098 xmms_output_format_list_clear (output);
1103 output->plugin = plugin;
1107 output->plugin = NULL;
1108 }
else if (!output->monitor_volume_thread) {
1109 output->monitor_volume_running = TRUE;
1110 output->monitor_volume_thread = g_thread_create (xmms_output_monitor_volume_thread,
1111 output, TRUE, NULL);
1122 for (i = 0; i < vl->num_channels; i++) {
1123 if (!strcmp (vl->names[i], name)) {
1140 if (a->num_channels != b->num_channels) {
1144 for (i = 0; i < a->num_channels; i++) {
1147 j = xmms_volume_map_lookup (b, a->names[i]);
1148 if (j == -1 || b->values[j] != a->values[i]) {
1160 vl->num_channels = 0;
1169 g_free (vl->values);
1177 dst->num_channels = src->num_channels;
1178 dst->status = src->status;
1181 g_free (dst->names);
1184 g_free (dst->values);
1190 dst->names = g_renew (
const gchar *, dst->names, src->num_channels);
1191 dst->values = g_renew (guint, dst->values, src->num_channels);
1193 memcpy (dst->names, src->names, src->num_channels * sizeof (gchar *));
1194 memcpy (dst->values, src->values, src->num_channels * sizeof (guint));
1203 ret = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
1209 for (i = 0; i < vl->num_channels; i++) {
1213 g_tree_replace (ret, (gpointer) vl->names[i], val);
1220 xmms_output_monitor_volume_thread (gpointer data)
1230 xmms_volume_map_init (&old);
1231 xmms_volume_map_init (&cur);
1233 while (output->monitor_volume_running) {
1234 cur.num_channels = 0;
1241 if (cur.num_channels < 1 ||
1245 cur.names = g_renew (
const gchar *, cur.names,
1247 cur.values = g_renew (guint, cur.values, cur.num_channels);
1262 if ((cur.status ^ old.status) ||
1263 (cur.status && old.status &&
1264 !xmms_volume_map_equal (&old, &cur))) {
1267 dict = xmms_volume_map_to_dict (&cur);
1271 g_tree_destroy (dict);
1280 xmms_volume_map_copy (&cur, &old);
1282 g_usleep (G_USEC_PER_SEC);
1285 xmms_volume_map_free (&old);
1286 xmms_volume_map_free (&cur);
gboolean xmms_output_plugin_method_format_set(xmms_output_plugin_t *plugin, xmms_output_t *output, xmms_stream_type_t *st)
#define XMMS_CMD_FUNC(cmdid)
struct xmms_ringbuf_St xmms_ringbuf_t
struct xmms_volume_map_St xmms_volume_map_t
guint xmms_sample_ms_to_samples(const xmms_stream_type_t *st, guint milliseconds)
convert from milliseconds to samples for this format.
#define VOLUME_MAX_CHANNELS
gint xmms_stream_type_get_int(const xmms_stream_type_t *st, xmms_stream_type_key_t key)
void xmmsv_unref(xmmsv_t *val)
Decreases the references for the xmmsv_t When the number of references reaches 0 it will be freed...
gboolean xmms_ringbuf_iseos(const xmms_ringbuf_t *ringbuf)
Tell if the ringbuffer is EOS.
#define xmms_object_unref(obj)
xmms_config_property_t * xmms_plugin_config_property_register(xmms_plugin_t *plugin, const gchar *name, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
xmms_config_property_t * xmms_output_config_lookup(xmms_output_t *output, const gchar *path)
Lookup a configuration directive for the output plugin.
void xmms_ringbuf_wait_free(const xmms_ringbuf_t *ringbuf, guint len, GMutex *mtx)
Block until we have free space in the ringbuffer.
struct xmms_stream_type_St xmms_stream_type_t
void xmms_object_cmd_add(xmms_object_t *object, guint cmdid, const xmms_object_cmd_desc_t *desc)
Add a command that could be called from the client API to a object.
gboolean xmms_output_plugin_method_status(xmms_output_plugin_t *plugin, xmms_output_t *output, gint st)
void xmms_ipc_broadcast_register(xmms_object_t *object, xmms_ipc_signals_t signalid)
Register a broadcast signal.
gint xmms_output_read(xmms_output_t *output, char *buffer, gint len)
Read a number of bytes of data from the output buffer into a buffer.
guint xmms_ringbuf_bytes_used(const xmms_ringbuf_t *ringbuf)
Number of bytes used in the buffer.
xmms_config_property_t * xmms_output_config_property_register(xmms_output_t *output, const gchar *name, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Register a configuration directive.
guint32 xmms_output_latency(xmms_output_t *output)
const gchar * xmms_plugin_shortname_get(const xmms_plugin_t *plugin)
guint xmms_output_plugin_method_latency_get(xmms_output_plugin_t *plugin, xmms_output_t *output)
gboolean xmms_output_plugin_method_new(xmms_output_plugin_t *plugin, xmms_output_t *output)
guint xmms_sample_bytes_to_ms(const xmms_stream_type_t *st, guint bytes)
Convert from bytes to milliseconds for this format.
gboolean xmms_playlist_advance(xmms_playlist_t *playlist)
Go to next song in playlist according to current playlist mode.
void xmms_ipc_signal_register(xmms_object_t *object, xmms_ipc_signals_t signalid)
Register a signal.
void xmms_output_flush(xmms_output_t *output)
Flush the buffers in soundcard.
void xmms_ringbuf_wait_used(const xmms_ringbuf_t *ringbuf, guint len, GMutex *mtx)
Block until we have used space in the buffer.
void xmms_output_plugin_method_flush(xmms_output_plugin_t *plugin, xmms_output_t *output)
gint xmms_config_property_get_int(const xmms_config_property_t *prop)
Return the value of a config property as an int.
guint xmms_ringbuf_read(xmms_ringbuf_t *ringbuf, gpointer data, guint len)
Reads data from the ringbuffer.
const gchar * xmms_error_message_get(xmms_error_t *err)
Get the human readable error.
gboolean xmms_stream_type_match(const xmms_stream_type_t *in_type, const xmms_stream_type_t *out_type)
xmms_output_t * xmms_output_new(xmms_output_plugin_t *plugin, xmms_playlist_t *playlist)
Allocate a new xmms_output_t.
gboolean xmms_output_plugin_method_volume_set_available(xmms_output_plugin_t *plugin)
#define xmms_log_error(fmt,...)
gboolean xmms_output_plugin_switch(xmms_output_t *output, xmms_output_plugin_t *new_plugin)
Switch to another output plugin.
void xmms_object_emit_f(xmms_object_t *object, guint32 signalid, xmmsv_type_t type,...)
Emits a signal on the current object.
xmms_ringbuf_t * xmms_ringbuf_new(guint size)
Allocate a new ringbuffer.
struct xmms_playlist_St xmms_playlist_t
struct xmms_output_St xmms_output_t
struct xmms_output_plugin_St xmms_output_plugin_t
void xmms_ipc_signal_unregister(xmms_ipc_signals_t signalid)
Unregister a signal.
xmmsv_t * xmmsv_new_int(int32_t i)
Allocates a new integer xmmsv_t.
void xmms_ipc_object_unregister(xmms_ipc_objects_t objectid)
Remove a object from the IPC core.
xmms_medialib_entry_t xmms_output_current_id(xmms_output_t *output)
Get the currently medialib id of the currently played entry.
gpointer xmms_output_private_data_get(xmms_output_t *output)
Retrieve the private data for the plugin that was set with xmms_output_private_data_set.
guint xmms_ringbuf_write_wait(xmms_ringbuf_t *ringbuf, gconstpointer data, guint len, GMutex *mtx)
Same as xmms_ringbuf_write but blocks until there is enough free space.
gboolean xmms_output_plugin_methods_volume_set(xmms_output_plugin_t *plugin, xmms_output_t *output, const gchar *chan, guint val)
void xmms_ringbuf_clear(xmms_ringbuf_t *ringbuf)
Clear the ringbuffers data.
void xmms_output_stream_type_add(xmms_output_t *output,...)
Add format to list of supported formats.
void xmms_output_private_data_set(xmms_output_t *output, gpointer data)
Set the private data for the plugin that can be retrived with xmms_output_private_data_get later...
#define xmms_object_ref(obj)
gboolean xmms_output_plugin_method_volume_get_available(xmms_output_plugin_t *plugin)
void xmms_ringbuf_set_eos(xmms_ringbuf_t *ringbuf, gboolean eos)
Set EOS flag on ringbuffer.
enum xmms_output_filler_state_E xmms_output_filler_state_t
xmms_output_filler_state_E
gint xmms_sample_frame_size_get(const xmms_stream_type_t *st)
gboolean xmms_output_plugin_format_set_always(xmms_output_plugin_t *plugin)
Check if an output plugin needs format updates on each track change.
#define XMMS_DBG(fmt,...)
void xmms_ringbuf_destroy(xmms_ringbuf_t *ringbuf)
Free all memory used by the ringbuffer.
void xmms_ipc_broadcast_unregister(xmms_ipc_signals_t signalid)
Unregister a broadcast signal.
#define xmms_object_new(objtype, destroyfunc)
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
xmms_config_property_t * xmms_config_property_register(const gchar *path, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Register a new config property.
xmms_config_property_t * xmms_plugin_config_lookup(xmms_plugin_t *plugin, const gchar *key)
struct xmms_config_property_St xmms_config_property_t
gboolean xmms_output_plugin_method_volume_get(xmms_output_plugin_t *plugin, xmms_output_t *output, const gchar **n, guint *x, guint *y)
void xmms_ipc_object_register(xmms_ipc_objects_t objectid, xmms_object_t *object)
Register a object to the IPC core.
void(* xmms_object_handler_t)(xmms_object_t *object, xmmsv_t *data, gpointer userdata)
XMMS_CMD_DEFINE(start, xmms_playback_client_start, xmms_output_t *, NONE, NONE, NONE)
xmms_stream_type_t * xmms_stream_type_parse(va_list ap)
xmms_medialib_entry_t xmms_playlist_current_entry(xmms_playlist_t *playlist)
Retrieve the currently active xmms_medialib_entry_t.
void xmms_output_plugin_method_destroy(xmms_output_plugin_t *plugin, xmms_output_t *output)
void xmms_ringbuf_hotspot_set(xmms_ringbuf_t *ringbuf, gboolean(*cb)(void *), void(*destroy)(void *), void *arg)
void xmms_output_set_error(xmms_output_t *output, xmms_error_t *error)
Set an error.