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