/*
**	VMOD -- Modify velocity of an MPU data stream
**	psl 8/88
*/
#include	<stdio.h>
#include	<midi.h>

#define	BARLEN	(2 * MPU_CLOCK_PERIOD)
#define	MAXT	64
#define	DI	0
#define	TI	1

struct	tstr	{
	double	ratio;		/* velocity ratio at end */
	long	beg;		/* when the change begins */
	long	end;		/* when the change ends */
} T[MAXT]	= {
	1.,	0,	0,
};
int	Nt	= 1;		/* how many entries in T[] */

double	interp();
extern double atof();

main(argc, argv)
char	*argv[];
{
	char *cp;
	int i, n;
	double q;
	FILE *ifp;

	if (argc < 2) {
syntax:
	    fprintf(stderr, "Usage: %s VSPEC [...] [files or stdin] >new\n",
	     argv[0]);
	    fprintf(stderr, "The VSPECs are in the form:\n");
	    fprintf(stderr, " -v#:##:###\n");
	    fprintf(stderr, "\t# is the (final) velocity ratio.\n");
	    fprintf(stderr, "\t## is the transition beginning point.\n");
	    fprintf(stderr, "\t### is the length of the transition.\n");
	    fprintf(stderr, "Both beginning and length are in units of input");
	    fprintf(stderr, " bars (480 MPU clocks) starting at 0.\n");
	    fprintf(stderr, "e.g. -v0.5:12:2 will cut the key velocity");
	    fprintf(stderr, " in half between input bars 12 & 14.\n");
	    exit(2);
	}
	for (i = 1; i < argc; i++) {
	    if (argv[i][0] == '-') {
		switch (argv[i][1]) {
		case 'v':
		    cp = &argv[i][2];
		    if ((q = atof(cp)) < 0.)
			goto syntax;
		    T[Nt].ratio = q;
		    for (; *cp && *cp != ':'; cp++);
		    if (*cp++ != ':')
			goto syntax;
		    T[Nt].beg = BARLEN * atof(cp);
		    for (; *cp && *cp != ':'; cp++);
		    if (*cp++ != ':')
			goto syntax;
		    T[Nt].end = T[Nt].beg + BARLEN * atof(cp);
		    if (T[Nt].end < T[Nt].beg)
			goto syntax;
		    Nt++;
		    break;
		default:
		    goto syntax;
		}
	    }
	}
/****for (i = 0; i < Nt; i++)
/**** fprintf(stderr, "%g\t%d\t%d\n", T[i].ratio, T[i].beg, T[i].end);
/****/
	n = 0;
	for (i = 1; i < argc; i++) {
	    if (argv[i][0] != '-') {
		if (ifp = fopen(argv[i], "r")) {
		    vmod(ifp, stdout);
		    fclose(ifp);
		    n++;
		} else {
		    perror(argv[i]);
		    goto syntax;
		}
	    }
	}
	if (n == 0)
	    vmod(stdin, stdout);
	exit(0);
}

vmod(ifp, ofp)
FILE	*ifp, *ofp;
{
	long now;
	MCMD *mp;

	now = 0L;
	putmcmd(0, 0);
	while (mp = getmcmd(ifp, now)) {
	    now = mp->when;
	    if ((mp->cmd[0] & M_CMD_MASK) == CH_KEY_ON)
		mp->cmd[2] = velcalc(now, mp->cmd[2]);
	    putmcmd(ofp, mp);
	}
}

velcalc(now, vel)
{
	int i;
	long dt, dur;
	double r, or, nr;

	if (vel == 0)
	    return(0);
	for (i = 1; i < Nt && now > T[i].beg; i++) {
	    or = T[i - 1].ratio;		/* starting ratio */
	    nr = T[i].ratio;			/* final ratio */
	    if (now >= T[i].end)
		r = nr;
	    else {
		dur = T[i].end - T[i].beg;	/* duration of change */
		dt = now - T[i].beg;		/* how far into change */
		r = or + ((nr - or) * dt) / dur;
	    }
	}
	vel = r * vel + 0.5;
	return(vel < 1? 1 : (vel > 127? 127 : vel));
}
