OSDN Git Service

Switch from old to new PCM API retaining binary compatibility
[android-x86/external-alsa-lib.git] / test / playmidi1.c
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., 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 }