/* ** CHART -- Display MIDI file graphically ** psl 12/85 */ /*#define SUN /* for Sun Workstation */ /*#define JERQ /* for Teletype DMD 5620 */ #ifdef SUN #include #include #define RECTF rect #endif #ifdef JERQ #include #include #define Dr Drect #define Display &display #define o origin #define c corner #define RECTF rectf #define getpid() 42 #define perror(x) fprintf(stderr, "perror(%s)\n", x) #endif #define MPU_CLOCK_PERIOD 240 #define RT_TCIP 0xF8 #define MAXCHAN 16 #define MAXKEY 128 #define SBHH 16 /* scroll bar half height */ char tempfile[32]; int bars = 0; /* >0 => how much to display, 0 => expand */ int border = 0; /* guaranteed empty space around score */ int expand = 1; /* expand to fill the space (vertically) */ int ppbar; /* pixels per bar */ int ppnote; /* pixels per note (vertically) */ int mink = 42, maxk = 78; /* range of notes in input */ int mint, maxt; /* range of time in input */ FILE *tfp; /* slightly preprocessed data */ Rectangle da; /* drawing area */ int tclefk[] = { 77, 74, 71, 67, 64, }; int bclefk[] = { 43, 47, 50, 53, 57, }; int chans[MAXCHAN] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, }; int bmark = 9999; /* mark every this many bars */ int key[MAXCHAN][MAXKEY]; int vel[MAXCHAN][MAXKEY]; long start[MAXCHAN][MAXKEY]; int vol[1200]; /* loudness at each pixel */ long minwhen, maxwhen; main(argc, argv) char *argv[]; { while (--argc > 0) { if (argv[argc][0] == '-') { switch (argv[argc][1]) { case 'B': border = atoi(&argv[argc][2]); break; case 'b': bars = atoi(&argv[argc][2]); break; case 'c': setchans(&argv[argc][2]); break; case 'e': expand = 0; break; case 'm': bmark = atoi(&argv[argc][2]); break; default: goto syntax; } } else { syntax: fprintf(stderr, "Usage: %s [-B#] [-b#] [-c#[,#,...]] [-e] [-m#] 0) { minwhen = 0; maxwhen = 2 * MPU_CLOCK_PERIOD * bars + 1; } else { minwhen = mint; maxwhen = maxt + 1; bars = (int) (1 + (maxt - mint - 1) / (2 * MPU_CLOCK_PERIOD)); } if (!expand) { mink = 0; maxk = MAXKEY; } setup(0); process(tfp); twiddle(); exit(0); } copyfile(ifp, ofp) FILE *ifp, *ofp; { register int i, k, v, curmode, last; long now; maxt = 0; mint = 99999; curmode = 0; now = 0; while ((i = getc(ifp)) != EOF) { if (i == 0xF8) { now += MPU_CLOCK_PERIOD; continue; } now += i; if ((k = getc(ifp)) == EOF) { fprintf(stderr, "EOF after 0x%x (now=%d)\n", i, now); exit(1); } if (k & 0x80) { if (k == 0xF9 || k == 0xFC || k == 0xFD || k == 0xFE || (k & 0x80) == 0) continue; /* there are some FF commands that should be handled here, */ /* (I think), common. real-time, ? */ curmode = k; if ((k = getc(ifp)) == EOF) { fprintf(stderr, "EOF after %x (mode)\n", curmode); exit(1); } } if ((curmode & 0xF0) == 0xD0) /* ignore pressure change */ continue; if (curmode == 0xFF) { /* this is a real pain to handle */ if (k & 0x0F) { /* midi system message */ getc(ifp); getc(ifp); } else /* system exclusive message */ while (getc(ifp) != 0xF7); /* could screw up */ continue; } if ((curmode & 0xF0) == 0x90) { /* key-on/off event */ if ((v = getc(ifp)) == EOF) { fprintf(stderr, "unexpected EOF in mode %x\n", curmode); exit(1); } if (chans[curmode & 0x0F] == 0) continue; for (i = now-last; i > MPU_CLOCK_PERIOD; i -= MPU_CLOCK_PERIOD) putc(RT_TCIP, ofp); putc(i, ofp); putc(curmode, ofp); putc(k, ofp); putc(v, ofp); if (v != 0) { if (k < mink) mink = k; if (k > maxk) maxk = k; } if (now < mint) mint = now; if (now > maxt) maxt = now; last = now; } } } process(ifp) FILE *ifp; { register int i, k, v, curmode; int chan, nowx; long now; curmode = 0; now = -minwhen; while ((i = getc(ifp)) != EOF) { if (i == 0xF8) { now += MPU_CLOCK_PERIOD; continue; } now += i; if (now > maxwhen - minwhen) break; curmode = getc(ifp); k = getc(ifp); v = getc(ifp); if (now < 0) continue; chan = curmode % MAXCHAN; if (chans[chan] == 0) continue; nowx = ctop(now); if (v == 0) { /* key-off */ --key[chan][k]; if (key[chan][k] < 0) { /* extra note-off */ key[chan][k] = 0; continue; } if (key[chan][k] == 0) { /* end of note */ plotnote(start[chan][k], ktop(k), nowx); addvol(start[chan][k], nowx, vel[chan][k]); } } else { /* key-on */ if (key[chan][k] != 0) { plotnote(start[chan][k], ktop(k), nowx); addvol(start[chan][k], nowx, vel[chan][k]); } start[chan][k] = nowx; vel[chan][k] = v; key[chan][k]++; } } disvol(da.o.x, da.c.x, da.c.y + SBHH + ppnote); } plotnote(bx, y, ex) { int dy; dy = ppnote / 2; if (ex == bx) ex++; RECTF(Display, Rect(bx, y - dy, ex, y + dy), F_STORE); } addvol(bx, ex, v) register int v; { register int x; if (ex == bx) ex++; for (x = bx; x < ex; x++) { vol[x] += v; v = (36 * v + 18) / 37; } } disvol(bx, ex, y) { register int x, dy, maxvol; maxvol = 0; for (x = bx; x < ex; x++) if (vol[x] > maxvol) maxvol = vol[x]; maxvol++; for (x = bx; x < ex; x++) { dy = (vol[x] * SBHH) / maxvol; segment(Display, Pt(x, y + dy), Pt(x, y - dy - 1), F_STORE); } RECTF(Display, Rect(bx - 1, y - SBHH, ex + 1, y + SBHH), F_XOR); } twiddle() { #ifdef SUN Go(); #endif #ifdef JERQ for (;;) { wait(CPU); if (P->state & RESHAPED) { setup(1); P->state &= ~RESHAPED; fclose(tfp); if ((tfp = fopen(tempfile, "r")) == (FILE *) NULL) { perror(tempfile); exit(1); } process(tfp); } if (kbdchar() != -1) exit(); } #endif } #ifdef SUN Input() { if (Reshaped) { setup(1); fseek(tfp, 0L, 0); process(tfp); } if (Poll(KBD) & KBD) if (kbdchar() == 'q') exit(0); } #endif setchans(arg) char *arg; { register char *cp; register int i, j; for (i = MAXCHAN; --i >= 0; chans[i] = 0); for (cp = arg; *cp; ) { i = j = atoi(cp); while ('0' <= *cp && *cp <= '9') cp++; if (*cp == '-') { j = atoi(++cp); while ('0' <= *cp && *cp <= '9') cp++; } else if (*cp == ',') cp++; if (i < 0 || i >= MAXCHAN || j < 0 || j >= MAXCHAN || i > j) { fprintf(stderr, "Bad channel number in %s\n", arg); exit(1); } while (i <= j) chans[i++] = 1; } } hex(c) char c; { if ('0' <= c && c <= '9') return(c - '0'); if ('A' <= c && c <= 'F') return(10 + c - 'A'); if ('a' <= c && c <= 'f') return(10 + c - 'a'); return(0); } setup(redraw) { register int i, x, y, y2; Point size; #ifdef SUN if (redraw == 0) { InitDisplay(); InitDevices(KBD); } #endif #ifdef JERQ if (redraw == 0) { request(KBD); } #endif RECTF(Display, Dr, F_CLR); da.o.x = Dr.o.x + border; da.o.y = Dr.o.y + border; da.c.x = Dr.c.x - border; da.c.y = Dr.c.y - border - 2 * SBHH - 6; size = sub(da.c, da.o); ppbar = (size.x - 8) / bars; ppnote = (size.y - 12) / (maxk - mink + 1); x = size.x - bars * ppbar; y = size.y - (maxk - mink) * ppnote; da.o.x += x / 2; da.o.y += y / 2; da.c.x -= x / 2; da.c.y -= y / 2; for (i = 5; --i >= 0; ) { /* horizontal staff lines */ y = ktop(tclefk[i]); segment(Display, Pt(da.o.x, y), Pt(da.c.x, y), F_STORE); y = ktop(bclefk[i]); segment(Display, Pt(da.o.x, y), Pt(da.c.x, y), F_STORE); } for (i = 0; i <= bars; i++) { /* vertical staff lines */ x = da.o.x + i * ppbar - 1; y = ktop(tclefk[0] + (i % bmark? 0 : 1)); y2 = ktop(bclefk[0] - (i % bmark? 0 : 1)); segment(Display, Pt(x, y), Pt(x, y2), F_STORE); } } ktop(k) /* convert key # to vertical pixel location */ { return(da.c.y - ppnote * (k - mink)); } ctop(when) /* convert midi clocks into horizontal pixel location */ long when; { return((int) (da.o.x + (when * ppbar) / (2 * MPU_CLOCK_PERIOD))); }