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 } |