Mercurial > audlegacy-plugins
comparison src/iris/iris.c @ 116:a578bf9b2851 trunk
[svn] - iris vis plugin port
author | nenolod |
---|---|
date | Tue, 24 Oct 2006 21:25:31 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
115:2e77e3fdd3c1 | 116:a578bf9b2851 |
---|---|
1 /* Iris - visualization plugin for XMMS | |
2 * Copyright (C) 2000-2002 Cédric DELFOSSE (cdelfosse@free.fr) | |
3 * | |
4 * This program is free software; you can redistribute it and/or modify | |
5 * it under the terms of the GNU General Public License as published by | |
6 * the Free Software Foundation; either version 2 of the License, or | |
7 * (at your option) any later version. | |
8 * | |
9 * This program is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 * GNU General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU General Public License | |
15 * along with this program; if not, write to the Free Software | |
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
17 */ | |
18 | |
19 #include "config.h" | |
20 | |
21 #include <X11/Xlib.h> | |
22 #include <X11/keysym.h> | |
23 #include <gtk/gtk.h> | |
24 #include <math.h> | |
25 | |
26 #include <GL/gl.h> | |
27 #include <GL/glx.h> | |
28 #include <X11/extensions/xf86vmode.h> | |
29 #include <pthread.h> | |
30 #ifdef HAVE_SCHED_SETSCHEDULER | |
31 #include <sched.h> | |
32 #endif | |
33 #include <stdlib.h> | |
34 #include <sys/time.h> | |
35 #include <unistd.h> | |
36 | |
37 #include <audacious/plugin.h> | |
38 #include <audacious/util.h> | |
39 #include <audacious/beepctrl.h> | |
40 #include <audacious/configdb.h> | |
41 #include "iris.h" | |
42 | |
43 #define BEAT_MAX 100 | |
44 | |
45 GLfloat y_angle = 45.0, y_speed = 0.5; | |
46 GLfloat x_angle = 20.0, x_speed = 0.0; | |
47 GLfloat z_angle = 0.0, z_speed = 0.0; | |
48 static GLfloat scale; | |
49 static GLfloat x_angle_wanted; | |
50 | |
51 GLWindow GLWin; | |
52 | |
53 /* attributes for a single buffered visual in RGBA format with at least | |
54 * 4 bits per color and a 16 bit depth buffer */ | |
55 static int attrListSgl[] = { GLX_RGBA, GLX_RED_SIZE, 4, | |
56 GLX_GREEN_SIZE, 4, | |
57 GLX_BLUE_SIZE, 4, | |
58 GLX_DEPTH_SIZE, 16, | |
59 None | |
60 }; | |
61 | |
62 /* attributes for a double buffered visual in RGBA format with at least | |
63 * 4 bits per color and a 16 bit depth buffer */ | |
64 static int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER, | |
65 GLX_RED_SIZE, 4, | |
66 GLX_GREEN_SIZE, 4, | |
67 GLX_BLUE_SIZE, 4, | |
68 GLX_DEPTH_SIZE, 16, | |
69 None | |
70 }; | |
71 | |
72 static gboolean going = FALSE; | |
73 static gboolean initialized = FALSE; | |
74 static gboolean grabbed_pointer = FALSE, firsttime = TRUE; | |
75 static Atom wmDelete; | |
76 static pthread_t draw_thread; | |
77 | |
78 datas_t datas; | |
79 | |
80 static int num_bands = NUM_BANDS; | |
81 static int beat = 0; | |
82 | |
83 unsigned int transition_frames = 0; | |
84 unsigned int max_transition_frames = 0; | |
85 | |
86 static void iris_init (void); | |
87 static void iris_cleanup (void); | |
88 static void iris_about (void); | |
89 static void iris_playback_start (void); | |
90 static void iris_playback_stop (void); | |
91 static void iris_render_freq (gint16 data[][256]); | |
92 | |
93 static float dps = 0; | |
94 | |
95 gint beat_before = -1; | |
96 | |
97 extern iris_config newconfig; | |
98 | |
99 VisPlugin iris_vp = { | |
100 NULL, | |
101 NULL, | |
102 0, | |
103 NULL, /* Description */ | |
104 0, | |
105 1, | |
106 iris_init, /* init */ | |
107 iris_cleanup, /* cleanup */ | |
108 iris_about, /* about */ | |
109 iris_configure, /* configure */ | |
110 NULL, /* disable_plugin */ | |
111 iris_playback_start, /* playback_start */ | |
112 iris_playback_stop, /* playback_stop */ | |
113 NULL, /* render_pcm */ | |
114 iris_render_freq /* render_freq */ | |
115 }; | |
116 | |
117 | |
118 void | |
119 about_close_clicked (GtkWidget * w, GtkWidget ** window) | |
120 { | |
121 gtk_widget_destroy (*window); | |
122 *window = NULL; | |
123 } | |
124 | |
125 | |
126 void | |
127 about_closed (GtkWidget * w, GdkEvent * e, GtkWidget ** window) | |
128 { | |
129 about_close_clicked (w, window); | |
130 } | |
131 | |
132 | |
133 static void | |
134 iris_about (void) | |
135 { | |
136 static GtkWidget *window_about = NULL; | |
137 GtkWidget *vbox, *button, *close, *label; | |
138 | |
139 if (window_about) | |
140 return; | |
141 | |
142 window_about = gtk_window_new (GTK_WINDOW_TOPLEVEL); | |
143 gtk_window_set_title (GTK_WINDOW (window_about), "About IRIS plugin"); | |
144 gtk_window_set_policy (GTK_WINDOW (window_about), FALSE, FALSE, FALSE); | |
145 gtk_window_set_position (GTK_WINDOW (window_about), GTK_WIN_POS_MOUSE); | |
146 vbox = gtk_vbox_new (FALSE, 4); | |
147 gtk_container_add (GTK_CONTAINER (window_about), vbox); | |
148 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); | |
149 gtk_widget_show (vbox); | |
150 | |
151 label = gtk_label_new ("\n\ | |
152 Iris XMMS Plugin.\n\ | |
153 Copyright (C) 2001-2003, Cédric Delfosse.\n\ | |
154 \n\ | |
155 Email: <cdelfosse@free.fr> \n\ | |
156 Homepage: <http://cdelfosse.free.fr/xmms-iris/>\n\ | |
157 Development: <http://savannah.gnu.org/projects/iris/>\n\ | |
158 \n\ | |
159 Authors:\n\ | |
160 Cédric Delfosse\n\ | |
161 Marinus Schraal\n\ | |
162 Ron Lockwood-Childs\n\ | |
163 Ported to Audacious by Arthur Taylor\n\ | |
164 \n\ | |
165 This program is free software; you can redistribute it and/or modify\n\ | |
166 it under the terms of the GNU General Public License as published by\n\ | |
167 the Free Software Foundation; either version 2 of the License, or\n\ | |
168 (at your option) any later version.\n\ | |
169 \n\ | |
170 This program is distributed in the hope that it will be useful,\n\ | |
171 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ | |
172 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\ | |
173 GNU General Public License for more details.\n\ | |
174 \n\ | |
175 You should have received a copy of the GNU General Public License\n\ | |
176 along with this program; if not, write to the Free Software\n\ | |
177 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307\n\ | |
178 USA"); | |
179 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 8); | |
180 gtk_widget_show (label); | |
181 | |
182 button = gtk_hbutton_box_new (); | |
183 gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 8); | |
184 gtk_widget_show (button); | |
185 | |
186 close = gtk_button_new_with_label ("Close"); | |
187 GTK_WIDGET_SET_FLAGS (close, GTK_CAN_DEFAULT); | |
188 gtk_window_set_default (GTK_WINDOW (window_about), close); | |
189 gtk_hbutton_box_set_layout_default (GTK_BUTTONBOX_END); | |
190 gtk_box_pack_end (GTK_BOX (button), close, FALSE, FALSE, 8); | |
191 gtk_widget_show (close); | |
192 | |
193 gtk_signal_connect (GTK_OBJECT (close), "clicked", | |
194 GTK_SIGNAL_FUNC (about_close_clicked), &window_about); | |
195 gtk_signal_connect (GTK_OBJECT (window_about), "delete-event", | |
196 GTK_SIGNAL_FUNC (about_closed), &window_about); | |
197 | |
198 gtk_widget_show (window_about); | |
199 } | |
200 | |
201 | |
202 /* Creates an empty cursor icon for when hovering over our window content */ | |
203 void | |
204 hide_cursor () | |
205 { | |
206 Cursor no_ptr; | |
207 Pixmap bm_no; | |
208 Colormap cmap; | |
209 XColor black, dummy; | |
210 | |
211 static unsigned char bm_no_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; | |
212 | |
213 cmap = DefaultColormap (GLWin.dpy, DefaultScreen (GLWin.dpy)); | |
214 XAllocNamedColor (GLWin.dpy, cmap, "black", &black, &dummy); | |
215 bm_no = XCreateBitmapFromData (GLWin.dpy, GLWin.window, bm_no_data, 8, 8); | |
216 no_ptr = | |
217 XCreatePixmapCursor (GLWin.dpy, bm_no, bm_no, &black, &black, 0, 0); | |
218 XDefineCursor (GLWin.dpy, GLWin.window, no_ptr); | |
219 } | |
220 | |
221 | |
222 /* this function creates our window and sets it up properly */ | |
223 static Window | |
224 create_window (char *title) | |
225 { | |
226 XVisualInfo *vi; | |
227 int i; | |
228 | |
229 /* get a connection */ | |
230 GLWin.dpy = XOpenDisplay (0); | |
231 if (GLWin.dpy == NULL) | |
232 g_log (NULL, G_LOG_LEVEL_CRITICAL, | |
233 __FILE__ ": XOpenDisplay returns NULL"); | |
234 GLWin.screen = DefaultScreen (GLWin.dpy); | |
235 | |
236 /* get an appropriate visual */ | |
237 vi = glXChooseVisual (GLWin.dpy, GLWin.screen, attrListDbl); | |
238 if (vi == NULL) | |
239 { | |
240 vi = glXChooseVisual (GLWin.dpy, GLWin.screen, attrListSgl); | |
241 GLWin.DoubleBuffered = 0; | |
242 } | |
243 else | |
244 GLWin.DoubleBuffered = 1; | |
245 | |
246 if (vi == NULL) | |
247 g_log (NULL, G_LOG_LEVEL_CRITICAL, | |
248 __FILE__ ": glXChooseVisual returns NULL"); | |
249 | |
250 glXQueryVersion (GLWin.dpy, &GLWin.glxMajorVersion, &GLWin.glxMinorVersion); | |
251 /* create a GLX context */ | |
252 GLWin.ctx = glXCreateContext (GLWin.dpy, vi, 0, GL_TRUE); | |
253 if (GLWin.ctx == NULL) | |
254 g_log (NULL, G_LOG_LEVEL_CRITICAL, | |
255 __FILE__ ": glXCreateContext can\'t create a rendering context"); | |
256 | |
257 /* create a color map */ | |
258 GLWin.attr.colormap = | |
259 XCreateColormap (GLWin.dpy, RootWindow (GLWin.dpy, vi->screen), | |
260 vi->visual, AllocNone); | |
261 GLWin.attr.border_pixel = 0; | |
262 | |
263 if ((config.fullscreen && firsttime) || GLWin.fs) | |
264 { | |
265 int bestMode = 0; | |
266 | |
267 GLWin.fs = True; | |
268 GLWin.fs_width = config.fs_width; | |
269 GLWin.fs_height = config.fs_height; | |
270 /* look for mode with requested resolution */ | |
271 for (i = 0; i < GLWin.modeNum; i++) | |
272 { | |
273 if ((GLWin.modes[i]->hdisplay == GLWin.fs_width) | |
274 && (GLWin.modes[i]->vdisplay == GLWin.fs_height)) | |
275 { | |
276 bestMode = i; | |
277 } | |
278 } | |
279 XF86VidModeSwitchToMode (GLWin.dpy, GLWin.screen, | |
280 GLWin.modes[bestMode]); | |
281 XF86VidModeSetViewPort (GLWin.dpy, GLWin.screen, 0, 0); | |
282 GLWin.fs_width = GLWin.modes[bestMode]->hdisplay; | |
283 GLWin.fs_height = GLWin.modes[bestMode]->vdisplay; | |
284 | |
285 /* create a fullscreen window */ | |
286 GLWin.attr.override_redirect = True; | |
287 GLWin.attr.event_mask = | |
288 ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask; | |
289 GLWin.window = | |
290 XCreateWindow (GLWin.dpy, | |
291 RootWindow (GLWin.dpy, vi->screen), 0, | |
292 0, GLWin.fs_width, GLWin.fs_height, 0, vi->depth, | |
293 InputOutput, vi->visual, | |
294 CWBorderPixel | CWColormap | | |
295 CWEventMask | CWOverrideRedirect, &GLWin.attr); | |
296 XWarpPointer (GLWin.dpy, None, GLWin.window, 0, 0, 0, 0, 0, 0); | |
297 XMapRaised (GLWin.dpy, GLWin.window); | |
298 XGrabKeyboard (GLWin.dpy, GLWin.window, True, GrabModeAsync, | |
299 GrabModeAsync, CurrentTime); | |
300 XGrabPointer (GLWin.dpy, GLWin.window, True, ButtonPressMask, | |
301 GrabModeAsync, GrabModeAsync, GLWin.window, | |
302 FALSE, CurrentTime); | |
303 } | |
304 else | |
305 { | |
306 XClassHint xclasshint={"xmms","visplugin"}; | |
307 GLWin.window_width = config.window_width; | |
308 GLWin.window_height = config.window_height; | |
309 /* needed if those aren't set yet - ideally need a way to get the default values for these */ | |
310 if (config.window_height == 0 || config.window_width == 0) | |
311 { | |
312 config.window_width = 640; | |
313 config.window_height = 480; | |
314 } | |
315 | |
316 /* create a window in window mode */ | |
317 GLWin.attr.event_mask = | |
318 ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask; | |
319 GLWin.window = | |
320 XCreateWindow (GLWin.dpy, | |
321 RootWindow (GLWin.dpy, vi->screen), 0, | |
322 0, GLWin.window_width, GLWin.window_height, 0, | |
323 vi->depth, InputOutput, vi->visual, | |
324 CWBorderPixel | CWColormap | CWEventMask, &GLWin.attr); | |
325 XmbSetWMProperties(GLWin.dpy, GLWin.window, "iris","iris", NULL, 0, NULL, NULL, &xclasshint); | |
326 | |
327 /* only set window title and handle wm_delete_events if in windowed mode */ | |
328 wmDelete = XInternAtom (GLWin.dpy, "WM_DELETE_WINDOW", True); | |
329 XSetWMProtocols (GLWin.dpy, GLWin.window, &wmDelete, 1); | |
330 XSetStandardProperties (GLWin.dpy, GLWin.window, title, | |
331 title, None, NULL, 0, NULL); | |
332 XMapRaised (GLWin.dpy, GLWin.window); | |
333 } | |
334 /* connect the glx-context to the window */ | |
335 if (!glXMakeCurrent (GLWin.dpy, GLWin.window, GLWin.ctx)) | |
336 g_log (NULL, G_LOG_LEVEL_CRITICAL, | |
337 __FILE__ ": glXMakeCurrent returns an error"); | |
338 if (glXIsDirect (GLWin.dpy, GLWin.ctx)) | |
339 GLWin.DRI = 1; | |
340 | |
341 hide_cursor (); | |
342 | |
343 XFree (vi); | |
344 firsttime = FALSE; | |
345 return GLWin.window; | |
346 } | |
347 | |
348 | |
349 VisPlugin * | |
350 get_vplugin_info (void) | |
351 { | |
352 iris_vp.description = g_strdup_printf ("iris 3D analyzer %s", VERSION); | |
353 return &iris_vp; | |
354 } | |
355 | |
356 | |
357 static int | |
358 detect_beat (gint32 loudness) | |
359 { | |
360 static gint32 aged; /* smoothed out oudness */ | |
361 static gint32 lowest; /* quietest point in current beat */ | |
362 static int elapsed; /* frames since last beat */ | |
363 static int isquiet; /* was previous frame quiet */ | |
364 int detected_beat; | |
365 | |
366 /* Incorporate the current loudness into history */ | |
367 aged = (aged * 7 + loudness) >> 3; | |
368 elapsed++; | |
369 | |
370 /* If silent, then clobber the beat */ | |
371 if (aged < 2000 || elapsed > BEAT_MAX) | |
372 { | |
373 elapsed = 0; | |
374 lowest = aged; | |
375 } | |
376 else if (aged < lowest) | |
377 lowest = aged; | |
378 | |
379 /* Beats are detected by looking for a sudden loudness after a lull. | |
380 * They are also limited to occur no more than once every 15 frames, | |
381 * so the beat flashes don't get too annoying. | |
382 */ | |
383 detected_beat = (loudness * 4 > aged * 3 && aged * 2 > lowest * 3 | |
384 && elapsed > 15); | |
385 if (detected_beat) | |
386 lowest = aged, elapsed = 0; | |
387 | |
388 /* Silence is computed from the aged loudness. The quietref value is | |
389 * set to TRUE only at the start of silence, not throughout the silent | |
390 * period. Also, there is some hysteresis so that silence followed | |
391 * by a slight noise and more silence won't count as two silent | |
392 * periods -- that sort of thing happens during many fade edits, so | |
393 * we have to account for it. | |
394 */ | |
395 if (aged < (isquiet ? 1500 : 500)) | |
396 /* Quiet now -- is this the start of quiet? */ | |
397 isquiet = TRUE; | |
398 else | |
399 isquiet = FALSE; | |
400 | |
401 /* return the result */ | |
402 return detected_beat; | |
403 } | |
404 | |
405 | |
406 /* limit_rotation_speed keep rotation speed to at least dps_config | |
407 by second */ | |
408 static void | |
409 limit_rotation_speed (gboolean init) | |
410 { | |
411 static struct timeval tv_past; | |
412 struct timezone tz; | |
413 struct timeval tv_now; | |
414 float dps_config = 15; | |
415 | |
416 if (!init) | |
417 { | |
418 long t; | |
419 | |
420 gettimeofday (&tv_now, &tz); | |
421 t = | |
422 (tv_now.tv_sec - tv_past.tv_sec) * 10000000 + (tv_now.tv_usec - | |
423 tv_past.tv_usec); | |
424 dps = y_speed * ((float) 1000000 / (float) t); | |
425 if (dps >= (float) dps_config) | |
426 y_speed -= 0.02; | |
427 else | |
428 y_speed += 0.02; | |
429 | |
430 memcpy (&tv_past, &tv_now, sizeof (struct timeval)); | |
431 } | |
432 else | |
433 gettimeofday (&tv_past, &tz); | |
434 } | |
435 | |
436 | |
437 /* limit_fps tries to keep the number of | |
438 frame per seconds to config.fps */ | |
439 static void | |
440 limit_fps (gboolean init) | |
441 { | |
442 static float fps = 0; | |
443 static struct timeval tv_past; | |
444 struct timezone tz; | |
445 struct timeval tv_now; | |
446 static int usec = 0; | |
447 | |
448 if (init) | |
449 gettimeofday (&tv_past, &tz); | |
450 else | |
451 { | |
452 long t; | |
453 | |
454 gettimeofday (&tv_now, &tz); | |
455 t = | |
456 (tv_now.tv_sec - tv_past.tv_sec) * 10000000 + (tv_now.tv_usec - | |
457 tv_past.tv_usec); | |
458 fps = (float) 1000000 / (float) t; | |
459 | |
460 if (fps >= (float) config.fps) | |
461 usec += 100; | |
462 else | |
463 { | |
464 if (usec > 0) | |
465 usec -= 100; | |
466 } | |
467 xmms_usleep (usec); | |
468 memcpy (&tv_past, &tv_now, sizeof (struct timeval)); | |
469 } | |
470 } | |
471 | |
472 static void | |
473 init_general_draw_mode (int num) | |
474 { | |
475 int transparency; | |
476 int wireframe; | |
477 | |
478 if (theme[num].config->global->transparency != -1) | |
479 transparency = theme[num].config->global->transparency; | |
480 else | |
481 transparency = (int) (2.0 * rand () / (RAND_MAX + 1.0)); | |
482 | |
483 if (theme[num].config->global->wireframe != -1) | |
484 wireframe = theme[num].config->global->wireframe; | |
485 else | |
486 wireframe = (int) (2.0 * rand () / (RAND_MAX + 1.0)); | |
487 | |
488 if (transparency) | |
489 { | |
490 if (!glIsEnabled (GL_BLEND)) | |
491 { | |
492 glBlendFunc (GL_SRC_ALPHA, GL_ONE); | |
493 glEnable (GL_BLEND); | |
494 } | |
495 glDisable (GL_DEPTH_TEST); | |
496 } | |
497 else | |
498 { | |
499 if (glIsEnabled (GL_BLEND)) | |
500 { | |
501 glDisable (GL_BLEND); | |
502 } | |
503 glEnable (GL_DEPTH_TEST); | |
504 } | |
505 | |
506 if (wireframe) | |
507 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); | |
508 else | |
509 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); | |
510 | |
511 if (glIsEnabled(GL_TEXTURE_2D)) | |
512 glDisable(GL_TEXTURE_2D); | |
513 } | |
514 | |
515 | |
516 static int | |
517 compute_theme () | |
518 { | |
519 gfloat f_rand, f; | |
520 gint i; | |
521 | |
522 for (i = 0, f_rand = 0.0; i < THEME_NUMBER; i++) | |
523 f_rand += theme[i].config->global->priority; | |
524 f_rand = f_rand * rand () / (RAND_MAX + 1.0); | |
525 | |
526 i = 0; | |
527 f = 0; | |
528 while (i < THEME_NUMBER) | |
529 { | |
530 gfloat f_theme; | |
531 f_theme = theme[i].config->global->priority; | |
532 if (f_theme) | |
533 { | |
534 f += f_theme; | |
535 if (f_rand < f) | |
536 break; | |
537 } | |
538 i++; | |
539 } | |
540 | |
541 if (!f) | |
542 return (int) ((gfloat) THEME_NUMBER * rand () / (RAND_MAX + 1.0)); | |
543 else | |
544 return i; | |
545 } | |
546 | |
547 | |
548 static int | |
549 choose_theme (gboolean init) | |
550 { | |
551 static long sec_btw_theme = 10 * 10000000; | |
552 static struct timeval tv_past; | |
553 struct timezone tz; | |
554 struct timeval tv_now; | |
555 static int th, th_tmp; | |
556 | |
557 if (!init) | |
558 { | |
559 long t; | |
560 | |
561 gettimeofday (&tv_now, &tz); | |
562 t = | |
563 (tv_now.tv_sec - tv_past.tv_sec) * 10000000 + (tv_now.tv_usec - | |
564 tv_past.tv_usec); | |
565 if ((t > sec_btw_theme) || (beat && config.change_theme_on_beat)) | |
566 { | |
567 /* we come here if: | |
568 - time for the theme is expired | |
569 - a beat is detected and the change_theme_on_beat option is on | |
570 */ | |
571 if (config.transition) | |
572 { | |
573 if (transition_frames == 0) | |
574 { | |
575 th_tmp = compute_theme (); | |
576 if (th != th_tmp) | |
577 { | |
578 transition_frames = | |
579 (unsigned int) config.fps * config.trans_duration / | |
580 10; | |
581 max_transition_frames = transition_frames; | |
582 memcpy (&tv_past, &tv_now, sizeof (struct timeval)); | |
583 init_theme_transition (transition_frames, | |
584 max_transition_frames); | |
585 } | |
586 } | |
587 } | |
588 else | |
589 { | |
590 th = compute_theme (); | |
591 init_general_draw_mode (th); | |
592 if (theme[th].init_draw_mode != NULL) | |
593 theme[th].init_draw_mode (); | |
594 memcpy (&tv_past, &tv_now, sizeof (struct timeval)); | |
595 x_angle_wanted = theme[th].get_x_angle (); | |
596 x_speed = copysign (0.08, x_angle_wanted - x_angle); | |
597 } | |
598 } | |
599 /* change theme after half of the set frames have passed */ | |
600 else if ((((int) max_transition_frames / 2) == transition_frames) | |
601 && config.transition && (transition_frames != 0)) | |
602 { | |
603 th = th_tmp; | |
604 init_general_draw_mode (th); | |
605 if (theme[th].init_draw_mode != NULL) | |
606 theme[th].init_draw_mode (); | |
607 x_angle_wanted = theme[th].get_x_angle (); | |
608 x_speed = copysign (0.08, x_angle_wanted - x_angle); | |
609 } | |
610 } | |
611 else | |
612 { | |
613 /* the first time choose_theme is called, | |
614 tv_past is initialized */ | |
615 gettimeofday (&tv_past, &tz); | |
616 th = compute_theme (); | |
617 init_general_draw_mode (th); | |
618 if (theme[th].init_draw_mode != NULL) | |
619 theme[th].init_draw_mode (); | |
620 } | |
621 | |
622 return (th); | |
623 } | |
624 | |
625 | |
626 static void | |
627 draw_iris (void) | |
628 { | |
629 int th; | |
630 | |
631 limit_fps (FALSE); | |
632 th = choose_theme (FALSE); | |
633 | |
634 if ((config.color_beat) && (beat_before > 0)) | |
635 glClearColor (config.color_flash_red, config.color_flash_green, | |
636 config.color_flash_blue, 1); | |
637 else | |
638 glClearColor (config.bgc_red, config.bgc_green, config.bgc_blue, 1); | |
639 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
640 | |
641 glPushMatrix (); | |
642 glTranslatef (0.0, -0.5, -7.0); | |
643 glRotatef (x_angle, 1.0, 0.0, 0.0); | |
644 glRotatef (y_angle, 0.0, 1.0, 0.0); | |
645 glRotatef (z_angle, 0.0, 0.0, 1.0); | |
646 | |
647 if (transition_frames > 0 && config.transition) | |
648 { | |
649 theme_transition (); | |
650 transition_frames--; | |
651 } | |
652 | |
653 theme[th].draw_one_frame (beat); | |
654 | |
655 glEnd (); | |
656 glPopMatrix (); | |
657 | |
658 glXSwapBuffers (GLWin.dpy, GLWin.window); | |
659 } | |
660 | |
661 | |
662 static gint | |
663 disable_func (gpointer data) | |
664 { | |
665 iris_vp.disable_plugin (&iris_vp); | |
666 return FALSE; | |
667 } | |
668 | |
669 | |
670 GLvoid | |
671 init_gl (GLvoid) | |
672 { | |
673 glMatrixMode (GL_PROJECTION); | |
674 glLoadIdentity (); | |
675 glFrustum (-1, 1, -1.5, 1, 1.5, 12); | |
676 glMatrixMode (GL_MODELVIEW); | |
677 glLoadIdentity (); | |
678 } | |
679 | |
680 | |
681 GLvoid | |
682 kill_gl_window (GLvoid) | |
683 { | |
684 if (GLWin.ctx) | |
685 { | |
686 glXMakeCurrent (GLWin.dpy, None, NULL); | |
687 glXDestroyContext (GLWin.dpy, GLWin.ctx); | |
688 GLWin.ctx = NULL; | |
689 } | |
690 /* switch back to original desktop resolution if we were in fs */ | |
691 if (GLWin.fs) | |
692 { | |
693 XF86VidModeSwitchToMode (GLWin.dpy, GLWin.screen, &GLWin.deskMode); | |
694 XF86VidModeSetViewPort (GLWin.dpy, GLWin.screen, 0, 0); | |
695 } | |
696 } | |
697 | |
698 | |
699 void * | |
700 draw_thread_func (void *arg) | |
701 { | |
702 Bool configured = FALSE; | |
703 | |
704 g_log (NULL, G_LOG_LEVEL_DEBUG, __FILE__ ": draw_thread_func: Starting."); | |
705 | |
706 if ((GLWin.window = create_window ("Iris")) == 0) | |
707 { | |
708 g_log (NULL, G_LOG_LEVEL_CRITICAL, | |
709 __FILE__ ": unable to create window"); | |
710 pthread_exit (NULL); | |
711 } | |
712 | |
713 init_gl (); | |
714 choose_theme (TRUE); | |
715 | |
716 | |
717 #ifdef HAVE_SCHED_SETSCHEDULER | |
718 if (xmms_check_realtime_priority ()) | |
719 { | |
720 struct sched_param sparam; | |
721 sparam.sched_priority = sched_get_priority_max (SCHED_OTHER); | |
722 pthread_setschedparam (pthread_self (), SCHED_OTHER, &sparam); | |
723 } | |
724 #endif | |
725 | |
726 while (going) /* plugin enabled */ | |
727 { | |
728 while (XPending (GLWin.dpy)) | |
729 { | |
730 XEvent event; | |
731 KeySym keysym; | |
732 char buf[16]; | |
733 | |
734 XNextEvent (GLWin.dpy, &event); | |
735 switch (event.type) | |
736 { | |
737 case Expose: | |
738 if (event.xexpose.count != 0) | |
739 break; | |
740 configured = TRUE; | |
741 break; | |
742 case ConfigureNotify: | |
743 glViewport (0, 0, event.xconfigure.width, | |
744 event.xconfigure.height); | |
745 configured = TRUE; | |
746 break; | |
747 case KeyPress: | |
748 | |
749 XLookupString (&event.xkey, buf, 16, &keysym, NULL); | |
750 switch (keysym) | |
751 { | |
752 case XK_Escape: | |
753 | |
754 /* Ugly hack to get the disable_plugin call in the main thread. */ | |
755 GDK_THREADS_ENTER (); | |
756 gtk_idle_add (disable_func, NULL); | |
757 GDK_THREADS_LEAVE (); | |
758 break; | |
759 case XK_z: | |
760 xmms_remote_playlist_prev (iris_vp.xmms_session); | |
761 break; | |
762 case XK_x: | |
763 xmms_remote_play (iris_vp.xmms_session); | |
764 break; | |
765 case XK_c: | |
766 xmms_remote_pause (iris_vp.xmms_session); | |
767 break; | |
768 case XK_v: | |
769 xmms_remote_stop (iris_vp.xmms_session); | |
770 break; | |
771 case XK_b: | |
772 xmms_remote_playlist_next (iris_vp.xmms_session); | |
773 break; | |
774 case XK_Left: | |
775 y_speed -= 0.1; | |
776 if (y_speed < -3.0) | |
777 y_speed = -3.0; | |
778 break; | |
779 case XK_Right: | |
780 y_speed += 0.1; | |
781 if (y_speed > 3.0) | |
782 y_speed = 3.0; | |
783 break; | |
784 case XK_Return: | |
785 x_speed = 0.0; | |
786 y_speed = 0.3; | |
787 z_speed = 0.0; | |
788 x_angle = 70.0; | |
789 y_angle = 45.0; | |
790 z_angle = 0.0; | |
791 break; | |
792 case XK_Tab: | |
793 iris_configure (); | |
794 break; | |
795 case XK_f: | |
796 //iris_save_window_attributes (); | |
797 kill_gl_window (); | |
798 XCloseDisplay (GLWin.dpy); | |
799 GLWin.fs = !GLWin.fs; | |
800 create_window ("Iris"); | |
801 init_gl (); | |
802 choose_theme (TRUE); | |
803 break; | |
804 } | |
805 | |
806 break; | |
807 case ClientMessage: | |
808 if ((Atom) event.xclient.data.l[0] == wmDelete) | |
809 { | |
810 GDK_THREADS_ENTER (); | |
811 gtk_idle_add (disable_func, NULL); | |
812 GDK_THREADS_LEAVE (); | |
813 } | |
814 break; | |
815 } | |
816 } | |
817 if (configured) | |
818 { | |
819 limit_rotation_speed (FALSE); | |
820 | |
821 if ((x_angle > x_angle_wanted) && (x_speed > 0)) | |
822 x_angle = x_angle_wanted; | |
823 else if ((x_angle < x_angle_wanted) && (x_speed < 0)) | |
824 x_angle = x_angle_wanted; | |
825 | |
826 x_angle += x_speed; | |
827 if (x_angle > 85.0) | |
828 x_angle = 85.0; | |
829 else if (x_angle < 0.0) | |
830 x_angle = 0.0; | |
831 | |
832 y_angle += y_speed; | |
833 if (y_angle >= 360.0) | |
834 y_angle -= 360.0; | |
835 | |
836 z_angle += z_speed; | |
837 if (z_angle >= 360.0) | |
838 z_angle -= 360.0; | |
839 | |
840 draw_iris (); | |
841 } | |
842 } | |
843 | |
844 g_log (NULL, G_LOG_LEVEL_DEBUG, __FILE__ ": draw_thread_func: Exiting."); | |
845 pthread_exit (NULL); | |
846 } | |
847 | |
848 | |
849 static void | |
850 start_display (void) | |
851 { | |
852 scale = 1.0 / log (256.0); | |
853 | |
854 x_speed = 0.0; | |
855 y_speed = 0.3; | |
856 z_speed = 0.0; | |
857 x_angle = 45.0; | |
858 y_angle = 45.0; | |
859 z_angle = 0.0; | |
860 | |
861 going = TRUE; | |
862 limit_fps (TRUE); | |
863 limit_rotation_speed (TRUE); | |
864 if (pthread_create (&draw_thread, NULL, draw_thread_func, NULL)) | |
865 g_log (NULL, G_LOG_LEVEL_CRITICAL, __FILE__ ": pthread_create: Can't create drawing thread."); | |
866 | |
867 } | |
868 | |
869 | |
870 static void | |
871 stop_display (void) | |
872 { | |
873 if (going) | |
874 { | |
875 going = FALSE; | |
876 pthread_join (draw_thread, NULL); | |
877 } | |
878 | |
879 kill_gl_window (); | |
880 | |
881 if (GLWin.window) | |
882 { | |
883 if (grabbed_pointer) | |
884 { | |
885 XUngrabPointer (GLWin.dpy, CurrentTime); | |
886 grabbed_pointer = FALSE; | |
887 } | |
888 | |
889 XDestroyWindow (GLWin.dpy, GLWin.window); | |
890 GLWin.window = 0; | |
891 } | |
892 XCloseDisplay (GLWin.dpy); | |
893 } | |
894 | |
895 | |
896 static void | |
897 iris_init (void) | |
898 { | |
899 int i; | |
900 | |
901 initialized = TRUE; | |
902 iris_first_init (); | |
903 datas.loudness = 0; | |
904 /* if the config window is open, we don't want to trash the config the user | |
905 * has done */ | |
906 if (!config_window) | |
907 iris_config_read (); | |
908 for (i = 0; i < THEME_NUMBER; i++) | |
909 if (theme[i].init != NULL) | |
910 theme[i].init (); | |
911 srand (666); | |
912 start_display (); | |
913 } | |
914 | |
915 | |
916 static void | |
917 iris_cleanup (void) | |
918 { | |
919 int i; | |
920 | |
921 if(initialized) | |
922 { | |
923 stop_display (); | |
924 for (i = 0; i < THEME_NUMBER; i++) | |
925 if (theme[i].cleanup != NULL) | |
926 theme[i].cleanup (); | |
927 } | |
928 } | |
929 | |
930 | |
931 static void | |
932 iris_playback_start (void) | |
933 { | |
934 } | |
935 | |
936 | |
937 static void | |
938 iris_playback_stop (void) | |
939 { | |
940 } | |
941 | |
942 | |
943 static void | |
944 iris_render_freq (gint16 data[2][256]) | |
945 { | |
946 GLfloat val; | |
947 static int angle = 0; | |
948 int i; | |
949 | |
950 for (i = 0; i < num_bands; i++) | |
951 { | |
952 int y, c; | |
953 gint xscale[] = | |
954 { 0, 1, 2, 3, 5, 7, 10, 14, 20, 28, 40, 54, 74, 101, 137, 156, | |
955 255 | |
956 }; | |
957 gint xscale8[] = { 0, 2, 5, 10, 20, 40, 74, 137, 255 }; | |
958 | |
959 if (num_bands == 16) | |
960 for (c = xscale[i], y = 0; c < xscale[i + 1]; c++) | |
961 { | |
962 if (data[0][c] > y) | |
963 y = data[0][c]; | |
964 } | |
965 else | |
966 for (c = xscale8[i], y = 0; c < xscale8[i + 1]; c++) | |
967 { | |
968 if (data[0][c] > y) | |
969 y = data[0][c]; | |
970 } | |
971 | |
972 datas.loudness += | |
973 (y / (xscale[i + 1] - xscale[i] + 1)) * (abs (i - NUM_BANDS / 2) + | |
974 NUM_BANDS / 2) * (4 + i); | |
975 | |
976 y >>= 7; | |
977 if (y > 0) | |
978 val = (log (y) * scale); | |
979 else | |
980 val = 0; | |
981 datas.data360[angle][i] = val; | |
982 datas.data1[i] = val; | |
983 } | |
984 | |
985 datas.loudness /= (NUM_BANDS * 4); | |
986 | |
987 beat = detect_beat (datas.loudness); | |
988 if (beat) | |
989 { | |
990 beat_before = config.flash_speed; | |
991 if (dps <= 90.0) | |
992 y_speed += 0.7; | |
993 | |
994 if (config.bgc_random) | |
995 { | |
996 config.bgc_red = 1.0 * rand () / (RAND_MAX + 1.0); | |
997 config.bgc_green = 1.0 * rand () / (RAND_MAX + 1.0); | |
998 config.bgc_blue = 1.0 * rand () / (RAND_MAX + 1.0); | |
999 } | |
1000 | |
1001 if (config.color12_random) | |
1002 { | |
1003 config.color1_red = 1.0 * rand () / (RAND_MAX + 1.0); | |
1004 config.color1_green = 1.0 * rand () / (RAND_MAX + 1.0); | |
1005 config.color1_blue = 1.0 * rand () / (RAND_MAX + 1.0); | |
1006 config.color2_red = 1.0 * rand () / (RAND_MAX + 1.0); | |
1007 config.color2_green = 1.0 * rand () / (RAND_MAX + 1.0); | |
1008 config.color2_blue = 1.0 * rand () / (RAND_MAX + 1.0); | |
1009 } | |
1010 | |
1011 if (config.color_random) | |
1012 { | |
1013 config.color_red = 1.0 * rand () / (RAND_MAX + 1.0); | |
1014 config.color_green = 1.0 * rand () / (RAND_MAX + 1.0); | |
1015 config.color_blue = 1.0 * rand () / (RAND_MAX + 1.0); | |
1016 } | |
1017 } | |
1018 | |
1019 if (beat_before > 0) | |
1020 beat_before--; | |
1021 | |
1022 angle++; | |
1023 if (angle == 360) | |
1024 angle = 0; | |
1025 } |