Annotation of src/usr.bin/sndiod/midi.c, Revision 1.31
1.31 ! jsg 1: /* $OpenBSD: midi.c,v 1.30 2024/05/03 05:18:09 ratchov Exp $ */
1.1 ratchov 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:
36: struct midiops port_midiops = {
37: port_imsg,
38: port_omsg,
39: port_fill,
40: port_exit
41: };
42:
43: #define MIDI_NEP 32
44: struct midi midi_ep[MIDI_NEP];
45: struct port *port_list = NULL;
46: unsigned int midi_portnum = 0;
47:
48: struct midithru {
1.2 ratchov 49: unsigned int txmask, rxmask;
1.1 ratchov 50: #define MIDITHRU_NMAX 32
51: } midithru[MIDITHRU_NMAX];
52:
53: /*
54: * length of voice and common messages (status byte included)
55: */
1.26 naddy 56: const unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
57: const unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
1.1 ratchov 58:
59: void
60: midi_log(struct midi *ep)
61: {
62: log_puts("midi");
63: log_putu(ep - midi_ep);
64: }
65:
66: void
67: midi_init(void)
68: {
69: }
70:
71: void
72: midi_done(void)
73: {
74: }
75:
76: struct midi *
77: midi_new(struct midiops *ops, void *arg, int mode)
78: {
79: int i;
80: struct midi *ep;
81:
82: for (i = 0, ep = midi_ep;; i++, ep++) {
83: if (i == MIDI_NEP)
84: return NULL;
85: if (ep->ops == NULL)
86: break;
87: }
88: ep->ops = ops;
89: ep->arg = arg;
90: ep->used = 0;
91: ep->len = 0;
92: ep->idx = 0;
93: ep->st = 0;
1.23 ratchov 94: ep->last_st = 0;
1.1 ratchov 95: ep->txmask = 0;
1.2 ratchov 96: ep->self = 1 << i;
97: ep->tickets = 0;
1.1 ratchov 98: ep->mode = mode;
1.2 ratchov 99:
1.1 ratchov 100: /*
1.16 ratchov 101: * the output buffer is the client input
1.1 ratchov 102: */
1.2 ratchov 103: if (ep->mode & MODE_MIDIIN)
1.1 ratchov 104: abuf_init(&ep->obuf, MIDI_BUFSZ);
1.2 ratchov 105: midi_tickets(ep);
1.1 ratchov 106: return ep;
107: }
108:
109: void
110: midi_del(struct midi *ep)
111: {
112: int i;
1.2 ratchov 113: struct midi *peer;
1.1 ratchov 114:
1.2 ratchov 115: ep->txmask = 0;
116: for (i = 0; i < MIDI_NEP; i++) {
117: peer = midi_ep + i;
118: if (peer->txmask & ep->self) {
119: peer->txmask &= ~ep->self;
120: midi_tickets(peer);
121: }
122: }
123: for (i = 0; i < MIDITHRU_NMAX; i++) {
124: midithru[i].txmask &= ~ep->self;
125: midithru[i].rxmask &= ~ep->self;
126: }
1.1 ratchov 127: ep->ops = NULL;
128: if (ep->mode & MODE_MIDIIN) {
129: abuf_done(&ep->obuf);
130: }
131: }
132:
133: /*
1.2 ratchov 134: * connect two midi endpoints
1.1 ratchov 135: */
136: void
1.2 ratchov 137: midi_link(struct midi *ep, struct midi *peer)
1.1 ratchov 138: {
1.2 ratchov 139: if (ep->mode & MODE_MIDIOUT) {
140: ep->txmask |= peer->self;
141: midi_tickets(ep);
142: }
143: if (ep->mode & MODE_MIDIIN) {
144: #ifdef DEBUG
145: if (ep->obuf.used > 0) {
146: midi_log(ep);
147: log_puts(": linked with non-empty buffer\n");
148: panic();
149: }
150: #endif
1.16 ratchov 151: /* ep has empty buffer, so no need to call midi_tickets() */
1.2 ratchov 152: peer->txmask |= ep->self;
1.1 ratchov 153: }
1.30 ratchov 154: }
155:
156: /*
157: * return the list of endpoints the given one receives from
158: */
159: unsigned int
160: midi_rxmask(struct midi *ep)
161: {
162: int i, rxmask;
163:
164: for (rxmask = 0, i = 0; i < MIDI_NEP; i++) {
165: if ((midi_ep[i].txmask & ep->self) == 0)
166: continue;
167: rxmask |= midi_ep[i].self;
168: }
169:
170: return rxmask;
1.1 ratchov 171: }
172:
173: /*
1.2 ratchov 174: * add the midi endpoint in the ``tag'' midi thru box
1.1 ratchov 175: */
176: void
1.2 ratchov 177: midi_tag(struct midi *ep, unsigned int tag)
1.1 ratchov 178: {
1.2 ratchov 179: struct midi *peer;
180: struct midithru *t = midithru + tag;
1.1 ratchov 181: int i;
182:
1.2 ratchov 183: if (ep->mode & MODE_MIDIOUT) {
184: ep->txmask |= t->txmask;
185: midi_tickets(ep);
186: }
187: if (ep->mode & MODE_MIDIIN) {
188: #ifdef DEBUG
189: if (ep->obuf.used > 0) {
190: midi_log(ep);
191: log_puts(": tagged with non-empty buffer\n");
192: panic();
193: }
194: #endif
195: for (i = 0; i < MIDI_NEP; i++) {
196: if (!(t->rxmask & (1 << i)))
197: continue;
198: peer = midi_ep + i;
199: peer->txmask |= ep->self;
200: }
1.1 ratchov 201: }
1.2 ratchov 202: if (ep->mode & MODE_MIDIOUT)
203: t->rxmask |= ep->self;
204: if (ep->mode & MODE_MIDIIN)
205: t->txmask |= ep->self;
1.24 ratchov 206: }
207:
208: /*
209: * return the list of tags
210: */
211: unsigned int
212: midi_tags(struct midi *ep)
213: {
214: int i;
215: struct midithru *t;
216: unsigned int tags;
217:
218: tags = 0;
219: for (i = 0; i < MIDITHRU_NMAX; i++) {
220: t = midithru + i;
221: if ((t->txmask | t->rxmask) & ep->self)
222: tags |= 1 << i;
223: }
224: return tags;
1.1 ratchov 225: }
226:
227: /*
1.6 ratchov 228: * broadcast the given message to other endpoints
1.1 ratchov 229: */
230: void
231: midi_send(struct midi *iep, unsigned char *msg, int size)
232: {
233: struct midi *oep;
234: int i;
235:
236: #ifdef DEBUG
237: if (log_level >= 4) {
238: midi_log(iep);
239: log_puts(": sending:");
240: for (i = 0; i < size; i++) {
241: log_puts(" ");
242: log_putx(msg[i]);
243: }
244: log_puts("\n");
245: }
246: #endif
247: for (i = 0; i < MIDI_NEP ; i++) {
248: if ((iep->txmask & (1 << i)) == 0)
249: continue;
250: oep = midi_ep + i;
251: if (msg[0] <= 0x7f) {
252: if (oep->owner != iep)
253: continue;
254: } else if (msg[0] <= 0xf7)
255: oep->owner = iep;
256: #ifdef DEBUG
257: if (log_level >= 4) {
258: midi_log(iep);
259: log_puts(" -> ");
260: midi_log(oep);
261: log_puts("\n");
262: }
263: #endif
264: oep->ops->omsg(oep->arg, msg, size);
265: }
266: }
267:
1.2 ratchov 268: /*
269: * determine if we have gained more input tickets, and if so call the
270: * fill() call-back to notify the i/o layer that it can send more data
271: */
272: void
273: midi_tickets(struct midi *iep)
274: {
275: int i, tickets, avail, maxavail;
276: struct midi *oep;
1.17 ratchov 277:
278: /*
279: * don't request iep->ops->fill() too often as it generates
280: * useless network traffic: wait until we reach half of the
281: * max tickets count. As in the worst case (see comment below)
282: * one ticket may consume two bytes, the max ticket count is
283: * BUFSZ / 2 and halt of it is simply BUFSZ / 4.
284: */
285: if (iep->tickets >= MIDI_BUFSZ / 4)
286: return;
1.2 ratchov 287:
288: maxavail = MIDI_BUFSZ;
289: for (i = 0; i < MIDI_NEP ; i++) {
290: if ((iep->txmask & (1 << i)) == 0)
291: continue;
292: oep = midi_ep + i;
293: avail = oep->obuf.len - oep->obuf.used;
294: if (maxavail > avail)
295: maxavail = avail;
296: }
297:
298: /*
1.15 ratchov 299: * in the worst case output message is twice the
1.2 ratchov 300: * input message (2-byte messages with running status)
301: */
302: tickets = maxavail / 2 - iep->tickets;
303: if (tickets > 0) {
304: iep->tickets += tickets;
305: iep->ops->fill(iep->arg, tickets);
306: }
307: }
308:
309: /*
310: * recalculate tickets of endpoints sending data to this one
311: */
1.1 ratchov 312: void
313: midi_fill(struct midi *oep)
314: {
1.2 ratchov 315: int i;
1.1 ratchov 316: struct midi *iep;
317:
1.2 ratchov 318: for (i = 0; i < MIDI_NEP; i++) {
1.1 ratchov 319: iep = midi_ep + i;
1.2 ratchov 320: if (iep->txmask & oep->self)
321: midi_tickets(iep);
1.1 ratchov 322: }
323: }
324:
325: /*
1.2 ratchov 326: * parse then give data chunk, and calling imsg() for each message
1.1 ratchov 327: */
328: void
1.2 ratchov 329: midi_in(struct midi *iep, unsigned char *idata, int icount)
1.1 ratchov 330: {
331: int i;
332: unsigned char c;
333:
334: for (i = 0; i < icount; i++) {
335: c = *idata++;
336: if (c >= 0xf8) {
337: if (c != MIDI_ACK)
338: iep->ops->imsg(iep->arg, &c, 1);
339: } else if (c == SYSEX_END) {
340: if (iep->st == SYSEX_START) {
341: iep->msg[iep->idx++] = c;
342: iep->ops->imsg(iep->arg, iep->msg, iep->idx);
343: }
1.23 ratchov 344:
345: /*
346: * There are bogus MIDI sources that keep
347: * state across sysex; Linux virmidi ports fed
348: * by the sequencer is an example. We
349: * workaround this by saving the current
350: * status and restoring it at the end of the
351: * sysex.
352: */
353: iep->st = iep->last_st;
354: if (iep->st)
355: iep->len = voice_len[(iep->st >> 4) & 7];
1.1 ratchov 356: iep->idx = 0;
357: } else if (c >= 0xf0) {
358: iep->msg[0] = c;
1.19 ratchov 359: iep->len = common_len[c & 7];
1.1 ratchov 360: iep->st = c;
361: iep->idx = 1;
362: } else if (c >= 0x80) {
363: iep->msg[0] = c;
364: iep->len = voice_len[(c >> 4) & 7];
1.23 ratchov 365: iep->last_st = iep->st = c;
1.1 ratchov 366: iep->idx = 1;
367: } else if (iep->st) {
368: if (iep->idx == 0 && iep->st != SYSEX_START)
369: iep->msg[iep->idx++] = iep->st;
370: iep->msg[iep->idx++] = c;
371: if (iep->idx == iep->len) {
372: iep->ops->imsg(iep->arg, iep->msg, iep->idx);
373: if (iep->st >= 0xf0)
374: iep->st = 0;
375: iep->idx = 0;
376: } else if (iep->idx == MIDI_MSGMAX) {
377: /* sysex continued */
378: iep->ops->imsg(iep->arg, iep->msg, iep->idx);
379: iep->idx = 0;
380: }
381: }
382: }
1.2 ratchov 383: iep->tickets -= icount;
384: if (iep->tickets < 0)
385: iep->tickets = 0;
1.7 ratchov 386: midi_tickets(iep);
1.1 ratchov 387: }
388:
389: /*
390: * store the given message in the output buffer
391: */
392: void
1.15 ratchov 393: midi_out(struct midi *oep, unsigned char *idata, int icount)
1.1 ratchov 394: {
395: unsigned char *odata;
396: int ocount;
397: #ifdef DEBUG
398: int i;
399: #endif
1.15 ratchov 400:
1.1 ratchov 401: while (icount > 0) {
402: if (oep->obuf.used == oep->obuf.len) {
403: #ifdef DEBUG
404: if (log_level >= 2) {
405: midi_log(oep);
1.2 ratchov 406: log_puts(": too slow, discarding ");
1.1 ratchov 407: log_putu(oep->obuf.used);
408: log_puts(" bytes\n");
409: }
410: #endif
411: abuf_rdiscard(&oep->obuf, oep->obuf.used);
412: oep->owner = NULL;
413: return;
414: }
415: odata = abuf_wgetblk(&oep->obuf, &ocount);
416: if (ocount > icount)
417: ocount = icount;
418: memcpy(odata, idata, ocount);
419: #ifdef DEBUG
420: if (log_level >= 4) {
421: midi_log(oep);
422: log_puts(": out: ");
423: for (i = 0; i < ocount; i++) {
424: log_puts(" ");
425: log_putx(odata[i]);
426: }
427: log_puts("\n");
428: }
429: #endif
430: abuf_wcommit(&oep->obuf, ocount);
431: icount -= ocount;
432: idata += ocount;
433: }
434: }
435:
1.27 ratchov 436: /*
437: * disconnect clients attached to this end-point
438: */
439: void
440: midi_abort(struct midi *p)
441: {
442: int i;
443: struct midi *ep;
444:
445: for (i = 0; i < MIDI_NEP; i++) {
446: ep = midi_ep + i;
447: if ((ep->txmask & p->self) || (p->txmask & ep->self))
448: ep->ops->exit(ep->arg);
449: }
450: }
451:
1.29 ratchov 452: /*
453: * connect to "nep" all endpoints currently connected to "oep"
454: */
455: void
456: midi_migrate(struct midi *oep, struct midi *nep)
457: {
458: struct midithru *t;
459: struct midi *ep;
460: int i;
461:
462: for (i = 0; i < MIDITHRU_NMAX; i++) {
463: t = midithru + i;
464: if (t->txmask & oep->self) {
465: t->txmask &= ~oep->self;
466: t->txmask |= nep->self;
467: }
468: if (t->rxmask & oep->self) {
469: t->rxmask &= ~oep->self;
470: t->rxmask |= nep->self;
471: }
472: }
473:
474: for (i = 0; i < MIDI_NEP; i++) {
475: ep = midi_ep + i;
476: if (ep->txmask & oep->self) {
477: ep->txmask &= ~oep->self;
478: ep->txmask |= nep->self;
479: }
480: }
481:
482: for (i = 0; i < MIDI_NEP; i++) {
483: ep = midi_ep + i;
484: if (oep->txmask & ep->self) {
485: oep->txmask &= ~ep->self;
486: nep->txmask |= ep->self;
487: }
488: }
489: }
490:
1.1 ratchov 491: void
492: port_log(struct port *p)
493: {
494: midi_log(p->midi);
495: }
496:
497: void
498: port_imsg(void *arg, unsigned char *msg, int size)
499: {
500: struct port *p = arg;
501:
502: midi_send(p->midi, msg, size);
503: }
504:
505:
506: void
507: port_omsg(void *arg, unsigned char *msg, int size)
508: {
509: struct port *p = arg;
510:
511: midi_out(p->midi, msg, size);
512: }
513:
514: void
515: port_fill(void *arg, int count)
516: {
517: /* no flow control */
518: }
519:
520: void
521: port_exit(void *arg)
522: {
523: #ifdef DEBUG
524: struct port *p = arg;
525:
526: if (log_level >= 3) {
527: port_log(p);
1.3 ratchov 528: log_puts(": port exit\n");
529: panic();
1.1 ratchov 530: }
531: #endif
532: }
533:
534: /*
535: * create a new midi port
536: */
537: struct port *
1.4 ratchov 538: port_new(char *path, unsigned int mode, int hold)
1.1 ratchov 539: {
1.29 ratchov 540: struct port *c;
1.1 ratchov 541:
542: c = xmalloc(sizeof(struct port));
1.29 ratchov 543: c->path = path;
1.1 ratchov 544: c->state = PORT_CFG;
1.4 ratchov 545: c->hold = hold;
1.1 ratchov 546: c->midi = midi_new(&port_midiops, c, mode);
1.11 ratchov 547: c->num = midi_portnum++;
1.29 ratchov 548: c->alt_next = c;
549: c->next = port_list;
550: port_list = c;
1.1 ratchov 551: return c;
552: }
553:
554: /*
555: * destroy the given midi port
556: */
557: void
558: port_del(struct port *c)
559: {
560: struct port **p;
561:
562: if (c->state != PORT_CFG)
563: port_close(c);
564: midi_del(c->midi);
565: for (p = &port_list; *p != c; p = &(*p)->next) {
566: #ifdef DEBUG
567: if (*p == NULL) {
568: log_puts("port to delete not on list\n");
569: panic();
570: }
571: #endif
572: }
573: *p = c->next;
574: xfree(c);
575: }
576:
1.3 ratchov 577: int
578: port_ref(struct port *c)
579: {
580: #ifdef DEBUG
581: if (log_level >= 3) {
582: port_log(c);
583: log_puts(": port requested\n");
584: }
585: #endif
586: if (c->state == PORT_CFG && !port_open(c))
587: return 0;
588: return 1;
589: }
590:
591: void
592: port_unref(struct port *c)
593: {
594: int i, rxmask;
595:
596: #ifdef DEBUG
597: if (log_level >= 3) {
598: port_log(c);
599: log_puts(": port released\n");
600: }
601: #endif
602: for (rxmask = 0, i = 0; i < MIDI_NEP; i++)
603: rxmask |= midi_ep[i].txmask;
1.10 ratchov 604: if ((rxmask & c->midi->self) == 0 && c->midi->txmask == 0 &&
605: c->state == PORT_INIT && !c->hold)
1.5 ratchov 606: port_drain(c);
1.3 ratchov 607: }
608:
1.1 ratchov 609: struct port *
1.29 ratchov 610: port_alt_ref(int num)
611: {
612: struct port *a, *p;
613:
614: a = port_bynum(num);
615: if (a == NULL)
616: return NULL;
617:
618: /* circulate to first alt port */
619: while (a->alt_next->num > a->num)
620: a = a->alt_next;
621:
622: p = a;
623: while (1) {
624: if (port_ref(p))
625: break;
626: p = p->alt_next;
627: if (p == a)
628: return NULL;
629: }
630:
631: return p;
632: }
633:
634: struct port *
635: port_migrate(struct port *op)
636: {
637: struct port *np;
638:
639: /* not opened */
640: if (op->state == PORT_CFG)
641: return op;
642:
643: np = op;
644: while (1) {
645: /* try next one, circulating through the list */
646: np = np->alt_next;
647: if (np == op) {
648: if (log_level >= 2) {
649: port_log(op);
650: log_puts(": no fall-back port found\n");
651: }
652: return op;
653: }
654:
655: if (port_ref(np))
656: break;
657: }
658:
659: if (log_level >= 2) {
660: port_log(op);
661: log_puts(": switching to ");
662: port_log(np);
663: log_puts("\n");
664: }
665:
666: midi_migrate(op->midi, np->midi);
667: return np;
668: }
669:
670: struct port *
1.1 ratchov 671: port_bynum(int num)
672: {
673: struct port *p;
674:
675: for (p = port_list; p != NULL; p = p->next) {
1.12 ratchov 676: if (p->num == num)
1.1 ratchov 677: return p;
678: }
679: return NULL;
680: }
681:
682: int
683: port_open(struct port *c)
684: {
685: if (!port_mio_open(c)) {
686: if (log_level >= 1) {
1.21 ratchov 687: port_log(c);
1.1 ratchov 688: log_puts(": failed to open midi port\n");
689: }
690: return 0;
691: }
692: c->state = PORT_INIT;
693: return 1;
1.20 ratchov 694: }
695:
1.1 ratchov 696: int
697: port_close(struct port *c)
698: {
699: #ifdef DEBUG
700: if (c->state == PORT_CFG) {
701: port_log(c);
702: log_puts(": can't close port (not opened)\n");
1.3 ratchov 703: panic();
1.1 ratchov 704: }
705: #endif
1.29 ratchov 706: port_log(c);
707: log_puts(": closed\n");
1.15 ratchov 708: c->state = PORT_CFG;
1.1 ratchov 709: port_mio_close(c);
710: return 1;
711: }
712:
1.5 ratchov 713: void
714: port_drain(struct port *c)
715: {
716: struct midi *ep = c->midi;
717:
718: if (!(ep->mode & MODE_MIDIOUT) || ep->obuf.used == 0)
719: port_close(c);
720: else {
721: c->state = PORT_DRAIN;
722: #ifdef DEBUG
723: if (log_level >= 3) {
724: port_log(c);
725: log_puts(": draining\n");
726: }
727: #endif
728: }
729: }
730:
1.1 ratchov 731: int
732: port_init(struct port *c)
733: {
1.4 ratchov 734: if (c->hold)
735: return port_open(c);
736: return 1;
1.1 ratchov 737: }
738:
739: void
740: port_done(struct port *c)
741: {
1.5 ratchov 742: if (c->state == PORT_INIT)
743: port_drain(c);
1.1 ratchov 744: }