• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *   MIDI file player for ALSA sequencer
3  *   (type 0 only!, the library that is used doesn't support merging of tracks)
4  *
5  *   Copyright (c) 1998 by Frank van de Pol <F.K.W.van.de.Pol@inter.nl.net>
6  *
7  *   Modified so that this uses alsa-lib
8  *   1999 Jan. by Isaku Yamahata <yamahata@kusm.kyoto-u.ac.jp>
9  *
10  *   19990604	Takashi Iwai <iwai@ww.uni-erlangen.de>
11  *	- use blocking mode
12  *	- fix tempo event bug
13  *	- add command line options
14  *
15  *   19990827	Takashi Iwai <iwai@ww.uni-erlangen.de>
16  *	- use snd_seq_alloc_queue()
17  *
18  *   19990916	Takashi Iwai <iwai@ww.uni-erlangen.de>
19  *	- use middle-level sequencer routines and macros
20  *
21  *   This program is free software; you can redistribute it and/or modify
22  *   it under the terms of the GNU General Public License as published by
23  *   the Free Software Foundation; either version 2 of the License, or
24  *   (at your option) any later version.
25  *
26  *   This program is distributed in the hope that it will be useful,
27  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
28  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  *   GNU General Public License for more details.
30  *
31  *   You should have received a copy of the GNU General Public License
32  *   along with this program; if not, write to the Free Software
33  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
34  *
35  */
36 
37 #include <stdio.h>
38 #include <ctype.h>
39 #include <fcntl.h>
40 #include <stdlib.h>
41 #include <sys/ioctl.h>
42 #include <unistd.h>
43 #include <errno.h>
44 #include <string.h>
45 
46 #include "midifile.h"		/* SMF library header */
47 #include "midifile.c"		/* SMF library code */
48 
49 #include "../include/asoundlib.h"
50 
51 /* send the real-time time stamps (instead of midi ticks) to the ALSA sequencer */
52 static int use_realtime = 0;
53 
54 /* control the event buffering by using a blocking mode */
55 static int use_blocking_mode = 1;
56 
57 /* default destination queue, client and port numbers */
58 #define DEST_CLIENT_NUMBER	65
59 #define DEST_PORT_NUMBER	0
60 
61 /* event pool size */
62 #define WRITE_POOL_SIZE		200
63 #define WRITE_POOL_SPACE	10
64 #define READ_POOL_SIZE		10	/* we need to read the pool only for echoing */
65 
66 static FILE *F;
67 static snd_seq_t *seq_handle = NULL;
68 static int ppq = 96;
69 static int slave_ppq = 96;
70 
71 static double local_secs = 0;
72 static int local_ticks = 0;
73 static int local_tempo = 500000;
74 
75 static int dest_queue = -1;
76 static int shared_queue = 0;
77 static int tick_offset = 0;
78 static int dest_client = DEST_CLIENT_NUMBER;
79 static int dest_port = DEST_PORT_NUMBER;
80 static int my_port = 0;
81 
82 static int verbose = 0;
83 static int slave   = 0;		/* allow external sync */
84 
85 #define VERB_INFO	1
86 #define VERB_MUCH	2
87 #define VERB_EVENT	3
88 
89 static void alsa_start_timer(void);
90 static void alsa_stop_timer(void);
91 static void wait_start(void);
92 
93 
tick2time_dbl(int tick)94 static inline double tick2time_dbl(int tick)
95 {
96 	return local_secs + ((double) (tick - local_ticks) * (double) local_tempo * 1.0E-6 / (double) ppq);
97 }
98 
tick2time(snd_seq_real_time_t * tm,int tick)99 static void tick2time(snd_seq_real_time_t * tm, int tick)
100 {
101 	double secs = tick2time_dbl(tick);
102 	tm->tv_sec = secs;
103 	tm->tv_nsec = (secs - tm->tv_sec) * 1.0E9;
104 }
105 
write_ev(snd_seq_event_t * ev)106 static void write_ev(snd_seq_event_t *ev)
107 {
108 	int rc;
109 
110 	if (use_blocking_mode) {
111 		rc = snd_seq_event_output(seq_handle, ev);
112 		if (rc < 0) {
113 			printf("written = %i (%s)\n", rc, snd_strerror(rc));
114 			exit(1);
115 		}
116 		return;
117 	}
118 	while ((rc = snd_seq_event_output(seq_handle, ev)) < 0) {
119 		int npfds = snd_seq_poll_descriptors_count(seq_handle, POLLOUT);
120 		struct pollfd *pfds = alloca(sizeof(*pfds) * npfds);
121 		snd_seq_poll_descriptors(seq_handle, pfds, npfds, POLLOUT);
122 		if ((rc = poll(pfds, npfds, -1)) < 0) {
123 			printf("poll error = %i (%s)\n", rc, snd_strerror(errno));
124 			exit(1);
125 		}
126 	}
127 }
128 
129 /* read the byte */
mygetc(void)130 static int mygetc(void)
131 {
132 	return getc(F);
133 }
134 
135 /* print out the text */
mytext(int type ATTRIBUTE_UNUSED,int leng,char * msg)136 static void mytext(int type ATTRIBUTE_UNUSED, int leng, char *msg)
137 {
138 	char *p;
139 	char *ep = msg + leng;
140 
141 	if (verbose >= VERB_INFO) {
142 		for (p = msg; p < ep; p++)
143 			putchar(isprint(*p) ? *p : '?');
144 		putchar('\n');
145 	}
146 }
147 
do_header(int format,int ntracks,int division)148 static void do_header(int format, int ntracks, int division)
149 {
150 	snd_seq_queue_tempo_t *tempo;
151 
152 	if (verbose >= VERB_INFO)
153 		printf("smf format %d, %d tracks, %d ppq\n", format, ntracks, division);
154 	ppq = division;
155 
156 	if (format != 0 || ntracks != 1) {
157 		printf("This player does not support merging of tracks.\n");
158 		if (! shared_queue)
159 			alsa_stop_timer();
160 		exit(1);
161 	}
162 	/* set the ppq */
163 	snd_seq_queue_tempo_alloca(&tempo);
164 	/* ppq must be set before starting the timer */
165 	if (snd_seq_get_queue_tempo(seq_handle, dest_queue, tempo) < 0) {
166     		perror("get_queue_tempo");
167     		exit(1);
168 	}
169 	if ((slave_ppq = snd_seq_queue_tempo_get_ppq(tempo)) != ppq) {
170 		snd_seq_queue_tempo_set_ppq(tempo, ppq);
171 		if (snd_seq_set_queue_tempo(seq_handle, dest_queue, tempo) < 0) {
172     			perror("set_queue_tempo");
173     			if (!slave && !shared_queue)
174     				exit(1);
175 			else
176 				printf("different PPQ %d in SMF from queue PPQ %d\n", ppq, slave_ppq);
177 		} else
178 			slave_ppq = ppq;
179 		if (verbose >= VERB_INFO)
180 			printf("ALSA Timer updated, PPQ = %d\n", snd_seq_queue_tempo_get_ppq(tempo));
181 	}
182 
183 	/* start playing... */
184 	if (slave) {
185 		if (verbose >= VERB_INFO)
186 			printf("Wait till timer starts...\n");
187 		wait_start();
188 		if (verbose >= VERB_INFO)
189 			printf("Go!\n");
190 	} else if (shared_queue) {
191 		snd_seq_queue_status_t *stat;
192 		snd_seq_queue_status_alloca(&stat);
193 		snd_seq_get_queue_status(seq_handle, dest_queue, stat);
194 		tick_offset = snd_seq_queue_status_get_tick_time(stat);
195 		fprintf(stderr, "tick offset = %d\n", tick_offset);
196 	} else {
197 		alsa_start_timer();
198 		tick_offset = 0;
199 	}
200 }
201 
202 /* fill the event time */
set_event_time(snd_seq_event_t * ev,unsigned int currtime)203 static void set_event_time(snd_seq_event_t *ev, unsigned int currtime)
204 {
205 	if (use_realtime) {
206 		snd_seq_real_time_t rtime;
207 		if (ppq != slave_ppq)
208 			currtime = (currtime * slave_ppq) / ppq;
209 		tick2time(&rtime, currtime);
210 		snd_seq_ev_schedule_real(ev, dest_queue, 0, &rtime);
211 	} else {
212 		if (ppq != slave_ppq)
213 			currtime = (currtime * slave_ppq) / ppq;
214 		currtime += tick_offset;
215 		snd_seq_ev_schedule_tick(ev, dest_queue, 0, currtime);
216 	}
217 }
218 
219 /* fill the normal event header */
set_event_header(snd_seq_event_t * ev)220 static void set_event_header(snd_seq_event_t *ev)
221 {
222 	snd_seq_ev_clear(ev);
223 	snd_seq_ev_set_dest(ev, dest_client, dest_port);
224 	snd_seq_ev_set_source(ev, my_port);
225 	set_event_time(ev, Mf_currtime);
226 }
227 
228 /* start the timer */
alsa_start_timer(void)229 static void alsa_start_timer(void)
230 {
231 	snd_seq_start_queue(seq_handle, dest_queue, NULL);
232 }
233 
234 /* stop the timer */
alsa_stop_timer(void)235 static void alsa_stop_timer(void)
236 {
237 	snd_seq_event_t ev;
238 	set_event_header(&ev);
239 	snd_seq_stop_queue(seq_handle, dest_queue, &ev);
240 }
241 
242 /* change the tempo */
do_tempo(int us)243 static void do_tempo(int us)
244 {
245 	snd_seq_event_t ev;
246 
247 	if (verbose >= VERB_MUCH) {
248 		double bpm;
249 		bpm = 60.0E6 / (double) us;
250 		printf("Tempo %d us/beat, %.2f bpm\n", us, bpm);
251 	}
252 
253 	/* store the new tempo and timestamp of the tempo change */
254 	local_secs = tick2time_dbl(Mf_currtime);
255 	local_ticks = Mf_currtime;
256 	local_tempo = us;
257 
258 	set_event_header(&ev);
259 	if (!slave)
260 		snd_seq_change_queue_tempo(seq_handle, dest_queue, us, &ev);
261 }
262 
do_noteon(int chan,int pitch,int vol)263 static void do_noteon(int chan, int pitch, int vol)
264 {
265 	snd_seq_event_t ev;
266 
267 	if (verbose >= VERB_EVENT)
268 		printf("%lu: NoteOn (%d) %d %d\n", Mf_currtime, chan, pitch, vol);
269 	set_event_header(&ev);
270 	snd_seq_ev_set_noteon(&ev, chan, pitch, vol);
271 	write_ev(&ev);
272 }
273 
274 
do_noteoff(int chan,int pitch,int vol)275 static void do_noteoff(int chan, int pitch, int vol)
276 {
277 	snd_seq_event_t ev;
278 
279 	if (verbose >= VERB_EVENT)
280 		printf("%lu: NoteOff (%d) %d %d\n", Mf_currtime, chan, pitch, vol);
281 	set_event_header(&ev);
282 	snd_seq_ev_set_noteoff(&ev, chan, pitch, vol);
283 	write_ev(&ev);
284 }
285 
286 
do_program(int chan,int program)287 static void do_program(int chan, int program)
288 {
289 	snd_seq_event_t ev;
290 
291 	if (verbose >= VERB_EVENT)
292 		printf("%lu: Program (%d) %d\n", Mf_currtime, chan, program);
293 	set_event_header(&ev);
294 	snd_seq_ev_set_pgmchange(&ev, chan, program);
295 	write_ev(&ev);
296 }
297 
298 
do_parameter(int chan,int control,int value)299 static void do_parameter(int chan, int control, int value)
300 {
301 	snd_seq_event_t ev;
302 
303 	if (verbose >= VERB_EVENT)
304 		printf("%lu: Control (%d) %d %d\n", Mf_currtime, chan, control, value);
305 	set_event_header(&ev);
306 	snd_seq_ev_set_controller(&ev, chan, control, value);
307 	write_ev(&ev);
308 }
309 
310 
do_pitchbend(int chan,int lsb,int msb)311 static void do_pitchbend(int chan, int lsb, int msb)
312 {	/* !@#$% lsb & msb are in the wrong order in docs */
313 	snd_seq_event_t ev;
314 
315 	if (verbose >= VERB_EVENT)
316 		printf("%lu: Pitchbend (%d) %d %d\n", Mf_currtime, chan, lsb, msb);
317 	set_event_header(&ev);
318 	snd_seq_ev_set_pitchbend(&ev, chan, (lsb + (msb << 7)) - 8192);
319 	write_ev(&ev);
320 }
321 
do_pressure(int chan,int pitch,int pressure)322 static void do_pressure(int chan, int pitch, int pressure)
323 {
324 	snd_seq_event_t ev;
325 
326 	if (verbose >= VERB_EVENT)
327 		printf("%lu: KeyPress (%d) %d %d\n", Mf_currtime, chan, pitch, pressure);
328 	set_event_header(&ev);
329 	snd_seq_ev_set_keypress(&ev, chan, pitch, pressure);
330 	write_ev(&ev);
331 }
332 
do_chanpressure(int chan,int pressure)333 static void do_chanpressure(int chan, int pressure)
334 {
335 	snd_seq_event_t ev;
336 
337 	if (verbose >= VERB_EVENT)
338 		printf("%lu: ChanPress (%d) %d\n", Mf_currtime, chan, pressure);
339 	set_event_header(&ev);
340 	snd_seq_ev_set_chanpress(&ev, chan, pressure);
341 	write_ev(&ev);
342 }
343 
do_sysex(int len,char * msg)344 static void do_sysex(int len, char *msg)
345 {
346 	snd_seq_event_t ev;
347 
348 	if (verbose >= VERB_MUCH) {
349 		int c;
350 		printf("%lu: Sysex, len=%d\n", Mf_currtime, len);
351 		for (c = 0; c < len; c++) {
352 			printf(" %02x", (unsigned char)msg[c]);
353 			if (c % 16 == 15)
354 				putchar('\n');
355 		}
356 		if (c % 16 != 15)
357 			putchar('\n');
358 	}
359 
360 	set_event_header(&ev);
361 	snd_seq_ev_set_sysex(&ev, len, msg);
362 	write_ev(&ev);
363 }
364 
wait_for_event(void)365 static snd_seq_event_t *wait_for_event(void)
366 {
367 	int left;
368 	snd_seq_event_t *input_event;
369 
370 	if (use_blocking_mode) {
371 		/* read the event - blocked until any event is read */
372 		left = snd_seq_event_input(seq_handle, &input_event);
373 	} else {
374 		/* read the event - using select syscall */
375 		while ((left = snd_seq_event_input(seq_handle, &input_event)) >= 0 &&
376 		       input_event == NULL) {
377 			int npfds = snd_seq_poll_descriptors_count(seq_handle, POLLIN);
378 			struct pollfd *pfds = alloca(sizeof(*pfds) * npfds);
379 			snd_seq_poll_descriptors(seq_handle, pfds, npfds, POLLIN);
380 			if ((left = poll(pfds, npfds, -1)) < 0) {
381 				printf("poll error = %i (%s)\n", errno, snd_strerror(errno));
382 				exit(1);
383 			}
384 		}
385 	}
386 
387 	if (left < 0) {
388 		printf("alsa_sync error!:%s\n", snd_strerror(left));
389 		return NULL;
390 	}
391 
392 	return input_event;
393 }
394 
395 /* synchronize to the end of the event */
alsa_sync(void)396 static void alsa_sync(void)
397 {
398 	/* send the echo event to the self client. */
399 	if (verbose >= VERB_MUCH)
400 		printf("alsa_sync syncing...\n");
401 	/* dump the buffer */
402 	snd_seq_drain_output(seq_handle);
403 	snd_seq_sync_output_queue(seq_handle);
404 	if (verbose >= VERB_MUCH)
405 		printf("alsa_sync synced\n");
406 	sleep(1); /* give a time for note releasing.. */
407 }
408 
409 
410 /* wait for the start of the queue */
wait_start(void)411 static void wait_start(void)
412 {
413 	snd_seq_event_t *input_event;
414 
415 	/* wait for the start event from the system timer */
416 	for (;;) {
417 		input_event = wait_for_event();
418 		if (input_event) {
419 			if (verbose >= VERB_MUCH)
420 				printf("wait_start got event. type=%d, flags=%d\n",
421 				       input_event->type, input_event->flags);
422 			if (input_event->type == SND_SEQ_EVENT_START &&
423 			    input_event->data.queue.queue == dest_queue) {
424 				snd_seq_free_event(input_event);
425 				break;
426 			}
427 			snd_seq_free_event(input_event);
428 		}
429 	}
430 	if (verbose >= VERB_MUCH)
431 		printf("start received\n");
432 }
433 
434 
435 /* print the usage */
usage(void)436 static void usage(void)
437 {
438 	fprintf(stderr, "usage: playmidi1 [options] [file]\n");
439 	fprintf(stderr, "  options:\n");
440 	fprintf(stderr, "  -v: verbose mode\n");
441 	fprintf(stderr, "  -a client:port : set destination address (default=%d:%d)\n",
442 		DEST_CLIENT_NUMBER, DEST_PORT_NUMBER);
443 	fprintf(stderr, "  -q queue: use the specified queue\n");
444 	fprintf(stderr, "  -s queue: slave mode (allow external clock synchronization)\n");
445 	fprintf(stderr, "  -r : play on real-time mode\n");
446 	fprintf(stderr, "  -b : play on non-blocking mode\n");
447 }
448 
main(int argc,char * argv[])449 int main(int argc, char *argv[])
450 {
451 	int tmp;
452 	int c;
453 	snd_seq_addr_t dest_addr;
454 	const char *addr = "65:0";
455 
456 	while ((c = getopt(argc, argv, "s:a:p:q:vrb")) != -1) {
457 		switch (c) {
458 		case 'v':
459 			verbose++;
460 			break;
461 		case 'a':
462 		case 'p':
463 			addr = optarg;
464 			break;
465 		case 'q':
466 			dest_queue = atoi(optarg);
467 			if (dest_queue < 0) {
468 				fprintf(stderr, "invalid queue number %d\n", dest_queue);
469 				exit(1);
470 			}
471 			break;
472 		case 's':
473 			slave = 1;
474 			dest_queue = atoi(optarg);
475 			if (dest_queue < 0) {
476 				fprintf(stderr, "invalid queue number %d\n", dest_queue);
477 				exit(1);
478 			}
479 			break;
480 		case 'r':
481 			use_realtime = 1;
482 			break;
483 		case 'b':
484 			use_blocking_mode = 0;
485 			break;
486 		default:
487 			usage();
488 			exit(1);
489 		}
490 	}
491 
492 	if (verbose >= VERB_INFO) {
493 		if (use_realtime)
494 			printf("ALSA MIDI Player, feeding events to real-time queue\n");
495 		else
496 			printf("ALSA MIDI Player, feeding events to song queue\n");
497 	}
498 
499 	/* open the sequencer device */
500 	/* Here we open the device in read/write for slave mode. */
501 	tmp = snd_seq_open(&seq_handle, "hw", slave ? SND_SEQ_OPEN_DUPLEX : SND_SEQ_OPEN_OUTPUT, 0);
502 	if (tmp < 0) {
503 		perror("open /dev/snd/seq");
504 		exit(1);
505 	}
506 
507 	tmp = snd_seq_nonblock(seq_handle, !use_blocking_mode);
508 	if (tmp < 0) {
509 		perror("block_mode");
510 		exit(1);
511 	}
512 
513 	/* set the name */
514 	/* set the event filter to receive only the echo event */
515 	/* if running in slave mode, also listen for a START event */
516 	if (slave)
517 		snd_seq_set_client_event_filter(seq_handle, SND_SEQ_EVENT_START);
518 	snd_seq_set_client_name(seq_handle, "MIDI file player");
519 
520 	/* create the port */
521 	my_port = snd_seq_create_simple_port(seq_handle, "Port 0",
522 					     SND_SEQ_PORT_CAP_WRITE |
523 					     SND_SEQ_PORT_CAP_READ,
524 					     SND_SEQ_PORT_TYPE_MIDI_GENERIC);
525 	if (my_port < 0) {
526 		perror("create port");
527 		exit(1);
528 	}
529 
530 	if (snd_seq_parse_address(seq_handle, &dest_addr, addr) < 0) {
531 		perror("invalid destination address");
532 		exit(1);
533 	}
534 	dest_client = dest_addr.client;
535 	dest_port = dest_addr.port;
536 
537 	/* set the queue */
538 	if (dest_queue >= 0) {
539 		shared_queue = 1;
540 		if (snd_seq_set_queue_usage(seq_handle, dest_queue, 1) < 0) {
541 			perror("use queue");
542 			exit(1);
543 		}
544 	} else {
545 		shared_queue = 0;
546 		dest_queue = snd_seq_alloc_queue(seq_handle);
547 		if (dest_queue < 0) {
548 			perror("alloc queue");
549 			exit(1);
550 		}
551 	}
552 
553 	/* set the subscriber */
554 	tmp = snd_seq_connect_to(seq_handle, my_port, dest_client, dest_port);
555 	if (tmp < 0) {
556 		perror("subscribe");
557 		exit(1);
558 	}
559 
560 	/* subscribe for the timer START event */
561 	if (slave) {
562 		tmp = snd_seq_connect_from(seq_handle, my_port,
563 					   SND_SEQ_CLIENT_SYSTEM,
564 					   dest_queue + 16 /*snd_seq_queue_sync_port(dest_queue)*/);
565 		if (tmp < 0) {
566 			perror("subscribe");
567 			exit(1);
568 		}
569 	}
570 
571 	/* change the pool size */
572 	if (snd_seq_set_client_pool_output(seq_handle, WRITE_POOL_SIZE) < 0 ||
573 	    snd_seq_set_client_pool_input(seq_handle, READ_POOL_SIZE) < 0 ||
574 	    snd_seq_set_client_pool_output_room(seq_handle, WRITE_POOL_SPACE) < 0) {
575 		perror("pool");
576 		exit(1);
577 	}
578 
579 	if (optind < argc) {
580 		F = fopen(argv[optind], "r");
581 		if (F == NULL) {
582 			fprintf(stderr, "playmidi1: can't open file %s\n", argv[optind]);
583 			exit(1);
584 		}
585 	} else
586 		F = stdin;
587 
588 	Mf_header = do_header;
589 	Mf_tempo = do_tempo;
590 	Mf_getc = mygetc;
591 	Mf_text = mytext;
592 
593 	Mf_noteon = do_noteon;
594 	Mf_noteoff = do_noteoff;
595 	Mf_program = do_program;
596 	Mf_parameter = do_parameter;
597 	Mf_pitchbend = do_pitchbend;
598 	Mf_pressure = do_pressure;
599 	Mf_chanpressure = do_chanpressure;
600 	Mf_sysex = do_sysex;
601 
602 	/* go.. go.. go.. */
603 	mfread();
604 
605 	alsa_sync();
606 	if (! shared_queue)
607 		alsa_stop_timer();
608 
609 	snd_seq_close(seq_handle);
610 
611 	if (verbose >= VERB_INFO) {
612 		printf("Stopping at %f s,  tick %f\n",
613 		       tick2time_dbl(Mf_currtime + 1), (double) (Mf_currtime + 1));
614 	}
615 
616 	exit(0);
617 }
618