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