XMMS2
|
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 /** @} */