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. */
7 #include "../../pdbox.h"
11 int printf(const char *fmt
, ...);
12 #endif /* SIMULATOR */
16 #include <stdio.h> /* for read/write to files */
22 /* see also the "plot" object in g_scalar.c which deals with graphing
23 arrays which are fields in scalars. Someday we should unify the
26 /* aux routine to bash leading '#' to '$' for dialogs in u_main.tk
27 which can't send symbols starting with '$' (because the Pd message
28 interpreter would change them!) */
30 static t_symbol
*sharptodollar(t_symbol
*s
)
32 if (*s
->s_name
== '#')
34 char buf
[MAXPDSTRING
];
35 strncpy(buf
, s
->s_name
, MAXPDSTRING
);
36 buf
[MAXPDSTRING
-1] = 0;
43 /* --------- "pure" arrays with scalars for elements. --------------- */
45 /* Pure arrays have no a priori graphical capabilities.
46 They are instantiated by "garrays" below or can be elements of other
47 scalars (g_scalar.c); their graphical behavior is defined accordingly. */
49 t_array
*array_new(t_symbol
*templatesym
, t_gpointer
*parent
)
51 t_array
*x
= (t_array
*)getbytes(sizeof (*x
));
56 template = template_findbyname(templatesym
);
57 x
->a_templatesym
= templatesym
;
59 x
->a_elemsize
= sizeof(t_word
) * template->t_n
;
60 x
->a_vec
= (char *)getbytes(x
->a_elemsize
);
61 /* note here we blithely copy a gpointer instead of "setting" a
62 new one; this gpointer isn't accounted for and needn't be since
63 we'll be deleted before the thing pointed to gets deleted anyway;
66 x
->a_stub
= gstub_new(0, x
);
67 word_init((t_word
*)(x
->a_vec
), template, parent
);
71 void array_resize(t_array
*x
, t_template
*template, int n
)
81 elemsize
= sizeof(t_word
) * template->t_n
;
83 x
->a_vec
= (char *)resizebytes(x
->a_vec
, oldn
* elemsize
,
88 char *cp
= x
->a_vec
+ elemsize
* oldn
;
90 for (; i
--; cp
+= elemsize
)
92 t_word
*wp
= (t_word
*)cp
;
93 word_init(wp
, template, &x
->a_gp
);
98 void word_free(t_word
*wp
, t_template
*template);
100 void array_free(t_array
*x
)
103 t_template
*scalartemplate
= template_findbyname(x
->a_templatesym
);
104 /* we don't unset our gpointer here since it was never "set." */
105 /* gpointer_unset(&x->a_gp); */
106 gstub_cutoff(x
->a_stub
);
107 for (i
= 0; i
< x
->a_n
; i
++)
109 t_word
*wp
= (t_word
*)(x
->a_vec
+ x
->a_elemsize
* i
);
110 word_free(wp
, scalartemplate
);
112 freebytes(x
->a_vec
, x
->a_elemsize
* x
->a_n
);
113 freebytes(x
, sizeof *x
);
116 /* --------------------- graphical arrays (garrays) ------------------- */
118 t_class
*garray_class
;
119 static int gcount
= 0;
125 t_array x_array
; /* actual array; note only 4 fields used as below */
127 t_symbol
*x_realname
; /* name with "$" expanded */
128 t_float x_firstx
; /* X value of first item */
129 t_float x_xinc
; /* X increment */
130 char x_usedindsp
; /* true if some DSP routine is using this */
131 char x_saveit
; /* true if we should save this with parent */
134 /* macros to get into the "array" structure */
135 #define x_n x_array.a_n
136 #define x_elemsize x_array.a_elemsize
137 #define x_vec x_array.a_vec
138 #define x_templatesym x_array.a_templatesym
140 t_garray
*graph_array(t_glist
*gl
, t_symbol
*s
, t_symbol
*templatesym
,
141 t_floatarg f
, t_floatarg saveit
)
147 t_template
*template;
153 snprintf(buf
, sizeof(buf
), "array%d", ++gcount
);
155 sprintf(buf
, "array%d", ++gcount
);
158 templatesym
= &s_float
;
161 else if (!strncmp((str
= s
->s_name
), "array", 5)
162 && (zz
= atoi(str
+ 5)) > gcount
) gcount
= zz
;
163 template = template_findbyname(templatesym
);
166 error("array: couldn't find template %s", templatesym
->s_name
);
169 nwords
= template->t_n
;
170 for (i
= 0; i
< nwords
; i
++)
172 /* we can't have array or list elements yet because what scalar
173 can act as their "parent"??? */
174 if (template->t_vec
[i
].ds_type
== DT_ARRAY
175 || template->t_vec
[i
].ds_type
== DT_LIST
)
177 error("array: template %s can't have sublists or arrays",
178 templatesym
->s_name
);
182 x
= (t_garray
*)pd_new(garray_class
);
186 x
->x_elemsize
= nwords
* sizeof(t_word
);
187 x
->x_vec
= getbytes(x
->x_n
* x
->x_elemsize
);
188 memset(x
->x_vec
, 0, x
->x_n
* x
->x_elemsize
);
189 /* LATER should check that malloc */
191 x
->x_realname
= canvas_realizedollar(gl
, s
);
192 pd_bind(&x
->x_gobj
.g_pd
, x
->x_realname
);
193 x
->x_templatesym
= templatesym
;
195 x
->x_xinc
= 1; /* LATER make methods to set this... */
196 glist_add(gl
, &x
->x_gobj
);
199 x
->x_saveit
= (saveit
!= 0);
200 if((x2
= pd_findbyclass(gensym("#A"), garray_class
)))
201 pd_unbind(x2
, gensym("#A"));
203 pd_bind(&x
->x_gobj
.g_pd
, gensym("#A"));
208 /* called from array menu item to create a new one */
209 void canvas_menuarray(t_glist
*canvas
)
214 t_glist
*x
= (t_glist
*)canvas
;
216 sprintf(cmdbuf
, "pdtk_array_dialog %%s array%d 100 1 1\n",
218 gfxstub_new(&x
->gl_pd
, x
, cmdbuf
);
222 /* called from graph_dialog to set properties */
223 void garray_properties(t_garray
*x
)
229 gfxstub_deleteforkey(x
);
230 /* create dialog window. LATER fix this to escape '$'
231 properly; right now we just detect a leading '$' and escape
232 it. There should be a systematic way of doing this. */
233 if (x
->x_name
->s_name
[0] == '$')
234 sprintf(cmdbuf
, "pdtk_array_dialog %%s \\%s %d %d 0\n",
235 x
->x_name
->s_name
, x
->x_n
, x
->x_saveit
);
236 else sprintf(cmdbuf
, "pdtk_array_dialog %%s %s %d %d 0\n",
237 x
->x_name
->s_name
, x
->x_n
, x
->x_saveit
);
238 gfxstub_new(&x
->x_gobj
.g_pd
, x
, cmdbuf
);
242 /* this is called back from the dialog window to create a garray.
243 The otherflag requests that we find an existing graph to put it in. */
244 void glist_arraydialog(t_glist
*parent
, t_symbol
*name
, t_floatarg size
,
245 t_floatarg saveit
, t_floatarg otherflag
)
251 if (otherflag
== 0 || (!(gl
= glist_findgraph(parent
))))
252 gl
= glist_addglist(parent
, &s_
, 0, 1,
253 (size
> 1 ? size
-1 : size
), -1, 0, 0, 0, 0);
254 a
= graph_array(gl
, sharptodollar(name
), &s_float
, size
, saveit
);
257 /* this is called from the properties dialog window for an existing array */
258 void garray_arraydialog(t_garray
*x
, t_symbol
*name
, t_floatarg fsize
,
259 t_floatarg saveit
, t_floatarg deleteit
)
263 glist_delete(x
->x_glist
, &x
->x_gobj
);
268 t_symbol
*argname
= sharptodollar(name
);
269 if (argname
!= x
->x_name
)
272 pd_unbind(&x
->x_gobj
.g_pd
, x
->x_realname
);
273 x
->x_realname
= canvas_realizedollar(x
->x_glist
, argname
);
274 pd_bind(&x
->x_gobj
.g_pd
, x
->x_realname
);
280 garray_resize(x
, size
);
281 garray_setsaveit(x
, (saveit
!= 0));
286 static void garray_free(t_garray
*x
)
290 gfxstub_deleteforkey(x
);
292 pd_unbind(&x
->x_gobj
.g_pd
, x
->x_realname
);
293 /* LATER find a way to get #A unbound earlier (at end of load?) */
294 while((x2
= pd_findbyclass(gensym("#A"), garray_class
)))
295 pd_unbind(x2
, gensym("#A"));
296 freebytes(x
->x_vec
, x
->x_n
* x
->x_elemsize
);
299 /* ------------- code used by both array and plot widget functions ---- */
301 /* routine to get screen coordinates of a point in an array */
302 void array_getcoordinate(t_glist
*glist
,
303 char *elem
, int xonset
, int yonset
, int wonset
, int indx
,
304 float basex
, float basey
, float xinc
,
305 float *xp
, float *yp
, float *wp
)
307 float xval
, yval
, ypix
, wpix
;
309 xval
= fixtof(*(t_sample
*)(elem
+ xonset
));
310 else xval
= indx
* xinc
;
312 yval
= fixtof(*(t_sample
*)(elem
+ yonset
));
314 ypix
= glist_ytopixels(glist
, basey
+ yval
);
317 /* found "w" field which controls linewidth. */
318 float wval
= *(float *)(elem
+ wonset
);
319 wpix
= glist_ytopixels(glist
, basey
+ yval
+ wval
) - ypix
;
324 *xp
= glist_xtopixels(glist
, basex
+ xval
);
329 static float array_motion_xcumulative
;
330 static float array_motion_ycumulative
;
331 static t_symbol
*array_motion_xfield
;
332 static t_symbol
*array_motion_yfield
;
333 static t_glist
*array_motion_glist
;
334 static t_gobj
*array_motion_gobj
;
335 static t_word
*array_motion_wp
;
336 static t_template
*array_motion_template
;
337 static int array_motion_npoints
;
338 static int array_motion_elemsize
;
340 static int array_motion_altkey
;
342 static float array_motion_initx
;
343 static float array_motion_xperpix
;
344 static float array_motion_yperpix
;
345 static int array_motion_lastx
;
346 static int array_motion_fatten
;
348 /* LATER protect against the template changing or the scalar disappearing
349 probably by attaching a gpointer here ... */
351 static void array_motion(void *z
, t_floatarg dx
, t_floatarg dy
)
356 array_motion_xcumulative
+= dx
* array_motion_xperpix
;
357 array_motion_ycumulative
+= dy
* array_motion_yperpix
;
358 if (*array_motion_xfield
->s_name
)
360 /* it's an x, y plot; can drag many points at once */
362 char *charword
= (char *)array_motion_wp
;
363 for (i
= 0; i
< array_motion_npoints
; i
++)
365 t_word
*thisword
= (t_word
*)(charword
+ i
* array_motion_elemsize
);
366 if (*array_motion_xfield
->s_name
)
368 float xwas
= template_getfloat(array_motion_template
,
369 array_motion_xfield
, thisword
, 1);
370 template_setfloat(array_motion_template
,
371 array_motion_xfield
, thisword
, xwas
+ dx
, 1);
373 if (*array_motion_yfield
->s_name
)
375 float ywas
= template_getfloat(array_motion_template
,
376 array_motion_yfield
, thisword
, 1);
377 if (array_motion_fatten
)
381 float newy
= ywas
+ dy
* array_motion_yperpix
;
384 template_setfloat(array_motion_template
,
385 array_motion_yfield
, thisword
, newy
, 1);
390 template_setfloat(array_motion_template
,
391 array_motion_yfield
, thisword
,
392 ywas
+ dy
* array_motion_yperpix
, 1);
400 int thisx
= array_motion_initx
+
401 array_motion_xcumulative
, x2
;
402 int increment
, i
, nchange
;
403 char *charword
= (char *)array_motion_wp
;
404 float newy
= array_motion_ycumulative
,
405 oldy
= template_getfloat(
406 array_motion_template
, array_motion_yfield
,
407 (t_word
*)(charword
+ array_motion_elemsize
* array_motion_lastx
), 1);
408 float ydiff
= newy
- oldy
;
409 if (thisx
< 0) thisx
= 0;
410 else if (thisx
>= array_motion_npoints
)
411 thisx
= array_motion_npoints
- 1;
412 increment
= (thisx
> array_motion_lastx
? -1 : 1);
413 nchange
= 1 + increment
* (array_motion_lastx
- thisx
);
415 for (i
= 0, x2
= thisx
; i
< nchange
; i
++, x2
+= increment
)
417 template_setfloat(array_motion_template
,
419 (t_word
*)(charword
+ array_motion_elemsize
* x2
),
422 newy
-= ydiff
* (1./(nchange
- 1));
424 array_motion_lastx
= thisx
;
426 glist_redrawitem(array_motion_glist
, array_motion_gobj
);
429 int array_doclick(t_array
*array
, t_glist
*glist
, t_gobj
*gobj
,
430 t_symbol
*elemtemplatesym
,
431 float linewidth
, float xloc
, float xinc
, float yloc
,
432 int xpix
, int ypix
, int shift
, int alt
, int dbl
, int doit
)
434 t_canvas
*elemtemplatecanvas
;
435 t_template
*elemtemplate
;
436 int elemsize
, yonset
, wonset
, xonset
, i
;
444 if (!array_getfields(elemtemplatesym
, &elemtemplatecanvas
,
445 &elemtemplate
, &elemsize
, &xonset
, &yonset
, &wonset
))
449 /* if it has more than 2000 points, just check 300 of them. */
450 if (array
->a_n
< 2000)
452 else incr
= array
->a_n
/ 300;
453 for (i
= 0; i
< array
->a_n
; i
+= incr
)
455 float pxpix
, pypix
, pwpix
, dx
, dy
;
456 array_getcoordinate(glist
, (char *)(array
->a_vec
) + i
* elemsize
,
457 xonset
, yonset
, wonset
, i
, xloc
, yloc
, xinc
,
458 &pxpix
, &pypix
, &pwpix
);
462 if (dx
< 0) dx
= -dx
;
466 if (dy
< 0) dy
= -dy
;
471 dy
= (pypix
+ pwpix
) - ypix
;
472 if (dy
< 0) dy
= -dy
;
475 dy
= (pypix
- pwpix
) - ypix
;
476 if (dy
< 0) dy
= -dy
;
483 best
+= 0.001; /* add truncation error margin */
484 for (i
= 0; i
< array
->a_n
; i
+= incr
)
486 float pxpix
, pypix
, pwpix
, dx
, dy
, dy2
, dy3
;
487 array_getcoordinate(glist
, (char *)(array
->a_vec
) + i
* elemsize
,
488 xonset
, yonset
, wonset
, i
, xloc
, yloc
, xinc
,
489 &pxpix
, &pypix
, &pwpix
);
493 if (dx
< 0) dx
= -dx
;
495 if (dy
< 0) dy
= -dy
;
498 dy2
= (pypix
+ pwpix
) - ypix
;
499 if (dy2
< 0) dy2
= -dy2
;
500 dy3
= (pypix
- pwpix
) - ypix
;
501 if (dy3
< 0) dy3
= -dy3
;
505 else dy2
= dy3
= 100;
506 if (dx
+ dy
<= best
|| dx
+ dy2
<= best
|| dx
+ dy3
<= best
)
508 if (dy
< dy2
&& dy
< dy3
)
509 array_motion_fatten
= 0;
511 array_motion_fatten
= -1;
512 else array_motion_fatten
= 1;
515 char *elem
= (char *)array
->a_vec
;
516 array_motion_elemsize
= elemsize
;
517 array_motion_glist
= glist
;
518 array_motion_gobj
= gobj
;
519 array_motion_template
= elemtemplate
;
520 array_motion_xperpix
= glist_dpixtodx(glist
, 1);
521 array_motion_yperpix
= glist_dpixtody(glist
, 1);
522 if (alt
&& xpix
< pxpix
) /* delete a point */
526 memmove((char *)(array
->a_vec
) + elemsize
* i
,
527 (char *)(array
->a_vec
) + elemsize
* (i
+1),
528 (array
->a_n
- 1 - i
) * elemsize
);
529 array_resize(array
, elemtemplate
, array
->a_n
- 1);
530 glist_redrawitem(array_motion_glist
, array_motion_gobj
);
535 /* add a point (after the clicked-on one) */
536 array_resize(array
, elemtemplate
, array
->a_n
+ 1);
537 elem
= (char *)array
->a_vec
;
538 memmove(elem
+ elemsize
* (i
+1),
540 (array
->a_n
- i
- 1) * elemsize
);
545 array_motion_xfield
= gensym("x");
546 array_motion_xcumulative
=
547 *(float *)((elem
+ elemsize
* i
) + xonset
);
548 array_motion_wp
= (t_word
*)(elem
+ i
* elemsize
);
549 array_motion_npoints
= array
->a_n
- i
;
553 array_motion_xfield
= &s_
;
554 array_motion_xcumulative
= 0;
555 array_motion_wp
= (t_word
*)elem
;
556 array_motion_npoints
= array
->a_n
;
558 array_motion_initx
= i
;
559 array_motion_lastx
= i
;
560 array_motion_xperpix
*= (xinc
== 0 ? 1 : 1./xinc
);
562 if (array_motion_fatten
)
564 array_motion_yfield
= gensym("w");
565 array_motion_ycumulative
=
566 *(float *)((elem
+ elemsize
* i
) + wonset
);
567 array_motion_yperpix
*= array_motion_fatten
;
569 else if (yonset
>= 0)
571 array_motion_yfield
= gensym("y");
572 array_motion_ycumulative
=
573 *(float *)((elem
+ elemsize
* i
) + yonset
);
577 array_motion_yfield
= &s_
;
578 array_motion_ycumulative
= 0;
580 glist_grab(glist
, 0, array_motion
, 0, xpix
, ypix
);
585 return (CURSOR_EDITMODE_DISCONNECT
);
586 else return (CURSOR_RUNMODE_ADDPOINT
);
588 else return (array_motion_fatten
?
589 CURSOR_RUNMODE_THICKEN
: CURSOR_RUNMODE_CLICKME
);
596 /* -------------------- widget behavior for garray ------------ */
598 static void garray_getrect(t_gobj
*z
, t_glist
*glist
,
599 int *xp1
, int *yp1
, int *xp2
, int *yp2
)
601 t_garray
*x
= (t_garray
*)z
;
602 float x1
= 0x7fffffff, y1
= 0x7fffffff, x2
= -0x7fffffff, y2
= -0x7fffffff;
603 t_canvas
*elemtemplatecanvas
;
604 t_template
*elemtemplate
;
605 int elemsize
, yonset
, wonset
, xonset
, i
;
607 if (!array_getfields(x
->x_templatesym
, &elemtemplatecanvas
,
608 &elemtemplate
, &elemsize
, &xonset
, &yonset
, &wonset
))
611 /* if it has more than 2000 points, just check 300 of them. */
612 if (x
->x_array
.a_n
< 2000)
614 else incr
= x
->x_array
.a_n
/ 300;
615 for (i
= 0; i
< x
->x_array
.a_n
; i
+= incr
)
618 float pxpix
, pypix
, pwpix
;
620 float pxpix
, pypix
, pwpix
, dx
, dy
;
622 array_getcoordinate(glist
, (char *)(x
->x_array
.a_vec
) +
624 xonset
, yonset
, wonset
, i
, 0, 0, 1,
625 &pxpix
, &pypix
, &pwpix
);
632 if (pypix
- pwpix
< y1
)
634 if (pypix
+ pwpix
> y2
)
644 static void garray_displace(t_gobj
*z
, t_glist
*glist
, int dx
, int dy
)
655 static void garray_select(t_gobj
*z
, t_glist
*glist
, int state
)
662 t_garray
*x
= (t_garray
*)z
;
667 static void garray_activate(t_gobj
*z
, t_glist
*glist
, int state
)
676 static void garray_delete(t_gobj
*z
, t_glist
*glist
)
685 static void garray_vis(t_gobj
*z
, t_glist
*glist
, int vis
)
687 t_garray
*x
= (t_garray
*)z
;
690 int i
, xonset
, yonset
, type
;
692 t_template
*template = template_findbyname(x
->x_templatesym
);
695 if (!template_find_field(template, gensym("y"), &yonset
, &type
,
696 &arraytype
) || type
!= DT_FLOAT
)
698 error("%s: needs floating-point 'y' field",
699 x
->x_templatesym
->s_name
);
701 sys_vgui(".x%x.c create text 50 50 -text foo\
703 glist_getcanvas(glist
), glist_getcanvas(glist
), x
);
706 else if (!template_find_field(template, gensym("x"), &xonset
, &type
,
707 &arraytype
) || type
!= DT_FLOAT
)
709 float firsty
, xcum
= x
->x_firstx
;
710 int lastpixel
= -1, ndrawn
= 0;
711 float yval
= 0, xpix
;
714 sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist
));
716 for (i
= 0; i
< x
->x_n
; i
++)
718 yval
= fixtof(*(t_sample
*)(x
->x_vec
+
719 template->t_n
* i
* sizeof (t_word
) + yonset
));
720 xpix
= glist_xtopixels(glist
, xcum
);
722 if (ixpix
!= lastpixel
)
725 sys_vgui("%d %f \\\n", ixpix
,
726 glist_ytopixels(glist
, yval
));
731 if (ndrawn
>= 1000) break;
734 /* TK will complain if there aren't at least 2 points... */
736 if (ndrawn
== 0) sys_vgui("0 0 0 0 \\\n");
737 else if (ndrawn
== 1) sys_vgui("%d %f \\\n", ixpix
,
738 glist_ytopixels(glist
, yval
));
739 sys_vgui("-tags .x%x.a%x\n", glist_getcanvas(glist
), x
);
741 firsty
= fixtof(*(t_sample
*)(x
->x_vec
+ yonset
));
743 sys_vgui(".x%x.c create text %f %f -text {%s} -anchor e\
744 -font -*-courier-bold--normal--%d-* -tags .x%x.a%x\n",
745 glist_getcanvas(glist
),
746 glist_xtopixels(glist
, x
->x_firstx
) - 5.,
747 glist_ytopixels(glist
, firsty
),
748 x
->x_name
->s_name
, glist_getfont(glist
),
749 glist_getcanvas(glist
), x
);
754 post("x, y arrays not yet supported");
760 sys_vgui(".x%x.c delete .x%x.a%x\n",
761 glist_getcanvas(glist
), glist_getcanvas(glist
), x
);
766 static int garray_click(t_gobj
*z
, struct _glist
*glist
,
767 int xpix
, int ypix
, int shift
, int alt
, int dbl
, int doit
)
769 t_garray
*x
= (t_garray
*)z
;
770 return (array_doclick(&x
->x_array
, glist
, z
, x
->x_templatesym
, 1, 0, 1, 0,
771 xpix
, ypix
, shift
, alt
, dbl
, doit
));
774 #define ARRAYWRITECHUNKSIZE 1000
776 static void garray_save(t_gobj
*z
, t_binbuf
*b
)
778 t_garray
*x
= (t_garray
*)z
;
779 binbuf_addv(b
, "sssisi;", gensym("#X"), gensym("array"),
780 x
->x_name
, x
->x_n
, x
->x_templatesym
, x
->x_saveit
);
783 printf("array save\n");
784 #endif /* SIMULATOR */
786 fprintf(stderr
,"array save\n");
790 int n
= x
->x_n
, n2
= 0;
791 if (x
->x_templatesym
!= &s_float
)
793 pd_error(x
, "sorry, you can only save 'float' arrays now");
797 post("warning: I'm saving an array with %d points!\n", n
);
800 int chunk
= n
- n2
, i
;
801 if (chunk
> ARRAYWRITECHUNKSIZE
)
802 chunk
= ARRAYWRITECHUNKSIZE
;
803 binbuf_addv(b
, "si", gensym("#A"), n2
);
804 for (i
= 0; i
< chunk
; i
++)
805 binbuf_addv(b
, "f", fixtof(((t_sample
*)(x
->x_vec
))[n2
+i
]));
812 t_widgetbehavior garray_widgetbehavior
=
823 /* ----------------------- public functions -------------------- */
825 void garray_usedindsp(t_garray
*x
)
830 void garray_redraw(t_garray
*x
)
832 if (glist_isvisible(x
->x_glist
))
834 garray_vis(&x
->x_gobj
, x
->x_glist
, 0);
835 garray_vis(&x
->x_gobj
, x
->x_glist
, 1);
839 /* This functiopn gets the template of an array; if we can't figure
840 out what template an array's elements belong to we're in grave trouble
841 when it's time to free or resize it. */
842 t_template
*garray_template(t_garray
*x
)
844 t_template
*template = template_findbyname(x
->x_templatesym
);
846 bug("garray_template");
850 int garray_npoints(t_garray
*x
) /* get the length */
855 char *garray_vec(t_garray
*x
) /* get the contents */
857 return ((char *)(x
->x_vec
));
860 /* routine that checks if we're just an array of floats and if
861 so returns the goods */
863 int garray_getfloatarray(t_garray
*x
, int *size
, t_sample
**vec
)
865 t_template
*template = garray_template(x
);
868 if (!template_find_field(template, gensym("y"), &yonset
,
869 &type
, &arraytype
) || type
!= DT_FLOAT
)
870 error("%s: needs floating-point 'y' field",
871 x
->x_templatesym
->s_name
);
872 else if (template->t_n
!= 1)
873 error("%s: has more than one field", x
->x_templatesym
->s_name
);
876 *size
= garray_npoints(x
);
877 *vec
= (t_sample
*)garray_vec(x
);
883 /* get any floating-point field of any element of an array */
884 float garray_get(t_garray
*x
, t_symbol
*s
, t_int indx
)
886 t_template
*template = garray_template(x
);
889 if (!template_find_field(template, gensym("y"), &yonset
,
890 &type
, &arraytype
) || type
!= DT_FLOAT
)
892 error("%s: needs floating-point '%s' field", x
->x_templatesym
->s_name
,
896 if (indx
< 0) indx
= 0;
897 else if (indx
>= x
->x_n
) indx
= x
->x_n
- 1;
898 return (*(float *)((x
->x_vec
+ sizeof(t_word
) * indx
) + yonset
));
901 /* set the "saveit" flag */
902 void garray_setsaveit(t_garray
*x
, int saveit
)
904 if (x
->x_saveit
&& !saveit
)
905 post("warning: array %s: clearing save-in-patch flag",
907 x
->x_saveit
= saveit
;
910 /*------------------- Pd messages ------------------------ */
911 static void garray_const(t_garray
*x
, t_floatarg g
)
913 t_template
*template = garray_template(x
);
916 if (!template_find_field(template, gensym("y"), &yonset
,
917 &type
, &arraytype
) || type
!= DT_FLOAT
)
918 error("%s: needs floating-point 'y' field",
919 x
->x_templatesym
->s_name
);
920 else for (i
= 0; i
< x
->x_n
; i
++)
921 *(float *)(((char *)x
->x_vec
+ sizeof(t_word
) * i
) + yonset
) = g
;
925 /* sum of Fourier components; called from routines below */
926 static void garray_dofo(t_garray
*x
, int npoints
, float dcval
,
927 int nsin
, t_float
*vsin
, int sineflag
)
929 t_template
*template = garray_template(x
);
930 int yonset
, type
, i
, j
;
932 double phase
, phaseincr
, fj
;
934 npoints
= 512; /* dunno what a good default would be... */
935 if (npoints
!= (1 << ilog2(npoints
)))
936 post("%s: rounnding to %d points", x
->x_templatesym
->s_name
,
937 (npoints
= (1<<ilog2(npoints
))));
938 garray_resize(x
, npoints
+ 3);
939 phaseincr
= 2. * 3.14159 / npoints
;
940 if (!template_find_field(template, gensym("y"), &yonset
,
941 &type
, &arraytype
) || type
!= DT_FLOAT
)
943 error("%s: needs floating-point 'y' field", x
->x_templatesym
->s_name
);
946 for (i
= 0, phase
= -phaseincr
; i
< x
->x_n
; i
++, phase
+= phaseincr
)
950 for (j
= 0, fj
= phase
; j
< nsin
; j
++, fj
+= phase
)
951 sum
+= vsin
[j
] * sin(fj
);
953 for (j
= 0, fj
= 0; j
< nsin
; j
++, fj
+= phase
)
954 sum
+= vsin
[j
] * cos(fj
);
955 *(float *)((x
->x_vec
+ sizeof(t_word
) * i
) + yonset
) = sum
;
960 static void garray_sinesum(t_garray
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
965 t_template
*template = garray_template(x
);
968 t_float
*svec
= (t_float
*)t_getbytes(sizeof(t_float
) * argc
);
972 error("sinesum: %s: need number of points and partial strengths",
973 x
->x_templatesym
->s_name
);
977 npoints
= atom_getfloatarg(0, argc
, argv
);
980 svec
= (t_float
*)t_getbytes(sizeof(t_float
) * argc
);
983 for (i
= 0; i
< argc
; i
++)
984 svec
[i
] = atom_getfloatarg(i
, argc
, argv
);
985 garray_dofo(x
, npoints
, 0, argc
, svec
, 1);
986 t_freebytes(svec
, sizeof(t_float
) * argc
);
989 static void garray_cosinesum(t_garray
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
994 t_template
*template = garray_template(x
);
997 t_float
*svec
= (t_float
*)t_getbytes(sizeof(t_float
) * argc
);
1001 error("sinesum: %s: need number of points and partial strengths",
1002 x
->x_templatesym
->s_name
);
1006 npoints
= atom_getfloatarg(0, argc
, argv
);
1009 svec
= (t_float
*)t_getbytes(sizeof(t_float
) * argc
);
1012 for (i
= 0; i
< argc
; i
++)
1013 svec
[i
] = atom_getfloatarg(i
, argc
, argv
);
1014 garray_dofo(x
, npoints
, 0, argc
, svec
, 0);
1015 t_freebytes(svec
, sizeof(t_float
) * argc
);
1018 static void garray_normalize(t_garray
*x
, t_float f
)
1020 t_template
*template = garray_template(x
);
1022 int yonset
, type
, i
;
1024 int yonset
, type
, npoints
, i
;
1026 double maxv
, renormer
;
1027 t_symbol
*arraytype
;
1032 if (!template_find_field(template, gensym("y"), &yonset
,
1033 &type
, &arraytype
) || type
!= DT_FLOAT
)
1035 error("%s: needs floating-point 'y' field", x
->x_templatesym
->s_name
);
1038 for (i
= 0, maxv
= 0; i
< x
->x_n
; i
++)
1040 double v
= *(float *)((x
->x_vec
+ sizeof(t_word
) * i
) + yonset
);
1048 renormer
= f
/ maxv
;
1049 for (i
= 0; i
< x
->x_n
; i
++)
1051 *(float *)((x
->x_vec
+ sizeof(t_word
) * i
) + yonset
)
1058 /* list -- the first value is an index; subsequent values are put in
1059 the "y" slot of the array. This generalizes Max's "table", sort of. */
1060 static void garray_list(t_garray
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
1062 t_template
*template = garray_template(x
);
1063 int yonset
, type
, i
;
1064 t_symbol
*arraytype
;
1068 if (!template_find_field(template, gensym("y"), &yonset
,
1069 &type
, &arraytype
) || type
!= DT_FLOAT
)
1070 error("%s: needs floating-point 'y' field",
1071 x
->x_templatesym
->s_name
);
1072 else if (argc
< 2) return;
1075 int firstindex
= atom_getfloat(argv
);
1078 /* drop negative x values */
1084 if (argc
<= 0) return;
1086 if (argc
+ firstindex
> x
->x_n
)
1088 argc
= x
->x_n
- firstindex
;
1089 if (argc
<= 0) return;
1091 for (i
= 0; i
< argc
; i
++)
1092 *(t_sample
*)((x
->x_vec
+ sizeof(t_word
) * (i
+ firstindex
)) + yonset
) =
1093 ftofix(atom_getfloat(argv
+ i
));
1098 /* forward a "bounds" message to the owning graph */
1099 static void garray_bounds(t_garray
*x
, t_floatarg x1
, t_floatarg y1
,
1100 t_floatarg x2
, t_floatarg y2
)
1102 vmess(&x
->x_glist
->gl_pd
, gensym("bounds"), "ffff", x1
, y1
, x2
, y2
);
1105 /* same for "xticks", etc */
1106 static void garray_xticks(t_garray
*x
,
1107 t_floatarg point
, t_floatarg inc
, t_floatarg f
)
1109 vmess(&x
->x_glist
->gl_pd
, gensym("xticks"), "fff", point
, inc
, f
);
1112 static void garray_yticks(t_garray
*x
,
1113 t_floatarg point
, t_floatarg inc
, t_floatarg f
)
1115 vmess(&x
->x_glist
->gl_pd
, gensym("yticks"), "fff", point
, inc
, f
);
1118 static void garray_xlabel(t_garray
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
1120 typedmess(&x
->x_glist
->gl_pd
, s
, argc
, argv
);
1123 static void garray_ylabel(t_garray
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
1125 typedmess(&x
->x_glist
->gl_pd
, s
, argc
, argv
);
1127 /* change the name of a garray. */
1128 static void garray_rename(t_garray
*x
, t_symbol
*s
)
1130 pd_unbind(&x
->x_gobj
.g_pd
, x
->x_realname
);
1131 pd_bind(&x
->x_gobj
.g_pd
, x
->x_realname
= x
->x_name
= s
);
1135 static void garray_read(t_garray
*x
, t_symbol
*filename
)
1137 int nelem
= x
->x_n
, filedesc
;
1143 char buf
[MAXPDSTRING
], *bufptr
;
1144 t_template
*template = garray_template(x
);
1145 int yonset
, type
, i
;
1146 t_symbol
*arraytype
;
1147 if (!template_find_field(template, gensym("y"), &yonset
,
1148 &type
, &arraytype
) || type
!= DT_FLOAT
)
1150 error("%s: needs floating-point 'y' field", x
->x_templatesym
->s_name
);
1153 if ((filedesc
= open_via_path(
1154 canvas_getdir(glist_getcanvas(x
->x_glist
))->s_name
,
1155 filename
->s_name
, "", buf
, &bufptr
, MAXPDSTRING
, 0)) < 0
1159 || !(fd
= fdopen(filedesc
, "r")))
1162 error("%s: can't open", filename
->s_name
);
1165 for (i
= 0; i
< nelem
; i
++)
1168 if(rb_fscanf_f(fd
, (float*)((x
->x_vec
+ sizeof(t_word
) * i
) + yonset
)))
1170 if (!fscanf(fd
, "%f", (float *)((x
->x_vec
+ sizeof(t_word
) * i
) +
1174 post("%s: read %d elements into table of size %d",
1175 filename
->s_name
, i
, nelem
);
1180 *(float *)((x
->x_vec
+ sizeof(t_word
) * i
) + yonset
) = 0, i
++;
1189 /* this should be renamed and moved... */
1190 int garray_ambigendian(void)
1192 unsigned short s
= 1;
1193 unsigned char c
= *(char *)(&s
);
1197 #define BINREADMODE "rb"
1198 #define BINWRITEMODE "wb"
1200 static void garray_read16(t_garray
*x
, t_symbol
*filename
,
1201 t_symbol
*endian
, t_floatarg fskip
)
1203 int skip
= fskip
, filedesc
;
1211 char buf
[MAXPDSTRING
], *bufptr
;
1213 int cpubig
= garray_ambigendian(), swap
= 0;
1214 char c
= endian
->s_name
[0];
1217 if (!cpubig
) swap
= 1;
1221 if (cpubig
) swap
= 1;
1225 error("array_read16: endianness is 'l' (low byte first ala INTEL)");
1226 post("... or 'b' (high byte first ala MIPS,DEC,PPC)");
1228 if (!garray_getfloatarray(x
, &nelem
, &vec
))
1230 error("%s: not a float array", x
->x_templatesym
->s_name
);
1233 if ((filedesc
= open_via_path(
1234 canvas_getdir(glist_getcanvas(x
->x_glist
))->s_name
,
1235 filename
->s_name
, "", buf
, &bufptr
, MAXPDSTRING
, 1)) < 0
1239 || !(fd
= fdopen(filedesc
, BINREADMODE
)))
1242 error("%s: can't open", filename
->s_name
);
1248 long pos
= lseek(fd
, (long)skip
, SEEK_SET
);
1250 long pos
= fseek(fd
, (long)skip
, SEEK_SET
);
1254 error("%s: can't seek to byte %d", buf
, skip
);
1264 for (i
= 0; i
< nelem
; i
++)
1267 if(read(fd
, &s
, sizeof(s
)) < 1)
1269 if (fread(&s
, sizeof(s
), 1, fd
) < 1)
1272 post("%s: read %d elements into table of size %d",
1273 filename
->s_name
, i
, nelem
);
1276 if (swap
) s
= ((s
& 0xff) << 8) | ((s
& 0xff00) >> 8);
1277 vec
[i
] = s
* (1./32768.);
1279 while (i
< nelem
) vec
[i
++] = 0;
1288 static void garray_write(t_garray
*x
, t_symbol
*filename
)
1295 char buf
[MAXPDSTRING
];
1296 t_template
*template = garray_template(x
);
1297 int yonset
, type
, i
;
1298 t_symbol
*arraytype
;
1299 if (!template_find_field(template, gensym("y"), &yonset
,
1300 &type
, &arraytype
) || type
!= DT_FLOAT
)
1302 error("%s: needs floating-point 'y' field", x
->x_templatesym
->s_name
);
1305 canvas_makefilename(glist_getcanvas(x
->x_glist
), filename
->s_name
,
1307 sys_bashfilename(buf
, buf
);
1309 if(!(fd
= open(buf
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666)))
1311 if (!(fd
= fopen(buf
, "w")))
1314 error("%s: can't create", buf
);
1317 for (i
= 0; i
< x
->x_n
; i
++)
1322 if (fprintf(fd
, "%g\n",
1323 #endif /* ROCKBOX */
1324 *(float *)((x
->x_vec
+ sizeof(t_word
) * i
) + yonset
)) < 1)
1326 post("%s: write error", filename
->s_name
);
1337 static unsigned char waveheader
[] = {
1338 0x52, 0x49, 0x46, 0x46,
1339 0x00, 0x00, 0x00, 0x00,
1340 0x57, 0x41, 0x56, 0x45,
1341 0x66, 0x6d, 0x74, 0x20,
1343 0x10, 0x00, 0x00, 0x00,
1344 0x01, 0x00, 0x01, 0x00,
1345 0x44, 0xac, 0x00, 0x00,
1346 0x88, 0x58, 0x01, 0x00,
1348 0x02, 0x00, 0x10, 0x00,
1349 0x64, 0x61, 0x74, 0x61,
1350 0x00, 0x00, 0x00, 0x00,
1353 /* wave format only so far */
1354 static void garray_write16(t_garray
*x
, t_symbol
*filename
, t_symbol
*format
)
1356 t_template
*template = garray_template(x
);
1357 int yonset
, type
, i
;
1358 t_symbol
*arraytype
;
1364 int aiff
= (format
== gensym("aiff"));
1365 char filenamebuf
[MAXPDSTRING
], buf2
[MAXPDSTRING
];
1366 int swap
= garray_ambigendian(); /* wave is only little endian */
1368 strncpy(filenamebuf
, filename
->s_name
, MAXPDSTRING
-10);
1369 filenamebuf
[MAXPDSTRING
-10] = 0;
1370 if (sizeof(int) != 4) post("write16: only works on 32-bit machines");
1373 if (strcmp(filenamebuf
+ strlen(filenamebuf
)-5, ".aiff"))
1374 strcat(filenamebuf
, ".aiff");
1378 if (strcmp(filenamebuf
+ strlen(filenamebuf
)-4, ".wav"))
1379 strcat(filenamebuf
, ".wav");
1381 if (!template_find_field(template, gensym("y"), &yonset
,
1382 &type
, &arraytype
) || type
!= DT_FLOAT
)
1384 error("%s: needs floating-point 'y' field", x
->x_templatesym
->s_name
);
1387 canvas_makefilename(glist_getcanvas(x
->x_glist
), filenamebuf
,
1389 sys_bashfilename(buf2
, buf2
);
1391 if(!(fd
= open(buf2
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0666)))
1393 if (!(fd
= fopen(buf2
, BINWRITEMODE
)))
1396 error("%s: can't create", buf2
);
1399 intbuf
= 2 * x
->x_n
+ 36;
1402 unsigned char *foo
= (unsigned char *)&intbuf
, xxx
;
1403 xxx
= foo
[0]; foo
[0] = foo
[3]; foo
[3] = xxx
;
1404 xxx
= foo
[1]; foo
[1] = foo
[2]; foo
[2] = xxx
;
1406 memcpy((void *)(waveheader
+ 4), (void *)(&intbuf
), 4);
1407 intbuf
= 2 * x
->x_n
;
1410 unsigned char *foo
= (unsigned char *)&intbuf
, xxx
;
1411 xxx
= foo
[0]; foo
[0] = foo
[3]; foo
[3] = xxx
;
1412 xxx
= foo
[1]; foo
[1] = foo
[2]; foo
[2] = xxx
;
1414 memcpy((void *)(waveheader
+ 40), (void *)(&intbuf
), 4);
1416 if(write(fd
, waveheader
, sizeof(waveheader
)) < 1)
1418 if (fwrite(waveheader
, sizeof(waveheader
), 1, fd
) < 1)
1421 post("%s: write error", buf2
);
1424 for (i
= 0; i
< x
->x_n
; i
++)
1426 float f
= 32767. * *(float *)((x
->x_vec
+ sizeof(t_word
) * i
) + yonset
);
1428 if (f
< -32768) f
= -32768;
1429 else if (f
> 32767) f
= 32767;
1433 unsigned char *foo
= (unsigned char *)&sh
, xxx
;
1434 xxx
= foo
[0]; foo
[0] = foo
[1]; foo
[1] = xxx
;
1437 if(write(fd
, &sh
, sizeof(sh
)) < 1)
1439 if (fwrite(&sh
, sizeof(sh
), 1, fd
) < 1)
1442 post("%s: write error", buf2
);
1454 void garray_resize(t_garray
*x
, t_floatarg f
)
1456 int was
= x
->x_n
, elemsize
;
1465 elemsize
= template_findbyname(x
->x_templatesym
)->t_n
* sizeof(t_word
);
1466 nvec
= t_resizebytes(x
->x_vec
, was
* elemsize
, n
* elemsize
);
1469 pd_error(x
, "array resize failed: out of memory");
1473 /* LATER should check t_resizebytes result */
1475 memset(x
->x_vec
+ was
*elemsize
,
1476 0, (n
- was
) * elemsize
);
1479 /* if this is the only array in the graph,
1480 reset the graph's coordinates */
1482 if (gl
->gl_list
== &x
->x_gobj
&& !x
->x_gobj
.g_next
)
1484 vmess(&gl
->gl_pd
, gensym("bounds"), "ffff",
1485 0., gl
->gl_y1
, (double)(n
> 1 ? n
-1 : 1), gl
->gl_y2
);
1486 /* close any dialogs that might have the wrong info now... */
1488 gfxstub_deleteforkey(gl
);
1491 else garray_redraw(x
);
1492 if (x
->x_usedindsp
) canvas_update_dsp();
1495 static void garray_print(t_garray
*x
)
1497 post("garray %s: template %s, length %d",
1498 x
->x_name
->s_name
, x
->x_templatesym
->s_name
, x
->x_n
);
1501 void g_array_setup(void)
1503 garray_class
= class_new(gensym("array"), 0, (t_method
)garray_free
,
1504 sizeof(t_garray
), CLASS_GOBJ
, 0);
1505 class_setwidget(garray_class
, &garray_widgetbehavior
);
1506 class_addmethod(garray_class
, (t_method
)garray_const
, gensym("const"),
1507 A_DEFFLOAT
, A_NULL
);
1508 class_addlist(garray_class
, garray_list
);
1509 class_addmethod(garray_class
, (t_method
)garray_bounds
, gensym("bounds"),
1510 A_FLOAT
, A_FLOAT
, A_FLOAT
, A_FLOAT
, A_NULL
);
1511 class_addmethod(garray_class
, (t_method
)garray_xticks
, gensym("xticks"),
1512 A_FLOAT
, A_FLOAT
, A_FLOAT
, 0);
1513 class_addmethod(garray_class
, (t_method
)garray_xlabel
, gensym("xlabel"),
1515 class_addmethod(garray_class
, (t_method
)garray_yticks
, gensym("yticks"),
1516 A_FLOAT
, A_FLOAT
, A_FLOAT
, 0);
1517 class_addmethod(garray_class
, (t_method
)garray_ylabel
, gensym("ylabel"),
1519 class_addmethod(garray_class
, (t_method
)garray_rename
, gensym("rename"),
1521 class_addmethod(garray_class
, (t_method
)garray_read
, gensym("read"),
1523 class_addmethod(garray_class
, (t_method
)garray_read16
, gensym("read16"),
1524 A_SYMBOL
, A_DEFFLOAT
, A_DEFSYM
, A_NULL
);
1525 class_addmethod(garray_class
, (t_method
)garray_write
, gensym("write"),
1527 class_addmethod(garray_class
, (t_method
)garray_write16
, gensym("write16"),
1528 A_SYMBOL
, A_DEFSYM
, A_NULL
);
1529 class_addmethod(garray_class
, (t_method
)garray_resize
, gensym("resize"),
1531 class_addmethod(garray_class
, (t_method
)garray_print
, gensym("print"),
1533 class_addmethod(garray_class
, (t_method
)garray_sinesum
, gensym("sinesum"),
1535 class_addmethod(garray_class
, (t_method
)garray_cosinesum
,
1536 gensym("cosinesum"), A_GIMME
, 0);
1537 class_addmethod(garray_class
, (t_method
)garray_normalize
,
1538 gensym("normalize"), A_DEFFLOAT
, 0);
1539 class_addmethod(garray_class
, (t_method
)garray_arraydialog
,
1540 gensym("arraydialog"), A_SYMBOL
, A_FLOAT
, A_FLOAT
, A_FLOAT
, A_NULL
);
1541 class_setsavefn(garray_class
, garray_save
);