/* ** CCC -- Chord Chart Compiler ** psl 12/85 */ #include #include "midi.h" #define MAXCH 256 #define NAMELEN 16 #define MAXV 8 #define BAR_PERIOD (2 * MPU_CLOCK_PERIOD) #define MINOCT -1 #define MAXOCT 9 char Noteval[] = { 9, 11, 0, 2, 4, 5, 7, }; char Ebuf[128]; int Step = BAR_PERIOD / 4; /* defaults to quarter notes */ int Style = 'n'; /* legato, normal, or staccato */ int Numchords = 1; /* "-" already defined */ long Time = 0; struct chstr { char name[NAMELEN]; char key[MAXV]; } Ch[MAXCH] = { { "-", { 0, }, }, /* to allow "-" to be used as a rest */ }; char *peel(), *copy(); main(argc, argv) char *argv[]; { register int i; FILE *ifp; if (argc == 1) process(stdin); else { for (i = 1; i < argc; i++) { if ((ifp = fopen(argv[i], "r")) == NULL) perror(argv[i]); else { process(ifp); fclose(ifp); } } } } process(ifp) FILE *ifp; { register char *bp, *cp; register int v, chnum, lastchnum, m, n, dur; char buf[512]; FILE *fp; while (fgets(buf, sizeof buf, ifp) != NULL) { bp = peel(cp = buf); if (*cp == '\n' || *cp == '#') { continue; } else if (wrdcmp(cp, "Include") == 0) { if (*bp++ != '"') syntax("Include must use quotes (\")"); for (cp = bp; *cp && *cp != '"'; cp++); if (*cp != '"') syntax("Include must use quotes (\")"); *cp = '\0'; if ((fp = fopen(bp, "r")) == NULL) { perror(bp); exit(1); } process(fp); fclose(fp); } else if (wrdcmp(cp, "Style") == 0) { Style = *bp; } else if (wrdcmp(cp, "Quantum") == 0) { if (wrdcmp(bp, "whole") == 0) Step = BAR_PERIOD; else if (wrdcmp(bp, "half") == 0) Step = BAR_PERIOD / 2; else if (wrdcmp(bp, "quarter") == 0) Step = BAR_PERIOD / 4; else if (wrdcmp(bp, "eighth") == 0) Step = BAR_PERIOD / 8; else if (wrdcmp(bp, "sixteenth") == 0) Step = BAR_PERIOD / 16; else syntax("Unrecognized quantum arg"); } else if (wrdcmp(cp, "Chord") == 0) { bp = peel(cp = bp); if ((chnum = Numchords++) >= MAXCH) syntax("Too many chords defined"); copy(cp, Ch[chnum].name); for (v = 0; v < MAXV && (bp = peel(cp = bp)) && *cp; v++) Ch[chnum].key[v] = keynum(cp); } else { dur = (Style == 's'? Step / 4 : Step); dur = (Style == 'n'? (4 * Step) / 5 : dur); lastchnum = -1; while (*cp) { if (*cp == '/') { if ((chnum = lastchnum) == -1) syntax("Line beginning with '/'"); } else { if ((chnum = find(cp)) < 0) { sprintf(Ebuf, "Undefined chord: %s", cp); syntax(Ebuf); } } for (v = 0; v < MAXV; v++) { n = Ch[chnum].key[v]; if (Style == 'l' && lastchnum != -1) { if ((m = Ch[lastchnum].key[v]) == n) continue; if (m != 0) { putdt(Time, stdout); putc(m, stdout); putc(0, stdout); } } if (n != 0) { putdt(Time, stdout); putc(0x90, stdout); putc(n, stdout); putc(0x40, stdout); } } lastchnum = chnum; if (Style != 'l') { for (v = 0; v < MAXV && (n = Ch[chnum].key[v]); v++) { putdt(Time + dur, stdout); putc(n, stdout); putc(0, stdout); } } Time += Step; bp = peel(cp = bp); } if (Style == 'l' && lastchnum != -1) { for (v = 0; v < MAXV && (n = Ch[chnum].key[v]); v++) { putdt(Time, stdout); putc(n, stdout); putc(0, stdout); } } } } } wrdcmp(ap, bp) char *ap, *bp; { while (*bp && *ap == *bp) { ap++; bp++; } return ((*ap <= ' ' && *bp == '\0')? 0 : *ap - *bp); } syntax(msg) char *msg; { fprintf(stderr, "Chord chart syntax error: %s\n", msg); exit(1); } char * peel(sp) char *sp; { while (*sp > ' ') sp++; if (*sp != '\0') { *sp++ = '\0'; while (*sp && *sp <= ' ') sp++; } return(sp); } putdt(time, ofp) long time; FILE *ofp; { register int dt; static long last; dt = time - last; while (dt >= MPU_CLOCK_PERIOD) { putc(RT_TCIP, ofp); dt -= MPU_CLOCK_PERIOD; } putc(dt, ofp); last = time; } find(name) char *name; { register int i; for (i = 0; i < Numchords; i++) if (wrdcmp(name, Ch[i].name) == 0) return(i); return(-1); } keynum(str) char *str; { register char *cp; int i, note, oct; cp = str; i = *cp++; if ('0' <= i && i <= '9') { /* decimal */ return(atoi(--cp)); } else if (i == 'x' || i == 'X') { /* hex */ return(hex(cp[0]) << 4 + hex(cp[1])); } else { /* note name */ if ('A' <= i && i <= 'G') note = Noteval[i - 'A']; else if ('a' <= i && i <= 'g') note = Noteval[i - 'a']; else goto oops; if (*cp == '#') { note++; cp++; } else if (*cp == 'b') { --note; cp++; } oct = atoi(cp); if (oct < MINOCT || oct > MAXOCT) { oops: sprintf(Ebuf, "Note format error: %s\n", str); syntax(Ebuf); } return(12 * (oct - MINOCT) + note); } } hex(c) char c; { if ('0' <= c && c <= '9') return(c - '0'); if ('A' <= c && c <= 'F') return(c - 'A' + 10); if ('a' <= c && c <= 'f') return(c - 'a' + 10); fprintf(stderr, "Error in hex digit '%c'\n", c); return(0); } char * copy(f, t) register char *f, *t; { while (*t++ = *f++); return(--t); }