Home | History | Annotate | Download | only in test
      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 (at) inter.nl.net>
      6  *
      7  *   Modified so that this uses alsa-lib
      8  *   1999 Jan. by Isaku Yamahata <yamahata (at) kusm.kyoto-u.ac.jp>
      9  *
     10  *   19990604	Takashi Iwai <iwai (at) ww.uni-erlangen.de>
     11  *	- use blocking mode
     12  *	- fix tempo event bug
     13  *	- add command line options
     14  *
     15  *   19990827	Takashi Iwai <iwai (at) ww.uni-erlangen.de>
     16  *	- use snd_seq_alloc_queue()
     17  *
     18  *   19990916	Takashi Iwai <iwai (at) 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 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 
     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 
     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 
    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 */
    130 static int mygetc(void)
    131 {
    132 	return getc(F);
    133 }
    134 
    135 /* print out the text */
    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 
    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 */
    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 */
    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 */
    229 static void alsa_start_timer(void)
    230 {
    231 	snd_seq_start_queue(seq_handle, dest_queue, NULL);
    232 }
    233 
    234 /* stop the timer */
    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 */
    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 
    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("%ld: 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 
    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("%ld: 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 
    287 static void do_program(int chan, int program)
    288 {
    289 	snd_seq_event_t ev;
    290 
    291 	if (verbose >= VERB_EVENT)
    292 		printf("%ld: 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 
    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("%ld: 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 
    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("%ld: 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 
    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("%ld: 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 
    333 static void do_chanpressure(int chan, int pressure)
    334 {
    335 	snd_seq_event_t ev;
    336 
    337 	if (verbose >= VERB_EVENT)
    338 		printf("%ld: 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 
    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("%ld: 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 
    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 */
    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 */
    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 */
    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 
    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