Mercurial > audlegacy-plugins
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iris/iris.c Tue Oct 24 21:25:31 2006 -0700 @@ -0,0 +1,1025 @@ +/* Iris - visualization plugin for XMMS + * Copyright (C) 2000-2002 Cédric DELFOSSE (cdelfosse@free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <X11/Xlib.h> +#include <X11/keysym.h> +#include <gtk/gtk.h> +#include <math.h> + +#include <GL/gl.h> +#include <GL/glx.h> +#include <X11/extensions/xf86vmode.h> +#include <pthread.h> +#ifdef HAVE_SCHED_SETSCHEDULER +#include <sched.h> +#endif +#include <stdlib.h> +#include <sys/time.h> +#include <unistd.h> + +#include <audacious/plugin.h> +#include <audacious/util.h> +#include <audacious/beepctrl.h> +#include <audacious/configdb.h> +#include "iris.h" + +#define BEAT_MAX 100 + +GLfloat y_angle = 45.0, y_speed = 0.5; +GLfloat x_angle = 20.0, x_speed = 0.0; +GLfloat z_angle = 0.0, z_speed = 0.0; +static GLfloat scale; +static GLfloat x_angle_wanted; + +GLWindow GLWin; + +/* attributes for a single buffered visual in RGBA format with at least + * 4 bits per color and a 16 bit depth buffer */ +static int attrListSgl[] = { GLX_RGBA, GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, + GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, + None +}; + +/* attributes for a double buffered visual in RGBA format with at least + * 4 bits per color and a 16 bit depth buffer */ +static int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, + GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, + None +}; + +static gboolean going = FALSE; +static gboolean initialized = FALSE; +static gboolean grabbed_pointer = FALSE, firsttime = TRUE; +static Atom wmDelete; +static pthread_t draw_thread; + +datas_t datas; + +static int num_bands = NUM_BANDS; +static int beat = 0; + +unsigned int transition_frames = 0; +unsigned int max_transition_frames = 0; + +static void iris_init (void); +static void iris_cleanup (void); +static void iris_about (void); +static void iris_playback_start (void); +static void iris_playback_stop (void); +static void iris_render_freq (gint16 data[][256]); + +static float dps = 0; + +gint beat_before = -1; + +extern iris_config newconfig; + +VisPlugin iris_vp = { + NULL, + NULL, + 0, + NULL, /* Description */ + 0, + 1, + iris_init, /* init */ + iris_cleanup, /* cleanup */ + iris_about, /* about */ + iris_configure, /* configure */ + NULL, /* disable_plugin */ + iris_playback_start, /* playback_start */ + iris_playback_stop, /* playback_stop */ + NULL, /* render_pcm */ + iris_render_freq /* render_freq */ +}; + + +void +about_close_clicked (GtkWidget * w, GtkWidget ** window) +{ + gtk_widget_destroy (*window); + *window = NULL; +} + + +void +about_closed (GtkWidget * w, GdkEvent * e, GtkWidget ** window) +{ + about_close_clicked (w, window); +} + + +static void +iris_about (void) +{ + static GtkWidget *window_about = NULL; + GtkWidget *vbox, *button, *close, *label; + + if (window_about) + return; + + window_about = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window_about), "About IRIS plugin"); + gtk_window_set_policy (GTK_WINDOW (window_about), FALSE, FALSE, FALSE); + gtk_window_set_position (GTK_WINDOW (window_about), GTK_WIN_POS_MOUSE); + vbox = gtk_vbox_new (FALSE, 4); + gtk_container_add (GTK_CONTAINER (window_about), vbox); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); + gtk_widget_show (vbox); + + label = gtk_label_new ("\n\ +Iris XMMS Plugin.\n\ +Copyright (C) 2001-2003, Cédric Delfosse.\n\ +\n\ +Email: <cdelfosse@free.fr> \n\ +Homepage: <http://cdelfosse.free.fr/xmms-iris/>\n\ +Development: <http://savannah.gnu.org/projects/iris/>\n\ +\n\ +Authors:\n\ +Cédric Delfosse\n\ +Marinus Schraal\n\ +Ron Lockwood-Childs\n\ +Ported to Audacious by Arthur Taylor\n\ +\n\ +This program is free software; you can redistribute it and/or modify\n\ +it under the terms of the GNU General Public License as published by\n\ +the Free Software Foundation; either version 2 of the License, or\n\ +(at your option) any later version.\n\ +\n\ +This program is distributed in the hope that it will be useful,\n\ +but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\ +GNU General Public License for more details.\n\ +\n\ +You should have received a copy of the GNU General Public License\n\ +along with this program; if not, write to the Free Software\n\ +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307\n\ +USA"); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 8); + gtk_widget_show (label); + + button = gtk_hbutton_box_new (); + gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 8); + gtk_widget_show (button); + + close = gtk_button_new_with_label ("Close"); + GTK_WIDGET_SET_FLAGS (close, GTK_CAN_DEFAULT); + gtk_window_set_default (GTK_WINDOW (window_about), close); + gtk_hbutton_box_set_layout_default (GTK_BUTTONBOX_END); + gtk_box_pack_end (GTK_BOX (button), close, FALSE, FALSE, 8); + gtk_widget_show (close); + + gtk_signal_connect (GTK_OBJECT (close), "clicked", + GTK_SIGNAL_FUNC (about_close_clicked), &window_about); + gtk_signal_connect (GTK_OBJECT (window_about), "delete-event", + GTK_SIGNAL_FUNC (about_closed), &window_about); + + gtk_widget_show (window_about); +} + + +/* Creates an empty cursor icon for when hovering over our window content */ +void +hide_cursor () +{ + Cursor no_ptr; + Pixmap bm_no; + Colormap cmap; + XColor black, dummy; + + static unsigned char bm_no_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + cmap = DefaultColormap (GLWin.dpy, DefaultScreen (GLWin.dpy)); + XAllocNamedColor (GLWin.dpy, cmap, "black", &black, &dummy); + bm_no = XCreateBitmapFromData (GLWin.dpy, GLWin.window, bm_no_data, 8, 8); + no_ptr = + XCreatePixmapCursor (GLWin.dpy, bm_no, bm_no, &black, &black, 0, 0); + XDefineCursor (GLWin.dpy, GLWin.window, no_ptr); +} + + +/* this function creates our window and sets it up properly */ +static Window +create_window (char *title) +{ + XVisualInfo *vi; + int i; + + /* get a connection */ + GLWin.dpy = XOpenDisplay (0); + if (GLWin.dpy == NULL) + g_log (NULL, G_LOG_LEVEL_CRITICAL, + __FILE__ ": XOpenDisplay returns NULL"); + GLWin.screen = DefaultScreen (GLWin.dpy); + + /* get an appropriate visual */ + vi = glXChooseVisual (GLWin.dpy, GLWin.screen, attrListDbl); + if (vi == NULL) + { + vi = glXChooseVisual (GLWin.dpy, GLWin.screen, attrListSgl); + GLWin.DoubleBuffered = 0; + } + else + GLWin.DoubleBuffered = 1; + + if (vi == NULL) + g_log (NULL, G_LOG_LEVEL_CRITICAL, + __FILE__ ": glXChooseVisual returns NULL"); + + glXQueryVersion (GLWin.dpy, &GLWin.glxMajorVersion, &GLWin.glxMinorVersion); + /* create a GLX context */ + GLWin.ctx = glXCreateContext (GLWin.dpy, vi, 0, GL_TRUE); + if (GLWin.ctx == NULL) + g_log (NULL, G_LOG_LEVEL_CRITICAL, + __FILE__ ": glXCreateContext can\'t create a rendering context"); + + /* create a color map */ + GLWin.attr.colormap = + XCreateColormap (GLWin.dpy, RootWindow (GLWin.dpy, vi->screen), + vi->visual, AllocNone); + GLWin.attr.border_pixel = 0; + + if ((config.fullscreen && firsttime) || GLWin.fs) + { + int bestMode = 0; + + GLWin.fs = True; + GLWin.fs_width = config.fs_width; + GLWin.fs_height = config.fs_height; + /* look for mode with requested resolution */ + for (i = 0; i < GLWin.modeNum; i++) + { + if ((GLWin.modes[i]->hdisplay == GLWin.fs_width) + && (GLWin.modes[i]->vdisplay == GLWin.fs_height)) + { + bestMode = i; + } + } + XF86VidModeSwitchToMode (GLWin.dpy, GLWin.screen, + GLWin.modes[bestMode]); + XF86VidModeSetViewPort (GLWin.dpy, GLWin.screen, 0, 0); + GLWin.fs_width = GLWin.modes[bestMode]->hdisplay; + GLWin.fs_height = GLWin.modes[bestMode]->vdisplay; + + /* create a fullscreen window */ + GLWin.attr.override_redirect = True; + GLWin.attr.event_mask = + ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask; + GLWin.window = + XCreateWindow (GLWin.dpy, + RootWindow (GLWin.dpy, vi->screen), 0, + 0, GLWin.fs_width, GLWin.fs_height, 0, vi->depth, + InputOutput, vi->visual, + CWBorderPixel | CWColormap | + CWEventMask | CWOverrideRedirect, &GLWin.attr); + XWarpPointer (GLWin.dpy, None, GLWin.window, 0, 0, 0, 0, 0, 0); + XMapRaised (GLWin.dpy, GLWin.window); + XGrabKeyboard (GLWin.dpy, GLWin.window, True, GrabModeAsync, + GrabModeAsync, CurrentTime); + XGrabPointer (GLWin.dpy, GLWin.window, True, ButtonPressMask, + GrabModeAsync, GrabModeAsync, GLWin.window, + FALSE, CurrentTime); + } + else + { + XClassHint xclasshint={"xmms","visplugin"}; + GLWin.window_width = config.window_width; + GLWin.window_height = config.window_height; + /* needed if those aren't set yet - ideally need a way to get the default values for these */ + if (config.window_height == 0 || config.window_width == 0) + { + config.window_width = 640; + config.window_height = 480; + } + + /* create a window in window mode */ + GLWin.attr.event_mask = + ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask; + GLWin.window = + XCreateWindow (GLWin.dpy, + RootWindow (GLWin.dpy, vi->screen), 0, + 0, GLWin.window_width, GLWin.window_height, 0, + vi->depth, InputOutput, vi->visual, + CWBorderPixel | CWColormap | CWEventMask, &GLWin.attr); + XmbSetWMProperties(GLWin.dpy, GLWin.window, "iris","iris", NULL, 0, NULL, NULL, &xclasshint); + + /* only set window title and handle wm_delete_events if in windowed mode */ + wmDelete = XInternAtom (GLWin.dpy, "WM_DELETE_WINDOW", True); + XSetWMProtocols (GLWin.dpy, GLWin.window, &wmDelete, 1); + XSetStandardProperties (GLWin.dpy, GLWin.window, title, + title, None, NULL, 0, NULL); + XMapRaised (GLWin.dpy, GLWin.window); + } + /* connect the glx-context to the window */ + if (!glXMakeCurrent (GLWin.dpy, GLWin.window, GLWin.ctx)) + g_log (NULL, G_LOG_LEVEL_CRITICAL, + __FILE__ ": glXMakeCurrent returns an error"); + if (glXIsDirect (GLWin.dpy, GLWin.ctx)) + GLWin.DRI = 1; + + hide_cursor (); + + XFree (vi); + firsttime = FALSE; + return GLWin.window; +} + + +VisPlugin * +get_vplugin_info (void) +{ + iris_vp.description = g_strdup_printf ("iris 3D analyzer %s", VERSION); + return &iris_vp; +} + + +static int +detect_beat (gint32 loudness) +{ + static gint32 aged; /* smoothed out oudness */ + static gint32 lowest; /* quietest point in current beat */ + static int elapsed; /* frames since last beat */ + static int isquiet; /* was previous frame quiet */ + int detected_beat; + + /* Incorporate the current loudness into history */ + aged = (aged * 7 + loudness) >> 3; + elapsed++; + + /* If silent, then clobber the beat */ + if (aged < 2000 || elapsed > BEAT_MAX) + { + elapsed = 0; + lowest = aged; + } + else if (aged < lowest) + lowest = aged; + + /* Beats are detected by looking for a sudden loudness after a lull. + * They are also limited to occur no more than once every 15 frames, + * so the beat flashes don't get too annoying. + */ + detected_beat = (loudness * 4 > aged * 3 && aged * 2 > lowest * 3 + && elapsed > 15); + if (detected_beat) + lowest = aged, elapsed = 0; + + /* Silence is computed from the aged loudness. The quietref value is + * set to TRUE only at the start of silence, not throughout the silent + * period. Also, there is some hysteresis so that silence followed + * by a slight noise and more silence won't count as two silent + * periods -- that sort of thing happens during many fade edits, so + * we have to account for it. + */ + if (aged < (isquiet ? 1500 : 500)) + /* Quiet now -- is this the start of quiet? */ + isquiet = TRUE; + else + isquiet = FALSE; + + /* return the result */ + return detected_beat; +} + + +/* limit_rotation_speed keep rotation speed to at least dps_config + by second */ +static void +limit_rotation_speed (gboolean init) +{ + static struct timeval tv_past; + struct timezone tz; + struct timeval tv_now; + float dps_config = 15; + + if (!init) + { + long t; + + gettimeofday (&tv_now, &tz); + t = + (tv_now.tv_sec - tv_past.tv_sec) * 10000000 + (tv_now.tv_usec - + tv_past.tv_usec); + dps = y_speed * ((float) 1000000 / (float) t); + if (dps >= (float) dps_config) + y_speed -= 0.02; + else + y_speed += 0.02; + + memcpy (&tv_past, &tv_now, sizeof (struct timeval)); + } + else + gettimeofday (&tv_past, &tz); +} + + +/* limit_fps tries to keep the number of + frame per seconds to config.fps */ +static void +limit_fps (gboolean init) +{ + static float fps = 0; + static struct timeval tv_past; + struct timezone tz; + struct timeval tv_now; + static int usec = 0; + + if (init) + gettimeofday (&tv_past, &tz); + else + { + long t; + + gettimeofday (&tv_now, &tz); + t = + (tv_now.tv_sec - tv_past.tv_sec) * 10000000 + (tv_now.tv_usec - + tv_past.tv_usec); + fps = (float) 1000000 / (float) t; + + if (fps >= (float) config.fps) + usec += 100; + else + { + if (usec > 0) + usec -= 100; + } + xmms_usleep (usec); + memcpy (&tv_past, &tv_now, sizeof (struct timeval)); + } +} + +static void +init_general_draw_mode (int num) +{ + int transparency; + int wireframe; + + if (theme[num].config->global->transparency != -1) + transparency = theme[num].config->global->transparency; + else + transparency = (int) (2.0 * rand () / (RAND_MAX + 1.0)); + + if (theme[num].config->global->wireframe != -1) + wireframe = theme[num].config->global->wireframe; + else + wireframe = (int) (2.0 * rand () / (RAND_MAX + 1.0)); + + if (transparency) + { + if (!glIsEnabled (GL_BLEND)) + { + glBlendFunc (GL_SRC_ALPHA, GL_ONE); + glEnable (GL_BLEND); + } + glDisable (GL_DEPTH_TEST); + } + else + { + if (glIsEnabled (GL_BLEND)) + { + glDisable (GL_BLEND); + } + glEnable (GL_DEPTH_TEST); + } + + if (wireframe) + glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + else + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + + if (glIsEnabled(GL_TEXTURE_2D)) + glDisable(GL_TEXTURE_2D); +} + + +static int +compute_theme () +{ + gfloat f_rand, f; + gint i; + + for (i = 0, f_rand = 0.0; i < THEME_NUMBER; i++) + f_rand += theme[i].config->global->priority; + f_rand = f_rand * rand () / (RAND_MAX + 1.0); + + i = 0; + f = 0; + while (i < THEME_NUMBER) + { + gfloat f_theme; + f_theme = theme[i].config->global->priority; + if (f_theme) + { + f += f_theme; + if (f_rand < f) + break; + } + i++; + } + + if (!f) + return (int) ((gfloat) THEME_NUMBER * rand () / (RAND_MAX + 1.0)); + else + return i; +} + + +static int +choose_theme (gboolean init) +{ + static long sec_btw_theme = 10 * 10000000; + static struct timeval tv_past; + struct timezone tz; + struct timeval tv_now; + static int th, th_tmp; + + if (!init) + { + long t; + + gettimeofday (&tv_now, &tz); + t = + (tv_now.tv_sec - tv_past.tv_sec) * 10000000 + (tv_now.tv_usec - + tv_past.tv_usec); + if ((t > sec_btw_theme) || (beat && config.change_theme_on_beat)) + { + /* we come here if: + - time for the theme is expired + - a beat is detected and the change_theme_on_beat option is on + */ + if (config.transition) + { + if (transition_frames == 0) + { + th_tmp = compute_theme (); + if (th != th_tmp) + { + transition_frames = + (unsigned int) config.fps * config.trans_duration / + 10; + max_transition_frames = transition_frames; + memcpy (&tv_past, &tv_now, sizeof (struct timeval)); + init_theme_transition (transition_frames, + max_transition_frames); + } + } + } + else + { + th = compute_theme (); + init_general_draw_mode (th); + if (theme[th].init_draw_mode != NULL) + theme[th].init_draw_mode (); + memcpy (&tv_past, &tv_now, sizeof (struct timeval)); + x_angle_wanted = theme[th].get_x_angle (); + x_speed = copysign (0.08, x_angle_wanted - x_angle); + } + } + /* change theme after half of the set frames have passed */ + else if ((((int) max_transition_frames / 2) == transition_frames) + && config.transition && (transition_frames != 0)) + { + th = th_tmp; + init_general_draw_mode (th); + if (theme[th].init_draw_mode != NULL) + theme[th].init_draw_mode (); + x_angle_wanted = theme[th].get_x_angle (); + x_speed = copysign (0.08, x_angle_wanted - x_angle); + } + } + else + { + /* the first time choose_theme is called, + tv_past is initialized */ + gettimeofday (&tv_past, &tz); + th = compute_theme (); + init_general_draw_mode (th); + if (theme[th].init_draw_mode != NULL) + theme[th].init_draw_mode (); + } + + return (th); +} + + +static void +draw_iris (void) +{ + int th; + + limit_fps (FALSE); + th = choose_theme (FALSE); + + if ((config.color_beat) && (beat_before > 0)) + glClearColor (config.color_flash_red, config.color_flash_green, + config.color_flash_blue, 1); + else + glClearColor (config.bgc_red, config.bgc_green, config.bgc_blue, 1); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix (); + glTranslatef (0.0, -0.5, -7.0); + glRotatef (x_angle, 1.0, 0.0, 0.0); + glRotatef (y_angle, 0.0, 1.0, 0.0); + glRotatef (z_angle, 0.0, 0.0, 1.0); + + if (transition_frames > 0 && config.transition) + { + theme_transition (); + transition_frames--; + } + + theme[th].draw_one_frame (beat); + + glEnd (); + glPopMatrix (); + + glXSwapBuffers (GLWin.dpy, GLWin.window); +} + + +static gint +disable_func (gpointer data) +{ + iris_vp.disable_plugin (&iris_vp); + return FALSE; +} + + +GLvoid +init_gl (GLvoid) +{ + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glFrustum (-1, 1, -1.5, 1, 1.5, 12); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); +} + + +GLvoid +kill_gl_window (GLvoid) +{ + if (GLWin.ctx) + { + glXMakeCurrent (GLWin.dpy, None, NULL); + glXDestroyContext (GLWin.dpy, GLWin.ctx); + GLWin.ctx = NULL; + } + /* switch back to original desktop resolution if we were in fs */ + if (GLWin.fs) + { + XF86VidModeSwitchToMode (GLWin.dpy, GLWin.screen, &GLWin.deskMode); + XF86VidModeSetViewPort (GLWin.dpy, GLWin.screen, 0, 0); + } +} + + +void * +draw_thread_func (void *arg) +{ + Bool configured = FALSE; + + g_log (NULL, G_LOG_LEVEL_DEBUG, __FILE__ ": draw_thread_func: Starting."); + + if ((GLWin.window = create_window ("Iris")) == 0) + { + g_log (NULL, G_LOG_LEVEL_CRITICAL, + __FILE__ ": unable to create window"); + pthread_exit (NULL); + } + + init_gl (); + choose_theme (TRUE); + + +#ifdef HAVE_SCHED_SETSCHEDULER + if (xmms_check_realtime_priority ()) + { + struct sched_param sparam; + sparam.sched_priority = sched_get_priority_max (SCHED_OTHER); + pthread_setschedparam (pthread_self (), SCHED_OTHER, &sparam); + } +#endif + + while (going) /* plugin enabled */ + { + while (XPending (GLWin.dpy)) + { + XEvent event; + KeySym keysym; + char buf[16]; + + XNextEvent (GLWin.dpy, &event); + switch (event.type) + { + case Expose: + if (event.xexpose.count != 0) + break; + configured = TRUE; + break; + case ConfigureNotify: + glViewport (0, 0, event.xconfigure.width, + event.xconfigure.height); + configured = TRUE; + break; + case KeyPress: + + XLookupString (&event.xkey, buf, 16, &keysym, NULL); + switch (keysym) + { + case XK_Escape: + + /* Ugly hack to get the disable_plugin call in the main thread. */ + GDK_THREADS_ENTER (); + gtk_idle_add (disable_func, NULL); + GDK_THREADS_LEAVE (); + break; + case XK_z: + xmms_remote_playlist_prev (iris_vp.xmms_session); + break; + case XK_x: + xmms_remote_play (iris_vp.xmms_session); + break; + case XK_c: + xmms_remote_pause (iris_vp.xmms_session); + break; + case XK_v: + xmms_remote_stop (iris_vp.xmms_session); + break; + case XK_b: + xmms_remote_playlist_next (iris_vp.xmms_session); + break; + case XK_Left: + y_speed -= 0.1; + if (y_speed < -3.0) + y_speed = -3.0; + break; + case XK_Right: + y_speed += 0.1; + if (y_speed > 3.0) + y_speed = 3.0; + break; + case XK_Return: + x_speed = 0.0; + y_speed = 0.3; + z_speed = 0.0; + x_angle = 70.0; + y_angle = 45.0; + z_angle = 0.0; + break; + case XK_Tab: + iris_configure (); + break; + case XK_f: + //iris_save_window_attributes (); + kill_gl_window (); + XCloseDisplay (GLWin.dpy); + GLWin.fs = !GLWin.fs; + create_window ("Iris"); + init_gl (); + choose_theme (TRUE); + break; + } + + break; + case ClientMessage: + if ((Atom) event.xclient.data.l[0] == wmDelete) + { + GDK_THREADS_ENTER (); + gtk_idle_add (disable_func, NULL); + GDK_THREADS_LEAVE (); + } + break; + } + } + if (configured) + { + limit_rotation_speed (FALSE); + + if ((x_angle > x_angle_wanted) && (x_speed > 0)) + x_angle = x_angle_wanted; + else if ((x_angle < x_angle_wanted) && (x_speed < 0)) + x_angle = x_angle_wanted; + + x_angle += x_speed; + if (x_angle > 85.0) + x_angle = 85.0; + else if (x_angle < 0.0) + x_angle = 0.0; + + y_angle += y_speed; + if (y_angle >= 360.0) + y_angle -= 360.0; + + z_angle += z_speed; + if (z_angle >= 360.0) + z_angle -= 360.0; + + draw_iris (); + } + } + + g_log (NULL, G_LOG_LEVEL_DEBUG, __FILE__ ": draw_thread_func: Exiting."); + pthread_exit (NULL); +} + + +static void +start_display (void) +{ + scale = 1.0 / log (256.0); + + x_speed = 0.0; + y_speed = 0.3; + z_speed = 0.0; + x_angle = 45.0; + y_angle = 45.0; + z_angle = 0.0; + + going = TRUE; + limit_fps (TRUE); + limit_rotation_speed (TRUE); + if (pthread_create (&draw_thread, NULL, draw_thread_func, NULL)) + g_log (NULL, G_LOG_LEVEL_CRITICAL, __FILE__ ": pthread_create: Can't create drawing thread."); + +} + + +static void +stop_display (void) +{ + if (going) + { + going = FALSE; + pthread_join (draw_thread, NULL); + } + + kill_gl_window (); + + if (GLWin.window) + { + if (grabbed_pointer) + { + XUngrabPointer (GLWin.dpy, CurrentTime); + grabbed_pointer = FALSE; + } + + XDestroyWindow (GLWin.dpy, GLWin.window); + GLWin.window = 0; + } + XCloseDisplay (GLWin.dpy); +} + + +static void +iris_init (void) +{ + int i; + + initialized = TRUE; + iris_first_init (); + datas.loudness = 0; + /* if the config window is open, we don't want to trash the config the user + * has done */ + if (!config_window) + iris_config_read (); + for (i = 0; i < THEME_NUMBER; i++) + if (theme[i].init != NULL) + theme[i].init (); + srand (666); + start_display (); +} + + +static void +iris_cleanup (void) +{ + int i; + + if(initialized) + { + stop_display (); + for (i = 0; i < THEME_NUMBER; i++) + if (theme[i].cleanup != NULL) + theme[i].cleanup (); + } +} + + +static void +iris_playback_start (void) +{ +} + + +static void +iris_playback_stop (void) +{ +} + + +static void +iris_render_freq (gint16 data[2][256]) +{ + GLfloat val; + static int angle = 0; + int i; + + for (i = 0; i < num_bands; i++) + { + int y, c; + gint xscale[] = + { 0, 1, 2, 3, 5, 7, 10, 14, 20, 28, 40, 54, 74, 101, 137, 156, + 255 + }; + gint xscale8[] = { 0, 2, 5, 10, 20, 40, 74, 137, 255 }; + + if (num_bands == 16) + for (c = xscale[i], y = 0; c < xscale[i + 1]; c++) + { + if (data[0][c] > y) + y = data[0][c]; + } + else + for (c = xscale8[i], y = 0; c < xscale8[i + 1]; c++) + { + if (data[0][c] > y) + y = data[0][c]; + } + + datas.loudness += + (y / (xscale[i + 1] - xscale[i] + 1)) * (abs (i - NUM_BANDS / 2) + + NUM_BANDS / 2) * (4 + i); + + y >>= 7; + if (y > 0) + val = (log (y) * scale); + else + val = 0; + datas.data360[angle][i] = val; + datas.data1[i] = val; + } + + datas.loudness /= (NUM_BANDS * 4); + + beat = detect_beat (datas.loudness); + if (beat) + { + beat_before = config.flash_speed; + if (dps <= 90.0) + y_speed += 0.7; + + if (config.bgc_random) + { + config.bgc_red = 1.0 * rand () / (RAND_MAX + 1.0); + config.bgc_green = 1.0 * rand () / (RAND_MAX + 1.0); + config.bgc_blue = 1.0 * rand () / (RAND_MAX + 1.0); + } + + if (config.color12_random) + { + config.color1_red = 1.0 * rand () / (RAND_MAX + 1.0); + config.color1_green = 1.0 * rand () / (RAND_MAX + 1.0); + config.color1_blue = 1.0 * rand () / (RAND_MAX + 1.0); + config.color2_red = 1.0 * rand () / (RAND_MAX + 1.0); + config.color2_green = 1.0 * rand () / (RAND_MAX + 1.0); + config.color2_blue = 1.0 * rand () / (RAND_MAX + 1.0); + } + + if (config.color_random) + { + config.color_red = 1.0 * rand () / (RAND_MAX + 1.0); + config.color_green = 1.0 * rand () / (RAND_MAX + 1.0); + config.color_blue = 1.0 * rand () / (RAND_MAX + 1.0); + } + } + + if (beat_before > 0) + beat_before--; + + angle++; + if (angle == 360) + angle = 0; +}