/* ** BARS -- Either count the number of bars (measures) in the input, ** or copy the specified number of bars of MIDI data from ** (the head or tail of) the input to the standard output. ** A bar is defined as 480 MPU clocks by default. ** A bar is defined as the data between TCWME codes if the "-L" option is used. ** A bar is defined as 119 MPU clocks if the "-L119" option is used. ** Examples: ** 'bars foo >fum' puts a measure count (based on 480 clock bars) in fum. ** 'bars -L foo >fum' puts a measure count (based on TCWME) in fum. ** 'bars -q foo >fum' puts a measure count in fum without file name. ** 'bars -h4 foo >fum' copies the first 4 bars of foo into fum. ** 'bars -t4 foo >fum' copies the last 4 bars of foo into fum. ** 'bars -f4 -l8 foo >fum' copies bars 4 through 8 of foo into fum. ** Algorithm is multi-pass: ** On first pass determine all bar times in bartime[] and copy into temp file. ** If no args, just output bar counts and exit. ** If Head specified make a pass to output Head bars; ** If Frst & Last specified make a pass to output Frst through Last bars; ** If Tail specified make a pass to output Tail bars. ** psl 10/85 */ #include #include syntax(prog) char *prog; { fprintf(stderr, "Usage: %s [options] [files or stdin]\n", prog); fprintf(stderr, "-B\tuse TCWME codes to define bars\n"); fprintf(stderr, "-B#\tset fixed bar length to # MIDI clocks\n"); fprintf(stderr, "-c\tdon't clean up hanging notes\n"); fprintf(stderr, "-f#\tfirst; start output at bar #\n"); fprintf(stderr, "-h#\thead; output the first # bars\n"); fprintf(stderr, "-l#\tlast; stop output at bar #\n"); fprintf(stderr, "-q\tquiet; don't print file names\n"); fprintf(stderr, "-t#\ttail; output the last # bars\n"); exit(2); } #define CPB (2 * MPU_CLOCK_PERIOD) /* default clocks per bar */ #define MAXBARS 1024 #define UNSPEC -1. int Clean = 1; /* terminate hanging notes */ int Quiet = 0; /* more terse */ int Barlen = CPB; /* fixed bar length if > 0 */ int Data = 0; /* output requested */ long Numticks; /* file length in MIDI ticks */ int Numbars = 0; /* index into bartime[] */ long Bartime[MAXBARS]; /* when bars start */ double Frst = UNSPEC, Last = UNSPEC; double Head, Tail; long pass1(), b2t(); extern float atof(); main(argc,argv) char *argv[]; { int i, filearg; FILE *f; filearg = 0; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'B': Barlen = atoi(&argv[i][2]); break; case 'c': Clean = 0; break; case 'f': Data++; Frst = atof(&argv[i][2]); break; case 'h': Data++; Head = atof(&argv[i][2]); break; case 'l': Data++; Last = atof(&argv[i][2]); break; case 'q': Quiet++; break; case 't': Data++; Tail = atof(&argv[i][2]); break; default: syntax(argv[0]); } } else filearg = 1; } if (filearg == 0) bars(stdin, "stdin"); else { for (i = 1; i < argc; i++) { if (argv[i][0] != '-') { if (f = sopen(argv[i],"r")) { bars(f, argv[i]); sclose(f); } else perror(argv[i]); } } } exit(0); } bars(fp, file) FILE *fp; char *file; { char tempfile[64]; double fract, maxbars, f, l; FILE *tfp; if (Data) { sprintf(tempfile, "/tmp/bars%d", getpid()); if ((tfp = fopen(tempfile, "w")) == NULL) { perror(tempfile); exit(1); } } else tfp = 0; Numticks = pass1(fp, tfp); if (tfp) fclose(tfp); if (Barlen) maxbars = Numticks * 1. / Barlen; else if (Numbars > 1) { fract = Numticks - Bartime[Numbars - 1]; fract = fract / (Bartime[Numbars - 1] - Bartime[Numbars - 2]); maxbars = Numbars - 1. + fract; } else maxbars = 0.; /* this could be wrong... */ if (!Data) { if (!Quiet) printf("%s:\t", file); printf("%g\n", maxbars); return; } if ((tfp = fopen(tempfile, "r")) == NULL) { perror(tempfile); exit(1); } if (Head > 0.) pass2(tfp, stdout, 0., Head); if (Frst != UNSPEC || Last != UNSPEC) { f = (Frst == UNSPEC)? 0. : Frst; l = (Last == UNSPEC)? maxbars : Last; pass2(tfp, stdout, f, l); } if (Tail > 0.) pass2(tfp, stdout, maxbars - Tail, maxbars); fclose(tfp); unlink(tempfile); } long pass1(fp, tp) FILE *fp, *tp; { long now, when; MCMD *mp; now = 0; putmcmd(0, 0); Numbars = 0; Bartime[Numbars++] = now; /* start with a barline */ while (mp = getmcmd(fp, now)) { now = mp->when; if (tp) putmcmd(tp, mp); if (!Barlen && mp->cmd[0] == RT_TCWME && Bartime[Numbars - 1] != now) { if (Numbars >= MAXBARS) fprintf(stderr, "Too many TCWME\n"); else Bartime[Numbars++] = now; } } if (Barlen) for (when = Barlen; when <= now; when += Barlen) Bartime[Numbars++] = when; return(now); } pass2(tfp, ofp, bstrt, bstop) FILE *tfp, *ofp; double bstrt, bstop; { u_char buf[4]; int c, k, on[MIDI_MAX_CHANS][MIDI_NUM_KEYS]; long now, strt, stop; MCMD m, *mp; putmcmd(0, 0); if (Barlen) { strt = bstrt * Barlen; if (strt < 0) strt = 0; stop = bstop * Barlen; } else { strt = b2t(bstrt); stop = b2t(bstop); } if (strt >= stop) return; fseek(tfp, 0L, 0); now = 0L; if (Clean) for (c = MIDI_MAX_CHANS; --c >= 0; ) for (k = MIDI_NUM_KEYS; --k >= 0; on[c][k] = 0); while (mp = getmcmd(tfp, now)) { now = mp->when; if (now >= stop) break; if (now >= strt) { mp->when -= strt; putmcmd(ofp, mp); if (Clean) { if ((mp->cmd[0] & M_CMD_MASK) == CH_KEY_ON && mp->cmd[2] == 0) mp->cmd[0] = CH_KEY_OFF | (mp->cmd[0] & M_CHAN_MASK); if ((mp->cmd[0] & M_CMD_MASK) == CH_KEY_ON) on[mp->cmd[0] & M_CHAN_MASK][mp->cmd[1]]++; else if ((mp->cmd[0] & M_CMD_MASK) == CH_KEY_OFF) --on[mp->cmd[0] & M_CHAN_MASK][mp->cmd[1]]; } } } if (Clean) { m.when = stop - strt; m.len = 3; m.cmd = buf; m.cmd[2] = 0; for (c = MIDI_MAX_CHANS; --c >= 0; ) { for (k = MIDI_NUM_KEYS; --k >= 0; ) { while (--on[c][k] >= 0) { m.cmd[0] = CH_KEY_ON | c; m.cmd[1] = k; putmcmd(ofp, &m); } } } } Mpu_nop.when = stop - strt; putmcmd(ofp, &Mpu_nop); } long b2t(bar) double bar; { register int i; long tick; double fract; i = bar; if (i >= Numbars) tick = Numticks; else if (i >= 0) { tick = Bartime[i]; fract = bar - i; if (fract > 0. && i + 1 < Numbars) tick += (long) ((Bartime[i + 1] - Bartime[i]) * fract); } else tick = 0; return(tick); }