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"
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
33static xmms_visualization_t *vis = NULL;
34
35static int32_t xmms_visualization_client_query_version (xmms_visualization_t *vis, xmms_error_t *err);
36static int32_t xmms_visualization_client_register (xmms_visualization_t *vis, xmms_error_t *err);
37static int32_t xmms_visualization_client_init_shm (xmms_visualization_t *vis, int32_t id, const char *shmid, xmms_error_t *err);
38static int32_t xmms_visualization_client_init_udp (xmms_visualization_t *vis, int32_t id, xmms_error_t *err);
39static int32_t xmms_visualization_client_set_property (xmms_visualization_t *vis, int32_t id, const gchar *key, const gchar *value, xmms_error_t *err);
40static int32_t xmms_visualization_client_set_properties (xmms_visualization_t *vis, int32_t id, xmmsv_t *prop, xmms_error_t *err);
41static void xmms_visualization_client_shutdown (xmms_visualization_t *vis, int32_t id, xmms_error_t *err);
42static 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! */
47static int32_t
48create_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
73get_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! */
83void
84delete_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
134static void
135xmms_visualization_destroy (xmms_object_t *object)
136{
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);
150 }
151
152 xmms_visualization_unregister_ipc_commands ();
153}
154
155static int32_t
156xmms_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
162}
163
164static void
165properties_init (xmmsc_vis_properties_t *p)
166{
167 p->type = VIS_PCM;
168 p->stereo = 1;
169 p->pcm_hardwire = 0;
170}
171
172static gboolean
173property_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
202static int32_t
203xmms_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
224static int32_t
225xmms_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
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
242static int32_t
243xmms_visualization_client_set_properties (xmms_visualization_t *vis, int32_t id, xmmsv_t* prop, xmms_error_t *err)
244{
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
271
272 return (++c->format);
273}
274
275static int32_t
276xmms_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
289static int32_t
290xmms_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
296static void
297xmms_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
304static gboolean
305package_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
315void
316send_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/** @} */
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
int32_t init_udp(xmms_visualization_t *vis, int32_t id, xmms_error_t *err)
Definition udp.c:107
void cleanup_udp(xmmsc_vis_udp_t *t, xmms_socket_t socket)
Definition udp.c:174
void cleanup_shm(xmmsc_vis_unixshm_t *t)
Definition dummy.c:27
int32_t init_shm(xmms_visualization_t *vis, int32_t id, int32_t shmid, xmms_error_t *err)
Definition dummy.c:20
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
void fft_init(void)
Definition format.c:18
#define x_fetch_client(id)
Definition common.h:63
#define x_release_client()
Definition common.h:71
void xmmsv_dict_iter_next(xmmsv_dict_iter_t *it)
Advance the iterator to the next pair in the dict.
Definition value.c:2009
struct xmmsv_dict_iter_St xmmsv_dict_iter_t
Definition xmmsv_dict.h:59
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
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
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
struct xmms_output_St xmms_output_t
guint32 xmms_output_latency(xmms_output_t *output)
Definition output.c:789
int xmmsv_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
Definition value.c:863
struct xmmsv_St xmmsv_t
xmmsv_type_t xmmsv_get_type(const xmmsv_t *val)
Get the type of the value.
Definition value.c:392
@ XMMSV_TYPE_DICT
#define XMMS_VISPACKET_VERSION
xmms_vis_client_t * get_client(int32_t id)
Definition object.c:73
xmms_visualization_t * xmms_visualization_new(xmms_output_t *output)
Initialize the Vis module.
Definition object.c:113
void delete_client(int32_t id)
Definition object.c:84
void send_data(int channels, int size, short *buf)
Definition object.c:316
@ VIS_UNIXSHM
@ VIS_SPECTRUM
The structures for a vis client.
Definition common.h:30
xmmsc_vis_udp_t udp
Definition common.h:33
unsigned short format
Definition common.h:36
xmmsc_vis_transport_t type
Definition common.h:35
xmmsc_vis_properties_t prop
Definition common.h:37
union xmms_vis_client_t::@2 transport
xmmsc_vis_unixshm_t shm
Definition common.h:32
The structures for the vis module.
Definition common.h:78
GIOChannel * socketio
Definition common.h:82
xmms_socket_t socket
Definition common.h:81
GMutex * clientlock
Definition common.h:84
xmms_output_t * output
Definition common.h:80
xmms_vis_client_t ** clientv
Definition common.h:86
Properties of the delivered vis data.
#define xmms_log_info(fmt,...)
Definition xmms_log.h:34
#define xmms_log_debug
Definition xmms_log.h:26
#define XMMS_DBG(fmt,...)
Definition xmms_log.h:32
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
#define xmms_object_new(objtype, destroyfunc)
#define XMMS_OBJECT(p)
Definition xmms_object.h:77
#define xmms_object_ref(obj)
#define xmms_object_unref(obj)
@ XMMS_ERROR_INVAL
@ XMMS_ERROR_OOM
void xmms_socket_close(xmms_socket_t socket)
Definition socket_unix.c:47
void xmms_socket_invalidate(xmms_socket_t *socket)
Definition socket_unix.c:43
int xmms_socket_valid(xmms_socket_t socket)
Definition socket_unix.c:36