1 /************************************************************************
2    playmidi.c -- last change: 1 Jan 96
3 
4    Plays a MIDI file to any supported synth (including midi) device
5 
6    Copyright (C) 1994-1996 Nathan I. Laredo
7 
8    This program is modifiable/redistributable under the terms
9    of the GNU General Public Licence.
10 
11    You should have received a copy of the GNU General Public License
12    along with this program; if not, write to the Free Software
13    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
14    Send your comments and all your spare pocket change to
15    laredo@gnu.ai.mit.edu (Nathan Laredo) or to PSC 1, BOX 709, 2401
16    Kelly Drive, Lackland AFB, TX 78236-5128, USA.
17  *************************************************************************/
18 #ifndef __FreeBSD__
19 #include <getopt.h>
20 #endif
21 #include <fcntl.h>
22 #include <ctype.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include "playmidi.h"
26 
27 SEQ_DEFINEBUF(SEQUENCERBLOCKSIZE);
28 
29 #ifdef PLAY_FM
30 int play_fm = 0xffff, play_gus = 0, play_ext = 0, play_awe = 0;
31 #endif
32 #ifdef PLAY_GUS
33 int play_fm = 0, play_gus = 0xffff, play_ext = 0, play_awe = 0;
34 #endif
35 #ifdef PLAY_MIDI
36 int play_fm = 0, play_gus = 0, play_ext = 0xffff, play_awe = 0;
37 #endif
38 #ifdef PLAY_AWE32
39 int play_fm = 0, play_gus = 0, play_ext = 0, play_awe = 0xffff;
40 #endif
41 
42 struct miditrack seq[MAXTRKS];
43 
44 int verbose = 0, chanmask = 0xffff, perc = PERCUSSION;
45 int dochan = 1, force8bit = 0, wantopl3 = FM_DEFAULT_MODE;
46 int patchloaded[256], fmloaded[256], useprog[16], usevol[16];
47 int graphics = 0, reverb = 0, chorus = 0, nrsynths, nrmidis;
48 int sb_dev = -1, gus_dev = -1, ext_dev = -1, awe_dev = 2, p_remap = 0;
49 int seqfd, find_header = 0, MT32 = 0;
50 FILE *mfd;
51 int FORCE_EXT_DEV = DEFAULT_MIDI_DEV;
52 unsigned long int default_tempo;
53 char *filename;
54 float skew = 1.0;
55 extern int ntrks;
56 extern char *gmvoice[256];
57 extern int mt32pgm[128];
58 extern int playevents();
59 extern int gus_load(int);
60 extern int readmidi(unsigned char *, off_t);
61 extern void loadfm();
62 extern void setup_show(int, char **);
63 extern void close_show(int);
64 
65 struct synth_info card_info[MAX_CARDS];
synth_setup()66 void synth_setup()
67 {
68 #ifdef ULTRA_DRIVER
69     gus_dev = 0;
70     sb_dev = -1;
71     ext_dev = -1;
72     awe_dev = -1;
73     play_ext = 0;
74     play_fm = 0;
75     play_gus = 0xffff;
76     play_awe = 0;
77 #else
78     int i;
79 
80     if (ioctl(seqfd, SNDCTL_SEQ_NRSYNTHS, &nrsynths) == -1) {
81 	fprintf(stderr, "there is no soundcard\n");
82 	exit(-1);
83     }
84     for (i = 0; i < nrsynths; i++) {
85 	card_info[i].device = i;
86 	if (ioctl(seqfd, SNDCTL_SYNTH_INFO, &card_info[i]) == -1) {
87 	    fprintf(stderr, "cannot get info on soundcard\n");
88 	    perror(SEQUENCER_DEV);
89 	    exit(-1);
90 	}
91 	if (card_info[i].synth_type == SYNTH_TYPE_SAMPLE
92 	    && card_info[i].synth_subtype == SAMPLE_TYPE_GUS)
93 	    gus_dev = i;
94 	else if (card_info[i].synth_type == SYNTH_TYPE_SAMPLE
95 	    && card_info[i].synth_subtype == SAMPLE_TYPE_AWE32)
96 	    awe_dev = i;
97 	else if (card_info[i].synth_type == SYNTH_TYPE_FM) {
98 	    sb_dev = i;
99 	    if (play_fm)
100 		loadfm();
101 	    if (wantopl3)
102 		card_info[i].nr_voices = 12;	/* we have 12 with 4-op */
103 	}
104     }
105 
106     if (gus_dev >= 0) {
107 	if (ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev) == -1) {
108 	    perror("Sample reset");
109 	    exit(-1);
110 	}
111     }
112     if (ioctl(seqfd, SNDCTL_SEQ_NRMIDIS, &nrmidis) == -1) {
113 	fprintf(stderr, "can't get info about midi ports\n");
114 	exit(-1);
115     }
116     if (nrmidis > 0) {
117 	if (FORCE_EXT_DEV >= 0)
118 	    ext_dev = FORCE_EXT_DEV;
119 	else
120 	    ext_dev = nrmidis - 1;
121     }
122     if (!play_gus)
123 	gus_dev = -1;
124     if (!play_fm)
125 	sb_dev = -1;
126     if (!play_ext)
127 	ext_dev = -1;
128     if (!play_awe)
129 	awe_dev = -1;
130     if (ext_dev < 0)
131 	play_ext = 0;
132     if (sb_dev < 0)
133 	play_fm = 0;
134     if (gus_dev < 0)
135 	play_gus = 0;
136     if (awe_dev < 0)
137 	play_awe = 0;
138 #endif
139 }
140 
seqbuf_dump()141 void seqbuf_dump()
142 {
143     if (_seqbufptr)
144 	if (write(seqfd, _seqbuf, _seqbufptr) == -1) {
145 	    perror("write " SEQUENCER_DEV);
146 	    exit(-1);
147 	}
148     _seqbufptr = 0;
149 }
150 
hextoi(s)151 int hextoi(s)
152 char *s;
153 {
154     int i, j, k, l;
155 
156     j = 0;
157     k = strlen(s);
158     for (i = 0; i < k; i++) {
159 	l = toupper(s[i]);
160 	if (l > 64 && l < 71)
161 	    j += (l - 55) << ((k - i - 1) * 4);
162 	else if (l > 47 && l < 58)
163 	    j += (l - 48) << ((k - i - 1) * 4);
164 	else if (l != 88) {
165 	    fprintf(stderr, "invalid character in hexidecimal mask\n");
166 	    exit(1);
167 	}
168     }
169     if (j < 0 || j > 0xffff) {
170 	fprintf(stderr, "mask must be between 0 and ffff hexidecimal\n");
171 	exit(1);
172     }
173     return j;
174 
175 }
176 
main(argc,argv)177 int main(argc, argv)
178 int argc;
179 char **argv;
180 {
181     extern char *optarg;
182     extern int optind;
183     int i, error = 0, j, newprog;
184     char *extra;
185     char *filebuf;
186     struct stat info;
187     int piped = 0;
188 
189     printf("%s Copyright (C) 1994-1997 Nathan I. Laredo,"
190 	   " AWE32 by Takashi Iwai\n"
191 	   "This is free software with ABSOLUTELY NO WARRANTY.\n"
192 	   "For details please see the file COPYING.\n", RELEASE);
193     for (i = 0; i < 16; i++)
194 	useprog[i] = usevol[i] = 0;	/* reset options */
195     while ((i = getopt(argc, argv,
196 		     "48c:aA:C:dD:eE:fF:gh:G:i:IMp:P:rR:t:vV:x:z")) != -1)
197 	switch (i) {
198 	case '8':
199 	    force8bit++;
200 	    break;
201 	case 'x':
202 	    j = atoi(optarg);
203 	    if (j < 1 || j > 16) {
204 		fprintf(stderr, "option -x channel must be 1 - 16\n");
205 		exit(1);
206 	    }
207 	    j = 1 << (j - 1);
208 	    chanmask &= ~j;
209 	    break;
210 	case 'c':
211 	    if (chanmask == 0xffff)
212 		chanmask = hextoi(optarg);
213 	    else
214 		chanmask |= hextoi(optarg);
215 	    break;
216 	case 'D':
217 	    FORCE_EXT_DEV = atoi(optarg);
218 	    break;
219 	case 'e':
220 	    play_ext = 0xffff;
221 	    play_gus = play_fm = play_awe = 0;
222 	    break;
223 	case 'g':
224 	    play_gus = 0xffff;
225 	    play_ext = play_fm = play_awe = 0;
226 	    break;
227 	case 'a':
228 	    play_awe = 0xffff;
229 	    play_ext = play_fm = play_gus = 0;
230 	    break;
231 	case 'h':
232 	    find_header = atoi(optarg);
233 	    if (find_header < 1) {
234 		fprintf(stderr, "option -h header must be > 0\n");
235 		exit(1);
236 	    }
237 	    break;
238 	case 'f':
239 	case '4':
240 	    play_fm = 0xffff;
241 	    play_ext = play_gus = play_awe = 0;
242 	    wantopl3 = (i == '4');
243 	    break;
244 	case 'I':
245 	    {
246 		int k;
247 		printf("Gravis Ultrasound Program Info:");
248 		for (j = 0; j < 128; j++) {
249 		    extra = gmvoice[j];
250 		    printf("%c%3d %s", j % 6 ? ' ' : '\n', j + 1, extra);
251 		    for (k = strlen(extra); k < 8; k++)
252 			putchar(' ');
253 		}
254 		putchar('\n');
255 		exit(1);
256 	    }
257 	    break;
258 	case 'i':
259 	    chanmask &= ~hextoi(optarg);
260 	    break;
261 	case 'M':
262 	    MT32++;
263 	    break;
264 	case 'p':
265 	    if (strchr(optarg, ',') == NULL) {	/* set all channels */
266 		newprog = atoi(optarg);
267 		if (newprog < 1 || newprog > 129) {
268 		    fprintf(stderr, "option -p prog must be 1 - 129\n");
269 		    exit(1);
270 		}
271 		for (j = 0; j < 16; j++)
272 		    useprog[j] = newprog;
273 	    } else {		/* set channels individually */
274 		extra = optarg;
275 		while (extra != NULL) {
276 		    j = atoi(extra);
277 		    if (j < 1 || j > 16) {
278 			fprintf(stderr, "opton -p chan must be 1 - 16\n");
279 			exit(1);
280 		    }
281 		    extra = strchr(extra, ',');
282 		    if (extra == NULL) {
283 			fprintf(stderr, "option -p prog needed for chan %d\n",
284 				j);
285 			exit(1);
286 		    } else
287 			extra++;
288 		    newprog = atoi(extra);
289 		    if (newprog < 1 || newprog > 129) {
290 			fprintf(stderr, "option -p prog must be 1 - 129\n");
291 			fprintf(stderr, "DANGER: 129 may screw everything!\n");
292 			exit(1);
293 		    }
294 		    useprog[j - 1] = newprog;
295 		    extra = strchr(extra, ',');
296 		    if (extra != NULL)
297 			extra++;
298 		}
299 	    }
300 	    break;
301 	case 'r':
302 	    graphics++;
303 	    break;
304 	case 't':
305 	    if ((skew = atof(optarg)) < .25) {
306 		fprintf(stderr, "option -t skew under 0.25 unplayable\n");
307 		exit(1);
308 	    }
309 	    break;
310 	case 'E':
311 	    play_ext = hextoi(optarg);
312 	    play_fm &= ~play_ext;
313 	    play_gus &= ~play_ext;
314 	    play_awe &= ~play_ext;
315 	    break;
316 	case 'F':
317 	    play_fm = hextoi(optarg);
318 	    play_ext &= ~play_fm;
319 	    play_gus &= ~play_fm;
320 	    play_awe &= ~play_fm;
321 	    break;
322 	case 'G':
323 	    play_gus = hextoi(optarg);
324 	    play_fm &= ~play_gus;
325 	    play_ext &= ~play_gus;
326 	    play_awe &= ~play_gus;
327 	    break;
328 	case 'A':
329 	    play_awe = hextoi(optarg);
330 	    play_ext &= ~play_awe;
331 	    play_fm &= ~play_awe;
332 	    play_gus &= ~play_awe;
333 	    break;
334 	case 'R':
335 	    reverb = atoi(optarg);
336 	    if (reverb < 0 || reverb > 127) {
337 		fprintf(stderr, "option -R reverb must be 0 - 127\n");
338 		exit(1);
339 	    }
340 	    break;
341 	case 'C':
342 	    chorus = atoi(optarg);
343 	    if (chorus < 0 || chorus > 127) {
344 		fprintf(stderr, "option -C chorus must be 0 - 127\n");
345 		exit(1);
346 	    }
347 	    break;
348 	case 'P':
349 	    p_remap = atoi(optarg);
350 	    if (p_remap < 1 || p_remap > 16) {
351 		fprintf(stderr, "option -P channel must be 1 - 16\n");
352 		exit(1);
353 	    }
354 	    break;
355 	case 'v':
356 	    verbose++;
357 	    break;
358 	case 'V':
359 	    extra = optarg;
360 	    while (extra != NULL) {
361 		j = atoi(extra);
362 		if (j < 1 || j > 16) {
363 		    fprintf(stderr, "opton -V chan must be 1 - 16\n");
364 		    exit(1);
365 		}
366 		extra = strchr(extra, ',');
367 		if (extra == NULL) {
368 		    fprintf(stderr, "option -V volume needed for chan %d\n",
369 			    j);
370 		    exit(1);
371 		}
372 		extra++;
373 		newprog = atoi(extra);
374 		if (newprog < 1 || newprog > 127) {
375 		    fprintf(stderr, "option -V volume must be 1 - 127\n");
376 		    exit(1);
377 		}
378 		usevol[j - 1] = newprog;
379 		extra = strchr(extra, ',');
380 		if (extra != NULL)
381 		    extra++;
382 	    }
383 	    break;
384 	case 'z':
385 	    dochan = 0;
386 	    break;
387 	case 'd':
388 	    chanmask &= ~perc;
389 	    break;
390 	default:
391 	    error++;
392 	    break;
393 	}
394 
395     if (error || optind >= argc) {
396 	fprintf(stderr, "usage: %s [-options] file1 [file2 ...]\n", argv[0]);
397 	fprintf(stderr, "  -v       verbosity (additive)\n"
398 		"  -i x     ignore channels set in bitmask x (hex)\n"
399 		"  -c x     play only channels set in bitmask x (hex)\n"
400 		"  -x x     exclude channel x from playable bitmask\n"
401 		"  -p [c,]x play program x on channel c (all if no c)\n"
402 		"  -V [c,]x play channel c with volume x (all if no c)\n"
403 		"  -t x     skew tempo by x (float)\n"
404 		"  -d       don't play any percussion\n"
405 		"  -P x     play percussion on channel x\n"
406 		"  -e       output to external midi\n"
407 		"  -D x     output to midi device x\n"
408 		"  -f       output to fm (sb patches)\n"
409 		"  -4       output to 4-op fm (opl/3 patches)\n"
410 		"  -a       output to awe32 wave synth\n"
411 		"  -h x     skip to header x in large archive\n"
412 		"  -g       output to gravis ultrasound\n"
413 		"  -E x     play channels in bitmask x external\n"
414 		"  -F x     play channels in bitmask x on fm\n"
415 		"  -G x     play channels in bitmask x on gus\n"
416 		"  -A x     play channels in bitmask x on awe32\n"
417 		"  -z       ignore channel of all events\n"
418 		"  -8       force 8-bit samples on GUS\n"
419 		"  -M       enable MT-32 to GM translation mode\n"
420 		"  -I       show list of all GM programs (see -p)\n"
421 		"  -R x     set initial reverb to x (0-127)\n"
422 		"  -C x     set initial chorus to x (0-127)\n"
423 		"  -r       real-time playback graphics\n"
424 		"  -k       kill anything using /dev/sequencer\n"
425 		"  -w       wait for anything using /dev/sequencer\n");
426 	exit(1);
427     }
428     if ((seqfd = open(SEQUENCER_DEV, O_WRONLY, 0)) < 0) {
429 	perror("open " SEQUENCER_DEV);
430 	exit(-1);
431     }
432     synth_setup();
433     if (!(play_gus || play_fm || play_ext || play_awe)) {
434 	fprintf(stderr, "%s: No playback device found.\n", argv[0]);
435 	exit(-1);
436     }
437     setup_show(argc, argv);
438     /* play all filenames listed on command line */
439     for (i = optind; i < argc;) {
440 	filename = argv[i];
441 	if (stat(filename, &info) == -1) {
442 	    if ((extra = malloc(strlen(filename) + 4)) == NULL)
443 		close_show(-1);
444 	    sprintf(extra, "%s.mid", filename);
445 	    if (stat(extra, &info) == -1)
446 		close_show(-1);
447 	    if ((mfd = fopen(extra, "r")) == NULL)
448 		close_show(-1);
449 	    free(extra);
450 	} else {
451 	    char *ext = strrchr(filename, '.');
452 	    if (ext && strcmp(ext, ".gz") == 0) {
453 		char temp[1024];
454 		piped = 1;
455 		sprintf(temp, "gzip -l %s", filename);
456 		if ((mfd = popen(temp, "r")) == NULL)
457 		    close_show(-1);
458 		fgets(temp, sizeof(temp), mfd); /* skip 1st line */
459 		fgets(temp, sizeof(temp), mfd);
460 		strtok(temp, " "); /* compressed size */
461 		info.st_size = atoi(strtok(NULL, " ")); /* original size */
462 		pclose(mfd);
463 		sprintf(temp, "gzip -d -c %s", filename);
464 		if ((mfd = popen(temp, "r")) == NULL)
465 		    close_show(-1);
466 	    } else if ((mfd = fopen(filename, "r")) == NULL)
467 		close_show(-1);
468 	}
469 	if ((filebuf = malloc(info.st_size)) == NULL)
470 	    close_show(-1);
471 	fread(filebuf, 1, info.st_size, mfd);
472 	if (piped)
473 	    pclose(mfd);
474 	else
475 	    fclose(mfd);
476 	do {
477 	    if (play_gus)
478 		gus_load(-1);
479 	    default_tempo = 500000;
480 	    /* error holds number of tracks read */
481 	    error = readmidi(filebuf, info.st_size);
482 	    if (play_gus && error > 0) {
483 		int i;		/* need to keep other i safe */
484 #define CMD (seq[i].data[j] & 0xf0)
485 #define CHN (seq[i].data[j] & 0x0f)
486 #define PGM (seq[i].data[j + 1])
487 		/* REALLY STUPID way to preload GUS, but it works */
488 		for (i = 0; i < ntrks; i++)
489 		    for (j = 0; j < seq[i].length - 5; j++)
490 			if (ISGUS(CHN) && !(PGM & 0x80) &&
491 			    ((CMD == MIDI_PGM_CHANGE && !ISPERC(CHN))
492 			     || (CMD == MIDI_NOTEON && ISPERC(CHN))))
493 			    gus_load(ISPERC(CHN) ? PGM + 128 :
494 				     useprog[CHN] ? useprog[CHN] - 1 :
495 				     MT32 ? mt32pgm[PGM] : PGM);
496 		/* make sure that some program was loaded to use */
497 		for (j = 0; patchloaded[j] != 1 && j < 128; j++);
498 		if (j > 127)
499 		    gus_load(0);
500 	    }
501 	    newprog = 1;	/* if there's an error skip to next file */
502 	    if (error > 0)	/* error holds number of tracks read */
503 		while ((newprog = playevents()) == 0);
504 	    if (find_header)	/* play headers following selected */
505 		find_header += newprog;
506 	} while (find_header);
507 	if ((i += newprog) < optind)
508 	    i = optind;		/* can't skip back past first file */
509 	free(filebuf);
510     }
511     close(seqfd);
512     close_show(0);
513     exit(0);			/* this statement is here to keep the compiler happy */
514 }
515 /* end of file */
516