Mercurial > audlegacy-plugins
comparison src/paranormal/plugin.c @ 149:fd9c0a5871ac trunk
[svn] - new and IMPROVED paranormal visualization studio
| author | nenolod |
|---|---|
| date | Mon, 30 Oct 2006 23:02:33 -0800 |
| parents | |
| children | a089e72f4c25 |
comparison
equal
deleted
inserted
replaced
| 148:9d9fc9e1de48 | 149:fd9c0a5871ac |
|---|---|
| 1 /* FIXME: issues with not uniniting variables between | |
| 2 enables? I wasn't too careful about that, but it | |
| 3 seems to work fine. If there are problems perhaps | |
| 4 look for a bug there? | |
| 5 */ | |
| 6 | |
| 7 #ifdef HAVE_CONFIG_H | |
| 8 # include <config.h> | |
| 9 #endif | |
| 10 | |
| 11 #include <stdlib.h> | |
| 12 #include <stdio.h> | |
| 13 #include <stdarg.h> | |
| 14 #include <memory.h> | |
| 15 #include <math.h> | |
| 16 #include <setjmp.h> | |
| 17 | |
| 18 #include <glib.h> | |
| 19 #include <gtk/gtk.h> | |
| 20 #include <audacious/plugin.h> | |
| 21 #include <SDL/SDL.h> | |
| 22 #include <SDL/SDL_thread.h> | |
| 23 | |
| 24 #include "paranormal.h" | |
| 25 #include "actuators.h" | |
| 26 #include "presets.h" | |
| 27 #include "containers.h" | |
| 28 | |
| 29 /* Error reporting dlg */ | |
| 30 static GtkWidget *err_dialog; | |
| 31 | |
| 32 /* Draw thread stuff */ | |
| 33 /* FIXME: Do I need mutex for pn_done? */ | |
| 34 static SDL_Thread *draw_thread = NULL; | |
| 35 static SDL_mutex *sound_data_mutex; | |
| 36 static SDL_mutex *config_mutex; | |
| 37 | |
| 38 static gboolean pn_done = FALSE; | |
| 39 jmp_buf quit_jmp; | |
| 40 gboolean timeout_set = FALSE; | |
| 41 guint quit_timeout; | |
| 42 | |
| 43 /* Sound stuffs */ | |
| 44 static gboolean new_pcm_data = FALSE; | |
| 45 static gboolean new_freq_data = FALSE; | |
| 46 static gint16 tmp_pcm_data[2][512]; | |
| 47 static gint16 tmp_freq_data[2][256]; | |
| 48 | |
| 49 /* XMMS interface */ | |
| 50 static void pn_xmms_init (void); | |
| 51 static void pn_xmms_cleanup (void); | |
| 52 static void pn_xmms_about (void); | |
| 53 static void pn_xmms_configure (void); | |
| 54 static void pn_xmms_render_pcm (gint16 data[2][512]); | |
| 55 static void pn_xmms_render_freq (gint16 data[2][256]); | |
| 56 | |
| 57 static VisPlugin pn_vp = | |
| 58 { | |
| 59 NULL, | |
| 60 NULL, | |
| 61 0, | |
| 62 "Paranormal Visualization Studio " VERSION, | |
| 63 2, | |
| 64 2, | |
| 65 pn_xmms_init, | |
| 66 pn_xmms_cleanup, | |
| 67 pn_xmms_about, | |
| 68 pn_xmms_configure, | |
| 69 NULL, /* disable_plugin */ | |
| 70 NULL, /* pn_xmms_playback_start */ | |
| 71 NULL, /* pn_xmms_playback_stop */ | |
| 72 pn_xmms_render_pcm, | |
| 73 pn_xmms_render_freq | |
| 74 }; | |
| 75 | |
| 76 VisPlugin * | |
| 77 get_vplugin_info (void) | |
| 78 { | |
| 79 return &pn_vp; | |
| 80 } | |
| 81 | |
| 82 static void | |
| 83 load_pn_rc (void) | |
| 84 { | |
| 85 struct pn_actuator *a, *b; | |
| 86 | |
| 87 if (! pn_rc) | |
| 88 pn_rc = g_new0 (struct pn_rc, 1); | |
| 89 | |
| 90 /* load a default preset */ | |
| 91 pn_rc->actuator = create_actuator ("container_simple"); | |
| 92 if (! pn_rc->actuator) goto ugh; | |
| 93 a = create_actuator ("container_once"); | |
| 94 if (! a) goto ugh; | |
| 95 b = create_actuator ("cmap_bwgradient"); | |
| 96 if (! b) goto ugh; | |
| 97 b->options[2].val.cval.r = b->options[2].val.cval.g = 0; | |
| 98 container_add_actuator (a, b); | |
| 99 container_add_actuator (pn_rc->actuator, a); | |
| 100 a = create_actuator ("general_fade"); | |
| 101 if (! a) goto ugh; | |
| 102 container_add_actuator (pn_rc->actuator, a); | |
| 103 a = create_actuator ("xform_spin"); | |
| 104 if (! a) goto ugh; | |
| 105 a->options[0].val.fval = -4.0; | |
| 106 a->options[2].val.fval = 0.9; | |
| 107 container_add_actuator (pn_rc->actuator, a); | |
| 108 a = create_actuator ("wave_horizontal"); | |
| 109 if (! a) goto ugh; | |
| 110 container_add_actuator (pn_rc->actuator, a); | |
| 111 a = create_actuator ("general_blur"); | |
| 112 if (! a) goto ugh; | |
| 113 container_add_actuator (pn_rc->actuator, a); | |
| 114 | |
| 115 return; | |
| 116 | |
| 117 ugh: | |
| 118 if (pn_rc->actuator) | |
| 119 destroy_actuator (pn_rc->actuator); | |
| 120 pn_error ("Error loading default preset"); | |
| 121 } | |
| 122 | |
| 123 static int | |
| 124 draw_thread_fn (gpointer data) | |
| 125 { | |
| 126 pn_init (); | |
| 127 | |
| 128 /* Used when pn_quit is called from this thread */ | |
| 129 if (setjmp (quit_jmp) != 0) | |
| 130 pn_done = TRUE; | |
| 131 | |
| 132 while (! pn_done) | |
| 133 { | |
| 134 SDL_mutexP (sound_data_mutex); | |
| 135 if (new_freq_data) | |
| 136 { | |
| 137 memcpy (pn_sound_data->freq_data, tmp_freq_data, | |
| 138 sizeof (gint16) * 2 * 256); | |
| 139 new_freq_data = FALSE; | |
| 140 } | |
| 141 if (new_pcm_data) | |
| 142 { | |
| 143 memcpy (pn_sound_data->pcm_data, tmp_pcm_data, | |
| 144 sizeof (gint16) * 2 * 512); | |
| 145 new_freq_data = FALSE; | |
| 146 } | |
| 147 SDL_mutexV (sound_data_mutex); | |
| 148 SDL_mutexP (config_mutex); | |
| 149 pn_render (); | |
| 150 SDL_mutexV (config_mutex); | |
| 151 } | |
| 152 | |
| 153 /* Just in case a pn_quit () was called in the loop */ | |
| 154 /* SDL_mutexV (sound_data_mutex); */ | |
| 155 | |
| 156 pn_cleanup (); | |
| 157 | |
| 158 return 0; | |
| 159 } | |
| 160 | |
| 161 /* Is there a better way to do this? this = messy | |
| 162 It appears that calling disable_plugin () in some | |
| 163 thread other than the one that called pn_xmms_init () | |
| 164 causes a seg fault :( */ | |
| 165 static int | |
| 166 quit_timeout_fn (gpointer data) | |
| 167 { | |
| 168 if (pn_done) | |
| 169 { | |
| 170 pn_vp.disable_plugin (&pn_vp); | |
| 171 return FALSE; | |
| 172 } | |
| 173 | |
| 174 return TRUE; | |
| 175 } | |
| 176 | |
| 177 static void | |
| 178 pn_xmms_init (void) | |
| 179 { | |
| 180 /* If it isn't already loaded, load the run control */ | |
| 181 load_pn_rc (); | |
| 182 | |
| 183 sound_data_mutex = SDL_CreateMutex (); | |
| 184 config_mutex = SDL_CreateMutex (); | |
| 185 if (! sound_data_mutex) | |
| 186 pn_fatal_error ("Unable to create a new mutex: %s", | |
| 187 SDL_GetError ()); | |
| 188 | |
| 189 pn_done = FALSE; | |
| 190 draw_thread = SDL_CreateThread (draw_thread_fn, NULL); | |
| 191 if (! draw_thread) | |
| 192 pn_fatal_error ("Unable to create a new thread: %s", | |
| 193 SDL_GetError ()); | |
| 194 | |
| 195 /* Add a gtk timeout to test for quits */ | |
| 196 quit_timeout = gtk_timeout_add (1000, quit_timeout_fn, NULL); | |
| 197 timeout_set = TRUE; | |
| 198 } | |
| 199 | |
| 200 static void | |
| 201 pn_xmms_cleanup (void) | |
| 202 { | |
| 203 if (timeout_set) | |
| 204 { | |
| 205 gtk_timeout_remove (quit_timeout); | |
| 206 timeout_set = FALSE; | |
| 207 } | |
| 208 | |
| 209 if (draw_thread) | |
| 210 { | |
| 211 pn_done = TRUE; | |
| 212 SDL_WaitThread (draw_thread, NULL); | |
| 213 draw_thread = NULL; | |
| 214 } | |
| 215 | |
| 216 if (sound_data_mutex) | |
| 217 { | |
| 218 SDL_DestroyMutex (sound_data_mutex); | |
| 219 sound_data_mutex = NULL; | |
| 220 } | |
| 221 | |
| 222 if (config_mutex) | |
| 223 { | |
| 224 SDL_DestroyMutex (config_mutex); | |
| 225 config_mutex = NULL; | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 static void | |
| 230 about_close_clicked(GtkWidget *w, GtkWidget **window) | |
| 231 { | |
| 232 gtk_widget_destroy(*window); | |
| 233 *window=NULL; | |
| 234 } | |
| 235 | |
| 236 static void | |
| 237 about_closed(GtkWidget *w, GdkEvent *e, GtkWidget **window) | |
| 238 { | |
| 239 about_close_clicked(w,window); | |
| 240 } | |
| 241 | |
| 242 static void | |
| 243 pn_xmms_about (void) | |
| 244 { | |
| 245 static GtkWidget *window=NULL; | |
| 246 GtkWidget *vbox, *buttonbox, *close, *label; | |
| 247 | |
| 248 if(window) | |
| 249 return; | |
| 250 | |
| 251 window=gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
| 252 gtk_window_set_title(GTK_WINDOW(window), "Paranormal Visualization Studio " VERSION); | |
| 253 gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); | |
| 254 | |
| 255 vbox=gtk_vbox_new(FALSE, 4); | |
| 256 gtk_container_add(GTK_CONTAINER(window), vbox); | |
| 257 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8); | |
| 258 gtk_widget_show(vbox); | |
| 259 | |
| 260 label=gtk_label_new("\n\n Paranormal Visualization Studio " " VERSION "\n\n\ | |
| 261 Copyright (C) 2006, William Pitcock. <nenolod -at- nenolod.net>\n\ | |
| 262 Copyright (C) 2001, Jamie Gennis. (jgennis@mindspring.com)\n\ | |
| 263 \n\ | |
| 264 This program is free software; you can redistribute it and/or modify\n\ | |
| 265 it under the terms of the GNU General Public License as published by\n\ | |
| 266 the Free Software Foundation; either version 2 of the License, or\n\ | |
| 267 (at your option) any later version.\n\ | |
| 268 \n\ | |
| 269 This program is distributed in the hope that it will be useful,\n\ | |
| 270 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ | |
| 271 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\ | |
| 272 GNU General Public License for more details.\n\ | |
| 273 \n\ | |
| 274 You should have received a copy of the GNU General Public License\n\ | |
| 275 along with this program; if not, write to the Free Software\n\ | |
| 276 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307\n\ | |
| 277 USA"); | |
| 278 | |
| 279 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 8); | |
| 280 gtk_widget_show(label); | |
| 281 | |
| 282 buttonbox=gtk_hbutton_box_new(); | |
| 283 gtk_box_pack_end(GTK_BOX(vbox), buttonbox, FALSE, FALSE,8); | |
| 284 gtk_widget_show(buttonbox); | |
| 285 | |
| 286 close=gtk_button_new_with_label("Close"); | |
| 287 GTK_WIDGET_SET_FLAGS(close, GTK_CAN_DEFAULT); | |
| 288 gtk_window_set_default(GTK_WINDOW(window), close); | |
| 289 gtk_hbutton_box_set_layout_default(GTK_BUTTONBOX_END); | |
| 290 gtk_box_pack_end(GTK_BOX(buttonbox), close, FALSE, FALSE,8); | |
| 291 gtk_widget_show(close); | |
| 292 | |
| 293 gtk_signal_connect(GTK_OBJECT(close), "clicked", GTK_SIGNAL_FUNC(about_close_clicked), &window); | |
| 294 gtk_signal_connect(GTK_OBJECT(window), "delete-event", GTK_SIGNAL_FUNC(about_closed), &window); | |
| 295 | |
| 296 gtk_widget_show(window); | |
| 297 } | |
| 298 | |
| 299 static void | |
| 300 pn_xmms_configure (void) | |
| 301 { | |
| 302 load_pn_rc (); | |
| 303 | |
| 304 /* We should already have a GDK_THREADS_ENTER | |
| 305 but we need to give it config_mutex */ | |
| 306 if (config_mutex) | |
| 307 SDL_mutexP (config_mutex); | |
| 308 | |
| 309 pn_configure (); | |
| 310 | |
| 311 if (config_mutex) | |
| 312 SDL_mutexV (config_mutex); | |
| 313 } | |
| 314 | |
| 315 static void | |
| 316 pn_xmms_render_pcm (gint16 data[2][512]) | |
| 317 { | |
| 318 SDL_mutexP (sound_data_mutex); | |
| 319 memcpy (tmp_pcm_data, data, sizeof (gint16) * 2 * 512); | |
| 320 new_pcm_data = TRUE; | |
| 321 SDL_mutexV (sound_data_mutex); | |
| 322 } | |
| 323 | |
| 324 static void | |
| 325 pn_xmms_render_freq (gint16 data[2][256]) | |
| 326 { | |
| 327 SDL_mutexP (sound_data_mutex); | |
| 328 memcpy (tmp_freq_data, data, sizeof (gint16) * 2 * 256); | |
| 329 new_freq_data = TRUE; | |
| 330 SDL_mutexV (sound_data_mutex); | |
| 331 } | |
| 332 | |
| 333 /* **************** paranormal.h stuff **************** */ | |
| 334 | |
| 335 void | |
| 336 pn_set_rc (struct pn_rc *new_rc) | |
| 337 { | |
| 338 if (config_mutex) | |
| 339 SDL_mutexP (config_mutex); | |
| 340 | |
| 341 if (pn_rc->actuator) | |
| 342 destroy_actuator (pn_rc->actuator); | |
| 343 pn_rc->actuator = new_rc->actuator; | |
| 344 | |
| 345 if (config_mutex) | |
| 346 SDL_mutexV (config_mutex); | |
| 347 } | |
| 348 | |
| 349 void | |
| 350 pn_fatal_error (const char *fmt, ...) | |
| 351 { | |
| 352 char *errstr; | |
| 353 va_list ap; | |
| 354 GtkWidget *dialog; | |
| 355 GtkWidget *close, *label; | |
| 356 | |
| 357 /* Don't wanna try to lock GDK if we already have it */ | |
| 358 if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread)) | |
| 359 GDK_THREADS_ENTER (); | |
| 360 | |
| 361 /* now report the error... */ | |
| 362 va_start (ap, fmt); | |
| 363 errstr = g_strdup_vprintf (fmt, ap); | |
| 364 va_end (ap); | |
| 365 | |
| 366 dialog=gtk_dialog_new(); | |
| 367 gtk_window_set_title(GTK_WINDOW(dialog), "Error - " PACKAGE " " VERSION); | |
| 368 gtk_container_border_width (GTK_CONTAINER (dialog), 8); | |
| 369 | |
| 370 label=gtk_label_new(errstr); | |
| 371 fprintf (stderr, "%s\n", errstr); | |
| 372 g_free (errstr); | |
| 373 | |
| 374 close = gtk_button_new_with_label ("Close"); | |
| 375 gtk_signal_connect_object (GTK_OBJECT (close), "clicked", | |
| 376 GTK_SIGNAL_FUNC (gtk_widget_destroy), | |
| 377 GTK_OBJECT (dialog)); | |
| 378 | |
| 379 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, FALSE, | |
| 380 FALSE, 0); | |
| 381 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), close, | |
| 382 FALSE, FALSE, 0); | |
| 383 gtk_widget_show (label); | |
| 384 gtk_widget_show (close); | |
| 385 | |
| 386 gtk_widget_show (dialog); | |
| 387 gtk_widget_grab_focus (dialog); | |
| 388 | |
| 389 if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread)) | |
| 390 GDK_THREADS_LEAVE (); | |
| 391 | |
| 392 pn_quit (); | |
| 393 } | |
| 394 | |
| 395 | |
| 396 void | |
| 397 pn_error (const char *fmt, ...) | |
| 398 { | |
| 399 char *errstr; | |
| 400 va_list ap; | |
| 401 static GtkWidget *text; | |
| 402 static GtkTextBuffer *textbuf; | |
| 403 | |
| 404 /* now report the error... */ | |
| 405 va_start (ap, fmt); | |
| 406 errstr = g_strdup_vprintf (fmt, ap); | |
| 407 va_end (ap); | |
| 408 fprintf (stderr, PACKAGE ": %s\n", errstr); | |
| 409 | |
| 410 /* This is the easiest way of making sure we don't | |
| 411 get stuck trying to lock a mutex that this thread | |
| 412 already owns since this fn can be called from either | |
| 413 thread */ | |
| 414 if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread)) | |
| 415 GDK_THREADS_ENTER (); | |
| 416 | |
| 417 if (! err_dialog) | |
| 418 { | |
| 419 GtkWidget *close; | |
| 420 | |
| 421 err_dialog=gtk_dialog_new(); | |
| 422 gtk_window_set_title (GTK_WINDOW (err_dialog), "Error - " PACKAGE " " VERSION); | |
| 423 gtk_window_set_policy (GTK_WINDOW (err_dialog), FALSE, FALSE, FALSE); | |
| 424 gtk_widget_set_usize (err_dialog, 400, 200); | |
| 425 gtk_container_border_width (GTK_CONTAINER (err_dialog), 8); | |
| 426 | |
| 427 textbuf = gtk_text_buffer_new(NULL); | |
| 428 text = gtk_text_view_new_with_buffer (textbuf); | |
| 429 | |
| 430 close = gtk_button_new_with_label ("Close"); | |
| 431 gtk_signal_connect_object (GTK_OBJECT (close), "clicked", | |
| 432 GTK_SIGNAL_FUNC (gtk_widget_hide), | |
| 433 GTK_OBJECT (err_dialog)); | |
| 434 gtk_signal_connect_object (GTK_OBJECT (err_dialog), "delete-event", | |
| 435 GTK_SIGNAL_FUNC (gtk_widget_hide), | |
| 436 GTK_OBJECT (err_dialog)); | |
| 437 | |
| 438 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (err_dialog)->vbox), text, FALSE, | |
| 439 FALSE, 0); | |
| 440 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (err_dialog)->action_area), close, | |
| 441 FALSE, FALSE, 0); | |
| 442 gtk_widget_show (text); | |
| 443 gtk_widget_show (close); | |
| 444 } | |
| 445 | |
| 446 gtk_text_buffer_set_text(GTK_TEXT_BUFFER(textbuf), errstr, -1); | |
| 447 g_free (errstr); | |
| 448 | |
| 449 gtk_widget_show (err_dialog); | |
| 450 gtk_widget_grab_focus (err_dialog); | |
| 451 | |
| 452 if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread)) | |
| 453 GDK_THREADS_LEAVE (); | |
| 454 } | |
| 455 | |
| 456 | |
| 457 /* This is confusing... | |
| 458 Don't call this from anywhere but the draw thread or | |
| 459 the initialization xmms thread (ie NOT the xmms sound | |
| 460 data functions) */ | |
| 461 void | |
| 462 pn_quit (void) | |
| 463 { | |
| 464 if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread)) | |
| 465 { | |
| 466 /* We're in the draw thread so be careful */ | |
| 467 longjmp (quit_jmp, 1); | |
| 468 } | |
| 469 else | |
| 470 { | |
| 471 /* We're not in the draw thread, so don't sweat it... | |
| 472 addendum: looks like we have to bend over backwards (forwards?) | |
| 473 for xmms here too */ | |
| 474 pn_vp.disable_plugin (&pn_vp); | |
| 475 while (1) | |
| 476 gtk_main_iteration (); | |
| 477 } | |
| 478 } |
