XMMS2

src/xmms/visualization/object.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2009 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 #include <string.h>
00018 #include <stdio.h>
00019 #include <stdlib.h>
00020 
00021 #include "xmms/xmms_object.h"
00022 #include "xmmspriv/xmms_ipc.h"
00023 #include "xmmspriv/xmms_sample.h"
00024 
00025 #include "common.h"
00026 
00027 /** @defgroup Visualization Visualization
00028   * @ingroup XMMSServer
00029   * @brief Feeds playing data in various forms to the client.
00030   * @{
00031   */
00032 
00033 static xmms_visualization_t *vis = NULL;
00034 
00035 static int32_t xmms_visualization_client_version (xmms_visualization_t *vis, xmms_error_t *err);
00036 static int32_t xmms_visualization_client_register (xmms_visualization_t *vis, xmms_error_t *err);
00037 static int32_t xmms_visualization_client_init_shm (xmms_visualization_t *vis, int32_t id, const char *shmid, xmms_error_t *err);
00038 static int32_t xmms_visualization_client_init_udp (xmms_visualization_t *vis, int32_t id, xmms_error_t *err);
00039 static int32_t xmms_visualization_client_property_set (xmms_visualization_t *vis, int32_t id, const gchar *key, const gchar *value, xmms_error_t *err);
00040 static int32_t xmms_visualization_client_properties_set (xmms_visualization_t *vis, int32_t id, xmmsv_t *prop, xmms_error_t *err);
00041 static void xmms_visualization_client_shutdown (xmms_visualization_t *vis, int32_t id, xmms_error_t *err);
00042 static void xmms_visualization_destroy (xmms_object_t *object);
00043 
00044 
00045 XMMS_CMD_DEFINE (query_version, xmms_visualization_client_version, xmms_visualization_t *, INT32, NONE, NONE);
00046 XMMS_CMD_DEFINE (registercl, xmms_visualization_client_register, xmms_visualization_t *, INT32, NONE, NONE);
00047 XMMS_CMD_DEFINE (init_shm, xmms_visualization_client_init_shm, xmms_visualization_t *, INT32, INT32, STRING);
00048 XMMS_CMD_DEFINE (init_udp, xmms_visualization_client_init_udp, xmms_visualization_t *, INT32, INT32, NONE);
00049 XMMS_CMD_DEFINE3 (property_set, xmms_visualization_client_property_set, xmms_visualization_t *, INT32, INT32, STRING, STRING);
00050 XMMS_CMD_DEFINE (properties_set, xmms_visualization_client_properties_set, xmms_visualization_t *, INT32, INT32, DICT);
00051 XMMS_CMD_DEFINE (shutdown, xmms_visualization_client_shutdown, xmms_visualization_t *, NONE, INT32, NONE);
00052 
00053 /* create an uninitialised vis client. don't use this method without mutex! */
00054 static int32_t
00055 create_client (void)
00056 {
00057     int32_t id;
00058 
00059     for (id = 0; id < vis->clientc; ++id) {
00060         if (!vis->clientv[id]) {
00061             break;
00062         }
00063     }
00064 
00065     if (id == vis->clientc) {
00066         vis->clientc++;
00067     }
00068 
00069     vis->clientv = g_renew (xmms_vis_client_t*, vis->clientv, vis->clientc);
00070     if (!vis->clientv || (!(vis->clientv[id] = g_new (xmms_vis_client_t, 1)))) {
00071         vis->clientc = 0;
00072         id = -1;
00073     }
00074 
00075     xmms_log_info ("Attached visualization client %d", id);
00076     return id;
00077 }
00078 
00079 xmms_vis_client_t *
00080 get_client (int32_t id)
00081 {
00082     if (id < 0 || id >= vis->clientc) {
00083         return NULL;
00084     }
00085 
00086     return vis->clientv[id];
00087 }
00088 
00089 /* delete a vis client. don't use this method without mutex! */
00090 void
00091 delete_client (int32_t id)
00092 {
00093     xmms_vis_client_t *c;
00094 
00095     if (id < 0 || id >= vis->clientc) {
00096         return;
00097     }
00098 
00099     c = vis->clientv[id];
00100     if (c == NULL) {
00101         return;
00102     }
00103 
00104     if (c->type == VIS_UNIXSHM) {
00105         cleanup_shm (&c->transport.shm);
00106     } else if (c->type == VIS_UDP) {
00107         cleanup_udp (&c->transport.udp, vis->socket);
00108     }
00109 
00110     g_free (c);
00111     vis->clientv[id] = NULL;
00112 
00113     xmms_log_info ("Removed visualization client %d", id);
00114 }
00115 
00116 /**
00117  * Initialize the Vis module.
00118  */
00119 xmms_visualization_t *
00120 xmms_visualization_new (xmms_output_t *output)
00121 {
00122     vis = xmms_object_new (xmms_visualization_t, xmms_visualization_destroy);
00123     vis->clientlock = g_mutex_new ();
00124     vis->clientc = 0;
00125     vis->output = output;
00126 
00127     xmms_object_ref (output);
00128 
00129     xmms_ipc_object_register (XMMS_IPC_OBJECT_VISUALIZATION, XMMS_OBJECT (vis));
00130     xmms_object_cmd_add (XMMS_OBJECT (vis),
00131                          XMMS_IPC_CMD_VISUALIZATION_QUERY_VERSION,
00132                          XMMS_CMD_FUNC (query_version));
00133     xmms_object_cmd_add (XMMS_OBJECT (vis),
00134                          XMMS_IPC_CMD_VISUALIZATION_REGISTER,
00135                          XMMS_CMD_FUNC (registercl));
00136     xmms_object_cmd_add (XMMS_OBJECT (vis),
00137                          XMMS_IPC_CMD_VISUALIZATION_INIT_SHM,
00138                          XMMS_CMD_FUNC (init_shm));
00139     xmms_object_cmd_add (XMMS_OBJECT (vis),
00140                          XMMS_IPC_CMD_VISUALIZATION_INIT_UDP,
00141                          XMMS_CMD_FUNC (init_udp));
00142     xmms_object_cmd_add (XMMS_OBJECT (vis),
00143                          XMMS_IPC_CMD_VISUALIZATION_PROPERTY,
00144                          XMMS_CMD_FUNC (property_set));
00145     xmms_object_cmd_add (XMMS_OBJECT (vis),
00146                          XMMS_IPC_CMD_VISUALIZATION_PROPERTIES,
00147                          XMMS_CMD_FUNC (properties_set));
00148     xmms_object_cmd_add (XMMS_OBJECT (vis),
00149                          XMMS_IPC_CMD_VISUALIZATION_SHUTDOWN,
00150                          XMMS_CMD_FUNC (shutdown));
00151 
00152     xmms_socket_invalidate (&vis->socket);
00153 
00154     return vis;
00155 }
00156 
00157 /**
00158  * Free all resoures used by visualization module.
00159  * TODO: Fill this in properly, unregister etc!
00160  */
00161 
00162 static void
00163 xmms_visualization_destroy (xmms_object_t *object)
00164 {
00165     xmms_object_unref (vis->output);
00166 
00167     /* TODO: assure that the xform is already dead! */
00168     g_mutex_free (vis->clientlock);
00169     xmms_log_debug ("starting cleanup of %d vis clients", vis->clientc);
00170     for (; vis->clientc > 0; --vis->clientc) {
00171         delete_client (vis->clientc - 1);
00172     }
00173 
00174     if (xmms_socket_valid (vis->socket)) {
00175         /* it seems there is no way to remove the watch */
00176         g_io_channel_shutdown (vis->socketio, FALSE, NULL);
00177         xmms_socket_close (vis->socket);
00178     }
00179     xmms_ipc_object_unregister (XMMS_IPC_OBJECT_VISUALIZATION);
00180 }
00181 
00182 static int32_t
00183 xmms_visualization_client_version (xmms_visualization_t *vis, xmms_error_t *err)
00184 {
00185     /* if there is a way to disable visualization support on the server side,
00186        we could return 0 here, or we could return an error? */
00187 
00188     return XMMS_VISPACKET_VERSION;
00189 }
00190 
00191 static void
00192 properties_init (xmmsc_vis_properties_t *p)
00193 {
00194     p->type = VIS_PCM;
00195     p->stereo = 1;
00196     p->pcm_hardwire = 0;
00197 }
00198 
00199 static gboolean
00200 property_set (xmmsc_vis_properties_t *p, const gchar* key, const gchar* data)
00201 {
00202 
00203     if (!g_strcasecmp (key, "type")) {
00204         if (!g_strcasecmp (data, "pcm")) {
00205             p->type = VIS_PCM;
00206         } else if (!g_strcasecmp (data, "spectrum")) {
00207             p->type = VIS_SPECTRUM;
00208         } else if (!g_strcasecmp (data, "peak")) {
00209             p->type = VIS_PEAK;
00210         } else {
00211             return FALSE;
00212         }
00213     } else if (!g_strcasecmp (key, "stereo")) {
00214         p->stereo = (atoi (data) > 0);
00215     } else if (!g_strcasecmp (key, "pcm.hardwire")) {
00216         p->pcm_hardwire = (atoi (data) > 0);
00217     /* TODO: all the stuff following */
00218     } else if (!g_strcasecmp (key, "timeframe")) {
00219         p->timeframe = g_strtod (data, NULL);
00220         if (p->timeframe == 0.0) {
00221             return FALSE;
00222         }
00223     } else {
00224         return FALSE;
00225     }
00226     return TRUE;
00227 }
00228 
00229 static int32_t
00230 xmms_visualization_client_register (xmms_visualization_t *vis, xmms_error_t *err)
00231 {
00232     int32_t id;
00233     xmms_vis_client_t *c;
00234 
00235     g_mutex_lock (vis->clientlock);
00236     id = create_client ();
00237     if (id < 0) {
00238         xmms_error_set (err, XMMS_ERROR_OOM, "could not allocate dataset");
00239     } else {
00240         /* do necessary initialisations here */
00241         c = get_client (id);
00242         c->type = VIS_NONE;
00243         c->format = 0;
00244         properties_init (&c->prop);
00245     }
00246     g_mutex_unlock (vis->clientlock);
00247     return id;
00248 }
00249 
00250 
00251 static int32_t
00252 xmms_visualization_client_property_set (xmms_visualization_t *vis, int32_t id, const gchar* key, const gchar* value, xmms_error_t *err)
00253 {
00254     xmms_vis_client_t *c;
00255 
00256     x_fetch_client (id);
00257 
00258     if (!property_set (&c->prop, key, value)) {
00259         xmms_error_set (err, XMMS_ERROR_INVAL, "property could not be set!");
00260     }
00261 
00262     x_release_client ();
00263 
00264     /* the format identifier (between client and server) changes. so the client can recognize the first packet
00265        which is built using the new format according to the newly set property */
00266     return (++c->format);
00267 }
00268 
00269 static int32_t
00270 xmms_visualization_client_properties_set (xmms_visualization_t *vis, int32_t id, xmmsv_t* prop, xmms_error_t *err)
00271 {
00272     xmms_vis_client_t *c;
00273     xmmsv_dict_iter_t *it;
00274     const gchar *key, *valstr;
00275     xmmsv_t *value;
00276 
00277     x_fetch_client (id);
00278 
00279     if (!xmmsv_get_type (prop) == XMMSV_TYPE_DICT) {
00280         xmms_error_set (err, XMMS_ERROR_INVAL, "properties must be sent as a dict!");
00281     } else {
00282         /* record every pair */
00283         xmmsv_get_dict_iter (prop, &it);
00284         while (xmmsv_dict_iter_valid (it)) {
00285             if (!xmmsv_dict_iter_pair (it, &key, &value)) {
00286                 xmms_error_set (err, XMMS_ERROR_INVAL, "key-value property pair could not be read!");
00287             } else if (!xmmsv_get_string (value, &valstr)) {
00288                 xmms_error_set (err, XMMS_ERROR_INVAL, "property value could not be read!");
00289             } else if (!property_set (&c->prop, key, valstr)) {
00290                 xmms_error_set (err, XMMS_ERROR_INVAL, "property could not be set!");
00291             }
00292             xmmsv_dict_iter_next (it);
00293         }
00294         /* TODO: propagate new format to xform! */
00295     }
00296 
00297     x_release_client ();
00298 
00299     return (++c->format);
00300 }
00301 
00302 static int32_t
00303 xmms_visualization_client_init_shm (xmms_visualization_t *vis, int32_t id, const char *shmidstr, xmms_error_t *err)
00304 {
00305     int shmid;
00306 
00307     XMMS_DBG ("Trying to init shm!");
00308 
00309     if (sscanf (shmidstr, "%d", &shmid) != 1) {
00310         xmms_error_set (err, XMMS_ERROR_INVAL, "couldn't parse shmid");
00311         return -1;
00312     }
00313     return init_shm (vis, id, shmid, err);
00314 }
00315 
00316 static int32_t
00317 xmms_visualization_client_init_udp (xmms_visualization_t *vis, int32_t id, xmms_error_t *err)
00318 {
00319     XMMS_DBG ("Trying to init udp!");
00320     return init_udp (vis, id, err);
00321 }
00322 
00323 static void
00324 xmms_visualization_client_shutdown (xmms_visualization_t *vis, int32_t id, xmms_error_t *err)
00325 {
00326     g_mutex_lock (vis->clientlock);
00327     delete_client (id);
00328     g_mutex_unlock (vis->clientlock);
00329 }
00330 
00331 static gboolean
00332 package_write (xmms_vis_client_t *c, int32_t id, struct timeval *time, int channels, int size, short *buf)
00333 {
00334     if (c->type == VIS_UNIXSHM) {
00335         return write_shm (&c->transport.shm, c, id, time, channels, size, buf);
00336     } else if (c->type == VIS_UDP) {
00337         return write_udp (&c->transport.udp, c, id, time, channels, size, buf, vis->socket);
00338     }
00339     return FALSE;
00340 }
00341 
00342 void
00343 send_data (int channels, int size, short *buf)
00344 {
00345     int i;
00346     struct timeval time;
00347     guint32 latency;
00348 
00349     if (!vis) {
00350         return;
00351     }
00352 
00353     latency = xmms_output_latency (vis->output);
00354 
00355     fft_init ();
00356 
00357     gettimeofday (&time, NULL);
00358     time.tv_sec += (latency / 1000);
00359     time.tv_usec += (latency % 1000) * 1000;
00360     if (time.tv_usec > 1000000) {
00361         time.tv_sec++;
00362         time.tv_usec -= 1000000;
00363     }
00364 
00365     g_mutex_lock (vis->clientlock);
00366     for (i = 0; i < vis->clientc; ++i) {
00367         if (vis->clientv[i]) {
00368             package_write (vis->clientv[i], i, &time, channels, size, buf);
00369         }
00370     }
00371     g_mutex_unlock (vis->clientlock);
00372 }
00373 
00374 /** @} */