/* XMMS - Cross-platform multimedia player * Copyright (C) 1998-1999 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies * * Fishmatic Plugin * Copyright (C) 2001 Jamie Strachan * * 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 #include #include #include #include #include #ifdef HAVE_SCHED_SETSCHEDULER #include #endif #include #include #include #include "xmms/xmmsctrl.h" #include "xmms/configfile.h" #include "xmms/plugin.h" #include "fishmatic.h" #include "fishmatic_plugin.h" GLfloat heights[16]; pthread_mutex_t height_mutex; static Display *display; static Colormap colormap = 0; static GLXContext glxcontext = NULL; static Window root, window = 0; static Atom wm_delete_window_atom; static int x, y, w, h; static pthread_t draw_thread; static pthread_mutex_t still_going; gboolean paused; static void fishmatic_init(void); static void fishmatic_cleanup(void); static void fishmatic_playback_start(void); static void fishmatic_playback_stop(void); static void fishmatic_render_freq(gint16 data[2][256]); static void init_mutexes(void); static void destroy_mutexes(void); static void create_window(void); static void destroy_window(void); static void *draw_thread_loop(void *arg); fishmatic_config fishmatic_cfg; VisPlugin fishmatic_vplugin = { NULL, /* internal */ NULL, /* internal */ 0, /* internal */ NULL, /* description */ 0, /* # of pcm channels wanted */ 1, /* # of freq channels wanted */ fishmatic_init, /* called when plugin enabled */ fishmatic_cleanup, /* called when plugin disabled */ NULL, /* display about box */ fishmatic_configure, /* display configure box */ NULL, /* called to disable plugin */ fishmatic_playback_start, /* called when playback starts */ fishmatic_playback_stop, /* called when playback stops */ NULL, /* called with pcm data */ fishmatic_render_freq /* called with freq data */ }; VisPlugin* get_vplugin_info(void) { fishmatic_vplugin.description = g_strdup_printf("OpenGL Fishmatic Plugin %s", VERSION); return &fishmatic_vplugin; } void fishmatic_read_config(void) { ConfigFile *cfg; gchar *filename; fishmatic_cfg.stereo_repeat = 5; fishmatic_cfg.fish_speed = 1; fishmatic_cfg.fish_count = 4; filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL); cfg = xmms_cfg_open_file(filename); if (cfg) { xmms_cfg_read_int(cfg, "Fishmatic Plugin", "stereo_repeat", &fishmatic_cfg.stereo_repeat); xmms_cfg_read_float(cfg, "Fishmatic Plugin", "fish_speed", &fishmatic_cfg.fish_speed); xmms_cfg_read_int(cfg, "Fishmatic Plugin", "fish_count", &fishmatic_cfg.fish_count); xmms_cfg_free(cfg); } g_free(filename); } void fishmatic_write_config(void) { ConfigFile *cfg; gchar *filename; filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL); cfg = xmms_cfg_open_file(filename); if (!cfg) cfg = xmms_cfg_new(); xmms_cfg_write_int(cfg, "Fishmatic Plugin", "stereo_repeat", fishmatic_cfg.stereo_repeat); xmms_cfg_write_float(cfg,"Fishmatic Plugin", "fish_speed", fishmatic_cfg.fish_speed); xmms_cfg_write_int(cfg, "Fishmatic Plugin", "fish_count", fishmatic_cfg.fish_count); xmms_cfg_write_file(cfg, filename); xmms_cfg_free(cfg); g_free(filename); } void init_vidmodes(void) { // Hmm, nothing to do here. } void fishmatic_init(void) { fishmatic_read_config(); init_mutexes(); pthread_mutex_lock(&still_going); pthread_create(&draw_thread, NULL, draw_thread_loop, NULL); } void fishmatic_cleanup(void) { if (pthread_mutex_trylock(&still_going)) { pthread_mutex_unlock(&still_going); pthread_join(draw_thread, NULL); } destroy_mutexes(); } void fishmatic_playback_start(void) { } void fishmatic_playback_stop(void) { } void fishmatic_render_freq(gint16 data[2][256]) { GLfloat scale = 1.0 / log(256.0); gint data_ranges[] = {0, 1, 2, 3, 5, 7, 10, 14, 20, 28, 40, 54, 74, 101, 137, 187, 255}; gint section, i, y; pthread_mutex_lock(&height_mutex); for(section = 0; section < 16; section++) { y = 0; for(i = data_ranges[section]; i < data_ranges[section+1]; i++) { if(data[0][i] > y) y = data[0][i]; } y >>= 7; if(y > 0) heights[section] = (log(y) * scale); else heights[section] = 0; } pthread_mutex_unlock(&height_mutex); } static gint disable_fishmatic_vplugin(gpointer data) { fishmatic_vplugin.disable_plugin(&fishmatic_vplugin); return FALSE; } /* ***************************************************************** */ static void init_mutexes(void) { pthread_mutex_init(&height_mutex, NULL); pthread_mutex_init(&still_going, NULL); } static void destroy_mutexes(void) { pthread_mutex_destroy(&height_mutex); pthread_mutex_destroy(&still_going); } static void x_configure(XEvent *event) { glViewport(0, 0, event->xconfigure.width, event->xconfigure.height); } static void x_keypress(KeySym *key_sym) { switch (*key_sym) { case XK_Escape: case XK_q: case XK_Q: /* need to get this in the main thread */ GDK_THREADS_ENTER(); gtk_idle_add(disable_fishmatic_vplugin, NULL); GDK_THREADS_LEAVE(); break; case XK_p: case XK_P: if (paused) paused = FALSE; else paused = TRUE; break; case XK_z: case XK_Z: xmms_remote_playlist_prev(0); break; case XK_x: case XK_X: xmms_remote_play(0); break; case XK_c: case XK_C: xmms_remote_pause(0); break; case XK_v: case XK_V: xmms_remote_stop(0); break; case XK_b: case XK_B: xmms_remote_playlist_next(0); break; case XK_s: case XK_S: xmms_remote_toggle_shuffle(0); break; case XK_r: case XK_R: xmms_remote_toggle_repeat(0); break; } } static void x_message(Atom *atom) { if (*atom == wm_delete_window_atom) { /* need to get this in the main thread */ GDK_THREADS_ENTER(); gtk_idle_add(disable_fishmatic_vplugin, NULL); GDK_THREADS_LEAVE(); } } static void check_x_events(void) { while (XPending(display)) { XEvent event; KeySym key_sym; char buf[16]; XNextEvent(display, &event); switch(event.type) { case ConfigureNotify: x_configure(&event); break; case KeyPress: XLookupString(&event.xkey, buf, 16, &key_sym, NULL); x_keypress(&key_sym); break; case ClientMessage: x_message(&event.xclient.data.l[0]); break; } } } static void create_window(void) { int attr_list[] = { GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None }; int screen_num; XSetWindowAttributes attr; unsigned long mask; XVisualInfo *vis_info; Atom wm_protocols[1]; display = XOpenDisplay(NULL); if (!display) return; screen_num = DefaultScreen(display); root = RootWindow(display, screen_num); vis_info = glXChooseVisual(display, screen_num, attr_list); if (!vis_info) return; attr.background_pixel = 0; attr.border_pixel = 0; attr.colormap = colormap = XCreateColormap(display, root, vis_info->visual, AllocNone); attr.event_mask = StructureNotifyMask | KeyPressMask; mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; x = y = 0; w = 640; h = 480; window = XCreateWindow(display, root, x, y, w, h, 0, vis_info->depth, InputOutput, vis_info->visual, mask, &attr); XmbSetWMProperties(display, window, "OpenGL Fishmatic", "Fishmatic", NULL, 0, NULL, NULL, NULL); wm_delete_window_atom = wm_protocols[0] = XInternAtom(display, "WM_DELETE_WINDOW", False); XSetWMProtocols(display, window, wm_protocols, 1); glxcontext = glXCreateContext(display, vis_info, NULL, True); glXMakeCurrent(display, window, glxcontext); XMapWindow(display, window); x = y = 50; XMoveWindow(display, window, x, y); } static void destroy_window(void) { if (glxcontext) { glXMakeCurrent(display, 0, NULL); glXDestroyContext(display, glxcontext); glxcontext = NULL; } if (window) { XDestroyWindow(display, window); window = 0; } if (colormap) { XFreeColormap(display, colormap); colormap = 0; } if (display) { XCloseDisplay(display); display = NULL; } } static void set_priority(void) { #ifdef HAVE_SCHED_SETSCHEDULER struct sched_param sparam; sparam.sched_priority = sched_get_priority_max(SCHED_OTHER); pthread_setschedparam(pthread_self(), SCHED_OTHER, &sparam); #endif } static void *draw_thread_loop(void *arg) { App_Data_t *app_data; create_window(); if (!display || !window) pthread_exit(NULL); init_vidmodes(); app_data = init_gl(); if (!app_data) pthread_exit(NULL); draw_gl(app_data); glXSwapBuffers(display, window); set_priority(); paused = FALSE; while (pthread_mutex_trylock(&still_going)) { check_x_events(); if (!paused) { draw_gl(app_data); glXSwapBuffers(display, window); } #if 0 sched_yield(); usleep(100); #endif } kill_gl(app_data); destroy_window(); pthread_exit(NULL); }