#include #include #include #include #include #include #include "builtins.h" #include "blocktype.h" typedef struct priv PRIV; typedef struct sb SB; typedef struct sbr SBR; typedef struct par PAR; struct par { double attack; double hold; double decay; } ; struct priv { PAR par; int a_s; int h_s; int d_s; int s; double last; double ascale; double dscale; } ; struct sb { void (*cont)(void *, void *); void *contarg; PAR p; } ; struct sbr { void (*cont)(void *, int); void *contarg; void *priv; PAR p; } ; static int my_w = -1; static int my_h; static int label_x; static int attack_x; static int hold_x; static int decay_x; static int par_x; static int out_x; static BLOCKDIMS size(BLOCK_SIZE_ARGS) { int w_label; int w_time; int w_attack; int w_hold; int w_decay; int w_in; int w_out; int w; int o; if (my_w < 0) { w_label = widthof("Envelope",8); w_time = widthof("000000",6); w_attack = widthof("Attack:",7); w_hold = widthof("Hold:",5); w_decay = widthof("Decay:",6); w = (w_attack > w_hold) ? w_attack : w_hold; if (w_decay > w) w = w_decay; w_in = widthof("Trig",4); w_out = widthof("Out",3); if (w_label > w + font_space + w_time) { my_w = w_in + w_label + w_out + (4 * font_space); o = (w_label - (w + font_space + w_time)) / 2; } else { my_w = w_in + w + w_time + w_out + (5 * font_space); o = 0; } label_x = ((my_w - w_in - w_out - w_label) / 2) + w_in; out_x = my_w - w_out - font_space - o; par_x = out_x - font_space - w_time; attack_x = par_x - font_space - w_attack; hold_x = par_x - font_space - w_hold; decay_x = par_x - font_space - w_decay; my_h = 5 * font_baselineskip; } return((BLOCKDIMS){.w=my_w,.h=my_h}); } static void setup_cont(int ok, void *sbv) { SB sb; PRIV *p; sb = *(SB *)sbv; free(sbv); if (ok) { p = malloc(sizeof(PRIV)); p->par = sb.p; } else { p = 0; } (*sb.cont)(p,sb.contarg); } static void setup_decay(int ok, void *sbv) { if (ok) { setup_getarg("Decay time",&setup_cont,sbv,&((SB *)sbv)->p.decay,PT(PAR_TIME)); } else { setup_cont(0,sbv); } } static void setup_hold(int ok, void *sbv) { if (ok) { setup_getarg("Hold time",&setup_decay,sbv,&((SB *)sbv)->p.hold,PT(PAR_TIME)); } else { setup_cont(0,sbv); } } static void setup(BLOCK_SETUP_ARGS) { SB *sb; sb = malloc(sizeof(SB)); sb->cont = cont; sb->contarg = contarg; setup_getarg("Attack time",&setup_hold,sb,&sb->p.attack,PT(PAR_TIME)); } static void resetup_cont(int ok, void *sbrv) { SBR sbr; sbr = *(SBR *)sbrv; free(sbrv); if (ok) ((PRIV *)sbr.priv)->par = sbr.p; (*sbr.cont)(sbr.contarg,ok); } static void resetup_decay(int ok, void *sbrv) { if (ok) { setup_getarg("Decay time",&resetup_cont,sbrv,&((SBR *)sbrv)->p.decay,PTD(PAR_TIME)); } else { resetup_cont(0,sbrv); } } static void resetup_hold(int ok, void *sbrv) { if (ok) { setup_getarg("Hold time",&resetup_decay,sbrv,&((SBR *)sbrv)->p.hold,PTD(PAR_TIME)); } else { resetup_cont(0,sbrv); } } static void resetup(BLOCK_RESETUP_ARGS) { SBR *sbr; sbr = malloc(sizeof(SBR)); sbr->cont = cont; sbr->contarg = contarg; sbr->priv = priv; sbr->p = ((PRIV *)priv)->par; setup_getarg("Attack time",&resetup_hold,sbr,&sbr->p.attack,PTD(PAR_TIME)); } static void destroy(BLOCK_DESTROY_ARGS) { free(priv); } static int format_time(char *s, double t) { int l; if (t < 9.99994) { l = sprintf(s,"%6.4f",t); } else if (t < 99.9994) { l = sprintf(s,"%6.3f",t); } else if (t < 999.994) { l = sprintf(s,"%6.2f",t); } else if (t < 9999.94) { l = sprintf(s,"%6.1f",t); } else { l = sprintf(s,"%d",(int)rint(t)); } while ((l > 0) && (s[l-1] == '0')) l --; if ((l > 0) && (s[l-1] == '.')) l --; if (l == 0) s[l++] = '0'; return(l); } static void render(BLOCK_RENDER_ARGS) { PRIV *p; int y; char s[16]; int l; p = priv; XFillRectangle(disp,d,gc_bg,0,0,my_w,my_h); XDrawLine(disp,d,gc_fg,0,0,my_w-2,0); XDrawLine(disp,d,gc_fg,my_w-1,0,my_w-1,my_h-2); XDrawLine(disp,d,gc_fg,my_w-1,my_h-1,1,my_h-1); XDrawLine(disp,d,gc_fg,0,my_h-1,0,1); y = font_baseline + (font_baselineskip / 2); XDrawString(disp,d,gc_fg,label_x,y,"Envelope",8); y += font_baselineskip; XDrawString(disp,d,gc_fg,attack_x,y,"Attack",6); l = format_time(&s[0],p->par.attack); XDrawString(disp,d,gc_fg,par_x,y,&s[0],l); y += font_baselineskip; XDrawString(disp,d,gc_fg,hold_x,y,"Hold",4); l = format_time(&s[0],p->par.hold); XDrawString(disp,d,gc_fg,par_x,y,&s[0],l); y += font_baselineskip; XDrawString(disp,d,gc_fg,decay_x,y,"Decay",5); l = format_time(&s[0],p->par.decay); XDrawString(disp,d,gc_fg,par_x,y,&s[0],l); y = font_baseline + (2 * font_baselineskip); XDrawString(disp,d,gc_fg,font_space,y,"Trig",4); XDrawString(disp,d,gc_fg,out_x,y,"Out",3); } static int iny(BLOCK_INPUT_Y_ARGS) { return((5*font_baselineskip)/2); } static int outy(BLOCK_OUTPUT_Y_ARGS) { return((5*font_baselineskip)/2); } static void rinit(BLOCK_RUN_INIT_ARGS) { PRIV *p; p = priv; p->s = -1; p->last = 0; p->a_s = p->par.attack * 44100; p->h_s = p->a_s + (int)(p->par.hold * 44100); p->d_s = p->h_s + (int)(p->par.decay * 44100); if (p->a_s > 0) p->ascale = 1.0 / p->a_s; if (p->d_s > p->h_s) p->dscale = 1.0 / (p->d_s - p->h_s); } static void rstep(BLOCK_RUN_STEP_ARGS) { PRIV *p; p = priv; if (p->s < 0) { if ((p->last <= 0) && (ins[0] > 0)) { p->s = 0; } } else { p->s ++; if (p->s >= p->d_s) p->s = -1; } p->last = ins[0]; } static double out(BLOCK_RUN_OUT_ARGS) { PRIV *p; p = priv; if (p->s < 0) return(0); if (p->s < p->a_s) return(p->s*p->ascale); if (p->s < p->h_s) return(1); if (p->s < p->d_s) return(1-((p->s-p->h_s)*p->dscale)); return(0); } static const char *ins[] = { "Trig" }; static const char *outs[] = { "Out" }; static BLOCKPARAM params[] = { { .name = "Attack", .type = PAR_TIME }, { .name = "Hold", .type = PAR_TIME }, { .name = "Decay", .type = PAR_TIME } }; BLOCKTYPE block_envelope = BLOCKTYPE_I_O_P("Envelope",ins,outs,params,size,setup,resetup,destroy,render,iny,outy,rinit,rstep,out);