aboutsummaryrefslogtreecommitdiff
path: root/cyclone/sickle/Scope.c
diff options
context:
space:
mode:
Diffstat (limited to 'cyclone/sickle/Scope.c')
-rw-r--r--cyclone/sickle/Scope.c1043
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);
+}