/* ** 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 #include #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]); }