XMMS2
object.c
Go to the documentation of this file.
1 /* XMMS2 - X Music Multiplexer System
2  * Copyright (C) 2003-2009 XMMS2 Team
3  *
4  * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  */
16 
17 #include <string.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 
21 #include "xmms/xmms_object.h"
22 #include "xmmspriv/xmms_ipc.h"
23 #include "xmmspriv/xmms_sample.h"
24 
25 #include "common.h"
26 
27 /** @defgroup Visualization Visualization
28  * @ingroup XMMSServer
29  * @brief Feeds playing data in various forms to the client.
30  * @{
31  */
32 
33 static xmms_visualization_t *vis = NULL;
34 
35 static int32_t xmms_visualization_client_version (xmms_visualization_t *vis, xmms_error_t *err);
36 static int32_t xmms_visualization_client_register (xmms_visualization_t *vis, xmms_error_t *err);
37 static int32_t xmms_visualization_client_init_shm (xmms_visualization_t *vis, int32_t id, const char *shmid, xmms_error_t *err);
38 static int32_t xmms_visualization_client_init_udp (xmms_visualization_t *vis, int32_t id, xmms_error_t *err);
39 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);
40 static int32_t xmms_visualization_client_properties_set (xmms_visualization_t *vis, int32_t id, xmmsv_t *prop, xmms_error_t *err);
41 static void xmms_visualization_client_shutdown (xmms_visualization_t *vis, int32_t id, xmms_error_t *err);
42 static void xmms_visualization_destroy (xmms_object_t *object);
43 
44 
45 XMMS_CMD_DEFINE (query_version, xmms_visualization_client_version, xmms_visualization_t *, INT32, NONE, NONE);
46 XMMS_CMD_DEFINE (registercl, xmms_visualization_client_register, xmms_visualization_t *, INT32, NONE, NONE);
47 XMMS_CMD_DEFINE (init_shm, xmms_visualization_client_init_shm, xmms_visualization_t *, INT32, INT32, STRING);
48 XMMS_CMD_DEFINE (init_udp, xmms_visualization_client_init_udp, xmms_visualization_t *, INT32, INT32, NONE);
49 XMMS_CMD_DEFINE3 (property_set, xmms_visualization_client_property_set, xmms_visualization_t *, INT32, INT32, STRING, STRING);
50 XMMS_CMD_DEFINE (properties_set, xmms_visualization_client_properties_set, xmms_visualization_t *, INT32, INT32, DICT);
51 XMMS_CMD_DEFINE (shutdown, xmms_visualization_client_shutdown, xmms_visualization_t *, NONE, INT32, NONE);
52 
53 /* create an uninitialised vis client. don't use this method without mutex! */
54 static int32_t
55 create_client (void)
56 {
57  int32_t id;
58 
59  for (id = 0; id < vis->clientc; ++id) {
60  if (!vis->clientv[id]) {
61  break;
62  }
63  }
64 
65  if (id == vis->clientc) {
66  vis->clientc++;
67  }
68 
69  vis->clientv = g_renew (xmms_vis_client_t*, vis->clientv, vis->clientc);
70  if (!vis->clientv || (!(vis->clientv[id] = g_new (xmms_vis_client_t, 1)))) {
71  vis->clientc = 0;
72  id = -1;
73  }
74 
75  xmms_log_info ("Attached visualization client %d", id);
76  return id;
77 }
78 
80 get_client (int32_t id)
81 {
82  if (id < 0 || id >= vis->clientc) {
83  return NULL;
84  }
85 
86  return vis->clientv[id];
87 }
88 
89 /* delete a vis client. don't use this method without mutex! */
90 void
91 delete_client (int32_t id)
92 {
94 
95  if (id < 0 || id >= vis->clientc) {
96  return;
97  }
98 
99  c = vis->clientv[id];
100  if (c == NULL) {
101  return;
102  }
103 
104  if (c->type == VIS_UNIXSHM) {
105  cleanup_shm (&c->transport.shm);
106  } else if (c->type == VIS_UDP) {
107  cleanup_udp (&c->transport.udp, vis->socket);
108  }
109 
110  g_free (c);
111  vis->clientv[id] = NULL;
112 
113  xmms_log_info ("Removed visualization client %d", id);
114 }
115 
116 /**
117  * Initialize the Vis module.
118  */
121 {
122  vis = xmms_object_new (xmms_visualization_t, xmms_visualization_destroy);
123  vis->clientlock = g_mutex_new ();
124  vis->clientc = 0;
125  vis->output = output;
126 
127  xmms_object_ref (output);
128 
132  XMMS_CMD_FUNC (query_version));
135  XMMS_CMD_FUNC (registercl));
144  XMMS_CMD_FUNC (property_set));
147  XMMS_CMD_FUNC (properties_set));
150  XMMS_CMD_FUNC (shutdown));
151 
153 
154  return vis;
155 }
156 
157 /**
158  * Free all resoures used by visualization module.
159  * TODO: Fill this in properly, unregister etc!
160  */
161 
162 static void
163 xmms_visualization_destroy (xmms_object_t *object)
164 {
165  xmms_object_unref (vis->output);
166 
167  /* TODO: assure that the xform is already dead! */
168  g_mutex_free (vis->clientlock);
169  xmms_log_debug ("starting cleanup of %d vis clients", vis->clientc);
170  for (; vis->clientc > 0; --vis->clientc) {
171  delete_client (vis->clientc - 1);
172  }
173 
174  if (xmms_socket_valid (vis->socket)) {
175  /* it seems there is no way to remove the watch */
176  g_io_channel_shutdown (vis->socketio, FALSE, NULL);
177  xmms_socket_close (vis->socket);
178  }
180 }
181 
182 static int32_t
183 xmms_visualization_client_version (xmms_visualization_t *vis, xmms_error_t *err)
184 {
185  /* if there is a way to disable visualization support on the server side,
186  we could return 0 here, or we could return an error? */
187 
188  return XMMS_VISPACKET_VERSION;
189 }
190 
191 static void
192 properties_init (xmmsc_vis_properties_t *p)
193 {
194  p->type = VIS_PCM;
195  p->stereo = 1;
196  p->pcm_hardwire = 0;
197 }
198 
199 static gboolean
200 property_set (xmmsc_vis_properties_t *p, const gchar* key, const gchar* data)
201 {
202 
203  if (!g_strcasecmp (key, "type")) {
204  if (!g_strcasecmp (data, "pcm")) {
205  p->type = VIS_PCM;
206  } else if (!g_strcasecmp (data, "spectrum")) {
207  p->type = VIS_SPECTRUM;
208  } else if (!g_strcasecmp (data, "peak")) {
209  p->type = VIS_PEAK;
210  } else {
211  return FALSE;
212  }
213  } else if (!g_strcasecmp (key, "stereo")) {
214  p->stereo = (atoi (data) > 0);
215  } else if (!g_strcasecmp (key, "pcm.hardwire")) {
216  p->pcm_hardwire = (atoi (data) > 0);
217  /* TODO: all the stuff following */
218  } else if (!g_strcasecmp (key, "timeframe")) {
219  p->timeframe = g_strtod (data, NULL);
220  if (p->timeframe == 0.0) {
221  return FALSE;
222  }
223  } else {
224  return FALSE;
225  }
226  return TRUE;
227 }
228 
229 static int32_t
230 xmms_visualization_client_register (xmms_visualization_t *vis, xmms_error_t *err)
231 {
232  int32_t id;
234 
235  g_mutex_lock (vis->clientlock);
236  id = create_client ();
237  if (id < 0) {
238  xmms_error_set (err, XMMS_ERROR_OOM, "could not allocate dataset");
239  } else {
240  /* do necessary initialisations here */
241  c = get_client (id);
242  c->type = VIS_NONE;
243  c->format = 0;
244  properties_init (&c->prop);
245  }
246  g_mutex_unlock (vis->clientlock);
247  return id;
248 }
249 
250 
251 static int32_t
252 xmms_visualization_client_property_set (xmms_visualization_t *vis, int32_t id, const gchar* key, const gchar* value, xmms_error_t *err)
253 {
255 
256  x_fetch_client (id);
257 
258  if (!property_set (&c->prop, key, value)) {
259  xmms_error_set (err, XMMS_ERROR_INVAL, "property could not be set!");
260  }
261 
262  x_release_client ();
263 
264  /* the format identifier (between client and server) changes. so the client can recognize the first packet
265  which is built using the new format according to the newly set property */
266  return (++c->format);
267 }
268 
269 static int32_t
270 xmms_visualization_client_properties_set (xmms_visualization_t *vis, int32_t id, xmmsv_t* prop, xmms_error_t *err)
271 {
273  xmmsv_dict_iter_t *it;
274  const gchar *key, *valstr;
275  xmmsv_t *value;
276 
277  x_fetch_client (id);
278 
279  if (!xmmsv_get_type (prop) == XMMSV_TYPE_DICT) {
280  xmms_error_set (err, XMMS_ERROR_INVAL, "properties must be sent as a dict!");
281  } else {
282  /* record every pair */
283  xmmsv_get_dict_iter (prop, &it);
284  while (xmmsv_dict_iter_valid (it)) {
285  if (!xmmsv_dict_iter_pair (it, &key, &value)) {
286  xmms_error_set (err, XMMS_ERROR_INVAL, "key-value property pair could not be read!");
287  } else if (!xmmsv_get_string (value, &valstr)) {
288  xmms_error_set (err, XMMS_ERROR_INVAL, "property value could not be read!");
289  } else if (!property_set (&c->prop, key, valstr)) {
290  xmms_error_set (err, XMMS_ERROR_INVAL, "property could not be set!");
291  }
293  }
294  /* TODO: propagate new format to xform! */
295  }
296 
297  x_release_client ();
298 
299  return (++c->format);
300 }
301 
302 static int32_t
303 xmms_visualization_client_init_shm (xmms_visualization_t *vis, int32_t id, const char *shmidstr, xmms_error_t *err)
304 {
305  int shmid;
306 
307  XMMS_DBG ("Trying to init shm!");
308 
309  if (sscanf (shmidstr, "%d", &shmid) != 1) {
310  xmms_error_set (err, XMMS_ERROR_INVAL, "couldn't parse shmid");
311  return -1;
312  }
313  return init_shm (vis, id, shmid, err);
314 }
315 
316 static int32_t
317 xmms_visualization_client_init_udp (xmms_visualization_t *vis, int32_t id, xmms_error_t *err)
318 {
319  XMMS_DBG ("Trying to init udp!");
320  return init_udp (vis, id, err);
321 }
322 
323 static void
324 xmms_visualization_client_shutdown (xmms_visualization_t *vis, int32_t id, xmms_error_t *err)
325 {
326  g_mutex_lock (vis->clientlock);
327  delete_client (id);
328  g_mutex_unlock (vis->clientlock);
329 }
330 
331 static gboolean
332 package_write (xmms_vis_client_t *c, int32_t id, struct timeval *time, int channels, int size, short *buf)
333 {
334  if (c->type == VIS_UNIXSHM) {
335  return write_shm (&c->transport.shm, c, id, time, channels, size, buf);
336  } else if (c->type == VIS_UDP) {
337  return write_udp (&c->transport.udp, c, id, time, channels, size, buf, vis->socket);
338  }
339  return FALSE;
340 }
341 
342 void
343 send_data (int channels, int size, short *buf)
344 {
345  int i;
346  struct timeval time;
347  guint32 latency;
348 
349  if (!vis) {
350  return;
351  }
352 
353  latency = xmms_output_latency (vis->output);
354 
355  fft_init ();
356 
357  gettimeofday (&time, NULL);
358  time.tv_sec += (latency / 1000);
359  time.tv_usec += (latency % 1000) * 1000;
360  if (time.tv_usec > 1000000) {
361  time.tv_sec++;
362  time.tv_usec -= 1000000;
363  }
364 
365  g_mutex_lock (vis->clientlock);
366  for (i = 0; i < vis->clientc; ++i) {
367  if (vis->clientv[i]) {
368  package_write (vis->clientv[i], i, &time, channels, size, buf);
369  }
370  }
371  g_mutex_unlock (vis->clientlock);
372 }
373 
374 /** @} */
struct xmmsv_St xmmsv_t
Definition: xmmsv.h:51
#define XMMS_CMD_FUNC(cmdid)
Definition: xmms_object.h:181
#define XMMS_OBJECT(p)
Definition: xmms_object.h:84
#define xmms_object_unref(obj)
Definition: xmms_object.h:193
int xmmsv_dict_iter_valid(xmmsv_dict_iter_t *it)
Check whether the iterator is valid and points to a valid pair.
Definition: value.c:1975
xmmsc_vis_properties_t prop
Definition: common.h:37
The structures for a vis client.
Definition: common.h:30
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.
Definition: object.c:321
xmms_socket_t socket
Definition: common.h:81
gboolean write_udp(xmmsc_vis_udp_t *t, xmms_vis_client_t *c, int32_t id, struct timeval *time, int channels, int size, short *buf, int socket)
Definition: udp.c:181
guint32 xmms_output_latency(xmms_output_t *output)
Definition: output.c:790
union xmms_vis_client_t::@2 transport
xmms_vis_client_t ** clientv
Definition: common.h:86
xmms_vis_client_t * get_client(int32_t id)
Definition: object.c:80
void cleanup_shm(xmmsc_vis_unixshm_t *t)
Definition: dummy.c:27
GMutex * clientlock
Definition: common.h:84
int xmmsv_dict_iter_pair(xmmsv_dict_iter_t *it, const char **key, xmmsv_t **val)
Get the key-element pair currently pointed at by the iterator.
Definition: value.c:1940
int32_t init_udp(xmms_visualization_t *vis, int32_t id, xmms_error_t *err)
Definition: udp.c:106
#define x_fetch_client(id)
Definition: common.h:63
xmmsc_vis_transport_t type
Definition: common.h:35
struct xmms_output_St xmms_output_t
#define x_release_client()
Definition: common.h:71
xmmsc_vis_udp_t udp
Definition: common.h:33
void xmms_socket_invalidate(xmms_socket_t *socket)
Definition: socket_unix.c:43
xmmsv_type_t xmmsv_get_type(const xmmsv_t *val)
Get the type of the value.
Definition: value.c:384
xmms_visualization_t * xmms_visualization_new(xmms_output_t *output)
Initialize the Vis module.
Definition: object.c:120
void xmms_ipc_object_unregister(xmms_ipc_objects_t objectid)
Remove a object from the IPC core.
Definition: ipc.c:769
void xmmsv_dict_iter_next(xmmsv_dict_iter_t *it)
Advance the iterator to the next pair in the dict.
Definition: value.c:2001
Properties of the delivered vis data.
#define xmms_log_info(fmt,...)
Definition: xmms_log.h:34
XMMS_CMD_DEFINE3(property_set, xmms_visualization_client_property_set, xmms_visualization_t *, INT32, INT32, STRING, STRING)
int xmmsv_get_dict_iter(const xmmsv_t *val, xmmsv_dict_iter_t **it)
Retrieves a dict iterator from a dict xmmsv_t.
Definition: value.c:947
#define xmms_object_ref(obj)
Definition: xmms_object.h:187
void fft_init(void)
Definition: format.c:18
void xmms_log_debug(const gchar *fmt,...)
Definition: testclient.c:11
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
XMMS_CMD_DEFINE(query_version, xmms_visualization_client_version, xmms_visualization_t *, INT32, NONE, NONE)
void delete_client(int32_t id)
Definition: object.c:91
void send_data(int channels, int size, short *buf)
Definition: object.c:343
void cleanup_udp(xmmsc_vis_udp_t *t, xmms_socket_t socket)
Definition: udp.c:173
int xmmsv_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
Definition: value.c:855
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:199
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
xmmsc_vis_unixshm_t shm
Definition: common.h:32
#define XMMS_VISPACKET_VERSION
GIOChannel * socketio
Definition: common.h:82
xmms_output_t * output
Definition: common.h:80
void xmms_ipc_object_register(xmms_ipc_objects_t objectid, xmms_object_t *object)
Register a object to the IPC core.
Definition: ipc.c:758
void xmms_socket_close(xmms_socket_t socket)
Definition: socket_unix.c:47
int32_t init_shm(xmms_visualization_t *vis, int32_t id, int32_t shmid, xmms_error_t *err)
Definition: dummy.c:20
unsigned short format
Definition: common.h:36
The structures for the vis module.
Definition: common.h:78
int xmms_socket_valid(xmms_socket_t socket)
Definition: socket_unix.c:36
struct xmmsv_dict_iter_St xmmsv_dict_iter_t
Definition: xmmsv.h:54
gboolean write_shm(xmmsc_vis_unixshm_t *t, xmms_vis_client_t *c, int32_t id, struct timeval *time, int channels, int size, short *buf)
Definition: dummy.c:36