116
|
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 }
|