/*	MIRSET -- Set Mirage Parameters using Button Command Codes
**	psl 10/87
*/
#include	<stdio.h>
#include	<midi.h>
#include	<sys/time.h>

#define	DEVMPU	"/dev/mpu0"

int	Verbose	= 0;

main(argc, argv)
char	*argv[];
{
	register int i, p, v, fh;
	char *cp;

	if (argc < 2) {
syntax:
	    fprintf(stderr, "Usage: %s [-v] param=val [...]\n", argv[0]);
	    fprintf(stderr, "param is a decimal integer\n");
	    fprintf(stderr, "val may be 'on', 'off', dec, hex (0x#), or '?'\n");
	    exit(2);
	}
	if ((fh = open(DEVMPU, 2)) < 0) {
	    perror(DEVMPU);
	    exit(1);
	}
	for (i = 1; i < argc; i++) {
	    if (argv[i][0] == '-') {
		switch (argv[i][1]) {
		case 'v':
		    Verbose++;
		    break;
		default:
		    goto syntax;
		}
	    }
	}
	for (i = 1; i < argc; i++) {
	    if (argv[i][0] == '-')
		continue;
	    for (cp = argv[i]; *cp && *cp != '='; cp++);
	    if (*cp++ != '=')
		goto syntax;
	    p = atoi(argv[i]);
	    if (p < 0 || p > 99 || Masos_par[p].ptype == MP_UNUSED) {
		fprintf(stderr, "Unknown MASOS parameter: %d\n", p);
		goto syntax;
	    }
	    if (Masos_par[p].ptype == MP_ACT) {
		fprintf(stderr, "[%d] %s is an action, not a settable param.\n",
		 p, Masos_par[p].pname);
		goto syntax;
	    }
	    if (*cp == 'o')
		v = (cp[1] == 'n' ? 1 : 0);
	    else if (*cp == '?')
		v = -1;
	    else {
		v = myatoi(cp);
		if (v < Masos_par[p].lolim || v > Masos_par[p].hilim) {
		    fprintf(stderr, "[%d] %s has a range of %d to %d\n",
		     p, Masos_par[p].pname,
		     Masos_par[p].lolim, Masos_par[p].hilim);
		    goto syntax;
		}
	    }
	    mirset(fh, p, v);
	}
	exit(0);
}

mirset(fh, p, nv)
{
	register int ov;

	ov = getparam(fh, p);
	if (nv == -1) {
	    if (Masos_par[p].pfmt == MP_BOOL) {
		if (Verbose)
		    printf("[%d] %s is %s (%d)",
		     p, Masos_par[p].pname, ov? "on" : "off", ov);
		else
		    printf("%d=%s\n", p, ov? "on" : "off");
	    } else if (Masos_par[p].pfmt == MP_DEC) {
		if (Verbose)
		    printf("[%d] %s is %d (0x%x)",
		     p, Masos_par[p].pname, ov, ov);
		else
		    printf("%d=%d\n", p, ov);
	    } else if (Masos_par[p].pfmt == MP_HEX) {
		if (Verbose)
		    printf("[%d] %s is 0x%02x (%d)",
		     p, Masos_par[p].pname, ov, ov);
		else
		    printf("%d=0x%02x\n", p, ov);
	    }
	    return;
	}
	while (ov != nv) {
	    setparam(fh, ov, nv);
	    ov = getparam(fh, p);
	}
}

