Mercurial > audlegacy-plugins
comparison src/rootvis/rootvis.c @ 900:d985f0dcdeb0 trunk
[svn] - add a starting point for xmms-rootvis port. giacomo will need to
finish this up, as my XLib skills are not enough at this time.
author | nenolod |
---|---|
date | Mon, 26 Mar 2007 01:19:26 -0700 |
parents | |
children | 5aaf6c282617 |
comparison
equal
deleted
inserted
replaced
899:68508f8cdf25 | 900:d985f0dcdeb0 |
---|---|
1 #include <string.h> | |
2 #include <math.h> | |
3 #include <pthread.h> | |
4 #include <time.h> | |
5 | |
6 #include "rootvis.h" | |
7 // as imlib2 uses X definitions, it has to be included after the X includes, which are done in rootvis.h | |
8 #include <Imlib2.h> | |
9 #include "config.h" | |
10 | |
11 extern Window ToonGetRootWindow(Display*, int, Window*); | |
12 | |
13 // Forward declarations | |
14 static void rootvis_init(void); | |
15 static void rootvis_cleanup(void); | |
16 static void rootvis_about(void); | |
17 static void rootvis_configure(void); | |
18 static void rootvis_playback_start(void); | |
19 static void rootvis_playback_stop(void); | |
20 static void rootvis_render_freq(gint16 freq_data[2][256]); | |
21 | |
22 // Callback functions | |
23 VisPlugin rootvis_vtable = { | |
24 0, // Handle, filled in by xmms | |
25 0, // Filename, filled in by xmms | |
26 | |
27 0, // Session ID | |
28 "Root Spectrum Analyzer 0.0.8", // description | |
29 | |
30 0, // # of PCM channels for render_pcm() | |
31 2, // # of freq channels wanted for render_freq() | |
32 | |
33 rootvis_init, // Called when plugin is enabled | |
34 rootvis_cleanup, // Called when plugin is disabled | |
35 NULL,//rootvis_about, // Show the about box | |
36 rootvis_configure, // Show the configure box | |
37 0, // Called to disable plugin, filled in by xmms | |
38 rootvis_playback_start, // Called when playback starts | |
39 rootvis_playback_stop, // Called when playback stops | |
40 0, // Render the PCM data, must return quickly | |
41 rootvis_render_freq // Render the freq data, must return quickly | |
42 }; | |
43 | |
44 // XMMS entry point | |
45 VisPlugin *get_vplugin_info(void) { | |
46 return &rootvis_vtable; | |
47 } | |
48 | |
49 // X related | |
50 struct rootvis_x { | |
51 int screen; | |
52 Display *display; | |
53 Window rootWin, Parent; | |
54 Pixmap rootBg; | |
55 GC gc; | |
56 | |
57 Visual *vis; | |
58 Colormap cm; | |
59 Imlib_Image background; | |
60 Imlib_Image buffer; | |
61 }; | |
62 | |
63 // thread talk | |
64 | |
65 struct rootvis_threads { | |
66 gint16 freq_data[2][256]; | |
67 pthread_t worker[2]; | |
68 pthread_mutex_t mutex1; | |
69 enum {GO, STOP} control; | |
70 char dirty; | |
71 /*** dirty flaglist *** | |
72 1: channel 1 geometry change | |
73 2: channel 1 color change | |
74 4: channel 2 geometry change | |
75 8: channel 2 color change | |
76 16: no data yet (don't do anything) | |
77 32: switch mono/stereo | |
78 */ | |
79 } threads; | |
80 | |
81 // For use in config_backend: | |
82 | |
83 void threads_lock(void) { | |
84 print_status("Locking"); | |
85 pthread_mutex_lock(&threads.mutex1); | |
86 } | |
87 | |
88 void threads_unlock(char dirty) { | |
89 print_status("Unlocking"); | |
90 threads.dirty = threads.dirty & dirty; | |
91 pthread_mutex_unlock(&threads.mutex1); | |
92 } | |
93 | |
94 // Some helper stuff | |
95 | |
96 void clean_data(void) { | |
97 pthread_mutex_lock(&threads.mutex1); | |
98 memset(threads.freq_data, 0, sizeof(gint16) * 2 * 256); | |
99 pthread_mutex_unlock(&threads.mutex1); | |
100 } | |
101 | |
102 void print_status(char msg[]) { | |
103 if (conf.debug == 1) printf(">> rootvis >> %s\n", msg); // for debug purposes, but doesn't tell much anyway | |
104 } | |
105 | |
106 void error_exit(char msg[]) { | |
107 printf("*** ERROR (rootvis): %s\n", msg); | |
108 rootvis_vtable.disable_plugin(&rootvis_vtable); | |
109 } | |
110 | |
111 void initialize_X(struct rootvis_x* drw, char* display) { | |
112 print_status("Opening X Display"); | |
113 drw->display = XOpenDisplay(display); | |
114 if (drw->display == NULL) { | |
115 fprintf(stderr, "cannot connect to X server %s\n", | |
116 getenv("DISPLAY") ? getenv("DISPLAY") : "(default)"); | |
117 error_exit("Connecting to X server failed"); | |
118 pthread_exit(NULL); | |
119 } | |
120 print_status("Getting screen and window"); | |
121 drw->screen = DefaultScreen(drw->display); | |
122 drw->rootWin = ToonGetRootWindow(drw->display, drw->screen, &drw->Parent); | |
123 | |
124 print_status("Initializing Imlib2"); | |
125 | |
126 drw->vis = DefaultVisual(drw->display, drw->screen); | |
127 drw->cm = DefaultColormap(drw->display, drw->screen); | |
128 | |
129 imlib_context_set_display(drw->display); | |
130 imlib_context_set_visual(drw->vis); | |
131 imlib_context_set_colormap(drw->cm); | |
132 | |
133 imlib_context_set_dither(0); | |
134 imlib_context_set_blend(1); | |
135 } | |
136 | |
137 void draw_init(struct rootvis_x* drw, unsigned short damage_coords[4]) | |
138 { | |
139 Atom tmp_rootmapid, tmp_type; | |
140 int tmp_format; | |
141 unsigned long tmp_length, tmp_after; | |
142 unsigned char *data = NULL; | |
143 | |
144 if ((tmp_rootmapid = XInternAtom(drw->display, "_XROOTPMAP_ID", True)) != None) | |
145 { | |
146 int ret = XGetWindowProperty(drw->display, drw->rootWin, tmp_rootmapid, 0L, 1L, False, AnyPropertyType, | |
147 &tmp_type, &tmp_format, &tmp_length, &tmp_after,&data); | |
148 if ((ret == Success)&&(tmp_type == XA_PIXMAP)&&((drw->rootBg = *((Pixmap *)data)) != None)) { | |
149 pthread_mutex_lock(&threads.mutex1); | |
150 imlib_context_set_drawable(drw->rootBg); | |
151 drw->background = imlib_create_image_from_drawable(0, damage_coords[0], damage_coords[1], damage_coords[2], damage_coords[3], 1); | |
152 pthread_mutex_unlock(&threads.mutex1); | |
153 } | |
154 if (drw->background == NULL) | |
155 error_exit("Initial image could not be created"); | |
156 } | |
157 } | |
158 | |
159 void draw_close(struct rootvis_x* drw, unsigned short damage_coords[4]) { | |
160 pthread_mutex_lock(&threads.mutex1); | |
161 imlib_context_set_image(drw->background); | |
162 imlib_render_image_on_drawable(damage_coords[0], damage_coords[1]); | |
163 XClearArea(drw->display, drw->rootWin, damage_coords[0], damage_coords[1], damage_coords[2], damage_coords[3], True); | |
164 imlib_free_image(); | |
165 pthread_mutex_unlock(&threads.mutex1); | |
166 } | |
167 | |
168 void draw_start(struct rootvis_x* drw, unsigned short damage_coords[4]) { | |
169 imlib_context_set_image(drw->background); | |
170 drw->buffer = imlib_clone_image(); | |
171 imlib_context_set_image(drw->buffer); | |
172 } | |
173 | |
174 void draw_end(struct rootvis_x* drw, unsigned short damage_coords[4]) { | |
175 imlib_context_set_drawable(drw->rootWin); | |
176 imlib_render_image_on_drawable(damage_coords[0], damage_coords[1]); | |
177 imlib_free_image(); | |
178 } | |
179 | |
180 void draw_bar(struct rootvis_x* drw, int t, int i, unsigned short level, unsigned short oldlevel, unsigned short peak, unsigned short oldpeak) { | |
181 | |
182 /* to make following cleaner, we work with redundant helper variables | |
183 this also avoids some calculations */ | |
184 register int a, b, c, d; | |
185 float angle; | |
186 Imlib_Color_Range range = imlib_create_color_range(); | |
187 | |
188 if (conf.geo[t].orientation < 2) { | |
189 a = i*(conf.bar[t].width + conf.bar[t].shadow + conf.geo[t].space); | |
190 c = conf.bar[t].width; | |
191 b = d = 0; | |
192 } else { | |
193 b = (conf.data[t].cutoff/conf.data[t].div - i - 1) | |
194 *(conf.bar[t].width + conf.bar[t].shadow + conf.geo[t].space); | |
195 d = conf.bar[t].width; | |
196 a = c = 0; | |
197 } | |
198 | |
199 if (conf.geo[t].orientation == 0) { b = conf.geo[t].height - level; d = level; } | |
200 else if (conf.geo[t].orientation == 1) { b = 0; d = level; } | |
201 else if (conf.geo[t].orientation == 2) { a = 0; c = level; } | |
202 else { a = conf.geo[t].height - level; c = level; } | |
203 | |
204 if (conf.bar[t].shadow > 0) { | |
205 imlib_context_set_color(conf.bar[t].shadow_color[0], conf.bar[t].shadow_color[1], | |
206 conf.bar[t].shadow_color[2], conf.bar[t].shadow_color[3]); | |
207 if (conf.bar[t].gradient) | |
208 imlib_image_fill_rectangle(a + conf.bar[t].shadow, b + conf.bar[t].shadow, c, d); | |
209 else if (conf.bar[t].bevel) | |
210 imlib_image_draw_rectangle(a + conf.bar[t].shadow, b + conf.bar[t].shadow, c, d); | |
211 | |
212 if (conf.peak[t].shadow > 0) | |
213 { | |
214 int aa = a, bb = b, cc = c, dd = d; | |
215 if (conf.geo[t].orientation == 0) { bb = conf.geo[t].height - peak; dd = 1; } | |
216 else if (conf.geo[t].orientation == 1) { bb = peak - 1; dd = 1; } | |
217 else if (conf.geo[t].orientation == 2) { aa = peak - 1; cc = 1; } | |
218 else { aa = conf.geo[t].height - peak; cc = 1; } | |
219 imlib_image_fill_rectangle(aa + conf.bar[t].shadow, bb + conf.bar[t].shadow, cc, dd); | |
220 } | |
221 } | |
222 | |
223 if (conf.bar[t].gradient) | |
224 { | |
225 switch (conf.geo[t].orientation) { | |
226 case 0: angle = 0.0; break; | |
227 case 1: angle = 180.0; break; | |
228 case 2: angle = 90.0; break; | |
229 case 3: default: | |
230 angle = -90.0; | |
231 } | |
232 | |
233 imlib_context_set_color_range(range); | |
234 imlib_context_set_color(conf.bar[t].color[3][0], conf.bar[t].color[3][1], conf.bar[t].color[3][2], conf.bar[t].color[3][3]); | |
235 imlib_add_color_to_color_range(0); | |
236 imlib_context_set_color(conf.bar[t].color[2][0], conf.bar[t].color[2][1], conf.bar[t].color[2][2], conf.bar[t].color[2][3]); | |
237 imlib_add_color_to_color_range(level * 2 / 5); | |
238 imlib_context_set_color(conf.bar[t].color[1][0], conf.bar[t].color[1][1], conf.bar[t].color[1][2], conf.bar[t].color[1][3]); | |
239 imlib_add_color_to_color_range(level * 4 / 5); | |
240 imlib_context_set_color(conf.bar[t].color[0][0], conf.bar[t].color[0][1], conf.bar[t].color[0][2], conf.bar[t].color[0][3]); | |
241 imlib_add_color_to_color_range(level); | |
242 imlib_image_fill_color_range_rectangle(a, b, c, d, angle); | |
243 imlib_free_color_range(); | |
244 } | |
245 | |
246 if (conf.bar[t].bevel) | |
247 { | |
248 imlib_context_set_color(conf.bar[t].bevel_color[0], conf.bar[t].bevel_color[1], | |
249 conf.bar[t].bevel_color[2], conf.bar[t].bevel_color[3]); | |
250 imlib_image_draw_rectangle(a, b, c, d); | |
251 } | |
252 | |
253 if (peak > 0) { | |
254 if (conf.geo[t].orientation == 0) { b = conf.geo[t].height - peak; d = 1; } | |
255 else if (conf.geo[t].orientation == 1) { b = peak - 1; d = 1; } | |
256 else if (conf.geo[t].orientation == 2) { a = peak - 1; c = 1; } | |
257 else { a = conf.geo[t].height - peak; c = 1; } | |
258 imlib_context_set_color(conf.peak[t].color[0], conf.peak[t].color[1], conf.peak[t].color[2], conf.peak[t].color[3]); | |
259 imlib_image_fill_rectangle(a, b, c, d); | |
260 } | |
261 } | |
262 | |
263 // Our worker thread | |
264 | |
265 void* worker_func(void* threadnump) { | |
266 struct rootvis_x draw; | |
267 gint16 freq_data[256]; | |
268 double scale = 0.0, x00 = 0.0, y00 = 0.0; | |
269 unsigned int threadnum, i, j, level; | |
270 unsigned short damage_coords[4]; | |
271 unsigned short *level1 = NULL, *level2 = NULL, *levelsw, *peak1 = NULL, *peak2 = NULL, *peakstep; | |
272 int barcount = 0; | |
273 | |
274 if (threadnump == NULL) threadnum = 0; else threadnum = 1; | |
275 | |
276 print_status("Memory allocations"); | |
277 level1 = (unsigned short*)calloc(256, sizeof(short)); // need to be zeroed out | |
278 level2 = (unsigned short*)malloc(256*sizeof(short)); | |
279 peak1 = (unsigned short*)calloc(256, sizeof(short)); // need to be zeroed out | |
280 peak2 = (unsigned short*)calloc(256, sizeof(short)); // need to be zeroed out for disabled peaks | |
281 peakstep = (unsigned short*)calloc(256, sizeof(short)); // need to be zeroed out | |
282 if ((level1 == NULL)||(level2 == NULL)||(peak1 == NULL)||(peak2 == NULL)||(peakstep == NULL)) { | |
283 error_exit("Allocation of memory failed"); | |
284 pthread_exit(NULL); | |
285 } | |
286 print_status("Allocations done"); | |
287 | |
288 draw.display = NULL; | |
289 | |
290 while (threads.control != STOP) { | |
291 | |
292 { | |
293 //print_status("start sleep"); | |
294 struct timespec sleeptime; | |
295 sleeptime.tv_sec = 0; | |
296 sleeptime.tv_nsec = 999999999 / conf.data[threadnum].fps; | |
297 while (nanosleep(&sleeptime, &sleeptime) == -1) {}; //print_status("INTR"); | |
298 //print_status("end sleep"); | |
299 } | |
300 | |
301 /* we will unset our own dirty flags after receiving them */ | |
302 pthread_mutex_lock(&threads.mutex1); | |
303 memcpy(&freq_data, &threads.freq_data[threadnum], sizeof(gint16)*256); | |
304 i = threads.dirty; | |
305 if ((i & 16) == 0) threads.dirty = i & (~(3 + threadnum*9)); | |
306 pthread_mutex_unlock(&threads.mutex1); | |
307 | |
308 if ((i & 16) == 0) { // we've gotten data | |
309 if (draw.display == NULL) initialize_X(&draw, conf.geo[threadnum].display); | |
310 else if (i & (1 + threadnum*3)) draw_close(&draw, damage_coords); | |
311 | |
312 if (i & (1 + threadnum*3)) { // geometry has changed | |
313 damage_coords[0] = conf.geo[threadnum].posx; | |
314 damage_coords[1] = conf.geo[threadnum].posy; | |
315 if (conf.geo[threadnum].orientation < 2) { | |
316 damage_coords[2] = conf.data[threadnum].cutoff/conf.data[threadnum].div | |
317 *(conf.bar[threadnum].width + conf.bar[threadnum].shadow + conf.geo[threadnum].space); | |
318 damage_coords[3] = conf.geo[threadnum].height + conf.bar[threadnum].shadow; | |
319 } else { | |
320 damage_coords[2] = conf.geo[threadnum].height + conf.bar[threadnum].shadow; | |
321 damage_coords[3] = conf.data[threadnum].cutoff/conf.data[threadnum].div | |
322 *(conf.bar[threadnum].width + conf.bar[threadnum].shadow + conf.geo[threadnum].space); | |
323 } | |
324 print_status("Geometry recalculations"); | |
325 scale = conf.geo[threadnum].height / | |
326 (log((1 - conf.data[threadnum].linearity) / conf.data[threadnum].linearity) * 4); | |
327 x00 = conf.data[threadnum].linearity*conf.data[threadnum].linearity*32768.0 / | |
328 (2*conf.data[threadnum].linearity - 1); | |
329 y00 = -log(-x00) * scale; | |
330 barcount = conf.data[threadnum].cutoff/conf.data[threadnum].div; | |
331 memset(level1, 0, 256*sizeof(short)); | |
332 memset(peak1, 0, 256*sizeof(short)); | |
333 memset(peak2, 0, 256*sizeof(short)); | |
334 | |
335 draw_init(&draw, damage_coords); | |
336 } | |
337 /*if (i & (2 + threadnum*6)) { // colors have changed | |
338 }*/ | |
339 | |
340 /* instead of copying the old level array to the second array, | |
341 we just tell the first is now the second one */ | |
342 levelsw = level1; | |
343 level1 = level2; | |
344 level2 = levelsw; | |
345 levelsw = peak1; | |
346 peak1 = peak2; | |
347 peak2 = levelsw; | |
348 | |
349 for (i = 0; i < barcount; i++) { | |
350 level = 0; | |
351 for (j = i*conf.data[threadnum].div; j < (i+1)*conf.data[threadnum].div; j++) | |
352 if (level < freq_data[j]) | |
353 level = freq_data[j]; | |
354 level = level * (i*conf.data[threadnum].div + 1); | |
355 level = floor(abs(log(level - x00)*scale + y00)); | |
356 if (level < conf.geo[threadnum].height) { | |
357 if ((level2[i] > conf.bar[threadnum].falloff)&&(level < level2[i] - conf.bar[threadnum].falloff)) | |
358 level1[i] = level2[i] - conf.bar[threadnum].falloff; | |
359 else level1[i] = level; | |
360 } else level1[i] = conf.geo[threadnum].height; | |
361 if (conf.peak[threadnum].enabled) { | |
362 if (level1[i] > peak2[i] - conf.peak[threadnum].falloff) { | |
363 peak1[i] = level1[i]; | |
364 peakstep[i] = 0; | |
365 } else if (peakstep[i] == conf.peak[threadnum].step) | |
366 if (peak2[i] > conf.peak[threadnum].falloff) | |
367 peak1[i] = peak2[i] - conf.peak[threadnum].falloff; | |
368 else peak1[i] = 0; | |
369 else { | |
370 peak1[i] = peak2[i]; | |
371 peakstep[i]++; | |
372 } | |
373 } | |
374 } | |
375 | |
376 pthread_mutex_lock(&threads.mutex1); | |
377 draw_start(&draw, damage_coords); | |
378 for (i = 0; i < barcount; i++) | |
379 draw_bar(&draw, threadnum, i, level1[i], level2[i], peak1[i], peak2[i]); | |
380 draw_end(&draw, damage_coords); | |
381 pthread_mutex_unlock(&threads.mutex1); | |
382 } | |
383 } | |
384 print_status("Worker thread: Exiting"); | |
385 if (draw.display != NULL) { | |
386 draw_close(&draw, damage_coords); | |
387 XCloseDisplay(draw.display); | |
388 } | |
389 free(level1); free(level2); free(peak1); free(peak2); free(peakstep); | |
390 return NULL; | |
391 } | |
392 | |
393 | |
394 // da xmms functions | |
395 | |
396 static void rootvis_init(void) { | |
397 int rc1; | |
398 print_status("Initializing"); | |
399 pthread_mutex_init(&threads.mutex1, NULL); | |
400 threads.control = GO; | |
401 clean_data(); | |
402 config_init(); | |
403 threads.dirty = 31; // this means simply everything has changed and there was no data | |
404 if ((rc1 = pthread_create(&threads.worker[0], NULL, worker_func, NULL))) { | |
405 fprintf(stderr, "Thread creation failed: %d\n", rc1); | |
406 error_exit("Thread creation failed"); | |
407 } | |
408 if ((conf.stereo)&&(rc1 = pthread_create(&threads.worker[1], NULL, worker_func, &rc1))) { | |
409 fprintf(stderr, "Thread creation failed: %d\n", rc1); | |
410 error_exit("Thread creation failed"); | |
411 } | |
412 } | |
413 | |
414 static void rootvis_cleanup(void) { | |
415 print_status("Cleanup... "); | |
416 threads.control = STOP; | |
417 pthread_join(threads.worker[0], NULL); | |
418 if (conf.stereo) pthread_join(threads.worker[1], NULL); | |
419 print_status("Clean Exit"); | |
420 } | |
421 | |
422 static void rootvis_about(void) | |
423 { | |
424 print_status("About"); | |
425 } | |
426 | |
427 static void rootvis_configure(void) | |
428 { | |
429 print_status("Configuration trigger"); | |
430 config_init(); | |
431 config_show(2); | |
432 } | |
433 | |
434 static void rootvis_playback_start(void) | |
435 { | |
436 print_status("Playback starting"); | |
437 } | |
438 | |
439 static void rootvis_playback_stop(void) | |
440 { | |
441 clean_data(); | |
442 } | |
443 | |
444 static void rootvis_render_freq(gint16 freq_data[2][256]) { | |
445 int channel, bucket; | |
446 pthread_mutex_lock(&threads.mutex1); | |
447 threads.dirty = threads.dirty & (~(16)); // unset no data yet flag | |
448 for (channel = 0; channel < 2; channel++) { | |
449 for (bucket = 0; bucket < 256; bucket++) { | |
450 if (conf.stereo) threads.freq_data[channel][bucket] = freq_data[channel][bucket]; | |
451 else if (channel == 0) threads.freq_data[0][bucket] = freq_data[channel][bucket] / 2; | |
452 else threads.freq_data[0][bucket] += freq_data[channel][bucket] / 2; | |
453 } | |
454 } | |
455 pthread_mutex_unlock(&threads.mutex1); | |
456 } |