Mercurial > audlegacy-plugins
diff src/paranormal-ng/plugin.c @ 2078:1fa3c8cd366a
paranormal-ng: a GL visualiser. lots to do, most stuff won't work, but hey, this will do cool stuff too soon
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Mon, 15 Oct 2007 06:20:13 -0500 |
parents | |
children | bd3a24b39058 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal-ng/plugin.c Mon Oct 15 06:20:13 2007 -0500 @@ -0,0 +1,469 @@ +/* + * paranormal: iterated pipeline-driven visualization plugin + * Copyright (c) 2006, 2007 William Pitcock <nenolod@dereferenced.org> + * Portions copyright (c) 2001 Jamie Gennis <jgennis@mindspring.com> + * + * 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; under version 2 of the License. + * + * 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 + */ + +/* FIXME: issues with not uniniting variables between + enables? I wasn't too careful about that, but it + seems to work fine. If there are problems perhaps + look for a bug there? +*/ + +#include <config.h> + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <memory.h> +#include <math.h> +#include <setjmp.h> +#include <unistd.h> + +#include <glib.h> +#include <audacious/i18n.h> + +#include <gtk/gtk.h> +#include <audacious/plugin.h> +#include <audacious/util.h> +#include <SDL/SDL.h> +#include <SDL/SDL_thread.h> + +#include "paranormal.h" +#include "actuators.h" +#include "presets.h" +#include "containers.h" + +/* Error reporting dlg */ +static GtkWidget *err_dialog; + +/* Draw thread stuff */ +/* FIXME: Do I need mutex for pn_done? */ +static SDL_Thread *draw_thread = NULL; +static SDL_mutex *sound_data_mutex; +static SDL_mutex *config_mutex; + +static gboolean pn_done = FALSE; +jmp_buf quit_jmp; +gboolean timeout_set = FALSE; +guint quit_timeout; + +/* Sound stuffs */ +static gboolean new_pcm_data = FALSE; +static gboolean new_freq_data = FALSE; +static gint16 tmp_pcm_data[2][512]; +static gint16 tmp_freq_data[2][256]; + +/* XMMS interface */ +static void pn_xmms_init (void); +static void pn_xmms_cleanup (void); +static void pn_xmms_about (void); +static void pn_xmms_configure (void); +static void pn_xmms_render_pcm (gint16 data[2][512]); +static void pn_xmms_render_freq (gint16 data[2][256]); + +static VisPlugin pn_vp = +{ + .description = "Paranormal Visualization Studio", + .num_pcm_chs_wanted = 2, + .num_freq_chs_wanted = 2, + .init = pn_xmms_init, + .cleanup = pn_xmms_cleanup, + .about = pn_xmms_about, + .configure = pn_xmms_configure, + .render_pcm = pn_xmms_render_pcm, + .render_freq = pn_xmms_render_freq +}; + +VisPlugin *pn_vplist[] = { &pn_vp, NULL }; + +DECLARE_PLUGIN(paranormal, NULL, NULL, NULL, NULL, NULL, NULL, pn_vplist,NULL); + +static void +load_pn_rc (void) +{ + struct pn_actuator *a, *b; + + if (! pn_rc) + pn_rc = g_new0 (struct pn_rc, 1); + + /* load a default preset */ + pn_rc->actuator = create_actuator ("container_simple"); + if (! pn_rc->actuator) goto ugh; + + a = create_actuator ("general_clear"); + if (! a) goto ugh; + container_add_actuator (pn_rc->actuator, a); + + a = create_actuator ("wave_horizontal"); + if (! a) goto ugh; + container_add_actuator (pn_rc->actuator, a); + + return; + + ugh: + if (pn_rc->actuator) + destroy_actuator (pn_rc->actuator); + pn_error ("Error loading default preset"); +} + +static int +draw_thread_fn (gpointer data) +{ + gfloat fps = 0.0; + guint last_time = 0, last_second = 0; + guint this_time; + pn_init (); + + /* Used when pn_quit is called from this thread */ + if (setjmp (quit_jmp) != 0) + pn_done = TRUE; + + while (! pn_done) + { + SDL_mutexP (sound_data_mutex); + if (new_freq_data) + { + memcpy (pn_sound_data->freq_data, tmp_freq_data, + sizeof (gint16) * 2 * 256); + new_freq_data = FALSE; + } + if (new_pcm_data) + { + memcpy (pn_sound_data->pcm_data, tmp_pcm_data, + sizeof (gint16) * 2 * 512); + new_freq_data = FALSE; + } + SDL_mutexV (sound_data_mutex); + SDL_mutexP (config_mutex); + pn_render (); + SDL_mutexV (config_mutex); + + /* Compute the FPS */ + this_time = SDL_GetTicks (); + + fps = fps * .95 + (1000. / (gfloat) (this_time - last_time)) * .05; + if (this_time > 2000 + last_second) + { + last_second = this_time; + g_print ("FPS: %f\n", fps); + } + last_time = this_time; + +#ifdef _POSIX_PRIORITY_SCHEDULING + sched_yield(); +#endif + } + + /* Just in case a pn_quit () was called in the loop */ +/* SDL_mutexV (sound_data_mutex); */ + + pn_cleanup (); + + return 0; +} + +/* Is there a better way to do this? this = messy + It appears that calling disable_plugin () in some + thread other than the one that called pn_xmms_init () + causes a seg fault :( */ +static int +quit_timeout_fn (gpointer data) +{ + if (pn_done) + { + pn_vp.disable_plugin (&pn_vp); + return FALSE; + } + + return TRUE; +} + +static void +pn_xmms_init (void) +{ + /* If it isn't already loaded, load the run control */ + if (! pn_rc) + load_pn_rc (); + + sound_data_mutex = SDL_CreateMutex (); + config_mutex = SDL_CreateMutex (); + if (! sound_data_mutex) + pn_fatal_error ("Unable to create a new mutex: %s", + SDL_GetError ()); + + pn_done = FALSE; + draw_thread = SDL_CreateThread (draw_thread_fn, NULL); + if (! draw_thread) + pn_fatal_error ("Unable to create a new thread: %s", + SDL_GetError ()); + + /* Add a gtk timeout to test for quits */ + quit_timeout = gtk_timeout_add (1000, quit_timeout_fn, NULL); + timeout_set = TRUE; +} + +static void +pn_xmms_cleanup (void) +{ + if (timeout_set) + { + gtk_timeout_remove (quit_timeout); + timeout_set = FALSE; + } + + if (draw_thread) + { + pn_done = TRUE; + SDL_WaitThread (draw_thread, NULL); + draw_thread = NULL; + } + + if (sound_data_mutex) + { + SDL_DestroyMutex (sound_data_mutex); + sound_data_mutex = NULL; + } + + if (config_mutex) + { + SDL_DestroyMutex (config_mutex); + config_mutex = NULL; + } +} + +static void +about_close_clicked(GtkWidget *w, GtkWidget **window) +{ + gtk_widget_destroy(*window); + *window=NULL; +} + +static void +about_closed(GtkWidget *w, GdkEvent *e, GtkWidget **window) +{ + about_close_clicked(w,window); +} + +static void +pn_xmms_about (void) +{ + audacious_info_dialog("About Paranormal Visualization Studio", + +"Paranormal Visualization Studio " VERSION "\n\n\ +Copyright (C) 2006, William Pitcock <nenolod -at- nenolod.net>\n\ +Portions Copyright (C) 2001, Jamie Gennis <jgennis -at- mindspring.com>\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", _("Ok"), FALSE, NULL, NULL); +} + +static void +pn_xmms_configure (void) +{ + /* We should already have a GDK_THREADS_ENTER + but we need to give it config_mutex */ + if (config_mutex) + SDL_mutexP (config_mutex); + + if (! pn_rc) + load_pn_rc (); + + pn_configure (); + + if (config_mutex) + SDL_mutexV (config_mutex); +} + +static void +pn_xmms_render_pcm (gint16 data[2][512]) +{ + SDL_mutexP (sound_data_mutex); + memcpy (tmp_pcm_data, data, sizeof (gint16) * 2 * 512); + new_pcm_data = TRUE; + SDL_mutexV (sound_data_mutex); +} + +static void +pn_xmms_render_freq (gint16 data[2][256]) +{ + SDL_mutexP (sound_data_mutex); + memcpy (tmp_freq_data, data, sizeof (gint16) * 2 * 256); + new_freq_data = TRUE; + SDL_mutexV (sound_data_mutex); +} + +/* **************** paranormal.h stuff **************** */ + +void +pn_set_rc (struct pn_rc *new_rc) +{ + if (config_mutex) + SDL_mutexP (config_mutex); + + if (! pn_rc) + load_pn_rc (); + + if (pn_rc->actuator) + destroy_actuator (pn_rc->actuator); + pn_rc->actuator = new_rc->actuator; + + if (config_mutex) + SDL_mutexV (config_mutex); +} + +void +pn_fatal_error (const char *fmt, ...) +{ + char *errstr; + va_list ap; + GtkWidget *dialog; + GtkWidget *close, *label; + + /* Don't wanna try to lock GDK if we already have it */ + if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread)) + GDK_THREADS_ENTER (); + + /* now report the error... */ + va_start (ap, fmt); + errstr = g_strdup_vprintf (fmt, ap); + va_end (ap); + + dialog=gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(dialog), "Error - Paranormal Visualization Studio - " VERSION); + gtk_container_border_width (GTK_CONTAINER (dialog), 8); + + label=gtk_label_new(errstr); + fprintf (stderr, "%s\n", errstr); + g_free (errstr); + + close = gtk_button_new_with_label ("Close"); + gtk_signal_connect_object (GTK_OBJECT (close), "clicked", + GTK_SIGNAL_FUNC (gtk_widget_destroy), + GTK_OBJECT (dialog)); + + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, FALSE, + FALSE, 0); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), close, + FALSE, FALSE, 0); + gtk_widget_show (label); + gtk_widget_show (close); + + gtk_widget_show (dialog); + gtk_widget_grab_focus (dialog); + + if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread)) + GDK_THREADS_LEAVE (); + + pn_quit (); +} + + +void +pn_error (const char *fmt, ...) +{ + char *errstr; + va_list ap; + static GtkWidget *text; + static GtkTextBuffer *textbuf; + + /* now report the error... */ + va_start (ap, fmt); + errstr = g_strdup_vprintf (fmt, ap); + va_end (ap); + fprintf (stderr, "Paranormal-CRITICAL **: %s\n", errstr); + + /* This is the easiest way of making sure we don't + get stuck trying to lock a mutex that this thread + already owns since this fn can be called from either + thread */ + if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread)) + GDK_THREADS_ENTER (); + + if (! err_dialog) + { + GtkWidget *close; + + err_dialog=gtk_dialog_new(); + gtk_window_set_title (GTK_WINDOW (err_dialog), "Error - Paranormal Visualization Studio - " VERSION); + gtk_window_set_policy (GTK_WINDOW (err_dialog), FALSE, FALSE, FALSE); + gtk_widget_set_usize (err_dialog, 400, 200); + gtk_container_border_width (GTK_CONTAINER (err_dialog), 8); + + textbuf = gtk_text_buffer_new(NULL); + text = gtk_text_view_new_with_buffer (textbuf); + + close = gtk_button_new_with_label ("Close"); + gtk_signal_connect_object (GTK_OBJECT (close), "clicked", + GTK_SIGNAL_FUNC (gtk_widget_hide), + GTK_OBJECT (err_dialog)); + gtk_signal_connect_object (GTK_OBJECT (err_dialog), "delete-event", + GTK_SIGNAL_FUNC (gtk_widget_hide), + GTK_OBJECT (err_dialog)); + + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (err_dialog)->vbox), text, FALSE, + FALSE, 0); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (err_dialog)->action_area), close, + FALSE, FALSE, 0); + gtk_widget_show (text); + gtk_widget_show (close); + } + + gtk_text_buffer_set_text(GTK_TEXT_BUFFER(textbuf), errstr, -1); + g_free (errstr); + + gtk_widget_show (err_dialog); + gtk_widget_grab_focus (err_dialog); + + if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread)) + GDK_THREADS_LEAVE (); +} + + +/* This is confusing... + Don't call this from anywhere but the draw thread or + the initialization xmms thread (ie NOT the xmms sound + data functions) */ +void +pn_quit (void) +{ + if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread)) + { + /* We're in the draw thread so be careful */ + longjmp (quit_jmp, 1); + } + else + { + /* We're not in the draw thread, so don't sweat it... + addendum: looks like we have to bend over backwards (forwards?) + for xmms here too */ + pn_vp.disable_plugin (&pn_vp); + while (1) + gtk_main_iteration (); + } +}