[BACK]Return to midi.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sndiod

Annotation of src/usr.bin/sndiod/midi.c, Revision 1.20

1.20    ! ratchov     1: /*     $OpenBSD: midi.c,v 1.19 2019/05/10 04:45:47 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.14      ratchov   441:        c->path = xstrdup(path);
1.1       ratchov   442:        c->state = PORT_CFG;
1.4       ratchov   443:        c->hold = hold;
1.1       ratchov   444:        c->midi = midi_new(&port_midiops, c, mode);
1.11      ratchov   445:        c->num = midi_portnum++;
1.12      ratchov   446:        c->next = port_list;
                    447:        port_list = c;
1.1       ratchov   448:        return c;
                    449: }
                    450:
                    451: /*
                    452:  * destroy the given midi port
                    453:  */
                    454: void
                    455: port_del(struct port *c)
                    456: {
                    457:        struct port **p;
                    458:
                    459:        if (c->state != PORT_CFG)
                    460:                port_close(c);
                    461:        midi_del(c->midi);
                    462:        for (p = &port_list; *p != c; p = &(*p)->next) {
                    463: #ifdef DEBUG
                    464:                if (*p == NULL) {
                    465:                        log_puts("port to delete not on list\n");
                    466:                        panic();
                    467:                }
                    468: #endif
                    469:        }
                    470:        *p = c->next;
1.14      ratchov   471:        xfree(c->path);
1.1       ratchov   472:        xfree(c);
                    473: }
                    474:
1.3       ratchov   475: int
                    476: port_ref(struct port *c)
                    477: {
                    478: #ifdef DEBUG
                    479:        if (log_level >= 3) {
                    480:                port_log(c);
                    481:                log_puts(": port requested\n");
                    482:        }
                    483: #endif
                    484:        if (c->state == PORT_CFG && !port_open(c))
                    485:                return 0;
                    486:        return 1;
                    487: }
                    488:
                    489: void
                    490: port_unref(struct port *c)
                    491: {
                    492:        int i, rxmask;
                    493:
                    494: #ifdef DEBUG
                    495:        if (log_level >= 3) {
                    496:                port_log(c);
                    497:                log_puts(": port released\n");
                    498:        }
                    499: #endif
                    500:        for (rxmask = 0, i = 0; i < MIDI_NEP; i++)
                    501:                rxmask |= midi_ep[i].txmask;
1.10      ratchov   502:        if ((rxmask & c->midi->self) == 0 && c->midi->txmask == 0 &&
                    503:            c->state == PORT_INIT && !c->hold)
1.5       ratchov   504:                port_drain(c);
1.3       ratchov   505: }
                    506:
1.1       ratchov   507: struct port *
                    508: port_bynum(int num)
                    509: {
                    510:        struct port *p;
                    511:
                    512:        for (p = port_list; p != NULL; p = p->next) {
1.12      ratchov   513:                if (p->num == num)
1.1       ratchov   514:                        return p;
                    515:        }
                    516:        return NULL;
                    517: }
                    518:
                    519: int
                    520: port_open(struct port *c)
                    521: {
                    522:        if (!port_mio_open(c)) {
                    523:                if (log_level >= 1) {
                    524:                        log_puts(c->path);
                    525:                        log_puts(": failed to open midi port\n");
                    526:                }
                    527:                return 0;
                    528:        }
                    529:        c->state = PORT_INIT;
                    530:        return 1;
                    531: }
                    532:
1.20    ! ratchov   533: void
        !           534: port_exitall(struct port *c)
        !           535: {
        !           536:        int i;
        !           537:        struct midi *ep;
        !           538:
        !           539:        for (i = 0; i < MIDI_NEP; i++) {
        !           540:                ep = midi_ep + i;
        !           541:                if ((ep->txmask & c->midi->self) ||
        !           542:                    (c->midi->txmask & ep->self))
        !           543:                        ep->ops->exit(ep->arg);
        !           544:        }
        !           545: }
        !           546:
1.1       ratchov   547: int
                    548: port_close(struct port *c)
                    549: {
                    550: #ifdef DEBUG
                    551:        if (c->state == PORT_CFG) {
                    552:                port_log(c);
                    553:                log_puts(": can't close port (not opened)\n");
1.3       ratchov   554:                panic();
1.1       ratchov   555:        }
                    556: #endif
1.15      ratchov   557:        c->state = PORT_CFG;
1.1       ratchov   558:        port_mio_close(c);
1.15      ratchov   559:
1.20    ! ratchov   560:        port_exitall(c);
1.1       ratchov   561:        return 1;
                    562: }
                    563:
1.5       ratchov   564: void
                    565: port_drain(struct port *c)
                    566: {
                    567:        struct midi *ep = c->midi;
                    568:
                    569:        if (!(ep->mode & MODE_MIDIOUT) || ep->obuf.used == 0)
                    570:                port_close(c);
                    571:        else {
                    572:                c->state = PORT_DRAIN;
                    573: #ifdef DEBUG
                    574:                if (log_level >= 3) {
                    575:                        port_log(c);
                    576:                        log_puts(": draining\n");
                    577:                }
                    578: #endif
                    579:        }
                    580: }
                    581:
1.1       ratchov   582: int
                    583: port_init(struct port *c)
                    584: {
1.4       ratchov   585:        if (c->hold)
                    586:                return port_open(c);
                    587:        return 1;
1.1       ratchov   588: }
                    589:
                    590: void
                    591: port_done(struct port *c)
                    592: {
1.5       ratchov   593:        if (c->state == PORT_INIT)
                    594:                port_drain(c);
1.1       ratchov   595: }