/* ** MIXPLAY -- Mix recorded & "live" MIDI data & play it. ** P. Langston, 10/87 */ #include #include #include #include #include #include #include #define MAXMPUS MPUS_PER_BOARD /* how many MPUs we've got */ #define MAXTPM 8 /* how many tracks each MPU can have */ #define MAXINP 64 /* how many simultaneous inputs we can have */ int Tapesync = 0; /* if true, put MPU in FSK INT SYNC mode */ int Record = -1; /* MPU #, record (overdub) while playing */ int Debug = 0; /* if set, output is to a tty */ int Midifh; /* file descriptor for /dev/mpu */ int Fpipe = 0; /* is at least one source a pipe? */ int Ninp = 0; /* number of sources */ struct mpustr { int fd; /* file descriptor for the MPU device */ int trax; /* how many tracks we've assigned */ int mode; /* mode in which to start MPU */ char name[32]; /* device file name */ } Mpu[MAXMPUS]; struct inpstr { /* track info */ FILE *fp; /* input file pointer */ int fd; /* file descriptors for Fp[] */ int lm; /* 1 ==> "live" MIDI, 0 ==> time-tagged MPU */ int mpu; /* which MPU this track uses */ int trak; /* which track on this MPU */ } Inp[MAXINP]; #define TBITSLOC 1 /* (in both sequences) */ char Sinit[] = { /* default tape sync initialization */ MPU_ACTIVE_TRACKS, 0, MPU_CLEAR_PLAY_COUNTERS, MPU_MIDI_THRU_OFF, MPU_BENDER_ON, MPU_FSK_CLOCK, 0, }; char Ninit[] = { /* default "normal" initialization */ MPU_ACTIVE_TRACKS, 0, MPU_CLEAR_PLAY_COUNTERS, MPU_MIDI_THRU_OFF, MPU_BENDER_ON, 0, }; main(argc, argv) char *argv[]; { char *init_file = NULL, *arg; int i, Wait=0; int inraw, mpu; void interrupt(); /****/setbuf(stderr, 0); /* THOSE ASSHOLES! */ av0 = argv[0]; signal(SIGINT, interrupt); for (i = MAXMPUS; --i >= 0; ) { Mpu[i].fd = -1; sprintf(Mpu[i].name, "/dev/mpu%d", i); } for (i = MAXINP; --i >= 0; Inp[i].fd = -1); inraw = 0; openmpu(mpu = 0); for (i = 1; i < argc; i++) { arg = argv[i]; if (arg[0] == '-') { switch (arg[1]) { case '-': in(&arg[2], mpu, inraw); break; case 'b': MpuSet(MPU_METRO_MEAS), MpuSet(atoi(&arg[2])); break; case 'c': init_file = &arg[2]; break; case 'D': Debug = 1; break; case 'd': openmpu(mpu = atoi(&arg[2])); break; case 'M': MpuSet(MPU_METRO_ACC); break; case 'm': MpuSet(MPU_METRO_NO_ACC); break; case 'R': inraw = 1; break; case 'r': if (Record >= 0) syntax(1); Mpu[Record = mpu].mode = MPU_START_OVERDUB; break; case 'S': Tapesync = 1; break; case 'T': inraw = 0; break; case 't': MpuSet(MPU_TEMPO), MpuSet(atoi(&arg[2])); break; case 'w': Wait = atoi(&arg[2]); break; case 'x': MpuSet(MPU_EXCLUSIVE_TO_HOST_ON), MpuSet(MPU_SEND_MEASURE_END_OFF); break; default : syntax(1); } } else in(arg, mpu, inraw); } /****/fprintf(stderr, "mpu=%d, (%s)\n", mpu, Mpu[mpu].name); if (Wait || Fpipe) sleep(Wait? Wait : 2); /* let input pipes fill up a little */ if (midiinit(init_file)) Error(init_file); splay(); if (Record >= 0) for(;;) record(fileno(stdout)); done(); } openmpu(mpu) /* open appropriate /dev/mpu? */ { if (mpu < 0 || mpu >= MAXMPUS) { fprintf(stderr, "mpu number, %d, out of range (0-%d).\n", mpu, MAXMPUS - 1); syntax(1); } if ((Mpu[mpu].fd = open(Mpu[mpu].name, 2)) < 0) { /****/fprintf(stderr, "openmpu() "); perror(Mpu[mpu].name); syntax(1); } Mpu[mpu].mode = MPU_START_PLAY; } in(file, mpu, lflg) /* associate input data with mpu track */ char *file; { struct mpustr *mp; struct inpstr *ip; if (mpu < 0 || mpu > MAXMPUS) { fprintf(stderr, "mpu number, %d, out of range (0-%d).\n", mpu, MAXMPUS - 1); syntax(1); } if (Ninp >= MAXINP) { fprintf(stderr, "Too many input sources, max is %d.\n", MAXINP); syntax(1); } mp = &Mpu[mpu]; if (mp->trax >= MAXTPM) { fprintf(stderr, "Too many tracks for mpu%d, max is %d.\n", mpu, MAXTPM); syntax(1); } ip = &Inp[Ninp++]; if (file && *file) ip->fp = sopen(file, "r"); else ip->fp = stdin; if (!ip->fp) { /****/fprintf(stderr, "in(%s, %d, %d) ", file, mpu, lflg); perror(file); ip->fd = -1; /* ip->fp = (FILE *) 0; */ } else { ip->fd = fileno(ip->fp); Fpipe += isapipe(ip->fd); ip->lm = lflg; #ifdef UNDEF if (lflg) fseek(ip->fp, 0, 2); /* seek to end of live files */ #endif ip->mpu = mpu; ip->trak = mp->trax++; } } done() { int i; for (i = 0; i < MAXMPUS; i++) if (Mpu[i].fd >= 0) close(Mpu[i].fd); exit(0); } Error(s) { MidiError("%s: ", av0); /****/fprintf(stderr, "Error "); perror(s); exit(1); } midiinit(file) char *file; { register int i, n; char buf[1024], *iseq; int d, trackbits[MAXMPUS], islen; struct inpstr *ip; struct mpustr *mp; FILE *ifp; for (i = MAXMPUS; --i >= 0; trackbits[i] = 0); for (i = 0; i < Ninp; i++) { ip = &Inp[i]; mp = &Mpu[ip->mpu]; if (!ip->lm) { trackbits[ip->mpu] |= (1 << ip->trak); if ((n = read(ip->fd, buf, sizeof buf)) < sizeof buf) { sclose(ip->fp); ip->fp = (FILE *) 0; ip->fd = -1; } if (n > 0 && (MpuSetTrack(mp->fd, ip->trak) == -1 || write(mp->fd, buf, n) != n)) { /****/fprintf(stderr, "midiinit() MpuSetTrack() || write() failed "); perror(mp->name); return(-1); } } } /* send initialization sequence */ if (file) { if (!(ifp = sopen(file,"r"))) Error(file); for (iseq = buf; fscanf(ifp, "%x", &d) == 1; *iseq++ = d); islen = iseq - buf; iseq = buf; sclose(ifp); } else if (Tapesync) { /* default tape sync initialization */ iseq = Sinit; islen = sizeof Sinit; } else { /* default "normal" initialization */ iseq = Ninit; islen = sizeof Ninit; } for (i = 0; i < MAXMPUS; i++) { if (Mpu[i].fd < 0) continue; if (!file) { iseq[TBITSLOC] = trackbits[i]; iseq[islen - 1] = Mpu[i].mode; } MpuSetTrack(Mpu[i].fd, MPU_TR_COM); write(Mpu[i].fd, iseq, islen); if (MpuSetTrack(Mpu[i].fd, 0) == -1) { /****/fprintf(stderr, "midiinit() MpuSetTrack() failed "); perror(Mpu[i].name); return(-1); } } return(0); } syntax(i) { MidiError("Usage: %s [files or stdin]\n", av0); MidiError( "flags:\n\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", "-- read stdin for data\n", "-bN set # beats/measure to N\n", "-cfile use contents of \"file\" to initialize MPU\n", "-dN following data sources are for /dev/mpuN\n", "-M metronome on, with accent\n", "-m metronome on, no accent\n", "-R following data sources are \"raw\".\n", "-r record (writing the recording to stdout)\n", "-S synchronize with FSK signal at TAPE IN jack\n", "-T following data sources are time-tagged.\n", "-tN set tempo to N (beats/minute, default=100)\n", "-wN wait for N seconds before starting\n", "-x enable recording system exclusive data\n", 0); exit(i); } record(ofh) /* copy any data from Midifh to ofh */ { char c; int n, fh; if (Record < 0) return; if (iwait(0, 0)) { /* input arrived from keyboard; quit */ getchar(); done(); /*NOTREACHED*/ } fh = Mpu[Record].fd; if (iwait(fh, 0) && (n = read(fh, &c, 1)) > 0) { if (Debug) printf("0x%02x\n", c); else if (write(ofh, &c, n) != n) perror("write"); } } splay() /* simultaneously play all files in "Fp", "Fd" & "Lm" */ { int n, in, dataleft; char s[512]; struct inpstr *ip; struct mpustr *mp; do { dataleft = 0; for (in = 0; in < Ninp; in++) { ip = &Inp[in]; if (ip->fd < 0) continue; dataleft++; if (ip->lm) { if (iwait(ip->fd, 0) > 0) livedata(ip); } else { mp = &Mpu[ip->mpu]; MpuSetTrack(mp->fd, ip->trak); if ((n = read(ip->fd, s, sizeof s)) > 0) { if (write(mp->fd, s, n) != n) /****/{ /****/fprintf(stderr, "splay() write(%d, %x, %d) failed ", mp->fd, s, n); perror(mp->name); /****/} } else { sclose(ip->fp); ip->fp = (FILE *)0; ip->fd = -1; putTCIP(mp->fd); } } } if (Record >= 0) record(fileno(stdout)); } while (dataleft); } livedata(ip) /* read & write out "live" data */ struct inpstr *ip; { register int c, n, ifh; u_char buf[64], *cp, *bep; /* not big enough for SysEx */ static int status; struct mpustr *mp; ifh = ip->fd; mp = &Mpu[ip->mpu]; while (iwait(ifh, 0) && (n = read(ifh, buf, 1)) == 1) { cp = buf; c = *cp; n = statproc(&status, c); MpuSetTrack(mp->fd, MPU_TR_COM); if (n < 0) { /* Sys Excl */ *cp++ = MPU_SEND_SYSTEM_MESSAGE; *cp++ = c; /* SX_CMD */ mwrite(mp->fd, buf, cp - buf); for (cp = buf; read(ifh, cp, 1) >= 0; ) { c = *cp++; if (cp - buf >= sizeof buf) { mwrite(mp->fd, buf, cp - buf); cp = buf; } if (c == SX_EOB) break; } } else { *cp++ = MPU_WANT_TO_SEND_DATA + ip->trak; *cp++ = c; for (bep = &buf[n+1]; cp < bep; cp += read(ifh, cp, bep - cp)); } if (cp > buf) mwrite(mp->fd, buf, cp - buf); } if (n == 0) { /* EOF */ sclose(ip->fp); ip->fp = (FILE *)0; ip->fd = -1; /**** ioctl(mp->fd, MPU_IOC_PURGE, &ip->trak); /****/ } } mwrite(fh, buf, len) /* write out midi data to fh */ u_char *buf; { register int i; if (Debug) { for (i = 0; i < len; i++) fprintf(stderr, " %02x", buf[i] & 0xFF); fprintf(stderr, "\n"); } else if ((i = write(fh, buf, len)) != len) perror("mixplay:mwrite"); return(i); } void interrupt() /* user must ^C again to really quit, I think */ { int i; for (i = 0; i < MAXMPUS; i++) if (Mpu[i].fd >= 0) ioctl(Mpu[i].fd, MPU_IOC_PURGE, 0); done(); } iwait(f, timeout) unsigned long timeout; /* in seconds */ /* * Wait until 'f' is ready for reading, or 'timeout'. * Return '>=0' when 'f' is readable, '0' if timeout, '-1' on error. * Example: 'iwait(f,0)' polls a file descriptor * without blocking and returns true if it's readable; * 'iwait(0,0)' returns true when standard input is available. */ { int readfd; struct timeval t; readfd = (1 << f); t.tv_sec = timeout; t.tv_usec = 0; return(select(f + 1, &readfd, (int *)0, (int *)0, &t)); }