/* scivi - visualization plugin for XMMS * Copyright (C) 2003 Vitaly V. Bursov * * 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 */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*#include */ #include #define START_TRAP 0 #if START_TRAP #include "signal.h" #endif #include "common.h" #include "scivi.h" #include "fpsctl.h" #include "utilf.h" #include "gtkgui/interface.h" #include "gtkgui/support.h" static Scivi scivi[1]; static pthread_t thread; static int plugin_locked = 0; static void plugin_init(void); static void plugin_cleanup(void); static void plugin_about(void); static void plugin_configure(void); static void plugin_render_pcm(gint16 pcm_data[2][512]); static void plugin_render_freq(gint16 freq_data[2][256]); #ifdef USE_ASM_K7 static void plugin_render_pcm_k7(gint16 pcm_data[2][512]); static void plugin_render_freq_k7(gint16 freq_data[2][256]); #endif static gint disable_func(gpointer data); static gint scivi_control_spawn(gpointer message); static gint message_func(gpointer data); static VisPlugin vis_plugin = { /*void *handle; Filled in by xmms */ NULL, /*char *filename; Filled in by xmms */ NULL, /*int xmms_session; The session ID for attaching to the control socket */ 0, /*char *description; The description that is shown in the preferences box */ PACKAGE_STRING, /*int num_pcm_chs_wanted; Numbers of PCM channels wanted in the call to render_pcm */ 2, /*int num_freq_chs_wanted; Numbers of freq channels wanted in the call to render_freq */ 2, /*void (*init)(void); Called when the plugin is enabled */ plugin_init, /*void (*cleanup)(void); Called when the plugin is disabled */ plugin_cleanup, /*void (*about)(void); Show the about box */ plugin_about, /*void (*configure)(void); Show the configure box */ plugin_configure, /*void (*disable_plugin)(struct _VisPlugin *); Call this with a pointer to your plugin to disable the plugin */ NULL, /*void (*playback_start)(void); Called when playback starts */ NULL, /*void (*playback_stop)(void); Called when playback stops */ NULL, /*void (*render_pcm)(gint16 pcm_data[2][512]); Render the PCM data, don't do anything time consuming in here */ /*void (*render_freq)(gint16 freq_data[2][256]); Render the freq data, don't do anything time consuming in here */ #ifdef USE_ASM_K7 plugin_render_pcm_k7, plugin_render_freq_k7 #else plugin_render_pcm, plugin_render_freq #endif }; VisPlugin *get_vplugin_info() { return &vis_plugin; } int scivi_get_xmms_session() { return vis_plugin.xmms_session; } static void init_mutexes(void) { if (!scivi->mutexes_initok){ pthread_mutex_init(&scivi->thread_mutex, NULL); pthread_mutex_init(&scivi->thread_config_mutex, NULL); scivi->mutexes_initok = 1; } } static void plugin_init(void) { #ifdef DATADIR /* function is at gtkgui/support.c. uses static list */ static int pdadded = 0; if (! pdadded) { add_pixmap_directory(DATADIR); pdadded = 1; } #endif #if START_TRAP raise(SIGTRAP); #endif if (plugin_locked){ message_func(g_strdup("Please close configuration dialog first.")); gtk_idle_add(disable_func, NULL); return; } memset(&scivi, 0, sizeof(scivi)); scivi->disable_func = disable_func; scivi->contols_spawn_func = scivi_control_spawn; dprintf("Starting OpenGL\n"); init_mutexes(); if (pthread_create(&thread, NULL, scivi_plugin_thread, (void*)scivi)){ eprintf("Couldn't create vis thread: %s\n", strerror(errno)); pthread_mutex_destroy(&scivi->thread_mutex); pthread_mutex_destroy(&scivi->thread_config_mutex); scivi->mutexes_initok = 0; return; } scivi->plugin_ok = 1; } static void plugin_cleanup(void) { void *retcode; if (!scivi->plugin_ok) return; dprintf("cleanup\n"); scivi->plugin_ok = 0; if (thread) { dprintf("Waiting thread to terminate...\n"); scivi->thread_quit_flag = 1; pthread_join(thread, &retcode); dprintf("OK, RetCode: %d\n", (int)retcode); } /* hmm... cleanup needed. */ if (XDPY){ XCloseDisplay(XDPY); XDPY = NULL; } pthread_mutex_destroy(&scivi->thread_mutex); pthread_mutex_destroy(&scivi->thread_config_mutex); scivi->mutexes_initok = 0; #ifdef DMALLOC dmalloc_shutdown(); #endif } void plugin_about(void) { static char *about_text = "Scivi " PACKAGE_VERSION "\n" "\n" "Copyright (c) 2003 Vitaly V. Bursov \n" "http://xmms-scivi.sourceforge.net/\n" "\n" "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.\n" "\n" "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.\n"; GtkWidget *wnd; GtkWidget *text; wnd = create_scivi_window_about(); text = lookup_widget(wnd, "about_text"); gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL, about_text, strlen(about_text)); gtk_text_set_word_wrap(GTK_TEXT(text), 1); gtk_widget_show(wnd); } void plugin_configure(void) { char cbuf[32]; GtkWidget *wnd; GtkWidget *w, *ww; GtkWidget *m; int i, cnt, sel, tmp; char *pc; int free_cfg = 0; init_mutexes(); SCIVICFG_LOCK(); if (!scivi->plugin_ok){ plugin_locked = 1; scivi_plugin_read_config(scivi); free_cfg = 1; } wnd = create_scivi_window_config(); w = lookup_widget(wnd, "cfg_optionmenu_fps_method"); ww = gtk_option_menu_get_menu(GTK_OPTION_MENU(w)); cnt = scivi_get_fps_ctl_count(); sel = 0; for (i=0;idescr); gtk_object_set_data(GTK_OBJECT(m), "data", (gpointer)i); gtk_widget_show(m); gtk_menu_append(GTK_MENU(ww), m); if (scivi->fps_limit_method == scivi_get_fps_ctl(i)->uuid) sel = i; } gtk_option_menu_set_history(GTK_OPTION_MENU(w), sel); w = lookup_widget(wnd, "cfg_spinbutton_maxfps"); gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), scivi->max_fps); w = lookup_widget(wnd, "cfg_togglebutton_vsync"); /* Nothing to do :( */ w = lookup_widget(wnd, "cfg_spinbutton_width"); gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), scivi->width); w = lookup_widget(wnd, "cfg_spinbutton_height"); gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), scivi->height); w = lookup_widget(wnd, "cfg_optionmenu_texwidth"); ww = gtk_option_menu_get_menu(GTK_OPTION_MENU(w)); sel = 0; for (i=0, tmp=32; tmp<=4096; i++, tmp = tmp<<1){ sprintf(cbuf, "%d", tmp); m = gtk_menu_item_new_with_label (cbuf); gtk_object_set_data(GTK_OBJECT(m), "data", (gpointer)tmp); gtk_widget_show(m); gtk_menu_append(GTK_MENU(ww), m); if (tmp == scivi->pbuf_want_w) sel = i; } gtk_option_menu_set_history(GTK_OPTION_MENU(w), sel); w = lookup_widget(wnd, "cfg_optionmenu_texheight"); ww = gtk_option_menu_get_menu(GTK_OPTION_MENU(w)); sel = 0; for (i=0, tmp=32; tmp<=4096; i++, tmp = tmp<<1){ sprintf(cbuf, "%d", tmp); m = gtk_menu_item_new_with_label (cbuf); gtk_object_set_data(GTK_OBJECT(m), "data", (gpointer)tmp); gtk_widget_show(m); gtk_menu_append(GTK_MENU(ww), m); if (tmp == scivi->pbuf_want_h) sel = i; } gtk_option_menu_set_history(GTK_OPTION_MENU(w), sel); w = lookup_widget(wnd, "path_list"); for (i=0;scivi->preset_dirs[i];i++){ m=gtk_label_new(scivi->preset_dirs[i]); ww=gtk_list_item_new(); gtk_container_add(GTK_CONTAINER(ww), m); gtk_widget_show(m); gtk_container_add(GTK_CONTAINER(w), ww); gtk_widget_show(ww); gtk_object_set_data(GTK_OBJECT(ww), "data", g_strdup(scivi->preset_dirs[i])); } if (!scivi->plugin_ok && free_cfg){ scivi_plugin_read_config_finit(scivi); } gtk_widget_show(wnd); SCIVICFG_UNLOCK(); } gint scivi_config_cancel_func(gpointer data) { plugin_locked = 0; return FALSE; } gint scivi_config_done_func(gpointer data) { GtkWidget *wnd; GtkWidget *w, *ww; GtkWidget *m; int i; int oldcfg[16]; char **oldl, **newl; GList *ps, *pl; wnd = GTK_WIDGET(data); SCIVICFG_LOCK(); w = lookup_widget(wnd, "cfg_optionmenu_fps_method"); ww = gtk_menu_get_active(GTK_MENU(GTK_OPTION_MENU(w)->menu)); i = (int)gtk_object_get_data(GTK_OBJECT(ww), "data"); oldcfg[0] = scivi->fps_limit_method; scivi->fps_limit_method = scivi_get_fps_ctl(i)->uuid; w = lookup_widget(wnd, "cfg_spinbutton_maxfps"); oldcfg[1] = scivi->max_fps; scivi->max_fps = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w)); /*w = lookup_widget(wnd, "cfg_togglebutton_vsync");*/ w = lookup_widget(wnd, "cfg_spinbutton_width"); oldcfg[2] = scivi->width; scivi->width = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w)); w = lookup_widget(wnd, "cfg_spinbutton_height"); oldcfg[3] = scivi->height; scivi->height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w)); w = lookup_widget(wnd, "cfg_optionmenu_texwidth"); ww = gtk_menu_get_active(GTK_MENU(GTK_OPTION_MENU(w)->menu)); i = (int)gtk_object_get_data(GTK_OBJECT(ww), "data"); oldcfg[4] = scivi->pbuf_want_w; scivi->pbuf_want_w = i; w = lookup_widget(wnd, "cfg_optionmenu_texheight"); ww = gtk_menu_get_active(GTK_MENU(GTK_OPTION_MENU(w)->menu)); i = (int)gtk_object_get_data(GTK_OBJECT(ww), "data"); oldcfg[5] = scivi->pbuf_want_h; scivi->pbuf_want_h = i; oldl = scivi->preset_dirs; w = lookup_widget(wnd, "path_list"); ps = gtk_container_children(GTK_CONTAINER(w)); pl = ps; newl = malloc(sizeof(char*)*(g_list_length(ps)+1)); i = 0; while (pl){ newl[i++] = gtk_object_get_data(GTK_OBJECT(pl->data), "data"); pl = g_list_next(pl); } newl[i] = NULL; scivi->preset_dirs = newl; scivi_plugin_save_config(scivi); scivi->max_fps = oldcfg[1]; scivi->width = oldcfg[2]; scivi->height = oldcfg[3]; scivi->pbuf_want_w = oldcfg[4]; scivi->pbuf_want_h = oldcfg[5]; scivi->preset_dirs = oldl; for (i=0;newl[i];i++) g_free(newl[i]); free(newl); SCIVICFG_UNLOCK(); gtk_widget_destroy(wnd); if (!plugin_locked){ plugin_cleanup(); plugin_init(); } else plugin_locked = 0; return FALSE; } /* KEEP IN SYNC WITH callbacks.c */ #define CTL_MESSAGE_RETTOWIN 1 #define CTL_MESSAGE_PRST_REL 10 #define CTL_MESSAGE_PRST_PREV 11 #define CTL_MESSAGE_PRST_NEXT 12 gint scivi_control_message(gpointer message) { switch ((int)message){ case CTL_MESSAGE_RETTOWIN: scivi->plugin_ctrl_message = SCIVI_CTRL_RETURN_TO_WINDOW_MODE; break; case CTL_MESSAGE_PRST_REL: scivi->plugin_ctrl_message = SCIVI_CTRL_PRESETS_RELOAD; break; case CTL_MESSAGE_PRST_PREV: scivi->plugin_ctrl_message = SCIVI_CTRL_PRESETS_PREVIOUS; break; case CTL_MESSAGE_PRST_NEXT: scivi->plugin_ctrl_message = SCIVI_CTRL_PRESETS_NEXT; break; } return FALSE; } static gint scivi_control_spawn(gpointer message) { GtkWidget *wnd; wnd = create_scivi_ctl(); gtk_widget_show(wnd); return FALSE; } static gint message_func(gpointer data) { GtkWidget *wnd; GtkWidget *w; wnd = create_scivi_message(); w = lookup_widget(wnd, "label"); gtk_label_set_text(GTK_LABEL(w), data); g_free(data); gtk_widget_show(wnd); return FALSE; } void scivi_display_message(const char *msg, ...) { char buf[4096]; /* FIXME */ char *buff; va_list args; va_start(args, msg); vsprintf(buf, msg, args); va_end(args); buff = g_strdup(buf); GDK_THREADS_ENTER(); gtk_idle_add(message_func, buff); GDK_THREADS_LEAVE(); } void scivi_complier_error(const char *msg, ...) { char buf[4096]; /* FIXME */ char *buff; va_list args; va_start(args, msg); vsprintf(buf, msg, args); va_end(args); eprintf("Error: %s", buf); } void scivi_complier_warning(const char *msg, ...) { char buf[4096]; /* FIXME */ char *buff; va_list args; va_start(args, msg); vsprintf(buf, msg, args); va_end(args); eprintf("Warning: %s", buf); } static void plugin_render_pcm(gint16 pcm_data[2][512]) { int i,j; if (scivi->plugin_ok){ SCIVI_LOCK(); /* FIXME What should we do if data is not yet processed? */ if (!scivi->plugin_pcm_fdata_count){ for (i=0;i<2;i++) for (j=0;j<512;j++){ scivi->plugin_pcm_fdata[scivi->plugin_pcm_fdata_buffer][i][j] += (float)pcm_data[i][j]/65536.0f; } scivi->plugin_pcm_fdata_count++; } SCIVI_UNLOCK(); } } #ifdef USE_ASM_K7 static void plugin_render_pcm_k7(gint16 pcm_data[2][512]) { const static v2sf one_div_64k = { 1.0f/65536.0f, 1.0f/65536.0f }; int i,j; gint16 *s; float *d; if (scivi->plugin_ok){ SCIVI_LOCK(); for (i=0;i<2;i++){ s = pcm_data[i]; d = scivi->plugin_pcm_fdata[scivi->plugin_pcm_fdata_buffer][i]; for (j=0;j<64;j++){ v4hi in1,in2; v4hi tmp1,tmp2; v2sf dst1,dst2,dst3,dst4; __builtin_prefetch(((void*)&((v2sf*)d)[j<<2])+256,1,1); in1 = ((v4hi*)s)[(j<<1)]; in2 = ((v4hi*)s)[(j<<1)+1]; tmp1 = __builtin_ia32_punpckhwd(in1, in1); in1 = __builtin_ia32_punpcklwd(in1, in1); dst1 = __builtin_ia32_pi2fw((v2si)in1); dst2 = __builtin_ia32_pi2fw((v2si)tmp1); tmp2 = __builtin_ia32_punpckhwd(in2, in2); in2 = __builtin_ia32_punpcklwd(in2, in2); dst3 = __builtin_ia32_pi2fw((v2si)in2); dst4 = __builtin_ia32_pi2fw((v2si)tmp2); dst1 = __builtin_ia32_pfmul(dst1, one_div_64k); dst2 = __builtin_ia32_pfmul(dst2, one_div_64k); dst3 = __builtin_ia32_pfmul(dst3, one_div_64k); dst4 = __builtin_ia32_pfmul(dst4, one_div_64k); ((v2sf*)d)[(j<<2) + 0] = dst1; ((v2sf*)d)[(j<<2) + 1] = dst2; ((v2sf*)d)[(j<<2) + 3] = dst4; ((v2sf*)d)[(j<<2) + 2] = dst3; } } // __builtin_ia32_femms (); __asm__ __volatile__("femms"); scivi->plugin_pcm_fdata_count = 1; SCIVI_UNLOCK(); } } #endif static void plugin_render_freq(gint16 freq_data[2][256]) { int i,j; if (scivi->plugin_ok){ SCIVI_LOCK(); /* FIXME What should we do if data is not yet processed? */ for (i=0;i<2;i++) for (j=0;j<256;j++){ scivi->plugin_freq_fdata[scivi->plugin_freq_fdata_buffer][i][j] += (float)freq_data[i][j]/32768.0f; } scivi->plugin_freq_fdata_count++; SCIVI_UNLOCK(); } } #ifdef USE_ASM_K7 static void plugin_render_freq_k7(gint16 freq_data[2][256]) { const static v2sf one_div_32k = { 1.0f/32768.0f, 1.0f/32768.0f }; int i,j; gint16 *s; float *d; if (scivi->plugin_ok){ SCIVI_LOCK(); for (i=0;i<2;i++){ s = freq_data[i]; d = scivi->plugin_freq_fdata[scivi->plugin_freq_fdata_buffer][i]; for (j=0;j<32;j++){ v4hi in1,in2; v4hi tmp1,tmp2; v2sf dst1,dst2,dst3,dst4; __builtin_prefetch(((void*)&((v2sf*)d)[j<<2])+192,1,1); in1 = ((v4hi*)s)[(j<<1)]; in2 = ((v4hi*)s)[(j<<1)+1]; tmp1 = __builtin_ia32_punpckhwd(in1, in1); in1 = __builtin_ia32_punpcklwd(in1, in1); dst1 = __builtin_ia32_pi2fw((v2si)in1); dst2 = __builtin_ia32_pi2fw((v2si)tmp1); tmp2 = __builtin_ia32_punpckhwd(in2, in2); in2 = __builtin_ia32_punpcklwd(in2, in2); dst3 = __builtin_ia32_pi2fw((v2si)in2); dst4 = __builtin_ia32_pi2fw((v2si)tmp2); dst1 = __builtin_ia32_pfmul(dst1, one_div_32k); dst2 = __builtin_ia32_pfmul(dst2, one_div_32k); ((v2sf*)d)[(j<<2) + 0] = __builtin_ia32_pfadd(((v2sf*)d)[(j<<2) + 0], dst1); ((v2sf*)d)[(j<<2) + 1] = __builtin_ia32_pfadd(((v2sf*)d)[(j<<2) + 1], dst2); dst3 = __builtin_ia32_pfmul(dst3, one_div_32k); dst4 = __builtin_ia32_pfmul(dst4, one_div_32k); ((v2sf*)d)[(j<<2) + 2] = __builtin_ia32_pfadd(((v2sf*)d)[(j<<2) + 2], dst3); ((v2sf*)d)[(j<<2) + 3] = __builtin_ia32_pfadd(((v2sf*)d)[(j<<2) + 3], dst4); } } // __builtin_ia32_femms (); __asm__ __volatile__("femms"); scivi->plugin_freq_fdata_count++; SCIVI_UNLOCK(); } } #endif static gint disable_func(gpointer data) { vis_plugin.disable_plugin(&vis_plugin); return FALSE; }