xref: /openbsd-src/usr.bin/sndiod/midi.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: midi.c,v 1.25 2020/06/12 15:40:18 ratchov Exp $	*/
2 /*
3  * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include "abuf.h"
22 #include "defs.h"
23 #include "dev.h"
24 #include "file.h"
25 #include "midi.h"
26 #include "miofile.h"
27 #include "sysex.h"
28 #include "utils.h"
29 
30 int  port_open(struct port *);
31 void port_imsg(void *, unsigned char *, int);
32 void port_omsg(void *, unsigned char *, int);
33 void port_fill(void *, int);
34 void port_exit(void *);
35 void port_exitall(struct port *);
36 
37 struct midiops port_midiops = {
38 	port_imsg,
39 	port_omsg,
40 	port_fill,
41 	port_exit
42 };
43 
44 #define MIDI_NEP 32
45 struct midi midi_ep[MIDI_NEP];
46 struct port *port_list = NULL;
47 unsigned int midi_portnum = 0;
48 
49 struct midithru {
50 	unsigned int txmask, rxmask;
51 #define MIDITHRU_NMAX 32
52 } midithru[MIDITHRU_NMAX];
53 
54 /*
55  * length of voice and common messages (status byte included)
56  */
57 unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
58 unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
59 
60 void
61 midi_log(struct midi *ep)
62 {
63 	log_puts("midi");
64 	log_putu(ep - midi_ep);
65 }
66 
67 void
68 midi_init(void)
69 {
70 }
71 
72 void
73 midi_done(void)
74 {
75 }
76 
77 struct midi *
78 midi_new(struct midiops *ops, void *arg, int mode)
79 {
80 	int i;
81 	struct midi *ep;
82 
83 	for (i = 0, ep = midi_ep;; i++, ep++) {
84 		if (i == MIDI_NEP)
85 			return NULL;
86 		if (ep->ops == NULL)
87 			break;
88 	}
89 	ep->ops = ops;
90 	ep->arg = arg;
91 	ep->used = 0;
92 	ep->len = 0;
93 	ep->idx = 0;
94 	ep->st = 0;
95 	ep->last_st = 0;
96 	ep->txmask = 0;
97 	ep->self = 1 << i;
98 	ep->tickets = 0;
99 	ep->mode = mode;
100 
101 	/*
102 	 * the output buffer is the client input
103 	 */
104 	if (ep->mode & MODE_MIDIIN)
105 		abuf_init(&ep->obuf, MIDI_BUFSZ);
106 	midi_tickets(ep);
107 	return ep;
108 }
109 
110 void
111 midi_del(struct midi *ep)
112 {
113 	int i;
114 	struct midi *peer;
115 
116 	ep->txmask = 0;
117 	for (i = 0; i < MIDI_NEP; i++) {
118 		peer = midi_ep + i;
119 		if (peer->txmask & ep->self) {
120 			peer->txmask &= ~ep->self;
121 			midi_tickets(peer);
122 		}
123 	}
124 	for (i = 0; i < MIDITHRU_NMAX; i++) {
125 		midithru[i].txmask &= ~ep->self;
126 		midithru[i].rxmask &= ~ep->self;
127 	}
128 	ep->ops = NULL;
129 	if (ep->mode & MODE_MIDIIN) {
130 		abuf_done(&ep->obuf);
131 	}
132 }
133 
134 /*
135  * connect two midi endpoints
136  */
137 void
138 midi_link(struct midi *ep, struct midi *peer)
139 {
140 	if (ep->mode & MODE_MIDIOUT) {
141 		ep->txmask |= peer->self;
142 		midi_tickets(ep);
143 	}
144 	if (ep->mode & MODE_MIDIIN) {
145 #ifdef DEBUG
146 		if (ep->obuf.used > 0) {
147 			midi_log(ep);
148 			log_puts(": linked with non-empty buffer\n");
149 			panic();
150 		}
151 #endif
152 		/* ep has empty buffer, so no need to call midi_tickets() */
153 		peer->txmask |= ep->self;
154 	}
155 }
156 
157 /*
158  * add the midi endpoint in the ``tag'' midi thru box
159  */
160 void
161 midi_tag(struct midi *ep, unsigned int tag)
162 {
163 	struct midi *peer;
164 	struct midithru *t = midithru + tag;
165 	int i;
166 
167 	if (ep->mode & MODE_MIDIOUT) {
168 		ep->txmask |= t->txmask;
169 		midi_tickets(ep);
170 	}
171 	if (ep->mode & MODE_MIDIIN) {
172 #ifdef DEBUG
173 		if (ep->obuf.used > 0) {
174 			midi_log(ep);
175 			log_puts(": tagged with non-empty buffer\n");
176 			panic();
177 		}
178 #endif
179 		for (i = 0; i < MIDI_NEP; i++) {
180 			if (!(t->rxmask & (1 << i)))
181 				continue;
182 			peer = midi_ep + i;
183 			peer->txmask |= ep->self;
184 		}
185 	}
186 	if (ep->mode & MODE_MIDIOUT)
187 		t->rxmask |= ep->self;
188 	if (ep->mode & MODE_MIDIIN)
189 		t->txmask |= ep->self;
190 }
191 
192 /*
193  * return the list of tags
194  */
195 unsigned int
196 midi_tags(struct midi *ep)
197 {
198 	int i;
199 	struct midithru *t;
200 	unsigned int tags;
201 
202 	tags = 0;
203 	for (i = 0; i < MIDITHRU_NMAX; i++) {
204 		t = midithru + i;
205 		if ((t->txmask | t->rxmask) & ep->self)
206 			tags |= 1 << i;
207 	}
208 	return tags;
209 }
210 
211 /*
212  * broadcast the given message to other endpoints
213  */
214 void
215 midi_send(struct midi *iep, unsigned char *msg, int size)
216 {
217 	struct midi *oep;
218 	int i;
219 
220 #ifdef DEBUG
221 	if (log_level >= 4) {
222 		midi_log(iep);
223 		log_puts(": sending:");
224 		for (i = 0; i < size; i++) {
225 			log_puts(" ");
226 			log_putx(msg[i]);
227 		}
228 		log_puts("\n");
229 	}
230 #endif
231 	for (i = 0; i < MIDI_NEP ; i++) {
232 		if ((iep->txmask & (1 << i)) == 0)
233 			continue;
234 		oep = midi_ep + i;
235 		if (msg[0] <= 0x7f) {
236 			if (oep->owner != iep)
237 				continue;
238 		} else if (msg[0] <= 0xf7)
239 			oep->owner = iep;
240 #ifdef DEBUG
241 		if (log_level >= 4) {
242 			midi_log(iep);
243 			log_puts(" -> ");
244 			midi_log(oep);
245 			log_puts("\n");
246 		}
247 #endif
248 		oep->ops->omsg(oep->arg, msg, size);
249 	}
250 }
251 
252 /*
253  * determine if we have gained more input tickets, and if so call the
254  * fill() call-back to notify the i/o layer that it can send more data
255  */
256 void
257 midi_tickets(struct midi *iep)
258 {
259 	int i, tickets, avail, maxavail;
260 	struct midi *oep;
261 
262 	/*
263 	 * don't request iep->ops->fill() too often as it generates
264 	 * useless network traffic: wait until we reach half of the
265 	 * max tickets count. As in the worst case (see comment below)
266 	 * one ticket may consume two bytes, the max ticket count is
267 	 * BUFSZ / 2 and halt of it is simply BUFSZ / 4.
268 	 */
269 	if (iep->tickets >= MIDI_BUFSZ / 4)
270 		return;
271 
272 	maxavail = MIDI_BUFSZ;
273 	for (i = 0; i < MIDI_NEP ; i++) {
274 		if ((iep->txmask & (1 << i)) == 0)
275 			continue;
276 		oep = midi_ep + i;
277 		avail = oep->obuf.len - oep->obuf.used;
278 		if (maxavail > avail)
279 			maxavail = avail;
280 	}
281 
282 	/*
283 	 * in the worst case output message is twice the
284 	 * input message (2-byte messages with running status)
285 	 */
286 	tickets = maxavail / 2 - iep->tickets;
287 	if (tickets > 0) {
288 		iep->tickets += tickets;
289 		iep->ops->fill(iep->arg, tickets);
290 	}
291 }
292 
293 /*
294  * recalculate tickets of endpoints sending data to this one
295  */
296 void
297 midi_fill(struct midi *oep)
298 {
299 	int i;
300 	struct midi *iep;
301 
302 	for (i = 0; i < MIDI_NEP; i++) {
303 		iep = midi_ep + i;
304 		if (iep->txmask & oep->self)
305 			midi_tickets(iep);
306 	}
307 }
308 
309 /*
310  * parse then give data chunk, and calling imsg() for each message
311  */
312 void
313 midi_in(struct midi *iep, unsigned char *idata, int icount)
314 {
315 	int i;
316 	unsigned char c;
317 
318 	for (i = 0; i < icount; i++) {
319 		c = *idata++;
320 		if (c >= 0xf8) {
321 			if (c != MIDI_ACK)
322 				iep->ops->imsg(iep->arg, &c, 1);
323 		} else if (c == SYSEX_END) {
324 			if (iep->st == SYSEX_START) {
325 				iep->msg[iep->idx++] = c;
326 				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
327 			}
328 
329 			/*
330 			 * There are bogus MIDI sources that keep
331 			 * state across sysex; Linux virmidi ports fed
332 			 * by the sequencer is an example. We
333 			 * workaround this by saving the current
334 			 * status and restoring it at the end of the
335 			 * sysex.
336 			 */
337 			iep->st = iep->last_st;
338 			if (iep->st)
339 				iep->len = voice_len[(iep->st >> 4) & 7];
340 			iep->idx = 0;
341 		} else if (c >= 0xf0) {
342 			iep->msg[0] = c;
343 			iep->len = common_len[c & 7];
344 			iep->st = c;
345 			iep->idx = 1;
346 		} else if (c >= 0x80) {
347 			iep->msg[0] = c;
348 			iep->len = voice_len[(c >> 4) & 7];
349 			iep->last_st = iep->st = c;
350 			iep->idx = 1;
351 		} else if (iep->st) {
352 			if (iep->idx == 0 && iep->st != SYSEX_START)
353 				iep->msg[iep->idx++] = iep->st;
354 			iep->msg[iep->idx++] = c;
355 			if (iep->idx == iep->len) {
356 				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
357 				if (iep->st >= 0xf0)
358 					iep->st = 0;
359 				iep->idx = 0;
360 			} else if (iep->idx == MIDI_MSGMAX) {
361 				/* sysex continued */
362 				iep->ops->imsg(iep->arg, iep->msg, iep->idx);
363 				iep->idx = 0;
364 			}
365 		}
366 	}
367 	iep->tickets -= icount;
368 	if (iep->tickets < 0)
369 		iep->tickets = 0;
370 	midi_tickets(iep);
371 }
372 
373 /*
374  * store the given message in the output buffer
375  */
376 void
377 midi_out(struct midi *oep, unsigned char *idata, int icount)
378 {
379 	unsigned char *odata;
380 	int ocount;
381 #ifdef DEBUG
382 	int i;
383 #endif
384 
385 	while (icount > 0) {
386 		if (oep->obuf.used == oep->obuf.len) {
387 #ifdef DEBUG
388 			if (log_level >= 2) {
389 				midi_log(oep);
390 				log_puts(": too slow, discarding ");
391 				log_putu(oep->obuf.used);
392 				log_puts(" bytes\n");
393 			}
394 #endif
395 			abuf_rdiscard(&oep->obuf, oep->obuf.used);
396 			oep->owner = NULL;
397 			return;
398 		}
399 		odata = abuf_wgetblk(&oep->obuf, &ocount);
400 		if (ocount > icount)
401 			ocount = icount;
402 		memcpy(odata, idata, ocount);
403 #ifdef DEBUG
404 		if (log_level >= 4) {
405 			midi_log(oep);
406 			log_puts(": out: ");
407 			for (i = 0; i < ocount; i++) {
408 				log_puts(" ");
409 				log_putx(odata[i]);
410 			}
411 			log_puts("\n");
412 		}
413 #endif
414 		abuf_wcommit(&oep->obuf, ocount);
415 		icount -= ocount;
416 		idata += ocount;
417 	}
418 }
419 
420 void
421 port_log(struct port *p)
422 {
423 	midi_log(p->midi);
424 }
425 
426 void
427 port_imsg(void *arg, unsigned char *msg, int size)
428 {
429 	struct port *p = arg;
430 
431 	midi_send(p->midi, msg, size);
432 }
433 
434 
435 void
436 port_omsg(void *arg, unsigned char *msg, int size)
437 {
438 	struct port *p = arg;
439 
440 	midi_out(p->midi, msg, size);
441 }
442 
443 void
444 port_fill(void *arg, int count)
445 {
446 	/* no flow control */
447 }
448 
449 void
450 port_exit(void *arg)
451 {
452 #ifdef DEBUG
453 	struct port *p = arg;
454 
455 	if (log_level >= 3) {
456 		port_log(p);
457 		log_puts(": port exit\n");
458 		panic();
459 	}
460 #endif
461 }
462 
463 /*
464  * create a new midi port
465  */
466 struct port *
467 port_new(char *path, unsigned int mode, int hold)
468 {
469 	struct port *c;
470 
471 	c = xmalloc(sizeof(struct port));
472 	c->path_list = NULL;
473 	namelist_add(&c->path_list, path);
474 	c->state = PORT_CFG;
475 	c->hold = hold;
476 	c->midi = midi_new(&port_midiops, c, mode);
477 	c->num = midi_portnum++;
478 	c->next = port_list;
479 	port_list = c;
480 	return c;
481 }
482 
483 /*
484  * destroy the given midi port
485  */
486 void
487 port_del(struct port *c)
488 {
489 	struct port **p;
490 
491 	if (c->state != PORT_CFG)
492 		port_close(c);
493 	midi_del(c->midi);
494 	for (p = &port_list; *p != c; p = &(*p)->next) {
495 #ifdef DEBUG
496 		if (*p == NULL) {
497 			log_puts("port to delete not on list\n");
498 			panic();
499 		}
500 #endif
501 	}
502 	*p = c->next;
503 	namelist_clear(&c->path_list);
504 	xfree(c);
505 }
506 
507 int
508 port_ref(struct port *c)
509 {
510 #ifdef DEBUG
511 	if (log_level >= 3) {
512 		port_log(c);
513 		log_puts(": port requested\n");
514 	}
515 #endif
516 	if (c->state == PORT_CFG && !port_open(c))
517 		return 0;
518 	return 1;
519 }
520 
521 void
522 port_unref(struct port *c)
523 {
524 	int i, rxmask;
525 
526 #ifdef DEBUG
527 	if (log_level >= 3) {
528 		port_log(c);
529 		log_puts(": port released\n");
530 	}
531 #endif
532 	for (rxmask = 0, i = 0; i < MIDI_NEP; i++)
533 		rxmask |= midi_ep[i].txmask;
534 	if ((rxmask & c->midi->self) == 0 && c->midi->txmask == 0 &&
535 	    c->state == PORT_INIT && !c->hold)
536 		port_drain(c);
537 }
538 
539 struct port *
540 port_bynum(int num)
541 {
542 	struct port *p;
543 
544 	for (p = port_list; p != NULL; p = p->next) {
545 		if (p->num == num)
546 			return p;
547 	}
548 	return NULL;
549 }
550 
551 int
552 port_open(struct port *c)
553 {
554 	if (!port_mio_open(c)) {
555 		if (log_level >= 1) {
556 			port_log(c);
557 			log_puts(": failed to open midi port\n");
558 		}
559 		return 0;
560 	}
561 	c->state = PORT_INIT;
562 	return 1;
563 }
564 
565 void
566 port_abort(struct port *c)
567 {
568 	int i;
569 	struct midi *ep;
570 
571 	for (i = 0; i < MIDI_NEP; i++) {
572 		ep = midi_ep + i;
573 		if ((ep->txmask & c->midi->self) ||
574 		    (c->midi->txmask & ep->self))
575 			ep->ops->exit(ep->arg);
576 	}
577 
578 	if (c->state != PORT_CFG)
579 		port_close(c);
580 }
581 
582 int
583 port_close(struct port *c)
584 {
585 #ifdef DEBUG
586 	if (c->state == PORT_CFG) {
587 		port_log(c);
588 		log_puts(": can't close port (not opened)\n");
589 		panic();
590 	}
591 #endif
592 	c->state = PORT_CFG;
593 	port_mio_close(c);
594 	return 1;
595 }
596 
597 void
598 port_drain(struct port *c)
599 {
600 	struct midi *ep = c->midi;
601 
602 	if (!(ep->mode & MODE_MIDIOUT) || ep->obuf.used == 0)
603 		port_close(c);
604 	else {
605 		c->state = PORT_DRAIN;
606 #ifdef DEBUG
607 		if (log_level >= 3) {
608 			port_log(c);
609 			log_puts(": draining\n");
610 		}
611 #endif
612 	}
613 }
614 
615 int
616 port_init(struct port *c)
617 {
618 	if (c->hold)
619 		return port_open(c);
620 	return 1;
621 }
622 
623 void
624 port_done(struct port *c)
625 {
626 	if (c->state == PORT_INIT)
627 		port_drain(c);
628 }
629 
630 int
631 port_reopen(struct port *p)
632 {
633 	if (p->state == PORT_CFG)
634 		return 1;
635 
636 	if (!port_mio_reopen(p))
637 		return 0;
638 
639 	return 1;
640 }
641