[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.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: }