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