/*
**	STATS -- Print various statistics about an MPU piece
**	psl 4/88
*/
#include	<stdio.h>
#include	<midi.h>

#define	NUMCHAN	MIDI_MAX_CHANS
#define	NUMKEYS	128
#define	NUMVELS	128
#define	MAXDUR	480

char	Cstr[256], Istr[256];
int	Strlim	= 64;
int	Coflo	= 0;
int	Ioflo	= 0;
int	Events[NUMCHAN];
int	Dur[MAXDUR];
int	Mcmd[NUMCHAN][7];
int	Key[NUMKEYS];
int	Koff	= 0;
int	Vel[NUMVELS];
int	Sys[16];
int	Chan	= -1;		/* specified channel */
int	Vflg	= 0;		/* verbose */

char	*key2name(), *endof();

main(argc, argv)
char	*argv[];
{
	register int i, j;
	FILE *ifp;

	j = 0;
	for (i = 1; i < argc; i++) {
	    if (argv[i][0] == '-') {
		switch (argv[i][1]) {
		case 'c':			/* only count one channel */
		    Chan = atoi(&argv[i][2]) - 1;
		    if (Chan < 0 || Chan >= NUMCHAN)
			goto syntax;
		    break;
		case 'v':			/* verbose */
		    Vflg++;
		    Strlim = 240;
		    break;
		default:
syntax:
		    fprintf(stderr,
		     "Usage: %s [-c#] [-v] [files or stdin]\n", argv[0]);
		    fprintf(stderr, "channel numbers are 1-16\n");
		    exit(2);
		}
	    } else {
		if (ifp = fopen(argv[i], "r")) {
		    printf("\n%s\t", argv[i]);
		    stats(ifp);
		    fclose(ifp);
		} else
		    perror(argv[i]);
		j++;
	    }
	}
	if (j == 0)
	    stats(stdin);
}

