1 /************************************************************************ 2 * toy.c - start of midi recording package. Will take input from 3 * /dev/sequencer and layer with an existing /dev/sequencer dump file, 4 * saving the result as a new /dev/sequencer dump file. In short, you 5 * must have hardware midi devices to use this "toy". 6 * 7 * This program is an experiment in midi recording to solve some timing 8 * and input/output issues that I've had while writing my midi studio 9 * package (basically to test how to best use select() on /dev/sequencer). 10 * 11 * This code was written by by Nathan Laredo (laredo@gnu.ai.mit.edu) 12 * Source code may be freely distributed in unmodified form. 13 *************************************************************************/ 14 #include <stdio.h> 15 #ifndef __FreeBSD__ 16 #include <getopt.h> 17 #endif 18 #include <fcntl.h> 19 #include <unistd.h> 20 #include <stdlib.h> 21 #include <sys/time.h> 22 #ifndef __FreeBSD__ 23 #include <sys/soundcard.h> 24 #else 25 #include <machine/soundcard.h> 26 #endif 27 #include <sys/ioctl.h> 28 29 30 #define SEQUENCER_DEV "/dev/sequencer" 31 #define SEQUENCERBLOCKSIZE 128 32 33 /* 34 * The following are if you have more than one midi device on 35 * your system as I do. My Roland piano is on the 2nd midi port (GUS), and 36 * my Korg daughterboard is on the 1st midi port (SB16). 37 * Input will be taken from any midi device known by the kernel sequencer. 38 * device. 39 */ 40 41 #define OUT_DEV 0 42 43 SEQ_DEFINEBUF(SEQUENCERBLOCKSIZE); 44 unsigned char inputbuf[SEQUENCERBLOCKSIZE]; 45 unsigned char outputbuf[SEQUENCERBLOCKSIZE]; 46 int seqfd, outfile, infile; 47 48 /* indexed by high nibble of command */ 49 int cmdlen[16] = 50 {0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0}; 51 52 void seqbuf_dump() 53 { 54 if (_seqbufptr) { 55 if (write(seqfd, _seqbuf, _seqbufptr) == -1) { 56 perror(SEQUENCER_DEV); 57 exit(-1); 58 } 59 if (write(outfile, _seqbuf, _seqbufptr) == -1) { 60 perror("write"); 61 exit(-1); 62 } 63 } 64 _seqbufptr = 0; 65 } 66 67 void seq_addevent(s, c) 68 unsigned char *s; 69 int c; 70 { 71 int i; 72 73 if (*s == SEQ_SYNCTIMER) /* we want only one of these messages */ 74 return; 75 _SEQ_NEEDBUF(c); 76 for (i = 0; i < c; i++) 77 _seqbuf[_seqbufptr + i] = s[i]; 78 _SEQ_ADVBUF(c); 79 } 80 81 int seqread(f, buf) 82 int f; 83 unsigned char *buf; 84 { 85 return read(f, buf, 4); 86 } 87 88 89 int main(argc, argv) 90 int argc; 91 char **argv; 92 { 93 extern char *optarg; 94 extern int optind; 95 int i, transpose = 0, channel = 0, program = 0, error = 0; 96 unsigned char mid[8], imid[8], cmd = 0, icmd = 0; 97 unsigned int oticks = 0, ticks = 0, db = 0, idb = 0, wait = 0; 98 fd_set rdfs; 99 struct timeval tv, start, now, want; 100 101 while ((i = getopt(argc, argv, "c:p:wt:")) != -1) 102 switch (i) { 103 case 'c': 104 channel = atoi(optarg); 105 break; 106 case 'p': 107 program = atoi(optarg); 108 break; 109 case 'w': 110 wait++; 111 break; 112 case 't': 113 transpose = atoi(optarg); 114 break; 115 default: 116 error++; 117 break; 118 } 119 120 if (error || argc - optind != 2) { 121 fprintf(stderr, "usage: %s [-t semitones]" 122 " [-c channel] [-p program] [-wait] " 123 "inputfile.seq outputfile.seq\n", argv[0]); 124 exit(1); 125 } 126 if ((seqfd = open(SEQUENCER_DEV, O_RDWR, 0)) < 0) { 127 perror("open " SEQUENCER_DEV); 128 exit(-1); 129 } 130 if ((infile = open(argv[optind], O_RDONLY, 0)) < 0) { 131 perror(argv[optind]); 132 exit(-1); 133 } 134 if ((outfile = open(argv[optind + 1], O_WRONLY | O_CREAT | O_TRUNC, 0666)) 135 < 0) { 136 perror(argv[optind + 1]); 137 exit(-1); 138 } 139 ioctl(seqfd, SNDCTL_SEQ_RESET); 140 if (program >= 1 && program <= 128) { 141 if (channel < 1 || channel > 16) 142 channel = 1; 143 SEQ_MIDIOUT(OUT_DEV, 0xc0 | (channel - 1)); 144 SEQ_MIDIOUT(OUT_DEV, program - 1); 145 } 146 /* extra byte for converting 3-byte timer ticks */ 147 inputbuf[4] = 0; 148 outputbuf[4] = 0; 149 if (wait) { /* wait for midi input to start */ 150 FD_ZERO(&rdfs); 151 FD_SET(seqfd, &rdfs); 152 select(FD_SETSIZE, &rdfs, NULL, NULL, NULL); 153 seqread(seqfd, outputbuf); /* trash time stamp (hopefully) */ 154 } 155 SEQ_START_TIMER(); 156 SEQ_DUMPBUF(); 157 gettimeofday(&start, NULL); 158 while (ticks < 0xffffff) { 159 FD_ZERO(&rdfs); 160 FD_SET(infile, &rdfs); 161 tv.tv_sec = tv.tv_usec = 0; /* no wait */ 162 if (!select(FD_SETSIZE, &rdfs, NULL, NULL, &tv)) { 163 /* wait forever for more input -- mark end of input */ 164 inputbuf[0] = SEQ_WAIT; 165 inputbuf[1] = inputbuf[2] = inputbuf[3] = 0xff; 166 } else if (seqread(infile, inputbuf) < 4) { 167 inputbuf[0] = SEQ_WAIT; 168 inputbuf[1] = inputbuf[2] = inputbuf[3] = 0xff; 169 } 170 if (inputbuf[0] == SEQ_WAIT) 171 ticks = (*(unsigned int *) &inputbuf[1]); 172 want.tv_sec = ticks / 100 + start.tv_sec; 173 want.tv_usec = (ticks % 100) * 10000 + start.tv_usec; 174 if (want.tv_usec >= 1000000) { 175 want.tv_usec -= 1000000; 176 want.tv_sec++; 177 } 178 if (*inputbuf == SEQ_MIDIPUTC) { 179 if (inputbuf[1] & 0x80) 180 icmd = imid[idb = 0] = inputbuf[1]; 181 else 182 imid[idb] = inputbuf[1]; 183 idb++; 184 if (idb == cmdlen[icmd >> 4] + 1) { 185 fprintf(stderr, ">%8d ", ticks); 186 for (i = 0; i < idb; i++) { 187 fprintf(stderr, "%02x", imid[i]); 188 SEQ_MIDIOUT(OUT_DEV, imid[i]); 189 } 190 fprintf(stderr, "\n"); 191 idb = 1; 192 } 193 } else { 194 gettimeofday(&now, NULL); 195 while (timercmp(&now, &want, <)) { 196 tv.tv_sec = want.tv_sec - now.tv_sec; 197 tv.tv_usec = want.tv_usec - now.tv_usec; 198 if (tv.tv_usec < 0) { 199 tv.tv_usec += 1000000; 200 tv.tv_sec--; 201 } 202 FD_ZERO(&rdfs); 203 FD_SET(seqfd, &rdfs); 204 if (select(FD_SETSIZE, &rdfs, NULL, NULL, &tv)) { 205 seqread(seqfd, outputbuf); 206 if (outputbuf[0] == SEQ_WAIT) { 207 oticks = (*(unsigned int *) &outputbuf[1]); 208 if (oticks - ticks) 209 seq_addevent(outputbuf, 4); 210 } 211 if (outputbuf[0] == SEQ_MIDIPUTC) { 212 if (outputbuf[1] & 0x80) 213 cmd = (mid[db = 0] = outputbuf[1]) & 0xf0; 214 else 215 mid[db] = outputbuf[1]; 216 db++; 217 if (db == cmdlen[cmd >> 4] + 1) { 218 if (transpose && (cmd == 0x80 || cmd == 0x90)) 219 mid[1] += transpose; 220 fprintf(stderr, "<%8d ", oticks); 221 for (i = 0; i < db; i++) { 222 if (channel >= 1 && channel <= 16) 223 mid[0] = cmd | (channel - 1); 224 fprintf(stderr, "%02x", mid[i]); 225 SEQ_MIDIOUT(OUT_DEV, mid[i]); 226 } 227 fprintf(stderr, "\n"); 228 db = 1; 229 SEQ_DUMPBUF(); 230 } 231 } 232 } 233 gettimeofday(&now, NULL); 234 } 235 seq_addevent(inputbuf, 4); 236 } 237 SEQ_DUMPBUF(); 238 } 239 /* should NEVER get to here, if we do, something is really screwed */ 240 close(seqfd); 241 close(infile); 242 close(outfile); 243 exit(0); 244 } 245 /* end of file */ 246