XMMS2
|
00001 /* XMMS2 - X Music Multiplexer System 00002 * Copyright (C) 2003-2011 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_query_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_set_property (xmms_visualization_t *vis, int32_t id, const gchar *key, const gchar *value, xmms_error_t *err); 00040 static int32_t xmms_visualization_client_set_properties (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 #include "visualization/object_ipc.c" 00045 00046 /* create an uninitialised vis client. don't use this method without mutex! */ 00047 static int32_t 00048 create_client (void) 00049 { 00050 int32_t id; 00051 00052 for (id = 0; id < vis->clientc; ++id) { 00053 if (!vis->clientv[id]) { 00054 break; 00055 } 00056 } 00057 00058 if (id == vis->clientc) { 00059 vis->clientc++; 00060 } 00061 00062 vis->clientv = g_renew (xmms_vis_client_t*, vis->clientv, vis->clientc); 00063 if (!vis->clientv || (!(vis->clientv[id] = g_new (xmms_vis_client_t, 1)))) { 00064 vis->clientc = 0; 00065 id = -1; 00066 } 00067 00068 xmms_log_info ("Attached visualization client %d", id); 00069 return id; 00070 } 00071 00072 xmms_vis_client_t * 00073 get_client (int32_t id) 00074 { 00075 if (id < 0 || id >= vis->clientc) { 00076 return NULL; 00077 } 00078 00079 return vis->clientv[id]; 00080 } 00081 00082 /* delete a vis client. don't use this method without mutex! */ 00083 void 00084 delete_client (int32_t id) 00085 { 00086 xmms_vis_client_t *c; 00087 00088 if (id < 0 || id >= vis->clientc) { 00089 return; 00090 } 00091 00092 c = vis->clientv[id]; 00093 if (c == NULL) { 00094 return; 00095 } 00096 00097 if (c->type == VIS_UNIXSHM) { 00098 cleanup_shm (&c->transport.shm); 00099 } else if (c->type == VIS_UDP) { 00100 cleanup_udp (&c->transport.udp, vis->socket); 00101 } 00102 00103 g_free (c); 00104 vis->clientv[id] = NULL; 00105 00106 xmms_log_info ("Removed visualization client %d", id); 00107 } 00108 00109 /** 00110 * Initialize the Vis module. 00111 */ 00112 xmms_visualization_t * 00113 xmms_visualization_new (xmms_output_t *output) 00114 { 00115 vis = xmms_object_new (xmms_visualization_t, xmms_visualization_destroy); 00116 vis->clientlock = g_mutex_new (); 00117 vis->clientc = 0; 00118 vis->output = output; 00119 00120 xmms_object_ref (output); 00121 00122 xmms_visualization_register_ipc_commands (XMMS_OBJECT (vis)); 00123 00124 xmms_socket_invalidate (&vis->socket); 00125 00126 return vis; 00127 } 00128 00129 /** 00130 * Free all resoures used by visualization module. 00131 * TODO: Fill this in properly, unregister etc! 00132 */ 00133 00134 static void 00135 xmms_visualization_destroy (xmms_object_t *object) 00136 { 00137 xmms_object_unref (vis->output); 00138 00139 /* TODO: assure that the xform is already dead! */ 00140 g_mutex_free (vis->clientlock); 00141 xmms_log_debug ("starting cleanup of %d vis clients", vis->clientc); 00142 for (; vis->clientc > 0; --vis->clientc) { 00143 delete_client (vis->clientc - 1); 00144 } 00145 00146 if (xmms_socket_valid (vis->socket)) { 00147 /* it seems there is no way to remove the watch */ 00148 g_io_channel_shutdown (vis->socketio, FALSE, NULL); 00149 xmms_socket_close (vis->socket); 00150 } 00151 00152 xmms_visualization_unregister_ipc_commands (); 00153 } 00154 00155 static int32_t 00156 xmms_visualization_client_query_version (xmms_visualization_t *vis, xmms_error_t *err) 00157 { 00158 /* if there is a way to disable visualization support on the server side, 00159 we could return 0 here, or we could return an error? */ 00160 00161 return XMMS_VISPACKET_VERSION; 00162 } 00163 00164 static void 00165 properties_init (xmmsc_vis_properties_t *p) 00166 { 00167 p->type = VIS_PCM; 00168 p->stereo = 1; 00169 p->pcm_hardwire = 0; 00170 } 00171 00172 static gboolean 00173 property_set (xmmsc_vis_properties_t *p, const gchar* key, const gchar* data) 00174 { 00175 00176 if (!g_strcasecmp (key, "type")) { 00177 if (!g_strcasecmp (data, "pcm")) { 00178 p->type = VIS_PCM; 00179 } else if (!g_strcasecmp (data, "spectrum")) { 00180 p->type = VIS_SPECTRUM; 00181 } else if (!g_strcasecmp (data, "peak")) { 00182 p->type = VIS_PEAK; 00183 } else { 00184 return FALSE; 00185 } 00186 } else if (!g_strcasecmp (key, "stereo")) { 00187 p->stereo = (atoi (data) > 0); 00188 } else if (!g_strcasecmp (key, "pcm.hardwire")) { 00189 p->pcm_hardwire = (atoi (data) > 0); 00190 /* TODO: all the stuff following */ 00191 } else if (!g_strcasecmp (key, "timeframe")) { 00192 p->timeframe = g_strtod (data, NULL); 00193 if (p->timeframe == 0.0) { 00194 return FALSE; 00195 } 00196 } else { 00197 return FALSE; 00198 } 00199 return TRUE; 00200 } 00201 00202 static int32_t 00203 xmms_visualization_client_register (xmms_visualization_t *vis, xmms_error_t *err) 00204 { 00205 int32_t id; 00206 xmms_vis_client_t *c; 00207 00208 g_mutex_lock (vis->clientlock); 00209 id = create_client (); 00210 if (id < 0) { 00211 xmms_error_set (err, XMMS_ERROR_OOM, "could not allocate dataset"); 00212 } else { 00213 /* do necessary initialisations here */ 00214 c = get_client (id); 00215 c->type = VIS_NONE; 00216 c->format = 0; 00217 properties_init (&c->prop); 00218 } 00219 g_mutex_unlock (vis->clientlock); 00220 return id; 00221 } 00222 00223 00224 static int32_t 00225 xmms_visualization_client_set_property (xmms_visualization_t *vis, int32_t id, const gchar* key, const gchar* value, xmms_error_t *err) 00226 { 00227 xmms_vis_client_t *c; 00228 00229 x_fetch_client (id); 00230 00231 if (!property_set (&c->prop, key, value)) { 00232 xmms_error_set (err, XMMS_ERROR_INVAL, "property could not be set!"); 00233 } 00234 00235 x_release_client (); 00236 00237 /* the format identifier (between client and server) changes. so the client can recognize the first packet 00238 which is built using the new format according to the newly set property */ 00239 return (++c->format); 00240 } 00241 00242 static int32_t 00243 xmms_visualization_client_set_properties (xmms_visualization_t *vis, int32_t id, xmmsv_t* prop, xmms_error_t *err) 00244 { 00245 xmms_vis_client_t *c; 00246 xmmsv_dict_iter_t *it; 00247 const gchar *key, *valstr; 00248 xmmsv_t *value; 00249 00250 x_fetch_client (id); 00251 00252 if (!xmmsv_get_type (prop) == XMMSV_TYPE_DICT) { 00253 xmms_error_set (err, XMMS_ERROR_INVAL, "properties must be sent as a dict!"); 00254 } else { 00255 /* record every pair */ 00256 xmmsv_get_dict_iter (prop, &it); 00257 while (xmmsv_dict_iter_valid (it)) { 00258 if (!xmmsv_dict_iter_pair (it, &key, &value)) { 00259 xmms_error_set (err, XMMS_ERROR_INVAL, "key-value property pair could not be read!"); 00260 } else if (!xmmsv_get_string (value, &valstr)) { 00261 xmms_error_set (err, XMMS_ERROR_INVAL, "property value could not be read!"); 00262 } else if (!property_set (&c->prop, key, valstr)) { 00263 xmms_error_set (err, XMMS_ERROR_INVAL, "property could not be set!"); 00264 } 00265 xmmsv_dict_iter_next (it); 00266 } 00267 /* TODO: propagate new format to xform! */ 00268 } 00269 00270 x_release_client (); 00271 00272 return (++c->format); 00273 } 00274 00275 static int32_t 00276 xmms_visualization_client_init_shm (xmms_visualization_t *vis, int32_t id, const char *shmidstr, xmms_error_t *err) 00277 { 00278 int shmid; 00279 00280 XMMS_DBG ("Trying to init shm!"); 00281 00282 if (sscanf (shmidstr, "%d", &shmid) != 1) { 00283 xmms_error_set (err, XMMS_ERROR_INVAL, "couldn't parse shmid"); 00284 return -1; 00285 } 00286 return init_shm (vis, id, shmid, err); 00287 } 00288 00289 static int32_t 00290 xmms_visualization_client_init_udp (xmms_visualization_t *vis, int32_t id, xmms_error_t *err) 00291 { 00292 XMMS_DBG ("Trying to init udp!"); 00293 return init_udp (vis, id, err); 00294 } 00295 00296 static void 00297 xmms_visualization_client_shutdown (xmms_visualization_t *vis, int32_t id, xmms_error_t *err) 00298 { 00299 g_mutex_lock (vis->clientlock); 00300 delete_client (id); 00301 g_mutex_unlock (vis->clientlock); 00302 } 00303 00304 static gboolean 00305 package_write (xmms_vis_client_t *c, int32_t id, struct timeval *time, int channels, int size, short *buf) 00306 { 00307 if (c->type == VIS_UNIXSHM) { 00308 return write_shm (&c->transport.shm, c, id, time, channels, size, buf); 00309 } else if (c->type == VIS_UDP) { 00310 return write_udp (&c->transport.udp, c, id, time, channels, size, buf, vis->socket); 00311 } 00312 return FALSE; 00313 } 00314 00315 void 00316 send_data (int channels, int size, short *buf) 00317 { 00318 int i; 00319 struct timeval time; 00320 guint32 latency; 00321 00322 if (!vis) { 00323 return; 00324 } 00325 00326 latency = xmms_output_latency (vis->output); 00327 00328 fft_init (); 00329 00330 gettimeofday (&time, NULL); 00331 time.tv_sec += (latency / 1000); 00332 time.tv_usec += (latency % 1000) * 1000; 00333 if (time.tv_usec > 1000000) { 00334 time.tv_sec++; 00335 time.tv_usec -= 1000000; 00336 } 00337 00338 g_mutex_lock (vis->clientlock); 00339 for (i = 0; i < vis->clientc; ++i) { 00340 if (vis->clientv[i]) { 00341 package_write (vis->clientv[i], i, &time, channels, size, buf); 00342 } 00343 } 00344 g_mutex_unlock (vis->clientlock); 00345 } 00346 00347 /** @} */