/* ** FRACT -- Fractal melody interpolator ** psl 12/86 */ #include #include #include #define DEFRUG 4.0 u_char Pnobuf[8]; /* pending note off command buffer */ int Res; /* longest fractal segment */ int Seed; /* "random" number seed */ int Dflg = 0; /* Diatonic; force C major scale */ int Chan = -1; /* channel for interpolated notes */ double Rug = DEFRUG; /* ruggedness */ MCMD Pno; /* pending note off command */ char *key[] = { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B", }; main(argc, argv) char *argv[]; { u_char obuf[8], nbuf[8]; int status; long onow, nnow; MCMD om, *nmp; extern double atof(); while (--argc > 0) { if (argv[argc][0] == '-') { switch (argv[argc][1]) { case 'c': /* set output channel */ Chan = atoi(&argv[argc][2]) - 1; break; case 'd': /* diatonic output */ Dflg++; break; case 'r': /* set ruggedness */ Rug = atof(&argv[argc][2]); break; case 's': /* set "random" SEED */ Seed = atoi(&argv[argc][2]); break; default: goto syntax; } } else if (Res == 0) Res = atof(argv[argc]) * 120; else { syntax: fprintf(stderr, "Usage: %s resolution [-c#] [-diatonic] [-rRUGGED] [-sSEED]]\n", argv[0]); fprintf(stderr, "Resolution in quarter notes (120 clocks).\n"); fprintf(stderr, "-c# puts all added notes on channel #.\n"); fprintf(stderr, "Diatonic forces C major scale.\n"); fprintf(stderr, "Default ruggedness is %g.\n", DEFRUG); fprintf(stderr, "Default SEED is taken from the time.\n"); exit(2); } } if (Seed == 0) Seed = time(0); onow = -1; nnow = 0; while (nmp = getmcmd(stdin, nnow)) { nnow = nmp->when; status = nmp->cmd[0]; if ((status & M_CMD_MASK) == CH_KEY_ON || (status & M_CMD_MASK) == CH_KEY_OFF) { if (nmp->cmd[2] == 0 || (status & M_CMD_MASK) == CH_KEY_OFF) { Pno.when = nnow; Pno = *nmp; savcmd(&Pno, Pnobuf); continue; } savcmd(nmp, nbuf); if (onow < 0) out(nmp); else fract(&om, nmp); onow = nnow; om = *nmp; savcmd(&om, obuf); } } Rt_tcwme.when = nnow; out(&Rt_tcwme); exit(0); } fract(omp, nmp) MCMD *omp, *nmp; { u_char mbuf[8]; int ov, nv; long d; MCMD mm; d = nmp->when - omp->when; if (d <= Res) out(nmp); else { mm = *nmp; savcmd(&mm, mbuf); mm.when = (omp->when + nmp->when) / 2; if (Chan >= 0) { mm.cmd[0] &= M_CMD_MASK; mm.cmd[0] |= Chan; } mm.cmd[1] = interp(omp->cmd[1], nmp->cmd[1], mm.when, d); ov = omp->cmd[2]? omp->cmd[2] : (nmp->cmd[2] / 2); nv = nmp->cmd[2]? nmp->cmd[2] : (omp->cmd[2] / 2); mm.cmd[2] = (ov + nv) / 2; fract(omp, &mm); fract(&mm, nmp); } } interp(on, nn, now, d) u_char on, nn; long now, d; { double dmid; int imid, i; now += Seed; i = ((now * now) & 0x0FFFFFFF) % 2001; dmid = (on + nn) / 2. + (Rug * d * (1000 - i)) / 120000.; imid = dmid + 0.5; i = imid % 12; if (Dflg && key[i][1] == 'b') imid = (imid > dmid)? imid - 1 : imid + 1; return(imid); } out(mp) MCMD *mp; { static unsigned char lbuf[8]; static MCMD l; if (Pno.when && Pno.when <= mp->when) { putmcmd(stdout, &Pno); if (l.cmd && eveq(&l, &Pno)) l.cmd = 0; if (mp != (MCMD *) 0 && eveq(mp, &Pno)) mp->cmd = 0; } Pno.when = 0; if (l.cmd) { l.cmd[2] = 0; l.when = mp->when; putmcmd(stdout, &l); if (mp != (MCMD *) 0 && mp->cmd && eveq(mp, &l)) mp->cmd = 0; l.cmd = 0; } if (mp == (MCMD *) 0) return; if (mp->cmd) { putmcmd(stdout, mp); if ((mp->cmd[0] & M_CMD_MASK) == CH_KEY_ON && mp->cmd[2]) { l = *mp; savcmd(&l, lbuf); l.cmd[2] = 0; } else l.cmd = 0; } else { Rt_tcwme.when = mp->when; putmcmd(stdout, &Rt_tcwme); } } eveq(ap, bp) MCMD *ap, *bp; { register int i; if (ap->len != bp->len) return(0); for (i = ap->len; --i >= 0; ) if (ap->cmd[i] != bp->cmd[i]) return(0); return(1); } savcmd(mp, buf) MCMD *mp; unsigned char *buf; { register int i; for (i = mp->len; --i >= 0; ) buf[i] = mp->cmd[i]; mp->cmd = buf; }