/* Copyright (c) 2002-2005 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 #include #include "m_pd.h" #include "g_canvas.h" #include "common/loud.h" #include "common/grow.h" #include "common/fitter.h" #include "unstable/forky.h" #include "sickle/sic.h" #ifdef KRZYSZCZ //#define SCOPE_DEBUG #endif /* 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) { loudbug_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), 0, "samples per element"); if (!s && result == LOUD_ARGOVER) fittermax_warning(*(t_pd *)x, "more than %g samples per element requested", SCOPE_MAXPERIOD); 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), 0, "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 && result == LOUD_ARGOK) fittermax_warning(*(t_pd *)x, "more than %g display elements requested", SCOPE_WARNBUFSIZE); } 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%lx.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%lx.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%lx.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%lx.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%lx.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