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