1 /*
2  *  Sequencer Interface - middle-level routines
3  *
4  *  Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
5  *
6  *
7  *   This library is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU Lesser General Public License as
9  *   published by the Free Software Foundation; either version 2.1 of
10  *   the License, or (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU Lesser General Public License for more details.
16  *
17  *   You should have received a copy of the GNU Lesser General Public
18  *   License along with this library; if not, write to the Free Software
19  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22 
23 #include "seq_local.h"
24 #include <unistd.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <sys/ioctl.h>
29 
30 /**
31  * \brief queue controls - start/stop/continue
32  * \param seq sequencer handle
33  * \param q queue id to control
34  * \param type event type
35  * \param value event value
36  * \param ev event instance
37  *
38  * This function sets up general queue control event and sends it.
39  * To send at scheduled time, set the schedule in \a ev.
40  * If \a ev is NULL, the event is composed locally and sent immediately
41  * to the specified queue.  In any cases, you need to call #snd_seq_drain_output()
42  * appropriately to feed the event.
43  *
44  * \sa snd_seq_alloc_queue()
45  */
snd_seq_control_queue(snd_seq_t *seq, int q, int type, int value, snd_seq_event_t *ev)46 int snd_seq_control_queue(snd_seq_t *seq, int q, int type, int value, snd_seq_event_t *ev)
47 {
48 	snd_seq_event_t tmpev;
49 	if (ev == NULL) {
50 		snd_seq_ev_clear(&tmpev);
51 		ev = &tmpev;
52 		snd_seq_ev_set_direct(ev);
53 	}
54 	snd_seq_ev_set_queue_control(ev, type, q, value);
55 	return snd_seq_event_output(seq, ev);
56 }
57 
58 
59 /**
60  * \brief create a port - simple version
61  * \param seq sequencer handle
62  * \param name the name of the port
63  * \param caps capability bits
64  * \param type type bits
65  * \return the created port number or negative error code
66  *
67  * Creates a port with the given capability and type bits.
68  *
69  * \sa snd_seq_create_port(), snd_seq_delete_simple_port()
70  */
snd_seq_create_simple_port(snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)71 int snd_seq_create_simple_port(snd_seq_t *seq, const char *name,
72 			       unsigned int caps, unsigned int type)
73 {
74 	snd_seq_port_info_t pinfo;
75 	int result;
76 
77 	memset(&pinfo, 0, sizeof(pinfo));
78 	if (name)
79 		strncpy(pinfo.name, name, sizeof(pinfo.name) - 1);
80 	pinfo.capability = caps;
81 	pinfo.type = type;
82 	pinfo.midi_channels = 16;
83 	pinfo.midi_voices = 64; /* XXX */
84 	pinfo.synth_voices = 0; /* XXX */
85 
86 	result = snd_seq_create_port(seq, &pinfo);
87 	if (result < 0)
88 		return result;
89 	else
90 		return pinfo.addr.port;
91 }
92 
93 /**
94  * \brief delete the port
95  * \param seq sequencer handle
96  * \param port port id
97  * \return 0 on success or negative error code
98  *
99  * \sa snd_seq_delete_port(), snd_seq_create_simple_port()
100  */
snd_seq_delete_simple_port(snd_seq_t *seq, int port)101 int snd_seq_delete_simple_port(snd_seq_t *seq, int port)
102 {
103 	return snd_seq_delete_port(seq, port);
104 }
105 
106 /**
107  * \brief simple subscription (w/o exclusive & time conversion)
108  * \param seq sequencer handle
109  * \param myport the port id as receiver
110  * \param src_client sender client id
111  * \param src_port sender port id
112  * \return 0 on success or negative error code
113  *
114  * Connect from the given sender client:port to the given destination port in the
115  * current client.
116  *
117  * \sa snd_seq_subscribe_port(), snd_seq_disconnect_from()
118  */
snd_seq_connect_from(snd_seq_t *seq, int myport, int src_client, int src_port)119 int snd_seq_connect_from(snd_seq_t *seq, int myport, int src_client, int src_port)
120 {
121 	snd_seq_port_subscribe_t subs;
122 
123 	memset(&subs, 0, sizeof(subs));
124 	subs.sender.client = src_client;
125 	subs.sender.port = src_port;
126 	/*subs.dest.client = seq->client;*/
127 	subs.dest.client = snd_seq_client_id(seq);
128 	subs.dest.port = myport;
129 
130 	return snd_seq_subscribe_port(seq, &subs);
131 }
132 
133 /**
134  * \brief simple subscription (w/o exclusive & time conversion)
135  * \param seq sequencer handle
136  * \param myport the port id as sender
137  * \param dest_client destination client id
138  * \param dest_port destination port id
139  * \return 0 on success or negative error code
140  *
141  * Connect from the given receiver port in the current client
142  * to the given destination client:port.
143  *
144  * \sa snd_seq_subscribe_port(), snd_seq_disconnect_to()
145  */
snd_seq_connect_to(snd_seq_t *seq, int myport, int dest_client, int dest_port)146 int snd_seq_connect_to(snd_seq_t *seq, int myport, int dest_client, int dest_port)
147 {
148 	snd_seq_port_subscribe_t subs;
149 
150 	memset(&subs, 0, sizeof(subs));
151 	/*subs.sender.client = seq->client;*/
152 	subs.sender.client = snd_seq_client_id(seq);
153 	subs.sender.port = myport;
154 	subs.dest.client = dest_client;
155 	subs.dest.port = dest_port;
156 
157 	return snd_seq_subscribe_port(seq, &subs);
158 }
159 
160 /**
161  * \brief simple disconnection
162  * \param seq sequencer handle
163  * \param myport the port id as receiver
164  * \param src_client sender client id
165  * \param src_port sender port id
166  * \return 0 on success or negative error code
167  *
168  * Remove connection from the given sender client:port
169  * to the given destination port in the current client.
170  *
171  * \sa snd_seq_unsubscribe_port(), snd_seq_connect_from()
172  */
snd_seq_disconnect_from(snd_seq_t *seq, int myport, int src_client, int src_port)173 int snd_seq_disconnect_from(snd_seq_t *seq, int myport, int src_client, int src_port)
174 {
175 	snd_seq_port_subscribe_t subs;
176 
177 	memset(&subs, 0, sizeof(subs));
178 	subs.sender.client = src_client;
179 	subs.sender.port = src_port;
180 	/*subs.dest.client = seq->client;*/
181 	subs.dest.client = snd_seq_client_id(seq);
182 	subs.dest.port = myport;
183 
184 	return snd_seq_unsubscribe_port(seq, &subs);
185 }
186 
187 /**
188  * \brief simple disconnection
189  * \param seq sequencer handle
190  * \param myport the port id as sender
191  * \param dest_client destination client id
192  * \param dest_port destination port id
193  * \return 0 on success or negative error code
194  *
195  * Remove connection from the given sender client:port
196  * to the given destination port in the current client.
197  *
198  * \sa snd_seq_unsubscribe_port(), snd_seq_connect_to()
199  */
snd_seq_disconnect_to(snd_seq_t *seq, int myport, int dest_client, int dest_port)200 int snd_seq_disconnect_to(snd_seq_t *seq, int myport, int dest_client, int dest_port)
201 {
202 	snd_seq_port_subscribe_t subs;
203 
204 	memset(&subs, 0, sizeof(subs));
205 	/*subs.sender.client = seq->client;*/
206 	subs.sender.client = snd_seq_client_id(seq);
207 	subs.sender.port = myport;
208 	subs.dest.client = dest_client;
209 	subs.dest.port = dest_port;
210 
211 	return snd_seq_unsubscribe_port(seq, &subs);
212 }
213 
214 /*
215  * set client information
216  */
217 
218 /**
219  * \brief set client name
220  * \param seq sequencer handle
221  * \param name name string
222  * \return 0 on success or negative error code
223  *
224  * \sa snd_seq_set_client_info()
225  */
snd_seq_set_client_name(snd_seq_t *seq, const char *name)226 int snd_seq_set_client_name(snd_seq_t *seq, const char *name)
227 {
228 	snd_seq_client_info_t info;
229 	int err;
230 
231 	if ((err = snd_seq_get_client_info(seq, &info)) < 0)
232 		return err;
233 	strncpy(info.name, name, sizeof(info.name) - 1);
234 	return snd_seq_set_client_info(seq, &info);
235 }
236 
237 /**
238  * \brief add client event filter
239  * \param seq sequencer handle
240  * \param event_type event type to be added
241  * \return 0 on success or negative error code
242  *
243  * \sa snd_seq_set_client_info()
244  */
snd_seq_set_client_event_filter(snd_seq_t *seq, int event_type)245 int snd_seq_set_client_event_filter(snd_seq_t *seq, int event_type)
246 {
247 	snd_seq_client_info_t info;
248 	int err;
249 
250 	if ((err = snd_seq_get_client_info(seq, &info)) < 0)
251 		return err;
252 	snd_seq_client_info_event_filter_add(&info, event_type);
253 	return snd_seq_set_client_info(seq, &info);
254 }
255 
256 /**
257  * \brief set client MIDI protocol version
258  * \param seq sequencer handle
259  * \param midi_version MIDI protocol version to set
260  * \return 0 on success or negative error code
261  *
262  * \sa snd_seq_set_client_info()
263  */
snd_seq_set_client_midi_version(snd_seq_t *seq, int midi_version)264 int snd_seq_set_client_midi_version(snd_seq_t *seq, int midi_version)
265 {
266 	snd_seq_client_info_t info;
267 	int err;
268 
269 	if ((err = snd_seq_get_client_info(seq, &info)) < 0)
270 		return err;
271 	snd_seq_client_info_set_midi_version(&info, midi_version);
272 	return snd_seq_set_client_info(seq, &info);
273 }
274 
275 /**
276  * \brief enable/disable client's automatic conversion of UMP/legacy events
277  * \param seq sequencer handle
278  * \param enable 0 or 1 to disable/enable the conversion
279  * \return 0 on success or negative error code
280  *
281  * \sa snd_seq_set_client_info()
282  */
snd_seq_set_client_ump_conversion(snd_seq_t *seq, int enable)283 int snd_seq_set_client_ump_conversion(snd_seq_t *seq, int enable)
284 {
285 	snd_seq_client_info_t info;
286 	int err;
287 
288 	if ((err = snd_seq_get_client_info(seq, &info)) < 0)
289 		return err;
290 	snd_seq_client_info_set_ump_conversion(&info, enable);
291 	return snd_seq_set_client_info(seq, &info);
292 }
293 
294 /**
295  * \brief change the output pool size of the given client
296  * \param seq sequencer handle
297  * \param size output pool size
298  * \return 0 on success or negative error code
299  *
300  * \sa snd_seq_set_client_pool()
301  */
snd_seq_set_client_pool_output(snd_seq_t *seq, size_t size)302 int snd_seq_set_client_pool_output(snd_seq_t *seq, size_t size)
303 {
304 	snd_seq_client_pool_t info;
305 	int err;
306 
307 	if ((err = snd_seq_get_client_pool(seq, &info)) < 0)
308 		return err;
309 	info.output_pool = size;
310 	return snd_seq_set_client_pool(seq, &info);
311 }
312 
313 /**
314  * \brief change the output room size of the given client
315  * \param seq sequencer handle
316  * \param size output room size
317  * \return 0 on success or negative error code
318  *
319  * \sa snd_seq_set_client_pool()
320  */
snd_seq_set_client_pool_output_room(snd_seq_t *seq, size_t size)321 int snd_seq_set_client_pool_output_room(snd_seq_t *seq, size_t size)
322 {
323 	snd_seq_client_pool_t info;
324 	int err;
325 
326 	if ((err = snd_seq_get_client_pool(seq, &info)) < 0)
327 		return err;
328 	info.output_room = size;
329 	return snd_seq_set_client_pool(seq, &info);
330 }
331 
332 /**
333  * \brief change the input pool size of the given client
334  * \param seq sequencer handle
335  * \param size input pool size
336  * \return 0 on success or negative error code
337  *
338  * \sa snd_seq_set_client_pool()
339  */
snd_seq_set_client_pool_input(snd_seq_t *seq, size_t size)340 int snd_seq_set_client_pool_input(snd_seq_t *seq, size_t size)
341 {
342 	snd_seq_client_pool_t info;
343 	int err;
344 
345 	if ((err = snd_seq_get_client_pool(seq, &info)) < 0)
346 		return err;
347 	info.input_pool = size;
348 	return snd_seq_set_client_pool(seq, &info);
349 }
350 
351 /**
352  * \brief reset client output pool
353  * \param seq sequencer handle
354  * \return 0 on success or negative error code
355  *
356  * So far, this works identically like #snd_seq_drop_output().
357  */
snd_seq_reset_pool_output(snd_seq_t *seq)358 int snd_seq_reset_pool_output(snd_seq_t *seq)
359 {
360 	return snd_seq_drop_output(seq);
361 }
362 
363 /**
364  * \brief reset client input pool
365  * \param seq sequencer handle
366  * \return 0 on success or negative error code
367  *
368  * So far, this works identically like #snd_seq_drop_input().
369  */
snd_seq_reset_pool_input(snd_seq_t *seq)370 int snd_seq_reset_pool_input(snd_seq_t *seq)
371 {
372 	return snd_seq_drop_input(seq);
373 }
374 
375 /**
376  * \brief wait until all events are processed
377  * \param seq sequencer handle
378  * \return 0 on success or negative error code
379  *
380  * This function waits until all events of this client are processed.
381  *
382  * \sa snd_seq_drain_output()
383  */
snd_seq_sync_output_queue(snd_seq_t *seq)384 int snd_seq_sync_output_queue(snd_seq_t *seq)
385 {
386 	int err;
387 	snd_seq_client_pool_t info;
388 	int saved_room;
389 	struct pollfd pfd;
390 
391 	assert(seq);
392 	/* reprogram the room size to full */
393 	if ((err = snd_seq_get_client_pool(seq, &info)) < 0)
394 		return err;
395 	saved_room = info.output_room;
396 	info.output_room = info.output_pool; /* wait until all gone */
397 	if ((err = snd_seq_set_client_pool(seq, &info)) < 0)
398 		return err;
399 	/* wait until all events are purged */
400 	pfd.fd = seq->poll_fd;
401 	pfd.events = POLLOUT;
402 	err = poll(&pfd, 1, -1);
403 	/* restore the room size */
404 	info.output_room = saved_room;
405 	snd_seq_set_client_pool(seq, &info);
406 	return err;
407 }
408 
409 /**
410  * \brief parse the given string and get the sequencer address
411  * \param seq sequencer handle
412  * \param addr the address pointer to be returned
413  * \param arg the string to be parsed
414  * \return 0 on success or negative error code
415  *
416  * This function parses the sequencer client and port numbers from the given string.
417  * The client and port tokens are separated by either colon or period, e.g. 128:1.
418  * When \a seq is not NULL, the function accepts also a client name not only
419  * digit numbers.
420  * Actually \a arg need to be only a prefix of the wanted client.
421  * That is, if a client named "Foobar XXL Master 2012" with number 128 is available,
422  * then parsing "Foobar" will return the address 128:0 if no other client is
423  * an exact match.
424  */
snd_seq_parse_address(snd_seq_t *seq, snd_seq_addr_t *addr, const char *arg)425 int snd_seq_parse_address(snd_seq_t *seq, snd_seq_addr_t *addr, const char *arg)
426 {
427 	char *p, *buf;
428 	const char *s;
429 	char c;
430 	long client, port = 0;
431 	int len;
432 
433 	assert(addr && arg);
434 
435 	c = *arg;
436 	if (c == '"' || c == '\'') {
437 		s = ++arg;
438 		while (*s && *s != c) s++;
439 		len = s - arg;
440 		if (*s)
441 			s++;
442 		if (*s) {
443 			if (*s != '.' && *s != ':')
444 				return -EINVAL;
445 			if ((port = atoi(s + 1)) < 0)
446 				return -EINVAL;
447 		}
448 	} else {
449 		if ((p = strpbrk(arg, ":.")) != NULL) {
450 			if ((port = atoi(p + 1)) < 0)
451 				return -EINVAL;
452 			len = (int)(p - arg); /* length of client name */
453 		} else {
454 			len = strlen(arg);
455 		}
456 	}
457 	if (len == 0)
458 		return -EINVAL;
459 	buf = alloca(len + 1);
460 	strncpy(buf, arg, len);
461 	buf[len] = '\0';
462 	addr->port = port;
463 	if (safe_strtol(buf, &client) == 0) {
464 		addr->client = client;
465 	} else {
466 		/* convert from the name */
467 		snd_seq_client_info_t cinfo;
468 
469 		if (! seq)
470 			return -EINVAL;
471 		if (len <= 0)
472 			return -EINVAL;
473 		client = -1;
474 		cinfo.client = -1;
475 		while (snd_seq_query_next_client(seq, &cinfo) >= 0) {
476 			if (!strncmp(arg, cinfo.name, len)) {
477 				if (strlen(cinfo.name) == (size_t)len) {
478 					/* exact match */
479 					addr->client = cinfo.client;
480 					return 0;
481 				}
482 				if (client < 0)
483 					client = cinfo.client;
484 			}
485 		}
486 		if (client >= 0) {
487 			/* prefix match */
488 			addr->client = client;
489 			return 0;
490 		}
491 		return -ENOENT; /* not found */
492 	}
493 	return 0;
494 }
495 
496