Mercurial > audlegacy-plugins
view src/paranormal/plugin.c @ 2284:d19b53359b24
cleaned up the sndfile wav plugin, currently limiting it ONLY TO WAV
PLAYBACK. if somebody is more experienced with it and wants to restore
the other formats, go ahead (maybe change the name of the plugin too?).
author | mf0102 <0102@gmx.at> |
---|---|
date | Wed, 09 Jan 2008 15:41:22 +0100 |
parents | 573436f1a56e |
children | bd3a24b39058 |
line wrap: on
line source
/* * 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.h> #include <SDL_thread.h> #ifdef _POSIX_PRIORITY_SCHEDULING #include <sched.h> #endif #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 ("container_once"); if (! a) goto ugh; b = create_actuator ("cmap_bwgradient"); if (! b) goto ugh; b->options[2].val.cval.r = 64; b->options[2].val.cval.g = 128; container_add_actuator (a, b); container_add_actuator (pn_rc->actuator, a); a = create_actuator ("wave_horizontal"); if (! a) goto ugh; container_add_actuator (pn_rc->actuator, a); a = create_actuator ("xform_movement"); if (! a) goto ugh; a->options[0].val.sval = g_strdup("d = cos(d)^2;"); container_add_actuator (pn_rc->actuator, a); a = create_actuator ("general_fade"); if (! a) goto ugh; container_add_actuator (pn_rc->actuator, a); a = create_actuator ("general_blur"); 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 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 (); } }