#include <stdio.h>
#include <midi.h>
/*
**	KMAP		psl 10/85
**	Copy midi data, remapping the key numbers.
*/

#define	MINOCT	-1
#define	MAXOCT	9

char	noteval[]	= { 9, 11, 0, 2, 4, 5, 7, };
int	map[128];		/* key mappings */
int	alloct;			/* set by keynum() */

main(argc,argv)
char	*argv[];
{
	register char *cp;
	int i, j, args, only, noct;
	long now;
	MCMD *mp;

	for (i = 128; --i >= 0; map[i] = -1);
	args = only = 0;
	while (--argc > 0) {
            cp = argv[argc];
            if (*cp == '-') {
                if (*++cp == 'o')
                    only = 1;
		else
		    goto syntax;
		continue;
            }
	    i = keynum(cp);
	    noct = alloct;
	    while (*cp && *cp != '=')
		cp++;
	    if (*cp++ != '=')
		goto syntax;
	    j = keynum(cp);
	    if (alloct && noct)
		for (; i < 128 && j < 128; i += 12, j+= 12)
		    map[j] = i;
	    else if (alloct)
		for (; j < 128; j += 12)
		    map[j] = i;
	    else
		map[j] = i;
	    args++;
        }
	if (args == 0) {
syntax:
	    fprintf(stderr, "Usage: %s [-only] new=old ...\n", argv[0]);
	    fprintf(stderr, "where 'new' is the destination key,\n");
	    fprintf(stderr, "      'old' is the original key\n");
	    fprintf(stderr, "  and 'only' means ignore other keys.\n");
	    fprintf(stderr, "Keys may be specified as:\n");
	    fprintf(stderr, "	note name -- 'c#4', 'Bb3', 'c*', or 'G#*' \n");
	    fprintf(stderr, "	hex value -- 'X3C', 'x3C', 'X3c', or 'x3c'\n");
	    fprintf(stderr, "	dec value -- '60'\n");
	    fprintf(stderr, "C4=C*, C*=D* are legal, but C*=D4 is not\n");
	    exit(2);
	}
	if (only == 0)
	    for (i = 128; --i >= 0; )
		if (map[i] == -1)
		    map[i] = i;
	for (now = 0l; mp = getmcmd(stdin, now); now = mp->when) {
	    cp = (char *) mp->cmd;
	    if ((*cp & M_CMD_MASK) == CH_KEY_ON
	     || (*cp & M_CMD_MASK) == CH_KEY_OFF)
		if ((cp[1] = map[cp[1]]) == -1)
		    continue;
	    putmcmd(stdout, mp);
	}
}

keynum(str)
char	*str;
{
	register char *cp;
	int note, oct;

	alloct = 0;
	cp = str;
	if ('0' <= *cp && *cp <= '9') {		/* decimal */
	    return(atoi(cp));
	} else if (*cp == 'x' || *cp == 'X') {	/* hex */
	    return((hex(cp[1]) << 4) + hex(cp[2]));
	} else {				/* note name */
	    if ('A' <= *cp && *cp <= 'G')
		note = noteval[*cp - 'A'];
	    else if ('a' <= *cp && *cp <= 'g')
		note = noteval[*cp - 'a'];
	    else
		goto oops;
	    if (*cp == '#') {
		note++;
		cp++;
	    } else if (*cp == 'b') {
		--note;
		cp++;
	    }
	    if (*cp == '*') {
		alloct = 1;
		oct = MINOCT;
	    } else {
		oct = atoi(cp);
		if (oct < MINOCT || oct > MAXOCT) {
oops:
		    fprintf(stderr, "Note format error: %s\n", str);
		    exit(2);
		}
	    }
	    return(12 * (oct - MINOCT) + note);
	}
}

hex(c)
char	c;
{
	if ('0' <= c && c <= '9')
	    return(c - '0');
	if ('A' <= c && c <= 'F')
	    return(c - 'A' + 10);
	if ('a' <= c && c <= 'f')
	    return(c - 'a' + 10);
	fprintf(stderr, "Error in hex digit '%c'\n", c);
	return(0);
}
