summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/src/g_array.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/g_array.c')
-rw-r--r--apps/plugins/pdbox/PDa/src/g_array.c2734
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
13arrays which are fields in scalars. Someday we should unify the
14two, 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
20static 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.
36They are instantiated by "garrays" below or can be elements of other
37scalars (g_scalar.c); their graphical behavior is defined accordingly. */
38
39t_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
59void 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
84void word_free(t_word *wp, t_template *template);
85
86void 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
104t_class *garray_class;
105static int gcount = 0;
106
107struct _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
126t_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 */
191void 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 */
201void 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. */
218void 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 */
232void 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
260static 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 */
274void 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
301static float array_motion_xcumulative;
302static float array_motion_ycumulative;
303static t_symbol *array_motion_xfield;
304static t_symbol *array_motion_yfield;
305static t_glist *array_motion_glist;
306static t_gobj *array_motion_gobj;
307static t_word *array_motion_wp;
308static t_template *array_motion_template;
309static int array_motion_npoints;
310static int array_motion_elemsize;
311static int array_motion_altkey;
312static float array_motion_initx;
313static float array_motion_xperpix;
314static float array_motion_yperpix;
315static int array_motion_lastx;
316static int array_motion_fatten;
317
318 /* LATER protect against the template changing or the scalar disappearing
319 probably by attaching a gpointer here ... */
320
321static 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
396int 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
559static 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
601static void garray_displace(t_gobj *z, t_glist *glist, int dx, int dy)
602{
603 /* refuse */
604}
605
606static 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
612static void garray_activate(t_gobj *z, t_glist *glist, int state)
613{
614}
615
616static void garray_delete(t_gobj *z, t_glist *glist)
617{
618 /* nothing to do */
619}
620
621static 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
690static 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
700static 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
730t_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
743void garray_usedindsp(t_garray *x)
744{
745 x->x_usedindsp = 1;
746}
747
748void 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. */
760t_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
768int garray_npoints(t_garray *x) /* get the length */
769{
770 return (x->x_n);
771}
772
773char *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
781int 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 */
802float 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 */
820void 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 ------------------------ */
829static 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 */
844static 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
878static 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
903static 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
928static 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. */
966static 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 */
1002static 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 */
1009static 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
1015static 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
1021static 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
1026static 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. */
1031static 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
1038static 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... */
1077int 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
1087static 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
1151static 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
1184static unsigned char waveheader[] = {
11850x52, 0x49, 0x46, 0x46,
11860x00, 0x00, 0x00, 0x00,
11870x57, 0x41, 0x56, 0x45,
11880x66, 0x6d, 0x74, 0x20,
1189
11900x10, 0x00, 0x00, 0x00,
11910x01, 0x00, 0x01, 0x00,
11920x44, 0xac, 0x00, 0x00,
11930x88, 0x58, 0x01, 0x00,
1194
11950x02, 0x00, 0x10, 0x00,
11960x64, 0x61, 0x74, 0x61,
11970x00, 0x00, 0x00, 0x00,
1198};
1199
1200 /* wave format only so far */
1201static 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 }
1277closeit:
1278 fclose(fd);
1279}
1280
1281void 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
1318static 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
1324void 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
1380arrays which are fields in scalars. Someday we should unify the
1381two, 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
1387static 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.
1403They are instantiated by "garrays" below or can be elements of other
1404scalars (g_scalar.c); their graphical behavior is defined accordingly. */
1405
1406t_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
1426void 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
1451void word_free(t_word *wp, t_template *template);
1452
1453void 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
1471t_class *garray_class;
1472static int gcount = 0;
1473
1474struct _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
1493t_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 */
1558void 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 */
1568void 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. */
1585void 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 */
1599void 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
1627static 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 */
1641void 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
1668static float array_motion_xcumulative;
1669static float array_motion_ycumulative;
1670static t_symbol *array_motion_xfield;
1671static t_symbol *array_motion_yfield;
1672static t_glist *array_motion_glist;
1673static t_gobj *array_motion_gobj;
1674static t_word *array_motion_wp;
1675static t_template *array_motion_template;
1676static int array_motion_npoints;
1677static int array_motion_elemsize;
1678static int array_motion_altkey;
1679static float array_motion_initx;
1680static float array_motion_xperpix;
1681static float array_motion_yperpix;
1682static int array_motion_lastx;
1683static int array_motion_fatten;
1684
1685 /* LATER protect against the template changing or the scalar disappearing
1686 probably by attaching a gpointer here ... */
1687
1688static 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
1763int 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
1926static 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
1968static void garray_displace(t_gobj *z, t_glist *glist, int dx, int dy)
1969{
1970 /* refuse */
1971}
1972
1973static 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
1979static void garray_activate(t_gobj *z, t_glist *glist, int state)
1980{
1981}
1982
1983static void garray_delete(t_gobj *z, t_glist *glist)
1984{
1985 /* nothing to do */
1986}
1987
1988static 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
2057static 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
2067static 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
2097t_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
2110void garray_usedindsp(t_garray *x)
2111{
2112 x->x_usedindsp = 1;
2113}
2114
2115void 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. */
2127t_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
2135int garray_npoints(t_garray *x) /* get the length */
2136{
2137 return (x->x_n);
2138}
2139
2140char *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
2148int 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 */
2169float 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 */
2187void 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 ------------------------ */
2196static 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 */
2211static 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
2245static 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
2270static 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
2295static 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. */
2333static 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 */
2369static 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 */
2376static 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
2382static 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
2388static 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
2393static 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. */
2398static 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
2405static 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... */
2444int 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
2454static 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
2518static 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
2551static unsigned char waveheader[] = {
25520x52, 0x49, 0x46, 0x46,
25530x00, 0x00, 0x00, 0x00,
25540x57, 0x41, 0x56, 0x45,
25550x66, 0x6d, 0x74, 0x20,
2556
25570x10, 0x00, 0x00, 0x00,
25580x01, 0x00, 0x01, 0x00,
25590x44, 0xac, 0x00, 0x00,
25600x88, 0x58, 0x01, 0x00,
2561
25620x02, 0x00, 0x10, 0x00,
25630x64, 0x61, 0x74, 0x61,
25640x00, 0x00, 0x00, 0x00,
2565};
2566
2567 /* wave format only so far */
2568static 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 }
2644closeit:
2645 fclose(fd);
2646}
2647
2648void 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
2685static 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
2691void 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