2
* paranormal: iterated pipeline-driven visualization plugin
3
* Copyright (c) 2006, 2007 William Pitcock <nenolod@dereferenced.org>
4
* Portions copyright (c) 2001 Jamie Gennis <jgennis@mindspring.com>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; under version 2 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
/* FIXME: issues with not uniniting variables between
21
enables? I wasn't too careful about that, but it
22
seems to work fine. If there are problems perhaps
37
#include <audacious/i18n.h>
40
#include <audacious/plugin.h>
42
#include <SDL/SDL_thread.h>
44
#include "paranormal.h"
45
#include "actuators.h"
47
#include "containers.h"
49
/* Error reporting dlg */
50
static GtkWidget *err_dialog;
52
/* Draw thread stuff */
53
/* FIXME: Do I need mutex for pn_done? */
54
static SDL_Thread *draw_thread = NULL;
55
static SDL_mutex *sound_data_mutex;
56
static SDL_mutex *config_mutex;
58
static gboolean pn_done = FALSE;
60
gboolean timeout_set = FALSE;
64
static gboolean new_pcm_data = FALSE;
65
static gboolean new_freq_data = FALSE;
66
static gint16 tmp_pcm_data[2][512];
67
static gint16 tmp_freq_data[2][256];
70
static void pn_xmms_init (void);
71
static void pn_xmms_cleanup (void);
72
static void pn_xmms_about (void);
73
static void pn_xmms_configure (void);
74
static void pn_xmms_render_pcm (gint16 data[2][512]);
75
static void pn_xmms_render_freq (gint16 data[2][256]);
77
static VisPlugin pn_vp =
79
.description = "Paranormal Visualization Studio",
80
.num_pcm_chs_wanted = 2,
81
.num_freq_chs_wanted = 2,
83
.cleanup = pn_xmms_cleanup,
84
.about = pn_xmms_about,
85
.configure = pn_xmms_configure,
86
.render_pcm = pn_xmms_render_pcm,
87
.render_freq = pn_xmms_render_freq
90
VisPlugin *pn_vplist[] = { &pn_vp, NULL };
92
DECLARE_PLUGIN(paranormal, NULL, NULL, NULL, NULL, NULL, NULL, pn_vplist,NULL);
97
struct pn_actuator *a, *b;
100
pn_rc = g_new0 (struct pn_rc, 1);
102
/* load a default preset */
103
pn_rc->actuator = create_actuator ("container_simple");
104
if (! pn_rc->actuator) goto ugh;
106
a = create_actuator ("general_clear");
108
container_add_actuator (pn_rc->actuator, a);
110
a = create_actuator ("wave_horizontal");
112
container_add_actuator (pn_rc->actuator, a);
118
destroy_actuator (pn_rc->actuator);
119
pn_error ("Error loading default preset");
123
draw_thread_fn (gpointer data)
126
guint last_time = 0, last_second = 0;
130
/* Used when pn_quit is called from this thread */
131
if (setjmp (quit_jmp) != 0)
136
SDL_mutexP (sound_data_mutex);
139
memcpy (pn_sound_data->freq_data, tmp_freq_data,
140
sizeof (gint16) * 2 * 256);
141
new_freq_data = FALSE;
145
memcpy (pn_sound_data->pcm_data, tmp_pcm_data,
146
sizeof (gint16) * 2 * 512);
147
new_freq_data = FALSE;
149
SDL_mutexV (sound_data_mutex);
150
SDL_mutexP (config_mutex);
152
SDL_mutexV (config_mutex);
154
/* Compute the FPS */
155
this_time = SDL_GetTicks ();
157
fps = fps * .95 + (1000. / (gfloat) (this_time - last_time)) * .05;
158
if (this_time > 2000 + last_second)
160
last_second = this_time;
161
g_print ("FPS: %f\n", fps);
163
last_time = this_time;
165
#ifdef _POSIX_PRIORITY_SCHEDULING
170
/* Just in case a pn_quit () was called in the loop */
171
/* SDL_mutexV (sound_data_mutex); */
178
/* Is there a better way to do this? this = messy
179
It appears that calling disable_plugin () in some
180
thread other than the one that called pn_xmms_init ()
181
causes a seg fault :( */
183
quit_timeout_fn (gpointer data)
187
pn_vp.disable_plugin (&pn_vp);
197
/* If it isn't already loaded, load the run control */
201
sound_data_mutex = SDL_CreateMutex ();
202
config_mutex = SDL_CreateMutex ();
203
if (! sound_data_mutex)
204
pn_fatal_error ("Unable to create a new mutex: %s",
208
draw_thread = SDL_CreateThread (draw_thread_fn, NULL);
210
pn_fatal_error ("Unable to create a new thread: %s",
213
/* Add a gtk timeout to test for quits */
214
quit_timeout = gtk_timeout_add (1000, quit_timeout_fn, NULL);
219
pn_xmms_cleanup (void)
223
gtk_timeout_remove (quit_timeout);
230
SDL_WaitThread (draw_thread, NULL);
234
if (sound_data_mutex)
236
SDL_DestroyMutex (sound_data_mutex);
237
sound_data_mutex = NULL;
242
SDL_DestroyMutex (config_mutex);
248
about_close_clicked(GtkWidget *w, GtkWidget **window)
250
gtk_widget_destroy(*window);
255
about_closed(GtkWidget *w, GdkEvent *e, GtkWidget **window)
257
about_close_clicked(w,window);
263
audacious_info_dialog("About Paranormal Visualization Studio",
265
"Paranormal Visualization Studio " VERSION "\n\n\
266
Copyright (C) 2006, William Pitcock <nenolod -at- nenolod.net>\n\
267
Portions Copyright (C) 2001, Jamie Gennis <jgennis -at- mindspring.com>\n\
269
This program is free software; you can redistribute it and/or modify\n\
270
it under the terms of the GNU General Public License as published by\n\
271
the Free Software Foundation; either version 2 of the License, or\n\
272
(at your option) any later version.\n\
274
This program is distributed in the hope that it will be useful,\n\
275
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
276
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
277
GNU General Public License for more details.\n\
279
You should have received a copy of the GNU General Public License\n\
280
along with this program; if not, write to the Free Software\n\
281
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301\n\
282
USA", _("Ok"), FALSE, NULL, NULL);
286
pn_xmms_configure (void)
288
/* We should already have a GDK_THREADS_ENTER
289
but we need to give it config_mutex */
291
SDL_mutexP (config_mutex);
299
SDL_mutexV (config_mutex);
303
pn_xmms_render_pcm (gint16 data[2][512])
305
SDL_mutexP (sound_data_mutex);
306
memcpy (tmp_pcm_data, data, sizeof (gint16) * 2 * 512);
308
SDL_mutexV (sound_data_mutex);
312
pn_xmms_render_freq (gint16 data[2][256])
314
SDL_mutexP (sound_data_mutex);
315
memcpy (tmp_freq_data, data, sizeof (gint16) * 2 * 256);
316
new_freq_data = TRUE;
317
SDL_mutexV (sound_data_mutex);
320
/* **************** paranormal.h stuff **************** */
323
pn_set_rc (struct pn_rc *new_rc)
326
SDL_mutexP (config_mutex);
332
destroy_actuator (pn_rc->actuator);
333
pn_rc->actuator = new_rc->actuator;
336
SDL_mutexV (config_mutex);
340
pn_fatal_error (const char *fmt, ...)
345
GtkWidget *close, *label;
347
/* Don't wanna try to lock GDK if we already have it */
348
if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread))
349
GDK_THREADS_ENTER ();
351
/* now report the error... */
353
errstr = g_strdup_vprintf (fmt, ap);
356
dialog=gtk_dialog_new();
357
gtk_window_set_title(GTK_WINDOW(dialog), "Error - Paranormal Visualization Studio - " VERSION);
358
gtk_container_border_width (GTK_CONTAINER (dialog), 8);
360
label=gtk_label_new(errstr);
361
fprintf (stderr, "%s\n", errstr);
364
close = gtk_button_new_with_label ("Close");
365
gtk_signal_connect_object (GTK_OBJECT (close), "clicked",
366
GTK_SIGNAL_FUNC (gtk_widget_destroy),
367
GTK_OBJECT (dialog));
369
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, FALSE,
371
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), close,
373
gtk_widget_show (label);
374
gtk_widget_show (close);
376
gtk_widget_show (dialog);
377
gtk_widget_grab_focus (dialog);
379
if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread))
380
GDK_THREADS_LEAVE ();
387
pn_error (const char *fmt, ...)
391
static GtkWidget *text;
392
static GtkTextBuffer *textbuf;
394
/* now report the error... */
396
errstr = g_strdup_vprintf (fmt, ap);
398
fprintf (stderr, "Paranormal-CRITICAL **: %s\n", errstr);
400
/* This is the easiest way of making sure we don't
401
get stuck trying to lock a mutex that this thread
402
already owns since this fn can be called from either
404
if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread))
405
GDK_THREADS_ENTER ();
411
err_dialog=gtk_dialog_new();
412
gtk_window_set_title (GTK_WINDOW (err_dialog), "Error - Paranormal Visualization Studio - " VERSION);
413
gtk_window_set_policy (GTK_WINDOW (err_dialog), FALSE, FALSE, FALSE);
414
gtk_widget_set_usize (err_dialog, 400, 200);
415
gtk_container_border_width (GTK_CONTAINER (err_dialog), 8);
417
textbuf = gtk_text_buffer_new(NULL);
418
text = gtk_text_view_new_with_buffer (textbuf);
420
close = gtk_button_new_with_label ("Close");
421
gtk_signal_connect_object (GTK_OBJECT (close), "clicked",
422
GTK_SIGNAL_FUNC (gtk_widget_hide),
423
GTK_OBJECT (err_dialog));
424
gtk_signal_connect_object (GTK_OBJECT (err_dialog), "delete-event",
425
GTK_SIGNAL_FUNC (gtk_widget_hide),
426
GTK_OBJECT (err_dialog));
428
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (err_dialog)->vbox), text, FALSE,
430
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (err_dialog)->action_area), close,
432
gtk_widget_show (text);
433
gtk_widget_show (close);
436
gtk_text_buffer_set_text(GTK_TEXT_BUFFER(textbuf), errstr, -1);
439
gtk_widget_show (err_dialog);
440
gtk_widget_grab_focus (err_dialog);
442
if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread))
443
GDK_THREADS_LEAVE ();
447
/* This is confusing...
448
Don't call this from anywhere but the draw thread or
449
the initialization xmms thread (ie NOT the xmms sound
454
if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread))
456
/* We're in the draw thread so be careful */
457
longjmp (quit_jmp, 1);
461
/* We're not in the draw thread, so don't sweat it...
462
addendum: looks like we have to bend over backwards (forwards?)
464
pn_vp.disable_plugin (&pn_vp);
466
gtk_main_iteration ();