/*
**	KEYVEL		psl 9/1/85
** Copy midi data, processing the key velocities as follows:
**  1) scale it by "gain"
**  2) compress it by "comp"
**  3) expand it by "expa"
**  4) randomize it by "rnd"
**  5) limit it to the range "bottom" to "top"
** or
**  6) print a histogram of key velocites, (but no midi output)
** Gain simply scales the data (a multiplication), making an asymmetric
** transfer function as in the first figure below.
** Compression scales the data symmetrically around the mid-point, making
** a flattened, symmetrical transfer function as in the second figure.
** Expansion maps the domain through a polynomial term, making a symmetric,
** (smooth), S-curve transfer function as in the third figure.
** Each randomization adds a uniformly distributed random number in the range
** -(int)(rnd/2) to (int)((rnd-1)/2).  Several randomizations can be specified;
** as more are added the effect becomes more gaussian.
** Limiting sets all values below "bottom" to "bottom" and all those above
** "top" to "top".  The defaults are 1 and 127.
** BEWARE: setting "bottom" to 0 will turn note-on events to note-off events!
** 127 |       /---:	127 |       :	127 |          ___:
**     |      /    :	    |       :	    |         /   :
** out |     /     :	out |       /	out |       /     :
**     |    /      :	    |     / :	    |      /      :
**  64 |   /       :	 64 |   /   :	 64 |      |      :
**     |  /        :	    | /     :	    |      /      :
**     | /  g=1.4  :	    /  c=1. :	    |     /  s=1. :
**     |/          :	    |       :	    |___/         :
**   0 +-----+-----+	  0 +---+---+	  0 +------+------+
**     0    64    127	    0  64  127	    0     64     127
**	    in		       in	          in
** Negative gain makes all the data closer to 1 (the minimum value).
** Negative compression makes a symmetric, three-segment "expansion".
** Negative expansion, makes a symmetric, smooth, S-curve "compression".
** Combinations of these three yield "innaresting" results.
*/
#include	<stdio.h>
#include	<midi.h>

#define	MAXR	8

int	Hist = 1, Bot = 1, Top = 127, Rnd[MAXR], Nr = 0;
double	Comp = 1., Expa = 1., Gain = 1.;

char	*Usage =
	"Usage: %s [-g#.#] [-c#.#] [-e#.#] [-b#] [-t#] [-h] [files or stdin]\n";

extern	double	atof(), pow();

main(argc, argv)
char	*argv[];
{
	int i = 1, n = 0;
	FILE *f;

	for (i = 1; i < argc; i++) {
	    if (argv[i][0] == '-') {
		switch (argv[i][1]) {
		case 'g': --Hist; Gain = atof(&argv[i][2]); break;
		case 'c': --Hist; Comp = pow(2., -atof(&argv[i][2])); break;
		case 'e': --Hist; Expa = pow(2., -atof(&argv[i][2])); break;
		case 'b': --Hist; Bot = atoi(&argv[i][2]); break;
		case 'r': --Hist; if (Nr < MAXR) Rnd[Nr++] = atoi(&argv[i][2]);
		    break;
		case 't': --Hist; Top = atoi(&argv[i][2]); break;
		case 'h': Hist = 99; break;
		default: fprintf(stderr, Usage, argv[0]); exit(2);
		}
	    } else
		n++;
	}
	if (Hist < 0)
	    Hist = 0;
	if (Nr)
	    srand((int) time(0));
	if (n == 0)
	    keyvel(stdin, Hist? 0 : stdout);
	else {
	    for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-')
		    continue;
		if (f = sopen(argv[i], "r")) {
		    keyvel(f, Hist? 0 : stdout);
		    sclose(f);
		} else
		    perror(argv[i]);
	    }
	}
	exit(0);
}

keyvel(in, out)
FILE	*in, *out;
{
	register int i, j;
	int h[128];
	long now;
	double x;
	MCMD *mp;

	if (Hist)
	    for (i = 0; i < 128; h[i++] = 0);
	for (now = 0L; mp = getmcmd(in, now); ) {
	    now = mp->when;
	    if ((mp->cmd[0] & M_CMD_MASK) == CH_KEY_ON) {
		if ((i = mp->cmd[2]) > 0) {
		    if (Gain != 1.)
			i = i * Gain;
		    if (Comp != 1.)
			i = (i - 64) * Comp + 64;
		    if (Expa != 1.)
			if ((x = (i - 64) / 64.) >= 0)
			    i = 64 + pow(x, Expa) * 64.;
			else
			    i = 64 - pow(-x, Expa) * 64.;
		    for (j = Nr; --j >= 0; )
			i += (rand() % Rnd[j]) - Rnd[j] / 2;
		    i = i < Bot? Bot : (i>Top? Top : i);
		    if (Hist)
			h[i]++;
		    mp->cmd[2] = i;
		}
	    }
	    if (out)
		putmcmd(out, mp);
	}
	if (Hist)
	    for (i = 0; i < 128; i++)
		if (h[i])
		    printf("%3d: %d\n", i, h[i]);
}