getparam(fh, p)
{
	register u_char *bp;
	register int i, n, ov, len;
	u_char obuf[32], ibuf[256];

	bp = obuf;
	*bp++ = ID_ENSONIQ;
	*bp++ = ID_MIRAGE;
	*bp++ = MIR_SXF_CC;
	*bp++ = p / 10;
	*bp++ = p % 10;
	*bp++ = MIRBUT_VALUE;
	*bp++ = MIR_CC_END;
	if (Verbose > 1) {
	    fprintf(stderr, "About to send:");
	    for (i = 0; i < bp - obuf; i++)
		fprintf(stderr, " %x", obuf[i]);
	    fprintf(stderr, "\n");
	}
	len = Masos_par[p].ptype == MIR_SXF_PPAR? MIR_PPAR_LEN :
	 (Masos_par[p].ptype == MIR_SXF_WPAR? MIR_WPAR_LEN :
	  (Masos_par[p].ptype == MIR_SXF_WAVE? MIR_WAVE_LEN : -1));
	if (len == -1) {
	    printf("Unknown parameter: [%d]\n", p);
	    return(-1);
	}
	sysexout(fh, obuf, bp - obuf);
	for (ov = 0; ; ov++) {
	    i = sysexin(fh, ibuf, sizeof ibuf);
	    if (i == 0) {
		printf("Got 0 bytes back, wanted %d; giving up on [%d] %s.\n",
		 len, p, Masos_par[p].pname);
		return(-1);
	    }
	    if (Verbose > 1) {
		for (n = 0; n < i; n++)
		    fprintf(stderr, " %x", ibuf[n]);
		fprintf(stderr, "\n", ibuf[n]);
	    }
	    if (i != len) {
		if (Verbose > 0)
		    fprintf(stderr, "Got %d byte(s) back, wanted %d\n", i, len);
		continue;
	    }
	    if (ibuf[0] != ID_ENSONIQ || ibuf[1] != ID_MIRAGE) {
		fprintf(stderr, "Not Ensonic Mirage?\n");
		ov++;
	    } else if (ibuf[2] != Masos_par[p].ptype) {
		if (Verbose > 0)
		    fprintf(stderr, "Wrong type of parameter?  %x\n", ibuf[2]);
	    } else if (Masos_par[p].ptype != MP_WSEL && ibuf[4] != p) {
		if (Verbose > 0)
		    fprintf(stderr, "Wrong parameter?  %d != %d\n", ibuf[4], p);
	    } else
		break;
	    if (ov > 6) {
		printf("Too many failures; giving up on [%d] %s.\n",
		 p, Masos_par[p].pname);
		return(-1);
	    }
	}
	if (Masos_par[p].ptype == MP_WSEL)
	    ov = ibuf[3] & 0x07;
	else
	    ov = ((ibuf[6] << 4) | ibuf[5]) / Masos_par[p].pfact;
	return(ov + Masos_par[p].pbase);
}

setparam(fh, ov, nv)
{
	register u_char *bp;
	register int i, dv, ud, n;
	u_char obuf[32], ibuf[256];

	dv = nv - ov;
	ud = MIRBUT_UP;
	if (dv < 0) {
	    dv = -dv;
	    ud = MIRBUT_DOWN;
	}
	while (dv > 0) {
	    bp = obuf;
	    *bp++ = ID_ENSONIQ;
	    *bp++ = ID_MIRAGE;
	    *bp++ = MIR_SXF_CC;
	    n = dv > 5? 5 : dv;
	    for (i = 0; i < n; i++)
		*bp++ = ud;
	    *bp++ = MIR_CC_END;
	    for(;;) {
		if (Verbose > 1)
		    fprintf(stderr, "Send %d %s push(es)\n",
		     n, ud == MIRBUT_UP? "up" : "down");
		sysexout(fh, obuf, bp - obuf);
		dv -= n;
		if (dv < n)
		    break;
		while (!inwait(fh, 0, 200000))	/* wait for .2 sec idle */
		    read(fh, ibuf, sizeof ibuf);
	    }
	}
	while (!inwait(fh, 0, 200000))		/* flush old input */
	    read(fh, ibuf, sizeof ibuf);
}

sysexout(ofh, data, len)			/* send sys excl. */
u_char	*data;
{
	u_char buf[4096], *bp;

	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;
	*bp++ = MPU_EXCLUSIVE_TO_HOST_ON;
	*bp++ = MPU_SEND_MEASURE_END_OFF;
	write(ofh, buf, bp - buf);
}

sysexin(ifh, rbuf, rlen)			/* read sys excl. */
u_char	*rbuf;
{
	u_char *rbp;
	int i, mode;

	mode = 0;
	for (rbp = rbuf; rlen > 0; ) {
	    if (inwait(ifh, 2, 0)) {
		if (Verbose)
		    fprintf(stderr, "read timed out\n");
		break;
	    }
	    if ((i = read(ifh, rbp, 1)) <= 0) {
		fprintf(stderr, "read(midi) returned %d\n", i);
		return(0);
	    }
	    if (mode == 0) {
		if (*rbp == SX_CMD)
		    mode = SX_CMD;
		continue;
	    }
	    if (MIDI_EOX(*rbp)) {
		*rbp = '\0';
		break;
	    }
	    rbp++;
	    --rlen;
	}
	return(rbp - rbuf);
}

myatoi(cp)
char	*cp;
{
	register int i, n;

	if (*cp == '0' && (cp[1] == 'x' || cp[1] == 'X'))
	    for (cp += 2, n = 0; (i = hex(*cp)) >= 0; cp++, n = (16*n) + i);
	else
	    n = atoi(cp);
	return(n);
}

hex(c)
char	c;
{
	if ('0' <= c && c <= '9')
	    return(c - '0');
	if ('A' <= c && c <= 'F')
	    return(10 + c - 'A');
	if ('a' <= c && c <= 'f')
	    return(10 + c - 'a');
	return(-1);
}

inwait(fh, sec, usec)
{
	int rfds;
	struct timeval tout;

	if (fh < 0)
	    return(1);
	rfds = 1 << fh;
	tout.tv_sec = sec;
	tout.tv_usec = usec;
	return(select(fh + 1, &rfds, 0, 0, &tout) == 0);
}
