/* ** NOTEDUR -- Adjust note durations ** Two pass, first copy all but key-offs and calc key-off times. ** On second pass insert key-offs. ** newdur = max(Fact * olddur + Konst, Mindur) ** psl 1/86 */ #include #include #define MAXCHAN 16 #define MAXKEY 128 #define UNITY 1024 #define MAXQ 4096 struct qstr { /* key-off events */ long when; /* when it should happen */ char eflg; /* set when key-off for 0-length note */ char mode; /* status & channel */ char key; /* key number */ } Queue[MAXQ]; struct qstr *Qip = Queue; struct qstr *Qop = Queue; char Tempfile[64]; int Konst = 0; /* constant note duration, MIDI clocks */ int Fact = UNITY; /* proportion of old duration */ int Mindur = 10; /* minimum note duration, MIDI clocks */ int Mono = 0; /* if set, clip overlapped notes */ int Join = 0; /* if set, join overlapped notes */ int Legato = 0; /* if set, connect notes */ main(argc, argv) char *argv[]; { FILE *tfp; int comp(); extern double atof(); if (argc < 2) { syntax: fprintf(stderr, "Usage: %s [options] new\n", argv[0]); fprintf(stderr, "-f#.# sets the new/old duration ratio.\n"); fprintf(stderr, "-k# sets a duration constant in MPU clocks.\n"); fprintf(stderr, "-legato connects notes (per channel)\n"); fprintf(stderr, "-m# sets the minimum duration in MPU clocks.\n"); fprintf(stderr, "-mono clips overlapping notes (per channel).\n"); fprintf(stderr, "-staccato is a synonym for: -f0.25 -k10.\n"); fprintf(stderr, "-articulated is a synonym for: -f0.98.\n"); fprintf(stderr, "-join causes overlapped notes to become one.\n"); fprintf(stderr, "In general, new_dur = max(f * old_dur + k, m)\n"); fprintf(stderr, "defaults are: -f1.0 -k0 -m10\n"); exit(2); } while (--argc > 0) { if (argv[argc][0] == '-') { switch (argv[argc][1]) { case 'a': Fact = UNITY * 0.98; break; case 'f': Fact = UNITY * atof(&argv[argc][2]); break; case 'j': Join = 1; break; case 'k': Konst = atoi(&argv[argc][2]); break; case 'l': Legato++; break; case 'm': if (argv[argc][2] == 'o') Mono++; else Mindur = atoi(&argv[argc][2]); break; case 's': Fact = UNITY * 0.25; Konst = 10; break; default: goto syntax; } } } if (Mindur < 0) goto syntax; sprintf(Tempfile, "/tmp/notedur%d", getpid()); if ((tfp = fopen(Tempfile, "w")) == (FILE *) NULL) { perror(Tempfile); exit(1); } copyfile(stdin, tfp); fclose(tfp); qsort(Queue, Qip - Qop, sizeof Queue[0], comp); if ((tfp = fopen(Tempfile, "r")) == (FILE *) NULL) { perror(Tempfile); exit(1); } merge(tfp, stdout); unlink(Tempfile); } copyfile(ifp, ofp) /* first pass, copy all but note offs */ FILE *ifp, *ofp; { register int chan, k, v; int key[MAXCHAN][MAXKEY], lkey[MAXCHAN]; long now, start[MAXCHAN][MAXKEY]; MCMD *mp; for (chan = MAXCHAN; --chan >= 0; lkey[chan] = 0) for (k = MAXKEY; --k >= 0; key[chan][k] = 0); for (now = 0L; mp = getmcmd(ifp, now); ) { now = mp->when; if ((mp->cmd[0] & M_CMD_MASK) != CH_KEY_OFF && (mp->cmd[0] & M_CMD_MASK) != CH_KEY_ON) { putmcmd(ofp, mp); continue; } if ((mp->cmd[0] & M_CMD_MASK) == CH_KEY_OFF) mp->cmd[2] = 0; chan = mp->cmd[0] & M_CHAN_MASK; k = mp->cmd[1]; v = mp->cmd[2]; if (v != 0) { /* key-on */ if (key[chan][k] == 0 || Join == 0) { putmcmd(ofp, mp); start[chan][k] = now; } key[chan][k]++; if (!Legato && !Mono) continue; if (lkey[chan] && lkey[chan] != k) key_off(now, chan, lkey[chan], start, key, lkey); lkey[chan] = k; } else /* key-off */ if (!Legato) key_off(now, chan, k, start, key, lkey); } for (chan = MAXCHAN; --chan >= 0; ) for (k = MAXKEY; --k >= 0; ) if (key[chan][k]) key_off(now, chan, k, start, key, lkey); } key_off(when, chan, k, start, key, lkey) long when; long start[MAXCHAN][MAXKEY]; int key[MAXCHAN][MAXKEY], lkey[MAXCHAN]; { int dur; if (k == lkey[chan]) /* key-off */ lkey[chan] = 0; if (--key[chan][k] < 0) { /* extra key-off */ key[chan][k] = 0; return; } if (key[chan][k] == 0 || Join == 0) { dur = (Fact * (when - start[chan][k])) / UNITY + Konst; dur = dur < Mindur? Mindur : dur; Qip->when = start[chan][k] + dur; Qip->eflg = (dur == 0); Qip->mode = 0x90 | chan; Qip->key = k; if (++Qip == &Queue[MAXQ]) Qip = Queue; if (Qip == Qop) { fprintf(stderr, "Key-off queue overflow!\n"); exit(1); } } } comp(q1, q2) struct qstr *q1, *q2; { register int i; i = (int) (q1->when - q2->when); return(i? i : (q1->eflg - q2->eflg)); } merge(ifp, ofp) FILE *ifp, *ofp; { register int lastmode; unsigned char mbuf[8]; long now; MCMD *mp, m; lastmode = 0; m.len = 3; m.cmd = mbuf; mbuf[2] = 0; putmcmd((FILE *) 0, (MCMD *) 0); /* reset output clock */ for (now = 0L; mp = getmcmd(ifp, now); lastmode = mp->cmd[0]) { now = mp->when; while (Qop != Qip && (Qop->when < now || (Qop->when == now && Qop->eflg == 0))) { m.when = Qop->when; mbuf[0] = Qop->mode; mbuf[1] = Qop->key; putmcmd(ofp, &m); if (++Qop == &Queue[MAXQ]) Qop = Queue; } putmcmd(ofp, mp); } while (Qop != Qip) { m.when = Qop->when; mbuf[0] = lastmode = Qop->mode; mbuf[1] = Qop->key; putmcmd(ofp, &m); if (++Qop == &Queue[MAXQ]) Qop = Queue; } if (lastmode != RT_TCWME) { /* a final command */ Rt_tcwme.when = now; putmcmd (ofp, &Rt_tcwme); } }