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 ();
+    }
+}