Annotation of src/usr.bin/sndiod/midi.c, Revision 1.1
1.1 ! ratchov 1: /* $OpenBSD$ */
! 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: /*
! 18: * TODO
! 19: *
! 20: * use shadow variables (to save NRPNs, LSB of controller)
! 21: * in the midi merger
! 22: */
! 23: #include <stdio.h>
! 24: #include <stdlib.h>
! 25: #include <string.h>
! 26:
! 27: #include "abuf.h"
! 28: #include "defs.h"
! 29: #include "dev.h"
! 30: #include "file.h"
! 31: #include "midi.h"
! 32: #include "miofile.h"
! 33: #include "sysex.h"
! 34: #include "utils.h"
! 35:
! 36: /*
! 37: * input data rate is XFER / TIMO (in bytes per microsecond),
! 38: * it must be slightly larger than the MIDI standard 3125 bytes/s
! 39: */
! 40: #define MIDI_XFER 1
! 41: #define MIDI_TIMO 100000
! 42:
! 43: int port_open(struct port *);
! 44: void port_imsg(void *, unsigned char *, int);
! 45: void port_omsg(void *, unsigned char *, int);
! 46: void port_fill(void *, int);
! 47: void port_exit(void *);
! 48:
! 49: struct midiops port_midiops = {
! 50: port_imsg,
! 51: port_omsg,
! 52: port_fill,
! 53: port_exit
! 54: };
! 55:
! 56: #define MIDI_NEP 32
! 57: struct midi midi_ep[MIDI_NEP];
! 58: struct timo midi_timo;
! 59: struct port *port_list = NULL;
! 60: unsigned int midi_portnum = 0;
! 61:
! 62: struct midithru {
! 63: unsigned txmask;
! 64: #define MIDITHRU_NMAX 32
! 65: } midithru[MIDITHRU_NMAX];
! 66:
! 67: /*
! 68: * length of voice and common messages (status byte included)
! 69: */
! 70: unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
! 71: unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };
! 72:
! 73: void
! 74: midi_log(struct midi *ep)
! 75: {
! 76: log_puts("midi");
! 77: log_putu(ep - midi_ep);
! 78: }
! 79:
! 80: void
! 81: midi_ontimo(void *arg)
! 82: {
! 83: int i;
! 84: struct midi *ep;
! 85:
! 86: for (i = MIDI_NEP, ep = midi_ep; i > 0; i--, ep++) {
! 87: }
! 88: timo_add(&midi_timo, MIDI_TIMO);
! 89: }
! 90:
! 91: void
! 92: midi_init(void)
! 93: {
! 94: timo_set(&midi_timo, midi_ontimo, NULL);
! 95: timo_add(&midi_timo, MIDI_TIMO);
! 96: }
! 97:
! 98: void
! 99: midi_done(void)
! 100: {
! 101: timo_del(&midi_timo);
! 102: }
! 103:
! 104: struct midi *
! 105: midi_new(struct midiops *ops, void *arg, int mode)
! 106: {
! 107: int i;
! 108: struct midi *ep;
! 109:
! 110: for (i = 0, ep = midi_ep;; i++, ep++) {
! 111: if (i == MIDI_NEP)
! 112: return NULL;
! 113: if (ep->ops == NULL)
! 114: break;
! 115: }
! 116: ep->ops = ops;
! 117: ep->arg = arg;
! 118: ep->used = 0;
! 119: ep->len = 0;
! 120: ep->idx = 0;
! 121: ep->st = 0;
! 122: ep->txmask = 0;
! 123: ep->rxmask = 1 << i;
! 124: ep->mode = mode;
! 125: /*
! 126: * client output is our input (ibuf) and our output (obuf) goes
! 127: * to client input
! 128: */
! 129: if (ep->mode & MODE_MIDIOUT) {
! 130: abuf_init(&ep->ibuf, MIDI_BUFSZ);
! 131: }
! 132: if (ep->mode & MODE_MIDIIN) {
! 133: abuf_init(&ep->obuf, MIDI_BUFSZ);
! 134: }
! 135: return ep;
! 136: }
! 137:
! 138: void
! 139: midi_del(struct midi *ep)
! 140: {
! 141: int i;
! 142:
! 143: for (i = 0; i < MIDI_NEP; i++)
! 144: midi_ep[i].txmask &= ~ep->rxmask;
! 145: for (i = 0; i < MIDITHRU_NMAX; i++)
! 146: midithru[i].txmask &= ~ep->rxmask;
! 147:
! 148: /* XXX: drain output */
! 149: ep->ops = NULL;
! 150: if (ep->mode & MODE_MIDIOUT) {
! 151: abuf_done(&ep->ibuf);
! 152: }
! 153: if (ep->mode & MODE_MIDIIN) {
! 154: abuf_done(&ep->obuf);
! 155: }
! 156: }
! 157:
! 158: /*
! 159: * add the midi endpoint in the ``tag'' midi thru box
! 160: */
! 161: void
! 162: midi_tag(struct midi *ep, unsigned int tag)
! 163: {
! 164: int i;
! 165: struct midi *m;
! 166: unsigned members;
! 167:
! 168: members = midithru[tag].txmask;
! 169: midithru[tag].txmask |= ep->rxmask;
! 170:
! 171: for (i = 0, m = midi_ep; i < MIDI_NEP; i++, m++) {
! 172: if (!(members & (1 << i)))
! 173: continue;
! 174: if (ep->mode & MODE_MIDIOUT)
! 175: ep->txmask |= m->rxmask;
! 176: if (ep->mode & MODE_MIDIIN)
! 177: m->txmask |= ep->rxmask;
! 178: }
! 179: }
! 180:
! 181: /*
! 182: * remove the midi endpoint from the ``tag'' midi thru box
! 183: */
! 184: void
! 185: midi_untag(struct midi *ep, unsigned int tag)
! 186: {
! 187: int i;
! 188: struct midi *m;
! 189: unsigned members;
! 190:
! 191: members = midithru[tag].txmask;
! 192: midithru[tag].txmask &= ~ep->rxmask;
! 193:
! 194: for (i = 0, m = midi_ep;; i++, m++) {
! 195: if (!(members & (1 << i)))
! 196: continue;
! 197: ep->txmask &= ~m->rxmask;
! 198: m->txmask &= ~ep->rxmask;
! 199: }
! 200: }
! 201:
! 202: /*
! 203: * broadcast the given message to other members of the thru box
! 204: */
! 205: void
! 206: midi_send(struct midi *iep, unsigned char *msg, int size)
! 207: {
! 208: struct midi *oep;
! 209: int i;
! 210:
! 211: #ifdef DEBUG
! 212: if (log_level >= 4) {
! 213: midi_log(iep);
! 214: log_puts(": sending:");
! 215: for (i = 0; i < size; i++) {
! 216: log_puts(" ");
! 217: log_putx(msg[i]);
! 218: }
! 219: log_puts("\n");
! 220: }
! 221: #endif
! 222: for (i = 0; i < MIDI_NEP ; i++) {
! 223: if ((iep->txmask & (1 << i)) == 0)
! 224: continue;
! 225: oep = midi_ep + i;
! 226: if (msg[0] <= 0x7f) {
! 227: if (oep->owner != iep)
! 228: continue;
! 229: } else if (msg[0] <= 0xf7)
! 230: oep->owner = iep;
! 231: #ifdef DEBUG
! 232: if (log_level >= 4) {
! 233: midi_log(iep);
! 234: log_puts(" -> ");
! 235: midi_log(oep);
! 236: log_puts("\n");
! 237: }
! 238: #endif
! 239: oep->ops->omsg(oep->arg, msg, size);
! 240: }
! 241: }
! 242:
! 243:
! 244: void
! 245: midi_fill(struct midi *oep)
! 246: {
! 247: int i, count;
! 248: struct midi *iep;
! 249:
! 250: for (i = 0; i < MIDI_NEP ; i++) {
! 251: if ((oep->rxmask & (1 << i)) == 0)
! 252: continue;
! 253: iep = midi_ep + i;
! 254: count = midi_in(iep);
! 255: if (count)
! 256: iep->ops->fill(iep->arg, count);
! 257: }
! 258: }
! 259:
! 260: /*
! 261: * parse the give data chunk, and calling imsg() for each message
! 262: */
! 263: void
! 264: midi_parse(struct midi *iep, unsigned char *idata, int icount)
! 265: {
! 266: int i;
! 267: unsigned char c;
! 268:
! 269: for (i = 0; i < icount; i++) {
! 270: c = *idata++;
! 271: if (c >= 0xf8) {
! 272: if (c != MIDI_ACK)
! 273: iep->ops->imsg(iep->arg, &c, 1);
! 274: } else if (c == SYSEX_END) {
! 275: if (iep->st == SYSEX_START) {
! 276: iep->msg[iep->idx++] = c;
! 277: iep->ops->imsg(iep->arg, iep->msg, iep->idx);
! 278: }
! 279: iep->st = 0;
! 280: iep->idx = 0;
! 281: } else if (c >= 0xf0) {
! 282: iep->msg[0] = c;
! 283: iep->len = common_len[c & 7];
! 284: iep->st = c;
! 285: iep->idx = 1;
! 286: } else if (c >= 0x80) {
! 287: iep->msg[0] = c;
! 288: iep->len = voice_len[(c >> 4) & 7];
! 289: iep->st = c;
! 290: iep->idx = 1;
! 291: } else if (iep->st) {
! 292: if (iep->idx == 0 && iep->st != SYSEX_START)
! 293: iep->msg[iep->idx++] = iep->st;
! 294: iep->msg[iep->idx++] = c;
! 295: if (iep->idx == iep->len) {
! 296: iep->ops->imsg(iep->arg, iep->msg, iep->idx);
! 297: if (iep->st >= 0xf0)
! 298: iep->st = 0;
! 299: iep->idx = 0;
! 300: } else if (iep->idx == MIDI_MSGMAX) {
! 301: /* sysex continued */
! 302: iep->ops->imsg(iep->arg, iep->msg, iep->idx);
! 303: iep->idx = 0;
! 304: }
! 305: }
! 306: }
! 307: }
! 308:
! 309: /*
! 310: * process input data stored in ep->ibuf
! 311: */
! 312: int
! 313: midi_in(struct midi *iep)
! 314: {
! 315: unsigned char *idata;
! 316: int i, icount, maxavail, avail, idone;
! 317: struct midi *oep;
! 318:
! 319: /*
! 320: * calculate the max message size we can process
! 321: */
! 322: maxavail = MIDI_BUFSZ;
! 323: for (i = 0; i < MIDI_NEP ; i++) {
! 324: if ((iep->txmask & (1 << i)) == 0)
! 325: continue;
! 326: oep = midi_ep + i;
! 327: avail = oep->obuf.len - oep->obuf.used;
! 328: if (maxavail > avail)
! 329: maxavail = avail;
! 330: }
! 331:
! 332: /*
! 333: * in the works case output message is twice the
! 334: * input message (2-byte messages with running status)
! 335: */
! 336: maxavail /= 2;
! 337: idone = 0;
! 338: for (;;) {
! 339: idata = abuf_rgetblk(&iep->ibuf, &icount);
! 340: if (icount > maxavail)
! 341: icount = maxavail;
! 342: if (icount == 0)
! 343: break;
! 344: maxavail -= icount;
! 345: #ifdef DEBUG
! 346: if (log_level >= 4) {
! 347: midi_log(iep);
! 348: log_puts(": in:");
! 349: for (i = 0; i < icount; i++) {
! 350: log_puts(" ");
! 351: log_putx(idata[i]);
! 352: }
! 353: log_puts("\n");
! 354: }
! 355: #endif
! 356: midi_parse(iep, idata, icount);
! 357: abuf_rdiscard(&iep->ibuf, icount);
! 358: idone += icount;
! 359: }
! 360: return idone;
! 361: }
! 362:
! 363: /*
! 364: * store the given message in the output buffer
! 365: */
! 366: void
! 367: midi_out(struct midi *oep, unsigned char *idata, int icount)
! 368: {
! 369: unsigned char *odata;
! 370: int ocount;
! 371: #ifdef DEBUG
! 372: int i;
! 373: #endif
! 374:
! 375: while (icount > 0) {
! 376: if (oep->obuf.used == oep->obuf.len) {
! 377: #ifdef DEBUG
! 378: if (log_level >= 2) {
! 379: midi_log(oep);
! 380: log_puts(": overrun, discarding ");
! 381: log_putu(oep->obuf.used);
! 382: log_puts(" bytes\n");
! 383: }
! 384: #endif
! 385: abuf_rdiscard(&oep->obuf, oep->obuf.used);
! 386: oep->owner = NULL;
! 387: return;
! 388: }
! 389: odata = abuf_wgetblk(&oep->obuf, &ocount);
! 390: if (ocount > icount)
! 391: ocount = icount;
! 392: memcpy(odata, idata, ocount);
! 393: #ifdef DEBUG
! 394: if (log_level >= 4) {
! 395: midi_log(oep);
! 396: log_puts(": out: ");
! 397: for (i = 0; i < ocount; i++) {
! 398: log_puts(" ");
! 399: log_putx(odata[i]);
! 400: }
! 401: log_puts("\n");
! 402: }
! 403: #endif
! 404: abuf_wcommit(&oep->obuf, ocount);
! 405: icount -= ocount;
! 406: idata += ocount;
! 407: }
! 408: }
! 409:
! 410: #ifdef DEBUG
! 411: void
! 412: port_log(struct port *p)
! 413: {
! 414: midi_log(p->midi);
! 415: }
! 416: #endif
! 417:
! 418: void
! 419: port_imsg(void *arg, unsigned char *msg, int size)
! 420: {
! 421: struct port *p = arg;
! 422:
! 423: midi_send(p->midi, msg, size);
! 424: }
! 425:
! 426:
! 427: void
! 428: port_omsg(void *arg, unsigned char *msg, int size)
! 429: {
! 430: struct port *p = arg;
! 431:
! 432: midi_out(p->midi, msg, size);
! 433: }
! 434:
! 435: void
! 436: port_fill(void *arg, int count)
! 437: {
! 438: /* no flow control */
! 439: }
! 440:
! 441: void
! 442: port_exit(void *arg)
! 443: {
! 444: #ifdef DEBUG
! 445: struct port *p = arg;
! 446:
! 447: if (log_level >= 3) {
! 448: port_log(p);
! 449: log_puts(": exit\n");
! 450: }
! 451: #endif
! 452: }
! 453:
! 454: /*
! 455: * create a new midi port
! 456: */
! 457: struct port *
! 458: port_new(char *path, unsigned int mode)
! 459: {
! 460: struct port *c;
! 461:
! 462: c = xmalloc(sizeof(struct port));
! 463: c->path = path;
! 464: c->state = PORT_CFG;
! 465: c->midi = midi_new(&port_midiops, c, mode);
! 466: midi_portnum++;
! 467: c->next = port_list;
! 468: port_list = c;
! 469: return c;
! 470: }
! 471:
! 472: /*
! 473: * destroy the given midi port
! 474: */
! 475: void
! 476: port_del(struct port *c)
! 477: {
! 478: struct port **p;
! 479:
! 480: if (c->state != PORT_CFG)
! 481: port_close(c);
! 482: midi_del(c->midi);
! 483: for (p = &port_list; *p != c; p = &(*p)->next) {
! 484: #ifdef DEBUG
! 485: if (*p == NULL) {
! 486: log_puts("port to delete not on list\n");
! 487: panic();
! 488: }
! 489: #endif
! 490: }
! 491: *p = c->next;
! 492: xfree(c);
! 493: }
! 494:
! 495: struct port *
! 496: port_bynum(int num)
! 497: {
! 498: struct port *p;
! 499:
! 500: for (p = port_list; p != NULL; p = p->next) {
! 501: if (num-- == 0)
! 502: return p;
! 503: }
! 504: return NULL;
! 505: }
! 506:
! 507: int
! 508: port_open(struct port *c)
! 509: {
! 510: if (!port_mio_open(c)) {
! 511: if (log_level >= 1) {
! 512: log_puts(c->path);
! 513: log_puts(": failed to open midi port\n");
! 514: }
! 515: return 0;
! 516: }
! 517: c->state = PORT_INIT;
! 518: return 1;
! 519: }
! 520:
! 521: int
! 522: port_close(struct port *c)
! 523: {
! 524: #ifdef DEBUG
! 525: if (c->state == PORT_CFG) {
! 526: port_log(c);
! 527: log_puts(": can't close port (not opened)\n");
! 528: }
! 529: #endif
! 530: port_mio_close(c);
! 531: c->state = PORT_CFG;
! 532: return 1;
! 533: }
! 534:
! 535: int
! 536: port_init(struct port *c)
! 537: {
! 538: return port_open(c);
! 539: }
! 540:
! 541: void
! 542: port_done(struct port *c)
! 543: {
! 544: /* XXX: drain? */
! 545: if (c->state != PORT_CFG)
! 546: port_close(c);
! 547: }