/* ** MIDI2M -- Turn MIDI files into m-format files ** psl 3/86 ** Added "percussion"; ignore note-offs & use the next note-on ** to define time value. ** Also use "X" for lyric instead of "-" ** Added triplets. ** Added forced barlines */ #include #include #include #define MAXCHAN 16 #define MAXV 8 #define MAXKEY 128 #define SCALE 8 #define BAR (SCALE * 2 * MPU_CLOCK_PERIOD) char *Title = 0; /* title of piece */ int Meter1 = 4, Meter2 = 4; /* time signature of piece */ int Bflg = 0; /* force barlines */ int Cpb = BAR; /* clocks per bar (scaled) */ int Cpw = BAR; /* clocks per whole note (scaled) */ int Perc = 0; /* input is percussion */ long Start[MAXCHAN]; /* starts of notes */ int Curkey[MAXCHAN]; /* keys of notes */ int Qpb = 192; /* quanta per bar */ int Quant; /* clocks per quantum */ int Numv = 1; /* number of voices to display */ int Voices[MAXCHAN] = { 1, }; /* associate voices & channels */ int Chans[MAXV + 1] = { 0, }; /* associate channels & voices */ long quantize(); syntax(prog) char *prog; { fprintf(stderr, "Usage: %s [options] files...\n", prog); fprintf(stderr, "-b\tForce barlines every bar (instead of TCWME)\n"); fprintf(stderr, "-L#\tSet # MIDI clocks per bar\n"); fprintf(stderr, "-m#/#\tSet meter to #/#\n"); fprintf(stderr, "-p\tTreat the inmput as percussion (weird)\n"); fprintf(stderr, "-q#\tSet smallest quantum to #\n"); fprintf(stderr, "-tTITLE\tSet title to TITLE\n"); fprintf(stderr, "-v#=#\tTake voice # (1-8) from channel # (1-16)\n"); exit(2); } main(argc, argv) char *argv[]; { int i, n, voice, chan; FILE *f; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'b': Bflg++; break; case 'L': Cpb = SCALE * atoi(&argv[i][2]); break; case 'm': Meter1 = atoi(&argv[i][2]); if (argv[i][3] == '/') Meter2 = atoi(&argv[i][4]); else if (argv[i][4] == '/') Meter2 = atoi(&argv[i][5]); else syntax(argv[0]); if (Meter2 <= 1 || Meter2 <= 0) syntax(argv[0]); break; case 'p': Perc = 1; break; case 'q': Qpb = atoi(&argv[i][2]); break; case 't': Title = &argv[i][2]; break; case 'v': voice = atoi(&argv[i][2]); chan = atoi(&argv[i][4]) - 1; if (argv[i][3] != '=' || voice < 1 || voice > MAXV || chan < 0 || chan >= MAXCHAN) syntax(argv[0]); if (voice > Numv) Numv = voice; Chans[voice] = chan; Voices[chan] = voice; break; default: syntax(argv[0]); } } } Quant = Cpb / Qpb; if (Qpb * Quant != Cpb) printf("Warning: %d does not divide %d evenly enough.\n", Qpb, Cpb / SCALE); Cpw = (Meter2 * Cpb) / Meter1; n = 0; for (i = 1; i < argc; i++) { if (argv[i][0] != '-') { if ((f = sopen(argv[i], "r")) != NULL) { cpn(f, stdout); sclose(f); n++; } else perror(argv[i]); } } if (n == 0) cpn(stdin, stdout); } cpn(ifp, ofp) FILE *ifp, *ofp; { int status, type, voice, chan, key, vel; long now, snow, nbar; MCMD *mp; long flsh(); if (Title) fprintf(ofp, "#TITLE\t%s\n", Title); fprintf(ofp, "#METER\t%d\t%d\n", Meter1, Meter2); fprintf(ofp, "#VOICES"); for (voice = 1; voice <= Numv; fprintf(ofp, "\t%d", voice++)); fprintf(ofp, "\n"); fprintf(ofp, "#CHAN"); for (voice = 1; voice <= Numv; fprintf(ofp, "\t%d", Chans[voice++]+1)); fprintf(ofp, "\n"); fprintf(ofp, "#BAR\n"); nbar = Bflg? Cpb / SCALE : 999999; now = 0L; while (mp = getmcmd(ifp, now)) { now = mp->when; snow = SCALE * now; status = mp->cmd[0]; type = (status & M_CMD_MASK); if (type == CH_KEY_OFF) { type = CH_KEY_ON; mp->cmd[2] = 0; } if (type == CH_KEY_ON) { if (nbar <= mp->when) nbar = flsh(ofp, nbar, mp->when, snow); chan = (status & M_CHAN_MASK); if (Voices[chan] == 0) continue; key = mp->cmd[1]; vel = mp->cmd[2]; if (vel > 0) { putcpn(ofp, chan, snow); Curkey[chan] = key; } else if (key == Curkey[chan] && !Perc) { putcpn(ofp, chan, snow); Curkey[chan] = 0; } } else if (status == RT_TCWME && nbar == 999999) flsh(ofp, nbar, 0L, snow); } flsh(ofp, nbar, now, snow); if (nbar < now + Cpb / SCALE) fprintf(ofp, "#BAR\n"); } long flsh(ofp, nbar, when, snow) FILE *ofp; long nbar, when, snow; { register int voice; for (voice = 1; voice <= Numv; voice++) { putcpn(ofp, Chans[voice], snow); Start[Chans[voice]] = snow; /* shouldn't be needed, but */ } while (nbar <= when) { fprintf(ofp, "#BAR\n"); nbar += Cpb / SCALE; } if (nbar == 999999) fprintf(ofp, "#BAR\n"); return(nbar); } putcpn(fp, chan, now) /* current note or rest ends now; output it */ FILE *fp; long now; { int key, dur; long tbeg; key = Curkey[chan]; tbeg = Start[chan]; dur = quantize(now - tbeg); if (dur) Start[chan] = tbeg + putnote(fp, key, dur, chan); } char *nn[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", }; putnote(fp, n, t, chan) /* put out note and return actual duration */ FILE *fp; { register int i, voice, vd, vt, dur; static char dbuf[8], tbuf[8]; voice = Voices[chan]; fprintf(fp, "%c\t", Perc? 'X' : '/'); for (i = 1; i < voice; i++) fprintf(fp, "-\t"); if (n == 0) fprintf(fp, "R"); else fprintf(fp, "%s%d", nn[n%12], n / 12 - 1); if ((vd = tvd(t, dbuf)) == t || vd >= (vt = tvt(t, tbuf))) { fputs(dbuf, fp); dur = vd; } else { fputs(tbuf, fp); dur = vt; } for (i = voice; i < Numv; i++) fprintf(fp, "\t-"); putc('\n', fp); if (t - dur > Cpw / 64) dur += putnote(fp, n, t - dur, chan); /* divine [sic] */ return(dur); } tvd(t, cp) /* return closest dotted approx. (always <= t) */ register char *cp; { register int i, v, vd; v = Cpw; for (i = 0; v > t && i < 6; v >>= 1, i++); *cp++ = "whqestf"[i]; vd = v; for (i = 0; vd + (v >>= 1) <= t && i < 4 && v >= Quant; i++) { *cp++ = '.'; vd += v; } *cp = '\0'; return(vd); } tvt(t, cp) /* return closest triplet approx. (always <= t) */ register char *cp; { register int i, v; v = (2 * Cpw) / 3; for (i = 0; v > t && i < 6; v >>= 1, i++); *cp++ = "whqestf"[i]; *cp++ = 't'; *cp = '\0'; return(v); } long quantize(t) long t; { return(Quant * ((t + Quant / 2) / Quant)); }