/* MIXER -- Programmable Control panel for IOTA Midi-Fader ** Output is "raw" MIDI (i.e. w/o time tags) and goes to stdout ** psl 10/87 */ #include #include #include #include #include #include #include #define PIX_INV PIX_NOT(PIX_DST) #define PIX_XOR (PIX_SRC^PIX_DST) #define PIX_OR (PIX_SRC|PIX_DST) #define POINT2(PW,DX,DY,OP) pw_vector(PW,DX,DY,DX,DY,OP,1) #define HLINER(PW,X0,Y0,X1,OP) pw_vector(PW,X0,Y0,X1,Y0,OP,1) #define DEFCHAN 1 /* default channel for Iota(s) */ #define MAXSLIDE 32 /* how many sliders we can display */ #define MAXFADE 32 /* how many faders we can control */ #define DEFFADE 24 /* default number of faders */ #define F2SXU(F) (F/8) /* convert fader # to sys.excl. unit # */ #define FCTRL0 32 /* first fader ctrl number */ #define MCTRL0 64 /* first mute ctrl number */ #define INIT 1 /* menu entry values */ #define SAVE 2 #define RESTORE 3 #define ALLUP 4 #define ALLDOWN 5 #define QUIT 6 #define MUTE 1 /* values for Spmode[] & for Wfbu */ #define SOLO 2 struct fstr { int siz; /* based on button radius */ char *nam; /* font name */ } Fs[] = { 20, "/usr/lib/fonts/fixedwidthfonts/cour.b.24", 16, "/usr/lib/fonts/fixedwidthfonts/cour.b.18", 12, "/usr/lib/fonts/fixedwidthfonts/cour.b.16", 9, "/usr/lib/fonts/fixedwidthfonts/cour.b.12", 0, "/usr/lib/fonts/fixedwidthfonts/sail.r.6", }; #define DOWN6DB 103 /* default initial setting */ int Ticks[] = { 127, 47, 15, 2, /* 24 db points */ 79, 27, 5, /* 12 db points */ 103, 59, 35, 21, 9, 3, /* 6 db points */ 0, }; struct statestr { /* current control state */ int _Slide[MAXSLIDE]; /* slider settings 0-127 */ int _Spsolo[MAXSLIDE]; /* slide pot solo buttons */ int _Spmute[MAXSLIDE]; /* slide pot mute buttons */ int _Fsolo[MAXSLIDE]; /* fader solo buttons */ int _Fmute[MAXFADE]; /* fader mute buttons */ } Curstate; #define Slide Curstate._Slide #define Spsolo Curstate._Spsolo #define Spmute Curstate._Spmute #define Fsolo Curstate._Fsolo #define Fmute Curstate._Fmute char *Sfilfmt = "/tmp/mixstate%d"; int Dchange = 0; /* display changes */ int Xchange = 0; /* transmit changes */ int Showfm = 1; /* show the fader monitor */ int Ofh = -1; /* where Iota commands go to */ int Chan; /* Iota's MIDI channel */ int Map[MAXFADE][MAXSLIDE]; /* how sliders affect faders */ int Nslide; /* how many sliders we actually need */ int Nfade; /* how many faders we actually need */ int Fmlx, Fmhx, Fmly, Fmhy; /* bounds of fader monitor area */ int Fms = 1; /* fader monitor includes solo buttons */ int Fmm = 1; /* fader monitor includes mute buttons */ int Fmh; /* fader monitor height */ int Fmy; /* fader monitor (top) y */ int Fmdx; /* fader monitor x separation */ int Fmw; /* fader monitor width */ int Fmx[MAXFADE]; /* fader monitor (center) x */ int Splx, Sphx, Sply, Sphy; /* bounds of slide pot area */ int Spused[MAXSLIDE]; /* which sliders are actually in use */ int Fused[MAXSLIDE]; /* which faders are actually in use */ int Spsg[MAXSLIDE]; /* slide pot solo group */ int Spmode[MAXSLIDE]; /* solo or mute priority? */ int Fade[MAXFADE]; /* fader settings 0-127 */ int Um[MAXFADE]; /* Iota fader mutes, 0=>muted */ int Sph; /* slide pot height */ int Spy; /* slide pot (top) y */ int Spdx; /* slide pot x separation */ int Spx[MAXSLIDE]; /* slide pot (center) x */ int Sy; /* solo button y */ int Fmsy; /* fader monitor solo button y */ int Sdx; /* solo button x offset from Spx[] */ int My; /* mute button y */ int Fmmy; /* fader monitor mute button y */ int Mdx; /* mute button x offset from Spx[] */ int Br; /* button radius */ int Fmbr; /* fader monitor button radius */ int Spw; /* slide pot width */ int Spw2; /* slide pot half-width */ int Mouseown = -1; /* slider that "claims" the mouse */ int Wfbu = 0; /* "Waiting For Button Up" */ Frame Dframe; /* whole display's frame */ Canvas Dcanvas; /* whole display's canvas */ Pixwin *Dpw = 0; /* whole display pixwin */ Menu Rmenu; /* right mouse button menu */ Pixrect *Grey; /* grey texture */ Pixrect *Ltgrey; /* light grey texture */ Pixrect *Dkgrey; /* dark grey texture */ struct pixfont *Fontp; static short Grey_bits[] = { 0xAAAA, 0x5555, 0xAAAA, 0x5555, 0xAAAA, 0x5555, 0xAAAA, 0x5555, 0xAAAA, 0x5555, 0xAAAA, 0x5555, 0xAAAA, 0x5555, 0xAAAA, 0x5555, }; static short Ltgrey_bits[] = { 0x4444, 0x1111, 0x4444, 0x1111, 0x4444, 0x1111, 0x4444, 0x1111, 0x4444, 0x1111, 0x4444, 0x1111, 0x4444, 0x1111, 0x4444, 0x1111, }; static short Dkgrey_bits[] = { 0x7777, 0xDDDD, 0x7777, 0xDDDD, 0x7777, 0xDDDD, 0x7777, 0xDDDD, 0x7777, 0xDDDD, 0x7777, 0xDDDD, 0x7777, 0xDDDD, 0x7777, 0xDDDD, }; static short Mixicon_bits[] = { /* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16 */ 0x3FFF,0xFFFF,0xFFFF,0xFFFF,0x6000,0x0000,0x0000,0x0001, 0xE800,0x0000,0x0000,0x0001,0xB800,0x0000,0x001F,0x83F1, 0xA3FF,0xFFFF,0xFE3F,0xC7F9,0xA272,0xA32A,0x6230,0xC619, 0xA2AA,0xB2AA,0xA621,0x4429,0xA2AA,0xAAAA,0xAA22,0x4449, 0xA3FF,0xFFFF,0xFE24,0x4489,0xA000,0x0000,0x0036,0xC6D9, 0xA000,0x0000,0x002F,0x45E9,0xA155,0x5555,0x543F,0xC7F9, 0xA000,0x0000,0x0000,0x0001,0xA000,0x0000,0x0000,0x0001, 0xA1DD,0xDDDD,0xDC66,0x6661,0xA155,0x5555,0x5466,0x6661, 0xA1DD,0xDDDD,0xDC00,0x0001,0xA000,0x0000,0x0066,0x6661, 0xA000,0x0000,0x0066,0x6661,0xA1DD,0xDDDD,0xDC00,0x0001, 0xA155,0x5555,0x5466,0x6661,0xA1DD,0xDDDD,0xDC66,0x6661, 0xA000,0x0000,0x0000,0x0001,0xA000,0x0000,0x0000,0x0001, 0xA000,0x0000,0x0006,0x00C1,0xA199,0x9999,0x9882,0x1081, 0xA088,0x8888,0x8804,0x0041,0xA088,0x8888,0x8882,0x1081, 0xA088,0x8888,0x8884,0x1041,0xA089,0xC888,0x8802,0x0081, 0xA089,0xC888,0x8884,0x1041,0xA09C,0x8888,0x8802,0x0081, 0xA1DD,0x9999,0x989F,0x93F1,0xA1C8,0x89C8,0x880F,0x01E1, 0xA088,0x89C8,0x9C84,0x1041,0xA088,0x8888,0x9C02,0x0081, 0xA088,0x9C89,0xC804,0x0041,0xA088,0x9C89,0xC802,0x0081, 0xA088,0x8888,0x8884,0x1041,0xA199,0x9999,0x9942,0x3881, 0xA088,0x8888,0x8884,0x1041,0xA088,0x8888,0x8802,0x0081, 0xA088,0x8888,0x8804,0x0041,0xA088,0x8888,0x8882,0x1081, 0xA088,0x8888,0x89C4,0x2841,0xA088,0x8888,0x8882,0x1081, 0xA199,0x9999,0x9804,0x0041,0xA088,0x8888,0x8802,0x0081, 0xA088,0x8888,0x8884,0x1041,0xA088,0x8888,0x8942,0x2881, 0xA088,0x8888,0x8884,0x1041,0xA088,0x889C,0x8802,0x0081, 0xA199,0x999D,0x9806,0x00C1,0xA000,0x0000,0x0000,0x0001, 0xE000,0x0000,0x0000,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF, 0xC000,0x0000,0x0000,0x0003,0x8000,0x2272,0x4E70,0x0001, 0x9111,0x3622,0xC849,0x1111,0xAAAA,0xBE21,0x8C72,0xAAA9, 0x8444,0x2A23,0x4850,0x4441,0x8000,0x2272,0x4E48,0x0001, 0xC000,0x0000,0x0000,0x0003,0xFFFF,0xFFFF,0xFFFF,0xFFFF }; DEFINE_ICON_FROM_IMAGE(Mixicon, Mixicon_bits); main(argc, argv) char *argv[]; { char *cp, *mflg, *sflg; int iflg, s, f; extern char *getenv(); /****/setbuf(stderr, 0); iflg = 0; /* no parameter initialization */ mflg = (char *) 0; /* no map file */ sflg = (char *) 0; /* no slider level file */ if (!(cp = getenv("IOTA")) || (Chan = atoi(cp) - 1) < 0) Chan = DEFCHAN; while (--argc > 0) { if (argv[argc][0] == '-') { switch (argv[argc][1]) { case 'f': /* don't display fader monitor */ Showfm = 0; break; case 'i': /* specify Iota initialization */ iflg++; break; case 'm': /* mixer map file */ mflg = &argv[argc][2]; break; case 's': /* restore from saved file */ sflg = &argv[argc][2]; break; default: goto syntax; } } else { syntax: fprintf(stderr, "Usage: %s [-f] [-i] [-p[#]] [-sFILE] >file\n", argv[0]); fprintf(stderr, "-f suppresses the fader display\n"); fprintf(stderr, "-i inits Midi-Fader parameters explicitly\n"); fprintf(stderr, "-mFILE gets mixer map from 'file'\n"); fprintf(stderr, "-sFILE gets slider levels from 'file'\n"); exit(2); } } mapinit(mflg); /* calc Nslide & Nfade also */ Ofh = fileno(stdout); if (iflg) sendinit(); miscinits(); Dchange = Xchange = 1; if (sflg) restore(sflg); else for (s = Nslide; --s >= 0; ) if (Spused[s]) setslider(s, DOWN6DB); #define DBG #ifdef DBG { int i, catcher(); for (i=16; --i>0; signal(i, catcher));} #endif DBG window_main_loop(Dframe); exit(0); } #ifdef DBG catcher(sig) { fprintf(stderr, "Caught %d\n", sig); if (sig == 13) abort(); signal(sig, catcher); } #endif DBG mapinit(file) char *file; { register int f, s, op, spsg; char buf[128], *cp; FILE *fp; for (s = MAXSLIDE; --s >= 0; ) { Spsg[s] = -1; Spmode[s] = MUTE; } Nslide = Nfade = spsg = 0; if (file && (fp = fopen(file, "r"))) { while (fgets(buf, sizeof buf, fp)) { if (*buf == '#') /* comment */ continue; else if (*buf == 'f') { /* fader map */ f = atoi(&buf[1]) - 1; if (f < 0 || MAXFADE <= f) { fprintf(stderr, "bad fader # (%d) in %s", f, buf); continue; } Fused[f] = 1; if (f >= Nfade) Nfade = f + 1; for (cp = buf;;) { while (*cp && *cp != '#' && *cp != '=' && *cp != '+' && *cp != '*') cp++; if ((op = *cp++) < ' ' || op == '#') break; s = atoi(cp) - 1; if (s < 0 || MAXSLIDE <= s) { fprintf(stderr, "bad slider # (%d) in %s", s, buf); continue; } if (op == '*') { if (Map[f][s]) fprintf(stderr, "%d=#%c%d*%d (or equiv) is an error)\n", f, Map[f][s]>0? '+' : '*', s, s); Map[f][s] = -1; } else { if (Map[f][s] < 0) fprintf(stderr, "%d=#*%d+%d (or equiv) is an error)\n", f, s, s); Map[f][s]++; } if (s >= Nslide) Nslide = s + 1; Spused[s] = 1; } } else if (*buf == 's') { /* solo group */ if (spsg >= MAXSLIDE) { fprintf(stderr, "Too many solo groups; max is %d.\n", MAXSLIDE); continue; } for (cp = buf;;) { while (*cp && *cp != '=' && *cp != ',' && *cp != '#') cp++; if ((op = *cp++) < ' ' || op == '#') break; s = atoi(cp) - 1; if (s < 0 || MAXSLIDE <= s) { fprintf(stderr, "bad slider # (%d) in %s", s, buf); continue; } Spsg[s] = spsg; } spsg++; } } } else { /* default case */ Nslide = Nfade = DEFFADE; for (f = Nfade; --f >= 0; ) Map[f][f] = Spused[f] = Fused[f] = 1; } if (spsg == 0) { /* no solo groups specified */ for (s = f = 0; s < Nslide; s++) { if (Spused[s]) { if (f++) { /* don't mark 1st in group */ if (f == 2) /* until we've seen the 2nd */ Spsg[s - 1] = spsg; Spsg[s] = spsg; } } else { if (f > 1) spsg++; f = 0; } } } } miscinits() { void resized(), input(); Notify_value fadein(); Dframe = window_create(NULL, FRAME, FRAME_SHOW_LABEL, FALSE, FRAME_ICON, &Mixicon, WIN_WIDTH, 10 + 25 * Nslide, WIN_HEIGHT, Showfm? 328 : 248, 0); Dcanvas = window_create(Dframe, CANVAS, CANVAS_FIXED_IMAGE, FALSE, CANVAS_RESIZE_PROC, resized, WIN_CONSUME_PICK_EVENT, WIN_NO_EVENTS, WIN_CONSUME_PICK_EVENT, LOC_WINENTER, WIN_CONSUME_PICK_EVENT, LOC_WINEXIT, WIN_CONSUME_PICK_EVENT, LOC_MOVE, WIN_CONSUME_PICK_EVENT, WIN_MOUSE_BUTTONS, WIN_CONSUME_PICK_EVENT, LOC_DRAG, WIN_CONSUME_KBD_EVENT, WIN_ASCII_EVENTS, WIN_EVENT_PROC, input, 0); Dpw = canvas_pixwin(Dcanvas); Rmenu = menu_create( MENU_BOXED, TRUE, MENU_ITEM, MENU_STRING, "INIT", MENU_VALUE, INIT, 0, MENU_ITEM, MENU_STRING, "SAVE", MENU_VALUE, SAVE, 0, MENU_ITEM, MENU_STRING, "RESTORE", MENU_VALUE, RESTORE, 0, MENU_ITEM, MENU_STRING, "ALL UP", MENU_VALUE, ALLUP, 0, MENU_ITEM, MENU_STRING, "ALL DOWN", MENU_VALUE, ALLDOWN, 0, MENU_ITEM, MENU_STRING, "QUIT", MENU_VALUE, QUIT, 0, 0); Grey = mem_point(16, 16, 1, Grey_bits); Ltgrey = mem_point(16, 16, 1, Ltgrey_bits); Dkgrey = mem_point(16, 16, 1, Dkgrey_bits); } void resized(canvas, width, height) Canvas canvas; { register int i, x, y, q, chw, chh, bs; int fw, fh, sw, sh; char sb[2], mb[2]; if (canvas != Dcanvas) return; /* oops? */ Splx = Sply = 0; Sphx = width; Sphy = height; if (Showfm && Nfade > 0) { Fmlx = Fmly = 0; Fmhx = width; Fmhy = height; x = (11 * width) / Nslide; /* ideal height for faders above */ y = (24 * width) / (3 * Nslide + Nfade); /* & for side-by-side */ i = (x + y) / 2; /* somewhere in between */ if (height >= i) /* put faders above */ Sply = Fmhy = height / 4; else /* put faders to the right */ Sphx = Fmlx = (width * 3 * Nslide) / (3 * Nslide + Nfade); fw = Fmhx - Fmlx; fh = Fmhy - Fmly; pw_replrop(Dpw, Fmlx, 0, fw, fh, PIX_SRC, Dkgrey, 0, 0); Fmdx = fw / (Nfade + 1); if (Fms || Fmm) { x = Fmdx; y = fh / 6; i = x < y? (x < 40? x : 40) : (y < 40? y : 40); Fmbr = (2 * i) / 5; if (Fmm) { fh -= i; Fmmy = fh + i / 2; } if (Fms) { fh -= i; Fmsy = fh + i / 2; } } Fmh = (4 * fh) / 5; /* height of fader display sliders */ Fmw = Fmdx - 4; /* width " */ Fmy = (fh - Fmh) / 2; for (i = 0; i < Nfade; i++) { if (!Fused[i]) continue; Fmx[i] = Fmlx + (i + 1) * Fmdx; x = Fmx[i] - Fmw / 2; y = Fmy; pw_replrop(Dpw, x, y, Fmw, Fmh, PIX_SRC, Grey, 0, y&1); q = f_to_y(Fade[i]); pw_rop(Dpw, x, y, Fmw, q - y, PIX_SET, 0, 0, 0); x = Fmx[i]; if (Fms) { pw_disc(Dpw, x, Fmsy, Fmbr, Fsolo[i]? PIX_SET : PIX_CLR); pw_disc(Dpw, x, Fmsy, Fmbr - 1, PIX_INV); } if (Fmm) { pw_disc(Dpw, x, Fmmy, Fmbr, Fmute[i]? PIX_SET : PIX_CLR); pw_disc(Dpw, x, Fmmy, Fmbr - 1, PIX_INV); } } } sw = Sphx - Splx; sh = Sphy - Sply; Spdx = sw / Nslide; /* slide pot separation */ if (5 * Spdx < sh) { /* put buttons over-under */ bs = sh / 5; /* vertical space for buttons */ Br = bs / 5; /* button radius */ if (Spdx < 3 * Br) { /* if button would be huge */ Br = Spdx / 3; bs = 5 * Br; } Sy = Sply + bs / 4; /* solo button y coord */ My = Sply + (bs * 3) / 4; /* mute button y coord */ Sdx = Mdx = 0; } else { /* put buttons side-by-side */ bs = sh / 8; Br = bs / 3; Sy = Sply + (5 * bs) / 12; My = Sply + (7 * bs) / 12; Sdx = (2 * bs) / 5; Mdx = -Sdx; } Spy = Sply + bs; /* top of slide pot y */ Sph = sh - bs; /* slide pot height */ Spw = Spdx / 2; /* slide pot width */ Spw2 = Spdx / 4; /* half slide pot width */ Sph -= 4; /* leave room for outline */ Spy += 2; /* ditto */ if (Fontp) pf_close(Fontp); for (i = 0; Fs[i].siz > Br; i++); Fontp = pf_open(Fs[i].nam); chw = Fontp->pf_defaultsize.x / 2; chh = (Fontp->pf_defaultsize.y - 2) / 2; pw_replrop(Dpw, 0, Sply, sw, sh, PIX_SRC, Ltgrey, 0, 0); i = 0; do { y = s_to_y(Ticks[i]); pw_vector(Dpw, 0, y, sw, y, PIX_SET, 1); } while (Ticks[i++]); sb[0] = 'S'; mb[0] = 'M'; sb[1] = mb[1] = '\0'; for (i = 0; i < Nslide; i++) { if (!Spused[i]) continue; Spx[i] = i * Spdx + Spdx / 2; if (Spsg[i] >= 0) { x = Spx[i] + Sdx; pw_disc(Dpw, x, Sy, Br, Spsolo[i]? PIX_SET : PIX_CLR); pw_disc(Dpw, x, Sy, Br - 1, PIX_INV); pw_ttext(Dpw, x - chw, Sy + chh, PIX_INV, Fontp, sb); } x = Spx[i] + Mdx; pw_disc(Dpw, x, My, Br, Spmute[i]? PIX_SET : PIX_CLR); pw_disc(Dpw, x, My, Br - 1, PIX_INV); pw_ttext(Dpw, x - chw, My + chh, PIX_INV, Fontp, mb); x = Spx[i] - Spw2; y = Spy; pw_rop(Dpw, x - 2, y - 2, Spw + 4, Sph + 4, PIX_SET, 0, 0, 0); pw_replrop(Dpw, x, y, Spw, Sph, PIX_SRC, Grey, 0, y&1); q = s_to_y(Slide[i]); pw_rop(Dpw, x, y, Spw, q - y, PIX_CLR, 0, 0, 0); } } void input(window, event, arg) Window window; Event *event; { register int i = event_id(event); if (i == MS_LEFT || i == MS_MIDDLE || i == LOC_DRAG) leftmouse(window, event, arg); if (i == MS_RIGHT) rightmouse(window, event, arg); } rightmouse(window, event, arg) Window window; Event *event; { char file[64]; int s; switch (menu_show(Rmenu, window, event, 0)) { case INIT: sendinit(); break; case SAVE: sprintf(file, Sfilfmt, 0); save(file); break; case RESTORE: sprintf(file, Sfilfmt, 0); restore(file); break; case ALLUP: for (s = Nslide; --s >= 0; setslider(s, 127)); break; case ALLDOWN: for (s = Nslide; --s >= 0; setslider(s, 0)); break; case QUIT: exit(0); } } leftmouse(window, event, arg) Window window; Event *event; { register int x, y, s, f, middown; if (event_is_up(event)) { Mouseown = -1; Wfbu = 0; return; } x = event_x(event); y = event_y(event); middown = (int) window_get(Dcanvas, WIN_EVENT_STATE, MS_MIDDLE); if (!middown && Mouseown >= 0) /* if a slider claims the mouse */ setslider(Mouseown, y_to_f(y)); else if (Splx < x && x < Sphx && Sply < y && y < Sphy) { s = (x - Splx) / Spdx; /* the slider panel */ if (s < 0 || s >= Nslide || !Spused[s]) return; /* too far or unused */ if (Spy - 2 <= y && y <= Spy + Sph + 2 /* sliders */ && Spx[s] - Spw2 < x && x < Spx[s] + Spw2) { setslider(s, y_to_f(y)); Mouseown = middown? -1 : s; } else if (octd(Spx[s]+Mdx - x, My - y) < Br) { /* MUTE */ x = MUTE + (s << 2); if (Wfbu != x) { invspmute(s); Wfbu = x; } } else if (octd(Spx[s]+Sdx - x, Sy - y) < Br) { /* SOLO */ if (Spsg[s] >= 0) { x = SOLO + (s << 2); if (Wfbu != x) { invspsolo(s); Wfbu = x; } } } } else if (Fmlx < x && x < Fmhx && Fmly < y && y < Fmhy) { f = (x - Fmlx) / Fmdx; /* the fader monitor */ if (f < 0 || f >= Nfade) return; if (octd(Fmx[f] - x, Fmmy - y) < Fmbr) { /* MUTE */ x = MUTE + (f << 7); if (Wfbu != x) { invfmute(f); Wfbu = x; } } else if (octd(Fmx[f] - x, Fmsy - y) < Fmbr) { /* SOLO */ x = SOLO + (f << 7); if (Wfbu != x) { invfsolo(f); Wfbu = x; } } } } octd(dx, dy) /* octagonal distance measure */ register int dx, dy; { if (dx < 0) dx = -dx; if (dy < 0) dy = -dy; return (dx > dy? dx + (dy >> 1) : (dx >> 1) + dy); } f_to_y(f) { register int y; y = Fmy + Fmh - ((f * Fmh) / 127); return(y < Fmy? Fmy : (y < Fmy + Fmh? y : Fmy + Fmh)); } s_to_y(s) { register int y; y = Spy + Sph - ((s * Sph) / 127); return(y < Spy? Spy : (y < Spy + Sph? y : Spy + Sph)); } y_to_f(y) { register int f; f = ((Spy + Sph - y) * 127) / Sph; return(f < 0? 0 : (f < 127? f : 127)); } setslider(s, new) /* set slider s to new value & tell Iota */ { register int old, oldy, newy; old = Slide[s]; if (new != old) { Slide[s] = new; oldy = s_to_y(old); newy = s_to_y(new); if (Dchange) { if (newy < oldy) /* new level is higher */ pw_replrop(Dpw, Spx[s] - Spw2, newy, Spw, oldy - newy, PIX_SRC, Grey, 0, newy&1); else /* new level is lower */ pw_rop(Dpw, Spx[s] - Spw2, oldy, Spw, newy - oldy, PIX_CLR, 0, 0, 0); } if (Xchange) schng(s); } } schng(s) /* a slider has changed, update faders */ { register int f; for (f = Nfade; --f >= 0; ) if (Map[f][s]) setfader(f); } setfader(f) /* a contributor has changed, recalculate */ { register int new, old, num, den, s, m, oldy, newy, mode; num = den = 0; for (s = 0; s < Nslide; s++) { if (m = Map[f][s]) { new = Slide[s]; if (Spsg[s] >= 0) mode = Spmode[Spsg[s]]; else mode = MUTE; if ((mode == MUTE && Spmute[s]) || (mode == SOLO && !Spsolo[s])) new = 0; if (m > 0) { num += m * new; den += m; } else if (m < 0) { num *= new; den *= 127; } } } if (den > 0) { old = Fade[f]; new = num / den; if (new != old) { Fade[f] = new; if (Xchange) mput(CH_CTL | Chan, FCTRL0 + f, new); if (Dchange && Showfm) { oldy = f_to_y(old); newy = f_to_y(new); if (newy < oldy) /* new level is higher */ pw_replrop(Dpw, Fmx[f] - Fmw/2, newy, Fmw, oldy - newy, PIX_SRC, Grey, 0, newy&1); else /* new level is lower */ pw_rop(Dpw, Fmx[f] - Fmw/2, oldy, Fmw, newy - oldy, PIX_SET, 0, 0, 0); } } } } invspmute(s) /* invert slide pot mute */ { Spmute[s] ^= 1; pw_disc(Dpw, Spx[s] + Mdx, My, Br, PIX_INV); if (Slide[s]) schng(s); } invfmute(f) /* invert fader mute */ { Fmute[f] ^= 1; pw_disc(Dpw, Fmx[f], Fmmy, Fmbr, PIX_INV); if (Xchange) domutes(); } invspsolo(sp) /* invert slide pot solo */ { register int i, s, spsg; Spsolo[sp] ^= 1; spsg = Spsg[sp]; if (spsg < 0) return; pw_disc(Dpw, Spx[sp] + Sdx, Sy, Br, PIX_INV); sp = MUTE; for (s = Nslide; --s >= 0; ) if (Spsg[s] == spsg && Spsolo[s]) { sp = SOLO; break; } Spmode[spsg] = sp; for (i = Nfade; --i >= 0; setfader(i)); } invfsolo(f) /* invert fader solo */ { Fsolo[f] ^= 1; pw_disc(Dpw, Fmx[f], Fmsy, Fmbr, PIX_INV); if (Xchange) domutes(); } domutes() /* fader mutes (not slide pot mutes) */ { register int i; int um[MAXFADE]; if (Xchange) { for (i = Nfade; --i >=0 && Fsolo[i] == 0; ); if (i >= 0) /* Solo takes precedence */ for (i = Nfade; --i >= 0; ) um[i] = Fsolo[i]? 1 : 0; else /* Mute takes precedence */ for (i = Nfade; --i >= 0; ) um[i] = Fmute[i]? 0 : 1; for (i = Nfade; --i >= 0; ) if (Um[i] != um[i]) mput(CH_CTL | Chan, MCTRL0 + i, 127 * (Um[i] = um[i])); } } mput(a, b, c) { u_char buf[8], *bp; bp = buf; *bp++ = a; *bp++ = b; if (c != -1) *bp++ = c; write(Ofh, buf, bp - buf); /****fprintf(stderr, "mput(%d, %d, %d)\n", a, b, c); /****/ } sysex(data, len) /* send sys excl */ u_char *data; { u_char buf[4096], *bp; bp = buf; *bp++ = SX_CMD; write(Ofh, buf, bp - buf); write(Ofh, data, len); bp = buf; *bp++ = SX_EOB; write(Ofh, buf, bp - buf); return(0); } /* ** Draw a disc with radius r centered at x,y in Pixwin *pw using op. ** The boundary is a sequence of vertically, horizontally, or diagonally ** adjacent points that minimize abs(x^2+y^2-r^2). ** The disc is guaranteed to be symmetric about the horizontal, vertical, ** and diagonal axes. */ pw_disc(pw, x, y, r, op) Pixwin *pw; { register x1, y1, eps, x0, y0; int dxsq, dysq, exy; x0 = x1 = x; y1 = y + r; y0 = y - r; eps = 0; /* x^2 + y^2 - r^2 */ dxsq = 1; /* (x+dx)^2-x^2*/ dysq = 1 - 2*r; while (y1 > y0) { exy = eps + dxsq + dysq; if (-exy <= eps+dxsq) { HLINER(pw, x0, y0, x1, op); HLINER(pw, x0, y1, x1, op); y1--; y0++; eps += dysq; dysq += 2; } if (exy <= -eps) { x1++; x0--; eps += dxsq; dxsq += 2; } } HLINER(pw, x0, y0, x1, op); } struct setupstr { /* initial setup structure */ u_char id[4]; /* identify Iota & Midi-Fader */ u_char unit; /* system exclusive unit number */ u_char cmd; /* what command (6 = setup data dump) */ u_char reclev; /* receive levels over MIDI (1=yes) */ u_char sndlev; /* send levels over MIDI (1=yes) */ u_char recpgm; /* receive prog commands over MIDI (1=yes) */ u_char sndpgm; /* send prog commands over MIDI (1=yes) */ u_char prgchan; /* program change channel */ u_char autolev; /* auto send levels every second (1=yes) */ u_char rate; /* fader change rate (2^rate ms.) */ u_char sens; /* dial sensitivity */ struct fsetstr { u_char fchan; /* fader channel */ u_char norc; /* fader controlled by note(1) or ctrl(0) */ u_char fctrl; /* fader note or ctrl number */ u_char mctrl; /* mute ctrl number */ } fade[8]; } Isetup = { /* Initial setup data */ { ID_MISC, ID1_IOTA, ID2_IOTA, ID_IMF, },/* identify Iota Midi-Fader */ 0x00, /* Unit number */ IMF_SXC_DAT_SETUP, /* command; setup data dump */ 0x01, 0x01, /* do get/send levels */ 0x00, 0x00, /* don't get/send prog cmds */ 0x00, /* prog change MIDI channel */ 0x00, /* no levels every sec */ 0x00, /* fader change rate = 1 ms */ 0x0a, /* dial sensitivity = 10 */ { 0x00, 0x00, 0x00, 0x00, }, /* fader 1 */ /* etc. for 7 more */ }; sendinit() /* send SysExcl initialization */ { register int i, f; for (i = 0; i < Nfade; i += 8) { Isetup.unit = F2SXU(i); for (f = 0; f < 8; f++) { Isetup.fade[f].fchan = Chan + 1; /* N.B. +1 */ Isetup.fade[f].norc = 0; Isetup.fade[f].fctrl = FCTRL0 + i + f; Isetup.fade[f].mctrl = MCTRL0 + i + f; } sysex(&Isetup, sizeof Isetup); } } save(file) /* save current slider settings */ char *file; { int fh; if ((fh = creat(file, 0644)) < 0) perror(file); else if (write(fh, &Curstate, sizeof Curstate) != sizeof Curstate) perror("writing state file"); else { close(fh); fprintf(stderr, "sliders saved in %s\n", file); } } restore(file) /* restore slider settings */ char *file; { int fh, s, f; struct statestr tmpstate; if ((fh = open(file, 0)) < 0) perror(file); else if (read(fh, &tmpstate, sizeof tmpstate) != sizeof tmpstate) perror("reading state file"); else { close(fh); for (s = Nslide; --s >= 0; ) { setslider(s, tmpstate._Slide[s]); if (Spsolo[s] != tmpstate._Spsolo[s]) invspsolo(s); if (Spmute[s] != tmpstate._Spmute[s]) invspmute(s); } for (f = Nfade; --f >= 0; ) { if (Fsolo[f] != tmpstate._Fsolo[f]) invfsolo(f); if (Fmute[f] != tmpstate._Fmute[f]) invfmute(f); } } }