Annotation of src/usr.bin/sndiod/midi.c, Revision 1.22
1.22 ! ratchov 1: /* $OpenBSD: midi.c,v 1.21 2019/08/29 07:19:15 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: */
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->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: }
154: }
155:
156: /*
1.2 ratchov 157: * add the midi endpoint in the ``tag'' midi thru box
1.1 ratchov 158: */
159: void
1.2 ratchov 160: midi_tag(struct midi *ep, unsigned int tag)
1.1 ratchov 161: {
1.2 ratchov 162: struct midi *peer;
163: struct midithru *t = midithru + tag;
1.1 ratchov 164: int i;
165:
1.2 ratchov 166: if (ep->mode & MODE_MIDIOUT) {
167: ep->txmask |= t->txmask;
168: midi_tickets(ep);
169: }
170: if (ep->mode & MODE_MIDIIN) {
171: #ifdef DEBUG
172: if (ep->obuf.used > 0) {
173: midi_log(ep);
174: log_puts(": tagged with non-empty buffer\n");
175: panic();
176: }
177: #endif
178: for (i = 0; i < MIDI_NEP; i++) {
179: if (!(t->rxmask & (1 << i)))
180: continue;
181: peer = midi_ep + i;
182: peer->txmask |= ep->self;
183: }
1.1 ratchov 184: }
1.2 ratchov 185: if (ep->mode & MODE_MIDIOUT)
186: t->rxmask |= ep->self;
187: if (ep->mode & MODE_MIDIIN)
188: t->txmask |= ep->self;
1.1 ratchov 189: }
190:
191: /*
1.6 ratchov 192: * broadcast the given message to other endpoints
1.1 ratchov 193: */
194: void
195: midi_send(struct midi *iep, unsigned char *msg, int size)
196: {
197: struct midi *oep;
198: int i;
199:
200: #ifdef DEBUG
201: if (log_level >= 4) {
202: midi_log(iep);
203: log_puts(": sending:");
204: for (i = 0; i < size; i++) {
205: log_puts(" ");
206: log_putx(msg[i]);
207: }
208: log_puts("\n");
209: }
210: #endif
211: for (i = 0; i < MIDI_NEP ; i++) {
212: if ((iep->txmask & (1 << i)) == 0)
213: continue;
214: oep = midi_ep + i;
215: if (msg[0] <= 0x7f) {
216: if (oep->owner != iep)
217: continue;
218: } else if (msg[0] <= 0xf7)
219: oep->owner = iep;
220: #ifdef DEBUG
221: if (log_level >= 4) {
222: midi_log(iep);
223: log_puts(" -> ");
224: midi_log(oep);
225: log_puts("\n");
226: }
227: #endif
228: oep->ops->omsg(oep->arg, msg, size);
229: }
230: }
231:
1.2 ratchov 232: /*
233: * determine if we have gained more input tickets, and if so call the
234: * fill() call-back to notify the i/o layer that it can send more data
235: */
236: void
237: midi_tickets(struct midi *iep)
238: {
239: int i, tickets, avail, maxavail;
240: struct midi *oep;
1.17 ratchov 241:
242: /*
243: * don't request iep->ops->fill() too often as it generates
244: * useless network traffic: wait until we reach half of the
245: * max tickets count. As in the worst case (see comment below)
246: * one ticket may consume two bytes, the max ticket count is
247: * BUFSZ / 2 and halt of it is simply BUFSZ / 4.
248: */
249: if (iep->tickets >= MIDI_BUFSZ / 4)
250: return;
1.2 ratchov 251:
252: maxavail = MIDI_BUFSZ;
253: for (i = 0; i < MIDI_NEP ; i++) {
254: if ((iep->txmask & (1 << i)) == 0)
255: continue;
256: oep = midi_ep + i;
257: avail = oep->obuf.len - oep->obuf.used;
258: if (maxavail > avail)
259: maxavail = avail;
260: }
261:
262: /*
1.15 ratchov 263: * in the worst case output message is twice the
1.2 ratchov 264: * input message (2-byte messages with running status)
265: */
266: tickets = maxavail / 2 - iep->tickets;
267: if (tickets > 0) {
268: iep->tickets += tickets;
269: iep->ops->fill(iep->arg, tickets);
270: }
271: }
272:
273: /*
274: * recalculate tickets of endpoints sending data to this one
275: */
1.1 ratchov 276: void
277: midi_fill(struct midi *oep)
278: {
1.2 ratchov 279: int i;
1.1 ratchov 280: struct midi *iep;
281:
1.2 ratchov 282: for (i = 0; i < MIDI_NEP; i++) {
1.1 ratchov 283: iep = midi_ep + i;
1.2 ratchov 284: if (iep->txmask & oep->self)
285: midi_tickets(iep);
1.1 ratchov 286: }
287: }
288:
289: /*
1.2 ratchov 290: * parse then give data chunk, and calling imsg() for each message
1.1 ratchov 291: */
292: void
1.2 ratchov 293: midi_in(struct midi *iep, unsigned char *idata, int icount)
1.1 ratchov 294: {
295: int i;
296: unsigned char c;
297:
298: for (i = 0; i < icount; i++) {
299: c = *idata++;
300: if (c >= 0xf8) {
301: if (c != MIDI_ACK)
302: iep->ops->imsg(iep->arg, &c, 1);
303: } else if (c == SYSEX_END) {
304: if (iep->st == SYSEX_START) {
305: iep->msg[iep->idx++] = c;
306: iep->ops->imsg(iep->arg, iep->msg, iep->idx);
307: }
308: iep->st = 0;
309: iep->idx = 0;
310: } else if (c >= 0xf0) {
311: iep->msg[0] = c;
1.19 ratchov 312: iep->len = common_len[c & 7];
1.1 ratchov 313: iep->st = c;
314: iep->idx = 1;
315: } else if (c >= 0x80) {
316: iep->msg[0] = c;
317: iep->len = voice_len[(c >> 4) & 7];
318: iep->st = c;
319: iep->idx = 1;
320: } else if (iep->st) {
321: if (iep->idx == 0 && iep->st != SYSEX_START)
322: iep->msg[iep->idx++] = iep->st;
323: iep->msg[iep->idx++] = c;
324: if (iep->idx == iep->len) {
325: iep->ops->imsg(iep->arg, iep->msg, iep->idx);
326: if (iep->st >= 0xf0)
327: iep->st = 0;
328: iep->idx = 0;
329: } else if (iep->idx == MIDI_MSGMAX) {
330: /* sysex continued */
331: iep->ops->imsg(iep->arg, iep->msg, iep->idx);
332: iep->idx = 0;
333: }
334: }
335: }
1.2 ratchov 336: iep->tickets -= icount;
337: if (iep->tickets < 0)
338: iep->tickets = 0;
1.7 ratchov 339: midi_tickets(iep);
1.1 ratchov 340: }
341:
342: /*
343: * store the given message in the output buffer
344: */
345: void
1.15 ratchov 346: midi_out(struct midi *oep, unsigned char *idata, int icount)
1.1 ratchov 347: {
348: unsigned char *odata;
349: int ocount;
350: #ifdef DEBUG
351: int i;
352: #endif
1.15 ratchov 353:
1.1 ratchov 354: while (icount > 0) {
355: if (oep->obuf.used == oep->obuf.len) {
356: #ifdef DEBUG
357: if (log_level >= 2) {
358: midi_log(oep);
1.2 ratchov 359: log_puts(": too slow, discarding ");
1.1 ratchov 360: log_putu(oep->obuf.used);
361: log_puts(" bytes\n");
362: }
363: #endif
364: abuf_rdiscard(&oep->obuf, oep->obuf.used);
365: oep->owner = NULL;
366: return;
367: }
368: odata = abuf_wgetblk(&oep->obuf, &ocount);
369: if (ocount > icount)
370: ocount = icount;
371: memcpy(odata, idata, ocount);
372: #ifdef DEBUG
373: if (log_level >= 4) {
374: midi_log(oep);
375: log_puts(": out: ");
376: for (i = 0; i < ocount; i++) {
377: log_puts(" ");
378: log_putx(odata[i]);
379: }
380: log_puts("\n");
381: }
382: #endif
383: abuf_wcommit(&oep->obuf, ocount);
384: icount -= ocount;
385: idata += ocount;
386: }
387: }
388:
389: void
390: port_log(struct port *p)
391: {
392: midi_log(p->midi);
393: }
394:
395: void
396: port_imsg(void *arg, unsigned char *msg, int size)
397: {
398: struct port *p = arg;
399:
400: midi_send(p->midi, msg, size);
401: }
402:
403:
404: void
405: port_omsg(void *arg, unsigned char *msg, int size)
406: {
407: struct port *p = arg;
408:
409: midi_out(p->midi, msg, size);
410: }
411:
412: void
413: port_fill(void *arg, int count)
414: {
415: /* no flow control */
416: }
417:
418: void
419: port_exit(void *arg)
420: {
421: #ifdef DEBUG
422: struct port *p = arg;
423:
424: if (log_level >= 3) {
425: port_log(p);
1.3 ratchov 426: log_puts(": port exit\n");
427: panic();
1.1 ratchov 428: }
429: #endif
430: }
431:
432: /*
433: * create a new midi port
434: */
435: struct port *
1.4 ratchov 436: port_new(char *path, unsigned int mode, int hold)
1.1 ratchov 437: {
1.12 ratchov 438: struct port *c;
1.1 ratchov 439:
440: c = xmalloc(sizeof(struct port));
1.22 ! ratchov 441: c->path_list = NULL;
! 442: namelist_add(&c->path_list, path);
1.1 ratchov 443: c->state = PORT_CFG;
1.4 ratchov 444: c->hold = hold;
1.1 ratchov 445: c->midi = midi_new(&port_midiops, c, mode);
1.11 ratchov 446: c->num = midi_portnum++;
1.12 ratchov 447: c->next = port_list;
448: port_list = c;
1.1 ratchov 449: return c;
450: }
451:
452: /*
453: * destroy the given midi port
454: */
455: void
456: port_del(struct port *c)
457: {
458: struct port **p;
459:
460: if (c->state != PORT_CFG)
461: port_close(c);
462: midi_del(c->midi);
463: for (p = &port_list; *p != c; p = &(*p)->next) {
464: #ifdef DEBUG
465: if (*p == NULL) {
466: log_puts("port to delete not on list\n");
467: panic();
468: }
469: #endif
470: }
471: *p = c->next;
1.22 ! ratchov 472: namelist_clear(&c->path_list);
1.1 ratchov 473: xfree(c);
474: }
475:
1.3 ratchov 476: int
477: port_ref(struct port *c)
478: {
479: #ifdef DEBUG
480: if (log_level >= 3) {
481: port_log(c);
482: log_puts(": port requested\n");
483: }
484: #endif
485: if (c->state == PORT_CFG && !port_open(c))
486: return 0;
487: return 1;
488: }
489:
490: void
491: port_unref(struct port *c)
492: {
493: int i, rxmask;
494:
495: #ifdef DEBUG
496: if (log_level >= 3) {
497: port_log(c);
498: log_puts(": port released\n");
499: }
500: #endif
501: for (rxmask = 0, i = 0; i < MIDI_NEP; i++)
502: rxmask |= midi_ep[i].txmask;
1.10 ratchov 503: if ((rxmask & c->midi->self) == 0 && c->midi->txmask == 0 &&
504: c->state == PORT_INIT && !c->hold)
1.5 ratchov 505: port_drain(c);
1.3 ratchov 506: }
507:
1.1 ratchov 508: struct port *
509: port_bynum(int num)
510: {
511: struct port *p;
512:
513: for (p = port_list; p != NULL; p = p->next) {
1.12 ratchov 514: if (p->num == num)
1.1 ratchov 515: return p;
516: }
517: return NULL;
518: }
519:
520: int
521: port_open(struct port *c)
522: {
523: if (!port_mio_open(c)) {
524: if (log_level >= 1) {
1.21 ratchov 525: port_log(c);
1.1 ratchov 526: log_puts(": failed to open midi port\n");
527: }
528: return 0;
529: }
530: c->state = PORT_INIT;
531: return 1;
532: }
533:
1.20 ratchov 534: void
535: port_exitall(struct port *c)
536: {
537: int i;
538: struct midi *ep;
539:
540: for (i = 0; i < MIDI_NEP; i++) {
541: ep = midi_ep + i;
542: if ((ep->txmask & c->midi->self) ||
543: (c->midi->txmask & ep->self))
544: ep->ops->exit(ep->arg);
545: }
546: }
547:
1.1 ratchov 548: int
549: port_close(struct port *c)
550: {
551: #ifdef DEBUG
552: if (c->state == PORT_CFG) {
553: port_log(c);
554: log_puts(": can't close port (not opened)\n");
1.3 ratchov 555: panic();
1.1 ratchov 556: }
557: #endif
1.15 ratchov 558: c->state = PORT_CFG;
1.1 ratchov 559: port_mio_close(c);
1.15 ratchov 560:
1.20 ratchov 561: port_exitall(c);
1.1 ratchov 562: return 1;
563: }
564:
1.5 ratchov 565: void
566: port_drain(struct port *c)
567: {
568: struct midi *ep = c->midi;
569:
570: if (!(ep->mode & MODE_MIDIOUT) || ep->obuf.used == 0)
571: port_close(c);
572: else {
573: c->state = PORT_DRAIN;
574: #ifdef DEBUG
575: if (log_level >= 3) {
576: port_log(c);
577: log_puts(": draining\n");
578: }
579: #endif
580: }
581: }
582:
1.1 ratchov 583: int
584: port_init(struct port *c)
585: {
1.4 ratchov 586: if (c->hold)
587: return port_open(c);
588: return 1;
1.1 ratchov 589: }
590:
591: void
592: port_done(struct port *c)
593: {
1.5 ratchov 594: if (c->state == PORT_INIT)
595: port_drain(c);
1.22 ! ratchov 596: }
! 597:
! 598: int
! 599: port_reopen(struct port *p)
! 600: {
! 601: if (p->state == PORT_CFG)
! 602: return 1;
! 603:
! 604: if (!port_mio_reopen(p))
! 605: return 0;
! 606:
! 607: return 1;
1.1 ratchov 608: }