diff options
Diffstat (limited to 'cyclone/sickle/Scope.c')
-rw-r--r-- | cyclone/sickle/Scope.c | 1043 |
1 files changed, 1043 insertions, 0 deletions
diff --git a/cyclone/sickle/Scope.c b/cyclone/sickle/Scope.c new file mode 100644 index 0000000..75e825d --- /dev/null +++ b/cyclone/sickle/Scope.c @@ -0,0 +1,1043 @@ +/* Copyright (c) 2002-2003 krzYszcz and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* LATER cache gui commands */ +/* LATER think about resizing scheme. Currently mouse events are not bound + to any part of Scope~'s 'widget' as such, but to a special item, which is + created only for a selected Scope~. For the other scheme see the 'comment' + class (no indicator there, though -- neither a handle, nor a pointer change). + One way or the other, the traffic from the gui layer should be kept possibly + low, at least in run-mode. */ + +#include <stdio.h> +#include <string.h> +#include "m_pd.h" +#include "g_canvas.h" +#include "common/loud.h" +#include "common/grow.h" +#include "unstable/forky.h" +#include "sickle/sic.h" + +//#define SCOPE_DEBUG + +/* these are powers of 2 + margins */ +#define SCOPE_DEFWIDTH 130 /* CHECKED */ +#define SCOPE_MINWIDTH 66 +#define SCOPE_DEFHEIGHT 130 /* CHECKED */ +#define SCOPE_MINHEIGHT 34 +#define SCOPE_DEFPERIOD 256 +#define SCOPE_MINPERIOD 2 +#define SCOPE_MAXPERIOD 8092 +#define SCOPE_DEFBUFSIZE 128 +#define SCOPE_MINBUFSIZE 8 +#define SCOPE_MAXBUFSIZE 800 /* LATER rethink */ +#define SCOPE_WARNBUFSIZE 256 +#define SCOPE_DEFMINVAL -1. +#define SCOPE_DEFMAXVAL 1. +#define SCOPE_DEFDELAY 0 +#define SCOPE_MINDELAY 0 +#define SCOPE_TRIGLINEMODE 0 +#define SCOPE_TRIGUPMODE 1 +#define SCOPE_TRIGDOWNMODE 2 +#define SCOPE_DEFTRIGMODE SCOPE_TRIGLINEMODE +#define SCOPE_MINTRIGMODE SCOPE_TRIGLINEMODE +#define SCOPE_MAXTRIGMODE SCOPE_TRIGDOWNMODE +#define SCOPE_DEFTRIGLEVEL 0. +#define SCOPE_MINCOLOR 0 +#define SCOPE_MAXCOLOR 255 +#define SCOPE_DEFFGRED 102 +#define SCOPE_DEFFGGREEN 255 +#define SCOPE_DEFFGBLUE 51 +#define SCOPE_DEFBGRED 135 +#define SCOPE_DEFBGGREEN 135 +#define SCOPE_DEFBGBLUE 135 +#define SCOPE_SELCOLOR "#8080ff" /* a bit lighter shade of blue */ +#define SCOPE_FGWIDTH 0.7 /* line width is float */ +#define SCOPE_GRIDWIDTH 0.9 +#define SCOPE_SELBDWIDTH 3.0 +#define SCOPEHANDLE_WIDTH 10 /* item size is int */ +#define SCOPEHANDLE_HEIGHT 10 +/* these are performance-related hacks, LATER investigate */ +#define SCOPE_GUICHUNKMONO 16 +#define SCOPE_GUICHUNKXY 32 + +typedef struct _scope +{ + t_sic x_sic; + t_glist *x_glist; + t_canvas *x_canvas; /* also an 'isvised' flag */ + char x_tag[64]; + char x_fgtag[64]; + char x_bgtag[64]; + char x_gridtag[64]; + int x_width; + int x_height; + float x_minval; + float x_maxval; + int x_delay; + int x_trigmode; + float x_triglevel; + unsigned char x_fgred; + unsigned char x_fggreen; + unsigned char x_fgblue; + unsigned char x_bgred; + unsigned char x_bggreen; + unsigned char x_bgblue; + int x_xymode; + float *x_xbuffer; + float *x_ybuffer; + float x_xbufini[SCOPE_DEFBUFSIZE]; + float x_ybufini[SCOPE_DEFBUFSIZE]; + int x_allocsize; + int x_bufsize; + int x_bufphase; + int x_period; + int x_phase; + int x_precount; + int x_retrigger; + float x_ksr; + float x_currx; + float x_curry; + float x_trigx; + int x_frozen; + t_clock *x_clock; + t_pd *x_handle; +} t_scope; + +typedef struct _scopehandle +{ + t_pd h_pd; + t_scope *h_master; + t_symbol *h_bindsym; + char h_pathname[64]; + char h_outlinetag[64]; + int h_dragon; + int h_dragx; + int h_dragy; +} t_scopehandle; + +static t_class *scope_class; +static t_class *scopehandle_class; + +static void scope_clear(t_scope *x, int withdelay) +{ + x->x_bufphase = 0; + x->x_phase = 0; + x->x_precount = (withdelay ? (int)(x->x_delay * x->x_ksr) : 0); + /* CHECKED delay does not matter (refman is wrong) */ + x->x_retrigger = (x->x_trigmode != SCOPE_TRIGLINEMODE); + x->x_trigx = x->x_triglevel; +} + +static t_int *scope_monoperform(t_int *w) +{ + t_scope *x = (t_scope *)(w[1]); + int bufphase = x->x_bufphase; + int bufsize = x->x_bufsize; + if (bufphase < bufsize) + { + int nblock = (int)(w[2]); + if (x->x_precount >= nblock) + x->x_precount -= nblock; + else + { + t_float *in = (t_float *)(w[3]); + int phase = x->x_phase; + int period = x->x_period; + float *bp1 = x->x_xbuffer + bufphase; + float *bp2 = x->x_ybuffer + bufphase; + float currx = x->x_currx; + if (x->x_precount > 0) + { + nblock -= x->x_precount; + in += x->x_precount; + x->x_precount = 0; + } + while (x->x_retrigger) + { + float triglevel = x->x_triglevel; + if (x->x_trigmode == SCOPE_TRIGUPMODE) + { + if (x->x_trigx < triglevel) + { + while (nblock--) if (*in++ >= triglevel) + { + x->x_retrigger = 0; + break; + } + } + else while (nblock--) if (*in++ < triglevel) + { + x->x_trigx = triglevel - 1.; + break; + } + } + else + { + if (x->x_trigx > triglevel) + { + while (nblock--) if (*in++ <= triglevel) + { + x->x_retrigger = 0; + break; + } + } + else while (nblock--) if (*in++ > triglevel) + { + x->x_trigx = triglevel + 1.; + break; + } + } + if (nblock <= 0) + return (w + 4); + } + while (nblock--) + { + if (phase) + { + float f = *in++; + /* CHECKED */ + if ((currx < 0 && (f < currx || f > -currx)) || + (currx > 0 && (f > currx || f < -currx))) + currx = f; + } + else currx = *in++; + if (currx != currx) + currx = 0.; /* CHECKED NaNs bashed to zeros */ + if (++phase == period) + { + phase = 0; + if (++bufphase == bufsize) + { + *bp1 = *bp2 = currx; + clock_delay(x->x_clock, 0); + break; + } + else *bp1++ = *bp2++ = currx; + } + } + x->x_currx = currx; + x->x_bufphase = bufphase; + x->x_phase = phase; + } + } + return (w + 4); +} + +static t_int *scope_xyperform(t_int *w) +{ + t_scope *x = (t_scope *)(w[1]); + int bufphase = x->x_bufphase; + int bufsize = x->x_bufsize; + if (bufphase < bufsize) + { + int nblock = (int)(w[2]); + if (x->x_precount >= nblock) + x->x_precount -= nblock; + else + { + t_float *in1 = (t_float *)(w[3]); + t_float *in2 = (t_float *)(w[4]); + int phase = x->x_phase; + int period = x->x_period; + float freq = 1. / period; + float *bp1 = x->x_xbuffer + bufphase; + float *bp2 = x->x_ybuffer + bufphase; + float currx = x->x_currx; + float curry = x->x_curry; + if (x->x_precount > 0) + { + nblock -= x->x_precount; + in1 += x->x_precount; + in2 += x->x_precount; + x->x_precount = 0; + } + if (x->x_retrigger) + { + /* CHECKME and FIXME */ + x->x_retrigger = 0; + } + while (nblock--) + { + if (phase) + { + /* CHECKME */ + currx += *in1++; + curry += *in2++; + } + else + { + currx = *in1++; + curry = *in2++; + } + if (currx != currx) + currx = 0.; /* CHECKME NaNs bashed to zeros */ + if (curry != curry) + curry = 0.; /* CHECKME NaNs bashed to zeros */ + if (++phase == period) + { + phase = 0; + if (++bufphase == bufsize) + { + *bp1 = currx * freq; + *bp2 = curry * freq; + clock_delay(x->x_clock, 0); + break; + } + else + { + *bp1++ = currx * freq; + *bp2++ = curry * freq; + } + } + } + x->x_currx = currx; + x->x_curry = curry; + x->x_bufphase = bufphase; + x->x_phase = phase; + } + } + return (w + 5); +} + +static void scope_setxymode(t_scope *x, int xymode); + +static void scope_dsp(t_scope *x, t_signal **sp) +{ + x->x_ksr = sp[0]->s_sr * 0.001; + scope_setxymode(x, + forky_hasfeeders((t_object *)x, x->x_glist, 1, &s_signal)); + if (x->x_xymode) + dsp_add(scope_xyperform, 4, x, sp[0]->s_n, sp[0]->s_vec, sp[1]->s_vec); + else + dsp_add(scope_monoperform, 3, x, sp[0]->s_n, sp[0]->s_vec); +} + +static t_canvas *scope_getcanvas(t_scope *x, t_glist *glist) +{ + if (glist != x->x_glist) + { + bug("scope_getcanvas"); + x->x_glist = glist; + } + return (x->x_canvas = glist_getcanvas(glist)); +} + +/* answers the question: ``can we draw and where to?'' */ +static t_canvas *scope_isvisible(t_scope *x) +{ + return (glist_isvisible(x->x_glist) ? x->x_canvas : 0); +} + +static void scope_period(t_scope *x, t_symbol *s, int ac, t_atom *av) +{ + t_float period = (s ? x->x_period : SCOPE_DEFPERIOD); + int result = loud_floatarg(*(t_pd *)x, (s ? 0 : 2), ac, av, &period, + SCOPE_MINPERIOD, SCOPE_MAXPERIOD, + /* LATER rethink warning rules */ + (s ? LOUD_CLIP : LOUD_CLIP | LOUD_WARN), + (s ? 0 : LOUD_WARN), "samples per element"); + if (!s || result == LOUD_ARGOK || result == LOUD_ARGOVER) + { + x->x_period = (int)period; + scope_clear(x, 0); + } +} + +static void scope_float(t_scope *x, t_float f) +{ + t_atom at; + SETFLOAT(&at, f); + scope_period(x, &s_float, 1, &at); +} + +static void scope_bufsize(t_scope *x, t_symbol *s, int ac, t_atom *av) +{ + t_float bufsize = (s ? x->x_bufsize : SCOPE_DEFBUFSIZE); + int result = loud_floatarg(*(t_pd *)x, (s ? 0 : 4), ac, av, &bufsize, + SCOPE_MINBUFSIZE, SCOPE_WARNBUFSIZE, + /* LATER rethink warning rules */ + (s ? LOUD_CLIP : LOUD_CLIP | LOUD_WARN), + (s ? 0 : LOUD_WARN), "display elements"); + if (result == LOUD_ARGOVER) + { + bufsize = (s ? x->x_bufsize : SCOPE_DEFBUFSIZE); + result = loud_floatarg(*(t_pd *)x, (s ? 0 : 4), ac, av, &bufsize, + 0, SCOPE_MAXBUFSIZE, 0, LOUD_CLIP | LOUD_WARN, + "display elements"); + } + if (!s) + { + x->x_allocsize = SCOPE_DEFBUFSIZE; + x->x_bufsize = 0; + x->x_xbuffer = x->x_xbufini; + x->x_ybuffer = x->x_ybufini; + } + if (!s || result == LOUD_ARGOK) + { + int newsize = (int)bufsize; + if (newsize > x->x_allocsize) + { + int nrequested = newsize; + int allocsize = x->x_allocsize; + int oldsize = x->x_bufsize; + x->x_xbuffer = grow_withdata(&nrequested, &oldsize, + &allocsize, x->x_xbuffer, + SCOPE_DEFBUFSIZE, x->x_xbufini, + sizeof(*x->x_xbuffer)); + if (nrequested == newsize) + { + allocsize = x->x_allocsize; + oldsize = x->x_bufsize; + x->x_ybuffer = grow_withdata(&nrequested, &oldsize, + &allocsize, x->x_ybuffer, + SCOPE_DEFBUFSIZE, x->x_ybufini, + sizeof(*x->x_ybuffer)); + } + if (nrequested == newsize) + { + x->x_allocsize = allocsize; + x->x_bufsize = newsize; + } + else + { + if (x->x_xbuffer != x->x_xbufini) + freebytes(x->x_xbuffer, + x->x_allocsize * sizeof(*x->x_xbuffer)); + if (x->x_ybuffer != x->x_ybufini) + freebytes(x->x_ybuffer, + x->x_allocsize * sizeof(*x->x_ybuffer)); + x->x_allocsize = SCOPE_DEFBUFSIZE; + x->x_bufsize = SCOPE_DEFBUFSIZE; + x->x_xbuffer = x->x_xbufini; + x->x_ybuffer = x->x_ybufini; + } + } + else x->x_bufsize = newsize; + scope_clear(x, 0); + } +} + +static void scope_range(t_scope *x, t_symbol *s, int ac, t_atom *av) +{ + t_float minval = (s ? x->x_minval : SCOPE_DEFMINVAL); + t_float maxval = (s ? x->x_maxval : SCOPE_DEFMAXVAL); + loud_floatarg(*(t_pd *)x, (s ? 0 : 5), ac, av, &minval, 0, 0, 0, 0, 0); + loud_floatarg(*(t_pd *)x, (s ? 1 : 6), ac, av, &maxval, 0, 0, 0, 0, 0); + /* CHECKME swapping, ignoring if equal */ + if (minval < maxval) + { + x->x_minval = minval; + x->x_maxval = maxval; + } + else if (minval > maxval) + { + x->x_minval = maxval; + x->x_maxval = minval; + } + else if (!s) + { + x->x_minval = SCOPE_DEFMINVAL; + x->x_maxval = SCOPE_DEFMAXVAL; + } +} + +static void scope_delay(t_scope *x, t_symbol *s, int ac, t_atom *av) +{ + t_float delay = (s ? x->x_delay : SCOPE_DEFDELAY); + int result = loud_floatarg(*(t_pd *)x, (s ? 0 : 7), ac, av, &delay, + SCOPE_MINDELAY, 0, + LOUD_CLIP | LOUD_WARN, 0, "delay"); + if (!s || result == LOUD_ARGOK) + x->x_delay = delay; +} + +static void scope_trigger(t_scope *x, t_symbol *s, int ac, t_atom *av) +{ + t_float trigmode = (s ? x->x_trigmode : SCOPE_DEFTRIGMODE); + loud_floatarg(*(t_pd *)x, (s ? 0 : 9), ac, av, &trigmode, + SCOPE_MINTRIGMODE, SCOPE_MAXTRIGMODE, + LOUD_CLIP | LOUD_WARN, LOUD_CLIP | LOUD_WARN, + "trigger mode"); + x->x_trigmode = (int)trigmode; + if (x->x_trigmode == SCOPE_TRIGLINEMODE) + x->x_retrigger = 0; +} + +static void scope_triglevel(t_scope *x, t_symbol *s, int ac, t_atom *av) +{ + t_float triglevel = (s ? x->x_triglevel : SCOPE_DEFTRIGLEVEL); + loud_floatarg(*(t_pd *)x, (s ? 0 : 10), ac, av, &triglevel, 0, 0, 0, 0, 0); + x->x_triglevel = triglevel; +} + +static void scope_frgb(t_scope *x, t_symbol *s, int ac, t_atom *av) +{ + t_float fgred = (s ? x->x_fgred : SCOPE_DEFFGRED); + t_float fggreen = (s ? x->x_fggreen : SCOPE_DEFFGGREEN); + t_float fgblue = (s ? x->x_fgblue : SCOPE_DEFFGBLUE); + t_canvas *cv; + loud_floatarg(*(t_pd *)x, (s ? 0 : 11), ac, av, &fgred, + SCOPE_MINCOLOR, SCOPE_MAXCOLOR, + LOUD_CLIP | LOUD_WARN, LOUD_CLIP | LOUD_WARN, "color"); + loud_floatarg(*(t_pd *)x, (s ? 1 : 12), ac, av, &fggreen, + SCOPE_MINCOLOR, SCOPE_MAXCOLOR, + LOUD_CLIP | LOUD_WARN, LOUD_CLIP | LOUD_WARN, "color"); + loud_floatarg(*(t_pd *)x, (s ? 2 : 13), ac, av, &fgblue, + SCOPE_MINCOLOR, SCOPE_MAXCOLOR, + LOUD_CLIP | LOUD_WARN, LOUD_CLIP | LOUD_WARN, "color"); + x->x_fgred = (int)fgred; + x->x_fggreen = (int)fggreen; + x->x_fgblue = (int)fgblue; + if (cv = scope_isvisible(x)) + sys_vgui(".x%x.c itemconfigure %s -fill #%2.2x%2.2x%2.2x\n", + cv, x->x_fgtag, x->x_fgred, x->x_fggreen, x->x_fgblue); +} + +static void scope_brgb(t_scope *x, t_symbol *s, int ac, t_atom *av) +{ + t_float bgred = (s ? x->x_bgred : SCOPE_DEFBGRED); + t_float bggreen = (s ? x->x_bggreen : SCOPE_DEFBGGREEN); + t_float bgblue = (s ? x->x_bgblue : SCOPE_DEFBGBLUE); + t_canvas *cv; + loud_floatarg(*(t_pd *)x, (s ? 0 : 14), ac, av, &bgred, + SCOPE_MINCOLOR, SCOPE_MAXCOLOR, + LOUD_CLIP | LOUD_WARN, LOUD_CLIP | LOUD_WARN, "color"); + loud_floatarg(*(t_pd *)x, (s ? 1 : 15), ac, av, &bggreen, + SCOPE_MINCOLOR, SCOPE_MAXCOLOR, + LOUD_CLIP | LOUD_WARN, LOUD_CLIP | LOUD_WARN, "color"); + loud_floatarg(*(t_pd *)x, (s ? 2 : 16), ac, av, &bgblue, + SCOPE_MINCOLOR, SCOPE_MAXCOLOR, + LOUD_CLIP | LOUD_WARN, LOUD_CLIP | LOUD_WARN, "color"); + x->x_bgred = (int)bgred; + x->x_bggreen = (int)bggreen; + x->x_bgblue = (int)bgblue; + if (cv = scope_isvisible(x)) + sys_vgui(".x%x.c itemconfigure %s -fill #%2.2x%2.2x%2.2x\n", + cv, x->x_bgtag, x->x_bgred, x->x_bggreen, x->x_bgblue); +} + +static void scope_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_scope *x = (t_scope *)z; + float x1, y1, x2, y2; + x1 = text_xpix((t_text *)x, glist); + y1 = text_ypix((t_text *)x, glist); + x2 = x1 + x->x_width; + y2 = y1 + x->x_height; + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void scope_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + t_scope *x = (t_scope *)z; + t_text *t = (t_text *)z; + t->te_xpix += dx; + t->te_ypix += dy; + if (glist_isvisible(glist)) + { + t_canvas *cv = scope_getcanvas(x, glist); + sys_vgui(".x%x.c move %s %d %d\n", cv, x->x_tag, dx, dy); + canvas_fixlinesfor(cv, t); + } +} + +static void scope_select(t_gobj *z, t_glist *glist, int state) +{ + t_scope *x = (t_scope *)z; + t_canvas *cv = scope_getcanvas(x, glist); + t_scopehandle *sh = (t_scopehandle *)x->x_handle; + if (state) + { + int x1, y1, x2, y2; + scope_getrect(z, glist, &x1, &y1, &x2, &y2); + + sys_vgui(".x%x.c itemconfigure %s -outline blue -width %f -fill %s\n", + cv, x->x_bgtag, SCOPE_SELBDWIDTH, SCOPE_SELCOLOR); + + sys_vgui("canvas %s -width %d -height %d -bg #fedc00 -bd 0\n", + sh->h_pathname, SCOPEHANDLE_WIDTH, SCOPEHANDLE_HEIGHT); + sys_vgui(".x%x.c create window %f %f -anchor nw\ + -width %d -height %d -window %s -tags %s\n", + cv, x2 - (SCOPEHANDLE_WIDTH - SCOPE_SELBDWIDTH), + y2 - (SCOPEHANDLE_HEIGHT - SCOPE_SELBDWIDTH), + SCOPEHANDLE_WIDTH, SCOPEHANDLE_HEIGHT, + sh->h_pathname, x->x_tag); + sys_vgui("bind %s <Button> {pd [concat %s _click 1 \\;]}\n", + sh->h_pathname, sh->h_bindsym->s_name); + sys_vgui("bind %s <ButtonRelease> {pd [concat %s _click 0 \\;]}\n", + sh->h_pathname, sh->h_bindsym->s_name); + sys_vgui("bind %s <Motion> {pd [concat %s _motion %%x %%y \\;]}\n", + sh->h_pathname, sh->h_bindsym->s_name); + } + else + { + sys_vgui(".x%x.c itemconfigure %s -outline black -width %f\ + -fill #%2.2x%2.2x%2.2x\n", cv, x->x_bgtag, SCOPE_GRIDWIDTH, + x->x_bgred, x->x_bggreen, x->x_bgblue); + sys_vgui("destroy %s\n", sh->h_pathname); + } +} + +static void scope_delete(t_gobj *z, t_glist *glist) +{ + canvas_deletelinesfor(glist, (t_text *)z); +} + +static void scope_drawfgmono(t_scope *x, t_canvas *cv, + int x1, int y1, int x2, int y2) +{ + int i; + float dx, dy, xx, yy, sc; + float *bp; + dx = (float)(x2 - x1) / (float)x->x_bufsize; + sc = ((float)x->x_height - 2.) / (float)(x->x_maxval - x->x_minval); + sys_vgui(".x%x.c create line \\\n", cv); + for (i = 0, xx = x1, bp = x->x_xbuffer; + i < x->x_bufsize; i++, xx += dx, bp++) + { + yy = (y2 - 1) - sc * (*bp - x->x_minval); +#ifndef SCOPE_DEBUG + if (yy > y2) yy = y2; else if (yy < y1) yy = y1; +#endif + sys_vgui("%d %d \\\n", (int)xx, (int)yy); + } + sys_vgui("-fill #%2.2x%2.2x%2.2x -width %f -tags {%s %s}\n", + x->x_fgred, x->x_fggreen, x->x_fgblue, + SCOPE_FGWIDTH, x->x_fgtag, x->x_tag); + + /* margin lines: masking overflows, so that they appear as gaps, + rather than clipped signal values, LATER rethink */ + sys_vgui(".x%x.c create line %d %d %d %d\ + -fill #%2.2x%2.2x%2.2x -width %f -tags {%s %s}\n", + cv, x1, y1, x2, y1, x->x_bgred, x->x_bggreen, x->x_bgblue, + 1., x->x_fgtag, x->x_tag); + sys_vgui(".x%x.c create line %d %d %d %d\ + -fill #%2.2x%2.2x%2.2x -width %f -tags {%s %s}\n", + cv, x1, y2, x2, y2, x->x_bgred, x->x_bggreen, x->x_bgblue, + 1., x->x_fgtag, x->x_tag); +} + +static void scope_drawfgxy(t_scope *x, t_canvas *cv, + int x1, int y1, int x2, int y2) +{ + int nleft = x->x_bufsize; + float *xbp = x->x_xbuffer, *ybp = x->x_ybuffer; + char chunk[200 * SCOPE_GUICHUNKXY]; /* LATER estimate */ + char *chunkp = chunk; + char cmd1[64], cmd2[64]; + float xx, yy, xsc, ysc; + xx = yy = 0; + /* subtract 1-pixel margins, see below */ + xsc = ((float)x->x_width - 2.) / (float)(x->x_maxval - x->x_minval); + ysc = ((float)x->x_height - 2.) / (float)(x->x_maxval - x->x_minval); + sprintf(cmd1, ".x%x.c create line", (int)cv); + sprintf(cmd2, "-fill #%2.2x%2.2x%2.2x -width %f -tags {%s %s}\n ", + x->x_fgred, x->x_fggreen, x->x_fgblue, + SCOPE_FGWIDTH, x->x_fgtag, x->x_tag); + while (nleft > SCOPE_GUICHUNKXY) + { + int i = SCOPE_GUICHUNKXY; + while (i--) + { + float oldx = xx, oldy = yy, dx, dy; + xx = x1 + xsc * (*xbp++ - x->x_minval); + yy = y2 - ysc * (*ybp++ - x->x_minval); + /* using 1-pixel margins */ + dx = (xx > oldx ? 1. : -1.); + dy = (yy > oldy ? 1. : -1.); +#ifndef SCOPE_DEBUG + if (xx < x1 || xx > x2 || yy < y1 || yy > y2) + continue; +#endif + sprintf(chunkp, "%s %d %d %d %d %s", cmd1, + (int)(xx - dx), (int)(yy - dy), + (int)(xx + dx), (int)(yy + dy), cmd2); + chunkp += strlen(chunkp); + } + if (chunkp > chunk) + sys_gui(chunk); + chunkp = chunk; + nleft -= SCOPE_GUICHUNKXY; + } + while (nleft--) + { + float oldx = xx, oldy = yy, dx, dy; + xx = x1 + xsc * (*xbp++ - x->x_minval); + yy = y2 - ysc * (*ybp++ - x->x_minval); + /* using 1-pixel margins */ + dx = (xx > oldx ? 1. : -1.); + dy = (yy > oldy ? 1. : -1.); +#ifndef SCOPE_DEBUG + if (xx < x1 || xx > x2 || yy < y1 || yy > y2) + continue; +#endif + sprintf(chunkp, "%s %d %d %d %d %s", cmd1, + (int)(xx - dx), (int)(yy - dy), + (int)(xx + dx), (int)(yy + dy), cmd2); + chunkp += strlen(chunkp); + } + if (chunkp > chunk) + sys_gui(chunk); +} + +static void scope_drawbg(t_scope *x, t_canvas *cv, + int x1, int y1, int x2, int y2) +{ + int i; + float dx, dy, xx, yy; + dx = (x2 - x1) * 0.125; + dy = (y2 - y1) * 0.25; + sys_vgui(".x%x.c create rectangle %d %d %d %d\ + -fill #%2.2x%2.2x%2.2x -width %f -tags {%s %s}\n", + cv, x1, y1, x2, y2, + x->x_bgred, x->x_bggreen, x->x_bgblue, + SCOPE_GRIDWIDTH, x->x_bgtag, x->x_tag); + for (i = 0, xx = x1 + dx; i < 7; i++, xx += dx) + sys_vgui(".x%x.c create line %f %d %f %d\ + -width %f -tags {%s %s}\n", cv, xx, y1, xx, y2, + SCOPE_GRIDWIDTH, x->x_gridtag, x->x_tag); + for (i = 0, yy = y1 + dy; i < 3; i++, yy += dy) + sys_vgui(".x%x.c create line %d %f %d %f\ + -width %f -tags {%s %s}\n", cv, x1, yy, x2, yy, + SCOPE_GRIDWIDTH, x->x_gridtag, x->x_tag); +} + +static void scope_drawmono(t_scope *x, t_canvas *cv) +{ + int x1, y1, x2, y2; + scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2); + scope_drawbg(x, cv, x1, y1, x2, y2); + scope_drawfgmono(x, cv, x1, y1, x2, y2); +} + +static void scope_redrawmono(t_scope *x, t_canvas *cv) +{ + int nleft = x->x_bufsize; + float *bp = x->x_xbuffer; + char chunk[32 * SCOPE_GUICHUNKMONO]; /* LATER estimate */ + char *chunkp = chunk; + int x1, y1, x2, y2; + float dx, dy, xx, yy, sc; + scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2); + dx = (float)(x2 - x1) / (float)x->x_bufsize; + sc = ((float)x->x_height - 2.) / (float)(x->x_maxval - x->x_minval); + xx = x1; + sys_vgui(".x%x.c coords %s \\\n", cv, x->x_fgtag); + while (nleft > SCOPE_GUICHUNKMONO) + { + int i = SCOPE_GUICHUNKMONO; + while (i--) + { + yy = (y2 - 1) - sc * (*bp++ - x->x_minval); +#ifndef SCOPE_DEBUG + if (yy > y2) yy = y2; else if (yy < y1) yy = y1; +#endif + sprintf(chunkp, "%d %d ", (int)xx, (int)yy); + chunkp += strlen(chunkp); + xx += dx; + } + strcpy(chunkp, "\\\n"); + sys_gui(chunk); + chunkp = chunk; + nleft -= SCOPE_GUICHUNKMONO; + } + while (nleft--) + { + yy = (y2 - 1) - sc * (*bp++ - x->x_minval); +#ifndef SCOPE_DEBUG + if (yy > y2) yy = y2; else if (yy < y1) yy = y1; +#endif + sprintf(chunkp, "%d %d ", (int)xx, (int)yy); + chunkp += strlen(chunkp); + xx += dx; + } + strcpy(chunkp, "\n"); + sys_gui(chunk); +} + +static void scope_drawxy(t_scope *x, t_canvas *cv) +{ + int x1, y1, x2, y2; + scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2); + scope_drawbg(x, cv, x1, y1, x2, y2); + scope_drawfgxy(x, cv, x1, y1, x2, y2); +} + +static void scope_redrawxy(t_scope *x, t_canvas *cv) +{ + int x1, y1, x2, y2; + scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2); + sys_vgui(".x%x.c delete %s\n", cv, x->x_fgtag); + scope_drawfgxy(x, cv, x1, y1, x2, y2); +} + +static void scope_revis(t_scope *x, t_canvas *cv) +{ + sys_vgui(".x%x.c delete %s\n", cv, x->x_tag); + if (x->x_xymode) + scope_drawxy(x, cv); + else + scope_drawmono(x, cv); +} + +static void scope_vis(t_gobj *z, t_glist *glist, int vis) +{ + t_scope *x = (t_scope *)z; + t_text *t = (t_text *)z; + t_canvas *cv = scope_getcanvas(x, glist); + if (vis) + { + t_scopehandle *sh = (t_scopehandle *)x->x_handle; +#ifndef PD_MINOR_VERSION + rtext_new(glist, t, glist->gl_editor->e_rtext, 0); +#endif + sprintf(sh->h_pathname, ".x%x.h%x", (int)cv, (int)sh); + if (x->x_xymode) + scope_drawxy(x, cv); + else + scope_drawmono(x, cv); + } + else + { +#ifndef PD_MINOR_VERSION + t_rtext *rt = glist_findrtext(glist, t); + if (rt) rtext_free(rt); +#endif + sys_vgui(".x%x.c delete %s\n", cv, x->x_tag); + x->x_canvas = 0; + } +} + +static int scope_click(t_gobj *z, t_glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, + int doit) +{ + t_scope *x = (t_scope *)z; + x->x_frozen = doit; + return (CURSOR_RUNMODE_CLICKME); +} + +/* CHECKED there is only one copy of state variables, + the same, whether modified with messages, or in the inspector */ +static void scope_save(t_gobj *z, t_binbuf *b) +{ + t_scope *x = (t_scope *)z; + t_text *t = (t_text *)x; + binbuf_addv(b, "ssiisiiiiiffififiiiiiii;", gensym("#X"), gensym("obj"), + (int)t->te_xpix, (int)t->te_ypix, + gensym("Scope~"), + x->x_width, x->x_height, x->x_period, 3, x->x_bufsize, + x->x_minval, x->x_maxval, x->x_delay, 0., + x->x_trigmode, x->x_triglevel, + x->x_fgred, x->x_fggreen, x->x_fgblue, + x->x_bgred, x->x_bggreen, x->x_bgblue, 0); +} + +static t_widgetbehavior scope_widgetbehavior = +{ + scope_getrect, + scope_displace, + scope_select, + 0, + scope_delete, + scope_vis, + scope_click, + scope_save, + 0 +}; + +static void scope_setxymode(t_scope *x, int xymode) +{ + if (xymode != x->x_xymode) + { + t_canvas *cv; + if (cv = scope_isvisible(x)) + { + sys_vgui(".x%x.c delete %s\n", cv, x->x_fgtag); + if (!xymode) + { + int x1, y1, x2, y2; + scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2); + scope_drawfgmono(x, cv, x1, y1, x2, y2); + } + } + x->x_xymode = xymode; + scope_clear(x, 0); + } +} + +static void scope_tick(t_scope *x) +{ + t_canvas *cv; + if (!x->x_frozen && (cv = scope_isvisible(x))) + { + if (x->x_xymode) + scope_redrawxy(x, cv); + else + scope_redrawmono(x, cv); + } + scope_clear(x, 1); +} + +static void scopehandle__clickhook(t_scopehandle *sh, t_floatarg f) +{ + int newstate = (int)f; + if (sh->h_dragon && newstate == 0) + { + t_scope *x = sh->h_master; + t_canvas *cv; + x->x_width += sh->h_dragx; + x->x_height += sh->h_dragy; + if (cv = scope_isvisible(x)) + { + sys_vgui(".x%x.c delete %s\n", cv, sh->h_outlinetag); + scope_revis(x, cv); + sys_vgui("destroy %s\n", sh->h_pathname); + scope_select((t_gobj *)x, x->x_glist, 1); + canvas_fixlinesfor(x->x_glist, (t_text *)x); /* 2nd inlet */ + } + } + else if (!sh->h_dragon && newstate) + { + t_scope *x = sh->h_master; + t_canvas *cv; + if (cv = scope_isvisible(x)) + { + int x1, y1, x2, y2; + scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2); + sys_vgui("lower %s\n", sh->h_pathname); + sys_vgui(".x%x.c create rectangle %d %d %d %d\ + -outline blue -width %f -tags %s\n", + cv, x1, y1, x2, y2, SCOPE_SELBDWIDTH, sh->h_outlinetag); + } + sh->h_dragx = 0; + sh->h_dragy = 0; + } + sh->h_dragon = newstate; +} + +static void scopehandle__motionhook(t_scopehandle *sh, + t_floatarg f1, t_floatarg f2) +{ + if (sh->h_dragon) + { + t_scope *x = sh->h_master; + int dx = (int)f1, dy = (int)f2; + int x1, y1, x2, y2, newx, newy; + scope_getrect((t_gobj *)x, x->x_glist, &x1, &y1, &x2, &y2); + newx = x2 + dx; + newy = y2 + dy; + if (newx > x1 + SCOPE_MINWIDTH && newy > y1 + SCOPE_MINHEIGHT) + { + t_canvas *cv; + if (cv = scope_isvisible(x)) + sys_vgui(".x%x.c coords %s %d %d %d %d\n", + cv, sh->h_outlinetag, x1, y1, newx, newy); + sh->h_dragx = dx; + sh->h_dragy = dy; + } + } +} + +static void scope_free(t_scope *x) +{ + if (x->x_clock) clock_free(x->x_clock); + if (x->x_xbuffer != x->x_xbufini) + freebytes(x->x_xbuffer, x->x_allocsize * sizeof(*x->x_xbuffer)); + if (x->x_ybuffer != x->x_ybufini) + freebytes(x->x_ybuffer, x->x_allocsize * sizeof(*x->x_ybuffer)); + if (x->x_handle) + { + pd_unbind(x->x_handle, ((t_scopehandle *)x->x_handle)->h_bindsym); + pd_free(x->x_handle); + } +} + +static void *scope_new(t_symbol *s, int ac, t_atom *av) +{ + t_scope *x = (t_scope *)pd_new(scope_class); + t_scopehandle *sh; + t_float width = SCOPE_DEFWIDTH; + t_float height = SCOPE_DEFHEIGHT; + char buf[64]; + x->x_glist = canvas_getcurrent(); + x->x_canvas = 0; + loud_floatarg(*(t_pd *)x, 0, ac, av, &width, + SCOPE_MINWIDTH, 0, + LOUD_CLIP | LOUD_WARN, 0, "width"); + x->x_width = (int)width; + loud_floatarg(*(t_pd *)x, 1, ac, av, &height, + SCOPE_MINHEIGHT, 0, + LOUD_CLIP | LOUD_WARN, 0, "height"); + x->x_height = (int)height; + scope_period(x, 0, ac, av); + /* CHECKME 6th argument (default 3 for mono, 1 for xy */ + scope_bufsize(x, 0, ac, av); + scope_range(x, 0, ac, av); + scope_delay(x, 0, ac, av); + /* CHECKME 11th argument (default 0.) */ + scope_trigger(x, 0, ac, av); + scope_triglevel(x, 0, ac, av); + scope_frgb(x, 0, ac, av); + scope_brgb(x, 0, ac, av); + /* CHECKME last argument (default 0) */ + + sprintf(x->x_tag, "all%x", (int)x); + sprintf(x->x_bgtag, "bg%x", (int)x); + sprintf(x->x_gridtag, "gr%x", (int)x); + sprintf(x->x_fgtag, "fg%x", (int)x); + x->x_xymode = 0; + x->x_ksr = sys_getsr() * 0.001; /* redundant */ + x->x_frozen = 0; + inlet_new((t_object *)x, (t_pd *)x, &s_signal, &s_signal); + x->x_clock = clock_new(x, (t_method)scope_tick); + scope_clear(x, 0); + + x->x_handle = pd_new(scopehandle_class); + sh = (t_scopehandle *)x->x_handle; + sh->h_master = x; + sprintf(buf, "_h%x", (int)sh); + pd_bind(x->x_handle, sh->h_bindsym = gensym(buf)); + sprintf(sh->h_outlinetag, "h%x", (int)sh); + sh->h_dragon = 0; + return (x); +} + +void Scope_tilde_setup(void) +{ + scope_class = class_new(gensym("Scope~"), + (t_newmethod)scope_new, + (t_method)scope_free, + sizeof(t_scope), 0, A_GIMME, 0); + sic_setup(scope_class, scope_dsp, scope_float); + class_addmethod(scope_class, (t_method)scope_bufsize, + gensym("bufsize"), A_GIMME, 0); + class_addmethod(scope_class, (t_method)scope_range, + gensym("range"), A_GIMME, 0); + class_addmethod(scope_class, (t_method)scope_delay, + gensym("delay"), A_GIMME, 0); + class_addmethod(scope_class, (t_method)scope_trigger, + gensym("trigger"), A_GIMME, 0); + class_addmethod(scope_class, (t_method)scope_triglevel, + gensym("triglevel"), A_GIMME, 0); + class_addmethod(scope_class, (t_method)scope_frgb, + gensym("frgb"), A_GIMME, 0); + class_addmethod(scope_class, (t_method)scope_brgb, + gensym("brgb"), A_GIMME, 0); + class_addmethod(scope_class, (t_method)scope_click, + gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_setwidget(scope_class, &scope_widgetbehavior); + scopehandle_class = class_new(gensym("_scopehandle"), 0, 0, + sizeof(t_scopehandle), CLASS_PD, 0); + class_addmethod(scopehandle_class, (t_method)scopehandle__clickhook, + gensym("_click"), A_FLOAT, 0); + class_addmethod(scopehandle_class, (t_method)scopehandle__motionhook, + gensym("_motion"), A_FLOAT, A_FLOAT, 0); +} |