diff options
author | Peter D'Hoye <peter.dhoye@gmail.com> | 2009-05-22 21:58:48 +0000 |
---|---|---|
committer | Peter D'Hoye <peter.dhoye@gmail.com> | 2009-05-22 21:58:48 +0000 |
commit | 513389b4c1bc8afe4b2dc9947c534bfeb105e3da (patch) | |
tree | 10e673b35651ac567fed2eda0c679c7ade64cbc6 /apps/plugins/pdbox/PDa/src/g_array.c | |
parent | 95fa7f6a2ef466444fbe3fe87efc6d5db6b77b36 (diff) | |
download | rockbox-513389b4c1bc8afe4b2dc9947c534bfeb105e3da.tar.gz rockbox-513389b4c1bc8afe4b2dc9947c534bfeb105e3da.zip |
Add FS #10214. Initial commit of the original PDa code for the GSoC Pure Data plugin project of Wincent Balin. Stripped some non-sourcefiles and added a rockbox readme that needs a bit more info from Wincent. Is added to CATEGORIES and viewers, but not yet to SUBDIRS (ie doesn't build yet)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21044 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/g_array.c')
-rw-r--r-- | apps/plugins/pdbox/PDa/src/g_array.c | 2734 |
1 files changed, 2734 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/g_array.c b/apps/plugins/pdbox/PDa/src/g_array.c new file mode 100644 index 0000000000..f259ac8dcb --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_array.c | |||
@@ -0,0 +1,2734 @@ | |||
1 | /* Copyright (c) 1997-1999 Miller Puckette. | ||
2 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL | ||
3 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ | ||
4 | |||
5 | #include <stdlib.h> | ||
6 | #include <string.h> | ||
7 | #include <stdio.h> /* for read/write to files */ | ||
8 | #include "m_pd.h" | ||
9 | #include "g_canvas.h" | ||
10 | #include <math.h> | ||
11 | |||
12 | /* see also the "plot" object in g_scalar.c which deals with graphing | ||
13 | arrays which are fields in scalars. Someday we should unify the | ||
14 | two, but how? */ | ||
15 | |||
16 | /* aux routine to bash leading '#' to '$' for dialogs in u_main.tk | ||
17 | which can't send symbols starting with '$' (because the Pd message | ||
18 | interpreter would change them!) */ | ||
19 | |||
20 | static t_symbol *sharptodollar(t_symbol *s) | ||
21 | { | ||
22 | if (*s->s_name == '#') | ||
23 | { | ||
24 | char buf[MAXPDSTRING]; | ||
25 | strncpy(buf, s->s_name, MAXPDSTRING); | ||
26 | buf[MAXPDSTRING-1] = 0; | ||
27 | buf[0] = '$'; | ||
28 | return (gensym(buf)); | ||
29 | } | ||
30 | else return (s); | ||
31 | } | ||
32 | |||
33 | /* --------- "pure" arrays with scalars for elements. --------------- */ | ||
34 | |||
35 | /* Pure arrays have no a priori graphical capabilities. | ||
36 | They are instantiated by "garrays" below or can be elements of other | ||
37 | scalars (g_scalar.c); their graphical behavior is defined accordingly. */ | ||
38 | |||
39 | t_array *array_new(t_symbol *templatesym, t_gpointer *parent) | ||
40 | { | ||
41 | t_array *x = (t_array *)getbytes(sizeof (*x)); | ||
42 | t_template *template; | ||
43 | t_gpointer *gp; | ||
44 | template = template_findbyname(templatesym); | ||
45 | x->a_templatesym = templatesym; | ||
46 | x->a_n = 1; | ||
47 | x->a_elemsize = sizeof(t_word) * template->t_n; | ||
48 | x->a_vec = (char *)getbytes(x->a_elemsize); | ||
49 | /* note here we blithely copy a gpointer instead of "setting" a | ||
50 | new one; this gpointer isn't accounted for and needn't be since | ||
51 | we'll be deleted before the thing pointed to gets deleted anyway; | ||
52 | see array_free. */ | ||
53 | x->a_gp = *parent; | ||
54 | x->a_stub = gstub_new(0, x); | ||
55 | word_init((t_word *)(x->a_vec), template, parent); | ||
56 | return (x); | ||
57 | } | ||
58 | |||
59 | void array_resize(t_array *x, t_template *template, int n) | ||
60 | { | ||
61 | int elemsize, oldn; | ||
62 | t_gpointer *gp; | ||
63 | |||
64 | if (n < 1) | ||
65 | n = 1; | ||
66 | oldn = x->a_n; | ||
67 | elemsize = sizeof(t_word) * template->t_n; | ||
68 | |||
69 | x->a_vec = (char *)resizebytes(x->a_vec, oldn * elemsize, | ||
70 | n * elemsize); | ||
71 | x->a_n = n; | ||
72 | if (n > oldn) | ||
73 | { | ||
74 | char *cp = x->a_vec + elemsize * oldn; | ||
75 | int i = n - oldn; | ||
76 | for (; i--; cp += elemsize) | ||
77 | { | ||
78 | t_word *wp = (t_word *)cp; | ||
79 | word_init(wp, template, &x->a_gp); | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | |||
84 | void word_free(t_word *wp, t_template *template); | ||
85 | |||
86 | void array_free(t_array *x) | ||
87 | { | ||
88 | int i; | ||
89 | t_template *scalartemplate = template_findbyname(x->a_templatesym); | ||
90 | /* we don't unset our gpointer here since it was never "set." */ | ||
91 | /* gpointer_unset(&x->a_gp); */ | ||
92 | gstub_cutoff(x->a_stub); | ||
93 | for (i = 0; i < x->a_n; i++) | ||
94 | { | ||
95 | t_word *wp = (t_word *)(x->a_vec + x->a_elemsize * i); | ||
96 | word_free(wp, scalartemplate); | ||
97 | } | ||
98 | freebytes(x->a_vec, x->a_elemsize * x->a_n); | ||
99 | freebytes(x, sizeof *x); | ||
100 | } | ||
101 | |||
102 | /* --------------------- graphical arrays (garrays) ------------------- */ | ||
103 | |||
104 | t_class *garray_class; | ||
105 | static int gcount = 0; | ||
106 | |||
107 | struct _garray | ||
108 | { | ||
109 | t_gobj x_gobj; | ||
110 | t_glist *x_glist; | ||
111 | t_array x_array; /* actual array; note only 4 fields used as below */ | ||
112 | t_symbol *x_name; | ||
113 | t_symbol *x_realname; /* name with "$" expanded */ | ||
114 | t_float x_firstx; /* X value of first item */ | ||
115 | t_float x_xinc; /* X increment */ | ||
116 | char x_usedindsp; /* true if some DSP routine is using this */ | ||
117 | char x_saveit; /* true if we should save this with parent */ | ||
118 | }; | ||
119 | |||
120 | /* macros to get into the "array" structure */ | ||
121 | #define x_n x_array.a_n | ||
122 | #define x_elemsize x_array.a_elemsize | ||
123 | #define x_vec x_array.a_vec | ||
124 | #define x_templatesym x_array.a_templatesym | ||
125 | |||
126 | t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *templatesym, | ||
127 | t_floatarg f, t_floatarg saveit) | ||
128 | { | ||
129 | int n = f, i; | ||
130 | int zz, nwords; | ||
131 | t_garray *x; | ||
132 | t_pd *x2; | ||
133 | t_template *template; | ||
134 | char *str; | ||
135 | if (s == &s_) | ||
136 | { | ||
137 | char buf[40]; | ||
138 | sprintf(buf, "array%d", ++gcount); | ||
139 | s = gensym(buf); | ||
140 | templatesym = &s_float; | ||
141 | n = 100; | ||
142 | } | ||
143 | else if (!strncmp((str = s->s_name), "array", 5) | ||
144 | && (zz = atoi(str + 5)) > gcount) gcount = zz; | ||
145 | template = template_findbyname(templatesym); | ||
146 | if (!template) | ||
147 | { | ||
148 | error("array: couldn't find template %s", templatesym->s_name); | ||
149 | return (0); | ||
150 | } | ||
151 | nwords = template->t_n; | ||
152 | for (i = 0; i < nwords; i++) | ||
153 | { | ||
154 | /* we can't have array or list elements yet because what scalar | ||
155 | can act as their "parent"??? */ | ||
156 | if (template->t_vec[i].ds_type == DT_ARRAY | ||
157 | || template->t_vec[i].ds_type == DT_LIST) | ||
158 | { | ||
159 | error("array: template %s can't have sublists or arrays", | ||
160 | templatesym->s_name); | ||
161 | return (0); | ||
162 | } | ||
163 | } | ||
164 | x = (t_garray *)pd_new(garray_class); | ||
165 | |||
166 | if (n <= 0) n = 100; | ||
167 | x->x_n = n; | ||
168 | x->x_elemsize = nwords * sizeof(t_word); | ||
169 | x->x_vec = getbytes(x->x_n * x->x_elemsize); | ||
170 | memset(x->x_vec, 0, x->x_n * x->x_elemsize); | ||
171 | /* LATER should check that malloc */ | ||
172 | x->x_name = s; | ||
173 | x->x_realname = canvas_realizedollar(gl, s); | ||
174 | pd_bind(&x->x_gobj.g_pd, x->x_realname); | ||
175 | x->x_templatesym = templatesym; | ||
176 | x->x_firstx = 0; | ||
177 | x->x_xinc = 1; /* LATER make methods to set this... */ | ||
178 | glist_add(gl, &x->x_gobj); | ||
179 | x->x_glist = gl; | ||
180 | x->x_usedindsp = 0; | ||
181 | x->x_saveit = (saveit != 0); | ||
182 | if (x2 = pd_findbyclass(gensym("#A"), garray_class)) | ||
183 | pd_unbind(x2, gensym("#A")); | ||
184 | |||
185 | pd_bind(&x->x_gobj.g_pd, gensym("#A")); | ||
186 | |||
187 | return (x); | ||
188 | } | ||
189 | |||
190 | /* called from array menu item to create a new one */ | ||
191 | void canvas_menuarray(t_glist *canvas) | ||
192 | { | ||
193 | t_glist *x = (t_glist *)canvas; | ||
194 | char cmdbuf[200]; | ||
195 | sprintf(cmdbuf, "pdtk_array_dialog %%s array%d 100 1 1\n", | ||
196 | ++gcount); | ||
197 | gfxstub_new(&x->gl_pd, x, cmdbuf); | ||
198 | } | ||
199 | |||
200 | /* called from graph_dialog to set properties */ | ||
201 | void garray_properties(t_garray *x) | ||
202 | { | ||
203 | char cmdbuf[200]; | ||
204 | gfxstub_deleteforkey(x); | ||
205 | /* create dialog window. LATER fix this to escape '$' | ||
206 | properly; right now we just detect a leading '$' and escape | ||
207 | it. There should be a systematic way of doing this. */ | ||
208 | if (x->x_name->s_name[0] == '$') | ||
209 | sprintf(cmdbuf, "pdtk_array_dialog %%s \\%s %d %d 0\n", | ||
210 | x->x_name->s_name, x->x_n, x->x_saveit); | ||
211 | else sprintf(cmdbuf, "pdtk_array_dialog %%s %s %d %d 0\n", | ||
212 | x->x_name->s_name, x->x_n, x->x_saveit); | ||
213 | gfxstub_new(&x->x_gobj.g_pd, x, cmdbuf); | ||
214 | } | ||
215 | |||
216 | /* this is called back from the dialog window to create a garray. | ||
217 | The otherflag requests that we find an existing graph to put it in. */ | ||
218 | void glist_arraydialog(t_glist *parent, t_symbol *name, t_floatarg size, | ||
219 | t_floatarg saveit, t_floatarg otherflag) | ||
220 | { | ||
221 | t_glist *gl; | ||
222 | t_garray *a; | ||
223 | if (size < 1) | ||
224 | size = 1; | ||
225 | if (otherflag == 0 || (!(gl = glist_findgraph(parent)))) | ||
226 | gl = glist_addglist(parent, &s_, 0, 1, | ||
227 | (size > 1 ? size-1 : size), -1, 0, 0, 0, 0); | ||
228 | a = graph_array(gl, sharptodollar(name), &s_float, size, saveit); | ||
229 | } | ||
230 | |||
231 | /* this is called from the properties dialog window for an existing array */ | ||
232 | void garray_arraydialog(t_garray *x, t_symbol *name, t_floatarg fsize, | ||
233 | t_floatarg saveit, t_floatarg deleteit) | ||
234 | { | ||
235 | if (deleteit != 0) | ||
236 | { | ||
237 | glist_delete(x->x_glist, &x->x_gobj); | ||
238 | } | ||
239 | else | ||
240 | { | ||
241 | int size; | ||
242 | t_symbol *argname = sharptodollar(name); | ||
243 | if (argname != x->x_name) | ||
244 | { | ||
245 | x->x_name = argname; | ||
246 | pd_unbind(&x->x_gobj.g_pd, x->x_realname); | ||
247 | x->x_realname = canvas_realizedollar(x->x_glist, argname); | ||
248 | pd_bind(&x->x_gobj.g_pd, x->x_realname); | ||
249 | } | ||
250 | size = fsize; | ||
251 | if (size < 1) | ||
252 | size = 1; | ||
253 | if (size != x->x_n) | ||
254 | garray_resize(x, size); | ||
255 | garray_setsaveit(x, (saveit != 0)); | ||
256 | garray_redraw(x); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | static void garray_free(t_garray *x) | ||
261 | { | ||
262 | t_pd *x2; | ||
263 | gfxstub_deleteforkey(x); | ||
264 | pd_unbind(&x->x_gobj.g_pd, x->x_realname); | ||
265 | /* LATER find a way to get #A unbound earlier (at end of load?) */ | ||
266 | while (x2 = pd_findbyclass(gensym("#A"), garray_class)) | ||
267 | pd_unbind(x2, gensym("#A")); | ||
268 | freebytes(x->x_vec, x->x_n * x->x_elemsize); | ||
269 | } | ||
270 | |||
271 | /* ------------- code used by both array and plot widget functions ---- */ | ||
272 | |||
273 | /* routine to get screen coordinates of a point in an array */ | ||
274 | void array_getcoordinate(t_glist *glist, | ||
275 | char *elem, int xonset, int yonset, int wonset, int indx, | ||
276 | float basex, float basey, float xinc, | ||
277 | float *xp, float *yp, float *wp) | ||
278 | { | ||
279 | float xval, yval, ypix, wpix; | ||
280 | if (xonset >= 0) | ||
281 | xval = fixtof(*(t_sample *)(elem + xonset)); | ||
282 | else xval = indx * xinc; | ||
283 | if (yonset >= 0) | ||
284 | yval = fixtof(*(t_sample *)(elem + yonset)); | ||
285 | else yval = 0; | ||
286 | ypix = glist_ytopixels(glist, basey + yval); | ||
287 | if (wonset >= 0) | ||
288 | { | ||
289 | /* found "w" field which controls linewidth. */ | ||
290 | float wval = *(float *)(elem + wonset); | ||
291 | wpix = glist_ytopixels(glist, basey + yval + wval) - ypix; | ||
292 | if (wpix < 0) | ||
293 | wpix = -wpix; | ||
294 | } | ||
295 | else wpix = 1; | ||
296 | *xp = glist_xtopixels(glist, basex + xval); | ||
297 | *yp = ypix; | ||
298 | *wp = wpix; | ||
299 | } | ||
300 | |||
301 | static float array_motion_xcumulative; | ||
302 | static float array_motion_ycumulative; | ||
303 | static t_symbol *array_motion_xfield; | ||
304 | static t_symbol *array_motion_yfield; | ||
305 | static t_glist *array_motion_glist; | ||
306 | static t_gobj *array_motion_gobj; | ||
307 | static t_word *array_motion_wp; | ||
308 | static t_template *array_motion_template; | ||
309 | static int array_motion_npoints; | ||
310 | static int array_motion_elemsize; | ||
311 | static int array_motion_altkey; | ||
312 | static float array_motion_initx; | ||
313 | static float array_motion_xperpix; | ||
314 | static float array_motion_yperpix; | ||
315 | static int array_motion_lastx; | ||
316 | static int array_motion_fatten; | ||
317 | |||
318 | /* LATER protect against the template changing or the scalar disappearing | ||
319 | probably by attaching a gpointer here ... */ | ||
320 | |||
321 | static void array_motion(void *z, t_floatarg dx, t_floatarg dy) | ||
322 | { | ||
323 | array_motion_xcumulative += dx * array_motion_xperpix; | ||
324 | array_motion_ycumulative += dy * array_motion_yperpix; | ||
325 | if (*array_motion_xfield->s_name) | ||
326 | { | ||
327 | /* it's an x, y plot; can drag many points at once */ | ||
328 | int i; | ||
329 | char *charword = (char *)array_motion_wp; | ||
330 | for (i = 0; i < array_motion_npoints; i++) | ||
331 | { | ||
332 | t_word *thisword = (t_word *)(charword + i * array_motion_elemsize); | ||
333 | if (*array_motion_xfield->s_name) | ||
334 | { | ||
335 | float xwas = template_getfloat(array_motion_template, | ||
336 | array_motion_xfield, thisword, 1); | ||
337 | template_setfloat(array_motion_template, | ||
338 | array_motion_xfield, thisword, xwas + dx, 1); | ||
339 | } | ||
340 | if (*array_motion_yfield->s_name) | ||
341 | { | ||
342 | float ywas = template_getfloat(array_motion_template, | ||
343 | array_motion_yfield, thisword, 1); | ||
344 | if (array_motion_fatten) | ||
345 | { | ||
346 | if (i == 0) | ||
347 | { | ||
348 | float newy = ywas + dy * array_motion_yperpix; | ||
349 | if (newy < 0) | ||
350 | newy = 0; | ||
351 | template_setfloat(array_motion_template, | ||
352 | array_motion_yfield, thisword, newy, 1); | ||
353 | } | ||
354 | } | ||
355 | else | ||
356 | { | ||
357 | template_setfloat(array_motion_template, | ||
358 | array_motion_yfield, thisword, | ||
359 | ywas + dy * array_motion_yperpix, 1); | ||
360 | } | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | else | ||
365 | { | ||
366 | /* a y-only plot. */ | ||
367 | int thisx = array_motion_initx + | ||
368 | array_motion_xcumulative, x2; | ||
369 | int increment, i, nchange; | ||
370 | char *charword = (char *)array_motion_wp; | ||
371 | float newy = array_motion_ycumulative, | ||
372 | oldy = template_getfloat( | ||
373 | array_motion_template, array_motion_yfield, | ||
374 | (t_word *)(charword + array_motion_elemsize * array_motion_lastx), 1); | ||
375 | float ydiff = newy - oldy; | ||
376 | if (thisx < 0) thisx = 0; | ||
377 | else if (thisx >= array_motion_npoints) | ||
378 | thisx = array_motion_npoints - 1; | ||
379 | increment = (thisx > array_motion_lastx ? -1 : 1); | ||
380 | nchange = 1 + increment * (array_motion_lastx - thisx); | ||
381 | |||
382 | for (i = 0, x2 = thisx; i < nchange; i++, x2 += increment) | ||
383 | { | ||
384 | template_setfloat(array_motion_template, | ||
385 | array_motion_yfield, | ||
386 | (t_word *)(charword + array_motion_elemsize * x2), | ||
387 | newy, 1); | ||
388 | if (nchange > 1) | ||
389 | newy -= ydiff * (1./(nchange - 1)); | ||
390 | } | ||
391 | array_motion_lastx = thisx; | ||
392 | } | ||
393 | glist_redrawitem(array_motion_glist, array_motion_gobj); | ||
394 | } | ||
395 | |||
396 | int array_doclick(t_array *array, t_glist *glist, t_gobj *gobj, | ||
397 | t_symbol *elemtemplatesym, | ||
398 | float linewidth, float xloc, float xinc, float yloc, | ||
399 | int xpix, int ypix, int shift, int alt, int dbl, int doit) | ||
400 | { | ||
401 | t_canvas *elemtemplatecanvas; | ||
402 | t_template *elemtemplate; | ||
403 | int elemsize, yonset, wonset, xonset, i; | ||
404 | |||
405 | if (!array_getfields(elemtemplatesym, &elemtemplatecanvas, | ||
406 | &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) | ||
407 | { | ||
408 | float best = 100; | ||
409 | int incr; | ||
410 | /* if it has more than 2000 points, just check 300 of them. */ | ||
411 | if (array->a_n < 2000) | ||
412 | incr = 1; | ||
413 | else incr = array->a_n / 300; | ||
414 | for (i = 0; i < array->a_n; i += incr) | ||
415 | { | ||
416 | float pxpix, pypix, pwpix, dx, dy; | ||
417 | array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, | ||
418 | xonset, yonset, wonset, i, xloc, yloc, xinc, | ||
419 | &pxpix, &pypix, &pwpix); | ||
420 | if (pwpix < 4) | ||
421 | pwpix = 4; | ||
422 | dx = pxpix - xpix; | ||
423 | if (dx < 0) dx = -dx; | ||
424 | if (dx > 8) | ||
425 | continue; | ||
426 | dy = pypix - ypix; | ||
427 | if (dy < 0) dy = -dy; | ||
428 | if (dx + dy < best) | ||
429 | best = dx + dy; | ||
430 | if (wonset >= 0) | ||
431 | { | ||
432 | dy = (pypix + pwpix) - ypix; | ||
433 | if (dy < 0) dy = -dy; | ||
434 | if (dx + dy < best) | ||
435 | best = dx + dy; | ||
436 | dy = (pypix - pwpix) - ypix; | ||
437 | if (dy < 0) dy = -dy; | ||
438 | if (dx + dy < best) | ||
439 | best = dx + dy; | ||
440 | } | ||
441 | } | ||
442 | if (best > 8) | ||
443 | return (0); | ||
444 | best += 0.001; /* add truncation error margin */ | ||
445 | for (i = 0; i < array->a_n; i += incr) | ||
446 | { | ||
447 | float pxpix, pypix, pwpix, dx, dy, dy2, dy3; | ||
448 | array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, | ||
449 | xonset, yonset, wonset, i, xloc, yloc, xinc, | ||
450 | &pxpix, &pypix, &pwpix); | ||
451 | if (pwpix < 4) | ||
452 | pwpix = 4; | ||
453 | dx = pxpix - xpix; | ||
454 | if (dx < 0) dx = -dx; | ||
455 | dy = pypix - ypix; | ||
456 | if (dy < 0) dy = -dy; | ||
457 | if (wonset >= 0) | ||
458 | { | ||
459 | dy2 = (pypix + pwpix) - ypix; | ||
460 | if (dy2 < 0) dy2 = -dy2; | ||
461 | dy3 = (pypix - pwpix) - ypix; | ||
462 | if (dy3 < 0) dy3 = -dy3; | ||
463 | if (yonset <= 0) | ||
464 | dy = 100; | ||
465 | } | ||
466 | else dy2 = dy3 = 100; | ||
467 | if (dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best) | ||
468 | { | ||
469 | if (dy < dy2 && dy < dy3) | ||
470 | array_motion_fatten = 0; | ||
471 | else if (dy2 < dy3) | ||
472 | array_motion_fatten = -1; | ||
473 | else array_motion_fatten = 1; | ||
474 | if (doit) | ||
475 | { | ||
476 | char *elem = (char *)array->a_vec; | ||
477 | array_motion_elemsize = elemsize; | ||
478 | array_motion_glist = glist; | ||
479 | array_motion_gobj = gobj; | ||
480 | array_motion_template = elemtemplate; | ||
481 | array_motion_xperpix = glist_dpixtodx(glist, 1); | ||
482 | array_motion_yperpix = glist_dpixtody(glist, 1); | ||
483 | if (alt && xpix < pxpix) /* delete a point */ | ||
484 | { | ||
485 | if (array->a_n <= 1) | ||
486 | return (0); | ||
487 | memmove((char *)(array->a_vec) + elemsize * i, | ||
488 | (char *)(array->a_vec) + elemsize * (i+1), | ||
489 | (array->a_n - 1 - i) * elemsize); | ||
490 | array_resize(array, elemtemplate, array->a_n - 1); | ||
491 | glist_redrawitem(array_motion_glist, array_motion_gobj); | ||
492 | return (0); | ||
493 | } | ||
494 | else if (alt) | ||
495 | { | ||
496 | /* add a point (after the clicked-on one) */ | ||
497 | array_resize(array, elemtemplate, array->a_n + 1); | ||
498 | elem = (char *)array->a_vec; | ||
499 | memmove(elem + elemsize * (i+1), | ||
500 | elem + elemsize * i, | ||
501 | (array->a_n - i - 1) * elemsize); | ||
502 | i++; | ||
503 | } | ||
504 | if (xonset >= 0) | ||
505 | { | ||
506 | array_motion_xfield = gensym("x"); | ||
507 | array_motion_xcumulative = | ||
508 | *(float *)((elem + elemsize * i) + xonset); | ||
509 | array_motion_wp = (t_word *)(elem + i * elemsize); | ||
510 | array_motion_npoints = array->a_n - i; | ||
511 | } | ||
512 | else | ||
513 | { | ||
514 | array_motion_xfield = &s_; | ||
515 | array_motion_xcumulative = 0; | ||
516 | array_motion_wp = (t_word *)elem; | ||
517 | array_motion_npoints = array->a_n; | ||
518 | |||
519 | array_motion_initx = i; | ||
520 | array_motion_lastx = i; | ||
521 | array_motion_xperpix *= (xinc == 0 ? 1 : 1./xinc); | ||
522 | } | ||
523 | if (array_motion_fatten) | ||
524 | { | ||
525 | array_motion_yfield = gensym("w"); | ||
526 | array_motion_ycumulative = | ||
527 | *(float *)((elem + elemsize * i) + wonset); | ||
528 | array_motion_yperpix *= array_motion_fatten; | ||
529 | } | ||
530 | else if (yonset >= 0) | ||
531 | { | ||
532 | array_motion_yfield = gensym("y"); | ||
533 | array_motion_ycumulative = | ||
534 | *(float *)((elem + elemsize * i) + yonset); | ||
535 | } | ||
536 | else | ||
537 | { | ||
538 | array_motion_yfield = &s_; | ||
539 | array_motion_ycumulative = 0; | ||
540 | } | ||
541 | glist_grab(glist, 0, array_motion, 0, xpix, ypix); | ||
542 | } | ||
543 | if (alt) | ||
544 | { | ||
545 | if (xpix < pxpix) | ||
546 | return (CURSOR_EDITMODE_DISCONNECT); | ||
547 | else return (CURSOR_RUNMODE_ADDPOINT); | ||
548 | } | ||
549 | else return (array_motion_fatten ? | ||
550 | CURSOR_RUNMODE_THICKEN : CURSOR_RUNMODE_CLICKME); | ||
551 | } | ||
552 | } | ||
553 | } | ||
554 | return (0); | ||
555 | } | ||
556 | |||
557 | /* -------------------- widget behavior for garray ------------ */ | ||
558 | |||
559 | static void garray_getrect(t_gobj *z, t_glist *glist, | ||
560 | int *xp1, int *yp1, int *xp2, int *yp2) | ||
561 | { | ||
562 | t_garray *x = (t_garray *)z; | ||
563 | float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; | ||
564 | t_canvas *elemtemplatecanvas; | ||
565 | t_template *elemtemplate; | ||
566 | int elemsize, yonset, wonset, xonset, i; | ||
567 | |||
568 | if (!array_getfields(x->x_templatesym, &elemtemplatecanvas, | ||
569 | &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) | ||
570 | { | ||
571 | int incr; | ||
572 | /* if it has more than 2000 points, just check 300 of them. */ | ||
573 | if (x->x_array.a_n < 2000) | ||
574 | incr = 1; | ||
575 | else incr = x->x_array.a_n / 300; | ||
576 | for (i = 0; i < x->x_array.a_n; i += incr) | ||
577 | { | ||
578 | float pxpix, pypix, pwpix, dx, dy; | ||
579 | array_getcoordinate(glist, (char *)(x->x_array.a_vec) + | ||
580 | i * elemsize, | ||
581 | xonset, yonset, wonset, i, 0, 0, 1, | ||
582 | &pxpix, &pypix, &pwpix); | ||
583 | if (pwpix < 2) | ||
584 | pwpix = 2; | ||
585 | if (pxpix < x1) | ||
586 | x1 = pxpix; | ||
587 | if (pxpix > x2) | ||
588 | x2 = pxpix; | ||
589 | if (pypix - pwpix < y1) | ||
590 | y1 = pypix - pwpix; | ||
591 | if (pypix + pwpix > y2) | ||
592 | y2 = pypix + pwpix; | ||
593 | } | ||
594 | } | ||
595 | *xp1 = x1; | ||
596 | *yp1 = y1; | ||
597 | *xp2 = x2; | ||
598 | *yp2 = y2; | ||
599 | } | ||
600 | |||
601 | static void garray_displace(t_gobj *z, t_glist *glist, int dx, int dy) | ||
602 | { | ||
603 | /* refuse */ | ||
604 | } | ||
605 | |||
606 | static void garray_select(t_gobj *z, t_glist *glist, int state) | ||
607 | { | ||
608 | t_garray *x = (t_garray *)z; | ||
609 | /* fill in later */ | ||
610 | } | ||
611 | |||
612 | static void garray_activate(t_gobj *z, t_glist *glist, int state) | ||
613 | { | ||
614 | } | ||
615 | |||
616 | static void garray_delete(t_gobj *z, t_glist *glist) | ||
617 | { | ||
618 | /* nothing to do */ | ||
619 | } | ||
620 | |||
621 | static void garray_vis(t_gobj *z, t_glist *glist, int vis) | ||
622 | { | ||
623 | t_garray *x = (t_garray *)z; | ||
624 | if (vis) | ||
625 | { | ||
626 | int i, xonset, yonset, type; | ||
627 | t_symbol *arraytype; | ||
628 | t_template *template = template_findbyname(x->x_templatesym); | ||
629 | if (!template) | ||
630 | return; | ||
631 | if (!template_find_field(template, gensym("y"), &yonset, &type, | ||
632 | &arraytype) || type != DT_FLOAT) | ||
633 | { | ||
634 | error("%s: needs floating-point 'y' field", | ||
635 | x->x_templatesym->s_name); | ||
636 | sys_vgui(".x%x.c create text 50 50 -text foo\ | ||
637 | -tags .x%x.a%x\n", | ||
638 | glist_getcanvas(glist), glist_getcanvas(glist), x); | ||
639 | } | ||
640 | else if (!template_find_field(template, gensym("x"), &xonset, &type, | ||
641 | &arraytype) || type != DT_FLOAT) | ||
642 | { | ||
643 | float firsty, xcum = x->x_firstx; | ||
644 | int lastpixel = -1, ndrawn = 0; | ||
645 | float yval = 0, xpix; | ||
646 | int ixpix = 0; | ||
647 | sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist)); | ||
648 | for (i = 0; i < x->x_n; i++) | ||
649 | { | ||
650 | yval = fixtof(*(t_sample *)(x->x_vec + | ||
651 | template->t_n * i * sizeof (t_word) + yonset)); | ||
652 | xpix = glist_xtopixels(glist, xcum); | ||
653 | ixpix = xpix + 0.5; | ||
654 | if (ixpix != lastpixel) | ||
655 | { | ||
656 | sys_vgui("%d %f \\\n", ixpix, | ||
657 | glist_ytopixels(glist, yval)); | ||
658 | ndrawn++; | ||
659 | } | ||
660 | lastpixel = ixpix; | ||
661 | if (ndrawn >= 1000) break; | ||
662 | xcum += x->x_xinc; | ||
663 | } | ||
664 | /* TK will complain if there aren't at least 2 points... */ | ||
665 | if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n"); | ||
666 | else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix, | ||
667 | glist_ytopixels(glist, yval)); | ||
668 | sys_vgui("-tags .x%x.a%x\n", glist_getcanvas(glist), x); | ||
669 | firsty = fixtof(*(t_sample *)(x->x_vec + yonset)); | ||
670 | sys_vgui(".x%x.c create text %f %f -text {%s} -anchor e\ | ||
671 | -font -*-courier-bold--normal--%d-* -tags .x%x.a%x\n", | ||
672 | glist_getcanvas(glist), | ||
673 | glist_xtopixels(glist, x->x_firstx) - 5., | ||
674 | glist_ytopixels(glist, firsty), | ||
675 | x->x_name->s_name, glist_getfont(glist), | ||
676 | glist_getcanvas(glist), x); | ||
677 | } | ||
678 | else | ||
679 | { | ||
680 | post("x, y arrays not yet supported"); | ||
681 | } | ||
682 | } | ||
683 | else | ||
684 | { | ||
685 | sys_vgui(".x%x.c delete .x%x.a%x\n", | ||
686 | glist_getcanvas(glist), glist_getcanvas(glist), x); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | static int garray_click(t_gobj *z, struct _glist *glist, | ||
691 | int xpix, int ypix, int shift, int alt, int dbl, int doit) | ||
692 | { | ||
693 | t_garray *x = (t_garray *)z; | ||
694 | return (array_doclick(&x->x_array, glist, z, x->x_templatesym, 1, 0, 1, 0, | ||
695 | xpix, ypix, shift, alt, dbl, doit)); | ||
696 | } | ||
697 | |||
698 | #define ARRAYWRITECHUNKSIZE 1000 | ||
699 | |||
700 | static void garray_save(t_gobj *z, t_binbuf *b) | ||
701 | { | ||
702 | t_garray *x = (t_garray *)z; | ||
703 | binbuf_addv(b, "sssisi;", gensym("#X"), gensym("array"), | ||
704 | x->x_name, x->x_n, x->x_templatesym, x->x_saveit); | ||
705 | fprintf(stderr,"array save\n"); | ||
706 | if (x->x_saveit) | ||
707 | { | ||
708 | int n = x->x_n, n2 = 0; | ||
709 | if (x->x_templatesym != &s_float) | ||
710 | { | ||
711 | pd_error(x, "sorry, you can only save 'float' arrays now"); | ||
712 | return; | ||
713 | } | ||
714 | if (n > 200000) | ||
715 | post("warning: I'm saving an array with %d points!\n", n); | ||
716 | while (n2 < n) | ||
717 | { | ||
718 | int chunk = n - n2, i; | ||
719 | if (chunk > ARRAYWRITECHUNKSIZE) | ||
720 | chunk = ARRAYWRITECHUNKSIZE; | ||
721 | binbuf_addv(b, "si", gensym("#A"), n2); | ||
722 | for (i = 0; i < chunk; i++) | ||
723 | binbuf_addv(b, "f", fixtof(((t_sample *)(x->x_vec))[n2+i])); | ||
724 | binbuf_addv(b, ";"); | ||
725 | n2 += chunk; | ||
726 | } | ||
727 | } | ||
728 | } | ||
729 | |||
730 | t_widgetbehavior garray_widgetbehavior = | ||
731 | { | ||
732 | garray_getrect, | ||
733 | garray_displace, | ||
734 | garray_select, | ||
735 | garray_activate, | ||
736 | garray_delete, | ||
737 | garray_vis, | ||
738 | garray_click | ||
739 | }; | ||
740 | |||
741 | /* ----------------------- public functions -------------------- */ | ||
742 | |||
743 | void garray_usedindsp(t_garray *x) | ||
744 | { | ||
745 | x->x_usedindsp = 1; | ||
746 | } | ||
747 | |||
748 | void garray_redraw(t_garray *x) | ||
749 | { | ||
750 | if (glist_isvisible(x->x_glist)) | ||
751 | { | ||
752 | garray_vis(&x->x_gobj, x->x_glist, 0); | ||
753 | garray_vis(&x->x_gobj, x->x_glist, 1); | ||
754 | } | ||
755 | } | ||
756 | |||
757 | /* This functiopn gets the template of an array; if we can't figure | ||
758 | out what template an array's elements belong to we're in grave trouble | ||
759 | when it's time to free or resize it. */ | ||
760 | t_template *garray_template(t_garray *x) | ||
761 | { | ||
762 | t_template *template = template_findbyname(x->x_templatesym); | ||
763 | if (!template) | ||
764 | bug("garray_template"); | ||
765 | return (template); | ||
766 | } | ||
767 | |||
768 | int garray_npoints(t_garray *x) /* get the length */ | ||
769 | { | ||
770 | return (x->x_n); | ||
771 | } | ||
772 | |||
773 | char *garray_vec(t_garray *x) /* get the contents */ | ||
774 | { | ||
775 | return ((char *)(x->x_vec)); | ||
776 | } | ||
777 | |||
778 | /* routine that checks if we're just an array of floats and if | ||
779 | so returns the goods */ | ||
780 | |||
781 | int garray_getfloatarray(t_garray *x, int *size, t_sample **vec) | ||
782 | { | ||
783 | t_template *template = garray_template(x); | ||
784 | int yonset, type; | ||
785 | t_symbol *arraytype; | ||
786 | if (!template_find_field(template, gensym("y"), &yonset, | ||
787 | &type, &arraytype) || type != DT_FLOAT) | ||
788 | error("%s: needs floating-point 'y' field", | ||
789 | x->x_templatesym->s_name); | ||
790 | else if (template->t_n != 1) | ||
791 | error("%s: has more than one field", x->x_templatesym->s_name); | ||
792 | else | ||
793 | { | ||
794 | *size = garray_npoints(x); | ||
795 | *vec = (t_sample *)garray_vec(x); | ||
796 | return (1); | ||
797 | } | ||
798 | return (0); | ||
799 | } | ||
800 | |||
801 | /* get any floating-point field of any element of an array */ | ||
802 | float garray_get(t_garray *x, t_symbol *s, t_int indx) | ||
803 | { | ||
804 | t_template *template = garray_template(x); | ||
805 | int yonset, type; | ||
806 | t_symbol *arraytype; | ||
807 | if (!template_find_field(template, gensym("y"), &yonset, | ||
808 | &type, &arraytype) || type != DT_FLOAT) | ||
809 | { | ||
810 | error("%s: needs floating-point '%s' field", x->x_templatesym->s_name, | ||
811 | s->s_name); | ||
812 | return (0); | ||
813 | } | ||
814 | if (indx < 0) indx = 0; | ||
815 | else if (indx >= x->x_n) indx = x->x_n - 1; | ||
816 | return (*(float *)((x->x_vec + sizeof(t_word) * indx) + yonset)); | ||
817 | } | ||
818 | |||
819 | /* set the "saveit" flag */ | ||
820 | void garray_setsaveit(t_garray *x, int saveit) | ||
821 | { | ||
822 | if (x->x_saveit && !saveit) | ||
823 | post("warning: array %s: clearing save-in-patch flag", | ||
824 | x->x_name->s_name); | ||
825 | x->x_saveit = saveit; | ||
826 | } | ||
827 | |||
828 | /*------------------- Pd messages ------------------------ */ | ||
829 | static void garray_const(t_garray *x, t_floatarg g) | ||
830 | { | ||
831 | t_template *template = garray_template(x); | ||
832 | int yonset, type, i; | ||
833 | t_symbol *arraytype; | ||
834 | if (!template_find_field(template, gensym("y"), &yonset, | ||
835 | &type, &arraytype) || type != DT_FLOAT) | ||
836 | error("%s: needs floating-point 'y' field", | ||
837 | x->x_templatesym->s_name); | ||
838 | else for (i = 0; i < x->x_n; i++) | ||
839 | *(float *)(((char *)x->x_vec + sizeof(t_word) * i) + yonset) = g; | ||
840 | garray_redraw(x); | ||
841 | } | ||
842 | |||
843 | /* sum of Fourier components; called from routines below */ | ||
844 | static void garray_dofo(t_garray *x, int npoints, float dcval, | ||
845 | int nsin, t_float *vsin, int sineflag) | ||
846 | { | ||
847 | t_template *template = garray_template(x); | ||
848 | int yonset, type, i, j; | ||
849 | t_symbol *arraytype; | ||
850 | double phase, phaseincr, fj; | ||
851 | if (npoints == 0) | ||
852 | npoints = 512; /* dunno what a good default would be... */ | ||
853 | if (npoints != (1 << ilog2(npoints))) | ||
854 | post("%s: rounnding to %d points", x->x_templatesym->s_name, | ||
855 | (npoints = (1<<ilog2(npoints)))); | ||
856 | garray_resize(x, npoints + 3); | ||
857 | phaseincr = 2. * 3.14159 / npoints; | ||
858 | if (!template_find_field(template, gensym("y"), &yonset, | ||
859 | &type, &arraytype) || type != DT_FLOAT) | ||
860 | { | ||
861 | error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); | ||
862 | return; | ||
863 | } | ||
864 | for (i = 0, phase = -phaseincr; i < x->x_n; i++, phase += phaseincr ) | ||
865 | { | ||
866 | double sum = dcval; | ||
867 | if (sineflag) | ||
868 | for (j = 0, fj = phase; j < nsin; j++, fj += phase) | ||
869 | sum += vsin[j] * sin(fj); | ||
870 | else | ||
871 | for (j = 0, fj = 0; j < nsin; j++, fj += phase) | ||
872 | sum += vsin[j] * cos(fj); | ||
873 | *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) = sum; | ||
874 | } | ||
875 | garray_redraw(x); | ||
876 | } | ||
877 | |||
878 | static void garray_sinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) | ||
879 | { | ||
880 | t_template *template = garray_template(x); | ||
881 | |||
882 | t_float *svec = (t_float *)t_getbytes(sizeof(t_float) * argc); | ||
883 | int npoints, i; | ||
884 | if (argc < 2) | ||
885 | { | ||
886 | error("sinesum: %s: need number of points and partial strengths", | ||
887 | x->x_templatesym->s_name); | ||
888 | return; | ||
889 | } | ||
890 | |||
891 | npoints = atom_getfloatarg(0, argc, argv); | ||
892 | argv++, argc--; | ||
893 | |||
894 | svec = (t_float *)t_getbytes(sizeof(t_float) * argc); | ||
895 | if (!svec) return; | ||
896 | |||
897 | for (i = 0; i < argc; i++) | ||
898 | svec[i] = atom_getfloatarg(i, argc, argv); | ||
899 | garray_dofo(x, npoints, 0, argc, svec, 1); | ||
900 | t_freebytes(svec, sizeof(t_float) * argc); | ||
901 | } | ||
902 | |||
903 | static void garray_cosinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) | ||
904 | { | ||
905 | t_template *template = garray_template(x); | ||
906 | |||
907 | t_float *svec = (t_float *)t_getbytes(sizeof(t_float) * argc); | ||
908 | int npoints, i; | ||
909 | if (argc < 2) | ||
910 | { | ||
911 | error("sinesum: %s: need number of points and partial strengths", | ||
912 | x->x_templatesym->s_name); | ||
913 | return; | ||
914 | } | ||
915 | |||
916 | npoints = atom_getfloatarg(0, argc, argv); | ||
917 | argv++, argc--; | ||
918 | |||
919 | svec = (t_float *)t_getbytes(sizeof(t_float) * argc); | ||
920 | if (!svec) return; | ||
921 | |||
922 | for (i = 0; i < argc; i++) | ||
923 | svec[i] = atom_getfloatarg(i, argc, argv); | ||
924 | garray_dofo(x, npoints, 0, argc, svec, 0); | ||
925 | t_freebytes(svec, sizeof(t_float) * argc); | ||
926 | } | ||
927 | |||
928 | static void garray_normalize(t_garray *x, t_float f) | ||
929 | { | ||
930 | t_template *template = garray_template(x); | ||
931 | int yonset, type, npoints, i; | ||
932 | double maxv, renormer; | ||
933 | t_symbol *arraytype; | ||
934 | |||
935 | if (f <= 0) | ||
936 | f = 1; | ||
937 | |||
938 | if (!template_find_field(template, gensym("y"), &yonset, | ||
939 | &type, &arraytype) || type != DT_FLOAT) | ||
940 | { | ||
941 | error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); | ||
942 | return; | ||
943 | } | ||
944 | for (i = 0, maxv = 0; i < x->x_n; i++) | ||
945 | { | ||
946 | double v = *(float *)((x->x_vec + sizeof(t_word) * i) + yonset); | ||
947 | if (v > maxv) | ||
948 | maxv = v; | ||
949 | if (-v > maxv) | ||
950 | maxv = -v; | ||
951 | } | ||
952 | if (maxv >= 0) | ||
953 | { | ||
954 | renormer = f / maxv; | ||
955 | for (i = 0; i < x->x_n; i++) | ||
956 | { | ||
957 | *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) | ||
958 | *= renormer; | ||
959 | } | ||
960 | } | ||
961 | garray_redraw(x); | ||
962 | } | ||
963 | |||
964 | /* list -- the first value is an index; subsequent values are put in | ||
965 | the "y" slot of the array. This generalizes Max's "table", sort of. */ | ||
966 | static void garray_list(t_garray *x, t_symbol *s, int argc, t_atom *argv) | ||
967 | { | ||
968 | t_template *template = garray_template(x); | ||
969 | int yonset, type, i; | ||
970 | t_symbol *arraytype; | ||
971 | if (!template_find_field(template, gensym("y"), &yonset, | ||
972 | &type, &arraytype) || type != DT_FLOAT) | ||
973 | error("%s: needs floating-point 'y' field", | ||
974 | x->x_templatesym->s_name); | ||
975 | else if (argc < 2) return; | ||
976 | else | ||
977 | { | ||
978 | int firstindex = atom_getfloat(argv); | ||
979 | argc--; | ||
980 | argv++; | ||
981 | /* drop negative x values */ | ||
982 | if (firstindex < 0) | ||
983 | { | ||
984 | argc += firstindex; | ||
985 | argv -= firstindex; | ||
986 | firstindex = 0; | ||
987 | if (argc <= 0) return; | ||
988 | } | ||
989 | if (argc + firstindex > x->x_n) | ||
990 | { | ||
991 | argc = x->x_n - firstindex; | ||
992 | if (argc <= 0) return; | ||
993 | } | ||
994 | for (i = 0; i < argc; i++) | ||
995 | *(t_sample *)((x->x_vec + sizeof(t_word) * (i + firstindex)) + yonset) = | ||
996 | ftofix(atom_getfloat(argv + i)); | ||
997 | } | ||
998 | garray_redraw(x); | ||
999 | } | ||
1000 | |||
1001 | /* forward a "bounds" message to the owning graph */ | ||
1002 | static void garray_bounds(t_garray *x, t_floatarg x1, t_floatarg y1, | ||
1003 | t_floatarg x2, t_floatarg y2) | ||
1004 | { | ||
1005 | vmess(&x->x_glist->gl_pd, gensym("bounds"), "ffff", x1, y1, x2, y2); | ||
1006 | } | ||
1007 | |||
1008 | /* same for "xticks", etc */ | ||
1009 | static void garray_xticks(t_garray *x, | ||
1010 | t_floatarg point, t_floatarg inc, t_floatarg f) | ||
1011 | { | ||
1012 | vmess(&x->x_glist->gl_pd, gensym("xticks"), "fff", point, inc, f); | ||
1013 | } | ||
1014 | |||
1015 | static void garray_yticks(t_garray *x, | ||
1016 | t_floatarg point, t_floatarg inc, t_floatarg f) | ||
1017 | { | ||
1018 | vmess(&x->x_glist->gl_pd, gensym("yticks"), "fff", point, inc, f); | ||
1019 | } | ||
1020 | |||
1021 | static void garray_xlabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) | ||
1022 | { | ||
1023 | typedmess(&x->x_glist->gl_pd, s, argc, argv); | ||
1024 | } | ||
1025 | |||
1026 | static void garray_ylabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) | ||
1027 | { | ||
1028 | typedmess(&x->x_glist->gl_pd, s, argc, argv); | ||
1029 | } | ||
1030 | /* change the name of a garray. */ | ||
1031 | static void garray_rename(t_garray *x, t_symbol *s) | ||
1032 | { | ||
1033 | pd_unbind(&x->x_gobj.g_pd, x->x_realname); | ||
1034 | pd_bind(&x->x_gobj.g_pd, x->x_realname = x->x_name = s); | ||
1035 | garray_redraw(x); | ||
1036 | } | ||
1037 | |||
1038 | static void garray_read(t_garray *x, t_symbol *filename) | ||
1039 | { | ||
1040 | int nelem = x->x_n, filedesc; | ||
1041 | FILE *fd; | ||
1042 | char buf[MAXPDSTRING], *bufptr; | ||
1043 | t_template *template = garray_template(x); | ||
1044 | int yonset, type, i; | ||
1045 | t_symbol *arraytype; | ||
1046 | if (!template_find_field(template, gensym("y"), &yonset, | ||
1047 | &type, &arraytype) || type != DT_FLOAT) | ||
1048 | { | ||
1049 | error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); | ||
1050 | return; | ||
1051 | } | ||
1052 | if ((filedesc = open_via_path( | ||
1053 | canvas_getdir(glist_getcanvas(x->x_glist))->s_name, | ||
1054 | filename->s_name, "", buf, &bufptr, MAXPDSTRING, 0)) < 0 | ||
1055 | || !(fd = fdopen(filedesc, "r"))) | ||
1056 | { | ||
1057 | error("%s: can't open", filename->s_name); | ||
1058 | return; | ||
1059 | } | ||
1060 | for (i = 0; i < nelem; i++) | ||
1061 | { | ||
1062 | if (!fscanf(fd, "%f", (float *)((x->x_vec + sizeof(t_word) * i) + | ||
1063 | yonset))) | ||
1064 | { | ||
1065 | post("%s: read %d elements into table of size %d", | ||
1066 | filename->s_name, i, nelem); | ||
1067 | break; | ||
1068 | } | ||
1069 | } | ||
1070 | while (i < nelem) | ||
1071 | *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) = 0, i++; | ||
1072 | fclose(fd); | ||
1073 | garray_redraw(x); | ||
1074 | } | ||
1075 | |||
1076 | /* this should be renamed and moved... */ | ||
1077 | int garray_ambigendian(void) | ||
1078 | { | ||
1079 | unsigned short s = 1; | ||
1080 | unsigned char c = *(char *)(&s); | ||
1081 | return (c==0); | ||
1082 | } | ||
1083 | |||
1084 | #define BINREADMODE "rb" | ||
1085 | #define BINWRITEMODE "wb" | ||
1086 | |||
1087 | static void garray_read16(t_garray *x, t_symbol *filename, | ||
1088 | t_symbol *endian, t_floatarg fskip) | ||
1089 | { | ||
1090 | int skip = fskip, filedesc; | ||
1091 | int i, nelem; | ||
1092 | t_sample *vec; | ||
1093 | FILE *fd; | ||
1094 | char buf[MAXPDSTRING], *bufptr; | ||
1095 | short s; | ||
1096 | int cpubig = garray_ambigendian(), swap = 0; | ||
1097 | char c = endian->s_name[0]; | ||
1098 | if (c == 'b') | ||
1099 | { | ||
1100 | if (!cpubig) swap = 1; | ||
1101 | } | ||
1102 | else if (c == 'l') | ||
1103 | { | ||
1104 | if (cpubig) swap = 1; | ||
1105 | } | ||
1106 | else if (c) | ||
1107 | { | ||
1108 | error("array_read16: endianness is 'l' (low byte first ala INTEL)"); | ||
1109 | post("... or 'b' (high byte first ala MIPS,DEC,PPC)"); | ||
1110 | } | ||
1111 | if (!garray_getfloatarray(x, &nelem, &vec)) | ||
1112 | { | ||
1113 | error("%s: not a float array", x->x_templatesym->s_name); | ||
1114 | return; | ||
1115 | } | ||
1116 | if ((filedesc = open_via_path( | ||
1117 | canvas_getdir(glist_getcanvas(x->x_glist))->s_name, | ||
1118 | filename->s_name, "", buf, &bufptr, MAXPDSTRING, 1)) < 0 | ||
1119 | || !(fd = fdopen(filedesc, BINREADMODE))) | ||
1120 | { | ||
1121 | error("%s: can't open", filename->s_name); | ||
1122 | return; | ||
1123 | } | ||
1124 | if (skip) | ||
1125 | { | ||
1126 | long pos = fseek(fd, (long)skip, SEEK_SET); | ||
1127 | if (pos < 0) | ||
1128 | { | ||
1129 | error("%s: can't seek to byte %d", buf, skip); | ||
1130 | fclose(fd); | ||
1131 | return; | ||
1132 | } | ||
1133 | } | ||
1134 | |||
1135 | for (i = 0; i < nelem; i++) | ||
1136 | { | ||
1137 | if (fread(&s, sizeof(s), 1, fd) < 1) | ||
1138 | { | ||
1139 | post("%s: read %d elements into table of size %d", | ||
1140 | filename->s_name, i, nelem); | ||
1141 | break; | ||
1142 | } | ||
1143 | if (swap) s = ((s & 0xff) << 8) | ((s & 0xff00) >> 8); | ||
1144 | vec[i] = s * (1./32768.); | ||
1145 | } | ||
1146 | while (i < nelem) vec[i++] = 0; | ||
1147 | fclose(fd); | ||
1148 | garray_redraw(x); | ||
1149 | } | ||
1150 | |||
1151 | static void garray_write(t_garray *x, t_symbol *filename) | ||
1152 | { | ||
1153 | FILE *fd; | ||
1154 | char buf[MAXPDSTRING]; | ||
1155 | t_template *template = garray_template(x); | ||
1156 | int yonset, type, i; | ||
1157 | t_symbol *arraytype; | ||
1158 | if (!template_find_field(template, gensym("y"), &yonset, | ||
1159 | &type, &arraytype) || type != DT_FLOAT) | ||
1160 | { | ||
1161 | error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); | ||
1162 | return; | ||
1163 | } | ||
1164 | canvas_makefilename(glist_getcanvas(x->x_glist), filename->s_name, | ||
1165 | buf, MAXPDSTRING); | ||
1166 | sys_bashfilename(buf, buf); | ||
1167 | if (!(fd = fopen(buf, "w"))) | ||
1168 | { | ||
1169 | error("%s: can't create", buf); | ||
1170 | return; | ||
1171 | } | ||
1172 | for (i = 0; i < x->x_n; i++) | ||
1173 | { | ||
1174 | if (fprintf(fd, "%g\n", | ||
1175 | *(float *)((x->x_vec + sizeof(t_word) * i) + yonset)) < 1) | ||
1176 | { | ||
1177 | post("%s: write error", filename->s_name); | ||
1178 | break; | ||
1179 | } | ||
1180 | } | ||
1181 | fclose(fd); | ||
1182 | } | ||
1183 | |||
1184 | static unsigned char waveheader[] = { | ||
1185 | 0x52, 0x49, 0x46, 0x46, | ||
1186 | 0x00, 0x00, 0x00, 0x00, | ||
1187 | 0x57, 0x41, 0x56, 0x45, | ||
1188 | 0x66, 0x6d, 0x74, 0x20, | ||
1189 | |||
1190 | 0x10, 0x00, 0x00, 0x00, | ||
1191 | 0x01, 0x00, 0x01, 0x00, | ||
1192 | 0x44, 0xac, 0x00, 0x00, | ||
1193 | 0x88, 0x58, 0x01, 0x00, | ||
1194 | |||
1195 | 0x02, 0x00, 0x10, 0x00, | ||
1196 | 0x64, 0x61, 0x74, 0x61, | ||
1197 | 0x00, 0x00, 0x00, 0x00, | ||
1198 | }; | ||
1199 | |||
1200 | /* wave format only so far */ | ||
1201 | static void garray_write16(t_garray *x, t_symbol *filename, t_symbol *format) | ||
1202 | { | ||
1203 | t_template *template = garray_template(x); | ||
1204 | int yonset, type, i; | ||
1205 | t_symbol *arraytype; | ||
1206 | FILE *fd; | ||
1207 | int aiff = (format == gensym("aiff")); | ||
1208 | char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING]; | ||
1209 | int swap = garray_ambigendian(); /* wave is only little endian */ | ||
1210 | int intbuf; | ||
1211 | strncpy(filenamebuf, filename->s_name, MAXPDSTRING-10); | ||
1212 | filenamebuf[MAXPDSTRING-10] = 0; | ||
1213 | if (sizeof(int) != 4) post("write16: only works on 32-bit machines"); | ||
1214 | if (aiff) | ||
1215 | { | ||
1216 | if (strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff")) | ||
1217 | strcat(filenamebuf, ".aiff"); | ||
1218 | } | ||
1219 | else | ||
1220 | { | ||
1221 | if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav")) | ||
1222 | strcat(filenamebuf, ".wav"); | ||
1223 | } | ||
1224 | if (!template_find_field(template, gensym("y"), &yonset, | ||
1225 | &type, &arraytype) || type != DT_FLOAT) | ||
1226 | { | ||
1227 | error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); | ||
1228 | return; | ||
1229 | } | ||
1230 | canvas_makefilename(glist_getcanvas(x->x_glist), filenamebuf, | ||
1231 | buf2, MAXPDSTRING); | ||
1232 | sys_bashfilename(buf2, buf2); | ||
1233 | if (!(fd = fopen(buf2, BINWRITEMODE))) | ||
1234 | { | ||
1235 | error("%s: can't create", buf2); | ||
1236 | return; | ||
1237 | } | ||
1238 | intbuf = 2 * x->x_n + 36; | ||
1239 | if (swap) | ||
1240 | { | ||
1241 | unsigned char *foo = (unsigned char *)&intbuf, xxx; | ||
1242 | xxx = foo[0]; foo[0] = foo[3]; foo[3] = xxx; | ||
1243 | xxx = foo[1]; foo[1] = foo[2]; foo[2] = xxx; | ||
1244 | } | ||
1245 | memcpy((void *)(waveheader + 4), (void *)(&intbuf), 4); | ||
1246 | intbuf = 2 * x->x_n; | ||
1247 | if (swap) | ||
1248 | { | ||
1249 | unsigned char *foo = (unsigned char *)&intbuf, xxx; | ||
1250 | xxx = foo[0]; foo[0] = foo[3]; foo[3] = xxx; | ||
1251 | xxx = foo[1]; foo[1] = foo[2]; foo[2] = xxx; | ||
1252 | } | ||
1253 | memcpy((void *)(waveheader + 40), (void *)(&intbuf), 4); | ||
1254 | if (fwrite(waveheader, sizeof(waveheader), 1, fd) < 1) | ||
1255 | { | ||
1256 | post("%s: write error", buf2); | ||
1257 | goto closeit; | ||
1258 | } | ||
1259 | for (i = 0; i < x->x_n; i++) | ||
1260 | { | ||
1261 | float f = 32767. * *(float *)((x->x_vec + sizeof(t_word) * i) + yonset); | ||
1262 | short sh; | ||
1263 | if (f < -32768) f = -32768; | ||
1264 | else if (f > 32767) f = 32767; | ||
1265 | sh = f; | ||
1266 | if (swap) | ||
1267 | { | ||
1268 | unsigned char *foo = (unsigned char *)&sh, xxx; | ||
1269 | xxx = foo[0]; foo[0] = foo[1]; foo[1] = xxx; | ||
1270 | } | ||
1271 | if (fwrite(&sh, sizeof(sh), 1, fd) < 1) | ||
1272 | { | ||
1273 | post("%s: write error", buf2); | ||
1274 | goto closeit; | ||
1275 | } | ||
1276 | } | ||
1277 | closeit: | ||
1278 | fclose(fd); | ||
1279 | } | ||
1280 | |||
1281 | void garray_resize(t_garray *x, t_floatarg f) | ||
1282 | { | ||
1283 | int was = x->x_n, elemsize; | ||
1284 | t_glist *gl; | ||
1285 | int dspwas; | ||
1286 | int n = f; | ||
1287 | char *nvec; | ||
1288 | |||
1289 | if (n < 1) n = 1; | ||
1290 | elemsize = template_findbyname(x->x_templatesym)->t_n * sizeof(t_word); | ||
1291 | nvec = t_resizebytes(x->x_vec, was * elemsize, n * elemsize); | ||
1292 | if (!nvec) | ||
1293 | { | ||
1294 | pd_error(x, "array resize failed: out of memory"); | ||
1295 | return; | ||
1296 | } | ||
1297 | x->x_vec = nvec; | ||
1298 | /* LATER should check t_resizebytes result */ | ||
1299 | if (n > was) | ||
1300 | memset(x->x_vec + was*elemsize, | ||
1301 | 0, (n - was) * elemsize); | ||
1302 | x->x_n = n; | ||
1303 | |||
1304 | /* if this is the only array in the graph, | ||
1305 | reset the graph's coordinates */ | ||
1306 | gl = x->x_glist; | ||
1307 | if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next) | ||
1308 | { | ||
1309 | vmess(&gl->gl_pd, gensym("bounds"), "ffff", | ||
1310 | 0., gl->gl_y1, (double)(n > 1 ? n-1 : 1), gl->gl_y2); | ||
1311 | /* close any dialogs that might have the wrong info now... */ | ||
1312 | gfxstub_deleteforkey(gl); | ||
1313 | } | ||
1314 | else garray_redraw(x); | ||
1315 | if (x->x_usedindsp) canvas_update_dsp(); | ||
1316 | } | ||
1317 | |||
1318 | static void garray_print(t_garray *x) | ||
1319 | { | ||
1320 | post("garray %s: template %s, length %d", | ||
1321 | x->x_name->s_name, x->x_templatesym->s_name, x->x_n); | ||
1322 | } | ||
1323 | |||
1324 | void g_array_setup(void) | ||
1325 | { | ||
1326 | garray_class = class_new(gensym("array"), 0, (t_method)garray_free, | ||
1327 | sizeof(t_garray), CLASS_GOBJ, 0); | ||
1328 | class_setwidget(garray_class, &garray_widgetbehavior); | ||
1329 | class_addmethod(garray_class, (t_method)garray_const, gensym("const"), | ||
1330 | A_DEFFLOAT, A_NULL); | ||
1331 | class_addlist(garray_class, garray_list); | ||
1332 | class_addmethod(garray_class, (t_method)garray_bounds, gensym("bounds"), | ||
1333 | A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); | ||
1334 | class_addmethod(garray_class, (t_method)garray_xticks, gensym("xticks"), | ||
1335 | A_FLOAT, A_FLOAT, A_FLOAT, 0); | ||
1336 | class_addmethod(garray_class, (t_method)garray_xlabel, gensym("xlabel"), | ||
1337 | A_GIMME, 0); | ||
1338 | class_addmethod(garray_class, (t_method)garray_yticks, gensym("yticks"), | ||
1339 | A_FLOAT, A_FLOAT, A_FLOAT, 0); | ||
1340 | class_addmethod(garray_class, (t_method)garray_ylabel, gensym("ylabel"), | ||
1341 | A_GIMME, 0); | ||
1342 | class_addmethod(garray_class, (t_method)garray_rename, gensym("rename"), | ||
1343 | A_SYMBOL, 0); | ||
1344 | class_addmethod(garray_class, (t_method)garray_read, gensym("read"), | ||
1345 | A_SYMBOL, A_NULL); | ||
1346 | class_addmethod(garray_class, (t_method)garray_read16, gensym("read16"), | ||
1347 | A_SYMBOL, A_DEFFLOAT, A_DEFSYM, A_NULL); | ||
1348 | class_addmethod(garray_class, (t_method)garray_write, gensym("write"), | ||
1349 | A_SYMBOL, A_NULL); | ||
1350 | class_addmethod(garray_class, (t_method)garray_write16, gensym("write16"), | ||
1351 | A_SYMBOL, A_DEFSYM, A_NULL); | ||
1352 | class_addmethod(garray_class, (t_method)garray_resize, gensym("resize"), | ||
1353 | A_FLOAT, A_NULL); | ||
1354 | class_addmethod(garray_class, (t_method)garray_print, gensym("print"), | ||
1355 | A_NULL); | ||
1356 | class_addmethod(garray_class, (t_method)garray_sinesum, gensym("sinesum"), | ||
1357 | A_GIMME, 0); | ||
1358 | class_addmethod(garray_class, (t_method)garray_cosinesum, | ||
1359 | gensym("cosinesum"), A_GIMME, 0); | ||
1360 | class_addmethod(garray_class, (t_method)garray_normalize, | ||
1361 | gensym("normalize"), A_DEFFLOAT, 0); | ||
1362 | class_addmethod(garray_class, (t_method)garray_arraydialog, | ||
1363 | gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); | ||
1364 | class_setsavefn(garray_class, garray_save); | ||
1365 | } | ||
1366 | |||
1367 | |||
1368 | /* Copyright (c) 1997-1999 Miller Puckette. | ||
1369 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL | ||
1370 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ | ||
1371 | |||
1372 | #include <stdlib.h> | ||
1373 | #include <string.h> | ||
1374 | #include <stdio.h> /* for read/write to files */ | ||
1375 | #include "m_pd.h" | ||
1376 | #include "g_canvas.h" | ||
1377 | #include <math.h> | ||
1378 | |||
1379 | /* see also the "plot" object in g_scalar.c which deals with graphing | ||
1380 | arrays which are fields in scalars. Someday we should unify the | ||
1381 | two, but how? */ | ||
1382 | |||
1383 | /* aux routine to bash leading '#' to '$' for dialogs in u_main.tk | ||
1384 | which can't send symbols starting with '$' (because the Pd message | ||
1385 | interpreter would change them!) */ | ||
1386 | |||
1387 | static t_symbol *sharptodollar(t_symbol *s) | ||
1388 | { | ||
1389 | if (*s->s_name == '#') | ||
1390 | { | ||
1391 | char buf[MAXPDSTRING]; | ||
1392 | strncpy(buf, s->s_name, MAXPDSTRING); | ||
1393 | buf[MAXPDSTRING-1] = 0; | ||
1394 | buf[0] = '$'; | ||
1395 | return (gensym(buf)); | ||
1396 | } | ||
1397 | else return (s); | ||
1398 | } | ||
1399 | |||
1400 | /* --------- "pure" arrays with scalars for elements. --------------- */ | ||
1401 | |||
1402 | /* Pure arrays have no a priori graphical capabilities. | ||
1403 | They are instantiated by "garrays" below or can be elements of other | ||
1404 | scalars (g_scalar.c); their graphical behavior is defined accordingly. */ | ||
1405 | |||
1406 | t_array *array_new(t_symbol *templatesym, t_gpointer *parent) | ||
1407 | { | ||
1408 | t_array *x = (t_array *)getbytes(sizeof (*x)); | ||
1409 | t_template *template; | ||
1410 | t_gpointer *gp; | ||
1411 | template = template_findbyname(templatesym); | ||
1412 | x->a_templatesym = templatesym; | ||
1413 | x->a_n = 1; | ||
1414 | x->a_elemsize = sizeof(t_word) * template->t_n; | ||
1415 | x->a_vec = (char *)getbytes(x->a_elemsize); | ||
1416 | /* note here we blithely copy a gpointer instead of "setting" a | ||
1417 | new one; this gpointer isn't accounted for and needn't be since | ||
1418 | we'll be deleted before the thing pointed to gets deleted anyway; | ||
1419 | see array_free. */ | ||
1420 | x->a_gp = *parent; | ||
1421 | x->a_stub = gstub_new(0, x); | ||
1422 | word_init((t_word *)(x->a_vec), template, parent); | ||
1423 | return (x); | ||
1424 | } | ||
1425 | |||
1426 | void array_resize(t_array *x, t_template *template, int n) | ||
1427 | { | ||
1428 | int elemsize, oldn; | ||
1429 | t_gpointer *gp; | ||
1430 | |||
1431 | if (n < 1) | ||
1432 | n = 1; | ||
1433 | oldn = x->a_n; | ||
1434 | elemsize = sizeof(t_word) * template->t_n; | ||
1435 | |||
1436 | x->a_vec = (char *)resizebytes(x->a_vec, oldn * elemsize, | ||
1437 | n * elemsize); | ||
1438 | x->a_n = n; | ||
1439 | if (n > oldn) | ||
1440 | { | ||
1441 | char *cp = x->a_vec + elemsize * oldn; | ||
1442 | int i = n - oldn; | ||
1443 | for (; i--; cp += elemsize) | ||
1444 | { | ||
1445 | t_word *wp = (t_word *)cp; | ||
1446 | word_init(wp, template, &x->a_gp); | ||
1447 | } | ||
1448 | } | ||
1449 | } | ||
1450 | |||
1451 | void word_free(t_word *wp, t_template *template); | ||
1452 | |||
1453 | void array_free(t_array *x) | ||
1454 | { | ||
1455 | int i; | ||
1456 | t_template *scalartemplate = template_findbyname(x->a_templatesym); | ||
1457 | /* we don't unset our gpointer here since it was never "set." */ | ||
1458 | /* gpointer_unset(&x->a_gp); */ | ||
1459 | gstub_cutoff(x->a_stub); | ||
1460 | for (i = 0; i < x->a_n; i++) | ||
1461 | { | ||
1462 | t_word *wp = (t_word *)(x->a_vec + x->a_elemsize * i); | ||
1463 | word_free(wp, scalartemplate); | ||
1464 | } | ||
1465 | freebytes(x->a_vec, x->a_elemsize * x->a_n); | ||
1466 | freebytes(x, sizeof *x); | ||
1467 | } | ||
1468 | |||
1469 | /* --------------------- graphical arrays (garrays) ------------------- */ | ||
1470 | |||
1471 | t_class *garray_class; | ||
1472 | static int gcount = 0; | ||
1473 | |||
1474 | struct _garray | ||
1475 | { | ||
1476 | t_gobj x_gobj; | ||
1477 | t_glist *x_glist; | ||
1478 | t_array x_array; /* actual array; note only 4 fields used as below */ | ||
1479 | t_symbol *x_name; | ||
1480 | t_symbol *x_realname; /* name with "$" expanded */ | ||
1481 | t_float x_firstx; /* X value of first item */ | ||
1482 | t_float x_xinc; /* X increment */ | ||
1483 | char x_usedindsp; /* true if some DSP routine is using this */ | ||
1484 | char x_saveit; /* true if we should save this with parent */ | ||
1485 | }; | ||
1486 | |||
1487 | /* macros to get into the "array" structure */ | ||
1488 | #define x_n x_array.a_n | ||
1489 | #define x_elemsize x_array.a_elemsize | ||
1490 | #define x_vec x_array.a_vec | ||
1491 | #define x_templatesym x_array.a_templatesym | ||
1492 | |||
1493 | t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *templatesym, | ||
1494 | t_floatarg f, t_floatarg saveit) | ||
1495 | { | ||
1496 | int n = f, i; | ||
1497 | int zz, nwords; | ||
1498 | t_garray *x; | ||
1499 | t_pd *x2; | ||
1500 | t_template *template; | ||
1501 | char *str; | ||
1502 | if (s == &s_) | ||
1503 | { | ||
1504 | char buf[40]; | ||
1505 | sprintf(buf, "array%d", ++gcount); | ||
1506 | s = gensym(buf); | ||
1507 | templatesym = &s_float; | ||
1508 | n = 100; | ||
1509 | } | ||
1510 | else if (!strncmp((str = s->s_name), "array", 5) | ||
1511 | && (zz = atoi(str + 5)) > gcount) gcount = zz; | ||
1512 | template = template_findbyname(templatesym); | ||
1513 | if (!template) | ||
1514 | { | ||
1515 | error("array: couldn't find template %s", templatesym->s_name); | ||
1516 | return (0); | ||
1517 | } | ||
1518 | nwords = template->t_n; | ||
1519 | for (i = 0; i < nwords; i++) | ||
1520 | { | ||
1521 | /* we can't have array or list elements yet because what scalar | ||
1522 | can act as their "parent"??? */ | ||
1523 | if (template->t_vec[i].ds_type == DT_ARRAY | ||
1524 | || template->t_vec[i].ds_type == DT_LIST) | ||
1525 | { | ||
1526 | error("array: template %s can't have sublists or arrays", | ||
1527 | templatesym->s_name); | ||
1528 | return (0); | ||
1529 | } | ||
1530 | } | ||
1531 | x = (t_garray *)pd_new(garray_class); | ||
1532 | |||
1533 | if (n <= 0) n = 100; | ||
1534 | x->x_n = n; | ||
1535 | x->x_elemsize = nwords * sizeof(t_word); | ||
1536 | x->x_vec = getbytes(x->x_n * x->x_elemsize); | ||
1537 | memset(x->x_vec, 0, x->x_n * x->x_elemsize); | ||
1538 | /* LATER should check that malloc */ | ||
1539 | x->x_name = s; | ||
1540 | x->x_realname = canvas_realizedollar(gl, s); | ||
1541 | pd_bind(&x->x_gobj.g_pd, x->x_realname); | ||
1542 | x->x_templatesym = templatesym; | ||
1543 | x->x_firstx = 0; | ||
1544 | x->x_xinc = 1; /* LATER make methods to set this... */ | ||
1545 | glist_add(gl, &x->x_gobj); | ||
1546 | x->x_glist = gl; | ||
1547 | x->x_usedindsp = 0; | ||
1548 | x->x_saveit = (saveit != 0); | ||
1549 | if (x2 = pd_findbyclass(gensym("#A"), garray_class)) | ||
1550 | pd_unbind(x2, gensym("#A")); | ||
1551 | |||
1552 | pd_bind(&x->x_gobj.g_pd, gensym("#A")); | ||
1553 | |||
1554 | return (x); | ||
1555 | } | ||
1556 | |||
1557 | /* called from array menu item to create a new one */ | ||
1558 | void canvas_menuarray(t_glist *canvas) | ||
1559 | { | ||
1560 | t_glist *x = (t_glist *)canvas; | ||
1561 | char cmdbuf[200]; | ||
1562 | sprintf(cmdbuf, "pdtk_array_dialog %%s array%d 100 1 1\n", | ||
1563 | ++gcount); | ||
1564 | gfxstub_new(&x->gl_pd, x, cmdbuf); | ||
1565 | } | ||
1566 | |||
1567 | /* called from graph_dialog to set properties */ | ||
1568 | void garray_properties(t_garray *x) | ||
1569 | { | ||
1570 | char cmdbuf[200]; | ||
1571 | gfxstub_deleteforkey(x); | ||
1572 | /* create dialog window. LATER fix this to escape '$' | ||
1573 | properly; right now we just detect a leading '$' and escape | ||
1574 | it. There should be a systematic way of doing this. */ | ||
1575 | if (x->x_name->s_name[0] == '$') | ||
1576 | sprintf(cmdbuf, "pdtk_array_dialog %%s \\%s %d %d 0\n", | ||
1577 | x->x_name->s_name, x->x_n, x->x_saveit); | ||
1578 | else sprintf(cmdbuf, "pdtk_array_dialog %%s %s %d %d 0\n", | ||
1579 | x->x_name->s_name, x->x_n, x->x_saveit); | ||
1580 | gfxstub_new(&x->x_gobj.g_pd, x, cmdbuf); | ||
1581 | } | ||
1582 | |||
1583 | /* this is called back from the dialog window to create a garray. | ||
1584 | The otherflag requests that we find an existing graph to put it in. */ | ||
1585 | void glist_arraydialog(t_glist *parent, t_symbol *name, t_floatarg size, | ||
1586 | t_floatarg saveit, t_floatarg otherflag) | ||
1587 | { | ||
1588 | t_glist *gl; | ||
1589 | t_garray *a; | ||
1590 | if (size < 1) | ||
1591 | size = 1; | ||
1592 | if (otherflag == 0 || (!(gl = glist_findgraph(parent)))) | ||
1593 | gl = glist_addglist(parent, &s_, 0, 1, | ||
1594 | (size > 1 ? size-1 : size), -1, 0, 0, 0, 0); | ||
1595 | a = graph_array(gl, sharptodollar(name), &s_float, size, saveit); | ||
1596 | } | ||
1597 | |||
1598 | /* this is called from the properties dialog window for an existing array */ | ||
1599 | void garray_arraydialog(t_garray *x, t_symbol *name, t_floatarg fsize, | ||
1600 | t_floatarg saveit, t_floatarg deleteit) | ||
1601 | { | ||
1602 | if (deleteit != 0) | ||
1603 | { | ||
1604 | glist_delete(x->x_glist, &x->x_gobj); | ||
1605 | } | ||
1606 | else | ||
1607 | { | ||
1608 | int size; | ||
1609 | t_symbol *argname = sharptodollar(name); | ||
1610 | if (argname != x->x_name) | ||
1611 | { | ||
1612 | x->x_name = argname; | ||
1613 | pd_unbind(&x->x_gobj.g_pd, x->x_realname); | ||
1614 | x->x_realname = canvas_realizedollar(x->x_glist, argname); | ||
1615 | pd_bind(&x->x_gobj.g_pd, x->x_realname); | ||
1616 | } | ||
1617 | size = fsize; | ||
1618 | if (size < 1) | ||
1619 | size = 1; | ||
1620 | if (size != x->x_n) | ||
1621 | garray_resize(x, size); | ||
1622 | garray_setsaveit(x, (saveit != 0)); | ||
1623 | garray_redraw(x); | ||
1624 | } | ||
1625 | } | ||
1626 | |||
1627 | static void garray_free(t_garray *x) | ||
1628 | { | ||
1629 | t_pd *x2; | ||
1630 | gfxstub_deleteforkey(x); | ||
1631 | pd_unbind(&x->x_gobj.g_pd, x->x_realname); | ||
1632 | /* LATER find a way to get #A unbound earlier (at end of load?) */ | ||
1633 | while (x2 = pd_findbyclass(gensym("#A"), garray_class)) | ||
1634 | pd_unbind(x2, gensym("#A")); | ||
1635 | freebytes(x->x_vec, x->x_n * x->x_elemsize); | ||
1636 | } | ||
1637 | |||
1638 | /* ------------- code used by both array and plot widget functions ---- */ | ||
1639 | |||
1640 | /* routine to get screen coordinates of a point in an array */ | ||
1641 | void array_getcoordinate(t_glist *glist, | ||
1642 | char *elem, int xonset, int yonset, int wonset, int indx, | ||
1643 | float basex, float basey, float xinc, | ||
1644 | float *xp, float *yp, float *wp) | ||
1645 | { | ||
1646 | float xval, yval, ypix, wpix; | ||
1647 | if (xonset >= 0) | ||
1648 | xval = fixtof(*(t_sample *)(elem + xonset)); | ||
1649 | else xval = indx * xinc; | ||
1650 | if (yonset >= 0) | ||
1651 | yval = fixtof(*(t_sample *)(elem + yonset)); | ||
1652 | else yval = 0; | ||
1653 | ypix = glist_ytopixels(glist, basey + yval); | ||
1654 | if (wonset >= 0) | ||
1655 | { | ||
1656 | /* found "w" field which controls linewidth. */ | ||
1657 | float wval = *(float *)(elem + wonset); | ||
1658 | wpix = glist_ytopixels(glist, basey + yval + wval) - ypix; | ||
1659 | if (wpix < 0) | ||
1660 | wpix = -wpix; | ||
1661 | } | ||
1662 | else wpix = 1; | ||
1663 | *xp = glist_xtopixels(glist, basex + xval); | ||
1664 | *yp = ypix; | ||
1665 | *wp = wpix; | ||
1666 | } | ||
1667 | |||
1668 | static float array_motion_xcumulative; | ||
1669 | static float array_motion_ycumulative; | ||
1670 | static t_symbol *array_motion_xfield; | ||
1671 | static t_symbol *array_motion_yfield; | ||
1672 | static t_glist *array_motion_glist; | ||
1673 | static t_gobj *array_motion_gobj; | ||
1674 | static t_word *array_motion_wp; | ||
1675 | static t_template *array_motion_template; | ||
1676 | static int array_motion_npoints; | ||
1677 | static int array_motion_elemsize; | ||
1678 | static int array_motion_altkey; | ||
1679 | static float array_motion_initx; | ||
1680 | static float array_motion_xperpix; | ||
1681 | static float array_motion_yperpix; | ||
1682 | static int array_motion_lastx; | ||
1683 | static int array_motion_fatten; | ||
1684 | |||
1685 | /* LATER protect against the template changing or the scalar disappearing | ||
1686 | probably by attaching a gpointer here ... */ | ||
1687 | |||
1688 | static void array_motion(void *z, t_floatarg dx, t_floatarg dy) | ||
1689 | { | ||
1690 | array_motion_xcumulative += dx * array_motion_xperpix; | ||
1691 | array_motion_ycumulative += dy * array_motion_yperpix; | ||
1692 | if (*array_motion_xfield->s_name) | ||
1693 | { | ||
1694 | /* it's an x, y plot; can drag many points at once */ | ||
1695 | int i; | ||
1696 | char *charword = (char *)array_motion_wp; | ||
1697 | for (i = 0; i < array_motion_npoints; i++) | ||
1698 | { | ||
1699 | t_word *thisword = (t_word *)(charword + i * array_motion_elemsize); | ||
1700 | if (*array_motion_xfield->s_name) | ||
1701 | { | ||
1702 | float xwas = template_getfloat(array_motion_template, | ||
1703 | array_motion_xfield, thisword, 1); | ||
1704 | template_setfloat(array_motion_template, | ||
1705 | array_motion_xfield, thisword, xwas + dx, 1); | ||
1706 | } | ||
1707 | if (*array_motion_yfield->s_name) | ||
1708 | { | ||
1709 | float ywas = template_getfloat(array_motion_template, | ||
1710 | array_motion_yfield, thisword, 1); | ||
1711 | if (array_motion_fatten) | ||
1712 | { | ||
1713 | if (i == 0) | ||
1714 | { | ||
1715 | float newy = ywas + dy * array_motion_yperpix; | ||
1716 | if (newy < 0) | ||
1717 | newy = 0; | ||
1718 | template_setfloat(array_motion_template, | ||
1719 | array_motion_yfield, thisword, newy, 1); | ||
1720 | } | ||
1721 | } | ||
1722 | else | ||
1723 | { | ||
1724 | template_setfloat(array_motion_template, | ||
1725 | array_motion_yfield, thisword, | ||
1726 | ywas + dy * array_motion_yperpix, 1); | ||
1727 | } | ||
1728 | } | ||
1729 | } | ||
1730 | } | ||
1731 | else | ||
1732 | { | ||
1733 | /* a y-only plot. */ | ||
1734 | int thisx = array_motion_initx + | ||
1735 | array_motion_xcumulative, x2; | ||
1736 | int increment, i, nchange; | ||
1737 | char *charword = (char *)array_motion_wp; | ||
1738 | float newy = array_motion_ycumulative, | ||
1739 | oldy = template_getfloat( | ||
1740 | array_motion_template, array_motion_yfield, | ||
1741 | (t_word *)(charword + array_motion_elemsize * array_motion_lastx), 1); | ||
1742 | float ydiff = newy - oldy; | ||
1743 | if (thisx < 0) thisx = 0; | ||
1744 | else if (thisx >= array_motion_npoints) | ||
1745 | thisx = array_motion_npoints - 1; | ||
1746 | increment = (thisx > array_motion_lastx ? -1 : 1); | ||
1747 | nchange = 1 + increment * (array_motion_lastx - thisx); | ||
1748 | |||
1749 | for (i = 0, x2 = thisx; i < nchange; i++, x2 += increment) | ||
1750 | { | ||
1751 | template_setfloat(array_motion_template, | ||
1752 | array_motion_yfield, | ||
1753 | (t_word *)(charword + array_motion_elemsize * x2), | ||
1754 | newy, 1); | ||
1755 | if (nchange > 1) | ||
1756 | newy -= ydiff * (1./(nchange - 1)); | ||
1757 | } | ||
1758 | array_motion_lastx = thisx; | ||
1759 | } | ||
1760 | glist_redrawitem(array_motion_glist, array_motion_gobj); | ||
1761 | } | ||
1762 | |||
1763 | int array_doclick(t_array *array, t_glist *glist, t_gobj *gobj, | ||
1764 | t_symbol *elemtemplatesym, | ||
1765 | float linewidth, float xloc, float xinc, float yloc, | ||
1766 | int xpix, int ypix, int shift, int alt, int dbl, int doit) | ||
1767 | { | ||
1768 | t_canvas *elemtemplatecanvas; | ||
1769 | t_template *elemtemplate; | ||
1770 | int elemsize, yonset, wonset, xonset, i; | ||
1771 | |||
1772 | if (!array_getfields(elemtemplatesym, &elemtemplatecanvas, | ||
1773 | &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) | ||
1774 | { | ||
1775 | float best = 100; | ||
1776 | int incr; | ||
1777 | /* if it has more than 2000 points, just check 300 of them. */ | ||
1778 | if (array->a_n < 2000) | ||
1779 | incr = 1; | ||
1780 | else incr = array->a_n / 300; | ||
1781 | for (i = 0; i < array->a_n; i += incr) | ||
1782 | { | ||
1783 | float pxpix, pypix, pwpix, dx, dy; | ||
1784 | array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, | ||
1785 | xonset, yonset, wonset, i, xloc, yloc, xinc, | ||
1786 | &pxpix, &pypix, &pwpix); | ||
1787 | if (pwpix < 4) | ||
1788 | pwpix = 4; | ||
1789 | dx = pxpix - xpix; | ||
1790 | if (dx < 0) dx = -dx; | ||
1791 | if (dx > 8) | ||
1792 | continue; | ||
1793 | dy = pypix - ypix; | ||
1794 | if (dy < 0) dy = -dy; | ||
1795 | if (dx + dy < best) | ||
1796 | best = dx + dy; | ||
1797 | if (wonset >= 0) | ||
1798 | { | ||
1799 | dy = (pypix + pwpix) - ypix; | ||
1800 | if (dy < 0) dy = -dy; | ||
1801 | if (dx + dy < best) | ||
1802 | best = dx + dy; | ||
1803 | dy = (pypix - pwpix) - ypix; | ||
1804 | if (dy < 0) dy = -dy; | ||
1805 | if (dx + dy < best) | ||
1806 | best = dx + dy; | ||
1807 | } | ||
1808 | } | ||
1809 | if (best > 8) | ||
1810 | return (0); | ||
1811 | best += 0.001; /* add truncation error margin */ | ||
1812 | for (i = 0; i < array->a_n; i += incr) | ||
1813 | { | ||
1814 | float pxpix, pypix, pwpix, dx, dy, dy2, dy3; | ||
1815 | array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, | ||
1816 | xonset, yonset, wonset, i, xloc, yloc, xinc, | ||
1817 | &pxpix, &pypix, &pwpix); | ||
1818 | if (pwpix < 4) | ||
1819 | pwpix = 4; | ||
1820 | dx = pxpix - xpix; | ||
1821 | if (dx < 0) dx = -dx; | ||
1822 | dy = pypix - ypix; | ||
1823 | if (dy < 0) dy = -dy; | ||
1824 | if (wonset >= 0) | ||
1825 | { | ||
1826 | dy2 = (pypix + pwpix) - ypix; | ||
1827 | if (dy2 < 0) dy2 = -dy2; | ||
1828 | dy3 = (pypix - pwpix) - ypix; | ||
1829 | if (dy3 < 0) dy3 = -dy3; | ||
1830 | if (yonset <= 0) | ||
1831 | dy = 100; | ||
1832 | } | ||
1833 | else dy2 = dy3 = 100; | ||
1834 | if (dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best) | ||
1835 | { | ||
1836 | if (dy < dy2 && dy < dy3) | ||
1837 | array_motion_fatten = 0; | ||
1838 | else if (dy2 < dy3) | ||
1839 | array_motion_fatten = -1; | ||
1840 | else array_motion_fatten = 1; | ||
1841 | if (doit) | ||
1842 | { | ||
1843 | char *elem = (char *)array->a_vec; | ||
1844 | array_motion_elemsize = elemsize; | ||
1845 | array_motion_glist = glist; | ||
1846 | array_motion_gobj = gobj; | ||
1847 | array_motion_template = elemtemplate; | ||
1848 | array_motion_xperpix = glist_dpixtodx(glist, 1); | ||
1849 | array_motion_yperpix = glist_dpixtody(glist, 1); | ||
1850 | if (alt && xpix < pxpix) /* delete a point */ | ||
1851 | { | ||
1852 | if (array->a_n <= 1) | ||
1853 | return (0); | ||
1854 | memmove((char *)(array->a_vec) + elemsize * i, | ||
1855 | (char *)(array->a_vec) + elemsize * (i+1), | ||
1856 | (array->a_n - 1 - i) * elemsize); | ||
1857 | array_resize(array, elemtemplate, array->a_n - 1); | ||
1858 | glist_redrawitem(array_motion_glist, array_motion_gobj); | ||
1859 | return (0); | ||
1860 | } | ||
1861 | else if (alt) | ||
1862 | { | ||
1863 | /* add a point (after the clicked-on one) */ | ||
1864 | array_resize(array, elemtemplate, array->a_n + 1); | ||
1865 | elem = (char *)array->a_vec; | ||
1866 | memmove(elem + elemsize * (i+1), | ||
1867 | elem + elemsize * i, | ||
1868 | (array->a_n - i - 1) * elemsize); | ||
1869 | i++; | ||
1870 | } | ||
1871 | if (xonset >= 0) | ||
1872 | { | ||
1873 | array_motion_xfield = gensym("x"); | ||
1874 | array_motion_xcumulative = | ||
1875 | *(float *)((elem + elemsize * i) + xonset); | ||
1876 | array_motion_wp = (t_word *)(elem + i * elemsize); | ||
1877 | array_motion_npoints = array->a_n - i; | ||
1878 | } | ||
1879 | else | ||
1880 | { | ||
1881 | array_motion_xfield = &s_; | ||
1882 | array_motion_xcumulative = 0; | ||
1883 | array_motion_wp = (t_word *)elem; | ||
1884 | array_motion_npoints = array->a_n; | ||
1885 | |||
1886 | array_motion_initx = i; | ||
1887 | array_motion_lastx = i; | ||
1888 | array_motion_xperpix *= (xinc == 0 ? 1 : 1./xinc); | ||
1889 | } | ||
1890 | if (array_motion_fatten) | ||
1891 | { | ||
1892 | array_motion_yfield = gensym("w"); | ||
1893 | array_motion_ycumulative = | ||
1894 | *(float *)((elem + elemsize * i) + wonset); | ||
1895 | array_motion_yperpix *= array_motion_fatten; | ||
1896 | } | ||
1897 | else if (yonset >= 0) | ||
1898 | { | ||
1899 | array_motion_yfield = gensym("y"); | ||
1900 | array_motion_ycumulative = | ||
1901 | *(float *)((elem + elemsize * i) + yonset); | ||
1902 | } | ||
1903 | else | ||
1904 | { | ||
1905 | array_motion_yfield = &s_; | ||
1906 | array_motion_ycumulative = 0; | ||
1907 | } | ||
1908 | glist_grab(glist, 0, array_motion, 0, xpix, ypix); | ||
1909 | } | ||
1910 | if (alt) | ||
1911 | { | ||
1912 | if (xpix < pxpix) | ||
1913 | return (CURSOR_EDITMODE_DISCONNECT); | ||
1914 | else return (CURSOR_RUNMODE_ADDPOINT); | ||
1915 | } | ||
1916 | else return (array_motion_fatten ? | ||
1917 | CURSOR_RUNMODE_THICKEN : CURSOR_RUNMODE_CLICKME); | ||
1918 | } | ||
1919 | } | ||
1920 | } | ||
1921 | return (0); | ||
1922 | } | ||
1923 | |||
1924 | /* -------------------- widget behavior for garray ------------ */ | ||
1925 | |||
1926 | static void garray_getrect(t_gobj *z, t_glist *glist, | ||
1927 | int *xp1, int *yp1, int *xp2, int *yp2) | ||
1928 | { | ||
1929 | t_garray *x = (t_garray *)z; | ||
1930 | float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; | ||
1931 | t_canvas *elemtemplatecanvas; | ||
1932 | t_template *elemtemplate; | ||
1933 | int elemsize, yonset, wonset, xonset, i; | ||
1934 | |||
1935 | if (!array_getfields(x->x_templatesym, &elemtemplatecanvas, | ||
1936 | &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) | ||
1937 | { | ||
1938 | int incr; | ||
1939 | /* if it has more than 2000 points, just check 300 of them. */ | ||
1940 | if (x->x_array.a_n < 2000) | ||
1941 | incr = 1; | ||
1942 | else incr = x->x_array.a_n / 300; | ||
1943 | for (i = 0; i < x->x_array.a_n; i += incr) | ||
1944 | { | ||
1945 | float pxpix, pypix, pwpix, dx, dy; | ||
1946 | array_getcoordinate(glist, (char *)(x->x_array.a_vec) + | ||
1947 | i * elemsize, | ||
1948 | xonset, yonset, wonset, i, 0, 0, 1, | ||
1949 | &pxpix, &pypix, &pwpix); | ||
1950 | if (pwpix < 2) | ||
1951 | pwpix = 2; | ||
1952 | if (pxpix < x1) | ||
1953 | x1 = pxpix; | ||
1954 | if (pxpix > x2) | ||
1955 | x2 = pxpix; | ||
1956 | if (pypix - pwpix < y1) | ||
1957 | y1 = pypix - pwpix; | ||
1958 | if (pypix + pwpix > y2) | ||
1959 | y2 = pypix + pwpix; | ||
1960 | } | ||
1961 | } | ||
1962 | *xp1 = x1; | ||
1963 | *yp1 = y1; | ||
1964 | *xp2 = x2; | ||
1965 | *yp2 = y2; | ||
1966 | } | ||
1967 | |||
1968 | static void garray_displace(t_gobj *z, t_glist *glist, int dx, int dy) | ||
1969 | { | ||
1970 | /* refuse */ | ||
1971 | } | ||
1972 | |||
1973 | static void garray_select(t_gobj *z, t_glist *glist, int state) | ||
1974 | { | ||
1975 | t_garray *x = (t_garray *)z; | ||
1976 | /* fill in later */ | ||
1977 | } | ||
1978 | |||
1979 | static void garray_activate(t_gobj *z, t_glist *glist, int state) | ||
1980 | { | ||
1981 | } | ||
1982 | |||
1983 | static void garray_delete(t_gobj *z, t_glist *glist) | ||
1984 | { | ||
1985 | /* nothing to do */ | ||
1986 | } | ||
1987 | |||
1988 | static void garray_vis(t_gobj *z, t_glist *glist, int vis) | ||
1989 | { | ||
1990 | t_garray *x = (t_garray *)z; | ||
1991 | if (vis) | ||
1992 | { | ||
1993 | int i, xonset, yonset, type; | ||
1994 | t_symbol *arraytype; | ||
1995 | t_template *template = template_findbyname(x->x_templatesym); | ||
1996 | if (!template) | ||
1997 | return; | ||
1998 | if (!template_find_field(template, gensym("y"), &yonset, &type, | ||
1999 | &arraytype) || type != DT_FLOAT) | ||
2000 | { | ||
2001 | error("%s: needs floating-point 'y' field", | ||
2002 | x->x_templatesym->s_name); | ||
2003 | sys_vgui(".x%x.c create text 50 50 -text foo\ | ||
2004 | -tags .x%x.a%x\n", | ||
2005 | glist_getcanvas(glist), glist_getcanvas(glist), x); | ||
2006 | } | ||
2007 | else if (!template_find_field(template, gensym("x"), &xonset, &type, | ||
2008 | &arraytype) || type != DT_FLOAT) | ||
2009 | { | ||
2010 | float firsty, xcum = x->x_firstx; | ||
2011 | int lastpixel = -1, ndrawn = 0; | ||
2012 | float yval = 0, xpix; | ||
2013 | int ixpix = 0; | ||
2014 | sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist)); | ||
2015 | for (i = 0; i < x->x_n; i++) | ||
2016 | { | ||
2017 | yval = fixtof(*(t_sample *)(x->x_vec + | ||
2018 | template->t_n * i * sizeof (t_word) + yonset)); | ||
2019 | xpix = glist_xtopixels(glist, xcum); | ||
2020 | ixpix = xpix + 0.5; | ||
2021 | if (ixpix != lastpixel) | ||
2022 | { | ||
2023 | sys_vgui("%d %f \\\n", ixpix, | ||
2024 | glist_ytopixels(glist, yval)); | ||
2025 | ndrawn++; | ||
2026 | } | ||
2027 | lastpixel = ixpix; | ||
2028 | if (ndrawn >= 1000) break; | ||
2029 | xcum += x->x_xinc; | ||
2030 | } | ||
2031 | /* TK will complain if there aren't at least 2 points... */ | ||
2032 | if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n"); | ||
2033 | else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix, | ||
2034 | glist_ytopixels(glist, yval)); | ||
2035 | sys_vgui("-tags .x%x.a%x\n", glist_getcanvas(glist), x); | ||
2036 | firsty = fixtof(*(t_sample *)(x->x_vec + yonset)); | ||
2037 | sys_vgui(".x%x.c create text %f %f -text {%s} -anchor e\ | ||
2038 | -font -*-courier-bold--normal--%d-* -tags .x%x.a%x\n", | ||
2039 | glist_getcanvas(glist), | ||
2040 | glist_xtopixels(glist, x->x_firstx) - 5., | ||
2041 | glist_ytopixels(glist, firsty), | ||
2042 | x->x_name->s_name, glist_getfont(glist), | ||
2043 | glist_getcanvas(glist), x); | ||
2044 | } | ||
2045 | else | ||
2046 | { | ||
2047 | post("x, y arrays not yet supported"); | ||
2048 | } | ||
2049 | } | ||
2050 | else | ||
2051 | { | ||
2052 | sys_vgui(".x%x.c delete .x%x.a%x\n", | ||
2053 | glist_getcanvas(glist), glist_getcanvas(glist), x); | ||
2054 | } | ||
2055 | } | ||
2056 | |||
2057 | static int garray_click(t_gobj *z, struct _glist *glist, | ||
2058 | int xpix, int ypix, int shift, int alt, int dbl, int doit) | ||
2059 | { | ||
2060 | t_garray *x = (t_garray *)z; | ||
2061 | return (array_doclick(&x->x_array, glist, z, x->x_templatesym, 1, 0, 1, 0, | ||
2062 | xpix, ypix, shift, alt, dbl, doit)); | ||
2063 | } | ||
2064 | |||
2065 | #define ARRAYWRITECHUNKSIZE 1000 | ||
2066 | |||
2067 | static void garray_save(t_gobj *z, t_binbuf *b) | ||
2068 | { | ||
2069 | t_garray *x = (t_garray *)z; | ||
2070 | binbuf_addv(b, "sssisi;", gensym("#X"), gensym("array"), | ||
2071 | x->x_name, x->x_n, x->x_templatesym, x->x_saveit); | ||
2072 | fprintf(stderr,"array save\n"); | ||
2073 | if (x->x_saveit) | ||
2074 | { | ||
2075 | int n = x->x_n, n2 = 0; | ||
2076 | if (x->x_templatesym != &s_float) | ||
2077 | { | ||
2078 | pd_error(x, "sorry, you can only save 'float' arrays now"); | ||
2079 | return; | ||
2080 | } | ||
2081 | if (n > 200000) | ||
2082 | post("warning: I'm saving an array with %d points!\n", n); | ||
2083 | while (n2 < n) | ||
2084 | { | ||
2085 | int chunk = n - n2, i; | ||
2086 | if (chunk > ARRAYWRITECHUNKSIZE) | ||
2087 | chunk = ARRAYWRITECHUNKSIZE; | ||
2088 | binbuf_addv(b, "si", gensym("#A"), n2); | ||
2089 | for (i = 0; i < chunk; i++) | ||
2090 | binbuf_addv(b, "f", fixtof(((t_sample *)(x->x_vec))[n2+i])); | ||
2091 | binbuf_addv(b, ";"); | ||
2092 | n2 += chunk; | ||
2093 | } | ||
2094 | } | ||
2095 | } | ||
2096 | |||
2097 | t_widgetbehavior garray_widgetbehavior = | ||
2098 | { | ||
2099 | garray_getrect, | ||
2100 | garray_displace, | ||
2101 | garray_select, | ||
2102 | garray_activate, | ||
2103 | garray_delete, | ||
2104 | garray_vis, | ||
2105 | garray_click | ||
2106 | }; | ||
2107 | |||
2108 | /* ----------------------- public functions -------------------- */ | ||
2109 | |||
2110 | void garray_usedindsp(t_garray *x) | ||
2111 | { | ||
2112 | x->x_usedindsp = 1; | ||
2113 | } | ||
2114 | |||
2115 | void garray_redraw(t_garray *x) | ||
2116 | { | ||
2117 | if (glist_isvisible(x->x_glist)) | ||
2118 | { | ||
2119 | garray_vis(&x->x_gobj, x->x_glist, 0); | ||
2120 | garray_vis(&x->x_gobj, x->x_glist, 1); | ||
2121 | } | ||
2122 | } | ||
2123 | |||
2124 | /* This functiopn gets the template of an array; if we can't figure | ||
2125 | out what template an array's elements belong to we're in grave trouble | ||
2126 | when it's time to free or resize it. */ | ||
2127 | t_template *garray_template(t_garray *x) | ||
2128 | { | ||
2129 | t_template *template = template_findbyname(x->x_templatesym); | ||
2130 | if (!template) | ||
2131 | bug("garray_template"); | ||
2132 | return (template); | ||
2133 | } | ||
2134 | |||
2135 | int garray_npoints(t_garray *x) /* get the length */ | ||
2136 | { | ||
2137 | return (x->x_n); | ||
2138 | } | ||
2139 | |||
2140 | char *garray_vec(t_garray *x) /* get the contents */ | ||
2141 | { | ||
2142 | return ((char *)(x->x_vec)); | ||
2143 | } | ||
2144 | |||
2145 | /* routine that checks if we're just an array of floats and if | ||
2146 | so returns the goods */ | ||
2147 | |||
2148 | int garray_getfloatarray(t_garray *x, int *size, t_sample **vec) | ||
2149 | { | ||
2150 | t_template *template = garray_template(x); | ||
2151 | int yonset, type; | ||
2152 | t_symbol *arraytype; | ||
2153 | if (!template_find_field(template, gensym("y"), &yonset, | ||
2154 | &type, &arraytype) || type != DT_FLOAT) | ||
2155 | error("%s: needs floating-point 'y' field", | ||
2156 | x->x_templatesym->s_name); | ||
2157 | else if (template->t_n != 1) | ||
2158 | error("%s: has more than one field", x->x_templatesym->s_name); | ||
2159 | else | ||
2160 | { | ||
2161 | *size = garray_npoints(x); | ||
2162 | *vec = (t_sample *)garray_vec(x); | ||
2163 | return (1); | ||
2164 | } | ||
2165 | return (0); | ||
2166 | } | ||
2167 | |||
2168 | /* get any floating-point field of any element of an array */ | ||
2169 | float garray_get(t_garray *x, t_symbol *s, t_int indx) | ||
2170 | { | ||
2171 | t_template *template = garray_template(x); | ||
2172 | int yonset, type; | ||
2173 | t_symbol *arraytype; | ||
2174 | if (!template_find_field(template, gensym("y"), &yonset, | ||
2175 | &type, &arraytype) || type != DT_FLOAT) | ||
2176 | { | ||
2177 | error("%s: needs floating-point '%s' field", x->x_templatesym->s_name, | ||
2178 | s->s_name); | ||
2179 | return (0); | ||
2180 | } | ||
2181 | if (indx < 0) indx = 0; | ||
2182 | else if (indx >= x->x_n) indx = x->x_n - 1; | ||
2183 | return (*(float *)((x->x_vec + sizeof(t_word) * indx) + yonset)); | ||
2184 | } | ||
2185 | |||
2186 | /* set the "saveit" flag */ | ||
2187 | void garray_setsaveit(t_garray *x, int saveit) | ||
2188 | { | ||
2189 | if (x->x_saveit && !saveit) | ||
2190 | post("warning: array %s: clearing save-in-patch flag", | ||
2191 | x->x_name->s_name); | ||
2192 | x->x_saveit = saveit; | ||
2193 | } | ||
2194 | |||
2195 | /*------------------- Pd messages ------------------------ */ | ||
2196 | static void garray_const(t_garray *x, t_floatarg g) | ||
2197 | { | ||
2198 | t_template *template = garray_template(x); | ||
2199 | int yonset, type, i; | ||
2200 | t_symbol *arraytype; | ||
2201 | if (!template_find_field(template, gensym("y"), &yonset, | ||
2202 | &type, &arraytype) || type != DT_FLOAT) | ||
2203 | error("%s: needs floating-point 'y' field", | ||
2204 | x->x_templatesym->s_name); | ||
2205 | else for (i = 0; i < x->x_n; i++) | ||
2206 | *(float *)(((char *)x->x_vec + sizeof(t_word) * i) + yonset) = g; | ||
2207 | garray_redraw(x); | ||
2208 | } | ||
2209 | |||
2210 | /* sum of Fourier components; called from routines below */ | ||
2211 | static void garray_dofo(t_garray *x, int npoints, float dcval, | ||
2212 | int nsin, t_float *vsin, int sineflag) | ||
2213 | { | ||
2214 | t_template *template = garray_template(x); | ||
2215 | int yonset, type, i, j; | ||
2216 | t_symbol *arraytype; | ||
2217 | double phase, phaseincr, fj; | ||
2218 | if (npoints == 0) | ||
2219 | npoints = 512; /* dunno what a good default would be... */ | ||
2220 | if (npoints != (1 << ilog2(npoints))) | ||
2221 | post("%s: rounnding to %d points", x->x_templatesym->s_name, | ||
2222 | (npoints = (1<<ilog2(npoints)))); | ||
2223 | garray_resize(x, npoints + 3); | ||
2224 | phaseincr = 2. * 3.14159 / npoints; | ||
2225 | if (!template_find_field(template, gensym("y"), &yonset, | ||
2226 | &type, &arraytype) || type != DT_FLOAT) | ||
2227 | { | ||
2228 | error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); | ||
2229 | return; | ||
2230 | } | ||
2231 | for (i = 0, phase = -phaseincr; i < x->x_n; i++, phase += phaseincr ) | ||
2232 | { | ||
2233 | double sum = dcval; | ||
2234 | if (sineflag) | ||
2235 | for (j = 0, fj = phase; j < nsin; j++, fj += phase) | ||
2236 | sum += vsin[j] * sin(fj); | ||
2237 | else | ||
2238 | for (j = 0, fj = 0; j < nsin; j++, fj += phase) | ||
2239 | sum += vsin[j] * cos(fj); | ||
2240 | *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) = sum; | ||
2241 | } | ||
2242 | garray_redraw(x); | ||
2243 | } | ||
2244 | |||
2245 | static void garray_sinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) | ||
2246 | { | ||
2247 | t_template *template = garray_template(x); | ||
2248 | |||
2249 | t_float *svec = (t_float *)t_getbytes(sizeof(t_float) * argc); | ||
2250 | int npoints, i; | ||
2251 | if (argc < 2) | ||
2252 | { | ||
2253 | error("sinesum: %s: need number of points and partial strengths", | ||
2254 | x->x_templatesym->s_name); | ||
2255 | return; | ||
2256 | } | ||
2257 | |||
2258 | npoints = atom_getfloatarg(0, argc, argv); | ||
2259 | argv++, argc--; | ||
2260 | |||
2261 | svec = (t_float *)t_getbytes(sizeof(t_float) * argc); | ||
2262 | if (!svec) return; | ||
2263 | |||
2264 | for (i = 0; i < argc; i++) | ||
2265 | svec[i] = atom_getfloatarg(i, argc, argv); | ||
2266 | garray_dofo(x, npoints, 0, argc, svec, 1); | ||
2267 | t_freebytes(svec, sizeof(t_float) * argc); | ||
2268 | } | ||
2269 | |||
2270 | static void garray_cosinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) | ||
2271 | { | ||
2272 | t_template *template = garray_template(x); | ||
2273 | |||
2274 | t_float *svec = (t_float *)t_getbytes(sizeof(t_float) * argc); | ||
2275 | int npoints, i; | ||
2276 | if (argc < 2) | ||
2277 | { | ||
2278 | error("sinesum: %s: need number of points and partial strengths", | ||
2279 | x->x_templatesym->s_name); | ||
2280 | return; | ||
2281 | } | ||
2282 | |||
2283 | npoints = atom_getfloatarg(0, argc, argv); | ||
2284 | argv++, argc--; | ||
2285 | |||
2286 | svec = (t_float *)t_getbytes(sizeof(t_float) * argc); | ||
2287 | if (!svec) return; | ||
2288 | |||
2289 | for (i = 0; i < argc; i++) | ||
2290 | svec[i] = atom_getfloatarg(i, argc, argv); | ||
2291 | garray_dofo(x, npoints, 0, argc, svec, 0); | ||
2292 | t_freebytes(svec, sizeof(t_float) * argc); | ||
2293 | } | ||
2294 | |||
2295 | static void garray_normalize(t_garray *x, t_float f) | ||
2296 | { | ||
2297 | t_template *template = garray_template(x); | ||
2298 | int yonset, type, npoints, i; | ||
2299 | double maxv, renormer; | ||
2300 | t_symbol *arraytype; | ||
2301 | |||
2302 | if (f <= 0) | ||
2303 | f = 1; | ||
2304 | |||
2305 | if (!template_find_field(template, gensym("y"), &yonset, | ||
2306 | &type, &arraytype) || type != DT_FLOAT) | ||
2307 | { | ||
2308 | error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); | ||
2309 | return; | ||
2310 | } | ||
2311 | for (i = 0, maxv = 0; i < x->x_n; i++) | ||
2312 | { | ||
2313 | double v = *(float *)((x->x_vec + sizeof(t_word) * i) + yonset); | ||
2314 | if (v > maxv) | ||
2315 | maxv = v; | ||
2316 | if (-v > maxv) | ||
2317 | maxv = -v; | ||
2318 | } | ||
2319 | if (maxv >= 0) | ||
2320 | { | ||
2321 | renormer = f / maxv; | ||
2322 | for (i = 0; i < x->x_n; i++) | ||
2323 | { | ||
2324 | *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) | ||
2325 | *= renormer; | ||
2326 | } | ||
2327 | } | ||
2328 | garray_redraw(x); | ||
2329 | } | ||
2330 | |||
2331 | /* list -- the first value is an index; subsequent values are put in | ||
2332 | the "y" slot of the array. This generalizes Max's "table", sort of. */ | ||
2333 | static void garray_list(t_garray *x, t_symbol *s, int argc, t_atom *argv) | ||
2334 | { | ||
2335 | t_template *template = garray_template(x); | ||
2336 | int yonset, type, i; | ||
2337 | t_symbol *arraytype; | ||
2338 | if (!template_find_field(template, gensym("y"), &yonset, | ||
2339 | &type, &arraytype) || type != DT_FLOAT) | ||
2340 | error("%s: needs floating-point 'y' field", | ||
2341 | x->x_templatesym->s_name); | ||
2342 | else if (argc < 2) return; | ||
2343 | else | ||
2344 | { | ||
2345 | int firstindex = atom_getfloat(argv); | ||
2346 | argc--; | ||
2347 | argv++; | ||
2348 | /* drop negative x values */ | ||
2349 | if (firstindex < 0) | ||
2350 | { | ||
2351 | argc += firstindex; | ||
2352 | argv -= firstindex; | ||
2353 | firstindex = 0; | ||
2354 | if (argc <= 0) return; | ||
2355 | } | ||
2356 | if (argc + firstindex > x->x_n) | ||
2357 | { | ||
2358 | argc = x->x_n - firstindex; | ||
2359 | if (argc <= 0) return; | ||
2360 | } | ||
2361 | for (i = 0; i < argc; i++) | ||
2362 | *(t_sample *)((x->x_vec + sizeof(t_word) * (i + firstindex)) + yonset) = | ||
2363 | ftofix(atom_getfloat(argv + i)); | ||
2364 | } | ||
2365 | garray_redraw(x); | ||
2366 | } | ||
2367 | |||
2368 | /* forward a "bounds" message to the owning graph */ | ||
2369 | static void garray_bounds(t_garray *x, t_floatarg x1, t_floatarg y1, | ||
2370 | t_floatarg x2, t_floatarg y2) | ||
2371 | { | ||
2372 | vmess(&x->x_glist->gl_pd, gensym("bounds"), "ffff", x1, y1, x2, y2); | ||
2373 | } | ||
2374 | |||
2375 | /* same for "xticks", etc */ | ||
2376 | static void garray_xticks(t_garray *x, | ||
2377 | t_floatarg point, t_floatarg inc, t_floatarg f) | ||
2378 | { | ||
2379 | vmess(&x->x_glist->gl_pd, gensym("xticks"), "fff", point, inc, f); | ||
2380 | } | ||
2381 | |||
2382 | static void garray_yticks(t_garray *x, | ||
2383 | t_floatarg point, t_floatarg inc, t_floatarg f) | ||
2384 | { | ||
2385 | vmess(&x->x_glist->gl_pd, gensym("yticks"), "fff", point, inc, f); | ||
2386 | } | ||
2387 | |||
2388 | static void garray_xlabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) | ||
2389 | { | ||
2390 | typedmess(&x->x_glist->gl_pd, s, argc, argv); | ||
2391 | } | ||
2392 | |||
2393 | static void garray_ylabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) | ||
2394 | { | ||
2395 | typedmess(&x->x_glist->gl_pd, s, argc, argv); | ||
2396 | } | ||
2397 | /* change the name of a garray. */ | ||
2398 | static void garray_rename(t_garray *x, t_symbol *s) | ||
2399 | { | ||
2400 | pd_unbind(&x->x_gobj.g_pd, x->x_realname); | ||
2401 | pd_bind(&x->x_gobj.g_pd, x->x_realname = x->x_name = s); | ||
2402 | garray_redraw(x); | ||
2403 | } | ||
2404 | |||
2405 | static void garray_read(t_garray *x, t_symbol *filename) | ||
2406 | { | ||
2407 | int nelem = x->x_n, filedesc; | ||
2408 | FILE *fd; | ||
2409 | char buf[MAXPDSTRING], *bufptr; | ||
2410 | t_template *template = garray_template(x); | ||
2411 | int yonset, type, i; | ||
2412 | t_symbol *arraytype; | ||
2413 | if (!template_find_field(template, gensym("y"), &yonset, | ||
2414 | &type, &arraytype) || type != DT_FLOAT) | ||
2415 | { | ||
2416 | error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); | ||
2417 | return; | ||
2418 | } | ||
2419 | if ((filedesc = open_via_path( | ||
2420 | canvas_getdir(glist_getcanvas(x->x_glist))->s_name, | ||
2421 | filename->s_name, "", buf, &bufptr, MAXPDSTRING, 0)) < 0 | ||
2422 | || !(fd = fdopen(filedesc, "r"))) | ||
2423 | { | ||
2424 | error("%s: can't open", filename->s_name); | ||
2425 | return; | ||
2426 | } | ||
2427 | for (i = 0; i < nelem; i++) | ||
2428 | { | ||
2429 | if (!fscanf(fd, "%f", (float *)((x->x_vec + sizeof(t_word) * i) + | ||
2430 | yonset))) | ||
2431 | { | ||
2432 | post("%s: read %d elements into table of size %d", | ||
2433 | filename->s_name, i, nelem); | ||
2434 | break; | ||
2435 | } | ||
2436 | } | ||
2437 | while (i < nelem) | ||
2438 | *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) = 0, i++; | ||
2439 | fclose(fd); | ||
2440 | garray_redraw(x); | ||
2441 | } | ||
2442 | |||
2443 | /* this should be renamed and moved... */ | ||
2444 | int garray_ambigendian(void) | ||
2445 | { | ||
2446 | unsigned short s = 1; | ||
2447 | unsigned char c = *(char *)(&s); | ||
2448 | return (c==0); | ||
2449 | } | ||
2450 | |||
2451 | #define BINREADMODE "rb" | ||
2452 | #define BINWRITEMODE "wb" | ||
2453 | |||
2454 | static void garray_read16(t_garray *x, t_symbol *filename, | ||
2455 | t_symbol *endian, t_floatarg fskip) | ||
2456 | { | ||
2457 | int skip = fskip, filedesc; | ||
2458 | int i, nelem; | ||
2459 | t_sample *vec; | ||
2460 | FILE *fd; | ||
2461 | char buf[MAXPDSTRING], *bufptr; | ||
2462 | short s; | ||
2463 | int cpubig = garray_ambigendian(), swap = 0; | ||
2464 | char c = endian->s_name[0]; | ||
2465 | if (c == 'b') | ||
2466 | { | ||
2467 | if (!cpubig) swap = 1; | ||
2468 | } | ||
2469 | else if (c == 'l') | ||
2470 | { | ||
2471 | if (cpubig) swap = 1; | ||
2472 | } | ||
2473 | else if (c) | ||
2474 | { | ||
2475 | error("array_read16: endianness is 'l' (low byte first ala INTEL)"); | ||
2476 | post("... or 'b' (high byte first ala MIPS,DEC,PPC)"); | ||
2477 | } | ||
2478 | if (!garray_getfloatarray(x, &nelem, &vec)) | ||
2479 | { | ||
2480 | error("%s: not a float array", x->x_templatesym->s_name); | ||
2481 | return; | ||
2482 | } | ||
2483 | if ((filedesc = open_via_path( | ||
2484 | canvas_getdir(glist_getcanvas(x->x_glist))->s_name, | ||
2485 | filename->s_name, "", buf, &bufptr, MAXPDSTRING, 1)) < 0 | ||
2486 | || !(fd = fdopen(filedesc, BINREADMODE))) | ||
2487 | { | ||
2488 | error("%s: can't open", filename->s_name); | ||
2489 | return; | ||
2490 | } | ||
2491 | if (skip) | ||
2492 | { | ||
2493 | long pos = fseek(fd, (long)skip, SEEK_SET); | ||
2494 | if (pos < 0) | ||
2495 | { | ||
2496 | error("%s: can't seek to byte %d", buf, skip); | ||
2497 | fclose(fd); | ||
2498 | return; | ||
2499 | } | ||
2500 | } | ||
2501 | |||
2502 | for (i = 0; i < nelem; i++) | ||
2503 | { | ||
2504 | if (fread(&s, sizeof(s), 1, fd) < 1) | ||
2505 | { | ||
2506 | post("%s: read %d elements into table of size %d", | ||
2507 | filename->s_name, i, nelem); | ||
2508 | break; | ||
2509 | } | ||
2510 | if (swap) s = ((s & 0xff) << 8) | ((s & 0xff00) >> 8); | ||
2511 | vec[i] = s * (1./32768.); | ||
2512 | } | ||
2513 | while (i < nelem) vec[i++] = 0; | ||
2514 | fclose(fd); | ||
2515 | garray_redraw(x); | ||
2516 | } | ||
2517 | |||
2518 | static void garray_write(t_garray *x, t_symbol *filename) | ||
2519 | { | ||
2520 | FILE *fd; | ||
2521 | char buf[MAXPDSTRING]; | ||
2522 | t_template *template = garray_template(x); | ||
2523 | int yonset, type, i; | ||
2524 | t_symbol *arraytype; | ||
2525 | if (!template_find_field(template, gensym("y"), &yonset, | ||
2526 | &type, &arraytype) || type != DT_FLOAT) | ||
2527 | { | ||
2528 | error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); | ||
2529 | return; | ||
2530 | } | ||
2531 | canvas_makefilename(glist_getcanvas(x->x_glist), filename->s_name, | ||
2532 | buf, MAXPDSTRING); | ||
2533 | sys_bashfilename(buf, buf); | ||
2534 | if (!(fd = fopen(buf, "w"))) | ||
2535 | { | ||
2536 | error("%s: can't create", buf); | ||
2537 | return; | ||
2538 | } | ||
2539 | for (i = 0; i < x->x_n; i++) | ||
2540 | { | ||
2541 | if (fprintf(fd, "%g\n", | ||
2542 | *(float *)((x->x_vec + sizeof(t_word) * i) + yonset)) < 1) | ||
2543 | { | ||
2544 | post("%s: write error", filename->s_name); | ||
2545 | break; | ||
2546 | } | ||
2547 | } | ||
2548 | fclose(fd); | ||
2549 | } | ||
2550 | |||
2551 | static unsigned char waveheader[] = { | ||
2552 | 0x52, 0x49, 0x46, 0x46, | ||
2553 | 0x00, 0x00, 0x00, 0x00, | ||
2554 | 0x57, 0x41, 0x56, 0x45, | ||
2555 | 0x66, 0x6d, 0x74, 0x20, | ||
2556 | |||
2557 | 0x10, 0x00, 0x00, 0x00, | ||
2558 | 0x01, 0x00, 0x01, 0x00, | ||
2559 | 0x44, 0xac, 0x00, 0x00, | ||
2560 | 0x88, 0x58, 0x01, 0x00, | ||
2561 | |||
2562 | 0x02, 0x00, 0x10, 0x00, | ||
2563 | 0x64, 0x61, 0x74, 0x61, | ||
2564 | 0x00, 0x00, 0x00, 0x00, | ||
2565 | }; | ||
2566 | |||
2567 | /* wave format only so far */ | ||
2568 | static void garray_write16(t_garray *x, t_symbol *filename, t_symbol *format) | ||
2569 | { | ||
2570 | t_template *template = garray_template(x); | ||
2571 | int yonset, type, i; | ||
2572 | t_symbol *arraytype; | ||
2573 | FILE *fd; | ||
2574 | int aiff = (format == gensym("aiff")); | ||
2575 | char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING]; | ||
2576 | int swap = garray_ambigendian(); /* wave is only little endian */ | ||
2577 | int intbuf; | ||
2578 | strncpy(filenamebuf, filename->s_name, MAXPDSTRING-10); | ||
2579 | filenamebuf[MAXPDSTRING-10] = 0; | ||
2580 | if (sizeof(int) != 4) post("write16: only works on 32-bit machines"); | ||
2581 | if (aiff) | ||
2582 | { | ||
2583 | if (strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff")) | ||
2584 | strcat(filenamebuf, ".aiff"); | ||
2585 | } | ||
2586 | else | ||
2587 | { | ||
2588 | if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav")) | ||
2589 | strcat(filenamebuf, ".wav"); | ||
2590 | } | ||
2591 | if (!template_find_field(template, gensym("y"), &yonset, | ||
2592 | &type, &arraytype) || type != DT_FLOAT) | ||
2593 | { | ||
2594 | error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); | ||
2595 | return; | ||
2596 | } | ||
2597 | canvas_makefilename(glist_getcanvas(x->x_glist), filenamebuf, | ||
2598 | buf2, MAXPDSTRING); | ||
2599 | sys_bashfilename(buf2, buf2); | ||
2600 | if (!(fd = fopen(buf2, BINWRITEMODE))) | ||
2601 | { | ||
2602 | error("%s: can't create", buf2); | ||
2603 | return; | ||
2604 | } | ||
2605 | intbuf = 2 * x->x_n + 36; | ||
2606 | if (swap) | ||
2607 | { | ||
2608 | unsigned char *foo = (unsigned char *)&intbuf, xxx; | ||
2609 | xxx = foo[0]; foo[0] = foo[3]; foo[3] = xxx; | ||
2610 | xxx = foo[1]; foo[1] = foo[2]; foo[2] = xxx; | ||
2611 | } | ||
2612 | memcpy((void *)(waveheader + 4), (void *)(&intbuf), 4); | ||
2613 | intbuf = 2 * x->x_n; | ||
2614 | if (swap) | ||
2615 | { | ||
2616 | unsigned char *foo = (unsigned char *)&intbuf, xxx; | ||
2617 | xxx = foo[0]; foo[0] = foo[3]; foo[3] = xxx; | ||
2618 | xxx = foo[1]; foo[1] = foo[2]; foo[2] = xxx; | ||
2619 | } | ||
2620 | memcpy((void *)(waveheader + 40), (void *)(&intbuf), 4); | ||
2621 | if (fwrite(waveheader, sizeof(waveheader), 1, fd) < 1) | ||
2622 | { | ||
2623 | post("%s: write error", buf2); | ||
2624 | goto closeit; | ||
2625 | } | ||
2626 | for (i = 0; i < x->x_n; i++) | ||
2627 | { | ||
2628 | float f = 32767. * *(float *)((x->x_vec + sizeof(t_word) * i) + yonset); | ||
2629 | short sh; | ||
2630 | if (f < -32768) f = -32768; | ||
2631 | else if (f > 32767) f = 32767; | ||
2632 | sh = f; | ||
2633 | if (swap) | ||
2634 | { | ||
2635 | unsigned char *foo = (unsigned char *)&sh, xxx; | ||
2636 | xxx = foo[0]; foo[0] = foo[1]; foo[1] = xxx; | ||
2637 | } | ||
2638 | if (fwrite(&sh, sizeof(sh), 1, fd) < 1) | ||
2639 | { | ||
2640 | post("%s: write error", buf2); | ||
2641 | goto closeit; | ||
2642 | } | ||
2643 | } | ||
2644 | closeit: | ||
2645 | fclose(fd); | ||
2646 | } | ||
2647 | |||
2648 | void garray_resize(t_garray *x, t_floatarg f) | ||
2649 | { | ||
2650 | int was = x->x_n, elemsize; | ||
2651 | t_glist *gl; | ||
2652 | int dspwas; | ||
2653 | int n = f; | ||
2654 | char *nvec; | ||
2655 | |||
2656 | if (n < 1) n = 1; | ||
2657 | elemsize = template_findbyname(x->x_templatesym)->t_n * sizeof(t_word); | ||
2658 | nvec = t_resizebytes(x->x_vec, was * elemsize, n * elemsize); | ||
2659 | if (!nvec) | ||
2660 | { | ||
2661 | pd_error(x, "array resize failed: out of memory"); | ||
2662 | return; | ||
2663 | } | ||
2664 | x->x_vec = nvec; | ||
2665 | /* LATER should check t_resizebytes result */ | ||
2666 | if (n > was) | ||
2667 | memset(x->x_vec + was*elemsize, | ||
2668 | 0, (n - was) * elemsize); | ||
2669 | x->x_n = n; | ||
2670 | |||
2671 | /* if this is the only array in the graph, | ||
2672 | reset the graph's coordinates */ | ||
2673 | gl = x->x_glist; | ||
2674 | if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next) | ||
2675 | { | ||
2676 | vmess(&gl->gl_pd, gensym("bounds"), "ffff", | ||
2677 | 0., gl->gl_y1, (double)(n > 1 ? n-1 : 1), gl->gl_y2); | ||
2678 | /* close any dialogs that might have the wrong info now... */ | ||
2679 | gfxstub_deleteforkey(gl); | ||
2680 | } | ||
2681 | else garray_redraw(x); | ||
2682 | if (x->x_usedindsp) canvas_update_dsp(); | ||
2683 | } | ||
2684 | |||
2685 | static void garray_print(t_garray *x) | ||
2686 | { | ||
2687 | post("garray %s: template %s, length %d", | ||
2688 | x->x_name->s_name, x->x_templatesym->s_name, x->x_n); | ||
2689 | } | ||
2690 | |||
2691 | void g_array_setup(void) | ||
2692 | { | ||
2693 | garray_class = class_new(gensym("array"), 0, (t_method)garray_free, | ||
2694 | sizeof(t_garray), CLASS_GOBJ, 0); | ||
2695 | class_setwidget(garray_class, &garray_widgetbehavior); | ||
2696 | class_addmethod(garray_class, (t_method)garray_const, gensym("const"), | ||
2697 | A_DEFFLOAT, A_NULL); | ||
2698 | class_addlist(garray_class, garray_list); | ||
2699 | class_addmethod(garray_class, (t_method)garray_bounds, gensym("bounds"), | ||
2700 | A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); | ||
2701 | class_addmethod(garray_class, (t_method)garray_xticks, gensym("xticks"), | ||
2702 | A_FLOAT, A_FLOAT, A_FLOAT, 0); | ||
2703 | class_addmethod(garray_class, (t_method)garray_xlabel, gensym("xlabel"), | ||
2704 | A_GIMME, 0); | ||
2705 | class_addmethod(garray_class, (t_method)garray_yticks, gensym("yticks"), | ||
2706 | A_FLOAT, A_FLOAT, A_FLOAT, 0); | ||
2707 | class_addmethod(garray_class, (t_method)garray_ylabel, gensym("ylabel"), | ||
2708 | A_GIMME, 0); | ||
2709 | class_addmethod(garray_class, (t_method)garray_rename, gensym("rename"), | ||
2710 | A_SYMBOL, 0); | ||
2711 | class_addmethod(garray_class, (t_method)garray_read, gensym("read"), | ||
2712 | A_SYMBOL, A_NULL); | ||
2713 | class_addmethod(garray_class, (t_method)garray_read16, gensym("read16"), | ||
2714 | A_SYMBOL, A_DEFFLOAT, A_DEFSYM, A_NULL); | ||
2715 | class_addmethod(garray_class, (t_method)garray_write, gensym("write"), | ||
2716 | A_SYMBOL, A_NULL); | ||
2717 | class_addmethod(garray_class, (t_method)garray_write16, gensym("write16"), | ||
2718 | A_SYMBOL, A_DEFSYM, A_NULL); | ||
2719 | class_addmethod(garray_class, (t_method)garray_resize, gensym("resize"), | ||
2720 | A_FLOAT, A_NULL); | ||
2721 | class_addmethod(garray_class, (t_method)garray_print, gensym("print"), | ||
2722 | A_NULL); | ||
2723 | class_addmethod(garray_class, (t_method)garray_sinesum, gensym("sinesum"), | ||
2724 | A_GIMME, 0); | ||
2725 | class_addmethod(garray_class, (t_method)garray_cosinesum, | ||
2726 | gensym("cosinesum"), A_GIMME, 0); | ||
2727 | class_addmethod(garray_class, (t_method)garray_normalize, | ||
2728 | gensym("normalize"), A_DEFFLOAT, 0); | ||
2729 | class_addmethod(garray_class, (t_method)garray_arraydialog, | ||
2730 | gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); | ||
2731 | class_setsavefn(garray_class, garray_save); | ||
2732 | } | ||
2733 | |||
2734 | |||