1507
|
1 /* Paranormal - A highly customizable audio visualization library
|
|
2 * Copyright (C) 2001 Jamie Gennis <jgennis@mindspring.com>
|
|
3 *
|
|
4 * This library is free software; you can redistribute it and/or
|
|
5 * modify it under the terms of the GNU Library General Public
|
|
6 * License as published by the Free Software Foundation; either
|
|
7 * version 2 of the License, or (at your option) any later version.
|
|
8 *
|
|
9 * This library is distributed in the hope that it will be useful,
|
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
12 * Library General Public License for more details.
|
|
13 *
|
|
14 * You should have received a copy of the GNU Library General Public
|
|
15 * License along with this library; if not, write to the Free
|
|
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
17 */
|
|
18
|
|
19 #include <math.h>
|
|
20 #include <glib.h>
|
|
21 #include "pnscope.h"
|
|
22 #include "pnlistoption.h"
|
|
23 #include "pnstringoption.h"
|
|
24
|
|
25 enum
|
|
26 {
|
|
27 PN_SCOPE_DRAW_METHOD_DOTS = 0,
|
|
28 PN_SCOPE_DRAW_METHOD_LINES
|
|
29 };
|
|
30
|
|
31 static void pn_scope_class_init (PnScopeClass *class);
|
|
32 static void pn_scope_init (PnScope *scope,
|
|
33 PnScopeClass *class);
|
|
34 /* PnObject signals */
|
|
35 static void pn_scope_destroy (PnObject *object);
|
|
36
|
|
37 /* PnActuator methods */
|
|
38 static void pn_scope_prepare (PnScope *scope,
|
|
39 PnImage *image);
|
|
40 static void pn_scope_execute (PnScope *scope,
|
|
41 PnImage *image,
|
|
42 PnAudioData *audio_data);
|
|
43
|
|
44 static PnActuatorClass *parent_class = NULL;
|
|
45
|
|
46 GType
|
|
47 pn_scope_get_type (void)
|
|
48 {
|
|
49 static GType scope_type = 0;
|
|
50
|
|
51 if (! scope_type)
|
|
52 {
|
|
53 static const GTypeInfo scope_info =
|
|
54 {
|
|
55 sizeof (PnScopeClass),
|
|
56 NULL, /* base_init */
|
|
57 NULL, /* base_finalize */
|
|
58 (GClassInitFunc) pn_scope_class_init,
|
|
59 NULL, /* class_finalize */
|
|
60 NULL, /* class_data */
|
|
61 sizeof (PnScope),
|
|
62 0, /* n_preallocs */
|
|
63 (GInstanceInitFunc) pn_scope_init
|
|
64 };
|
|
65
|
|
66 /* FIXME: should this be dynamic? */
|
|
67 scope_type = g_type_register_static (PN_TYPE_ACTUATOR,
|
|
68 "PnScope",
|
|
69 &scope_info,
|
|
70 0);
|
|
71 }
|
|
72 return scope_type;
|
|
73 }
|
|
74
|
|
75 static void
|
|
76 pn_scope_class_init (PnScopeClass *class)
|
|
77 {
|
|
78 PnObjectClass *object_class;
|
|
79 PnUserObjectClass *user_object_class;
|
|
80 PnActuatorClass *actuator_class;
|
|
81
|
|
82 parent_class = g_type_class_peek_parent (class);
|
|
83
|
|
84 object_class = (PnObjectClass *) class;
|
|
85 user_object_class = (PnUserObjectClass *) class;
|
|
86 actuator_class = (PnActuatorClass *) class;
|
|
87
|
|
88 /* PnObject signals */
|
|
89 object_class->destroy = pn_scope_destroy;
|
|
90
|
|
91 /* PnActuator methods */
|
|
92 actuator_class->prepare = (PnActuatorPrepFunc) pn_scope_prepare;
|
|
93 actuator_class->execute = (PnActuatorExecFunc) pn_scope_execute;
|
|
94 }
|
|
95
|
|
96 static void
|
|
97 pn_scope_init (PnScope *scope, PnScopeClass *class)
|
|
98 {
|
|
99 PnListOption *draw_method_opt;
|
|
100 PnStringOption *init_script_opt;
|
|
101 PnStringOption *frame_script_opt;
|
|
102 PnStringOption *sample_script_opt;
|
|
103
|
|
104 /* Set up the name and description */
|
|
105 pn_user_object_set_name (PN_USER_OBJECT (scope), "Render.Scope");
|
|
106 pn_user_object_set_description (PN_USER_OBJECT (scope),
|
|
107 "A test scope actuator");
|
|
108
|
|
109 /* Set up the options */
|
|
110 draw_method_opt = pn_list_option_new ("draw_method", "The way the points will be drawn");
|
|
111 init_script_opt = pn_string_option_new ("init_script", "The initialization script");
|
|
112 frame_script_opt = pn_string_option_new ("frame_script", "The per-frame script");
|
|
113 sample_script_opt = pn_string_option_new ("sample_script", "The per-sample script");
|
|
114
|
|
115 pn_list_option_add_item (draw_method_opt, "Dots");
|
|
116 pn_list_option_add_item (draw_method_opt, "Lines");
|
|
117
|
|
118 pn_actuator_add_option (PN_ACTUATOR (scope), PN_OPTION (draw_method_opt));
|
|
119 pn_actuator_add_option (PN_ACTUATOR (scope), PN_OPTION (init_script_opt));
|
|
120 pn_actuator_add_option (PN_ACTUATOR (scope), PN_OPTION (frame_script_opt));
|
|
121 pn_actuator_add_option (PN_ACTUATOR (scope), PN_OPTION (sample_script_opt));
|
|
122
|
|
123 /* Create the script objects and symbol table */
|
|
124 scope->init_script = pn_script_new ();
|
|
125 pn_object_ref (PN_OBJECT (scope->init_script));
|
|
126 pn_object_sink (PN_OBJECT (scope->init_script));
|
|
127 scope->frame_script = pn_script_new ();
|
|
128 pn_object_ref (PN_OBJECT (scope->frame_script));
|
|
129 pn_object_sink (PN_OBJECT (scope->frame_script));
|
|
130 scope->sample_script = pn_script_new ();
|
|
131 pn_object_ref (PN_OBJECT (scope->sample_script));
|
|
132 pn_object_sink (PN_OBJECT (scope->sample_script));
|
|
133 scope->symbol_table = pn_symbol_table_new ();
|
|
134 pn_object_ref (PN_OBJECT (scope->symbol_table));
|
|
135 pn_object_sink (PN_OBJECT (scope->symbol_table));
|
|
136
|
|
137 /* Get the variables from the symbol table */
|
|
138 scope->samples_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "samples");
|
|
139 scope->width_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "width");
|
|
140 scope->height_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "height");
|
|
141 scope->x_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "x");
|
|
142 scope->y_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "y");
|
|
143 scope->iteration_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "iteration");
|
|
144 scope->value_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "value");
|
|
145 scope->red_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "red");
|
|
146 scope->green_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "green");
|
|
147 scope->blue_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "blue");
|
|
148 scope->volume_var = pn_symbol_table_ref_variable_by_name (scope->symbol_table, "volume");
|
|
149
|
|
150 PN_VARIABLE_VALUE (scope->samples_var) = 0.0;
|
|
151 PN_VARIABLE_VALUE (scope->red_var) = 1.0;
|
|
152 PN_VARIABLE_VALUE (scope->green_var) = 1.0;
|
|
153 PN_VARIABLE_VALUE (scope->blue_var) = 1.0;
|
|
154 }
|
|
155
|
|
156 static void
|
|
157 pn_scope_destroy (PnObject *object)
|
|
158 {
|
|
159 PnScope *scope;
|
|
160
|
|
161 scope = (PnScope *) object;
|
|
162
|
|
163 pn_object_unref (PN_OBJECT (scope->init_script));
|
|
164 pn_object_unref (PN_OBJECT (scope->frame_script));
|
|
165 pn_object_unref (PN_OBJECT (scope->sample_script));
|
|
166 pn_object_unref (PN_OBJECT (scope->symbol_table));
|
|
167 }
|
|
168
|
|
169 static void
|
|
170 pn_scope_prepare (PnScope *scope, PnImage *image)
|
|
171 {
|
|
172 PnStringOption *init_script_opt;
|
|
173 PnStringOption *frame_script_opt;
|
|
174 PnStringOption *sample_script_opt;
|
|
175
|
|
176 g_return_if_fail (scope != NULL);
|
|
177 g_return_if_fail (PN_IS_SCOPE (scope));
|
|
178 g_return_if_fail (image != NULL);
|
|
179 g_return_if_fail (PN_IS_IMAGE (image));
|
|
180
|
|
181 /* Parse the script strings */
|
|
182 init_script_opt = (PnStringOption *) pn_actuator_get_option_by_index (PN_ACTUATOR (scope),
|
|
183 PN_SCOPE_OPT_INIT_SCRIPT);
|
|
184 frame_script_opt = (PnStringOption *) pn_actuator_get_option_by_index (PN_ACTUATOR (scope),
|
|
185 PN_SCOPE_OPT_FRAME_SCRIPT);
|
|
186 sample_script_opt = (PnStringOption *) pn_actuator_get_option_by_index (PN_ACTUATOR (scope),
|
|
187 PN_SCOPE_OPT_SAMPLE_SCRIPT);
|
|
188
|
|
189 pn_script_parse_string (scope->init_script,
|
|
190 scope->symbol_table,
|
|
191 pn_string_option_get_value (init_script_opt));
|
|
192 pn_script_parse_string (scope->frame_script,
|
|
193 scope->symbol_table,
|
|
194 pn_string_option_get_value (frame_script_opt));
|
|
195 pn_script_parse_string (scope->sample_script,
|
|
196 scope->symbol_table,
|
|
197 pn_string_option_get_value (sample_script_opt));
|
|
198
|
|
199 /* Set up the width and height in-script variables */
|
|
200 PN_VARIABLE_VALUE (scope->width_var) = pn_image_get_width (image);
|
|
201 PN_VARIABLE_VALUE (scope->height_var) = pn_image_get_height (image);
|
|
202
|
|
203 /* Run the init script */
|
|
204 pn_script_execute (scope->init_script);
|
|
205
|
|
206 if (parent_class->prepare)
|
|
207 parent_class->prepare (PN_ACTUATOR (scope), image);
|
|
208 }
|
|
209
|
|
210 static void
|
|
211 pn_scope_execute (PnScope *scope, PnImage *image, PnAudioData *audio_data)
|
|
212 {
|
|
213 gdouble i, samples;
|
|
214 gdouble half_width, neg_half_height, increment = 0.0;
|
|
215 PnColor color = { 255, 255, 255 };
|
|
216 guint last_x = 0, last_y = 0, x, y;
|
|
217 PnListOption *draw_method_opt;
|
|
218 gboolean use_lines;
|
|
219
|
|
220 g_return_if_fail (scope != NULL);
|
|
221 g_return_if_fail (PN_IS_SCOPE (scope));
|
|
222 g_return_if_fail (image != NULL);
|
|
223 g_return_if_fail (PN_IS_IMAGE (image));
|
|
224 g_return_if_fail (audio_data != NULL);
|
|
225 g_return_if_fail (PN_IS_AUDIO_DATA (audio_data));
|
|
226
|
|
227 half_width = (((gdouble) pn_image_get_width (image)) - 1.0) * 0.5;
|
|
228 neg_half_height = (((gdouble) pn_image_get_height (image)) - 1.0) * -0.5;
|
|
229
|
|
230 draw_method_opt = (PnListOption *) pn_actuator_get_option_by_index (PN_ACTUATOR (scope),
|
|
231 PN_SCOPE_OPT_DRAW_METHOD);
|
|
232 use_lines = pn_list_option_get_index (draw_method_opt) == PN_SCOPE_DRAW_METHOD_LINES;
|
|
233
|
|
234 /* Set the volume in-script variable */
|
|
235 PN_VARIABLE_VALUE (scope->volume_var) = pn_audio_data_get_volume (audio_data);
|
|
236
|
|
237 /* Run the frame script */
|
|
238 pn_script_execute (scope->frame_script);
|
|
239
|
|
240 /* Go through and draw each sample-point */
|
|
241 samples = PN_VARIABLE_VALUE (scope->samples_var);
|
|
242 if (samples > 1)
|
|
243 {
|
|
244 increment = 1 / (samples - 1);
|
|
245 PN_VARIABLE_VALUE (scope->iteration_var) = -increment;
|
|
246 }
|
|
247 else if (samples > 0)
|
|
248 {
|
|
249 increment = 0;
|
|
250 PN_VARIABLE_VALUE (scope->iteration_var) = 0.0;
|
|
251 }
|
|
252 for (i=0; i < samples; i++)
|
|
253 {
|
|
254 /* Set up the in-script variables */
|
|
255 PN_VARIABLE_VALUE (scope->x_var) = 2.0;
|
|
256 PN_VARIABLE_VALUE (scope->y_var) = 2.0;
|
|
257 PN_VARIABLE_VALUE (scope->iteration_var) += increment;
|
|
258
|
|
259 /* FIXME: This should be done in PnAudioData */
|
|
260 /* Interpolate the sample value */
|
|
261 {
|
|
262 gdouble sample, bias;
|
|
263 guint lsamp, usamp;
|
|
264
|
|
265 sample = (gdouble)PN_AUDIO_DATA_PCM_SAMPLES(audio_data)
|
|
266 * (gdouble)PN_VARIABLE_VALUE (scope->iteration_var);
|
|
267
|
|
268 lsamp = (guint) sample;
|
|
269 if (lsamp != sample)
|
|
270 {
|
|
271 usamp = lsamp + 1;
|
|
272
|
|
273 bias = sample - (gdouble) lsamp;
|
|
274
|
|
275 PN_VARIABLE_VALUE (scope->value_var) =
|
|
276 (PN_AUDIO_DATA_PCM_DATA(audio_data, PN_CHANNEL_LEFT)[lsamp] * (1 - bias))
|
|
277 + (PN_AUDIO_DATA_PCM_DATA(audio_data, PN_CHANNEL_LEFT)[lsamp] * bias);
|
|
278 }
|
|
279 else
|
|
280 PN_VARIABLE_VALUE (scope->value_var) =
|
|
281 PN_AUDIO_DATA_PCM_DATA (audio_data, PN_CHANNEL_LEFT)[lsamp];
|
|
282 }
|
|
283
|
|
284 /* Run the sample script */
|
|
285 pn_script_execute (scope->sample_script);
|
|
286
|
|
287 /* Get the color */
|
|
288 color.red = CLAMP (PN_VARIABLE_VALUE (scope->red_var), 0.0, 1.0) * 255.0;
|
|
289 color.green = CLAMP (PN_VARIABLE_VALUE (scope->green_var), 0.0, 1.0) * 255.0;
|
|
290 color.blue = CLAMP (PN_VARIABLE_VALUE (scope->blue_var), 0.0, 1.0) * 255.0;
|
|
291
|
|
292 /* Render the point */
|
|
293 x = rint ((PN_VARIABLE_VALUE (scope->x_var) + 1.0) * ((gdouble) half_width ));
|
|
294 y = rint ((PN_VARIABLE_VALUE (scope->y_var) - 1.0) * ((gdouble) neg_half_height));
|
|
295
|
|
296 if (use_lines)
|
|
297 {
|
|
298 if (i > 0)
|
|
299 pn_image_render_line (image, last_x, last_y, x, y, color);
|
|
300
|
|
301 last_x = x;
|
|
302 last_y = y;
|
|
303 }
|
|
304 else
|
|
305 pn_image_render_pixel (image, x, y, color);
|
|
306 }
|
|
307
|
|
308 if (parent_class->execute)
|
|
309 parent_class->execute (PN_ACTUATOR (scope), image, audio_data);
|
|
310 }
|
|
311
|
|
312 PnScope*
|
|
313 pn_scope_new (void)
|
|
314 {
|
|
315 return (PnScope *) g_object_new (PN_TYPE_SCOPE, NULL);
|
|
316 }
|