/*
**	TMOD -- Modify timing of a MPU data stream
**	psl 5/88
*/
#include	<stdio.h>
#include	<midi.h>

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

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

long	whencalc();
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 TSPEC [...] [files or stdin] >new\n",
	     argv[0]);
	    fprintf(stderr, "The TSPECs can be in one of two forms:\n");
	    fprintf(stderr, " -d#:##:###\n");
	    fprintf(stderr, "\t# is the (final) duration ratio.\n");
	    fprintf(stderr, "\t## is the transition beginning point.\n");
	    fprintf(stderr, "\t### is the length of the transition.\n");
	    fprintf(stderr, " -t#:#:#\n");
	    fprintf(stderr, "\t# is the ending tempo 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. either -d2:12:2 or -t0.5:12:2 will cut the");
	    fprintf(stderr, " tempo 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 'd':
		case 't':
		    cp = &argv[i][2];
		    if ((q = atof(cp)) == 0.)
			goto syntax;
		    if (argv[i][1] == 'd')
			T[Nt].dort = DI;
		    else {
			T[Nt].dort = TI;
			q = 1. / q;
		    }
		    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")) {
		    tmod(ifp, stdout);
		    fclose(ifp);
		    n++;
		} else {
		    perror(argv[i]);
		    goto syntax;
		}
	    }
	}
	if (n == 0)
	    tmod(stdin, stdout);
	exit(0);
}

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

	now = 0L;
	putmcmd(0, 0);
	while (mp = getmcmd(ifp, now)) {
	    now = mp->when;
	    mp->when = whencalc(now);
	    putmcmd(ofp, mp);
	}
}

long
whencalc(now)
long	now;
{
	int i;
	long when, dt, dur;
	double r, or, nr;

	when = 0L;
	for (i = 1; i < Nt && now > T[i].beg; i++) {
	    or = T[i - 1].ratio;		/* starting duration ratio */
	    nr = T[i].ratio;			/* final duration ratio */
	    dt = T[i].beg - T[i - 1].end;	/* time at previous ratio */
	    if (dt > 0L)
		when += or * dt;
	    dur = T[i].end - T[i].beg;		/* duration of change */
	    if (dur > 0L) {
		dt = (now < T[i].end)? (now - T[i].beg) : dur;
		r = interp(T[i].dort, or, nr, dur, dt);	/* avg duration ratio */
		when += r * dt;
	    }
	}
	dt = now - T[i - 1].end;
	if (dt > 0L)
	    when += T[i - 1].ratio * dt;
	return(when);
}

double
interp(dort, b, e, whole, part)	/* "linearly" interpolat between b & e */
double	b, e;
long	whole, part;
{
	double r;

	if (dort == TI) {
	    b = 1. / b;
	    e = 1. / e;
	}
	r = b + ((e - b) * part) / whole;
	return(dort == TI? 1. / r : r);
}
