Annotation of src/usr.bin/sndiod/midi.c, Revision 1.29
1.29 ! ratchov 1: /* $OpenBSD: midi.c,v 1.28 2021/03/08 09:42:50 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 *);
1.20 ratchov 35: void port_exitall(struct port *);
1.1 ratchov 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 {
1.2 ratchov 50: unsigned int txmask, rxmask;
1.1 ratchov 51: #define MIDITHRU_NMAX 32
52: } midithru[MIDITHRU_NMAX];
53:
54: /*
55: * length of voice and common messages (status byte included)
56: */
1.26 naddy 57: const unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
58: const unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
1.1 ratchov 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;
1.23 ratchov 95: ep->last_st = 0;
1.1 ratchov 96: ep->txmask = 0;
1.2 ratchov 97: ep->self = 1 << i;
98: ep->tickets = 0;
1.1 ratchov 99: ep->mode = mode;
1.2 ratchov 100:
1.1 ratchov 101: /*
1.16 ratchov 102: * the output buffer is the client input
1.1 ratchov 103: */
1.2 ratchov 104: if (ep->mode & MODE_MIDIIN)
1.1 ratchov 105: abuf_init(&ep->obuf, MIDI_BUFSZ);
1.2 ratchov 106: midi_tickets(ep);
1.1 ratchov 107: return ep;
108: }
109:
110: void
111: midi_del(struct midi *ep)
112: {
113: int i;
1.2 ratchov 114: struct midi *peer;
1.1 ratchov 115:
1.2 ratchov 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: }
1.1 ratchov 128: ep->ops = NULL;
129: if (ep->mode & MODE_MIDIIN) {
130: abuf_done(&ep->obuf);
131: }
132: }
133:
134: /*
1.2 ratchov 135: * connect two midi endpoints
1.1 ratchov 136: */
137: void
1.2 ratchov 138: midi_link(struct midi *ep, struct midi *peer)
1.1 ratchov 139: {
1.2 ratchov 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
1.16 ratchov 152: /* ep has empty buffer, so no need to call midi_tickets() */
1.2 ratchov 153: peer->txmask |= ep->self;
1.1 ratchov 154: }
155: }
156:
157: /*
1.2 ratchov 158: * add the midi endpoint in the ``tag'' midi thru box
1.1 ratchov 159: */
160: void
1.2 ratchov 161: midi_tag(struct midi *ep, unsigned int tag)
1.1 ratchov 162: {
1.2 ratchov 163: struct midi *peer;
164: struct midithru *t = midithru + tag;
1.1 ratchov 165: int i;
166:
1.2 ratchov 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: }
1.1 ratchov 185: }
1.2 ratchov 186: if (ep->mode & MODE_MIDIOUT)
187: t->rxmask |= ep->self;
188: if (ep->mode & MODE_MIDIIN)
189: t->txmask |= ep->self;
1.24 ratchov 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;
1.1 ratchov 209: }
210:
211: /*
1.6 ratchov 212: * broadcast the given message to other endpoints
1.1 ratchov 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:
1.2 ratchov 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;
1.17 ratchov 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;
1.2 ratchov 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: /*
1.15 ratchov 283: * in the worst case output message is twice the
1.2 ratchov 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: */
1.1 ratchov 296: void
297: midi_fill(struct midi *oep)
298: {
1.2 ratchov 299: int i;
1.1 ratchov 300: struct midi *iep;
301:
1.2 ratchov 302: for (i = 0; i < MIDI_NEP; i++) {
1.1 ratchov 303: iep = midi_ep + i;
1.2 ratchov 304: if (iep->txmask & oep->self)
305: midi_tickets(iep);
1.1 ratchov 306: }
307: }
308:
309: /*
1.2 ratchov 310: * parse then give data chunk, and calling imsg() for each message
1.1 ratchov 311: */
312: void
1.2 ratchov 313: midi_in(struct midi *iep, unsigned char *idata, int icount)
1.1 ratchov 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: }
1.23 ratchov 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];
1.1 ratchov 340: iep->idx = 0;
341: } else if (c >= 0xf0) {
342: iep->msg[0] = c;
1.19 ratchov 343: iep->len = common_len[c & 7];
1.1 ratchov 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];
1.23 ratchov 349: iep->last_st = iep->st = c;
1.1 ratchov 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: }
1.2 ratchov 367: iep->tickets -= icount;
368: if (iep->tickets < 0)
369: iep->tickets = 0;
1.7 ratchov 370: midi_tickets(iep);
1.1 ratchov 371: }
372:
373: /*
374: * store the given message in the output buffer
375: */
376: void
1.15 ratchov 377: midi_out(struct midi *oep, unsigned char *idata, int icount)
1.1 ratchov 378: {
379: unsigned char *odata;
380: int ocount;
381: #ifdef DEBUG
382: int i;
383: #endif
1.15 ratchov 384:
1.1 ratchov 385: while (icount > 0) {
386: if (oep->obuf.used == oep->obuf.len) {
387: #ifdef DEBUG
388: if (log_level >= 2) {
389: midi_log(oep);
1.2 ratchov 390: log_puts(": too slow, discarding ");
1.1 ratchov 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:
1.27 ratchov 420: /*
421: * disconnect clients attached to this end-point
422: */
423: void
424: midi_abort(struct midi *p)
425: {
426: int i;
427: struct midi *ep;
428:
429: for (i = 0; i < MIDI_NEP; i++) {
430: ep = midi_ep + i;
431: if ((ep->txmask & p->self) || (p->txmask & ep->self))
432: ep->ops->exit(ep->arg);
433: }
434: }
435:
1.29 ! ratchov 436: /*
! 437: * connect to "nep" all endpoints currently connected to "oep"
! 438: */
! 439: void
! 440: midi_migrate(struct midi *oep, struct midi *nep)
! 441: {
! 442: struct midithru *t;
! 443: struct midi *ep;
! 444: int i;
! 445:
! 446: for (i = 0; i < MIDITHRU_NMAX; i++) {
! 447: t = midithru + i;
! 448: if (t->txmask & oep->self) {
! 449: t->txmask &= ~oep->self;
! 450: t->txmask |= nep->self;
! 451: }
! 452: if (t->rxmask & oep->self) {
! 453: t->rxmask &= ~oep->self;
! 454: t->rxmask |= nep->self;
! 455: }
! 456: }
! 457:
! 458: for (i = 0; i < MIDI_NEP; i++) {
! 459: ep = midi_ep + i;
! 460: if (ep->txmask & oep->self) {
! 461: ep->txmask &= ~oep->self;
! 462: ep->txmask |= nep->self;
! 463: }
! 464: }
! 465:
! 466: for (i = 0; i < MIDI_NEP; i++) {
! 467: ep = midi_ep + i;
! 468: if (oep->txmask & ep->self) {
! 469: oep->txmask &= ~ep->self;
! 470: nep->txmask |= ep->self;
! 471: }
! 472: }
! 473: }
! 474:
1.1 ratchov 475: void
476: port_log(struct port *p)
477: {
478: midi_log(p->midi);
479: }
480:
481: void
482: port_imsg(void *arg, unsigned char *msg, int size)
483: {
484: struct port *p = arg;
485:
486: midi_send(p->midi, msg, size);
487: }
488:
489:
490: void
491: port_omsg(void *arg, unsigned char *msg, int size)
492: {
493: struct port *p = arg;
494:
495: midi_out(p->midi, msg, size);
496: }
497:
498: void
499: port_fill(void *arg, int count)
500: {
501: /* no flow control */
502: }
503:
504: void
505: port_exit(void *arg)
506: {
507: #ifdef DEBUG
508: struct port *p = arg;
509:
510: if (log_level >= 3) {
511: port_log(p);
1.3 ratchov 512: log_puts(": port exit\n");
513: panic();
1.1 ratchov 514: }
515: #endif
516: }
517:
518: /*
519: * create a new midi port
520: */
521: struct port *
1.4 ratchov 522: port_new(char *path, unsigned int mode, int hold)
1.1 ratchov 523: {
1.29 ! ratchov 524: struct port *c;
1.1 ratchov 525:
526: c = xmalloc(sizeof(struct port));
1.29 ! ratchov 527: c->path = path;
1.1 ratchov 528: c->state = PORT_CFG;
1.4 ratchov 529: c->hold = hold;
1.1 ratchov 530: c->midi = midi_new(&port_midiops, c, mode);
1.11 ratchov 531: c->num = midi_portnum++;
1.29 ! ratchov 532: c->alt_next = c;
! 533: c->next = port_list;
! 534: port_list = c;
1.1 ratchov 535: return c;
536: }
537:
538: /*
539: * destroy the given midi port
540: */
541: void
542: port_del(struct port *c)
543: {
544: struct port **p;
545:
546: if (c->state != PORT_CFG)
547: port_close(c);
548: midi_del(c->midi);
549: for (p = &port_list; *p != c; p = &(*p)->next) {
550: #ifdef DEBUG
551: if (*p == NULL) {
552: log_puts("port to delete not on list\n");
553: panic();
554: }
555: #endif
556: }
557: *p = c->next;
558: xfree(c);
559: }
560:
1.3 ratchov 561: int
562: port_ref(struct port *c)
563: {
564: #ifdef DEBUG
565: if (log_level >= 3) {
566: port_log(c);
567: log_puts(": port requested\n");
568: }
569: #endif
570: if (c->state == PORT_CFG && !port_open(c))
571: return 0;
572: return 1;
573: }
574:
575: void
576: port_unref(struct port *c)
577: {
578: int i, rxmask;
579:
580: #ifdef DEBUG
581: if (log_level >= 3) {
582: port_log(c);
583: log_puts(": port released\n");
584: }
585: #endif
586: for (rxmask = 0, i = 0; i < MIDI_NEP; i++)
587: rxmask |= midi_ep[i].txmask;
1.10 ratchov 588: if ((rxmask & c->midi->self) == 0 && c->midi->txmask == 0 &&
589: c->state == PORT_INIT && !c->hold)
1.5 ratchov 590: port_drain(c);
1.3 ratchov 591: }
592:
1.1 ratchov 593: struct port *
1.29 ! ratchov 594: port_alt_ref(int num)
! 595: {
! 596: struct port *a, *p;
! 597:
! 598: a = port_bynum(num);
! 599: if (a == NULL)
! 600: return NULL;
! 601:
! 602: /* circulate to first alt port */
! 603: while (a->alt_next->num > a->num)
! 604: a = a->alt_next;
! 605:
! 606: p = a;
! 607: while (1) {
! 608: if (port_ref(p))
! 609: break;
! 610: p = p->alt_next;
! 611: if (p == a)
! 612: return NULL;
! 613: }
! 614:
! 615: return p;
! 616: }
! 617:
! 618: struct port *
! 619: port_migrate(struct port *op)
! 620: {
! 621: struct port *np;
! 622:
! 623: /* not opened */
! 624: if (op->state == PORT_CFG)
! 625: return op;
! 626:
! 627: np = op;
! 628: while (1) {
! 629: /* try next one, circulating through the list */
! 630: np = np->alt_next;
! 631: if (np == op) {
! 632: if (log_level >= 2) {
! 633: port_log(op);
! 634: log_puts(": no fall-back port found\n");
! 635: }
! 636: return op;
! 637: }
! 638:
! 639: if (port_ref(np))
! 640: break;
! 641: }
! 642:
! 643: if (log_level >= 2) {
! 644: port_log(op);
! 645: log_puts(": switching to ");
! 646: port_log(np);
! 647: log_puts("\n");
! 648: }
! 649:
! 650: midi_migrate(op->midi, np->midi);
! 651: return np;
! 652: }
! 653:
! 654: struct port *
1.1 ratchov 655: port_bynum(int num)
656: {
657: struct port *p;
658:
659: for (p = port_list; p != NULL; p = p->next) {
1.12 ratchov 660: if (p->num == num)
1.1 ratchov 661: return p;
662: }
663: return NULL;
664: }
665:
666: int
667: port_open(struct port *c)
668: {
669: if (!port_mio_open(c)) {
670: if (log_level >= 1) {
1.21 ratchov 671: port_log(c);
1.1 ratchov 672: log_puts(": failed to open midi port\n");
673: }
674: return 0;
675: }
676: c->state = PORT_INIT;
677: return 1;
1.20 ratchov 678: }
679:
1.1 ratchov 680: int
681: port_close(struct port *c)
682: {
683: #ifdef DEBUG
684: if (c->state == PORT_CFG) {
685: port_log(c);
686: log_puts(": can't close port (not opened)\n");
1.3 ratchov 687: panic();
1.1 ratchov 688: }
689: #endif
1.29 ! ratchov 690: port_log(c);
! 691: log_puts(": closed\n");
1.15 ratchov 692: c->state = PORT_CFG;
1.1 ratchov 693: port_mio_close(c);
694: return 1;
695: }
696:
1.5 ratchov 697: void
698: port_drain(struct port *c)
699: {
700: struct midi *ep = c->midi;
701:
702: if (!(ep->mode & MODE_MIDIOUT) || ep->obuf.used == 0)
703: port_close(c);
704: else {
705: c->state = PORT_DRAIN;
706: #ifdef DEBUG
707: if (log_level >= 3) {
708: port_log(c);
709: log_puts(": draining\n");
710: }
711: #endif
712: }
713: }
714:
1.1 ratchov 715: int
716: port_init(struct port *c)
717: {
1.4 ratchov 718: if (c->hold)
719: return port_open(c);
720: return 1;
1.1 ratchov 721: }
722:
723: void
724: port_done(struct port *c)
725: {
1.5 ratchov 726: if (c->state == PORT_INIT)
727: port_drain(c);
1.1 ratchov 728: }