stats(ifp)
FILE	*ifp;
{
	register int i, j, chan, stat;
	char *cp;
	long now, start[NUMCHAN][NUMKEYS];
	MCMD *mp;

	for (i = NUMCHAN; --i >= 0; Events[i] = 0)
	    for (j = 7; --j >= 0; Mcmd[i][j] = 0);
	for (i = NUMKEYS; --i >= 0; Key[i] = 0);
	for (i = NUMVELS; --i >= 0; Vel[i] = 0);
	for (i = 16; --i >= 0; Sys[i] = 0);
	Coflo = Ioflo = 0;
	if (Vflg) {
	    for (i = MAXDUR; --i >= 0; Dur[i] = 0);
	    Koff = 0;
	    for (i = NUMCHAN; --i >= 0; )
		for (j = NUMKEYS; --j >= 0; start[i][j] = 0);
	}
	for (now = 0L; mp = getmcmd(ifp, now); now = mp->when) {
	    stat = mp->cmd[0];
	    if (stat >= SX_CMD) {	/* these have no channel */
		if (Chan > -1)
		    continue;
		Sys[stat & 0x0F]++;
		if (stat == SX_CMD
		 && mp->cmd[1] == ID_MISC
		 && SUBID(mp->cmd[2],mp->cmd[3]) == SUBID_HEADER) {
		    mp->cmd[mp->len - 1] = '\0';
		    printf("%s\n", &mp->cmd[4]);
		}
		continue;
	    }
	    chan = (stat & M_CHAN_MASK);
	    if (Chan > -1 && chan != Chan)
		continue;
	    Events[chan]++;
	    Mcmd[chan][(stat >> 4) & 0x07]++;
	    if ((stat & M_CMD_MASK) == CH_KEY_OFF) {
		if (Vflg)
		    Koff++;
		stat ^= (CH_KEY_OFF ^ CH_KEY_ON);
		mp->cmd[2] = 0;
	    }
	    if ((stat & M_CMD_MASK) == CH_KEY_ON && mp->cmd[2] > 0) {
		Key[mp->cmd[1]]++;
		Vel[mp->cmd[2]]++;
		if (Vflg)
		    start[chan][mp->cmd[1]] = mp->when;
	    } else if ((stat & M_CMD_MASK) == CH_KEY_ON) {	/* (key-off) */
		if (Vflg) {
		    j = mp->when - start[chan][mp->cmd[1]];
		    Dur[j < MAXDUR? j : MAXDUR - 1]++;
		}
	    } else if ((stat & M_CMD_MASK) == CH_PRG) {
		if ((cp = endof(Istr)) < Istr + Strlim)
		    sprintf(cp, " %d=%d", chan + 1, mp->cmd[1] + 1);
		else
		    Ioflo++;
	    } else if ((stat & M_CMD_MASK) == CH_CTL) {
		if ((cp = endof(Cstr)) < Cstr + Strlim)
		    sprintf(cp, " %d,%d=%d", chan + 1, mp->cmd[1], mp->cmd[2]);
		else
		    Coflo++;
	    }
	}
	printf("%g bars (%d MPU clocks)\n",
	 now / (2. * MPU_CLOCK_PERIOD), now);
	for (chan = 0; chan < NUMCHAN; chan++) {
	   if (Events[chan]) {
		if (Vflg)
		    printf("%d event%s from chan %d\n",
		     Events[chan], Events[chan] == 1? "" : "s", chan + 1);
		else
		    printf("%sC%d: %d event%s", chan < 9? " " : "",
		     chan + 1, Events[chan], Events[chan] == 1? "" : "s");
		pmc(Mcmd[chan][0], "note-off", 0x80);
		pmc(Mcmd[chan][1], "note-on", 0x90);
		pmc(Mcmd[chan][2], "poly pressure", 0xA0);
		pmc(Mcmd[chan][3], "controller", 0xB0);
		pmc(Mcmd[chan][4], "program", 0xC0);
		pmc(Mcmd[chan][5], "channel pressure", 0xD0);
		pmc(Mcmd[chan][6], "pitch bend", 0xE0);
		if (!Vflg)
		    printf("\n");
	    }
	}
	printf("Keys:");
	i = 0;
	for (j = 0; j < NUMKEYS; j++)
	    if (Key[j])
		printf("%s%4d %3s(%x)",
		 (i++ && (i % 4) == 1)? "\n\t" : "\t", Key[j], key2name(j), j);
	printf("\nVels:");
	i = 0;
	for (j = 0; j < NUMVELS; j++)
	    if (Vel[j])
		printf("%s%4d %3d",
		 (i++ && (i % 4) == 1)? "\n\t" : "\t", Vel[j], j);
	if (Vflg) {
	    printf("\nDurs:");
	    i = 0;
	    for (j = 0; j < MAXDUR; j++)
		if (Dur[j])
		    printf("%s%4d %3d",
		     (i++ && (i % 4) == 1)? "\n\t" : "\t", Dur[j], j);
	}
	printf("\n");
	if (i = Sys[0])
	    printf("%d sys exclusive (0xF0) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[1])
	    printf("%d sys undef (0xF1) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[2])
	    printf("%d measure select (0xF2) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[3])
	    printf("%d song select (0xF3) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[4])
	    printf("%d common undef (0xF4) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[5])
	    printf("%d common undef (0xF5) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[6])
	    printf("%d tune request (0xF6) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[8])
	    printf("%d no-op (0xF8) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[9])
	    printf("%d measure end (0xF9) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[10])
	    printf("%d RT start (0xFA) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[11])
	    printf("%d RT continue (0xFB) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[12])
	    printf("%d RT undef (0xFC) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[13])
	    printf("%d RT undef (0xFD) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[14])
	    printf("%d RT undef (0xFE) command%s\n", i, i == 1? "" : "s");
	if (i = Sys[15])
	    printf("%d RT reset (0xFF) command%s\n", i, i == 1? "" : "s");
	if (*Istr)
	    printf("inst%s%s\n", Istr, Ioflo? "..." : "");
	if (*Cstr)
	    printf("cntl%s%s\n", Cstr, Coflo? "..." : "");
	if (Vflg)
	    printf("%d key-off (0x8?) command%s\n", Koff, Koff == 1? "" : "s");
}

pmc(i, nam, num)
char	*nam;
{
	if (i == 0)
	    return;
	if (Vflg)
	    printf("%d %s (0x%x) command%s\n", i, nam, num, i == 1? "" : "s");
	else
	    printf(" %d(0x%x)", i, num);
}

char	*
endof(str)
register char	*str;
{
	for (; *str; str++);
	return(str);
}
