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