/* ** DP2MPU -- Convert drum pattern file to MPU (time-tagged MIDI) ** psl 4/88 ** See dp(5) for details. */ #include #include #define MAXNOTES 4096 /* maximum notes per file */ #define MAXKEY 128 #define MAXDEFS 256 #define MAXROLLS 8 #define SYMLEN 16 #define TSCALE 36 /* time kept in 36ths of MPU clocks */ struct defstr { char sym[SYMLEN]; char val[SYMLEN]; } Def[MAXDEFS]; int Ndefs = 0; struct rollstr { char sym; /* char to indicate roll */ int vol; /* index into Vol[] */ int quant; /* beats/bar */ } Roll[MAXROLLS] = { '~', 3, 64, }; int Nrolls = 1; struct notestr { long when; /* MPU clocks * TSCALE */ u_char mode; u_char key; u_char vel; } N[MAXNOTES]; int Dur = 1; /* 0 => make duration = 0 (else Cpn) */ int Nnotes = 0; int Quant = 8; int Barlen = TSCALE * 2 * MPU_CLOCK_PERIOD; int Cpn = (TSCALE * 2 * MPU_CLOCK_PERIOD) / 8; int Vol[] = { 0x01, 0x0e, 0x1c, 0x2a, 0x38, 0x47, 0x55, 0x63, 0x71, 0x7f, }; long When[MIDI_MAX_CHANS][MAXKEY]; long putdt(); struct defstr *lookup(); struct rollstr *rlookup(); main(argc, argv) char *argv[]; { register char *cp; char buf[512]; int i, dt, chan, key; long now, maxwhen, nextwhen; struct rollstr *rp; int compar(); for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'd': if (argv[i][2] != '0' || argv[i][3] != '\0') goto syntax; Dur = 0; break; default: goto syntax; } } else { syntax: fprintf(stderr, "Usage: %s [-d0] file.mpu\n", argv[0]); exit(2); } } maxwhen = 0L; while (fgets(buf, sizeof buf, stdin)) { if (*buf == '#') { for (cp = buf; *cp > ' '; cp++); for (; *cp && *cp <= ' '; *cp++ = '\0'); if (strcmp(buf, "#BARLEN") == 0) { Barlen = TSCALE * strtol(cp, 0, 10); Cpn = Barlen / Quant; } else if (strcmp(buf, "#DEFINE") == 0) { defdef(cp); } else if (strcmp(buf, "#QUANT") == 0) { Quant = strtol(cp, 0, 10); Cpn = Barlen / Quant; } else if (strcmp(buf, "#ROLL") == 0) { rolldef(cp); } else if (strcmp(buf, "#SYNC") == 0) { for (chan = MIDI_MAX_CHANS; --chan >= 0; ) for (key = MAXKEY; --key >= 0; ) When[chan][key] = maxwhen; } continue; } if (*buf == '\n') continue; for (cp = buf; *cp > ' '; cp++); for (; *cp && *cp <= ' '; *cp++ = '\0'); if (*cp == '\n' || *cp == '\0') continue; if ((i = chankey(buf)) < 0) exit(1); chan = i >> 8; key = i & 0x7F; for (; *cp > ' '; cp++) { nextwhen = When[chan][key] + Cpn; if ('0' <= *cp && *cp <= '9') { if (Nnotes >= MAXNOTES -1) { fprintf(stderr, "Too many notes, limit is %d\n", MAXNOTES / 2); exit(1); } hit(chan, key, *cp - '0', Cpn); } else if (*cp != '-') { if ((rp = rlookup(*cp))) { dt = Barlen / rp->quant; while ((i = nextwhen - When[chan][key]) > 0) { dt = dt > i? i : dt; hit(chan, key, rp->vol, dt); When[chan][key] += dt; } } else continue; } When[chan][key] = nextwhen; if (nextwhen > maxwhen) maxwhen = nextwhen; } } qsort(N, Nnotes, sizeof N[0], compar); now = 0l; for (i = 0; i < Nnotes; i++) { now = putdt(stdout, now, N[i].when); putc(N[i].mode, stdout); putc(N[i].key, stdout); putc(N[i].vel, stdout); } if (now < maxwhen) { putdt(stdout, now, maxwhen); putc(MPU_NO_OP, stdout); } } hit(chan, key, v, d) { N[Nnotes].when = When[chan][key]; N[Nnotes].mode = CH_KEY_ON | chan; N[Nnotes].key = key; N[Nnotes].vel = Vol[v]; Nnotes++; N[Nnotes].when = When[chan][key] + (Dur? d : 0); N[Nnotes].mode = CH_KEY_ON | chan; N[Nnotes].key = key; N[Nnotes].vel = 0; Nnotes++; } long putdt(ofp, now, when) FILE *ofp; long now, when; { register int dt; dt = (when - now) / TSCALE; when = now + dt * TSCALE; while (dt >= MPU_CLOCK_PERIOD) { dt -= MPU_CLOCK_PERIOD; putc(RT_TCIP, ofp); } putc(dt, ofp); return(when); } defdef(buf) char *buf; { register char *cp; int i; struct defstr *dp; for (cp = buf; *cp > ' '; cp++); for (; *cp && *cp <= ' '; *cp++ = '\0'); if (*cp == '\n' || *cp == '\0') { fprintf(stderr, "Bad #DEFINE in: %s", buf); exit(1); } if (strlen(buf) > SYMLEN) { fprintf(stderr, "Symbol too long in #DEFINE at: %s %s", buf, cp); exit(1); } if (!(dp = lookup(buf))) { if (Ndefs >= MAXDEFS) { fprintf(stderr, "Too many #DEFINEs at: %s %s", buf, cp); exit(1); } dp = &Def[Ndefs++]; strcpy(dp->sym, buf); } cp[SYMLEN - 1] = '\0'; if ((i = chankey(cp)) < 0) exit(1); sprintf(dp->val, "%x/%02x", (i >> 8) + 1, i & 0x3F); } chankey(ip) char *ip; { char *cp; int i, chan, key; struct defstr *dp; for (cp = ip; *cp > ' '; cp++); *cp = '\0'; for (i = 0; i < 99 && ip[1] != '/'; i++) { if (!(dp = lookup(ip))) { fprintf(stderr, "Bad chan/key in: %s\n", ip); exit(1); } ip = dp->val; } chan = strtol(ip, 0, 16) - 1; if (chan < 0 || chan >= MIDI_MAX_CHANS) { fprintf(stderr, "Bad chan in: %s\n", ip); return(-1); } key = strtol(&ip[2], 0, 16); if (key <= 0 || key >= MAXKEY) { fprintf(stderr, "Bad key (%x) in: %s\n", key, ip); return(-1); } return((chan << 8) | key); } struct defstr * lookup(buf) char *buf; { struct defstr *dp; for (dp = &Def[Ndefs]; --dp >= Def; ) if (strcmp(buf, dp->sym) == 0) return(dp); return((struct defstr *) 0); } rolldef(buf) char *buf; { register char *cp; int sym, vol, quant; struct rollstr *rp; cp = buf; sym = *cp++; if (sym <= ' ' || sym > '~' || *cp++ > ' ') goto rdoops; for (; *cp <= ' '; cp++); vol = *cp++ - '0'; if (vol < 0 || vol > 9 || *cp++ > ' ') goto rdoops; for (; *cp <= ' '; cp++); quant = atoi(cp); if (quant < 1 || quant > 2 * MPU_CLOCK_PERIOD) { rdoops: fprintf(stderr, "Bad format in: #ROLL %s", buf); exit(1); } if (!(rp = rlookup(sym))) { if (Nrolls >= MAXROLLS) { fprintf(stderr, "Too many #ROLLs at: %s", buf); exit(1); } rp = &Roll[Nrolls++]; rp->sym = sym; } rp->vol = vol; rp->quant = quant; } struct rollstr * rlookup(sym) char sym; { struct rollstr *rp; for (rp = &Roll[Nrolls]; --rp >= Roll; ) if (rp->sym == sym) return(rp); return((struct rollstr *) 0); } compar(p1, p2) struct notestr *p1, *p2; { register int i; i = (int)(p1->when - p2->when); if (i == 0) /* if Dur then off before on, else on before off */ i = Dur? (p1->vel - p2->vel) : (p2->vel - p1->vel); return(i); }