/*	FADE -- Control panel for IOTA Midi-Fader, psl 10/87 */

#include	<midi.h>
#include	<mpuvar.h>
#include	<stdio.h>

#define	DEVMPU	"/dev/mpu0"

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, lev=32, mute=48 */
	0x07, 0x00, 0x21, 0x31,		/* fader2, lev=33, mute=49 */
	0x07, 0x00, 0x22, 0x32,		/* fader3, lev=34, mute=50 */
	0x07, 0x00, 0x23, 0x33,		/* fader4, lev=35, mute=51 */
	0x07, 0x00, 0x24, 0x34,		/* fader5, lev=36, mute=52 */
	0x07, 0x00, 0x25, 0x35,		/* fader6, lev=37, mute=53 */
	0x07, 0x00, 0x26, 0x36,		/* fader7, lev=38, mute=54 */
	0x07, 0x00, 0x27, 0x37,		/* fader8, lev=39, mute=55 */
};

u_char	Osetup[]	= {	/* Other 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 */
	0x01,				/* send levels every second */
	0x02,				/* fader change rate = 4 ms */
	0x0a,				/* dial sensitivity = 10 */
	0x07, 0x00, 0x20, 0x30,		/* fader1, lev=32, mute=48 */
	0x07, 0x00, 0x21, 0x31,		/* fader2, lev=33, mute=49 */
	0x07, 0x00, 0x22, 0x32,		/* fader3, lev=34, mute=50 */
	0x07, 0x00, 0x23, 0x33,		/* fader4, lev=35, mute=51 */
	0x07, 0x00, 0x24, 0x34,		/* fader5, lev=36, mute=52 */
	0x07, 0x00, 0x25, 0x35,		/* fader6, lev=37, mute=53 */
	0x07, 0x00, 0x26, 0x36,		/* fader7, lev=38, mute=54 */
	0x07, 0x00, 0x27, 0x37,		/* fader8, lev=39, mute=55 */
};

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 */
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	nlen[]		= {	/* length of "normal" status commands */
	3, 3, 3, 3, 2, 2, 3, 0,
};
int	slen[]		= {	/* length of system status commands */
	0, 1, 3, 2, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1,
};

main(argc, argv)
char	*argv[];
{
	char *cp;
	int i, mask, amask;
	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();
	amask = (1 << Ifh) | (1 << 0);
	rstart();
	for (;;) {
	    for (i = 0; i < 8; i++)
		printf("%d%c", Fade[i], i==7? '\n' : ':');
	    mask = amask;
	    if (select(Ifh + 1, &mask, 0, 0, 0) < 1)
		continue;
	    if (mask & (1 << Ifh)) {
		fadein();
	    } else {
		keyin();
	    }
	}
}

sendinit()
{
/****/printf("sendinit()\n");
	sysex(Isetup, sizeof Isetup, 0, 0);
}

getidata()
{
	register int i, offset;
	u_char buf[32];

/****/printf("getidata()\n");
	sysex(Abreq, sizeof Abreq, buf, sizeof buf);
	for (offset = 0; offset < 5 && buf[offset] != SX_CMD; offset++);
/****/if (offset >= 5) { printf("getidata() screwed up...\n"); return; }
	offset += 7;
	for (i = 8; --i >= 0; Fade[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);
/****/;
	}
}

fadein()
{
	int i, ourstatus, skiplen, cmdlen, c, v;
	u_char buf[512], *bip = buf, *bop = buf;
	static int status;
   
	ourstatus = CH_CTL | Chan;
	bip = buf + read(Ifh, bop = buf, sizeof buf);
	while (bop < bip) {
	    i = (*bop++ & 0xFF);	/* timing byte */
	    if (i == RT_TCIP || i == RT_TCIS)
		continue;
	    skiplen = 0;
	    if (*bop & M_CMD) {
		i = (*bop & M_CMD_MASK);
		if (i < SX_CMD) {	/* normal status commands */
		    status = *bop++;
		    cmdlen = nlen[(status >> 4) & 07];
		} else {		/* weird ones */
		    if (*bop == SX_CMD) {
			while (*bop != SX_EOB)	/* scan to end of sysex */
			    if (++bop >= bip)	/* (could get stuck) */
				bip = buf + read(Ifh, bop = buf, sizeof buf);
			skiplen = 1;
		    } else
			skiplen = slen[*bop & 0x0F];
		}
	    }
	    if (skiplen == 0 && status != ourstatus)
		skiplen = cmdlen - 1;		/* we're past the status */
	    if (skiplen) {		/* skip over rest of command */
		while (skiplen > (bip - bop)) {
		    skiplen -= (bip - bop);	/* (could get stuck) */
		    bip = buf + read(Ifh, bop = buf, sizeof buf);
		}
		bop += skiplen;
	    } else {			/* this is a fader setting */
		while (bop >= bip)
		    bip = buf + read(Ifh, bop = buf, sizeof buf);
		c = *bop++;
		while (bop >= bip)
		    bip = buf + read(Ifh, bop = buf, sizeof buf);
		v = *bop++;
		if (0x20 <= c && c <= 0x27)
		    Fade[c - 0x20] = v;
		if (0x30 <= c && c <= 0x37)
		    Mute[c - 0x30] = (v < 64);
	    }
	}
}

keyin()
{
	int i, j;
	static char buf[512];
  
/****/printf("keyin()\n");
	i = read(0, buf, sizeof buf);
	if (i < 1)
	    return;
	if (buf[0] == '*') {
	    getidata();
	    rstart();
	} else if (buf[0] == 'X') {
	    sysex(Isetup, sizeof Isetup, 0, 0);
	    rstart();
	} else if (buf[0] == 'Z') {
	    sysex(Osetup, sizeof Osetup, 0, 0);
	    rstart();
	} else if (i > 3 && buf[1] == '=') {
	    i = atoi(buf);
	    j = atoi(&buf[2]);
	    setfader(i, j);
	} else
	    printf("Urecognized: %s", buf);
}

setfader(n, new)
{
	register int old, oldy, newy;

	mput(CH_CTL | Chan, Fcntl[n], new);
}

newfader(n, new)
{
	Fade[n] = new;
}

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;
/****/printf("mput(%x,%x,%x); Ofh=%d\n", a, b, c, Ofh);
	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 */
	    *bp++ = MPU_RESET;
	    *bp++ = MPU_MIDI_THRU_OFF;
	    *bp++ = MPU_SEND_MEASURE_END_OFF;
	    *bp++ = MPU_START_RECORD;
	    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 = read(Ifh, rbp, rlen)) <= 0) {
		fprintf(stderr, "read(midi) returned %d\n", i);
		break;
	    }
/****/printf("sysex() read %d byte(s)\n", i);
	    bufend = rbp + i;
	    for (bp = rbp; bp < bufend && *bp != SX_EOB; bp++);
	    rlen -= i;
	    rbp += i;
	} while (rlen > 0 && bp >= bufend);
/****/printf("sysex() returns %d\n", rbp - rbuf);
	return(rbp - rbuf);
}

rstart()
{
	char *bp, buf[64];

	bp = buf;
	*bp++ = MPU_RESET;
	*bp++ = MPU_MIDI_THRU_OFF;
	*bp++ = MPU_SEND_MEASURE_END_OFF;
	*bp++ = MPU_BENDER_ON;
	*bp++ = MPU_START_RECORD;
	write(Ofh, buf, bp - buf);
}
