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