/* MIXER -- Control panel for IOTA Midi-Fader, psl 10/87 ** Stand-alone version; output is sent to /dev/mpu0 */ #include #include #include #include #include #include #include #define DEVMPU "/dev/mpu0" typedef struct pr_pos Point; #define PIX_INV PIX_NOT(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 REINIT 1 #define RESYNC 2 #define QUIT 3 u_char Abreq[] = { /* SysExcl request to dump active buffer */ ID_MISC, ID1_IOTA, ID2_IOTA, /* identify Iota */ ID_IMF, /* identify Midi-Fader */ 0x00, /* Unit number */ IMF_SXC_REQ_ABUF, /* command; dump active buffer */ }; u_char Isetup[] = { /* Initial setup data */ ID_MISC, ID1_IOTA, ID2_IOTA, /* identify Iota */ ID_IMF, /* identify 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 commands */ 0x07, /* prog change MIDI channel */ 0x00, /* don't send levels every second */ 0x02, /* fader change rate = 4 ms */ 0x0a, /* dial sensitivity = 10 */ 0x07, 0x00, 0x20, 0x30, /* fader1, chan=8, lev=32, mute=48 */ 0x07, 0x00, 0x21, 0x31, /* fader2, chan=8, lev=33, mute=49 */ 0x07, 0x00, 0x22, 0x32, /* fader3, chan=8, lev=34, mute=50 */ 0x07, 0x00, 0x23, 0x33, /* fader4, chan=8, lev=35, mute=51 */ 0x07, 0x00, 0x24, 0x34, /* fader5, chan=8, lev=36, mute=52 */ 0x07, 0x00, 0x25, 0x35, /* fader6, chan=8, lev=37, mute=53 */ 0x07, 0x00, 0x26, 0x36, /* fader7, chan=8, lev=38, mute=54 */ 0x07, 0x00, 0x27, 0x37, /* fader8, chan=8, lev=39, mute=55 */ }; 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", }; 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, }; int Dchange = 0; /* display changes */ int Xchange = 0; /* transmit changes */ int Ifh = -1; /* where Iota data comes from */ int Ofh = -1; /* where Iota commands go to */ int Chan; /* Iota's MIDI channel */ int Iflg = 0; /* initialization flag */ int Fade[8] = { /* 8 fader settings 0-127 */ 127, 127, 127, 127, 127, 127, 127, 127, }; int Fh; /* fader height */ int Sy; /* solo button y */ int Br; /* button radius */ int Fy; /* fader (top) y */ int My; /* mute button y */ int Fw; /* fader width */ int Fw2; /* fader half-width */ int Fx[8]; /* fader (center) x */ int Solo[8]; /* 8 solo buttons */ int Mute[8]; /* 8 mute buttons */ int Um[8]; /* Iota fader mutes, 0=>muted */ int Fcntl[8] = { /* controller numbers for the faders */ 32, 33, 34, 35, 36, 37, 38, 39, }; int Mcntl[8] = { /* controller numbers for the mutes */ 48, 49, 50, 51, 52, 53, 54, 55, }; int Mouseown = -1; /* slider that "claims" the mouse */ int Wfbu = 0; /* "Waiting For Button Up" */ Notify_client Mynum = (Notify_client) 4321; /* nonsense number */ Frame Dframe; /* whole display's frame */ Canvas Dcanvas; /* whole display's canvas */ Pixwin *Dpw = 0; /* whole display pixwin */ Menu Rmenu; /* right mouse button menu */ struct pixfont *Fontp; 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; extern char *getenv(); if (!(cp = getenv("IOTA")) || (Chan = atoi(cp) - 1) < 0) Chan = 0; while (--argc > 0) { if (argv[argc][0] == '-') { switch (argv[argc][1]) { case 'i': /* initialize Iota */ Iflg++; break; case 'f': /* use stdin/stdout */ Ifh = fileno(stdin); Ofh = fileno(stdout); break; default: goto syntax; } } else { syntax: fprintf(stderr, "Usage: %s [-i] [-f >file]\n", argv[0]); fprintf(stderr, "-i initializes Midi-Fader\n"); fprintf(stderr, "-f uses stdin/stdout (no buffering)\n"); exit(2); } } if (Ifh == -1 && Ofh == -1) { if ((Ifh = Ofh = open(DEVMPU, 2)) < 0) { perror(DEVMPU); exit(1); } } else { if (Ifh == -1 && (Ifh = open(DEVMPU, 0)) < 0) { perror(DEVMPU); exit(1); } if (Ofh == -1 && (Ofh = open(DEVMPU, 1)) < 0) { perror(DEVMPU); exit(1); } } if (Iflg) sendinit(); getidata(); /* get current setup from MIDI-Fader */ miscinits(); Dchange = Xchange = 1; window_main_loop(Dframe); exit(0); } sendinit() { sysex(Isetup, sizeof Isetup, 0, 0); } getidata() { register int i, offset; u_char buf[32]; sysex(Abreq, sizeof Abreq, buf, sizeof buf); for (offset = 0; offset < 5 && buf[offset] != SX_CMD; offset++); offset += 7; for (i = 8; --i >= 0; setfader(i, buf[offset + i])); for (i = 0; i < 4; i++) { setmute(i, (buf[offset + 8] & (1 << i))? 0 : 1); setmute(i + 4, (buf[offset + 9] & (1 << i))? 0 : 1); } for (i = 8; --i >= 0; ) { Um[i] = Mute[i]? 0 : 1; setsolo(i, 0); } } miscinits() { void resized(), input(); Notify_value fadein(); Dframe = window_create(NULL, FRAME, FRAME_LABEL, "-=[ MIXER ]=-", FRAME_ICON, &Mixicon, WIN_WIDTH, 160, WIN_HEIGHT, 188, 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, "RE-INIT", MENU_VALUE, REINIT, 0, MENU_ITEM, MENU_STRING, "RESYNC", MENU_VALUE, RESYNC, 0, MENU_ITEM, MENU_STRING, "QUIT", MENU_VALUE, QUIT, 0, 0); notify_set_input_func(Mynum, fadein, Ifh); } Notify_value fadein(clinum, fh) Notify_client clinum; { int i, j; static char buf[512]; /****/printf("fadein(%d, %d)\n", clinum, fh); if (clinum != Mynum || fh != Ifh) { /* "can't happen" */ /****/printf("Eh? clinum(%d)!=Mynum(%d) or fh(%d)!=Ifh(%d)\n", /****/ clinum, Mynum, fh, Ifh); return(NOTIFY_IGNORED); } i = read(Ifh, buf, sizeof buf); /****/printf("read %d byte(s)\n", i); /****/for (j = 0; j < i; j++) /****/ printf(" %02x", buf[j]); /****/printf("\n"); return(NOTIFY_DONE); } void resized(canvas, width, height) Canvas canvas; { register int i, dx, bhh, x, y, q, chw, chh, bs; char sb[2], mb[2]; if (canvas != Dcanvas) return; /* oops */ dx = width / 8; /* fader separation */ Fh = height * 0.8; /* fader height */ bs = (height - Fh) / 2; /* height of button space */ bhh = bs / 2; /* half-height of button space */ Sy = bhh; /* solo button y */ Br = bhh * 0.8; /* button radius */ if (dx < 3 * Br) Br = dx / 3; /* button radius */ Fy = bs; /* top of fader y */ My = Fy + Fh + bhh; /* mute button y */ My = height - bhh; /* mute button y */ Fw = dx / 2; /* fader width */ Fw2 = dx / 4; /* half fader width */ Fh -= 4; /* leave room for outline */ Fy += 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_rop(Dpw,0,0,width,height,PIX_CLR,0,0,0); i = 0; do { y = f_to_y(Ticks[i]); pw_vector(Dpw, 0, y, width, y, PIX_SET, 1); } while (Ticks[i++]); sb[0] = 'S'; mb[0] = 'M'; sb[1] = mb[1] = '\0'; for (i = 0; i < 8; i++) { Fx[i] = i * dx + dx / 2; pw_circle(Dpw, Fx[i], Sy, Br, PIX_SET); pw_text(Dpw, Fx[i] - chw, Sy + chh, PIX_OR, Fontp, sb); if (Solo[i]) pw_disc(Dpw, Fx[i], Sy, Br, PIX_INV); pw_circle(Dpw, Fx[i], My, Br, PIX_SET); pw_text(Dpw, Fx[i] - chw, My + chh, PIX_OR, Fontp, mb); if (Mute[i]) pw_disc(Dpw, Fx[i], My, Br, PIX_INV); x = Fx[i] - Fw2; y = Fy; pw_rop(Dpw, x - 2, y - 2, Fw + 4, Fh + 4, PIX_SET, 0, 0, 0); q = f_to_y(Fade[i]); pw_rop(Dpw, x, y, Fw, 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; { register int i, x, y, c, mindx, dx; switch (menu_show(Rmenu, window, event, 0)) { case REINIT: sendinit(); break; case RESYNC: Xchange = 0; getidata(); Xchange = 1; break; case QUIT: exit(0); } } leftmouse(window, event, arg) Window window; Event *event; { register int i, x, y, c, mindx, dx; if (event_is_up(event)) { Mouseown = -1; Wfbu = 0; return; } x = event_x(event); y = event_y(event); if (Mouseown >= 0) /* if a slider claims the mouse */ setfader(Mouseown, y_to_f(y)); else if (!Wfbu) { /* who wants the mouse? */ if (x < Fx[0] - Fw || x > Fx[7] + Fw || y < Sy - Br || y > My + Br) return; /* no one */ c = -1; mindx = 999; for (i = 8; --i >= 0; ) { dx = x - Fx[i]; if (dx < 0) dx = -dx; if (dx < mindx) { mindx = dx; c = i; } } if (My-Br < y && y < My+Br && mindx < Br) { /* MUTE */ setmute(c, Mute[c] ^ 1); Wfbu++; } else if (Sy-Br < y && y < Sy+Br && mindx < Br) { /* SOLO */ setsolo(c, Solo[c] ^ 1); Wfbu++; } else if (Fy <= y && y <= Fy+Fh && mindx < Fw2) { /* sliders */ Mouseown = c; setfader(Mouseown, y_to_f(y)); } } } f_to_y(f) { register int y; y = Fy + Fh - ((f * Fh) / 127); return(y < Fy? Fy : (y < Fy + Fh? y : Fy + Fh)); } y_to_f(y) { register int f; f = ((Fy + Fh - y) * 127) / Fh; return(f < 0? 0 : (f < 127? f : 127)); } setfader(n, new) { register int old, oldy, newy; old = Fade[n]; if (new != old) { if (Xchange) mput(CH_CTL | Chan, Fcntl[n], new); Fade[n] = new; oldy = f_to_y(old); newy = f_to_y(new); if (Dchange) { if (newy < oldy) { /* new level is higher */ pw_rop(Dpw, Fx[n] - Fw2, newy, Fw, oldy - newy, PIX_SET, 0, 0, 0); } else { /* new level is lower */ pw_rop(Dpw, Fx[n] - Fw2, oldy, Fw, newy - oldy, PIX_CLR, 0, 0, 0); } } } } setmute(n, value) { if (Mute[n] != value) { Mute[n] = value; if (Dchange) pw_disc(Dpw, Fx[n], My, Br, PIX_INV); } if (Xchange) domutes(); } setsolo(n, value) { if (Solo[n] != value) { Solo[n] = value; if (Dchange) pw_disc(Dpw, Fx[n], Sy, Br, PIX_INV); } if (Xchange) domutes(); } domutes() { register int i; int um[8]; if (Xchange) { for (i = 8; --i >=0 && Solo[i] == 0; ); if (i >= 0) /* Solo takes precedence */ for (i = 8; --i >= 0; ) um[i] = Solo[i]? 1 : 0; else /* Mute takes precedence */ for (i = 8; --i >= 0; ) um[i] = Mute[i]? 0 : 1; for (i = 8; --i >= 0; ) if (Um[i] != um[i]) mput(CH_CTL | Chan, Mcntl[i], 127 * (Um[i] = um[i])); } } mput(a, b, c) { u_char buf[8]; buf[0] = MPU_WANT_TO_SEND_DATA + 0; /* 0 = track */ buf[1] = a; buf[2] = b; buf[3] = c; write(Ofh, buf, 4); } sysex(data, len, rbuf, rlen) /* send sys excl., maybe read response */ u_char *data, *rbuf; { u_char buf[4096], *bp, *rbp, *bufend; int i; bp = buf; *bp++ = MPU_RESET; *bp++ = MPU_MIDI_THRU_OFF; *bp++ = MPU_SEND_SYSTEM_MESSAGE; *bp++ = SX_CMD; while (--len >= 0) *bp++ = *data++; *bp++ = SX_EOB; if (!rbuf || !rlen) { /* no reply */ write(Ofh, buf, bp - buf); return(0); } *bp++ = MPU_EXCLUSIVE_TO_HOST_ON; /* get reply */ *bp++ = MPU_SEND_MEASURE_END_OFF; write(Ofh, buf, bp - buf); rbp = rbuf; do { if ((i = iwait(Ifh, 10)) <= 0) { fprintf(stderr, "read(midi) timed out\n"); break; } if ((i = read(Ifh, rbp, rlen)) <= 0) { fprintf(stderr, "read(midi) returned %d\n", i); break; } bufend = rbp + i; for (bp = rbp; bp < bufend && *bp != SX_EOB; bp++); rlen -= i; rbp += i; } while (rlen > 0 && bp >= bufend); return(rbp - rbuf); } /* ** Draw a circle of radius r with center 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 circle is guaranteed to be symmetric about the horizontal, vertical, ** and diagonal axes. */ pw_circle(pw, x, y, r, op) Pixwin *pw; { register int y1, eps, dxsq, dysq, exy, y0; int x0, x1; x0 = x1 = x; y0 = y - r; y1 = y + r; eps = 0; /* x^2 + y^2 - r^2 */ dxsq = 1; /* (x+dx)^2-x^2*/ dysq = 1 - 2*r; /*x1++; /* to offset half-open lines (jerq?) */ if (op == PIX_INV) { /* endpoints coincide */ POINT2(pw, x0, y0, op); POINT2(pw, x0, y1, op); } while (y1 > y0) { POINT2(pw, x0, y0, op); POINT2(pw, x0, y1, op); POINT2(pw, x1, y0, op); POINT2(pw, x1, y1, op); exy = eps + dxsq + dysq; if (-exy <= eps+dxsq) { y1--; y0++; eps += dysq; dysq += 2; } if (exy <= -eps) { x1++; x0--; eps += dxsq; dxsq += 2; } } POINT2(pw, x0, y0, op); POINT2(pw, x1, y0, op); } /* ** 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); }