XMMS2
object.c
Go to the documentation of this file.
1 /* XMMS2 - X Music Multiplexer System
2  * Copyright (C) 2003-2011 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_query_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_set_property (xmms_visualization_t *vis, int32_t id, const gchar *key, const gchar *value, xmms_error_t *err);
40 static int32_t xmms_visualization_client_set_properties (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 #include "visualization/object_ipc.c"
45 
46 /* create an uninitialised vis client. don't use this method without mutex! */
47 static int32_t
48 create_client (void)
49 {
50  int32_t id;
51 
52  for (id = 0; id < vis->clientc; ++id) {
53  if (!vis->clientv[id]) {
54  break;
55  }
56  }
57 
58  if (id == vis->clientc) {
59  vis->clientc++;
60  }
61 
62  vis->clientv = g_renew (xmms_vis_client_t*, vis->clientv, vis->clientc);
63  if (!vis->clientv || (!(vis->clientv[id] = g_new (xmms_vis_client_t, 1)))) {
64  vis->clientc = 0;
65  id = -1;
66  }
67 
68  xmms_log_info ("Attached visualization client %d", id);
69  return id;
70 }
71 
73 get_client (int32_t id)
74 {
75  if (id < 0 || id >= vis->clientc) {
76  return NULL;
77  }
78 
79  return vis->clientv[id];
80 }
81 
82 /* delete a vis client. don't use this method without mutex! */
83 void
84 delete_client (int32_t id)
85 {
87 
88  if (id < 0 || id >= vis->clientc) {
89  return;
90  }
91 
92  c = vis->clientv[id];
93  if (c == NULL) {
94  return;
95  }
96 
97  if (c->type == VIS_UNIXSHM) {
99  } else if (c->type == VIS_UDP) {
100  cleanup_udp (&c->transport.udp, vis->socket);
101  }
102 
103  g_free (c);
104  vis->clientv[id] = NULL;
105 
106  xmms_log_info ("Removed visualization client %d", id);
107 }
108 
109 /**
110  * Initialize the Vis module.
111  */
114 {
115  vis = xmms_object_new (xmms_visualization_t, xmms_visualization_destroy);
116  vis->clientlock = g_mutex_new ();
117  vis->clientc = 0;
118  vis->output = output;
119 
120  xmms_object_ref (output);
121 
122  xmms_visualization_register_ipc_commands (XMMS_OBJECT (vis));
123 
125 
126  return vis;
127 }
128 
129 /**
130  * Free all resoures used by visualization module.
131  * TODO: Fill this in properly, unregister etc!
132  */
133 
134 static void
135 xmms_visualization_destroy (xmms_object_t *object)
136 {
137  xmms_object_unref (vis->output);
138 
139  /* TODO: assure that the xform is already dead! */
140  g_mutex_free (vis->clientlock);
141  xmms_log_debug ("starting cleanup of %d vis clients", vis->clientc);
142  for (; vis->clientc > 0; --vis->clientc) {
143  delete_client (vis->clientc - 1);
144  }
145 
146  if (xmms_socket_valid (vis->socket)) {
147  /* it seems there is no way to remove the watch */
148  g_io_channel_shutdown (vis->socketio, FALSE, NULL);
149  xmms_socket_close (vis->socket);
150  }
151 
152  xmms_visualization_unregister_ipc_commands ();
153 }
154 
155 static int32_t
156 xmms_visualization_client_query_version (xmms_visualization_t *vis, xmms_error_t *err)
157 {
158  /* if there is a way to disable visualization support on the server side,
159  we could return 0 here, or we could return an error? */
160 
161  return XMMS_VISPACKET_VERSION;
162 }
163 
164 static void
165 properties_init (xmmsc_vis_properties_t *p)
166 {
167  p->type = VIS_PCM;
168  p->stereo = 1;
169  p->pcm_hardwire = 0;
170 }
171 
172 static gboolean
173 property_set (xmmsc_vis_properties_t *p, const gchar* key, const gchar* data)
174 {
175 
176  if (!g_strcasecmp (key, "type")) {
177  if (!g_strcasecmp (data, "pcm")) {
178  p->type = VIS_PCM;
179  } else if (!g_strcasecmp (data, "spectrum")) {
180  p->type = VIS_SPECTRUM;
181  } else if (!g_strcasecmp (data, "peak")) {
182  p->type = VIS_PEAK;
183  } else {
184  return FALSE;
185  }
186  } else if (!g_strcasecmp (key, "stereo")) {
187  p->stereo = (atoi (data) > 0);
188  } else if (!g_strcasecmp (key, "pcm.hardwire")) {
189  p->pcm_hardwire = (atoi (data) > 0);
190  /* TODO: all the stuff following */
191  } else if (!g_strcasecmp (key, "timeframe")) {
192  p->timeframe = g_strtod (data, NULL);
193  if (p->timeframe == 0.0) {
194  return FALSE;
195  }
196  } else {
197  return FALSE;
198  }
199  return TRUE;
200 }
201 
202 static int32_t
203 xmms_visualization_client_register (xmms_visualization_t *vis, xmms_error_t *err)
204 {
205  int32_t id;
207 
208  g_mutex_lock (vis->clientlock);
209  id = create_client ();
210  if (id < 0) {
211  xmms_error_set (err, XMMS_ERROR_OOM, "could not allocate dataset");
212  } else {
213  /* do necessary initialisations here */
214  c = get_client (id);
215  c->type = VIS_NONE;
216  c->format = 0;
217  properties_init (&c->prop);
218  }
219  g_mutex_unlock (vis->clientlock);
220  return id;
221 }
222 
223 
224 static int32_t
225 xmms_visualization_client_set_property (xmms_visualization_t *vis, int32_t id, const gchar* key, const gchar* value, xmms_error_t *err)
226 {
228 
229  x_fetch_client (id);
230 
231  if (!property_set (&c->prop, key, value)) {
232  xmms_error_set (err, XMMS_ERROR_INVAL, "property could not be set!");
233  }
234 
235  x_release_client ();
236 
237  /* the format identifier (between client and server) changes. so the client can recognize the first packet
238  which is built using the new format according to the newly set property */
239  return (++c->format);
240 }
241 
242 static int32_t
243 xmms_visualization_client_set_properties (xmms_visualization_t *vis, int32_t id, xmmsv_t* prop, xmms_error_t *err)
244 {
246  xmmsv_dict_iter_t *it;
247  const gchar *key, *valstr;
248  xmmsv_t *value;
249 
250  x_fetch_client (id);
251 
252  if (!xmmsv_get_type (prop) == XMMSV_TYPE_DICT) {
253  xmms_error_set (err, XMMS_ERROR_INVAL, "properties must be sent as a dict!");
254  } else {
255  /* record every pair */
256  xmmsv_get_dict_iter (prop, &it);
257  while (xmmsv_dict_iter_valid (it)) {
258  if (!xmmsv_dict_iter_pair (it, &key, &value)) {
259  xmms_error_set (err, XMMS_ERROR_INVAL, "key-value property pair could not be read!");
260  } else if (!xmmsv_get_string (value, &valstr)) {
261  xmms_error_set (err, XMMS_ERROR_INVAL, "property value could not be read!");
262  } else if (!property_set (&c->prop, key, valstr)) {
263  xmms_error_set (err, XMMS_ERROR_INVAL, "property could not be set!");
264  }
266  }
267  /* TODO: propagate new format to xform! */
268  }
269 
270  x_release_client ();
271 
272  return (++c->format);
273 }
274 
275 static int32_t
276 xmms_visualization_client_init_shm (xmms_visualization_t *vis, int32_t id, const char *shmidstr, xmms_error_t *err)
277 {
278  int shmid;
279 
280  XMMS_DBG ("Trying to init shm!");
281 
282  if (sscanf (shmidstr, "%d", &shmid) != 1) {
283  xmms_error_set (err, XMMS_ERROR_INVAL, "couldn't parse shmid");
284  return -1;
285  }
286  return init_shm (vis, id, shmid, err);
287 }
288 
289 static int32_t
290 xmms_visualization_client_init_udp (xmms_visualization_t *vis, int32_t id, xmms_error_t *err)
291 {
292  XMMS_DBG ("Trying to init udp!");
293  return init_udp (vis, id, err);
294 }
295 
296 static void
297 xmms_visualization_client_shutdown (xmms_visualization_t *vis, int32_t id, xmms_error_t *err)
298 {
299  g_mutex_lock (vis->clientlock);
300  delete_client (id);
301  g_mutex_unlock (vis->clientlock);
302 }
303 
304 static gboolean
305 package_write (xmms_vis_client_t *c, int32_t id, struct timeval *time, int channels, int size, short *buf)
306 {
307  if (c->type == VIS_UNIXSHM) {
308  return write_shm (&c->transport.shm, c, id, time, channels, size, buf);
309  } else if (c->type == VIS_UDP) {
310  return write_udp (&c->transport.udp, c, id, time, channels, size, buf, vis->socket);
311  }
312  return FALSE;
313 }
314 
315 void
316 send_data (int channels, int size, short *buf)
317 {
318  int i;
319  struct timeval time;
320  guint32 latency;
321 
322  if (!vis) {
323  return;
324  }
325 
326  latency = xmms_output_latency (vis->output);
327 
328  fft_init ();
329 
330  gettimeofday (&time, NULL);
331  time.tv_sec += (latency / 1000);
332  time.tv_usec += (latency % 1000) * 1000;
333  if (time.tv_usec > 1000000) {
334  time.tv_sec++;
335  time.tv_usec -= 1000000;
336  }
337 
338  g_mutex_lock (vis->clientlock);
339  for (i = 0; i < vis->clientc; ++i) {
340  if (vis->clientv[i]) {
341  package_write (vis->clientv[i], i, &time, channels, size, buf);
342  }
343  }
344  g_mutex_unlock (vis->clientlock);
345 }
346 
347 /** @} */
#define XMMS_OBJECT(p)
Definition: xmms_object.h:77
#define xmms_object_unref(obj)
Definition: xmms_object.h:109
xmmsc_vis_properties_t prop
Definition: common.h:37
The structures for a vis client.
Definition: common.h:30
struct xmmsv_St xmmsv_t
Definition: xmmsv_general.h:48
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:1948
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:182
void xmmsv_dict_iter_next(xmmsv_dict_iter_t *it)
Advance the iterator to the next pair in the dict.
Definition: value.c:2009
guint32 xmms_output_latency(xmms_output_t *output)
Definition: output.c:789
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:73
void cleanup_shm(xmmsc_vis_unixshm_t *t)
Definition: dummy.c:27
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:1983
GMutex * clientlock
Definition: common.h:84
int32_t init_udp(xmms_visualization_t *vis, int32_t id, xmms_error_t *err)
Definition: udp.c:107
#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:392
xmms_visualization_t * xmms_visualization_new(xmms_output_t *output)
Initialize the Vis module.
Definition: object.c:113
Properties of the delivered vis data.
#define xmms_log_info(fmt,...)
Definition: xmms_log.h:34
#define xmms_object_ref(obj)
Definition: xmms_object.h:103
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
void delete_client(int32_t id)
Definition: object.c:84
void send_data(int channels, int size, short *buf)
Definition: object.c:316
void cleanup_udp(xmmsc_vis_udp_t *t, xmms_socket_t socket)
Definition: udp.c:174
int xmmsv_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
Definition: value.c:863
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:115
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
xmmsc_vis_unixshm_t shm
Definition: common.h:32
#define XMMS_VISPACKET_VERSION
struct xmmsv_dict_iter_St xmmsv_dict_iter_t
Definition: xmmsv_dict.h:59
GIOChannel * socketio
Definition: common.h:82
xmms_output_t * output
Definition: common.h:80
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:955
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
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